Add push notifications implementation plan
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
This commit is contained in:
parent
03fdba3022
commit
c368110b3d
1 changed files with 114 additions and 0 deletions
114
plan.md
Normal file
114
plan.md
Normal file
|
|
@ -0,0 +1,114 @@
|
||||||
|
# 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:
|
||||||
|
1. **It's their turn to pick** (highest priority)
|
||||||
|
2. **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:**
|
||||||
|
```typescript
|
||||||
|
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 `useDraftNotifications` hook
|
||||||
|
- In the existing `handlePickMade` socket 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!")
|
||||||
|
- Pass notification state down to the `QueueSection` component
|
||||||
|
|
||||||
|
### 4. Add toggle to the sidebar
|
||||||
|
**Modify:** `app/components/draft/QueueSection.tsx`
|
||||||
|
|
||||||
|
- Add the `NotificationSettings` component below the `AutodraftSettings` component (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 `isSupported` check
|
||||||
|
- **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.
|
||||||
Loading…
Add table
Reference in a new issue