diff --git a/src/store.ts b/src/store.ts index 6bafafd..11a0d58 100644 --- a/src/store.ts +++ b/src/store.ts @@ -9,10 +9,17 @@ import { UpdateUserResponse } from './types' import { reactive } from 'vue' import { API } from './main' import { applyTheme } from './themeLoader.ts'; +import { setLanguage } from './i18n' let store: Store | null = null export const initAuth = getAuthData +export interface UserConfig { + language: string | null; + theme: string | null; + compact_layout: boolean | null; +} + async function getStore() { if (!store) store = await load('store.json') return store @@ -75,17 +82,116 @@ export async function getLastRoom(): Promise { return (await s.get('last_room_uuid')) ?? null } +// ==== Global config logic ==== + +export async function getLocalConfig(): Promise { + const s = await getStore(); + let config = await s.get('config'); + + if (!config) { + // Fallback migration check for old config fields + const legacyTheme = await s.get('theme'); + const legacyLang = await s.get('language'); + const legacyCompact = await s.get('compact_layout'); + + config = { + language: legacyLang ?? null, + theme: legacyTheme ?? null, + compact_layout: legacyCompact ?? null + }; + + await s.set('config', config); + await s.save(); + } + + return config; +} + +export async function saveLocalConfig(config: UserConfig) { + const s = await getStore(); + await s.set('config', config); + await s.save(); +} + +// Queue system to ensure config changes sync reliably with the API +let isSyncing = false; +let pendingConfig: UserConfig | null = null; +const retryDelays = [500, 2000, 5000]; + +async function syncConfigWithRetry(config: UserConfig) { + pendingConfig = config; + + if (isSyncing) return; + isSyncing = true; + + while (pendingConfig !== null) { + const configToUpload = pendingConfig; + pendingConfig = null; + + let success = false; + let attempt = 0; + + while (!success) { + const auth = await getAuthData(); + if (!auth.token) { + // Stop syncing if the session is cleared + isSyncing = false; + return; + } + + try { + await apiFetch('/account/save-config', { + method: 'POST', + body: JSON.stringify(configToUpload), + }); + success = true; + } catch (error) { + const delay = retryDelays[attempt] ?? 5000; + attempt++; + console.warn(`Sync failed. Retrying in ${delay / 1000}s...`, error); + await new Promise((resolve) => setTimeout(resolve, delay)); + } + } + } + + isSyncing = false; +} + +export async function fetchAndApplyRemoteConfig() { + const auth = await getAuthData(); + if (!auth.token) return; + + try { + const remoteConfig = await apiFetch('/account/get-config'); + if (remoteConfig) { + await saveLocalConfig(remoteConfig); + if (remoteConfig.theme) { + applyTheme(remoteConfig.theme); + } + if (remoteConfig.language) { + setLanguage(remoteConfig.language); + } + } + } catch (error) { + console.error("Failed to fetch remote config:", error); + } +} + +// ==== Language preferences ==== + export async function saveLocalePreference(locale: string) { - const s = await getStore() - await s.set('language', locale) - await s.save() + const config = await getLocalConfig(); + config.language = locale; + await saveLocalConfig(config); + await syncConfigWithRetry(config); } export async function getLocalePreference(): Promise { - const s = await getStore() - return (await s.get('language')) ?? null + const config = await getLocalConfig(); + return config.language; } +// ==== Auth operations ==== export async function login(email: string, username: string, password: string) { const res: LoginResponse = await apiFetch('/login', { @@ -100,6 +206,7 @@ export async function login(email: string, username: string, password: string) { avatar_url: getAvatar(res.uuid) }; await saveAuthData(res.token, user) + await fetchAndApplyRemoteConfig() return { token: res.token, uuid: res.uuid, isAuthenticated: true } } @@ -189,21 +296,21 @@ export function refreshAvatar(uuid: string) { // ==== Color themes ==== export async function saveThemePreference(themeId: string) { - const s = await getStore(); - await s.set('theme', themeId); - await s.save(); + const config = await getLocalConfig(); + config.theme = themeId; + await saveLocalConfig(config); applyTheme(themeId); + await syncConfigWithRetry(config); } export async function initTheme() { - const s = await getStore(); - const themeId = (await s.get('theme')) || 'default'; + const themeId = await getThemePreference(); applyTheme(themeId); } export async function getThemePreference(): Promise { - const s = await getStore() - return (await s.get('theme')) ?? 'default' + const config = await getLocalConfig(); + return config.theme ?? 'default'; } export async function applyStoredTheme() { @@ -214,14 +321,15 @@ export async function applyStoredTheme() { // ==== Layout ==== export async function saveCompactLayoutPreference(enabled: boolean) { - const s = await getStore(); - await s.set('compact_layout', enabled); - await s.save(); + const config = await getLocalConfig(); + config.compact_layout = enabled; + await saveLocalConfig(config); + await syncConfigWithRetry(config); } export async function getCompactLayoutPreference(): Promise { - const s = await getStore(); - return (await s.get('compact_layout')) ?? false; + const config = await getLocalConfig(); + return config.compact_layout ?? false; } // ==== Message draft ==== diff --git a/yarn.lock b/yarn.lock index 2a0ec33..373c326 100644 --- a/yarn.lock +++ b/yarn.lock @@ -345,77 +345,77 @@ resolved "https://registry.yarnpkg.com/@tauri-apps/api/-/api-2.11.1.tgz#cd6b13fc26403ca095a02e39ecdbec8048d2872d" integrity sha512-M2FPuYND2m+wh5hfW9ZpSdxMPdEJovPBWwoHJmwUpysTYNHaOkVFN419m/K0LIgjb/7KU2vBgsUepJWugQCvAA== -"@tauri-apps/cli-darwin-arm64@2.11.3": - version "2.11.3" - resolved "https://registry.yarnpkg.com/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-2.11.3.tgz#8896f34e1925f41b543e522552d0a7a5b8d60153" - integrity sha512-BxpaM8bsCoXs3wd4WKYhas/G1gs7+r7B+e4WnyRk2GEoVOouJB1hoL6E6YLXZDXbYci6VFdrNnobQwd2uVL4ew== +"@tauri-apps/cli-darwin-arm64@2.11.4": + version "2.11.4" + resolved "https://registry.yarnpkg.com/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-2.11.4.tgz#2884e6f2e62318e4ca1db0d6d3be8380dcb6c542" + integrity sha512-1ryOF3ZhpZ/nemHV5zVwBQBz9jDGKmKPvWPADOhc83ig0P4bMc2iER4NbC6r9sjeIZ6RVQ4g3RZIYvezhcl4TQ== -"@tauri-apps/cli-darwin-x64@2.11.3": - version "2.11.3" - resolved "https://registry.yarnpkg.com/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-2.11.3.tgz#854300d71a791c8746c51647ec616f31d1747aa3" - integrity sha512-DbZYuPB1ZEzcAHYeyCvo3ltzM27+aXwPloCrtexPnmgPgulYJm3TOq6aC4S+wPhSXteddg8zImtNkvx/gQzmwg== +"@tauri-apps/cli-darwin-x64@2.11.4": + version "2.11.4" + resolved "https://registry.yarnpkg.com/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-2.11.4.tgz#d9e2d92b15ee2ff0b7819ea5c6df398f2fc754ba" + integrity sha512-uFsGQAAfuyz1k/yGLmkWfkBlgKAqZfxqlHmLWx81QU27RJWfmbNHCIq8T8w1e+VClleIuZUjpHWfoE4E3DLo3A== -"@tauri-apps/cli-linux-arm-gnueabihf@2.11.3": - version "2.11.3" - resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-2.11.3.tgz#c978ae1a403d5a9eff85876c73cc9b571e43d3e3" - integrity sha512-741NduqBmz1XkdU8yz3OI/kBZtqHbvxo9F9ytIeWYU69/Ba9dcZEbqOU++Dp0G/XU8vAI0TfTywEl+p+BbLvaA== +"@tauri-apps/cli-linux-arm-gnueabihf@2.11.4": + version "2.11.4" + resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-2.11.4.tgz#af14ae237d37a403acdb56c7421476b65057db27" + integrity sha512-IaHZn5CdBL21oUmjiVOS1ctw6Ip1O0pjp70FwOWmYz1myWe0SY96ZIj2FYf7pT0m8bI2h/hrs5ZbEXXh44/MkQ== -"@tauri-apps/cli-linux-arm64-gnu@2.11.3": - version "2.11.3" - resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-2.11.3.tgz#82bbc11a877eba75f113e88468a8c79061ec57f4" - integrity sha512-RWAXT8pTqIczXcoic+LXlo6uEbAXGB0cgh6Pg7Y9xVnEbzryQ1JHtRGj9SxzrKSemBIDBH6Qc24kK2G69i8ofA== +"@tauri-apps/cli-linux-arm64-gnu@2.11.4": + version "2.11.4" + resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-2.11.4.tgz#fe521657fb011eb9a5236c5c9d0fa63ebce39dd4" + integrity sha512-N41/ukTRVe6XSuUTESuFdGeOW2i7k62tK+6gHK5Kd5/q5RPvvi19GaWAVPPb9u95HSGmTChSolBfzynUsssFaA== -"@tauri-apps/cli-linux-arm64-musl@2.11.3": - version "2.11.3" - resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.11.3.tgz#3de5f411ae426e008dfaba2c00f822a067740e13" - integrity sha512-qomqYS+yAkd0gXMRmhguWXc7RfVN+XKKXaEwbf5QmKURwydLFOTldd6F8/WoZDSsBMrV8dpNxz0YneGLmobiSA== +"@tauri-apps/cli-linux-arm64-musl@2.11.4": + version "2.11.4" + resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.11.4.tgz#4ac7dcc22c25b1738b1d390432714aa9d9905a10" + integrity sha512-v277UnT/fB64xAfSroL5N3Km3tLmvATWqJJw/wRI+g6o+HkeD0slyE7gOhNs1MbjE41R7bQOTxMVoL3aomUJmw== -"@tauri-apps/cli-linux-riscv64-gnu@2.11.3": - version "2.11.3" - resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-riscv64-gnu/-/cli-linux-riscv64-gnu-2.11.3.tgz#c0cda09650572c0b407388898235c906164d9336" - integrity sha512-jOCXbDqeDj5XcclsOBAaXjtTgwZCVg8zEZ+dbPUCoADOgljFgL0rOkYTc96vUYgOrYEfuHYihWMxIDGaD6GwJw== +"@tauri-apps/cli-linux-riscv64-gnu@2.11.4": + version "2.11.4" + resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-riscv64-gnu/-/cli-linux-riscv64-gnu-2.11.4.tgz#f0cd8de249c8ede745cf69b57d96ad9f1dc55743" + integrity sha512-qqgNkQ2u1yZHxjhxsZaxUtRDW8dIqIYm33rx/mzwQv0SfY9x1B+iraj8vWeFiXjjSVVhEMepXSOts1TqPzvXNQ== -"@tauri-apps/cli-linux-x64-gnu@2.11.3": - version "2.11.3" - resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-2.11.3.tgz#9e686bb414e7eecee53465013c791c93790323e2" - integrity sha512-+u3HO/F3gHwL48t9gWN/urqZvpaEJzBFmTaq5eSIhvy8TOvnhb+LgJr3Q3BG+5JxuBrCUjqtOEz6gMttdJFSBA== +"@tauri-apps/cli-linux-x64-gnu@2.11.4": + version "2.11.4" + resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-2.11.4.tgz#944636a54656c95bf0eb6ca4af3fe317f4d9a82e" + integrity sha512-2VRNWl84FOH0m2giiDkO2h0QXlcMJeX+zJDpI5kDIQAx6s+geF3v48F4DXfJez4GS/FdoDGnPnw1C2iYGbQ7bQ== -"@tauri-apps/cli-linux-x64-musl@2.11.3": - version "2.11.3" - resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-2.11.3.tgz#31cb7e6525fb86b58bdf7be46af29b2ce8903ff0" - integrity sha512-spr5Jpr6KF/vehkLwJ0YmdGv8QwpWU+uw7J8bgijO0sox6ZCYsSNMbcsQjTqPi4xl+p0woIYpWXgChgHYpAc8g== +"@tauri-apps/cli-linux-x64-musl@2.11.4": + version "2.11.4" + resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-2.11.4.tgz#050f98b7800eeb7812d6d583423d37a5b629f571" + integrity sha512-o9GyhYor/nc7xarmwDE3ka2szuW3uuZzXjHWh64Q8YX5AtSgxdQkFWzrY4O8KiGtVNvFBI14H3Q49Qj5TOIP/A== -"@tauri-apps/cli-win32-arm64-msvc@2.11.3": - version "2.11.3" - resolved "https://registry.yarnpkg.com/@tauri-apps/cli-win32-arm64-msvc/-/cli-win32-arm64-msvc-2.11.3.tgz#263b9fb94a099f477944d4ceaf293d4482dcd606" - integrity sha512-abkoRQih5xBa3vz2spWaex0kP/MzVzVPQHom2f8jnCq46R/luOD6Uy85EMU9/bfzf6ZzdorWJsgO+OMX90Fx2w== +"@tauri-apps/cli-win32-arm64-msvc@2.11.4": + version "2.11.4" + resolved "https://registry.yarnpkg.com/@tauri-apps/cli-win32-arm64-msvc/-/cli-win32-arm64-msvc-2.11.4.tgz#24be91dc58f89cd8c9142b52797544991d8cc16c" + integrity sha512-ld5Ehb598m0VkYyylRPNeCFsBe/km0jxis6KgMpl3IGY6I/i1RwQXO05I1AsXUXO2WC6AvB/Lw4qTf/asiuEiQ== -"@tauri-apps/cli-win32-ia32-msvc@2.11.3": - version "2.11.3" - resolved "https://registry.yarnpkg.com/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-2.11.3.tgz#4222fb680addd41d8aa6fde98bae9b7a3c85d88f" - integrity sha512-Vy6AvzFm1G40hg3r+OYDB3jkuu7R4wnMzbQBKuun9v6Cgg8IierpLL7toMzrZKs/8NlG8Sg4x1iLFR52oknyHg== +"@tauri-apps/cli-win32-ia32-msvc@2.11.4": + version "2.11.4" + resolved "https://registry.yarnpkg.com/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-2.11.4.tgz#0fb80df916a4bd0576ca3c30d2308e786833f433" + integrity sha512-12Hxi0XX/H5VFxO/bGgHkFWhml9VMgEOu9CidjeCeTNQ1l6fpUlbiGgSP7CLI3PFtW9/FfbeHieZ+kyWK5H7CA== -"@tauri-apps/cli-win32-x64-msvc@2.11.3": - version "2.11.3" - resolved "https://registry.yarnpkg.com/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-2.11.3.tgz#00dbcddbff37ff1fafc317cbe22c78d00b9b5014" - integrity sha512-GlciF75GdbseajOyib2aCHwE3BXIqZ1liGKWLFRvCdN5wm8h8hFssEVKQ/6E+2jsMLg9v7LCTb983YFnn0QSww== +"@tauri-apps/cli-win32-x64-msvc@2.11.4": + version "2.11.4" + resolved "https://registry.yarnpkg.com/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-2.11.4.tgz#31917e255214731b31a0fb847279be71fe3e05d8" + integrity sha512-+vDiqBIU5dMISg/wNvX3sF+ZHfgJGJ5T0AcO+EHNXV9GGAG+P5fzodlDXD3QdKCRgZxMoCm5PPvj3BqLNjBthw== "@tauri-apps/cli@^2": - version "2.11.3" - resolved "https://registry.yarnpkg.com/@tauri-apps/cli/-/cli-2.11.3.tgz#edeabf8c94bdb21e04b69f9f1f3267d97df38a55" - integrity sha512-EElQe8z8uD7Pi5++tJ/UfEwWuK08rd3oCDYdeIbJAb6pZRrxlqmoF5gh5H5YvzmUPhS4IRCaLSsQhvWkrfK+GQ== + version "2.11.4" + resolved "https://registry.yarnpkg.com/@tauri-apps/cli/-/cli-2.11.4.tgz#eafeb0d3a7bd364d27014843fb0a84d9c972e626" + integrity sha512-R8xGtMpwyetawSqm9kYOuMmEqkhUbvcUy8n0aNXIxollKBLESUu5f4Fx+64hgASYm1H+jSWq6jCW6zqTnH6hqQ== optionalDependencies: - "@tauri-apps/cli-darwin-arm64" "2.11.3" - "@tauri-apps/cli-darwin-x64" "2.11.3" - "@tauri-apps/cli-linux-arm-gnueabihf" "2.11.3" - "@tauri-apps/cli-linux-arm64-gnu" "2.11.3" - "@tauri-apps/cli-linux-arm64-musl" "2.11.3" - "@tauri-apps/cli-linux-riscv64-gnu" "2.11.3" - "@tauri-apps/cli-linux-x64-gnu" "2.11.3" - "@tauri-apps/cli-linux-x64-musl" "2.11.3" - "@tauri-apps/cli-win32-arm64-msvc" "2.11.3" - "@tauri-apps/cli-win32-ia32-msvc" "2.11.3" - "@tauri-apps/cli-win32-x64-msvc" "2.11.3" + "@tauri-apps/cli-darwin-arm64" "2.11.4" + "@tauri-apps/cli-darwin-x64" "2.11.4" + "@tauri-apps/cli-linux-arm-gnueabihf" "2.11.4" + "@tauri-apps/cli-linux-arm64-gnu" "2.11.4" + "@tauri-apps/cli-linux-arm64-musl" "2.11.4" + "@tauri-apps/cli-linux-riscv64-gnu" "2.11.4" + "@tauri-apps/cli-linux-x64-gnu" "2.11.4" + "@tauri-apps/cli-linux-x64-musl" "2.11.4" + "@tauri-apps/cli-win32-arm64-msvc" "2.11.4" + "@tauri-apps/cli-win32-ia32-msvc" "2.11.4" + "@tauri-apps/cli-win32-x64-msvc" "2.11.4" "@tauri-apps/plugin-dialog@~2": version "2.7.1" @@ -661,9 +661,9 @@ birpc@^2.6.1: integrity sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw== brace-expansion@^2.0.2: - version "2.1.1" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.1.1.tgz#c68b1c4111c76aae3a6fba55d496cee10c39dad8" - integrity sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA== + version "2.1.2" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.1.2.tgz#0bba2271feb7d458b0d31ad13625aaa4754431e2" + integrity sha512-w5JZcKgdhDOgOwm8H+KgbosopHMuGcl6qbulwjtz3SM7I7P3yW1eAjzMPLrIE+NQ9vjgANKHWeMHnrT0OXW1oA== dependencies: balanced-match "^1.0.0" @@ -799,14 +799,14 @@ picocolors@^1.1.1: integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== picomatch@^4.0.2, picomatch@^4.0.3, picomatch@^4.0.4: - version "4.0.4" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.4.tgz#fd6f5e00a143086e074dffe4c924b8fb293b0589" - integrity sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A== + version "4.0.5" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.5.tgz#51ea57a17d86f605f81039595fbc40ed06a55fab" + integrity sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A== postcss@^8.5.15, postcss@^8.5.3: - version "8.5.15" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.5.15.tgz#d1eaf677a324e9ec02196da2d3fecf4a0b9a735c" - integrity sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A== + version "8.5.16" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.5.16.tgz#1230ce0b5df354c24c0ea45f99ce5f6a88279d28" + integrity sha512-vuwillviilfKZsg0VGj5R/YwwcHx4SLsIOI/7K6mQkWx+l5cUHTjj5g0AasTBcyXsbfTgrwsUNmVUb5xVwyPwg== dependencies: nanoid "^3.3.12" picocolors "^1.1.1"