Task #601: iOS PWA push notifications enablement
PWA + Web Push infra was already fully built (SW, VAPID auto-gen,
subscribe/unsubscribe API, sendPushToUser called from orders/meetings/
notes/replies, iOS-PWA detection hook). The user wasn't getting
lock-screen alerts because iPhone/iPad weren't installed as a PWA —
iOS only delivers Web Push from a Home Screen icon, not a Safari tab.
This commit polishes the iOS install path so the gap is obvious:
1. push-enable-prompt.tsx — Added a dedicated iOS install-steps card
variant that renders on iPhone/iPad Safari when running outside
standalone mode. Shows Share → Add to Home Screen → open from icon.
Independent 14-day dismiss memory from the regular Enable card.
2. notification-settings.tsx — PushToggleRow now detects iOS-non-
standalone and shows the unsupported_ios_safari hint inline with a
disabled toggle, instead of teasing an Enable affordance that always
fails.
3. use-push-subscription.ts — Exported isIosSafariNonStandalone() so
both surfaces share the detection logic (was private).
4. lib/push.ts (server) — Added info logs in loadVapid() for both the
env-key and disk-key paths, removed an accidental duplicate disk-read
block introduced during editing. Now the production redeploy check
is a one-liner: grep for "VAPID keys loaded" in the api logs.
5. ar.json / en.json — Added notifSettings.push.iosInstall.{title,desc,
step1,step2,step3} bilingual strings for the new card.
No DB migrations. No deps changed. tx-os tsc passes; api-server tsc
errors are pre-existing (routes/push.ts handler type, font_settings)
and not touched by this change.
Smoke test on real iPhone/iPad still owed (out-of-band — task step #4
is a manual verification on the user's devices after redeploy).
This commit is contained in:
@@ -50,6 +50,10 @@ async function loadVapid(): Promise<{
|
||||
if (envPub && envPriv) {
|
||||
try {
|
||||
webpush.setVapidDetails(subject, envPub, envPriv);
|
||||
logger.info(
|
||||
{ source: "env" },
|
||||
"VAPID keys loaded from environment — push subscriptions persist across container rebuilds.",
|
||||
);
|
||||
return { publicKey: envPub, privateKey: envPriv, subject };
|
||||
} catch (err) {
|
||||
logger.error({ err }, "Invalid VAPID env keys — falling back to disk");
|
||||
@@ -65,6 +69,10 @@ async function loadVapid(): Promise<{
|
||||
};
|
||||
if (parsed.publicKey && parsed.privateKey) {
|
||||
webpush.setVapidDetails(subject, parsed.publicKey, parsed.privateKey);
|
||||
logger.info(
|
||||
{ source: "disk", file },
|
||||
"VAPID keys loaded from disk. To share across hosts, copy them into the VAPID_PUBLIC_KEY / VAPID_PRIVATE_KEY env vars.",
|
||||
);
|
||||
return {
|
||||
publicKey: parsed.publicKey,
|
||||
privateKey: parsed.privateKey,
|
||||
|
||||
Reference in New Issue
Block a user