Files
cinny/src/app/components/user-profile/UserHero.tsx

157 lines
5.1 KiB
TypeScript

import React, { useState } from 'react';
import { Avatar, Box, Modal, Overlay, OverlayBackdrop, OverlayCenter, Text, toRem } from 'folds';
import { Icon, Icons } from '../icons';
import classNames from 'classnames';
import FocusTrap from 'focus-trap-react';
import * as css from './styles.css';
import { UserAvatar } from '../user-avatar';
import colorMXID from '../../../util/colorMXID';
import { getMxIdLocalPart } from '../../utils/matrix';
import { BreakWord, LineClamp3 } from '../../styles/Text.css';
import { UserPresence } from '../../hooks/useUserPresence';
import { AvatarPresence, PresenceBadge } from '../presence';
import { ImageViewer } from '../image-viewer';
import { stopPropagation } from '../../utils/keyboard';
import { useOtherUserColor } from '../../hooks/useUserColor';
import { useOtherUserBanner } from '../../hooks/useUserBanner';
import { useOtherUserProfileStyle } from '../../hooks/useUserProfileStyle';
type UserHeroProps = {
userId: string;
avatarUrl?: string;
avatarMxc?: string;
presence?: UserPresence;
};
export function UserHero({ userId, avatarUrl, avatarMxc, presence }: UserHeroProps) {
const [viewAvatar, setViewAvatar] = useState<string>();
const bannerUrl = useOtherUserBanner(userId, avatarMxc); // Now returns blob URL directly
const profileStyle = useOtherUserProfileStyle(userId, avatarMxc);
console.log('[UserHero]', {
userId,
avatarMxc,
bannerUrl,
profileStyle,
});
return (
<Box
direction="Column"
className={css.UserHero}
>
<div
className={css.UserHeroCoverContainer}
style={{
backgroundColor: bannerUrl ? undefined : colorMXID(userId),
filter: bannerUrl || avatarUrl ? undefined : 'brightness(50%)',
}}
>
{bannerUrl ? (
<img className={css.UserHeroBanner} src={bannerUrl} alt={userId} draggable="false" />
) : (
avatarUrl && (
<img className={css.UserHeroCover} src={avatarUrl} alt={userId} draggable="false" />
)
)}
</div>
<div className={css.UserHeroAvatarContainer}>
<AvatarPresence
className={css.UserAvatarContainer}
style={profileStyle.avatarBorderColor ? { backgroundColor: profileStyle.avatarBorderColor } : undefined}
badgeBackgroundColor={profileStyle.avatarBorderColor}
badge={
presence && <PresenceBadge presence={presence.presence} status={presence.status} />
}
>
<Avatar
as={avatarUrl ? 'button' : 'div'}
onClick={avatarUrl ? () => setViewAvatar(avatarUrl) : undefined}
className={css.UserHeroAvatar}
size="500"
style={{
width: toRem(72),
height: toRem(72),
...(profileStyle.avatarBorderColor ? {
outline: `${toRem(4)} solid ${profileStyle.avatarBorderColor}`,
outlineOffset: toRem(-1),
} : {}),
}}
>
<UserAvatar
className={css.UserHeroAvatarImg}
userId={userId}
src={avatarUrl}
alt={userId}
renderFallback={() => <Icon size="500" src={Icons.User} filled />}
/>
</Avatar>
</AvatarPresence>
{viewAvatar && (
<Overlay open backdrop={<OverlayBackdrop />}>
<OverlayCenter>
<FocusTrap
focusTrapOptions={{
initialFocus: false,
onDeactivate: () => setViewAvatar(undefined),
clickOutsideDeactivates: true,
escapeDeactivates: stopPropagation,
}}
>
<Modal size="500" onContextMenu={(evt: React.MouseEvent) => evt.stopPropagation()}>
<ImageViewer
src={viewAvatar}
alt={userId}
requestClose={() => setViewAvatar(undefined)}
/>
</Modal>
</FocusTrap>
</OverlayCenter>
</Overlay>
)}
</div>
</Box>
);
}
type UserHeroNameProps = {
displayName?: string;
userId: string;
avatarMxc?: string;
};
export function UserHeroName({ displayName, userId, avatarMxc }: UserHeroNameProps) {
const username = getMxIdLocalPart(userId);
const userColor = useOtherUserColor(userId, avatarMxc);
console.log('[UserHeroName]', {
userId,
avatarMxc,
userColor,
fallbackColor: colorMXID(userId),
});
return (
<Box grow="Yes" direction="Column" gap="100">
<Box alignItems="Baseline" gap="200" wrap="Wrap">
<Text
size="H4"
className={classNames(BreakWord, LineClamp3)}
title={displayName ?? username}
style={{ color: userColor || colorMXID(userId) }}
>
{displayName ?? username ?? userId}
</Text>
</Box>
<Box alignItems="Center" gap="100" wrap="Wrap">
<Text
size="T200"
className={BreakWord}
title={userId}
style={{ color: userColor || colorMXID(userId) }}
>
{userId}
</Text>
</Box>
</Box>
);
}