From d7f8b98be789dccc398263d01675dce62bcf1eef Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Tue, 31 Mar 2026 14:54:07 +0300 Subject: [PATCH 1/4] better toValue --- src/App.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/App.js b/src/App.js index 44ff6aee3..0a5303dc0 100644 --- a/src/App.js +++ b/src/App.js @@ -1,6 +1,6 @@ import { throttle } from 'lodash' import { mapState } from 'pinia' -import { defineAsyncComponent, toValue } from 'vue' +import { defineAsyncComponent } from 'vue' import DesktopNav from './components/desktop_nav/desktop_nav.vue' import EditStatusModal from './components/edit_status_modal/edit_status_modal.vue' @@ -33,7 +33,7 @@ import messages from 'src/i18n/messages' import localeService from 'src/services/locale/locale.service.js' // Helper to unwrap reactive proxies -window.toValue = toValue +window.toValue = x => JSON.parse(JSON.stringify(x)) export default { name: 'app', From fe2604376bc75abcac7b504f926f93590bd89ad0 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Tue, 31 Mar 2026 15:21:24 +0300 Subject: [PATCH 2/4] fix language setting --- src/modules/default_config_state.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/default_config_state.js b/src/modules/default_config_state.js index 3800d4deb..074716715 100644 --- a/src/modules/default_config_state.js +++ b/src/modules/default_config_state.js @@ -270,7 +270,7 @@ export const INSTANCE_DEFAULT_CONFIG_DEFINITIONS = { }, interfaceLanguage: { description: 'UI language', - default: browserLocale, + default: [browserLocale], }, hideScopeNotice: { description: 'Hide scope notification', From 799562242328d71fd5c612f51afc352c708747fd Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Tue, 31 Mar 2026 15:52:26 +0300 Subject: [PATCH 3/4] actually clean up journal for unknown paths --- src/App.js | 2 +- src/modules/default_config_state.js | 2 +- src/stores/sync_config.js | 182 ++++++++++++++++------------ 3 files changed, 105 insertions(+), 81 deletions(-) diff --git a/src/App.js b/src/App.js index 0a5303dc0..487090565 100644 --- a/src/App.js +++ b/src/App.js @@ -33,7 +33,7 @@ import messages from 'src/i18n/messages' import localeService from 'src/services/locale/locale.service.js' // Helper to unwrap reactive proxies -window.toValue = x => JSON.parse(JSON.stringify(x)) +window.toValue = (x) => JSON.parse(JSON.stringify(x)) export default { name: 'app', diff --git a/src/modules/default_config_state.js b/src/modules/default_config_state.js index 074716715..b5958adb6 100644 --- a/src/modules/default_config_state.js +++ b/src/modules/default_config_state.js @@ -744,7 +744,7 @@ export const validateSetting = ({ throw new Error(string) } else { console.error(string) - return value + return undefined } } diff --git a/src/stores/sync_config.js b/src/stores/sync_config.js index 045579e41..9bce83a13 100644 --- a/src/stores/sync_config.js +++ b/src/stores/sync_config.js @@ -290,87 +290,90 @@ export const _mergePrefs = (recent, stale) => { */ const resultOutput = { ...recentData } const totalJournal = _mergeJournal(staleJournal, recentJournal) - totalJournal.forEach(({ path, operation, args }) => { - if (path.startsWith('_')) { - throw new Error( - `journal contains entry to edit internal (starts with _) field '${path}', something is incorrect here, ignoring.`, - ) - } - switch (operation) { - case 'set': { - if (path.startsWith('collections')) { - return console.error('Illegal operation "set" on a collection') - } - if (path.split(/\./g).length <= 1) { - return console.error( - `Calling set on depth <= 1 (path: ${path}) is not allowed`, - ) - } + totalJournal + .filter(({ path, operation, args }) => { + const definition = path.startsWith('simple.muteFilters') + ? { default: {} } + : ROOT_CONFIG_DEFINITIONS[path.split('.')[1]] - const definition = path.startsWith('simple.muteFilters') - ? { default: {} } - : ROOT_CONFIG_DEFINITIONS[path.split('.')[1]] + const finalValue = validateSetting({ + path: path.split('.')[1], + value: args[0], + definition, + throwError: false, + defaultState: ROOT_CONFIG, + }) - const finalValue = validateSetting({ - path: path.split('.')[1], - value: args[0], - definition, - throwError: false, - defaultState: ROOT_CONFIG, - }) - - set(resultOutput, path, finalValue) - break - } - case 'unset': - if (path.startsWith('collections')) { - return console.error('Illegal operation "unset" on a collection') - } - if (path.split(/\./g).length <= 2) { - return console.error( - `Calling unset on depth <= 2 (path: ${path}) is not allowed`, - ) - } - unset(resultOutput, path) - break - case 'addToCollection': - if (!path.startsWith('collections')) { - return console.error( - 'Illegal operation "addToCollection" on a non-collection', - ) - } - set( - resultOutput, - path, - Array.from(new Set(get(resultOutput, path)).add(args[0])), + return finalValue !== undefined + }) + .forEach(({ path, operation, args }) => { + if (path.startsWith('_')) { + throw new Error( + `journal contains entry to edit internal (starts with _) field '${path}', something is incorrect here, ignoring.`, ) - break - case 'removeFromCollection': { - if (!path.startsWith('collections')) { - return console.error( - 'Illegal operation "removeFromCollection" on a non-collection', - ) + } + switch (operation) { + case 'set': { + if (path.startsWith('collections')) { + return console.error('Illegal operation "set" on a collection') + } + if (path.split(/\./g).length <= 1) { + return console.error( + `Calling set on depth <= 1 (path: ${path}) is not allowed`, + ) + } + set(resultOutput, path, args[0]) + break } - const newSet = new Set(get(resultOutput, path)) - newSet.delete(args[0]) - set(resultOutput, path, Array.from(newSet)) - break + case 'unset': + if (path.startsWith('collections')) { + return console.error('Illegal operation "unset" on a collection') + } + if (path.split(/\./g).length <= 2) { + return console.error( + `Calling unset on depth <= 2 (path: ${path}) is not allowed`, + ) + } + unset(resultOutput, path) + break + case 'addToCollection': + if (!path.startsWith('collections')) { + return console.error( + 'Illegal operation "addToCollection" on a non-collection', + ) + } + set( + resultOutput, + path, + Array.from(new Set(get(resultOutput, path)).add(args[0])), + ) + break + case 'removeFromCollection': { + if (!path.startsWith('collections')) { + return console.error( + 'Illegal operation "removeFromCollection" on a non-collection', + ) + } + const newSet = new Set(get(resultOutput, path)) + newSet.delete(args[0]) + set(resultOutput, path, Array.from(newSet)) + break + } + case 'reorderCollection': { + const [value, movement] = args + set( + resultOutput, + path, + _moveItemInArray(get(resultOutput, path), value, movement), + ) + break + } + default: + return console.error( + `Unknown journal operation: '${operation}', did we forget to run reverse migrations beforehand?`, + ) } - case 'reorderCollection': { - const [value, movement] = args - set( - resultOutput, - path, - _moveItemInArray(get(resultOutput, path), value, movement), - ) - break - } - default: - return console.error( - `Unknown journal operation: '${operation}', did we forget to run reverse migrations beforehand?`, - ) - } - }) + }) return { ...resultOutput, _journal: totalJournal } } @@ -513,7 +516,7 @@ export const useSyncConfigStore = defineStore('sync_config', { defaultState: ROOT_CONFIG, }) - set(this.prefsStorage, path, finalValue) + if (finalValue !== undefined) set(this.prefsStorage, path, finalValue) this.prefsStorage._journal = [ ...this.prefsStorage._journal, @@ -754,7 +757,6 @@ export const useSyncConfigStore = defineStore('sync_config', { this.flagStorage = this.cache.flagStorage this.prefsStorage = this.cache.prefsStorage this.pushSyncConfig() - console.log('C', this.cache) }, pushSyncConfig({ force = false } = {}) { const needPush = this.dirty || force @@ -766,7 +768,29 @@ export const useSyncConfigStore = defineStore('sync_config', { }, persist: { afterLoad(state) { - return state + console.log('Validating persisted state of SyncConfig') + const newState = { ...state } + const newEntries = Object.entries(newState.prefsStorage.simple).map( + ([path, value]) => { + if (path === 'muteFilters') { + return value + } + const definition = ROOT_CONFIG_DEFINITIONS[path] + const finalValue = validateSetting({ + path, + value, + definition, + throwError: false, + defaultState: ROOT_CONFIG, + }) + + return finalValue === undefined ? undefined : [path, finalValue] + }, + ) + newState.prefsStorage.simple = Object.fromEntries( + newEntries.filter((_) => _), + ) + return newState }, }, }) From be82385d79bcf505ff3406dddbfef8af90e90a33 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Tue, 31 Mar 2026 17:44:34 +0300 Subject: [PATCH 4/4] fix highlight test --- test/unit/specs/stores/user_highlight.spec.js | 34 ++++++++----------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/test/unit/specs/stores/user_highlight.spec.js b/test/unit/specs/stores/user_highlight.spec.js index 4e66a9bbb..c46852256 100644 --- a/test/unit/specs/stores/user_highlight.spec.js +++ b/test/unit/specs/stores/user_highlight.spec.js @@ -174,13 +174,11 @@ describe('The UserHighlight store', () => { it('should prefer recent and apply journal to it', () => { expect( _mergeHighlights( - // RECENT { - highlight: { - 'a@test.xyz': 1, - 'b@test.xyz': 0, - 'c@test.xyz': true, - }, + // RECENT + 'a@test.xyz': 1, + 'b@test.xyz': 0, + 'c@test.xyz': true, _journal: [ { user: 'b@test.xyz', @@ -196,13 +194,11 @@ describe('The UserHighlight store', () => { }, ], }, - // STALE { - highlight: { - 'a@test.xyz': 1, - 'b@test.xyz': 1, - 'c@test.xyz': false, - }, + // STALE + 'a@test.xyz': 1, + 'b@test.xyz': 1, + 'c@test.xyz': false, _journal: [ { user: 'a@test.xyz', @@ -220,7 +216,9 @@ describe('The UserHighlight store', () => { }, ), ).to.eql({ - highlight: { 'a@test.xyz': 1, 'b@test.xyz': 1, 'c@test.xyz': true }, + 'a@test.xyz': 1, + 'b@test.xyz': 1, + 'c@test.xyz': true, _journal: [ { user: 'a@test.xyz', operation: 'set', args: [1], timestamp: 1 }, { user: 'b@test.xyz', operation: 'set', args: [1], timestamp: 3 }, @@ -239,7 +237,7 @@ describe('The UserHighlight store', () => { _mergeHighlights( // RECENT { - highlight: { 'a@test.xyz': { type: 'foo' } }, + 'a@test.xyz': { type: 'foo' }, _journal: [ { user: 'a@test.xyz', @@ -251,7 +249,7 @@ describe('The UserHighlight store', () => { }, // STALE { - highlight: { 'a@test.xyz': { type: 'bar' } }, + 'a@test.xyz': { type: 'bar' }, _journal: [ { user: 'a@test.xyz', @@ -263,7 +261,7 @@ describe('The UserHighlight store', () => { }, ), ).to.eql({ - highlight: { 'a@test.xyz': { type: 'bar' } }, + 'a@test.xyz': { type: 'bar' }, _journal: [ { user: 'a@test.xyz', @@ -280,7 +278,7 @@ describe('The UserHighlight store', () => { _mergeHighlights( // RECENT { - highlight: { 'a@test.xyz': { type: 'foo' } }, + 'a@test.xyz': { type: 'foo' }, _journal: [ { user: 'a@test.xyz', @@ -292,7 +290,6 @@ describe('The UserHighlight store', () => { }, // STALE { - highlight: {}, _journal: [ { user: 'a@test.xyz', @@ -304,7 +301,6 @@ describe('The UserHighlight store', () => { }, ), ).to.eql({ - highlight: {}, _journal: [ { user: 'a@test.xyz',