From 70b90b57383461ed4de22b98545fdbcdbedb027a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Cocchi?= Date: Sat, 27 May 2023 18:21:25 +0200 Subject: [PATCH] Frequently used in sync with packs & way to clear Frequently used --- web/src/frequently-used.js | 74 +++++++++++++++++++++++++++++++++----- web/src/index.js | 61 ++++++++++++++++++++----------- 2 files changed, 106 insertions(+), 29 deletions(-) diff --git a/web/src/frequently-used.js b/web/src/frequently-used.js index a754e9a..d35f6a9 100644 --- a/web/src/frequently-used.js +++ b/web/src/frequently-used.js @@ -13,22 +13,80 @@ // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -const FREQUENTLY_USED = JSON.parse(window.localStorage.mauFrequentlyUsedStickerIDs || "{}") + +const FREQUENTLY_USED_STORAGE_KEY = 'mauFrequentlyUsedStickerIDs' +const FREQUENTLY_USED_STORAGE_CACHE_KEY = 'mauFrequentlyUsedStickerCache' + +let FREQUENTLY_USED = JSON.parse(window.localStorage[FREQUENTLY_USED_STORAGE_KEY] ?? '{}') let FREQUENTLY_USED_SORTED = null -export const add = id => { - const [count] = FREQUENTLY_USED[id] || [0] - FREQUENTLY_USED[id] = [count + 1, Date.now()] - window.localStorage.mauFrequentlyUsedStickerIDs = JSON.stringify(FREQUENTLY_USED) +const sortFrequentlyUsedEntries = (entry1, entry2) => { + const [, [count1, date1]] = entry1 + const [, [count2, date2]] = entry2 + return count2 === count1 ? date2 - date1 : count2 - count1 +} + +export const setFrequentlyUsedStorage = (frequentlyUsed) => { + FREQUENTLY_USED = frequentlyUsed ?? {} + window.localStorage[FREQUENTLY_USED_STORAGE_KEY] = JSON.stringify(FREQUENTLY_USED) FREQUENTLY_USED_SORTED = null } +export const setFrequentlyUsedCacheStorage = (stickers) => { + const toPutInCache = stickers.map(sticker => [sticker.id, sticker]) + window.localStorage[FREQUENTLY_USED_STORAGE_CACHE_KEY] = JSON.stringify(toPutInCache) +} + +export const add = (id) => { + let FREQUENTLY_USED_COPY = { ...FREQUENTLY_USED } + const [count] = FREQUENTLY_USED_COPY[id] || [0] + FREQUENTLY_USED_COPY[id] = [count + 1, Date.now()] + setFrequentlyUsedStorage(FREQUENTLY_USED_COPY) +} + export const get = (limit = 16) => { if (FREQUENTLY_USED_SORTED === null) { - FREQUENTLY_USED_SORTED = Object.entries(FREQUENTLY_USED) - .sort(([, [count1, date1]], [, [count2, date2]]) => - count2 === count1 ? date2 - date1 : count2 - count1) + FREQUENTLY_USED_SORTED = Object.entries(FREQUENTLY_USED || {}) + .sort(sortFrequentlyUsedEntries) .map(([emoji]) => emoji) } return FREQUENTLY_USED_SORTED.slice(0, limit) } + +export const getFromCache = () => { + return Object.values(JSON.parse(localStorage[FREQUENTLY_USED_STORAGE_CACHE_KEY] ?? '[]')) +} + +export const remove = (id) => { + let FREQUENTLY_USED_COPY = { ...FREQUENTLY_USED } + if (FREQUENTLY_USED_COPY[id]) { + delete FREQUENTLY_USED_COPY[id] + setFrequentlyUsedStorage(FREQUENTLY_USED_COPY) + } +} + +export const removeMultiple = (ids) => { + let FREQUENTLY_USED_COPY = { ...FREQUENTLY_USED } + ids.forEach((id) => { + delete FREQUENTLY_USED_COPY[id] + }) + setFrequentlyUsedStorage(FREQUENTLY_USED_COPY) +} + +export const removeAll = setFrequentlyUsedStorage + +const compareStorageWith = (packs) => { + const stickersIDsFromPacks = packs.map((pack) => pack.stickers).flat().map((sticker) => sticker.id) + const stickersIDsFromFrequentlyUsedStorage = get() + + const notFound = stickersIDsFromFrequentlyUsedStorage.filter((id) => !stickersIDsFromPacks.includes(id)) + const found = stickersIDsFromFrequentlyUsedStorage.filter((id) => !notFound.includes(id)) + + return { found, notFound } +} + +export const removeNotFoundFromStorage = (packs) => { + const { found, notFound } = compareStorageWith(packs) + removeMultiple(notFound) + return found +} diff --git a/web/src/index.js b/web/src/index.js index 80fc9bf..f705d0e 100644 --- a/web/src/index.js +++ b/web/src/index.js @@ -47,6 +47,13 @@ const defaultState = { }, } +const makeFrequentlyUsedState = ({ stickerIDs, stickers } = {}) => ({ + id: "frequently-used", + title: "Frequently used", + stickerIDs: stickerIDs ?? [], + stickers: stickers ?? [], +}) + class App extends Component { constructor(props) { super(props) @@ -57,29 +64,26 @@ class App extends Component { error: null, stickersPerRow: parseInt(localStorage.mauStickersPerRow || "4"), theme: localStorage.mauStickerThemeOverride || this.defaultTheme, - frequentlyUsed: { - id: "frequently-used", - title: "Frequently used", - stickerIDs: frequent.get(), - stickers: [], - }, + frequentlyUsed: makeFrequentlyUsedState(), filtering: defaultState.filtering, } + if (!supportedThemes.includes(this.state.theme)) { this.state.theme = "light" } if (!supportedThemes.includes(this.defaultTheme)) { this.defaultTheme = "light" } - this.stickersByID = new Map(JSON.parse(localStorage.mauFrequentlyUsedStickerCache || "[]")) - this.state.frequentlyUsed.stickers = this._getStickersByID(this.state.frequentlyUsed.stickerIDs) + this.imageObserver = null this.packListRef = null this.navRef = null + this.searchStickers = this.searchStickers.bind(this) this.sendSticker = this.sendSticker.bind(this) this.navScroll = this.navScroll.bind(this) this.reloadPacks = this.reloadPacks.bind(this) + this.clearFrequentlyUsed = this.clearFrequentlyUsed.bind(this) this.observeSectionIntersections = this.observeSectionIntersections.bind(this) this.observeImageIntersections = this.observeImageIntersections.bind(this) } @@ -88,17 +92,26 @@ class App extends Component { return ids.map(id => this.stickersByID.get(id)).filter(sticker => !!sticker) } + _setFrequentlyUsed(stickerIDs = []) { + const stickers = this._getStickersByID(stickerIDs) + const frequentlyUsed = makeFrequentlyUsedState({ stickerIDs, stickers }) + this.setState({ frequentlyUsed }) + frequent.setFrequentlyUsedCacheStorage(stickers) + } + updateFrequentlyUsed() { const stickerIDs = frequent.get() - const stickers = this._getStickersByID(stickerIDs) - this.setState({ - frequentlyUsed: { - ...this.state.frequentlyUsed, - stickerIDs, - stickers, - }, - }) - localStorage.mauFrequentlyUsedStickerCache = JSON.stringify(stickers.map(sticker => [sticker.id, sticker])) + this._setFrequentlyUsed(stickerIDs) + } + + refreshFrequentlyUsed(packs) { + const stickerIDs = frequent.removeNotFoundFromStorage(packs) + this._setFrequentlyUsed(stickerIDs) + } + + clearFrequentlyUsed() { + frequent.removeAll() + this._setFrequentlyUsed() } searchStickers(e) { @@ -152,6 +165,10 @@ class App extends Component { this._loadPacks(true) } + _initializeStickersByID(ids) { + this.stickersByID = new Map(ids ?? []) + } + async populateStickersByID(allPacks) { const allStickers = allPacks.map(({ stickers }) => stickers).flat() allStickers.forEach((sticker) => { @@ -187,17 +204,18 @@ class App extends Component { loading: false, }) this.populateStickersByID(fetchedPacks) - this.updateFrequentlyUsed() + this.refreshFrequentlyUsed(fetchedPacks) return fetchedPacks }, error => this.setState({ loading: false, error })) } componentDidMount() { document.documentElement.style.setProperty("--stickers-per-row", this.state.stickersPerRow.toString()) + this._loadPacks() - this.imageObserver = new IntersectionObserver(this.observeImageIntersections, { - rootMargin: "100px", - }) + this._initializeStickersByID(frequent.getFromCache()) + + this.imageObserver = new IntersectionObserver(this.observeImageIntersections, { rootMargin: "100px" }) this.sectionObserver = new IntersectionObserver(this.observeSectionIntersections) } @@ -309,6 +327,7 @@ const Settings = ({ app }) => html`

Settings

+