brackt/app/components/DraftSidebar.tsx

125 lines
3.9 KiB
TypeScript
Raw Normal View History

2026-04-17 23:38:29 -07:00
import { useState } from "react";
import type { ReactNode } from "react";
2026-04-17 23:38:29 -07:00
import { ChevronDown, ChevronLeft, ChevronRight } from "lucide-react";
import { Button } from "~/components/ui/button";
import { cn } from "~/lib/utils";
interface DraftSidebarProps {
collapsed: boolean;
onCollapsedChange: (collapsed: boolean) => void;
queueSection: ReactNode;
recentPicksSection: ReactNode;
Add browser push notifications toggle to draft page (#11) * 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 * Add browser push notifications toggle to draft page Adds a Notifications toggle in the draft sidebar (below Autodraft) that uses the Browser Notification API to alert users when a pick is made or when it's their turn, while the tab is not focused. - New useDraftNotifications hook for state, permission, and localStorage - New NotificationSettings component with Switch toggle - Fires "It's your turn to pick!" when the next pick is the user's - Fires "{Team} picked {Player}" for all other picks - Gracefully hides toggle when Notification API is unavailable https://claude.ai/code/session_0149MvVUYDY6pFUAV1fL4K69 * Fix all code review issues with push notifications - Extract snake draft order calculation to shared getTeamForPick() helper in lib/draft-order.ts, removing the duplicated logic - Fix notification firing on user's own picks (guard with team id check) - Fix notification firing after draft is complete (early return) - Use a ref for sendNotification to prevent full socket handler re-registration every time the toggle is changed - Add permission drift listener via Permissions API so the UI reacts if the user revokes notification permission in browser settings - Add SSR guard for document.hidden in sendNotification - Remove redundant isSupported prop from NotificationSettings; derive unsupported state from permissionState === "unsupported" - Move NotificationSettings out of QueueSection prop-drilling path; render it via a new settingsSection slot on DraftSidebar instead https://claude.ai/code/session_0149MvVUYDY6pFUAV1fL4K69 * Fix second round of notification code review issues - Add My Turn Only / All Picks mode granularity so users can choose to only be notified when it's their pick, or for every pick - Remove double top-border by stripping leftover border-t/pt-4/mt-4 wrapper from NotificationSettings (now provided by DraftSidebar) - Remove unused isSupported from hook return value - Add n.onclick = () => window.focus() so clicking a notification brings the draft tab back into focus - Scope localStorage keys to userId to avoid shared-browser conflicts (keys now: draftNotifications-{userId}-{seasonId}) - Add notificationsModeRef alongside sendNotificationRef so mode changes don't trigger full socket handler re-registration https://claude.ai/code/session_0149MvVUYDY6pFUAV1fL4K69 --------- Co-authored-by: Claude <noreply@anthropic.com>
2026-02-20 19:30:53 -08:00
settingsSection?: ReactNode;
className?: string;
}
export function DraftSidebar({
collapsed,
onCollapsedChange,
queueSection,
recentPicksSection,
Add browser push notifications toggle to draft page (#11) * 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 * Add browser push notifications toggle to draft page Adds a Notifications toggle in the draft sidebar (below Autodraft) that uses the Browser Notification API to alert users when a pick is made or when it's their turn, while the tab is not focused. - New useDraftNotifications hook for state, permission, and localStorage - New NotificationSettings component with Switch toggle - Fires "It's your turn to pick!" when the next pick is the user's - Fires "{Team} picked {Player}" for all other picks - Gracefully hides toggle when Notification API is unavailable https://claude.ai/code/session_0149MvVUYDY6pFUAV1fL4K69 * Fix all code review issues with push notifications - Extract snake draft order calculation to shared getTeamForPick() helper in lib/draft-order.ts, removing the duplicated logic - Fix notification firing on user's own picks (guard with team id check) - Fix notification firing after draft is complete (early return) - Use a ref for sendNotification to prevent full socket handler re-registration every time the toggle is changed - Add permission drift listener via Permissions API so the UI reacts if the user revokes notification permission in browser settings - Add SSR guard for document.hidden in sendNotification - Remove redundant isSupported prop from NotificationSettings; derive unsupported state from permissionState === "unsupported" - Move NotificationSettings out of QueueSection prop-drilling path; render it via a new settingsSection slot on DraftSidebar instead https://claude.ai/code/session_0149MvVUYDY6pFUAV1fL4K69 * Fix second round of notification code review issues - Add My Turn Only / All Picks mode granularity so users can choose to only be notified when it's their pick, or for every pick - Remove double top-border by stripping leftover border-t/pt-4/mt-4 wrapper from NotificationSettings (now provided by DraftSidebar) - Remove unused isSupported from hook return value - Add n.onclick = () => window.focus() so clicking a notification brings the draft tab back into focus - Scope localStorage keys to userId to avoid shared-browser conflicts (keys now: draftNotifications-{userId}-{seasonId}) - Add notificationsModeRef alongside sendNotificationRef so mode changes don't trigger full socket handler re-registration https://claude.ai/code/session_0149MvVUYDY6pFUAV1fL4K69 --------- Co-authored-by: Claude <noreply@anthropic.com>
2026-02-20 19:30:53 -08:00
settingsSection,
className,
}: DraftSidebarProps) {
2026-04-17 23:38:29 -07:00
const [queueOpen, setQueueOpen] = useState(true);
const [picksOpen, setPicksOpen] = useState(true);
if (collapsed) {
return (
<div
className={cn(
2026-04-17 23:38:29 -07:00
"relative flex-shrink-0 bg-card border-r border-border transition-all duration-300 flex flex-col h-full",
"w-12 hidden lg:flex",
className
)}
>
<div className="flex-1" />
<div className="border-t border-border p-2 flex-shrink-0">
<Button
variant="ghost"
size="icon"
onClick={() => onCollapsedChange(false)}
className="w-full"
aria-label="Expand sidebar"
>
<ChevronRight className="h-4 w-4" />
</Button>
</div>
</div>
);
}
return (
<>
{/* Mobile backdrop */}
<div
className="fixed inset-0 bg-black/50 z-40 lg:hidden"
onClick={() => onCollapsedChange(true)}
aria-label="Close sidebar"
/>
<div
className={cn(
"relative flex-shrink-0 bg-card border-r border-border transition-all duration-300 flex flex-col",
"w-[300px]",
"fixed inset-y-0 left-0 z-50 lg:relative lg:z-auto",
className
)}
>
2026-04-17 23:38:29 -07:00
<div className="flex-1 flex flex-col min-h-0 overflow-hidden">
{/* Queue Section */}
2026-04-17 23:38:29 -07:00
<div className={cn("flex flex-col border-b", queueOpen ? "flex-1 min-h-0" : "flex-shrink-0")}>
<button
onClick={() => setQueueOpen((o) => !o)}
className="px-4 py-3 bg-muted/50 hover:bg-muted flex items-center justify-between w-full flex-shrink-0"
>
<h2 className="font-semibold text-sm">My Queue</h2>
2026-04-17 23:38:29 -07:00
<ChevronDown className={cn("h-4 w-4 transition-transform", queueOpen && "rotate-180")} />
</button>
{queueOpen && (
<div className="flex-1 overflow-y-auto min-h-0">
{queueSection}
</div>
)}
</div>
2026-04-17 23:38:29 -07:00
{/* Picks Section */}
<div className={cn("flex flex-col", picksOpen ? "flex-1 min-h-0" : "flex-shrink-0")}>
<button
onClick={() => setPicksOpen((o) => !o)}
className="px-4 py-3 bg-muted/50 hover:bg-muted flex items-center justify-between w-full flex-shrink-0"
>
<h2 className="font-semibold text-sm">Picks</h2>
2026-04-17 23:38:29 -07:00
<ChevronDown className={cn("h-4 w-4 transition-transform", picksOpen && "rotate-180")} />
</button>
{picksOpen && (
<div className="flex-1 overflow-y-auto min-h-0">
{recentPicksSection}
</div>
)}
</div>
</div>
Add browser push notifications toggle to draft page (#11) * 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 * Add browser push notifications toggle to draft page Adds a Notifications toggle in the draft sidebar (below Autodraft) that uses the Browser Notification API to alert users when a pick is made or when it's their turn, while the tab is not focused. - New useDraftNotifications hook for state, permission, and localStorage - New NotificationSettings component with Switch toggle - Fires "It's your turn to pick!" when the next pick is the user's - Fires "{Team} picked {Player}" for all other picks - Gracefully hides toggle when Notification API is unavailable https://claude.ai/code/session_0149MvVUYDY6pFUAV1fL4K69 * Fix all code review issues with push notifications - Extract snake draft order calculation to shared getTeamForPick() helper in lib/draft-order.ts, removing the duplicated logic - Fix notification firing on user's own picks (guard with team id check) - Fix notification firing after draft is complete (early return) - Use a ref for sendNotification to prevent full socket handler re-registration every time the toggle is changed - Add permission drift listener via Permissions API so the UI reacts if the user revokes notification permission in browser settings - Add SSR guard for document.hidden in sendNotification - Remove redundant isSupported prop from NotificationSettings; derive unsupported state from permissionState === "unsupported" - Move NotificationSettings out of QueueSection prop-drilling path; render it via a new settingsSection slot on DraftSidebar instead https://claude.ai/code/session_0149MvVUYDY6pFUAV1fL4K69 * Fix second round of notification code review issues - Add My Turn Only / All Picks mode granularity so users can choose to only be notified when it's their pick, or for every pick - Remove double top-border by stripping leftover border-t/pt-4/mt-4 wrapper from NotificationSettings (now provided by DraftSidebar) - Remove unused isSupported from hook return value - Add n.onclick = () => window.focus() so clicking a notification brings the draft tab back into focus - Scope localStorage keys to userId to avoid shared-browser conflicts (keys now: draftNotifications-{userId}-{seasonId}) - Add notificationsModeRef alongside sendNotificationRef so mode changes don't trigger full socket handler re-registration https://claude.ai/code/session_0149MvVUYDY6pFUAV1fL4K69 --------- Co-authored-by: Claude <noreply@anthropic.com>
2026-02-20 19:30:53 -08:00
{settingsSection && (
<div className="border-t border-border px-4 py-3 flex-shrink-0">
{settingsSection}
</div>
)}
<div className="border-t border-border p-2 flex-shrink-0">
<Button
variant="ghost"
size="sm"
onClick={() => onCollapsedChange(true)}
className="w-full flex items-center justify-center gap-2"
aria-label="Collapse sidebar"
>
<ChevronLeft className="h-4 w-4" />
<span className="text-sm">Hide Sidebar</span>
</Button>
</div>
</div>
</>
);
}