pl-fe: Actually do half of the url-purify lib code on pl-fe side lmao

Signed-off-by: mkljczk <git@mkljczk.pl>
This commit is contained in:
mkljczk
2025-03-09 13:13:45 +01:00
parent e7ca259762
commit 04ecc47822
4 changed files with 77 additions and 22 deletions

View File

@ -64,7 +64,7 @@ const UrlPrivacy = () => {
<FormGroup
labelText={<FormattedMessage id='url_privacy.rules_url.label' defaultMessage='URL cleaning rules database address' />}
hintText={<FormattedMessage id='url_privacy.rules_url.placeholder' defaultMessage='Rules database in ClearURLs-compatible format, eg. {url}' values={{ url: 'https://rules2.clearurls.xyz/data.minify.json' }} />}
hintText={<FormattedMessage id='url_privacy.rules_url.hint' defaultMessage='Rules database in ClearURLs-compatible format, eg. {url}' values={{ url: 'https://rules2.clearurls.xyz/data.minify.json' }} />}
>
<Input
type='text'
@ -76,7 +76,7 @@ const UrlPrivacy = () => {
<FormGroup
labelText={<FormattedMessage id='url_privacy.hash_url.label' defaultMessage='URL cleaning rules hash address (optional)' />}
hintText={<FormattedMessage id='url_privacy.hash_url.placeholder' defaultMessage='SHA256 hash of rules database, used to avoid unnecessary fetches, eg. {url}' values={{ url: 'https://rules2.clearurls.xyz/rules.minify.hash' }} />}
hintText={<FormattedMessage id='url_privacy.hash_url.hint' defaultMessage='SHA256 hash of rules database, used to avoid unnecessary fetches, eg. {url}' values={{ url: 'https://rules2.clearurls.xyz/rules.minify.hash' }} />}
>
<Input
type='text'

View File

@ -1641,10 +1641,12 @@
"url_privacy.allow_referral_marketing": "Make exception for referral marketing parameters",
"url_privacy.clear_links_in_compose": "Suggest removing tracking parameters when composing a post",
"url_privacy.clear_links_in_content": "Remove tracking parameters from displayed posts",
"url_privacy.hash_url.hint": "SHA256 hash of rules database, used to avoid unnecessary fetches, eg. {url}",
"url_privacy.hash_url.label": "URL cleaning rules hash address (optional)",
"url_privacy.hash_url.placeholder": "SHA256 hash of rules database, used to avoid unnecessary fetches, eg. {url}",
"url_privacy.hash_url.placeholder": "Hash URL",
"url_privacy.rules_url.hint": "Rules database in ClearURLs-compatible format, eg. {url}",
"url_privacy.rules_url.label": "URL cleaning rules database address",
"url_privacy.rules_url.placeholder": "Rules database in ClearURLs-compatible format, eg. {url}",
"url_privacy.rules_url.placeholder": "Rules URL",
"url_privacy.save": "Save",
"video.download": "Download file",
"video.exit_fullscreen": "Exit full screen",

View File

@ -3,10 +3,15 @@ import { create } from 'zustand';
import { mutative } from 'zustand-mutative';
import { settingsSchema, type Settings } from 'pl-fe/schemas/pl-fe/settings';
import { updateRulesFromUrl } from 'pl-fe/utils/url-purify';
import type { Emoji } from 'pl-fe/features/emoji';
import type { store } from 'pl-fe/store';
import type { APIEntity } from 'pl-fe/types/entities';
let lazyStore: typeof store;
import('pl-fe/store').then(({ store }) => 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<State>()(mutative((set) => ({
defaultSettings: v.parse(settingsSchema, {}),
@ -90,4 +104,3 @@ const useSettingsStore = create<State>()(mutative((set) => ({
}), { enableAutoFreeze: true }));
export { useSettingsStore };

View File

@ -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<KVStoreItem>(`url-purify-rules:${user}`, {
hashUrl,
rulesUrl,
hash,
rules: parsedRules,
fetchedAt: Date.now(),
});
};
const getRulesForUser = (user: Me) => {
if (!user || typeof user !== 'string') return;
KVStore.getItem<KVStoreItem>(`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 };