import { useEffect, useState } from 'react'; import { Room, RoomStateEvent } from 'matrix-js-sdk'; import { isOneOnOneRoom } from '../utils/room'; // Reactive «is this room strictly 2 members» — re-runs whenever a member event // lands so live demotions (1:1 → 3-person via invite) and promotions (peer // leaves → solo) flip the chrome immediately. Without the subscription, the // `IsOneOnOneProvider` value would freeze at mount-time for the active route // and `DmCallButton` / peer-avatar fallback / membership-sysline gate would // stay stale until the user navigates away. // // `RoomStateEvent.Members` is emitted on every `m.room.member` state event // (join, invite, leave, knock, ban, profile change). Profile-only updates are // no-ops here because they don't change `getInvitedAndJoinedMemberCount()`. export const useIsOneOnOneRoom = (room: Room): boolean => { const [value, setValue] = useState(() => isOneOnOneRoom(room)); useEffect(() => { // Capture `currentState` once per effect run so the cleanup detaches // from the same emitter we attached to. matrix-js-sdk replaces // `room.currentState` on a fresh `/sync` snapshot in rare cases (room // upgrade + late state catch-up); without the capture, the cleanup would // call removeListener on the new state and leak the old subscription. const state = room.currentState; setValue(isOneOnOneRoom(room)); const handler = () => setValue(isOneOnOneRoom(room)); state.on(RoomStateEvent.Members, handler); return () => { state.removeListener(RoomStateEvent.Members, handler); }; }, [room]); return value; };