From 180b1ccfe17ec23bb51a7a395aef3249739fd9b7 Mon Sep 17 00:00:00 2001 From: Thomas Petersen Date: Wed, 3 Jun 2026 13:59:04 -0400 Subject: [PATCH] fix(desktop): polish status editor interactions Keep status editing focused in the dialog while preserving the profile card hit area and simplifying workspace menu branches. Co-authored-by: Cursor --- desktop/src/app/AppShell.tsx | 3 - .../features/profile/ui/ProfilePopover.tsx | 3 - .../src/features/sidebar/ui/AppSidebar.tsx | 3 - .../sidebar/ui/SidebarProfileCard.tsx | 36 +++++++-- .../user-status/ui/SetStatusDialog.tsx | 73 +++++++------------ .../workspaces/ui/WorkspaceSwitcher.tsx | 20 ++--- 6 files changed, 65 insertions(+), 73 deletions(-) diff --git a/desktop/src/app/AppShell.tsx b/desktop/src/app/AppShell.tsx index 3a7dea54b..5e09cb90e 100644 --- a/desktop/src/app/AppShell.tsx +++ b/desktop/src/app/AppShell.tsx @@ -797,9 +797,6 @@ export function AppShell() { onSetUserStatus={(text, emoji) => setUserStatusMutation.mutate({ text, emoji }) } - onClearUserStatus={() => - setUserStatusMutation.mutate({ text: "", emoji: "" }) - } profile={profileQuery.data} selfUserStatus={ deferredPubkey diff --git a/desktop/src/features/profile/ui/ProfilePopover.tsx b/desktop/src/features/profile/ui/ProfilePopover.tsx index 1e43ee2d5..39cf64190 100644 --- a/desktop/src/features/profile/ui/ProfilePopover.tsx +++ b/desktop/src/features/profile/ui/ProfilePopover.tsx @@ -23,7 +23,6 @@ interface ProfilePopoverProps { userStatusEmoji?: string; onSetStatus: (status: PresenceStatus) => void; onSetUserStatus: (text: string, emoji: string) => void; - onClearUserStatus: () => void; onOpenSettings: (section?: "profile" | "appearance") => void; children: React.ReactNode; // Optional outer container whose clicks should NOT close the popover. @@ -60,7 +59,6 @@ export function ProfilePopover({ userStatusEmoji, onSetStatus, onSetUserStatus, - onClearUserStatus, onOpenSettings, children, triggerContainerRef, @@ -292,7 +290,6 @@ export function ProfilePopover({ hasExistingStatus={hasUserStatus} initialEmoji={userStatusEmoji} initialText={userStatusText} - onClear={onClearUserStatus} onOpenChange={setStatusDialogOpen} onSave={onSetUserStatus} open={statusDialogOpen} diff --git a/desktop/src/features/sidebar/ui/AppSidebar.tsx b/desktop/src/features/sidebar/ui/AppSidebar.tsx index 3fbd121d3..40a036116 100644 --- a/desktop/src/features/sidebar/ui/AppSidebar.tsx +++ b/desktop/src/features/sidebar/ui/AppSidebar.tsx @@ -138,7 +138,6 @@ type AppSidebarProps = { onSelectSettings: (section?: "profile" | "appearance") => void; onSetPresenceStatus?: (status: "online" | "away" | "offline") => void; onSetUserStatus: (text: string, emoji: string) => void; - onClearUserStatus: () => void; onSwitchWorkspace: (id: string) => void; selfUserStatus?: UserStatus; isPresencePending?: boolean; @@ -193,7 +192,6 @@ export function AppSidebar({ onSelectSettings, onSetPresenceStatus, onSetUserStatus, - onClearUserStatus, onSwitchWorkspace, selfUserStatus, isPresencePending, @@ -662,7 +660,6 @@ export function AppSidebar({ void; onOpenAddWorkspace: () => void; onOpenSettings: (section?: "profile" | "appearance") => void; onRemoveWorkspace: (id: string) => void; @@ -33,7 +32,6 @@ type SidebarProfileCardProps = { export function SidebarProfileCard({ activeWorkspace, isPresencePending, - onClearUserStatus, onOpenAddWorkspace, onOpenSettings, onRemoveWorkspace, @@ -53,10 +51,23 @@ export function SidebarProfileCard({ () => setProfilePopoverOpen((prev) => !prev), [], ); + const handleCardClick = React.useCallback( + (event: React.MouseEvent) => { + const target = event.target; + if ( + !(target instanceof Node) || + !profileCardRef.current?.contains(target) + ) { + return; + } + toggleProfilePopover(); + }, + [toggleProfilePopover], + ); const hasStatus = Boolean(selfUserStatus?.text || selfUserStatus?.emoji); const workspaceLabel = activeWorkspace?.name ?? "No workspace"; const readonlyWorkspaceLabel = ( - + @@ -65,9 +76,11 @@ export function SidebarProfileCard({ ); return ( + // biome-ignore lint/a11y/noStaticElementInteractions lint/a11y/useKeyWithClickEvents: child buttons provide keyboard access; wrapper fills pointer gaps between them.
@@ -75,7 +88,10 @@ export function SidebarProfileCard({ aria-label={`Open profile menu for ${resolvedDisplayName}`} className="relative shrink-0 rounded-xl outline-hidden focus:outline-none focus-visible:outline-none" data-testid="sidebar-profile-avatar-button" - onClick={toggleProfilePopover} + onClick={(event) => { + event.stopPropagation(); + toggleProfilePopover(); + }} type="button" > {})} onSetUserStatus={onSetUserStatus} - onClearUserStatus={onClearUserStatus} onOpenSettings={onOpenSettings} triggerContainerRef={profileCardRef} workspaceSwitcherSlot={ @@ -123,6 +138,10 @@ export function SidebarProfileCard({ } > - - {emoji ? ( - - ) : null} -
+ + + setText(event.target.value)} onKeyDown={handleKeyDown} placeholder="What's your status?" value={text} /> + {hasExistingStatus || text || emoji ? ( + + ) : null}
@@ -168,20 +164,7 @@ export function SetStatusDialog({ ))}
-
-
- {hasExistingStatus ? ( - - ) : null} -
+