1
1
using System . Collections . Concurrent ;
2
2
using System . Collections . Generic ;
3
+ using System . Linq ;
3
4
using System . Text . Json . Serialization ;
4
5
using Flow . Launcher . Infrastructure . Storage ;
5
6
using Flow . Launcher . Plugin ;
@@ -8,14 +9,29 @@ namespace Flow.Launcher.Storage
8
9
{
9
10
public class FlowLauncherJsonStorageTopMostRecord : ISavable
10
11
{
11
- private readonly FlowLauncherJsonStorage < TopMostRecord > _topMostRecordStorage ;
12
-
13
- private readonly TopMostRecord _topMostRecord ;
12
+ private readonly FlowLauncherJsonStorage < MultipleTopMostRecord > _topMostRecordStorage ;
13
+ private readonly MultipleTopMostRecord _topMostRecord ;
14
14
15
15
public FlowLauncherJsonStorageTopMostRecord ( )
16
16
{
17
- _topMostRecordStorage = new FlowLauncherJsonStorage < TopMostRecord > ( ) ;
18
- _topMostRecord = _topMostRecordStorage . Load ( ) ;
17
+ var topMostRecordStorage = new FlowLauncherJsonStorage < TopMostRecord > ( ) ;
18
+ var exist = topMostRecordStorage . Exists ( ) ;
19
+ if ( exist )
20
+ {
21
+ // Get old data
22
+ var topMostRecord = topMostRecordStorage . Load ( ) ;
23
+
24
+ // Convert to new data
25
+ _topMostRecordStorage = new FlowLauncherJsonStorage < MultipleTopMostRecord > ( ) ;
26
+ _topMostRecord = _topMostRecordStorage . Load ( ) ;
27
+ _topMostRecord . Add ( topMostRecord ) ;
28
+ }
29
+ else
30
+ {
31
+ // Get new data
32
+ _topMostRecordStorage = new FlowLauncherJsonStorage < MultipleTopMostRecord > ( ) ;
33
+ _topMostRecord = _topMostRecordStorage . Load ( ) ;
34
+ }
19
35
}
20
36
21
37
public void Save ( )
@@ -42,7 +58,7 @@ public void AddOrUpdate(Result result)
42
58
public class TopMostRecord
43
59
{
44
60
[ JsonInclude ]
45
- public ConcurrentDictionary < string , Record > records { get ; private set ; } = new ConcurrentDictionary < string , Record > ( ) ;
61
+ public ConcurrentDictionary < string , Record > records { get ; private set ; } = new ( ) ;
46
62
47
63
internal bool IsTopMost ( Result result )
48
64
{
@@ -90,12 +106,113 @@ internal void AddOrUpdate(Result result)
90
106
}
91
107
}
92
108
109
+ public class MultipleTopMostRecord
110
+ {
111
+ [ JsonInclude ]
112
+ public ConcurrentDictionary < string , ConcurrentBag < Record > > records { get ; private set ; } = new ( ) ;
113
+
114
+ internal void Add ( TopMostRecord topMostRecord )
115
+ {
116
+ if ( topMostRecord == null || topMostRecord . records . IsEmpty )
117
+ {
118
+ return ;
119
+ }
120
+
121
+ foreach ( var record in topMostRecord . records )
122
+ {
123
+ records . AddOrUpdate ( record . Key , new ConcurrentBag < Record > { record . Value } , ( key , oldValue ) =>
124
+ {
125
+ oldValue . Add ( record . Value ) ;
126
+ return oldValue ;
127
+ } ) ;
128
+ }
129
+ }
130
+
131
+ internal bool IsTopMost ( Result result )
132
+ {
133
+ // origin query is null when user select the context menu item directly of one item from query list
134
+ // in this case, we do not need to check if the result is top most
135
+ if ( records . IsEmpty || result . OriginQuery == null ||
136
+ ! records . TryGetValue ( result . OriginQuery . RawQuery , out var value ) )
137
+ {
138
+ return false ;
139
+ }
140
+
141
+ // since this dictionary should be very small (or empty) going over it should be pretty fast.
142
+ return value . Any ( record => record . Equals ( result ) ) ;
143
+ }
144
+
145
+ internal void Remove ( Result result )
146
+ {
147
+ // origin query is null when user select the context menu item directly of one item from query list
148
+ // in this case, we do not need to remove the record
149
+ if ( result . OriginQuery == null ||
150
+ ! records . TryGetValue ( result . OriginQuery . RawQuery , out var value ) )
151
+ {
152
+ return ;
153
+ }
154
+
155
+ // remove the record from the bag
156
+ var recordToRemove = value . FirstOrDefault ( r => r . Equals ( result ) ) ;
157
+ if ( recordToRemove != null )
158
+ {
159
+ value . TryTake ( out recordToRemove ) ;
160
+ }
161
+ }
162
+
163
+ internal void AddOrUpdate ( Result result )
164
+ {
165
+ // origin query is null when user select the context menu item directly of one item from query list
166
+ // in this case, we do not need to add or update the record
167
+ if ( result . OriginQuery == null )
168
+ {
169
+ return ;
170
+ }
171
+
172
+ var record = new Record
173
+ {
174
+ PluginID = result . PluginID ,
175
+ Title = result . Title ,
176
+ SubTitle = result . SubTitle ,
177
+ RecordKey = result . RecordKey
178
+ } ;
179
+ if ( ! records . TryGetValue ( result . OriginQuery . RawQuery , out var value ) )
180
+ {
181
+ // create a new bag if it does not exist
182
+ value = new ConcurrentBag < Record > ( )
183
+ {
184
+ record
185
+ } ;
186
+ records . TryAdd ( result . OriginQuery . RawQuery , value ) ;
187
+ }
188
+ else
189
+ {
190
+ // add or update the record in the bag
191
+ if ( value . Any ( r => r . Equals ( result ) ) )
192
+ {
193
+ // update the record
194
+ var recordToUpdate = value . FirstOrDefault ( r => r . Equals ( result ) ) ;
195
+ if ( recordToUpdate != null )
196
+ {
197
+ value . TryTake ( out recordToUpdate ) ;
198
+ value . Add ( record ) ;
199
+ }
200
+ }
201
+ else
202
+ {
203
+ // add the record
204
+ value . Add ( record ) ;
205
+ }
206
+ }
207
+ }
208
+ }
209
+
93
210
public class Record
94
211
{
95
- public string Title { get ; set ; }
96
- public string SubTitle { get ; set ; }
97
- public string PluginID { get ; set ; }
98
- public string RecordKey { get ; set ; }
212
+ public string Title { get ; init ; }
213
+ public string SubTitle { get ; init ; }
214
+ public string PluginID { get ; init ; }
215
+ public string RecordKey { get ; init ; }
99
216
100
217
public bool Equals ( Result r )
101
218
{
0 commit comments