import { Box, Button, Chip, config, Dialog, Header, Icon, IconButton, Icons, Line, Menu, MenuItem, Overlay, OverlayBackdrop, OverlayCenter, PopOut, RectCords, Spinner, Text, toRem, } from 'folds'; import React, { MouseEventHandler, useCallback, useState } from 'react'; import FocusTrap from 'focus-trap-react'; import { isKeyHotkey } from 'is-hotkey'; import { useMatrixClient } from '../../hooks/useMatrixClient'; import { useMediaAuthentication } from '../../hooks/useMediaAuthentication'; import { PowerColorBadge, PowerIcon } from '../power'; import { useGetMemberPowerLevel, usePowerLevels } from '../../hooks/usePowerLevels'; import { getPowers, usePowerLevelTags } from '../../hooks/usePowerLevelTags'; import { stopPropagation } from '../../utils/keyboard'; import { StateEvent } from '../../../types/matrix/room'; import { useOpenRoomSettings } from '../../state/hooks/roomSettings'; import { RoomSettingsPage } from '../../state/roomSettings'; import { useRoom } from '../../hooks/useRoom'; import { useSpaceOptionally } from '../../hooks/useSpace'; import { CutoutCard } from '../cutout-card'; import { useOpenSpaceSettings } from '../../state/hooks/spaceSettings'; import { SpaceSettingsPage } from '../../state/spaceSettings'; import { AsyncStatus, useAsyncCallback } from '../../hooks/useAsyncCallback'; import { BreakWord } from '../../styles/Text.css'; import { getPowerTagIconSrc, useGetMemberPowerTag } from '../../hooks/useMemberPowerTag'; import { useRoomCreators } from '../../hooks/useRoomCreators'; import { useRoomPermissions } from '../../hooks/useRoomPermissions'; import { useMemberPowerCompare } from '../../hooks/useMemberPowerCompare'; type SelfDemoteAlertProps = { power: number; onCancel: () => void; onChange: (power: number) => void; }; function SelfDemoteAlert({ power, onCancel, onChange }: SelfDemoteAlertProps) { return ( }>
Self Demotion
You are about to demote yourself! You will not be able to regain this power yourself. Are you sure?
); } type SharedPowerAlertProps = { power: number; onCancel: () => void; onChange: (power: number) => void; }; function SharedPowerAlert({ power, onCancel, onChange }: SharedPowerAlertProps) { return ( }>
Shared Power
You are promoting the user to have the same power as yourself! You will not be able to change their power afterward. Are you sure?
); } export function PowerChip({ userId }: { userId: string }) { const mx = useMatrixClient(); const room = useRoom(); const space = useSpaceOptionally(); const useAuthentication = useMediaAuthentication(); const openRoomSettings = useOpenRoomSettings(); const openSpaceSettings = useOpenSpaceSettings(); const powerLevels = usePowerLevels(room); const creators = useRoomCreators(room); const permissions = useRoomPermissions(creators, powerLevels); const getMemberPowerLevel = useGetMemberPowerLevel(powerLevels); const { hasMorePower } = useMemberPowerCompare(creators, powerLevels); const powerLevelTags = usePowerLevelTags(room, powerLevels); const getMemberPowerTag = useGetMemberPowerTag(room, creators, powerLevels); const myUserId = mx.getSafeUserId(); const canChangePowers = permissions.stateEvent(StateEvent.RoomPowerLevels, myUserId) && (myUserId === userId ? true : hasMorePower(myUserId, userId)); const tag = getMemberPowerTag(userId); const tagIconSrc = tag.icon && getPowerTagIconSrc(mx, useAuthentication, tag.icon); const [cords, setCords] = useState(); const open: MouseEventHandler = (evt) => { setCords(evt.currentTarget.getBoundingClientRect()); }; const close = () => setCords(undefined); const [powerState, changePower] = useAsyncCallback( useCallback( async (power: number) => { await mx.setPowerLevel(room.roomId, userId, power); }, [mx, userId, room] ) ); const changing = powerState.status === AsyncStatus.Loading; const error = powerState.status === AsyncStatus.Error; const [selfDemote, setSelfDemote] = useState(); const [sharedPower, setSharedPower] = useState(); const handlePowerSelect = (power: number): void => { close(); if (!canChangePowers) return; if (power === getMemberPowerLevel(userId)) return; if (userId === mx.getSafeUserId()) { setSelfDemote(power); return; } if (!creators.has(myUserId) && power === getMemberPowerLevel(myUserId)) { setSharedPower(power); return; } changePower(power); }; const handleSelfDemote = (power: number) => { setSelfDemote(undefined); changePower(power); }; const handleSharedPower = (power: number) => { setSharedPower(undefined); changePower(power); }; return ( <> isKeyHotkey('arrowdown', evt), isKeyBackward: (evt: KeyboardEvent) => isKeyHotkey('arrowup', evt), }} > {error && ( Error: {powerState.error.name} {powerState.error.message} )} {getPowers(powerLevelTags).map((power) => { const powerTag = powerLevelTags[power]; const powerTagIconSrc = powerTag.icon && getPowerTagIconSrc(mx, useAuthentication, powerTag.icon); const selected = getMemberPowerLevel(userId) === power; const canAssignPower = creators.has(myUserId) ? true : power <= getMemberPowerLevel(myUserId); return ( } after={ powerTagIconSrc ? ( ) : undefined } onClick={ canChangePowers && canAssignPower ? () => handlePowerSelect(power) : undefined } > {powerTag.name} ); })}
{ if (room.isSpaceRoom()) { openSpaceSettings( room.roomId, space?.roomId, SpaceSettingsPage.PermissionsPage ); } else { openRoomSettings( room.roomId, space?.roomId, RoomSettingsPage.PermissionsPage ); } close(); }} > Manage Powers
} > ) : ( <> {!changing && } {changing && } ) } after={tagIconSrc ? : undefined} onClick={open} aria-pressed={!!cords} > {tag.name}
{typeof selfDemote === 'number' ? ( setSelfDemote(undefined)} onChange={handleSelfDemote} /> ) : null} {typeof sharedPower === 'number' ? ( setSharedPower(undefined)} onChange={handleSharedPower} /> ) : null} ); }