diff --git a/build/emojis_plugin.js b/build/emojis_plugin.js index 7979086dd..9872f5331 100644 --- a/build/emojis_plugin.js +++ b/build/emojis_plugin.js @@ -25,7 +25,11 @@ const getAllAccessibleAnnotations = async (projectRoot) => { await access(importFile) return `'${lang}': () => import('${importModule}')` } catch (e) { - console.error(e) + if (e.message.match(/ENOENT/)) { + console.warn(`Missing emoji annotations locale: ${destLang}`) + } else { + console.error('test', e.message) + } return } }), diff --git a/src/App.js b/src/App.js index a0bf0aaa4..487090565 100644 --- a/src/App.js +++ b/src/App.js @@ -1,7 +1,6 @@ import { throttle } from 'lodash' import { mapState } from 'pinia' import { defineAsyncComponent } from 'vue' -import { mapGetters } from 'vuex' import DesktopNav from './components/desktop_nav/desktop_nav.vue' import EditStatusModal from './components/edit_status_modal/edit_status_modal.vue' @@ -22,11 +21,20 @@ import WhoToFollowPanel from './components/who_to_follow_panel/who_to_follow_pan import { getOrCreateServiceWorker } from './services/sw/sw' import { windowHeight, windowWidth } from './services/window_utils/window_utils' +import { useEmojiStore } from 'src/stores/emoji.js' +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 messages from 'src/i18n/messages' +import localeService from 'src/services/locale/locale.service.js' + +// Helper to unwrap reactive proxies +window.toValue = (x) => JSON.parse(JSON.stringify(x)) + export default { name: 'app', components: { @@ -72,8 +80,10 @@ export default { }, created() { // Load the locale from the storage - const val = this.$store.getters.mergedConfig.interfaceLanguage - this.$store.dispatch('setOption', { name: 'interfaceLanguage', value: val }) + const value = useMergedConfigStore().mergedConfig.interfaceLanguage + useI18nStore().setLanguage(value) + useEmojiStore().loadUnicodeEmojiData(value) + document.getElementById('modal').classList = ['-' + this.layoutType] // Create bound handlers @@ -122,7 +132,7 @@ export default { ] }, navClasses() { - const { navbarColumnStretch } = this.$store.getters.mergedConfig + const { navbarColumnStretch } = useMergedConfigStore().mergedConfig return [ '-' + this.layoutType, ...(navbarColumnStretch ? ['-column-stretch'] : []), @@ -135,7 +145,9 @@ export default { return this.currentUser.background_image }, instanceBackground() { - return this.mergedConfig.hideInstanceWallpaper ? null : this.background + return useMergedConfigStore().mergedConfig.hideInstanceWallpaper + ? null + : this.instanceBackgroundUrl }, background() { return this.userBackground || this.instanceBackground @@ -160,19 +172,21 @@ export default { if (this.isChats) return false if (this.isListEdit) return false return ( - this.$store.getters.mergedConfig.alwaysShowNewPostButton || + useMergedConfigStore().mergedConfig.alwaysShowNewPostButton || this.layoutType === 'mobile' ) }, shoutboxPosition() { - return this.$store.getters.mergedConfig.alwaysShowNewPostButton || false + return ( + useMergedConfigStore().mergedConfig.alwaysShowNewPostButton || false + ) }, hideShoutbox() { - return this.$store.getters.mergedConfig.hideShoutbox + return useMergedConfigStore().mergedConfig.hideShoutbox }, reverseLayout() { const { thirdColumnMode, sidebarRight: reverseSetting } = - this.$store.getters.mergedConfig + useMergedConfigStore().mergedConfig if (this.layoutType !== 'wide') { return reverseSetting } else { @@ -182,10 +196,10 @@ export default { } }, noSticky() { - return this.$store.getters.mergedConfig.disableStickyHeaders + return useMergedConfigStore().mergedConfig.disableStickyHeaders }, showScrollbars() { - return this.$store.getters.mergedConfig.showScrollbars + return useMergedConfigStore().mergedConfig.showScrollbars }, scrollParent() { return window /* this.$refs.appContentRef */ @@ -193,10 +207,10 @@ export default { showInstanceSpecificPanel() { return ( this.instanceSpecificPanelPresent && - !this.$store.getters.mergedConfig.hideISP + !useMergedConfigStore().mergedConfig.hideISP ) }, - ...mapGetters(['mergedConfig']), + ...mapState(useMergedConfigStore, ['mergedConfig']), ...mapState(useInterfaceStore, [ 'themeApplied', 'styleDataUsed', @@ -208,7 +222,7 @@ export default { 'editingAvailable', ]), ...mapState(useInstanceStore, { - background: (store) => store.instanceIdentity.background, + instanceBackgroundUrl: (store) => store.instanceIdentity.background, showFeaturesPanel: (store) => store.instanceIdentity.showFeaturesPanel, instanceSpecificPanelPresent: (store) => store.instanceIdentity.showInstanceSpecificPanel && diff --git a/src/App.scss b/src/App.scss index 1eee63a7e..b36e702d5 100644 --- a/src/App.scss +++ b/src/App.scss @@ -797,14 +797,17 @@ option { } .notice-dismissible { - padding-right: 4rem; - position: relative; + display: flex; + padding: 0.75em 1em; + align-items: baseline; + line-height: 1.5; + + span { + display: block; + flex: 1 1 auto; + } .dismiss { - position: absolute; - top: 0; - right: 0; - padding: 0.5em; color: inherit; } } diff --git a/src/boot/after_store.js b/src/boot/after_store.js index 97f9b8a42..d16852bf8 100644 --- a/src/boot/after_store.js +++ b/src/boot/after_store.js @@ -32,12 +32,17 @@ import { useI18nStore } from 'src/stores/i18n' 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' import VBodyScrollLock from 'src/directives/body_scroll_lock' import { - instanceDefaultConfig, - staticOrApiConfigDefault, + INSTANCE_DEFAULT_CONFIG_DEFINITIONS, + INSTANCE_IDENTITY_DEFAULT_DEFINITIONS, + INSTANCE_IDENTIY_EXTERNAL, } from 'src/modules/default_config_state.js' let staticInitialResults = null @@ -80,7 +85,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( @@ -88,8 +93,8 @@ const getInstanceConfig = async ({ store }) => { data.pleroma, ) useInstanceStore().set({ - path: 'textlimit', - value: textlimit, + path: 'limits.textLimit', + value: textLimit, }) useInstanceStore().set({ path: 'accountApprovalRequired', @@ -166,18 +171,21 @@ const setSettings = async ({ apiConfig, staticConfig, store }) => { config = Object.assign({}, staticConfig, apiConfig) } - const copyInstanceOption = ({ source, destination }) => { - if (typeof config[source] !== 'undefined') { - useInstanceStore().set({ path: destination, value: config[source] }) - } - } + Object.keys(INSTANCE_IDENTITY_DEFAULT_DEFINITIONS).forEach((source) => { + if (source === 'name') return + if (INSTANCE_IDENTIY_EXTERNAL.has(source)) return + useInstanceStore().set({ + value: config[source], + path: `instanceIdentity.${source}`, + }) + }) - Object.keys(staticOrApiConfigDefault) - .map((k) => ({ source: k, destination: `instanceIdentity.${k}` })) - .forEach(copyInstanceOption) - Object.keys(instanceDefaultConfig) - .map((k) => ({ source: k, destination: `prefsStorage.${k}` })) - .forEach(copyInstanceOption) + Object.keys(INSTANCE_DEFAULT_CONFIG_DEFINITIONS).forEach((source) => + useInstanceStore().set({ + value: config[source], + path: `prefsStorage.${source}`, + }), + ) useAuthFlowStore().setInitialStrategy(config.loginMethod) } @@ -187,7 +195,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 } @@ -272,7 +280,7 @@ const getNodeInfo = async ({ store }) => { const metadata = data.metadata const features = metadata.features useInstanceStore().set({ - path: 'name', + path: 'instanceIdentity.name', value: metadata.nodeName, }) useInstanceStore().set({ @@ -523,6 +531,11 @@ 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() + FaviconService.initFaviconService() initServiceWorker(store) @@ -533,7 +546,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 { @@ -547,7 +560,7 @@ const afterStoreSetup = async ({ pinia, store, storageError, i18n }) => { return Promise.reject(e) } - applyStyleConfig(store.state.config, 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 f52d5c797..404843e8b 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 { useMergedConfigStore } from 'src/stores/merged_config.js' const About = { components: { @@ -21,7 +22,7 @@ const About = { showInstanceSpecificPanel() { return ( useInstanceStore().instanceIdentity.showInstanceSpecificPanel && - !this.$store.getters.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 8fce4b5af..f204adbde 100644 --- a/src/components/account_actions/account_actions.js +++ b/src/components/account_actions/account_actions.js @@ -7,6 +7,7 @@ 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 { library } from '@fortawesome/fontawesome-svg-core' @@ -89,10 +90,10 @@ const AccountActions = { }, computed: { shouldConfirmBlock() { - return this.$store.getters.mergedConfig.modalOnBlock + return useMergedConfigStore().mergedConfig.modalOnBlock }, shouldConfirmRemoveUserFromFollowers() { - return this.$store.getters.mergedConfig.modalOnRemoveUserFromFollowers + return useMergedConfigStore().mergedConfig.modalOnRemoveUserFromFollowers }, ...mapState(useInstanceCapabilitiesStore, [ 'blockExpiration', diff --git a/src/components/attachment/attachment.js b/src/components/attachment/attachment.js index b1a3d82b8..fbe77a687 100644 --- a/src/components/attachment/attachment.js +++ b/src/components/attachment/attachment.js @@ -1,4 +1,4 @@ -import { mapGetters } from 'vuex' +import { mapState } from 'pinia' import nsfwImage from '../../assets/nsfw.png' import Flash from '../flash/flash.vue' @@ -8,6 +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 { useMergedConfigStore } from 'src/stores/merged_config.js' import { library } from '@fortawesome/fontawesome-svg-core' import { @@ -57,8 +58,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: useMergedConfigStore().mergedConfig.hideNsfw, + preloadImage: useMergedConfigStore().mergedConfig.preloadImage, loading: false, img: this.attachment.type === 'image' && document.createElement('img'), modalOpen: false, @@ -90,7 +91,7 @@ const Attachment = { return this.size === 'hide' }, useContainFit() { - return this.$store.getters.mergedConfig.useContainFit + return this.mergedConfig.useContainFit }, placeholderName() { if (this.attachment.description === '' || !this.attachment.description) { @@ -133,7 +134,7 @@ const Attachment = { videoTag() { return this.useModal ? 'button' : 'span' }, - ...mapGetters(['mergedConfig']), + ...mapState(useMergedConfigStore, ['mergedConfig']), }, watch: { 'attachment.description'(newVal) { @@ -186,7 +187,8 @@ const Attachment = { if ( this.mergedConfig.useOneClickNsfw && !this.showHidden && - (this.attachment.type !== 'video' || this.mergedConfig.playVideosInModal) + (this.attachment.type !== 'video' || + this.mergedConfig.playVideosInModal) ) { this.openModal(event) return diff --git a/src/components/chat_list_item/chat_list_item.js b/src/components/chat_list_item/chat_list_item.js index 202455c53..74df26c1f 100644 --- a/src/components/chat_list_item/chat_list_item.js +++ b/src/components/chat_list_item/chat_list_item.js @@ -25,9 +25,7 @@ const ChatListItem = { return } - const types = this.chat.lastMessage.attachments.map((file) => - file.type, - ) + const types = this.chat.lastMessage.attachments.map((file) => file.type) if (types.includes('video')) { return this.$t('file_type.video') } else if (types.includes('audio')) { diff --git a/src/components/chat_message/chat_message.js b/src/components/chat_message/chat_message.js index af3701ebf..1675f9ddd 100644 --- a/src/components/chat_message/chat_message.js +++ b/src/components/chat_message/chat_message.js @@ -12,6 +12,7 @@ import UserAvatar from '../user_avatar/user_avatar.vue' import { useInstanceStore } from 'src/stores/instance.js' import { useInterfaceStore } from 'src/stores/interface' +import { useMergedConfigStore } from 'src/stores/merged_config.js' import { library } from '@fortawesome/fontawesome-svg-core' import { faEllipsisH, faTimes } from '@fortawesome/free-solid-svg-icons' @@ -85,7 +86,7 @@ const ChatMessage = { return { left: 50 } } }, - ...mapGetters(['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 c486ad7e4..ffd3a3076 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 { useMergedConfigStore } from 'src/stores/merged_config.js' + export default { props: ['type', 'user', 'status'], emits: ['hide', 'show', 'muted'], @@ -43,7 +45,7 @@ export default { } } }, - ...mapGetters(['mergedConfig']), + ...mapState(useMergedConfigStore, ['mergedConfig']), }, methods: { optionallyPrompt() { diff --git a/src/components/conversation/conversation.js b/src/components/conversation/conversation.js index cb7cf4782..b0d3a304c 100644 --- a/src/components/conversation/conversation.js +++ b/src/components/conversation/conversation.js @@ -1,6 +1,6 @@ import { clone, filter, findIndex, get, reduce } from 'lodash' import { mapState as mapPiniaState } from 'pinia' -import { mapGetters, mapState } from 'vuex' +import { mapState } from 'vuex' import { WSConnectionStatus } from '../../services/api/api.service.js' import QuickFilterSettings from '../quick_filter_settings/quick_filter_settings.vue' @@ -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 { useMergedConfigStore } from 'src/stores/merged_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 = this.mergedConfig.maxDepthInThread - 2 return maxDepth >= 1 ? maxDepth : 1 }, streamingEnabled() { @@ -91,22 +92,22 @@ const conversation = { ) }, displayStyle() { - return this.$store.getters.mergedConfig.conversationDisplay + return this.mergedConfig.conversationDisplay }, isTreeView() { return !this.isLinearView }, treeViewIsSimple() { - return !this.$store.getters.mergedConfig.conversationTreeAdvanced + return !this.mergedConfig.conversationTreeAdvanced }, isLinearView() { return this.displayStyle === 'linear' }, shouldFadeAncestors() { - return this.$store.getters.mergedConfig.conversationTreeFadeAncestors + return this.mergedConfig.conversationTreeFadeAncestors }, otherRepliesButtonPosition() { - return this.$store.getters.mergedConfig.conversationOtherRepliesButton + return this.mergedConfig.conversationOtherRepliesButton }, showOtherRepliesButtonBelowStatus() { return this.otherRepliesButtonPosition === 'below' @@ -392,7 +393,7 @@ const conversation = { maybeHighlight() { return this.isExpanded ? this.highlight : null }, - ...mapGetters(['mergedConfig']), + ...mapPiniaState(useMergedConfigStore, ['mergedConfig']), ...mapState({ mastoUserSocketStatus: (state) => state.api.mastoUserSocketStatus, }), diff --git a/src/components/conversation/conversation.vue b/src/components/conversation/conversation.vue index 2f3de3a86..8d4734083 100644 --- a/src/components/conversation/conversation.vue +++ b/src/components/conversation/conversation.vue @@ -88,38 +88,34 @@ class="thread-ancestor" :class="{'thread-ancestor-has-other-replies': getReplies(status.id).length > 1, '-faded': shouldFadeAncestors}" > - { this.updateEmojiSize() }) - return this.$store.getters.mergedConfig.fontSize + return useMergedConfigStore().mergedConfig.fontSize }, emojiHeight() { return this.emojiSize @@ -405,7 +406,7 @@ const EmojiPicker = { }, languages() { return ensureFinalFallback( - this.$store.getters.mergedConfig.interfaceLanguage, + useMergedConfigStore().mergedConfig.interfaceLanguage, ) }, maybeLocalizedEmojiName() { diff --git a/src/components/emoji_reactions/emoji_reactions.js b/src/components/emoji_reactions/emoji_reactions.js index 8a121befc..e1a28a8f1 100644 --- a/src/components/emoji_reactions/emoji_reactions.js +++ b/src/components/emoji_reactions/emoji_reactions.js @@ -2,6 +2,8 @@ import StillImage from 'src/components/still-image/still-image.vue' import UserAvatar from '../user_avatar/user_avatar.vue' import UserListPopover from '../user_list_popover/user_list_popover.vue' +import { useInstanceStore } from 'src/stores/instance.js' + import { library } from '@fortawesome/fontawesome-svg-core' import { faCheck, faMinus, faPlus } from '@fortawesome/free-solid-svg-icons' @@ -42,7 +44,7 @@ const EmojiReactions = { return !!this.$store.state.users.currentUser }, remoteInteractionLink() { - return this.$store.getters.remoteInteractionLink({ + return useInstanceStore().getRemoteInteractionLink({ statusId: this.status.id, }) }, diff --git a/src/components/extra_notifications/extra_notifications.js b/src/components/extra_notifications/extra_notifications.js index 85f4f72bd..31ca57aaa 100644 --- a/src/components/extra_notifications/extra_notifications.js +++ b/src/components/extra_notifications/extra_notifications.js @@ -3,6 +3,8 @@ import { mapGetters } from 'vuex' import { useAnnouncementsStore } from 'src/stores/announcements.js' import { useInterfaceStore } from 'src/stores/interface.js' +import { useMergedConfigStore } from 'src/stores/merged_config.js' +import { useSyncConfigStore } from 'src/stores/sync_config.js' import { library } from '@fortawesome/fontawesome-svg-core' import { @@ -51,18 +53,19 @@ const ExtraNotifications = { currentUser() { return this.$store.state.users.currentUser }, - ...mapGetters(['unreadChatCount', 'followRequestCount', 'mergedConfig']), + ...mapGetters(['unreadChatCount', 'followRequestCount']), ...mapPiniaState(useAnnouncementsStore, { unreadAnnouncementCount: 'unreadAnnouncementCount', }), + ...mapPiniaState(useMergedConfigStore, ['mergedConfig']), }, methods: { openNotificationSettings() { return useInterfaceStore().openSettingsModalTab('notifications') }, dismissConfigurationTip() { - return this.$store.dispatch('setOption', { - name: 'showExtraNotificationsTip', + return useSyncConfigStore().setSimplePrefAndSave({ + path: 'showExtraNotificationsTip', value: false, }) }, 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/follow_button/follow_button.js b/src/components/follow_button/follow_button.js index eb545b28d..644f95f85 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 { useMergedConfigStore } from 'src/stores/merged_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 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 c037ddf42..a6ffcd28b 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 { useMergedConfigStore } from 'src/stores/merged_config.js' + const FollowRequestCard = { props: ['user'], components: { @@ -76,7 +78,7 @@ const FollowRequestCard = { }, computed: { mergedConfig() { - return this.$store.getters.mergedConfig + return useMergedConfigStore().mergedConfig }, shouldConfirmApprove() { return this.mergedConfig.modalOnApproveFollow diff --git a/src/components/font_control/font_control.js b/src/components/font_control/font_control.js index 45e5fde81..81c46ec89 100644 --- a/src/components/font_control/font_control.js +++ b/src/components/font_control/font_control.js @@ -1,5 +1,6 @@ import Checkbox from 'src/components/checkbox/checkbox.vue' import Popover from 'src/components/popover/popover.vue' +import LocalSettingIndicator from 'src/components/settings_modal/helpers/local_setting_indicator.vue' import Select from '../select/select.vue' import { useInterfaceStore } from 'src/stores/interface.js' @@ -18,6 +19,7 @@ export default { Select, Checkbox, Popover, + LocalSettingIndicator, }, props: ['name', 'label', 'modelValue', 'fallback', 'options', 'no-inherit'], mounted() { @@ -43,7 +45,7 @@ export default { }, computed: { present() { - return typeof this.modelValue !== 'undefined' + return this.modelValue != null }, localFontsList() { return useInterfaceStore().localFonts diff --git a/src/components/font_control/font_control.vue b/src/components/font_control/font_control.vue index 5124fd53a..62f8d052d 100644 --- a/src/components/font_control/font_control.vue +++ b/src/components/font_control/font_control.vue @@ -6,8 +6,10 @@ :id="name + '-o'" class="font-checkbox setting-control setting-label" :model-value="present" - @change="$emit('update:modelValue', typeof modelValue === 'undefined' ? fallback : undefined)" + @change="$emit('update:modelValue', modelValue == null ? fallback : null)" > + + {{ ' ' }} { - useListsStore() - .setListAccounts({ - listId: list.id, - accountIds: [...this.addedUserIds], - }) + useListsStore().setListAccounts({ + listId: list.id, + accountIds: [...this.addedUserIds], + }) return list.id }) .then((listId) => { diff --git a/src/components/media_upload/media_upload.js b/src/components/media_upload/media_upload.js index ee61fa369..2cb6a96e6 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 { useMergedConfigStore } from 'src/stores/merged_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 (!useMergedConfigStore().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 + !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 37c30355c..5edc89516 100644 --- a/src/components/mention_link/mention_link.js +++ b/src/components/mention_link/mention_link.js @@ -1,3 +1,4 @@ +import { mapState as mapPiniaState } from 'pinia' import { defineAsyncComponent } from 'vue' import { mapGetters, mapState } from 'vuex' @@ -8,6 +9,9 @@ import { import UnicodeDomainIndicator from '../unicode_domain_indicator/unicode_domain_indicator.vue' import UserAvatar from '../user_avatar/user_avatar.vue' +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' import { library } from '@fortawesome/fontawesome-svg-core' @@ -97,23 +101,23 @@ const MentionLink = { userNameFullUi() { return this.user && this.user.screen_name_ui }, - highlight() { - return this.user && this.mergedConfig.highlight[this.user.screen_name] + highlightData() { + return this.highlight[this.user?.screen_name] }, highlightType() { - return this.highlight && '-' + this.highlight.type + return this.highlightData && '-' + this.highlightData.type }, highlightClass() { - if (this.highlight) return highlightClass(this.user) + return this.highlightData && highlightClass(this.user) }, style() { - if (this.highlight) { + if (this.highlightData) { const { backgroundColor, backgroundPosition, backgroundImage, ...rest - } = highlightStyle(this.highlight) + } = highlightStyle(this.highlightData) return rest } }, @@ -121,7 +125,7 @@ const MentionLink = { return [ { '-you': this.isYou && this.shouldBoldenYou, - '-highlighted': this.highlight, + '-highlighted': !!this.highlightData, '-has-selection': this.hasSelection, }, this.highlightType, @@ -156,7 +160,8 @@ const MentionLink = { shouldFadeDomain() { return this.mergedConfig.mentionLinkFadeDomain }, - ...mapGetters(['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 e6aa392a0..88d2b3257 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 { useMergedConfigStore } from 'src/stores/merged_config.js' + export const MENTIONS_LIMIT = 5 const MentionsLine = { @@ -26,7 +28,7 @@ const MentionsLine = { manyMentions() { return this.extraMentions.length > 0 }, - ...mapGetters(['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 c5b1d66f6..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 { useServerSideStorageStore } from 'src/stores/serverSideStorage.js' +import { useMergedConfigStore } from 'src/stores/merged_config.js' import { library } from '@fortawesome/fontawesome-svg-core' import { @@ -52,11 +52,19 @@ const MobileNav = { return this.$store.state.users.currentUser }, unseenNotifications() { - return unseenNotificationsFromStore(this.$store) + return unseenNotificationsFromStore( + this.$store, + useMergedConfigStore().mergedConfig.notificationVisibility, + useMergedConfigStore().mergedConfig.ignoreInactionableSeen, + ) }, unseenNotificationsCount() { return ( - this.unseenNotifications.length + countExtraNotifications(this.$store) + this.unseenNotifications.length + + countExtraNotifications( + this.$store, + useMergedConfigStore().mergedConfig, + ) ) }, unseenCount() { @@ -75,15 +83,15 @@ const MobileNav = { return this.$route.name === 'chat' }, ...mapState(useAnnouncementsStore, ['unreadAnnouncementCount']), - ...mapState(useServerSideStorageStore, { + ...mapState(useMergedConfigStore, { pinnedItems: (store) => new Set(store.prefsStorage.collections.pinnedNavItems).has('chats'), }), shouldConfirmLogout() { - return this.$store.getters.mergedConfig.modalOnLogout + return useMergedConfigStore().mergedConfig.modalOnLogout }, closingDrawerMarksAsSeen() { - return this.$store.getters.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 83103c827..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,5 +1,6 @@ import { debounce } from 'lodash' +import { useMergedConfigStore } from 'src/stores/merged_config.js' import { usePostStatusStore } from 'src/stores/post_status.js' import { library } from '@fortawesome/fontawesome-svg-core' @@ -45,10 +46,10 @@ const MobilePostStatusButton = { ) }, isPersistent() { - return !!this.$store.getters.mergedConfig.alwaysShowNewPostButton + return !!useMergedConfigStore().mergedConfig.alwaysShowNewPostButton }, autohideFloatingPostButton() { - return !!this.$store.getters.mergedConfig.autohideFloatingPostButton + return !!useMergedConfigStore().mergedConfig.autohideFloatingPostButton }, }, watch: { diff --git a/src/components/mrf_transparency_panel/mrf_transparency_panel.js b/src/components/mrf_transparency_panel/mrf_transparency_panel.js index 2bd82f486..d77a0a839 100644 --- a/src/components/mrf_transparency_panel/mrf_transparency_panel.js +++ b/src/components/mrf_transparency_panel/mrf_transparency_panel.js @@ -1,5 +1,6 @@ import { get } from 'lodash' -import { mapState } from 'vuex' +import { mapState } from 'pinia' +import { useInstanceStore } from 'src/stores/instance.js' /** * This is for backwards compatibility. We originally didn't recieve @@ -18,64 +19,64 @@ const toInstanceReasonObject = (instances, info, key) => { const MRFTransparencyPanel = { computed: { - ...mapState({ - federationPolicy: (state) => get(state, 'instance.federationPolicy'), + ...mapState(useInstanceStore, { + federationPolicy: (state) => state.federationPolicy, mrfPolicies: (state) => - get(state, 'instance.federationPolicy.mrf_policies', []), + get(state, 'federationPolicy.mrf_policies', []), quarantineInstances: (state) => toInstanceReasonObject( - get(state, 'instance.federationPolicy.quarantined_instances', []), + get(state, 'federationPolicy.quarantined_instances', []), get( state, - 'instance.federationPolicy.quarantined_instances_info', + 'federationPolicy.quarantined_instances_info', [], ), 'quarantined_instances', ), acceptInstances: (state) => toInstanceReasonObject( - get(state, 'instance.federationPolicy.mrf_simple.accept', []), - get(state, 'instance.federationPolicy.mrf_simple_info', []), + get(state, 'federationPolicy.mrf_simple.accept', []), + get(state, 'federationPolicy.mrf_simple_info', []), 'accept', ), rejectInstances: (state) => toInstanceReasonObject( - get(state, 'instance.federationPolicy.mrf_simple.reject', []), - get(state, 'instance.federationPolicy.mrf_simple_info', []), + get(state, 'federationPolicy.mrf_simple.reject', []), + get(state, 'federationPolicy.mrf_simple_info', []), 'reject', ), ftlRemovalInstances: (state) => toInstanceReasonObject( get( state, - 'instance.federationPolicy.mrf_simple.federated_timeline_removal', + 'federationPolicy.mrf_simple.federated_timeline_removal', [], ), - get(state, 'instance.federationPolicy.mrf_simple_info', []), + get(state, 'federationPolicy.mrf_simple_info', []), 'federated_timeline_removal', ), mediaNsfwInstances: (state) => toInstanceReasonObject( - get(state, 'instance.federationPolicy.mrf_simple.media_nsfw', []), - get(state, 'instance.federationPolicy.mrf_simple_info', []), + get(state, 'federationPolicy.mrf_simple.media_nsfw', []), + get(state, 'federationPolicy.mrf_simple_info', []), 'media_nsfw', ), mediaRemovalInstances: (state) => toInstanceReasonObject( - get(state, 'instance.federationPolicy.mrf_simple.media_removal', []), - get(state, 'instance.federationPolicy.mrf_simple_info', []), + get(state, 'federationPolicy.mrf_simple.media_removal', []), + get(state, 'federationPolicy.mrf_simple_info', []), 'media_removal', ), keywordsFtlRemoval: (state) => get( state, - 'instance.federationPolicy.mrf_keyword.federated_timeline_removal', + 'federationPolicy.mrf_keyword.federated_timeline_removal', [], ), keywordsReject: (state) => - get(state, 'instance.federationPolicy.mrf_keyword.reject', []), + get(state, 'federationPolicy.mrf_keyword.reject', []), keywordsReplace: (state) => - get(state, 'instance.federationPolicy.mrf_keyword.replace', []), + get(state, 'federationPolicy.mrf_keyword.replace', []), }), hasInstanceSpecificPolicies() { return ( diff --git a/src/components/nav_panel/nav_panel.js b/src/components/nav_panel/nav_panel.js index 8aff31f02..ae6264217 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({ - path: 'simple.collapseNav', + useSyncConfigStore().setSimplePrefAndSave({ + path: '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/notification/notification.js b/src/components/notification/notification.js index 16084dee4..5f612cb9c 100644 --- a/src/components/notification/notification.js +++ b/src/components/notification/notification.js @@ -17,6 +17,8 @@ import UserLink from '../user_link/user_link.vue' import UserPopover from '../user_popover/user_popover.vue' import { useInstanceStore } from 'src/stores/instance.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' @@ -181,9 +183,8 @@ const Notification = { return highlightClass(this.notification.from_profile) }, userStyle() { - const highlight = this.$store.getters.mergedConfig.highlight - const user = this.notification.from_profile - return highlightStyle(highlight[user.screen_name]) + const user = this.notification.from_profile.screen_name + return highlightStyle(useUserHighlightStore().get(user)) }, expandable() { return new Set(['like', 'pleroma:emoji_reaction', 'repeat', 'poll']).has( @@ -209,7 +210,7 @@ const Notification = { return isStatusNotification(this.notification.type) }, mergedConfig() { - return this.$store.getters.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 7be8eb76b..9232930df 100644 --- a/src/components/notifications/notification_filters.vue +++ b/src/components/notifications/notification_filters.vue @@ -108,6 +108,9 @@