import React, { forwardRef, useState } from 'react'; import { Room } from 'matrix-js-sdk'; import { useAtomValue } from 'jotai'; import { Box, Line, Menu, MenuItem, Text, config, toRem } from 'folds'; import { Icon, Icons } from '../../components/icons'; import type { HierarchyItem } from '../../hooks/useSpaceHierarchy'; import { useMatrixClient } from '../../hooks/useMatrixClient'; import { useOpenCreateRoomModal } from '../../state/hooks/createRoomModal'; import { useOpenCreateSpaceModal } from '../../state/hooks/createSpaceModal'; import { AddExistingModal } from '../add-existing'; import { useSetting } from '../../state/hooks/settings'; import { settingsAtom } from '../../state/settings'; import { usePowerLevels } from '../../hooks/usePowerLevels'; import { useRoomCreators } from '../../hooks/useRoomCreators'; import { useRoomPermissions } from '../../hooks/useRoomPermissions'; import { useOpenSpaceSettings } from '../../state/hooks/spaceSettings'; import { useRoomNavigate } from '../../hooks/useRoomNavigate'; import { useMarkRoomsAsRead } from '../../hooks/useMarkAsRead'; import { useRecursiveChildScopeFactory, useSpaceChildren } from '../../state/hooks/roomList'; import { allRoomsAtom } from '../../state/room-list/roomList'; import { roomToParentsAtom } from '../../state/room/roomToParents'; import { useRoomsUnread } from '../../state/hooks/unread'; import { roomToUnreadAtom } from '../../state/room/roomToUnread'; import { UseStateProvider } from '../../components/UseStateProvider'; import { LeaveSpacePrompt } from '../../components/leave-space-prompt'; import { RemoveInaccessiblePrompt } from '../../components/remove-inaccessible-prompt'; import { InviteUserPrompt } from '../../components/invite-user-prompt'; import { copyToClipboard } from '../../utils/dom'; import { getCanonicalAliasOrRoomId, isRoomAlias } from '../../utils/matrix'; import { getMatrixToRoom } from '../../plugins/matrix-to'; import { getViaServers } from '../../plugins/via-servers'; import { useNavigate } from 'react-router-dom'; import { getSpaceFeedPath, getSpaceLobbyPath } from '../../pages/pathUtils'; import { shouldShowForumLobby } from '../../utils/room'; import { useSpaceFeedSelected } from '../../hooks/router/useSelectedSpace'; import { StateEvent } from '../../../types/matrix/room'; type ForumAddOptions = { item: HierarchyItem; showAddSpace: boolean; }; export type OpenAddExistingOptions = { parentId: string; space: boolean; }; type SpaceOptionsMenuProps = { room: Room; requestClose: () => void; forumAdd?: ForumAddOptions; /** Render Add Existing outside the popout (forum kebab). */ onOpenAddExisting?: (options: OpenAddExistingOptions) => void; }; export const SpaceOptionsMenu = forwardRef( ({ room, requestClose, forumAdd, onOpenAddExisting }, ref) => { const mx = useMatrixClient(); const navigate = useNavigate(); const onFeed = useSpaceFeedSelected(); const isForum = shouldShowForumLobby(room); const [hideActivity] = useSetting(settingsAtom, 'hideActivity'); const [developerTools] = useSetting(settingsAtom, 'developerTools'); const roomToParents = useAtomValue(roomToParentsAtom); const powerLevels = usePowerLevels(room); const creators = useRoomCreators(room); const permissions = useRoomPermissions(creators, powerLevels); const canInvite = permissions.action('invite', mx.getSafeUserId()); const canEditChildren = permissions.stateEvent(StateEvent.SpaceChild, mx.getSafeUserId()); const openSpaceSettings = useOpenSpaceSettings(); const { navigateRoom } = useRoomNavigate(); const markRoomsAsRead = useMarkRoomsAsRead(mx); const [invitePrompt, setInvitePrompt] = useState(false); const [addExistingRoom, setAddExistingRoom] = useState(false); const [addExistingSpace, setAddExistingSpace] = useState(false); const openCreateRoomModal = useOpenCreateRoomModal(); const openCreateSpaceModal = useOpenCreateSpaceModal(); const allChild = useSpaceChildren( allRoomsAtom, room.roomId, useRecursiveChildScopeFactory(mx, roomToParents) ); const unread = useRoomsUnread(allChild, roomToUnreadAtom); const handleMarkAsRead = () => { markRoomsAsRead(allChild, hideActivity); requestClose(); }; const handleCopyLink = () => { const roomIdOrAlias = getCanonicalAliasOrRoomId(mx, room.roomId); const viaServers = isRoomAlias(roomIdOrAlias) ? undefined : getViaServers(room); copyToClipboard(getMatrixToRoom(roomIdOrAlias, viaServers)); requestClose(); }; const handleInvite = () => { setInvitePrompt(true); }; const handleRoomSettings = () => { openSpaceSettings(room.roomId); requestClose(); }; const handleOpenTimeline = () => { navigateRoom(room.roomId); requestClose(); }; const handleOpenFeed = () => { navigate(getSpaceFeedPath(getCanonicalAliasOrRoomId(mx, room.roomId))); requestClose(); }; const handleOpenManage = () => { navigate(getSpaceLobbyPath(getCanonicalAliasOrRoomId(mx, room.roomId))); requestClose(); }; const handleAddTopic = () => { if (!forumAdd) return; openCreateRoomModal(forumAdd.item.roomId); requestClose(); }; const handleAddCategory = () => { if (!forumAdd) return; openCreateSpaceModal(forumAdd.item.roomId); requestClose(); }; const handleAddExistingTopic = () => { if (!forumAdd) return; if (onOpenAddExisting) { onOpenAddExisting({ parentId: forumAdd.item.roomId, space: false }); requestClose(); return; } setAddExistingRoom(true); }; const handleAddExistingCategory = () => { if (!forumAdd) return; if (onOpenAddExisting) { onOpenAddExisting({ parentId: forumAdd.item.roomId, space: true }); requestClose(); return; } setAddExistingSpace(true); }; return ( <> {forumAdd && ( <> } radii="300" > Add Topic Add Existing Topic {forumAdd.showAddSpace && ( <> } radii="300" fill="None" > Add Section Add Existing Section )} )} {invitePrompt && ( { setInvitePrompt(false); requestClose(); }} /> )} {isForum && !onFeed && ( } radii="300" > Feed )} {isForum && onFeed && ( } radii="300" > Manage )} } radii="300" disabled={!unread} > Mark as Read } radii="300" aria-pressed={invitePrompt} disabled={!canInvite} > Invite } radii="300" > Copy Link } radii="300" > Space Settings {developerTools && ( } radii="300" > Event Timeline )} {canEditChildren && ( {(promptRemove, setPromptRemove) => ( <> setPromptRemove(true)} variant="Critical" fill="None" size="300" after={} radii="300" aria-pressed={promptRemove} > Remove Inaccessible {promptRemove && ( setPromptRemove(false)} /> )} )} )} {(promptLeave, setPromptLeave) => ( <> setPromptLeave(true)} variant="Critical" fill="None" size="300" after={} radii="300" aria-pressed={promptLeave} > Leave Space {promptLeave && ( setPromptLeave(false)} /> )} )} {forumAdd && addExistingRoom && ( setAddExistingRoom(false)} /> )} {forumAdd && addExistingSpace && ( setAddExistingSpace(false)} /> )} ); } );