Chat fixes

This commit is contained in:
Kalle 2026-03-11 20:59:41 +02:00
parent b063b9300a
commit d884cb3f3b
5 changed files with 70 additions and 28 deletions

View File

@ -4,7 +4,11 @@ import * as React from "react";
import { useMatches, useRevalidator } from "react-router"; import { useMatches, useRevalidator } from "react-router";
import { logger } from "~/utils/logger"; import { logger } from "~/utils/logger";
import { soundPath } from "~/utils/urls"; import { soundPath } from "~/utils/urls";
import type { RoomInfo, ServerRoomInfo } from "./chat-provider-types"; import type {
RoomInfo,
RoomMetadata,
ServerRoomInfo,
} from "./chat-provider-types";
import type { ChatMessage, ChatUser } from "./chat-types"; import type { ChatMessage, ChatUser } from "./chat-types";
import { messageTypeToSound, soundEnabled, soundVolume } from "./chat-utils"; import { messageTypeToSound, soundEnabled, soundVolume } from "./chat-utils";
import { ChatContext } from "./useChatContext"; import { ChatContext } from "./useChatContext";
@ -161,7 +165,7 @@ function ChatProviderInner({
return; return;
} }
// CHAT_HISTORY response // CHAT_HISTORY response (also returned by SUBSCRIBE with metadata)
if (parsed.event === "CHAT_HISTORY" && Array.isArray(parsed.messages)) { if (parsed.event === "CHAT_HISTORY" && Array.isArray(parsed.messages)) {
const chatCode = parsed.chatCode as string; const chatCode = parsed.chatCode as string;
const messages = parsed.messages as ChatMessage[]; const messages = parsed.messages as ChatMessage[];
@ -169,6 +173,30 @@ function ChatProviderInner({
...prev, ...prev,
[chatCode]: messages, [chatCode]: messages,
})); }));
if (parsed.metadata) {
const metadata = parsed.metadata as RoomMetadata;
const newRoom: RoomInfo = {
chatCode,
header: metadata.header,
subtitle: metadata.subtitle ?? "",
url: metadata.url ?? "",
participantUserIds: metadata.participantUserIds,
expiresAt: metadata.expiresAt,
lastMessageTimestamp: messages.at(-1)?.timestamp ?? 0,
totalMessageCount: messages.length,
};
setRooms((prev) => {
const exists = prev.some((r) => r.chatCode === chatCode);
if (exists) return prev;
return [...prev, newRoom];
});
if (metadata.chatUsers) {
setChatUsersCache((prev) => ({ ...prev, ...metadata.chatUsers }));
}
}
return; return;
} }
@ -379,6 +407,8 @@ function ChatProviderInner({
setSidebarOpen, setSidebarOpen,
subscribe, subscribe,
unsubscribe, unsubscribe,
setRooms,
setMessagesByRoom,
}); });
const contextValue = React.useMemo( const contextValue = React.useMemo(
@ -422,6 +452,9 @@ function ChatProviderInner({
); );
} }
// xxx: bug: when viewing as non-participant can't go back to list view
// xxx: bug: room should close automatically if non-participant and leaves the route
// xxx: bug: route loading state should show before room metadata loads
function useChatRouteSync({ function useChatRouteSync({
rooms, rooms,
userId, userId,
@ -429,6 +462,8 @@ function useChatRouteSync({
setSidebarOpen, setSidebarOpen,
subscribe, subscribe,
unsubscribe, unsubscribe,
setRooms,
setMessagesByRoom,
}: { }: {
rooms: RoomInfo[]; rooms: RoomInfo[];
userId: number; userId: number;
@ -436,6 +471,10 @@ function useChatRouteSync({
setSidebarOpen: (open: boolean) => void; setSidebarOpen: (open: boolean) => void;
subscribe: (chatCode: string) => void; subscribe: (chatCode: string) => void;
unsubscribe: (chatCode: string) => void; unsubscribe: (chatCode: string) => void;
setRooms: React.Dispatch<React.SetStateAction<RoomInfo[]>>;
setMessagesByRoom: React.Dispatch<
React.SetStateAction<Record<string, ChatMessage[]>>
>;
}) { }) {
const matches = useMatches(); const matches = useMatches();
const subscribedRoomRef = React.useRef<string | null>(null); const subscribedRoomRef = React.useRef<string | null>(null);
@ -451,38 +490,31 @@ function useChatRouteSync({
} }
} }
if (!chatCode) { const previousSubscribed = subscribedRoomRef.current;
// Leaving a chat route: unsubscribe from staff-subscribed rooms
if (subscribedRoomRef.current) { // Clean up previous non-participant subscription if chatCode changed
unsubscribe(subscribedRoomRef.current); if (previousSubscribed && previousSubscribed !== chatCode) {
subscribedRoomRef.current = null; unsubscribe(previousSubscribed);
} setRooms((prev) => prev.filter((r) => r.chatCode !== previousSubscribed));
return; setMessagesByRoom((prev) => {
const { [previousSubscribed]: _, ...rest } = prev;
return rest;
});
subscribedRoomRef.current = null;
} }
if (!chatCode) return;
const room = rooms.find((r) => r.chatCode === chatCode); const room = rooms.find((r) => r.chatCode === chatCode);
const isParticipant = room?.participantUserIds.includes(userId);
if (room) { if (!isParticipant && subscribedRoomRef.current !== chatCode) {
const isParticipant = room.participantUserIds.includes(userId); subscribe(chatCode);
subscribedRoomRef.current = chatCode;
if (isParticipant) {
setActiveRoom(chatCode);
setSidebarOpen(true);
} else {
// Staff/TO viewing: subscribe temporarily
subscribe(chatCode);
subscribedRoomRef.current = chatCode;
setActiveRoom(chatCode);
setSidebarOpen(true);
}
} }
return () => { setActiveRoom(chatCode);
if (subscribedRoomRef.current) { setSidebarOpen(true);
unsubscribe(subscribedRoomRef.current);
subscribedRoomRef.current = null;
}
};
}, [ }, [
matches, matches,
rooms, rooms,
@ -491,5 +523,7 @@ function useChatRouteSync({
setSidebarOpen, setSidebarOpen,
subscribe, subscribe,
unsubscribe, unsubscribe,
setRooms,
setMessagesByRoom,
]); ]);
} }

