diff --git a/pkg/models/user_app_cloud_setting.go b/pkg/models/user_app_cloud_setting.go index 3817bd6a..8f0601d0 100644 --- a/pkg/models/user_app_cloud_setting.go +++ b/pkg/models/user_app_cloud_setting.go @@ -15,7 +15,10 @@ const ( var ALL_ALLOWED_CLOUD_SYNC_APP_SETTING_KEY_TYPES = map[string]UserApplicationCloudSettingType{ // Basic Settings - "showAccountBalance": USER_APPLICATION_CLOUD_SETTING_TYPE_BOOLEAN, + "showAccountBalance": USER_APPLICATION_CLOUD_SETTING_TYPE_BOOLEAN, + "autoUpdateExchangeRatesData": USER_APPLICATION_CLOUD_SETTING_TYPE_BOOLEAN, + // Navigation Bar + "showAddTransactionButtonInDesktopNavbar": USER_APPLICATION_CLOUD_SETTING_TYPE_BOOLEAN, // Overview Page "showAmountInHomePage": USER_APPLICATION_CLOUD_SETTING_TYPE_BOOLEAN, "timezoneUsedForStatisticsInHomePage": USER_APPLICATION_CLOUD_SETTING_TYPE_NUMBER, @@ -41,6 +44,9 @@ var ALL_ALLOWED_CLOUD_SYNC_APP_SETTING_KEY_TYPES = map[string]UserApplicationClo "hideCategoriesWithoutAccounts": USER_APPLICATION_CLOUD_SETTING_TYPE_BOOLEAN, // Exchange Rates Data Page "currencySortByInExchangeRatesPage": USER_APPLICATION_CLOUD_SETTING_TYPE_NUMBER, + // Browser Cache Management + "mapCacheExpiration": USER_APPLICATION_CLOUD_SETTING_TYPE_NUMBER, + "exchangeRatesDataCacheExpiration": USER_APPLICATION_CLOUD_SETTING_TYPE_NUMBER, // Statistics Settings "statistics.defaultChartDataType": USER_APPLICATION_CLOUD_SETTING_TYPE_NUMBER, "statistics.defaultTimezoneType": USER_APPLICATION_CLOUD_SETTING_TYPE_NUMBER, diff --git a/src/DesktopApp.vue b/src/DesktopApp.vue index 8b3d0025..ac58e202 100644 --- a/src/DesktopApp.vue +++ b/src/DesktopApp.vue @@ -78,16 +78,6 @@ onMounted(() => { const languageInfo = getCurrentLanguageInfo(); initMapProvider(languageInfo?.alternativeLanguageTag); }); - - if ('serviceWorker' in navigator) { - navigator.serviceWorker.addEventListener('controllerchange', () => { - updateMapCacheExpiration(settingsStore.appSettings.mapCacheExpiration); - }); - - if (navigator.serviceWorker.controller) { - updateMapCacheExpiration(settingsStore.appSettings.mapCacheExpiration); - } - } }); watch(currentNotificationContent, (newValue) => { @@ -114,7 +104,6 @@ window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', fun let localeDefaultSettings = initLocale(userStore.currentUserLanguage, settingsStore.appSettings.timeZone); settingsStore.updateLocalizedDefaultSettings(localeDefaultSettings); -exchangeRatesStore.removeExpiredExchangeRates(true); setExpenseAndIncomeAmountColor(userStore.currentUserExpenseAmountColor, userStore.currentUserIncomeAmountColor); @@ -132,12 +121,11 @@ if (isUserLogined() && initialRoutePath !== '/verify_email' && initialRoutePath rootStore.setNotificationContent(response.notificationContent); } } - }); - // auto refresh exchange rates data - if (settingsStore.appSettings.autoUpdateExchangeRatesData) { - exchangeRatesStore.getLatestExchangeRates({ silent: true, force: false }); - } + updateMapCacheExpiration(settingsStore.appSettings.mapCacheExpiration); + exchangeRatesStore.removeExpiredExchangeRates(true); + exchangeRatesStore.autoUpdateExchangeRatesData(); + }); } } diff --git a/src/MobileApp.vue b/src/MobileApp.vue index 302dc496..a43846b2 100644 --- a/src/MobileApp.vue +++ b/src/MobileApp.vue @@ -181,16 +181,6 @@ onMounted(() => { const languageInfo = getCurrentLanguageInfo(); initMapProvider(languageInfo?.alternativeLanguageTag); }); - - if ('serviceWorker' in navigator) { - navigator.serviceWorker.addEventListener('controllerchange', () => { - updateMapCacheExpiration(settingsStore.appSettings.mapCacheExpiration); - }); - - if (navigator.serviceWorker.controller) { - updateMapCacheExpiration(settingsStore.appSettings.mapCacheExpiration); - } - } }); watch(currentNotificationContent, (newValue) => { @@ -221,7 +211,6 @@ watch(currentNotificationContent, (newValue) => { let localeDefaultSettings = initLocale(userStore.currentUserLanguage, settingsStore.appSettings.timeZone); settingsStore.updateLocalizedDefaultSettings(localeDefaultSettings); -exchangeRatesStore.removeExpiredExchangeRates(true); setExpenseAndIncomeAmountColor(userStore.currentUserExpenseAmountColor, userStore.currentUserIncomeAmountColor); @@ -239,12 +228,11 @@ if (isUserLogined()) { rootStore.setNotificationContent(response.notificationContent); } } - }); - // auto refresh exchange rates data - if (settingsStore.appSettings.autoUpdateExchangeRatesData) { - exchangeRatesStore.getLatestExchangeRates({ silent: true, force: false }); - } + updateMapCacheExpiration(settingsStore.appSettings.mapCacheExpiration); + exchangeRatesStore.removeExpiredExchangeRates(true); + exchangeRatesStore.autoUpdateExchangeRatesData(); + }); } } diff --git a/src/core/setting.ts b/src/core/setting.ts index 62ce9e8a..bec8d320 100644 --- a/src/core/setting.ts +++ b/src/core/setting.ts @@ -62,7 +62,7 @@ export interface ApplicationSettings extends BaseApplicationSetting { hideCategoriesWithoutAccounts: boolean; // Exchange Rates Data Page currencySortByInExchangeRatesPage: number; - // Browser Cache Settings + // Browser Cache Management mapCacheExpiration: number, exchangeRatesDataCacheExpiration: number, // Statistics Settings @@ -110,6 +110,9 @@ export interface WebAuthnConfig { export const ALL_ALLOWED_CLOUD_SYNC_APP_SETTING_KEY_TYPES: Record = { // Basic Settings 'showAccountBalance': UserApplicationCloudSettingType.Boolean, + 'autoUpdateExchangeRatesData': UserApplicationCloudSettingType.Boolean, + // Navigation Bar + 'showAddTransactionButtonInDesktopNavbar': UserApplicationCloudSettingType.Boolean, // Overview Page 'showAmountInHomePage': UserApplicationCloudSettingType.Boolean, 'timezoneUsedForStatisticsInHomePage': UserApplicationCloudSettingType.Number, @@ -135,6 +138,9 @@ export const ALL_ALLOWED_CLOUD_SYNC_APP_SETTING_KEY_TYPES: Record { if (!window.caches) { logger.error('caches API is not supported in this browser'); @@ -35,6 +37,46 @@ function findFirstCacheName(prefix: string): Promise { }); } +function doUpdateMapCacheExpiration(expireSeconds: number): Promise { + const config: SWMapCacheConfig = { + enabled: expireSeconds >= 0, + patterns: services.getMapProxyTileImageAndAnnotationImageUrlPatterns(), + maxEntries: MAP_CACHE_MAX_ENTRIES, + maxAgeMilliseconds: expireSeconds * 1000 + }; + + return new Promise((resolve, reject) => { + if (!navigator.serviceWorker || !navigator.serviceWorker.controller) { + reject(new Error('Service worker is not supported or not active')); + return; + } + + const controller = navigator.serviceWorker.controller; + + navigator.serviceWorker.ready.then(() => { + const messageChannel = new MessageChannel(); + + messageChannel.port1.onmessage = (event) => { + if (event.data && event.data.type === SW_MESSAGE_TYPE_UPDATE_MAP_CACHE_CONFIG_RESPONSE) { + logger.info('Map cache config updated successfully in service worker: ' + JSON.stringify(event.data.payload)); + resolve(); + } else { + logger.error('cannot update map cache config, invalid response from service worker', event); + reject(new Error('Invalid response from service worker')); + } + }; + + controller.postMessage({ + type: SW_MESSAGE_TYPE_UPDATE_MAP_CACHE_CONFIG, + payload: config + }, [messageChannel.port2]); + }).catch(error => { + logger.error('failed to update map cache config', error); + reject(error); + }); + }); +} + async function getCacheTotalSize(cacheName: string): Promise { if (!window.caches) { logger.error('caches API is not supported in this browser'); @@ -118,44 +160,19 @@ export function loadBrowserCacheStatistics(): Promise { }); } -export function updateMapCacheExpiration(expireSeconds: number): Promise { - const config: SWMapCacheConfig = { - enabled: expireSeconds >= 0, - patterns: services.getMapProxyTileImageAndAnnotationImageUrlPatterns(), - maxEntries: MAP_CACHE_MAX_ENTRIES, - maxAgeMilliseconds: expireSeconds * 1000 - }; - - return new Promise((resolve, reject) => { - if (!navigator.serviceWorker || !navigator.serviceWorker.controller) { - reject(new Error('Service worker is not supported or not active')); - return; +export function updateMapCacheExpiration(expireSeconds: number): void { + if ('serviceWorker' in navigator) { + if (!controllerchangeListenerAdded) { + navigator.serviceWorker.addEventListener('controllerchange', () => { + doUpdateMapCacheExpiration(expireSeconds); + }); + controllerchangeListenerAdded = true; } - const controller = navigator.serviceWorker.controller; - - navigator.serviceWorker.ready.then(() => { - const messageChannel = new MessageChannel(); - - messageChannel.port1.onmessage = (event) => { - if (event.data && event.data.type === SW_MESSAGE_TYPE_UPDATE_MAP_CACHE_CONFIG_RESPONSE) { - logger.info('Map cache config updated successfully in service worker: ' + JSON.stringify(event.data.payload)); - resolve(); - } else { - logger.error('cannot update map cache config, invalid response from service worker', event); - reject(new Error('Invalid response from service worker')); - } - }; - - controller.postMessage({ - type: SW_MESSAGE_TYPE_UPDATE_MAP_CACHE_CONFIG, - payload: config - }, [messageChannel.port2]); - }).catch(error => { - logger.error('failed to update map cache config', error); - reject(error); - }); - }); + if (navigator.serviceWorker.controller) { + doUpdateMapCacheExpiration(expireSeconds); + } + } } export function clearMapDataCache(): Promise { diff --git a/src/stores/exchangeRates.ts b/src/stores/exchangeRates.ts index 51127449..40de1d6a 100644 --- a/src/stores/exchangeRates.ts +++ b/src/stores/exchangeRates.ts @@ -169,6 +169,12 @@ export const useExchangeRatesStore = defineStore('exchangeRates', () => { clearExchangeRatesFromLocalStorage(); } + function autoUpdateExchangeRatesData(): void { + if (settingsStore.appSettings.autoUpdateExchangeRatesData) { + getLatestExchangeRates({ silent: true, force: false }); + } + } + function getLatestExchangeRates({ silent, force }: { silent: boolean, force: boolean }): Promise { const currentExchangeRateData = latestExchangeRates.value; const now = getCurrentUnixTime(); @@ -331,6 +337,7 @@ export const useExchangeRatesStore = defineStore('exchangeRates', () => { getExchangeRatesCacheSize, removeExpiredExchangeRates, resetLatestExchangeRates, + autoUpdateExchangeRatesData, getLatestExchangeRates, updateUserCustomExchangeRate, deleteUserCustomExchangeRate, diff --git a/src/stores/setting.ts b/src/stores/setting.ts index 1bff2859..069952a6 100644 --- a/src/stores/setting.ts +++ b/src/stores/setting.ts @@ -152,6 +152,7 @@ export const useSettingsStore = defineStore('settings', () => { function setAutoUpdateExchangeRatesData(value: boolean): void { updateApplicationSettingsValue('autoUpdateExchangeRatesData', value); appSettings.value.autoUpdateExchangeRatesData = value; + updateUserApplicationCloudSettingValue('autoUpdateExchangeRatesData', value); } function setShowAccountBalance(value: boolean): void { @@ -185,6 +186,7 @@ export const useSettingsStore = defineStore('settings', () => { function setShowAddTransactionButtonInDesktopNavbar(value: boolean): void { updateApplicationSettingsValue('showAddTransactionButtonInDesktopNavbar', value); appSettings.value.showAddTransactionButtonInDesktopNavbar = value; + updateUserApplicationCloudSettingValue('showAddTransactionButtonInDesktopNavbar', value); } // Overview Page @@ -310,15 +312,17 @@ export const useSettingsStore = defineStore('settings', () => { updateUserApplicationCloudSettingValue('currencySortByInExchangeRatesPage', value); } - // Browser Cache Settings + // Browser Cache Management function setMapCacheExpiration(value: number): void { updateApplicationSettingsValue('mapCacheExpiration', value); appSettings.value.mapCacheExpiration = value; + updateUserApplicationCloudSettingValue('mapCacheExpiration', value); } function setExchangeRatesDataCacheExpiration(value: number): void { updateApplicationSettingsValue('exchangeRatesDataCacheExpiration', value); appSettings.value.exchangeRatesDataCacheExpiration = value; + updateUserApplicationCloudSettingValue('exchangeRatesDataCacheExpiration', value); } // Statistics Settings @@ -542,7 +546,7 @@ export const useSettingsStore = defineStore('settings', () => { setHideCategoriesWithoutAccounts, // -- Exchange Rates Data Page setCurrencySortByInExchangeRatesPage, - // -- Browser Cache Settings + // -- Browser Cache Management setMapCacheExpiration, setExchangeRatesDataCacheExpiration, // -- Statistics Settings diff --git a/src/views/base/LoginPageBase.ts b/src/views/base/LoginPageBase.ts index 6fe66938..c34d3f06 100644 --- a/src/views/base/LoginPageBase.ts +++ b/src/views/base/LoginPageBase.ts @@ -8,6 +8,7 @@ import { useExchangeRatesStore } from '@/stores/exchangeRates.ts'; import type { AuthResponse } from '@/models/auth_response.ts'; +import { updateMapCacheExpiration } from '@/lib/cache.ts'; import { getOAuth2Provider, getOIDCCustomDisplayNames, getLoginPageTips } from '@/lib/server_settings.ts'; import { getClientDisplayVersion } from '@/lib/version.ts'; import { setExpenseAndIncomeAmountColor } from '@/lib/ui/common.ts'; @@ -54,9 +55,9 @@ export function useLoginPageBase(platform: 'mobile' | 'desktop') { setExpenseAndIncomeAmountColor(authResponse.user.expenseAmountColor, authResponse.user.incomeAmountColor); } - if (settingsStore.appSettings.autoUpdateExchangeRatesData) { - exchangeRatesStore.getLatestExchangeRates({ silent: true, force: false }); - } + updateMapCacheExpiration(settingsStore.appSettings.mapCacheExpiration); + exchangeRatesStore.removeExpiredExchangeRates(true); + exchangeRatesStore.autoUpdateExchangeRatesData(); if (authResponse.notificationContent) { rootStore.setNotificationContent(authResponse.notificationContent); diff --git a/src/views/base/SignupPageBase.ts b/src/views/base/SignupPageBase.ts index 4d7f8da6..431e3ffc 100644 --- a/src/views/base/SignupPageBase.ts +++ b/src/views/base/SignupPageBase.ts @@ -10,6 +10,8 @@ import { useExchangeRatesStore } from '@/stores/exchangeRates.ts'; import { CategoryType } from '@/core/category.ts'; import type { RegisterResponse } from '@/models/auth_response.ts'; import type { User } from '@/models/user.ts'; + +import { updateMapCacheExpiration } from '@/lib/cache.ts'; import { setExpenseAndIncomeAmountColor } from '@/lib/ui/common.ts'; export function useSignupPageBase() { @@ -114,9 +116,9 @@ export function useSignupPageBase() { setExpenseAndIncomeAmountColor(response.user.expenseAmountColor, response.user.incomeAmountColor); } - if (settingsStore.appSettings.autoUpdateExchangeRatesData) { - exchangeRatesStore.getLatestExchangeRates({ silent: true, force: false }); - } + updateMapCacheExpiration(settingsStore.appSettings.mapCacheExpiration); + exchangeRatesStore.removeExpiredExchangeRates(true); + exchangeRatesStore.autoUpdateExchangeRatesData(); if (response.notificationContent) { rootStore.setNotificationContent(response.notificationContent); diff --git a/src/views/base/UnlockPageBase.ts b/src/views/base/UnlockPageBase.ts index 72f5c9f6..0cca9f43 100644 --- a/src/views/base/UnlockPageBase.ts +++ b/src/views/base/UnlockPageBase.ts @@ -11,6 +11,7 @@ import { useExchangeRatesStore } from '@/stores/exchangeRates.ts'; import { isWebAuthnSupported } from '@/lib/webauthn.ts'; import { hasWebAuthnConfig } from '@/lib/userstate.ts'; +import { updateMapCacheExpiration } from '@/lib/cache.ts'; import { getClientDisplayVersion } from '@/lib/version.ts'; import { setExpenseAndIncomeAmountColor } from '@/lib/ui/common.ts'; @@ -48,14 +49,14 @@ export function useUnlockPageBase() { setExpenseAndIncomeAmountColor(response.user.expenseAmountColor, response.user.incomeAmountColor); } + updateMapCacheExpiration(settingsStore.appSettings.mapCacheExpiration); + exchangeRatesStore.removeExpiredExchangeRates(true); + exchangeRatesStore.autoUpdateExchangeRatesData(); + if (response.notificationContent) { rootStore.setNotificationContent(response.notificationContent); } }); - - if (settingsStore.appSettings.autoUpdateExchangeRatesData) { - exchangeRatesStore.getLatestExchangeRates({ silent: true, force: false }); - } } function doRelogin(): void { diff --git a/src/views/base/settings/AppCloudSyncPageBase.ts b/src/views/base/settings/AppCloudSyncPageBase.ts index 02a4e943..4c586310 100644 --- a/src/views/base/settings/AppCloudSyncPageBase.ts +++ b/src/views/base/settings/AppCloudSyncPageBase.ts @@ -23,7 +23,14 @@ export const ALL_APPLICATION_CLOUD_SETTINGS: CategorizedApplicationCloudSettingI { categoryName: 'Basic Settings', items: [ - { settingKey: 'showAccountBalance', settingName: 'Show Account Balance', mobile: true, desktop: true } + { settingKey: 'showAccountBalance', settingName: 'Show Account Balance', mobile: true, desktop: true }, + { settingKey: 'autoUpdateExchangeRatesData', settingName: 'Auto-update Exchange Rates Data', mobile: true, desktop: true } + ] + }, + { + categoryName: 'Navigation Bar', + items: [ + { settingKey: 'showAddTransactionButtonInDesktopNavbar', settingName: 'Show Add Transaction Button', mobile: false, desktop: true } ] }, { @@ -78,6 +85,13 @@ export const ALL_APPLICATION_CLOUD_SETTINGS: CategorizedApplicationCloudSettingI { settingKey: 'currencySortByInExchangeRatesPage', settingName: 'Sort by', mobile: true, desktop: true } ] }, + { + categoryName: 'Browser Cache Management', + items: [ + { settingKey: 'mapCacheExpiration', settingName: 'Cache Expiration for Map Data', mobile: true, desktop: true }, + { settingKey: 'exchangeRatesDataCacheExpiration', settingName: 'Cache Expiration for Exchange Rates Data', mobile: true, desktop: true } + ] + }, { categoryName: 'Statistics Settings', categorySubName: 'Common Settings',