From 0aa31c0683bcb9b3e41b0793acb8a7f29897954b Mon Sep 17 00:00:00 2001 From: Kalle <38327916+Sendouc@users.noreply.github.com> Date: Mon, 23 Mar 2026 21:04:49 +0200 Subject: [PATCH] Add TO/Stream labels back to tournament chats --- app/components/layout/ChatSidebar.tsx | 13 ++++++- app/features/chat/ChatProvider.tsx | 9 +++++ app/features/chat/chat-provider-types.ts | 3 ++ app/features/tournament/routes/to.$id.tsx | 41 +++++++++++++++++++++++ 4 files changed, 65 insertions(+), 1 deletion(-) diff --git a/app/components/layout/ChatSidebar.tsx b/app/components/layout/ChatSidebar.tsx index d4a1429f9..68feb5b8c 100644 --- a/app/components/layout/ChatSidebar.tsx +++ b/app/components/layout/ChatSidebar.tsx @@ -156,6 +156,17 @@ function ChatView({ onClose }: { onClose?: () => void }) { const roomExpired = Boolean(room?.expiresAt && room.expiresAt < Date.now()); const messages = chatContext.messagesForRoom(activeRoom); + const participantIds = new Set(room?.participantUserIds ?? []); + const usersWithLabels = { ...chatContext.chatUsers }; + for (const [userIdStr, label] of Object.entries(chatContext.chatLabels)) { + const userId = Number(userIdStr); + if (participantIds.has(userId)) continue; + const existing = usersWithLabels[userId]; + if (existing) { + usersWithLabels[userId] = { ...existing, title: label }; + } + } + const chatAdapter = { messages, send: (contents: string) => chatContext.send(activeRoom, contents), @@ -229,7 +240,7 @@ function ChatView({ onClose }: { onClose?: () => void }) {
>({}); + const [chatLabels, setChatLabels] = React.useState>( + {}, + ); + const clearChatLabels = React.useCallback(() => setChatLabels({}), []); const ws = React.useRef(undefined); @@ -508,6 +512,9 @@ function ChatProviderInner({ setChatOpen, activeRoom, setActiveRoom, + chatLabels, + setChatLabels, + clearChatLabels, }), [ isLoading, @@ -525,6 +532,8 @@ function ChatProviderInner({ chatOpen, activeRoom, setChatOpen, + chatLabels, + clearChatLabels, ], ); diff --git a/app/features/chat/chat-provider-types.ts b/app/features/chat/chat-provider-types.ts index 59c3a5cd4..4d1162278 100644 --- a/app/features/chat/chat-provider-types.ts +++ b/app/features/chat/chat-provider-types.ts @@ -52,4 +52,7 @@ export interface ChatContextValue { setChatOpen: (open: boolean) => void; activeRoom: string | null; setActiveRoom: (chatCode: string | null) => void; + chatLabels: Record; + setChatLabels: (labels: Record) => void; + clearChatLabels: () => void; } diff --git a/app/features/tournament/routes/to.$id.tsx b/app/features/tournament/routes/to.$id.tsx index 8441bbdd1..de987ccd1 100644 --- a/app/features/tournament/routes/to.$id.tsx +++ b/app/features/tournament/routes/to.$id.tsx @@ -12,6 +12,7 @@ import { Placeholder } from "~/components/Placeholder"; import { SubNav, SubNavLink } from "~/components/SubNav"; import { DANGEROUS_CAN_ACCESS_DEV_CONTROLS } from "~/features/admin/core/dev-controls"; import { useUser } from "~/features/auth/core/user"; +import { useChatContext } from "~/features/chat/useChatContext"; import { Tournament } from "~/features/tournament-bracket/core/Tournament"; import { useIsMounted } from "~/hooks/useIsMounted"; import type { SendouRouteHandle } from "~/utils/remix.server"; @@ -111,6 +112,8 @@ export function TournamentLayout() { ); const [bracketExpanded, setBracketExpanded] = React.useState(true); + useTournamentChatLabels(tournament); + // this is nice to debug with tournament in browser console if (process.env.NODE_ENV === "development") { // biome-ignore lint/correctness/useHookAtTopLevel: process.env.NODE_ENV is a constant @@ -240,3 +243,41 @@ export function useTournamentFriendCodes() { export function useTournamentPreparedMaps() { return useOutletContext().preparedMaps; } + +function useTournamentChatLabels(tournament: Tournament) { + const chatContext = useChatContext(); + const setChatLabels = chatContext?.setChatLabels; + const clearChatLabels = chatContext?.clearChatLabels; + + React.useEffect(() => { + if (!setChatLabels || !clearChatLabels) return; + + const labels: Record = {}; + + labels[tournament.ctx.author.id] = "TO"; + + for (const staff of tournament.ctx.staff) { + if (staff.role === "ORGANIZER") { + labels[staff.id] = "TO"; + } else if (staff.role === "STREAMER") { + labels[staff.id] = "Stream"; + } + } + + if (tournament.ctx.organization) { + for (const member of tournament.ctx.organization.members) { + if (["ADMIN", "ORGANIZER"].includes(member.role)) { + labels[member.userId] = "TO"; + } else if (member.role === "STREAMER") { + labels[member.userId] = "Stream"; + } + } + } + + setChatLabels(labels); + + return () => { + clearChatLabels(); + }; + }, [setChatLabels, clearChatLabels, tournament]); +}