From de7844cbaa970626c2313d5dba9f02a4083455be Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Sun, 15 Mar 2026 17:59:13 +0200 Subject: [PATCH] attempt to do migration in a different way --- src/modules/default_config_state.js | 30 ++- src/modules/old_default_config_state.js | 306 ++++++++++++++---------- src/stores/sync_config.js | 38 ++- 3 files changed, 227 insertions(+), 147 deletions(-) diff --git a/src/modules/default_config_state.js b/src/modules/default_config_state.js index af2f60ac4..b2ec6405f 100644 --- a/src/modules/default_config_state.js +++ b/src/modules/default_config_state.js @@ -45,7 +45,6 @@ export const instanceDefaultConfig = { muteSensitiveStatuses: false, collapseMessageWithSubject: false, padEmoji: true, - hideAttachments: false, hideAttachmentsInConv: false, hideScrobbles: false, hideScrobblesAfter: '2d', @@ -133,19 +132,8 @@ export const instanceDefaultConfig = { userPopoverOverlay: false, userCardLeftJustify: false, userCardHidePersonalMarks: false, - sidebarColumnWidth: '25rem', - contentColumnWidth: '45rem', - notifsColumnWidth: '25rem', - themeEditorMinWidth: '0rem', - emojiReactionsScale: 0.5, - textSize: '1rem', - emojiSize: '2.2rem', - navbarSize: '3.5rem', - panelHeaderSize: '3.2rem', forcedRoundness: -1, - navbarColumnStretch: false, greentext: false, - mentionLinkDisplay: 'short', mentionLinkShowTooltip: true, mentionLinkShowAvatar: false, mentionLinkFadeDomain: true, @@ -175,8 +163,24 @@ export const instanceDefaultConfig = { useAbsoluteTimeFormat: false, absoluteTimeFormatMinAge: '0d', absoluteTime12h: '24h', - imageCompression: true, +} + +export const defaultConfigLocal = { + hideAttachments: false, + hideAttachmentsInConv: false, + sidebarColumnWidth: '25rem', + contentColumnWidth: '45rem', + notifsColumnWidth: '25rem', + themeEditorMinWidth: '0rem', + emojiReactionsScale: 0.5, + textSize: '1rem', + emojiSize: '2.2rem', + navbarSize: '3.5rem', + panelHeaderSize: '3.2rem', + navbarColumnStretch: false, + mentionLinkDisplay: 'short', alwaysUseJpeg: false, + imageCompression: true, } export const makeUndefined = (c) => diff --git a/src/modules/old_default_config_state.js b/src/modules/old_default_config_state.js index 3977a5d89..e53cafe29 100644 --- a/src/modules/old_default_config_state.js +++ b/src/modules/old_default_config_state.js @@ -1,129 +1,187 @@ // this is a snapshot of config keys used prior to sync config. // used to migrate from old config. -export const defaultStateKeys = [ - 'expertLevel', - 'hideISP', - 'hideInstanceWallpaper', - 'hideShoutbox', - 'hideMutedPosts', - 'hideMutedThreads', - 'hideWordFilteredPosts', - 'muteBotStatuses', - 'muteSensitiveStatuses', - 'collapseMessageWithSubject', - 'padEmoji', - 'hideAttachments', - 'hideAttachmentsInConv', - 'hideScrobbles', - 'hideScrobblesAfter', - 'maxThumbnails', - 'hideNsfw', - 'preloadImage', - 'loopVideo', - 'loopVideoSilentOnly', - 'streaming', - 'emojiReactionsOnTimeline', - 'alwaysShowNewPostButton', - 'autohideFloatingPostButton', - 'pauseOnUnfocused', - 'stopGifs', - 'replyVisibility', - 'thirdColumnMode', - 'notificationVisibility', - 'notificationNative', - 'webPushNotifications', - 'webPushAlwaysShowNotifications', - 'interfaceLanguage', - 'hideScopeNotice', - 'useStreamingApi', - 'sidebarRight', - 'scopeCopy', - 'subjectLineBehavior', - 'alwaysShowSubjectInput', - 'postContentType', - 'minimalScopesMode', - 'hideFilteredStatuses', - 'modalOnRepeat', - 'modalOnUnfollow', - 'modalOnBlock', - 'modalOnMute', - 'modalOnMuteConversation', - 'modalOnMuteDomain', - 'modalOnDelete', - 'modalOnLogout', - 'modalOnApproveFollow', - 'modalOnDenyFollow', - 'modalOnRemoveUserFromFollowers', - 'onMuteDefaultAction', - 'onBlockDefaultAction', - 'modalMobileCenter', - 'playVideosInModal', - 'useOneClickNsfw', - 'useContainFit', - 'disableStickyHeaders', - 'showScrollbars', - 'userPopoverAvatarAction', - 'userPopoverOverlay', - 'userCardLeftJustify', - 'userCardHidePersonalMarks', - 'sidebarColumnWidth', - 'contentColumnWidth', - 'notifsColumnWidth', - 'themeEditorMinWidth', - 'emojiReactionsScale', - 'textSize', - 'emojiSize', - 'navbarSize', - 'panelHeaderSize', - 'forcedRoundness', - 'navbarColumnStretch', - 'greentext', - 'mentionLinkDisplay', - 'mentionLinkShowTooltip', - 'mentionLinkShowAvatar', - 'mentionLinkFadeDomain', - 'mentionLinkShowYous', - 'mentionLinkBoldenYou', - 'hidePostStats', - 'hideBotIndication', - 'hideUserStats', - 'virtualScrolling', - 'sensitiveByDefault', - 'conversationDisplay', - 'conversationTreeAdvanced', - 'conversationOtherRepliesButton', - 'conversationTreeFadeAncestors', - 'showExtraNotifications', - 'showExtraNotificationsTip', - 'showChatsInExtraNotifications', - 'showAnnouncementsInExtraNotifications', - 'showFollowRequestsInExtraNotifications', - 'maxDepthInThread', - 'autocompleteSelect', - 'closingDrawerMarksAsSeen', - 'unseenAtTop', - 'ignoreInactionableSeen', - 'unsavedPostAction', - 'autoSaveDraft', - 'useAbsoluteTimeFormat', - 'absoluteTimeFormatMinAge', - 'absoluteTime12h', - 'imageCompression', - 'alwaysUseJpeg', - 'theme', - 'colors', +// commented entries are unsynced stuff +export const defaultConfigSync = { + expertLevel: 0, // used to track which settings to show and hide + hideISP: false, + hideInstanceWallpaper: false, + hideShoutbox: false, + // bad name: actually hides posts of muted USERS + hideMutedPosts: false, + hideMutedThreads: true, + hideWordFilteredPosts: false, + muteBotStatuses: false, + muteSensitiveStatuses: false, + collapseMessageWithSubject: false, + padEmoji: true, + hideScrobbles: false, + hideScrobblesAfter: '2d', + maxThumbnails: 16, + hideNsfw: true, + preloadImage: true, + loopVideo: true, + loopVideoSilentOnly: true, + /// This is not the streaming API configuration, but rather an option + /// for automatically loading new posts into the timeline without + /// the user clicking the Show New button. + streaming: false, + emojiReactionsOnTimeline: true, + alwaysShowNewPostButton: false, + autohideFloatingPostButton: false, + pauseOnUnfocused: true, + stopGifs: true, + replyVisibility: 'all', + thirdColumnMode: 'notifications', + notificationVisibility: { + follows: true, + mentions: true, + statuses: true, + likes: true, + repeats: true, + moves: true, + emojiReactions: true, + followRequest: true, + reports: true, + chatMention: true, + polls: true, + }, + notificationNative: { + follows: true, + mentions: true, + statuses: true, + likes: false, + repeats: false, + moves: false, + emojiReactions: false, + followRequest: true, + reports: true, + chatMention: true, + polls: true, + }, + webPushNotifications: false, + webPushAlwaysShowNotifications: false, + //interfaceLanguage: '', + hideScopeNotice: false, + useStreamingApi: false, + sidebarRight: false, + scopeCopy: true, + subjectLineBehavior: 'email', + alwaysShowSubjectInput: true, + postContentType: 'text/plain', + minimalScopesMode: false, - 'customTheme', - 'customThemeSource', + // This hides statuses filtered via a word filter + hideFilteredStatuses: false, - 'style', - 'styleCustomData', - 'palette', - 'paletteCustomData', - 'themeDebug', - 'forceThemeRecompilation', - 'theme3hacks', - // 'muteWords', // mutes migrated separately - // 'highlight', // highlight migration is done separately -] + // Confirmations + modalOnRepeat: false, + modalOnUnfollow: false, + modalOnBlock: true, + modalOnMute: false, + modalOnMuteConversation: false, + modalOnMuteDomain: true, + modalOnDelete: true, + modalOnLogout: true, + modalOnApproveFollow: false, + modalOnDenyFollow: false, + modalOnRemoveUserFromFollowers: false, + + // Expiry confirmations/default actions + onMuteDefaultAction: 'ask', + onBlockDefaultAction: 'ask', + + modalMobileCenter: false, + playVideosInModal: false, + useOneClickNsfw: false, + useContainFit: true, + disableStickyHeaders: false, + showScrollbars: false, + userPopoverAvatarAction: 'open', + userPopoverOverlay: false, + userCardLeftJustify: false, + userCardHidePersonalMarks: false, + forcedRoundness: -1, + greentext: false, + mentionLinkShowTooltip: true, + mentionLinkShowAvatar: false, + mentionLinkFadeDomain: true, + mentionLinkShowYous: false, + mentionLinkBoldenYou: true, + hidePostStats: false, + hideBotIndication: false, + hideUserStats: false, + virtualScrolling: true, + sensitiveByDefault: false, + conversationDisplay: 'linear', + conversationTreeAdvanced: false, + conversationOtherRepliesButton: 'below', + conversationTreeFadeAncestors: false, + showExtraNotifications: true, + showExtraNotificationsTip: true, + showChatsInExtraNotifications: true, + showAnnouncementsInExtraNotifications: true, + showFollowRequestsInExtraNotifications: true, + maxDepthInThread: 6, + autocompleteSelect: false, + closingDrawerMarksAsSeen: true, + unseenAtTop: false, + ignoreInactionableSeen: false, + unsavedPostAction: 'confirm', + autoSaveDraft: false, + useAbsoluteTimeFormat: false, + absoluteTimeFormatMinAge: '0d', + absoluteTime12h: '24h', + theme3hacks: { + // Hacks, user overrides that are independent of theme used + underlay: 'none', + fonts: { + interface: undefined, + input: undefined, + post: undefined, + monospace: undefined, + }, + }, + // Special processing + // Theme stuff + theme: undefined, // Very old theme store, stores preset name, still in use + + // V1 + colors: {}, // VERY old theme store, just colors of V1, probably not even used anymore + + // V2 + customTheme: undefined, // "snapshot", previously was used as actual theme store for V2 so it's still used in case of PleromaFE downgrade event. + customThemeSource: undefined, // "source", stores original theme data + + // V3 + style: null, + styleCustomData: null, + palette: null, + paletteCustomData: null, + themeDebug: false, // debug mode that uses computed backgrounds instead of real ones to debug contrast functions + forceThemeRecompilation: false, // flag that forces recompilation on boot even if cache exists + + // Those are handled outside config now + // muteWords: [], + // highlight: {}, +} + +export const defaultConfigLocal = { + hideAttachments: false, + hideAttachmentsInConv: false, + sidebarColumnWidth: '25rem', + contentColumnWidth: '45rem', + notifsColumnWidth: '25rem', + themeEditorMinWidth: '0rem', + emojiReactionsScale: 0.5, + textSize: '1rem', + emojiSize: '2.2rem', + navbarSize: '3.5rem', + panelHeaderSize: '3.2rem', + navbarColumnStretch: false, + mentionLinkDisplay: 'short', + imageCompression: true, + alwaysUseJpeg: false, + imageCompression: true, + alwaysUseJpeg: false, +} diff --git a/src/stores/sync_config.js b/src/stores/sync_config.js index c460b38b0..8a1952c5a 100644 --- a/src/stores/sync_config.js +++ b/src/stores/sync_config.js @@ -7,6 +7,7 @@ import { get, groupBy, isEqual, + deepEqual, set, takeRight, uniqWith, @@ -22,7 +23,7 @@ import { useLocalConfigStore } from 'src/stores/local_config.js' import { storage } from 'src/lib/storage.js' import { defaultState as configDefaultState } from 'src/modules/default_config_state.js' -import { defaultStateKeys } from 'src/modules/old_default_config_state.js' +import { defaultConfigSync } from 'src/modules/old_default_config_state.js' export const VERSION = 2 export const NEW_USER_DATE = new Date('2022-08-04') // date of writing this, basically @@ -38,7 +39,6 @@ export const defaultState = { // storage of flags - stuff that can only be set and incremented flagStorage: { updateCounter: 0, // Counter for most recent update notification seen - configMigration: 0, // Counter for config -> server-side migrations reset: 0, // special flag that can be used to force-reset all data, debug purposes only // special reset codes: // 1000: trim keys to those known by currently running FE @@ -417,14 +417,6 @@ export const _doMigrations = async (data, setPreference) => { console.debug( 'Data has older version, seeing if there any migrations that can be applied', ) - - if (data._version === 1) { - // Migrate old config to sync config - const vuexState = await storage.getItem('vuex-lz') - defaultStateKeys.forEach((key) => { - setPreference({ path: `simple.${key}`, value: vuexState.config[key] }) - }) - } } if (data._version > VERSION) { @@ -623,6 +615,30 @@ export const useSyncConfigStore = defineStore('sync_config', { const flagsTemplate = userNew ? newUserFlags : defaultState.flagStorage let dirty = false + console.debug('Migrating from old config') + const vuexState = await storage.getItem('vuex-lz') + const { config } = vuexState + + const migratedEntries = new Set(config._syncMigration ?? []) + + Object.entries(defaultConfigSync).forEach(([key, value]) => { + const oldValue = config[key] + const defaultValue = value + + const present = oldValue !== undefined + const migrated = migratedEntries.has(key) + const different = !deepEqual(oldValue, defaultValue) + + if (present && !migrated && different) { + console.debug(`Migrating config ${key}: ${oldValue}`,) + // this.setPreference({ path: `simple.${key}`, oldValue }) + migratedEntries.add(key) + console.debug(`Migrating config ${key}: ${oldValue}`,) + } + }) + vuexState.config._syncMigration = [...migratedEntries] + storage.setItem('vuex-lz', vuexState) + if (recent === null) { console.debug( `Data is empty, initializing for ${userNew ? 'new' : 'existing'} user`, @@ -642,6 +658,8 @@ export const useSyncConfigStore = defineStore('sync_config', { const { _timestamp: _0, _version: _1, ...recentData } = recent const { _timestamp: _2, _version: _3, ...staleData } = stale dirty = !isEqual(recentData, staleData) + console.log(JSON.stringify(recentData)) + console.log(JSON.stringify(staleData)) console.debug(`Data ${dirty ? 'needs' : "doesn't need"} merging`) }