diff --git a/src/components/UserSatisfactionPoll.vue b/src/components/UserSatisfactionPoll.vue index 2a8056bc..3533a14b 100644 --- a/src/components/UserSatisfactionPoll.vue +++ b/src/components/UserSatisfactionPoll.vue @@ -70,8 +70,6 @@ import moment from 'moment'; import { useSettingsStore } from '~/stores/settings'; const NUM_OPTIONS = 10; -// INITIAL_WAIT_PERIOD is how long to wait from initialTimestamp to the first time that the poll shows up -const INITIAL_WAIT_PERIOD = 7 * 24 * 60 * 60; // BACKOFF_PERIOD is how many seconds to wait to show the poll again if the user closed it const BACKOFF_PERIOD = 7 * 24 * 60 * 60; // The following may be used for testing @@ -106,16 +104,6 @@ export default { }, }, async mounted() { - // Get the rest of the data - const settingsStore = useSettingsStore(); - if (!this.data) { - this.data = { - isEnabled: true, - nextPollTime: settingsStore.initialTimestamp.add(INITIAL_WAIT_PERIOD, 'seconds'), - timesPollIsShown: 0, - }; - } - if (!this.data.isEnabled) { return; } @@ -135,9 +123,7 @@ export default { methods: { submit() { this.isPollVisible = false; - const data = this.data; - data.isEnabled = false; - this.data = data; + this.data = { ...this.data, isEnabled: false }; if (parseInt(this.rating) >= 6) { this.isPosFollowUpVisible = true; @@ -147,9 +133,7 @@ export default { }, dontShowAgain() { this.isPollVisible = false; - const data = this.data; - data.isEnabled = false; - this.data = data; + this.data = { ...this.data, isEnabled: false }; }, }, }; diff --git a/src/stores/settings.ts b/src/stores/settings.ts index a243f546..891852fa 100644 --- a/src/stores/settings.ts +++ b/src/stores/settings.ts @@ -3,11 +3,21 @@ import moment, { Moment } from 'moment'; import { getClient } from '~/util/awclient'; import { Category, defaultCategories } from '~/util/classes'; import { View, defaultViews } from '~/stores/views'; +import { isEqual } from 'lodash'; + +function jsonEq(a: any, b: any) { + const jsonA = JSON.parse(JSON.stringify(a)); + const jsonB = JSON.parse(JSON.stringify(b)); + return isEqual(jsonA, jsonB); +} // Backoffs for NewReleaseNotification export const SHORT_BACKOFF_PERIOD = 24 * 60 * 60; export const LONG_BACKOFF_PERIOD = 5 * 24 * 60 * 60; +// Initial wait period for UserSatisfactionPoll +export const INITIAL_WAIT_PERIOD = 7 * 24 * 60 * 60; + interface State { // Timestamp when user was first seen (first time webapp is run) initialTimestamp: Moment; @@ -20,7 +30,11 @@ interface State { theme: 'light' | 'dark'; newReleaseCheckData: Record; - userSatisfactionPollData: Record; + userSatisfactionPollData: { + isEnabled: boolean; + nextPollTime: Moment; + timesPollIsShown: number; + }; always_active_pattern: string; classes: Category[]; views: View[]; @@ -53,7 +67,11 @@ export const useSettingsStore = defineStore('settings', { howOftenToCheck: SHORT_BACKOFF_PERIOD, timesChecked: 0, }, - userSatisfactionPollData: {}, + userSatisfactionPollData: { + isEnabled: true, + nextPollTime: moment().add(INITIAL_WAIT_PERIOD, 'seconds'), + timesPollIsShown: 0, + }, always_active_pattern: '', classes: defaultCategories, @@ -110,8 +128,8 @@ export const useSettingsStore = defineStore('settings', { //const locstr = set_in_server ? '[server]' : '[localStorage]'; //console.debug(`${locstr} ${key}:`, value); - // Keys ending with 'Data' are JSON-serialized objects - if (key.includes('Data') && !set_in_server) { + // Keys ending with 'Data' are JSON-serialized objects in localStorage + if (key.endsWith('Data') && !set_in_server) { try { storage[key] = JSON.parse(value); } catch (e) { @@ -134,6 +152,11 @@ export const useSettingsStore = defineStore('settings', { } }, async save() { + // Important check, to avoid saving settings before they are loaded (potentially overwriting them with defaults) + if (!this.loaded) { + console.error('Settings not loaded, not saving'); + return; + } // We want to avoid saving to localStorage to not accidentally mess up pre-migration data // For example, if the user is using several browsers, and opened in their non-main browser on first run after upgrade. const saveToLocalStorage = false; @@ -141,11 +164,22 @@ export const useSettingsStore = defineStore('settings', { // Save to localStorage and backend // NOTE: localStorage deprecated, will be removed in future const client = getClient(); + + // Fetch current settings from server + const server_settings = await client.get_settings(); + + // Save settings for (const key of Object.keys(this.$state)) { + // Skip keys starting with underscore, as they are local to the vuex store. + if (key.startsWith('_')) { + continue; + } + const value = this.$state[key]; // Save to localStorage - if (saveToLocalStorage) { + // NOTE: we always save the theme and landingpage to localStorage, since they are used before the settings are loaded + if (saveToLocalStorage || key == 'theme' || key == 'landingpage') { if (typeof value === 'object') { localStorage.setItem(key, JSON.stringify(value)); } else { @@ -153,12 +187,21 @@ export const useSettingsStore = defineStore('settings', { } } - // Save to backend - await client.req.post('/0/settings/' + key, value, { - headers: { - 'Content-Type': 'application/json', - }, - }); + // Save changed settings to backend + if (server_settings[key] === undefined || !jsonEq(server_settings[key], value)) { + if (server_settings[key] === undefined && value === false) { + // Skip saving settings that are set to false and not already saved on the server + continue; + } + console.log('Saving', { [key]: value }); + //console.log('Was:', server_settings[key]); + //console.log('Now:', value); + await client.req.post('/0/settings/' + key, value, { + headers: { + 'Content-Type': 'application/json', + }, + }); + } } // After save, reload