diff --git a/packages/web/src/components/modals/SpaceSettings.tsx b/packages/web/src/components/modals/SpaceSettings.tsx index 26e9864f..562ded73 100644 --- a/packages/web/src/components/modals/SpaceSettings.tsx +++ b/packages/web/src/components/modals/SpaceSettings.tsx @@ -23,9 +23,9 @@ function DiscoveryPanel({ spaceId }: { spaceId: string }) { (space?.visibility as SpaceVisibility) ?? 'private' ); const [description, setDescription] = useState(space?.description ?? ''); + const addToast = useUIStore((s) => s.addToast); const [saving, setSaving] = useState(false); const [saveError, setSaveError] = useState(''); - const [saveSuccess, setSaveSuccess] = useState(false); useEffect(() => { if (space) { @@ -43,11 +43,9 @@ function DiscoveryPanel({ spaceId }: { spaceId: string }) { const handleSave = async () => { setSaving(true); setSaveError(''); - setSaveSuccess(false); try { await api.spaces.update(spaceId, { visibility, description: description.trim() }); - setSaveSuccess(true); - setTimeout(() => setSaveSuccess(false), 2000); + addToast('Settings saved', 'success', 2000); } catch (err) { setSaveError(err instanceof Error ? err.message : 'Failed to save'); } finally { @@ -126,10 +124,6 @@ function DiscoveryPanel({ spaceId }: { spaceId: string }) { {saveError && (
{saveError}
)} - {saveSuccess && ( -
Settings saved
- )} - {/* Pending Join Requests — only shown when visibility is 'request' */} {(visibility === 'request' || (space.visibility as SpaceVisibility) === 'request') && ( diff --git a/packages/web/src/components/modals/instanceSettingsPanels/GeneralPanel.tsx b/packages/web/src/components/modals/instanceSettingsPanels/GeneralPanel.tsx index d8d56aa2..0c1d9b60 100644 --- a/packages/web/src/components/modals/instanceSettingsPanels/GeneralPanel.tsx +++ b/packages/web/src/components/modals/instanceSettingsPanels/GeneralPanel.tsx @@ -1,5 +1,6 @@ import { useState, useEffect } from 'react'; import { useSettingsStore } from '../../../stores/settingsStore'; +import { useUIStore } from '../../../stores/uiStore'; import { Toggle } from '../../ui/Toggle'; import type { InstanceAdminSettings } from '@backspace/shared'; @@ -7,10 +8,11 @@ export function GeneralPanel() { const instanceSettings = useSettingsStore((s) => s.instanceSettings); const updateInstanceSettings = useSettingsStore((s) => s.updateInstanceSettings); + const addToast = useUIStore((s) => s.addToast); + const [draft, setDraft] = useState(null); const [saving, setSaving] = useState(false); const [saveError, setSaveError] = useState(''); - const [saveSuccess, setSaveSuccess] = useState(false); const [gifKeyDirty, setGifKeyDirty] = useState(false); const [gifKeyDraft, setGifKeyDraft] = useState(''); @@ -34,7 +36,6 @@ export function GeneralPanel() { const handleSave = async () => { setSaving(true); setSaveError(''); - setSaveSuccess(false); try { const payload: Partial = { instanceName: draft!.instanceName, @@ -47,8 +48,7 @@ export function GeneralPanel() { await updateInstanceSettings(payload); setGifKeyDirty(false); setGifKeyDraft(''); - setSaveSuccess(true); - setTimeout(() => setSaveSuccess(false), 2000); + addToast('Settings saved', 'success', 2000); } catch (err) { setSaveError(err instanceof Error ? err.message : 'Failed to save'); } finally { @@ -151,10 +151,6 @@ export function GeneralPanel() { {saveError && (
{saveError}
)} - {saveSuccess && ( -
Settings saved
- )} - {/* Save / Reset bar */} {hasChanges && (
diff --git a/packages/web/src/components/modals/instanceSettingsPanels/StreamingPanel.tsx b/packages/web/src/components/modals/instanceSettingsPanels/StreamingPanel.tsx index 3471336d..9674a3d2 100644 --- a/packages/web/src/components/modals/instanceSettingsPanels/StreamingPanel.tsx +++ b/packages/web/src/components/modals/instanceSettingsPanels/StreamingPanel.tsx @@ -1,5 +1,6 @@ import { useState, useEffect, useRef } from 'react'; import { useSettingsStore } from '../../../stores/settingsStore'; +import { useUIStore } from '../../../stores/uiStore'; import type { InstanceStreamingLimits } from '@backspace/shared'; import { STANDARD_RESOLUTIONS, STANDARD_FRAMERATES, @@ -19,10 +20,12 @@ export function StreamingPanel() { const limits = useSettingsStore((s) => s.streamingLimits); const updateStreamingLimits = useSettingsStore((s) => s.updateStreamingLimits); + const addToast = useUIStore((s) => s.addToast); + const [draft, setDraft] = useState(null); const [saving, setSaving] = useState(false); const [saveError, setSaveError] = useState(''); - const [saveSuccess, setSaveSuccess] = useState(false); + // Matrix editor state: full grid of kbps values (integers only) const [matrixDraft, setMatrixDraft] = useState>({}); @@ -93,7 +96,6 @@ export function StreamingPanel() { const handleSave = async () => { setSaving(true); setSaveError(''); - setSaveSuccess(false); try { // Compute sparse overrides: only cells that differ from defaults const overrides: Record = {}; @@ -107,8 +109,7 @@ export function StreamingPanel() { bitrateMatrixOverrides: Object.keys(overrides).length > 0 ? overrides : null, }; await updateStreamingLimits(payload); - setSaveSuccess(true); - setTimeout(() => setSaveSuccess(false), 2000); + addToast('Settings saved', 'success', 2000); } catch (err) { setSaveError(err instanceof Error ? err.message : 'Failed to save'); } finally { @@ -486,9 +487,6 @@ export function StreamingPanel() { {saveError && (
{saveError}
)} - {saveSuccess && ( -
Settings saved
- )} {hasChanges && (
diff --git a/packages/web/src/components/modals/spaceSettingsPanels/OverviewPanel.tsx b/packages/web/src/components/modals/spaceSettingsPanels/OverviewPanel.tsx index 1088e9a3..3063e4ba 100644 --- a/packages/web/src/components/modals/spaceSettingsPanels/OverviewPanel.tsx +++ b/packages/web/src/components/modals/spaceSettingsPanels/OverviewPanel.tsx @@ -47,7 +47,6 @@ export function OverviewPanel({ spaceId }: OverviewPanelProps) { const [saving, setSaving] = useState(false); const [saveError, setSaveError] = useState(''); - const [saveSuccess, setSaveSuccess] = useState(false); const [confirmDelete, setConfirmDelete] = useState(false); const fileInputRef = useRef(null); @@ -174,7 +173,6 @@ export function OverviewPanel({ spaceId }: OverviewPanelProps) { const handleSave = async () => { setSaving(true); setSaveError(''); - setSaveSuccess(false); try { const updates: { name?: string; icon?: string; banner?: string; avatarColor?: string } = {}; if (hasNameChange) updates.name = spaceName.trim(); @@ -198,8 +196,7 @@ export function OverviewPanel({ spaceId }: OverviewPanelProps) { URL.revokeObjectURL(bannerPreview); setBannerPreview(null); } - setSaveSuccess(true); - setTimeout(() => setSaveSuccess(false), 2000); + addToast('Settings saved', 'success', 2000); } catch (err) { setSaveError(err instanceof Error ? err.message : 'Failed to save'); } finally { @@ -449,9 +446,6 @@ export function OverviewPanel({ spaceId }: OverviewPanelProps) { {saveError && (
{saveError}
)} - {saveSuccess && ( -
Settings saved
- )} {/* Danger Zone */} {isOwner && (
diff --git a/packages/web/src/components/modals/spaceSettingsPanels/RolesPanel.tsx b/packages/web/src/components/modals/spaceSettingsPanels/RolesPanel.tsx index 055bef83..234f9542 100644 --- a/packages/web/src/components/modals/spaceSettingsPanels/RolesPanel.tsx +++ b/packages/web/src/components/modals/spaceSettingsPanels/RolesPanel.tsx @@ -1,5 +1,6 @@ import React, { useState, useRef, useEffect } from 'react'; import { useSpaceStore } from '../../../stores/spaceStore'; +import { useUIStore } from '../../../stores/uiStore'; import { api } from '../../../api/client'; import { PermissionBits, stringToPermissions, permissionsToString } from '../../../utils/permissions'; import type { Role } from '@backspace/shared'; @@ -227,10 +228,10 @@ function RoleEditView({ role, spaceId, isNew, onBack, onDeleted, onCopied }: Rol const [draftPermissions, setDraftPermissions] = useState( stringToPermissions(role.permissions) ); + const addToast = useUIStore((s) => s.addToast); const [saving, setSaving] = useState(false); const [deleting, setDeleting] = useState(false); const [saveError, setSaveError] = useState(''); - const [saveSuccess, setSaveSuccess] = useState(false); const [confirmDelete, setConfirmDelete] = useState(false); const hasNameChange = !isEveryone && draftName.trim() !== role.name; @@ -246,7 +247,6 @@ function RoleEditView({ role, spaceId, isNew, onBack, onDeleted, onCopied }: Rol setConfirmDelete(false); setSaving(true); setSaveError(''); - setSaveSuccess(false); try { const data: { name?: string; color?: string; permissions?: string } = {}; if (hasNameChange) data.name = draftName.trim(); @@ -254,8 +254,7 @@ function RoleEditView({ role, spaceId, isNew, onBack, onDeleted, onCopied }: Rol if (hasPermChange) data.permissions = permissionsToString(draftPermissions); await api.roles.update(spaceId, role.id, data); await loadSpaceDetail(spaceId); - setSaveSuccess(true); - setTimeout(() => setSaveSuccess(false), 2000); + addToast('Role saved', 'success', 2000); } catch (err) { const msg = err instanceof Error ? err.message : 'Failed to save role'; if (msg.includes('already exists')) { @@ -451,9 +450,6 @@ function RoleEditView({ role, spaceId, isNew, onBack, onDeleted, onCopied }: Rol {saveError && (
{saveError}
)} - {saveSuccess && ( -
Role saved
- )}