diff options
| author | Adam Soltys <[email protected]> | 2026-09-12 09:56:33 -0700 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2026-09-12 09:56:33 -0700 |
| commit | cab447766734cb99821c36988ebc8d27a0bce228 (patch) | |
| tree | 821b3670984b075261662463d99a721090de3847 /src/features/messages.js | |
| parent | 1ca77a5999bc546f92ec7189acfba1dc8088a2a7 (diff) | |
| download | coinosv3-cab447766734cb99821c36988ebc8d27a0bce228.tar.gz coinosv3-cab447766734cb99821c36988ebc8d27a0bce228.zip | |
Tell a phone with no push service what's actually wrong.
"Registration failed - push service error" is Chrome's own wording, and it
tells nobody anything. The cause is that Chromium routes web push through
Firebase: on GrapheneOS — or any Android without Google Play Services —
there is no push service to register with, so the app cannot be woken while
it's closed, and no amount of tapping the toggle will change that.
Both places that ask for push (the chat's offer and the NWC "answer while
closed" toggle) now recognise that specific failure and say: this browser has
no push service; on Android that's usually a phone without Play Services;
Firefox brings its own and works there; and zaps are still answered whenever
coinos is open. The last part matters most — the feature isn't broken, it's
narrower than they thought.
The failure is remembered so the app stops retrying it on every launch, and
an explicit tap on the toggle always clears that and tries again, because a
phone that changed browsers deserves another go.
tools/push-unavailable-test.js runs a browser whose subscribe() fails exactly
as Chrome's does and reads back what the user is told.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_019mrt228Upn9nNCwGixzSLx
Diffstat (limited to 'src/features/messages.js')
| -rw-r--r-- | src/features/messages.js | 17 |
1 files changed, 16 insertions, 1 deletions
diff --git a/src/features/messages.js b/src/features/messages.js index e1f3ea3..b12efa7 100644 --- a/src/features/messages.js +++ b/src/features/messages.js @@ -1278,6 +1278,10 @@ export function messagesFeature(ctx) { const { publicKey } = await (await fetch(`${NOTIFIER}/vapid`)).json(); const wantKey = b64ToBytes(publicKey); let sub = await reg.pushManager.getSubscription(); + // see nwc.js: a Chromium without Google Play Services (GrapheneOS, and + // any de-Googled Android) has no push service at all, and says so in a + // way nobody can act on. Remember it so the offer can explain itself. + const noPushService = (e) => /push service|Registration failed|AbortError|NotSupportedError/i.test((e && (e.message || e.name)) || ''); // A subscription made against a DIFFERENT VAPID key can never receive a // push — the notifier's sends fail with a permanent 403 and it keeps // reusing the dead sub, so the device goes silent forever (this is what @@ -1291,8 +1295,14 @@ export function messagesFeature(ctx) { }; if (sub && !keyMatches(sub)) { try { await sub.unsubscribe(); } catch {} sub = null; } if (!sub) { - sub = await reg.pushManager.subscribe({ userVisibleOnly: true, applicationServerKey: wantKey }); + try { + sub = await reg.pushManager.subscribe({ userVisibleOnly: true, applicationServerKey: wantKey }); + } catch (e) { + if (noPushService(e)) { const s2 = st(); s2.noPushService = true; save(s2); render(); } + return false; + } } + { const s2 = st(); if (s2.noPushService) { delete s2.noPushService; save(s2); } } const r = await fetch(`${NOTIFIER}/register`, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ subscription: sub.toJSON(), notify: pushWatch() }), @@ -3969,6 +3979,11 @@ export function messagesFeature(ctx) { class: 'btn-ghost btn-sm', onClick: () => { const s = st(); s.pushDismissed = true; save(s); render(); }, }, t('msgDismiss'))))); + // A browser with no push service at all: the offer above is beside the + // point (permission may even be granted), so say what's actually wrong + // wherever the user got to. + if (st().noPushService && !st().push) + kids.push(h('div', { class: 'notice info small' }, t('nwcNoPushService'))); // ---- the feed, above the conversations: it's the thing you read, they're // the things you answer |
