Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/fix-badge-positioning.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'default': patch
---

Fix badge positioning and alignment across all sidebar components
1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
v24.14.0
13 changes: 7 additions & 6 deletions src/app/components/message/layout/layout.css.ts
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@ export const PronounPill = style({
export const MessageTextBody = recipe({
base: {
wordBreak: 'break-word',
fontSize: '1rem !important', // Override folds Text component to enable page zoom scaling
},
variants: {
preWrap: {
Expand All @@ -229,27 +230,27 @@ export const MessageTextBody = recipe({
},
jumboEmoji: {
none: {
fontSize: '1em',
fontSize: '1rem !important',
lineHeight: 'inherit',
},
extraSmall: {
fontSize: '1.25em',
fontSize: '1.25rem !important',
lineHeight: '1.4em',
},
small: {
fontSize: '1.5em',
fontSize: '1.5rem !important',
lineHeight: '1.5em',
},
normal: {
fontSize: '1.805em',
fontSize: '1.805rem !important',
lineHeight: '1.625em',
},
large: {
fontSize: '2.1em',
fontSize: '2.1rem !important',
lineHeight: '1.675em',
},
extraLarge: {
fontSize: '2.4em',
fontSize: '2.4rem !important',
lineHeight: '1.7em',
},
},
Expand Down
33 changes: 33 additions & 0 deletions src/app/components/sidebar/Sidebar.css.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,39 @@ export const SidebarItemBadge = recipe({
defaultVariants: {
hasCount: false,
},
// Adjust positioning when badge is on a folder (closed folder button)
compoundVariants: [
{
variants: {
hasCount: true,
},
style: {
selectors: {
// When the badge is within an item containing a closed folder, adjust position
// to align with the folder's square border
'div:has(> button[data-id]) &': {
top: toRem(0),
left: 'auto',
right: toRem(0),
},
},
},
},
{
variants: {
hasCount: false,
},
style: {
selectors: {
'div:has(> button[data-id]) &': {
top: toRem(2),
left: 'auto',
right: toRem(2),
},
},
},
},
],
});
export type SidebarItemBadgeVariants = RecipeVariants<typeof SidebarItemBadge>;

Expand Down
2 changes: 1 addition & 1 deletion src/app/pages/client/sidebar/AccountSwitcherTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ export function AccountSwitcherTab() {
)}
</SidebarItemTooltip>
{(totalBackgroundUnread > 0 || anyBackgroundHighlight) && (
<SidebarItemBadge hasCount>
<SidebarItemBadge hasCount style={{ left: toRem(-6), right: 'auto' }}>
<UnreadBadge
highlight={anyBackgroundHighlight}
count={anyBackgroundHighlight ? totalBackgroundHighlight : totalBackgroundUnread}
Expand Down
139 changes: 91 additions & 48 deletions src/app/pages/client/sidebar/DirectDMsList.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useMemo, useState, useCallback } from 'react';
import { useNavigate } from 'react-router-dom';
import { Avatar, Text, Box } from 'folds';
import { Avatar, Text, Box, toRem } from 'folds';
import { useAtomValue } from 'jotai';
import { Room, SyncState } from '$types/matrix-sdk';
import { useDirects } from '$state/hooks/roomList';
Expand Down Expand Up @@ -56,62 +56,105 @@ function DMItem({ room, selected }: DMItemProps) {
// Get unread info for badge
const unread = roomToUnread.get(room.roomId);

return (
<SidebarItem active={selected}>
<SidebarItemTooltip tooltip={room.name}>
{(triggerRef) => (
<SidebarAvatar as="button" ref={triggerRef} outlined onClick={handleClick} size="400">
{isGroupDM ? (
<Box className={css.GroupAvatarContainer}>
<Box className={css.GroupAvatarRow}>
{groupMembers.map((member) => {
const avatarUrl = member.avatarUrl
? (mxcUrlToHttp(mx, member.avatarUrl, useAuthentication, 48, 48, 'crop') ??
undefined)
: undefined;

return (
<Avatar
key={member.userId}
size="200"
radii="300"
className={css.GroupAvatar}
>
<UserAvatar
userId={member.userId}
src={avatarUrl}
alt={member.displayName || member.userId}
renderFallback={() => (
<Text as="span" size="T300">
{nameInitials(member.displayName || member.userId)}
</Text>
)}
/>
</Avatar>
);
})}
</Box>
</Box>
) : (
<Avatar size="400" radii="400">
<RoomAvatar
roomId={room.roomId}
src={getDirectRoomAvatarUrl(mx, room, 96, useAuthentication)}
alt={room.name}
// Determine avatar src for single group DM member to avoid nested ternary
const getSingleMemberAvatarSrc = () => {
if (groupMembers.length !== 1 || !groupMembers[0].avatarUrl) {
return undefined;
}
return (
mxcUrlToHttp(mx, groupMembers[0].avatarUrl, useAuthentication, 96, 96, 'crop') ?? undefined
);
};

// Render appropriate avatar based on DM type
const renderAvatar = () => {
if (!isGroupDM) {
// Regular DM
return (
<Avatar size="400" radii="400">
<RoomAvatar
roomId={room.roomId}
src={getDirectRoomAvatarUrl(mx, room, 96, useAuthentication)}
alt={room.name}
renderFallback={() => (
<Text as="span" size="H6">
{nameInitials(room.name)}
</Text>
)}
/>
</Avatar>
);
}

if (groupMembers.length === 1) {
// Single member in group DM - fill the space like a normal DM
return (
<Avatar size="400" radii="400">
<UserAvatar
userId={groupMembers[0].userId}
src={getSingleMemberAvatarSrc()}
alt={groupMembers[0].displayName || groupMembers[0].userId}
renderFallback={() => (
<Text as="span" size="H6">
{nameInitials(groupMembers[0].displayName || groupMembers[0].userId)}
</Text>
)}
/>
</Avatar>
);
}

// Multiple members in group DM - triangle layout
return (
<Box className={css.GroupAvatarContainer}>
<Box className={css.GroupAvatarRow}>
{groupMembers.map((member) => {
const avatarUrl = member.avatarUrl
? (mxcUrlToHttp(mx, member.avatarUrl, useAuthentication, 48, 48, 'crop') ?? undefined)
: undefined;

return (
<Avatar key={member.userId} size="200" radii="300" className={css.GroupAvatar}>
<UserAvatar
userId={member.userId}
src={avatarUrl}
alt={member.displayName || member.userId}
renderFallback={() => (
<Text as="span" size="H6">
{nameInitials(room.name)}
<Text as="span" size="T300">
{nameInitials(member.displayName || member.userId)}
</Text>
)}
/>
</Avatar>
)}
);
})}
</Box>
</Box>
);
};

return (
<SidebarItem active={selected}>
<SidebarItemTooltip tooltip={room.name}>
{(triggerRef) => (
<SidebarAvatar as="button" ref={triggerRef} outlined onClick={handleClick} size="400">
{renderAvatar()}
</SidebarAvatar>
)}
</SidebarItemTooltip>
{unread && (unread.total > 0 || unread.highlight > 0) && (
<SidebarItemBadge hasCount={unread.total > 0}>
<UnreadBadge highlight={unread.highlight > 0} count={unread.total} dm />
<SidebarItemBadge
hasCount={unread.total > 0}
style={{
left: unread.total > 0 ? toRem(-6) : toRem(-2),
right: 'auto',
}}
>
<UnreadBadge
highlight={unread.highlight > 0}
count={unread.highlight > 0 ? unread.highlight : unread.total}
dm
/>
</SidebarItemBadge>
)}
</SidebarItem>
Expand Down
8 changes: 7 additions & 1 deletion src/app/pages/client/sidebar/DirectTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,13 @@ export function DirectTab() {
)}
</SidebarItemTooltip>
{directUnread && (
<SidebarItemBadge hasCount={directUnread.total > 0}>
<SidebarItemBadge
hasCount={directUnread.total > 0}
style={{
left: directUnread.total > 0 ? toRem(-6) : toRem(-2),
right: 'auto',
}}
>
<UnreadBadge
highlight={directUnread.highlight > 0}
count={directUnread.highlight > 0 ? directUnread.highlight : directUnread.total}
Expand Down
8 changes: 7 additions & 1 deletion src/app/pages/client/sidebar/HomeTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,13 @@ export function HomeTab() {
)}
</SidebarItemTooltip>
{homeUnread && (
<SidebarItemBadge hasCount={homeUnread.total > 0}>
<SidebarItemBadge
hasCount={homeUnread.total > 0}
style={{
left: homeUnread.total > 0 ? toRem(-6) : toRem(-2),
right: 'auto',
}}
>
<UnreadBadge
highlight={homeUnread.highlight > 0}
count={homeUnread.highlight > 0 ? homeUnread.highlight : homeUnread.total}
Expand Down
4 changes: 2 additions & 2 deletions src/app/pages/client/sidebar/InboxTab.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useNavigate } from 'react-router-dom';
import { Icon, Icons } from 'folds';
import { Icon, Icons, toRem } from 'folds';
import { useAtomValue } from 'jotai';
import {
SidebarAvatar,
Expand Down Expand Up @@ -52,7 +52,7 @@ export function InboxTab() {
)}
</SidebarItemTooltip>
{inviteCount > 0 && (
<SidebarItemBadge hasCount>
<SidebarItemBadge hasCount style={{ left: toRem(-6), right: 'auto' }}>
<UnreadBadge highlight count={inviteCount} />
</SidebarItemBadge>
)}
Expand Down
16 changes: 14 additions & 2 deletions src/app/pages/client/sidebar/SpaceTabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -465,7 +465,13 @@ function SpaceTab({
)}
</SidebarItemTooltip>
{unread && (
<SidebarItemBadge hasCount={unread.total > 0}>
<SidebarItemBadge
hasCount={unread.total > 0}
style={{
left: unread.total > 0 ? toRem(-6) : toRem(-2),
right: 'auto',
}}
>
<UnreadBadge
highlight={unread.highlight > 0}
count={unread.highlight > 0 ? unread.highlight : unread.total}
Expand Down Expand Up @@ -600,7 +606,13 @@ function ClosedSpaceFolder({
)}
</SidebarItemTooltip>
{unread && (
<SidebarItemBadge hasCount={unread.total > 0}>
<SidebarItemBadge
hasCount={unread.total > 0}
style={{
left: unread.total > 0 ? toRem(-6) : toRem(-2),
right: 'auto',
}}
>
<UnreadBadge
highlight={unread.highlight > 0}
count={unread.highlight > 0 ? unread.highlight : unread.total}
Expand Down
4 changes: 2 additions & 2 deletions src/app/pages/client/sidebar/UnverifiedTab.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useState } from 'react';
import { Badge, color, Icon, Icons, Text } from 'folds';
import { Badge, color, Icon, Icons, Text, toRem } from 'folds';
import {
SidebarAvatar,
SidebarItem,
Expand Down Expand Up @@ -66,7 +66,7 @@ function UnverifiedIndicator() {
)}
</SidebarItemTooltip>
{!unverified && unverifiedDeviceCount && unverifiedDeviceCount > 0 && (
<SidebarItemBadge hasCount>
<SidebarItemBadge hasCount style={{ left: toRem(-6), right: 'auto' }}>
<Badge variant="Warning" size="400" fill="Solid" radii="Pill" outlined={false}>
<Text as="span" size="L400">
{unverifiedDeviceCount}
Expand Down
Loading