From 775d51ded5fe9a6108d0f4fc12e69c6ccc9a8b76 Mon Sep 17 00:00:00 2001 From: Cristian Carralero Date: Wed, 13 Nov 2024 13:05:52 -0500 Subject: [PATCH] feat: Abstract invitations handling for `useInvitedRoom` --- .../RoomContainer/RoomContainer.tsx | 10 +- .../RoomContainer/RoomInvitedSplash.tsx | 4 + .../RoomContainer/hooks/useActiveRoom.ts | 202 +++--------------- src/hooks/matrix/useInvitedRoom.ts | 158 ++++++++++++++ 4 files changed, 198 insertions(+), 176 deletions(-) create mode 100644 src/hooks/matrix/useInvitedRoom.ts diff --git a/src/containers/RoomContainer/RoomContainer.tsx b/src/containers/RoomContainer/RoomContainer.tsx index 6d2657f7..4fa3eafb 100644 --- a/src/containers/RoomContainer/RoomContainer.tsx +++ b/src/containers/RoomContainer/RoomContainer.tsx @@ -10,18 +10,14 @@ import useRoomMembers from "../Roster/hooks/useRoomMembers" import useGlobalHotkey from "@/hooks/util/useGlobalHotkey" import useBreakpoint from "@/hooks/util/useMediaQuery" import RoomInvitedSplash from "./RoomInvitedSplash" +import useInvitedRoom from "@/hooks/matrix/useInvitedRoom" const RoomContainer: FC = () => { const [isRosterExpanded, setIsRosterExpanded] = useState(true) const {isSmall} = useBreakpoint() - const { - activeRoomId, - roomState, - clearActiveRoomId, - roomInvitedDetail, - onJoinRoom, - } = useActiveRoom() + const {activeRoomId, roomState, clearActiveRoomId} = useActiveRoom() + const {roomInvitedDetail, onJoinRoom} = useInvitedRoom(activeRoomId) const {membersState, onReloadMembers, onLazyReload, isLazyLoading} = useRoomMembers(activeRoomId) diff --git a/src/containers/RoomContainer/RoomInvitedSplash.tsx b/src/containers/RoomContainer/RoomInvitedSplash.tsx index d03f53e3..1d237658 100644 --- a/src/containers/RoomContainer/RoomInvitedSplash.tsx +++ b/src/containers/RoomContainer/RoomInvitedSplash.tsx @@ -130,6 +130,10 @@ const RoomInvitedSplash: FC = ({ + {topic === undefined && owners === undefined && ( + This room does not support preview. + )} + {topic !== undefined && (
activeRoomId: string | null clearActiveRoomId: () => void - onJoinRoom: () => Promise -} - -async function getChipsFromRoom(room: Room): Promise { - const chips: string[] = [] - - const isDirect = isDirectRoom(room.client, room.roomId) - const isSpace = room.isSpaceRoom() - const membersCount = room.getJoinedMemberCount() - - if (isDirect && !isSpace) { - chips.push("Direct") - } - - if (membersCount > 0 && !isDirect) { - chips.push(`+${membersCount} Members`) - } - - if (isSpace) { - chips.push("Space") - } else { - chips.push("Room") - } - - if (isSpace) { - try { - const hierarchy = await room.client.getRoomHierarchy(room.roomId) - - chips.push(`+${hierarchy.rooms.length} Rooms`) - } catch { - console.error(`Could not load the space hierarchy ${room.name}`) - } - } - - chips.push(strCapitalize(room.getJoinRule())) - - return chips -} - -async function getRoomOwners(room: Room): Promise { - const owners: RoomDetailOwner[] = [] - try { - const ownersId = getOwnersIdWithPowerLevels(room) - - const roomMembers = await room.client.getJoinedRoomMembers(room.roomId) - - for (const {userId} of ownersId) { - const ownerMember = roomMembers.joined[userId] - - if (ownerMember === undefined) { - continue - } - - owners.push({ - displayName: ownerMember.display_name, - avatarUrl: getImageUrl(ownerMember.avatar_url, room.client), - }) - } - } catch (error) { - console.error(error) - - return null - } - - 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() - - const setRoomInvitedDetailScope = useCallback( - (execute: () => Promise) => { - execute() - .then(value => { - setRoomInvitedDetail({status: "success", data: value}) - }) - .catch((error: Error) => { - setRoomInvitedDetail({status: "error", error}) - }) - }, - [setRoomInvitedDetail] - ) - - const onJoinRoom = useCallback(async () => { - if (client === null || activeRoomId === null) { - return - } - - await client.joinRoom(activeRoomId) - }, [activeRoomId, client]) useEffect(() => { setRoomState(RoomState.Idle) }, [activeRoomId]) - useEffect(() => { - if (client === null || activeRoomId === null) { - return - } + // useEffect(() => { + // if (client === null || activeRoomId === null) { + // return + // } - const room = client.getRoom(activeRoomId) + // const room = client.getRoom(activeRoomId) - if (activeSpaceId !== DASHBOARD_SPACE_ID && room === null) { - const space = client.getRoom(activeSpaceId) + // // 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.") - } + // // setRoomInvitedDetailScope(async () => { + // // if (space === null) { + // // throw new RoomInvitedError("You not have access to this space.") + // // } - const hierarchy = await client.getRoomHierarchy(space.roomId) + // // const hierarchy = await client.getRoomHierarchy(space.roomId) - const foundedRoom = hierarchy.rooms.find( - hierarchyRoom => hierarchyRoom.room_id === activeRoomId - ) + // // 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." - ) - } + // // if (foundedRoom === undefined) { + // // throw new RoomInvitedError( + // // "You do not have access to the room or it does not exist." + // // ) + // // } - validateHierarchyRoom(foundedRoom) + // // validateHierarchyRoom(foundedRoom) - return { - name: foundedRoom.name ?? foundedRoom.room_id, - topic: foundedRoom.topic, - avatarUrl: getImageUrl(foundedRoom.avatar_url, client), - detailChips: [], - } - }) + // // 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.") - } - - const chips = await getChipsFromRoom(room) - const owners = await getRoomOwners(room) - - return { - name: room.name, - topic: getRoomTopic(room) ?? undefined, - avatarUrl: getImageUrl(room.getMxcAvatarUrl(), client), - detailChips: chips, - owners: owners ?? undefined, - } - }) - }, [activeRoomId, activeSpaceId, client, setRoomInvitedDetailScope]) + // // return + // // } + // }, [activeRoomId, client]) useEffect(() => { if (client === null || activeRoomId === null) { @@ -262,10 +128,8 @@ const useActiveRoom = (): UseActiveRoomReturnType => { return { client, roomState, - roomInvitedDetail, activeRoomId, clearActiveRoomId, - onJoinRoom, } } diff --git a/src/hooks/matrix/useInvitedRoom.ts b/src/hooks/matrix/useInvitedRoom.ts new file mode 100644 index 00000000..ea96912c --- /dev/null +++ b/src/hooks/matrix/useInvitedRoom.ts @@ -0,0 +1,158 @@ +import { + RoomDetailOwner, + RoomDetailPreview, + RoomInvitedError, +} from "@/containers/RoomContainer/RoomInvitedSplash" +import useValueState, {ValueState} from "../util/useValueState" +import useMatrixClient from "./useMatrixClient" +import {useCallback, useEffect} from "react" +import {getImageUrl, getRoomTopic} from "@/utils/matrix" +import {getOwnersIdWithPowerLevels} from "@/utils/members" +import {isDirectRoom} from "@/utils/rooms" +import {strCapitalize} from "@/utils/util" +import {Room, JoinRule} from "matrix-js-sdk" +import {IHierarchyRoom} from "matrix-js-sdk/lib/@types/spaces" + +type UseInvitedRoomReturnType = { + roomInvitedDetail: ValueState + onJoinRoom: () => Promise +} + +const useInvitedRoom = ( + activeRoomId: string | null +): UseInvitedRoomReturnType => { + const client = useMatrixClient() + + const [roomInvitedDetail, setRoomInvitedDetail] = + useValueState() + + const setRoomInvitedDetailScope = useCallback( + (execute: () => Promise) => { + execute() + .then(value => { + setRoomInvitedDetail({status: "success", data: value}) + }) + .catch((error: Error) => { + setRoomInvitedDetail({status: "error", error}) + }) + }, + [setRoomInvitedDetail] + ) + + const onJoinRoom = useCallback(async () => { + if (client === null || activeRoomId === null) { + return + } + + await client.joinRoom(activeRoomId) + }, [activeRoomId, client]) + + useEffect(() => { + if (client === null || activeRoomId === null) { + return + } + + const room = client.getRoom(activeRoomId) + + setRoomInvitedDetailScope(async () => { + if (room === null) { + throw new RoomInvitedError("You not have access to this room.") + } + + const chips = await getChipsFromRoom(room) + const owners = await getRoomOwners(room) + + return { + name: room.name, + topic: getRoomTopic(room) ?? undefined, + avatarUrl: getImageUrl(room.getMxcAvatarUrl(), client), + detailChips: chips, + owners: owners ?? undefined, + } + }) + }, [activeRoomId, client, setRoomInvitedDetailScope]) + + return { + roomInvitedDetail, + onJoinRoom, + } +} + +async function getChipsFromRoom(room: Room): Promise { + const chips: string[] = [] + + const isDirect = isDirectRoom(room.client, room.roomId) + const isSpace = room.isSpaceRoom() + const membersCount = room.getJoinedMemberCount() + + if (isDirect && !isSpace) { + chips.push("Direct") + } + + if (membersCount > 0 && !isDirect) { + chips.push(`+${membersCount} Members`) + } + + if (isSpace) { + chips.push("Space") + } else { + chips.push("Room") + } + + if (isSpace) { + try { + const hierarchy = await room.client.getRoomHierarchy(room.roomId) + + chips.push(`+${hierarchy.rooms.length} Rooms`) + } catch { + console.error(`Could not load the space hierarchy ${room.name}`) + } + } + + chips.push(strCapitalize(room.getJoinRule())) + + return chips +} + +async function getRoomOwners(room: Room): Promise { + const owners: RoomDetailOwner[] = [] + try { + const ownersId = getOwnersIdWithPowerLevels(room) + + const roomMembers = await room.client.getJoinedRoomMembers(room.roomId) + + for (const {userId} of ownersId) { + const ownerMember = roomMembers.joined[userId] + + if (ownerMember === undefined) { + continue + } + + owners.push({ + displayName: ownerMember.display_name, + avatarUrl: getImageUrl(ownerMember.avatar_url, room.client), + }) + } + } catch (error) { + console.error(error) + + return null + } + + return owners.length === 0 ? null : owners +} + +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.") + } +} + +export default useInvitedRoom