From abd17f5d49e4b4e1cc7444d95b7294b30892d8bb Mon Sep 17 00:00:00 2001 From: hampus-fluxer Date: Tue, 6 Jan 2026 04:27:05 +0100 Subject: [PATCH] fix(app): improve UX for nested sidebars (#50) --- .../components/DesktopChannelSettingsView.tsx | 22 +++++++++-- .../components/DesktopGuildSettingsView.tsx | 37 ++++++++++++------- .../src/stores/SettingsSidebarStore.tsx | 28 +++++++++++++- 3 files changed, 69 insertions(+), 18 deletions(-) diff --git a/fluxer_app/src/components/modals/components/DesktopChannelSettingsView.tsx b/fluxer_app/src/components/modals/components/DesktopChannelSettingsView.tsx index caec15ad..cf619308 100644 --- a/fluxer_app/src/components/modals/components/DesktopChannelSettingsView.tsx +++ b/fluxer_app/src/components/modals/components/DesktopChannelSettingsView.tsx @@ -66,12 +66,24 @@ export const DesktopChannelSettingsView: React.FC `channel-permissions-${channel.id}`, + [channel.id], + ); + const handleTabSelect = React.useCallback( (tabType: ChannelSettingsTabType) => { if (checkUnsavedChanges()) return; + if ( + tabType === 'permissions' && + SettingsSidebarStore.ownerId === channelPermissionsOverrideOwnerId && + SettingsSidebarStore.isDismissed(channelPermissionsOverrideOwnerId) + ) { + SettingsSidebarStore.activateOverride(channelPermissionsOverrideOwnerId); + } onTabSelect(tabType); }, - [checkUnsavedChanges, onTabSelect], + [checkUnsavedChanges, onTabSelect, channelPermissionsOverrideOwnerId], ); const handleDeleteChannel = React.useCallback(() => { @@ -119,7 +131,7 @@ export const DesktopChannelSettingsView: React.FC} - onClick={() => SettingsSidebarStore.setUseOverride(false)} + onClick={() => SettingsSidebarStore.dismissOverride()} > {t`Back to Settings`} @@ -134,12 +146,14 @@ export const DesktopChannelSettingsView: React.FC - {SettingsSidebarStore.hasOverride && ( + {SettingsSidebarStore.hasOverride && + SettingsSidebarStore.ownerId === channelPermissionsOverrideOwnerId && + !SettingsSidebarStore.isDismissed(channelPermissionsOverrideOwnerId) && (
diff --git a/fluxer_app/src/components/modals/components/DesktopGuildSettingsView.tsx b/fluxer_app/src/components/modals/components/DesktopGuildSettingsView.tsx index 2311e74a..f89ddcd2 100644 --- a/fluxer_app/src/components/modals/components/DesktopGuildSettingsView.tsx +++ b/fluxer_app/src/components/modals/components/DesktopGuildSettingsView.tsx @@ -69,12 +69,21 @@ export const DesktopGuildSettingsView: React.FC = contentRef.current?.focus(); }, []); + const guildOverrideOwnerId = React.useMemo(() => `guild-roles-${guild.id}`, [guild.id]); + const handleTabSelect = React.useCallback( (tabType: GuildSettingsTabType) => { if (checkUnsavedChanges()) return; + if ( + tabType === 'roles' && + SettingsSidebarStore.ownerId === guildOverrideOwnerId && + SettingsSidebarStore.isDismissed(guildOverrideOwnerId) + ) { + SettingsSidebarStore.activateOverride(guildOverrideOwnerId); + } onTabSelect(tabType); }, - [checkUnsavedChanges, onTabSelect], + [checkUnsavedChanges, onTabSelect, guildOverrideOwnerId], ); const handleDeleteGuild = React.useCallback(() => { @@ -134,7 +143,7 @@ export const DesktopGuildSettingsView: React.FC = @@ -149,17 +158,19 @@ export const DesktopGuildSettingsView: React.FC = exit={prefersReducedMotion ? {opacity: 1} : {opacity: 0}} transition={prefersReducedMotion ? {duration: 0} : {duration: 0.2, ease: 'easeOut'}} > - {SettingsSidebarStore.hasOverride && ( -
- -
- )} + {SettingsSidebarStore.hasOverride && + SettingsSidebarStore.ownerId === guildOverrideOwnerId && + !SettingsSidebarStore.isDismissed(guildOverrideOwnerId) && ( +
+ +
+ )} {Object.entries(groupedSettingsTabs).map(([category, tabs]) => ( {category !== 'guild_settings' && ( diff --git a/fluxer_app/src/stores/SettingsSidebarStore.tsx b/fluxer_app/src/stores/SettingsSidebarStore.tsx index 47d5f150..b78a10c7 100644 --- a/fluxer_app/src/stores/SettingsSidebarStore.tsx +++ b/fluxer_app/src/stores/SettingsSidebarStore.tsx @@ -24,6 +24,7 @@ class SettingsSidebarStore { ownerId: string | null = null; overrideContent: React.ReactNode | null = null; useOverride = false; + dismissedOwnerId: string | null = null; constructor() { makeAutoObservable(this, {overrideContent: observable.ref}, {autoBind: true}); @@ -36,7 +37,8 @@ class SettingsSidebarStore { setOverride(ownerId: string, content: React.ReactNode, options?: {defaultOn?: boolean}): void { this.ownerId = ownerId; this.overrideContent = content; - if (options?.defaultOn) this.useOverride = true; + this.dismissedOwnerId = null; + this.useOverride = options?.defaultOn ?? false; } updateOverride(ownerId: string, content: React.ReactNode): void { @@ -48,16 +50,40 @@ class SettingsSidebarStore { if (ownerId && this.ownerId && this.ownerId !== ownerId) return; this.ownerId = null; this.overrideContent = null; + this.dismissedOwnerId = null; this.useOverride = false; } + dismissOverride(ownerId?: string): void { + if (ownerId && this.ownerId && this.ownerId !== ownerId) return; + const targetOwnerId = ownerId ?? this.ownerId; + this.useOverride = false; + this.dismissedOwnerId = targetOwnerId; + } + setUseOverride(value: boolean): void { if (!this.hasOverride) { this.useOverride = false; return; } + if (value) { + this.dismissedOwnerId = null; + } this.useOverride = value; } + + activateOverride(ownerId?: string): void { + if (!this.hasOverride) return; + if (ownerId && this.ownerId && this.ownerId !== ownerId) return; + this.useOverride = true; + this.dismissedOwnerId = null; + } + + isDismissed(ownerId?: string): boolean { + if (!this.dismissedOwnerId) return false; + if (!ownerId) return this.ownerId === this.dismissedOwnerId; + return ownerId === this.dismissedOwnerId; + } } export default new SettingsSidebarStore();