cheogram-www

created pr with cheogram-www/tauri-alpha.1 on 2026-10-29T17:17:18Z · by 3c31bc8c
commented on 2026-10-29T17:17:19Z · by 3c31bc8c
Changes are available at https://tangled.org/mrry.band/cheogram-www on branch tauri-alpha.
added cheogram-www/tauri-alpha.2 on 2026-11-04T17:46:08Z · by 3c31bc8c
1: 45e8ad7 = 1: 45e8ad7 Refactor service worker access
2: fab4022 ! 2: ce66e8f Move notifications code to one file
-: ------- > 3: 10e3574 Refactor notification code
3: d127ee0 ! 4: ba0891f npm run tauri init
4: 9e006bd = 5: 5b3abf9 Request microphone and camera usage on mac
5: 03d4934 = 6: 994a2e9 Tauri file system media store
6: b96216a = 7: 320fa8f Don't register or use serviceworker in tauri
7: faace0a = 8: e76db17 Open links in browser
8: 9c07308 ! 9: 761dd22 Tauri notifications
cmds
checkout latest patchset:
ssh pr.pico.sh pull cheogram-www:tauri-alpha | git am -3
checkout specific patchset revision:
ssh pr.pico.sh pull cheogram-www:tauri-alpha [rev] | git am -3
add changes to patch request:
git format-patch main --stdout | ssh pr.pico.sh cheogram-www:tauri-alpha
add comment to patch request:
ssh pr.pico.sh comment cheogram-www:tauri-alpha "lgtm!"

Patchset cheogram-www/tauri-alpha.1 on 2026-10-29T17:17:18Z · commit 45e8ad7

Refactor service worker access
Eric Roberts 2026-10-29T14:50:23Z
In the Tauri app we can't use the service worker because it needs to be
registered on https:// or localhost, and the tauri page runs at
tauri://localhost.

But in tauri `navigator.serviceWorker` is truthy still, and if you await
ready it will just hang forever.

So this refactors the way we access the service worker before actually
making the change to Tauri. Omitting from Tauri will then become a one
line change.

