logo

pleroma-fe

My custom branche(s) on git.pleroma.social/pleroma/pleroma-fe git clone https://anongit.hacktivis.me/git/pleroma-fe.git/
commit: f9254e5fb77f5dfb3bbc9021618cfeeb09248fa1
parent e3ca5b0a324d2c627d90f002e39e549caf93dce7
Author: Sean King <seanking2919@protonmail.com>
Date:   Thu,  6 Apr 2023 16:32:21 -0600

Move announcements module to store

Diffstat:

Msrc/boot/after_store.js3++-
Msrc/components/announcement/announcement.js7++++---
Msrc/components/announcements_page/announcements_page.js7++++---
Msrc/components/mobile_nav/mobile_nav.js5++++-
Msrc/components/nav_panel/nav_panel.js9+++++++--
Msrc/components/navigation/navigation.js1+
Msrc/components/navigation/navigation_entry.js3+++
Msrc/components/navigation/navigation_entry.vue6++++++
Msrc/components/notifications/notifications.js5++++-
Msrc/components/side_drawer/side_drawer.js11++++++++---
Msrc/main.js4+---
Dsrc/modules/announcements.js135-------------------------------------------------------------------------------
Asrc/stores/announcements.js115+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
13 files changed, 159 insertions(+), 152 deletions(-)

