From edfaf5e80cb232ba6452d07614895c08c2e3978f Mon Sep 17 00:00:00 2001 From: Sean King Date: Tue, 4 Apr 2023 14:38:19 -0600 Subject: [PATCH 01/22] Add Pinia as dependency --- package.json | 1 + yarn.lock | 15 ++++++++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 721400ffa..0cba43903 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "localforage": "1.10.0", "parse-link-header": "2.0.0", "phoenix": "1.6.2", + "pinia": "^2.0.33", "punycode.js": "2.3.0", "qrcode": "1.5.0", "querystring-es3": "0.2.1", diff --git a/yarn.lock b/yarn.lock index 34e9664db..03c711d0b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2220,6 +2220,11 @@ resolved "https://registry.yarnpkg.com/@vue/devtools-api/-/devtools-api-6.4.5.tgz#d54e844c1adbb1e677c81c665ecef1a2b4bb8380" integrity sha512-JD5fcdIuFxU4fQyXUu3w2KpAJHzTVdN+p4iOX2lMWSHMOoQdMAcpFLZzm9Z/2nmsoZ1a96QEhZ26e50xLBsgOQ== +"@vue/devtools-api@^6.5.0": + version "6.5.0" + resolved "https://registry.yarnpkg.com/@vue/devtools-api/-/devtools-api-6.5.0.tgz#98b99425edee70b4c992692628fa1ea2c1e57d07" + integrity sha512-o9KfBeaBmCKl10usN4crU53fYtC1r7jJwdGKjPT24t348rHxgfpZ0xL3Xm/gLUYnc0oTp8LAmrxOeLyu6tbk2Q== + "@vue/reactivity-transform@3.2.45": version "3.2.45" resolved "https://registry.yarnpkg.com/@vue/reactivity-transform/-/reactivity-transform-3.2.45.tgz#07ac83b8138550c83dfb50db43cde1e0e5e8124d" @@ -7075,6 +7080,14 @@ pify@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" +pinia@^2.0.33: + version "2.0.33" + resolved "https://registry.yarnpkg.com/pinia/-/pinia-2.0.33.tgz#b70065be697874d5824e9792f59bd5d87ddb5e7d" + integrity sha512-HOj1yVV2itw6rNIrR2f7+MirGNxhORjrULL8GWgRwXsGSvEqIQ+SE0MYt6cwtpegzCda3i+rVTZM+AM7CG+kRg== + dependencies: + "@vue/devtools-api" "^6.5.0" + vue-demi "*" + pirates@^4.0.5: version "4.0.5" resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.5.tgz#feec352ea5c3268fb23a37c702ab1699f35a5f3b" @@ -8855,7 +8868,7 @@ void-elements@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-2.0.1.tgz#c066afb582bb1cb4128d60ea92392e94d5e9dbec" -vue-demi@^0.13.11: +vue-demi@*, vue-demi@^0.13.11: version "0.13.11" resolved "https://registry.yarnpkg.com/vue-demi/-/vue-demi-0.13.11.tgz#7d90369bdae8974d87b1973564ad390182410d99" integrity sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A== From aa98e83ff003df834e50665f7ac5f265751784ed Mon Sep 17 00:00:00 2001 From: Sean King Date: Tue, 4 Apr 2023 14:40:12 -0600 Subject: [PATCH 02/22] Move i18n to new store --- src/boot/after_store.js | 8 ++++++++ src/main.js | 6 +----- src/modules/config.js | 3 ++- .../notification_utils/notification_utils.js | 3 ++- src/stores/i18n.js | 14 ++++++++++++++ 5 files changed, 27 insertions(+), 7 deletions(-) create mode 100644 src/stores/i18n.js diff --git a/src/boot/after_store.js b/src/boot/after_store.js index 9c1f007bd..c4da8b4f7 100644 --- a/src/boot/after_store.js +++ b/src/boot/after_store.js @@ -1,4 +1,5 @@ import { createApp } from 'vue' +import { createPinia } from 'pinia' import { createRouter, createWebHistory } from 'vue-router' import vClickOutside from 'click-outside-vue3' import VueVirtualScroller from 'vue-virtual-scroller' @@ -17,6 +18,8 @@ import { CURRENT_VERSION } from '../services/theme_data/theme_data.service.js' import { applyTheme, applyConfig } from '../services/style_setter/style_setter.js' import FaviconService from '../services/favicon_service/favicon_service.js' +import { useI18nStore } from '../stores/i18n' + let staticInitialResults = null const parsedInitialResults = () => { @@ -395,6 +398,11 @@ const afterStoreSetup = async ({ store, i18n }) => { }) const app = createApp(App) + const pinia = createPinia() + + app.use(pinia) + + useI18nStore().setI18n(i18n) app.use(router) app.use(store) diff --git a/src/main.js b/src/main.js index d3e60a0fa..43869b01d 100644 --- a/src/main.js +++ b/src/main.js @@ -67,11 +67,6 @@ const persistedStateOptions = { } const store = createStore({ modules: { - i18n: { - getters: { - i18n: () => i18n.global - } - }, interface: interfaceModule, instance: instanceModule, // TODO refactor users/statuses modules, they depend on each other @@ -99,6 +94,7 @@ const persistedStateOptions = { strict: false // Socket modifies itself, let's ignore this for now. // strict: process.env.NODE_ENV !== 'production' }) + if (storageError) { store.dispatch('pushGlobalNotice', { messageKey: 'errors.storage_unavailable', level: 'error' }) } diff --git a/src/modules/config.js b/src/modules/config.js index 7597886e0..c60ff11eb 100644 --- a/src/modules/config.js +++ b/src/modules/config.js @@ -2,6 +2,7 @@ import Cookies from 'js-cookie' import { setPreset, applyTheme, applyConfig } from '../services/style_setter/style_setter.js' import messages from '../i18n/messages' import localeService from '../services/locale/locale.service.js' +import { useI18nStore } from '../stores/i18n.js' const BACKEND_LANGUAGE_COOKIE_NAME = 'userLanguage' @@ -194,7 +195,7 @@ const config = { applyTheme(value) break case 'interfaceLanguage': - messages.setLanguage(this.getters.i18n, value) + messages.setLanguage(useI18nStore().i18n, value) dispatch('loadUnicodeEmojiData', value) Cookies.set( BACKEND_LANGUAGE_COOKIE_NAME, diff --git a/src/services/notification_utils/notification_utils.js b/src/services/notification_utils/notification_utils.js index 0f8b9b029..aec753c46 100644 --- a/src/services/notification_utils/notification_utils.js +++ b/src/services/notification_utils/notification_utils.js @@ -1,6 +1,7 @@ import { filter, sortBy, includes } from 'lodash' import { muteWordHits } from '../status_parser/status_parser.js' import { showDesktopNotification } from '../desktop_notification_utils/desktop_notification_utils.js' +import { useI18nStore } from '../../stores/i18n.js' export const notificationsFromStore = store => store.state.statuses.notifications.data @@ -59,7 +60,7 @@ export const maybeShowNotification = (store, notification) => { if (!visibleTypes(store).includes(notification.type)) return if (notification.type === 'mention' && isMutedNotification(store, notification)) return - const notificationObject = prepareNotificationObject(notification, store.rootGetters.i18n) + const notificationObject = prepareNotificationObject(notification, useI18nStore().i18n) showDesktopNotification(rootState, notificationObject) } diff --git a/src/stores/i18n.js b/src/stores/i18n.js new file mode 100644 index 000000000..d038b30a4 --- /dev/null +++ b/src/stores/i18n.js @@ -0,0 +1,14 @@ +import { defineStore } from 'pinia' + +export const useI18nStore = defineStore('i18n', { + state: () => ({ + i18n: null + }), + actions: { + setI18n (newI18n) { + this.$patch({ + i18n: newI18n.global + }) + } + } +}) From aa6c13f9e60913639ff27d40009b045e7feb17ca Mon Sep 17 00:00:00 2001 From: Sean King Date: Tue, 4 Apr 2023 21:17:54 -0600 Subject: [PATCH 03/22] Move shout module to store --- src/App.js | 3 +- src/boot/after_store.js | 11 ++---- src/components/shout_panel/shout_panel.js | 5 ++- src/components/side_drawer/side_drawer.js | 3 +- src/main.js | 8 ++-- src/modules/api.js | 3 +- src/modules/shout.js | 46 ----------------------- src/stores/shout.js | 32 ++++++++++++++++ 8 files changed, 50 insertions(+), 61 deletions(-) delete mode 100644 src/modules/shout.js create mode 100644 src/stores/shout.js diff --git a/src/App.js b/src/App.js index b7eb2f72e..ac6885f34 100644 --- a/src/App.js +++ b/src/App.js @@ -17,6 +17,7 @@ import GlobalNoticeList from './components/global_notice_list/global_notice_list import { windowWidth, windowHeight } from './services/window_utils/window_utils' import { mapGetters } from 'vuex' import { defineAsyncComponent } from 'vue' +import { useShoutStore } from './stores/shout' export default { name: 'app', @@ -86,7 +87,7 @@ export default { } } }, - shout () { return this.$store.state.shout.joined }, + shout () { return useShoutStore().joined }, suggestionsEnabled () { return this.$store.state.instance.suggestionsEnabled }, showInstanceSpecificPanel () { return this.$store.state.instance.showInstanceSpecificPanel && diff --git a/src/boot/after_store.js b/src/boot/after_store.js index c4da8b4f7..bc9b9996d 100644 --- a/src/boot/after_store.js +++ b/src/boot/after_store.js @@ -1,5 +1,4 @@ import { createApp } from 'vue' -import { createPinia } from 'pinia' import { createRouter, createWebHistory } from 'vue-router' import vClickOutside from 'click-outside-vue3' import VueVirtualScroller from 'vue-virtual-scroller' @@ -341,7 +340,10 @@ const checkOAuthToken = async ({ store }) => { }) } -const afterStoreSetup = async ({ store, i18n }) => { +const afterStoreSetup = async ({ pinia, store, i18n }) => { + const app = createApp(App) + app.use(pinia) + store.dispatch('setLayoutWidth', windowWidth()) store.dispatch('setLayoutHeight', windowHeight()) @@ -397,11 +399,6 @@ const afterStoreSetup = async ({ store, i18n }) => { } }) - const app = createApp(App) - const pinia = createPinia() - - app.use(pinia) - useI18nStore().setI18n(i18n) app.use(router) diff --git a/src/components/shout_panel/shout_panel.js b/src/components/shout_panel/shout_panel.js index fb0c5aa21..4b8e8c8d7 100644 --- a/src/components/shout_panel/shout_panel.js +++ b/src/components/shout_panel/shout_panel.js @@ -4,6 +4,7 @@ import { faBullhorn, faTimes } from '@fortawesome/free-solid-svg-icons' +import { useShoutStore } from '../../stores/shout' library.add( faBullhorn, @@ -21,12 +22,12 @@ const shoutPanel = { }, computed: { messages () { - return this.$store.state.shout.messages + return useShoutStore().messages } }, methods: { submit (message) { - this.$store.state.shout.channel.push('new_msg', { text: message }, 10000) + useShoutStore().channel.push('new_msg', { text: message }, 10000) this.currentMessage = '' }, togglePanel () { diff --git a/src/components/side_drawer/side_drawer.js b/src/components/side_drawer/side_drawer.js index 270195775..a0ab0ccbd 100644 --- a/src/components/side_drawer/side_drawer.js +++ b/src/components/side_drawer/side_drawer.js @@ -19,6 +19,7 @@ import { faCompass, faList } from '@fortawesome/free-solid-svg-icons' +import { useShoutStore } from '../../stores/shout' library.add( faSignInAlt, @@ -54,7 +55,7 @@ const SideDrawer = { currentUser () { return this.$store.state.users.currentUser }, - shout () { return this.$store.state.shout.joined }, + shout () { return useShoutStore().joined }, unseenNotifications () { return unseenNotificationsFromStore(this.$store) }, diff --git a/src/main.js b/src/main.js index 43869b01d..ee0adcb61 100644 --- a/src/main.js +++ b/src/main.js @@ -1,4 +1,5 @@ import { createStore } from 'vuex' +import { createPinia } from 'pinia' import 'custom-event-polyfill' import './lib/event_target_polyfill.js' @@ -12,7 +13,6 @@ import apiModule from './modules/api.js' import configModule from './modules/config.js' import serverSideConfigModule from './modules/serverSideConfig.js' import serverSideStorageModule from './modules/serverSideStorage.js' -import shoutModule from './modules/shout.js' import oauthModule from './modules/oauth.js' import authFlowModule from './modules/auth_flow.js' import mediaViewerModule from './modules/media_viewer.js' @@ -58,6 +58,7 @@ const persistedStateOptions = { (async () => { let storageError = false const plugins = [pushNotifications] + const pinia = createPinia() try { const persistedState = await createPersistedState(persistedStateOptions) plugins.push(persistedState) @@ -77,7 +78,6 @@ const persistedStateOptions = { config: configModule, serverSideConfig: serverSideConfigModule, serverSideStorage: serverSideStorageModule, - shout: shoutModule, oauth: oauthModule, authFlow: authFlowModule, mediaViewer: mediaViewerModule, @@ -98,7 +98,9 @@ const persistedStateOptions = { if (storageError) { store.dispatch('pushGlobalNotice', { messageKey: 'errors.storage_unavailable', level: 'error' }) } - afterStoreSetup({ store, i18n }) + + // Temporarily passing both vuex and pinia stores until migration is fully complete. + afterStoreSetup({ pinia, store, i18n }) })() // These are inlined by webpack's DefinePlugin diff --git a/src/modules/api.js b/src/modules/api.js index fee584e84..d6cef55f9 100644 --- a/src/modules/api.js +++ b/src/modules/api.js @@ -2,6 +2,7 @@ import backendInteractorService from '../services/backend_interactor_service/bac import { WSConnectionStatus } from '../services/api/api.service.js' import { maybeShowChatNotification } from '../services/chat_utils/chat_utils.js' import { Socket } from 'phoenix' +import { useShoutStore } from '../stores/shout.js' const retryTimeout = (multiplier) => 1000 * multiplier @@ -283,7 +284,7 @@ const api = { socket.connect() commit('setSocket', socket) - dispatch('initializeShout', socket) + useShoutStore().initializeShout(socket) } }, disconnectFromSocket ({ commit, state }) { diff --git a/src/modules/shout.js b/src/modules/shout.js deleted file mode 100644 index 88aefbfea..000000000 --- a/src/modules/shout.js +++ /dev/null @@ -1,46 +0,0 @@ -const shout = { - state: { - messages: [], - channel: { state: '' }, - joined: false - }, - mutations: { - setChannel (state, channel) { - state.channel = channel - }, - addMessage (state, message) { - state.messages.push(message) - state.messages = state.messages.slice(-19, 20) - }, - setMessages (state, messages) { - state.messages = messages.slice(-19, 20) - }, - setJoined (state, joined) { - state.joined = joined - } - }, - actions: { - initializeShout (store, socket) { - const channel = socket.channel('chat:public') - channel.joinPush.receive('ok', () => { - store.commit('setJoined', true) - }) - channel.onClose(() => { - store.commit('setJoined', false) - }) - channel.onError(() => { - store.commit('setJoined', false) - }) - channel.on('new_msg', (msg) => { - store.commit('addMessage', msg) - }) - channel.on('messages', ({ messages }) => { - store.commit('setMessages', messages) - }) - channel.join() - store.commit('setChannel', channel) - } - } -} - -export default shout diff --git a/src/stores/shout.js b/src/stores/shout.js new file mode 100644 index 000000000..105e80e6f --- /dev/null +++ b/src/stores/shout.js @@ -0,0 +1,32 @@ +import { defineStore } from 'pinia' + +export const useShoutStore = defineStore('shout', { + state: () => ({ + messages: [], + channel: { state: '' }, + joined: false + }), + actions: { + initializeShout (socket) { + const channel = socket.channel('chat:public') + channel.joinPush.receive('ok', () => { + this.joined = true + }) + channel.onClose(() => { + this.joined = false + }) + channel.onError(() => { + this.joined = false + }) + channel.on('new_msg', (msg) => { + this.messages.push(msg) + this.messages = this.messages.slice(-19, 20) + }) + channel.on('messages', ({ messages }) => { + this.messages = messages.slice(-19, 20) + }) + channel.join() + this.channel = channel + } + } +}) From 27e36dbc2ef9664d0acccb47dee36fe94a9dd4b8 Mon Sep 17 00:00:00 2001 From: Sean King Date: Wed, 5 Apr 2023 13:01:37 -0600 Subject: [PATCH 04/22] Move postStatus module to store --- .../mobile_post_status_button.js | 3 ++- .../post_status_modal/post_status_modal.js | 7 +++--- src/components/user_card/user_card.js | 3 ++- src/main.js | 2 -- src/modules/postStatus.js | 25 ------------------- src/stores/postStatus.js | 17 +++++++++++++ 6 files changed, 25 insertions(+), 32 deletions(-) delete mode 100644 src/modules/postStatus.js create mode 100644 src/stores/postStatus.js 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 f7f96cd67..9a8af4551 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 @@ -3,6 +3,7 @@ import { library } from '@fortawesome/fontawesome-svg-core' import { faPen } from '@fortawesome/free-solid-svg-icons' +import { usePostStatusStore } from '../../stores/postStatus' library.add( faPen @@ -71,7 +72,7 @@ const MobilePostStatusButton = { window.removeEventListener('scroll', this.handleScrollEnd) }, openPostForm () { - this.$store.dispatch('openPostStatusModal') + usePostStatusStore().openPostStatusModal() }, handleOSK () { // This is a big hack: we're guessing from changed window sizes if the diff --git a/src/components/post_status_modal/post_status_modal.js b/src/components/post_status_modal/post_status_modal.js index b44354db1..fb7436ab7 100644 --- a/src/components/post_status_modal/post_status_modal.js +++ b/src/components/post_status_modal/post_status_modal.js @@ -1,6 +1,7 @@ import PostStatusForm from '../post_status_form/post_status_form.vue' import Modal from '../modal/modal.vue' import get from 'lodash/get' +import { usePostStatusStore } from '../../stores/postStatus' const PostStatusModal = { components: { @@ -17,13 +18,13 @@ const PostStatusModal = { return !!this.$store.state.users.currentUser }, modalActivated () { - return this.$store.state.postStatus.modalActivated + return usePostStatusStore().modalActivated }, isFormVisible () { return this.isLoggedIn && !this.resettingForm && this.modalActivated }, params () { - return this.$store.state.postStatus.params || {} + return usePostStatusStore().params || {} } }, watch: { @@ -43,7 +44,7 @@ const PostStatusModal = { }, methods: { closeModal () { - this.$store.dispatch('closePostStatusModal') + usePostStatusStore().closePostStatusModal() } } } diff --git a/src/components/user_card/user_card.js b/src/components/user_card/user_card.js index e17bf8eb2..ccbe9ce71 100644 --- a/src/components/user_card/user_card.js +++ b/src/components/user_card/user_card.js @@ -12,6 +12,7 @@ import RichContent from 'src/components/rich_content/rich_content.jsx' import ConfirmModal from '../confirm_modal/confirm_modal.vue' import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator' import { mapGetters } from 'vuex' +import { usePostStatusStore } from '../../stores/postStatus' import { library } from '@fortawesome/fontawesome-svg-core' import { faBell, @@ -225,7 +226,7 @@ export default { this.$store.dispatch('setCurrentMedia', attachment) }, mentionUser () { - this.$store.dispatch('openPostStatusModal', { replyTo: true, repliedUser: this.user }) + usePostStatusStore().openPostStatusModal({ replyTo: true, repliedUser: this.user }) }, onAvatarClickHandler (e) { if (this.onAvatarClick) { diff --git a/src/main.js b/src/main.js index ee0adcb61..4ba689292 100644 --- a/src/main.js +++ b/src/main.js @@ -19,7 +19,6 @@ import mediaViewerModule from './modules/media_viewer.js' import oauthTokensModule from './modules/oauth_tokens.js' import reportsModule from './modules/reports.js' import pollsModule from './modules/polls.js' -import postStatusModule from './modules/postStatus.js' import editStatusModule from './modules/editStatus.js' import statusHistoryModule from './modules/statusHistory.js' @@ -84,7 +83,6 @@ const persistedStateOptions = { oauthTokens: oauthTokensModule, reports: reportsModule, polls: pollsModule, - postStatus: postStatusModule, editStatus: editStatusModule, statusHistory: statusHistoryModule, chats: chatsModule, diff --git a/src/modules/postStatus.js b/src/modules/postStatus.js deleted file mode 100644 index 638c1fb20..000000000 --- a/src/modules/postStatus.js +++ /dev/null @@ -1,25 +0,0 @@ -const postStatus = { - state: { - params: null, - modalActivated: false - }, - mutations: { - openPostStatusModal (state, params) { - state.params = params - state.modalActivated = true - }, - closePostStatusModal (state) { - state.modalActivated = false - } - }, - actions: { - openPostStatusModal ({ commit }, params) { - commit('openPostStatusModal', params) - }, - closePostStatusModal ({ commit }) { - commit('closePostStatusModal') - } - } -} - -export default postStatus diff --git a/src/stores/postStatus.js b/src/stores/postStatus.js new file mode 100644 index 000000000..b9fe96d3c --- /dev/null +++ b/src/stores/postStatus.js @@ -0,0 +1,17 @@ +import { defineStore } from 'pinia' + +export const usePostStatusStore = defineStore('postStatus', { + state: () => ({ + params: null, + modalActivated: false + }), + actions: { + openPostStatusModal (params) { + this.params = params + this.modalActivated = true + }, + closePostStatusModal () { + this.modalActivated = false + } + } +}) From 3430604ddae7ef9af666d673d9a4e627b482f226 Mon Sep 17 00:00:00 2001 From: Sean King Date: Wed, 5 Apr 2023 13:23:25 -0600 Subject: [PATCH 05/22] Move editStatus module to store --- .../edit_status_modal/edit_status_modal.js | 9 ++++--- src/components/extra_buttons/extra_buttons.js | 3 ++- src/main.js | 2 -- src/modules/editStatus.js | 25 ------------------- src/stores/editStatus.js | 17 +++++++++++++ 5 files changed, 24 insertions(+), 32 deletions(-) delete mode 100644 src/modules/editStatus.js create mode 100644 src/stores/editStatus.js diff --git a/src/components/edit_status_modal/edit_status_modal.js b/src/components/edit_status_modal/edit_status_modal.js index 75adfea75..c39211510 100644 --- a/src/components/edit_status_modal/edit_status_modal.js +++ b/src/components/edit_status_modal/edit_status_modal.js @@ -2,6 +2,7 @@ import PostStatusForm from '../post_status_form/post_status_form.vue' import Modal from '../modal/modal.vue' import statusPosterService from '../../services/status_poster/status_poster.service.js' import get from 'lodash/get' +import { useEditStatusStore } from '../../stores/editStatus' const EditStatusModal = { components: { @@ -18,13 +19,13 @@ const EditStatusModal = { return !!this.$store.state.users.currentUser }, modalActivated () { - return this.$store.state.editStatus.modalActivated + return useEditStatusStore().modalActivated }, isFormVisible () { return this.isLoggedIn && !this.resettingForm && this.modalActivated }, params () { - return this.$store.state.editStatus.params || {} + return useEditStatusStore().params || {} } }, watch: { @@ -46,7 +47,7 @@ const EditStatusModal = { doEditStatus ({ status, spoilerText, sensitive, media, contentType, poll }) { const params = { store: this.$store, - statusId: this.$store.state.editStatus.params.statusId, + statusId: useEditStatusStore().params.statusId, status, spoilerText, sensitive, @@ -67,7 +68,7 @@ const EditStatusModal = { }) }, closeModal () { - this.$store.dispatch('closeEditStatusModal') + useEditStatusStore().closeEditStatusModal() } } } diff --git a/src/components/extra_buttons/extra_buttons.js b/src/components/extra_buttons/extra_buttons.js index 48b960b2b..4f3c98e13 100644 --- a/src/components/extra_buttons/extra_buttons.js +++ b/src/components/extra_buttons/extra_buttons.js @@ -16,6 +16,7 @@ import { faBookmark as faBookmarkReg, faFlag } from '@fortawesome/free-regular-svg-icons' +import { useEditStatusStore } from '../../stores/editStatus' library.add( faEllipsisH, @@ -107,7 +108,7 @@ const ExtraButtons = { }, editStatus () { this.$store.dispatch('fetchStatusSource', { id: this.status.id }) - .then(data => this.$store.dispatch('openEditStatusModal', { + .then(data => useEditStatusStore().openEditStatusModal({ statusId: this.status.id, subject: data.spoiler_text, statusText: data.text, diff --git a/src/main.js b/src/main.js index 4ba689292..622c9a71f 100644 --- a/src/main.js +++ b/src/main.js @@ -19,7 +19,6 @@ import mediaViewerModule from './modules/media_viewer.js' import oauthTokensModule from './modules/oauth_tokens.js' import reportsModule from './modules/reports.js' import pollsModule from './modules/polls.js' -import editStatusModule from './modules/editStatus.js' import statusHistoryModule from './modules/statusHistory.js' import chatsModule from './modules/chats.js' @@ -83,7 +82,6 @@ const persistedStateOptions = { oauthTokens: oauthTokensModule, reports: reportsModule, polls: pollsModule, - editStatus: editStatusModule, statusHistory: statusHistoryModule, chats: chatsModule, announcements: announcementsModule diff --git a/src/modules/editStatus.js b/src/modules/editStatus.js deleted file mode 100644 index fd3165191..000000000 --- a/src/modules/editStatus.js +++ /dev/null @@ -1,25 +0,0 @@ -const editStatus = { - state: { - params: null, - modalActivated: false - }, - mutations: { - openEditStatusModal (state, params) { - state.params = params - state.modalActivated = true - }, - closeEditStatusModal (state) { - state.modalActivated = false - } - }, - actions: { - openEditStatusModal ({ commit }, params) { - commit('openEditStatusModal', params) - }, - closeEditStatusModal ({ commit }) { - commit('closeEditStatusModal') - } - } -} - -export default editStatus diff --git a/src/stores/editStatus.js b/src/stores/editStatus.js new file mode 100644 index 000000000..9ead99192 --- /dev/null +++ b/src/stores/editStatus.js @@ -0,0 +1,17 @@ +import { defineStore } from 'pinia' + +export const useEditStatusStore = defineStore('editStatus', { + state: () => ({ + params: null, + modalActivated: false + }), + actions: { + openEditStatusModal (params) { + this.params = params + this.modalActivated = true + }, + closeEditStatusModal () { + this.modalActivated = false + } + } +}) From c25cfe540b8b879c8ffafaa2cd99f49723cfb703 Mon Sep 17 00:00:00 2001 From: Sean King Date: Wed, 5 Apr 2023 13:55:38 -0600 Subject: [PATCH 06/22] Move media_viewer module to store --- src/components/attachment/attachment.js | 5 ++- src/components/gallery/gallery.js | 7 ++-- src/components/media_modal/media_modal.js | 13 +++--- .../status_content/status_content.js | 3 +- src/components/user_card/user_card.js | 5 ++- src/main.js | 2 - src/modules/media_viewer.js | 40 ------------------- src/stores/media_viewer.js | 30 ++++++++++++++ 8 files changed, 49 insertions(+), 56 deletions(-) delete mode 100644 src/modules/media_viewer.js create mode 100644 src/stores/media_viewer.js diff --git a/src/components/attachment/attachment.js b/src/components/attachment/attachment.js index 6e14b24d7..deffe22cd 100644 --- a/src/components/attachment/attachment.js +++ b/src/components/attachment/attachment.js @@ -18,6 +18,7 @@ import { faPencilAlt, faAlignRight } from '@fortawesome/free-solid-svg-icons' +import { useMediaViewerStore } from '../../stores/media_viewer' library.add( faFile, @@ -147,14 +148,14 @@ const Attachment = { openModal (event) { if (this.useModal) { this.$emit('setMedia') - this.$store.dispatch('setCurrentMedia', this.attachment) + useMediaViewerStore().setCurrentMedia(this.attachment) } else if (this.type === 'unknown') { window.open(this.attachment.url) } }, openModalForce (event) { this.$emit('setMedia') - this.$store.dispatch('setCurrentMedia', this.attachment) + useMediaViewerStore().setCurrentMedia(this.attachment) }, onEdit (event) { this.edit && this.edit(this.attachment, event) diff --git a/src/components/gallery/gallery.js b/src/components/gallery/gallery.js index e86a3eeaf..ab1adcf2f 100644 --- a/src/components/gallery/gallery.js +++ b/src/components/gallery/gallery.js @@ -1,3 +1,4 @@ +import { useMediaViewerStore } from '../../stores/media_viewer' import Attachment from '../attachment/attachment.vue' import { sumBy, set } from 'lodash' @@ -107,11 +108,11 @@ const Gallery = { this.hidingLong = event }, openGallery () { - this.$store.dispatch('setMedia', this.attachments) - this.$store.dispatch('setCurrentMedia', this.attachments[0]) + useMediaViewerStore().setMedia(this.attachments) + useMediaViewerStore().setCurrentMedia(this.attachments[0]) }, onMedia () { - this.$store.dispatch('setMedia', this.attachments) + useMediaViewerStore().setMedia(this.attachments) } } } diff --git a/src/components/media_modal/media_modal.js b/src/components/media_modal/media_modal.js index 05ef9fbe1..1c19afcad 100644 --- a/src/components/media_modal/media_modal.js +++ b/src/components/media_modal/media_modal.js @@ -13,6 +13,7 @@ import { faCircleNotch, faTimes } from '@fortawesome/free-solid-svg-icons' +import { useMediaViewerStore } from '../../stores/media_viewer' library.add( faChevronLeft, @@ -44,16 +45,16 @@ const MediaModal = { }, computed: { showing () { - return this.$store.state.mediaViewer.activated + return useMediaViewerStore().activated }, media () { - return this.$store.state.mediaViewer.media + return useMediaViewerStore().media }, description () { return this.currentMedia.description }, currentIndex () { - return this.$store.state.mediaViewer.currentIndex + return useMediaViewerStore().currentIndex }, currentMedia () { return this.media[this.currentIndex] @@ -79,7 +80,7 @@ const MediaModal = { // to be processed on the content below the overlay const transitionTime = 100 // ms setTimeout(() => { - this.$store.dispatch('closeMediaViewer') + useMediaViewerStore().closeMediaViewer() }, transitionTime) }, hideIfNotSwiped (event) { @@ -98,7 +99,7 @@ const MediaModal = { if (this.getType(newMedia) === 'image') { this.loading = true } - this.$store.dispatch('setCurrentMedia', newMedia) + useMediaViewerStore().setCurrentMedia(newMedia) } }, goNext () { @@ -108,7 +109,7 @@ const MediaModal = { if (this.getType(newMedia) === 'image') { this.loading = true } - this.$store.dispatch('setCurrentMedia', newMedia) + useMediaViewerStore().setCurrentMedia(newMedia) } }, onImageLoaded () { diff --git a/src/components/status_content/status_content.js b/src/components/status_content/status_content.js index 89f0aa517..af62f9e14 100644 --- a/src/components/status_content/status_content.js +++ b/src/components/status_content/status_content.js @@ -13,6 +13,7 @@ import { faLink, faPollH } from '@fortawesome/free-solid-svg-icons' +import { useMediaViewerStore } from '../../stores/media_viewer' library.add( faCircleNotch, @@ -123,7 +124,7 @@ const StatusContent = { }, setMedia () { const attachments = this.attachmentSize === 'hide' ? this.status.attachments : this.galleryAttachments - return () => this.$store.dispatch('setMedia', attachments) + return () => useMediaViewerStore().setMedia(attachments) } } } diff --git a/src/components/user_card/user_card.js b/src/components/user_card/user_card.js index ccbe9ce71..c2db91040 100644 --- a/src/components/user_card/user_card.js +++ b/src/components/user_card/user_card.js @@ -23,6 +23,7 @@ import { faTimes, faExpandAlt } from '@fortawesome/free-solid-svg-icons' +import { useMediaViewerStore } from '../../stores/media_viewer' library.add( faRss, @@ -222,8 +223,8 @@ export default { url: this.user.profile_image_url_original, mimetype: 'image' } - this.$store.dispatch('setMedia', [attachment]) - this.$store.dispatch('setCurrentMedia', attachment) + useMediaViewerStore().setMedia([attachment]) + useMediaViewerStore().setCurrentMedia(attachment) }, mentionUser () { usePostStatusStore().openPostStatusModal({ replyTo: true, repliedUser: this.user }) diff --git a/src/main.js b/src/main.js index 622c9a71f..503467c4a 100644 --- a/src/main.js +++ b/src/main.js @@ -15,7 +15,6 @@ import serverSideConfigModule from './modules/serverSideConfig.js' import serverSideStorageModule from './modules/serverSideStorage.js' import oauthModule from './modules/oauth.js' import authFlowModule from './modules/auth_flow.js' -import mediaViewerModule from './modules/media_viewer.js' import oauthTokensModule from './modules/oauth_tokens.js' import reportsModule from './modules/reports.js' import pollsModule from './modules/polls.js' @@ -78,7 +77,6 @@ const persistedStateOptions = { serverSideStorage: serverSideStorageModule, oauth: oauthModule, authFlow: authFlowModule, - mediaViewer: mediaViewerModule, oauthTokens: oauthTokensModule, reports: reportsModule, polls: pollsModule, diff --git a/src/modules/media_viewer.js b/src/modules/media_viewer.js deleted file mode 100644 index ebcba01d4..000000000 --- a/src/modules/media_viewer.js +++ /dev/null @@ -1,40 +0,0 @@ -import fileTypeService from '../services/file_type/file_type.service.js' -const supportedTypes = new Set(['image', 'video', 'audio', 'flash']) - -const mediaViewer = { - state: { - media: [], - currentIndex: 0, - activated: false - }, - mutations: { - setMedia (state, media) { - state.media = media - }, - setCurrentMedia (state, index) { - state.activated = true - state.currentIndex = index - }, - close (state) { - state.activated = false - } - }, - actions: { - setMedia ({ commit }, attachments) { - const media = attachments.filter(attachment => { - const type = fileTypeService.fileType(attachment.mimetype) - return supportedTypes.has(type) - }) - commit('setMedia', media) - }, - setCurrentMedia ({ commit, state }, current) { - const index = state.media.indexOf(current) - commit('setCurrentMedia', index || 0) - }, - closeMediaViewer ({ commit }) { - commit('close') - } - } -} - -export default mediaViewer diff --git a/src/stores/media_viewer.js b/src/stores/media_viewer.js new file mode 100644 index 000000000..0e0e18289 --- /dev/null +++ b/src/stores/media_viewer.js @@ -0,0 +1,30 @@ +import { defineStore } from 'pinia' +import fileTypeService from '../services/file_type/file_type.service.js' + +const supportedTypes = new Set(['image', 'video', 'audio', 'flash']) + +export const useMediaViewerStore = defineStore('mediaViewer', { + state: () => ({ + media: [], + currentIndex: 0, + activated: false + }), + actions: { + setMedia (attachments) { + const media = attachments.filter(attachment => { + const type = fileTypeService.fileType(attachment.mimetype) + return supportedTypes.has(type) + }) + + this.media = media + }, + setCurrentMedia (current) { + const index = this.media.indexOf(current) + this.activated = true + this.currentIndex = index + }, + closeMediaViewer () { + this.activated = false + } + } +}) From 872569ae8e7c593f31a5d98356d89fdb7f024548 Mon Sep 17 00:00:00 2001 From: Sean King Date: Wed, 5 Apr 2023 14:13:28 -0600 Subject: [PATCH 07/22] Move statusHistory module to store --- src/components/extra_buttons/extra_buttons.js | 3 ++- .../status_history_modal.js | 7 +++--- src/main.js | 2 -- src/modules/statusHistory.js | 25 ------------------- src/stores/statusHistory.js | 17 +++++++++++++ 5 files changed, 23 insertions(+), 31 deletions(-) delete mode 100644 src/modules/statusHistory.js create mode 100644 src/stores/statusHistory.js diff --git a/src/components/extra_buttons/extra_buttons.js b/src/components/extra_buttons/extra_buttons.js index 4f3c98e13..045d8a5fa 100644 --- a/src/components/extra_buttons/extra_buttons.js +++ b/src/components/extra_buttons/extra_buttons.js @@ -17,6 +17,7 @@ import { faFlag } from '@fortawesome/free-regular-svg-icons' import { useEditStatusStore } from '../../stores/editStatus' +import { useStatusHistoryStore } from '../../stores/statusHistory' library.add( faEllipsisH, @@ -123,7 +124,7 @@ const ExtraButtons = { const originalStatus = { ...this.status } const stripFieldsList = ['attachments', 'created_at', 'emojis', 'text', 'raw_html', 'nsfw', 'poll', 'summary', 'summary_raw_html'] stripFieldsList.forEach(p => delete originalStatus[p]) - this.$store.dispatch('openStatusHistoryModal', originalStatus) + useStatusHistoryStore().openStatusHistoryModal(originalStatus) } }, computed: { diff --git a/src/components/status_history_modal/status_history_modal.js b/src/components/status_history_modal/status_history_modal.js index 3941a56f8..752a7c2e9 100644 --- a/src/components/status_history_modal/status_history_modal.js +++ b/src/components/status_history_modal/status_history_modal.js @@ -1,6 +1,7 @@ import { get } from 'lodash' import Modal from '../modal/modal.vue' import Status from '../status/status.vue' +import { useStatusHistoryStore } from '../../stores/statusHistory' const StatusHistoryModal = { components: { @@ -14,10 +15,10 @@ const StatusHistoryModal = { }, computed: { modalActivated () { - return this.$store.state.statusHistory.modalActivated + return useStatusHistoryStore().modalActivated }, params () { - return this.$store.state.statusHistory.params + return useStatusHistoryStore().params }, statusId () { return this.params.id @@ -52,7 +53,7 @@ const StatusHistoryModal = { }) }, closeModal () { - this.$store.dispatch('closeStatusHistoryModal') + useStatusHistoryStore().closeStatusHistoryModal() } } } diff --git a/src/main.js b/src/main.js index 503467c4a..8c291f547 100644 --- a/src/main.js +++ b/src/main.js @@ -18,7 +18,6 @@ import authFlowModule from './modules/auth_flow.js' import oauthTokensModule from './modules/oauth_tokens.js' import reportsModule from './modules/reports.js' import pollsModule from './modules/polls.js' -import statusHistoryModule from './modules/statusHistory.js' import chatsModule from './modules/chats.js' import announcementsModule from './modules/announcements.js' @@ -80,7 +79,6 @@ const persistedStateOptions = { oauthTokens: oauthTokensModule, reports: reportsModule, polls: pollsModule, - statusHistory: statusHistoryModule, chats: chatsModule, announcements: announcementsModule }, diff --git a/src/modules/statusHistory.js b/src/modules/statusHistory.js deleted file mode 100644 index db3d6d4be..000000000 --- a/src/modules/statusHistory.js +++ /dev/null @@ -1,25 +0,0 @@ -const statusHistory = { - state: { - params: {}, - modalActivated: false - }, - mutations: { - openStatusHistoryModal (state, params) { - state.params = params - state.modalActivated = true - }, - closeStatusHistoryModal (state) { - state.modalActivated = false - } - }, - actions: { - openStatusHistoryModal ({ commit }, params) { - commit('openStatusHistoryModal', params) - }, - closeStatusHistoryModal ({ commit }) { - commit('closeStatusHistoryModal') - } - } -} - -export default statusHistory diff --git a/src/stores/statusHistory.js b/src/stores/statusHistory.js new file mode 100644 index 000000000..0d3c54c89 --- /dev/null +++ b/src/stores/statusHistory.js @@ -0,0 +1,17 @@ +import { defineStore } from 'pinia' + +export const useStatusHistoryStore = defineStore('statusHistory', { + state: () => ({ + params: {}, + modalActivated: false + }), + actions: { + openStatusHistoryModal (params) { + this.params = params + this.modalActivated = true + }, + closeStatusHistoryModal () { + this.modalActivated = false + } + } +}) From b1dcea01995f7da4ccb1d293969734d81ee71a5e Mon Sep 17 00:00:00 2001 From: Sean King Date: Wed, 5 Apr 2023 21:06:37 -0600 Subject: [PATCH 08/22] Migrate interface module to store --- src/App.js | 7 +- src/boot/after_store.js | 11 +- src/components/chat/chat.js | 6 +- src/components/chat_message/chat_message.js | 6 +- src/components/desktop_nav/desktop_nav.js | 3 +- .../global_notice_list/global_notice_list.js | 5 +- src/components/lists_edit/lists_edit.js | 3 +- src/components/notification/notification.js | 3 +- src/components/notifications/notifications.js | 11 +- .../post_status_form/post_status_form.js | 10 +- .../quick_filter_settings.js | 3 +- .../quick_view_settings.js | 3 +- .../settings_modal/settings_modal.js | 19 +-- .../settings_modal/settings_modal_content.js | 9 +- .../settings_modal/tabs/profile_tab.js | 5 +- .../tabs/theme_tab/theme_tab.js | 3 +- src/components/side_drawer/side_drawer.js | 7 +- src/components/status/status.js | 3 +- src/components/tab_switcher/tab_switcher.jsx | 7 +- src/components/timeline/timeline.js | 7 +- src/components/timeline_menu/timeline_menu.js | 3 +- src/components/user_card/user_card.js | 5 +- src/lib/persisted_state.js | 5 +- src/lib/push_notifications_plugin.js | 4 +- src/main.js | 14 +- src/modules/api.js | 5 +- src/modules/config.js | 3 +- src/modules/instance.js | 3 +- src/modules/reports.js | 3 +- src/modules/users.js | 13 +- .../notifications_fetcher.service.js | 3 +- .../timeline_fetcher.service.js | 3 +- src/stores/interface.js | 126 ++++++++++++++++++ 33 files changed, 244 insertions(+), 77 deletions(-) create mode 100644 src/stores/interface.js diff --git a/src/App.js b/src/App.js index ac6885f34..f283b3f14 100644 --- a/src/App.js +++ b/src/App.js @@ -18,6 +18,7 @@ import { windowWidth, windowHeight } from './services/window_utils/window_utils' import { mapGetters } from 'vuex' import { defineAsyncComponent } from 'vue' import { useShoutStore } from './stores/shout' +import { useInterfaceStore } from './stores/interface' export default { name: 'app', @@ -113,7 +114,7 @@ export default { hideShoutbox () { return this.$store.getters.mergedConfig.hideShoutbox }, - layoutType () { return this.$store.state.interface.layoutType }, + layoutType () { return useInterfaceStore().layoutType }, privateMode () { return this.$store.state.instance.private }, reverseLayout () { const { thirdColumnMode, sidebarRight: reverseSetting } = this.$store.getters.mergedConfig @@ -129,8 +130,8 @@ export default { }, methods: { updateMobileState () { - this.$store.dispatch('setLayoutWidth', windowWidth()) - this.$store.dispatch('setLayoutHeight', windowHeight()) + useInterfaceStore().setLayoutWidth(windowWidth()) + useInterfaceStore().setLayoutHeight(windowHeight()) } } } diff --git a/src/boot/after_store.js b/src/boot/after_store.js index bc9b9996d..6a722aff7 100644 --- a/src/boot/after_store.js +++ b/src/boot/after_store.js @@ -18,6 +18,7 @@ import { applyTheme, applyConfig } from '../services/style_setter/style_setter.j import FaviconService from '../services/favicon_service/favicon_service.js' import { useI18nStore } from '../stores/i18n' +import { useInterfaceStore } from '../stores/interface' let staticInitialResults = null @@ -340,12 +341,16 @@ const checkOAuthToken = async ({ store }) => { }) } -const afterStoreSetup = async ({ pinia, store, i18n }) => { +const afterStoreSetup = async ({ pinia, store, storageError, i18n }) => { const app = createApp(App) app.use(pinia) - store.dispatch('setLayoutWidth', windowWidth()) - store.dispatch('setLayoutHeight', windowHeight()) + if (storageError) { + useInterfaceStore().pushGlobalNotice({ messageKey: 'errors.storage_unavailable', level: 'error' }) + } + + useInterfaceStore().setLayoutWidth(windowWidth()) + useInterfaceStore().setLayoutHeight(windowHeight()) FaviconService.initFaviconService() diff --git a/src/components/chat/chat.js b/src/components/chat/chat.js index 79f24771a..28c2be890 100644 --- a/src/components/chat/chat.js +++ b/src/components/chat/chat.js @@ -1,6 +1,7 @@ import _ from 'lodash' import { WSConnectionStatus } from '../../services/api/api.service.js' import { mapGetters, mapState } from 'vuex' +import { mapState as mapPiniaState } from 'pinia' import ChatMessage from '../chat_message/chat_message.vue' import PostStatusForm from '../post_status_form/post_status_form.vue' import ChatTitle from '../chat_title/chat_title.vue' @@ -13,6 +14,7 @@ import { faChevronLeft } from '@fortawesome/free-solid-svg-icons' import { buildFakeMessage } from '../../services/chat_utils/chat_utils.js' +import { useInterfaceStore } from '../../stores/interface.js' library.add( faChevronDown, @@ -90,10 +92,12 @@ const Chat = { 'findOpenedChatByRecipientId', 'mergedConfig' ]), + ...mapPiniaState(useInterfaceStore, { + mobileLayout: store => store.layoutType === 'mobile' + }), ...mapState({ backendInteractor: state => state.api.backendInteractor, mastoUserSocketStatus: state => state.api.mastoUserSocketStatus, - mobileLayout: state => state.interface.layoutType === 'mobile', currentUser: state => state.users.currentUser }) }, diff --git a/src/components/chat_message/chat_message.js b/src/components/chat_message/chat_message.js index ebe09814c..9d84811d0 100644 --- a/src/components/chat_message/chat_message.js +++ b/src/components/chat_message/chat_message.js @@ -1,4 +1,5 @@ import { mapState, mapGetters } from 'vuex' +import { mapState as mapPiniaState } from 'pinia' import Popover from '../popover/popover.vue' import Attachment from '../attachment/attachment.vue' import UserAvatar from '../user_avatar/user_avatar.vue' @@ -12,6 +13,7 @@ import { faTimes, faEllipsisH } from '@fortawesome/free-solid-svg-icons' +import { useInterfaceStore } from '../../stores/interface' library.add( faTimes, @@ -65,8 +67,10 @@ const ChatMessage = { hasAttachment () { return this.message.attachments.length > 0 }, + ...mapPiniaState(useInterfaceStore, { + betterShadow: store => store.browserSupport.cssFilter + }), ...mapState({ - betterShadow: state => state.interface.browserSupport.cssFilter, currentUser: state => state.users.currentUser, restrictedNicknames: state => state.instance.restrictedNicknames }), diff --git a/src/components/desktop_nav/desktop_nav.js b/src/components/desktop_nav/desktop_nav.js index 745b1a815..69ac7b73f 100644 --- a/src/components/desktop_nav/desktop_nav.js +++ b/src/components/desktop_nav/desktop_nav.js @@ -14,6 +14,7 @@ import { faCog, faInfoCircle } from '@fortawesome/free-solid-svg-icons' +import { useInterfaceStore } from '../../stores/interface' library.add( faSignInAlt, @@ -107,7 +108,7 @@ export default { this.searchBarHidden = hidden }, openSettingsModal () { - this.$store.dispatch('openSettingsModal') + useInterfaceStore().openSettingsModal() } } } diff --git a/src/components/global_notice_list/global_notice_list.js b/src/components/global_notice_list/global_notice_list.js index e93fba752..0d67eef79 100644 --- a/src/components/global_notice_list/global_notice_list.js +++ b/src/components/global_notice_list/global_notice_list.js @@ -2,6 +2,7 @@ import { library } from '@fortawesome/fontawesome-svg-core' import { faTimes } from '@fortawesome/free-solid-svg-icons' +import { useInterfaceStore } from '../../stores/interface' library.add( faTimes @@ -10,12 +11,12 @@ library.add( const GlobalNoticeList = { computed: { notices () { - return this.$store.state.interface.globalNotices + return useInterfaceStore().globalNotices } }, methods: { closeNotice (notice) { - this.$store.dispatch('removeGlobalNotice', notice) + useInterfaceStore().removeGlobalNotice(notice) } } } diff --git a/src/components/lists_edit/lists_edit.js b/src/components/lists_edit/lists_edit.js index c33659dfc..d929dbdfe 100644 --- a/src/components/lists_edit/lists_edit.js +++ b/src/components/lists_edit/lists_edit.js @@ -9,6 +9,7 @@ import { faSearch, faChevronLeft } from '@fortawesome/free-solid-svg-icons' +import { useInterfaceStore } from '../../stores/interface' library.add( faSearch, @@ -128,7 +129,7 @@ const ListsNew = { this.$router.push({ name: 'lists-timeline', params: { id: listId } }) }) .catch((e) => { - this.$store.dispatch('pushGlobalNotice', { + useInterfaceStore().pushGlobalNotice({ messageKey: 'lists.error', messageArgs: [e.message], level: 'error' diff --git a/src/components/notification/notification.js b/src/components/notification/notification.js index 420db4f0f..d5b7fd266 100644 --- a/src/components/notification/notification.js +++ b/src/components/notification/notification.js @@ -25,6 +25,7 @@ import { faExpandAlt, faCompressAlt } from '@fortawesome/free-solid-svg-icons' +import { useInterfaceStore } from '../../stores/interface' library.add( faCheck, @@ -43,7 +44,7 @@ const Notification = { data () { return { statusExpanded: false, - betterShadow: this.$store.state.interface.browserSupport.cssFilter, + betterShadow: useInterfaceStore().browserSupport.cssFilter, unmuted: false, showingApproveConfirmDialog: false, showingDenyConfirmDialog: false diff --git a/src/components/notifications/notifications.js b/src/components/notifications/notifications.js index d499d3d62..e334d5178 100644 --- a/src/components/notifications/notifications.js +++ b/src/components/notifications/notifications.js @@ -11,6 +11,7 @@ import { import FaviconService from '../../services/favicon_service/favicon_service.js' import { library } from '@fortawesome/fontawesome-svg-core' import { faCircleNotch, faArrowUp, faMinus } from '@fortawesome/free-solid-svg-icons' +import { useInterfaceStore } from '../../stores/interface' library.add( faCircleNotch, @@ -75,11 +76,11 @@ const Notifications = { return this.$store.state.statuses.notifications.loading }, noHeading () { - const { layoutType } = this.$store.state.interface + const { layoutType } = useInterfaceStore() return this.minimalMode || layoutType === 'mobile' }, teleportTarget () { - const { layoutType } = this.$store.state.interface + const { layoutType } = useInterfaceStore() const map = { wide: '#notifs-column', mobile: '#mobile-notifications' @@ -87,7 +88,7 @@ const Notifications = { return map[layoutType] || '#notifs-sidebar' }, popoversZLayer () { - const { layoutType } = this.$store.state.interface + const { layoutType } = useInterfaceStore() return layoutType === 'mobile' ? 'navbar' : null }, notificationsToDisplay () { @@ -114,10 +115,10 @@ const Notifications = { unseenCountTitle (count) { if (count > 0) { FaviconService.drawFaviconBadge() - this.$store.dispatch('setPageTitle', `(${count})`) + useInterfaceStore().setPageTitle(`(${count})`) } else { FaviconService.clearFaviconBadge() - this.$store.dispatch('setPageTitle', '') + useInterfaceStore().setPageTitle('') } }, teleportTarget () { diff --git a/src/components/post_status_form/post_status_form.js b/src/components/post_status_form/post_status_form.js index b75fee691..69eb21aff 100644 --- a/src/components/post_status_form/post_status_form.js +++ b/src/components/post_status_form/post_status_form.js @@ -11,7 +11,8 @@ import { findOffset } from '../../services/offset_finder/offset_finder.service.j import { propsToNative } from '../../services/attributes_helper/attributes_helper.service.js' import { reject, map, uniqBy, debounce } from 'lodash' import suggestor from '../emoji_input/suggestor.js' -import { mapGetters, mapState } from 'vuex' +import { mapGetters } from 'vuex' +import { mapState } from 'pinia' import Checkbox from '../checkbox/checkbox.vue' import Select from '../select/select.vue' @@ -24,6 +25,7 @@ import { faTimes, faCircleNotch } from '@fortawesome/free-solid-svg-icons' +import { useInterfaceStore } from '../../stores/interface.js' library.add( faSmileBeam, @@ -266,8 +268,8 @@ const PostStatusForm = { return typeof this.statusId !== 'undefined' && this.statusId.trim() !== '' }, ...mapGetters(['mergedConfig']), - ...mapState({ - mobileLayout: state => state.interface.mobileLayout + ...mapState(useInterfaceStore, { + mobileLayout: store => store.mobileLayout }) }, watch: { @@ -629,7 +631,7 @@ const PostStatusForm = { this.idempotencyKey = Date.now().toString() }, openProfileTab () { - this.$store.dispatch('openSettingsModalTab', 'profile') + useInterfaceStore().openSettingsModalTab('profile') }, propsToNative (props) { return propsToNative(props) diff --git a/src/components/quick_filter_settings/quick_filter_settings.js b/src/components/quick_filter_settings/quick_filter_settings.js index e67e3a4b1..6986ee555 100644 --- a/src/components/quick_filter_settings/quick_filter_settings.js +++ b/src/components/quick_filter_settings/quick_filter_settings.js @@ -2,6 +2,7 @@ import Popover from '../popover/popover.vue' import { mapGetters } from 'vuex' import { library } from '@fortawesome/fontawesome-svg-core' import { faFilter, faFont, faWrench } from '@fortawesome/free-solid-svg-icons' +import { useInterfaceStore } from '../../stores/interface' library.add( faFilter, @@ -22,7 +23,7 @@ const QuickFilterSettings = { this.$store.dispatch('queueFlushAll') }, openTab (tab) { - this.$store.dispatch('openSettingsModalTab', tab) + useInterfaceStore().openSettingsModalTab(tab) } }, computed: { diff --git a/src/components/quick_view_settings/quick_view_settings.js b/src/components/quick_view_settings/quick_view_settings.js index 2798f37ac..e1054dca0 100644 --- a/src/components/quick_view_settings/quick_view_settings.js +++ b/src/components/quick_view_settings/quick_view_settings.js @@ -2,6 +2,7 @@ import Popover from '../popover/popover.vue' import { mapGetters } from 'vuex' import { library } from '@fortawesome/fontawesome-svg-core' import { faList, faFolderTree, faBars, faWrench } from '@fortawesome/free-solid-svg-icons' +import { useInterfaceStore } from '../../stores/interface' library.add( faList, @@ -22,7 +23,7 @@ const QuickViewSettings = { this.$store.dispatch('setOption', { name: 'conversationDisplay', value: visibility }) }, openTab (tab) { - this.$store.dispatch('openSettingsModalTab', tab) + useInterfaceStore().openSettingsModalTab(tab) } }, computed: { diff --git a/src/components/settings_modal/settings_modal.js b/src/components/settings_modal/settings_modal.js index 0a72dca1e..95edfeb72 100644 --- a/src/components/settings_modal/settings_modal.js +++ b/src/components/settings_modal/settings_modal.js @@ -19,6 +19,7 @@ import { import { faWindowMinimize } from '@fortawesome/free-regular-svg-icons' +import { useInterfaceStore } from '../../stores/interface' const PLEROMAFE_SETTINGS_MAJOR_VERSION = 1 const PLEROMAFE_SETTINGS_MINOR_VERSION = 0 @@ -64,10 +65,10 @@ const SettingsModal = { }, methods: { closeModal () { - this.$store.dispatch('closeSettingsModal') + useInterfaceStore().closeSettingsModal() }, peekModal () { - this.$store.dispatch('togglePeekSettingsModal') + useInterfaceStore().togglePeekSettingsModal() }, importValidator (data) { if (!Array.isArray(data._pleroma_settings_version)) { @@ -99,7 +100,7 @@ const SettingsModal = { } if (minor > PLEROMAFE_SETTINGS_MINOR_VERSION) { - this.$store.dispatch('pushGlobalNotice', { + useInterfaceStore().pushGlobalNotice({ level: 'warning', messageKey: 'settings.file_export_import.errors.file_slightly_new' }) @@ -109,9 +110,9 @@ const SettingsModal = { }, onImportFailure (result) { if (result.error) { - this.$store.dispatch('pushGlobalNotice', { messageKey: 'settings.invalid_settings_imported', level: 'error' }) + useInterfaceStore().pushGlobalNotice({ messageKey: 'settings.invalid_settings_imported', level: 'error' }) } else { - this.$store.dispatch('pushGlobalNotice', { ...result.validationResult, level: 'error' }) + useInterfaceStore().pushGlobalNotice({ ...result.validationResult, level: 'error' }) } }, onImport (data) { @@ -151,16 +152,16 @@ const SettingsModal = { }, computed: { currentSaveStateNotice () { - return this.$store.state.interface.settings.currentSaveStateNotice + return useInterfaceStore().settings.currentSaveStateNotice }, modalActivated () { - return this.$store.state.interface.settingsModalState !== 'hidden' + return useInterfaceStore().settingsModalState !== 'hidden' }, modalOpenedOnce () { - return this.$store.state.interface.settingsModalLoaded + return useInterfaceStore().settingsModalLoaded }, modalPeeked () { - return this.$store.state.interface.settingsModalState === 'minimized' + return useInterfaceStore().settingsModalState === 'minimized' }, expertLevel: { get () { diff --git a/src/components/settings_modal/settings_modal_content.js b/src/components/settings_modal/settings_modal_content.js index 9ac0301f6..ca933f6a2 100644 --- a/src/components/settings_modal/settings_modal_content.js +++ b/src/components/settings_modal/settings_modal_content.js @@ -21,6 +21,7 @@ import { faEyeSlash, faInfo } from '@fortawesome/free-solid-svg-icons' +import { useInterfaceStore } from '../../stores/interface' library.add( faWrench, @@ -52,15 +53,15 @@ const SettingsModalContent = { return !!this.$store.state.users.currentUser }, open () { - return this.$store.state.interface.settingsModalState !== 'hidden' + return useInterfaceStore().settingsModalState !== 'hidden' }, bodyLock () { - return this.$store.state.interface.settingsModalState === 'visible' + return useInterfaceStore().settingsModalState === 'visible' } }, methods: { onOpen () { - const targetTab = this.$store.state.interface.settingsModalTargetTab + const targetTab = useInterfaceStore().settingsModalTargetTab // We're being told to open in specific tab if (targetTab) { const tabIndex = this.$refs.tabSwitcher.$slots.default().findIndex(elm => { @@ -72,7 +73,7 @@ const SettingsModalContent = { } // Clear the state of target tab, so that next time settings is opened // it doesn't force it. - this.$store.dispatch('clearSettingsModalTargetTab') + useInterfaceStore().clearSettingsModalTargetTab() } }, mounted () { diff --git a/src/components/settings_modal/tabs/profile_tab.js b/src/components/settings_modal/tabs/profile_tab.js index eeacad489..35b78d71f 100644 --- a/src/components/settings_modal/tabs/profile_tab.js +++ b/src/components/settings_modal/tabs/profile_tab.js @@ -20,6 +20,7 @@ import { faPlus, faCircleNotch } from '@fortawesome/free-solid-svg-icons' +import { useInterfaceStore } from '../../../stores/interface' library.add( faTimes, @@ -166,7 +167,7 @@ const ProfileTab = { if (file.size > this.$store.state.instance[slot + 'limit']) { const filesize = fileSizeFormatService.fileSizeFormat(file.size) const allowedsize = fileSizeFormatService.fileSizeFormat(this.$store.state.instance[slot + 'limit']) - this.$store.dispatch('pushGlobalNotice', { + useInterfaceStore().pushGlobalNotice({ messageKey: 'upload.error.message', messageArgs: [ this.$t('upload.error.file_too_big', { @@ -257,7 +258,7 @@ const ProfileTab = { .finally(() => { this.backgroundUploading = false }) }, displayUploadError (error) { - this.$store.dispatch('pushGlobalNotice', { + useInterfaceStore().pushGlobalNotice({ messageKey: 'upload.error.message', messageArgs: [error.message], level: 'error' diff --git a/src/components/settings_modal/tabs/theme_tab/theme_tab.js b/src/components/settings_modal/tabs/theme_tab/theme_tab.js index 4a739f737..47f6417cb 100644 --- a/src/components/settings_modal/tabs/theme_tab/theme_tab.js +++ b/src/components/settings_modal/tabs/theme_tab/theme_tab.js @@ -38,6 +38,7 @@ import Checkbox from 'src/components/checkbox/checkbox.vue' import Select from 'src/components/select/select.vue' import Preview from './preview.vue' +import { useInterfaceStore } from '../../../../stores/interface' // List of color values used in v1 const v1OnlyNames = [ @@ -548,7 +549,7 @@ export default { this.loadTheme(parsed, 'file', forceSource) }, onImportFailure (result) { - this.$store.dispatch('pushGlobalNotice', { messageKey: 'settings.invalid_theme_imported', level: 'error' }) + useInterfaceStore().pushGlobalNotice({ messageKey: 'settings.invalid_theme_imported', level: 'error' }) }, importValidator (parsed) { const version = parsed._pleroma_theme_version diff --git a/src/components/side_drawer/side_drawer.js b/src/components/side_drawer/side_drawer.js index a0ab0ccbd..5c50eef92 100644 --- a/src/components/side_drawer/side_drawer.js +++ b/src/components/side_drawer/side_drawer.js @@ -20,6 +20,7 @@ import { faList } from '@fortawesome/free-solid-svg-icons' import { useShoutStore } from '../../stores/shout' +import { useInterfaceStore } from '../../stores/interface' library.add( faSignInAlt, @@ -85,8 +86,8 @@ const SideDrawer = { }, timelinesRoute () { let name - if (this.$store.state.interface.lastTimeline) { - name = this.$store.state.interface.lastTimeline + if (useInterfaceStore().lastTimeline) { + name = useInterfaceStore().lastTimeline } name = this.currentUser ? 'friends' : 'public-timeline' if (USERNAME_ROUTES.has(name)) { @@ -116,7 +117,7 @@ const SideDrawer = { GestureService.updateSwipe(e, this.closeGesture) }, openSettingsModal () { - this.$store.dispatch('openSettingsModal') + useInterfaceStore().openSettingsModal() } } } diff --git a/src/components/status/status.js b/src/components/status/status.js index 9a9bca7aa..bb86e8c9a 100644 --- a/src/components/status/status.js +++ b/src/components/status/status.js @@ -20,6 +20,7 @@ import generateProfileLink from 'src/services/user_profile_link_generator/user_p import { highlightClass, highlightStyle } from '../../services/user_highlighter/user_highlighter.js' import { muteWordHits } from '../../services/status_parser/status_parser.js' import { unescape, uniqBy } from 'lodash' +import { useInterfaceStore } from '../../stores/interface' import { library } from '@fortawesome/fontawesome-svg-core' import { @@ -379,7 +380,7 @@ const Status = { return this.$store.state.users.currentUser }, betterShadow () { - return this.$store.state.interface.browserSupport.cssFilter + return useInterfaceStore().browserSupport.cssFilter }, mergedConfig () { return this.$store.getters.mergedConfig diff --git a/src/components/tab_switcher/tab_switcher.jsx b/src/components/tab_switcher/tab_switcher.jsx index a7ef8560a..cbc7809aa 100644 --- a/src/components/tab_switcher/tab_switcher.jsx +++ b/src/components/tab_switcher/tab_switcher.jsx @@ -1,9 +1,10 @@ // eslint-disable-next-line no-unused import { h, Fragment } from 'vue' -import { mapState } from 'vuex' +import { mapState } from 'pinia' import { FontAwesomeIcon as FAIcon } from '@fortawesome/vue-fontawesome' import './tab_switcher.scss' +import { useInterfaceStore } from '../../stores/interface' const findFirstUsable = (slots) => slots.findIndex(_ => _.props) @@ -64,8 +65,8 @@ export default { settingsModalVisible () { return this.settingsModalState === 'visible' }, - ...mapState({ - settingsModalState: state => state.interface.settingsModalState + ...mapState(useInterfaceStore, { + settingsModalState: store => store.settingsModalState }) }, beforeUpdate () { diff --git a/src/components/timeline/timeline.js b/src/components/timeline/timeline.js index b74146109..e2f4d0336 100644 --- a/src/components/timeline/timeline.js +++ b/src/components/timeline/timeline.js @@ -1,5 +1,5 @@ import Status from '../status/status.vue' -import { mapState } from 'vuex' +import { mapState } from 'pinia' import timelineFetcher from '../../services/timeline_fetcher/timeline_fetcher.service.js' import Conversation from '../conversation/conversation.vue' import TimelineMenu from '../timeline_menu/timeline_menu.vue' @@ -8,6 +8,7 @@ import QuickViewSettings from '../quick_view_settings/quick_view_settings.vue' import { debounce, throttle, keyBy } from 'lodash' import { library } from '@fortawesome/fontawesome-svg-core' import { faCircleNotch, faCirclePlus, faCog, faMinus, faArrowUp, faCheck } from '@fortawesome/free-solid-svg-icons' +import { useInterfaceStore } from '../../stores/interface' library.add( faCircleNotch, @@ -101,8 +102,8 @@ const Timeline = { virtualScrollingEnabled () { return this.$store.getters.mergedConfig.virtualScrolling }, - ...mapState({ - mobileLayout: state => state.interface.layoutType === 'mobile' + ...mapState(useInterfaceStore, { + mobileLayout: store => store.layoutType === 'mobile' }) }, created () { diff --git a/src/components/timeline_menu/timeline_menu.js b/src/components/timeline_menu/timeline_menu.js index 5a2a86c2f..a9e7893cf 100644 --- a/src/components/timeline_menu/timeline_menu.js +++ b/src/components/timeline_menu/timeline_menu.js @@ -8,6 +8,7 @@ import { filterNavigation } from 'src/components/navigation/filter.js' import { faChevronDown } from '@fortawesome/free-solid-svg-icons' +import { useInterfaceStore } from '../../stores/interface' library.add(faChevronDown) @@ -36,7 +37,7 @@ const TimelineMenu = { }, created () { if (timelineNames()[this.$route.name]) { - this.$store.dispatch('setLastTimeline', this.$route.name) + useInterfaceStore().setLastTimeline(this.$route.name) } }, computed: { diff --git a/src/components/user_card/user_card.js b/src/components/user_card/user_card.js index c2db91040..1bc653378 100644 --- a/src/components/user_card/user_card.js +++ b/src/components/user_card/user_card.js @@ -24,6 +24,7 @@ import { faExpandAlt } from '@fortawesome/free-solid-svg-icons' import { useMediaViewerStore } from '../../stores/media_viewer' +import { useInterfaceStore } from '../../stores/interface' library.add( faRss, @@ -50,7 +51,7 @@ export default { data () { return { followRequestInProgress: false, - betterShadow: this.$store.state.interface.browserSupport.cssFilter, + betterShadow: useInterfaceStore().browserSupport.cssFilter, showingConfirmMute: false, muteExpiryAmount: 0, muteExpiryUnit: 'minutes' @@ -216,7 +217,7 @@ export default { ) }, openProfileTab () { - this.$store.dispatch('openSettingsModalTab', 'profile') + useInterfaceStore().openSettingsModalTab('profile') }, zoomAvatar () { const attachment = { diff --git a/src/lib/persisted_state.js b/src/lib/persisted_state.js index 6d59c5955..ab9a79b39 100644 --- a/src/lib/persisted_state.js +++ b/src/lib/persisted_state.js @@ -1,6 +1,7 @@ import merge from 'lodash.merge' import localforage from 'localforage' import { each, get, set, cloneDeep } from 'lodash' +import { useInterfaceStore } from '../stores/interface' let loaded = false @@ -76,12 +77,12 @@ export default function createPersistedState ({ .then(success => { if (typeof success !== 'undefined') { if (mutation.type === 'setOption' || mutation.type === 'setCurrentUser') { - store.dispatch('settingsSaved', { success }) + useInterfaceStore().settingsSaved({ success }) } } }, error => { if (mutation.type === 'setOption' || mutation.type === 'setCurrentUser') { - store.dispatch('settingsSaved', { error }) + useInterfaceStore().settingsSaved({ error }) } }) } diff --git a/src/lib/push_notifications_plugin.js b/src/lib/push_notifications_plugin.js index f75bb8230..57258b4e8 100644 --- a/src/lib/push_notifications_plugin.js +++ b/src/lib/push_notifications_plugin.js @@ -1,8 +1,10 @@ +import { useInterfaceStore } from '../stores/interface' + export default (store) => { store.subscribe((mutation, state) => { const vapidPublicKey = state.instance.vapidPublicKey const webPushNotification = state.config.webPushNotifications - const permission = state.interface.notificationPermission === 'granted' + const permission = useInterfaceStore().notificationPermission === 'granted' const user = state.users.currentUser const isUserMutation = mutation.type === 'setCurrentUser' diff --git a/src/main.js b/src/main.js index 8c291f547..030d70e65 100644 --- a/src/main.js +++ b/src/main.js @@ -4,7 +4,6 @@ import { createPinia } from 'pinia' import 'custom-event-polyfill' import './lib/event_target_polyfill.js' -import interfaceModule from './modules/interface.js' import instanceModule from './modules/instance.js' import statusesModule from './modules/statuses.js' import listsModule from './modules/lists.js' @@ -62,9 +61,10 @@ const persistedStateOptions = { console.error(e) storageError = true } - const store = createStore({ + + // Temporarily storing as a global variable while we migrate to Pinia + window.vuex = createStore({ modules: { - interface: interfaceModule, instance: instanceModule, // TODO refactor users/statuses modules, they depend on each other users: usersModule, @@ -87,12 +87,10 @@ const persistedStateOptions = { // strict: process.env.NODE_ENV !== 'production' }) - if (storageError) { - store.dispatch('pushGlobalNotice', { messageKey: 'errors.storage_unavailable', level: 'error' }) - } + const store = window.vuex - // Temporarily passing both vuex and pinia stores until migration is fully complete. - afterStoreSetup({ pinia, store, i18n }) + // Temporarily passing pinia and vuex stores along with storageError result until migration is fully complete. + afterStoreSetup({ pinia, store, storageError, i18n }) })() // These are inlined by webpack's DefinePlugin diff --git a/src/modules/api.js b/src/modules/api.js index d6cef55f9..0000ddc8e 100644 --- a/src/modules/api.js +++ b/src/modules/api.js @@ -3,6 +3,7 @@ import { WSConnectionStatus } from '../services/api/api.service.js' import { maybeShowChatNotification } from '../services/chat_utils/chat_utils.js' import { Socket } from 'phoenix' import { useShoutStore } from '../stores/shout.js' +import { useInterfaceStore } from '../stores/interface.js' const retryTimeout = (multiplier) => 1000 * multiplier @@ -132,7 +133,7 @@ const api = { state.mastoUserSocket.addEventListener('open', () => { // Do not show notification when we just opened up the page if (state.mastoUserSocketStatus !== WSConnectionStatus.STARTING_INITIAL) { - dispatch('pushGlobalNotice', { + useInterfaceStore().pushGlobalNotice({ level: 'success', messageKey: 'timeline.socket_reconnected', timeout: 5000 @@ -174,7 +175,7 @@ const api = { dispatch('startFetchingTimeline', { timeline: 'friends' }) dispatch('startFetchingNotifications') dispatch('startFetchingChats') - dispatch('pushGlobalNotice', { + useInterfaceStore().pushGlobalNotice({ level: 'error', messageKey: 'timeline.socket_broke', messageArgs: [code], diff --git a/src/modules/config.js b/src/modules/config.js index c60ff11eb..bbabebad6 100644 --- a/src/modules/config.js +++ b/src/modules/config.js @@ -3,6 +3,7 @@ import { setPreset, applyTheme, applyConfig } from '../services/style_setter/sty import messages from '../i18n/messages' import localeService from '../services/locale/locale.service.js' import { useI18nStore } from '../stores/i18n.js' +import { useInterfaceStore } from '../stores/interface.js' const BACKEND_LANGUAGE_COOKIE_NAME = 'userLanguage' @@ -203,7 +204,7 @@ const config = { ) break case 'thirdColumnMode': - dispatch('setLayoutWidth', undefined) + useInterfaceStore().setLayoutWidth(undefined) break } } diff --git a/src/modules/instance.js b/src/modules/instance.js index bb0292da0..4a417d831 100644 --- a/src/modules/instance.js +++ b/src/modules/instance.js @@ -3,6 +3,7 @@ import { CURRENT_VERSION } from '../services/theme_data/theme_data.service.js' import apiService from '../services/api/api.service.js' import { instanceDefaultProperties } from './config.js' import { langCodeToCldrName, ensureFinalFallback } from '../i18n/languages.js' +import { useInterfaceStore } from '../stores/interface.js' const SORTED_EMOJI_GROUP_IDS = [ 'smileys-and-emotion', @@ -261,7 +262,7 @@ const instance = { commit('setInstanceOption', { name, value }) switch (name) { case 'name': - dispatch('setPageTitle') + useInterfaceStore().setPageTitle() break case 'shoutAvailable': if (value) { diff --git a/src/modules/reports.js b/src/modules/reports.js index 925792c03..c75377cd3 100644 --- a/src/modules/reports.js +++ b/src/modules/reports.js @@ -1,4 +1,5 @@ import filter from 'lodash/filter' +import { useInterfaceStore } from '../stores/interface' const reports = { state: { @@ -46,7 +47,7 @@ const reports = { commit('setReportState', { id, state }) rootState.api.backendInteractor.setReportState({ id, state }).catch(e => { console.error('Failed to set report state', e) - dispatch('pushGlobalNotice', { + useInterfaceStore().pushGlobalNotice({ level: 'error', messageKey: 'general.generic_error_message', messageArgs: [e.message], diff --git a/src/modules/users.js b/src/modules/users.js index a1316ba25..e2204bb19 100644 --- a/src/modules/users.js +++ b/src/modules/users.js @@ -3,6 +3,7 @@ import { windowWidth, windowHeight } from '../services/window_utils/window_utils import oauthApi from '../services/new_api/oauth.js' import { compact, map, each, mergeWith, last, concat, uniq, isArray } from 'lodash' import { registerPushNotifications, unregisterPushNotifications } from '../services/push/push.js' +import { useInterfaceStore } from '../stores/interface.js' // TODO: Unify with mergeOrAdd in statuses.js export const mergeOrAdd = (arr, obj, item) => { @@ -542,9 +543,9 @@ const users = { store.commit('clearNotifications') store.commit('resetStatuses') store.dispatch('resetChats') - store.dispatch('setLastTimeline', 'public-timeline') - store.dispatch('setLayoutWidth', windowWidth()) - store.dispatch('setLayoutHeight', windowHeight()) + useInterfaceStore().setLastTimeline('public-timeline') + useInterfaceStore().setLayoutWidth(windowWidth()) + useInterfaceStore().setLayoutHeight(windowHeight()) store.commit('clearServerSideStorage') }) }, @@ -568,7 +569,7 @@ const users = { store.dispatch('fetchEmoji') getNotificationPermission() - .then(permission => commit('setNotificationPermission', permission)) + .then(permission => useInterfaceStore().setNotificationPermission(permission)) // Set our new backend interactor commit('setBackendInteractor', backendInteractorService(accessToken)) @@ -614,8 +615,8 @@ const users = { // Get user mutes store.dispatch('fetchMutes') - store.dispatch('setLayoutWidth', windowWidth()) - store.dispatch('setLayoutHeight', windowHeight()) + useInterfaceStore().setLayoutWidth(windowWidth()) + useInterfaceStore().setLayoutHeight(windowHeight()) // Fetch our friends store.rootState.api.backendInteractor.fetchFriends({ id: user.id }) diff --git a/src/services/notifications_fetcher/notifications_fetcher.service.js b/src/services/notifications_fetcher/notifications_fetcher.service.js index 6c2472107..3b4f9f7b1 100644 --- a/src/services/notifications_fetcher/notifications_fetcher.service.js +++ b/src/services/notifications_fetcher/notifications_fetcher.service.js @@ -1,3 +1,4 @@ +import { useInterfaceStore } from '../../stores/interface.js' import apiService from '../api/api.service.js' import { promiseInterval } from '../promise_interval/promise_interval.js' @@ -70,7 +71,7 @@ const fetchNotifications = ({ store, args, older }) => { return notifications }) .catch((error) => { - store.dispatch('pushGlobalNotice', { + useInterfaceStore().pushGlobalNotice({ level: 'error', messageKey: 'notifications.error', messageArgs: [error.message], diff --git a/src/services/timeline_fetcher/timeline_fetcher.service.js b/src/services/timeline_fetcher/timeline_fetcher.service.js index 8501907e4..c79590dff 100644 --- a/src/services/timeline_fetcher/timeline_fetcher.service.js +++ b/src/services/timeline_fetcher/timeline_fetcher.service.js @@ -2,6 +2,7 @@ import { camelCase } from 'lodash' import apiService from '../api/api.service.js' import { promiseInterval } from '../promise_interval/promise_interval.js' +import { useInterfaceStore } from '../../stores/interface.js' const update = ({ store, statuses, timeline, showImmediately, userId, listId, pagination }) => { const ccTimeline = camelCase(timeline) @@ -69,7 +70,7 @@ const fetchAndUpdate = ({ return { statuses, pagination } }) .catch((error) => { - store.dispatch('pushGlobalNotice', { + useInterfaceStore().pushGlobalNotice({ level: 'error', messageKey: 'timeline.error', messageArgs: [error.message], diff --git a/src/stores/interface.js b/src/stores/interface.js new file mode 100644 index 000000000..7a7195d39 --- /dev/null +++ b/src/stores/interface.js @@ -0,0 +1,126 @@ +import { defineStore } from 'pinia' + +export const useInterfaceStore = defineStore('interface', { + state: () => ({ + settingsModalState: 'hidden', + settingsModalLoaded: false, + settingsModalTargetTab: null, + settings: { + currentSaveStateNotice: null, + noticeClearTimeout: null, + notificationPermission: null + }, + browserSupport: { + cssFilter: window.CSS && window.CSS.supports && ( + window.CSS.supports('filter', 'drop-shadow(0 0)') || + window.CSS.supports('-webkit-filter', 'drop-shadow(0 0)') + ) + }, + layoutType: 'normal', + globalNotices: [], + layoutHeight: 0, + lastTimeline: null + }), + actions: { + setPageTitle (option = '') { + try { + document.title = `${option} ${window.vuex.state.instance.name}` + } catch (error) { + console.error(`${error}`) + } + }, + settingsSaved ({ success, error }) { + if (success) { + if (this.noticeClearTimeout) { + clearTimeout(this.noticeClearTimeout) + } + this.settings.currentSaveStateNotice = { error: false, data: success } + this.settings.noticeClearTimeout = setTimeout(() => delete this.settings.currentSaveStateNotice, 2000) + } else { + this.settings.currentSaveStateNotice = { error: true, errorData: error } + } + }, + setNotificationPermission (permission) { + this.notificationPermission = permission + }, + closeSettingsModal () { + this.settingsModalState = 'hidden' + }, + openSettingsModal () { + this.settingsModalState = 'visible' + if (!this.settingsModalLoaded) { + this.settingsModalLoaded = true + } + }, + togglePeekSettingsModal () { + switch (this.settingsModalState) { + case 'minimized': + this.settingsModalState = 'visible' + return + case 'visible': + this.settingsModalState = 'minimized' + return + default: + throw new Error('Illegal minimization state of settings modal') + } + }, + clearSettingsModalTargetTab () { + this.settingsModalTargetTab = null + }, + openSettingsModalTab (value) { + this.settingsModalTargetTab = value + this.openSettingsModal() + }, + removeGlobalNotice (notice) { + this.globalNotices = this.globalNotices.filter(n => n !== notice) + }, + pushGlobalNotice ( + { + messageKey, + messageArgs = {}, + level = 'error', + timeout = 0 + }) { + const notice = { + messageKey, + messageArgs, + level + } + + this.globalNotices.push(notice) + + // Adding a new element to array wraps it in a Proxy, which breaks the comparison + // TODO: Generate UUID or something instead or relying on !== operator? + const newNotice = this.globalNotices[this.globalNotices.length - 1] + if (timeout) { + setTimeout(() => this.removeGlobalNotice(newNotice), timeout) + } + + return newNotice + }, + setLayoutHeight (value) { + this.layoutHeight = value + }, + setLayoutWidth (value) { + let width = value + if (value !== undefined) { + this.layoutWidth = value + } else { + width = this.layoutWidth + } + + const mobileLayout = width <= 800 + const normalOrMobile = mobileLayout ? 'mobile' : 'normal' + const { thirdColumnMode } = window.vuex.getters.mergedConfig + if (thirdColumnMode === 'none' || !window.vuex.state.users.currentUser) { + this.layoutType = normalOrMobile + } else { + const wideLayout = width >= 1300 + this.layoutType = wideLayout ? 'wide' : normalOrMobile + } + }, + setLastTimeline (value) { + this.lastTimeline = value + } + } +}) From 22ab848f6ba7f734499011e54952c31634d216d5 Mon Sep 17 00:00:00 2001 From: Sean King Date: Wed, 5 Apr 2023 21:50:17 -0600 Subject: [PATCH 09/22] Remove old interface module --- src/modules/interface.js | 160 --------------------------------------- 1 file changed, 160 deletions(-) delete mode 100644 src/modules/interface.js diff --git a/src/modules/interface.js b/src/modules/interface.js deleted file mode 100644 index a86193eaf..000000000 --- a/src/modules/interface.js +++ /dev/null @@ -1,160 +0,0 @@ -const defaultState = { - settingsModalState: 'hidden', - settingsModalLoaded: false, - settingsModalTargetTab: null, - settings: { - currentSaveStateNotice: null, - noticeClearTimeout: null, - notificationPermission: null - }, - browserSupport: { - cssFilter: window.CSS && window.CSS.supports && ( - window.CSS.supports('filter', 'drop-shadow(0 0)') || - window.CSS.supports('-webkit-filter', 'drop-shadow(0 0)') - ) - }, - layoutType: 'normal', - globalNotices: [], - layoutHeight: 0, - lastTimeline: null -} - -const interfaceMod = { - state: defaultState, - mutations: { - settingsSaved (state, { success, error }) { - if (success) { - if (state.noticeClearTimeout) { - clearTimeout(state.noticeClearTimeout) - } - state.settings.currentSaveStateNotice = { error: false, data: success } - state.settings.noticeClearTimeout = setTimeout(() => delete state.settings.currentSaveStateNotice, 2000) - } else { - state.settings.currentSaveStateNotice = { error: true, errorData: error } - } - }, - setNotificationPermission (state, permission) { - state.notificationPermission = permission - }, - setLayoutType (state, value) { - state.layoutType = value - }, - closeSettingsModal (state) { - state.settingsModalState = 'hidden' - }, - togglePeekSettingsModal (state) { - switch (state.settingsModalState) { - case 'minimized': - state.settingsModalState = 'visible' - return - case 'visible': - state.settingsModalState = 'minimized' - return - default: - throw new Error('Illegal minimization state of settings modal') - } - }, - openSettingsModal (state) { - state.settingsModalState = 'visible' - if (!state.settingsModalLoaded) { - state.settingsModalLoaded = true - } - }, - setSettingsModalTargetTab (state, value) { - state.settingsModalTargetTab = value - }, - pushGlobalNotice (state, notice) { - state.globalNotices.push(notice) - }, - removeGlobalNotice (state, notice) { - state.globalNotices = state.globalNotices.filter(n => n !== notice) - }, - setLayoutHeight (state, value) { - state.layoutHeight = value - }, - setLayoutWidth (state, value) { - state.layoutWidth = value - }, - setLastTimeline (state, value) { - state.lastTimeline = value - } - }, - actions: { - setPageTitle ({ rootState }, option = '') { - document.title = `${option} ${rootState.instance.name}` - }, - settingsSaved ({ commit, dispatch }, { success, error }) { - commit('settingsSaved', { success, error }) - }, - setNotificationPermission ({ commit }, permission) { - commit('setNotificationPermission', permission) - }, - closeSettingsModal ({ commit }) { - commit('closeSettingsModal') - }, - openSettingsModal ({ commit }) { - commit('openSettingsModal') - }, - togglePeekSettingsModal ({ commit }) { - commit('togglePeekSettingsModal') - }, - clearSettingsModalTargetTab ({ commit }) { - commit('setSettingsModalTargetTab', null) - }, - openSettingsModalTab ({ commit }, value) { - commit('setSettingsModalTargetTab', value) - commit('openSettingsModal') - }, - pushGlobalNotice ( - { commit, dispatch, state }, - { - messageKey, - messageArgs = {}, - level = 'error', - timeout = 0 - }) { - const notice = { - messageKey, - messageArgs, - level - } - commit('pushGlobalNotice', notice) - // Adding a new element to array wraps it in a Proxy, which breaks the comparison - // TODO: Generate UUID or something instead or relying on !== operator? - const newNotice = state.globalNotices[state.globalNotices.length - 1] - if (timeout) { - setTimeout(() => dispatch('removeGlobalNotice', newNotice), timeout) - } - return newNotice - }, - removeGlobalNotice ({ commit }, notice) { - commit('removeGlobalNotice', notice) - }, - setLayoutHeight ({ commit }, value) { - commit('setLayoutHeight', value) - }, - // value is optional, assuming it was cached prior - setLayoutWidth ({ commit, state, rootGetters, rootState }, value) { - let width = value - if (value !== undefined) { - commit('setLayoutWidth', value) - } else { - width = state.layoutWidth - } - const mobileLayout = width <= 800 - const normalOrMobile = mobileLayout ? 'mobile' : 'normal' - const { thirdColumnMode } = rootGetters.mergedConfig - if (thirdColumnMode === 'none' || !rootState.users.currentUser) { - commit('setLayoutType', normalOrMobile) - } else { - const wideLayout = width >= 1300 - commit('setLayoutType', wideLayout ? 'wide' : normalOrMobile) - } - }, - setLastTimeline ({ commit }, value) { - commit('setLastTimeline', value) - } - } -} - -export default interfaceMod From e3ca5b0a324d2c627d90f002e39e549caf93dce7 Mon Sep 17 00:00:00 2001 From: Sean King Date: Wed, 5 Apr 2023 22:30:20 -0600 Subject: [PATCH 10/22] Move polls module to store --- src/components/poll/poll.js | 17 ++++----- src/main.js | 2 -- src/modules/polls.js | 69 ------------------------------------- src/stores/polls.js | 57 ++++++++++++++++++++++++++++++ 4 files changed, 64 insertions(+), 81 deletions(-) delete mode 100644 src/modules/polls.js create mode 100644 src/stores/polls.js diff --git a/src/components/poll/poll.js b/src/components/poll/poll.js index e4d6869a4..79f90e480 100644 --- a/src/components/poll/poll.js +++ b/src/components/poll/poll.js @@ -1,6 +1,7 @@ import Timeago from 'components/timeago/timeago.vue' import RichContent from 'components/rich_content/rich_content.jsx' import { forEach, map } from 'lodash' +import { usePollsStore } from '../../stores/polls' export default { name: 'Poll', @@ -17,20 +18,20 @@ export default { } }, created () { - if (!this.$store.state.polls.pollsObject[this.pollId]) { - this.$store.dispatch('mergeOrAddPoll', this.basePoll) + if (!usePollsStore().pollsObject[this.pollId]) { + usePollsStore().mergeOrAddPoll(this.basePoll) } - this.$store.dispatch('trackPoll', this.pollId) + usePollsStore().trackPoll(this.pollId) }, unmounted () { - this.$store.dispatch('untrackPoll', this.pollId) + usePollsStore().untrackPoll(this.pollId) }, computed: { pollId () { return this.basePoll.id }, poll () { - const storePoll = this.$store.state.polls.pollsObject[this.pollId] + const storePoll = usePollsStore().pollsObject[this.pollId] return storePoll || {} }, options () { @@ -76,9 +77,6 @@ export default { resultTitle (option) { return `${option.votes_count}/${this.totalVotesCount} ${this.$t('polls.votes')}` }, - fetchPoll () { - this.$store.dispatch('refreshPoll', { id: this.statusId, pollId: this.poll.id }) - }, activateOption (index) { // forgive me father: doing checking the radio/checkboxes // in code because of customized input elements need either @@ -106,8 +104,7 @@ export default { vote () { if (this.choiceIndices.length === 0) return this.loading = true - this.$store.dispatch( - 'votePoll', + usePollsStore().votePoll( { id: this.statusId, pollId: this.poll.id, choices: this.choiceIndices } ).then(poll => { this.loading = false diff --git a/src/main.js b/src/main.js index 030d70e65..695179aae 100644 --- a/src/main.js +++ b/src/main.js @@ -16,7 +16,6 @@ import oauthModule from './modules/oauth.js' import authFlowModule from './modules/auth_flow.js' import oauthTokensModule from './modules/oauth_tokens.js' import reportsModule from './modules/reports.js' -import pollsModule from './modules/polls.js' import chatsModule from './modules/chats.js' import announcementsModule from './modules/announcements.js' @@ -78,7 +77,6 @@ const persistedStateOptions = { authFlow: authFlowModule, oauthTokens: oauthTokensModule, reports: reportsModule, - polls: pollsModule, chats: chatsModule, announcements: announcementsModule }, diff --git a/src/modules/polls.js b/src/modules/polls.js deleted file mode 100644 index 1c4f98a49..000000000 --- a/src/modules/polls.js +++ /dev/null @@ -1,69 +0,0 @@ -import { merge } from 'lodash' - -const polls = { - state: { - // Contains key = id, value = number of trackers for this poll - trackedPolls: {}, - pollsObject: {} - }, - mutations: { - mergeOrAddPoll (state, poll) { - const existingPoll = state.pollsObject[poll.id] - // Make expired-state change trigger re-renders properly - poll.expired = Date.now() > Date.parse(poll.expires_at) - if (existingPoll) { - state.pollsObject[poll.id] = merge(existingPoll, poll) - } else { - state.pollsObject[poll.id] = poll - } - }, - trackPoll (state, pollId) { - const currentValue = state.trackedPolls[pollId] - if (currentValue) { - state.trackedPolls[pollId] = currentValue + 1 - } else { - state.trackedPolls[pollId] = 1 - } - }, - untrackPoll (state, pollId) { - const currentValue = state.trackedPolls[pollId] - if (currentValue) { - state.trackedPolls[pollId] = currentValue - 1 - } else { - state.trackedPolls[pollId] = 0 - } - } - }, - actions: { - mergeOrAddPoll ({ commit }, poll) { - commit('mergeOrAddPoll', poll) - }, - updateTrackedPoll ({ rootState, dispatch, commit }, pollId) { - rootState.api.backendInteractor.fetchPoll({ pollId }).then(poll => { - setTimeout(() => { - if (rootState.polls.trackedPolls[pollId]) { - dispatch('updateTrackedPoll', pollId) - } - }, 30 * 1000) - commit('mergeOrAddPoll', poll) - }) - }, - trackPoll ({ rootState, commit, dispatch }, pollId) { - if (!rootState.polls.trackedPolls[pollId]) { - setTimeout(() => dispatch('updateTrackedPoll', pollId), 30 * 1000) - } - commit('trackPoll', pollId) - }, - untrackPoll ({ commit }, pollId) { - commit('untrackPoll', pollId) - }, - votePoll ({ rootState, commit }, { id, pollId, choices }) { - return rootState.api.backendInteractor.vote({ pollId, choices }).then(poll => { - commit('mergeOrAddPoll', poll) - return poll - }) - } - } -} - -export default polls diff --git a/src/stores/polls.js b/src/stores/polls.js new file mode 100644 index 000000000..ab24a16df --- /dev/null +++ b/src/stores/polls.js @@ -0,0 +1,57 @@ +import { merge } from 'lodash' +import { defineStore } from 'pinia' + +export const usePollsStore = defineStore('polls', { + state: () => ({ + // Contains key = id, value = number of trackers for this poll + trackedPolls: {}, + pollsObject: {} + }), + actions: { + mergeOrAddPoll (poll) { + const existingPoll = this.pollsObject[poll.id] + // Make expired-state change trigger re-renders properly + poll.expired = Date.now() > Date.parse(poll.expires_at) + if (existingPoll) { + this.pollsObject[poll.id] = merge(existingPoll, poll) + } else { + this.pollsObject[poll.id] = poll + } + }, + updateTrackedPoll (pollId) { + window.vuex.state.api.backendInteractor.fetchPoll({ pollId }).then(poll => { + setTimeout(() => { + if (this.trackedPolls[pollId]) { + this.updateTrackedPoll(pollId) + } + }, 30 * 1000) + this.mergeOrAddPoll(poll) + }) + }, + trackPoll (pollId) { + if (!this.trackedPolls[pollId]) { + setTimeout(() => this.updateTrackedPoll(pollId), 30 * 1000) + } + const currentValue = this.trackedPolls[pollId] + if (currentValue) { + this.trackedPolls[pollId] = currentValue + 1 + } else { + this.trackedPolls[pollId] = 1 + } + }, + untrackPoll (pollId) { + const currentValue = this.trackedPolls[pollId] + if (currentValue) { + this.trackedPolls[pollId] = currentValue - 1 + } else { + this.trackedPolls[pollId] = 0 + } + }, + votePoll ({ id, pollId, choices }) { + return window.vuex.state.api.backendInteractor.vote({ pollId, choices }).then(poll => { + this.mergeOrAddPoll(poll) + return poll + }) + } + } +}) From f9254e5fb77f5dfb3bbc9021618cfeeb09248fa1 Mon Sep 17 00:00:00 2001 From: Sean King Date: Thu, 6 Apr 2023 16:32:21 -0600 Subject: [PATCH 11/22] Move announcements module to store --- src/boot/after_store.js | 3 +- src/components/announcement/announcement.js | 7 +- .../announcements_page/announcements_page.js | 7 +- src/components/mobile_nav/mobile_nav.js | 5 +- src/components/nav_panel/nav_panel.js | 9 +- src/components/navigation/navigation.js | 1 + src/components/navigation/navigation_entry.js | 3 + .../navigation/navigation_entry.vue | 6 + src/components/notifications/notifications.js | 5 +- src/components/side_drawer/side_drawer.js | 13 +- src/main.js | 4 +- src/modules/announcements.js | 135 ------------------ src/stores/announcements.js | 115 +++++++++++++++ 13 files changed, 160 insertions(+), 153 deletions(-) delete mode 100644 src/modules/announcements.js create mode 100644 src/stores/announcements.js diff --git a/src/boot/after_store.js b/src/boot/after_store.js index 6a722aff7..0f86aff5b 100644 --- a/src/boot/after_store.js +++ b/src/boot/after_store.js @@ -19,6 +19,7 @@ import FaviconService from '../services/favicon_service/favicon_service.js' import { useI18nStore } from '../stores/i18n' import { useInterfaceStore } from '../stores/interface' +import { useAnnouncementsStore } from '../stores/announcements' let staticInitialResults = null @@ -389,7 +390,7 @@ const afterStoreSetup = async ({ pinia, store, storageError, i18n }) => { // Start fetching things that don't need to block the UI store.dispatch('fetchMutes') - store.dispatch('startFetchingAnnouncements') + useAnnouncementsStore().startFetchingAnnouncements() getTOS({ store }) getStickers({ store }) diff --git a/src/components/announcement/announcement.js b/src/components/announcement/announcement.js index 30254926a..ab781a42b 100644 --- a/src/components/announcement/announcement.js +++ b/src/components/announcement/announcement.js @@ -2,6 +2,7 @@ import { mapState } from 'vuex' import AnnouncementEditor from '../announcement_editor/announcement_editor.vue' import RichContent from '../rich_content/rich_content.jsx' import localeService from '../../services/locale/locale.service.js' +import { useAnnouncementsStore } from '../../stores/announcements' const Announcement = { components: { @@ -67,11 +68,11 @@ const Announcement = { methods: { markAsRead () { if (!this.isRead) { - return this.$store.dispatch('markAnnouncementAsRead', this.announcement.id) + return useAnnouncementsStore().markAnnouncementAsRead(this.announcement.id) } }, deleteAnnouncement () { - return this.$store.dispatch('deleteAnnouncement', this.announcement.id) + return useAnnouncementsStore().deleteAnnouncement(this.announcement.id) }, formatTimeOrDate (time, locale) { const d = new Date(time) @@ -85,7 +86,7 @@ const Announcement = { this.editing = true }, submitEdit () { - this.$store.dispatch('editAnnouncement', { + useAnnouncementsStore().editAnnouncement({ id: this.announcement.id, ...this.editedAnnouncement }) diff --git a/src/components/announcements_page/announcements_page.js b/src/components/announcements_page/announcements_page.js index 8d1204d4c..2b8a85cf9 100644 --- a/src/components/announcements_page/announcements_page.js +++ b/src/components/announcements_page/announcements_page.js @@ -1,6 +1,7 @@ import { mapState } from 'vuex' import Announcement from '../announcement/announcement.vue' import AnnouncementEditor from '../announcement_editor/announcement_editor.vue' +import { useAnnouncementsStore } from '../../stores/announcements' const AnnouncementsPage = { components: { @@ -20,14 +21,14 @@ const AnnouncementsPage = { } }, mounted () { - this.$store.dispatch('fetchAnnouncements') + useAnnouncementsStore().fetchAnnouncements() }, computed: { ...mapState({ currentUser: state => state.users.currentUser }), announcements () { - return this.$store.state.announcements.announcements + return useAnnouncementsStore().announcements }, canPostAnnouncement () { return this.currentUser && this.currentUser.privileges.includes('announcements_manage_announcements') @@ -36,7 +37,7 @@ const AnnouncementsPage = { methods: { postAnnouncement () { this.posting = true - this.$store.dispatch('postAnnouncement', this.newAnnouncement) + useAnnouncementsStore().postAnnouncement(this.newAnnouncement) .then(() => { this.newAnnouncement.content = '' this.startsAt = undefined diff --git a/src/components/mobile_nav/mobile_nav.js b/src/components/mobile_nav/mobile_nav.js index dad1f6aa3..1c47fdf01 100644 --- a/src/components/mobile_nav/mobile_nav.js +++ b/src/components/mobile_nav/mobile_nav.js @@ -5,6 +5,7 @@ import { unseenNotificationsFromStore } from '../../services/notification_utils/ import GestureService from '../../services/gesture_service/gesture_service' import NavigationPins from 'src/components/navigation/navigation_pins.vue' import { mapGetters } from 'vuex' +import { mapState } from 'pinia' import { library } from '@fortawesome/fontawesome-svg-core' import { faTimes, @@ -13,6 +14,7 @@ import { faArrowUp, faMinus } from '@fortawesome/free-solid-svg-icons' +import { useAnnouncementsStore } from '../../stores/announcements' library.add( faTimes, @@ -57,7 +59,8 @@ const MobileNav = { isChat () { return this.$route.name === 'chat' }, - ...mapGetters(['unreadChatCount', 'unreadAnnouncementCount']), + ...mapState(useAnnouncementsStore, ['unreadAnnouncementCount']), + ...mapGetters(['unreadChatCount']), chatsPinned () { return new Set(this.$store.state.serverSideStorage.prefsStorage.collections.pinnedNavItems).has('chats') }, diff --git a/src/components/nav_panel/nav_panel.js b/src/components/nav_panel/nav_panel.js index 8c9c3b11f..429fcbce9 100644 --- a/src/components/nav_panel/nav_panel.js +++ b/src/components/nav_panel/nav_panel.js @@ -1,5 +1,6 @@ import ListsMenuContent from 'src/components/lists_menu/lists_menu_content.vue' import { mapState, mapGetters } from 'vuex' +import { mapState as mapPiniaState } from 'pinia' import { TIMELINES, ROOT_ITEMS } from 'src/components/navigation/navigation.js' import { filterNavigation } from 'src/components/navigation/filter.js' import NavigationEntry from 'src/components/navigation/navigation_entry.vue' @@ -21,6 +22,7 @@ import { faList, faBullhorn } from '@fortawesome/free-solid-svg-icons' +import { useAnnouncementsStore } from '../../stores/announcements' library.add( faUsers, @@ -82,13 +84,16 @@ const NavPanel = { } }, computed: { + ...mapPiniaState(useAnnouncementsStore, { + unreadAnnouncementCount: 'unreadAnnouncementCount', + supportsAnnouncements: store => store.supportsAnnouncements + }), ...mapState({ currentUser: state => state.users.currentUser, followRequestCount: state => state.api.followRequests.length, privateMode: state => state.instance.private, federating: state => state.instance.federating, pleromaChatMessagesAvailable: state => state.instance.pleromaChatMessagesAvailable, - supportsAnnouncements: state => state.announcements.supportsAnnouncements, pinnedItems: state => new Set(state.serverSideStorage.prefsStorage.collections.pinnedNavItems), collapsed: state => state.serverSideStorage.prefsStorage.simple.collapseNav }), @@ -120,7 +125,7 @@ const NavPanel = { } ) }, - ...mapGetters(['unreadChatCount', 'unreadAnnouncementCount']) + ...mapGetters(['unreadChatCount']) } } diff --git a/src/components/navigation/navigation.js b/src/components/navigation/navigation.js index face430ed..7018a5d0c 100644 --- a/src/components/navigation/navigation.js +++ b/src/components/navigation/navigation.js @@ -76,6 +76,7 @@ export const ROOT_ITEMS = { route: 'announcements', icon: 'bullhorn', label: 'nav.announcements', + store: 'announcements', badgeGetter: 'unreadAnnouncementCount', criteria: ['announcements'] } diff --git a/src/components/navigation/navigation_entry.js b/src/components/navigation/navigation_entry.js index 22ed77d9d..00376a03c 100644 --- a/src/components/navigation/navigation_entry.js +++ b/src/components/navigation/navigation_entry.js @@ -3,6 +3,8 @@ import { routeTo } from 'src/components/navigation/navigation.js' import OptionalRouterLink from 'src/components/optional_router_link/optional_router_link.vue' import { library } from '@fortawesome/fontawesome-svg-core' import { faThumbtack } from '@fortawesome/free-solid-svg-icons' +import { mapStores } from 'pinia' +import { useAnnouncementsStore } from '../../stores/announcements' library.add(faThumbtack) @@ -31,6 +33,7 @@ const NavigationEntry = { getters () { return this.$store.getters }, + ...mapStores(useAnnouncementsStore), ...mapState({ currentUser: state => state.users.currentUser, pinnedItems: state => new Set(state.serverSideStorage.prefsStorage.collections.pinnedNavItems) diff --git a/src/components/navigation/navigation_entry.vue b/src/components/navigation/navigation_entry.vue index 411ca5366..dd02e48b8 100644 --- a/src/components/navigation/navigation_entry.vue +++ b/src/components/navigation/navigation_entry.vue @@ -39,6 +39,12 @@ > {{ getters[item.badgeGetter] }} +
+ {{ this[`${item.store}Store`][item.badgeGetter] }} +