34 lines
1.6 KiB
TypeScript
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;
|
|
};
|