refactored notifications into their own module separate from statuses (WIP)

This commit is contained in:
Henry Jameson 2023-11-16 19:26:18 +02:00
parent fffa7a4f4a
commit aad3225d25
10 changed files with 170 additions and 158 deletions

View File

@ -249,7 +249,7 @@
<StatusContent
:class="{ faint: !statusExpanded }"
:compact="!statusExpanded"
:status="notification.action"
:status="notification.status"
/>
</template>
</div>

View File

@ -65,7 +65,7 @@ const Notifications = {
return notificationsFromStore(this.$store)
},
error () {
return this.$store.state.statuses.notifications.error
return this.$store.state.notifications.error
},
unseenNotifications () {
return unseenNotificationsFromStore(this.$store)
@ -86,7 +86,7 @@ const Notifications = {
return this.unseenNotifications.length + (this.unreadChatCount) + this.unreadAnnouncementCount
},
loading () {
return this.$store.state.statuses.notifications.loading
return this.$store.state.notifications.loading
},
noHeading () {
const { layoutType } = this.$store.state.interface
@ -160,17 +160,7 @@ const Notifications = {
this.showScrollTop = this.$refs.root.offsetTop < this.scrollerRef.scrollTop
},
notificationClicked (notification) {
const { type, id, seen } = notification
if (!seen) {
switch (type) {
case 'mention':
case 'pleroma:report':
case 'follow_request':
break
default:
this.markOneAsSeen(id)
}
}
// const { type, id, seen } = notification
},
notificationInteracted (notification) {
const { id, seen } = notification

View File

@ -6,6 +6,7 @@ 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 notificationsModule from './modules/notifications.js'
import listsModule from './modules/lists.js'
import usersModule from './modules/users.js'
import apiModule from './modules/api.js'
@ -78,6 +79,7 @@ const persistedStateOptions = {
// TODO refactor users/statuses modules, they depend on each other
users: usersModule,
statuses: statusesModule,
notifications: notificationsModule,
lists: listsModule,
api: apiModule,
config: configModule,

View File

@ -0,0 +1,158 @@
import apiService from '../services/api/api.service.js'
import {
isStatusNotification,
isValidNotification,
maybeShowNotification
} from '../services/notification_utils/notification_utils.js'
import {
closeDesktopNotification,
closeAllDesktopNotifications
} from '../services/desktop_notification_utils/desktop_notification_utils.js'
const emptyNotifications = () => ({
desktopNotificationSilence: true,
maxId: 0,
minId: Number.POSITIVE_INFINITY,
data: [],
idStore: {},
loading: false
})
export const defaultState = () => ({
...emptyNotifications()
})
export const notifications = {
state: defaultState(),
mutations: {
addNewNotifications (state, { notifications }) {
notifications.forEach(notification => {
state.data.push(notification)
state.idStore[notification.id] = notification
})
},
clearNotifications (state) {
state = emptyNotifications()
},
updateNotificationsMinMaxId (state, id) {
state.maxId = id > state.maxId ? id : state.maxId
state.minId = id < state.minId ? id : state.minId
},
setNotificationsLoading (state, { value }) {
state.loading = value
},
setNotificationsSilence (state, { value }) {
state.desktopNotificationSilence = value
},
markNotificationsAsSeen (state) {
state.data.forEach((notification) => {
notification.seen = true
})
},
markSingleNotificationAsSeen (state, { id }) {
const notification = find(state.data, n => n.id === id)
if (notification) notification.seen = true
},
dismissNotification (state, { id }) {
state.data = state.data.filter(n => n.id !== id)
},
dismissNotifications (state, { finder }) {
state.data = state.data.filter(n => finder)
},
updateNotification (state, { id, updater }) {
const notification = find(state.data, n => n.id === id)
notification && updater(notification)
}
},
actions: {
addNewNotifications (store, { notifications, older }) {
const { commit, dispatch, state, rootState } = store
const validNotifications = notifications.filter((notification) => {
// If invalid notification, update ids but don't add it to store
if (!isValidNotification(notification)) {
console.error('Invalid notification:', notification)
commit('updateNotificationsMinMaxId', notification.id)
return false
}
return true
})
const statusNotifications = validNotifications.filter(notification => isStatusNotification(notification.type) && notification.status)
// Synchronous commit to add all the statuses
commit('addNewStatuses', { statuses: statusNotifications.map(notification => notification.status) })
// Update references to statuses in notifications to ones in the store
statusNotifications.forEach(notification => {
const id = notification.status.id
const referenceStatus = rootState.statuses.allStatusesObject[id]
console.log()
if (referenceStatus) {
notification.status = referenceStatus
}
})
validNotifications.forEach(notification => {
if (notification.type === 'pleroma:report') {
dispatch('addReport', notification.report)
}
if (notification.type === 'pleroma:emoji_reaction') {
dispatch('fetchEmojiReactionsBy', notification.status.id)
}
// Only add a new notification if we don't have one for the same action
// eslint-disable-next-line no-prototype-builtins
if (!state.idStore.hasOwnProperty(notification.id)) {
commit('updateNotificationsMinMaxId', notification.id)
maybeShowNotification(store, notification)
} else if (notification.seen) {
state.idStore[notification.id].seen = true
}
})
commit('addNewNotifications', { notifications })
},
setNotificationsLoading ({ rootState, commit }, { value }) {
commit('setNotificationsLoading', { value })
},
setNotificationsSilence ({ rootState, commit }, { value }) {
commit('setNotificationsSilence', { value })
},
markNotificationsAsSeen ({ rootState, state, commit }) {
commit('markNotificationsAsSeen')
apiService.markNotificationsAsSeen({
id: state.maxId,
credentials: rootState.users.currentUser.credentials
}).then(() => {
closeAllDesktopNotifications(rootState)
})
},
markSingleNotificationAsSeen ({ rootState, commit }, { id }) {
commit('markSingleNotificationAsSeen', { id })
apiService.markNotificationsAsSeen({
single: true,
id,
credentials: rootState.users.currentUser.credentials
}).then(() => {
closeDesktopNotification(rootState, id)
})
},
dismissNotificationLocal ({ rootState, commit }, { id }) {
commit('dismissNotification', { id })
},
dismissNotification ({ rootState, commit }, { id }) {
commit('dismissNotification', { id })
rootState.api.backendInteractor.dismissNotification({ id })
},
updateNotification ({ rootState, commit }, { id, updater }) {
commit('updateNotification', { id, updater })
}
}
}
export default notifications

View File

@ -12,15 +12,6 @@ import {
isArray,
omitBy
} from 'lodash'
import {
isStatusNotification,
isValidNotification,
maybeShowNotification
} from '../services/notification_utils/notification_utils.js'
import {
closeDesktopNotification,
closeAllDesktopNotifications
} from '../services/desktop_notification_utils/desktop_notification_utils.js'
import apiService from '../services/api/api.service.js'
const emptyTl = (userId = 0) => ({
@ -40,22 +31,12 @@ const emptyTl = (userId = 0) => ({
flushMarker: 0
})
const emptyNotifications = () => ({
desktopNotificationSilence: true,
maxId: 0,
minId: Number.POSITIVE_INFINITY,
data: [],
idStore: {},
loading: false
})
export const defaultState = () => ({
allStatuses: [],
scrobblesNextFetch: {},
allStatusesObject: {},
conversationsObject: {},
maxId: 0,
notifications: emptyNotifications(),
favorites: new Set(),
timelines: {
mentions: emptyTl(),
@ -164,9 +145,6 @@ const removeStatusFromGlobalStorage = (state, status) => {
// TODO: Need to remove from allStatusesObject?
// Remove possible notification
remove(state.notifications.data, ({ action: { id } }) => id === status.id)
// Remove from conversation
const conversationId = status.statusnet_conversation_id
if (state.conversationsObject[conversationId]) {
@ -342,52 +320,6 @@ const addNewStatuses = (state, { statuses, showImmediately = false, timeline, us
}
}
const updateNotificationsMinMaxId = (state, notification) => {
state.notifications.maxId = notification.id > state.notifications.maxId
? notification.id
: state.notifications.maxId
state.notifications.minId = notification.id < state.notifications.minId
? notification.id
: state.notifications.minId
}
const addNewNotifications = (state, { dispatch, notifications, older, visibleNotificationTypes, rootGetters, newNotificationSideEffects }) => {
each(notifications, (notification) => {
// If invalid notification, update ids but don't add it to store
if (!isValidNotification(notification)) {
console.error('Invalid notification:', notification)
updateNotificationsMinMaxId(state, notification)
return
}
if (isStatusNotification(notification.type)) {
notification.action = addStatusToGlobalStorage(state, notification.action).item
notification.status = notification.status && addStatusToGlobalStorage(state, notification.status).item
}
if (notification.type === 'pleroma:report') {
dispatch('addReport', notification.report)
}
if (notification.type === 'pleroma:emoji_reaction') {
dispatch('fetchEmojiReactionsBy', notification.status.id)
}
// Only add a new notification if we don't have one for the same action
// eslint-disable-next-line no-prototype-builtins
if (!state.notifications.idStore.hasOwnProperty(notification.id)) {
updateNotificationsMinMaxId(state, notification)
state.notifications.data.push(notification)
state.notifications.idStore[notification.id] = notification
newNotificationSideEffects(notification)
} else if (notification.seen) {
state.notifications.idStore[notification.id].seen = true
}
})
}
const removeStatus = (state, { timeline, userId }) => {
const timelineObject = state.timelines[timeline]
if (userId) {
@ -400,7 +332,6 @@ const removeStatus = (state, { timeline, userId }) => {
export const mutations = {
addNewStatuses,
addNewNotifications,
removeStatus,
showNewStatuses (state, { timeline }) {
const oldTimeline = (state.timelines[timeline])
@ -422,9 +353,6 @@ export const mutations = {
const userId = excludeUserId ? state.timelines[timeline].userId : undefined
state.timelines[timeline] = emptyTl(userId)
},
clearNotifications (state) {
state.notifications = emptyNotifications()
},
setFavorited (state, { status, value }) {
const newStatus = state.allStatusesObject[status.id]
@ -507,31 +435,6 @@ export const mutations = {
const newStatus = state.allStatusesObject[id]
newStatus.nsfw = nsfw
},
setNotificationsLoading (state, { value }) {
state.notifications.loading = value
},
setNotificationsSilence (state, { value }) {
state.notifications.desktopNotificationSilence = value
},
markNotificationsAsSeen (state) {
each(state.notifications.data, (notification) => {
notification.seen = true
})
},
markSingleNotificationAsSeen (state, { id }) {
const notification = find(state.notifications.data, n => n.id === id)
if (notification) notification.seen = true
},
dismissNotification (state, { id }) {
state.notifications.data = state.notifications.data.filter(n => n.id !== id)
},
dismissNotifications (state, { finder }) {
state.notifications.data = state.notifications.data.filter(n => finder)
},
updateNotification (state, { id, updater }) {
const notification = find(state.notifications.data, n => n.id === id)
notification && updater(notification)
},
queueFlush (state, { timeline, id }) {
state.timelines[timeline].flushMarker = id
},
@ -615,20 +518,9 @@ const statuses = {
actions: {
addNewStatuses ({ rootState, commit }, { statuses, showImmediately = false, timeline = false, noIdUpdate = false, userId, pagination }) {
commit('addNewStatuses', { statuses, showImmediately, timeline, noIdUpdate, user: rootState.users.currentUser, userId, pagination })
},
addNewNotifications (store, { notifications, older }) {
const { commit, dispatch, rootGetters } = store
const newNotificationSideEffects = (notification) => {
maybeShowNotification(store, notification)
}
commit('addNewNotifications', { dispatch, notifications, older, rootGetters, newNotificationSideEffects })
},
setNotificationsLoading ({ rootState, commit }, { value }) {
commit('setNotificationsLoading', { value })
},
setNotificationsSilence ({ rootState, commit }, { value }) {
commit('setNotificationsSilence', { value })
const deletions = statuses.filter(status => status.type === 'deletion')
console.log(deletions)
},
fetchStatus ({ rootState, dispatch }, id) {
return rootState.api.backendInteractor.fetchStatus({ id })
@ -725,35 +617,6 @@ const statuses = {
queueFlushAll ({ rootState, commit }) {
commit('queueFlushAll')
},
markNotificationsAsSeen ({ rootState, commit }) {
commit('markNotificationsAsSeen')
apiService.markNotificationsAsSeen({
id: rootState.statuses.notifications.maxId,
credentials: rootState.users.currentUser.credentials
}).then(() => {
closeAllDesktopNotifications(rootState)
})
},
markSingleNotificationAsSeen ({ rootState, commit }, { id }) {
commit('markSingleNotificationAsSeen', { id })
apiService.markNotificationsAsSeen({
single: true,
id,
credentials: rootState.users.currentUser.credentials
}).then(() => {
closeDesktopNotification(rootState, id)
})
},
dismissNotificationLocal ({ rootState, commit }, { id }) {
commit('dismissNotification', { id })
},
dismissNotification ({ rootState, commit }, { id }) {
commit('dismissNotification', { id })
rootState.api.backendInteractor.dismissNotification({ id })
},
updateNotification ({ rootState, commit }, { id, updater }) {
commit('updateNotification', { id, updater })
},
fetchFavsAndRepeats ({ rootState, commit }, id) {
Promise.all([
rootState.api.backendInteractor.fetchFavoritedByUsers({ id }),

View File

@ -498,7 +498,7 @@ const users = {
store.commit('addNewUsers', users)
store.commit('addNewUsers', targetUsers)
const notificationsObject = store.rootState.statuses.notifications.idStore
const notificationsObject = store.rootState.notifications.idStore
const relevantNotifications = Object.entries(notificationsObject)
.filter(([k, val]) => notificationIds.includes(k))
.map(([k, val]) => val)

View File

@ -7,7 +7,7 @@ const state = { failCreateNotif: false }
export const showDesktopNotification = (rootState, desktopNotificationOpts) => {
if (!('Notification' in window && window.Notification.permission === 'granted')) return
if (rootState.statuses.notifications.desktopNotificationSilence) { return }
if (rootState.notifications.desktopNotificationSilence) { return }
if (isSWSupported()) {
swDesktopNotification(desktopNotificationOpts)

View File

@ -439,7 +439,6 @@ export const parseNotification = (data) => {
output.type = mastoDict[data.type] || data.type
output.seen = data.pleroma.is_seen
output.status = isStatusNotification(output.type) ? parseStatus(data.status) : null
output.action = output.status // TODO: Refactor, this is unneeded
output.target = output.type !== 'move'
? null
: parseUser(data.target)

View File

@ -6,7 +6,7 @@ import FaviconService from 'src/services/favicon_service/favicon_service.js'
let cachedBadgeUrl = null
export const notificationsFromStore = store => store.state.statuses.notifications.data
export const notificationsFromStore = store => store.state.notifications.data
export const visibleTypes = store => {
const rootState = store.rootState || store.state

View File

@ -21,7 +21,7 @@ const fetchAndUpdate = ({ store, credentials, older = false, since }) => {
const args = { credentials }
const { getters } = store
const rootState = store.rootState || store.state
const timelineData = rootState.statuses.notifications
const timelineData = rootState.notifications
const hideMutedPosts = getters.mergedConfig.hideMutedPosts
args.includeTypes = mastoApiNotificationTypes