From ffeda63676dadf20c4d455c2497f582c838f1278 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Sun, 5 Apr 2026 13:12:24 +0300 Subject: [PATCH 1/7] verify import settings, ignore theme for when it's not needed --- .../settings_modal/settings_modal.js | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/src/components/settings_modal/settings_modal.js b/src/components/settings_modal/settings_modal.js index 4ca2857b2..2c1ba5563 100644 --- a/src/components/settings_modal/settings_modal.js +++ b/src/components/settings_modal/settings_modal.js @@ -13,7 +13,11 @@ import { useLocalConfigStore } from 'src/stores/local_config.js' import { useMergedConfigStore } from 'src/stores/merged_config.js' import { useSyncConfigStore } from 'src/stores/sync_config.js' -import { LOCAL_ONLY_KEYS } from 'src/modules/default_config_state.js' +import { + LOCAL_ONLY_KEYS, + ROOT_CONFIG, + ROOT_CONFIG_DEFINITIONS, +} from 'src/modules/default_config_state.js' import { newExporter, newImporter, @@ -160,10 +164,22 @@ const SettingsModal = { }) }) } else { - useSyncConfigStore().setPreference({ + const definition = ROOT_CONFIG_DEFINITIONS[entry] + + const finalValue = validateSetting({ path: `simple.${path}`, value, + definition, + throwError: false, + defaultState: ROOT_CONFIG, }) + + if (finalValue !== undefined) { + useSyncConfigStore().setPreference({ + path: `simple.${path}`, + value, + }) + } } } }) @@ -187,6 +203,10 @@ const SettingsModal = { 'customTheme', 'customThemeSource', 'colors', + 'style', + 'styleCustomData', + 'palette', + 'paletteCustomData', ]) sample = Object.fromEntries( Object.entries(sample).filter( From 2c6df0887f6170fea539989a79465ffe4510bea2 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Sun, 5 Apr 2026 13:17:36 +0300 Subject: [PATCH 2/7] minimize exported settings --- src/components/settings_modal/settings_modal.js | 3 +++ src/stores/merged_config.js | 5 ++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/components/settings_modal/settings_modal.js b/src/components/settings_modal/settings_modal.js index 2c1ba5563..782a005e1 100644 --- a/src/components/settings_modal/settings_modal.js +++ b/src/components/settings_modal/settings_modal.js @@ -200,6 +200,7 @@ const SettingsModal = { let sample = config if (!theme) { const ignoreList = new Set([ + 'theme', 'customTheme', 'customThemeSource', 'colors', @@ -207,7 +208,9 @@ const SettingsModal = { 'styleCustomData', 'palette', 'paletteCustomData', + 'themeChecksum', ]) + sample = Object.fromEntries( Object.entries(sample).filter( ([key, value]) => !ignoreList.has(key) && value !== undefined, diff --git a/src/stores/merged_config.js b/src/stores/merged_config.js index c4f27082c..3e218c942 100644 --- a/src/stores/merged_config.js +++ b/src/stores/merged_config.js @@ -50,16 +50,15 @@ export const useMergedConfigStore = defineStore('merged_config', { return result }, mergedConfigWithoutDefaults: () => { - const instancePrefs = useInstanceStore().prefsStorage const tempPrefs = useLocalConfigStore().tempStorage const localPrefs = useLocalConfigStore().prefsStorage const syncPrefs = useSyncConfigStore().prefsStorage const getValue = (k) => - tempPrefs[k] ?? localPrefs[k] ?? syncPrefs.simple[k] ?? instancePrefs[k] + tempPrefs[k] ?? localPrefs[k] ?? syncPrefs.simple[k] const result = Object.fromEntries( - Object.keys(ROOT_CONFIG).map(([k, value]) => [k, getValue(k)]), + Object.keys(ROOT_CONFIG).map((k) => [k, getValue(k)]), ) return result }, From 1b59cb3bdf00cea0353956501569a0b2d9f43342 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Sun, 5 Apr 2026 13:23:53 +0300 Subject: [PATCH 3/7] lint --- .../settings_modal/settings_modal.js | 3 +- src/stores/merged_config.js | 10 +- test/unit/specs/stores/sync_config.spec.js | 96 ++++++++++++++++--- 3 files changed, 84 insertions(+), 25 deletions(-) diff --git a/src/components/settings_modal/settings_modal.js b/src/components/settings_modal/settings_modal.js index 782a005e1..c78659e4e 100644 --- a/src/components/settings_modal/settings_modal.js +++ b/src/components/settings_modal/settings_modal.js @@ -17,6 +17,7 @@ import { LOCAL_ONLY_KEYS, ROOT_CONFIG, ROOT_CONFIG_DEFINITIONS, + validateSetting, } from 'src/modules/default_config_state.js' import { newExporter, @@ -164,7 +165,7 @@ const SettingsModal = { }) }) } else { - const definition = ROOT_CONFIG_DEFINITIONS[entry] + const definition = ROOT_CONFIG_DEFINITIONS[path] const finalValue = validateSetting({ path: `simple.${path}`, diff --git a/src/stores/merged_config.js b/src/stores/merged_config.js index 3e218c942..334db6c4f 100644 --- a/src/stores/merged_config.js +++ b/src/stores/merged_config.js @@ -5,18 +5,10 @@ import { useLocalConfigStore } from 'src/stores/local_config.js' import { useSyncConfigStore } from 'src/stores/sync_config.js' import { - INSTANCE_DEFAULT_CONFIG, - LOCAL_DEFAULT_CONFIG, LOCAL_ONLY_KEYS, - THEME_CONFIG, + ROOT_CONFIG, } from 'src/modules/default_config_state.js' -const ROOT_CONFIG = { - ...INSTANCE_DEFAULT_CONFIG, - ...LOCAL_DEFAULT_CONFIG, - ...THEME_CONFIG, -} - export const useMergedConfigStore = defineStore('merged_config', { getters: { mergedConfig: () => { diff --git a/test/unit/specs/stores/sync_config.spec.js b/test/unit/specs/stores/sync_config.spec.js index 9a582a193..83324f62b 100644 --- a/test/unit/specs/stores/sync_config.spec.js +++ b/test/unit/specs/stores/sync_config.spec.js @@ -241,7 +241,9 @@ describe('The SyncConfig store', () => { store.setPreference({ path: 'simple.fontInput.family', value: 'test' }) store.unsetPreference({ path: 'simple.fontInput.family' }) store.updateCache(store, { username: 'test' }) - expect(store.prefsStorage.simple.fontInput).to.not.have.property('family') + expect(store.prefsStorage.simple.fontInput).to.not.have.property( + 'family', + ) expect(store.prefsStorage._journal.length).to.eql(1) }) @@ -395,7 +397,12 @@ describe('The SyncConfig store', () => { { simple: { theme: '1', style: '0', hideISP: true }, _journal: [ - { path: 'simple.style', operation: 'set', args: ['0'], timestamp: 2 }, + { + path: 'simple.style', + operation: 'set', + args: ['0'], + timestamp: 2, + }, { path: 'simple.hideISP', operation: 'set', @@ -408,17 +415,42 @@ describe('The SyncConfig store', () => { { simple: { theme: '1', style: '1', hideISP: false }, _journal: [ - { path: 'simple.theme', operation: 'set', args: ['1'], timestamp: 1 }, - { path: 'simple.style', operation: 'set', args: ['1'], timestamp: 3 }, + { + path: 'simple.theme', + operation: 'set', + args: ['1'], + timestamp: 1, + }, + { + path: 'simple.style', + operation: 'set', + args: ['1'], + timestamp: 3, + }, ], }, ), ).to.eql({ simple: { theme: '1', style: '1', hideISP: true }, _journal: [ - { path: 'simple.theme', operation: 'set', args: ['1'], timestamp: 1 }, - { path: 'simple.style', operation: 'set', args: ['1'], timestamp: 3 }, - { path: 'simple.hideISP', operation: 'set', args: [true], timestamp: 4 }, + { + path: 'simple.theme', + operation: 'set', + args: ['1'], + timestamp: 1, + }, + { + path: 'simple.style', + operation: 'set', + args: ['1'], + timestamp: 3, + }, + { + path: 'simple.hideISP', + operation: 'set', + args: [true], + timestamp: 4, + }, ], }) }) @@ -430,7 +462,12 @@ describe('The SyncConfig store', () => { { simple: { theme: '1', style: '0', hideISP: false }, _journal: [ - { path: 'simple.style', operation: 'set', args: ['0'], timestamp: 2 }, + { + path: 'simple.style', + operation: 'set', + args: ['0'], + timestamp: 2, + }, { path: 'simple.hideISP', operation: 'set', @@ -443,18 +480,42 @@ describe('The SyncConfig store', () => { { simple: { theme: '0', style: '0', hideISP: true }, _journal: [ - { path: 'simple.theme', operation: 'set', args: ['0'], timestamp: 1 }, - { path: 'simple.style', operation: 'set', args: ['0'], timestamp: 3 }, + { + path: 'simple.theme', + operation: 'set', + args: ['0'], + timestamp: 1, + }, + { + path: 'simple.style', + operation: 'set', + args: ['0'], + timestamp: 3, + }, ], }, ), ).to.eql({ - simple: { a: 0, b: 0, c: false }, simple: { theme: '0', style: '0', hideISP: false }, _journal: [ - { path: 'simple.theme', operation: 'set', args: ['0'], timestamp: 1 }, - { path: 'simple.style', operation: 'set', args: ['0'], timestamp: 3 }, - { path: 'simple.hideISP', operation: 'set', args: [false], timestamp: 4 }, + { + path: 'simple.theme', + operation: 'set', + args: ['0'], + timestamp: 1, + }, + { + path: 'simple.style', + operation: 'set', + args: ['0'], + timestamp: 3, + }, + { + path: 'simple.hideISP', + operation: 'set', + args: [false], + timestamp: 4, + }, ], }) }) @@ -490,7 +551,12 @@ describe('The SyncConfig store', () => { ).to.eql({ simple: { theme: 'bar' }, _journal: [ - { path: 'simple.theme', operation: 'set', args: ['bar'], timestamp: 4 }, + { + path: 'simple.theme', + operation: 'set', + args: ['bar'], + timestamp: 4, + }, ], }) }) From c1d7d2fa637354abd4747dfcdce8b53dd783bc54 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Sun, 5 Apr 2026 13:29:16 +0300 Subject: [PATCH 4/7] fix import --- .../settings_modal/settings_modal.js | 79 ++++++++++--------- src/modules/default_config_state.js | 2 +- 2 files changed, 42 insertions(+), 39 deletions(-) diff --git a/src/components/settings_modal/settings_modal.js b/src/components/settings_modal/settings_modal.js index c78659e4e..5c157e068 100644 --- a/src/components/settings_modal/settings_modal.js +++ b/src/components/settings_modal/settings_modal.js @@ -143,49 +143,52 @@ const SettingsModal = { }) } }, - onImport(data) { - if (data) { - Object.entries(data).forEach(([path, value]) => { - if (LOCAL_ONLY_KEYS.has(path)) { - useLocalConfigStore().set({ path, value }) + onImport(input) { + if (!input) return + const { _pleroma_settings_version, ...data } = input + + Object.entries(data).forEach(([path, value]) => { + const definition = ROOT_CONFIG_DEFINITIONS[path] + + const finalValue = validateSetting({ + path, + value, + definition, + throwError: false, + defaultState: ROOT_CONFIG, + }) + + if (finalValue === undefined) return + + if (LOCAL_ONLY_KEYS.has(path)) { + useLocalConfigStore().set({ path, value: finalValue }) + } else { + if (path.startsWith('muteFilters')) { + Object.keys( + useMergedConfigStore().mergedConfig.muteFilters, + ).forEach((key) => { + useSyncConfigStore().unsetPreference({ + path: `simple.${path}.${key}`, + }) + }) + + Object.entries(value).forEach(([key, filter]) => { + useSyncConfigStore().setPreference({ + path: `simple.${path}.${key}`, + value: filter, + }) + }) } else { - if (path.startsWith('muteFilters')) { - Object.keys( - useMergedConfigStore().mergedConfig.muteFilters, - ).forEach((key) => { - useSyncConfigStore().unsetPreference({ - path: `simple.${path}.${key}`, - }) - }) - - Object.entries(value).forEach(([key, filter]) => { - useSyncConfigStore().setPreference({ - path: `simple.${path}.${key}`, - value: filter, - }) - }) - } else { - const definition = ROOT_CONFIG_DEFINITIONS[path] - - const finalValue = validateSetting({ + if (finalValue !== undefined) { + useSyncConfigStore().setPreference({ path: `simple.${path}`, - value, - definition, - throwError: false, - defaultState: ROOT_CONFIG, + value: finalValue, }) - - if (finalValue !== undefined) { - useSyncConfigStore().setPreference({ - path: `simple.${path}`, - value, - }) - } } } - }) - useSyncConfigStore().pushSyncConfig() - } + } + }) + useSyncConfigStore().pushSyncConfig() }, restore() { this.dataImporter.importData() diff --git a/src/modules/default_config_state.js b/src/modules/default_config_state.js index b5958adb6..f642d6398 100644 --- a/src/modules/default_config_state.js +++ b/src/modules/default_config_state.js @@ -738,7 +738,7 @@ export const validateSetting = ({ }) => { if (value === undefined) return // only null is allowed as missing value if (get(defaultState, path) === undefined) { - const string = `Unknown instance option ${path}, value: ${value}` + const string = `Unknown option ${path}, value: ${value}` if (throwError) { throw new Error(string) From de13143e955e33b9429818eb76d0b500ec604de9 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Thu, 9 Apr 2026 23:40:55 +0300 Subject: [PATCH 5/7] fix collections journal getting filtered --- src/stores/sync_config.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/stores/sync_config.js b/src/stores/sync_config.js index 7a5b1f4e1..063595509 100644 --- a/src/stores/sync_config.js +++ b/src/stores/sync_config.js @@ -295,6 +295,8 @@ export const _mergePrefs = (recent, stale) => { const entry = path.split('.')[1] if (operation === 'unset') return ROOT_CONFIG[entry] !== undefined + if (operation !== 'set') return true + const definition = path.startsWith('simple.muteFilters') ? { default: {} } : ROOT_CONFIG_DEFINITIONS[entry] @@ -507,6 +509,8 @@ export const useSyncConfigStore = defineStore('sync_config', { ) } + if (path.startsWith('collections.')) return value + const definition = path.startsWith('simple.muteFilters') ? { default: {} } : ROOT_CONFIG_DEFINITIONS[path.split('.')[1]] From 3c45c223c737f73fa21bcf86214df333b7f1354b Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Fri, 10 Apr 2026 00:03:04 +0300 Subject: [PATCH 6/7] fix instance identity stuff --- src/boot/after_store.js | 12 ++++++++---- src/modules/default_config_state.js | 9 +++++++++ 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/boot/after_store.js b/src/boot/after_store.js index a345e2ae5..d16852bf8 100644 --- a/src/boot/after_store.js +++ b/src/boot/after_store.js @@ -42,6 +42,7 @@ import VBodyScrollLock from 'src/directives/body_scroll_lock' import { INSTANCE_DEFAULT_CONFIG_DEFINITIONS, INSTANCE_IDENTITY_DEFAULT_DEFINITIONS, + INSTANCE_IDENTIY_EXTERNAL, } from 'src/modules/default_config_state.js' let staticInitialResults = null @@ -170,12 +171,14 @@ const setSettings = async ({ apiConfig, staticConfig, store }) => { config = Object.assign({}, staticConfig, apiConfig) } - Object.keys(INSTANCE_IDENTITY_DEFAULT_DEFINITIONS).forEach((source) => + Object.keys(INSTANCE_IDENTITY_DEFAULT_DEFINITIONS).forEach((source) => { + if (source === 'name') return + if (INSTANCE_IDENTIY_EXTERNAL.has(source)) return useInstanceStore().set({ value: config[source], path: `instanceIdentity.${source}`, - }), - ) + }) + }) Object.keys(INSTANCE_DEFAULT_CONFIG_DEFINITIONS).forEach((source) => useInstanceStore().set({ @@ -277,7 +280,7 @@ const getNodeInfo = async ({ store }) => { const metadata = data.metadata const features = metadata.features useInstanceStore().set({ - path: 'name', + path: 'instanceIdentity.name', value: metadata.nodeName, }) useInstanceStore().set({ @@ -527,6 +530,7 @@ const afterStoreSetup = async ({ pinia, store, storageError, i18n }) => { useInterfaceStore().setLayoutWidth(windowWidth()) useInterfaceStore().setLayoutHeight(windowHeight()) + window.syncConfig = useSyncConfigStore() window.mergedConfig = useMergedConfigStore() window.localConfig = useLocalConfigStore() diff --git a/src/modules/default_config_state.js b/src/modules/default_config_state.js index f642d6398..ecd580577 100644 --- a/src/modules/default_config_state.js +++ b/src/modules/default_config_state.js @@ -112,10 +112,19 @@ export const INSTANCE_IDENTITY_DEFAULT_DEFINITIONS = { type: 'string', required: false, }, + name: { + description: 'Instance Name', + type: 'string', + required: false, + }, } export const INSTANCE_IDENTITY_DEFAULT = convertDefinitions( INSTANCE_IDENTITY_DEFAULT_DEFINITIONS, ) +export const INSTANCE_IDENTIY_EXTERNAL = new Set([ + 'tos', + 'instanceSpecificPanelContent', +]) /// This object contains setting entries that makes sense /// at the user level. The defaults can also be overriden by From 14e2e57c5536e73cd21b5f0e5a534cede106bbcd Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Fri, 10 Apr 2026 01:08:24 +0300 Subject: [PATCH 7/7] fix objects not loading/saved/verified --- src/modules/default_config_state.js | 38 ++++++++++++++++++++++++++--- src/stores/instance.js | 1 + src/stores/sync_config.js | 14 +++++------ 3 files changed, 41 insertions(+), 12 deletions(-) diff --git a/src/modules/default_config_state.js b/src/modules/default_config_state.js index ecd580577..ff43e4dee 100644 --- a/src/modules/default_config_state.js +++ b/src/modules/default_config_state.js @@ -531,6 +531,18 @@ export const INSTANCE_DEFAULT_CONFIG_DEFINITIONS = { type: 'string', required: false, }, + theme3hacks: { + description: 'Theme 3 hacks (need separation)', + type: 'object', + required: false, + default: {}, + }, + highlights: { + description: 'User highlights', + type: 'object', + required: false, + default: {}, + }, } export const INSTANCE_DEFAULT_CONFIG = convertDefinitions( INSTANCE_DEFAULT_CONFIG_DEFINITIONS, @@ -672,6 +684,11 @@ export const SYNC_DEFAULT_CONFIG_DEFINITIONS = { description: 'Collapse navigation panel to header only', default: false, }, + muteFilters: { + description: 'Object containing mute filters', + type: 'object', + default: {}, + } } export const SYNC_DEFAULT_CONFIG = convertDefinitions( SYNC_DEFAULT_CONFIG_DEFINITIONS, @@ -740,14 +757,27 @@ export const ROOT_CONFIG_DEFINITIONS = { export const validateSetting = ({ value, - path, + path: fullPath, definition, throwError, defaultState, + validateObjects = true, }) => { - if (value === undefined) return // only null is allowed as missing value - if (get(defaultState, path) === undefined) { - const string = `Unknown option ${path}, value: ${value}` + const path = fullPath.replace(/^simple./, '') + if (validateObjects && definition.type === 'object' && path.split('.').length <= 1) { + console.error(`attempt to set object ${fullPath} instead of its children. ignoring.`) + return undefined + } + + if (path.includes('muteFilters')) { + console.log('##', path, value, definition) + console.log(value) + console.log(path) + console.log('====') + } + if (value === undefined) return undefined // only null is allowed as missing value + if (get(defaultState, path.split('.')[0]) === undefined) { + const string = `Unknown option ${fullPath}, value: ${value}` if (throwError) { throw new Error(string) diff --git a/src/stores/instance.js b/src/stores/instance.js index 2923f3ad8..54b3cf43c 100644 --- a/src/stores/instance.js +++ b/src/stores/instance.js @@ -201,6 +201,7 @@ export const useInstanceStore = defineStore('instance', { definition, throwError: true, defaultState: DEFAULT_STATE, + validateObjects: false, }) set(this, path, finalValue) diff --git a/src/stores/sync_config.js b/src/stores/sync_config.js index 063595509..bad0a1515 100644 --- a/src/stores/sync_config.js +++ b/src/stores/sync_config.js @@ -302,7 +302,7 @@ export const _mergePrefs = (recent, stale) => { : ROOT_CONFIG_DEFINITIONS[entry] const finalValue = validateSetting({ - path: entry, + path, value: args[0], definition, throwError: false, @@ -516,7 +516,7 @@ export const useSyncConfigStore = defineStore('sync_config', { : ROOT_CONFIG_DEFINITIONS[path.split('.')[1]] const finalValue = validateSetting({ - path: path.split('.')[1], + path, value, definition, throwError: false, @@ -777,21 +777,19 @@ export const useSyncConfigStore = defineStore('sync_config', { afterLoad(state) { console.debug('Validating persisted state of SyncConfig') const newState = { ...state } - const newEntries = Object.entries(newState.prefsStorage.simple).map( + const newEntries = Object.entries(ROOT_CONFIG).map( ([path, value]) => { - if (path === 'muteFilters') { - return value - } const definition = ROOT_CONFIG_DEFINITIONS[path] const finalValue = validateSetting({ path, - value, + value: newState.prefsStorage.simple[path], definition, throwError: false, + validateObjects: false, defaultState: ROOT_CONFIG, }) - return finalValue === undefined ? undefined : [path, finalValue] + return finalValue === undefined ? definition.default : [path, finalValue] }, ) newState.prefsStorage.simple = Object.fromEntries(