diff --git a/Flow.Launcher.Infrastructure/Storage/JsonStorage.cs b/Flow.Launcher.Infrastructure/Storage/JsonStorage.cs index 0b10382ee..20aacb344 100644 --- a/Flow.Launcher.Infrastructure/Storage/JsonStorage.cs +++ b/Flow.Launcher.Infrastructure/Storage/JsonStorage.cs @@ -45,6 +45,11 @@ namespace Flow.Launcher.Infrastructure.Storage FilesFolders.ValidateDirectory(DirectoryPath); } + public bool Exists() + { + return File.Exists(FilePath); + } + public async Task LoadAsync() { if (Data != null) diff --git a/Flow.Launcher/Storage/TopMostRecord.cs b/Flow.Launcher/Storage/TopMostRecord.cs index 63f0a2fce..25774a2c0 100644 --- a/Flow.Launcher/Storage/TopMostRecord.cs +++ b/Flow.Launcher/Storage/TopMostRecord.cs @@ -1,5 +1,6 @@ using System.Collections.Concurrent; using System.Collections.Generic; +using System.Linq; using System.Text.Json.Serialization; using Flow.Launcher.Infrastructure.Storage; using Flow.Launcher.Plugin; @@ -8,14 +9,29 @@ namespace Flow.Launcher.Storage { public class FlowLauncherJsonStorageTopMostRecord : ISavable { - private readonly FlowLauncherJsonStorage _topMostRecordStorage; - - private readonly TopMostRecord _topMostRecord; + private readonly FlowLauncherJsonStorage _topMostRecordStorage; + private readonly MultipleTopMostRecord _topMostRecord; public FlowLauncherJsonStorageTopMostRecord() { - _topMostRecordStorage = new FlowLauncherJsonStorage(); - _topMostRecord = _topMostRecordStorage.Load(); + var topMostRecordStorage = new FlowLauncherJsonStorage(); + var exist = topMostRecordStorage.Exists(); + if (exist) + { + // Get old data + var topMostRecord = topMostRecordStorage.Load(); + + // Convert to new data + _topMostRecordStorage = new FlowLauncherJsonStorage(); + _topMostRecord = _topMostRecordStorage.Load(); + _topMostRecord.Add(topMostRecord); + } + else + { + // Get new data + _topMostRecordStorage = new FlowLauncherJsonStorage(); + _topMostRecord = _topMostRecordStorage.Load(); + } } public void Save() @@ -42,7 +58,7 @@ namespace Flow.Launcher.Storage public class TopMostRecord { [JsonInclude] - public ConcurrentDictionary records { get; private set; } = new ConcurrentDictionary(); + public ConcurrentDictionary records { get; private set; } = new(); internal bool IsTopMost(Result result) { @@ -90,12 +106,113 @@ namespace Flow.Launcher.Storage } } + public class MultipleTopMostRecord + { + [JsonInclude] + public ConcurrentDictionary> records { get; private set; } = new(); + + internal void Add(TopMostRecord topMostRecord) + { + if (topMostRecord == null || topMostRecord.records.IsEmpty) + { + return; + } + + foreach (var record in topMostRecord.records) + { + records.AddOrUpdate(record.Key, new ConcurrentBag { record.Value }, (key, oldValue) => + { + oldValue.Add(record.Value); + return oldValue; + }); + } + } + + internal bool IsTopMost(Result result) + { + // origin query is null when user select the context menu item directly of one item from query list + // in this case, we do not need to check if the result is top most + if (records.IsEmpty || result.OriginQuery == null || + !records.TryGetValue(result.OriginQuery.RawQuery, out var value)) + { + return false; + } + + // since this dictionary should be very small (or empty) going over it should be pretty fast. + return value.Any(record => record.Equals(result)); + } + + internal void Remove(Result result) + { + // origin query is null when user select the context menu item directly of one item from query list + // in this case, we do not need to remove the record + if (result.OriginQuery == null || + !records.TryGetValue(result.OriginQuery.RawQuery, out var value)) + { + return; + } + + // remove the record from the bag + var recordToRemove = value.FirstOrDefault(r => r.Equals(result)); + if (recordToRemove != null) + { + value.TryTake(out recordToRemove); + } + } + + internal void AddOrUpdate(Result result) + { + // origin query is null when user select the context menu item directly of one item from query list + // in this case, we do not need to add or update the record + if (result.OriginQuery == null) + { + return; + } + + var record = new Record + { + PluginID = result.PluginID, + Title = result.Title, + SubTitle = result.SubTitle, + RecordKey = result.RecordKey + }; + if (!records.TryGetValue(result.OriginQuery.RawQuery, out var value)) + { + // create a new bag if it does not exist + value = new ConcurrentBag() + { + record + }; + records.TryAdd(result.OriginQuery.RawQuery, value); + } + else + { + // add or update the record in the bag + if (value.Any(r => r.Equals(result))) + { + // update the record + var recordToUpdate = value.FirstOrDefault(r => r.Equals(result)); + if (recordToUpdate != null) + { + value.TryTake(out recordToUpdate); + value.Add(record); + } + } + else + { + // add the record + value.Add(record); + } + } + } + } + public class Record { - public string Title { get; set; } - public string SubTitle { get; set; } - public string PluginID { get; set; } - public string RecordKey { get; set; } + public string Title { get; init; } + public string SubTitle { get; init; } + public string PluginID { get; init; } + public string RecordKey { get; init; } public bool Equals(Result r) {