diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs index 5c4eaa1dadc..8f2d78d760b 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -281,7 +281,7 @@ public static async Task> QueryForPluginAsync(PluginPair pair, Quer return results; } - public static void UpdatePluginMetadata(List results, PluginMetadata metadata, Query query) + public static void UpdatePluginMetadata(IReadOnlyList results, PluginMetadata metadata, Query query) { foreach (var r in results) { diff --git a/Flow.Launcher.Plugin/Result.cs b/Flow.Launcher.Plugin/Result.cs index bb005752eba..9b16cc1cbf0 100644 --- a/Flow.Launcher.Plugin/Result.cs +++ b/Flow.Launcher.Plugin/Result.cs @@ -266,8 +266,9 @@ public ValueTask ExecuteAsync(ActionContext context) /// The key to identify the record. This is used when FL checks whether the result is the topmost record. Or FL calculates the hashcode of the result for user selected records. /// This can be useful when your plugin will change the Title or SubTitle of the result dynamically. /// If the plugin does not specific this, FL just uses Title and SubTitle to identify this result. + /// Note: Because old data does not have this key, we should use null as the default value for consistency. /// - public string RecordKey { get; set; } = string.Empty; + public string RecordKey { get; set; } = null; /// /// Info of the preview section of a diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 5c3251bfc7e..a9809a49a79 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -231,8 +231,8 @@ private void RegisterResultsUpdatedEvent() var token = e.Token == default ? _updateToken : e.Token; - // make a copy of results to avoid plugin change the result when updating view model - var resultsCopy = e.Results.ToList(); + // make a clone to avoid possible issue that plugin will also change the list and items when updating view model + var resultsCopy = DeepCloneResults(e.Results, token); PluginManager.UpdatePluginMetadata(resultsCopy, pair.Metadata, e.Query); if (!_resultsUpdateChannelWriter.TryWrite(new ResultsForUpdate(resultsCopy, pair.Metadata, e.Query, @@ -414,6 +414,22 @@ private async Task OpenResultAsync(string index) } } + private static IReadOnlyList DeepCloneResults(IReadOnlyList results, CancellationToken token = default) + { + var resultsCopy = new List(); + foreach (var result in results.ToList()) + { + if (token.IsCancellationRequested) + { + break; + } + + var resultCopy = result.Clone(); + resultsCopy.Add(resultCopy); + } + return resultsCopy; + } + #endregion #region BasicCommands @@ -1180,9 +1196,18 @@ async Task QueryTask(PluginPair plugin, bool reSelect = true) currentCancellationToken.ThrowIfCancellationRequested(); - results ??= _emptyResult; + IReadOnlyList resultsCopy; + if (results == null) + { + resultsCopy = _emptyResult; + } + else + { + // make a copy of results to avoid possible issue that FL changes some properties of the records, like score, etc. + resultsCopy = DeepCloneResults(results); + } - if (!_resultsUpdateChannelWriter.TryWrite(new ResultsForUpdate(results, plugin.Metadata, query, + if (!_resultsUpdateChannelWriter.TryWrite(new ResultsForUpdate(resultsCopy, plugin.Metadata, query, currentCancellationToken, reSelect))) { Log.Error("MainViewModel", "Unable to add item to Result Update Queue"); @@ -1471,12 +1496,31 @@ public void UpdateResultView(ICollection resultsForUpdates) { result.Score = Result.MaxScore; } - else if (result.Score != Result.MaxScore) + else { var priorityScore = metaResults.Metadata.Priority * 150; - result.Score += result.AddSelectedCount ? - _userSelectedRecord.GetSelectedCount(result) + priorityScore : - priorityScore; + if (result.AddSelectedCount) + { + if ((long)result.Score + _userSelectedRecord.GetSelectedCount(result) + priorityScore > Result.MaxScore) + { + result.Score = Result.MaxScore; + } + else + { + result.Score += _userSelectedRecord.GetSelectedCount(result) + priorityScore; + } + } + else + { + if ((long)result.Score + priorityScore > Result.MaxScore) + { + result.Score = Result.MaxScore; + } + else + { + result.Score += priorityScore; + } + } } } }