Files
web/src/main/state/user.ts
T

151 lines
5.1 KiB
TypeScript

import { create } from "zustand";
import type { User } from "@/core/types";
import api from "@/core/api";
import { API_BASE_URL } from "@/core/config";
import { initialize, subscribe, isSupported } from "@/core/push-notifications/push-notifications";
import { onlineStatusManager } from "@/core/onlineStatusManager";
import { typingManager } from "@/core/typingManager";
import type { UserState } from "./types";
interface UserStore {
user: UserState;
setUser: (token: string, user: User) => void;
logout: () => Promise<void>;
restoreFromStorage: () => Promise<void>;
setSuspended: (reason: string) => void;
}
export const useUserStore = create<UserStore>((set) => ({
user: {
currentUser: null,
authToken: null,
isSuspended: false,
suspensionReason: null
},
setUser: (token: string, user: User) => {
set({
user: {
currentUser: user,
authToken: token,
isSuspended: user.suspended || false,
suspensionReason: user.suspension_reason || null
}
});
onlineStatusManager.setAuthToken(token);
typingManager.setAuthToken(token);
try {
localStorage.setItem('authToken', token);
localStorage.setItem('currentUser', JSON.stringify(user));
} catch (error) {
console.error('Failed to store credentials in localStorage:', error);
}
// Ping will be sent automatically on WebSocket reconnect
// No need to send here to avoid duplicate pings
},
logout: async () => {
const token = useUserStore.getState().user.authToken;
if (token) {
try {
await api.user.auth.logout(token);
} catch (error) {
console.error("Server logout failed:", error);
}
}
try {
localStorage.removeItem("authToken");
localStorage.removeItem("currentUser");
} catch (error) {
console.error("Failed to clear localStorage:", error);
}
onlineStatusManager.setAuthToken(null);
typingManager.setAuthToken(null);
onlineStatusManager.cleanup();
typingManager.cleanup();
set({
user: {
currentUser: null,
authToken: null,
isSuspended: false,
suspensionReason: null
}
});
},
restoreFromStorage: async () => {
try {
const token = localStorage.getItem('authToken');
if (token) {
const fullResponse = await fetch(`${API_BASE_URL}/user/profile`, {
headers: api.user.auth.getAuthHeaders(token, true)
});
if (fullResponse.ok) {
const user: User = await fullResponse.json();
api.user.auth.restoreKeys();
try {
await api.user.auth.syncPublicKeyToServerIfMissing(token);
} catch (e) {
console.error("Public key sync to server failed:", e);
}
if (user.suspended) {
set({
user: {
currentUser: user,
authToken: token,
isSuspended: true,
suspensionReason: user.suspension_reason || null
}
});
return;
}
set({
user: {
currentUser: user,
authToken: token,
isSuspended: false,
suspensionReason: null
}
});
onlineStatusManager.setAuthToken(token);
typingManager.setAuthToken(token);
// Ping will be sent automatically on WebSocket reconnect
// No need to send here to avoid duplicate pings
try {
if (isSupported()) {
const initialized = await initialize();
if (initialized) {
await subscribe(token);
}
}
} catch (e) {
console.error("Notification setup failed (restored):", e);
}
} else {
throw new Error("Unable to authenticate");
}
}
} catch (error) {
console.error('Failed to restore user from localStorage:', error);
localStorage.removeItem('authToken');
localStorage.removeItem('currentUser');
}
},
setSuspended: (reason: string) => set((state) => ({
user: {
...state.user,
isSuspended: true,
suspensionReason: reason
}
}))
}));