diff --git a/packages/pl-fe/src/features/url-privacy/index.tsx b/packages/pl-fe/src/features/url-privacy/index.tsx index 7f7de8ddf..bebe61da0 100644 --- a/packages/pl-fe/src/features/url-privacy/index.tsx +++ b/packages/pl-fe/src/features/url-privacy/index.tsx @@ -64,7 +64,7 @@ const UrlPrivacy = () => { } - hintText={} + hintText={} > { } - hintText={} + hintText={} > lazyStore = store).catch(() => {}); + const settingsSchemaPartial = v.partial(settingsSchema); type State = { @@ -33,7 +38,16 @@ const changeSetting = (object: APIEntity, path: string[], value: any) => { return changeSetting(object[path[0]], path.slice(1), value); }; -const mergeSettings = (state: State) => state.settings = { ...state.defaultSettings, ...state.userSettings }; +const mergeSettings = (state: State) => { + const mergedSettings = { ...state.defaultSettings, ...state.userSettings }; + if (mergedSettings.urlPrivacy.rulesUrl && state.settings.urlPrivacy.rulesUrl !== mergedSettings.urlPrivacy.rulesUrl) { + const me = lazyStore?.getState().me; + if (me) { + updateRulesFromUrl(me, mergedSettings.urlPrivacy.rulesUrl, mergedSettings.urlPrivacy.hashUrl); + } + } + state.settings = mergedSettings; +}; const useSettingsStore = create()(mutative((set) => ({ defaultSettings: v.parse(settingsSchema, {}), @@ -90,4 +104,3 @@ const useSettingsStore = create()(mutative((set) => ({ }), { enableAutoFreeze: true })); export { useSettingsStore }; - diff --git a/packages/pl-fe/src/utils/url-purify.ts b/packages/pl-fe/src/utils/url-purify.ts index 185903562..3114ee839 100644 --- a/packages/pl-fe/src/utils/url-purify.ts +++ b/packages/pl-fe/src/utils/url-purify.ts @@ -15,6 +15,22 @@ import { URLPurify, type SerializedRules } from '@mkljczk/url-purify'; import KVStore from 'pl-fe/storage/kv-store'; +import { Me } from 'pl-fe/types/pl-fe'; + +interface KVStoreItem { + hashUrl: string; + rulesUrl: string; + hash: string; + rules: SerializedRules; + fetchedAt: number; +} + +const sha256 = async (message: string) => + Array.from(new Uint8Array( + await window.crypto.subtle.digest('SHA-256', (new TextEncoder()).encode(message)), + )) + .map((b) => b.toString(16).padStart(2, '0')) + .join(''); // Adapted from ClearURLs Rules // https://github.com/ClearURLs/Rules/blob/master/data.min.json @@ -90,24 +106,48 @@ const DEFAULT_RULESET: SerializedRules = { const Purify = new URLPurify({ rulesFromMemory: DEFAULT_RULESET, - // onFetchedRules: (hash, rules) => { - // const me = store.getState().auth.me; - - // KVStore.setItem('url-purify-rules:last', me || ''); - // KVStore.setItem(`url-purify-rules:${me}`, { - // hash, - // rules, - // fetchedAt: Date.now(), - // }); - // }, }); -KVStore.getItem('url-purify-rules:last', (url: string) => { - if (!url) return; - KVStore.getItem(`url-purify-rules:${url}`, (rules: any) => { +const updateRulesFromUrl = async (user: Me, rulesUrl: string, hashUrl: string, oldHash?: string) => { + if (oldHash) { + const newHash = await fetch(hashUrl).then((response) => response.text()); + if (newHash === oldHash) return; + } + + const rules = await fetch(rulesUrl).then((response) => response.text()); + + const parsedRules = JSON.parse(rules); + const hash = await sha256(rules); + Purify.setRules(parsedRules, hash); + + await KVStore.setItem('url-purify-rules:last', user); + return KVStore.setItem(`url-purify-rules:${user}`, { + hashUrl, + rulesUrl, + hash, + rules: parsedRules, + fetchedAt: Date.now(), + }); +}; + +const getRulesForUser = (user: Me) => { + if (!user || typeof user !== 'string') return; + KVStore.getItem(`url-purify-rules:${user}`, (rules) => { if (!rules) return; Purify.setRules(rules.rules, rules.hash); - }); -}); -export default Purify; + if (rules.fetchedAt + 1000 * 60 * 60 * 24 < Date.now()) { + updateRulesFromUrl(user, rules.rulesUrl, rules.hashUrl, rules.hash); + } + }); +}; + +const getRulesFromMemory = () => { + KVStore.getItem('url-purify-rules:last', (url: string) => { + getRulesForUser(url); + }); +}; + +getRulesFromMemory(); + +export { Purify as default, getRulesForUser, updateRulesFromUrl };