import { useStorage } from '@vueuse/core' import { isEeUI } from '#imports' export interface WorkspaceSettings { data: { showOtherUserPersonalViews?: boolean // any other ws level keys we can store here [key: string]: any } lastAccessed: number // TTL and tracking } export interface UserLocalStorageInfo { [userId: string]: { [workspaceId: string]: WorkspaceSettings } } export class UserLocalStorageInfoManager { private storage: Ref private readonly TTL = 180 * 24 * 60 * 60 * 1000 // 180 days private readonly defaultData: WorkspaceSettings['data'] = { showOtherUserPersonalViews: true, } constructor() { this.storage = useStorage('nc-user-local-storage-info', {}) this.cleanExpired() } /** Cleanup expired workspace settings */ private cleanExpired(): void { if (!isEeUI) return const now = Date.now() const data = this.storage.value Object.keys(data).forEach((userId) => { const workspaces = data[userId] if (!workspaces) return Object.keys(workspaces).forEach((wsId) => { if (now - workspaces[wsId]!.lastAccessed > this.TTL) { delete workspaces[wsId] } }) // remove user entirely if no workspaces left if (Object.keys(workspaces).length === 0) { delete data[userId] } }) } /** Cleanup local storage workspaces that no longer exist */ cleanMissingWorkspaces(userId: string, workspaceIds: string[]): void { if (!isEeUI) return const userWorkspaces = this.storage.value[userId] if (!userWorkspaces) return Object.keys(userWorkspaces).forEach((wsId) => { if (!workspaceIds.includes(wsId)) { delete userWorkspaces[wsId] } else { this.touch(userId, wsId) } }) // Remove user if no workspace left if (Object.keys(userWorkspaces).length === 0) { delete this.storage.value[userId] } } /** Ensure workspace exists for user */ private ensureWorkspace(userId: string, workspaceId: string): void { if (!isEeUI) return if (!userId || !workspaceId) return if (!this.storage.value[userId]) this.storage.value[userId] = {} if (!this.storage.value[userId][workspaceId]) { this.storage.value[userId][workspaceId] = { data: {}, lastAccessed: Date.now(), } } } /** Update lastAccessed timestamp */ private touch(userId: string, workspaceId: string): void { if (!userId || !workspaceId || !isEeUI) return if (this.storage.value[userId]?.[workspaceId]) { this.storage.value[userId][workspaceId].lastAccessed = Date.now() } else { this.storage.value[userId] = { ...(this.storage.value[userId] || {}), [workspaceId]: { data: {}, lastAccessed: Date.now(), }, } } } /** Get a workspace-level key */ get(userId: string, workspaceId: string, key: keyof WorkspaceSettings['data'], defaultValue: any = null): any { if (!userId || !workspaceId || !isEeUI) return defaultValue this.touch(userId, workspaceId) if (defaultValue !== null && ncIsUndefined(this.storage.value[userId]![workspaceId]!.data[key])) { this.storage.value[userId]![workspaceId]!.data[key] = defaultValue } return this.storage.value[userId]![workspaceId]!.data[key] } /** Set a workspace-level key */ set(userId: string, workspaceId: string, key: string, value: any): void { if (!userId || !workspaceId || !key || !isEeUI) return this.ensureWorkspace(userId, workspaceId) this.storage.value[userId]![workspaceId]!.data[key] = value this.touch(userId, workspaceId) } /** Clear workspace for user */ clearWorkspace(userId: string, workspaceId: string): void { if (!userId || !workspaceId || !isEeUI) return if (this.storage.value[userId]?.[workspaceId]) { delete this.storage.value[userId][workspaceId] } } /** Clear all data for a user */ clearUser(userId: string): void { if (!userId || !isEeUI) return if (this.storage.value[userId]) { delete this.storage.value[userId] } } } export const userLocalStorageInfoManager = new UserLocalStorageInfoManager()