mirror of
https://github.com/MathMan05/Fermi.git
synced 2026-10-22 17:31:19 +00:00
storages
This commit is contained in:
1 parent
db64ccefbe
commit
81817dddd5
3 files changed
+140
-126
No files matched your search
+118
-108
@@ -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<string, presencejson> = 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);
|
||||
|
||||
+2
-1
@@ -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) {
|
||||
|
||||
+20
-17
@@ -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<string>();
|
||||
@@ -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<void>((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() {
|
||||
|
||||
Reference in new issue
Block a user