fix: federation self-connection causing sidebar space duplication

Guard autoConnectAll against connecting to window.location.origin,
send perspective-correct replicatedInstances lists so remotes never
store self-references, and deduplicate unaccounted spaces in sidebar.
This commit is contained in:
Jannis Braun
2026-03-12 16:29:11 +01:00
parent 7a7e0784d8
commit 06b56472f9
2 changed files with 27 additions and 11 deletions
@@ -899,6 +899,7 @@ export function SpaceSidebar() {
for (const space of spaces) { for (const space of spaces) {
if (!accountedSpaceIds.has(space.id)) { if (!accountedSpaceIds.has(space.id)) {
result.push({ type: 'space', space }); result.push({ type: 'space', space });
accountedSpaceIds.add(space.id);
} }
} }
+26 -11
View File
@@ -478,25 +478,38 @@ export const useInstanceStore = create<InstanceState>((set, get) => ({
const currentUser = useAuthStore.getState().user; const currentUser = useAuthStore.getState().user;
if (!currentUser) return; if (!currentUser) return;
// Build the replicated instances list from all connected remotes // Build perspective-correct replicated instance lists.
const replicatedInstances: ReplicatedInstance[] = instances.map(inst => ({ // Each instance should store references to OTHER instances, never itself.
const homeOrigin = window.location.origin;
const homeUsername = currentUser.username.includes('@')
? currentUser.username.split('@')[0]!
: currentUser.username;
// Home list: all remotes (home never references itself)
const homeList: ReplicatedInstance[] = instances.map(inst => ({
origin: inst.origin, origin: inst.origin,
username: inst.username, username: inst.username,
})); }));
// Push to home instance // Push to home instance
const homePromise = api.users.update({ replicatedInstances }).catch((err) => { const homePromise = api.users.update({ replicatedInstances: homeList }).catch((err) => {
console.warn('Failed to sync instance list to home:', err); console.warn('Failed to sync instance list to home:', err);
}); });
// Push to each remote instance // Push perspective-correct list to each remote instance:
const remotePromises = instances // include home + all OTHER remotes, but exclude the remote's own origin
.filter(inst => inst.status === 'connected') const connectedInstances = instances.filter(inst => inst.status === 'connected');
.map(inst => const remotePromises = connectedInstances.map(inst => {
inst.api.users.update({ replicatedInstances }).catch((err) => { const listForRemote: ReplicatedInstance[] = [
console.warn(`Failed to sync instance list to ${inst.origin}:`, err); { origin: homeOrigin, username: homeUsername },
}) ...instances
); .filter(other => other.origin !== inst.origin)
.map(other => ({ origin: other.origin, username: other.username })),
];
return inst.api.users.update({ replicatedInstances: listForRemote }).catch((err) => {
console.warn(`Failed to sync instance list to ${inst.origin}:`, err);
});
});
await Promise.all([homePromise, ...remotePromises]); await Promise.all([homePromise, ...remotePromises]);
}, },
@@ -518,6 +531,8 @@ export const useInstanceStore = create<InstanceState>((set, get) => ({
for (const ri of currentUser.replicatedInstances) { for (const ri of currentUser.replicatedInstances) {
const origin = ri.origin || `https://${ri.domain}`; const origin = ri.origin || `https://${ri.domain}`;
// Never connect to ourselves — home WS is managed separately
if (origin === window.location.origin) continue;
if (get().instances.some(i => i.origin === origin)) continue; // already loaded if (get().instances.some(i => i.origin === origin)) continue; // already loaded
const entry = cached[origin]; const entry = cached[origin];
if (entry) { if (entry) {