Move announcements module to store
This commit is contained in:
parent
e3ca5b0a32
commit
f9254e5fb7
|
@ -19,6 +19,7 @@ import FaviconService from '../services/favicon_service/favicon_service.js'
|
||||||
|
|
||||||
import { useI18nStore } from '../stores/i18n'
|
import { useI18nStore } from '../stores/i18n'
|
||||||
import { useInterfaceStore } from '../stores/interface'
|
import { useInterfaceStore } from '../stores/interface'
|
||||||
|
import { useAnnouncementsStore } from '../stores/announcements'
|
||||||
|
|
||||||
let staticInitialResults = null
|
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
|
// Start fetching things that don't need to block the UI
|
||||||
store.dispatch('fetchMutes')
|
store.dispatch('fetchMutes')
|
||||||
store.dispatch('startFetchingAnnouncements')
|
useAnnouncementsStore().startFetchingAnnouncements()
|
||||||
getTOS({ store })
|
getTOS({ store })
|
||||||
getStickers({ store })
|
getStickers({ store })
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { mapState } from 'vuex'
|
||||||
import AnnouncementEditor from '../announcement_editor/announcement_editor.vue'
|
import AnnouncementEditor from '../announcement_editor/announcement_editor.vue'
|
||||||
import RichContent from '../rich_content/rich_content.jsx'
|
import RichContent from '../rich_content/rich_content.jsx'
|
||||||
import localeService from '../../services/locale/locale.service.js'
|
import localeService from '../../services/locale/locale.service.js'
|
||||||
|
import { useAnnouncementsStore } from '../../stores/announcements'
|
||||||
|
|
||||||
const Announcement = {
|
const Announcement = {
|
||||||
components: {
|
components: {
|
||||||
|
@ -67,11 +68,11 @@ const Announcement = {
|
||||||
methods: {
|
methods: {
|
||||||
markAsRead () {
|
markAsRead () {
|
||||||
if (!this.isRead) {
|
if (!this.isRead) {
|
||||||
return this.$store.dispatch('markAnnouncementAsRead', this.announcement.id)
|
return useAnnouncementsStore().markAnnouncementAsRead(this.announcement.id)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
deleteAnnouncement () {
|
deleteAnnouncement () {
|
||||||
return this.$store.dispatch('deleteAnnouncement', this.announcement.id)
|
return useAnnouncementsStore().deleteAnnouncement(this.announcement.id)
|
||||||
},
|
},
|
||||||
formatTimeOrDate (time, locale) {
|
formatTimeOrDate (time, locale) {
|
||||||
const d = new Date(time)
|
const d = new Date(time)
|
||||||
|
@ -85,7 +86,7 @@ const Announcement = {
|
||||||
this.editing = true
|
this.editing = true
|
||||||
},
|
},
|
||||||
submitEdit () {
|
submitEdit () {
|
||||||
this.$store.dispatch('editAnnouncement', {
|
useAnnouncementsStore().editAnnouncement({
|
||||||
id: this.announcement.id,
|
id: this.announcement.id,
|
||||||
...this.editedAnnouncement
|
...this.editedAnnouncement
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { mapState } from 'vuex'
|
import { mapState } from 'vuex'
|
||||||
import Announcement from '../announcement/announcement.vue'
|
import Announcement from '../announcement/announcement.vue'
|
||||||
import AnnouncementEditor from '../announcement_editor/announcement_editor.vue'
|
import AnnouncementEditor from '../announcement_editor/announcement_editor.vue'
|
||||||
|
import { useAnnouncementsStore } from '../../stores/announcements'
|
||||||
|
|
||||||
const AnnouncementsPage = {
|
const AnnouncementsPage = {
|
||||||
components: {
|
components: {
|
||||||
|
@ -20,14 +21,14 @@ const AnnouncementsPage = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted () {
|
mounted () {
|
||||||
this.$store.dispatch('fetchAnnouncements')
|
useAnnouncementsStore().fetchAnnouncements()
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapState({
|
...mapState({
|
||||||
currentUser: state => state.users.currentUser
|
currentUser: state => state.users.currentUser
|
||||||
}),
|
}),
|
||||||
announcements () {
|
announcements () {
|
||||||
return this.$store.state.announcements.announcements
|
return useAnnouncementsStore().announcements
|
||||||
},
|
},
|
||||||
canPostAnnouncement () {
|
canPostAnnouncement () {
|
||||||
return this.currentUser && this.currentUser.privileges.includes('announcements_manage_announcements')
|
return this.currentUser && this.currentUser.privileges.includes('announcements_manage_announcements')
|
||||||
|
@ -36,7 +37,7 @@ const AnnouncementsPage = {
|
||||||
methods: {
|
methods: {
|
||||||
postAnnouncement () {
|
postAnnouncement () {
|
||||||
this.posting = true
|
this.posting = true
|
||||||
this.$store.dispatch('postAnnouncement', this.newAnnouncement)
|
useAnnouncementsStore().postAnnouncement(this.newAnnouncement)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.newAnnouncement.content = ''
|
this.newAnnouncement.content = ''
|
||||||
this.startsAt = undefined
|
this.startsAt = undefined
|
||||||
|
|
|
@ -5,6 +5,7 @@ import { unseenNotificationsFromStore } from '../../services/notification_utils/
|
||||||
import GestureService from '../../services/gesture_service/gesture_service'
|
import GestureService from '../../services/gesture_service/gesture_service'
|
||||||
import NavigationPins from 'src/components/navigation/navigation_pins.vue'
|
import NavigationPins from 'src/components/navigation/navigation_pins.vue'
|
||||||
import { mapGetters } from 'vuex'
|
import { mapGetters } from 'vuex'
|
||||||
|
import { mapState } from 'pinia'
|
||||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||||
import {
|
import {
|
||||||
faTimes,
|
faTimes,
|
||||||
|
@ -13,6 +14,7 @@ import {
|
||||||
faArrowUp,
|
faArrowUp,
|
||||||
faMinus
|
faMinus
|
||||||
} from '@fortawesome/free-solid-svg-icons'
|
} from '@fortawesome/free-solid-svg-icons'
|
||||||
|
import { useAnnouncementsStore } from '../../stores/announcements'
|
||||||
|
|
||||||
library.add(
|
library.add(
|
||||||
faTimes,
|
faTimes,
|
||||||
|
@ -57,7 +59,8 @@ const MobileNav = {
|
||||||
isChat () {
|
isChat () {
|
||||||
return this.$route.name === 'chat'
|
return this.$route.name === 'chat'
|
||||||
},
|
},
|
||||||
...mapGetters(['unreadChatCount', 'unreadAnnouncementCount']),
|
...mapState(useAnnouncementsStore, ['unreadAnnouncementCount']),
|
||||||
|
...mapGetters(['unreadChatCount']),
|
||||||
chatsPinned () {
|
chatsPinned () {
|
||||||
return new Set(this.$store.state.serverSideStorage.prefsStorage.collections.pinnedNavItems).has('chats')
|
return new Set(this.$store.state.serverSideStorage.prefsStorage.collections.pinnedNavItems).has('chats')
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import ListsMenuContent from 'src/components/lists_menu/lists_menu_content.vue'
|
import ListsMenuContent from 'src/components/lists_menu/lists_menu_content.vue'
|
||||||
import { mapState, mapGetters } from 'vuex'
|
import { mapState, mapGetters } from 'vuex'
|
||||||
|
import { mapState as mapPiniaState } from 'pinia'
|
||||||
import { TIMELINES, ROOT_ITEMS } from 'src/components/navigation/navigation.js'
|
import { TIMELINES, ROOT_ITEMS } from 'src/components/navigation/navigation.js'
|
||||||
import { filterNavigation } from 'src/components/navigation/filter.js'
|
import { filterNavigation } from 'src/components/navigation/filter.js'
|
||||||
import NavigationEntry from 'src/components/navigation/navigation_entry.vue'
|
import NavigationEntry from 'src/components/navigation/navigation_entry.vue'
|
||||||
|
@ -21,6 +22,7 @@ import {
|
||||||
faList,
|
faList,
|
||||||
faBullhorn
|
faBullhorn
|
||||||
} from '@fortawesome/free-solid-svg-icons'
|
} from '@fortawesome/free-solid-svg-icons'
|
||||||
|
import { useAnnouncementsStore } from '../../stores/announcements'
|
||||||
|
|
||||||
library.add(
|
library.add(
|
||||||
faUsers,
|
faUsers,
|
||||||
|
@ -82,13 +84,16 @@ const NavPanel = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
...mapPiniaState(useAnnouncementsStore, {
|
||||||
|
unreadAnnouncementCount: 'unreadAnnouncementCount',
|
||||||
|
supportsAnnouncements: store => store.supportsAnnouncements
|
||||||
|
}),
|
||||||
...mapState({
|
...mapState({
|
||||||
currentUser: state => state.users.currentUser,
|
currentUser: state => state.users.currentUser,
|
||||||
followRequestCount: state => state.api.followRequests.length,
|
followRequestCount: state => state.api.followRequests.length,
|
||||||
privateMode: state => state.instance.private,
|
privateMode: state => state.instance.private,
|
||||||
federating: state => state.instance.federating,
|
federating: state => state.instance.federating,
|
||||||
pleromaChatMessagesAvailable: state => state.instance.pleromaChatMessagesAvailable,
|
pleromaChatMessagesAvailable: state => state.instance.pleromaChatMessagesAvailable,
|
||||||
supportsAnnouncements: state => state.announcements.supportsAnnouncements,
|
|
||||||
pinnedItems: state => new Set(state.serverSideStorage.prefsStorage.collections.pinnedNavItems),
|
pinnedItems: state => new Set(state.serverSideStorage.prefsStorage.collections.pinnedNavItems),
|
||||||
collapsed: state => state.serverSideStorage.prefsStorage.simple.collapseNav
|
collapsed: state => state.serverSideStorage.prefsStorage.simple.collapseNav
|
||||||
}),
|
}),
|
||||||
|
@ -120,7 +125,7 @@ const NavPanel = {
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
...mapGetters(['unreadChatCount', 'unreadAnnouncementCount'])
|
...mapGetters(['unreadChatCount'])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -76,6 +76,7 @@ export const ROOT_ITEMS = {
|
||||||
route: 'announcements',
|
route: 'announcements',
|
||||||
icon: 'bullhorn',
|
icon: 'bullhorn',
|
||||||
label: 'nav.announcements',
|
label: 'nav.announcements',
|
||||||
|
store: 'announcements',
|
||||||
badgeGetter: 'unreadAnnouncementCount',
|
badgeGetter: 'unreadAnnouncementCount',
|
||||||
criteria: ['announcements']
|
criteria: ['announcements']
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 OptionalRouterLink from 'src/components/optional_router_link/optional_router_link.vue'
|
||||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||||
import { faThumbtack } from '@fortawesome/free-solid-svg-icons'
|
import { faThumbtack } from '@fortawesome/free-solid-svg-icons'
|
||||||
|
import { mapStores } from 'pinia'
|
||||||
|
import { useAnnouncementsStore } from '../../stores/announcements'
|
||||||
|
|
||||||
library.add(faThumbtack)
|
library.add(faThumbtack)
|
||||||
|
|
||||||
|
@ -31,6 +33,7 @@ const NavigationEntry = {
|
||||||
getters () {
|
getters () {
|
||||||
return this.$store.getters
|
return this.$store.getters
|
||||||
},
|
},
|
||||||
|
...mapStores(useAnnouncementsStore),
|
||||||
...mapState({
|
...mapState({
|
||||||
currentUser: state => state.users.currentUser,
|
currentUser: state => state.users.currentUser,
|
||||||
pinnedItems: state => new Set(state.serverSideStorage.prefsStorage.collections.pinnedNavItems)
|
pinnedItems: state => new Set(state.serverSideStorage.prefsStorage.collections.pinnedNavItems)
|
||||||
|
|
|
@ -39,6 +39,12 @@
|
||||||
>
|
>
|
||||||
{{ getters[item.badgeGetter] }}
|
{{ getters[item.badgeGetter] }}
|
||||||
</div>
|
</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
|
<button
|
||||||
v-if="showPin && currentUser"
|
v-if="showPin && currentUser"
|
||||||
type="button"
|
type="button"
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import { mapGetters } from 'vuex'
|
import { mapGetters } from 'vuex'
|
||||||
|
import { mapState } from 'pinia'
|
||||||
import Notification from '../notification/notification.vue'
|
import Notification from '../notification/notification.vue'
|
||||||
import NotificationFilters from './notification_filters.vue'
|
import NotificationFilters from './notification_filters.vue'
|
||||||
import notificationsFetcher from '../../services/notifications_fetcher/notifications_fetcher.service.js'
|
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 { library } from '@fortawesome/fontawesome-svg-core'
|
||||||
import { faCircleNotch, faArrowUp, faMinus } from '@fortawesome/free-solid-svg-icons'
|
import { faCircleNotch, faArrowUp, faMinus } from '@fortawesome/free-solid-svg-icons'
|
||||||
import { useInterfaceStore } from '../../stores/interface'
|
import { useInterfaceStore } from '../../stores/interface'
|
||||||
|
import { useAnnouncementsStore } from '../../stores/announcements'
|
||||||
|
|
||||||
library.add(
|
library.add(
|
||||||
faCircleNotch,
|
faCircleNotch,
|
||||||
|
@ -95,7 +97,8 @@ const Notifications = {
|
||||||
return this.filteredNotifications.slice(0, this.unseenCount + this.seenToDisplayCount)
|
return this.filteredNotifications.slice(0, this.unseenCount + this.seenToDisplayCount)
|
||||||
},
|
},
|
||||||
noSticky () { return this.$store.getters.mergedConfig.disableStickyHeaders },
|
noSticky () { return this.$store.getters.mergedConfig.disableStickyHeaders },
|
||||||
...mapGetters(['unreadChatCount', 'unreadAnnouncementCount'])
|
...mapState(useAnnouncementsStore, ['unreadAnnouncementCount']),
|
||||||
|
...mapGetters(['unreadChatCount'])
|
||||||
},
|
},
|
||||||
mounted () {
|
mounted () {
|
||||||
this.scrollerRef = this.$refs.root.closest('.column.-scrollable')
|
this.scrollerRef = this.$refs.root.closest('.column.-scrollable')
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { mapState, mapGetters } from 'vuex'
|
import { mapState, mapGetters } from 'vuex'
|
||||||
|
import { mapState as mapPiniaState } from 'pinia'
|
||||||
import UserCard from '../user_card/user_card.vue'
|
import UserCard from '../user_card/user_card.vue'
|
||||||
import { unseenNotificationsFromStore } from '../../services/notification_utils/notification_utils'
|
import { unseenNotificationsFromStore } from '../../services/notification_utils/notification_utils'
|
||||||
import GestureService from '../../services/gesture_service/gesture_service'
|
import GestureService from '../../services/gesture_service/gesture_service'
|
||||||
|
@ -21,6 +22,7 @@ import {
|
||||||
} from '@fortawesome/free-solid-svg-icons'
|
} from '@fortawesome/free-solid-svg-icons'
|
||||||
import { useShoutStore } from '../../stores/shout'
|
import { useShoutStore } from '../../stores/shout'
|
||||||
import { useInterfaceStore } from '../../stores/interface'
|
import { useInterfaceStore } from '../../stores/interface'
|
||||||
|
import { useAnnouncementsStore } from '../../stores/announcements'
|
||||||
|
|
||||||
library.add(
|
library.add(
|
||||||
faSignInAlt,
|
faSignInAlt,
|
||||||
|
@ -96,11 +98,14 @@ const SideDrawer = {
|
||||||
return { name }
|
return { name }
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
...mapState({
|
...mapPiniaState(useAnnouncementsStore, {
|
||||||
pleromaChatMessagesAvailable: state => state.instance.pleromaChatMessagesAvailable,
|
supportsAnnouncements: store => store.supportsAnnouncements,
|
||||||
supportsAnnouncements: state => state.announcements.supportsAnnouncements
|
unreadAnnouncementCount: 'unreadAnnouncementCount'
|
||||||
}),
|
}),
|
||||||
...mapGetters(['unreadChatCount', 'unreadAnnouncementCount'])
|
...mapState({
|
||||||
|
pleromaChatMessagesAvailable: state => state.instance.pleromaChatMessagesAvailable
|
||||||
|
}),
|
||||||
|
...mapGetters(['unreadChatCount'])
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
toggleDrawer () {
|
toggleDrawer () {
|
||||||
|
|
|
@ -18,7 +18,6 @@ import oauthTokensModule from './modules/oauth_tokens.js'
|
||||||
import reportsModule from './modules/reports.js'
|
import reportsModule from './modules/reports.js'
|
||||||
|
|
||||||
import chatsModule from './modules/chats.js'
|
import chatsModule from './modules/chats.js'
|
||||||
import announcementsModule from './modules/announcements.js'
|
|
||||||
|
|
||||||
import { createI18n } from 'vue-i18n'
|
import { createI18n } from 'vue-i18n'
|
||||||
|
|
||||||
|
@ -77,8 +76,7 @@ const persistedStateOptions = {
|
||||||
authFlow: authFlowModule,
|
authFlow: authFlowModule,
|
||||||
oauthTokens: oauthTokensModule,
|
oauthTokens: oauthTokensModule,
|
||||||
reports: reportsModule,
|
reports: reportsModule,
|
||||||
chats: chatsModule,
|
chats: chatsModule
|
||||||
announcements: announcementsModule
|
|
||||||
},
|
},
|
||||||
plugins,
|
plugins,
|
||||||
strict: false // Socket modifies itself, let's ignore this for now.
|
strict: false // Socket modifies itself, let's ignore this for now.
|
||||||
|
|
|
@ -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
|
|
|
@ -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()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
Loading…
Reference in New Issue