diff --git a/client/src/api/index.ts b/client/src/api/index.ts index e22be0702139..3f3c1c689144 100644 --- a/client/src/api/index.ts +++ b/client/src/api/index.ts @@ -217,6 +217,7 @@ export function isHistoryItem(item: object): item is HistoryItemSummary { type RegisteredUserModel = components["schemas"]["DetailedUserModel"]; type AnonymousUserModel = components["schemas"]["AnonUserModel"]; +type UserModel = RegisteredUserModel | AnonymousUserModel; export interface RegisteredUser extends RegisteredUserModel { isAnonymous: false; @@ -226,20 +227,18 @@ export interface AnonymousUser extends AnonymousUserModel { isAnonymous: true; } -export type GenericUser = RegisteredUser | AnonymousUser; - /** Represents any user, including anonymous users or session-less (null) users.**/ -export type AnyUser = GenericUser | null; +export type AnyUser = RegisteredUser | AnonymousUser | null; -export function isRegisteredUser(user: AnyUser): user is RegisteredUser { +export function isRegisteredUser(user: AnyUser | UserModel): user is RegisteredUser { return user !== null && "email" in user; } -export function isAnonymousUser(user: AnyUser): user is AnonymousUser { +export function isAnonymousUser(user: AnyUser | UserModel): user is AnonymousUser { return user !== null && !isRegisteredUser(user); } -export function isAdminUser(user: AnyUser): user is RegisteredUser { +export function isAdminUser(user: AnyUser | UserModel): user is RegisteredUser { return isRegisteredUser(user) && user.is_admin; } diff --git a/client/src/components/History/CurrentHistory/HistoryMessages.vue b/client/src/components/History/CurrentHistory/HistoryMessages.vue index b078e41f5e73..f3c87772568e 100644 --- a/client/src/components/History/CurrentHistory/HistoryMessages.vue +++ b/client/src/components/History/CurrentHistory/HistoryMessages.vue @@ -5,14 +5,14 @@ import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome"; import { BAlert } from "bootstrap-vue"; import { computed, ref } from "vue"; -import { type GenericUser, type HistorySummary, userOwnsHistory } from "@/api"; +import { type AnyUser, type HistorySummary, userOwnsHistory } from "@/api"; import localize from "@/utils/localization"; library.add(faArchive, faBurn, faTrash); interface Props { history: HistorySummary; - currentUser: GenericUser | null; + currentUser: AnyUser; } const props = defineProps(); diff --git a/client/src/composables/hashedUserId.ts b/client/src/composables/hashedUserId.ts index ef832d774d47..5d86b2dd8c42 100644 --- a/client/src/composables/hashedUserId.ts +++ b/client/src/composables/hashedUserId.ts @@ -2,7 +2,7 @@ import { useLocalStorage } from "@vueuse/core"; import { storeToRefs } from "pinia"; import { computed, type Ref, ref, watch } from "vue"; -import { type GenericUser } from "@/api"; +import { type AnyUser } from "@/api"; import { useUserStore } from "@/stores/userStore"; async function hash32(value: string): Promise { @@ -32,8 +32,8 @@ let unhashedId: string | null = null; /** * One way hashed ID of the current User */ -export function useHashedUserId(user?: Ref) { - let currentUser: Ref; +export function useHashedUserId(user?: Ref) { + let currentUser: Ref; if (user) { currentUser = user; diff --git a/client/src/composables/userLocalStorage.ts b/client/src/composables/userLocalStorage.ts index 16e8a7ce00ea..f0787732a12d 100644 --- a/client/src/composables/userLocalStorage.ts +++ b/client/src/composables/userLocalStorage.ts @@ -1,7 +1,7 @@ import { useLocalStorage } from "@vueuse/core"; import { computed, customRef, type Ref, ref } from "vue"; -import { type GenericUser } from "@/api"; +import { type AnyUser } from "@/api"; import { useHashedUserId } from "./hashedUserId"; @@ -10,7 +10,7 @@ import { useHashedUserId } from "./hashedUserId"; * @param key * @param initialValue */ -export function useUserLocalStorage(key: string, initialValue: T, user?: Ref) { +export function useUserLocalStorage(key: string, initialValue: T, user?: Ref) { const { hashedUserId } = useHashedUserId(user); const storedRef = computed(() => { diff --git a/client/src/stores/userStore.ts b/client/src/stores/userStore.ts index 3691848ff1f0..55a49d491209 100644 --- a/client/src/stores/userStore.ts +++ b/client/src/stores/userStore.ts @@ -11,9 +11,14 @@ import { setCurrentThemeQuery, } from "@/stores/users/queries"; +interface FavoriteTools { + tools: string[]; +} + interface Preferences { - theme: string; - favorites: { tools: string[] }; + theme?: string; + favorites: FavoriteTools; + [key: string]: unknown; } type ListViewMode = "grid" | "list"; @@ -68,12 +73,15 @@ export const useUserStore = defineStore("userStore", () => { if (!loadPromise) { loadPromise = getCurrentUser() .then(async (user) => { - currentUser.value = { ...user, isAnonymous: !user.email }; - currentPreferences.value = user?.preferences ?? null; - // TODO: This is a hack to get around the fact that the API returns a string - if (currentPreferences.value?.favorites) { - currentPreferences.value.favorites = JSON.parse(user?.preferences?.favorites ?? { tools: [] }); + 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(); // load first few histories for user to start pagination @@ -116,7 +124,7 @@ export const useUserStore = defineStore("userStore", () => { function setFavoriteTools(tools: string[]) { if (currentPreferences.value) { - currentPreferences.value.favorites.tools = tools ?? { tools: [] }; + currentPreferences.value.favorites.tools = tools; } } @@ -128,6 +136,16 @@ export const useUserStore = defineStore("userStore", () => { toggledSideBar.value = toggledSideBar.value === currentOpen ? "" : currentOpen; } + 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,