Skip to content

Commit 07d487d

Browse files
authored
Redesign the user menu tab (#990)
<!-- Please read https://github.com/SableClient/Sable/blob/dev/CONTRIBUTING.md before submitting your pull request --> ### Description Redesigns the user tab to allow switching presence status. Shows full user profile. Fixes # #### Type of change - [ ] Bug fix (non-breaking change which fixes an issue) - [x] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) - [ ] This change requires a documentation update ### Checklist: - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my own code - [ ] I have commented my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [x] My changes generate no new warnings ### AI disclosure: - [ ] Partially AI assisted (clarify which code was AI assisted and briefly explain what it does). - [ ] Fully AI generated (explain what all the generated code does in moderate detail). Written with the assistance of a magic 8 ball
2 parents 4c1c522 + 8fc7504 commit 07d487d

9 files changed

Lines changed: 790 additions & 455 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
default: minor
3+
---
4+
5+
Redesign the user menu tab

src/app/components/presence/Presence.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { useId } from 'react';
55
import { Presence, usePresenceLabel } from '$hooks/useUserPresence';
66
import * as css from './styles.css';
77

8-
const PresenceToColor: Record<Presence, MainColor> = {
8+
export const PresenceToColor: Record<Presence, MainColor> = {
99
[Presence.Online]: 'Success',
1010
[Presence.Unavailable]: 'Warning',
1111
[Presence.Offline]: 'Secondary',

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

Lines changed: 97 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
Tooltip,
1414
toRem,
1515
Chip,
16+
config,
1617
} from 'folds';
1718
import classNames from 'classnames';
1819
import FocusTrap from 'focus-trap-react';
@@ -42,15 +43,26 @@ import * as css from './styles.css';
4243
import { copyToClipboard } from '$utils/dom';
4344
import { useTimeoutToggle } from '$hooks/useTimeoutToggle';
4445
import { CopyIcon, CrossIcon } from '@phosphor-icons/react';
46+
import { useOpenSettings } from '$features/settings';
4547

4648
type UserHeroProps = {
4749
userId: string;
4850
avatarUrl?: string;
4951
bannerUrl?: string;
5052
presence?: UserPresence;
5153
autoplayGifs?: boolean;
54+
showColor?: boolean;
55+
allowEditing?: boolean;
5256
};
53-
export function UserHero({ userId, avatarUrl, bannerUrl, presence, autoplayGifs }: UserHeroProps) {
57+
export function UserHero({
58+
userId,
59+
avatarUrl,
60+
bannerUrl,
61+
presence,
62+
autoplayGifs,
63+
allowEditing = false,
64+
showColor = true,
65+
}: UserHeroProps) {
5466
const [viewAvatar, setViewAvatar] = useState<string>();
5567
const [isFullStatus, setIsFullStatus] = useState(false);
5668

@@ -96,9 +108,14 @@ export function UserHero({ userId, avatarUrl, bannerUrl, presence, autoplayGifs
96108
((fetchedBrightness === 'light' || areColorsTooSimilar('#FFFFFF', cardColor)) && '#000000') ||
97109
undefined;
98110
const statusHoverBrightness = fetchedBrightness === 'light' ? 0.94 : 1.08;
111+
const openSettings = useOpenSettings();
99112

100113
return (
101-
<Box direction="Column" className={css.UserHero} style={{ backgroundColor: backgroundColor }}>
114+
<Box
115+
direction="Column"
116+
className={css.UserHero}
117+
style={showColor ? { backgroundColor: backgroundColor } : {}}
118+
>
102119
<div
103120
className={css.UserHeroCoverContainer}
104121
style={{
@@ -164,22 +181,28 @@ export function UserHero({ userId, avatarUrl, bannerUrl, presence, autoplayGifs
164181
</Overlay>
165182
)}
166183
</div>
167-
{status && status.length > 0 && (
184+
{((status && status.length > 0) || allowEditing) && (
168185
<div className={css.UserHeroStatusContainer}>
169186
<Tooltip
170187
radii="400"
171188
variant="Surface"
172-
onClick={isExpandable ? () => setIsFullStatus(!isFullStatus) : undefined}
189+
role={allowEditing ? 'button' : undefined}
190+
onClick={
191+
allowEditing
192+
? () => openSettings('account', 'status')
193+
: isExpandable
194+
? () => setIsFullStatus(!isFullStatus)
195+
: undefined
196+
}
173197
className={classNames(
174198
css.UserHeroStatusTooltip,
175199
isExpandable && css.UserHeroStatusTooltipInteractive
176200
)}
177201
style={{
178202
maxHeight: isFullStatus ? toRem(105) : toRem(48),
179-
cursor: isExpandable ? 'pointer' : 'default',
180-
transform: 'none',
181-
transition: 'none',
203+
cursor: allowEditing || isExpandable ? 'pointer' : 'default',
182204
display: 'flex',
205+
width: 'fit-content',
183206
padding: `${toRem(8)} ${toRem(12)}`,
184207
backgroundColor: statusSurfaceColor,
185208
color: textColor,
@@ -195,8 +218,14 @@ export function UserHero({ userId, avatarUrl, bannerUrl, presence, autoplayGifs
195218
<Box direction="Row" gap="100" style={{ height: '100%', width: '100%' }}>
196219
{isFullStatus ? (
197220
<Scroll visibility="Hover" hideTrack style={{ height: '100%', flex: 1 }}>
198-
<Text size="T200" style={{ wordBreak: 'break-word' }}>
199-
{status}
221+
<Text
222+
size="T200"
223+
style={{
224+
wordBreak: 'break-word',
225+
fontStyle: allowEditing && !status ? 'italic' : 'normal',
226+
}}
227+
>
228+
{status || (allowEditing && "What's on your mind?")}
200229
</Text>
201230
</Scroll>
202231
) : (
@@ -208,9 +237,11 @@ export function UserHero({ userId, avatarUrl, bannerUrl, presence, autoplayGifs
208237
WebkitLineClamp: 2,
209238
WebkitBoxOrient: 'vertical',
210239
overflow: 'hidden',
240+
fontStyle: allowEditing && !status ? 'italic' : 'normal',
241+
opacity: allowEditing && !status ? config.opacity.Placeholder : 1,
211242
}}
212243
>
213-
{status}
244+
{status || (allowEditing && "What's on your mind?")}
214245
</Text>
215246
)}
216247

@@ -241,17 +272,29 @@ type UserHeroNameProps = {
241272
server?: string;
242273
customHeroCards?: boolean;
243274
};
244-
export function UserHeroName({ displayName, userId, server, customHeroCards }: UserHeroNameProps) {
245-
const username = getMxIdLocalPart(userId);
246-
const nick = useNickname(userId);
275+
276+
type UserHeroNameInnerProps = {
277+
shownName: string;
278+
username?: string;
279+
nick?: string;
280+
server?: string;
281+
color?: string;
282+
font?: string;
283+
customHeroCards?: boolean;
284+
};
285+
286+
function UserHeroNameInner({
287+
shownName,
288+
nick,
289+
username,
290+
server,
291+
color,
292+
font,
293+
}: UserHeroNameInnerProps) {
247294
const [copied, setCopied] = useTimeoutToggle();
248295
const [isHovered, setIsHovered] = useState(false);
249296
const isSuccess = useRef(false);
250297

251-
// Sable username color and fonts
252-
const { color, font } = useSableCosmetics(userId, useRoom(), customHeroCards);
253-
const shownName = nick ?? displayName ?? username ?? userId;
254-
255298
return (
256299
<Box grow="Yes" direction="Column" gap="0">
257300
<Box alignItems="Baseline" gap="200" wrap="Wrap">
@@ -296,3 +339,40 @@ export function UserHeroName({ displayName, userId, server, customHeroCards }: U
296339
</Box>
297340
);
298341
}
342+
343+
export function UserHeroName({ displayName, userId, server, customHeroCards }: UserHeroNameProps) {
344+
const username = getMxIdLocalPart(userId);
345+
const nick = useNickname(userId);
346+
347+
// Sable username color and fonts
348+
const { color, font } = useSableCosmetics(userId, useRoom(), customHeroCards);
349+
const shownName = nick ?? displayName ?? username ?? userId;
350+
351+
return (
352+
<UserHeroNameInner
353+
username={username}
354+
server={server}
355+
shownName={shownName}
356+
color={color}
357+
font={font}
358+
/>
359+
);
360+
}
361+
362+
export function GlobalUserHeroName({ displayName, userId, server }: UserHeroNameProps) {
363+
const username = getMxIdLocalPart(userId);
364+
const nick = useNickname(userId);
365+
const profile = useUserProfile(userId);
366+
367+
const shownName = nick ?? displayName ?? username ?? userId;
368+
369+
return (
370+
<UserHeroNameInner
371+
username={username}
372+
server={server}
373+
shownName={shownName}
374+
font={profile.resolvedFont}
375+
color={profile.resolvedColor}
376+
/>
377+
);
378+
}

src/app/pages/client/SidebarNav.tsx

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,13 @@ import { stopPropagation } from '$utils/keyboard';
66
import { useSetting } from '$state/hooks/settings';
77
import { settingsAtom } from '$state/settings';
88
import { Sidebar, SidebarContent, SidebarStack } from '$components/sidebar';
9-
import {
10-
DirectTab,
11-
DirectDMsList,
12-
HomeTab,
13-
SpaceTabs,
14-
InboxTab,
15-
UnverifiedTab,
16-
AccountSwitcherTab,
17-
} from './sidebar';
9+
import { DirectTab, DirectDMsList, HomeTab, SpaceTabs, InboxTab, UnverifiedTab } from './sidebar';
1810
import { CreateTab } from './sidebar/CreateTab';
1911
import { SearchTab } from './sidebar/SearchTab';
2012
import { SettingsTab } from './sidebar/SettingsTab';
2113
import { UserQuickTools } from './sidebar/UserQuickTools';
2214
import { useScreenSizeContext, ScreenSize } from '$hooks/useScreenSize';
15+
import { UserMenuTab } from './sidebar/UserMenuTab';
2316

2417
export function SidebarNav() {
2518
const scrollRef = useRef<HTMLDivElement>(null);
@@ -152,7 +145,7 @@ export function SidebarNav() {
152145
<InboxTab />
153146
<div style={{ paddingBottom: config.space.S100 }}>
154147
{/*PROBS ADD SETTINGSTAB HERE WHEN ADDING THE STATUSES*/}
155-
<AccountSwitcherTab />
148+
<UserMenuTab />
156149
</div>
157150
</>
158151
) : (
@@ -166,7 +159,7 @@ export function SidebarNav() {
166159
)}
167160

168161
<Box style={{ height: toRem(57) }} alignItems="Center">
169-
<AccountSwitcherTab />
162+
<UserMenuTab />
170163
</Box>
171164
</>
172165
)}

0 commit comments

Comments
 (0)