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]);
+}