fix: replace inline save banners with toast notifications
Removes layout-shifting "Settings saved" / "Role saved" inline banners from 5 settings panels. Uses existing addToast from uiStore instead. Error banners kept inline.
This commit is contained in:
@@ -53,5 +53,8 @@ tmp/
|
||||
# Worktrees
|
||||
.worktrees/
|
||||
|
||||
# Superpowers brainstorm artifacts
|
||||
.superpowers/
|
||||
|
||||
# Local files
|
||||
Gemini Starter.rtf
|
||||
|
||||
@@ -7,10 +7,12 @@ import { AccountPanel } from './settingsPanels/AccountPanel';
|
||||
import { VoicePanel } from './settingsPanels/VoicePanel';
|
||||
import { PrivacyPanel } from './settingsPanels/PrivacyPanel';
|
||||
import { ConnectionsPanel } from './settingsPanels/ConnectionsPanel';
|
||||
import { DesktopPanel } from './settingsPanels/DesktopPanel';
|
||||
import { InstancePanel } from './settingsPanels/InstancePanel';
|
||||
import { isElectron } from '../../platform/platform';
|
||||
import { SettingsSectionsProvider, useSettingsSectionsContext } from './SettingsSectionsContext';
|
||||
|
||||
type SettingsTab = 'account' | 'voice' | 'privacy' | 'connections' | 'instance';
|
||||
type SettingsTab = 'account' | 'voice' | 'privacy' | 'connections' | 'desktop' | 'instance';
|
||||
|
||||
function SidebarSubLinks() {
|
||||
const ctx = useSettingsSectionsContext();
|
||||
@@ -123,6 +125,7 @@ export function UserSettingsModal() {
|
||||
<div className="border-t border-white/[0.04] my-2 mx-2" />
|
||||
<div className="text-[10px] font-semibold text-txt-tertiary uppercase tracking-wider px-3 py-1">App Settings</div>
|
||||
<button onClick={() => handleTabClick('connections')} className={tabClass('connections')}>Connections</button>
|
||||
{isElectron() && <button onClick={() => handleTabClick('desktop')} className={tabClass('desktop')}>Desktop</button>}
|
||||
|
||||
{isAdmin && (
|
||||
<>
|
||||
@@ -171,6 +174,7 @@ export function UserSettingsModal() {
|
||||
<div className="border-t border-white/[0.04] my-2 mx-2" />
|
||||
<div className="text-[10px] font-semibold text-txt-tertiary uppercase tracking-wider px-3 py-1">App Settings</div>
|
||||
<button onClick={() => handleTabClick('connections')} className={tabClass('connections')}>Connections</button>
|
||||
{isElectron() && <button onClick={() => handleTabClick('desktop')} className={tabClass('desktop')}>Desktop</button>}
|
||||
|
||||
{isAdmin && (
|
||||
<>
|
||||
@@ -212,6 +216,7 @@ export function UserSettingsModal() {
|
||||
{tab === 'voice' && <VoicePanel />}
|
||||
{tab === 'privacy' && <PrivacyPanel />}
|
||||
{tab === 'connections' && <ConnectionsPanel />}
|
||||
{tab === 'desktop' && <DesktopPanel />}
|
||||
{tab === 'instance' && isAdmin && <InstancePanel />}
|
||||
</div>
|
||||
</SettingsScrollContainer>
|
||||
|
||||
@@ -9,107 +9,6 @@ import { getAvatarGradient, adjustColor, mutedGradient, AVATAR_GRADIENT_MAP, BAN
|
||||
import { AVATAR_COLORS } from '@backspace/shared';
|
||||
import type { User, UserStatus, AvatarColor } from '@backspace/shared';
|
||||
import type { FederationOpResult } from '../../../utils/federationOps';
|
||||
import { isElectron } from '../../../platform/platform';
|
||||
import { Toggle } from '../../ui/Toggle';
|
||||
|
||||
|
||||
function AutoLaunchSettings() {
|
||||
const [openAtLogin, setOpenAtLogin] = useState(false);
|
||||
const [startMinimized, setStartMinimized] = useState(true);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
window.backspace?.getAutoLaunchSettings().then((settings) => {
|
||||
setOpenAtLogin(settings.openAtLogin);
|
||||
setStartMinimized(settings.startMinimized);
|
||||
setLoading(false);
|
||||
}).catch(() => setLoading(false));
|
||||
}, []);
|
||||
|
||||
const handleOpenAtLoginChange = async (enabled: boolean) => {
|
||||
setOpenAtLogin(enabled);
|
||||
try {
|
||||
const result = await window.backspace!.setAutoLaunchSettings({ openAtLogin: enabled });
|
||||
setOpenAtLogin(result.openAtLogin);
|
||||
setStartMinimized(result.startMinimized);
|
||||
} catch {
|
||||
setOpenAtLogin(!enabled);
|
||||
}
|
||||
};
|
||||
|
||||
const handleStartMinimizedChange = async (enabled: boolean) => {
|
||||
setStartMinimized(enabled);
|
||||
try {
|
||||
const result = await window.backspace!.setAutoLaunchSettings({ startMinimized: enabled });
|
||||
setOpenAtLogin(result.openAtLogin);
|
||||
setStartMinimized(result.startMinimized);
|
||||
} catch {
|
||||
setStartMinimized(!enabled);
|
||||
}
|
||||
};
|
||||
|
||||
if (loading) return null;
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex items-center justify-between py-1">
|
||||
<div className="flex-1 mr-4">
|
||||
<div className="text-sm text-txt-primary">Start at boot</div>
|
||||
<div className="text-xs text-txt-tertiary mt-0.5">
|
||||
Automatically launch Backspace when you log in
|
||||
</div>
|
||||
</div>
|
||||
<Toggle enabled={openAtLogin} onChange={handleOpenAtLoginChange} />
|
||||
</div>
|
||||
<div className="flex items-center justify-between py-1">
|
||||
<div className="flex-1 mr-4">
|
||||
<div className={`text-sm ${openAtLogin ? 'text-txt-primary' : 'text-txt-tertiary'}`}>Start minimized</div>
|
||||
<div className="text-xs text-txt-tertiary mt-0.5">
|
||||
Start hidden in the system tray instead of showing the window
|
||||
</div>
|
||||
</div>
|
||||
<Toggle enabled={startMinimized} onChange={handleStartMinimizedChange} />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function UpdateSettings() {
|
||||
const [version, setVersion] = useState<string | null>(null);
|
||||
const [checking, setChecking] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
window.backspace?.getVersion().then(setVersion).catch(() => {});
|
||||
}, []);
|
||||
|
||||
const handleCheck = () => {
|
||||
setChecking(true);
|
||||
window.backspace?.checkForUpdates();
|
||||
// Reset after a few seconds — electron-updater doesn't have a "no update" callback
|
||||
setTimeout(() => setChecking(false), 5000);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex items-center justify-between py-1">
|
||||
<div className="flex-1 mr-4">
|
||||
<div className="text-sm text-txt-primary">
|
||||
{version ? `Version ${version}` : 'Backspace Desktop'}
|
||||
</div>
|
||||
<div className="text-xs text-txt-tertiary mt-0.5">
|
||||
Check for new versions of the desktop app
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
onClick={handleCheck}
|
||||
disabled={checking}
|
||||
className="px-3 py-1.5 text-sm text-txt-secondary hover:text-txt-primary bg-white/[0.04] hover:bg-white/[0.08] rounded-lg transition-colors disabled:opacity-50"
|
||||
>
|
||||
{checking ? 'Checking...' : 'Check for Updates'}
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function AccountPanel() {
|
||||
const user = useAuthStore((s) => s.user);
|
||||
const updateProfile = useAuthStore((s) => s.updateProfile);
|
||||
@@ -787,32 +686,6 @@ export function AccountPanel() {
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{/* ── Desktop (Electron only) ── */}
|
||||
{isElectron() && (
|
||||
<div>
|
||||
<div className="text-[11px] font-semibold text-txt-tertiary uppercase tracking-wider mb-1.5">Desktop</div>
|
||||
<div className="rounded-lg bg-white/[0.03] border border-white/[0.04] p-3.5 space-y-3">
|
||||
<AutoLaunchSettings />
|
||||
<UpdateSettings />
|
||||
|
||||
<div className="border-t border-white/[0.04]" />
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<div className="text-sm text-txt-primary font-medium">{window.location.origin}</div>
|
||||
<div className="text-xs text-txt-tertiary mt-0.5">Currently connected instance</div>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => window.backspace?.clearInstanceUrl()}
|
||||
className="px-3 py-1.5 text-sm text-txt-secondary hover:text-txt-primary bg-white/[0.04] hover:bg-white/[0.08] rounded-lg transition-colors"
|
||||
>
|
||||
Change Instance
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* ── Danger Zone ── */}
|
||||
<div>
|
||||
<div className="text-[11px] font-semibold text-txt-tertiary uppercase tracking-wider mb-1.5">Danger Zone</div>
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { Toggle } from '../../ui/Toggle';
|
||||
|
||||
function AutoLaunchSettings() {
|
||||
const [openAtLogin, setOpenAtLogin] = useState(false);
|
||||
const [startMinimized, setStartMinimized] = useState(true);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
window.backspace?.getAutoLaunchSettings().then((settings) => {
|
||||
setOpenAtLogin(settings.openAtLogin);
|
||||
setStartMinimized(settings.startMinimized);
|
||||
setLoading(false);
|
||||
}).catch(() => setLoading(false));
|
||||
}, []);
|
||||
|
||||
const handleOpenAtLoginChange = async (enabled: boolean) => {
|
||||
setOpenAtLogin(enabled);
|
||||
try {
|
||||
const result = await window.backspace!.setAutoLaunchSettings({ openAtLogin: enabled });
|
||||
setOpenAtLogin(result.openAtLogin);
|
||||
setStartMinimized(result.startMinimized);
|
||||
} catch {
|
||||
setOpenAtLogin(!enabled);
|
||||
}
|
||||
};
|
||||
|
||||
const handleStartMinimizedChange = async (enabled: boolean) => {
|
||||
setStartMinimized(enabled);
|
||||
try {
|
||||
const result = await window.backspace!.setAutoLaunchSettings({ startMinimized: enabled });
|
||||
setOpenAtLogin(result.openAtLogin);
|
||||
setStartMinimized(result.startMinimized);
|
||||
} catch {
|
||||
setStartMinimized(!enabled);
|
||||
}
|
||||
};
|
||||
|
||||
if (loading) return null;
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex items-center justify-between py-1">
|
||||
<div className="flex-1 mr-4">
|
||||
<div className="text-sm text-txt-primary">Start at boot</div>
|
||||
<div className="text-xs text-txt-tertiary mt-0.5">
|
||||
Automatically launch Backspace when you log in
|
||||
</div>
|
||||
</div>
|
||||
<Toggle enabled={openAtLogin} onChange={handleOpenAtLoginChange} />
|
||||
</div>
|
||||
<div className="flex items-center justify-between py-1">
|
||||
<div className="flex-1 mr-4">
|
||||
<div className={`text-sm ${openAtLogin ? 'text-txt-primary' : 'text-txt-tertiary'}`}>Start minimized</div>
|
||||
<div className="text-xs text-txt-tertiary mt-0.5">
|
||||
Start hidden in the system tray instead of showing the window
|
||||
</div>
|
||||
</div>
|
||||
<Toggle enabled={startMinimized} onChange={handleStartMinimizedChange} />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function UpdateSettings() {
|
||||
const [version, setVersion] = useState<string | null>(null);
|
||||
const [checking, setChecking] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
window.backspace?.getVersion().then(setVersion).catch(() => {});
|
||||
}, []);
|
||||
|
||||
const handleCheck = () => {
|
||||
setChecking(true);
|
||||
window.backspace?.checkForUpdates();
|
||||
// Reset after a few seconds — electron-updater doesn't have a "no update" callback
|
||||
setTimeout(() => setChecking(false), 5000);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex items-center justify-between py-1">
|
||||
<div className="flex-1 mr-4">
|
||||
<div className="text-sm text-txt-primary">
|
||||
{version ? `Version ${version}` : 'Backspace Desktop'}
|
||||
</div>
|
||||
<div className="text-xs text-txt-tertiary mt-0.5">
|
||||
Check for new versions of the desktop app
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
onClick={handleCheck}
|
||||
disabled={checking}
|
||||
className="px-3 py-1.5 text-sm text-txt-secondary hover:text-txt-primary bg-white/[0.04] hover:bg-white/[0.08] rounded-lg transition-colors disabled:opacity-50"
|
||||
>
|
||||
{checking ? 'Checking...' : 'Check for Updates'}
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function DesktopPanel() {
|
||||
return (
|
||||
<div className="space-y-5">
|
||||
<h2 className="text-lg font-semibold text-txt-primary mb-6">Desktop</h2>
|
||||
|
||||
<div className="rounded-lg bg-white/[0.03] border border-white/[0.04] p-3.5 space-y-3">
|
||||
<AutoLaunchSettings />
|
||||
<UpdateSettings />
|
||||
|
||||
<div className="border-t border-white/[0.04]" />
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<div className="text-sm text-txt-primary font-medium">{window.location.origin}</div>
|
||||
<div className="text-xs text-txt-tertiary mt-0.5">Currently connected instance</div>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => window.backspace?.clearInstanceUrl()}
|
||||
className="px-3 py-1.5 text-sm text-txt-secondary hover:text-txt-primary bg-white/[0.04] hover:bg-white/[0.08] rounded-lg transition-colors"
|
||||
>
|
||||
Change Instance
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user