View File

@ -45,6 +45,8 @@ export const loader = async ({ params }: LoaderFunctionArgs) => {
return { return {
post, post,
// xxx: only chatCode if permissions
chatCode: post.chatCode,
anyUserPrefersNoScreen: anyUserPrefersNoScreen:
await UserRepository.anyUserPrefersNoScreen(participantIds), await UserRepository.anyUserPrefersNoScreen(participantIds),
tournamentMapPool: post.mapsTournament tournamentMapPool: post.mapsTournament

View File

@ -67,5 +67,7 @@ export const loader = async ({ params }: LoaderFunctionArgs) => {
}) })
: null, : null,
rawReportedWeapons, rawReportedWeapons,
// xxx: only chatCode if permissions
chatCode: match.chatCode,
}; };
}; };

View File

@ -57,6 +57,8 @@ export const loader = async ({ request }: LoaderFunctionArgs) => {
? [] ? []
: groups, : groups,
ownGroup, ownGroup,
// xxx: only chatCode if permissions
chatCode: ownGroup?.chatCode,
likes: ownGroup likes: ownGroup
? await SQGroupRepository.allLikesByGroupId(ownGroup.id) ? await SQGroupRepository.allLikesByGroupId(ownGroup.id)
: { : {

View File

@ -119,5 +119,7 @@ export const loader = async ({ params }: LoaderFunctionArgs) => {
matchIsOver, matchIsOver,
endedEarly, endedEarly,
noScreen, noScreen,
// xxx: only chatCode if permissions
chatCode: match.chatCode,
}; };
}; };