feat: web service worker + push subscription flow (#240)#273
Open
Tijesunimi004 wants to merge 1 commit into
Open
feat: web service worker + push subscription flow (#240)#273Tijesunimi004 wants to merge 1 commit into
Tijesunimi004 wants to merge 1 commit into
Conversation
Adds the full client-side push notification flow for apps/web:
public/sw.js
- Registers with skipWaiting/clients.claim so updates take effect immediately.
- push handler: shows a content-free notification (body: "You have a new
message") tagged by conversationId so duplicates are collapsed.
- notificationclick: posts sw:sync to an existing window or opens a new
tab at /app/conversations/{id}. Never displays message ciphertext.
src/hooks/usePushSubscription.ts
- Registers /sw.js on mount (browser support guarded).
- Exposes requestSubscription() — the only function that calls
Notification.requestPermission(); never called eagerly on page load.
- On grant, calls pushManager.subscribe() with the NEXT_PUBLIC_VAPID_PUBLIC_KEY
applicationServerKey and POSTs the result to /push/subscriptions with the
user's JWT in the Authorization header.
- Re-uses an existing subscription on re-mount to keep the server in sync.
src/components/PushPermissionPrompt.tsx
- Shown 5 s after the user enters the /app shell (contextual, not on load).
- Suppressed when permission is already granted/denied or when dismissed.
- Dismissal is recorded in sessionStorage so the banner stays gone for the
session without permanently refusing the browser prompt.
src/app/app/layout.tsx
- Mounts <PushPermissionPrompt />.
- Adds a navigator.serviceWorker "message" listener that reacts to sw:sync
events by navigating the router to the notified conversation, triggering
a data reload.
|
Hey @Tijesunimi004! 👋 It looks like this PR isn't linked to any issue. If this PR is for one of the issues assigned to you as part of a Wave, please link it to ensure your contribution is tracked properly. You can do this by adding a keyword to the PR description (e.g.,
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Implements the full client-side push notification flow in apps/web per #240.
Files
apps/web/public/sw.js(new)Plain-JS service worker served at
/sw.js. Three event handlers:install→skipWaiting()so updates activate immediately.activate→clients.claim()so the SW controls all open tabs.push→ shows a content-free notification. The body is always "You have a new message"; no message ciphertext is ever shown. Tagged byconversationIdso repeated pushes for the same thread collapse rather than stack.notificationclick→ closes the notification, then either postssw:syncto an already-open window and callsclient.focus(), or opens a new tab at/app/conversations/{id}. Never callsclient.navigate()(only valid on controlled clients).apps/web/src/hooks/usePushSubscription.ts(new)usePushSubscription(token)hook:/sw.json mount (guarded for SSR, noserviceWorker, and noPushManager).granted, re-POSTs any existing subscription to keep the server in sync.requestSubscription()— the only path that callsNotification.requestPermission(). Called only when the user explicitly clicks "Enable". Never called eagerly.NEXT_PUBLIC_VAPID_PUBLIC_KEY, thenPOST /push/subscriptionswithAuthorization: Bearer <token>.apps/web/src/components/PushPermissionPrompt.tsx(new)Dismissible banner:
Notification.permissionisgrantedordenied, when already subscribed, or when dismissed.sessionStorageso it stays hidden for the session.requestSubscription(); "Not now" hides for the session.apps/web/src/app/app/layout.tsx(modified)<PushPermissionPrompt />at the bottom of the authenticated shell.navigator.serviceWorker messagelistener that handlessw:syncevents from the SW'snotificationclickhandler, navigating the Next.js router to the relevant conversation (triggering a data sync).Acceptance criteria
usePushSubscriptionregisters the SW on mount and subscribes only whenrequestSubscription()is called (after the user clicks "Enable" in the prompt).postSubscription()POSTs thePushSubscriptionJSON toPOST /push/subscriptionswith the user's JWT.notificationclickpostssw:syncto the open client; the layout's message listener navigates to/app/conversations/{id}, which triggers the page's data fetching hooks.Environment variable required