From 29e71c8a26080568d2fc0a78d9577d275a4b8bbe Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Fri, 13 Feb 2026 13:59:00 +0200 Subject: [PATCH 001/167] sss -> sc --- src/components/mobile_nav/mobile_nav.js | 8 +- src/components/nav_panel/nav_panel.js | 14 +-- src/components/navigation/navigation_entry.js | 10 +- src/components/navigation/navigation_pins.js | 4 +- .../settings_modal/tabs/clutter_tab.js | 18 +-- .../settings_modal/tabs/filtering_tab.js | 22 ++-- src/components/status/status.js | 6 +- .../status_action_buttons.js | 12 +- .../update_notification.js | 16 +-- src/modules/notifications.js | 6 +- src/modules/users.js | 21 ++-- .../{serverSideStorage.js => sync_config.js} | 114 ++++++++++++------ .../sync_config.spec.js} | 81 +++++++++---- 13 files changed, 204 insertions(+), 128 deletions(-) rename src/stores/{serverSideStorage.js => sync_config.js} (87%) rename test/unit/specs/{modules/serverSideStorage.spec.js => stores/sync_config.spec.js} (87%) diff --git a/src/components/mobile_nav/mobile_nav.js b/src/components/mobile_nav/mobile_nav.js index c5b1d66f6..6242344ce 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 { useServerSideStorageStore } from 'src/stores/serverSideStorage.js' +import { useSyncConfigStore } from 'src/stores/sync_config.js' import { library } from '@fortawesome/fontawesome-svg-core' import { @@ -75,15 +75,15 @@ const MobileNav = { return this.$route.name === 'chat' }, ...mapState(useAnnouncementsStore, ['unreadAnnouncementCount']), - ...mapState(useServerSideStorageStore, { + ...mapState(useSyncConfigStore, { pinnedItems: (store) => new Set(store.prefsStorage.collections.pinnedNavItems).has('chats'), }), shouldConfirmLogout() { - return this.$store.getters.mergedConfig.modalOnLogout + return useSyncConfigStore().mergedConfig.modalOnLogout }, closingDrawerMarksAsSeen() { - return this.$store.getters.mergedConfig.closingDrawerMarksAsSeen + return useSyncConfigStore().mergedConfig.closingDrawerMarksAsSeen }, ...mapGetters(['unreadChatCount']), }, diff --git a/src/components/nav_panel/nav_panel.js b/src/components/nav_panel/nav_panel.js index 8aff31f02..9e5901f24 100644 --- a/src/components/nav_panel/nav_panel.js +++ b/src/components/nav_panel/nav_panel.js @@ -12,7 +12,7 @@ import NavigationPins from 'src/components/navigation/navigation_pins.vue' import { useAnnouncementsStore } from 'src/stores/announcements' import { useInstanceStore } from 'src/stores/instance.js' import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js' -import { useServerSideStorageStore } from 'src/stores/serverSideStorage' +import { useSyncConfigStore } from 'src/stores/sync_config.js' import { library } from '@fortawesome/fontawesome-svg-core' import { @@ -84,28 +84,28 @@ const NavPanel = { this.editMode = !this.editMode }, toggleCollapse() { - useServerSideStorageStore().setPreference({ + useSyncConfigStore().setPreference({ path: 'simple.collapseNav', value: !this.collapsed, }) - useServerSideStorageStore().pushServerSideStorage() + useSyncConfigStore().pushSyncConfig() }, isPinned(item) { return this.pinnedItems.has(item) }, togglePin(item) { if (this.isPinned(item)) { - useServerSideStorageStore().removeCollectionPreference({ + useSyncConfigStore().removeCollectionPreference({ path: 'collections.pinnedNavItems', value: item, }) } else { - useServerSideStorageStore().addCollectionPreference({ + useSyncConfigStore().addCollectionPreference({ path: 'collections.pinnedNavItems', value: item, }) } - useServerSideStorageStore().pushServerSideStorage() + useSyncConfigStore().pushSyncConfig() }, }, computed: { @@ -122,7 +122,7 @@ const NavPanel = { ...mapPiniaState(useInstanceStore, { privateMode: (store) => store.private, }), - ...mapPiniaState(useServerSideStorageStore, { + ...mapPiniaState(useSyncConfigStore, { collapsed: (store) => store.prefsStorage.simple.collapseNav, pinnedItems: (store) => new Set(store.prefsStorage.collections.pinnedNavItems), diff --git a/src/components/navigation/navigation_entry.js b/src/components/navigation/navigation_entry.js index 3384534be..7a43000ce 100644 --- a/src/components/navigation/navigation_entry.js +++ b/src/components/navigation/navigation_entry.js @@ -5,7 +5,7 @@ import { routeTo } from 'src/components/navigation/navigation.js' import OptionalRouterLink from 'src/components/optional_router_link/optional_router_link.vue' import { useAnnouncementsStore } from 'src/stores/announcements.js' -import { useServerSideStorageStore } from 'src/stores/serverSideStorage.js' +import { useSyncConfigStore } from 'src/stores/sync_config.js' import { library } from '@fortawesome/fontawesome-svg-core' import { faThumbtack } from '@fortawesome/free-solid-svg-icons' @@ -23,17 +23,17 @@ const NavigationEntry = { }, togglePin(value) { if (this.isPinned(value)) { - useServerSideStorageStore().removeCollectionPreference({ + useSyncConfigStore().removeCollectionPreference({ path: 'collections.pinnedNavItems', value, }) } else { - useServerSideStorageStore().addCollectionPreference({ + useSyncConfigStore().addCollectionPreference({ path: 'collections.pinnedNavItems', value, }) } - useServerSideStorageStore().pushServerSideStorage() + useSyncConfigStore().pushSyncConfig() }, }, computed: { @@ -47,7 +47,7 @@ const NavigationEntry = { ...mapState({ currentUser: (state) => state.users.currentUser, }), - ...mapPiniaState(useServerSideStorageStore, { + ...mapPiniaState(useSyncConfigStore, { pinnedItems: (store) => new Set(store.prefsStorage.collections.pinnedNavItems), }), diff --git a/src/components/navigation/navigation_pins.js b/src/components/navigation/navigation_pins.js index 698bf5d59..85f6fafee 100644 --- a/src/components/navigation/navigation_pins.js +++ b/src/components/navigation/navigation_pins.js @@ -18,7 +18,7 @@ import { useBookmarkFoldersStore } from 'src/stores/bookmark_folders' import { useInstanceStore } from 'src/stores/instance.js' import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js' import { useListsStore } from 'src/stores/lists' -import { useServerSideStorageStore } from 'src/stores/serverSideStorage' +import { useSyncConfigStore } from 'src/stores/sync_config.js' import { library } from '@fortawesome/fontawesome-svg-core' import { @@ -70,7 +70,7 @@ const NavPanel = { ...mapPiniaState(useBookmarkFoldersStore, { bookmarks: getBookmarkFolderEntries, }), - ...mapPiniaState(useServerSideStorageStore, { + ...mapPiniaState(useSyncConfigStore, { pinnedItems: (store) => new Set(store.prefsStorage.collections.pinnedNavItems), }), diff --git a/src/components/settings_modal/tabs/clutter_tab.js b/src/components/settings_modal/tabs/clutter_tab.js index 23e069d21..f81ad7040 100644 --- a/src/components/settings_modal/tabs/clutter_tab.js +++ b/src/components/settings_modal/tabs/clutter_tab.js @@ -12,7 +12,7 @@ import UnitSetting from '../helpers/unit_setting.vue' import { useInstanceStore } from 'src/stores/instance.js' import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js' -import { useServerSideStorageStore } from 'src/stores/serverSideStorage' +import { useSyncConfigStore } from 'src/stores/sync_config.js' const ClutterTab = { components: { @@ -33,7 +33,7 @@ const ClutterTab = { store.instanceIdentity.showInstanceSpecificPanel && store.instanceIdentity.instanceSpecificPanelContent, }), - ...mapState(useServerSideStorageStore, { + ...mapState(useSyncConfigStore, { muteFilters: (store) => Object.entries(store.prefsStorage.simple.muteFilters), muteFiltersObject: (store) => store.prefsStorage.simple.muteFilters, @@ -89,10 +89,10 @@ const ClutterTab = { }, }, methods: { - ...mapActions(useServerSideStorageStore, [ + ...mapActions(useSyncConfigStore, [ 'setPreference', 'unsetPreference', - 'pushServerSideStorage', + 'pushSyncConfig', ]), getDatetimeLocal(timestamp) { const date = new Date(timestamp) @@ -139,7 +139,7 @@ const ClutterTab = { filter.order = this.muteFilters.length + 2 this.muteFiltersDraftObject[newId] = filter this.setPreference({ path: 'simple.muteFilters.' + newId, value: filter }) - this.pushServerSideStorage() + this.pushSyncConfig() }, exportFilter(id) { this.exportedFilter = { ...this.muteFiltersDraftObject[id] } @@ -155,19 +155,19 @@ const ClutterTab = { this.muteFiltersDraftObject[newId] = filter this.setPreference({ path: 'simple.muteFilters.' + newId, value: filter }) - this.pushServerSideStorage() + this.pushSyncConfig() }, deleteFilter(id) { delete this.muteFiltersDraftObject[id] this.unsetPreference({ path: 'simple.muteFilters.' + id, value: null }) - this.pushServerSideStorage() + this.pushSyncConfig() }, purgeExpiredFilters() { this.muteFiltersExpired.forEach(([id]) => { delete this.muteFiltersDraftObject[id] this.unsetPreference({ path: 'simple.muteFilters.' + id, value: null }) }) - this.pushServerSideStorage() + this.pushSyncConfig() }, updateFilter(id, field, value) { const filter = { ...this.muteFiltersDraftObject[id] } @@ -193,7 +193,7 @@ const ClutterTab = { path: 'simple.muteFilters.' + id, value: this.muteFiltersDraftObject[id], }) - this.pushServerSideStorage() + this.pushSyncConfig() this.muteFiltersDraftDirty[id] = false }, }, diff --git a/src/components/settings_modal/tabs/filtering_tab.js b/src/components/settings_modal/tabs/filtering_tab.js index 68efbe762..a0544dd30 100644 --- a/src/components/settings_modal/tabs/filtering_tab.js +++ b/src/components/settings_modal/tabs/filtering_tab.js @@ -13,7 +13,7 @@ import UnitSetting from '../helpers/unit_setting.vue' import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js' import { useInterfaceStore } from 'src/stores/interface' -import { useServerSideStorageStore } from 'src/stores/serverSideStorage' +import { useSyncConfigStore } from 'src/stores/sync_config.js' import { newExporter, @@ -36,11 +36,11 @@ const FilteringTab = { label: this.$t(`user_card.mute_block_${mode}`), })), muteFiltersDraftObject: cloneDeep( - useServerSideStorageStore().prefsStorage.simple.muteFilters, + useSyncConfigStore().prefsStorage.simple.muteFilters, ), muteFiltersDraftDirty: Object.fromEntries( Object.entries( - useServerSideStorageStore().prefsStorage.simple.muteFilters, + useSyncConfigStore().prefsStorage.simple.muteFilters, ).map(([k]) => [k, false]), ), exportedFilter: null, @@ -92,7 +92,7 @@ const FilteringTab = { }, computed: { ...SharedComputedObject(), - ...mapState(useServerSideStorageStore, { + ...mapState(useSyncConfigStore, { muteFilters: (store) => Object.entries(store.prefsStorage.simple.muteFilters), muteFiltersObject: (store) => store.prefsStorage.simple.muteFilters, @@ -149,10 +149,10 @@ const FilteringTab = { }, }, methods: { - ...mapActions(useServerSideStorageStore, [ + ...mapActions(useSyncConfigStore, [ 'setPreference', 'unsetPreference', - 'pushServerSideStorage', + 'pushSyncConfig', ]), getDatetimeLocal(timestamp) { const date = new Date(timestamp) @@ -199,7 +199,7 @@ const FilteringTab = { filter.order = this.muteFilters.length + 2 this.muteFiltersDraftObject[newId] = filter this.setPreference({ path: 'simple.muteFilters.' + newId, value: filter }) - this.pushServerSideStorage() + this.pushSyncConfig() }, exportFilter(id) { this.exportedFilter = { ...this.muteFiltersDraftObject[id] } @@ -215,19 +215,19 @@ const FilteringTab = { this.muteFiltersDraftObject[newId] = filter this.setPreference({ path: 'simple.muteFilters.' + newId, value: filter }) - this.pushServerSideStorage() + this.pushSyncConfig() }, deleteFilter(id) { delete this.muteFiltersDraftObject[id] this.unsetPreference({ path: 'simple.muteFilters.' + id, value: null }) - this.pushServerSideStorage() + this.pushSyncConfig() }, purgeExpiredFilters() { this.muteFiltersExpired.forEach(([id]) => { delete this.muteFiltersDraftObject[id] this.unsetPreference({ path: 'simple.muteFilters.' + id, value: null }) }) - this.pushServerSideStorage() + this.pushSyncConfig() }, updateFilter(id, field, value) { const filter = { ...this.muteFiltersDraftObject[id] } @@ -253,7 +253,7 @@ const FilteringTab = { path: 'simple.muteFilters.' + id, value: this.muteFiltersDraftObject[id], }) - this.pushServerSideStorage() + this.pushSyncConfig() this.muteFiltersDraftDirty[id] = false }, }, diff --git a/src/components/status/status.js b/src/components/status/status.js index fbea34b4b..786864244 100644 --- a/src/components/status/status.js +++ b/src/components/status/status.js @@ -22,7 +22,7 @@ import UserPopover from '../user_popover/user_popover.vue' import { useInstanceStore } from 'src/stores/instance.js' import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js' -import { useServerSideStorageStore } from 'src/stores/serverSideStorage' +import { useSyncConfigStore } from 'src/stores/sync_config.js' import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator' @@ -259,9 +259,7 @@ const Status = { }, muteFilterHits() { return muteFilterHits( - Object.values( - useServerSideStorageStore().prefsStorage.simple.muteFilters, - ), + Object.values(useSyncConfigStore().prefsStorage.simple.muteFilters), this.status, ) }, diff --git a/src/components/status_action_buttons/status_action_buttons.js b/src/components/status_action_buttons/status_action_buttons.js index 2e08797c8..9938e6382 100644 --- a/src/components/status_action_buttons/status_action_buttons.js +++ b/src/components/status_action_buttons/status_action_buttons.js @@ -5,7 +5,7 @@ import Popover from 'src/components/popover/popover.vue' import ActionButtonContainer from './action_button_container.vue' import { BUTTONS } from './buttons_definitions.js' -import { useServerSideStorageStore } from 'src/stores/serverSideStorage.js' +import { useSyncConfigStore } from 'src/stores/sync_config.js' import genRandomSeed from 'src/services/random_seed/random_seed.service.js' @@ -36,7 +36,7 @@ const StatusActionButtons = { ActionButtonContainer, }, computed: { - ...mapState(useServerSideStorageStore, { + ...mapState(useSyncConfigStore, { pinnedItems: (store) => new Set(store.prefsStorage.collections.pinnedStatusActions), }), @@ -111,18 +111,18 @@ const StatusActionButtons = { return this.pinnedItems.has(button.name) }, unpin(button) { - useServerSideStorageStore().removeCollectionPreference({ + useSyncConfigStore().removeCollectionPreference({ path: 'collections.pinnedStatusActions', value: button.name, }) - useServerSideStorageStore().pushServerSideStorage() + useSyncConfigStore().pushSyncConfig() }, pin(button) { - useServerSideStorageStore().addCollectionPreference({ + useSyncConfigStore().addCollectionPreference({ path: 'collections.pinnedStatusActions', value: button.name, }) - useServerSideStorageStore().pushServerSideStorage() + useSyncConfigStore().pushSyncConfig() }, getComponent(button) { if (!this.$store.state.users.currentUser && button.anonLink) { diff --git a/src/components/update_notification/update_notification.js b/src/components/update_notification/update_notification.js index a8bc60676..78aaa79e8 100644 --- a/src/components/update_notification/update_notification.js +++ b/src/components/update_notification/update_notification.js @@ -1,7 +1,7 @@ import Modal from 'src/components/modal/modal.vue' import { useInstanceStore } from 'src/stores/instance.js' -import { useServerSideStorageStore } from 'src/stores/serverSideStorage.js' +import { useSyncConfigStore } from 'src/stores/sync_config.js' import pleromaTanFoxMask from 'src/assets/pleromatan_apology_fox_mask.png' import pleromaTanMask from 'src/assets/pleromatan_apology_mask.png' @@ -41,9 +41,9 @@ const UpdateNotification = { return ( !useInstanceStore().disableUpdateNotification && this.$store.state.users.currentUser && - useServerSideStorageStore().flagStorage.updateCounter < + useSyncConfigStore().flagStorage.updateCounter < CURRENT_UPDATE_COUNTER && - !useServerSideStorageStore().prefsStorage.simple.dontShowUpdateNotifs + !useSyncConfigStore().prefsStorage.simple.dontShowUpdateNotifs ) }, }, @@ -53,22 +53,22 @@ const UpdateNotification = { }, neverShowAgain() { this.toggleShow() - useServerSideStorageStore().setFlag({ + useSyncConfigStore().setFlag({ flag: 'updateCounter', value: CURRENT_UPDATE_COUNTER, }) - useServerSideStorageStore().setPreference({ + useSyncConfigStore().setPreference({ path: 'simple.dontShowUpdateNotifs', value: true, }) - useServerSideStorageStore().pushServerSideStorage() + useSyncConfigStore().pushSyncConfig() }, dismiss() { - useServerSideStorageStore().setFlag({ + useSyncConfigStore().setFlag({ flag: 'updateCounter', value: CURRENT_UPDATE_COUNTER, }) - useServerSideStorageStore().pushServerSideStorage() + useSyncConfigStore().pushSyncConfig() }, }, mounted() { diff --git a/src/modules/notifications.js b/src/modules/notifications.js index cb3d430db..9507177a2 100644 --- a/src/modules/notifications.js +++ b/src/modules/notifications.js @@ -10,7 +10,7 @@ import { } from '../services/notification_utils/notification_utils.js' import { useReportsStore } from 'src/stores/reports.js' -import { useServerSideStorageStore } from 'src/stores/serverSideStorage.js' +import { useSyncConfigStore } from 'src/stores/sync_config.js' const emptyNotifications = () => ({ desktopNotificationSilence: true, @@ -119,9 +119,7 @@ export const notifications = { maybeShowNotification( store, - Object.values( - useServerSideStorageStore().prefsStorage.simple.muteFilters, - ), + Object.values(useSyncConfigStore().prefsStorage.simple.muteFilters), notification, ) } else if (notification.seen) { diff --git a/src/modules/users.js b/src/modules/users.js index b0febcd3a..ea2a0ccb4 100644 --- a/src/modules/users.js +++ b/src/modules/users.js @@ -26,7 +26,7 @@ import { useInstanceStore } from 'src/stores/instance.js' import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js' import { useInterfaceStore } from 'src/stores/interface.js' import { useOAuthStore } from 'src/stores/oauth.js' -import { useServerSideStorageStore } from 'src/stores/serverSideStorage' +import { useSyncConfigStore } from 'src/stores/sync_config.js' import { declarations } from 'src/modules/config_declaration' @@ -682,7 +682,7 @@ const users = { useInterfaceStore().setLastTimeline('public-timeline') useInterfaceStore().setLayoutWidth(windowWidth()) useInterfaceStore().setLayoutHeight(windowHeight()) - store.commit('clearServerSideStorage') + //useSyncConfigStore().clearSyncConfig() }) }, loginUser(store, accessToken) { @@ -702,7 +702,7 @@ const users = { user.domainMutes = [] commit('setCurrentUser', user) - useServerSideStorageStore().setServerSideStorage(user) + useSyncConfigStore().setSyncConfig(user) commit('addNewUsers', [user]) useEmojiStore().fetchEmoji() @@ -723,17 +723,16 @@ const users = { /* // Reset wordfilter Object.keys( - useServerSideStorageStore().prefsStorage.simple.muteFilters + useSyncConfigStore().prefsStorage.simple.muteFilters ).forEach(key => { - useServerSideStorageStore().unsetPreference({ path: 'simple.muteFilters.' + key, value: null }) + useSyncConfigStore().unsetPreference({ path: 'simple.muteFilters.' + key, value: null }) }) // Reset flag to 0 to re-run migrations - useServerSideStorageStore().setFlag({ flag: 'configMigration', value: 0 }) + useSyncConfigStore().setFlag({ flag: 'configMigration', value: 0 }) /**/ - const { configMigration } = - useServerSideStorageStore().flagStorage + const { configMigration } = useSyncConfigStore().flagStorage declarations .filter((x) => { return ( @@ -744,12 +743,12 @@ const users = { }) .toSorted((a, b) => a.configMigration - b.configMigration) .forEach((value) => { - value.migration(useServerSideStorageStore(), store.rootState) - useServerSideStorageStore().setFlag({ + value.migration(useSyncConfigStore(), store.rootState) + useSyncConfigStore().setFlag({ flag: 'configMigration', value: value.migrationNum, }) - useServerSideStorageStore().pushServerSideStorage() + useSyncConfigStore().pushSyncConfig() }) if (user.token) { diff --git a/src/stores/serverSideStorage.js b/src/stores/sync_config.js similarity index 87% rename from src/stores/serverSideStorage.js rename to src/stores/sync_config.js index 99f5045f7..009ccbce4 100644 --- a/src/stores/serverSideStorage.js +++ b/src/stores/sync_config.js @@ -17,7 +17,11 @@ import { toRaw } from 'vue' import { CURRENT_UPDATE_COUNTER } from 'src/components/update_notification/update_notification.js' -export const VERSION = 1 +import { useInstanceStore } from 'src/stores/instance' + +import { defaultState as configDefaultState } from 'src/modules/default_config_state' + +export const VERSION = 2 export const NEW_USER_DATE = new Date('2022-08-04') // date of writing this, basically export const COMMAND_TRIM_FLAGS = 1000 @@ -41,6 +45,7 @@ export const defaultState = { dontShowUpdateNotifs: false, collapseNav: false, muteFilters: {}, + ...configDefaultState, }, collections: { pinnedStatusActions: ['reply', 'retweet', 'favorite', 'emoji'], @@ -128,13 +133,13 @@ export const _getRecentData = (cache, live, isTest) => { live._version === cache._version ) { console.debug( - 'Same version/timestamp on both source, source of truth irrelevant', + 'Same version/timestamp on both sources, source of truth irrelevant', ) result.recent = cache result.stale = live } else { console.debug( - 'Different timestamp, figuring out which one is more recent', + 'Different timestamp or version, figuring out which one is more recent', ) if (live._timestamp < cache._timestamp) { result.recent = cache @@ -208,7 +213,7 @@ const _mergeJournal = (...journals) => { // side effect journal.sort((a, b) => (a.timestamp > b.timestamp ? 1 : -1)) - if (path.startsWith('collections')) { + if (path.startsWith('collections') || path.startsWith('objectCollection')) { const lastRemoveIndex = findLastIndex( journal, ({ operation }) => operation === 'removeFromCollection', @@ -229,6 +234,7 @@ const _mergeJournal = (...journals) => { return false } if (a.operation === 'addToCollection') { + // TODO check how objectCollections behaves here return a.args[0] === b.args[0] } return false @@ -368,21 +374,21 @@ export const _resetFlags = ( return result } -export const _doMigrations = (cache) => { - if (!cache) return cache +export const _doMigrations = (cache, live) => { + const data = cache ?? live - if (cache._version < VERSION) { + if (data._version < VERSION) { console.debug( - 'Local cached data has older version, seeing if there any migrations that can be applied', + 'Data has older version, seeing if there any migrations that can be applied', ) // no migrations right now since we only have one version console.debug('No migrations found') } - if (cache._version > VERSION) { + if (data._version > VERSION) { console.debug( - 'Local cached data has newer version, seeing if there any reverse migrations that can be applied', + 'Data has newer version, seeing if there any reverse migrations that can be applied', ) // no reverse migrations right now but we leave a possibility of loading a hotpatch if need be @@ -391,9 +397,9 @@ export const _doMigrations = (cache) => { console.debug('Found hotpatch migration, applying') return window._PLEROMA_HOTPATCH.reverseMigrations.call( {}, - 'serverSideStorage', - { from: cache._version, to: VERSION }, - cache, + 'syncConfigStore', + { from: data._version, to: VERSION }, + data, ) } } @@ -402,7 +408,7 @@ export const _doMigrations = (cache) => { return cache } -export const useServerSideStorageStore = defineStore('serverSideStorage', { +export const useSyncConfigStore = defineStore('sync_config', { state() { return cloneDeep(defaultState) }, @@ -510,19 +516,40 @@ export const useServerSideStorageStore = defineStore('serverSideStorage', { `tried to edit internal (starts with _) field '${path}', ignoring.`, ) } - const collection = new Set(get(this.prefsStorage, path)) - collection.delete(value) - set(this.prefsStorage, path, [...collection]) - this.prefsStorage._journal = [ - ...this.prefsStorage._journal, - { - operation: 'removeFromCollection', - path, - args: [value], - timestamp: Date.now(), - }, - ] - this.dirty = true + + const { _key } = value + if (path.startsWith('collection')) { + const collection = new Set(get(this.prefsStorage, path)) + collection.delete(value) + set(this.prefsStorage, path, [...collection]) + + this.prefsStorage._journal = [ + ...this.prefsStorage._journal, + { + operation: 'removeFromCollection', + path, + args: [value], + timestamp: Date.now(), + }, + ] + this.dirty = true + } else if (path.startsWith('objectCollection')) { + const collection = new Set(get(this.prefsStorage, path + '.index')) + collection.delete(_key) + set(this.prefsStorage, path + '.index', [...collection]) + const data = get(this.prefsStorage, path + '.data') + delete data[_key] + + this.prefsStorage._journal = [ + ...this.prefsStorage._journal, + { + operation: 'removeFromCollection', + path, + args: [{ _key }], + timestamp: Date.now(), + }, + ] + } }, reorderCollectionPreference({ path, value, movement }) { if (path.startsWith('_')) { @@ -554,24 +581,23 @@ export const useServerSideStorageStore = defineStore('serverSideStorage', { username, ) }, - clearServerSideStorage() { + clearSyncConfig() { const blankState = { ...cloneDeep(defaultState) } Object.keys(this).forEach((k) => { this[k] = blankState[k] }) }, - setServerSideStorage(userData) { + setSyncConfig(userData) { const live = userData.storage this.raw = live let cache = this.cache - if (cache && cache._user !== userData.fqn) { + if (cache?._user !== userData.fqn) { console.warn( 'Cache belongs to another user! reinitializing local cache!', ) cache = null } - - cache = _doMigrations(cache) + console.log(cache, live) let { recent, stale, needUpload } = _getRecentData(cache, live) @@ -589,6 +615,9 @@ export const useServerSideStorageStore = defineStore('serverSideStorage', { }) } + recent = recent && _doMigrations(recent) + stale = stale && _doMigrations(stale) + if (!needUpload && recent && stale) { console.debug('Checking if data needs merging...') // discarding timestamps and versions @@ -627,7 +656,7 @@ export const useServerSideStorageStore = defineStore('serverSideStorage', { this.flagStorage = this.cache.flagStorage this.prefsStorage = this.cache.prefsStorage }, - pushServerSideStorage({ force = false } = {}) { + pushSyncConfig({ force = false } = {}) { const needPush = this.dirty || force if (!needPush) return this.updateCache({ username: window.vuex.state.users.currentUser.fqn }) @@ -635,9 +664,26 @@ export const useServerSideStorageStore = defineStore('serverSideStorage', { window.vuex.state.api.backendInteractor .updateProfileJSON({ params }) .then((user) => { - this.setServerSideStorage(user) + this.setSyncConfig(user) this.dirty = false }) }, }, + getters: { + mergedConfig: (state) => { + const instancePrefs = useInstanceStore().prefsStorage + const result = Object.fromEntries( + Object.entries(state.prefsStorage.simple).map(([k, v]) => [ + k, + v ?? instancePrefs[k], + ]), + ) + return result + }, + }, + persist: { + afterLoad(state) { + return state + }, + }, }) diff --git a/test/unit/specs/modules/serverSideStorage.spec.js b/test/unit/specs/stores/sync_config.spec.js similarity index 87% rename from test/unit/specs/modules/serverSideStorage.spec.js rename to test/unit/specs/stores/sync_config.spec.js index bd2028ea3..abcddbeb1 100644 --- a/test/unit/specs/modules/serverSideStorage.spec.js +++ b/test/unit/specs/stores/sync_config.spec.js @@ -12,25 +12,25 @@ import { COMMAND_TRIM_FLAGS_AND_RESET, defaultState, newUserFlags, - useServerSideStorageStore, + useSyncConfigStore, VERSION, -} from 'src/stores/serverSideStorage.js' +} from 'src/stores/sync_config.js' -describe('The serverSideStorage module', () => { +describe('The SyncConfig module', () => { beforeEach(() => { setActivePinia(createPinia()) }) describe('mutations', () => { - describe('setServerSideStorage', () => { + describe('setSyncConfig', () => { const user = { created_at: new Date('1999-02-09'), storage: {}, } it('should initialize storage if none present', () => { - const store = useServerSideStorageStore() - store.setServerSideStorage(store, user) + const store = useSyncConfigStore() + store.setSyncConfig({ ...user }) expect(store.cache._version).to.eql(VERSION) expect(store.cache._timestamp).to.be.a('number') expect(store.cache.flagStorage).to.eql(defaultState.flagStorage) @@ -38,8 +38,8 @@ describe('The serverSideStorage module', () => { }) it('should initialize storage with proper flags for new users if none present', () => { - const store = useServerSideStorageStore() - store.setServerSideStorage({ ...user, created_at: new Date() }) + const store = useSyncConfigStore() + store.setSyncConfig({ ...user, created_at: new Date() }) expect(store.cache._version).to.eql(VERSION) expect(store.cache._timestamp).to.be.a('number') expect(store.cache.flagStorage).to.eql(newUserFlags) @@ -47,14 +47,14 @@ describe('The serverSideStorage module', () => { }) it('should merge flags even if remote timestamp is older', () => { - const store = useServerSideStorageStore() + const store = useSyncConfigStore() store.cache = { _timestamp: Date.now(), _version: VERSION, ...cloneDeep(defaultState), } - store.setServerSideStorage({ + store.setSyncConfig({ ...user, storage: { _timestamp: 123, @@ -76,10 +76,10 @@ describe('The serverSideStorage module', () => { }) it('should reset local timestamp to remote if contents are the same', () => { - const store = useServerSideStorageStore() + const store = useSyncConfigStore() store.cache = null - store.setServerSideStorage({ + store.setSyncConfig({ ...user, storage: { _timestamp: 123, @@ -95,9 +95,9 @@ describe('The serverSideStorage module', () => { expect(store.cache.flagStorage.updateCounter).to.eql(999) }) - it('should remote version if local missing', () => { - const store = useServerSideStorageStore() - store.setServerSideStorage(store, user) + it('should use remote version if local missing', () => { + const store = useSyncConfigStore() + store.setSyncConfig(store, user) expect(store.cache._version).to.eql(VERSION) expect(store.cache._timestamp).to.be.a('number') expect(store.cache.flagStorage).to.eql(defaultState.flagStorage) @@ -105,7 +105,7 @@ describe('The serverSideStorage module', () => { }) describe('setPreference', () => { it('should set preference and update journal log accordingly', () => { - const store = useServerSideStorageStore() + const store = useSyncConfigStore() store.setPreference({ path: 'simple.testing', value: 1 }) expect(store.prefsStorage.simple.testing).to.eql(1) expect(store.prefsStorage._journal.length).to.eql(1) @@ -119,18 +119,34 @@ describe('The serverSideStorage module', () => { }) it('should keep journal to a minimum', () => { - const store = useServerSideStorageStore() + const store = useSyncConfigStore() store.setPreference({ path: 'simple.testing', value: 1 }) store.setPreference({ path: 'simple.testing', value: 2 }) store.addCollectionPreference({ path: 'collections.testing', value: 2 }) + store.addCollectionPreference({ + path: 'objectCollections.testing', + value: { _key: 'a', foo: 1 }, + }) + expect(store.prefsStorage.objectCollections.testing).to.eql({ + data: { a: { _key: 'a', foo: 1 } }, + index: ['a'], + }) store.removeCollectionPreference({ path: 'collections.testing', value: 2, }) + store.removeCollectionPreference({ + path: 'objectCollections.testing', + value: { _key: 'a' }, + }) store.updateCache({ username: 'test' }) expect(store.prefsStorage.simple.testing).to.eql(2) expect(store.prefsStorage.collections.testing).to.eql([]) - expect(store.prefsStorage._journal.length).to.eql(2) + expect(store.prefsStorage.objectCollections.testing).to.eql({ + data: {}, + index: [], + }) + expect(store.prefsStorage._journal.length).to.eql(3) expect(store.prefsStorage._journal[0]).to.eql({ path: 'simple.testing', operation: 'set', @@ -145,22 +161,41 @@ describe('The serverSideStorage module', () => { // should have A timestamp, we don't really care what it is timestamp: store.prefsStorage._journal[1].timestamp, }) + expect(store.prefsStorage._journal[2]).to.eql({ + path: 'objectCollections.testing', + operation: 'removeFromCollection', + args: [{ _key: 'a' }], + // should have A timestamp, we don't really care what it is + timestamp: store.prefsStorage._journal[2].timestamp, + }) }) it('should remove duplicate entries from journal', () => { - const store = useServerSideStorageStore() + const store = useSyncConfigStore() store.setPreference({ path: 'simple.testing', value: 1 }) store.setPreference({ path: 'simple.testing', value: 1 }) store.addCollectionPreference({ path: 'collections.testing', value: 2 }) store.addCollectionPreference({ path: 'collections.testing', value: 2 }) + store.addCollectionPreference({ + path: 'objectCollections.testing', + value: { _key: 'a', foo: 1 }, + }) + store.addCollectionPreference({ + path: 'objectCollections.testing', + value: { _key: 'a', foo: 1 }, + }) store.updateCache({ username: 'test' }) expect(store.prefsStorage.simple.testing).to.eql(1) expect(store.prefsStorage.collections.testing).to.eql([2]) - expect(store.prefsStorage._journal.length).to.eql(2) + expect(store.prefsStorage.objectCollections.testing).to.eql({ + data: { a: { _key: 'a', foo: 1 } }, + index: ['a'], + }) + expect(store.prefsStorage._journal.length).to.eql(4) }) it('should remove depth = 3 set/unset entries from journal', () => { - const store = useServerSideStorageStore() + const store = useSyncConfigStore() store.setPreference({ path: 'simple.object.foo', value: 1 }) store.unsetPreference({ path: 'simple.object.foo' }) store.updateCache(store, { username: 'test' }) @@ -169,7 +204,7 @@ describe('The serverSideStorage module', () => { }) it('should not allow unsetting depth <= 2', () => { - const store = useServerSideStorageStore() + const store = useSyncConfigStore() store.setPreference({ path: 'simple.object.foo', value: 1 }) expect(() => store.unsetPreference({ path: 'simple' })).to.throw() expect(() => @@ -178,7 +213,7 @@ describe('The serverSideStorage module', () => { }) it('should not allow (un)setting depth > 3', () => { - const store = useServerSideStorageStore() + const store = useSyncConfigStore() store.setPreference({ path: 'simple.object', value: {} }) expect(() => store.setPreference({ path: 'simple.object.lv3', value: 1 }), From 69656e0181409d49c70ef2ee3e9e36e4253f745d Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Fri, 13 Feb 2026 14:07:41 +0200 Subject: [PATCH 002/167] minor change for paths in SW --- src/sw.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/sw.js b/src/sw.js index 38a2fe7b2..e3225c91d 100644 --- a/src/sw.js +++ b/src/sw.js @@ -4,11 +4,10 @@ import 'virtual:pleroma-fe/service_worker_env' import { createI18n } from 'vue-i18n' -import { parseNotification } from './services/entity_normalizer/entity_normalizer.service.js' -import { prepareNotificationObject } from './services/notification_utils/notification_utils.js' -import { cacheKey, emojiCacheKey, shouldCache } from './services/sw/sw.js' - import { storage } from 'src/lib/storage.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' // Collects all messages for service workers // Needed because service workers cannot use dynamic imports From c9dede920e33960c4cdaf8ea8622d0ecd8f26012 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Fri, 13 Feb 2026 14:11:33 +0200 Subject: [PATCH 003/167] main.js --- src/lib/language.js | 14 ++++ src/lib/push_notifications_plugin.js | 101 +++++++++++++++++++++------ src/lib/style.js | 29 ++++++++ src/main.js | 15 +++- 4 files changed, 133 insertions(+), 26 deletions(-) create mode 100644 src/lib/language.js create mode 100644 src/lib/style.js diff --git a/src/lib/language.js b/src/lib/language.js new file mode 100644 index 000000000..f8904dd07 --- /dev/null +++ b/src/lib/language.js @@ -0,0 +1,14 @@ +import { useI18nStore } from 'src/stores/i18n.js' + +export const piniaLanguagePlugin = ({ store, options }) => { + if (store.$id === 'sync_config') { + store.$onAction(({ name, args }) => { + if (name === 'setPreference') { + const { path, value } = args[0] + if (path === 'simple.interfaceLanguage') { + useI18nStore().setLanguage(value) + } + } + }) + } +} diff --git a/src/lib/push_notifications_plugin.js b/src/lib/push_notifications_plugin.js index 9732a82c9..7d44625c2 100644 --- a/src/lib/push_notifications_plugin.js +++ b/src/lib/push_notifications_plugin.js @@ -1,37 +1,92 @@ import { useInstanceStore } from 'src/stores/instance.js' import { useInterfaceStore } from 'src/stores/interface.js' +import { useSyncConfigStore } from 'src/stores/sync_config.js' -export default (store) => { +export const piniaPushNotificationsPlugin = ({ store }) => { + if ( + store.$id !== 'sync_config' && + store.$id !== 'instance' && + store.$id !== 'interface' + ) + return + + store.$onAction(({ name: actionName, args }) => { + if ( + store.$id === 'interface' && + actionName !== 'setNotificationPermission' && + actionName !== 'setLoginStatus' + ) + return + + // Initial state + let vapidPublicKey = useInstanceStore().vapidPublicKey + let enabled = useSyncConfigStore().mergedConfig.webPushNotifications + let permissionGranted = + useInterfaceStore().notificationPermission === 'granted' + let permissionPresent = + useInterfaceStore().notificationPermission !== undefined + let user = !!window.vuex.state.users.currentUser + + if (store.$id === 'instance') { + if (actionName === 'set' && args[0].path === 'vapidPublicKey') { + const { value } = args[0] + vapidPublicKey = value + } + } + + if (!vapidPublicKey || !permissionPresent) { + return + } + + if (store.$id === 'interface') { + if (actionName === 'setNotificationPermission') { + permissionGranted = args[0] === 'granted' + } else if (actionName === 'setLoginStatus') { + user = args[0] + } else { + return + } + } else if (store.$id === 'sync_config') { + if ( + actionName === 'setPreference' && + args[0].path === 'simple.webPushNotifications' + ) { + const { value } = args[0] + enabled = value + } else { + return + } + } + + if (permissionGranted && enabled && user) { + return window.vuex.dispatch('registerPushNotifications') + } else { + return window.vuex.dispatch('unregisterPushNotifications') + } + }) +} + +export const vuexPushNotificationsPlugin = (store) => { store.subscribe((mutation, state) => { + // Initial state const vapidPublicKey = useInstanceStore().vapidPublicKey - const webPushNotification = state.config.webPushNotifications - const permission = useInterfaceStore().notificationPermission === 'granted' + const enabled = useSyncConfigStore().mergedConfig.webPushNotifications + const permissionGranted = + useInterfaceStore().notificationPermission === 'granted' + const permissionPresent = + useInterfaceStore().notificationPermission !== undefined const user = state.users.currentUser - const isUserMutation = mutation.type === 'setCurrentUser' - const isVapidMutation = - mutation.type === 'setInstanceOption' && - mutation.payload.name === 'vapidPublicKey' - const isPermMutation = - mutation.type === 'setNotificationPermission' && - mutation.payload === 'granted' - const isUserConfigMutation = - mutation.type === 'setOption' && - mutation.payload.name === 'webPushNotifications' - const isVisibilityMutation = - mutation.type === 'setOption' && - mutation.payload.name === 'notificationVisibility' + if (!permissionPresent || !vapidPublicKey) return if ( - isUserMutation || - isVapidMutation || - isPermMutation || - isUserConfigMutation || - isVisibilityMutation + mutation.type === 'setCurrentUser' || + mutation.type === 'clearCurrentUser' ) { - if (user && vapidPublicKey && permission && webPushNotification) { + console.log(!!user, permissionGranted, enabled) + if (user && permissionGranted && enabled) { return store.dispatch('registerPushNotifications') - } else if (isUserConfigMutation && !webPushNotification) { + } else { return store.dispatch('unregisterPushNotifications') } } diff --git a/src/lib/style.js b/src/lib/style.js new file mode 100644 index 000000000..96603925a --- /dev/null +++ b/src/lib/style.js @@ -0,0 +1,29 @@ +import { applyStyleConfig } from 'src/services/style_setter/style_setter.js' + +const APPEARANCE_SETTINGS_KEYS = new Set( + [ + 'sidebarColumnWidth', + 'contentColumnWidth', + 'notifsColumnWidth', + 'themeEditorMinWidth', + 'textSize', + 'navbarSize', + 'panelHeaderSize', + 'forcedRoundness', + 'emojiSize', + 'emojiReactionsScale', + ].map((x) => 'simple.' + x), +) + +export const piniaStylePlugin = ({ store, options }) => { + if (store.$id === 'sync_config') { + store.$onAction(({ name, args, after }) => { + if (name === 'setPreference') { + const { path } = args[0] + if (APPEARANCE_SETTINGS_KEYS.has(path)) { + after(() => applyStyleConfig(store.mergedConfig)) + } + } + }) + } +} diff --git a/src/main.js b/src/main.js index 91653973d..a4269b4a7 100644 --- a/src/main.js +++ b/src/main.js @@ -20,9 +20,15 @@ import messages from './i18n/messages.js' import createPersistedState, { piniaPersistPlugin, } from './lib/persisted_state.js' -import pushNotifications from './lib/push_notifications_plugin.js' +import { + piniaPushNotificationsPlugin, + vuexPushNotificationsPlugin, +} from './lib/push_notifications_plugin.js' import vuexModules from './modules/index.js' +import { piniaLanguagePlugin } from 'src/lib/language.js' +import { piniaStylePlugin } from 'src/lib/style.js' + const currentLocale = (window.navigator.language || 'en').split('-')[0] const i18n = createI18n({ @@ -35,7 +41,7 @@ const i18n = createI18n({ messages.setLanguage(i18n.global, currentLocale) const persistedStateOptions = { - paths: ['serverSideStorage.cache', 'config', 'users.lastLoginName', 'oauth'], + paths: ['syncConfig.cache', 'config', 'users.lastLoginName', 'oauth'], } ;(async () => { @@ -66,9 +72,12 @@ const persistedStateOptions = { try { let storageError - const plugins = [pushNotifications] + const plugins = [vuexPushNotificationsPlugin] const pinia = createPinia() pinia.use(piniaPersistPlugin()) + pinia.use(piniaLanguagePlugin) + pinia.use(piniaStylePlugin) + pinia.use(piniaPushNotificationsPlugin) try { const persistedState = await createPersistedState(persistedStateOptions) From dbc9bd9c462ee2f3259b623dc6b34ab02728d61d Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Fri, 13 Feb 2026 14:26:39 +0200 Subject: [PATCH 004/167] components --- src/components/about/about.js | 3 +- .../account_actions/account_actions.js | 5 +- src/components/attachment/attachment.js | 7 +- src/components/confirm_modal/mute_confirm.js | 6 +- src/components/conversation/conversation.js | 11 +-- src/components/desktop_nav/desktop_nav.js | 3 +- 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 | 4 +- src/components/emoji_input/suggestor.js | 2 + src/components/emoji_picker/emoji_picker.js | 5 +- src/components/follow_button/follow_button.js | 4 +- .../follow_request_card.js | 4 +- src/components/link-preview/link-preview.js | 6 +- src/components/media_upload/media_upload.js | 5 +- src/components/mentions_line/mentions_line.js | 6 +- .../mobile_post_status_button.js | 5 +- src/components/notification/notification.js | 5 +- .../notifications/notification_filters.vue | 4 +- src/components/notifications/notifications.js | 7 +- src/components/poll/poll.js | 3 +- .../post_status_form/post_status_form.js | 9 +-- .../remove_follower_button.js | 4 +- .../settings_modal/helpers/setting.js | 54 ++++++++++---- .../helpers/shared_computed_object.js | 29 ++++---- .../settings_modal/settings_modal.js | 7 +- .../settings_modal/tabs/appearance_tab.vue | 8 +-- .../settings_modal/tabs/clutter_tab.vue | 4 +- .../settings_modal/tabs/composing_tab.js | 3 +- .../settings_modal/tabs/general_tab.js | 6 +- .../settings_modal/tabs/general_tab.vue | 4 +- .../settings_modal/tabs/layout_tab.js | 5 +- .../tabs/old_theme_tab/old_theme_tab.js | 3 +- src/components/status/status.js | 2 +- .../status_content/status_content.js | 3 +- src/components/still-image/still-image.js | 4 +- src/components/timeline/timeline.js | 9 ++- src/components/user_card/user_card.js | 7 +- src/components/user_card/user_card.vue | 10 +-- src/components/user_popover/user_popover.js | 16 ++--- .../user_timed_filter_modal.js | 10 +-- .../video_attachment/video_attachment.js | 6 +- src/components/who_to_follow/who_to_follow.js | 2 + test/unit/specs/components/draft.spec.js | 70 +++++++++---------- .../unit/specs/components/emoji_input.spec.js | 23 ++++-- 46 files changed, 247 insertions(+), 160 deletions(-) diff --git a/src/components/about/about.js b/src/components/about/about.js index f52d5c797..ab1ace320 100644 --- a/src/components/about/about.js +++ b/src/components/about/about.js @@ -5,6 +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' const About = { components: { @@ -21,7 +22,7 @@ const About = { showInstanceSpecificPanel() { return ( useInstanceStore().instanceIdentity.showInstanceSpecificPanel && - !this.$store.getters.mergedConfig.hideISP && + !useSyncConfigStore().mergedConfig.hideISP && useInstanceStore().instanceIdentity.instanceSpecificPanelContent ) }, diff --git a/src/components/account_actions/account_actions.js b/src/components/account_actions/account_actions.js index 8fce4b5af..38f585675 100644 --- a/src/components/account_actions/account_actions.js +++ b/src/components/account_actions/account_actions.js @@ -8,6 +8,7 @@ import ProgressButton from '../progress_button/progress_button.vue' import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.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' @@ -89,10 +90,10 @@ const AccountActions = { }, computed: { shouldConfirmBlock() { - return this.$store.getters.mergedConfig.modalOnBlock + return useSyncConfigStore().mergedConfig.modalOnBlock }, shouldConfirmRemoveUserFromFollowers() { - return this.$store.getters.mergedConfig.modalOnRemoveUserFromFollowers + return useSyncConfigStore().mergedConfig.modalOnRemoveUserFromFollowers }, ...mapState(useInstanceCapabilitiesStore, [ 'blockExpiration', diff --git a/src/components/attachment/attachment.js b/src/components/attachment/attachment.js index db5171c80..a67bd475e 100644 --- a/src/components/attachment/attachment.js +++ b/src/components/attachment/attachment.js @@ -9,6 +9,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 { library } from '@fortawesome/fontawesome-svg-core' import { @@ -58,8 +59,8 @@ const Attachment = { localDescription: this.description || this.attachment.description, nsfwImage: useInstanceStore().instanceIdentity.nsfwCensorImage || nsfwImage, - hideNsfwLocal: this.$store.getters.mergedConfig.hideNsfw, - preloadImage: this.$store.getters.mergedConfig.preloadImage, + hideNsfwLocal: useSyncConfigStore().mergedConfig.hideNsfw, + preloadImage: useSyncConfigStore().mergedConfig.preloadImage, loading: false, img: fileTypeService.fileType(this.attachment.mimetype) === 'image' && @@ -93,7 +94,7 @@ const Attachment = { return this.size === 'hide' }, useContainFit() { - return this.$store.getters.mergedConfig.useContainFit + return useSyncConfigStore().mergedConfig.useContainFit }, placeholderName() { if (this.attachment.description === '' || !this.attachment.description) { diff --git a/src/components/confirm_modal/mute_confirm.js b/src/components/confirm_modal/mute_confirm.js index c486ad7e4..888aa017b 100644 --- a/src/components/confirm_modal/mute_confirm.js +++ b/src/components/confirm_modal/mute_confirm.js @@ -1,8 +1,10 @@ -import { mapGetters } from 'vuex' +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' + export default { props: ['type', 'user', 'status'], emits: ['hide', 'show', 'muted'], @@ -43,7 +45,7 @@ export default { } } }, - ...mapGetters(['mergedConfig']), + ...mapState(useSyncConfigStore, ['mergedConfig']), }, methods: { optionallyPrompt() { diff --git a/src/components/conversation/conversation.js b/src/components/conversation/conversation.js index cb7cf4782..76dd26352 100644 --- a/src/components/conversation/conversation.js +++ b/src/components/conversation/conversation.js @@ -9,6 +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 { library } from '@fortawesome/fontawesome-svg-core' import { @@ -81,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 = this.$store.getters.mergedConfig.maxDepthInThread - 2 + const maxDepth = useSyncConfigStore().mergedConfig.maxDepthInThread - 2 return maxDepth >= 1 ? maxDepth : 1 }, streamingEnabled() { @@ -91,22 +92,22 @@ const conversation = { ) }, displayStyle() { - return this.$store.getters.mergedConfig.conversationDisplay + return useSyncConfigStore().mergedConfig.conversationDisplay }, isTreeView() { return !this.isLinearView }, treeViewIsSimple() { - return !this.$store.getters.mergedConfig.conversationTreeAdvanced + return !useSyncConfigStore().mergedConfig.conversationTreeAdvanced }, isLinearView() { return this.displayStyle === 'linear' }, shouldFadeAncestors() { - return this.$store.getters.mergedConfig.conversationTreeFadeAncestors + return useSyncConfigStore().mergedConfig.conversationTreeFadeAncestors }, otherRepliesButtonPosition() { - return this.$store.getters.mergedConfig.conversationOtherRepliesButton + return useSyncConfigStore().mergedConfig.conversationOtherRepliesButton }, showOtherRepliesButtonBelowStatus() { return this.otherRepliesButtonPosition === 'below' diff --git a/src/components/desktop_nav/desktop_nav.js b/src/components/desktop_nav/desktop_nav.js index 943c66d1e..0871e0bd3 100644 --- a/src/components/desktop_nav/desktop_nav.js +++ b/src/components/desktop_nav/desktop_nav.js @@ -5,6 +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 { library } from '@fortawesome/fontawesome-svg-core' import { @@ -96,7 +97,7 @@ export default { return this.$store.state.users.currentUser }, shouldConfirmLogout() { - return this.$store.getters.mergedConfig.modalOnLogout + return useSyncConfigStore().mergedConfig.modalOnLogout }, }, methods: { diff --git a/src/components/dialog_modal/dialog_modal.js b/src/components/dialog_modal/dialog_modal.js index 8070d3429..e5c399086 100644 --- a/src/components/dialog_modal/dialog_modal.js +++ b/src/components/dialog_modal/dialog_modal.js @@ -1,3 +1,5 @@ +import { useSyncConfigStore } from 'src/stores/sync_config.js' + const DialogModal = { props: { darkOverlay: { @@ -13,7 +15,7 @@ const DialogModal = { }, computed: { mobileCenter() { - return this.$store.getters.mergedConfig.modalMobileCenter + return useSyncConfigStore().mergedConfig.modalMobileCenter }, }, } diff --git a/src/components/draft/draft.js b/src/components/draft/draft.js index 971a75b10..5ee85dff4 100644 --- a/src/components/draft/draft.js +++ b/src/components/draft/draft.js @@ -6,6 +6,8 @@ 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 { library } from '@fortawesome/fontawesome-svg-core' import { faPollH } from '@fortawesome/free-solid-svg-icons' @@ -57,7 +59,7 @@ const Draft = { : undefined }, localCollapseSubjectDefault() { - return this.$store.getters.mergedConfig.collapseMessageWithSubject + return useSyncConfigStore().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 d724ab4ac..5f297bf4c 100644 --- a/src/components/draft_closer/draft_closer.js +++ b/src/components/draft_closer/draft_closer.js @@ -1,5 +1,7 @@ import DialogModal from 'src/components/dialog_modal/dialog_modal.vue' +import { useSyncConfigStore } from 'src/stores/sync_config.js' + const DraftCloser = { data() { return { @@ -12,10 +14,10 @@ const DraftCloser = { emits: ['save', 'discard'], computed: { action() { - if (this.$store.getters.mergedConfig.autoSaveDraft) { + if (useSyncConfigStore().mergedConfig.autoSaveDraft) { return 'save' } else { - return this.$store.getters.mergedConfig.unsavedPostAction + return useSyncConfigStore().mergedConfig.unsavedPostAction } }, shouldConfirm() { diff --git a/src/components/emoji_input/emoji_input.js b/src/components/emoji_input/emoji_input.js index 24794640e..23de501de 100644 --- a/src/components/emoji_input/emoji_input.js +++ b/src/components/emoji_input/emoji_input.js @@ -9,6 +9,8 @@ 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 { library } from '@fortawesome/fontawesome-svg-core' import { faSmileBeam } from '@fortawesome/free-regular-svg-icons' @@ -131,7 +133,7 @@ const EmojiInput = { }, computed: { padEmoji() { - return this.$store.getters.mergedConfig.padEmoji + return useSyncConfigStore().mergedConfig.padEmoji }, defaultCandidateIndex() { return this.$store.getters.mergedConfig.autocompleteSelect ? 0 : -1 diff --git a/src/components/emoji_input/suggestor.js b/src/components/emoji_input/suggestor.js index c478fea67..c0d8f7ca1 100644 --- a/src/components/emoji_input/suggestor.js +++ b/src/components/emoji_input/suggestor.js @@ -1,3 +1,5 @@ +import { useEmojiStore } from 'src/stores/emoji.js' + /** * suggest - generates a suggestor function to be used by emoji-input * data: object providing source information for specific types of suggestions: diff --git a/src/components/emoji_picker/emoji_picker.js b/src/components/emoji_picker/emoji_picker.js index 79c0ab47e..1e4594b2c 100644 --- a/src/components/emoji_picker/emoji_picker.js +++ b/src/components/emoji_picker/emoji_picker.js @@ -8,6 +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 { library } from '@fortawesome/fontawesome-svg-core' import { @@ -340,7 +341,7 @@ const EmojiPicker = { this.$nextTick(() => { this.updateEmojiSize() }) - return this.$store.getters.mergedConfig.fontSize + return useSyncConfigStore().mergedConfig.fontSize }, emojiHeight() { return this.emojiSize @@ -405,7 +406,7 @@ const EmojiPicker = { }, languages() { return ensureFinalFallback( - this.$store.getters.mergedConfig.interfaceLanguage, + useSyncConfigStore().mergedConfig.interfaceLanguage, ) }, maybeLocalizedEmojiName() { diff --git a/src/components/follow_button/follow_button.js b/src/components/follow_button/follow_button.js index eb545b28d..539749bc8 100644 --- a/src/components/follow_button/follow_button.js +++ b/src/components/follow_button/follow_button.js @@ -3,6 +3,8 @@ import { requestUnfollow, } from '../../services/follow_manipulate/follow_manipulate' import ConfirmModal from '../confirm_modal/confirm_modal.vue' + +import { useSyncConfigStore } from 'src/stores/sync_config.js' export default { props: ['relationship', 'user', 'labelFollowing', 'buttonClass'], components: { @@ -16,7 +18,7 @@ export default { }, computed: { shouldConfirmUnfollow() { - return this.$store.getters.mergedConfig.modalOnUnfollow + return useSyncConfigStore().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 c037ddf42..b7959a2d3 100644 --- a/src/components/follow_request_card/follow_request_card.js +++ b/src/components/follow_request_card/follow_request_card.js @@ -2,6 +2,8 @@ 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' + const FollowRequestCard = { props: ['user'], components: { @@ -76,7 +78,7 @@ const FollowRequestCard = { }, computed: { mergedConfig() { - return this.$store.getters.mergedConfig + return useSyncConfigStore().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 ac91f916d..a465cc213 100644 --- a/src/components/link-preview/link-preview.js +++ b/src/components/link-preview/link-preview.js @@ -1,4 +1,6 @@ -import { mapGetters } from 'vuex' +import { mapState } from 'pinia' + +import { useSyncConfigStore } from 'src/stores/sync_config.js' const LinkPreview = { name: 'LinkPreview', @@ -24,7 +26,7 @@ const LinkPreview = { hideNsfwConfig() { return this.mergedConfig.hideNsfw }, - ...mapGetters(['mergedConfig']), + ...mapState(useSyncConfigStore, ['mergedConfig']), }, created() { if (this.useImage) { diff --git a/src/components/media_upload/media_upload.js b/src/components/media_upload/media_upload.js index ee61fa369..be62142c6 100644 --- a/src/components/media_upload/media_upload.js +++ b/src/components/media_upload/media_upload.js @@ -2,6 +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 { library } from '@fortawesome/fontawesome-svg-core' import { faCircleNotch, faUpload } from '@fortawesome/free-solid-svg-icons' @@ -33,7 +34,7 @@ const mediaUpload = { } // Skip if image compression is disabled - if (!this.$store.getters.mergedConfig.imageCompression) { + if (!useSyncConfigStore().mergedConfig.imageCompression) { return file } @@ -78,7 +79,7 @@ const mediaUpload = { // Convert to WebP if supported and alwaysUseJpeg is false, otherwise JPEG const type = - !this.$store.getters.mergedConfig.alwaysUseJpeg && supportsWebP + !useSyncConfigStore().mergedConfig.alwaysUseJpeg && supportsWebP ? 'image/webp' : 'image/jpeg' const extension = type === 'image/webp' ? '.webp' : '.jpg' diff --git a/src/components/mentions_line/mentions_line.js b/src/components/mentions_line/mentions_line.js index e6aa392a0..bd3882866 100644 --- a/src/components/mentions_line/mentions_line.js +++ b/src/components/mentions_line/mentions_line.js @@ -1,7 +1,9 @@ -import { mapGetters } from 'vuex' +import { mapState } from 'pinia' import MentionLink from 'src/components/mention_link/mention_link.vue' +import { useSyncConfigStore } from 'src/stores/sync_config.js' + export const MENTIONS_LIMIT = 5 const MentionsLine = { @@ -26,7 +28,7 @@ const MentionsLine = { manyMentions() { return this.extraMentions.length > 0 }, - ...mapGetters(['mergedConfig']), + ...mapState(useSyncConfigStore, ['mergedConfig']), }, methods: { toggleShowMore() { 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 83103c827..594cd6688 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,6 +1,7 @@ import { debounce } from 'lodash' 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' @@ -45,10 +46,10 @@ const MobilePostStatusButton = { ) }, isPersistent() { - return !!this.$store.getters.mergedConfig.alwaysShowNewPostButton + return !!useSyncConfigStore().mergedConfig.alwaysShowNewPostButton }, autohideFloatingPostButton() { - return !!this.$store.getters.mergedConfig.autohideFloatingPostButton + return !!useSyncConfigStore().mergedConfig.autohideFloatingPostButton }, }, watch: { diff --git a/src/components/notification/notification.js b/src/components/notification/notification.js index 16084dee4..f3f5b76da 100644 --- a/src/components/notification/notification.js +++ b/src/components/notification/notification.js @@ -17,6 +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 generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator' @@ -181,7 +182,7 @@ const Notification = { return highlightClass(this.notification.from_profile) }, userStyle() { - const highlight = this.$store.getters.mergedConfig.highlight + const highlight = useSyncConfigStore().mergedConfig.highlight const user = this.notification.from_profile return highlightStyle(highlight[user.screen_name]) }, @@ -209,7 +210,7 @@ const Notification = { return isStatusNotification(this.notification.type) }, mergedConfig() { - return this.$store.getters.mergedConfig + return useSyncConfigStore().mergedConfig }, shouldConfirmApprove() { return this.mergedConfig.modalOnApproveFollow diff --git a/src/components/notifications/notification_filters.vue b/src/components/notifications/notification_filters.vue index 7be8eb76b..5508e03ac 100644 --- a/src/components/notifications/notification_filters.vue +++ b/src/components/notifications/notification_filters.vue @@ -108,6 +108,8 @@