diff --git 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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -39,6 +39,12 @@ > {{ getters[item.badgeGetter] }} </div> + <div + v-else-if="item.badgeGetter && item.store && this[`${item.store}Store`][item.badgeGetter]" + class="badge badge-notification" + > + {{ this[`${item.store}Store`][item.badgeGetter] }} + </div> <button v-if="showPin && currentUser" type="button" diff --git a/src/components/notifications/notifications.js b/src/components/notifications/notifications.js @@ -1,5 +1,6 @@ import { computed } from 'vue' import { mapGetters } from 'vuex' +import { mapState } from 'pinia' import Notification from '../notification/notification.vue' import NotificationFilters from './notification_filters.vue' import notificationsFetcher from '../../services/notifications_fetcher/notifications_fetcher.service.js' @@ -12,6 +13,7 @@ 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' +import { useAnnouncementsStore } from '../../stores/announcements' library.add( faCircleNotch, @@ -95,7 +97,8 @@ const Notifications = { return this.filteredNotifications.slice(0, this.unseenCount + this.seenToDisplayCount) }, noSticky () { return this.$store.getters.mergedConfig.disableStickyHeaders }, - ...mapGetters(['unreadChatCount', 'unreadAnnouncementCount']) + ...mapState(useAnnouncementsStore, ['unreadAnnouncementCount']), + ...mapGetters(['unreadChatCount']) }, mounted () { this.scrollerRef = this.$refs.root.closest('.column.-scrollable') diff --git a/src/components/side_drawer/side_drawer.js b/src/components/side_drawer/side_drawer.js @@ -1,4 +1,5 @@ import { mapState, mapGetters } from 'vuex' +import { mapState as mapPiniaState } from 'pinia' import UserCard from '../user_card/user_card.vue' import { unseenNotificationsFromStore } from '../../services/notification_utils/notification_utils' import GestureService from '../../services/gesture_service/gesture_service' @@ -21,6 +22,7 @@ import { } from '@fortawesome/free-solid-svg-icons' import { useShoutStore } from '../../stores/shout' import { useInterfaceStore } from '../../stores/interface' +import { useAnnouncementsStore } from '../../stores/announcements' library.add( faSignInAlt, @@ -96,11 +98,14 @@ const SideDrawer = { return { name } } }, + ...mapPiniaState(useAnnouncementsStore, { + supportsAnnouncements: store => store.supportsAnnouncements, + unreadAnnouncementCount: 'unreadAnnouncementCount' + }), ...mapState({ - pleromaChatMessagesAvailable: state => state.instance.pleromaChatMessagesAvailable, - supportsAnnouncements: state => state.announcements.supportsAnnouncements + pleromaChatMessagesAvailable: state => state.instance.pleromaChatMessagesAvailable }), - ...mapGetters(['unreadChatCount', 'unreadAnnouncementCount']) + ...mapGetters(['unreadChatCount']) }, methods: { toggleDrawer () { diff --git a/src/main.js b/src/main.js @@ -18,7 +18,6 @@ import oauthTokensModule from './modules/oauth_tokens.js' import reportsModule from './modules/reports.js' import chatsModule from './modules/chats.js' -import announcementsModule from './modules/announcements.js' import { createI18n } from 'vue-i18n' @@ -77,8 +76,7 @@ const persistedStateOptions = { authFlow: authFlowModule, oauthTokens: oauthTokensModule, reports: reportsModule, - chats: chatsModule, - announcements: announcementsModule + chats: chatsModule }, plugins, strict: false // Socket modifies itself, let's ignore this for now. diff --git a/src/modules/announcements.js b/src/modules/announcements.js @@ -1,135 +0,0 @@ -const FETCH_ANNOUNCEMENT_INTERVAL_MS = 1000 * 60 * 5 - -export const defaultState = { - announcements: [], - supportsAnnouncements: true, - fetchAnnouncementsTimer: undefined -} - -export const mutations = { - setAnnouncements (state, announcements) { - state.announcements = announcements - }, - setAnnouncementRead (state, { id, read }) { - const index = state.announcements.findIndex(a => a.id === id) - - if (index < 0) { - return - } - - state.announcements[index].read = read - }, - setFetchAnnouncementsTimer (state, timer) { - state.fetchAnnouncementsTimer = timer - }, - setSupportsAnnouncements (state, supportsAnnouncements) { - state.supportsAnnouncements = supportsAnnouncements - } -} - -export const getters = { - unreadAnnouncementCount (state, _getters, rootState) { - if (!rootState.users.currentUser) { - return 0 - } - - const unread = state.announcements.filter(announcement => !(announcement.inactive || announcement.read)) - return unread.length - } -} - -const announcements = { - state: defaultState, - mutations, - getters, - actions: { - fetchAnnouncements (store) { - if (!store.state.supportsAnnouncements) { - return Promise.resolve() - } - - const currentUser = store.rootState.users.currentUser - const isAdmin = currentUser && currentUser.privileges.includes('announcements_manage_announcements') - - const getAnnouncements = async () => { - if (!isAdmin) { - return store.rootState.api.backendInteractor.fetchAnnouncements() - } - - const all = await store.rootState.api.backendInteractor.adminFetchAnnouncements() - const visible = await store.rootState.api.backendInteractor.fetchAnnouncements() - const visibleObject = visible.reduce((a, c) => { - a[c.id] = c - return a - }, {}) - const getWithinVisible = announcement => visibleObject[announcement.id] - - all.forEach(announcement => { - const visibleAnnouncement = getWithinVisible(announcement) - if (!visibleAnnouncement) { - announcement.inactive = true - } else { - announcement.read = visibleAnnouncement.read - } - }) - - return all - } - - return getAnnouncements() - .then(announcements => { - store.commit('setAnnouncements', announcements) - }) - .catch(error => { - // If and only if backend does not support announcements, it would return 404. - // In this case, silently ignores it. - if (error && error.statusCode === 404) { - store.commit('setSupportsAnnouncements', false) - } else { - throw error - } - }) - }, - markAnnouncementAsRead (store, id) { - return store.rootState.api.backendInteractor.dismissAnnouncement({ id }) - .then(() => { - store.commit('setAnnouncementRead', { id, read: true }) - }) - }, - startFetchingAnnouncements (store) { - if (store.state.fetchAnnouncementsTimer) { - return - } - - const interval = setInterval(() => store.dispatch('fetchAnnouncements'), FETCH_ANNOUNCEMENT_INTERVAL_MS) - store.commit('setFetchAnnouncementsTimer', interval) - - return store.dispatch('fetchAnnouncements') - }, - stopFetchingAnnouncements (store) { - const interval = store.state.fetchAnnouncementsTimer - store.commit('setFetchAnnouncementsTimer', undefined) - clearInterval(interval) - }, - postAnnouncement (store, { content, startsAt, endsAt, allDay }) { - return store.rootState.api.backendInteractor.postAnnouncement({ content, startsAt, endsAt, allDay }) - .then(() => { - return store.dispatch('fetchAnnouncements') - }) - }, - editAnnouncement (store, { id, content, startsAt, endsAt, allDay }) { - return store.rootState.api.backendInteractor.editAnnouncement({ id, content, startsAt, endsAt, allDay }) - .then(() => { - return store.dispatch('fetchAnnouncements') - }) - }, - deleteAnnouncement (store, id) { - return store.rootState.api.backendInteractor.deleteAnnouncement({ id }) - .then(() => { - return store.dispatch('fetchAnnouncements') - }) - } - } -} - -export default announcements diff --git a/src/stores/announcements.js b/src/stores/announcements.js @@ -0,0 +1,115 @@ +import { defineStore } from 'pinia' + +const FETCH_ANNOUNCEMENT_INTERVAL_MS = 1000 * 60 * 5 + +export const useAnnouncementsStore = defineStore('announcements', { + state: () => ({ + announcements: [], + supportsAnnouncements: true, + fetchAnnouncementsTimer: undefined + }), + getters: { + unreadAnnouncementCount () { + if (!window.vuex.state.users.currentUser) { + return 0 + } + + const unread = this.announcements.filter(announcement => !(announcement.inactive || announcement.read)) + return unread.length + } + }, + actions: { + fetchAnnouncements () { + if (!this.supportsAnnouncements) { + return Promise.resolve() + } + + const currentUser = window.vuex.state.users.currentUser + const isAdmin = currentUser && currentUser.privileges.includes('announcements_manage_announcements') + + const getAnnouncements = async () => { + if (!isAdmin) { + return window.vuex.state.api.backendInteractor.fetchAnnouncements() + } + + const all = await window.vuex.state.api.backendInteractor.adminFetchAnnouncements() + const visible = await window.vuex.state.api.backendInteractor.fetchAnnouncements() + const visibleObject = visible.reduce((a, c) => { + a[c.id] = c + return a + }, {}) + const getWithinVisible = announcement => visibleObject[announcement.id] + + all.forEach(announcement => { + const visibleAnnouncement = getWithinVisible(announcement) + if (!visibleAnnouncement) { + announcement.inactive = true + } else { + announcement.read = visibleAnnouncement.read + } + }) + + return all + } + + return getAnnouncements() + .then(announcements => { + this.announcements = announcements + }) + .catch(error => { + // If and only if backend does not support announcements, it would return 404. + // In this case, silently ignores it. + if (error && error.statusCode === 404) { + this.supportsAnnouncements = false + } else { + throw error + } + }) + }, + markAnnouncementAsRead (id) { + return window.vuex.state.api.backendInteractor.dismissAnnouncement({ id }) + .then(() => { + const index = this.announcements.findIndex(a => a.id === id) + + if (index < 0) { + return + } + + this.announcements[index].read = true + }) + }, + startFetchingAnnouncements () { + if (this.fetchAnnouncementsTimer) { + return + } + + const interval = setInterval(() => this.fetchAnnouncements(), FETCH_ANNOUNCEMENT_INTERVAL_MS) + this.fetchAnnouncementsTimer = interval + + return this.fetchAnnouncements() + }, + stopFetchingAnnouncements () { + const interval = this.fetchAnnouncementsTimer + this.fetchAnnouncementsTimer = undefined + clearInterval(interval) + }, + postAnnouncement ({ content, startsAt, endsAt, allDay }) { + return window.vuex.state.api.backendInteractor.postAnnouncement({ content, startsAt, endsAt, allDay }) + .then(() => { + return this.fetchAnnouncements() + }) + }, + editAnnouncement ({ id, content, startsAt, endsAt, allDay }) { + return window.vuex.state.api.backendInteractor.editAnnouncement({ id, content, startsAt, endsAt, allDay }) + .then(() => { + return this.fetchAnnouncements() + }) + }, + deleteAnnouncement (id) { + return window.vuex.state.api.backendInteractor.deleteAnnouncement({ id }) + .then(() => { + return this.fetchAnnouncements() + }) + } + } +})