From 6211b9adc904608c29e5e31a30d0ef37c9514aae Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Fri, 10 Apr 2026 15:04:00 +0300 Subject: [PATCH 1/9] fix invalid entry --- src/stores/sync_config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stores/sync_config.js b/src/stores/sync_config.js index d7422d0a9..871d09e14 100644 --- a/src/stores/sync_config.js +++ b/src/stores/sync_config.js @@ -789,7 +789,7 @@ export const useSyncConfigStore = defineStore('sync_config', { }) return finalValue === undefined - ? definition.default + ? [path, definition.default] : [path, finalValue] }) newState.prefsStorage.simple = Object.fromEntries( From a44060bd3bd0414c7ae833aa9e1b4f67646dcb65 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Fri, 10 Apr 2026 15:17:28 +0300 Subject: [PATCH 2/9] directly access storage for muteWords --- src/modules/config_declaration.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/modules/config_declaration.js b/src/modules/config_declaration.js index 38619f4fb..0d2f00e38 100644 --- a/src/modules/config_declaration.js +++ b/src/modules/config_declaration.js @@ -1,4 +1,4 @@ -import { useMergedConfigStore } from 'src/stores/merged_config.js' +import { storage } from 'src/lib/storage.js' export const CONFIG_MIGRATION = 1 @@ -23,8 +23,10 @@ export const declarations = [ migrationNum: 1, description: 'Mute filters, wordfilter/regexp/etc', valueType: 'complex', - migration(serverside, rootState) { - useMergedConfigStore().mergedConfig.muteWords.forEach((word, order) => { + migration: async (serverside, rootState) => { + const vuexState = (await storage.getItem('vuex-lz')) ?? {} + + vuexState.config?.muteWords?.forEach((word, order) => { const uniqueId = uuidv4() serverside.setPreference({ From 59ebce47bd226bea9fb3ae2f90786e72784662b8 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Fri, 10 Apr 2026 15:57:16 +0300 Subject: [PATCH 3/9] ignore type for nested entries --- 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 1d7873309..b3e14c70a 100644 --- a/src/modules/default_config_state.js +++ b/src/modules/default_config_state.js @@ -811,7 +811,7 @@ export const validateSetting = ({ } } - if (value !== null && type != null && typeof value !== type) { + if (depth > 2 && value !== null && type != null && typeof value !== type) { const string = `Invalid type for setting ${path}: expected type ${type}, got ${typeof value}, value ${value}; defaulting` if (throwError) { From e05ffbb350823670b4a1afb78ccc98cf8f0ee97b Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Fri, 10 Apr 2026 16:00:28 +0300 Subject: [PATCH 4/9] wip proper migration of mutewords/theme3hacks --- src/modules/config_declaration.js | 47 ----------------------------- src/modules/default_config_state.js | 23 ++++---------- src/modules/users.js | 21 ------------- src/stores/sync_config.js | 34 +++++++++++++++++---- 4 files changed, 34 insertions(+), 91 deletions(-) delete mode 100644 src/modules/config_declaration.js diff --git a/src/modules/config_declaration.js b/src/modules/config_declaration.js deleted file mode 100644 index 0d2f00e38..000000000 --- a/src/modules/config_declaration.js +++ /dev/null @@ -1,47 +0,0 @@ -import { storage } from 'src/lib/storage.js' - -export const CONFIG_MIGRATION = 1 - -import { v4 as uuidv4 } from 'uuid' - -// for future use -/* -const simpleDeclaration = { - store: 'server-side', - migrationFlag: 'configMigration', - migration(serverside, rootState) { - serverside.setSimplePrefAndSave({ path: field, value: rootState.config[oldField ?? field] }) - } -} -*/ - -export const declarations = [ - { - field: 'muteFilters', - store: 'server-side', - migrationFlag: 'configMigration', - migrationNum: 1, - description: 'Mute filters, wordfilter/regexp/etc', - valueType: 'complex', - migration: async (serverside, rootState) => { - const vuexState = (await storage.getItem('vuex-lz')) ?? {} - - vuexState.config?.muteWords?.forEach((word, order) => { - const uniqueId = uuidv4() - - serverside.setPreference({ - path: 'simple.muteFilters.' + uniqueId, - value: { - type: 'word', - value: word, - name: word, - enabled: true, - expires: null, - hide: false, - order, - }, - }) - }) - }, - }, -] diff --git a/src/modules/default_config_state.js b/src/modules/default_config_state.js index b3e14c70a..5f797126d 100644 --- a/src/modules/default_config_state.js +++ b/src/modules/default_config_state.js @@ -531,18 +531,17 @@ 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: {}, }, + underlay: { + description: 'Underlay override', + required: true, + default: 'none', + }, } export const INSTANCE_DEFAULT_CONFIG = convertDefinitions( INSTANCE_DEFAULT_CONFIG_DEFINITIONS, @@ -634,11 +633,6 @@ export const LOCAL_DEFAULT_CONFIG_DEFINITIONS = { description: 'Streaming API (WebSocket)', default: false, }, - underlay: { - description: 'Underlay override', - required: true, - default: 'none', - }, fontInterface: { description: 'Interface font override', type: 'object', @@ -766,6 +760,7 @@ export const validateSetting = ({ if (value === undefined) return undefined // only null is allowed as missing value if (definition === undefined) return undefined // invalid definition const path = fullPath.replace(/^simple./, '') + const depth = path.split('.') if ( validateObjects && definition.type === 'object' && @@ -777,12 +772,6 @@ export const validateSetting = ({ return undefined } - if (path.includes('muteFilters')) { - console.log('##', path, value, definition) - console.log(value) - console.log(path) - console.log('====') - } if (get(defaultState, path.split('.')[0]) === undefined) { const string = `Unknown option ${fullPath}, value: ${value}` diff --git a/src/modules/users.js b/src/modules/users.js index 8b2a991c5..db5b16ccc 100644 --- a/src/modules/users.js +++ b/src/modules/users.js @@ -30,8 +30,6 @@ import { useOAuthStore } from 'src/stores/oauth.js' import { useSyncConfigStore } from 'src/stores/sync_config.js' import { useUserHighlightStore } from 'src/stores/user_highlight.js' -import { declarations } from 'src/modules/config_declaration' - // TODO: Unify with mergeOrAdd in statuses.js export const mergeOrAdd = (arr, obj, item) => { if (!item) { @@ -742,25 +740,6 @@ const users = { useSyncConfigStore().setFlag({ flag: 'configMigration', value: 0 }) /**/ - const { configMigration } = useSyncConfigStore().flagStorage - declarations - .filter((x) => { - return ( - x.store === 'server-side' && - x.migrationNum > 0 && - x.migrationNum > configMigration - ) - }) - .toSorted((a, b) => a.configMigration - b.configMigration) - .forEach((value) => { - value.migration(useSyncConfigStore(), store.rootState) - useSyncConfigStore().setFlag({ - flag: 'configMigration', - value: value.migrationNum, - }) - useSyncConfigStore().pushSyncConfig() - }) - if (user.token) { dispatch('setWsToken', user.token) diff --git a/src/stores/sync_config.js b/src/stores/sync_config.js index 871d09e14..d5a5298be 100644 --- a/src/stores/sync_config.js +++ b/src/stores/sync_config.js @@ -14,6 +14,7 @@ import { uniqWith, unset, } from 'lodash' +import { v4 as uuidv4 } from 'uuid' import { defineStore } from 'pinia' import { toRaw } from 'vue' @@ -676,21 +677,24 @@ export const useSyncConfigStore = defineStore('sync_config', { // Various migrations console.debug('Migrating from old config') const vuexState = (await storage.getItem('vuex-lz')) ?? {} - vuexState.config = vuexState.config ?? {} + const config = vuexState.config ?? {} - const migratedEntries = new Set(vuexState.config._syncMigration ?? []) + const migratedEntries = new Set(config._syncMigration ?? []) console.debug( `Already migrated Values: ${[...migratedEntries].join() || '[none]'}`, ) + const { configMigration } = useSyncConfigStore().flagStorage + Object.entries(oldDefaultConfigSync).forEach(([key, value]) => { - const oldValue = vuexState.config[key] + const oldValue = config[key] const defaultValue = value const present = oldValue !== undefined const migrated = migratedEntries.has(key) const different = !isEqual(oldValue, defaultValue) + console.log(key, oldValue) if (present && !migrated && different) { console.debug(`Migrating config ${key}: ${oldValue}`) if (key === 'theme3hacks') { @@ -710,10 +714,27 @@ export const useSyncConfigStore = defineStore('sync_config', { path: 'fontMonospace', value: oldValue.fonts.monospace, }) - useLocalConfigStore().set({ + useSyncConfigStore().set({ path: 'underlay', value: oldValue.underlay, }) + } else if (key == 'muteWords') { + oldValue.forEach((word, order) => { + const uniqueId = uuidv4() + + useSyncConfigStore().setPreference({ + path: 'simple.muteFilters.' + uniqueId, + value: { + type: 'word', + value: word, + name: word, + enabled: true, + expires: null, + hide: false, + order, + }, + }) + }) } else { this.setPreference({ path: `simple.${key}`, value: oldValue }) } @@ -722,8 +743,9 @@ export const useSyncConfigStore = defineStore('sync_config', { } }) - vuexState.config._syncMigration = [...migratedEntries] - storage.setItem('vuex-lz', vuexState) + config._syncMigration = [...migratedEntries] + vuexState.config = config + //storage.setItem('vuex-lz', vuexState) if (!needUpload && recent && stale) { console.debug('Checking if data needs merging...') From 2563f6d285bb696755ec857ac62268809c9752e2 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Fri, 10 Apr 2026 16:21:34 +0300 Subject: [PATCH 5/9] wip --- src/stores/sync_config.js | 1 + src/stores/user_highlight.js | 9 +++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/stores/sync_config.js b/src/stores/sync_config.js index d5a5298be..53401b9bd 100644 --- a/src/stores/sync_config.js +++ b/src/stores/sync_config.js @@ -677,6 +677,7 @@ export const useSyncConfigStore = defineStore('sync_config', { // Various migrations console.debug('Migrating from old config') const vuexState = (await storage.getItem('vuex-lz')) ?? {} + console.log('### sc', vuexState) const config = vuexState.config ?? {} const migratedEntries = new Set(config._syncMigration ?? []) diff --git a/src/stores/user_highlight.js b/src/stores/user_highlight.js index ca61c6e4e..737c87b49 100644 --- a/src/stores/user_highlight.js +++ b/src/stores/user_highlight.js @@ -278,21 +278,22 @@ export const useUserHighlightStore = defineStore('user_highlight', { let dirty = false const vuexState = (await storage.getItem('vuex-lz')) ?? {} - vuexState.config = vuexState.config ?? {} - const highlight = vuexState.config.highlight ?? {} + const config = vuexState.config ?? {} + const highlight = config.highlight ?? {} Object.entries(highlight).forEach(([user, value]) => { if ((highlight[user]._migrated || 0) < 1) { dirty = true needUpload = true this.set({ user, value: clone(value) }) - vuexState.config.highlight[user]._migrated = 1 + highlight[user]._migrated = 1 console.debug( `[HIGHLIGHT] Migrating user ${user}: ${JSON.stringify(value)}`, ) } }) - storage.setItem('vuex-lz', vuexState) + console.log('### uh', vuexState) + //storage.setItem('vuex-lz', { ...vuexState, config: { ...config, highlight } }) if (recent === null) { console.debug( From 618ca1bfcb7fd6bea3ab528265d0e56a232c64de Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Fri, 10 Apr 2026 16:31:36 +0300 Subject: [PATCH 6/9] wip --- src/lib/persisted_state.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lib/persisted_state.js b/src/lib/persisted_state.js index f7e2b0b08..d5b1c1701 100644 --- a/src/lib/persisted_state.js +++ b/src/lib/persisted_state.js @@ -42,6 +42,8 @@ export default function createPersistedState({ console.info('waiting for old state to be loaded...') return Promise.resolve() } else { + console.log(key, state) + debugger return storage.setItem(key, state) } }, From 759fd5c9d5a5416de17b26c2df6d7792e91c00dc Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Fri, 10 Apr 2026 16:36:56 +0300 Subject: [PATCH 7/9] fix config data disappearing --- src/main.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.js b/src/main.js index ceb8c0878..44b01aabb 100644 --- a/src/main.js +++ b/src/main.js @@ -41,7 +41,7 @@ const i18n = createI18n({ messages.setLanguage(i18n.global, currentLocale) const persistedStateOptions = { - paths: ['users.lastLoginName', 'oauth'], + paths: ['users.lastLoginName', 'oauth', 'config'], } ;(async () => { From 37b562c4aa8c538cb1039aee056880ddd1787a8c Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Fri, 10 Apr 2026 16:54:16 +0300 Subject: [PATCH 8/9] fix vuex->pinia migration for sync/local stores --- src/lib/persisted_state.js | 2 -- src/stores/sync_config.js | 6 ++---- src/stores/user_highlight.js | 3 +-- 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/src/lib/persisted_state.js b/src/lib/persisted_state.js index d5b1c1701..f7e2b0b08 100644 --- a/src/lib/persisted_state.js +++ b/src/lib/persisted_state.js @@ -42,8 +42,6 @@ export default function createPersistedState({ console.info('waiting for old state to be loaded...') return Promise.resolve() } else { - console.log(key, state) - debugger return storage.setItem(key, state) } }, diff --git a/src/stores/sync_config.js b/src/stores/sync_config.js index 53401b9bd..722dd7f16 100644 --- a/src/stores/sync_config.js +++ b/src/stores/sync_config.js @@ -677,7 +677,6 @@ export const useSyncConfigStore = defineStore('sync_config', { // Various migrations console.debug('Migrating from old config') const vuexState = (await storage.getItem('vuex-lz')) ?? {} - console.log('### sc', vuexState) const config = vuexState.config ?? {} const migratedEntries = new Set(config._syncMigration ?? []) @@ -695,7 +694,6 @@ export const useSyncConfigStore = defineStore('sync_config', { const migrated = migratedEntries.has(key) const different = !isEqual(oldValue, defaultValue) - console.log(key, oldValue) if (present && !migrated && different) { console.debug(`Migrating config ${key}: ${oldValue}`) if (key === 'theme3hacks') { @@ -715,7 +713,7 @@ export const useSyncConfigStore = defineStore('sync_config', { path: 'fontMonospace', value: oldValue.fonts.monospace, }) - useSyncConfigStore().set({ + useSyncConfigStore().setSimplePrefAndSave({ path: 'underlay', value: oldValue.underlay, }) @@ -746,7 +744,7 @@ export const useSyncConfigStore = defineStore('sync_config', { config._syncMigration = [...migratedEntries] vuexState.config = config - //storage.setItem('vuex-lz', vuexState) + storage.setItem('vuex-lz', vuexState) if (!needUpload && recent && stale) { console.debug('Checking if data needs merging...') diff --git a/src/stores/user_highlight.js b/src/stores/user_highlight.js index 737c87b49..5ca13b6a9 100644 --- a/src/stores/user_highlight.js +++ b/src/stores/user_highlight.js @@ -292,8 +292,7 @@ export const useUserHighlightStore = defineStore('user_highlight', { ) } }) - console.log('### uh', vuexState) - //storage.setItem('vuex-lz', { ...vuexState, config: { ...config, highlight } }) + storage.setItem('vuex-lz', { ...vuexState, config: { ...config, highlight } }) if (recent === null) { console.debug( From bb48d949e1988e6faaee29db4f6614f25ef7e9c6 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Fri, 10 Apr 2026 17:04:32 +0300 Subject: [PATCH 9/9] fix underlay --- src/components/settings_modal/tabs/appearance_tab.vue | 2 +- src/lib/style.js | 7 +------ 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/components/settings_modal/tabs/appearance_tab.vue b/src/components/settings_modal/tabs/appearance_tab.vue index cd463a34a..a0439ed4a 100644 --- a/src/components/settings_modal/tabs/appearance_tab.vue +++ b/src/components/settings_modal/tabs/appearance_tab.vue @@ -235,7 +235,7 @@
  • {{ $t('settings.style.themes3.hacks.underlay_overrides') }} diff --git a/src/lib/style.js b/src/lib/style.js index e91362c24..534d120e3 100644 --- a/src/lib/style.js +++ b/src/lib/style.js @@ -23,15 +23,12 @@ const APPEARANCE_SETTINGS_KEYS = [ 'forcedRoundness', 'emojiSize', 'emojiReactionsScale', -] - -const HACKS_KEYS = new Set([ 'fontInterface', 'fontPosts', 'fontInput', 'fontMonospace', 'underlay', -]) +] const MIXED_KEYS = new Set([ ...APPEARANCE_SETTINGS_KEYS, @@ -45,8 +42,6 @@ export const piniaStylePlugin = ({ store, options }) => { const { path } = args[0] if (MIXED_KEYS.has(path)) { after(() => applyStyleConfig(useMergedConfigStore().mergedConfig)) - } - if (HACKS_KEYS.has(path)) { after(() => useInterfaceStore().applyTheme({ recompile: true })) } }