We've moved away from the automatic registration because the future
Tauri change will make `serviceWorker` from src/util.ts falsey, which
will then control the registration.
Semantic diff summary
0 added, 22 modified, 0 signature changed, 0 removed across 9 analyzed files
+11 -5 src/App.tsx #
......@@ -13,7 +13,14 @@ import {
1313 useNavigate,
1414 useParams,
1515 } from "react-router";
16-import { ios, webkit, href, useEffectEvent, showNotification } from "./util";
16+import {
17+ ios,
18+ webkit,
19+ href,
20+ useEffectEvent,
21+ showNotification,
22+ serviceWorker,
23+} from "./util";
1724 import { SettingsContext, SettingsData } from "./components/Settings";
1825 import { Call } from "./components/Call";
1926 import { LoginRequest } from "./components/LoginRequest";
......@@ -155,7 +162,7 @@ const AppInner = () => {
155162 }, [accounts]);
156163
157164 useEffect(() => {
158- if (!navigator.serviceWorker) return;
165+ if (!serviceWorker) return;
159166 const handler = (event: MessageEvent) => {
160167 const accountId = event.data?.data?.accountId;
161168 const chatId = event.data?.data?.chatId;
......@@ -187,10 +194,9 @@ const AppInner = () => {
187194 }
188195 }
189196 };
190- navigator.serviceWorker.addEventListener("message", handler);
197+ serviceWorker.addEventListener("message", handler);
191198
192- return () =>
193- navigator.serviceWorker.removeEventListener("message", handler);
199+ return () => serviceWorker.removeEventListener("message", handler);
194200 }, [accounts, navigate]);
195201
196202 return (
+4 -4 src/components/Accounts.tsx #
......@@ -33,7 +33,7 @@ import {
3333 } from "@fortawesome/free-solid-svg-icons";
3434
3535 import { SettingsContext } from "./Settings";
36-import { useChats } from "../util";
36+import { useChats, readyServiceWorker } from "../util";
3737
3838 export const Accounts = () => {
3939 const { accounts, setAccounts, persistence, setSettings } =
......@@ -144,9 +144,9 @@ export const Accounts = () => {
144144
145145 accounts[params.accountId].client.logout(completely);
146146
147- if (navigator.serviceWorker) {
148- const serviceWorkerRegistration = await navigator.serviceWorker.ready;
149- const sub = await serviceWorkerRegistration.pushManager.getSubscription();
147+ const serviceWorker = await readyServiceWorker;
148+ if (serviceWorker) {
149+ const sub = await serviceWorker.pushManager.getSubscription();
150150 if (!sub) return;
151151 await sub.unsubscribe(); // TODO: this isn't per account...
152152 }
+13 -18 src/components/Call.tsx #
......@@ -27,7 +27,7 @@ import {
2727 } from "@fortawesome/free-solid-svg-icons";
2828
2929 import { AvatarWithPlaceholder } from "./AvatarWithPlaceholder";
30-import { href, useEffectEvent } from "../util";
30+import { href, useEffectEvent, readyServiceWorker } from "../util";
3131
3232 const playTone = (frequency1: number, frequency2: number, duration: number) => {
3333 const audioContext = new AudioContext();
......@@ -129,10 +129,10 @@ const showCallNotification = async (
129129 ) => {
130130 if (!Notification?.permission) return null;
131131
132+ const serviceWorker = await readyServiceWorker;
132133 const tag = accountId + ">" + session.get_chatId() + ">call";
133- if (navigator.serviceWorker) {
134- const serviceWorkerRegistration = await navigator.serviceWorker.ready;
135- const notifs = await serviceWorkerRegistration.getNotifications({
134+ if (serviceWorker) {
135+ const notifs = await serviceWorker.getNotifications({
136136 tag: tag,
137137 });
138138 if (notifs.length > 0) return null; // Already notified, probably from push
......@@ -152,9 +152,8 @@ const showCallNotification = async (
152152 };
153153
154154 let notification = null;
155- if (navigator.serviceWorker) {
156- const serviceWorkerRegistration = await navigator.serviceWorker.ready;
157- await serviceWorkerRegistration.showNotification(title, options);
155+ if (serviceWorker) {
156+ await serviceWorker.showNotification(title, options);
158157 } else {
159158 notification = new Notification(title, options);
160159
......@@ -322,20 +321,16 @@ export const Call = ({
322321 })();
323322 });
324323
325- const callStatusCleanup = useEffectEvent(() => {
324+ const callStatusCleanup = useEffectEvent(async () => {
326325 playingCancelledRef.current = true;
327326 stopNowRef.current();
328-
329327 notificationRef.current?.close();
330- if (navigator.serviceWorker) {
331- (async () => {
332- const serviceWorkerRegistration = await navigator.serviceWorker.ready;
333- const notifs = await serviceWorkerRegistration.getNotifications({
334- tag: callAccountId + ">" + call?.get_chatId() + ">call",
335- });
336- notifs.forEach((notif) => notif.close());
337- })();
338- }
328+ const serviceWorker = await readyServiceWorker;
329+ if (!serviceWorker) return;
330+ const notifs = await serviceWorker.getNotifications({
331+ tag: callAccountId + ">" + call?.get_chatId() + ">call",
332+ });
333+ notifs.forEach((notif) => notif.close());
339334 });
340335
341336 useEffect(() => {
+7 -8 src/components/Chat.tsx #
......@@ -78,6 +78,7 @@ import {
7878 Attachment,
7979 ChatExtras,
8080 href,
81+ readyServiceWorker,
8182 } from "../util";
8283
8384 borogove.Config.relativeHashUri = true;
......@@ -967,14 +968,12 @@ export const ChatInner = () => {
967968 setTimeout(async () => {
968969 activeChat.markReadUpTo(mostRecentMessage);
969970 const tag = client.accountId() + ">" + activeChat.chatId;
970- if (navigator.serviceWorker) {
971- const serviceWorkerRegistration = await navigator.serviceWorker.ready;
972- if (serviceWorkerRegistration.getNotifications) {
973- const notifs = await serviceWorkerRegistration.getNotifications({
974- tag,
975- });
976- notifs.forEach((notif) => notif.close());
977- }
971+ const serviceWorker = await readyServiceWorker;
972+ if (serviceWorker?.getNotifications) {
973+ const notifs = await serviceWorker.getNotifications({
974+ tag,
975+ });
976+ notifs.forEach((notif) => notif.close());
978977 }
979978 }, 0);
980979 }, [activeChat, client]);
+4 -4 src/components/Login.tsx #
......@@ -1,4 +1,5 @@
11 import * as borogove from "borogove";
2+import { readyServiceWorker } from "../util";
23 import { Link, Navigate, useOutletContext } from "react-router";
34 import { Context } from "../App";
45 import { useEffect, useRef, useState } from "react";
......@@ -15,9 +16,8 @@ const registerPush = (
1516
1617 client.addStatusOnlineListener(() => {
1718 (async () => {
18- if (!navigator.serviceWorker || !Notification?.permission) return; // If no service worker support, don't bother with push
19-
20- const serviceWorkerRegistration = await navigator.serviceWorker.ready;
19+ const serviceWorker = await readyServiceWorker;
20+ if (!serviceWorker || !Notification?.permission) return;
2121 // public key can be computed from private key, but then we need code to do that, so we store both
2222 let key = (await idbp.get("vapid_key")) as {
2323 publicKey: ArrayBuffer;
......@@ -68,7 +68,7 @@ const registerPush = (
6868 await idbp.set("vapid_key", key);
6969 }
7070 client.subscribePush(
71- serviceWorkerRegistration,
71+ serviceWorker,
7272 import.meta.env.VITE_PUSH_SERVICE,
7373 keyPair,
7474 144,
+4 -3 src/components/LoginRequest.tsx #
......@@ -1,3 +1,4 @@
1+import { readyServiceWorker } from "../util";
12 import * as borogove from "borogove";
23 import { Paper } from "@mui/material";
34 import Draggable from "react-draggable";
......@@ -15,9 +16,9 @@ const showNotification = async (req: borogove.LoginRequest) => {
1516 tag: req.id,
1617 };
1718
18- if (navigator.serviceWorker) {
19- const serviceWorkerRegistration = await navigator.serviceWorker.ready;
20- await serviceWorkerRegistration.showNotification(title, options);
19+ const serviceWorker = await readyServiceWorker;
20+ if (serviceWorker) {
21+ await serviceWorker.showNotification(title, options);
2122 } else {
2223 new Notification(title, options);
2324 }
+6 -4 src/index.tsx #
......@@ -1,4 +1,5 @@
11 import { StrictMode } from "react";
2+import { readyServiceWorker, serviceWorker } from "./util";
23 import ReactDOM from "react-dom/client";
34 import { About } from "./components/About";
45 import { Account } from "./components/Account";
......@@ -92,14 +93,15 @@ root.render(
9293 </StrictMode>,
9394 );
9495
95-if (navigator.serviceWorker) {
96+if (serviceWorker) {
97+ if (import.meta.env.PROD) serviceWorker.register("/sw.js", { scope: "/" });
9698 (async () => {
97- const reg = await navigator.serviceWorker.ready;
98- if (!navigator.serviceWorker.controller) {
99+ const reg = await readyServiceWorker;
100+ if (!serviceWorker.controller) {
99101 // There is a service worker but it's not attached
100102 // Force attach
101103 // Needed at least in Firefox for hard-reload
102- reg.active?.postMessage("clientsClaim");
104+ reg?.active?.postMessage("clientsClaim");
103105 }
104106 })();
105107 }
+8 -6 src/util.ts #
......@@ -3,6 +3,9 @@ import Delta from "quill-delta";
33 import { href as rhref } from "react-router";
44 import { useRef, useCallback, useEffect, useSyncExternalStore } from "react";
55
6+export const serviceWorker = navigator.serviceWorker;
7+export const readyServiceWorker = serviceWorker?.ready ?? Promise.resolve(null);
8+
69 export const ios = navigator.userAgent.match(/(iPad|iPhone|iPod)/g);
710 export const webkit =
811 navigator.userAgent.match(/AppleWebKit/) &&
......@@ -88,11 +91,11 @@ export const showNotification = async (
8891 ) => {
8992 if (!Notification?.permission) return;
9093
94+ const serviceWorker = await readyServiceWorker;
9195 const tag = accountId + ">" + message.chatId();
92- if (navigator.serviceWorker) {
96+ if (serviceWorker) {
9397 // Check if we already notified this message
94- const serviceWorkerRegistration = await navigator.serviceWorker.ready;
95- const notifs = await serviceWorkerRegistration.getNotifications({
98+ const notifs = await serviceWorker.getNotifications({
9699 tag: tag,
97100 });
98101 for (const notif of notifs) {
......@@ -127,9 +130,8 @@ export const showNotification = async (
127130 timestamp: message.timeSent,
128131 },
129132 };
130- if (navigator.serviceWorker) {
131- const serviceWorkerRegistration = await navigator.serviceWorker.ready;
132- await serviceWorkerRegistration.showNotification(title, options);
133+ if (serviceWorker) {
134+ await serviceWorker.showNotification(title, options);
133135 } else {
134136 const notif = new Notification(title, options);
135137 notif.addEventListener("click", () => {
+1 -0 vite.config.js #
......@@ -32,6 +32,7 @@ export default defineConfig(({ mode }) => {
3232 svgr(),
3333 !isTest && eslint({ failOnError: false }),
3434 VitePWA({
35+ injectRegister: false,
3536 srcDir: "src",
3637 strategies: "injectManifest",
3738 manifestFilename: "manifest.json",
Back to top