brackt/app/components/user/settings/NotificationsSection.tsx

128 lines
4.6 KiB
TypeScript
Raw Permalink Normal View History

import { Link, useFetcher } from "react-router";
Add opt-in Discord pings to standings notifications (#429) * Add opt-in Discord ping to standings notifications Users who have linked their Discord account can enable a ping preference in Settings > Notifications. When enabled, their bracket username is replaced with a Discord @mention (<@userId>) in league standings update messages, sending them a push notification and showing their Discord display name. Adds discordPingEnabled column to users, a findDiscordIdsByUserIds model helper, allowed_mentions support to the Discord webhook payload, and a NotificationsSection settings component. https://claude.ai/code/session_01NUv93WRrufHhpZSMyY4bjs * Remove Display Name field from user profile settings Username is the only user-facing name field. The display_name column remains in the database since BetterAuth writes the OAuth provider name there, and it serves as a programmatic fallback via getUserDisplayName. https://claude.ai/code/session_01NUv93WRrufHhpZSMyY4bjs * Address code review feedback on Discord ping feature - Fix broken Account settings link in NotificationsSection: replace the non-functional ?section=account href with an onNavigateToAccount callback that drives the parent's useState-based section switcher - Replace hand-rolled toggle button with the project's Switch component (Radix UI) for consistent sizing, accessibility, and dark-mode support - Guard update-discord-ping action: return an error if the user attempts to enable pings without a Discord account linked - Surface action error in NotificationsSection UI - Cap allowed_mentions.users at 100 to avoid Discord rejecting the payload in large leagues - Remove the showWinner alias in scoring-calculator; inline winnerScoreChanged - Add comment to league settings test notification explaining why discordUserId is omitted - Clarify database schema comment: displayName is write-only from BetterAuth https://claude.ai/code/session_01NUv93WRrufHhpZSMyY4bjs --------- Co-authored-by: Claude <noreply@anthropic.com>
2026-05-15 10:06:54 -07:00
import { Switch } from "~/components/ui/switch";
import { Label } from "~/components/ui/label";
Add draft email notifications feature (#459) * Add on-the-clock email notifications and fix push notification modal - Add `draft_email_notifications_enabled` column to users table - New `sendOnTheClockEmail` service sends an email when a user's turn arrives in a draft; detects back-to-back picks and sends one combined email instead of two; skips notification if the team has autodraft enabled - Email is triggered after every manual pick (make-pick route) and every timer-expiry pick (executeAutoPick) once the full autodraft chain settles, so the notified user is always the first human on the clock - Add email notification toggle to the user settings Notifications section - Fix push notification dropdown in the draft room: the bare absolute div is replaced with a Radix Popover so clicking outside dismisses it without toggling notifications off - Rename draft room label from "Pick Notifications" to "Push Notifications" to distinguish from the new email notifications https://claude.ai/code/session_01LMGxgYvtE3CF8Jf3u2pXgA * Address code review feedback on email notification feature - sendOnTheClockEmail now fetches season (and currentPickNumber) internally, removing the blocking postChainSeason pre-fetch from both make-pick.ts and executeAutoPick; callers are now true fire-and-forget with no IIFE needed - Parallel Promise.all for team, autodraftSettings, and league queries reduces sequential DB round-trips from 5 to 3 - Remove team.ownerId type cast; assign to local after the null guard - Combine the two calculatePickInfo calls for the same pick into one - Fix popoverOpen/enabled divergence: remove the {enabled && ...} guard on PopoverContent so popoverOpen is the sole visibility control - Unify NotificationsSection feedback pattern: both Discord and email sections now read success/error directly from their respective fetcher data via a shared feedbackFromFetcher helper; removes the success/error props and the verbose cast that was used for email - Add 13 unit tests for sendOnTheClockEmail covering: early-exit paths (no season, past totalPicks, no owner, notifications disabled, autodraft enabled, no league), single vs back-to-back subject selection, draft room link presence, error logging without throwing, and parallel query execution https://claude.ai/code/session_01LMGxgYvtE3CF8Jf3u2pXgA --------- Co-authored-by: Claude <noreply@anthropic.com>
2026-05-20 15:35:43 -07:00
type ActionResult = { success?: boolean; error?: string };
function feedbackFromFetcher(fetcher: ReturnType<typeof useFetcher>) {
if (fetcher.state !== "idle" || !fetcher.data) return { success: false, error: undefined };
const data = fetcher.data as ActionResult;
return { success: !!data.success, error: data.error };
}
Add opt-in Discord pings to standings notifications (#429) * Add opt-in Discord ping to standings notifications Users who have linked their Discord account can enable a ping preference in Settings > Notifications. When enabled, their bracket username is replaced with a Discord @mention (<@userId>) in league standings update messages, sending them a push notification and showing their Discord display name. Adds discordPingEnabled column to users, a findDiscordIdsByUserIds model helper, allowed_mentions support to the Discord webhook payload, and a NotificationsSection settings component. https://claude.ai/code/session_01NUv93WRrufHhpZSMyY4bjs * Remove Display Name field from user profile settings Username is the only user-facing name field. The display_name column remains in the database since BetterAuth writes the OAuth provider name there, and it serves as a programmatic fallback via getUserDisplayName. https://claude.ai/code/session_01NUv93WRrufHhpZSMyY4bjs * Address code review feedback on Discord ping feature - Fix broken Account settings link in NotificationsSection: replace the non-functional ?section=account href with an onNavigateToAccount callback that drives the parent's useState-based section switcher - Replace hand-rolled toggle button with the project's Switch component (Radix UI) for consistent sizing, accessibility, and dark-mode support - Guard update-discord-ping action: return an error if the user attempts to enable pings without a Discord account linked - Surface action error in NotificationsSection UI - Cap allowed_mentions.users at 100 to avoid Discord rejecting the payload in large leagues - Remove the showWinner alias in scoring-calculator; inline winnerScoreChanged - Add comment to league settings test notification explaining why discordUserId is omitted - Clarify database schema comment: displayName is write-only from BetterAuth https://claude.ai/code/session_01NUv93WRrufHhpZSMyY4bjs --------- Co-authored-by: Claude <noreply@anthropic.com>
2026-05-15 10:06:54 -07:00
type Props = {
discordPingEnabled: boolean;
hasDiscordLinked: boolean;
Add draft email notifications feature (#459) * Add on-the-clock email notifications and fix push notification modal - Add `draft_email_notifications_enabled` column to users table - New `sendOnTheClockEmail` service sends an email when a user's turn arrives in a draft; detects back-to-back picks and sends one combined email instead of two; skips notification if the team has autodraft enabled - Email is triggered after every manual pick (make-pick route) and every timer-expiry pick (executeAutoPick) once the full autodraft chain settles, so the notified user is always the first human on the clock - Add email notification toggle to the user settings Notifications section - Fix push notification dropdown in the draft room: the bare absolute div is replaced with a Radix Popover so clicking outside dismisses it without toggling notifications off - Rename draft room label from "Pick Notifications" to "Push Notifications" to distinguish from the new email notifications https://claude.ai/code/session_01LMGxgYvtE3CF8Jf3u2pXgA * Address code review feedback on email notification feature - sendOnTheClockEmail now fetches season (and currentPickNumber) internally, removing the blocking postChainSeason pre-fetch from both make-pick.ts and executeAutoPick; callers are now true fire-and-forget with no IIFE needed - Parallel Promise.all for team, autodraftSettings, and league queries reduces sequential DB round-trips from 5 to 3 - Remove team.ownerId type cast; assign to local after the null guard - Combine the two calculatePickInfo calls for the same pick into one - Fix popoverOpen/enabled divergence: remove the {enabled && ...} guard on PopoverContent so popoverOpen is the sole visibility control - Unify NotificationsSection feedback pattern: both Discord and email sections now read success/error directly from their respective fetcher data via a shared feedbackFromFetcher helper; removes the success/error props and the verbose cast that was used for email - Add 13 unit tests for sendOnTheClockEmail covering: early-exit paths (no season, past totalPicks, no owner, notifications disabled, autodraft enabled, no league), single vs back-to-back subject selection, draft room link presence, error logging without throwing, and parallel query execution https://claude.ai/code/session_01LMGxgYvtE3CF8Jf3u2pXgA --------- Co-authored-by: Claude <noreply@anthropic.com>
2026-05-20 15:35:43 -07:00
draftEmailNotificationsEnabled: boolean;
Add opt-in Discord pings to standings notifications (#429) * Add opt-in Discord ping to standings notifications Users who have linked their Discord account can enable a ping preference in Settings > Notifications. When enabled, their bracket username is replaced with a Discord @mention (<@userId>) in league standings update messages, sending them a push notification and showing their Discord display name. Adds discordPingEnabled column to users, a findDiscordIdsByUserIds model helper, allowed_mentions support to the Discord webhook payload, and a NotificationsSection settings component. https://claude.ai/code/session_01NUv93WRrufHhpZSMyY4bjs * Remove Display Name field from user profile settings Username is the only user-facing name field. The display_name column remains in the database since BetterAuth writes the OAuth provider name there, and it serves as a programmatic fallback via getUserDisplayName. https://claude.ai/code/session_01NUv93WRrufHhpZSMyY4bjs * Address code review feedback on Discord ping feature - Fix broken Account settings link in NotificationsSection: replace the non-functional ?section=account href with an onNavigateToAccount callback that drives the parent's useState-based section switcher - Replace hand-rolled toggle button with the project's Switch component (Radix UI) for consistent sizing, accessibility, and dark-mode support - Guard update-discord-ping action: return an error if the user attempts to enable pings without a Discord account linked - Surface action error in NotificationsSection UI - Cap allowed_mentions.users at 100 to avoid Discord rejecting the payload in large leagues - Remove the showWinner alias in scoring-calculator; inline winnerScoreChanged - Add comment to league settings test notification explaining why discordUserId is omitted - Clarify database schema comment: displayName is write-only from BetterAuth https://claude.ai/code/session_01NUv93WRrufHhpZSMyY4bjs --------- Co-authored-by: Claude <noreply@anthropic.com>
2026-05-15 10:06:54 -07:00
};
export function NotificationsSection({ discordPingEnabled, hasDiscordLinked, draftEmailNotificationsEnabled }: Props) {
Add draft email notifications feature (#459) * Add on-the-clock email notifications and fix push notification modal - Add `draft_email_notifications_enabled` column to users table - New `sendOnTheClockEmail` service sends an email when a user's turn arrives in a draft; detects back-to-back picks and sends one combined email instead of two; skips notification if the team has autodraft enabled - Email is triggered after every manual pick (make-pick route) and every timer-expiry pick (executeAutoPick) once the full autodraft chain settles, so the notified user is always the first human on the clock - Add email notification toggle to the user settings Notifications section - Fix push notification dropdown in the draft room: the bare absolute div is replaced with a Radix Popover so clicking outside dismisses it without toggling notifications off - Rename draft room label from "Pick Notifications" to "Push Notifications" to distinguish from the new email notifications https://claude.ai/code/session_01LMGxgYvtE3CF8Jf3u2pXgA * Address code review feedback on email notification feature - sendOnTheClockEmail now fetches season (and currentPickNumber) internally, removing the blocking postChainSeason pre-fetch from both make-pick.ts and executeAutoPick; callers are now true fire-and-forget with no IIFE needed - Parallel Promise.all for team, autodraftSettings, and league queries reduces sequential DB round-trips from 5 to 3 - Remove team.ownerId type cast; assign to local after the null guard - Combine the two calculatePickInfo calls for the same pick into one - Fix popoverOpen/enabled divergence: remove the {enabled && ...} guard on PopoverContent so popoverOpen is the sole visibility control - Unify NotificationsSection feedback pattern: both Discord and email sections now read success/error directly from their respective fetcher data via a shared feedbackFromFetcher helper; removes the success/error props and the verbose cast that was used for email - Add 13 unit tests for sendOnTheClockEmail covering: early-exit paths (no season, past totalPicks, no owner, notifications disabled, autodraft enabled, no league), single vs back-to-back subject selection, draft room link presence, error logging without throwing, and parallel query execution https://claude.ai/code/session_01LMGxgYvtE3CF8Jf3u2pXgA --------- Co-authored-by: Claude <noreply@anthropic.com>
2026-05-20 15:35:43 -07:00
const discordFetcher = useFetcher();
const emailFetcher = useFetcher();
const optimisticDiscordEnabled =
discordFetcher.state !== "idle"
? discordFetcher.formData?.get("discordPingEnabled") === "true"
Add opt-in Discord pings to standings notifications (#429) * Add opt-in Discord ping to standings notifications Users who have linked their Discord account can enable a ping preference in Settings > Notifications. When enabled, their bracket username is replaced with a Discord @mention (<@userId>) in league standings update messages, sending them a push notification and showing their Discord display name. Adds discordPingEnabled column to users, a findDiscordIdsByUserIds model helper, allowed_mentions support to the Discord webhook payload, and a NotificationsSection settings component. https://claude.ai/code/session_01NUv93WRrufHhpZSMyY4bjs * Remove Display Name field from user profile settings Username is the only user-facing name field. The display_name column remains in the database since BetterAuth writes the OAuth provider name there, and it serves as a programmatic fallback via getUserDisplayName. https://claude.ai/code/session_01NUv93WRrufHhpZSMyY4bjs * Address code review feedback on Discord ping feature - Fix broken Account settings link in NotificationsSection: replace the non-functional ?section=account href with an onNavigateToAccount callback that drives the parent's useState-based section switcher - Replace hand-rolled toggle button with the project's Switch component (Radix UI) for consistent sizing, accessibility, and dark-mode support - Guard update-discord-ping action: return an error if the user attempts to enable pings without a Discord account linked - Surface action error in NotificationsSection UI - Cap allowed_mentions.users at 100 to avoid Discord rejecting the payload in large leagues - Remove the showWinner alias in scoring-calculator; inline winnerScoreChanged - Add comment to league settings test notification explaining why discordUserId is omitted - Clarify database schema comment: displayName is write-only from BetterAuth https://claude.ai/code/session_01NUv93WRrufHhpZSMyY4bjs --------- Co-authored-by: Claude <noreply@anthropic.com>
2026-05-15 10:06:54 -07:00
: discordPingEnabled;
Add draft email notifications feature (#459) * Add on-the-clock email notifications and fix push notification modal - Add `draft_email_notifications_enabled` column to users table - New `sendOnTheClockEmail` service sends an email when a user's turn arrives in a draft; detects back-to-back picks and sends one combined email instead of two; skips notification if the team has autodraft enabled - Email is triggered after every manual pick (make-pick route) and every timer-expiry pick (executeAutoPick) once the full autodraft chain settles, so the notified user is always the first human on the clock - Add email notification toggle to the user settings Notifications section - Fix push notification dropdown in the draft room: the bare absolute div is replaced with a Radix Popover so clicking outside dismisses it without toggling notifications off - Rename draft room label from "Pick Notifications" to "Push Notifications" to distinguish from the new email notifications https://claude.ai/code/session_01LMGxgYvtE3CF8Jf3u2pXgA * Address code review feedback on email notification feature - sendOnTheClockEmail now fetches season (and currentPickNumber) internally, removing the blocking postChainSeason pre-fetch from both make-pick.ts and executeAutoPick; callers are now true fire-and-forget with no IIFE needed - Parallel Promise.all for team, autodraftSettings, and league queries reduces sequential DB round-trips from 5 to 3 - Remove team.ownerId type cast; assign to local after the null guard - Combine the two calculatePickInfo calls for the same pick into one - Fix popoverOpen/enabled divergence: remove the {enabled && ...} guard on PopoverContent so popoverOpen is the sole visibility control - Unify NotificationsSection feedback pattern: both Discord and email sections now read success/error directly from their respective fetcher data via a shared feedbackFromFetcher helper; removes the success/error props and the verbose cast that was used for email - Add 13 unit tests for sendOnTheClockEmail covering: early-exit paths (no season, past totalPicks, no owner, notifications disabled, autodraft enabled, no league), single vs back-to-back subject selection, draft room link presence, error logging without throwing, and parallel query execution https://claude.ai/code/session_01LMGxgYvtE3CF8Jf3u2pXgA --------- Co-authored-by: Claude <noreply@anthropic.com>
2026-05-20 15:35:43 -07:00
const optimisticEmailEnabled =
emailFetcher.state !== "idle"
? emailFetcher.formData?.get("draftEmailNotificationsEnabled") === "true"
: draftEmailNotificationsEnabled;
const { success: discordSuccess, error: discordError } = feedbackFromFetcher(discordFetcher);
const { success: emailSuccess } = feedbackFromFetcher(emailFetcher);
const handleDiscordToggle = (checked: boolean) => {
discordFetcher.submit(
Add opt-in Discord pings to standings notifications (#429) * Add opt-in Discord ping to standings notifications Users who have linked their Discord account can enable a ping preference in Settings > Notifications. When enabled, their bracket username is replaced with a Discord @mention (<@userId>) in league standings update messages, sending them a push notification and showing their Discord display name. Adds discordPingEnabled column to users, a findDiscordIdsByUserIds model helper, allowed_mentions support to the Discord webhook payload, and a NotificationsSection settings component. https://claude.ai/code/session_01NUv93WRrufHhpZSMyY4bjs * Remove Display Name field from user profile settings Username is the only user-facing name field. The display_name column remains in the database since BetterAuth writes the OAuth provider name there, and it serves as a programmatic fallback via getUserDisplayName. https://claude.ai/code/session_01NUv93WRrufHhpZSMyY4bjs * Address code review feedback on Discord ping feature - Fix broken Account settings link in NotificationsSection: replace the non-functional ?section=account href with an onNavigateToAccount callback that drives the parent's useState-based section switcher - Replace hand-rolled toggle button with the project's Switch component (Radix UI) for consistent sizing, accessibility, and dark-mode support - Guard update-discord-ping action: return an error if the user attempts to enable pings without a Discord account linked - Surface action error in NotificationsSection UI - Cap allowed_mentions.users at 100 to avoid Discord rejecting the payload in large leagues - Remove the showWinner alias in scoring-calculator; inline winnerScoreChanged - Add comment to league settings test notification explaining why discordUserId is omitted - Clarify database schema comment: displayName is write-only from BetterAuth https://claude.ai/code/session_01NUv93WRrufHhpZSMyY4bjs --------- Co-authored-by: Claude <noreply@anthropic.com>
2026-05-15 10:06:54 -07:00
{ intent: "update-discord-ping", discordPingEnabled: String(checked) },
{ method: "post" }
);
};
Add draft email notifications feature (#459) * Add on-the-clock email notifications and fix push notification modal - Add `draft_email_notifications_enabled` column to users table - New `sendOnTheClockEmail` service sends an email when a user's turn arrives in a draft; detects back-to-back picks and sends one combined email instead of two; skips notification if the team has autodraft enabled - Email is triggered after every manual pick (make-pick route) and every timer-expiry pick (executeAutoPick) once the full autodraft chain settles, so the notified user is always the first human on the clock - Add email notification toggle to the user settings Notifications section - Fix push notification dropdown in the draft room: the bare absolute div is replaced with a Radix Popover so clicking outside dismisses it without toggling notifications off - Rename draft room label from "Pick Notifications" to "Push Notifications" to distinguish from the new email notifications https://claude.ai/code/session_01LMGxgYvtE3CF8Jf3u2pXgA * Address code review feedback on email notification feature - sendOnTheClockEmail now fetches season (and currentPickNumber) internally, removing the blocking postChainSeason pre-fetch from both make-pick.ts and executeAutoPick; callers are now true fire-and-forget with no IIFE needed - Parallel Promise.all for team, autodraftSettings, and league queries reduces sequential DB round-trips from 5 to 3 - Remove team.ownerId type cast; assign to local after the null guard - Combine the two calculatePickInfo calls for the same pick into one - Fix popoverOpen/enabled divergence: remove the {enabled && ...} guard on PopoverContent so popoverOpen is the sole visibility control - Unify NotificationsSection feedback pattern: both Discord and email sections now read success/error directly from their respective fetcher data via a shared feedbackFromFetcher helper; removes the success/error props and the verbose cast that was used for email - Add 13 unit tests for sendOnTheClockEmail covering: early-exit paths (no season, past totalPicks, no owner, notifications disabled, autodraft enabled, no league), single vs back-to-back subject selection, draft room link presence, error logging without throwing, and parallel query execution https://claude.ai/code/session_01LMGxgYvtE3CF8Jf3u2pXgA --------- Co-authored-by: Claude <noreply@anthropic.com>
2026-05-20 15:35:43 -07:00
const handleEmailToggle = (checked: boolean) => {
emailFetcher.submit(
{ intent: "update-email-draft-notifications", draftEmailNotificationsEnabled: String(checked) },
{ method: "post" }
);
};
Add opt-in Discord pings to standings notifications (#429) * Add opt-in Discord ping to standings notifications Users who have linked their Discord account can enable a ping preference in Settings > Notifications. When enabled, their bracket username is replaced with a Discord @mention (<@userId>) in league standings update messages, sending them a push notification and showing their Discord display name. Adds discordPingEnabled column to users, a findDiscordIdsByUserIds model helper, allowed_mentions support to the Discord webhook payload, and a NotificationsSection settings component. https://claude.ai/code/session_01NUv93WRrufHhpZSMyY4bjs * Remove Display Name field from user profile settings Username is the only user-facing name field. The display_name column remains in the database since BetterAuth writes the OAuth provider name there, and it serves as a programmatic fallback via getUserDisplayName. https://claude.ai/code/session_01NUv93WRrufHhpZSMyY4bjs * Address code review feedback on Discord ping feature - Fix broken Account settings link in NotificationsSection: replace the non-functional ?section=account href with an onNavigateToAccount callback that drives the parent's useState-based section switcher - Replace hand-rolled toggle button with the project's Switch component (Radix UI) for consistent sizing, accessibility, and dark-mode support - Guard update-discord-ping action: return an error if the user attempts to enable pings without a Discord account linked - Surface action error in NotificationsSection UI - Cap allowed_mentions.users at 100 to avoid Discord rejecting the payload in large leagues - Remove the showWinner alias in scoring-calculator; inline winnerScoreChanged - Add comment to league settings test notification explaining why discordUserId is omitted - Clarify database schema comment: displayName is write-only from BetterAuth https://claude.ai/code/session_01NUv93WRrufHhpZSMyY4bjs --------- Co-authored-by: Claude <noreply@anthropic.com>
2026-05-15 10:06:54 -07:00
return (
<div className="space-y-6">
<div>
<h2 className="text-lg font-semibold">Notifications</h2>
<p className="text-sm text-muted-foreground">
Manage how you receive league notifications.
</p>
</div>
Add draft email notifications feature (#459) * Add on-the-clock email notifications and fix push notification modal - Add `draft_email_notifications_enabled` column to users table - New `sendOnTheClockEmail` service sends an email when a user's turn arrives in a draft; detects back-to-back picks and sends one combined email instead of two; skips notification if the team has autodraft enabled - Email is triggered after every manual pick (make-pick route) and every timer-expiry pick (executeAutoPick) once the full autodraft chain settles, so the notified user is always the first human on the clock - Add email notification toggle to the user settings Notifications section - Fix push notification dropdown in the draft room: the bare absolute div is replaced with a Radix Popover so clicking outside dismisses it without toggling notifications off - Rename draft room label from "Pick Notifications" to "Push Notifications" to distinguish from the new email notifications https://claude.ai/code/session_01LMGxgYvtE3CF8Jf3u2pXgA * Address code review feedback on email notification feature - sendOnTheClockEmail now fetches season (and currentPickNumber) internally, removing the blocking postChainSeason pre-fetch from both make-pick.ts and executeAutoPick; callers are now true fire-and-forget with no IIFE needed - Parallel Promise.all for team, autodraftSettings, and league queries reduces sequential DB round-trips from 5 to 3 - Remove team.ownerId type cast; assign to local after the null guard - Combine the two calculatePickInfo calls for the same pick into one - Fix popoverOpen/enabled divergence: remove the {enabled && ...} guard on PopoverContent so popoverOpen is the sole visibility control - Unify NotificationsSection feedback pattern: both Discord and email sections now read success/error directly from their respective fetcher data via a shared feedbackFromFetcher helper; removes the success/error props and the verbose cast that was used for email - Add 13 unit tests for sendOnTheClockEmail covering: early-exit paths (no season, past totalPicks, no owner, notifications disabled, autodraft enabled, no league), single vs back-to-back subject selection, draft room link presence, error logging without throwing, and parallel query execution https://claude.ai/code/session_01LMGxgYvtE3CF8Jf3u2pXgA --------- Co-authored-by: Claude <noreply@anthropic.com>
2026-05-20 15:35:43 -07:00
<div className="rounded-lg border p-4 space-y-4">
<h3 className="text-sm font-medium">Draft Pick Emails</h3>
<div className="flex items-center justify-between gap-4">
<div className="space-y-1">
<Label htmlFor="email-draft-switch" className="text-sm font-normal">
Email me when I'm on the clock
</Label>
<p className="text-xs text-muted-foreground">
Receive an email when it's your turn to pick in a draft. If you have back-to-back
picks, you'll get a single email letting you know.
</p>
</div>
<Switch
id="email-draft-switch"
checked={optimisticEmailEnabled}
onCheckedChange={handleEmailToggle}
disabled={emailFetcher.state !== "idle"}
/>
</div>
{emailSuccess && (
<p className="text-sm text-green-600">Email notification preference saved.</p>
)}
</div>
Add opt-in Discord pings to standings notifications (#429) * Add opt-in Discord ping to standings notifications Users who have linked their Discord account can enable a ping preference in Settings > Notifications. When enabled, their bracket username is replaced with a Discord @mention (<@userId>) in league standings update messages, sending them a push notification and showing their Discord display name. Adds discordPingEnabled column to users, a findDiscordIdsByUserIds model helper, allowed_mentions support to the Discord webhook payload, and a NotificationsSection settings component. https://claude.ai/code/session_01NUv93WRrufHhpZSMyY4bjs * Remove Display Name field from user profile settings Username is the only user-facing name field. The display_name column remains in the database since BetterAuth writes the OAuth provider name there, and it serves as a programmatic fallback via getUserDisplayName. https://claude.ai/code/session_01NUv93WRrufHhpZSMyY4bjs * Address code review feedback on Discord ping feature - Fix broken Account settings link in NotificationsSection: replace the non-functional ?section=account href with an onNavigateToAccount callback that drives the parent's useState-based section switcher - Replace hand-rolled toggle button with the project's Switch component (Radix UI) for consistent sizing, accessibility, and dark-mode support - Guard update-discord-ping action: return an error if the user attempts to enable pings without a Discord account linked - Surface action error in NotificationsSection UI - Cap allowed_mentions.users at 100 to avoid Discord rejecting the payload in large leagues - Remove the showWinner alias in scoring-calculator; inline winnerScoreChanged - Add comment to league settings test notification explaining why discordUserId is omitted - Clarify database schema comment: displayName is write-only from BetterAuth https://claude.ai/code/session_01NUv93WRrufHhpZSMyY4bjs --------- Co-authored-by: Claude <noreply@anthropic.com>
2026-05-15 10:06:54 -07:00
<div className="rounded-lg border p-4 space-y-4">
<h3 className="text-sm font-medium">Discord Pings</h3>
{!hasDiscordLinked ? (
<p className="text-sm text-muted-foreground">
Link your Discord account in{" "}
<Link
to="/settings/account"
Add opt-in Discord pings to standings notifications (#429) * Add opt-in Discord ping to standings notifications Users who have linked their Discord account can enable a ping preference in Settings > Notifications. When enabled, their bracket username is replaced with a Discord @mention (<@userId>) in league standings update messages, sending them a push notification and showing their Discord display name. Adds discordPingEnabled column to users, a findDiscordIdsByUserIds model helper, allowed_mentions support to the Discord webhook payload, and a NotificationsSection settings component. https://claude.ai/code/session_01NUv93WRrufHhpZSMyY4bjs * Remove Display Name field from user profile settings Username is the only user-facing name field. The display_name column remains in the database since BetterAuth writes the OAuth provider name there, and it serves as a programmatic fallback via getUserDisplayName. https://claude.ai/code/session_01NUv93WRrufHhpZSMyY4bjs * Address code review feedback on Discord ping feature - Fix broken Account settings link in NotificationsSection: replace the non-functional ?section=account href with an onNavigateToAccount callback that drives the parent's useState-based section switcher - Replace hand-rolled toggle button with the project's Switch component (Radix UI) for consistent sizing, accessibility, and dark-mode support - Guard update-discord-ping action: return an error if the user attempts to enable pings without a Discord account linked - Surface action error in NotificationsSection UI - Cap allowed_mentions.users at 100 to avoid Discord rejecting the payload in large leagues - Remove the showWinner alias in scoring-calculator; inline winnerScoreChanged - Add comment to league settings test notification explaining why discordUserId is omitted - Clarify database schema comment: displayName is write-only from BetterAuth https://claude.ai/code/session_01NUv93WRrufHhpZSMyY4bjs --------- Co-authored-by: Claude <noreply@anthropic.com>
2026-05-15 10:06:54 -07:00
className="text-primary underline-offset-4 hover:underline"
>
Account settings
</Link>{" "}
Add opt-in Discord pings to standings notifications (#429) * Add opt-in Discord ping to standings notifications Users who have linked their Discord account can enable a ping preference in Settings > Notifications. When enabled, their bracket username is replaced with a Discord @mention (<@userId>) in league standings update messages, sending them a push notification and showing their Discord display name. Adds discordPingEnabled column to users, a findDiscordIdsByUserIds model helper, allowed_mentions support to the Discord webhook payload, and a NotificationsSection settings component. https://claude.ai/code/session_01NUv93WRrufHhpZSMyY4bjs * Remove Display Name field from user profile settings Username is the only user-facing name field. The display_name column remains in the database since BetterAuth writes the OAuth provider name there, and it serves as a programmatic fallback via getUserDisplayName. https://claude.ai/code/session_01NUv93WRrufHhpZSMyY4bjs * Address code review feedback on Discord ping feature - Fix broken Account settings link in NotificationsSection: replace the non-functional ?section=account href with an onNavigateToAccount callback that drives the parent's useState-based section switcher - Replace hand-rolled toggle button with the project's Switch component (Radix UI) for consistent sizing, accessibility, and dark-mode support - Guard update-discord-ping action: return an error if the user attempts to enable pings without a Discord account linked - Surface action error in NotificationsSection UI - Cap allowed_mentions.users at 100 to avoid Discord rejecting the payload in large leagues - Remove the showWinner alias in scoring-calculator; inline winnerScoreChanged - Add comment to league settings test notification explaining why discordUserId is omitted - Clarify database schema comment: displayName is write-only from BetterAuth https://claude.ai/code/session_01NUv93WRrufHhpZSMyY4bjs --------- Co-authored-by: Claude <noreply@anthropic.com>
2026-05-15 10:06:54 -07:00
to enable Discord pings for league notifications.
</p>
) : (
<>
<div className="flex items-center justify-between gap-4">
<div className="space-y-1">
<Label htmlFor="discord-ping-switch" className="text-sm font-normal">
Ping me in standings updates
</Label>
<p className="text-xs text-muted-foreground">
When enabled, you'll be @mentioned in league Discord notifications and your
Discord display name will appear in place of your brackt username.
</p>
</div>
<Switch
id="discord-ping-switch"
Add draft email notifications feature (#459) * Add on-the-clock email notifications and fix push notification modal - Add `draft_email_notifications_enabled` column to users table - New `sendOnTheClockEmail` service sends an email when a user's turn arrives in a draft; detects back-to-back picks and sends one combined email instead of two; skips notification if the team has autodraft enabled - Email is triggered after every manual pick (make-pick route) and every timer-expiry pick (executeAutoPick) once the full autodraft chain settles, so the notified user is always the first human on the clock - Add email notification toggle to the user settings Notifications section - Fix push notification dropdown in the draft room: the bare absolute div is replaced with a Radix Popover so clicking outside dismisses it without toggling notifications off - Rename draft room label from "Pick Notifications" to "Push Notifications" to distinguish from the new email notifications https://claude.ai/code/session_01LMGxgYvtE3CF8Jf3u2pXgA * Address code review feedback on email notification feature - sendOnTheClockEmail now fetches season (and currentPickNumber) internally, removing the blocking postChainSeason pre-fetch from both make-pick.ts and executeAutoPick; callers are now true fire-and-forget with no IIFE needed - Parallel Promise.all for team, autodraftSettings, and league queries reduces sequential DB round-trips from 5 to 3 - Remove team.ownerId type cast; assign to local after the null guard - Combine the two calculatePickInfo calls for the same pick into one - Fix popoverOpen/enabled divergence: remove the {enabled && ...} guard on PopoverContent so popoverOpen is the sole visibility control - Unify NotificationsSection feedback pattern: both Discord and email sections now read success/error directly from their respective fetcher data via a shared feedbackFromFetcher helper; removes the success/error props and the verbose cast that was used for email - Add 13 unit tests for sendOnTheClockEmail covering: early-exit paths (no season, past totalPicks, no owner, notifications disabled, autodraft enabled, no league), single vs back-to-back subject selection, draft room link presence, error logging without throwing, and parallel query execution https://claude.ai/code/session_01LMGxgYvtE3CF8Jf3u2pXgA --------- Co-authored-by: Claude <noreply@anthropic.com>
2026-05-20 15:35:43 -07:00
checked={optimisticDiscordEnabled}
onCheckedChange={handleDiscordToggle}
disabled={discordFetcher.state !== "idle"}
Add opt-in Discord pings to standings notifications (#429) * Add opt-in Discord ping to standings notifications Users who have linked their Discord account can enable a ping preference in Settings > Notifications. When enabled, their bracket username is replaced with a Discord @mention (<@userId>) in league standings update messages, sending them a push notification and showing their Discord display name. Adds discordPingEnabled column to users, a findDiscordIdsByUserIds model helper, allowed_mentions support to the Discord webhook payload, and a NotificationsSection settings component. https://claude.ai/code/session_01NUv93WRrufHhpZSMyY4bjs * Remove Display Name field from user profile settings Username is the only user-facing name field. The display_name column remains in the database since BetterAuth writes the OAuth provider name there, and it serves as a programmatic fallback via getUserDisplayName. https://claude.ai/code/session_01NUv93WRrufHhpZSMyY4bjs * Address code review feedback on Discord ping feature - Fix broken Account settings link in NotificationsSection: replace the non-functional ?section=account href with an onNavigateToAccount callback that drives the parent's useState-based section switcher - Replace hand-rolled toggle button with the project's Switch component (Radix UI) for consistent sizing, accessibility, and dark-mode support - Guard update-discord-ping action: return an error if the user attempts to enable pings without a Discord account linked - Surface action error in NotificationsSection UI - Cap allowed_mentions.users at 100 to avoid Discord rejecting the payload in large leagues - Remove the showWinner alias in scoring-calculator; inline winnerScoreChanged - Add comment to league settings test notification explaining why discordUserId is omitted - Clarify database schema comment: displayName is write-only from BetterAuth https://claude.ai/code/session_01NUv93WRrufHhpZSMyY4bjs --------- Co-authored-by: Claude <noreply@anthropic.com>
2026-05-15 10:06:54 -07:00
/>
</div>
Add draft email notifications feature (#459) * Add on-the-clock email notifications and fix push notification modal - Add `draft_email_notifications_enabled` column to users table - New `sendOnTheClockEmail` service sends an email when a user's turn arrives in a draft; detects back-to-back picks and sends one combined email instead of two; skips notification if the team has autodraft enabled - Email is triggered after every manual pick (make-pick route) and every timer-expiry pick (executeAutoPick) once the full autodraft chain settles, so the notified user is always the first human on the clock - Add email notification toggle to the user settings Notifications section - Fix push notification dropdown in the draft room: the bare absolute div is replaced with a Radix Popover so clicking outside dismisses it without toggling notifications off - Rename draft room label from "Pick Notifications" to "Push Notifications" to distinguish from the new email notifications https://claude.ai/code/session_01LMGxgYvtE3CF8Jf3u2pXgA * Address code review feedback on email notification feature - sendOnTheClockEmail now fetches season (and currentPickNumber) internally, removing the blocking postChainSeason pre-fetch from both make-pick.ts and executeAutoPick; callers are now true fire-and-forget with no IIFE needed - Parallel Promise.all for team, autodraftSettings, and league queries reduces sequential DB round-trips from 5 to 3 - Remove team.ownerId type cast; assign to local after the null guard - Combine the two calculatePickInfo calls for the same pick into one - Fix popoverOpen/enabled divergence: remove the {enabled && ...} guard on PopoverContent so popoverOpen is the sole visibility control - Unify NotificationsSection feedback pattern: both Discord and email sections now read success/error directly from their respective fetcher data via a shared feedbackFromFetcher helper; removes the success/error props and the verbose cast that was used for email - Add 13 unit tests for sendOnTheClockEmail covering: early-exit paths (no season, past totalPicks, no owner, notifications disabled, autodraft enabled, no league), single vs back-to-back subject selection, draft room link presence, error logging without throwing, and parallel query execution https://claude.ai/code/session_01LMGxgYvtE3CF8Jf3u2pXgA --------- Co-authored-by: Claude <noreply@anthropic.com>
2026-05-20 15:35:43 -07:00
{discordSuccess && (
Add opt-in Discord pings to standings notifications (#429) * Add opt-in Discord ping to standings notifications Users who have linked their Discord account can enable a ping preference in Settings > Notifications. When enabled, their bracket username is replaced with a Discord @mention (<@userId>) in league standings update messages, sending them a push notification and showing their Discord display name. Adds discordPingEnabled column to users, a findDiscordIdsByUserIds model helper, allowed_mentions support to the Discord webhook payload, and a NotificationsSection settings component. https://claude.ai/code/session_01NUv93WRrufHhpZSMyY4bjs * Remove Display Name field from user profile settings Username is the only user-facing name field. The display_name column remains in the database since BetterAuth writes the OAuth provider name there, and it serves as a programmatic fallback via getUserDisplayName. https://claude.ai/code/session_01NUv93WRrufHhpZSMyY4bjs * Address code review feedback on Discord ping feature - Fix broken Account settings link in NotificationsSection: replace the non-functional ?section=account href with an onNavigateToAccount callback that drives the parent's useState-based section switcher - Replace hand-rolled toggle button with the project's Switch component (Radix UI) for consistent sizing, accessibility, and dark-mode support - Guard update-discord-ping action: return an error if the user attempts to enable pings without a Discord account linked - Surface action error in NotificationsSection UI - Cap allowed_mentions.users at 100 to avoid Discord rejecting the payload in large leagues - Remove the showWinner alias in scoring-calculator; inline winnerScoreChanged - Add comment to league settings test notification explaining why discordUserId is omitted - Clarify database schema comment: displayName is write-only from BetterAuth https://claude.ai/code/session_01NUv93WRrufHhpZSMyY4bjs --------- Co-authored-by: Claude <noreply@anthropic.com>
2026-05-15 10:06:54 -07:00
<p className="text-sm text-green-600">Notification preference saved.</p>
)}
Add draft email notifications feature (#459) * Add on-the-clock email notifications and fix push notification modal - Add `draft_email_notifications_enabled` column to users table - New `sendOnTheClockEmail` service sends an email when a user's turn arrives in a draft; detects back-to-back picks and sends one combined email instead of two; skips notification if the team has autodraft enabled - Email is triggered after every manual pick (make-pick route) and every timer-expiry pick (executeAutoPick) once the full autodraft chain settles, so the notified user is always the first human on the clock - Add email notification toggle to the user settings Notifications section - Fix push notification dropdown in the draft room: the bare absolute div is replaced with a Radix Popover so clicking outside dismisses it without toggling notifications off - Rename draft room label from "Pick Notifications" to "Push Notifications" to distinguish from the new email notifications https://claude.ai/code/session_01LMGxgYvtE3CF8Jf3u2pXgA * Address code review feedback on email notification feature - sendOnTheClockEmail now fetches season (and currentPickNumber) internally, removing the blocking postChainSeason pre-fetch from both make-pick.ts and executeAutoPick; callers are now true fire-and-forget with no IIFE needed - Parallel Promise.all for team, autodraftSettings, and league queries reduces sequential DB round-trips from 5 to 3 - Remove team.ownerId type cast; assign to local after the null guard - Combine the two calculatePickInfo calls for the same pick into one - Fix popoverOpen/enabled divergence: remove the {enabled && ...} guard on PopoverContent so popoverOpen is the sole visibility control - Unify NotificationsSection feedback pattern: both Discord and email sections now read success/error directly from their respective fetcher data via a shared feedbackFromFetcher helper; removes the success/error props and the verbose cast that was used for email - Add 13 unit tests for sendOnTheClockEmail covering: early-exit paths (no season, past totalPicks, no owner, notifications disabled, autodraft enabled, no league), single vs back-to-back subject selection, draft room link presence, error logging without throwing, and parallel query execution https://claude.ai/code/session_01LMGxgYvtE3CF8Jf3u2pXgA --------- Co-authored-by: Claude <noreply@anthropic.com>
2026-05-20 15:35:43 -07:00
{discordError && (
<p className="text-sm text-destructive">{discordError}</p>
Add opt-in Discord pings to standings notifications (#429) * Add opt-in Discord ping to standings notifications Users who have linked their Discord account can enable a ping preference in Settings > Notifications. When enabled, their bracket username is replaced with a Discord @mention (<@userId>) in league standings update messages, sending them a push notification and showing their Discord display name. Adds discordPingEnabled column to users, a findDiscordIdsByUserIds model helper, allowed_mentions support to the Discord webhook payload, and a NotificationsSection settings component. https://claude.ai/code/session_01NUv93WRrufHhpZSMyY4bjs * Remove Display Name field from user profile settings Username is the only user-facing name field. The display_name column remains in the database since BetterAuth writes the OAuth provider name there, and it serves as a programmatic fallback via getUserDisplayName. https://claude.ai/code/session_01NUv93WRrufHhpZSMyY4bjs * Address code review feedback on Discord ping feature - Fix broken Account settings link in NotificationsSection: replace the non-functional ?section=account href with an onNavigateToAccount callback that drives the parent's useState-based section switcher - Replace hand-rolled toggle button with the project's Switch component (Radix UI) for consistent sizing, accessibility, and dark-mode support - Guard update-discord-ping action: return an error if the user attempts to enable pings without a Discord account linked - Surface action error in NotificationsSection UI - Cap allowed_mentions.users at 100 to avoid Discord rejecting the payload in large leagues - Remove the showWinner alias in scoring-calculator; inline winnerScoreChanged - Add comment to league settings test notification explaining why discordUserId is omitted - Clarify database schema comment: displayName is write-only from BetterAuth https://claude.ai/code/session_01NUv93WRrufHhpZSMyY4bjs --------- Co-authored-by: Claude <noreply@anthropic.com>
2026-05-15 10:06:54 -07:00
)}
</>
)}
</div>
</div>
);
}