@@ -20,13 +20,13 @@ let setTimeout = (fn, time) => setTimeout(UnitTracker.track(fn), time);
20
20
21
21
external toggleMenuItem : (Fluid . App . menuItem , bool ) => unit = "qmenu_toggleMenuItem" ;
22
22
23
- let fuzzyEmoji = (text, emoji) => {
23
+ let fuzzyEmoji = (text, recentlyUsedMap , emoji) => {
24
24
let score = Fuzzy . fuzzyScore(~exactWeight= 1000 , text, emoji. name);
25
25
let best = emoji. keywords-> Belt . Array . reduce(score, (score, kwd) => {
26
26
Fuzzy . maxScore(score, Fuzzy . fuzzyScore(text, kwd))
27
27
});
28
28
if (best. full) {
29
- Some ((best, emoji))
29
+ Some ((best, emoji, recentlyUsedMap -> Belt . Map . String . get(emoji . name) ))
30
30
} else {
31
31
None
32
32
}
@@ -58,8 +58,11 @@ let dimsForIndex = (~padding=0., index) => {
58
58
}
59
59
};
60
60
61
- let drawEmoji = (dims, {top, left, width, height}, emoji, isSelected) =>
61
+ let drawEmoji = (dims, {top, left, width, height}, emoji, used , isSelected) =>
62
62
if (dims. top +. size >= top && dims. top <= top +. height) {
63
+ if (used != None ) {
64
+ Fluid . Draw . fillRect(dims, {r: 0 . 6 , g: 0 . 6 , b: 0 . 8 , a: 0 . 2 });
65
+ };
63
66
if (isSelected) {
64
67
Fluid . Draw . rect(dims, {r: 0 . 4 , g: 0 . 4 , b: 0 . 4 , a: 0 . 5 });
65
68
};
@@ -90,19 +93,32 @@ module Config = {
90
93
showAtCursor: bool ,
91
94
checkForUpdates: bool ,
92
95
lastCheckedTag: string ,
96
+ recentlyUsed: Belt . Map . String . t ((int , float ))
93
97
};
94
- let default = {showAtCursor: false , checkForUpdates: true , lastCheckedTag: "-" };
98
+ let m = Unix . gettimeofday() ;
99
+ let default = {showAtCursor: false , checkForUpdates: true , lastCheckedTag: "-" , recentlyUsed: Belt . Map . String . empty};
95
100
let ofJson = json => {
96
101
open Json . Infix ;
97
102
let showAtCursor = json |> Json . get("showAtCursor" ) |?> Json . bool |? false ;
98
103
let checkForUpdates = json |> Json . get("checkForUpdates" ) |?> Json . bool |? true ;
99
104
let lastCheckedTag = json |> Json . get("lastCheckedTag" ) |?> Json . string |? "-" ;
100
- {showAtCursor, checkForUpdates, lastCheckedTag}
105
+ let recentlyUsed = json |> Json . get("recentlyUsed" ) |?> Json . array |?>> Belt . List . keepMap(_, item => {
106
+ switch ((item |> Json . get("name" ) |?> Json . string, item |> Json . get("count" ) |?> Json . number, item |> Json . get("date" ) |?> Json . number)) {
107
+ | (Some (name ), Some (count ), Some (date )) => Some ((name, (int_of_float(count), date)))
108
+ | _ => None
109
+ }
110
+ }) |?>> Belt . List . toArray |?>> Belt . Map . String . fromArray |? Belt . Map . String . empty;
111
+ {showAtCursor, checkForUpdates, lastCheckedTag, recentlyUsed}
101
112
};
102
- let toJson = ({showAtCursor, checkForUpdates, lastCheckedTag}) => Json . Object ([
113
+ let toJson = ({showAtCursor, checkForUpdates, lastCheckedTag, recentlyUsed }) => Json . Object ([
103
114
("showAtCursor" , showAtCursor ? Json . True : Json . False ),
104
115
("checkForUpdates" , checkForUpdates ? Json . True : Json . False ),
105
- ("lastCheckedTag" , Json . String (lastCheckedTag))
116
+ ("lastCheckedTag" , Json . String (lastCheckedTag)),
117
+ ("recentlyUsed" , Json . Array (recentlyUsed-> Belt . Map . String . toArray-> Belt . List . fromArray-> Belt . List . map(((name, (count, date))) => Json . Object ([
118
+ ("name" , Json . String (name)),
119
+ ("count" , Json . Number (float_of_int(count))),
120
+ ("date" , Json . Number (date)),
121
+ ] ))))
106
122
] );
107
123
let load = path => switch (Json . parse(Files . readFileExn(path))) {
108
124
| exception _ => default
@@ -121,10 +137,36 @@ module Config = {
121
137
current := fn(current^ );
122
138
save(current^, configPath);
123
139
};
140
+
141
+ let maxRecentlyUsed = 20 ;
142
+
143
+ let removeEmojiUse = name => update(({recentlyUsed} as config) => {... config, recentlyUsed: Belt . Map . String . remove(recentlyUsed, name)});
144
+
145
+ let useEmoji = name => {
146
+ update(({recentlyUsed} as config) => {
147
+ let recentlyUsed = switch (Belt . Map . String . get(recentlyUsed, name)) {
148
+ | Some ((count , date )) => Belt . Map . String . set(recentlyUsed, name, (count + 1 , Unix . gettimeofday() ))
149
+ | None =>
150
+ if (Belt . Map . String . size(recentlyUsed) < maxRecentlyUsed) {
151
+ Belt . Map . String . set(recentlyUsed, name, (1 , Unix . gettimeofday() ))
152
+ } else {
153
+ let least = recentlyUsed-> Belt . Map . String . reduce(None , (least, key, (count, date)) => switch least {
154
+ | None => Some ((key, count, date))
155
+ | Some ((okey , ocount , odate )) when ocount > count || (ocount == count && odate > date) => Some ((key, count, date))
156
+ | _ => least
157
+ });
158
+ switch least {
159
+ | None => recentlyUsed
160
+ | Some ((least , _ , _ )) => Belt . Map . String . remove(recentlyUsed, least)-> Belt . Map . String . set(name, (1 , Unix . gettimeofday() ))
161
+ }
162
+ }
163
+ };
164
+ {... config, recentlyUsed}
165
+ });
166
+ };
124
167
};
125
168
126
169
let checkVersion = (assetsDir, onDone) => {
127
- /* print_endline("Checking version"); */
128
170
switch (Files . readFile(assetsDir-> Filename . concat("git-head" ))) {
129
171
| None => onDone(None )
130
172
| Some (gitHead ) =>
@@ -193,11 +235,31 @@ let startChecking = assetsDir => {
193
235
check()
194
236
};
195
237
238
+ let compareUsed = (a, b) => switch (a, b) {
239
+ | (Some ((acount , adate )), Some ((bcount , bdate ))) =>
240
+ if (acount != bcount) {
241
+ bcount - acount
242
+ } else {
243
+ int_of_float(bdate) - int_of_float(adate)
244
+ }
245
+ | (Some (_), _) => - 1
246
+ | (_, Some (_)) => 1
247
+ | _ => 0
248
+ };
249
+
196
250
let main = (~assetsDir, ~emojis, ~onDone, hooks) => {
197
251
let %hook (text, setText) = useState("");
198
252
let %hook (selection, setSelection) = useState(0);
199
253
let %hook (hasNewVersion, setHasNewVersion) = useState(hasNewVersion^);
200
254
255
+ let onSelect = emoji => {
256
+ Config . useEmoji(emoji. name);
257
+ setSelection(0 );
258
+ setText("" );
259
+ onDone(Some (emoji. char))
260
+ };
261
+ let onCancel = () => onDone(None );
262
+
201
263
onHasNewVersion := setHasNewVersion;
202
264
203
265
let %hook rightClickMenu = useMemo(() => {
@@ -215,18 +277,25 @@ let main = (~assetsDir, ~emojis, ~onDone, hooks) => {
215
277
);
216
278
}, () );
217
279
218
- let filtered = text == "" ? emojis : {
219
- emojis-> Belt . List . keepMap(fuzzyEmoji(text))-> Belt . List . sort(
220
- ((ascore, amoji), (bscore, bmoji)) => Fuzzy . compareScores(ascore, bscore)
221
- )-> Belt . List . map(snd);
280
+ let filtered = text == "" ? emojis-> Belt . List . map(em => (em, Config . current^. recentlyUsed-> Belt . Map . String . get(em. name)))-> Belt . List . sort(((amoji, aused), (bmoji, bused)) => compareUsed(aused, bused)) : {
281
+ emojis-> Belt . List . keepMap(fuzzyEmoji(text, Config . current^. recentlyUsed))-> Belt . List . sort(
282
+ ((ascore, amoji, aused), (bscore, bmoji, bused)) => {
283
+ let uc = compareUsed(aused, bused);
284
+ if (uc == 0 ) {
285
+ Fuzzy . compareScores(ascore, bscore)
286
+ } else {
287
+ uc
288
+ }
289
+ }
290
+ )-> Belt . List . map(((_, emoji, used)) => (emoji, used));
222
291
};
223
292
224
293
let %hook prev = useRef(None);
225
294
226
295
let invalidated = switch (prev. contents) {
227
296
| None => ` Full
228
- | Some ((prevText , prevSelection )) =>
229
- if (prevText != text) {
297
+ | Some ((prevText , prevSelection , prevUsed )) =>
298
+ if (prevText != text || prevUsed !== Config . current ^. recentlyUsed ) {
230
299
` Full
231
300
} else if (prevSelection != selection) {
232
301
` Partial ([
@@ -238,7 +307,7 @@ let main = (~assetsDir, ~emojis, ~onDone, hooks) => {
238
307
}
239
308
};
240
309
241
- prev. contents = Some ((text, selection));
310
+ prev. contents = Some ((text, selection, Config . current ^. recentlyUsed ));
242
311
243
312
let rows = ceil(float_of_int(List . length(filtered)) /. rowf)-> int_of_float;
244
313
@@ -249,33 +318,39 @@ let main = (~assetsDir, ~emojis, ~onDone, hooks) => {
249
318
}
250
319
}, filtered)
251
320
321
+ let %hook rightMouseDown = useCallback((pos) => {
322
+ let index = indexForPos(pos);
323
+ switch (filtered-> Belt . List . get(index)) {
324
+ | None => ()
325
+ | Some ((emoji , _ )) =>
326
+ Config . removeEmojiUse(emoji. name);
327
+ setText(text);
328
+ }
329
+ }, (text, filtered))
330
+
252
331
let %hook mouseDown = useCallback((pos) => {
253
332
let index = indexForPos(pos);
254
333
switch (filtered-> Belt . List . get(index)) {
255
334
| None => ()
256
- | Some ({char}) =>
257
- setText("" )
258
- setSelection(0 );
259
- onDone(Some (char));
335
+ | Some ((emoji , _ )) =>
336
+ onSelect(emoji);
260
337
}
261
338
}, filtered)
262
339
263
340
let %hook draw = useCallback((bounds) => {
264
- filtered ->Belt . List . forEachWithIndex ((index , emoji ) => drawEmoji (dimsForIndex (index ), bounds , emoji , index == selection ));
265
- }, (text , selection));
341
+ filtered ->Belt . List . forEachWithIndex ((index , ( emoji , used )) => drawEmoji (dimsForIndex (index ), bounds , emoji , used , index == selection ));
342
+ }, (filtered , selection));
266
343
267
344
let %hook onEnter = useCallback(text => {
268
345
switch (Belt . List . get (filtered , selection )) {
269
- | None => onDone( None )
270
- | Some ({char}) => onDone( Some (char) )
346
+ | None => onCancel( )
347
+ | Some ((emoji, _)) => onSelect(emoji )
271
348
};
272
- setSelection(0 );
273
- setText("" );
274
349
}, (filtered, selection));
275
350
276
351
let %hook onEscape = useCallback({() => {
277
352
if (text == "" ) {
278
- onDone(None )
353
+ onCancel( )
279
354
} else {
280
355
setSelection(0);
281
356
setText("");
@@ -322,20 +397,21 @@ let main = (~assetsDir, ~emojis, ~onDone, hooks) => {
322
397
<view layout= {
323
398
Layout . style(~paddingHorizontal= 10 ., ~alignSelf= AlignStretch , () )
324
399
}>
325
- <custom
326
- invalidated
327
- layout= {Layout . style(~alignSelf= AlignStretch , ~height= (float_of_int(rows) *. size), () )}
328
- onMouseDown= {mouseDown}
329
- onMouseMove= {mouseMove}
330
- draw= {draw}
331
- />
400
+ <custom
401
+ invalidated
402
+ layout= {Layout . style(~alignSelf= AlignStretch , ~height= (float_of_int(rows) *. size), () )}
403
+ onMouseDown= {mouseDown}
404
+ onMouseMove= {mouseMove}
405
+ onRightMouseDown= {rightMouseDown}
406
+ draw= {draw}
407
+ />
332
408
</view >
333
409
] ,
334
410
()
335
411
)}
336
412
{switch (filtered-> Belt . List . get(selection)) {
337
413
| None => <view />
338
- | Some (emoji ) => <ShowEmoji emoji />
414
+ | Some (( emoji , _ ) ) => <ShowEmoji emoji />
339
415
}}
340
416
{switch (hasNewVersion) {
341
417
| None => <view />
0 commit comments