vojo/src/app/pages/client/direct/DirectSelfRow.tsx

137 lines
5.3 KiB
TypeScript

import React from 'react';
import { useTranslation } from 'react-i18next';
import { useNavigate } from 'react-router-dom';
import { Avatar, Box, Icon, Icons, Text, color, config, toRem } from 'folds';
import { NavButton, NavItem, NavItemContent } from '../../../components/nav';
import { UserAvatar } from '../../../components/user-avatar';
import { useMatrixClient } from '../../../hooks/useMatrixClient';
import { useUserProfile } from '../../../hooks/useUserProfile';
import { useMediaAuthentication } from '../../../hooks/useMediaAuthentication';
import { getMxIdLocalPart, mxcUrlToHttp } from '../../../utils/matrix';
import { nameInitials } from '../../../utils/common';
import { ScreenSize, useScreenSizeContext } from '../../../hooks/useScreenSize';
import { useOpenSettingsSheet } from '../../../state/hooks/settingsSheet';
import { SETTINGS_SHEET_DRAG_ORIGIN_ATTR } from '../../../features/settings/MobileSettingsHorseshoe';
import { SETTINGS_FROM_IN_APP_STATE } from '../../../features/settings/SettingsScreen';
import { getSettingsPath } from '../../pathUtils';
const MONO_FONT = '"JetBrains Mono Variable", ui-monospace, monospace';
const ROW_MIN_HEIGHT = toRem(68);
export function DirectSelfRow() {
const { t } = useTranslation();
const mx = useMatrixClient();
const useAuthentication = useMediaAuthentication();
const navigate = useNavigate();
const screenSize = useScreenSizeContext();
const openSheet = useOpenSettingsSheet();
const userId = mx.getSafeUserId();
const profile = useUserProfile(userId);
// Mobile: open the atom-driven bottom-up sheet so the DM list stays
// visible above with a void gap (the )|( horseshoe). The
// MobileSettingsHorseshoe wrapper owns the drag/animation; the tap
// just commits to fully-open.
//
// Desktop: navigate to `/settings` — the route renders Settings in
// the right pane of the nested horseshoe (DM list still on the left).
const handleOpen = () => {
if (screenSize === ScreenSize.Mobile) {
openSheet();
} else {
navigate(getSettingsPath(), { state: SETTINGS_FROM_IN_APP_STATE });
}
};
const displayName = profile.displayName ?? getMxIdLocalPart(userId) ?? userId;
const avatarUrl = profile.avatarUrl
? mxcUrlToHttp(mx, profile.avatarUrl, useAuthentication, 96, 96, 'crop') ?? undefined
: undefined;
// `data-settings-drag-origin` is the marker the mobile horseshoe
// wrapper uses to detect drag-up gestures starting on this row.
// Document-level touch/pointer listeners in `MobileSettingsHorseshoe`
// check `target.closest('[data-settings-drag-origin]')` so any
// touch landing inside this Box (anywhere on the row) starts the
// open-drag while still leaving the row's own onClick handler
// free to fire when there's no movement (tap → openSheet).
return (
<Box
shrink="No"
{...{ [SETTINGS_SHEET_DRAG_ORIGIN_ATTR]: true }}
style={{
padding: `${toRem(6)} ${config.space.S100}`,
borderTop: `${config.borderWidth.B300} solid ${color.Surface.ContainerLine}`,
}}
>
<NavItem variant="Background" radii="400" style={{ minHeight: ROW_MIN_HEIGHT }}>
<NavButton onClick={handleOpen} aria-label={t('Direct.self_row_preview')}>
<NavItemContent>
<Box
as="span"
grow="Yes"
alignItems="Center"
gap="300"
style={{
minHeight: ROW_MIN_HEIGHT,
boxSizing: 'border-box',
padding: `${toRem(8)} 0`,
}}
>
<Avatar size="300" radii="400">
<UserAvatar
userId={userId}
src={avatarUrl}
renderFallback={() => (
<Text as="span" size="H6">
{nameInitials(displayName)}
</Text>
)}
/>
</Avatar>
<Box
as="span"
direction="Column"
grow="Yes"
gap="100"
style={{ minWidth: 0, overflow: 'hidden' }}
>
<Box as="span" alignItems="Baseline" gap="100" style={{ minWidth: 0 }}>
<Text as="span" size="T300" truncate style={{ fontWeight: 600, flexShrink: 1 }}>
{t('Direct.self_row_label')}
</Text>
<span
style={{
fontFamily: MONO_FONT,
fontSize: toRem(11),
color: color.Surface.OnContainer,
opacity: 0.65,
whiteSpace: 'nowrap',
overflow: 'hidden',
textOverflow: 'ellipsis',
flexShrink: 1,
minWidth: 0,
}}
>
{userId}
</span>
</Box>
<Text as="span" size="T200" truncate style={{ opacity: 0.6 }}>
{t('Direct.self_row_preview')}
</Text>
</Box>
<Icon
src={Icons.Setting}
size="100"
style={{
opacity: 0.55,
flexShrink: 0,
}}
/>
</Box>
</NavItemContent>
</NavButton>
</NavItem>
</Box>
);
}