From 79f0c148b4fd82b1897aa259645138ffed86e76b Mon Sep 17 00:00:00 2001 From: Cristian Carralero Date: Mon, 11 Nov 2024 15:57:37 -0500 Subject: [PATCH] feat: Implement basic recommended room handling --- .../NavigationSection/NavigationSection.tsx | 15 +++--- .../RoomContainer/hooks/useActiveRoom.ts | 54 ++++++++++++++++++- src/hooks/matrix/useActiveSpaceIdStore.ts | 20 +++++++ 3 files changed, 80 insertions(+), 9 deletions(-) create mode 100644 src/hooks/matrix/useActiveSpaceIdStore.ts diff --git a/src/containers/NavigationSection/NavigationSection.tsx b/src/containers/NavigationSection/NavigationSection.tsx index e8a3013e..4100ffbf 100644 --- a/src/containers/NavigationSection/NavigationSection.tsx +++ b/src/containers/NavigationSection/NavigationSection.tsx @@ -18,6 +18,7 @@ import CreateSpaceModal from "@/components/CreateSpaceModal" import useGlobalHotkey from "@/hooks/util/useGlobalHotkey" import {Heading, Text} from "@/components/ui/typography" import useSpaceDetail from "@/hooks/matrix/useSpaceDetail" +import useActiveSpaceIdStore from "@/hooks/matrix/useActiveSpaceIdStore" export const DASHBOARD_SPACE_ID = "dashboard_space_id" @@ -25,17 +26,17 @@ const NavigationSection: FC<{className?: string; onLogOut: () => void}> = ({ className, onLogOut, }) => { - const [spaceSelected, setSpaceSelected] = useState(DASHBOARD_SPACE_ID) + const {activeSpaceId, setActiveSpaceId} = useActiveSpaceIdStore() const {spaces: spacesState, onCreateSpace, uploadSpaceAvatar} = useSpaces() const {userDataState, userData, onRefreshData} = useUserData() const {isSmall} = useBreakpoint() const [isCreateRoomModalOpen, setIsCreateRoomModalOpen] = useState(false) const {activeRoomId, setActiveRoomId} = useActiveRoomIdStore() const [modalCreateSpaceOpen, setModalCreateSpaceIsOpen] = useState(false) - const {name} = useSpaceDetail(spaceSelected) + const {name} = useSpaceDetail(activeSpaceId) const {isSectionsLoading, sections, onCreateRoom} = - useRoomNavigator(spaceSelected) + useRoomNavigator(activeSpaceId) useGlobalHotkey({key: "S", ctrl: true, shift: true}, () => setModalCreateSpaceIsOpen(true) @@ -91,8 +92,8 @@ const NavigationSection: FC<{className?: string; onLogOut: () => void}> = ({ {spaces => ( setModalCreateSpaceIsOpen(true)} /> )} @@ -104,7 +105,7 @@ const NavigationSection: FC<{className?: string; onLogOut: () => void}> = ({
- {spaceSelected === DASHBOARD_SPACE_ID ? "Dashboard" : name} + {activeSpaceId === DASHBOARD_SPACE_ID ? "Dashboard" : name}
@@ -116,7 +117,7 @@ const NavigationSection: FC<{className?: string; onLogOut: () => void}> = ({ roomSelected={activeRoomId ?? undefined} onRoomSelected={setActiveRoomId} sections={sections} - isDashboardActive={spaceSelected === undefined} + isDashboardActive={activeSpaceId === undefined} isLoading={isSectionsLoading} onCreateRoom={() => setIsCreateRoomModalOpen(true)} /> diff --git a/src/containers/RoomContainer/hooks/useActiveRoom.ts b/src/containers/RoomContainer/hooks/useActiveRoom.ts index bfa210f0..b240d3b3 100644 --- a/src/containers/RoomContainer/hooks/useActiveRoom.ts +++ b/src/containers/RoomContainer/hooks/useActiveRoom.ts @@ -1,7 +1,7 @@ import useActiveRoomIdStore from "@/hooks/matrix/useActiveRoomIdStore" import {useCallback, useEffect, useState} from "react" import {KnownMembership} from "matrix-js-sdk/lib/@types/membership" -import {type MatrixClient, Room, RoomMemberEvent} from "matrix-js-sdk" +import {JoinRule, type MatrixClient, Room, RoomMemberEvent} from "matrix-js-sdk" import useEventListener from "@/hooks/matrix/useEventListener" import useMatrixClient from "@/hooks/matrix/useMatrixClient" import useValueState, {ValueState} from "@/hooks/util/useValueState" @@ -14,6 +14,9 @@ import {getImageUrl, getRoomTopic} from "@/utils/matrix" import {isDirectRoom} from "@/utils/rooms" import {getOwnersIdWithPowerLevels} from "@/utils/members" import {strCapitalize} from "@/utils/util" +import useActiveSpaceIdStore from "@/hooks/matrix/useActiveSpaceIdStore" +import {DASHBOARD_SPACE_ID} from "@/containers/NavigationSection/SpacesNavigation" +import {IHierarchyRoom} from "matrix-js-sdk/lib/@types/spaces" export enum RoomState { Idle, @@ -95,10 +98,24 @@ async function getRoomOwners(room: Room): Promise { return owners.length === 0 ? null : owners } +export function validateHierarchyRoom(foundedRoom: IHierarchyRoom): void { + if (foundedRoom.room_type === "m.space") { + throw new RoomInvitedError("You cannot access the same parent space.") + } + + if ( + foundedRoom.join_rule !== JoinRule.Public && + !foundedRoom.guest_can_join + ) { + throw new RoomInvitedError("This room does not allow anyone to join.") + } +} + const useActiveRoom = (): UseActiveRoomReturnType => { const client = useMatrixClient() const [roomState, setRoomState] = useState(RoomState.Idle) const {activeRoomId, clearActiveRoomId} = useActiveRoomIdStore() + const {activeSpaceId} = useActiveSpaceIdStore() const [roomInvitedDetail, setRoomInvitedDetail] = useValueState() @@ -135,6 +152,39 @@ const useActiveRoom = (): UseActiveRoomReturnType => { const room = client.getRoom(activeRoomId) + if (activeSpaceId !== DASHBOARD_SPACE_ID && room === null) { + const space = client.getRoom(activeSpaceId) + + setRoomInvitedDetailScope(async () => { + if (space === null) { + throw new RoomInvitedError("You not have access to this space.") + } + + const hierarchy = await client.getRoomHierarchy(space.roomId) + + const foundedRoom = hierarchy.rooms.find( + hierarchyRoom => hierarchyRoom.room_id === activeRoomId + ) + + if (foundedRoom === undefined) { + throw new RoomInvitedError( + "You do not have access to the room or it does not exist." + ) + } + + validateHierarchyRoom(foundedRoom) + + return { + name: foundedRoom.name ?? foundedRoom.room_id, + topic: foundedRoom.topic, + avatarUrl: getImageUrl(foundedRoom.avatar_url, client), + detailChips: [], + } + }) + + return + } + setRoomInvitedDetailScope(async () => { if (room === null) { throw new RoomInvitedError("You not have access to this room.") @@ -151,7 +201,7 @@ const useActiveRoom = (): UseActiveRoomReturnType => { owners: owners ?? undefined, } }) - }, [activeRoomId, client, setRoomInvitedDetailScope]) + }, [activeRoomId, activeSpaceId, client, setRoomInvitedDetailScope]) useEffect(() => { if (client === null || activeRoomId === null) { diff --git a/src/hooks/matrix/useActiveSpaceIdStore.ts b/src/hooks/matrix/useActiveSpaceIdStore.ts new file mode 100644 index 00000000..48f9e66f --- /dev/null +++ b/src/hooks/matrix/useActiveSpaceIdStore.ts @@ -0,0 +1,20 @@ +import {DASHBOARD_SPACE_ID} from "@/containers/NavigationSection/SpacesNavigation" +import {create} from "zustand" + +type ActiveSpaceIdStore = { + activeSpaceId: string + clearActiveSpaceId: () => void + setActiveSpaceId: (spaceId: string) => void +} + +const useActiveSpaceIdStore = create(set => ({ + activeSpaceId: DASHBOARD_SPACE_ID, + clearActiveSpaceId() { + set(_state => ({activeSpaceId: DASHBOARD_SPACE_ID})) + }, + setActiveSpaceId(spaceId) { + set(_state => ({activeSpaceId: spaceId})) + }, +})) + +export default useActiveSpaceIdStore