diff --git a/app/features/chat/ChatProvider.tsx b/app/features/chat/ChatProvider.tsx index 63f5993a8..90ff90066 100644 --- a/app/features/chat/ChatProvider.tsx +++ b/app/features/chat/ChatProvider.tsx @@ -556,55 +556,71 @@ function useChatRouteSync({ React.SetStateAction> >; }) { - const chatCode = useCurrentRouteChatCode(); + const rawChatCode = useCurrentRouteChatCode(); + const chatCodesKey = rawChatCode + ? Array.isArray(rawChatCode) + ? rawChatCode.join(",") + : rawChatCode + : ""; const { pathname } = useLocation(); const layoutSize = useLayoutSize(); - const subscribedRoomRef = React.useRef(null); - const previousRouteChatCodeRef = React.useRef(null); + const subscribedRoomRef = React.useRef([]); + const previousRouteChatCodeRef = React.useRef([]); const previousPathnameRef = React.useRef(null); React.useEffect(() => { if (isLoading) return; - const previousSubscribed = subscribedRoomRef.current; + const chatCodes = chatCodesKey ? chatCodesKey.split(",") : []; - // Clean up previous non-participant subscription if chatCode changed - if (previousSubscribed && previousSubscribed !== chatCode) { - unsubscribe(previousSubscribed); - setRooms((prev) => prev.filter((r) => r.chatCode !== previousSubscribed)); + // Clean up subscriptions for rooms no longer in chatCodes + const previousSubscribed = subscribedRoomRef.current; + const removedRooms = previousSubscribed.filter( + (code) => !chatCodes.includes(code), + ); + for (const code of removedRooms) { + unsubscribe(code); + setRooms((prev) => prev.filter((r) => r.chatCode !== code)); setMessagesByRoom((prev) => { - const { [previousSubscribed]: _, ...rest } = prev; + const { [code]: _, ...rest } = prev; return rest; }); - if (activeRoom === previousSubscribed) { + if (activeRoom === code) { setActiveRoom(null); setChatOpen(false); } - subscribedRoomRef.current = null; + } + if (removedRooms.length > 0) { + subscribedRoomRef.current = previousSubscribed.filter((code) => + chatCodes.includes(code), + ); } - if (chatCode) { - const room = rooms.find((r) => r.chatCode === chatCode); - const isParticipant = room?.participantUserIds.includes(userId); + if (chatCodes.length > 0) { + for (const code of chatCodes) { + const alreadyInRooms = rooms.some((r) => r.chatCode === code); - if (!isParticipant && subscribedRoomRef.current !== chatCode) { - logger.debug("Subscribing to non-participant room:", chatCode); - subscribe(chatCode); - subscribedRoomRef.current = chatCode; + if (!alreadyInRooms && !subscribedRoomRef.current.includes(code)) { + logger.debug("Subscribing to non-participant room:", code); + subscribe(code); + subscribedRoomRef.current = [...subscribedRoomRef.current, code]; + } } + const previousCodes = previousRouteChatCodeRef.current; const routeChatCodeChanged = - previousRouteChatCodeRef.current !== chatCode; - previousRouteChatCodeRef.current = chatCode; + chatCodes.length !== previousCodes.length || + chatCodes.some((code, i) => previousCodes[i] !== code); + previousRouteChatCodeRef.current = chatCodes; if (routeChatCodeChanged) { - setActiveRoom(chatCode); + setActiveRoom(chatCodes[0]); if (layoutSize === "desktop") { setChatOpen(true); } } } else { - previousRouteChatCodeRef.current = null; + previousRouteChatCodeRef.current = []; const pathnameChanged = previousPathnameRef.current !== pathname; previousPathnameRef.current = pathname; @@ -624,7 +640,7 @@ function useChatRouteSync({ } }, [ isLoading, - chatCode, + chatCodesKey, pathname, rooms, userId, @@ -639,11 +655,13 @@ function useChatRouteSync({ ]); } -function useCurrentRouteChatCode() { +function useCurrentRouteChatCode(): string | string[] | null { const matches = useMatches(); for (const match of matches) { - const matchData = match.data as { chatCode?: string } | undefined; + const matchData = match.data as + | { chatCode?: string | string[] } + | undefined; if (matchData?.chatCode) { return matchData.chatCode; } diff --git a/app/features/sendouq-match/loaders/q.match.$id.server.ts b/app/features/sendouq-match/loaders/q.match.$id.server.ts index 46f6347db..5482bce0c 100644 --- a/app/features/sendouq-match/loaders/q.match.$id.server.ts +++ b/app/features/sendouq-match/loaders/q.match.$id.server.ts @@ -45,15 +45,30 @@ export const loader = async ({ params }: LoaderFunctionArgs) => { }) : null, rawReportedWeapons, - chatCode: - (user?.roles.includes("STAFF") || - (user && matchUsers.includes(user.id))) && - chatAccessible({ - isStaff: user?.roles.includes("STAFF") ?? false, + chatCode: (() => { + const isStaff = user?.roles.includes("STAFF") ?? false; + const isParticipant = user && matchUsers.includes(user.id); + + if (!(isStaff || isParticipant)) return null; + + const accessible = chatAccessible({ + isStaff, expiresAfterDays: 1, comparedTo: databaseTimestampToDate(matchUnmapped.createdAt), - }) - ? match.chatCode - : null, + }); + if (!accessible) return null; + + if (!isParticipant) return match.chatCode ?? null; + + const codes = [ + match.chatCode, + match.groupAlpha.chatCode, + match.groupBravo.chatCode, + ].filter((c): c is string => Boolean(c)); + + if (codes.length === 0) return null; + if (codes.length === 1) return codes[0]; + return codes; + })(), }; }; diff --git a/app/features/sendouq/actions/q.preparing.server.ts b/app/features/sendouq/actions/q.preparing.server.ts index ec732d6e5..a7c4a1368 100644 --- a/app/features/sendouq/actions/q.preparing.server.ts +++ b/app/features/sendouq/actions/q.preparing.server.ts @@ -9,6 +9,7 @@ import { assertUnreachable } from "~/utils/types"; import { SENDOUQ_LOOKING_PAGE } from "~/utils/urls"; import { refreshSendouQInstance, SendouQ } from "../core/SendouQ.server"; import { preparingSchema } from "../q-schemas.server"; +import { setGroupChatMetadata } from "../q-utils.server"; export type SendouQPreparingAction = typeof action; @@ -58,6 +59,14 @@ export const action = async ({ request }: ActionFunctionArgs) => { await refreshSendouQInstance(); + const updatedGroup = SendouQ.findOwnGroup(user.id); + if (updatedGroup?.chatCode) { + setGroupChatMetadata({ + chatCode: updatedGroup.chatCode, + members: updatedGroup.members, + }); + } + notify({ userIds: [data.id], notification: {