create hero member events state to prevent frequent async loads

This commit is contained in:
Ajay Bura 2026-08-28 13:52:30 +05:30
commit ccc29ae166
27 changed files with 180 additions and 137 deletions

View file

@ -84,9 +84,9 @@ export function RoomMentionAutocomplete({
const mDirects = useAtomValue(mDirectAtom);
const rooms = useAtomValue(allRoomsAtom);
const [getRoomHeroes, roomToHeroMembersResult] = useRoomHeroes(rooms);
const getRoomName = useGetRoomName(getRoomHeroes, roomToHeroMembersResult.value);
const getRoomAvatar = useGetRoomAvatar(getRoomHeroes, roomToHeroMembersResult.value);
const getRoomHeroes = useRoomHeroes();
const getRoomName = useGetRoomName(getRoomHeroes);
const getRoomAvatar = useGetRoomAvatar(getRoomHeroes);
const allRooms = useMemo(
() => Array.from(rooms).sort(factoryRoomIdByAtoZ(getRoomName)),

View file

@ -379,8 +379,8 @@ export function EmojiBoard({
addToRecentEmoji = true,
}: EmojiBoardProps) {
const ct = useClient();
const [getRoomHeroes, roomToHeroMembersResult] = useRoomHeroes(imagePackRooms);
const getRoomName = useGetRoomName(getRoomHeroes, roomToHeroMembersResult.value);
const getRoomHeroes = useRoomHeroes();
const getRoomName = useGetRoomName(getRoomHeroes);
const emojiTab = tab === EmojiBoardTab.Emoji;
const usage = emojiTab ? ImageUsage.Emoticon : ImageUsage.Sticker;

View file

@ -230,7 +230,6 @@ type MutualRoomsData = {
directs: string[];
};
const EMPTY_LIST: string[] = [];
export function MutualRoomsChip({ userId }: { userId: string }) {
const ct = useClient();
const mutualRoomsState = useMutualRooms(userId);
@ -240,11 +239,9 @@ export function MutualRoomsChip({ userId }: { userId: string }) {
const directs = useDirectRooms();
const useAuthentication = useMediaAuthentication();
const [getRoomHeroes, roomToHeroMembersResult] = useRoomHeroes(
mutualRoomsState.status === AsyncStatus.Success ? mutualRoomsState.data : EMPTY_LIST
);
const getRoomName = useGetRoomName(getRoomHeroes, roomToHeroMembersResult.value);
const getRoomAvatar = useGetRoomAvatar(getRoomHeroes, roomToHeroMembersResult.value);
const getRoomHeroes = useRoomHeroes();
const getRoomName = useGetRoomName(getRoomHeroes);
const getRoomAvatar = useGetRoomAvatar(getRoomHeroes);
const [cords, setCords] = useState<RectCords>();

View file

@ -87,9 +87,9 @@ export function AddExistingModal({ parentId, space, requestClose }: AddExistingM
const roomIdToParents = useAtomValue(roomToParentsAtom);
const scrollRef = useRef<HTMLDivElement>(null);
const [getRoomHeroes, roomToHeroMembersResult] = useRoomHeroes(ct.rooms.join.read);
const getRoomName = useGetRoomName(getRoomHeroes, roomToHeroMembersResult.value);
const getRoomAvatar = useGetRoomAvatar(getRoomHeroes, roomToHeroMembersResult.value);
const getRoomHeroes = useRoomHeroes();
const getRoomName = useGetRoomName(getRoomHeroes);
const getRoomAvatar = useGetRoomAvatar(getRoomHeroes);
const [selected, setSelected] = useState<string[]>([]);

View file

@ -148,9 +148,9 @@ export function LobbyHeader({ showProfile, powerLevels }: LobbyHeaderProps) {
const [menuAnchor, setMenuAnchor] = useState<RectCords>();
const screenSize = useScreenSizeContext();
const [getRoomHeroes, roomToHeroMembersResult] = useRoomHeroes(spaceId);
const getRoomName = useGetRoomName(getRoomHeroes, roomToHeroMembersResult.value);
const getRoomAvatar = useGetRoomAvatar(getRoomHeroes, roomToHeroMembersResult.value);
const getRoomHeroes = useRoomHeroes();
const getRoomName = useGetRoomName(getRoomHeroes);
const getRoomAvatar = useGetRoomAvatar(getRoomHeroes);
const avatarMxc = getRoomAvatar(spaceId);
const avatarUrl = avatarMxc

View file

@ -22,9 +22,9 @@ export function LobbyHero() {
const useAuthentication = useMediaAuthentication();
const spaceId = useSpace();
const [getRoomHeroes, roomToHeroMembersResult] = useRoomHeroes(spaceId);
const getRoomName = useGetRoomName(getRoomHeroes, roomToHeroMembersResult.value);
const getRoomAvatar = useGetRoomAvatar(getRoomHeroes, roomToHeroMembersResult.value);
const getRoomHeroes = useRoomHeroes();
const getRoomName = useGetRoomName(getRoomHeroes);
const getRoomAvatar = useGetRoomAvatar(getRoomHeroes);
const topic = useRoomTopic(spaceId);
const avatarMxc = getRoomAvatar(spaceId);

View file

@ -191,9 +191,10 @@ type SpaceProfileProps = {
function SpaceProfile({ roomId, suggested, closed, categoryId, handleClose }: SpaceProfileProps) {
const ct = useClient();
const useAuthentication = useMediaAuthentication();
const [getRoomHeroes, roomToHeroMembersResult] = useRoomHeroes(roomId);
const getRoomName = useGetRoomName(getRoomHeroes, roomToHeroMembersResult.value);
const getRoomAvatar = useGetRoomAvatar(getRoomHeroes, roomToHeroMembersResult.value);
const getRoomHeroes = useRoomHeroes();
const getRoomName = useGetRoomName(getRoomHeroes);
const getRoomAvatar = useGetRoomAvatar(getRoomHeroes);
const name = getRoomName(roomId) ?? 'Unknown';
const avatar = getRoomAvatar(roomId);

View file

@ -191,11 +191,10 @@ export function MessageSearch({
}, [lastVItemIndex, lastGroupIndex, fetchNextPage, isFetchingNextPage, hasNextPage]);
const roomToTimeline = useObservableValue(ct.roomToTimeline);
const [getRoomHeroes, roomToHeroMembersResult] = useRoomHeroes(
searchPathSearchParams.global === 'true' ? allRooms : rooms
);
const getRoomName = useGetRoomName(getRoomHeroes, roomToHeroMembersResult.value);
const getRoomAvatar = useGetRoomAvatar(getRoomHeroes, roomToHeroMembersResult.value);
const getRoomHeroes = useRoomHeroes();
const getRoomName = useGetRoomName(getRoomHeroes);
const getRoomAvatar = useGetRoomAvatar(getRoomHeroes);
return (
<Box direction="Column" gap="700">

View file

@ -77,9 +77,10 @@ export function RoomSettings({ initialPage, requestClose }: RoomSettingsProps) {
const ct = useClient();
const useAuthentication = useMediaAuthentication();
const [getRoomHeroes, roomToHeroMembersResult] = useRoomHeroes(roomId);
const getRoomName = useGetRoomName(getRoomHeroes, roomToHeroMembersResult.value);
const getRoomAvatar = useGetRoomAvatar(getRoomHeroes, roomToHeroMembersResult.value);
const getRoomHeroes = useRoomHeroes();
const getRoomName = useGetRoomName(getRoomHeroes);
const getRoomAvatar = useGetRoomAvatar(getRoomHeroes);
const roomAvatar = getRoomAvatar(roomId);
const roomName = getRoomName(roomId);

View file

@ -112,9 +112,10 @@ export function RoomTimeline({ eventId, roomInputRef, editor, timeline }: RoomTi
const powerLevels = usePowerLevelsContext();
const creators = useRoomCreators(roomId);
const [getRoomHeroes, roomToHeroMembersResult] = useRoomHeroes(roomId);
const getRoomName = useGetRoomName(getRoomHeroes, roomToHeroMembersResult.value);
const getRoomAvatar = useGetRoomAvatar(getRoomHeroes, roomToHeroMembersResult.value);
const getRoomHeroes = useRoomHeroes();
const getRoomName = useGetRoomName(getRoomHeroes);
const getRoomAvatar = useGetRoomAvatar(getRoomHeroes);
const membersAPI = useRoomMembersAPI();
const creatorsTag = useRoomCreatorsTag();

View file

@ -271,9 +271,10 @@ export function RoomViewHeader({ callView }: { callView?: boolean }) {
const roomToJoinRule = useObservableValue(ct.roomToJoinRule);
const pinnedEvents = useRoomPinnedEvents(roomId);
const [getRoomHeroes, roomToHeroMemberResult] = useRoomHeroes(roomId);
const getRoomName = useGetRoomName(getRoomHeroes, roomToHeroMemberResult.value);
const getRoomAvatar = useGetRoomAvatar(getRoomHeroes, roomToHeroMemberResult.value);
const getRoomHeroes = useRoomHeroes();
const getRoomName = useGetRoomName(getRoomHeroes);
const getRoomAvatar = useGetRoomAvatar(getRoomHeroes);
const avatarMxc = getRoomAvatar(roomId);
const name = getRoomName(roomId) ?? 'Unknown';

View file

@ -181,8 +181,9 @@ export const RoomPinMenu = forwardRef<HTMLDivElement, RoomPinMenuProps>(({ reque
const redactedSet = useObservableValueOptionally(timeline?.relations.redacted);
const edits = useObservableValueOptionally(timeline?.relations.edits.eventToRelations);
const [getRoomHeroes, roomToHeroMembersResult] = useRoomHeroes(roomId);
const getRoomName = useGetRoomName(getRoomHeroes, roomToHeroMembersResult.value);
const getRoomHeroes = useRoomHeroes();
const getRoomName = useGetRoomName(getRoomHeroes);
const membersAPI = useRoomMembersAPI();
const { linkifyOpts, htmlReactParserOptions } = useMessageParserOptions(
roomId,

View file

@ -108,8 +108,9 @@ export function TimelineView({ threadTimeline, threadInputRef }: TimelineViewPro
const powerLevels = usePowerLevelsContext();
const creators = useRoomCreators(roomId);
const [getRoomHeroes, roomToHeroMembersResult] = useRoomHeroes(roomId);
const getRoomName = useGetRoomName(getRoomHeroes, roomToHeroMembersResult.value);
const getRoomHeroes = useRoomHeroes();
const getRoomName = useGetRoomName(getRoomHeroes);
const membersAPI = useRoomMembersAPI();
const creatorsTag = useRoomCreatorsTag();

View file

@ -153,9 +153,9 @@ export function Search({ requestClose }: SearchProps) {
const topActiveRooms = useTopActiveRooms(searchRoomType, rooms, directs, spaces);
const targetRooms = useSearchTargetRooms(searchRoomType, rooms, directs, spaces);
const [getRoomHeroes, roomToHeroMembersResult] = useRoomHeroes(directs);
const getRoomName = useGetRoomName(getRoomHeroes, roomToHeroMembersResult.value);
const getRoomAvatar = useGetRoomAvatar(getRoomHeroes, roomToHeroMembersResult.value);
const getRoomHeroes = useRoomHeroes();
const getRoomName = useGetRoomName(getRoomHeroes);
const getRoomAvatar = useGetRoomAvatar(getRoomHeroes);
const getTargetStr: SearchItemStrGetter<string> = useCallback(
(roomId: string) => {

View file

@ -58,12 +58,8 @@ function GlobalPackSelector({
}) {
const ct = useClient();
const imagePackRooms = useMemo(
() => packs.map((pack) => pack.address?.roomId).filter((rId) => typeof rId === 'string'),
[packs]
);
const [getRoomHeroes, roomToHeroMembersResult] = useRoomHeroes(imagePackRooms);
const getRoomName = useGetRoomName(getRoomHeroes, roomToHeroMembersResult.value);
const getRoomHeroes = useRoomHeroes();
const getRoomName = useGetRoomName(getRoomHeroes);
const roomToPacks = useMemo(() => {
const rToP = new Map<string, ImagePack[]>();

View file

@ -77,9 +77,10 @@ export function SpaceSettings({ initialPage, requestClose }: SpaceSettingsProps)
const roomId = useRoom();
const useAuthentication = useMediaAuthentication();
const [getRoomHeroes, roomToHeroMembersResult] = useRoomHeroes(roomId);
const getRoomName = useGetRoomName(getRoomHeroes, roomToHeroMembersResult.value);
const getRoomAvatar = useGetRoomAvatar(getRoomHeroes, roomToHeroMembersResult.value);
const getRoomHeroes = useRoomHeroes();
const getRoomName = useGetRoomName(getRoomHeroes);
const getRoomAvatar = useGetRoomAvatar(getRoomHeroes);
const roomAvatar = getRoomAvatar(roomId);
const roomName = getRoomName(roomId);

View file

@ -1,17 +1,17 @@
import { useCallback } from 'react';
import { useAtomValue } from 'jotai';
import { useClient } from '../useClient';
import { useObservableValue } from '../useObservableValue';
import { GetRoomHeroesCallback, RoomToHeroMembers } from './useRoomHeros';
import { GetRoomHeroesCallback } from './useRoomHeros';
import { roomMemberContentAvatarUrl } from '../../utils/room';
import { roomToHeroMembersAtom } from '../../state/room/roomToHeroMembers';
export type GetRoomAvatarCallback = (roomId: string) => string | undefined;
export const useGetRoomAvatar = (
getRoomHeroes: GetRoomHeroesCallback,
roomToHeroMembers: RoomToHeroMembers | undefined
): GetRoomAvatarCallback => {
export const useGetRoomAvatar = (getRoomHeroes: GetRoomHeroesCallback): GetRoomAvatarCallback => {
const ct = useClient();
const roomToAvatar = useObservableValue(ct.roomToAvatar);
const roomToHeroMembers = useAtomValue(roomToHeroMembersAtom);
const getRoomAvatar: GetRoomAvatarCallback = useCallback(
(roomId: string) => {

View file

@ -1,19 +1,19 @@
import { useCallback } from 'react';
import { useAtomValue } from 'jotai';
import { useClient } from '../useClient';
import { useObservableValue } from '../useObservableValue';
import { GetRoomHeroesCallback, RoomToHeroMembers } from './useRoomHeros';
import { GetRoomHeroesCallback } from './useRoomHeros';
import { getMxIdLocalPart } from '../../utils/matrix';
import { roomMemberContentDisplayname } from '../../utils/room';
import { roomToHeroMembersAtom } from '../../state/room/roomToHeroMembers';
export type GetRoomNameCallback = (roomId: string) => string | undefined;
export const useGetRoomName = (
getRoomHeroes: GetRoomHeroesCallback,
roomToHeroMembers: RoomToHeroMembers | undefined
): GetRoomNameCallback => {
export const useGetRoomName = (getRoomHeroes: GetRoomHeroesCallback): GetRoomNameCallback => {
const ct = useClient();
const roomToName = useObservableValue(ct.roomToName);
const roomToAlias = useObservableValue(ct.roomToAlias);
const roomToHeroMembers = useAtomValue(roomToHeroMembersAtom);
const getRoomName: GetRoomNameCallback = useCallback(
(roomId: string) => {
@ -25,7 +25,7 @@ export const useGetRoomName = (
const members = roomToHeroMembers?.get(roomId);
const heroesName: string[] = getRoomHeroes(roomId).map((userId) => {
const heroNames: string[] = getRoomHeroes(roomId).map((userId) => {
const member = members?.get(userId);
if (!member) return getMxIdLocalPart(userId) ?? userId;
const { content } = member;
@ -34,9 +34,19 @@ export const useGetRoomName = (
return username;
});
if (heroesName) return heroesName.join(', ');
if (heroNames.length === 0) {
return undefined;
}
return undefined;
if (heroNames.length === 1) {
return heroNames[0];
}
if (heroNames.length === 2) {
return `${heroNames[0]} & ${heroNames[1]}`;
}
return `${heroNames.slice(0, -1).join(', ')} & ${heroNames.at(-1)}`;
},
[roomToName, roomToAlias, getRoomHeroes, roomToHeroMembers]
);

View file

@ -1,22 +1,10 @@
import { useCallback, useMemo } from 'react';
import { PendingObservableResult } from 'neurone/lib/utils/observable';
import { StateEventWithoutRoomId } from 'neurone/lib/api/types';
import { useCallback } from 'react';
import { useClient } from '../useClient';
import {
useComputedPendingObservableResult,
useObservableValue,
useObservableValuePromise,
} from '../useObservableValue';
import { getJoinRoomMemberId } from '../../../client/storage/collections';
export type UserIdToRoomMember = Map<string, StateEventWithoutRoomId>;
export type RoomToHeroMembers = Map<string, UserIdToRoomMember>;
import { useObservableValue } from '../useObservableValue';
export type GetRoomHeroesCallback = (roomId: string) => string[];
export const useRoomHeroes = (
roomOrRooms: string | string[]
): [GetRoomHeroesCallback, PendingObservableResult<RoomToHeroMembers>] => {
export const useRoomHeroes = (): GetRoomHeroesCallback => {
const ct = useClient();
const roomToSummary = useObservableValue(ct.roomToSummary);
const directRoomToUser = useObservableValue(ct.directRoomToUser);
@ -31,34 +19,5 @@ export const useRoomHeroes = (
},
[roomToSummary, directRoomToUser]
);
const membersResult = useObservableValuePromise(
useMemo(() => {
const ids = Array.isArray(roomOrRooms)
? roomOrRooms.flatMap((roomId) =>
getHeroes(roomId).map((userId) => getJoinRoomMemberId(roomId, userId))
)
: getHeroes(roomOrRooms).map((userId) => getJoinRoomMemberId(roomOrRooms, userId));
return ct.storage.joinRoomMember.observableRead.getMultiple(ids);
}, [roomOrRooms, getHeroes, ct])
);
const roomToHeroesResult: PendingObservableResult<RoomToHeroMembers> =
useComputedPendingObservableResult(
membersResult,
useCallback((members) => {
const roomToHeroes = new Map<string, UserIdToRoomMember>();
members.forEach((member) => {
if (!member) return;
const { roomId, userId, event } = member;
const userToEvent: UserIdToRoomMember = roomToHeroes.get(roomId) ?? new Map();
userToEvent.set(userId, event);
roomToHeroes.set(roomId, userToEvent);
});
return roomToHeroes;
}, [])
);
return [getHeroes, roomToHeroesResult];
return getHeroes;
};

View file

@ -18,9 +18,10 @@ export type LocalRoomSummary = {
};
export const useLocalRoomSummary = (roomId: string): LocalRoomSummary => {
const ct = useClient();
const [getRoomHeroes, roomToHeroMembersResult] = useRoomHeroes(roomId);
const getRoomName = useGetRoomName(getRoomHeroes, roomToHeroMembersResult.value);
const getRoomAvatar = useGetRoomAvatar(getRoomHeroes, roomToHeroMembersResult.value);
const getRoomHeroes = useRoomHeroes();
const getRoomName = useGetRoomName(getRoomHeroes);
const getRoomAvatar = useGetRoomAvatar(getRoomHeroes);
const roomTopic = useRoomTopic(roomId);
const roomToAlias = useObservableValue(ct.roomToAlias);
const roomToJoinRule = useObservableValue(ct.roomToJoinRule);

View file

@ -183,9 +183,10 @@ export function Direct() {
const scrollRef = useRef<HTMLDivElement>(null);
const directs = useDirectRooms();
const [getRoomHeroes, roomToHeroMembersResult] = useRoomHeroes(directs);
const getRoomName = useGetRoomName(getRoomHeroes, roomToHeroMembersResult.value);
const getRoomAvatar = useGetRoomAvatar(getRoomHeroes, roomToHeroMembersResult.value);
const getRoomHeroes = useRoomHeroes();
const getRoomName = useGetRoomName(getRoomHeroes);
const getRoomAvatar = useGetRoomAvatar(getRoomHeroes);
const roomToJoinRule = useObservableValue(ct.roomToJoinRule);
const roomToNotifications = useRoomsNotifications(directs, roomToNotificationsAtom);
const unreadRooms = useRoomsUnread(directs, unreadRoomsAtom);

View file

@ -207,9 +207,10 @@ export function Home() {
const scrollRef = useRef<HTMLDivElement>(null);
const rooms = useHomeRooms();
const [getRoomHeroes, roomToHeroMembersResult] = useRoomHeroes(rooms);
const getRoomName = useGetRoomName(getRoomHeroes, roomToHeroMembersResult.value);
const getRoomAvatar = useGetRoomAvatar(getRoomHeroes, roomToHeroMembersResult.value);
const getRoomHeroes = useRoomHeroes();
const getRoomName = useGetRoomName(getRoomHeroes);
const getRoomAvatar = useGetRoomAvatar(getRoomHeroes);
const roomToJoinRule = useObservableValue(ct.roomToJoinRule);
const roomToNotifications = useRoomsNotifications(rooms, roomToNotificationsAtom);
const unreadRooms = useRoomsUnread(rooms, unreadRoomsAtom);

View file

@ -596,15 +596,11 @@ export function Notifications() {
}
}, [timelineState, notificationTimeline, lastVItemIndex, loadTimeline]);
const notificationRooms = useMemo(
() => Array.from(new Set(notificationTimeline.groups.map((g) => g.roomId))),
[notificationTimeline]
);
const roomToTimeline = useObservableValue(ct.roomToTimeline);
const [getRoomHeroes, roomToHeroMembersResult] = useRoomHeroes(notificationRooms);
const getRoomName = useGetRoomName(getRoomHeroes, roomToHeroMembersResult.value);
const getRoomAvatar = useGetRoomAvatar(getRoomHeroes, roomToHeroMembersResult.value);
const getRoomHeroes = useRoomHeroes();
const getRoomName = useGetRoomName(getRoomHeroes);
const getRoomAvatar = useGetRoomAvatar(getRoomHeroes);
return (
<Page>

View file

@ -72,7 +72,6 @@ import {
parseSidebar,
sidebarItemWithout,
useSidebarItems,
useSidebarRooms,
} from '../../../hooks/useSidebarItems';
import { AccountDataType } from '../../../../types/matrix/accountData';
import { ScreenSize, useScreenSizeContext } from '../../../hooks/useScreenSize';
@ -667,11 +666,9 @@ export function SpaceTabs({ scrollRef }: SpaceTabsProps) {
const [openedFolder, setOpenedFolder] = useAtom(useOpenedSidebarFolderAtom());
const [draggingItem, setDraggingItem] = useState<SidebarDraggable>();
const sidebarRooms = useSidebarRooms(originalSidebarItems);
const [getRoomHeroes, roomToHeroResult] = useRoomHeroes(sidebarRooms);
const getRoomName = useGetRoomName(getRoomHeroes, roomToHeroResult.value);
const getRoomAvatar = useGetRoomAvatar(getRoomHeroes, roomToHeroResult.value);
const getRoomHeroes = useRoomHeroes();
const getRoomName = useGetRoomName(getRoomHeroes);
const getRoomAvatar = useGetRoomAvatar(getRoomHeroes);
useEffect(() => {
localEchoSidebarItem(originalSidebarItems);

View file

@ -401,9 +401,11 @@ export function Space() {
const hierarchy = useSpaceJoinedHierarchy(spaceId);
const rooms = useMemo(() => hierarchy.map((item) => item.roomId), [hierarchy]);
const [getRoomHeroes, roomToHeroMembersResult] = useRoomHeroes(rooms);
const getRoomName = useGetRoomName(getRoomHeroes, roomToHeroMembersResult.value);
const getRoomAvatar = useGetRoomAvatar(getRoomHeroes, roomToHeroMembersResult.value);
const getRoomHeroes = useRoomHeroes();
const getRoomName = useGetRoomName(getRoomHeroes);
const getRoomAvatar = useGetRoomAvatar(getRoomHeroes);
const roomToJoinRule = useObservableValue(ct.roomToJoinRule);
const roomToNotifications = useRoomsNotifications(rooms, roomToNotificationsAtom);
const unreadRooms = useRoomsUnread(rooms, unreadRoomsAtom);

View file

@ -8,12 +8,14 @@ import {
} from '../room/roomToNotifications';
import { ClientContext } from '../../../client/context';
import { unreadRoomsAtom, useBindUnreadRoomsAtom } from '../room/roomToUnread';
import { roomToHeroMembersAtom, useBindRoomToHeroMembers } from '../room/roomToHeroMembers';
export const useBindAtoms = (ct: ClientContext) => {
useBindMDirectAtom(ct, mDirectAtom);
useBindAllRoomsAtom(ct, allRoomsAtom);
useBindRoomToParentsAtom(ct, roomToParentsAtom);
useBindRoomToNotificationsAtom(ct, roomToNotificationsAtom);
useBindRoomToHeroMembers(ct, roomToHeroMembersAtom);
useBindUnreadRoomsAtom(ct, unreadRoomsAtom);
useBindRoomIdToTypingMembersAtom(ct, roomIdToTypingMembersAtom);

View file

@ -0,0 +1,75 @@
import { useCallback, useEffect } from 'react';
import { StateEventWithoutRoomId } from 'neurone/lib/api/types';
import { atom, useSetAtom } from 'jotai';
import { ClientContext } from '../../../client/context';
import { useObservableValue } from '../../hooks/useObservableValue';
import { AsyncStatus, useAsyncCallback } from '../../hooks/useAsyncCallback';
import { getJoinRoomMemberId } from '../../../client/storage/collections';
export type UserIdToRoomMember = Map<string, StateEventWithoutRoomId>;
export type RoomToHeroMembers = Map<string, UserIdToRoomMember>;
export const roomToHeroMembersAtom = atom<RoomToHeroMembers>(new Map());
export const useBindRoomToHeroMembers = (
ct: ClientContext,
heroMembersAtom: typeof roomToHeroMembersAtom
) => {
const setHeroMembersAtom = useSetAtom(heroMembersAtom);
const rooms = useObservableValue(ct.rooms.join);
const roomToSummary = useObservableValue(ct.roomToSummary);
const directRoomToUser = useObservableValue(ct.directRoomToUser);
const [heroMembersState, loadHeroMembers] = useAsyncCallback(
useCallback(async () => {
const getHeroes = (roomId: string): string[] => {
const heroes = roomToSummary.get(roomId)?.['m.heroes'];
if (heroes && heroes.length > 0) return heroes;
const dmUserId = directRoomToUser.get(roomId);
return dmUserId ? [dmUserId] : [];
};
const ids = rooms.flatMap((roomId) =>
getHeroes(roomId).map((userId) => getJoinRoomMemberId(roomId, userId))
);
return ct.storage.joinRoomMember.observableRead.getMultiple(ids);
}, [ct, rooms, roomToSummary, directRoomToUser])
);
useEffect(() => {
loadHeroMembers();
}, [loadHeroMembers]);
const heroMembers =
heroMembersState.status === AsyncStatus.Success ? heroMembersState.data : undefined;
// dispose
useEffect(() => () => heroMembers?.dispose(), [heroMembers]);
useEffect(() => {
const processHeroMembers = () => {
const roomToHeroes = new Map<string, UserIdToRoomMember>();
heroMembers?.read.forEach((member) => {
if (!member) return;
const { roomId, userId, event } = member;
const userToEvent: UserIdToRoomMember = roomToHeroes.get(roomId) ?? new Map();
userToEvent.set(userId, event);
roomToHeroes.set(roomId, userToEvent);
});
setHeroMembersAtom(roomToHeroes);
};
if (heroMembers) {
processHeroMembers();
}
const unsubscribe = heroMembers?.subscribe(processHeroMembers);
return () => {
unsubscribe?.();
};
}, [heroMembers, setHeroMembersAtom]);
};