feat: add space visibility/description, instance re-auth, and per-channel permissions

- Accept visibility and description fields when creating spaces
- Register creator in connectionManager on space creation for immediate WS broadcasts
- Return per-channel myPermissions and space-level myPermissions from GET /spaces/:id
- Populate permission maps in spaceStore from REST response
- Add reauthenticateInstance flow for tokenless federation placeholders
- Handle expired/missing tokens gracefully in autoConnectAll with visible error state
- Guard syncInstanceList against premature runs before autoConnectAll completes
This commit is contained in:
Jannis Braun
2026-03-08 22:08:48 +01:00
parent 0aba5ef8c1
commit a7b5d819bf
5 changed files with 325 additions and 139 deletions
+19 -4
View File
@@ -1,5 +1,5 @@
import { create } from 'zustand';
import type { Space, Channel, MemberWithUser, SpaceWithChannelsAndMembers, Role, SpaceFolder, DmChannel, User, UpdateSpaceRequest } from '@backspace/shared';
import type { Space, Channel, MemberWithUser, SpaceWithChannelsAndMembers, Role, SpaceFolder, DmChannel, User, UpdateSpaceRequest, CreateSpaceRequest } from '@backspace/shared';
import { api, BackspaceApiClient } from '../api/client';
import { resolveAssetUrl, normalizeUserAssets } from '../utils/assetUrls';
import { isSelf } from '../utils/identity';
@@ -49,7 +49,7 @@ interface SpaceState {
loadSpaces: () => Promise<void>;
loadSpaceDetail: (spaceId: string) => Promise<void>;
loadDmChannels: () => Promise<void>;
createSpace: (name: string, icon?: string) => Promise<Space>;
createSpace: (data: CreateSpaceRequest) => Promise<Space>;
updateSpace: (spaceId: string, data: UpdateSpaceRequest) => Promise<void>;
deleteSpace: (spaceId: string) => Promise<void>;
joinSpace: (spaceId: string, inviteCode: string) => Promise<void>;
@@ -156,11 +156,26 @@ export const useSpaceStore = create<SpaceState>((set, get) => ({
normalizeUserAssets(member.user, origin);
}
}
// Populate permission maps from REST response
const spacePermissions = new Map(get().spacePermissions);
const channelPermissions = new Map(get().channelPermissions);
if (detail.myPermissions) {
spacePermissions.set(spaceId, detail.myPermissions);
}
for (const ch of detail.channels) {
if (ch.myPermissions) {
channelPermissions.set(ch.id, ch.myPermissions);
}
}
set({
currentSpaceId: spaceId,
channels: detail.channels.sort((a, b) => a.position - b.position),
members: detail.members,
roles: detail.roles.sort((a, b) => b.position - a.position),
spacePermissions,
channelPermissions,
});
} catch {
// Handle error silently
@@ -176,8 +191,8 @@ export const useSpaceStore = create<SpaceState>((set, get) => ({
}
},
createSpace: async (name: string, icon?: string) => {
const space =await api.spaces.create({ name, icon });
createSpace: async (data: CreateSpaceRequest) => {
const space = await api.spaces.create(data);
const tagged: TaggedSpace = { ...space, _instanceOrigin: '' };
set((state) => ({ spaces: [...state.spaces, tagged] }));
return space;