refactor: make pendingSyncOrigins reactive in Zustand store

Add `pendingSyncOrigins: string[]` to InstanceState so components re-render
when pending password-sync flags change. Hydrate from localStorage on
autoConnectAll, clear on reset, and remove the localStorage-only read path
from `hasPendingPasswordSync`.
This commit is contained in:
Jannis Braun
2026-03-23 00:50:58 +01:00
parent 1bd684a6b0
commit 678e1bf358
+21 -7
View File
@@ -121,6 +121,7 @@ interface InstanceState {
isLoading: boolean; isLoading: boolean;
error: string | null; error: string | null;
_autoConnectDone: boolean; _autoConnectDone: boolean;
pendingSyncOrigins: string[];
probeInstance: (url: string) => Promise<InstanceInfoResponse & { origin: string }>; probeInstance: (url: string) => Promise<InstanceInfoResponse & { origin: string }>;
connectToRemote: (origin: string, password: string, displayName?: string) => Promise<void>; connectToRemote: (origin: string, password: string, displayName?: string) => Promise<void>;
@@ -142,6 +143,7 @@ export const useInstanceStore = create<InstanceState>((set, get) => ({
isLoading: false, isLoading: false,
error: null, error: null,
_autoConnectDone: false, _autoConnectDone: false,
pendingSyncOrigins: [],
probeInstance: async (url: string) => { probeInstance: async (url: string) => {
const origin = normalizeOrigin(url); const origin = normalizeOrigin(url);
@@ -457,15 +459,23 @@ export const useInstanceStore = create<InstanceState>((set, get) => ({
setPendingPasswordSync: (origin: string, pending: boolean) => { setPendingPasswordSync: (origin: string, pending: boolean) => {
const userId = useAuthStore.getState().user?.id; const userId = useAuthStore.getState().user?.id;
if (!userId) return; if (!userId) return;
// Update Zustand state (triggers React re-renders)
set((state) => ({
pendingSyncOrigins: pending
? state.pendingSyncOrigins.includes(origin)
? state.pendingSyncOrigins
: [...state.pendingSyncOrigins, origin]
: state.pendingSyncOrigins.filter(o => o !== origin),
}));
// Also persist to localStorage
const flags: Record<string, boolean> = { [origin]: pending }; const flags: Record<string, boolean> = { [origin]: pending };
saveCachedTokens(get().instances, userId, flags); saveCachedTokens(get().instances, userId, flags);
}, },
hasPendingPasswordSync: (origin: string) => { hasPendingPasswordSync: (origin: string) => {
const userId = useAuthStore.getState().user?.id; return get().pendingSyncOrigins.includes(origin);
if (!userId) return false;
const cached = loadCachedTokens(userId);
return cached[origin]?.pendingPasswordSync === true;
}, },
syncInstanceList: async () => { syncInstanceList: async () => {
@@ -668,8 +678,12 @@ export const useInstanceStore = create<InstanceState>((set, get) => ({
// so disconnected instances survive page reload and can auto-reconnect later // so disconnected instances survive page reload and can auto-reconnect later
saveCachedTokens(get().instances, currentUser.id); saveCachedTokens(get().instances, currentUser.id);
// Mark auto-connect complete so syncInstanceList is now safe to run // Hydrate pendingSyncOrigins from localStorage cache and mark auto-connect done
set({ _autoConnectDone: true }); const freshCached = loadCachedTokens(currentUser.id);
const pendingOrigins = Object.entries(freshCached)
.filter(([, v]) => v.pendingPasswordSync)
.map(([origin]) => origin);
set({ _autoConnectDone: true, pendingSyncOrigins: pendingOrigins });
}, },
reset: () => { reset: () => {
@@ -682,7 +696,7 @@ export const useInstanceStore = create<InstanceState>((set, get) => ({
// Tear down all remote WebSocket connections // Tear down all remote WebSocket connections
disconnectAllRemote(); disconnectAllRemote();
set({ instances: [], isLoading: false, error: null, _autoConnectDone: false }); set({ instances: [], isLoading: false, error: null, _autoConnectDone: false, pendingSyncOrigins: [] });
// Token cache preserved — scoped per user, survives logout for seamless reconnect // Token cache preserved — scoped per user, survives logout for seamless reconnect
}, },
})); }));