feat(profile): merge 1:1 header avatar and title into single identity button so name taps open the profile sheet

This commit is contained in:
heaven 2026-05-11 14:09:07 +03:00
parent 4d0b508ebb
commit e3e61afd4c
2 changed files with 79 additions and 50 deletions

View file

@ -11,19 +11,31 @@ export const HeaderShell = style({
paddingLeft: config.space.S200,
});
// Tap-target wrapper for the peer avatar — opens the user-room-profile sheet
// in 1:1 chrome. Reset native button styling so it sits flush with the
// surrounding header row.
export const PeerAvatarTrigger = style({
// Tap-target wrapper for the peer identity (avatar + title block) — opens
// the user-room-profile sheet in 1:1 chrome. Wrapping both the avatar and
// the title together gives one keyboard tab stop and one focus outline
// around the entire identity area, matching WhatsApp/Telegram chat-header
// behaviour where tapping the room name also opens contact info.
//
// Reset native button styling so the contents sit flush with the
// surrounding header row; the inner gap mirrors the parent row's `gap=200`
// so the avatar→title spacing is unchanged. `flex-grow: 1` + `min-width: 0`
// replaces the title Box's role of taking the remaining horizontal space
// and allowing the room name to truncate.
export const PeerIdentityTrigger = style({
background: 'transparent',
border: 'none',
padding: 0,
cursor: 'pointer',
display: 'inline-flex',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
borderRadius: '50%',
flexShrink: 0,
gap: config.space.S200,
flexGrow: 1,
minWidth: 0,
textAlign: 'left',
font: 'inherit',
color: 'inherit',
borderRadius: toRem(8),
':focus-visible': {
outline: `2px solid ${color.Primary.Main}`,
outlineOffset: 2,

View file

@ -572,6 +572,64 @@ export function RoomViewHeaderDm({ callView }: { callView?: boolean }) {
const showHandlePart = isOneOnOne && !!handle;
const showGroupSubline = !isOneOnOne;
// Title block (name row + subline) — identical for 1:1 and group; the
// difference is purely in the wrapper (clickable identity button for
// 1:1, plain row for groups).
const titleBlock = (
<Box grow="Yes" direction="Column" style={{ minWidth: 0 }}>
<div className={css.TitleRow}>
<Text size={isMobile ? 'H5' : 'H4'} truncate className={css.RoomName}>
{name}
</Text>
{showOnlineTag && (
<Text as="span" size="T200" className={css.OnlineTag}>
{t('Room.status_online')}
</Text>
)}
</div>
{showHandlePart && (
<Text as="span" size="T200" className={css.Subline}>
<span className={css.SublineMuted}>{handle}</span>
{e2eeChip}
</Text>
)}
{showGroupSubline && (
<Text as="span" size="T200" className={css.Subline}>
<span className={css.SublineMuted}>
{t('Room.members_count', {
count: memberCount,
formattedCount: memberCount,
})}
</span>
{e2eeChip}
</Text>
)}
</Box>
);
// For 1:1 rooms wrap the avatar AND title in a single button so a tap
// anywhere on the user identity (avatar, name, handle, online tag)
// opens the user-room-profile sheet — one tab stop, one focus
// outline, single popout anchor. Groups keep the avatar non-clickable
// and the title as a plain row.
const identityArea =
isOneOnOne && peerUserId ? (
<button
type="button"
className={css.PeerIdentityTrigger}
onClick={handlePeerProfile}
aria-label={t('Room.open_profile_of', { name })}
>
{avatarNode}
{titleBlock}
</button>
) : (
<>
<span className={css.PeerAvatarStatic}>{avatarNode}</span>
{titleBlock}
</>
);
return (
<PageHeader
className={`${ContainerColor({ variant: 'Surface' })} ${css.HeaderShell}`}
@ -594,48 +652,7 @@ export function RoomViewHeaderDm({ callView }: { callView?: boolean }) {
</BackRouteHandler>
)}
{isOneOnOne && peerUserId ? (
<button
type="button"
className={css.PeerAvatarTrigger}
onClick={handlePeerProfile}
aria-label={t('Room.open_profile_of', { name })}
>
{avatarNode}
</button>
) : (
<span className={css.PeerAvatarStatic}>{avatarNode}</span>
)}
<Box grow="Yes" direction="Column" style={{ minWidth: 0 }}>
<div className={css.TitleRow}>
<Text size={isMobile ? 'H5' : 'H4'} truncate className={css.RoomName}>
{name}
</Text>
{showOnlineTag && (
<Text as="span" size="T200" className={css.OnlineTag}>
{t('Room.status_online')}
</Text>
)}
</div>
{showHandlePart && (
<Text as="span" size="T200" className={css.Subline}>
<span className={css.SublineMuted}>{handle}</span>
{e2eeChip}
</Text>
)}
{showGroupSubline && (
<Text as="span" size="T200" className={css.Subline}>
<span className={css.SublineMuted}>
{t('Room.members_count', {
count: memberCount,
formattedCount: memberCount,
})}
</span>
{e2eeChip}
</Text>
)}
</Box>
{identityArea}
<Box shrink="No" alignItems="Center">
{callButtonVisible && <DmCallButton room={room} />}