feat: add drill-down navigation for Instance settings on mobile

Replace inline InstancePanel render with MobileInstancePanel that shows
a navigation list for General, Streaming, Storage, and Users sub-panels.
Each sub-panel pushes onto the mobile screen stack with MobileScreenHeader.
This commit is contained in:
Jannis Braun
2026-03-23 14:16:56 +01:00
parent 7c40a230bc
commit 54b367924d
3 changed files with 99 additions and 3 deletions
@@ -0,0 +1,68 @@
import React from 'react';
import { useUIStore } from '../../stores/uiStore';
import { MobileScreenHeader } from './MobileScreenHeader';
const sections = [
{
id: 'general',
label: 'General',
icon: (
<svg className="w-5 h-5 text-txt-secondary" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}>
<path strokeLinecap="round" strokeLinejoin="round" d="M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87.074.04.147.083.22.127.325.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 011.37.49l1.296 2.247a1.125 1.125 0 01-.26 1.431l-1.003.827c-.293.241-.438.613-.43.992a7.723 7.723 0 010 .255c-.008.378.137.75.43.991l1.004.827c.424.35.534.955.26 1.432l-1.298 2.247a1.125 1.125 0 01-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.47 6.47 0 01-.22.128c-.331.183-.581.495-.644.869l-.213 1.281c-.09.543-.56.94-1.11.94h-2.594c-.55 0-1.019-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 01-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 01-1.369-.49l-1.297-2.247a1.125 1.125 0 01.26-1.432l1.004-.827c.292-.24.437-.613.43-.991a6.932 6.932 0 010-.255c.007-.38-.138-.751-.43-.992l-1.004-.827a1.125 1.125 0 01-.26-1.43l1.297-2.248a1.125 1.125 0 011.37-.491l1.216.456c.356.133.751.072 1.076-.124.072-.044.146-.086.22-.128.332-.183.582-.495.644-.869l.214-1.28z" />
<path strokeLinecap="round" strokeLinejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
</svg>
),
},
{
id: 'streaming',
label: 'Streaming',
icon: (
<svg className="w-5 h-5 text-txt-secondary" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}>
<path strokeLinecap="round" strokeLinejoin="round" d="M15.75 10.5l4.72-4.72a.75.75 0 011.28.53v11.38a.75.75 0 01-1.28.53l-4.72-4.72M4.5 18.75h9a2.25 2.25 0 002.25-2.25v-9a2.25 2.25 0 00-2.25-2.25h-9A2.25 2.25 0 002.25 7.5v9a2.25 2.25 0 002.25 2.25z" />
</svg>
),
},
{
id: 'storage',
label: 'Storage',
icon: (
<svg className="w-5 h-5 text-txt-secondary" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}>
<path strokeLinecap="round" strokeLinejoin="round" d="M20.25 6.375c0 2.278-3.694 4.125-8.25 4.125S3.75 8.653 3.75 6.375m16.5 0c0-2.278-3.694-4.125-8.25-4.125S3.75 4.097 3.75 6.375m16.5 0v11.25c0 2.278-3.694 4.125-8.25 4.125s-8.25-1.847-8.25-4.125V6.375m16.5 0v3.75m-16.5-3.75v3.75m16.5 0v3.75C20.25 16.153 16.556 18 12 18s-8.25-1.847-8.25-4.125v-3.75m16.5 0c0 2.278-3.694 4.125-8.25 4.125s-8.25-1.847-8.25-4.125" />
</svg>
),
},
{
id: 'users',
label: 'Users',
icon: (
<svg className="w-5 h-5 text-txt-secondary" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}>
<path strokeLinecap="round" strokeLinejoin="round" d="M15 19.128a9.38 9.38 0 002.625.372 9.337 9.337 0 004.121-.952 4.125 4.125 0 00-7.533-2.493M15 19.128v-.003c0-1.113-.285-2.16-.786-3.07M15 19.128v.106A12.318 12.318 0 018.624 21c-2.331 0-4.512-.645-6.374-1.766l-.001-.109a6.375 6.375 0 0111.964-3.07M12 6.375a3.375 3.375 0 11-6.75 0 3.375 3.375 0 016.75 0zm8.25 2.25a2.625 2.625 0 11-5.25 0 2.625 2.625 0 015.25 0z" />
</svg>
),
},
];
export function MobileInstancePanel() {
const pushMobileScreen = useUIStore((s) => s.pushMobileScreen);
return (
<div className="flex flex-col h-full bg-surface-base">
<MobileScreenHeader title="Instance" />
<div className="flex-1 overflow-y-auto">
{sections.map((section) => (
<button
key={section.id}
onClick={() => pushMobileScreen(`settings-instance-${section.id}`)}
className="w-full flex items-center gap-3 px-4 py-3.5 hover:bg-interactive-hover text-left transition-colors"
>
{section.icon}
<span className="text-sm text-txt-primary flex-1">{section.label}</span>
<svg className="w-4 h-4 text-txt-tertiary" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
<path strokeLinecap="round" strokeLinejoin="round" d="M8.25 4.5l7.5 7.5-7.5 7.5" />
</svg>
</button>
))}
</div>
</div>
);
}
@@ -5,7 +5,6 @@ import { AccountPanel } from '../modals/settingsPanels/AccountPanel';
import { VoicePanel } from '../modals/settingsPanels/VoicePanel';
import { ConnectionsPanel } from '../modals/settingsPanels/ConnectionsPanel';
import { PrivacyPanel } from '../modals/settingsPanels/PrivacyPanel';
import { InstancePanel } from '../modals/settingsPanels/InstancePanel';
interface MobileSettingsScreenProps {
initialPanel?: string;
@@ -16,7 +15,6 @@ const panelConfig: Record<string, { title: string; component: React.ReactNode }>
voice: { title: 'Voice & Audio', component: <VoicePanel /> },
privacy: { title: 'Privacy', component: <PrivacyPanel /> },
connections: { title: 'Connections', component: <ConnectionsPanel /> },
instance: { title: 'Instance', component: <InstancePanel /> },
};
const sectionIcons: Record<string, React.ReactNode> = {
@@ -11,12 +11,18 @@ import { MobileDmsScreen } from './MobileDmsScreen';
import { MobileYouScreen } from './MobileYouScreen';
import { MobileChatScreen } from './MobileChatScreen';
import { MobileSettingsScreen } from './MobileSettingsScreen';
import { MobileInstancePanel } from './MobileInstancePanel';
import { MobileScreenHeader } from './MobileScreenHeader';
import { MobileVoiceMiniBar } from './MobileVoiceMiniBar';
import { MobileVoiceFullScreen } from './MobileVoiceFullScreen';
import { MemberSidebar } from './MemberSidebar';
import { FriendsPage } from '../chat/FriendsPage';
import { ExplorePage } from '../chat/ExplorePage';
import { UserProfileModal } from '../modals/UserProfileModal';
import { GeneralPanel } from '../modals/instanceSettingsPanels/GeneralPanel';
import { StreamingPanel } from '../modals/instanceSettingsPanels/StreamingPanel';
import { StoragePanel } from '../modals/instanceSettingsPanels/StoragePanel';
import { UsersPanel } from '../modals/instanceSettingsPanels/UsersPanel';
const screenMap: Record<string, (params?: Record<string, string>) => React.ReactNode> = {
'channel-chat': (params) => <MobileChatScreen params={params} />,
@@ -26,7 +32,31 @@ const screenMap: Record<string, (params?: Record<string, string>) => React.React
'settings-voice': () => <MobileSettingsScreen initialPanel="voice" />,
'settings-privacy': () => <MobileSettingsScreen initialPanel="privacy" />,
'settings-connections': () => <MobileSettingsScreen initialPanel="connections" />,
'settings-instance': () => <MobileSettingsScreen initialPanel="instance" />,
'settings-instance': () => <MobileInstancePanel />,
'settings-instance-general': () => (
<div className="flex flex-col h-full bg-surface-base">
<MobileScreenHeader title="General" />
<div className="flex-1 overflow-y-auto p-4"><GeneralPanel /></div>
</div>
),
'settings-instance-streaming': () => (
<div className="flex flex-col h-full bg-surface-base">
<MobileScreenHeader title="Streaming" />
<div className="flex-1 overflow-y-auto p-4"><StreamingPanel /></div>
</div>
),
'settings-instance-storage': () => (
<div className="flex flex-col h-full bg-surface-base">
<MobileScreenHeader title="Storage" />
<div className="flex-1 overflow-y-auto p-4"><StoragePanel /></div>
</div>
),
'settings-instance-users': () => (
<div className="flex flex-col h-full bg-surface-base">
<MobileScreenHeader title="Users" />
<div className="flex-1 overflow-y-auto p-4"><UsersPanel /></div>
</div>
),
'members': () => <MemberSidebar />,
'voice-full': () => <MobileVoiceFullScreen />,
'explore': () => <ExplorePage />,