fix(web): реактивно переключать пространство через SpaceProvider
Docker / build (push) Failing after 18s

localStorage не вызывал re-render; useSpaceId + remount main по spaceId.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Denozordec
2026-07-18 16:15:52 +07:00
co-authored by Cursor
parent a61b352f0a
commit c90b42c9c4
6 changed files with 84 additions and 17 deletions
+14 -1
View File
@@ -57,6 +57,7 @@ import {
can,
permissionForPath,
} from '@/lib/auth'
import { SpaceProvider, useSpaceId } from '@/lib/space'
interface NavItem {
to: string
@@ -162,6 +163,7 @@ export function AppShell({ children }: { children: ReactNode }) {
})).filter((g) => g.items.length > 0)
return (
<SpaceProvider>
<TooltipProvider delay={0}>
<SidebarProvider
style={
@@ -247,11 +249,22 @@ export function AppShell({ children }: { children: ReactNode }) {
</div>
</header>
<main className="flex flex-1 flex-col gap-4 px-4 py-4 md:gap-6 md:px-6 md:py-5">
{children}
<SpaceScopedMain>{children}</SpaceScopedMain>
</main>
</SidebarInset>
<GlobalSearch open={searchOpen} onOpenChange={setSearchOpen} />
</SidebarProvider>
</TooltipProvider>
</SpaceProvider>
)
}
/** Remount route content when space changes so snapshot queries pick new X-Space-Id. */
function SpaceScopedMain({ children }: { children: ReactNode }) {
const { spaceId } = useSpaceId()
return (
<div key={spaceId ?? 'default'} className="flex flex-1 flex-col gap-4 md:gap-6">
{children}
</div>
)
}
@@ -30,26 +30,28 @@ import {
} from '@cfdm/ui/components/sidebar'
import { api } from '@/lib/api-client'
import { getStoredSpaceId, setStoredSpaceId, type SpaceDto } from '@/lib/space'
import { useSpaceId, type SpaceDto } from '@/lib/space'
import { spacesKeys, spacesQueryOptions, snapshotKeys } from '@/queries/snapshot'
export function SpaceSwitcher() {
const { isMobile } = useSidebar()
const qc = useQueryClient()
const { spaceId, setSpaceId } = useSpaceId()
const { data: spaces = [] } = useQuery(spacesQueryOptions())
const currentId = getStoredSpaceId() ?? spaces[0]?.id
const currentId = spaceId ?? spaces[0]?.id
const current = spaces.find((s) => s.id === currentId) ?? spaces[0]
const [createOpen, setCreateOpen] = useState(false)
const [name, setName] = useState('')
useEffect(() => {
if (!getStoredSpaceId() && spaces[0]?.id) {
setStoredSpaceId(spaces[0].id)
if (!spaceId && spaces[0]?.id) {
setSpaceId(spaces[0].id)
}
}, [spaces])
}, [spaceId, spaces, setSpaceId])
function selectSpace(space: SpaceDto) {
setStoredSpaceId(space.id)
if (space.id === currentId) return
setSpaceId(space.id)
void qc.invalidateQueries({ queryKey: snapshotKeys.all })
void qc.invalidateQueries({ queryKey: spacesKeys.all })
toast.success(`Пространство: ${space.name}`)
@@ -64,7 +66,7 @@ export function SpaceSwitcher() {
if (!n) return
try {
const created = await api.createSpace({ name: n })
setStoredSpaceId(created.id)
setSpaceId(created.id)
setCreateOpen(false)
setName('')
await qc.invalidateQueries({ queryKey: spacesKeys.all })
+3 -2
View File
@@ -21,7 +21,7 @@ import {
} from '@cfdm/ui/components/sheet'
import { api } from '@/lib/api-client'
import { getStoredSpaceId } from '@/lib/space'
import { useSpaceId } from '@/lib/space'
import { spacesQueryOptions, snapshotKeys } from '@/queries/snapshot'
import type { Vps } from '@/types/entities'
@@ -33,8 +33,9 @@ type Props = {
export function VpsAccessSheet({ vps, open, onOpenChange }: Props) {
const qc = useQueryClient()
const { spaceId } = useSpaceId()
const { data: spaces = [] } = useQuery(spacesQueryOptions())
const fromSpaceId = getStoredSpaceId() ?? spaces.find((s) => s.kind === 'main')?.id ?? ''
const fromSpaceId = spaceId ?? spaces.find((s) => s.kind === 'main')?.id ?? ''
const targets = spaces.filter((s) => s.id !== fromSpaceId)
const [toSpaceId, setToSpaceId] = useState('')
const [permission, setPermission] = useState<'read' | 'write'>('read')