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:
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()
+ })
+ }
+ }
+})