diff --git a/src/index.ts b/src/index.ts index 882a219..4e82800 100644 --- a/src/index.ts +++ b/src/index.ts @@ -102,10 +102,6 @@ app.use("/", async (req: Request, res: Response)=>{ res.sendFile(path.join(__dirname, "webpage", "invite.html")); return; } - if(req.path.endsWith("service.js")){ - res.send("nope :3"); - return; - } const filePath = path.join(__dirname, "webpage", req.path); try{ await fs.access(filePath); diff --git a/src/webpage/localuser.ts b/src/webpage/localuser.ts index 5cbdf89..494e573 100644 --- a/src/webpage/localuser.ts +++ b/src/webpage/localuser.ts @@ -4,7 +4,7 @@ import{ Direct }from"./direct.js"; import{ AVoice }from"./audio.js"; import{ User }from"./user.js"; import{ Dialog }from"./dialog.js"; -import{ getapiurls, getBulkInfo, setTheme, Specialuser }from"./login.js"; +import{ getapiurls, getBulkInfo, setTheme, Specialuser, SW }from"./login.js"; import{channeljson,guildjson,mainuserjson,memberjson,memberlistupdatejson,messageCreateJson,presencejson,readyjson,startTypingjson,wsjson,}from"./jsontypes.js"; import{ Member }from"./member.js"; import{ Form, FormError, Options, Settings }from"./settings.js"; @@ -1274,6 +1274,21 @@ class Localuser{ } } } + { + const update=settings.addButton("Update settings") + const sw=update.addSelect("Service Worker setting",()=>{},["false","offlineOnly","true"],{ + defaultIndex:["false","offlineOnly","true"].indexOf(localStorage.getItem("SWMode") as string) + }); + sw.onchange=(e)=>{ + SW.setMode(["false","offlineOnly","true"][e] as "false"|"offlineOnly"|"true") + } + update.addButtonInput("","Check for update",()=>{ + SW.checkUpdate(); + }); + update.addButtonInput("","Clear cache",()=>{ + SW.forceClear(); + }); + } { const security = settings.addButton("Account Settings"); const genSecurity = ()=>{ diff --git a/src/webpage/login.ts b/src/webpage/login.ts index d8802cc..914d260 100644 --- a/src/webpage/login.ts +++ b/src/webpage/login.ts @@ -532,31 +532,54 @@ if(document.getElementById("form")){ } } //this currently does not work, and need to be implemented better at some time. -/* - if ("serviceWorker" in navigator){ +if(!localStorage.getItem("SWMode")){ + localStorage.setItem("SWMode","true"); +} +class SW{ + static worker:undefined|ServiceWorker; + static setMode(mode:"false"|"offlineOnly"|"true"){ + if(this.worker){ + this.worker.postMessage({data:mode,code:"setMode"}); + } + } + static checkUpdate(){ + if(this.worker){ + this.worker.postMessage({code:"CheckUpdate"}); + } + } + static forceClear(){ + if(this.worker){ + this.worker.postMessage({code:"ForceClear"}); + } + } +} +export {SW}; +if ("serviceWorker" in navigator){ navigator.serviceWorker.register("/service.js", { scope: "/", }).then((registration) => { - let serviceWorker:ServiceWorker; - if (registration.installing) { - serviceWorker = registration.installing; - console.log("installing"); - } else if (registration.waiting) { - serviceWorker = registration.waiting; - console.log("waiting"); - } else if (registration.active) { - serviceWorker = registration.active; - console.log("active"); - } - if (serviceWorker) { - console.log(serviceWorker.state); - serviceWorker.addEventListener("statechange", (e) => { - console.log(serviceWorker.state); - }); - } + let serviceWorker:ServiceWorker|undefined; + if (registration.installing) { + serviceWorker = registration.installing; + console.log("installing"); + } else if (registration.waiting) { + serviceWorker = registration.waiting; + console.log("waiting"); + } else if (registration.active) { + serviceWorker = registration.active; + console.log("active"); + } + SW.worker=serviceWorker; + SW.setMode(localStorage.getItem("SWMode") as "false"|"offlineOnly"|"true"); + if (serviceWorker) { + console.log(serviceWorker.state); + serviceWorker.addEventListener("statechange", (_) => { + console.log(serviceWorker.state); + }); + } }) - } - */ +} + const switchurl = document.getElementById("switch") as HTMLAreaElement; if(switchurl){ switchurl.href += window.location.search; diff --git a/src/webpage/service.ts b/src/webpage/service.ts index cf93f36..c1f494b 100644 --- a/src/webpage/service.ts +++ b/src/webpage/service.ts @@ -13,13 +13,13 @@ async function putInCache(request: URL | RequestInfo, response: Response){ console.error(error); } } -console.log("test"); let lastcache: string; self.addEventListener("activate", async ()=>{ - console.log("test2"); + console.log("Service Worker activated"); checkCache(); }); + async function checkCache(){ if(checkedrecently){ return; @@ -34,7 +34,7 @@ async function checkCache(){ console.log(text, lastcache); if(lastcache !== text){ deleteoldcache(); - putInCache("/getupdates", data.clone()); + putInCache("/getupdates", data); } checkedrecently = true; setTimeout((_: any)=>{ @@ -43,54 +43,97 @@ async function checkCache(){ }); } var checkedrecently = false; + function samedomain(url: string | URL){ return new URL(url).origin === self.origin; } -function isindexhtml(url: string | URL){ - console.log(url); - if(new URL(url).pathname.startsWith("/channels")){ - return true; + +const htmlFiles=new Set(["/index","/login","/home","/register","/oauth2/auth"]); + + +function isHtml(url:string):string|void{ + const path=new URL(url).pathname; + if(htmlFiles.has(path)||htmlFiles.has(path+".html")){ + return path+path.endsWith(".html")?"":".html"; } - return false; } -async function getfile(event: { -request: { url: URL | RequestInfo; clone: () => string | URL | Request }; -}){ - checkCache(); - if(!samedomain(event.request.url.toString())){ - return await fetch(event.request.clone()); +let enabled="false"; +let offline=false; + +function toPath(url:string):string{ + const Url= new URL(url); + let html=isHtml(url); + if(!html){ + const path=Url.pathname; + if(path.startsWith("/channels")){ + html="./index.html" + }else if(path.startsWith("/invite")){ + html="./invite.html" + } } - const responseFromCache = await caches.match(event.request.url); - console.log(responseFromCache, caches); + return html||Url.pathname; +} +let fails=0; +async function getfile(event: FetchEvent):Promise{ + checkCache(); + if(!samedomain(event.request.url)||enabled==="false"||(enabled==="offlineOnly"&&!offline)){ + const responce=await fetch(event.request.clone()); + if(samedomain(event.request.url)){ + if(enabled==="offlineOnly"&&responce.ok){ + putInCache(toPath(event.request.url),responce.clone()); + } + if(!responce.ok){ + fails++; + if(fails>5){ + offline=true; + } + } + } + return responce; + } + + let path=toPath(event.request.url); + + console.log("Getting path: "+path); + const responseFromCache = await caches.match(path); if(responseFromCache){ console.log("cache hit"); return responseFromCache; } - if(isindexhtml(event.request.url.toString())){ - console.log("is index.html"); - const responseFromCache = await caches.match("/index.html"); - if(responseFromCache){ - console.log("cache hit"); - return responseFromCache; - } - const responseFromNetwork = await fetch("/index.html"); - await putInCache("/index.html", responseFromNetwork.clone()); - return responseFromNetwork; - } - const responseFromNetwork = await fetch(event.request.clone()); - console.log(event.request.clone()); - await putInCache(event.request.clone(), responseFromNetwork.clone()); try{ + const responseFromNetwork = await fetch(path); + if(responseFromNetwork.ok){ + await putInCache(path, responseFromNetwork.clone()); + } return responseFromNetwork; }catch(e){ console.error(e); - return e; + return new Response(null); } } -self.addEventListener("fetch", (event: any)=>{ + + +self.addEventListener("fetch", (e)=>{ + const event=e as FetchEvent; try{ event.respondWith(getfile(event)); }catch(e){ console.error(e); } }); + +self.addEventListener("message", (message)=>{ + const data=message.data; + switch(data.code){ + case "setMode": + enabled=data.data; + break; + case "CheckUpdate": + checkedrecently=false; + checkCache(); + break; + case "ForceClear": + deleteoldcache(); + break; + } +}) diff --git a/tsconfig.json b/tsconfig.json index 0b4a564..5ec89b3 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -9,7 +9,8 @@ "incremental": true, "lib": [ "esnext", - "DOM" + "DOM", + "webworker" ], "module": "ESNext", "moduleResolution": "Bundler",