brackt/plan.md
Claude c368110b3d
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
2026-02-20 16:48:41 +00:00

5.6 KiB
Raw Blame History

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:

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.