* 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>
26 lines
968 B
TypeScript
26 lines
968 B
TypeScript
import { eq, inArray, and } from "drizzle-orm";
|
|
import { database } from "~/database/context";
|
|
import * as schema from "~/database/schema";
|
|
|
|
export type LinkedAccount = {
|
|
id: string;
|
|
providerId: string;
|
|
};
|
|
|
|
export async function findLinkedAccountsByUserId(userId: string): Promise<LinkedAccount[]> {
|
|
const db = database();
|
|
return db
|
|
.select({ id: schema.accounts.id, providerId: schema.accounts.providerId })
|
|
.from(schema.accounts)
|
|
.where(eq(schema.accounts.userId, userId));
|
|
}
|
|
|
|
export async function findDiscordIdsByUserIds(userIds: string[]): Promise<Map<string, string>> {
|
|
if (userIds.length === 0) return new Map();
|
|
const db = database();
|
|
const rows = await db
|
|
.select({ userId: schema.accounts.userId, accountId: schema.accounts.accountId })
|
|
.from(schema.accounts)
|
|
.where(and(inArray(schema.accounts.userId, userIds), eq(schema.accounts.providerId, "discord")));
|
|
return new Map(rows.map((r) => [r.userId, r.accountId]));
|
|
}
|