Documents the approach for adding a browser Notification API toggle to the draft page sidebar, including files to create/modify and edge cases to handle. https://claude.ai/code/session_0149MvVUYDY6pFUAV1fL4K69
5.6 KiB
Plan: Push Notifications Toggle on Draft Page
Overview
Add a toggle to the draft page sidebar that enables browser push notifications for draft events. When enabled, the user receives desktop notifications when:
- It's their turn to pick (highest priority)
- A pick is made by any team (so they can track the draft from another tab/window)
This uses the Browser Notification API (Notification.requestPermission() + new Notification()), which is the right fit because:
- Users already have an active Socket.IO connection on the draft page
- The primary use case is "I have the draft tab open but I'm looking at another tab/window"
- No server-side push infrastructure, service workers, or VAPID keys needed
- Simple, client-side-only feature — no database changes required
Implementation Steps
1. Create NotificationSettings component
New file: app/components/NotificationSettings.tsx
A toggle switch component (mirroring AutodraftSettings.tsx pattern) that:
- Shows a Switch toggle labeled "Notifications"
- On first enable: calls
Notification.requestPermission()to request browser permission - Stores the enabled/disabled preference in
localStorage(keyed per season, e.g.draftNotifications-{seasonId}) - Shows a small help message if the browser permission is
denied - Handles the case where the Notification API is unavailable (e.g. insecure context) by hiding the toggle entirely
- Receives no server props — this is entirely client-side state
Props:
interface NotificationSettingsProps {
seasonId: string;
enabled: boolean;
onEnabledChange: (enabled: boolean) => void;
}
2. Create useDraftNotifications hook
New file: app/hooks/useDraftNotifications.ts
Custom hook that:
- Manages the notification enabled/disabled state (persisted to
localStorage) - Checks browser support for Notification API on mount
- Returns
{ notificationsEnabled, setNotificationsEnabled, isSupported } - The actual notification firing will happen in the draft page component where socket events are already handled
3. Wire into the draft page
Modify: app/routes/leagues/$leagueId.draft.$seasonId.tsx
- Import and use
useDraftNotificationshook - In the existing
handlePickMadesocket handler (line ~359), add logic:- If notifications are enabled and
document.hidden(tab is not focused):- Fire a
new Notification()with the pick info (e.g. "Team X drafted Player Y") - If the pick means it's now the current user's turn, fire a more urgent notification ("It's your turn to pick!")
- Fire a
- If notifications are enabled and
- Pass notification state down to the
QueueSectioncomponent
4. Add toggle to the sidebar
Modify: app/components/draft/QueueSection.tsx
- Add the
NotificationSettingscomponent below theAutodraftSettingscomponent (inside the same sidebar section) - Accept new props for notification state
UI Placement
The toggle will sit in the sidebar's queue section, right below the existing Autodraft toggle:
┌─────────────────────────┐
│ My Queue │
│ ┌─────────────────────┐ │
│ │ 1. Player A [×] │ │
│ │ 2. Player B [×] │ │
│ └─────────────────────┘ │
│ │
│ ─── Autodraft ──── [·] │
│ ○ Next Pick │
│ ○ While On │
│ │
│ ─── Notifications ─ [·] │ ← NEW
│ │
└─────────────────────────┘
Notification Content
| Event | Condition | Title | Body |
|---|---|---|---|
| Pick made | Tab not focused, notifications on | "{League} Draft" | "{TeamName} picked {ParticipantName}" |
| Your turn | Tab not focused, notifications on, it's now user's turn | "{League} Draft" | "It's your turn to pick!" |
Notifications will include { tag: "draft-{seasonId}" } so they replace each other rather than stacking.
Edge Cases Handled
- Notification API not available (HTTP, unsupported browser): Toggle hidden entirely via
isSupportedcheck - Permission denied: Show inline message "Notifications blocked by browser. Update in browser settings."
- Permission not yet requested: Request on first toggle enable
- Tab is focused: Don't fire notifications (user can already see the draft)
- SSR: All Notification API checks guarded with
typeof window !== "undefined"
Files Changed
| File | Change |
|---|---|
app/hooks/useDraftNotifications.ts |
New — hook for notification state + localStorage persistence |
app/components/NotificationSettings.tsx |
New — toggle UI component |
app/components/draft/QueueSection.tsx |
Modified — add NotificationSettings below AutodraftSettings |
app/routes/leagues/$leagueId.draft.$seasonId.tsx |
Modified — use hook, fire notifications in socket handlers, pass props to QueueSection |
What This Does NOT Include
- Server-side web push (VAPID keys, web-push library, service worker, push subscriptions table) — not needed for the core use case. Can be added later if users want notifications when the tab is completely closed.
- Sound notifications — could be added as a follow-up.
- Granular notification preferences (per-event toggles) — a single on/off toggle is sufficient for v1. Can be expanded later.
- Database storage — preference stored in localStorage since it's device-specific and doesn't need to sync across devices.