From a461068e40585c8d781a84e253c0a45dad5adca4 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Tue, 24 Mar 2026 20:04:46 +0200 Subject: [PATCH 1/4] restructure settings definitions --- src/boot/after_store.js | 41 ++- .../features_panel/features_panel.js | 2 +- .../features_panel/features_panel.vue | 2 +- .../post_status_form/post_status_form.js | 2 +- src/modules/default_config_state.js | 254 ++++++++++++++---- src/stores/instance.js | 217 +++++++++++---- src/stores/local_config.js | 21 +- src/stores/sync_config.js | 28 +- src/sw.js | 4 +- 9 files changed, 432 insertions(+), 139 deletions(-) diff --git a/src/boot/after_store.js b/src/boot/after_store.js index 5c2aaa6ef..7d1995347 100644 --- a/src/boot/after_store.js +++ b/src/boot/after_store.js @@ -39,8 +39,8 @@ import { useUserHighlightStore } from 'src/stores/user_highlight.js' import VBodyScrollLock from 'src/directives/body_scroll_lock' import { - instanceDefaultConfigDefinitions, - instanceIdentityDefaultDefinitions, + INSTANCE_DEFAULT_CONFIG_DEFINITIONS, + INSTANCE_IDENTITY_DEFAULT_DEFINITIONS, } from 'src/modules/default_config_state.js' let staticInitialResults = null @@ -83,7 +83,7 @@ const getInstanceConfig = async ({ store }) => { const res = await preloadFetch('/api/v1/instance') if (res.ok) { const data = await res.json() - const textlimit = data.max_toot_chars + const textLimit = data.max_toot_chars const vapidPublicKey = data.pleroma.vapid_public_key useInstanceCapabilitiesStore().set( @@ -91,8 +91,8 @@ const getInstanceConfig = async ({ store }) => { data.pleroma, ) useInstanceStore().set({ - path: 'textlimit', - value: textlimit, + path: 'limits.textLimit', + value: textLimit, }) useInstanceStore().set({ path: 'accountApprovalRequired', @@ -169,22 +169,19 @@ const setSettings = async ({ apiConfig, staticConfig, store }) => { config = Object.assign({}, staticConfig, apiConfig) } - const copyInstanceOption = ({ source, definition = { required: true }, destination }) => { - const value = config[source] - let { required, type, default: defaultValue } = definition - if (type == null && defaultValue != null) type = typeof defaultValue - if (required && value == null) return - if (type != null && typeof value !== type) return + Object.keys(INSTANCE_IDENTITY_DEFAULT_DEFINITIONS).forEach((source) => + useInstanceStore().set({ + value: config[source], + path: `instanceIdentity.${source}`, + }), + ) - useInstanceStore().set({ path: destination, value }) - } - - Object.entries(instanceIdentityDefaultDefinitions) - .map(([source, definition]) => ({ source, definition, destination: `instanceIdentity.${source}` })) - .forEach(copyInstanceOption) - Object.keys(instanceDefaultConfigDefinitions) - .map(([source, definition]) => ({ source, definition, destination: `prefsStorage.${source}` })) - .forEach(copyInstanceOption) + Object.keys(INSTANCE_DEFAULT_CONFIG_DEFINITIONS).forEach((source) => + useInstanceStore().set({ + value: config[source], + path: `prefsStorage.${source}`, + }), + ) useAuthFlowStore().setInitialStrategy(config.loginMethod) } @@ -194,7 +191,7 @@ const getTOS = async ({ store }) => { const res = await window.fetch('/static/terms-of-service.html') if (res.ok) { const html = await res.text() - useInstanceStore().set({ name: 'instanceIdentity.tos', value: html }) + useInstanceStore().set({ path: 'instanceIdentity.tos', value: html }) } else { throw res } @@ -543,7 +540,7 @@ const afterStoreSetup = async ({ pinia, store, storageError, i18n }) => { typeof overrides.target !== 'undefined' ? overrides.target : window.location.origin - useInstanceStore().set({ name: 'server', value: server }) + useInstanceStore().set({ path: 'server', value: server }) await setConfig({ store }) try { diff --git a/src/components/features_panel/features_panel.js b/src/components/features_panel/features_panel.js index 106da1487..10deda250 100644 --- a/src/components/features_panel/features_panel.js +++ b/src/components/features_panel/features_panel.js @@ -15,7 +15,7 @@ const FeaturesPanel = { 'mediaProxyAvailable', ]), ...mapState(useInstanceStore, { - textlimit: (store) => store.limits.textlimit, + textLimit: (store) => store.limits.textLimit, uploadlimit: (store) => fileSizeFormatService.fileSizeFormat(store.limits.uploadlimit), }), diff --git a/src/components/features_panel/features_panel.vue b/src/components/features_panel/features_panel.vue index 4270436fa..f15ad0edb 100644 --- a/src/components/features_panel/features_panel.vue +++ b/src/components/features_panel/features_panel.vue @@ -24,7 +24,7 @@ {{ $t('features_panel.media_proxy') }}
  • {{ $t('features_panel.scope_options') }}
  • -
  • {{ $t('features_panel.text_limit') }} = {{ textlimit }}
  • +
  • {{ $t('features_panel.text_limit') }} = {{ textLimit }}
  • {{ $t('features_panel.upload_limit') }} = {{ uploadlimit.num }} {{ $t('upload.file_size_units.' + uploadlimit.unit) }}
  • diff --git a/src/components/post_status_form/post_status_form.js b/src/components/post_status_form/post_status_form.js index 2530392bc..20140df19 100644 --- a/src/components/post_status_form/post_status_form.js +++ b/src/components/post_status_form/post_status_form.js @@ -290,7 +290,7 @@ const PostStatusForm = { return this.newStatus.spoilerText.length }, statusLengthLimit() { - return useInstanceStore().textlimit + return useInstanceStore().limits.textLimit }, hasStatusLengthLimit() { return this.statusLengthLimit > 0 diff --git a/src/modules/default_config_state.js b/src/modules/default_config_state.js index 89047f314..4606c67a3 100644 --- a/src/modules/default_config_state.js +++ b/src/modules/default_config_state.js @@ -1,16 +1,19 @@ +import { get, set } from 'lodash' + const browserLocale = (navigator.language || 'en').split('-')[0] -const convertDefinitions = definitions => Object.fromEntries( - Object.entries(definitions).map(([k, v]) => [ - k, - v.default == null ? null : v.default, - ]), -) +export const convertDefinitions = (definitions) => + Object.fromEntries( + Object.entries(definitions).map(([k, v]) => { + const defaultValue = v.default ?? null + return [k, defaultValue] + }), + ) /// Instance config entries provided by static config or pleroma api /// Put settings here only if it does not make sense for a normal user /// to override it. -export const instanceIdentityDefaultDefinitions = { +export const INSTANCE_IDENTITY_DEFAULT_DEFINITIONS = { style: { description: 'Instance default style name', type: 'string', @@ -110,14 +113,17 @@ export const instanceIdentityDefaultDefinitions = { required: false, }, } -export const instanceIdentityDefault = convertDefinitions(instanceIdentityDefaultDefinitions) +export const INSTANCE_IDENTITY_DEFAULT = convertDefinitions( + INSTANCE_IDENTITY_DEFAULT_DEFINITIONS, +) /// This object contains setting entries that makes sense /// at the user level. The defaults can also be overriden by /// instance admins in the frontend_configuration endpoint or static config. -export const instanceDefaultConfigDefinitions = { +export const INSTANCE_DEFAULT_CONFIG_DEFINITIONS = { expertLevel: { - description: 'Used to track which settings to show and hide in settings modal', + description: + 'Used to track which settings to show and hide in settings modal', type: 'number', // not a boolean so we could potentially make multiple levels of expert-ness default: 0, }, @@ -133,7 +139,8 @@ export const instanceDefaultConfigDefinitions = { description: 'Hide shoutbox if present', default: false, }, - hideMutedPosts: { // bad name + hideMutedPosts: { + // bad name description: 'Hide posts of muted users entirely', default: false, }, @@ -205,7 +212,8 @@ export const instanceDefaultConfigDefinitions = { default: false, }, autohideFloatingPostButton: { - description: 'Automatically hide mobile "new post" button when scrolling down', + description: + 'Automatically hide mobile "new post" button when scrolling down', default: false, }, stopGifs: { @@ -400,7 +408,8 @@ export const instanceDefaultConfigDefinitions = { default: false, }, mentionLinkFadeDomain: { - description: 'Mute (fade) domain name in mention links if configured to show it', + description: + 'Mute (fade) domain name in mention links if configured to show it', default: true, }, mentionLinkShowYous: { @@ -448,7 +457,8 @@ export const instanceDefaultConfigDefinitions = { default: false, }, showExtraNotifications: { - description: 'Show extra notifications (chats, announcements etc) in notification panel', + description: + 'Show extra notifications (chats, announcements etc) in notification panel', default: true, }, showExtraNotificationsTip: { @@ -507,41 +517,141 @@ export const instanceDefaultConfigDefinitions = { description: 'Use 24h time format', default: '24h', }, + themeChecksum: { + description: 'Checksum of theme used', + type: 'string', + required: false, + }, } -export const instanceDefaultConfig = convertDefinitions(instanceDefaultConfigDefinitions) +export const INSTANCE_DEFAULT_CONFIG = convertDefinitions( + INSTANCE_DEFAULT_CONFIG_DEFINITIONS, +) -export const defaultConfigLocal = { - hideAttachments: false, - hideAttachmentsInConv: false, - hideNsfw: true, - useOneClickNsfw: false, - preloadImage: true, - postContentType: 'text/plain', - sidebarRight: 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, - useStreamingApi: false, - underlay: 'none', - fontInterface: undefined, - fontInput: undefined, - fontPosts: undefined, - fontMonospace: undefined, - 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 +export const LOCAL_DEFAULT_CONFIG_DEFINITIONS = { + // TODO these two used to be separate but since separation feature got broken it doesn't matter + hideAttachments: { + description: 'Hide attachments in timeline', + default: false, + }, + hideAttachmentsInConv: { + description: 'Hide attachments in coversation', + default: false, + }, + hideNsfw: { + description: 'Hide nsfw posts', + default: true, + }, + useOneClickNsfw: { + description: 'Open NSFW images directly in media modal', + default: false, + }, + preloadImage: { + description: 'Preload images for NSFW', + default: true, + }, + postContentType: { + description: 'Default post content type', + default: 'text/plain', + }, + sidebarRight: { + description: 'Reverse order of columns', + default: false, + }, + sidebarColumnWidth: { + description: 'Sidebar column width', + default: '25rem', + }, + contentColumnWidth: { + description: 'Middle column width', + default: '45rem', + }, + notifsColumnWidth: { + description: 'Notifications column width', + default: '25rem', + }, + themeEditorMinWidth: { + description: 'Hack for theme editor on mobile', + default: '0rem', + }, + emojiReactionsScale: { + description: 'Emoji reactions scale factor', + default: 0.5, + }, + textSize: { + description: 'Font size', + default: '1rem', + }, + emojiSize: { + description: 'Emoji size', + default: '2.2rem', + }, + navbarSize: { + description: 'Navbar size', + default: '3.5rem', + }, + panelHeaderSize: { + description: 'Panel header size', + default: '3.2rem', + }, + navbarColumnStretch: { + description: 'Stretch navbar to match columns width', + default: false, + }, + mentionLinkDisplay: { + description: 'How to display mention links', + default: 'short', + }, + imageCompression: { + description: 'Image compression (WebP/JPEG)', + default: true, + }, + alwaysUseJpeg: { + description: 'Compress images using JPEG only', + default: false, + }, + useStreamingApi: { + description: 'Streaming API (WebSocket)', + default: false, + }, + underlay: { + description: 'Underlay override', + default: 'none', + }, + fontInterface: { + description: 'Interface font override', + type: 'string', + default: null, + }, + fontInput: { + description: 'Input font override', + type: 'string', + default: null, + }, + fontPosts: { + description: 'Post font override', + type: 'string', + default: null, + }, + fontMonospace: { + description: 'Monospace font override', + type: 'string', + default: null, + }, + themeDebug: { + description: + 'Debug mode that uses computed backgrounds instead of real ones to debug contrast functions', + default: false, + }, + forceThemeRecompilation: { + description: 'Flag that forces recompilation on boot even if cache exists', + default: false, + }, } +export const LOCAL_DEFAULT_CONFIG = convertDefinitions( + LOCAL_DEFAULT_CONFIG_DEFINITIONS, +) -export const LOCAL_ONLY_KEYS = new Set(Object.keys(defaultConfigLocal)) +export const LOCAL_ONLY_KEYS = new Set(Object.keys(LOCAL_DEFAULT_CONFIG)) export const makeUndefined = (c) => Object.fromEntries(Object.keys(c).map((key) => [key, undefined])) @@ -550,8 +660,8 @@ export const makeUndefined = (c) => /// make sense to be overriden on a instance-wide level. export const defaultState = { // Set these to undefined so it does not interfere with default settings check - ...makeUndefined(instanceDefaultConfig), - ...makeUndefined(defaultConfigLocal), + ...makeUndefined(INSTANCE_DEFAULT_CONFIG), + ...makeUndefined(LOCAL_DEFAULT_CONFIG), // If there are any configurations that does not make sense to // have instance-wide default, put it here and explain why. @@ -572,3 +682,53 @@ export const defaultState = { palette: null, paletteCustomData: null, } + +export const validateSetting = ({ + value, + path, + definition, + throwError, + defaultState, +}) => { + if (value === undefined) return // only null is allowed as missing value + if (get(defaultState, path) === undefined) { + const string = `Unknown instance option ${path}, value: ${value}` + + if (throwError) { + throw new Error(string) + } else { + console.error(string) + return value + } + } + + let { required, type, default: defaultValue } = definition + + if (type == null && defaultValue != null) { + type = typeof defaultValue + } + + if (required && value == null) { + const string = `Value required for setting ${path} but was provided nullish; defaulting` + + if (throwError) { + throw new Error(string) + } else { + console.error(string) + return defaultValue + } + } + + if (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) { + throw new Error(string) + } else { + console.error(string) + return defaultValue + } + } + + return value +} diff --git a/src/stores/instance.js b/src/stores/instance.js index f1f31a8d1..71ee05e7a 100644 --- a/src/stores/instance.js +++ b/src/stores/instance.js @@ -1,41 +1,146 @@ import { get, set } from 'lodash' import { defineStore } from 'pinia' -import { instanceDefaultProperties } from '../modules/config.js' import { - defaultConfigLocal, - instanceDefaultConfig, - instanceIdentityDefault, + convertDefinitions, + INSTANCE_DEFAULT_CONFIG, + INSTANCE_DEFAULT_CONFIG_DEFINITIONS, + INSTANCE_IDENTITY_DEFAULT, + INSTANCE_IDENTITY_DEFAULT_DEFINITIONS, + LOCAL_DEFAULT_CONFIG, + LOCAL_DEFAULT_CONFIG_DEFINITIONS, + validateSetting, } from '../modules/default_config_state.js' import apiService from '../services/api/api.service.js' import { useInterfaceStore } from 'src/stores/interface.js' -import { ensureFinalFallback } from 'src/i18n/languages.js' - const REMOTE_INTERACTION_URL = '/main/ostatus' -const defaultState = { - name: 'Pleroma FE', - registrationOpen: true, - server: 'http://localhost:4040/', - textlimit: 5000, - privateMode: false, - federating: true, - federationPolicy: null, - themesIndex: null, - stylesIndex: null, - palettesIndex: null, - themeData: null, // used for theme editor v2 - vapidPublicKey: null, +const ROOT_STATE_DEFINITIONS = { + name: { + type: 'string', + default: 'PleromaFE', + }, + registrationOpen: { + required: true, + type: 'boolean', + }, + server: { + description: 'Server URL', + required: true, + type: 'string', + }, + privateMode: { + description: 'Private instance?', + required: true, + type: 'boolean', + }, + federating: { + description: 'Is federation enabled?', + required: true, + type: 'boolean', + }, + federationPolicy: { + description: '????', + required: true, + type: 'object', + }, + + // Indexes of themes/styles/palettes + themesIndex: { + required: true, + type: 'object', + }, + stylesIndex: { + required: true, + type: 'object', + }, + palettesIndex: { + required: true, + type: 'object', + }, + + themeData: { + description: 'Used for theme v2 editor', + required: true, + type: 'object', + }, + vapidPublicKey: { + description: 'Used for WebPush', + required: true, + type: 'string', + }, // Stuff from static/config.json - loginMethod: 'password', - disableUpdateNotification: false, + loginMethod: { + description: 'Login method (token/password)', + default: 'password', + }, + disableUpdateNotification: { + description: 'Disable update notification (pleroma-tan one)', + default: false, + }, + knownDomains: { + description: 'List of known domains; used for domain list for domain mutes', + default: [], + }, + // Moderation stuff + staffAccounts: { + description: 'List of staff accounts', + default: [], + }, + accountActivationRequired: { + description: + 'Account activation (by email or admin) required after registration', + required: true, + type: 'boolean', + }, + accountApprovalRequired: { + description: 'Admin approval required after registration', + required: true, + type: 'boolean', + }, + birthdayRequired: { + description: 'Require birthday entry when registering', + required: true, + type: 'boolean', + }, + birthdayMinAge: { + description: 'Minimum age required for registration', + default: 0, + }, + restrictedNicknames: { + description: 'List of nicknames that are not allowed', + default: [], + }, + localBubbleInstances: { + description: "Akkoma's bubble feature, list of instances", + default: [], + }, // Akkoma + + // Version Information + backendVersion: { + required: true, + type: 'string', + }, + backendRepository: { + required: true, + type: 'string', + }, + frontendVersion: { + required: true, + type: 'string', + }, +} +const ROOT_STATE = convertDefinitions(ROOT_STATE_DEFINITIONS) + +const DEFAULT_STATE = { + ...ROOT_STATE, // Instance-wide configurations that should not be changed by individual users instanceIdentity: { - ...instanceIdentityDefault, + ...INSTANCE_IDENTITY_DEFAULT, }, limits: { @@ -44,6 +149,7 @@ const defaultState = { backgroundlimit: null, uploadlimit: null, fieldsLimits: null, + textLimit: null, pollLimits: { max_options: 4, max_option_chars: 255, @@ -54,50 +160,53 @@ const defaultState = { // Instance admins can override default settings for the whole instance prefsStorage: { - ...instanceDefaultConfig, - ...defaultConfigLocal, + ...INSTANCE_DEFAULT_CONFIG, + ...LOCAL_DEFAULT_CONFIG, }, - - // Known domains list for user's domain-muting - knownDomains: [], - - // Moderation stuff - staffAccounts: [], - accountActivationRequired: null, - accountApprovalRequired: null, - birthdayRequired: false, - birthdayMinAge: 0, - restrictedNicknames: [], - localBubbleInstances: [], // Akkoma - - // Version Information - backendVersion: '', - backendRepository: '', - frontendVersion: '', } +console.log('===', ROOT_STATE_DEFINITIONS) export const useInstanceStore = defineStore('instance', { - state: () => ({ ...defaultState }), + state: () => ({ ...DEFAULT_STATE }), getters: { - instanceDefaultConfig(state) { - return instanceDefaultProperties - .map((key) => [key, state[key]]) - .reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {}) - }, instanceDomain(state) { return new URL(this.server).hostname }, }, actions: { - set({ path, name, value }) { - if (get(defaultState, path ?? name) === undefined) - console.error( - `Unknown instance option ${path ?? name}, value: ${value}`, - ) + set({ path, value }) { + let definition + const pathArray = path.split('.') + const subpath = pathArray[1] ?? pathArray[0] - set(this, path ?? name, value) + if (path.startsWith('instanceIdentity.')) { + definition = INSTANCE_IDENTITY_DEFAULT_DEFINITIONS[subpath] + } else if (path.startsWith('prefsStorage.')) { + definition = { + ...LOCAL_DEFAULT_CONFIG_DEFINITIONS, + ...INSTANCE_DEFAULT_CONFIG_DEFINITIONS, + }[subpath] + } else if (path.startsWith('limits.')) { + definition = + path === 'limits.pollLimits' || path === 'limits.fieldsLimits' + ? { required: true, type: 'object' } + : { required: true, type: 'number' } + } else { + const definitions = ROOT_STATE_DEFINITIONS + definition = definitions[subpath] + } - if ((path ?? name) === 'name') useInterfaceStore().setPageTitle() + const finalValue = validateSetting({ + path, + value, + definition, + throwError: true, + defaultState: DEFAULT_STATE, + }) + + set(this, path, finalValue) + + if (path === 'name') useInterfaceStore().setPageTitle() }, async getKnownDomains() { try { diff --git a/src/stores/local_config.js b/src/stores/local_config.js index 096d0b3f1..b81a55408 100644 --- a/src/stores/local_config.js +++ b/src/stores/local_config.js @@ -4,14 +4,17 @@ import { toRaw } from 'vue' import { useInstanceStore } from 'src/stores/instance' -import { defaultState as configDefaultState } from 'src/modules/default_config_state' +import { + LOCAL_DEFAULT_CONFIG, + LOCAL_DEFAULT_CONFIG_DEFINITIONS +} from 'src/modules/default_config_state' export const defaultState = { prefsStorage: { - ...configDefaultState, + ...LOCAL_DEFAULT_CONFIG, }, tempStorage: { - ...configDefaultState, + ...LOCAL_DEFAULT_CONFIG, }, } @@ -21,7 +24,17 @@ export const useLocalConfigStore = defineStore('local_config', { }, actions: { set({ path, value }) { - set(this.prefsStorage, path, value) + const definition = LOCAL_DEFAULT_CONFIG_DEFINITIONS[path] + + const finalValue = validateSetting({ + path, + value, + definition, + throwError: false, + defaultState: LOCAL_DEFAULT_CONFIG, + }) + + set(this.prefsStorage, path, finalValue) }, setTemporarily({ path, value }) { set(this.tempStorage, path, value) diff --git a/src/stores/sync_config.js b/src/stores/sync_config.js index d4b5ad50a..57deb9c60 100644 --- a/src/stores/sync_config.js +++ b/src/stores/sync_config.js @@ -25,8 +25,10 @@ import { useLocalConfigStore } from 'src/stores/local_config.js' import { storage } from 'src/lib/storage.js' import { defaultState as configDefaultState, - defaultConfigLocal, - instanceDefaultConfig, + validateSetting, + INSTANCE_DEFAULT_CONFIG, + INSTANCE_DEFAULT_CONFIG_DEFINITIONS, + LOCAL_DEFAULT_CONFIG, LOCAL_ONLY_KEYS, } from 'src/modules/default_config_state.js' import { oldDefaultConfigSync } from 'src/modules/old_default_config_state.js' @@ -488,7 +490,19 @@ export const useSyncConfigStore = defineStore('sync_config', { `Calling set on depth > 3 (path: ${path}) is not allowed`, ) } - set(this.prefsStorage, path, value) + + const definition = INSTANCE_DEFAULT_CONFIG_DEFINITIONS[path.split('.')[1]] + + const finalValue = validateSetting({ + path: path.split('.')[1], + value, + definition, + throwError: false, + defaultState: INSTANCE_DEFAULT_CONFIG, + }) + + set(this.prefsStorage, path, finalValue) + this.prefsStorage._journal = [ ...this.prefsStorage._journal, { operation: 'set', path, args: [value], timestamp: Date.now() }, @@ -750,12 +764,12 @@ export const useSyncConfigStore = defineStore('sync_config', { ? (tempPrefs[k] ?? localPrefs[k] ?? instancePrefs[k] ?? - defaultConfigLocal[k]) + LOCAL_DEFAULT_CONFIG[k]) : (tempPrefs[k] ?? localPrefs[k] ?? value ?? instancePrefs[k] ?? - instanceDefaultConfig[k]), + INSTANCE_DEFAULT_CONFIG[k]), ]), ) return result @@ -766,8 +780,8 @@ export const useSyncConfigStore = defineStore('sync_config', { Object.entries(state.prefsStorage.simple).map(([k, value]) => [ k, LOCAL_ONLY_KEYS.has(k) - ? (instancePrefs[k] ?? defaultConfigLocal[k]) - : (instancePrefs[k] ?? instanceDefaultConfig[k]), + ? (instancePrefs[k] ?? LOCAL_DEFAULT_CONFIG[k]) + : (instancePrefs[k] ?? INSTANCE_DEFAULT_CONFIG[k]), ]), ) return result diff --git a/src/sw.js b/src/sw.js index b7c551480..1e7abd3de 100644 --- a/src/sw.js +++ b/src/sw.js @@ -5,7 +5,7 @@ import 'virtual:pleroma-fe/service_worker_env' import { createI18n } from 'vue-i18n' import { storage } from 'src/lib/storage.js' -import { instanceDefaultConfig } from 'src/modules/default_config_state.js' +import { INSTANCE_DEFAULT_CONFIG } from 'src/modules/default_config_state.js' import { parseNotification } from 'src/services/entity_normalizer/entity_normalizer.service.js' import { prepareNotificationObject } from 'src/services/notification_utils/notification_utils.js' import { cacheKey, emojiCacheKey, shouldCache } from 'src/services/sw/sw.js' @@ -40,7 +40,7 @@ const setSettings = async () => { i18n.locale = locale const notificationsNativeArray = Object.entries( piniaState.prefsStorage.simple.notificationNative || - instanceDefaultConfig.notificationNative, + INSTANCE_DEFAULT_CONFIG.notificationNative, ) state.webPushAlwaysShowNotifications = piniaState.prefsStorage.simple.webPushAlwaysShowNotifications From 784ae2edb6fc8be84132ef824ea222e3630fd982 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Tue, 24 Mar 2026 20:22:07 +0200 Subject: [PATCH 2/4] remove old config module --- .../extra_notifications.js | 3 +- src/modules/config.js | 200 ------------------ src/modules/index.js | 2 - 3 files changed, 2 insertions(+), 203 deletions(-) delete mode 100644 src/modules/config.js diff --git a/src/components/extra_notifications/extra_notifications.js b/src/components/extra_notifications/extra_notifications.js index e00f58bec..67be59144 100644 --- a/src/components/extra_notifications/extra_notifications.js +++ b/src/components/extra_notifications/extra_notifications.js @@ -52,10 +52,11 @@ const ExtraNotifications = { currentUser() { return this.$store.state.users.currentUser }, - ...mapGetters(['unreadChatCount', 'followRequestCount', 'mergedConfig']), + ...mapGetters(['unreadChatCount', 'followRequestCount']), ...mapPiniaState(useAnnouncementsStore, { unreadAnnouncementCount: 'unreadAnnouncementCount', }), + ...mapPiniaState(useSyncConfigStore, ['mergedConfig']), }, methods: { openNotificationSettings() { diff --git a/src/modules/config.js b/src/modules/config.js deleted file mode 100644 index b68d547df..000000000 --- a/src/modules/config.js +++ /dev/null @@ -1,200 +0,0 @@ -import Cookies from 'js-cookie' -import { set } from 'lodash' - -import messages from '../i18n/messages' -import localeService from '../services/locale/locale.service.js' -import { applyStyleConfig } from '../services/style_setter/style_setter.js' -import { defaultState, instanceDefaultConfig } from './default_config_state.js' - -import { useEmojiStore } from 'src/stores/emoji.js' -import { useI18nStore } from 'src/stores/i18n.js' -import { useInstanceStore } from 'src/stores/instance.js' -import { useInterfaceStore } from 'src/stores/interface.js' - -const BACKEND_LANGUAGE_COOKIE_NAME = 'userLanguage' -const APPEARANCE_SETTINGS_KEYS = new Set([ - 'sidebarColumnWidth', - 'contentColumnWidth', - 'notifsColumnWidth', - 'themeEditorMinWidth', - 'textSize', - 'navbarSize', - 'panelHeaderSize', - 'forcedRoundness', - 'emojiSize', - 'emojiReactionsScale', -]) - -/* TODO this is a bit messy. - * We need to declare settings with their types and also deal with - * instance-default settings in some way, hopefully try to avoid copy-pasta - * in general. - */ -export const multiChoiceProperties = [ - 'postContentType', - 'subjectLineBehavior', - 'conversationDisplay', // tree | linear - 'conversationOtherRepliesButton', // below | inside - 'mentionLinkDisplay', // short | full_for_remote | full - 'userPopoverAvatarAction', // close | zoom | open - 'unsavedPostAction', // save | discard | confirm -] - -// caching the instance default properties -export const instanceDefaultProperties = Object.keys(instanceDefaultConfig) - -const config = { - state: { ...defaultState }, - getters: { - defaultConfig() { - return { - ...defaultState, - ...Object.fromEntries( - instanceDefaultProperties.map((key) => [ - key, - useInstanceStore().prefsStorage[key], - ]), - ), - } - }, - mergedConfig(state) { - const instancePrefs = useInstanceStore().prefsStorage - const result = Object.fromEntries( - Object.keys(defaultState).map((key) => [ - key, - state[key] ?? instancePrefs[key], - ]), - ) - return result - }, - }, - mutations: { - setOptionTemporarily(state, { name, value }) { - set(state, name, value) - applyStyleConfig(state) - }, - setOption(state, { name, value }) { - set(state, name, value) - }, - setHighlight(state, { user, color, type }) { - const data = this.state.config.highlight[user] - if (color || type) { - state.highlight[user] = { - color: color || data.color, - type: type || data.type, - } - } else { - delete state.highlight[user] - } - }, - }, - actions: { - loadSettings({ dispatch }, data) { - const knownKeys = new Set(Object.keys(defaultState)) - const presentKeys = new Set(Object.keys(data)) - const intersection = new Set() - for (const elem of presentKeys) { - if (knownKeys.has(elem)) { - intersection.add(elem) - } - } - - intersection.forEach((name) => - dispatch('setOption', { name, value: data[name] }), - ) - }, - setHighlight({ commit }, { user, color, type }) { - commit('setHighlight', { user, color, type }) - }, - setOptionTemporarily({ commit, dispatch, state }, { name, value }) { - if (useInterfaceStore().temporaryChangesTimeoutId !== null) { - console.warn("Can't track more than one temporary change") - return - } - const oldValue = state[name] - - commit('setOptionTemporarily', { name, value }) - - const confirm = () => { - dispatch('setOption', { name, value }) - useInterfaceStore().clearTemporaryChanges() - } - - const revert = () => { - commit('setOptionTemporarily', { name, value: oldValue }) - useInterfaceStore().clearTemporaryChanges() - } - - useInterfaceStore().setTemporaryChanges({ - confirm, - revert, - }) - }, - setThemeV2({ commit, dispatch }, { customTheme, customThemeSource }) { - commit('setOption', { name: 'theme', value: 'custom' }) - commit('setOption', { name: 'customTheme', value: customTheme }) - commit('setOption', { - name: 'customThemeSource', - value: customThemeSource, - }) - dispatch('setTheme', { themeData: customThemeSource, recompile: true }) - }, - setOption({ commit, dispatch, state }, { name, value }) { - const exceptions = new Set(['useStreamingApi']) - - if (exceptions.has(name)) { - switch (name) { - case 'useStreamingApi': { - const action = value ? 'enableMastoSockets' : 'disableMastoSockets' - - dispatch(action) - .then(() => { - commit('setOption', { name: 'useStreamingApi', value }) - }) - .catch((e) => { - console.error('Failed starting MastoAPI Streaming socket', e) - dispatch('disableMastoSockets') - dispatch('setOption', { name: 'useStreamingApi', value: false }) - }) - break - } - } - } else { - commit('setOption', { name, value }) - if (APPEARANCE_SETTINGS_KEYS.has(name)) { - applyStyleConfig(state) - } - if (name.startsWith('theme3hacks')) { - dispatch('applyTheme', { recompile: true }) - } - switch (name) { - case 'theme': - if (value === 'custom') break - dispatch('setTheme', { - themeName: value, - recompile: true, - saveData: true, - }) - break - case 'themeDebug': { - dispatch('setTheme', { recompile: true }) - break - } - case 'interfaceLanguage': - messages.setLanguage(useI18nStore().i18n, value) - useEmojiStore().loadUnicodeEmojiData(value) - Cookies.set( - BACKEND_LANGUAGE_COOKIE_NAME, - localeService.internalToBackendLocaleMulti(value), - ) - break - case 'thirdColumnMode': - useInterfaceStore().setLayoutWidth(undefined) - break - } - } - }, - }, -} - -export default config diff --git a/src/modules/index.js b/src/modules/index.js index 15e57f716..76bdbf7f3 100644 --- a/src/modules/index.js +++ b/src/modules/index.js @@ -1,7 +1,6 @@ import adminSettings from './adminSettings.js' import api from './api.js' import chats from './chats.js' -import config from './config.js' import drafts from './drafts.js' import notifications from './notifications.js' import profileConfig from './profileConfig.js' @@ -13,7 +12,6 @@ export default { notifications, users, api, - config, profileConfig, adminSettings, drafts, From 1be0debc63db6eec85ff3cf84de65349d157b741 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Tue, 24 Mar 2026 20:31:05 +0200 Subject: [PATCH 3/4] fix --- src/stores/local_config.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/stores/local_config.js b/src/stores/local_config.js index b81a55408..6d52f94d2 100644 --- a/src/stores/local_config.js +++ b/src/stores/local_config.js @@ -6,7 +6,8 @@ import { useInstanceStore } from 'src/stores/instance' import { LOCAL_DEFAULT_CONFIG, - LOCAL_DEFAULT_CONFIG_DEFINITIONS + LOCAL_DEFAULT_CONFIG_DEFINITIONS, + validateSetting, } from 'src/modules/default_config_state' export const defaultState = { @@ -55,8 +56,8 @@ export const useLocalConfigStore = defineStore('local_config', { persist: { afterLoad(state) { return { - prefsStorage: state.prefsStorage ?? { ...configDefaultState }, - tempStorage: { ...configDefaultState }, + prefsStorage: state.prefsStorage ?? { ...LOCAL_DEFAULT_CONFIG }, + tempStorage: { ...LOCAL_DEFAULT_CONFIG }, } }, }, From 4e235562aa0e398e03edd0810d66034cedd25c57 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Tue, 24 Mar 2026 21:42:22 +0200 Subject: [PATCH 4/4] massive rename and separation merged config into its own "store" --- src/App.js | 26 +++---- src/boot/after_store.js | 4 +- src/components/about/about.js | 4 +- .../account_actions/account_actions.js | 6 +- src/components/attachment/attachment.js | 10 +-- src/components/chat_message/chat_message.js | 4 +- src/components/confirm_modal/mute_confirm.js | 4 +- src/components/conversation/conversation.js | 14 ++-- src/components/desktop_nav/desktop_nav.js | 4 +- src/components/dialog_modal/dialog_modal.js | 4 +- src/components/draft/draft.js | 4 +- src/components/draft_closer/draft_closer.js | 6 +- src/components/emoji_input/emoji_input.js | 8 +-- src/components/emoji_picker/emoji_picker.js | 6 +- .../extra_notifications.js | 4 +- src/components/follow_button/follow_button.js | 4 +- .../follow_request_card.js | 4 +- src/components/link-preview/link-preview.js | 4 +- src/components/media_upload/media_upload.js | 6 +- src/components/mention_link/mention_link.js | 4 +- src/components/mentions_line/mentions_line.js | 4 +- src/components/mobile_nav/mobile_nav.js | 17 +++-- .../mobile_post_status_button.js | 6 +- src/components/notification/notification.js | 4 +- .../notifications/notification_filters.vue | 3 +- src/components/notifications/notifications.js | 18 ++--- src/components/poll/poll.js | 4 +- .../post_status_form/post_status_form.js | 11 +-- .../quick_filter_settings.js | 3 +- .../quick_view_settings.js | 3 +- .../remove_follower_button.js | 4 +- .../settings_modal/helpers/list_setting.js | 4 +- .../settings_modal/helpers/setting.js | 9 +-- .../helpers/shared_computed_object.js | 6 +- .../settings_modal_user_content.js | 4 +- .../settings_modal/tabs/composing_tab.js | 3 +- .../settings_modal/tabs/filtering_tab.js | 9 +-- .../settings_modal/tabs/general_tab.js | 3 +- .../settings_modal/tabs/layout_tab.js | 6 +- .../tabs/old_theme_tab/old_theme_tab.js | 4 +- src/components/side_drawer/side_drawer.js | 4 +- src/components/status/status.js | 3 +- .../buttons_definitions.js | 6 +- src/components/status_body/status_body.js | 4 +- .../status_content/status_content.js | 6 +- src/components/still-image/still-image.js | 4 +- src/components/timeago/timeago.js | 8 +-- src/components/timeline/timeline.js | 9 +-- .../update_notification.js | 3 +- src/components/user_card/user_card.js | 8 +-- src/components/user_popover/user_popover.js | 4 +- .../user_timed_filter_modal.js | 11 +-- .../video_attachment/video_attachment.js | 6 +- src/lib/push_notifications_plugin.js | 6 +- src/lib/style.js | 4 +- src/modules/config_declaration.js | 4 +- src/modules/default_config_state.js | 1 + src/modules/notifications.js | 3 +- src/modules/users.js | 7 +- .../notifications_fetcher.service.js | 4 +- src/services/style_setter/style_setter.js | 3 +- .../timeline_fetcher.service.js | 5 +- src/stores/interface.js | 9 +-- src/stores/merged_config.js | 69 +++++++++++++++++++ src/stores/sync_config.js | 47 +------------ 65 files changed, 272 insertions(+), 221 deletions(-) create mode 100644 src/stores/merged_config.js diff --git a/src/App.js b/src/App.js index 5b1403828..44ff6aee3 100644 --- a/src/App.js +++ b/src/App.js @@ -26,8 +26,8 @@ import { useI18nStore } from 'src/stores/i18n.js' import { useInstanceStore } from 'src/stores/instance.js' import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js' import { useInterfaceStore } from 'src/stores/interface.js' +import { useMergedConfigStore } from 'src/stores/merged_config.js' import { useShoutStore } from 'src/stores/shout.js' -import { useSyncConfigStore } from 'src/stores/sync_config.js' import messages from 'src/i18n/messages' import localeService from 'src/services/locale/locale.service.js' @@ -80,7 +80,7 @@ export default { }, created() { // Load the locale from the storage - const value = useSyncConfigStore().mergedConfig.interfaceLanguage + const value = useMergedConfigStore().mergedConfig.interfaceLanguage useI18nStore().setLanguage(value) useEmojiStore().loadUnicodeEmojiData(value) @@ -132,7 +132,7 @@ export default { ] }, navClasses() { - const { navbarColumnStretch } = useSyncConfigStore().mergedConfig + const { navbarColumnStretch } = useMergedConfigStore().mergedConfig return [ '-' + this.layoutType, ...(navbarColumnStretch ? ['-column-stretch'] : []), @@ -145,7 +145,7 @@ export default { return this.currentUser.background_image }, instanceBackground() { - return useSyncConfigStore().mergedConfig.hideInstanceWallpaper + return useMergedConfigStore().mergedConfig.hideInstanceWallpaper ? null : this.instanceBackgroundUrl }, @@ -172,19 +172,21 @@ export default { if (this.isChats) return false if (this.isListEdit) return false return ( - useSyncConfigStore().mergedConfig.alwaysShowNewPostButton || + useMergedConfigStore().mergedConfig.alwaysShowNewPostButton || this.layoutType === 'mobile' ) }, shoutboxPosition() { - return useSyncConfigStore().mergedConfig.alwaysShowNewPostButton || false + return ( + useMergedConfigStore().mergedConfig.alwaysShowNewPostButton || false + ) }, hideShoutbox() { - return useSyncConfigStore().mergedConfig.hideShoutbox + return useMergedConfigStore().mergedConfig.hideShoutbox }, reverseLayout() { const { thirdColumnMode, sidebarRight: reverseSetting } = - useSyncConfigStore().mergedConfig + useMergedConfigStore().mergedConfig if (this.layoutType !== 'wide') { return reverseSetting } else { @@ -194,10 +196,10 @@ export default { } }, noSticky() { - return useSyncConfigStore().mergedConfig.disableStickyHeaders + return useMergedConfigStore().mergedConfig.disableStickyHeaders }, showScrollbars() { - return useSyncConfigStore().mergedConfig.showScrollbars + return useMergedConfigStore().mergedConfig.showScrollbars }, scrollParent() { return window /* this.$refs.appContentRef */ @@ -205,10 +207,10 @@ export default { showInstanceSpecificPanel() { return ( this.instanceSpecificPanelPresent && - !useSyncConfigStore().mergedConfig.hideISP + !useMergedConfigStore().mergedConfig.hideISP ) }, - ...mapState(useSyncConfigStore, ['mergedConfig']), + ...mapState(useMergedConfigStore, ['mergedConfig']), ...mapState(useInterfaceStore, [ 'themeApplied', 'styleDataUsed', diff --git a/src/boot/after_store.js b/src/boot/after_store.js index 7d1995347..a345e2ae5 100644 --- a/src/boot/after_store.js +++ b/src/boot/after_store.js @@ -33,6 +33,7 @@ import { useInstanceStore } from 'src/stores/instance.js' import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js' import { useInterfaceStore } from 'src/stores/interface.js' import { useLocalConfigStore } from 'src/stores/local_config.js' +import { useMergedConfigStore } from 'src/stores/merged_config.js' import { useOAuthStore } from 'src/stores/oauth' import { useSyncConfigStore } from 'src/stores/sync_config.js' import { useUserHighlightStore } from 'src/stores/user_highlight.js' @@ -527,6 +528,7 @@ const afterStoreSetup = async ({ pinia, store, storageError, i18n }) => { useInterfaceStore().setLayoutWidth(windowWidth()) useInterfaceStore().setLayoutHeight(windowHeight()) window.syncConfig = useSyncConfigStore() + window.mergedConfig = useMergedConfigStore() window.localConfig = useLocalConfigStore() window.highlightConfig = useUserHighlightStore() @@ -554,7 +556,7 @@ const afterStoreSetup = async ({ pinia, store, storageError, i18n }) => { return Promise.reject(e) } - applyStyleConfig(useSyncConfigStore().mergedConfig, i18n.global) + applyStyleConfig(useMergedConfigStore().mergedConfig, i18n.global) // Now we can try getting the server settings and logging in // Most of these are preloaded into the index.html so blocking is minimized diff --git a/src/components/about/about.js b/src/components/about/about.js index ab1ace320..404843e8b 100644 --- a/src/components/about/about.js +++ b/src/components/about/about.js @@ -5,7 +5,7 @@ import StaffPanel from '../staff_panel/staff_panel.vue' import TermsOfServicePanel from '../terms_of_service_panel/terms_of_service_panel.vue' import { useInstanceStore } from 'src/stores/instance.js' -import { useSyncConfigStore } from 'src/stores/sync_config.js' +import { useMergedConfigStore } from 'src/stores/merged_config.js' const About = { components: { @@ -22,7 +22,7 @@ const About = { showInstanceSpecificPanel() { return ( useInstanceStore().instanceIdentity.showInstanceSpecificPanel && - !useSyncConfigStore().mergedConfig.hideISP && + !useMergedConfigStore().mergedConfig.hideISP && useInstanceStore().instanceIdentity.instanceSpecificPanelContent ) }, diff --git a/src/components/account_actions/account_actions.js b/src/components/account_actions/account_actions.js index 38f585675..f204adbde 100644 --- a/src/components/account_actions/account_actions.js +++ b/src/components/account_actions/account_actions.js @@ -7,8 +7,8 @@ import Popover from '../popover/popover.vue' import ProgressButton from '../progress_button/progress_button.vue' import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js' +import { useMergedConfigStore } from 'src/stores/merged_config.js' import { useReportsStore } from 'src/stores/reports' -import { useSyncConfigStore } from 'src/stores/sync_config.js' import { library } from '@fortawesome/fontawesome-svg-core' import { faEllipsisV } from '@fortawesome/free-solid-svg-icons' @@ -90,10 +90,10 @@ const AccountActions = { }, computed: { shouldConfirmBlock() { - return useSyncConfigStore().mergedConfig.modalOnBlock + return useMergedConfigStore().mergedConfig.modalOnBlock }, shouldConfirmRemoveUserFromFollowers() { - return useSyncConfigStore().mergedConfig.modalOnRemoveUserFromFollowers + return useMergedConfigStore().mergedConfig.modalOnRemoveUserFromFollowers }, ...mapState(useInstanceCapabilitiesStore, [ 'blockExpiration', diff --git a/src/components/attachment/attachment.js b/src/components/attachment/attachment.js index 32d8426b8..fbe77a687 100644 --- a/src/components/attachment/attachment.js +++ b/src/components/attachment/attachment.js @@ -8,7 +8,7 @@ import VideoAttachment from '../video_attachment/video_attachment.vue' import { useInstanceStore } from 'src/stores/instance.js' import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js' import { useMediaViewerStore } from 'src/stores/media_viewer' -import { useSyncConfigStore } from 'src/stores/sync_config.js' +import { useMergedConfigStore } from 'src/stores/merged_config.js' import { library } from '@fortawesome/fontawesome-svg-core' import { @@ -58,8 +58,8 @@ const Attachment = { localDescription: this.description || this.attachment.description, nsfwImage: useInstanceStore().instanceIdentity.nsfwCensorImage || nsfwImage, - hideNsfwLocal: useSyncConfigStore().mergedConfig.hideNsfw, - preloadImage: useSyncConfigStore().mergedConfig.preloadImage, + hideNsfwLocal: useMergedConfigStore().mergedConfig.hideNsfw, + preloadImage: useMergedConfigStore().mergedConfig.preloadImage, loading: false, img: this.attachment.type === 'image' && document.createElement('img'), modalOpen: false, @@ -91,7 +91,7 @@ const Attachment = { return this.size === 'hide' }, useContainFit() { - return useSyncConfigStore().mergedConfig.useContainFit + return this.mergedConfig.useContainFit }, placeholderName() { if (this.attachment.description === '' || !this.attachment.description) { @@ -134,7 +134,7 @@ const Attachment = { videoTag() { return this.useModal ? 'button' : 'span' }, - ...mapState(useSyncConfigStore, ['mergedConfig']), + ...mapState(useMergedConfigStore, ['mergedConfig']), }, watch: { 'attachment.description'(newVal) { diff --git a/src/components/chat_message/chat_message.js b/src/components/chat_message/chat_message.js index b7267be2b..1675f9ddd 100644 --- a/src/components/chat_message/chat_message.js +++ b/src/components/chat_message/chat_message.js @@ -12,7 +12,7 @@ import UserAvatar from '../user_avatar/user_avatar.vue' import { useInstanceStore } from 'src/stores/instance.js' import { useInterfaceStore } from 'src/stores/interface' -import { useSyncConfigStore } from 'src/stores/sync_config.js' +import { useMergedConfigStore } from 'src/stores/merged_config.js' import { library } from '@fortawesome/fontawesome-svg-core' import { faEllipsisH, faTimes } from '@fortawesome/free-solid-svg-icons' @@ -86,7 +86,7 @@ const ChatMessage = { return { left: 50 } } }, - ...mapPiniaState(useSyncConfigStore, ['mergedConfig', 'findUser']), + ...mapPiniaState(useMergedConfigStore, ['mergedConfig', 'findUser']), }, data() { return { diff --git a/src/components/confirm_modal/mute_confirm.js b/src/components/confirm_modal/mute_confirm.js index 888aa017b..ffd3a3076 100644 --- a/src/components/confirm_modal/mute_confirm.js +++ b/src/components/confirm_modal/mute_confirm.js @@ -3,7 +3,7 @@ import { mapState } from 'pinia' import Select from 'src/components/select/select.vue' import ConfirmModal from './confirm_modal.vue' -import { useSyncConfigStore } from 'src/stores/sync_config.js' +import { useMergedConfigStore } from 'src/stores/merged_config.js' export default { props: ['type', 'user', 'status'], @@ -45,7 +45,7 @@ export default { } } }, - ...mapState(useSyncConfigStore, ['mergedConfig']), + ...mapState(useMergedConfigStore, ['mergedConfig']), }, methods: { optionallyPrompt() { diff --git a/src/components/conversation/conversation.js b/src/components/conversation/conversation.js index c3bd50444..b0d3a304c 100644 --- a/src/components/conversation/conversation.js +++ b/src/components/conversation/conversation.js @@ -9,7 +9,7 @@ import Status from '../status/status.vue' import ThreadTree from '../thread_tree/thread_tree.vue' import { useInterfaceStore } from 'src/stores/interface' -import { useSyncConfigStore } from 'src/stores/sync_config.js' +import { useMergedConfigStore } from 'src/stores/merged_config.js' import { library } from '@fortawesome/fontawesome-svg-core' import { @@ -82,7 +82,7 @@ const conversation = { // maxDepthInThread = max number of depths that is *visible* // since our depth starts with 0 and "showing" means "showing children" // there is a -2 here - const maxDepth = useSyncConfigStore().mergedConfig.maxDepthInThread - 2 + const maxDepth = this.mergedConfig.maxDepthInThread - 2 return maxDepth >= 1 ? maxDepth : 1 }, streamingEnabled() { @@ -92,22 +92,22 @@ const conversation = { ) }, displayStyle() { - return useSyncConfigStore().mergedConfig.conversationDisplay + return this.mergedConfig.conversationDisplay }, isTreeView() { return !this.isLinearView }, treeViewIsSimple() { - return !useSyncConfigStore().mergedConfig.conversationTreeAdvanced + return !this.mergedConfig.conversationTreeAdvanced }, isLinearView() { return this.displayStyle === 'linear' }, shouldFadeAncestors() { - return useSyncConfigStore().mergedConfig.conversationTreeFadeAncestors + return this.mergedConfig.conversationTreeFadeAncestors }, otherRepliesButtonPosition() { - return useSyncConfigStore().mergedConfig.conversationOtherRepliesButton + return this.mergedConfig.conversationOtherRepliesButton }, showOtherRepliesButtonBelowStatus() { return this.otherRepliesButtonPosition === 'below' @@ -393,7 +393,7 @@ const conversation = { maybeHighlight() { return this.isExpanded ? this.highlight : null }, - ...mapPiniaState(useSyncConfigStore, ['mergedConfig']), + ...mapPiniaState(useMergedConfigStore, ['mergedConfig']), ...mapState({ mastoUserSocketStatus: (state) => state.api.mastoUserSocketStatus, }), diff --git a/src/components/desktop_nav/desktop_nav.js b/src/components/desktop_nav/desktop_nav.js index 0871e0bd3..3382413a0 100644 --- a/src/components/desktop_nav/desktop_nav.js +++ b/src/components/desktop_nav/desktop_nav.js @@ -5,7 +5,7 @@ import ConfirmModal from '../confirm_modal/confirm_modal.vue' import { useInstanceStore } from 'src/stores/instance.js' import { useInterfaceStore } from 'src/stores/interface' -import { useSyncConfigStore } from 'src/stores/sync_config.js' +import { useMergedConfigStore } from 'src/stores/merged_config.js' import { library } from '@fortawesome/fontawesome-svg-core' import { @@ -97,7 +97,7 @@ export default { return this.$store.state.users.currentUser }, shouldConfirmLogout() { - return useSyncConfigStore().mergedConfig.modalOnLogout + return useMergedConfigStore().mergedConfig.modalOnLogout }, }, methods: { diff --git a/src/components/dialog_modal/dialog_modal.js b/src/components/dialog_modal/dialog_modal.js index e5c399086..083f7a21d 100644 --- a/src/components/dialog_modal/dialog_modal.js +++ b/src/components/dialog_modal/dialog_modal.js @@ -1,4 +1,4 @@ -import { useSyncConfigStore } from 'src/stores/sync_config.js' +import { useMergedConfigStore } from 'src/stores/merged_config.js' const DialogModal = { props: { @@ -15,7 +15,7 @@ const DialogModal = { }, computed: { mobileCenter() { - return useSyncConfigStore().mergedConfig.modalMobileCenter + return useMergedConfigStore().mergedConfig.modalMobileCenter }, }, } diff --git a/src/components/draft/draft.js b/src/components/draft/draft.js index 5ee85dff4..ffc299af6 100644 --- a/src/components/draft/draft.js +++ b/src/components/draft/draft.js @@ -6,7 +6,7 @@ import Gallery from 'src/components/gallery/gallery.vue' import PostStatusForm from 'src/components/post_status_form/post_status_form.vue' import StatusContent from 'src/components/status_content/status_content.vue' -import { useSyncConfigStore } from 'src/stores/sync_config.js' +import { useMergedConfigStore } from 'src/stores/merged_config.js' import { library } from '@fortawesome/fontawesome-svg-core' import { faPollH } from '@fortawesome/free-solid-svg-icons' @@ -59,7 +59,7 @@ const Draft = { : undefined }, localCollapseSubjectDefault() { - return useSyncConfigStore().mergedConfig.collapseMessageWithSubject + return useMergedConfigStore().mergedConfig.collapseMessageWithSubject }, nsfwClickthrough() { if (!this.draft.nsfw) { diff --git a/src/components/draft_closer/draft_closer.js b/src/components/draft_closer/draft_closer.js index 5f297bf4c..2648b40c5 100644 --- a/src/components/draft_closer/draft_closer.js +++ b/src/components/draft_closer/draft_closer.js @@ -1,6 +1,6 @@ import DialogModal from 'src/components/dialog_modal/dialog_modal.vue' -import { useSyncConfigStore } from 'src/stores/sync_config.js' +import { useMergedConfigStore } from 'src/stores/merged_config.js' const DraftCloser = { data() { @@ -14,10 +14,10 @@ const DraftCloser = { emits: ['save', 'discard'], computed: { action() { - if (useSyncConfigStore().mergedConfig.autoSaveDraft) { + if (useMergedConfigStore().mergedConfig.autoSaveDraft) { return 'save' } else { - return useSyncConfigStore().mergedConfig.unsavedPostAction + return useMergedConfigStore().mergedConfig.unsavedPostAction } }, shouldConfirm() { diff --git a/src/components/emoji_input/emoji_input.js b/src/components/emoji_input/emoji_input.js index ae46b0334..0146a3de8 100644 --- a/src/components/emoji_input/emoji_input.js +++ b/src/components/emoji_input/emoji_input.js @@ -9,7 +9,7 @@ import genRandomSeed from '../../services/random_seed/random_seed.service.js' import EmojiPicker from '../emoji_picker/emoji_picker.vue' import UnicodeDomainIndicator from '../unicode_domain_indicator/unicode_domain_indicator.vue' -import { useSyncConfigStore } from 'src/stores/sync_config.js' +import { useMergedConfigStore } from 'src/stores/merged_config.js' import { library } from '@fortawesome/fontawesome-svg-core' import { faSmileBeam } from '@fortawesome/free-regular-svg-icons' @@ -133,10 +133,10 @@ const EmojiInput = { }, computed: { padEmoji() { - return useSyncConfigStore().mergedConfig.padEmoji + return useMergedConfigStore().mergedConfig.padEmoji }, defaultCandidateIndex() { - return useSyncConfigStore().mergedConfig.autocompleteSelect ? 0 : -1 + return useMergedConfigStore().mergedConfig.autocompleteSelect ? 0 : -1 }, preText() { return this.modelValue.slice(0, this.caret) @@ -165,7 +165,7 @@ const EmojiInput = { }, languages() { return ensureFinalFallback( - useSyncConfigStore().mergedConfig.interfaceLanguage, + useMergedConfigStore().mergedConfig.interfaceLanguage, ) }, maybeLocalizedEmojiNamesAndKeywords() { diff --git a/src/components/emoji_picker/emoji_picker.js b/src/components/emoji_picker/emoji_picker.js index 1e4594b2c..1d19987ac 100644 --- a/src/components/emoji_picker/emoji_picker.js +++ b/src/components/emoji_picker/emoji_picker.js @@ -8,7 +8,7 @@ import StillImage from '../still-image/still-image.vue' import { useEmojiStore } from 'src/stores/emoji.js' import { useInstanceStore } from 'src/stores/instance.js' -import { useSyncConfigStore } from 'src/stores/sync_config.js' +import { useMergedConfigStore } from 'src/stores/merged_config.js' import { library } from '@fortawesome/fontawesome-svg-core' import { @@ -341,7 +341,7 @@ const EmojiPicker = { this.$nextTick(() => { this.updateEmojiSize() }) - return useSyncConfigStore().mergedConfig.fontSize + return useMergedConfigStore().mergedConfig.fontSize }, emojiHeight() { return this.emojiSize @@ -406,7 +406,7 @@ const EmojiPicker = { }, languages() { return ensureFinalFallback( - useSyncConfigStore().mergedConfig.interfaceLanguage, + useMergedConfigStore().mergedConfig.interfaceLanguage, ) }, maybeLocalizedEmojiName() { diff --git a/src/components/extra_notifications/extra_notifications.js b/src/components/extra_notifications/extra_notifications.js index 67be59144..ec5ceebe1 100644 --- a/src/components/extra_notifications/extra_notifications.js +++ b/src/components/extra_notifications/extra_notifications.js @@ -3,7 +3,7 @@ import { mapGetters } from 'vuex' import { useAnnouncementsStore } from 'src/stores/announcements.js' import { useInterfaceStore } from 'src/stores/interface.js' -import { useSyncConfigStore } from 'src/stores/sync_config.js' +import { useMergedConfigStore } from 'src/stores/merged_config.js' import { library } from '@fortawesome/fontawesome-svg-core' import { @@ -56,7 +56,7 @@ const ExtraNotifications = { ...mapPiniaState(useAnnouncementsStore, { unreadAnnouncementCount: 'unreadAnnouncementCount', }), - ...mapPiniaState(useSyncConfigStore, ['mergedConfig']), + ...mapPiniaState(useMergedConfigStore, ['mergedConfig']), }, methods: { openNotificationSettings() { diff --git a/src/components/follow_button/follow_button.js b/src/components/follow_button/follow_button.js index 539749bc8..644f95f85 100644 --- a/src/components/follow_button/follow_button.js +++ b/src/components/follow_button/follow_button.js @@ -4,7 +4,7 @@ import { } from '../../services/follow_manipulate/follow_manipulate' import ConfirmModal from '../confirm_modal/confirm_modal.vue' -import { useSyncConfigStore } from 'src/stores/sync_config.js' +import { useMergedConfigStore } from 'src/stores/merged_config.js' export default { props: ['relationship', 'user', 'labelFollowing', 'buttonClass'], components: { @@ -18,7 +18,7 @@ export default { }, computed: { shouldConfirmUnfollow() { - return useSyncConfigStore().mergedConfig.modalOnUnfollow + return useMergedConfigStore().mergedConfig.modalOnUnfollow }, isPressed() { return this.inProgress || this.relationship.following diff --git a/src/components/follow_request_card/follow_request_card.js b/src/components/follow_request_card/follow_request_card.js index b7959a2d3..a6ffcd28b 100644 --- a/src/components/follow_request_card/follow_request_card.js +++ b/src/components/follow_request_card/follow_request_card.js @@ -2,7 +2,7 @@ import { notificationsFromStore } from '../../services/notification_utils/notifi import BasicUserCard from '../basic_user_card/basic_user_card.vue' import ConfirmModal from '../confirm_modal/confirm_modal.vue' -import { useSyncConfigStore } from 'src/stores/sync_config.js' +import { useMergedConfigStore } from 'src/stores/merged_config.js' const FollowRequestCard = { props: ['user'], @@ -78,7 +78,7 @@ const FollowRequestCard = { }, computed: { mergedConfig() { - return useSyncConfigStore().mergedConfig + return useMergedConfigStore().mergedConfig }, shouldConfirmApprove() { return this.mergedConfig.modalOnApproveFollow diff --git a/src/components/link-preview/link-preview.js b/src/components/link-preview/link-preview.js index a465cc213..808030fbb 100644 --- a/src/components/link-preview/link-preview.js +++ b/src/components/link-preview/link-preview.js @@ -1,6 +1,6 @@ import { mapState } from 'pinia' -import { useSyncConfigStore } from 'src/stores/sync_config.js' +import { useMergedConfigStore } from 'src/stores/merged_config.js' const LinkPreview = { name: 'LinkPreview', @@ -26,7 +26,7 @@ const LinkPreview = { hideNsfwConfig() { return this.mergedConfig.hideNsfw }, - ...mapState(useSyncConfigStore, ['mergedConfig']), + ...mapState(useMergedConfigStore, ['mergedConfig']), }, created() { if (this.useImage) { diff --git a/src/components/media_upload/media_upload.js b/src/components/media_upload/media_upload.js index be62142c6..2cb6a96e6 100644 --- a/src/components/media_upload/media_upload.js +++ b/src/components/media_upload/media_upload.js @@ -2,7 +2,7 @@ import fileSizeFormatService from '../../services/file_size_format/file_size_for import statusPosterService from '../../services/status_poster/status_poster.service.js' import { useInstanceStore } from 'src/stores/instance.js' -import { useSyncConfigStore } from 'src/stores/sync_config.js' +import { useMergedConfigStore } from 'src/stores/merged_config.js' import { library } from '@fortawesome/fontawesome-svg-core' import { faCircleNotch, faUpload } from '@fortawesome/free-solid-svg-icons' @@ -34,7 +34,7 @@ const mediaUpload = { } // Skip if image compression is disabled - if (!useSyncConfigStore().mergedConfig.imageCompression) { + if (!useMergedConfigStore().mergedConfig.imageCompression) { return file } @@ -79,7 +79,7 @@ const mediaUpload = { // Convert to WebP if supported and alwaysUseJpeg is false, otherwise JPEG const type = - !useSyncConfigStore().mergedConfig.alwaysUseJpeg && supportsWebP + !useMergedConfigStore().mergedConfig.alwaysUseJpeg && supportsWebP ? 'image/webp' : 'image/jpeg' const extension = type === 'image/webp' ? '.webp' : '.jpg' diff --git a/src/components/mention_link/mention_link.js b/src/components/mention_link/mention_link.js index 7d0113d35..5edc89516 100644 --- a/src/components/mention_link/mention_link.js +++ b/src/components/mention_link/mention_link.js @@ -9,7 +9,7 @@ import { import UnicodeDomainIndicator from '../unicode_domain_indicator/unicode_domain_indicator.vue' import UserAvatar from '../user_avatar/user_avatar.vue' -import { useSyncConfigStore } from 'src/stores/sync_config.js' +import { useMergedConfigStore } from 'src/stores/merged_config.js' import { useUserHighlightStore } from 'src/stores/user_highlight.js' import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator' @@ -160,7 +160,7 @@ const MentionLink = { shouldFadeDomain() { return this.mergedConfig.mentionLinkFadeDomain }, - ...mapPiniaState(useSyncConfigStore, ['mergedConfig']), + ...mapPiniaState(useMergedConfigStore, ['mergedConfig']), ...mapPiniaState(useUserHighlightStore, ['highlight']), ...mapState({ currentUser: (state) => state.users.currentUser, diff --git a/src/components/mentions_line/mentions_line.js b/src/components/mentions_line/mentions_line.js index bd3882866..88d2b3257 100644 --- a/src/components/mentions_line/mentions_line.js +++ b/src/components/mentions_line/mentions_line.js @@ -2,7 +2,7 @@ import { mapState } from 'pinia' import MentionLink from 'src/components/mention_link/mention_link.vue' -import { useSyncConfigStore } from 'src/stores/sync_config.js' +import { useMergedConfigStore } from 'src/stores/merged_config.js' export const MENTIONS_LIMIT = 5 @@ -28,7 +28,7 @@ const MentionsLine = { manyMentions() { return this.extraMentions.length > 0 }, - ...mapState(useSyncConfigStore, ['mergedConfig']), + ...mapState(useMergedConfigStore, ['mergedConfig']), }, methods: { toggleShowMore() { diff --git a/src/components/mobile_nav/mobile_nav.js b/src/components/mobile_nav/mobile_nav.js index d3301c325..f47cef893 100644 --- a/src/components/mobile_nav/mobile_nav.js +++ b/src/components/mobile_nav/mobile_nav.js @@ -13,7 +13,7 @@ import SideDrawer from '../side_drawer/side_drawer.vue' import { useAnnouncementsStore } from 'src/stores/announcements.js' import { useInstanceStore } from 'src/stores/instance.js' -import { useSyncConfigStore } from 'src/stores/sync_config.js' +import { useMergedConfigStore } from 'src/stores/merged_config.js' import { library } from '@fortawesome/fontawesome-svg-core' import { @@ -54,14 +54,17 @@ const MobileNav = { unseenNotifications() { return unseenNotificationsFromStore( this.$store, - useSyncConfigStore().mergedConfig.notificationVisibility, - useSyncConfigStore().mergedConfig.ignoreInactionableSeen, + useMergedConfigStore().mergedConfig.notificationVisibility, + useMergedConfigStore().mergedConfig.ignoreInactionableSeen, ) }, unseenNotificationsCount() { return ( this.unseenNotifications.length + - countExtraNotifications(this.$store, useSyncConfigStore().mergedConfig) + countExtraNotifications( + this.$store, + useMergedConfigStore().mergedConfig, + ) ) }, unseenCount() { @@ -80,15 +83,15 @@ const MobileNav = { return this.$route.name === 'chat' }, ...mapState(useAnnouncementsStore, ['unreadAnnouncementCount']), - ...mapState(useSyncConfigStore, { + ...mapState(useMergedConfigStore, { pinnedItems: (store) => new Set(store.prefsStorage.collections.pinnedNavItems).has('chats'), }), shouldConfirmLogout() { - return useSyncConfigStore().mergedConfig.modalOnLogout + return useMergedConfigStore().mergedConfig.modalOnLogout }, closingDrawerMarksAsSeen() { - return useSyncConfigStore().mergedConfig.closingDrawerMarksAsSeen + return useMergedConfigStore().mergedConfig.closingDrawerMarksAsSeen }, ...mapGetters(['unreadChatCount']), }, diff --git a/src/components/mobile_post_status_button/mobile_post_status_button.js b/src/components/mobile_post_status_button/mobile_post_status_button.js index 594cd6688..4969352f6 100644 --- a/src/components/mobile_post_status_button/mobile_post_status_button.js +++ b/src/components/mobile_post_status_button/mobile_post_status_button.js @@ -1,7 +1,7 @@ import { debounce } from 'lodash' +import { useMergedConfigStore } from 'src/stores/merged_config.js' import { usePostStatusStore } from 'src/stores/post_status.js' -import { useSyncConfigStore } from 'src/stores/sync_config.js' import { library } from '@fortawesome/fontawesome-svg-core' import { faPen } from '@fortawesome/free-solid-svg-icons' @@ -46,10 +46,10 @@ const MobilePostStatusButton = { ) }, isPersistent() { - return !!useSyncConfigStore().mergedConfig.alwaysShowNewPostButton + return !!useMergedConfigStore().mergedConfig.alwaysShowNewPostButton }, autohideFloatingPostButton() { - return !!useSyncConfigStore().mergedConfig.autohideFloatingPostButton + return !!useMergedConfigStore().mergedConfig.autohideFloatingPostButton }, }, watch: { diff --git a/src/components/notification/notification.js b/src/components/notification/notification.js index 152200790..5f612cb9c 100644 --- a/src/components/notification/notification.js +++ b/src/components/notification/notification.js @@ -17,7 +17,7 @@ import UserLink from '../user_link/user_link.vue' import UserPopover from '../user_popover/user_popover.vue' import { useInstanceStore } from 'src/stores/instance.js' -import { useSyncConfigStore } from 'src/stores/sync_config.js' +import { useMergedConfigStore } from 'src/stores/merged_config.js' import { useUserHighlightStore } from 'src/stores/user_highlight.js' import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator' @@ -210,7 +210,7 @@ const Notification = { return isStatusNotification(this.notification.type) }, mergedConfig() { - return useSyncConfigStore().mergedConfig + return useMergedConfigStore().mergedConfig }, shouldConfirmApprove() { return this.mergedConfig.modalOnApproveFollow diff --git a/src/components/notifications/notification_filters.vue b/src/components/notifications/notification_filters.vue index a29e6bb85..9232930df 100644 --- a/src/components/notifications/notification_filters.vue +++ b/src/components/notifications/notification_filters.vue @@ -108,6 +108,7 @@