From 81817dddd5f59ec0010d5d67d0a3d94a971f8913 Mon Sep 17 00:00:00 2001 From: "[it/its]@Rory&" <[it/its]@Rory&> Date: Thu, 1 Jan 2026 15:43:25 +0100 Subject: [PATCH] storages --- src/webpage/localuser.ts | 226 +++++++++++++++++++------------------ src/webpage/user.ts | 3 +- src/webpage/utils/utils.ts | 37 +++--- 3 files changed, 140 insertions(+), 126 deletions(-) diff --git a/src/webpage/localuser.ts b/src/webpage/localuser.ts index 02ecfc9..9a73c2b 100644 --- a/src/webpage/localuser.ts +++ b/src/webpage/localuser.ts @@ -36,6 +36,14 @@ import {Sticker} from "./sticker.js"; import {Hover} from "./hover.js"; import {AccountSwitcher} from "./utils/switcher.js"; import {Favorites} from "./favorites.js"; +import { + AnimateTristateValues, + getPreferences, + setPreferences, + ThemeOption, +} from "./utils/storage/userPreferences"; +import {getDeveloperSettings, setDeveloperSettings} from "./utils/storage/devSettings"; +import {getLocalSettings, ServiceWorkerModeValues} from "./utils/storage/localSettings.js"; type traceObj = { micros: number; calls?: (string | traceObj)[]; @@ -289,25 +297,26 @@ class Localuser { } async queryBlog() { this.perminfo.localuser ??= {}; - const bstate = localStorage.getItem("blogUpdates") as "Yes" | "No" | "Wait" | null; - if (!bstate) { - localStorage.setItem("blogUpdates", "Wait"); - } else if (bstate === "Wait") { + const prefs = await getPreferences(); + const bstate = prefs.showBlogUpdates; + if (bstate === undefined) { const pop = new Dialog(""); pop.options.addText(I18n.blog.wantUpdates()); const opts = pop.options.addOptions("", {ltr: true}); - opts.addButtonInput("", I18n.yes(), () => { - localStorage.setItem("blogUpdates", "Yes"); + opts.addButtonInput("", I18n.yes(), async () => { + prefs.showBlogUpdates = true; + await setPreferences(prefs); this.queryBlog(); pop.hide(); }); - opts.addButtonInput("", I18n.no(), () => { - localStorage.setItem("blogUpdates", "No"); + opts.addButtonInput("", I18n.no(), async () => { + prefs.showBlogUpdates = false; + await setPreferences(prefs); this.queryBlog(); pop.hide(); }); pop.show(); - } else if (bstate === "Yes") { + } else if (bstate) { const post = (await this.getPosts()).items[0]; if (this.perminfo.localuser.mostRecent !== post.url) { this.perminfo.localuser.mostRecent = post.url; @@ -441,7 +450,7 @@ class Localuser { if (!this.resume_gateway_url || !this.session_id) { resume = false; } - const doComp = DecompressionStream && !localStorage.getItem("gateWayComp"); + const doComp = DecompressionStream && !getDeveloperSettings().gatewayCompression; const ws = new WebSocket( (resume ? this.resume_gateway_url : this.serverurls.gateway.toString()) + "?encoding=json&v=9" + @@ -674,7 +683,7 @@ class Localuser { } async handleEvent(temp: wsjson) { if (temp.d._trace) this.handleTrace(temp.d._trace); - if (localStorage.getItem("logGateway")) console.debug(temp); + if (getDeveloperSettings().gatewayLogging) console.debug(temp); if (temp.s) this.lastSequence = temp.s; if (temp.op === 9 && this.ws) { this.errorBackoff = 0; @@ -2450,31 +2459,34 @@ class Localuser { } { - let userinfos = getBulkInfo(); + const prefs = await getPreferences(); tas.addColorInput( I18n.localuser.accentColor(), - (_) => { - userinfos = getBulkInfo(); - userinfos.accent_color = _; - localStorage.setItem("userinfos", JSON.stringify(userinfos)); - document.documentElement.style.setProperty("--accent-color", userinfos.accent_color); + async (_) => { + prefs.accentColor = _; + await setPreferences(prefs); + + document.documentElement.style.setProperty("--accent-color", prefs.accentColor); }, - {initColor: userinfos.accent_color}, + {initColor: prefs.accentColor}, ); } { + const prefs = await getPreferences(); const options = [[null, I18n.noEmojiFont()], ...Localuser.fonts] as const; - const cur = localStorage.getItem("emoji-font"); + const cur = prefs.emojiFont; let index = options.findIndex((_) => _[1] == cur); if (index === -1) index = 0; tas.addSelect( I18n.emojiSelect(), - (index) => { + async (index) => { if (options[index][0]) { - localStorage.setItem("emoji-font", options[index][1]); + prefs.emojiFont = options[index][1]; } else { - localStorage.removeItem("emoji-font"); + prefs.emojiFont = undefined; } + + await setPreferences(prefs); Localuser.loadFont(); }, options.map((font) => font[1]), @@ -2483,25 +2495,34 @@ class Localuser { }, ); } + { + const cur = prefs.renderJoinAvatars; + tas.addCheckboxInput( + I18n.renderJoinAvatars(), + async (v) => { + prefs.renderJoinAvatars = v; + await setPreferences(prefs); + }, + {initState: cur}, + ); + } } { const update = settings.addButton(I18n.localuser.updateSettings()); - let index = ["false", "offlineOnly", "true"].indexOf( - localStorage.getItem("SWMode") as string, - ); + let index = ServiceWorkerModeValues.indexOf(localSettings.serviceWorkerMode); if (index === -1) { index = 2; } const sw = update.addSelect( - I18n.localuser.swSettings(), + I18n.settings.updates.serviceWorkerMode.title(), () => {}, - (["SWOff", "SWOffline", "SWOn", "SWUnregistered"] as const).map((e) => I18n.localuser[e]()), + ServiceWorkerModeValues.map((e) => I18n.settings.updates.serviceWorkerMode[e]()), { defaultIndex: index, }, ); sw.onchange = (e) => { - SW.setMode((["false", "offlineOnly", "true", "unregistered"] as const)[e]); + SW.setMode(ServiceWorkerModeValues[e]); }; update.addButtonInput("", I18n.localuser.CheckUpdate(), async () => { const update = await SW.checkUpdates(); @@ -2774,16 +2795,12 @@ class Localuser { this.updateTranslations(); }, [...langmap.values()], - { - defaultIndex: I18n.options().indexOf(I18n.lang), - }, + {defaultIndex: I18n.options().indexOf(I18n.lang)}, ); { security.addButtonInput("", I18n.logout.logout(), async () => { - if (await this.userinfo.logout()) { - window.location.href = "/"; - } + if (await this.userinfo.logout()) window.location.href = "/"; }); } }; @@ -2797,9 +2814,7 @@ class Localuser { console.log(t); this.perminfo.user.disableColors = !t; }, - { - initState: !this.perminfo.user.disableColors, - }, + {initState: !this.perminfo.user.disableColors}, ); accessibility.addCheckboxInput( I18n.channel.allowIcons(), @@ -2807,36 +2822,25 @@ class Localuser { console.log(t); this.perminfo.user.disableIcons = !t; }, - { - initState: !this.perminfo.user.disableIcons, - }, + {initState: !this.perminfo.user.disableIcons}, ); - const gifSettings = ["hover", "always", "never"] as const; accessibility.addSelect( I18n.accessibility.playGif(), - (i) => { - localStorage.setItem("gifSetting", gifSettings[i]); - }, - gifSettings.map((_) => I18n.accessibility.gifSettings[_]()), - { - defaultIndex: - ((gifSettings as readonly string[]).indexOf( - localStorage.getItem("gifSetting") as string, - ) + 1 || 1) - 1, + async (i) => { + prefs.animateGifs = AnimateTristateValues[i]; + await setPreferences(prefs); }, + AnimateTristateValues.map((_) => I18n.accessibility.gifSettings[_]()), + {defaultIndex: AnimateTristateValues.indexOf(prefs.animateGifs)}, ); accessibility.addSelect( I18n.accessibility.playIcon(), - (i) => { - localStorage.setItem("iconSetting", gifSettings[i]); - }, - gifSettings.map((_) => I18n.accessibility.gifSettings[_]()), - { - defaultIndex: - ((gifSettings as readonly string[]).indexOf( - localStorage.getItem("iconSetting") as string, - ) + 1 || 1) - 1, + async (i) => { + prefs.animateIcons = AnimateTristateValues[i]; + await setPreferences(prefs); }, + AnimateTristateValues.map((_) => I18n.accessibility.gifSettings[_]()), + {defaultIndex: AnimateTristateValues.indexOf(prefs.animateIcons)}, ); } { @@ -2909,9 +2913,7 @@ class Localuser { I18n.localuser["team:"](), "team_id", ["Personal", ...teams.map((team: {name: string}) => team.name)], - { - defaultIndex: 0, - }, + {defaultIndex: 0}, ); }); @@ -3260,14 +3262,11 @@ class Localuser { const blog = settings.addButton(I18n.blog.blog()); blog.addCheckboxInput( I18n.blog.blogUpdates(), - (check) => { - if (check) { - localStorage.setItem("blogUpdates", "Yes"); - } else { - localStorage.setItem("blogUpdates", "No"); - } + async (check) => { + prefs.showBlogUpdates = check; + await setPreferences(prefs); }, - {initState: localStorage.getItem("blogUpdates") === "Yes"}, + {initState: prefs.showBlogUpdates}, ); (async () => { const posts = await this.getPosts(); @@ -3297,74 +3296,82 @@ class Localuser { devSettings.addText(I18n.devSettings.description()); devSettings.addHR(); const box1 = devSettings.addCheckboxInput(I18n.devSettings.logGateway(), () => {}, { - initState: Boolean(localStorage.getItem("logGateway")), + initState: getDeveloperSettings().gatewayLogging, }); box1.onchange = (e) => { - if (e) { - localStorage.setItem("logGateway", "true"); - } else { - localStorage.removeItem("logGateway"); - } + const settings = getDeveloperSettings(); + settings.gatewayLogging = e; + setDeveloperSettings(settings); }; + const box2 = devSettings.addCheckboxInput(I18n.devSettings.badUser(), () => {}, { - initState: Boolean(localStorage.getItem("logbad")), + initState: getDeveloperSettings().logBannedFields, }); box2.onchange = (e) => { - if (e) { - localStorage.setItem("logbad", "true"); - } else { - localStorage.removeItem("logbad"); - } + const settings = getDeveloperSettings(); + settings.logBannedFields = e; + setDeveloperSettings(settings); }; const box3 = devSettings.addCheckboxInput(I18n.devSettings.traces(), () => {}, { - initState: Boolean(localStorage.getItem("traces")), + initState: getDeveloperSettings().showTraces, }); box3.onchange = (e) => { - if (e) { - localStorage.setItem("traces", "true"); - } else { - localStorage.removeItem("traces"); - } + const settings = getDeveloperSettings(); + settings.showTraces = e; + setDeveloperSettings(settings); }; + const box4 = devSettings.addCheckboxInput(I18n.devSettings.cache(), () => {}, { - initState: !!localStorage.getItem("isDev"), + initState: getDeveloperSettings().cacheSourceMaps, }); box4.onchange = (e) => { - if (e) { - localStorage.setItem("isDev", "true"); - } else { - localStorage.removeItem("isDev"); - } + const settings = getDeveloperSettings(); + settings.cacheSourceMaps = e; + setDeveloperSettings(settings); SW.postMessage({code: "isDev", dev: e}); }; devSettings.addText(I18n.devSettings.cacheDesc()); const box5 = devSettings.addCheckboxInput(I18n.devSettings.captureTrace(), () => {}, { - initState: !!localStorage.getItem("capTrace"), + initState: getDeveloperSettings().interceptApiTraces, }); box5.onchange = (e) => { - if (e) { - localStorage.setItem("capTrace", "true"); - } else { - localStorage.removeItem("capTrace"); - } + const settings = getDeveloperSettings(); + settings.interceptApiTraces = e; + setDeveloperSettings(settings); SW.traceInit(); }; const box6 = devSettings.addCheckboxInput(I18n.devSettings.gatewayComp(), () => {}, { - initState: !!localStorage.getItem("gateWayComp"), + initState: getDeveloperSettings().gatewayCompression, }); box6.onchange = (e) => { - if (e) { - localStorage.setItem("gateWayComp", "true"); - } else { - localStorage.removeItem("gateWayComp"); - } + const settings = getDeveloperSettings(); + settings.gatewayCompression = e; + setDeveloperSettings(settings); SW.traceInit(); }; + + devSettings.addButtonInput("", I18n.devSettings.clearWellKnowns(), async () => { + const currentUserInfos = JSON.parse(localStorage.getItem("userinfos")!); + for (const user of Object.keys(currentUserInfos.users)) { + const key = + currentUserInfos.users[user].serverurls.value ?? + currentUserInfos.users[user].serverurls.wellknown ?? + currentUserInfos.users[user].serverurls.api; + currentUserInfos.users[user].serverurls = await getapiurls(key); + console.log(key, currentUserInfos.users[user].serverurls); + localStorage.setItem("userinfos", JSON.stringify(currentUserInfos)); + } + localStorage.removeItem("instanceinfo"); + await SW.postMessage("clearCdnCache"); + + // @ts-ignore - chromium is smelly for not supporting the `forceGet` option (aka skip cache) + window.location.reload(true); + }); } - if (this.trace.length && localStorage.getItem("traces")) { + if (this.trace.length && getDeveloperSettings().showTraces) { const traces = settings.addButton(I18n.localuser.trace(), { noSubmit: true, }); @@ -4362,17 +4369,20 @@ class Localuser { readonly presences: Map = new Map(); static font?: FontFace; static async loadFont() { - const fontName = localStorage.getItem("emoji-font"); + const prefs = await getPreferences(); + const fontName = prefs.emojiFont; + if (this.font) { //TODO see when/if this can be removed //@ts-ignore this is stupid. it's been here since 2020 document.fonts.delete(this.font); } + const realname = this.fonts.find((_) => _[1] === fontName)?.[0]; if (realname) { const font = new FontFace("emojiFont", `url("/emoji/${realname}")`); await font.load(); - console.error("fun"); + console.error("Loaded font:", fontName, "/", realname); //TODO see when/if this can be removed //@ts-ignore this is stupid. it's been here since 2020 document.fonts.add(font); diff --git a/src/webpage/user.ts b/src/webpage/user.ts index 367c6a0..8a9d476 100644 --- a/src/webpage/user.ts +++ b/src/webpage/user.ts @@ -14,6 +14,7 @@ import {createImg, removeAni, safeImg} from "./utils/utils.js"; import {Direct} from "./direct.js"; import {Permissions} from "./permissions.js"; import {Channel} from "./channel.js"; +import {getDeveloperSettings} from "./utils/storage/devSettings"; class User extends SnowFlake { owner: Localuser; hypotheticalpfp!: boolean; @@ -45,7 +46,7 @@ class User extends SnowFlake { constructor(userjson: userjson, owner: Localuser, dontclone: boolean = false) { super(userjson.id); this.owner = owner; - if (localStorage.getItem("logbad") && owner.user && owner.user.id !== userjson.id) { + if (getDeveloperSettings().logBannedFields && owner.user && owner.user.id !== userjson.id) { this.checkfortmi(userjson); } if (!owner) { diff --git a/src/webpage/utils/utils.ts b/src/webpage/utils/utils.ts index 243c995..70a1800 100644 --- a/src/webpage/utils/utils.ts +++ b/src/webpage/utils/utils.ts @@ -4,6 +4,9 @@ import {Dialog} from "../settings.js"; import {fix} from "./cssMagic.js"; import {messageFrom, messageTo} from "./serviceType.js"; import {isLoopback, trimTrailingSlashes} from "./netUtils"; +import {getLocalSettings, ServiceWorkerMode, setLocalSettings} from "./storage/localSettings"; +import {getPreferences} from "./storage/userPreferences"; +import {getDeveloperSettings} from "./storage/devSettings"; fix(); const apiDoms = new Set(); @@ -38,7 +41,7 @@ export function getBulkUsers() { const user = (json.users[thing] = new Specialuser(json.users[thing])); apiDoms.add(new URL(user.serverurls.api).host); } - if (localStorage.getItem("capTrace")) { + if (getDeveloperSettings().interceptApiTraces) { SW.postMessage({ code: "apiUrls", hosts: [...apiDoms], @@ -236,10 +239,6 @@ export class Specialuser { localStorage.setItem("userinfos", JSON.stringify(info)); } } -//this currently does not work, and need to be implemented better at some time. -if (!localStorage.getItem("SWMode")) { - localStorage.setItem("SWMode", "SWOn"); -} export function trimswitcher() { const json = getBulkInfo(); const map = new Map(); @@ -857,11 +856,10 @@ export {checkInstance}; export class SW { static worker: undefined | ServiceWorker; + static registration: ServiceWorkerRegistration; static port?: MessagePort; static init() { - SW.setMode( - (localStorage.getItem("SWMode") as "false" | "offlineOnly" | "true" | undefined) || "true", - ); + SW.setMode(getLocalSettings().serviceWorkerMode); const port = new MessageChannel(); SW.worker?.postMessage( { @@ -879,7 +877,7 @@ export class SW { port.port1.close(); }); this.postMessage({code: "ping"}); - this.postMessage({code: "isDev", dev: !!localStorage.getItem("isDev")}); + this.postMessage({code: "isDev", dev: getDeveloperSettings().cacheSourceMaps}); this.captureEvent("updates", (update, stop) => { this.needsUpdate ||= update.updates; if (update) { @@ -984,7 +982,7 @@ export class SW { if (!("serviceWorker" in navigator)) return; // If it's registered, it handles CDN caching regardless of settings. - if(localStorage.getItem("SWMode") === "unregistered") return; + if (getLocalSettings().serviceWorkerMode == ServiceWorkerMode.Unregistered) return; return new Promise((res) => { navigator.serviceWorker .register("/service.js", { @@ -994,32 +992,37 @@ export class SW { let serviceWorker: ServiceWorker | undefined; if (registration.installing) { serviceWorker = registration.installing; - console.log("installing"); + console.log("Service worker: installing"); } else if (registration.waiting) { serviceWorker = registration.waiting; - console.log("waiting"); + console.log("Service worker: waiting"); } else if (registration.active) { serviceWorker = registration.active; - console.log("active"); + console.log("Service worker: active"); } SW.worker = serviceWorker; + SW.registration = registration; SW.init(); if (serviceWorker) { - console.log(serviceWorker.state); + console.log("Service worker state changed:", serviceWorker.state); serviceWorker.addEventListener("statechange", (_) => { - console.log(serviceWorker.state); + console.log("Service worker state changed:", serviceWorker.state); }); res(); } }); }); } - static setMode(mode: "false" | "offlineOnly" | "true" | "unregistered") { - localStorage.setItem("SWMode", mode); + static setMode(mode: ServiceWorkerMode) { + const localSettings = getLocalSettings(); + localSettings.serviceWorkerMode = mode; + setLocalSettings(localSettings); if (this.worker) { this.worker.postMessage({data: mode, code: "setMode"}); } + + if (mode === ServiceWorkerMode.Unregistered) this.registration.unregister().then(r => console.log("Service worker unregistered:", r)); } static forceClear() {