import React, { MouseEventHandler, useCallback, useMemo, useState } from 'react'; import { useNavigate } from 'react-router-dom'; import FocusTrap from 'focus-trap-react'; import { isKeyHotkey } from 'is-hotkey'; import { Room } from 'matrix-js-sdk'; import { PopOut, Menu, MenuItem, config, Text, Line, Chip, Icon, Icons, RectCords, Spinner, toRem, Box, Scroll, Avatar, } from 'folds'; import { useMatrixClient } from '../../hooks/useMatrixClient'; import { getMxIdServer } from '../../utils/matrix'; import { useCloseUserRoomProfile } from '../../state/hooks/userRoomProfile'; import { stopPropagation } from '../../utils/keyboard'; import { copyToClipboard } from '../../utils/dom'; import { getExploreServerPath } from '../../pages/pathUtils'; import { AsyncStatus, useAsyncCallback } from '../../hooks/useAsyncCallback'; import { factoryRoomIdByAtoZ } from '../../utils/sort'; import { useMutualRooms, useMutualRoomsSupport } from '../../hooks/useMutualRooms'; import { useRoomNavigate } from '../../hooks/useRoomNavigate'; import { useDirectRooms } from '../../pages/client/direct/useDirectRooms'; import { useMediaAuthentication } from '../../hooks/useMediaAuthentication'; import { useAllJoinedRoomsSet, useGetRoom } from '../../hooks/useGetRoom'; import { RoomAvatar, RoomIcon } from '../room-avatar'; import { getDirectRoomAvatarUrl, getRoomAvatarUrl } from '../../utils/room'; import { nameInitials } from '../../utils/common'; import { getMatrixToUser } from '../../plugins/matrix-to'; import { useTimeoutToggle } from '../../hooks/useTimeoutToggle'; import { useIgnoredUsers } from '../../hooks/useIgnoredUsers'; import { CutoutCard } from '../cutout-card'; import { SettingTile } from '../setting-tile'; export function ServerChip({ server }: { server: string }) { const mx = useMatrixClient(); const myServer = getMxIdServer(mx.getSafeUserId()); const navigate = useNavigate(); const closeProfile = useCloseUserRoomProfile(); const [copied, setCopied] = useTimeoutToggle(); const [cords, setCords] = useState(); const open: MouseEventHandler = (evt) => { setCords(evt.currentTarget.getBoundingClientRect()); }; const close = () => setCords(undefined); return ( isKeyHotkey('arrowdown', evt), isKeyBackward: (evt: KeyboardEvent) => isKeyHotkey('arrowup', evt), }} >
{ copyToClipboard(server); setCopied(); close(); }} > Copy Server { navigate(getExploreServerPath(server)); closeProfile(); }} > Explore Community
{ window.open(`https://${server}`, '_blank'); close(); }} > Open in Browser
} > ) : ( ) } onClick={open} aria-pressed={!!cords} > {server}
); } export function ShareChip({ userId }: { userId: string }) { const [cords, setCords] = useState(); const [copied, setCopied] = useTimeoutToggle(); const open: MouseEventHandler = (evt) => { setCords(evt.currentTarget.getBoundingClientRect()); }; const close = () => setCords(undefined); return ( isKeyHotkey('arrowdown', evt), isKeyBackward: (evt: KeyboardEvent) => isKeyHotkey('arrowup', evt), }} >
{ copyToClipboard(userId); setCopied(); close(); }} > Copy User ID { copyToClipboard(getMatrixToUser(userId)); setCopied(); close(); }} > Copy User Link
} > ) : ( ) } onClick={open} aria-pressed={!!cords} > Share
); } type MutualRoomsData = { rooms: Room[]; spaces: Room[]; directs: Room[]; }; export function MutualRoomsChip({ userId }: { userId: string }) { const mx = useMatrixClient(); const mutualRoomSupported = useMutualRoomsSupport(); const mutualRoomsState = useMutualRooms(userId); const { navigateRoom, navigateSpace } = useRoomNavigate(); const closeUserRoomProfile = useCloseUserRoomProfile(); const directs = useDirectRooms(); const useAuthentication = useMediaAuthentication(); const allJoinedRooms = useAllJoinedRoomsSet(); const getRoom = useGetRoom(allJoinedRooms); const [cords, setCords] = useState(); const open: MouseEventHandler = (evt) => { setCords(evt.currentTarget.getBoundingClientRect()); }; const close = () => setCords(undefined); const mutual: MutualRoomsData = useMemo(() => { const data: MutualRoomsData = { rooms: [], spaces: [], directs: [], }; if (mutualRoomsState.status === AsyncStatus.Success) { const mutualRooms = mutualRoomsState.data .sort(factoryRoomIdByAtoZ(mx)) .map(getRoom) .filter((room) => !!room); mutualRooms.forEach((room) => { if (room.isSpaceRoom()) { data.spaces.push(room); return; } if (directs.includes(room.roomId)) { data.directs.push(room); return; } data.rooms.push(room); }); } return data; }, [mutualRoomsState, getRoom, directs, mx]); if ( userId === mx.getSafeUserId() || !mutualRoomSupported || mutualRoomsState.status === AsyncStatus.Error ) { return null; } const renderItem = (room: Room) => { const { roomId } = room; const dm = directs.includes(roomId); return ( { if (room.isSpaceRoom()) { navigateSpace(roomId); } else { navigateRoom(roomId); } closeUserRoomProfile(); }} before={ {dm || room.isSpaceRoom() ? ( ( {nameInitials(room.name)} )} /> ) : ( )} } > {room.name} ); }; return ( isKeyHotkey('arrowdown', evt), isKeyBackward: (evt: KeyboardEvent) => isKeyHotkey('arrowup', evt), }} > {mutual.spaces.length > 0 && ( Spaces {mutual.spaces.map(renderItem)} )} {mutual.rooms.length > 0 && ( Rooms {mutual.rooms.map(renderItem)} )} {mutual.directs.length > 0 && ( Direct Messages {mutual.directs.map(renderItem)} )} ) : null } > } disabled={ mutualRoomsState.status !== AsyncStatus.Success || mutualRoomsState.data.length === 0 } onClick={open} aria-pressed={!!cords} > {mutualRoomsState.status === AsyncStatus.Success && `${mutualRoomsState.data.length} Mutual Rooms`} {mutualRoomsState.status === AsyncStatus.Loading && 'Mutual Rooms'} ); } export function IgnoredUserAlert() { return ( Blocked User You do not receive any messages or invites from this user. ); } export function OptionsChip({ userId }: { userId: string }) { const mx = useMatrixClient(); const [cords, setCords] = useState(); const open: MouseEventHandler = (evt) => { setCords(evt.currentTarget.getBoundingClientRect()); }; const close = () => setCords(undefined); const ignoredUsers = useIgnoredUsers(); const ignored = ignoredUsers.includes(userId); const [ignoreState, toggleIgnore] = useAsyncCallback( useCallback(async () => { const users = ignoredUsers.filter((u) => u !== userId); if (!ignored) users.push(userId); await mx.setIgnoredUsers(users); }, [mx, ignoredUsers, userId, ignored]) ); const ignoring = ignoreState.status === AsyncStatus.Loading; return ( isKeyHotkey('arrowdown', evt), isKeyBackward: (evt: KeyboardEvent) => isKeyHotkey('arrowup', evt), }} >
{ toggleIgnore(); close(); }} before={ ignoring ? ( ) : ( ) } disabled={ignoring} > {ignored ? 'Unblock User' : 'Block User'}
} > {ignoring ? ( ) : ( )}
); }