diff --git a/src/components/views/rooms/RoomListPanel/RoomList.tsx b/src/components/views/rooms/RoomListPanel/RoomList.tsx index bd1c1aa17a7bcb745b5850155715ab62b0c6b90a..b4d994e6695b9690255a20d7312c026684a4b363 100644 --- a/src/components/views/rooms/RoomListPanel/RoomList.tsx +++ b/src/components/views/rooms/RoomListPanel/RoomList.tsx @@ -5,7 +5,7 @@ * Please see LICENSE files in the repository root for full details. */ -import React, { type JSX, useCallback, useEffect, useRef } from "react"; +import React, { type JSX, useCallback, useEffect, useRef, useState } from "react"; import { Virtuoso, type VirtuosoHandle } from "react-virtuoso"; import { type Room } from "matrix-js-sdk/src/matrix"; @@ -16,6 +16,9 @@ import { RovingTabIndexProvider } from "../../../../accessibility/RovingTabIndex import { getKeyBindingsManager } from "../../../../KeyBindingsManager"; import { KeyBindingAction } from "../../../../accessibility/KeyboardShortcuts"; import { Landmark, LandmarkNavigation } from "../../../../accessibility/LandmarkNavigation"; +import { useEventEmitterState } from "../../../../hooks/useEventEmitter"; +import { type SpaceKey, UPDATE_SELECTED_SPACE } from "../../../../stores/spaces"; +import SpaceStore from "../../../../stores/spaces/SpaceStore"; interface RoomListProps { /** @@ -30,66 +33,105 @@ interface RoomListProps { export function RoomList({ vm: { rooms, activeIndex: currentRoomIndex } }: RoomListProps): JSX.Element { // To follow the grid pattern (https://www.w3.org/WAI/ARIA/apg/patterns/grid/), we need to set the first element of the list as a row. // The virtuoso component is set to role="grid" and the items are set to role="gridcell". + // TODO REPLACE WITH A CUSToM LIST COMPONENT const scrollerRef = useCallback((node: HTMLElement | Window | null) => { if (node instanceof HTMLElement) { node.firstElementChild?.setAttribute("role", "row"); } }, []); - // Scroll to the current room when we switch between spaces const virtuosoRef = useRef<VirtuosoHandle | null>(null); + // UNCOMMENT IF YOU WANT SCROLLING TO THE CURRENT ROOM INDEX + // USE THIS OR THE KEY ON VIRTUOSO, NOT BOTH + // useEffect(() => { + // console.log("virtuoso useEffect: currentRoomIndex changed to", currentRoomIndex, "rooms", rooms.length); + // if (!virtuosoRef.current) return; + // + // console.log("virtuoso scrolling to current room index:", currentRoomIndex); + // + // virtuosoRef.current?.scrollIntoView({ + // index: currentRoomIndex || 0, + // behavior: "auto", + // }); + // + // // DATA AND INDEX CHANGES AT THE SAME TIME, WE NEED TO WAIT TO SET THE DATA FIRST + // // setTimeout(() => { + // // virtuosoRef.current?.scrollIntoView({ + // // index: currentRoomIndex || 0, + // // behavior: "auto", + // // }); + // // }, 0); + // }, [rooms, currentRoomIndex]); + + // Mound and unmount the virtuoso component when the space changes. + // We listen the rooms array to detect changes in the space because we want the rooms to be updated when the space changes. + // If only the room changes, the space key will not change, so we can keep the same virtuoso component. + // MOUNT AND UNMOUNT CREATE A VISUAL GLITCH + const currentSpaceRef = useRef(SpaceStore.instance.activeSpace); + const [currentSpaceKey, setCurrentSpaceKey] = useState(SpaceStore.instance.activeSpace); useEffect(() => { - if (!virtuosoRef.current) return; + const newSpace = SpaceStore.instance.activeSpace; + if (currentSpaceRef.current !== newSpace) { + setCurrentSpaceKey(newSpace); + currentSpaceRef.current = newSpace; + } + }, [rooms]); - // Without setTimeout, the scrollToIndex does nothing when the space change - setTimeout(() => virtuosoRef.current?.scrollToIndex(currentRoomIndex ?? 0), 0); - }, [currentRoomIndex]); + console.log("virtuoso key:", currentSpaceKey, "currentRoomIndex:", currentRoomIndex, "rooms:", rooms.length); + + useEffect(() => { + console.log("mounted virtuoso with spaceKey:", currentSpaceKey); + return () => { + console.log("unmounted virtuoso with spaceKey:", currentSpaceKey); + }; + }, [currentSpaceKey]); return ( - <RovingTabIndexProvider handleHomeEnd={true} handleUpDown={true}> - {({ onKeyDownHandler }) => ( - <Virtuoso<Room, { currentRoomIndex?: number }> - data-testid="room-list" - role="grid" - aria-label={_t("room_list|list_title")} - aria-rowcount={1} - aria-colcount={1} - style={{ flex: 1 }} - data={rooms} - fixedItemHeight={48} - context={{ currentRoomIndex }} - itemContent={(index, _room, { currentRoomIndex }) => ( - <RoomListItemView - room={rooms[index]} - isSelected={currentRoomIndex === index} - aria-colindex={index} - role="gridcell" - /> - )} - computeItemKey={(index, room) => room.roomId} - /* 240px = 5 rows */ - increaseViewportBy={240} - initialTopMostItemIndex={currentRoomIndex ?? 0} - ref={virtuosoRef} - scrollerRef={scrollerRef} - onKeyDown={(ev) => { - const navAction = getKeyBindingsManager().getNavigationAction(ev); - if ( - navAction === KeyBindingAction.NextLandmark || - navAction === KeyBindingAction.PreviousLandmark - ) { - LandmarkNavigation.findAndFocusNextLandmark( - Landmark.ROOM_LIST, - navAction === KeyBindingAction.PreviousLandmark, - ); - ev.stopPropagation(); - ev.preventDefault(); - return; - } - onKeyDownHandler(ev); - }} + // <RovingTabIndexProvider handleHomeEnd={true} handleUpDown={true}> + // {({ onKeyDownHandler }) => ( + <Virtuoso + key={currentSpaceKey} + data-testid="room-list" + role="grid" + aria-label={_t("room_list|list_title")} + aria-rowcount={1} + aria-colcount={1} + style={{ flex: 1 }} + totalCount={rooms.length} + fixedItemHeight={48} + context={{ currentRoomIndex }} + itemContent={(index, _room, { currentRoomIndex }) => ( + <RoomListItemView + room={rooms[index]} + isSelected={currentRoomIndex === index} + aria-colindex={index} + role="gridcell" /> )} - </RovingTabIndexProvider> + computeItemKey={(index) => rooms[index].roomId} + /* 240px = 5 rows */ + increaseViewportBy={240} + initialTopMostItemIndex={currentRoomIndex ?? 0} + ref={virtuosoRef} + scrollerRef={scrollerRef} + // onKeyDown={(ev) => { + // const navAction = getKeyBindingsManager().getNavigationAction(ev); + // if ( + // navAction === KeyBindingAction.NextLandmark || + // navAction === KeyBindingAction.PreviousLandmark + // ) { + // LandmarkNavigation.findAndFocusNextLandmark( + // Landmark.ROOM_LIST, + // navAction === KeyBindingAction.PreviousLandmark, + // ); + // ev.stopPropagation(); + // ev.preventDefault(); + // return; + // } + // onKeyDownHandler(ev); + // }} + /> + // )} + // </RovingTabIndexProvider> ); }