clone

a self-hosted multitenant nostr relay with sites, file storage, git hosting, and inbox and outbox delivery

match the installed app's launch colours to the theme and cache the shell offline

Dami committed addbbb8
commit
addbbb83bc9fad4c91782bf5641aeb8344b5428b
parent
0ace0e42789227359ff22b970516432fed6d70c1
author
Dami <dami.etoile@gmail.com>
12 files changed, +79 -13
Ainternal/webui/apple-touch-icon.pngbin
Minternal/webui/bridge.js7+++++--
Ainternal/webui/icon-192.pngbin
Ainternal/webui/icon-512.pngbin
Ainternal/webui/icon-maskable-512.pngbin
Ainternal/webui/icon-maskable.svg1+
Minternal/webui/manifest.webmanifest17+++++++++++++++--
Minternal/webui/page.html10++++++++--
Minternal/webui/style.css4++--
Minternal/webui/sw.js26+++++++++++++++++++++-----
Minternal/webui/templates.go12++++++++++++
Minternal/webui/webui.go15+++++++++++++++
A internal/webui/apple-touch-icon.png +0 -0

Binary file.

M internal/webui/bridge.js +5 -2
A internal/webui/icon-192.png +0 -0

Binary file.

A internal/webui/icon-512.png +0 -0

Binary file.

A internal/webui/icon-maskable-512.png +0 -0

Binary file.

A internal/webui/icon-maskable.svg +1 -0
M internal/webui/manifest.webmanifest +15 -2
M internal/webui/page.html +8 -2
M internal/webui/style.css +2 -2
@@ -24,13 +24,13 @@ img{max-width:100%}
2424 2525 /* Shell: a table with three columns; each column scrolls on its own. */2626 #topbar{display:none}27-#layout{border-collapse:collapse;table-layout:fixed;width:100%;height:calc(100vh - 28px)}27+#layout{border-collapse:collapse;table-layout:fixed;width:100%;height:calc(100vh - 28px - env(safe-area-inset-bottom,0px))}2828 #layout>tbody>tr>td{vertical-align:top;padding:0;position:relative;height:100%}2929 #railbox,#content,#panel{position:absolute;top:0;right:0;bottom:0;left:0;overflow:auto;overscroll-behavior:contain}3030 #rail{width:240px;background:var(--rail);border-right:1px solid var(--line)}3131 #aside{width:280px;border-left:1px solid var(--line)}3232 #content>section{padding:14px 22px 30px}33-#footer{position:fixed;left:0;right:0;bottom:0;height:28px;border-top:1px solid var(--line);background:var(--rail);font:13px var(--mono);padding:0 2ch 0 2.5ch;box-sizing:border-box}33+#footer{position:fixed;left:0;right:0;bottom:0;height:calc(28px + env(safe-area-inset-bottom,0px));border-top:1px solid var(--line);background:var(--rail);font:13px var(--mono);padding:0 calc(2ch + env(safe-area-inset-right,0px)) env(safe-area-inset-bottom,0px) calc(2.5ch + env(safe-area-inset-left,0px));box-sizing:border-box}3434 #footer table{width:100%;border-collapse:collapse;height:27px}#footer td{border:0;padding:0;vertical-align:middle}3535 #footer td:last-child{text-align:right}3636 #prompt{color:var(--muted)}#prompt b{color:var(--ink);font-weight:500}
M internal/webui/sw.js +21 -5
@@ -1,9 +1,25 @@
11 // Service worker: makes the relay installable and keeps navigation working2-// when the network fails by serving the last shell seen. Relay data is never3-// cached; every request goes to the network first.2+// when the network fails. Every request goes to the network first. Successful3+// page loads and the shell scripts are kept as the offline fallback; relay4+// data requested by scripts is never cached.5+const CACHE = "tiny-shell-v1";6+const shellAsset = url => /\/(fixi\.js|signer\.js|webmcp\.js|sw\.js|icon[^/]*\.(svg|png)|apple-touch-icon\.png|manifest\.webmanifest)$/.test(url.pathname);47 self.addEventListener("install", () => self.skipWaiting());5-self.addEventListener("activate", event => event.waitUntil(self.clients.claim()));8+self.addEventListener("activate", event => {9+ event.waitUntil(caches.keys().then(keys => Promise.all(keys.filter(key => key !== CACHE).map(key => caches.delete(key)))).then(() => self.clients.claim()));10+});611 self.addEventListener("fetch", event => {7- if (event.request.method !== "GET") return;8- event.respondWith(fetch(event.request).catch(() => caches.match(event.request)));12+ const request = event.request;13+ if (request.method !== "GET") return;14+ const url = new URL(request.url);15+ if (url.origin !== location.origin) return;16+ const keep = request.mode === "navigate" || shellAsset(url);17+ if (!keep) return;18+ event.respondWith(fetch(request).then(response => {19+ if (response.ok && response.type === "basic") {20+ const copy = response.clone();21+ caches.open(CACHE).then(cache => cache.put(request, copy)).catch(() => {});22+ }23+ return response;24+ }).catch(async () => (await caches.match(request)) || (request.mode === "navigate" ? caches.match(new Request(url.origin + url.pathname.replace(/\/[^/]*$/, "/"))) : undefined)));925 });
M internal/webui/templates.go +12 -0
M internal/webui/webui.go +15 -0
@@ -199,6 +199,12 @@ func (a *App) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
199199 _, _ = writer.Write(serviceWorkerJS)200200 return201201 }202+ if png, ok := map[string][]byte{"/icon-192.png": icon192PNG, "/icon-512.png": icon512PNG, "/icon-maskable-512.png": iconMaskablePNG, "/apple-touch-icon.png": appleTouchIconPNG}[request.URL.Path]; ok {203+ writer.Header().Set("content-type", "image/png")204+ writer.Header().Set("cache-control", "public, max-age=86400")205+ _, _ = writer.Write(png)206+ return207+ }202208 if request.URL.Path == "/icon.svg" || request.URL.Path == "/icon-mono.svg" {203209 writer.Header().Set("content-type", "image/svg+xml; charset=utf-8")204210 if request.URL.Path == "/icon.svg" {
@@ -730,6 +736,15 @@ func (a *App) manifest(writer http.ResponseWriter, request *http.Request) {
730736 encoded, _ := json.Marshal(name)731737 body := strings.ReplaceAll(manifestTemplate, `"NAME"`, string(encoded))732738 body = strings.ReplaceAll(body, "BASE/", requestPrefix(request)+"/")739+ // A manifest carries one set of launch colours. The page links the740+ // variant that matches the active theme so the installed app's splash741+ // screen and status bar do not flash light on a dark phone.742+ theme, background := "#f4f2ec", "#fbfaf7"743+ if request.URL.Query().Get("theme") == "dark" {744+ theme, background = "#1e1d1a", "#171614"745+ }746+ body = strings.ReplaceAll(body, "THEME", theme)747+ body = strings.ReplaceAll(body, "BACKGROUND", background)733748 writer.Header().Set("content-type", "application/manifest+json; charset=utf-8")734749 _, _ = writer.Write([]byte(body))735750 }