import { defineStore } from "pinia"; import { computed, ref } from "vue"; import { type AnyUser, isAdminUser, isAnonymousUser, isRegisteredUser, type RegisteredUser } from "@/api"; import { useHashedUserId } from "@/composables/hashedUserId"; import { useUserLocalStorageFromHashId } from "@/composables/userLocalStorageFromHashedId"; import { useHistoryStore } from "@/stores/historyStore"; import { addFavoriteToolQuery, getCurrentUser, removeFavoriteToolQuery, setCurrentThemeQuery, } from "@/stores/users/queries"; interface FavoriteTools { tools: string[]; } interface Preferences { theme?: string; favorites: FavoriteTools; [key: string]: unknown; } export type ListViewMode = "grid" | "list"; type UserListViewPreferences = Record; const RECENT_TOOLS_LIMIT = 10; export const useUserStore = defineStore("userStore", () => { const currentUser = ref(null); const currentPreferences = ref(null); const { hashedUserId } = useHashedUserId(currentUser); const currentListViewPreferences = useUserLocalStorageFromHashId( "user-store-list-view-preferences", {}, hashedUserId, ); const hasSeenUploadHelp = useUserLocalStorageFromHashId("user-store-seen-upload-help", false, hashedUserId); const historyPanelWidth = useUserLocalStorageFromHashId("user-store-history-panel-width", 300, hashedUserId); const recentTools = useUserLocalStorageFromHashId("user-store-recent-tools", [], hashedUserId); let loadPromise: Promise | null = null; function $reset() { currentUser.value = null; currentPreferences.value = null; recentTools.value = []; loadPromise = null; } const isAdmin = computed(() => { return isAdminUser(currentUser.value); }); const isAnonymous = computed(() => { return isAnonymousUser(currentUser.value); }); const currentTheme = computed(() => { return currentPreferences.value?.theme ?? null; }); const currentFavorites = computed(() => { if (currentPreferences.value?.favorites) { return currentPreferences.value.favorites; } else { return { tools: [] }; } }); const matchesCurrentUsername = computed(() => { return (username?: string) => { return isRegisteredUser(currentUser.value) && currentUser.value.username === username; }; }); function setCurrentUser(user: RegisteredUser) { currentUser.value = user; } function loadUser(includeHistories = true) { if (!loadPromise) { loadPromise = new Promise((resolve, reject) => { (async () => { try { const user = await getCurrentUser(); if (isRegisteredUser(user)) { currentUser.value = user; currentPreferences.value = processUserPreferences(user); } else if (isAnonymousUser(user)) { currentUser.value = user; } else if (user === null) { currentUser.value = null; } if (includeHistories) { const historyStore = useHistoryStore(); await historyStore.loadHistories(); } resolve(); // Resolve the promise after successful load } catch (e) { console.error("Failed to load user", e); reject(e); // Reject the promise on error } finally { //Don't clear the loadPromise, we still want multiple callers to await. //Instead we must clear it upon $reset // loadPromise = null; } })(); }); } return loadPromise; // Return the shared promise } async function setCurrentTheme(theme: string) { if (!currentUser.value || currentUser.value.isAnonymous) { return; } const currentTheme = await setCurrentThemeQuery(currentUser.value.id, theme); if (currentPreferences.value) { currentPreferences.value.theme = currentTheme; } } async function addFavoriteTool(toolId: string) { if (!currentUser.value || currentUser.value.isAnonymous) { return; } const tools = await addFavoriteToolQuery(currentUser.value.id, toolId); setFavoriteTools(tools); } async function removeFavoriteTool(toolId: string) { if (!currentUser.value || currentUser.value.isAnonymous) { return; } const tools = await removeFavoriteToolQuery(currentUser.value.id, toolId); setFavoriteTools(tools); } function setFavoriteTools(tools: string[]) { if (currentPreferences.value) { currentPreferences.value.favorites.tools = tools; } } function addRecentTool(toolId: string) { if (!toolId) { return; } const currentTools = recentTools.value || []; recentTools.value = [toolId, ...currentTools.filter((id) => id !== toolId)].slice(0, RECENT_TOOLS_LIMIT); } function clearRecentTools() { recentTools.value = []; } function setListViewPreference(listId: string, view: ListViewMode) { currentListViewPreferences.value = { ...currentListViewPreferences.value, [listId]: view, }; } function processUserPreferences(user: RegisteredUser): Preferences { // Favorites are returned as a JSON string by the API const favorites = typeof user.preferences.favorites === "string" ? JSON.parse(user.preferences.favorites) : { tools: [] }; return { ...user.preferences, favorites, }; } return { currentUser, currentPreferences, isAdmin, isAnonymous, currentTheme, currentFavorites, currentListViewPreferences, hasSeenUploadHelp, historyPanelWidth, recentTools, loadUser, matchesCurrentUsername, setCurrentUser, setCurrentTheme, setListViewPreference, addFavoriteTool, removeFavoriteTool, addRecentTool, clearRecentTools, $reset, }; });