roomList.tsx (6650B)
1 import { FC, memo, useContext, useEffect, useState } from "react"; 2 import RoomListItem from "./roomListItem/roomListItem"; 3 import { ChevronDown, ChevronRight } from "lucide-react"; 4 import './roomList.scss'; 5 import { useNavigate } from "react-router-dom"; 6 import { MatrixContext } from "../../app/sdk/client"; 7 import { useInView } from "react-intersection-observer"; 8 import { OnlineState } from "../../app/sdk/api/otherEnums"; 9 10 type Room = { 11 /** 12 * The URL of the Avatar image 13 */ 14 avatarUrl?: string 15 /** 16 * The displayname of the room list item 17 */ 18 displayname: string 19 /** 20 * Wether it is a DM or not 21 */ 22 dm: boolean 23 /** 24 * Wether the user is online. Only used if dm is true. 25 */ 26 online: OnlineState 27 /** 28 * The roomid of the Room 29 */ 30 roomID: string 31 }; 32 33 export type Section = { 34 /** 35 * Section Name. Can be a Space or a Tag 36 */ 37 sectionName: string 38 /** 39 * The Rooms within the Section 40 */ 41 rooms: Room[] 42 /** 43 * The Subsections of the Section 44 */ 45 subsections: Section[] 46 /** 47 * The roomid of the Space 48 */ 49 roomID: string 50 } 51 52 type RoomListProps = { 53 /** 54 * The Sections available 55 */ 56 sections: Section[] 57 /** 58 * Rooms outside of any Sections 59 */ 60 rooms: Room[] 61 /** 62 * Rooms outside of any Sections and DM 63 */ 64 dmRooms: Room[] 65 }; 66 67 type RoomListRoomsProps = { 68 /** 69 * The roomid of the Space 70 */ 71 sectionID: string 72 /** 73 * Rooms 74 */ 75 rooms: Room[] 76 /** 77 * The onClick handler 78 */ 79 onClick: (roomID: string) => void; 80 /** 81 * The activeRoom 82 */ 83 activeRoom?: string; 84 /** 85 * If rooms are hidden 86 */ 87 hidden: boolean 88 }; 89 90 const RoomListRooms: FC<RoomListRoomsProps> = memo(({ sectionID, rooms, onClick, activeRoom, hidden }: RoomListRoomsProps) => { 91 // Get room ids of rooms 92 const roomsRendered = rooms.map(room => { 93 return ( 94 <RoomListItem 95 roomId={room.roomID} 96 hidden={hidden} 97 key={`${room.roomID}+${sectionID}`} 98 avatarUrl={room.avatarUrl} 99 displayname={room.displayname} 100 dm={room.dm} 101 online={room.online} 102 active={room.roomID === activeRoom} 103 onClick={() => { onClick(room.roomID) }} 104 /> 105 ); 106 }); 107 return ( 108 <> 109 {roomsRendered} 110 </> 111 ); 112 }) 113 114 const RoomSection: FC<{ section: Section, onRoomClick: (roomID: string) => void, activeRoom: string | undefined }> = memo(({ section, onRoomClick, activeRoom }: { section: Section, onRoomClick: (roomID: string) => void, activeRoom: string | undefined }) => { 115 const [hidden, setHidden] = useState<boolean>(true); 116 const matrixClient = useContext(MatrixContext); 117 const { ref, inView } = useInView({ 118 triggerOnce: true, 119 threshold: 1, 120 onChange(inView) { 121 if (section.roomID !== "other") { 122 if (inView) { 123 matrixClient.addInViewRoom(section.roomID) 124 } else { 125 matrixClient.removeInViewRoom(section.roomID) 126 } 127 } 128 }, 129 }); 130 useEffect(() => { 131 if (hidden) { 132 matrixClient.removeSpaceOpen(section.roomID); 133 } else { 134 matrixClient.addSpaceOpen(section.roomID); 135 } 136 }, [hidden]) 137 return ( 138 <div ref={ref} key={section.roomID} className='room-section'> 139 { 140 inView && <div className="flex flex-row gap-2 py-1 items-center justify-start cursor-pointer h-8 text-slate-600" onClick={() => setHidden(prev => !prev)}> 141 {hidden ? <ChevronRight size={14} /> : <ChevronDown size={14} />} 142 <span className='font-normal text-base capitalize max-w-[32ch] overflow-hidden text-ellipsis w-full whitespace-nowrap'>{section.sectionName}</span> 143 </div >} 144 {!hidden && inView && (<RoomListRooms 145 hidden={hidden} 146 sectionID={section.roomID} 147 rooms={section.rooms} 148 onClick={onRoomClick} 149 activeRoom={activeRoom} 150 />)} 151 {!hidden && inView && ( 152 section.subsections.map(section => { 153 return ( 154 <RoomSection 155 key={section.roomID} 156 section={section} 157 onRoomClick={onRoomClick} 158 activeRoom={activeRoom} 159 /> 160 ); 161 }) 162 )} 163 </div> 164 ); 165 }) 166 167 const RoomList: FC<RoomListProps> = memo(({ sections, rooms, dmRooms }: RoomListProps) => { 168 const [activeRoom, setActiveRoom] = useState<string | undefined>(undefined); 169 const navigate = useNavigate(); 170 171 return ( 172 <div id='roomlist'> 173 {dmRooms?.length == 0 ? <></> : 174 <RoomSection 175 section={{ 176 sectionName: "DMs", 177 roomID: "dms", 178 subsections: [], 179 rooms: dmRooms 180 }} 181 onRoomClick={(roomID: string) => { 182 setActiveRoom(roomID); 183 navigate(`/${encodeURIComponent(roomID)}`); 184 }} 185 activeRoom={activeRoom} 186 />} 187 { 188 sections.map(section => { 189 return ( 190 <RoomSection 191 key={section.roomID} 192 section={section} 193 onRoomClick={(roomID: string) => { 194 setActiveRoom(roomID); 195 navigate(`/${encodeURIComponent(roomID)}`); 196 }} 197 activeRoom={activeRoom} 198 /> 199 ); 200 }) 201 } 202 {rooms.length > 0 ? <RoomSection 203 section={{ 204 sectionName: "Others", 205 roomID: "other", 206 subsections: [], 207 rooms: rooms 208 }} 209 onRoomClick={(roomID: string) => { 210 setActiveRoom(roomID); 211 navigate(`/${encodeURIComponent(roomID)}`); 212 }} 213 activeRoom={activeRoom} 214 /> : <></>} 215 216 </div> 217 ); 218 }) 219 220 export default RoomList