import { Room } from 'matrix-js-sdk'; import { useCallback, useEffect, useMemo, useState } from 'react'; import { AccountDataEvent } from '../../types/matrix/accountData'; import { StateEvent } from '../../types/matrix/room'; import { getGlobalSoundboardPacks, getRoomSoundboardPack, getRoomSoundboardPacks, getUserSoundboardPack, SoundboardPack, } from '../plugins/soundboard'; import { useMatrixClient } from './useMatrixClient'; import { useAccountDataCallback } from './useAccountDataCallback'; import { useStateEventCallback } from './useStateEventCallback'; // Parallels hooks/useImagePacks.ts (custom emoji). Same aggregation shape. export const useUserSoundboardPack = (): SoundboardPack | undefined => { const mx = useMatrixClient(); const [userPack, setUserPack] = useState(() => getUserSoundboardPack(mx)); useAccountDataCallback( mx, useCallback( (mEvent) => { if (mEvent.getType() === AccountDataEvent.LotusSoundboard) { setUserPack(getUserSoundboardPack(mx)); } }, [mx], ), ); return userPack; }; export const useGlobalSoundboardPacks = (): SoundboardPack[] => { const mx = useMatrixClient(); const [globalPacks, setGlobalPacks] = useState(() => getGlobalSoundboardPacks(mx)); useAccountDataCallback( mx, useCallback( (mEvent) => { if (mEvent.getType() === AccountDataEvent.LotusSoundboardRooms) { setGlobalPacks(getGlobalSoundboardPacks(mx)); } }, [mx], ), ); useStateEventCallback( mx, useCallback( (mEvent) => { const roomId = mEvent.getRoomId(); const stateKey = mEvent.getStateKey(); if ( mEvent.getType() === StateEvent.LotusSoundboardRoom && roomId && typeof stateKey === 'string' ) { const isGlobal = !!globalPacks.find( (pack) => pack.address && pack.address.roomId === roomId && pack.address.stateKey === stateKey, ); if (isGlobal) setGlobalPacks(getGlobalSoundboardPacks(mx)); } }, [mx, globalPacks], ), ); return globalPacks; }; export const useRoomSoundboardPack = (room: Room, stateKey: string): SoundboardPack | undefined => { const mx = useMatrixClient(); const [roomPack, setRoomPack] = useState(() => getRoomSoundboardPack(room, stateKey)); // Resync when the room/stateKey arg changes — the useState initializer only // runs on mount, so without this a re-target (e.g. a different room) kept // showing the first room's pack. useEffect(() => { setRoomPack(getRoomSoundboardPack(room, stateKey)); }, [room, stateKey]); useStateEventCallback( mx, useCallback( (mEvent) => { if ( mEvent.getRoomId() === room.roomId && mEvent.getType() === StateEvent.LotusSoundboardRoom && mEvent.getStateKey() === stateKey ) { setRoomPack(getRoomSoundboardPack(room, stateKey)); } }, [room, stateKey], ), ); return roomPack; }; export const useRoomSoundboardPacks = (room: Room): SoundboardPack[] => { const mx = useMatrixClient(); const [roomPacks, setRoomPacks] = useState(() => getRoomSoundboardPacks(room)); // Resync on room change (see useRoomSoundboardPack). useEffect(() => { setRoomPacks(getRoomSoundboardPacks(room)); }, [room]); useStateEventCallback( mx, useCallback( (mEvent) => { if ( mEvent.getRoomId() === room.roomId && mEvent.getType() === StateEvent.LotusSoundboardRoom ) { setRoomPacks(getRoomSoundboardPacks(room)); } }, [room], ), ); return roomPacks; }; export const useRoomsSoundboardPacks = (rooms: Room[]): SoundboardPack[] => { const mx = useMatrixClient(); const [roomPacks, setRoomPacks] = useState(() => rooms.flatMap(getRoomSoundboardPacks)); // Resync when the room list changes (callers pass a memoized array, so this // fires only on an actual change — see useRoomSoundboardPack). useEffect(() => { setRoomPacks(rooms.flatMap(getRoomSoundboardPacks)); }, [rooms]); useStateEventCallback( mx, useCallback( (mEvent) => { if ( rooms.find((room) => room.roomId === mEvent.getRoomId()) && mEvent.getType() === StateEvent.LotusSoundboardRoom ) { setRoomPacks(rooms.flatMap(getRoomSoundboardPacks)); } }, [rooms], ), ); return roomPacks; }; /** User ∪ global ∪ room packs, deduped by id, keeping only packs with clips. */ export const useRelevantSoundboardPacks = (rooms: Room[]): SoundboardPack[] => { const userPack = useUserSoundboardPack(); const globalPacks = useGlobalSoundboardPacks(); const roomsPacks = useRoomsSoundboardPacks(rooms); return useMemo(() => { const packs = userPack ? [userPack] : []; const globalPackIds = new Set(globalPacks.map((pack) => pack.id)); const relPacks = packs.concat( globalPacks, roomsPacks.filter((pack) => !globalPackIds.has(pack.id)), ); return relPacks.filter((pack) => pack.getClips().length > 0); }, [userPack, globalPacks, roomsPacks]); };