import { atom, useSetAtom } from 'jotai'; import { selectAtom } from 'jotai/utils'; import { MatrixClient, RoomMember, RoomMemberEvent, RoomMemberEventHandlerMap, } from 'matrix-js-sdk'; import { useEffect } from 'react'; export type IRoomIdToTypingMembers = Map; export type IRoomIdToTypingMembersAction = | { type: 'PUT'; roomId: string; member: RoomMember; } | { type: 'DELETE'; roomId: string; member: RoomMember; }; const baseRoomIdToTypingMembersAtom = atom(new Map()); export const roomIdToTypingMembersAtom = atom( (get) => get(baseRoomIdToTypingMembersAtom), (get, set, action) => { const roomIdToTypingMembers = get(baseRoomIdToTypingMembersAtom); let typingMembers = roomIdToTypingMembers.get(action.roomId) ?? []; typingMembers = typingMembers.filter((member) => member.userId !== action.member.userId); if (action.type === 'PUT') { typingMembers = [...typingMembers, action.member]; } roomIdToTypingMembers.set(action.roomId, typingMembers); set(baseRoomIdToTypingMembersAtom, new Map([...roomIdToTypingMembers])); } ); export const useBindRoomIdToTypingMembersAtom = ( mx: MatrixClient, typingMembersAtom: typeof roomIdToTypingMembersAtom ) => { const setTypingMembers = useSetAtom(typingMembersAtom); useEffect(() => { const handleTypingEvent: RoomMemberEventHandlerMap[RoomMemberEvent.Typing] = ( event, member ) => { setTypingMembers({ type: member.typing ? 'PUT' : 'DELETE', roomId: member.roomId, member, }); }; mx.on(RoomMemberEvent.Typing, handleTypingEvent); return () => { mx.removeListener(RoomMemberEvent.Typing, handleTypingEvent); }; }, [mx, setTypingMembers]); }; export const selectRoomTypingMembersAtom = ( roomId: string, typingMembersAtom: typeof roomIdToTypingMembersAtom ) => selectAtom(typingMembersAtom, (atoms) => atoms.get(roomId) ?? []);