mirror of https://github.com/fantasticit/think.git
feat: use lru-cache in emoji-picker
This commit is contained in:
parent
3a284ad903
commit
762ac3a3f5
|
@ -1,12 +1,13 @@
|
|||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { Popover, Typography } from '@douyinfe/semi-ui';
|
||||
import { EXPRESSIONES, GESTURES, SYMBOLS, OBJECTS, ACTIVITIES, SKY_WEATHER } from './constants';
|
||||
import { setStorage, getStorage } from 'helpers/storage';
|
||||
import { createKeysLocalStorageLRUCache } from 'helpers/lru-cache';
|
||||
import { useToggle } from 'hooks/use-toggle';
|
||||
import styles from './index.module.scss';
|
||||
|
||||
const { Title } = Typography;
|
||||
const RECENT_USED_EMOJI_KEY = 'RECENT_USED_EMOJI_KEY';
|
||||
|
||||
const emojiLocalStorageLRUCache = createKeysLocalStorageLRUCache('EMOJI_PICKER', 20);
|
||||
|
||||
const LIST = [
|
||||
{
|
||||
|
@ -49,22 +50,17 @@ export const EmojiPicker: React.FC<IProps> = ({ onSelectEmoji, children }) => {
|
|||
|
||||
const selectEmoji = useCallback(
|
||||
(emoji) => {
|
||||
setStorage(RECENT_USED_EMOJI_KEY, [...recentUsed, emoji].join('-'));
|
||||
setRecentUsed((arr) => [...arr, emoji]);
|
||||
emojiLocalStorageLRUCache.put(emoji);
|
||||
setRecentUsed(emojiLocalStorageLRUCache.get() as string[]);
|
||||
onSelectEmoji && onSelectEmoji(emoji);
|
||||
},
|
||||
[onSelectEmoji, recentUsed]
|
||||
[onSelectEmoji]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!visible) return;
|
||||
try {
|
||||
const recentUsed = getStorage(RECENT_USED_EMOJI_KEY);
|
||||
const toArr = recentUsed.split('-');
|
||||
setRecentUsed(toArr);
|
||||
} catch (e) {
|
||||
//
|
||||
}
|
||||
emojiLocalStorageLRUCache.syncFromStorage();
|
||||
setRecentUsed(emojiLocalStorageLRUCache.get() as string[]);
|
||||
}, [visible]);
|
||||
|
||||
return (
|
||||
|
|
|
@ -5,3 +5,11 @@ export const safeJSONParse = (str, defaultValue = {}) => {
|
|||
return defaultValue;
|
||||
}
|
||||
};
|
||||
|
||||
export const safeJSONStringify = (obj, defaultValue = '{}') => {
|
||||
try {
|
||||
return JSON.stringify(obj);
|
||||
} catch (e) {
|
||||
return defaultValue;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -0,0 +1,149 @@
|
|||
import { safeJSONParse, safeJSONStringify } from './json';
|
||||
import { setStorage, getStorage } from './storage';
|
||||
|
||||
class Node {
|
||||
public key: string;
|
||||
public value: string | number;
|
||||
public prev: Node | null;
|
||||
public next: Node | null;
|
||||
|
||||
constructor(key, value) {
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
this.prev = null;
|
||||
this.next = null;
|
||||
}
|
||||
}
|
||||
|
||||
export class LRUCache {
|
||||
private capacity: number;
|
||||
private usedCapacity: number;
|
||||
private head: Node;
|
||||
private tail: Node;
|
||||
private store: Record<string, Node>;
|
||||
|
||||
constructor(capacity) {
|
||||
this.capacity = capacity || 20;
|
||||
this.usedCapacity = 0;
|
||||
this.store = {};
|
||||
this.head = new Node('fakeHeadNode', 'fakeHeadNode');
|
||||
this.tail = new Node('fakeTailNode', 'fakeTailNode');
|
||||
|
||||
this.head.next = this.tail;
|
||||
this.tail.prev = this.head;
|
||||
}
|
||||
|
||||
private removeNode(node) {
|
||||
node.prev.next = node.next;
|
||||
node.next.prev = node.prev;
|
||||
}
|
||||
|
||||
private addToHead(node) {
|
||||
node.prev = this.head;
|
||||
node.next = this.head.next;
|
||||
this.head.next.prev = node;
|
||||
this.head.next = node;
|
||||
}
|
||||
|
||||
private moveToHead(node) {
|
||||
this.removeNode(node);
|
||||
this.addToHead(node);
|
||||
}
|
||||
|
||||
private removeTail() {
|
||||
const node = this.tail.prev;
|
||||
this.removeNode(node);
|
||||
return node;
|
||||
}
|
||||
|
||||
get(key) {
|
||||
if (key in this.store) {
|
||||
const node = this.store[key];
|
||||
this.moveToHead(node);
|
||||
return node.value;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
put(key, value) {
|
||||
if (key in this.store) {
|
||||
const node = this.store[key];
|
||||
node.value = value;
|
||||
this.moveToHead(node);
|
||||
} else {
|
||||
const node = new Node(key, value);
|
||||
this.addToHead(node);
|
||||
this.store[key] = node;
|
||||
this.usedCapacity += 1;
|
||||
|
||||
if (this.usedCapacity > this.capacity) {
|
||||
const tailNode = this.removeTail();
|
||||
delete this.store[tailNode.key];
|
||||
this.usedCapacity -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
keys() {
|
||||
const res = [];
|
||||
let node = this.head;
|
||||
|
||||
while (node) {
|
||||
res.push(node.key);
|
||||
node = node.next;
|
||||
}
|
||||
|
||||
return res.slice(1, -1);
|
||||
}
|
||||
|
||||
values() {
|
||||
const res = [];
|
||||
let node = this.head;
|
||||
|
||||
while (node) {
|
||||
res.push(node.value);
|
||||
node = node.next;
|
||||
}
|
||||
|
||||
return res.slice(1, -1);
|
||||
}
|
||||
|
||||
toJSON() {
|
||||
return this.store;
|
||||
}
|
||||
}
|
||||
|
||||
const USED_STORAGE_KEYS = [];
|
||||
|
||||
export const createKeysLocalStorageLRUCache = (storageKey, capacity) => {
|
||||
const lruCache = new LRUCache(capacity);
|
||||
|
||||
if (USED_STORAGE_KEYS.includes(storageKey)) {
|
||||
throw new Error(`Storage Key ${storageKey} has been used!`);
|
||||
}
|
||||
|
||||
USED_STORAGE_KEYS.push(storageKey);
|
||||
|
||||
return {
|
||||
syncFromStorage() {
|
||||
const data = getStorage(storageKey) || [];
|
||||
data
|
||||
.slice()
|
||||
.reverse()
|
||||
.forEach((key) => {
|
||||
lruCache.put(key, key);
|
||||
});
|
||||
},
|
||||
syncToStorage() {
|
||||
setStorage(storageKey, safeJSONStringify(lruCache.keys()));
|
||||
},
|
||||
put(key) {
|
||||
lruCache.put(key, key);
|
||||
this.syncToStorage();
|
||||
},
|
||||
get(key?: string) {
|
||||
return key ? lruCache.get(key) : lruCache.keys();
|
||||
},
|
||||
};
|
||||
};
|
Loading…
Reference in New Issue