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 { 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 })
|
||||
|
||||
|
|
|
@ -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
|
||||
})
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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')
|
||||
},
|
||||
|
|
|
@ -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'])
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -76,6 +76,7 @@ export const ROOT_ITEMS = {
|
|||
route: 'announcements',
|
||||
icon: 'bullhorn',
|
||||
label: 'nav.announcements',
|
||||
store: 'announcements',
|
||||
badgeGetter: 'unreadAnnouncementCount',
|
||||
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 { 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)
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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 }
|
||||
}
|
||||
},
|
||||
...mapState({
|
||||
pleromaChatMessagesAvailable: state => state.instance.pleromaChatMessagesAvailable,
|
||||
supportsAnnouncements: state => state.announcements.supportsAnnouncements
|
||||
...mapPiniaState(useAnnouncementsStore, {
|
||||
supportsAnnouncements: store => store.supportsAnnouncements,
|
||||
unreadAnnouncementCount: 'unreadAnnouncementCount'
|
||||
}),
|
||||
...mapGetters(['unreadChatCount', 'unreadAnnouncementCount'])
|
||||
...mapState({
|
||||
pleromaChatMessagesAvailable: state => state.instance.pleromaChatMessagesAvailable
|
||||
}),
|
||||
...mapGetters(['unreadChatCount'])
|
||||
},
|
||||
methods: {
|
||||
toggleDrawer () {
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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