Add a context menu option to view a user's raw extended profile fields

This commit is contained in:
Ginger 2025-10-06 14:02:50 -04:00
parent d42bcc6e3d
commit 205ea1655a
No known key found for this signature in database
2 changed files with 116 additions and 60 deletions

View file

@ -22,6 +22,10 @@ import {
TooltipProvider, TooltipProvider,
Tooltip, Tooltip,
Badge, Badge,
Overlay,
OverlayBackdrop,
OverlayCenter,
Modal,
} from 'folds'; } from 'folds';
import { useMatrixClient } from '../../hooks/useMatrixClient'; import { useMatrixClient } from '../../hooks/useMatrixClient';
import { getMxIdServer } from '../../utils/matrix'; import { getMxIdServer } from '../../utils/matrix';
@ -45,6 +49,10 @@ import { useIgnoredUsers } from '../../hooks/useIgnoredUsers';
import { CutoutCard } from '../cutout-card'; import { CutoutCard } from '../cutout-card';
import { SettingTile } from '../setting-tile'; import { SettingTile } from '../setting-tile';
import { useInterval } from '../../hooks/useInterval'; import { useInterval } from '../../hooks/useInterval';
import { TextViewer } from '../text-viewer';
import { ExtendedProfile } from '../../hooks/useExtendedProfile';
import { settingsAtom } from '../../state/settings';
import { useSetting } from '../../state/hooks/settings';
export function ServerChip({ server }: { server: string }) { export function ServerChip({ server }: { server: string }) {
const mx = useMatrixClient(); const mx = useMatrixClient();
@ -440,15 +448,24 @@ export function IgnoredUserAlert() {
); );
} }
export function OptionsChip({ userId }: { userId: string }) { export function OptionsChip({
userId,
extendedProfile,
}: {
userId: string;
extendedProfile: ExtendedProfile | null;
}) {
const mx = useMatrixClient(); const mx = useMatrixClient();
const [cords, setCords] = useState<RectCords>(); const [developerToolsEnabled] = useSetting(settingsAtom, 'developerTools');
const open: MouseEventHandler<HTMLButtonElement> = (evt) => { const [profileFieldsOpen, setProfileFieldsOpen] = useState(false);
setCords(evt.currentTarget.getBoundingClientRect()); const [menuCoords, setMenuCoords] = useState<RectCords>();
const openMenu: MouseEventHandler<HTMLButtonElement> = (evt) => {
setMenuCoords(evt.currentTarget.getBoundingClientRect());
}; };
const close = () => setCords(undefined); const closeMenu = () => setMenuCoords(undefined);
const ignoredUsers = useIgnoredUsers(); const ignoredUsers = useIgnoredUsers();
const ignored = ignoredUsers.includes(userId); const ignored = ignoredUsers.includes(userId);
@ -463,8 +480,31 @@ export function OptionsChip({ userId }: { userId: string }) {
const ignoring = ignoreState.status === AsyncStatus.Loading; const ignoring = ignoreState.status === AsyncStatus.Loading;
return ( return (
<>
{extendedProfile && (
<Overlay open={profileFieldsOpen} backdrop={<OverlayBackdrop />}>
<OverlayCenter>
<FocusTrap
focusTrapOptions={{
clickOutsideDeactivates: true,
onDeactivate: () => setProfileFieldsOpen(false),
escapeDeactivates: stopPropagation,
}}
>
<Modal variant="Surface" size="500">
<TextViewer
name="Profile Fields"
langName="json"
text={JSON.stringify(extendedProfile, null, 2)}
requestClose={() => setProfileFieldsOpen(false)}
/>
</Modal>
</FocusTrap>
</OverlayCenter>
</Overlay>
)}
<PopOut <PopOut
anchor={cords} anchor={menuCoords}
position="Bottom" position="Bottom"
align="Start" align="Start"
offset={4} offset={4}
@ -472,7 +512,7 @@ export function OptionsChip({ userId }: { userId: string }) {
<FocusTrap <FocusTrap
focusTrapOptions={{ focusTrapOptions={{
initialFocus: false, initialFocus: false,
onDeactivate: close, onDeactivate: closeMenu,
clickOutsideDeactivates: true, clickOutsideDeactivates: true,
escapeDeactivates: stopPropagation, escapeDeactivates: stopPropagation,
isKeyForward: (evt: KeyboardEvent) => isKeyHotkey('arrowdown', evt), isKeyForward: (evt: KeyboardEvent) => isKeyHotkey('arrowdown', evt),
@ -488,7 +528,7 @@ export function OptionsChip({ userId }: { userId: string }) {
radii="300" radii="300"
onClick={() => { onClick={() => {
toggleIgnore(); toggleIgnore();
close(); closeMenu();
}} }}
before={ before={
ignoring ? ( ignoring ? (
@ -501,12 +541,27 @@ export function OptionsChip({ userId }: { userId: string }) {
> >
<Text size="B300">{ignored ? 'Unblock User' : 'Block User'}</Text> <Text size="B300">{ignored ? 'Unblock User' : 'Block User'}</Text>
</MenuItem> </MenuItem>
{extendedProfile && developerToolsEnabled && (
<MenuItem
variant="Surface"
fill="None"
size="300"
radii="300"
onClick={() => {
setProfileFieldsOpen(true);
closeMenu();
}}
before={<Icon size="50" src={Icons.BlockCode} />}
>
<Text size="B300">View Profile Fields</Text>
</MenuItem>
)}
</div> </div>
</Menu> </Menu>
</FocusTrap> </FocusTrap>
} }
> >
<Chip variant="SurfaceVariant" radii="Pill" onClick={open} aria-pressed={!!cords}> <Chip variant="SurfaceVariant" radii="Pill" onClick={openMenu} aria-pressed={!!menuCoords}>
{ignoring ? ( {ignoring ? (
<Spinner variant="Secondary" size="50" /> <Spinner variant="Secondary" size="50" />
) : ( ) : (
@ -514,6 +569,7 @@ export function OptionsChip({ userId }: { userId: string }) {
)} )}
</Chip> </Chip>
</PopOut> </PopOut>
</>
); );
} }
@ -555,8 +611,8 @@ export function TimezoneChip({ timezone }: { timezone: string }) {
offset={5} offset={5}
align="Center" align="Center"
tooltip={ tooltip={
<Tooltip variant='SurfaceVariant' style={{ maxWidth: toRem(280) }}> <Tooltip variant="SurfaceVariant" style={{ maxWidth: toRem(280) }}>
<Box direction="Column" alignItems='Start' gap="100"> <Box direction="Column" alignItems="Start" gap="100">
<Box gap="100"> <Box gap="100">
<Text size="L400">Timezone:</Text> <Text size="L400">Timezone:</Text>
<Badge size="400" variant="Primary"> <Badge size="400" variant="Primary">
@ -573,7 +629,7 @@ export function TimezoneChip({ timezone }: { timezone: string }) {
ref={triggerRef} ref={triggerRef}
variant="SurfaceVariant" variant="SurfaceVariant"
radii="Pill" radii="Pill"
style={{ cursor: "initial" }} style={{ cursor: 'initial' }}
before={<Icon size="50" src={Icons.RecentClock} />} before={<Icon size="50" src={Icons.RecentClock} />}
> >
<Text size="B300" truncate> <Text size="B300" truncate>

View file

@ -115,7 +115,7 @@ export function UserRoomProfile({ userId }: UserRoomProfileProps) {
{timezone && <TimezoneChip timezone={timezone} />} {timezone && <TimezoneChip timezone={timezone} />}
{creator ? <CreatorChip /> : <PowerChip userId={userId} />} {creator ? <CreatorChip /> : <PowerChip userId={userId} />}
{userId !== myUserId && <MutualRoomsChip userId={userId} />} {userId !== myUserId && <MutualRoomsChip userId={userId} />}
{userId !== myUserId && <OptionsChip userId={userId} />} {userId !== myUserId && <OptionsChip userId={userId} extendedProfile={extendedProfile ?? null} />}
</Box> </Box>
</Box> </Box>
{ignored && <IgnoredUserAlert />} {ignored && <IgnoredUserAlert />}