Files
backspace/packages/web/src/stores/authStore.ts
T
Jannis Braun 4d9454382c refactor: unify settings into tabbed modal with UI polish
Split monolithic UserSettings into AccountPanel, VoicePanel, ConnectionsPanel,
and InstancePanel tabs. Remove standalone InstanceSettings modal. Add reusable
Toggle component fixing size deformation, color inconsistency (green→purple),
and flex-shrink issues. Fix custom status clearing by always sending the field
to the server. Wrap Log Out button in glass bubble. Add subtle card depth with
borders.
2026-03-10 03:57:01 +01:00

97 lines
3.3 KiB
TypeScript

import { create } from 'zustand';
import type { User, UserStatus } from '@backspace/shared';
import { api } from '../api/client';
import { useChatStore } from './chatStore';
import { useSpaceStore } from './spaceStore';
import { useSocialStore } from './socialStore';
import { useVoiceStore } from './voiceStore';
import { useInstanceStore } from './instanceStore';
interface AuthState {
token: string | null;
user: User | null;
isLoading: boolean;
error: string | null;
login: (username: string, password: string) => Promise<void>;
register: (username: string, password: string, displayName?: string) => Promise<void>;
logout: () => void;
loadUser: () => Promise<void>;
updateProfile: (data: { displayName?: string; avatar?: string; customStatus?: string; status?: UserStatus }) => Promise<void>;
setUser: (user: User) => void;
clearError: () => void;
}
export const useAuthStore = create<AuthState>((set, get) => ({
token: localStorage.getItem('backspace_token'),
user: null,
isLoading: false,
error: null,
login: async (username: string, password: string) => {
set({ isLoading: true, error: null });
try {
const response = await api.auth.login({ username, password });
localStorage.setItem('backspace_token', response.token);
set({ token: response.token, user: response.user, isLoading: false });
// Auto-connect to remote instances (fire-and-forget)
useInstanceStore.getState().autoConnectAll().catch(() => {});
} catch (err) {
set({ isLoading: false, error: err instanceof Error ? err.message : 'Login failed' });
throw err;
}
},
register: async (username: string, password: string, displayName?: string) => {
set({ isLoading: true, error: null });
try {
const response = await api.auth.register({ username, password, displayName });
localStorage.setItem('backspace_token', response.token);
set({ token: response.token, user: response.user, isLoading: false });
} catch (err) {
set({ isLoading: false, error: err instanceof Error ? err.message : 'Registration failed' });
throw err;
}
},
logout: () => {
localStorage.removeItem('backspace_token');
// Clear all user-scoped state to prevent data leaking between sessions
useChatStore.getState().clearAllMessages();
useSpaceStore.getState().populateFromReady('', [], [], []);
useSocialStore.getState().reset();
useVoiceStore.getState().clearAllVoiceUsers();
useInstanceStore.getState().reset();
set({ token: null, user: null });
},
loadUser: async () => {
const token = get().token;
if (!token) return;
set({ isLoading: true });
try {
const user = await api.users.me();
set({ user, isLoading: false });
// Auto-connect to remote instances (fire-and-forget)
useInstanceStore.getState().autoConnectAll().catch(() => {});
} catch {
localStorage.removeItem('backspace_token');
set({ token: null, user: null, isLoading: false });
}
},
updateProfile: async (data) => {
try {
const user = await api.users.update(data);
set({ user });
} catch (err) {
set({ error: err instanceof Error ? err.message : 'Update failed' });
throw err;
}
},
setUser: (user: User) => set({ user }),
clearError: () => set({ error: null }),
}));