vojo/src/app/hooks/useIsOneOnOneRoom.ts

34 lines
1.6 KiB
TypeScript

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<boolean>(() => 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;
};