notification_utils.js (6027B)
- import { muteWordHits } from '../status_parser/status_parser.js'
- import { showDesktopNotification } from '../desktop_notification_utils/desktop_notification_utils.js'
- import FaviconService from 'src/services/favicon_service/favicon_service.js'
- export const ACTIONABLE_NOTIFICATION_TYPES = new Set(['mention', 'pleroma:report', 'follow_request'])
- let cachedBadgeUrl = null
- export const notificationsFromStore = store => store.state.notifications.data
- export const visibleTypes = store => {
- // When called from within a module we need rootGetters to access wider scope
- // however when called from a component (i.e. this.$store) we already have wider scope
- const rootGetters = store.rootGetters || store.getters
- const { notificationVisibility } = rootGetters.mergedConfig
- return ([
- notificationVisibility.likes && 'like',
- notificationVisibility.mentions && 'mention',
- notificationVisibility.statuses && 'status',
- notificationVisibility.repeats && 'repeat',
- notificationVisibility.follows && 'follow',
- notificationVisibility.followRequest && 'follow_request',
- notificationVisibility.moves && 'move',
- notificationVisibility.emojiReactions && 'pleroma:emoji_reaction',
- notificationVisibility.reports && 'pleroma:report',
- notificationVisibility.polls && 'poll'
- ].filter(_ => _))
- }
- const statusNotifications = new Set(['like', 'mention', 'status', 'repeat', 'pleroma:emoji_reaction', 'poll'])
- export const isStatusNotification = (type) => statusNotifications.has(type)
- export const isValidNotification = (notification) => {
- if (isStatusNotification(notification.type) && !notification.status) {
- return false
- }
- return true
- }
- const sortById = (a, b) => {
- const seqA = Number(a.id)
- const seqB = Number(b.id)
- const isSeqA = !Number.isNaN(seqA)
- const isSeqB = !Number.isNaN(seqB)
- if (isSeqA && isSeqB) {
- return seqA > seqB ? -1 : 1
- } else if (isSeqA && !isSeqB) {
- return 1
- } else if (!isSeqA && isSeqB) {
- return -1
- } else {
- return a.id > b.id ? -1 : 1
- }
- }
- const isMutedNotification = (store, notification) => {
- if (!notification.status) return
- const rootGetters = store.rootGetters || store.getters
- return notification.status.muted || muteWordHits(notification.status, rootGetters.mergedConfig.muteWords).length > 0
- }
- export const maybeShowNotification = (store, notification) => {
- const rootState = store.rootState || store.state
- const rootGetters = store.rootGetters || store.getters
- if (notification.seen) return
- if (!visibleTypes(store).includes(notification.type)) return
- if (notification.type === 'mention' && isMutedNotification(store, notification)) return
- const notificationObject = prepareNotificationObject(notification, rootGetters.i18n)
- showDesktopNotification(rootState, notificationObject)
- }
- export const filteredNotificationsFromStore = (store, types) => {
- // map is just to clone the array since sort mutates it and it causes some issues
- const sortedNotifications = notificationsFromStore(store).map(_ => _).sort(sortById)
- // TODO implement sorting elsewhere and make it optional
- return sortedNotifications.filter(
- (notification) => (types || visibleTypes(store)).includes(notification.type)
- )
- }
- export const unseenNotificationsFromStore = store => {
- const rootGetters = store.rootGetters || store.getters
- const ignoreInactionableSeen = rootGetters.mergedConfig.ignoreInactionableSeen
- return filteredNotificationsFromStore(store).filter(({ seen, type }) => {
- if (!ignoreInactionableSeen) return !seen
- if (seen) return false
- return ACTIONABLE_NOTIFICATION_TYPES.has(type)
- })
- }
- export const prepareNotificationObject = (notification, i18n) => {
- if (cachedBadgeUrl === null) {
- const favicons = FaviconService.getOriginalFavicons()
- const favicon = favicons[favicons.length - 1]
- if (!favicon) {
- cachedBadgeUrl = 'about:blank'
- } else {
- cachedBadgeUrl = favicon.favimg.src
- }
- }
- const notifObj = {
- tag: notification.id,
- type: notification.type,
- badge: cachedBadgeUrl
- }
- const status = notification.status
- const title = notification.from_profile.name
- notifObj.title = title
- notifObj.icon = notification.from_profile.profile_image_url
- let i18nString
- switch (notification.type) {
- case 'like':
- i18nString = 'favorited_you'
- break
- case 'status':
- i18nString = 'subscribed_status'
- break
- case 'repeat':
- i18nString = 'repeated_you'
- break
- case 'follow':
- i18nString = 'followed_you'
- break
- case 'move':
- i18nString = 'migrated_to'
- break
- case 'follow_request':
- i18nString = 'follow_request'
- break
- case 'pleroma:report':
- i18nString = 'submitted_report'
- break
- case 'poll':
- i18nString = 'poll_ended'
- break
- }
- if (notification.type === 'pleroma:emoji_reaction') {
- notifObj.body = i18n.t('notifications.reacted_with', [notification.emoji])
- } else if (i18nString) {
- notifObj.body = i18n.t('notifications.' + i18nString)
- } else if (isStatusNotification(notification.type)) {
- notifObj.body = notification.status.text
- }
- // Shows first attached non-nsfw image, if any. Should add configuration for this somehow...
- if (status && status.attachments && status.attachments.length > 0 && !status.nsfw &&
- status.attachments[0].mimetype.startsWith('image/')) {
- notifObj.image = status.attachments[0].url
- }
- return notifObj
- }
- export const countExtraNotifications = (store) => {
- const rootGetters = store.rootGetters || store.getters
- const mergedConfig = rootGetters.mergedConfig
- if (!mergedConfig.showExtraNotifications) {
- return 0
- }
- return [
- mergedConfig.showChatsInExtraNotifications ? rootGetters.unreadChatCount : 0,
- mergedConfig.showAnnouncementsInExtraNotifications ? rootGetters.unreadAnnouncementCount : 0,
- mergedConfig.showFollowRequestsInExtraNotifications ? rootGetters.followRequestCount : 0
- ].reduce((a, c) => a + c, 0)
- }