logo

pleroma-fe

My custom branche(s) on git.pleroma.social/pleroma/pleroma-fe git clone https://anongit.hacktivis.me/git/pleroma-fe.git/

notification_utils.js (6100B)


  1. import { muteWordHits } from '../status_parser/status_parser.js'
  2. import { showDesktopNotification } from '../desktop_notification_utils/desktop_notification_utils.js'
  3. import { useI18nStore } from 'src/stores/i18n.js'
  4. import { useAnnouncementsStore } from 'src/stores/announcements'
  5. import FaviconService from 'src/services/favicon_service/favicon_service.js'
  6. export const ACTIONABLE_NOTIFICATION_TYPES = new Set(['mention', 'pleroma:report', 'follow_request'])
  7. let cachedBadgeUrl = null
  8. export const notificationsFromStore = store => store.state.notifications.data
  9. export const visibleTypes = store => {
  10. // When called from within a module we need rootGetters to access wider scope
  11. // however when called from a component (i.e. this.$store) we already have wider scope
  12. const rootGetters = store.rootGetters || store.getters
  13. const { notificationVisibility } = rootGetters.mergedConfig
  14. return ([
  15. notificationVisibility.likes && 'like',
  16. notificationVisibility.mentions && 'mention',
  17. notificationVisibility.statuses && 'status',
  18. notificationVisibility.repeats && 'repeat',
  19. notificationVisibility.follows && 'follow',
  20. notificationVisibility.followRequest && 'follow_request',
  21. notificationVisibility.moves && 'move',
  22. notificationVisibility.emojiReactions && 'pleroma:emoji_reaction',
  23. notificationVisibility.reports && 'pleroma:report',
  24. notificationVisibility.polls && 'poll'
  25. ].filter(_ => _))
  26. }
  27. const statusNotifications = new Set(['like', 'mention', 'status', 'repeat', 'pleroma:emoji_reaction', 'poll'])
  28. export const isStatusNotification = (type) => statusNotifications.has(type)
  29. export const isValidNotification = (notification) => {
  30. if (isStatusNotification(notification.type) && !notification.status) {
  31. return false
  32. }
  33. return true
  34. }
  35. const sortById = (a, b) => {
  36. const seqA = Number(a.id)
  37. const seqB = Number(b.id)
  38. const isSeqA = !Number.isNaN(seqA)
  39. const isSeqB = !Number.isNaN(seqB)
  40. if (isSeqA && isSeqB) {
  41. return seqA > seqB ? -1 : 1
  42. } else if (isSeqA && !isSeqB) {
  43. return 1
  44. } else if (!isSeqA && isSeqB) {
  45. return -1
  46. } else {
  47. return a.id > b.id ? -1 : 1
  48. }
  49. }
  50. const isMutedNotification = (store, notification) => {
  51. if (!notification.status) return
  52. const rootGetters = store.rootGetters || store.getters
  53. return notification.status.muted || muteWordHits(notification.status, rootGetters.mergedConfig.muteWords).length > 0
  54. }
  55. export const maybeShowNotification = (store, notification) => {
  56. const rootState = store.rootState || store.state
  57. if (notification.seen) return
  58. if (!visibleTypes(store).includes(notification.type)) return
  59. if (notification.type === 'mention' && isMutedNotification(store, notification)) return
  60. const notificationObject = prepareNotificationObject(notification, useI18nStore().i18n)
  61. showDesktopNotification(rootState, notificationObject)
  62. }
  63. export const filteredNotificationsFromStore = (store, types) => {
  64. // map is just to clone the array since sort mutates it and it causes some issues
  65. const sortedNotifications = notificationsFromStore(store).map(_ => _).sort(sortById)
  66. // TODO implement sorting elsewhere and make it optional
  67. return sortedNotifications.filter(
  68. (notification) => (types || visibleTypes(store)).includes(notification.type)
  69. )
  70. }
  71. export const unseenNotificationsFromStore = store => {
  72. const rootGetters = store.rootGetters || store.getters
  73. const ignoreInactionableSeen = rootGetters.mergedConfig.ignoreInactionableSeen
  74. return filteredNotificationsFromStore(store).filter(({ seen, type }) => {
  75. if (!ignoreInactionableSeen) return !seen
  76. if (seen) return false
  77. return ACTIONABLE_NOTIFICATION_TYPES.has(type)
  78. })
  79. }
  80. export const prepareNotificationObject = (notification, i18n) => {
  81. if (cachedBadgeUrl === null) {
  82. const favicons = FaviconService.getOriginalFavicons()
  83. const favicon = favicons[favicons.length - 1]
  84. if (!favicon) {
  85. cachedBadgeUrl = 'about:blank'
  86. } else {
  87. cachedBadgeUrl = favicon.favimg.src
  88. }
  89. }
  90. const notifObj = {
  91. tag: notification.id,
  92. type: notification.type,
  93. badge: cachedBadgeUrl
  94. }
  95. const status = notification.status
  96. const title = notification.from_profile.name
  97. notifObj.title = title
  98. notifObj.icon = notification.from_profile.profile_image_url
  99. let i18nString
  100. switch (notification.type) {
  101. case 'like':
  102. i18nString = 'favorited_you'
  103. break
  104. case 'status':
  105. i18nString = 'subscribed_status'
  106. break
  107. case 'repeat':
  108. i18nString = 'repeated_you'
  109. break
  110. case 'follow':
  111. i18nString = 'followed_you'
  112. break
  113. case 'move':
  114. i18nString = 'migrated_to'
  115. break
  116. case 'follow_request':
  117. i18nString = 'follow_request'
  118. break
  119. case 'pleroma:report':
  120. i18nString = 'submitted_report'
  121. break
  122. case 'poll':
  123. i18nString = 'poll_ended'
  124. break
  125. }
  126. if (notification.type === 'pleroma:emoji_reaction') {
  127. notifObj.body = i18n.t('notifications.reacted_with', [notification.emoji])
  128. } else if (i18nString) {
  129. notifObj.body = i18n.t('notifications.' + i18nString)
  130. } else if (isStatusNotification(notification.type)) {
  131. notifObj.body = notification.status.text
  132. }
  133. // Shows first attached non-nsfw image, if any. Should add configuration for this somehow...
  134. if (status && status.attachments && status.attachments.length > 0 && !status.nsfw &&
  135. status.attachments[0].mimetype.startsWith('image/')) {
  136. notifObj.image = status.attachments[0].url
  137. }
  138. return notifObj
  139. }
  140. export const countExtraNotifications = (store) => {
  141. const rootGetters = store.rootGetters || store.getters
  142. const mergedConfig = rootGetters.mergedConfig
  143. if (!mergedConfig.showExtraNotifications) {
  144. return 0
  145. }
  146. return [
  147. mergedConfig.showChatsInExtraNotifications ? rootGetters.unreadChatCount : 0,
  148. mergedConfig.showAnnouncementsInExtraNotifications ? useAnnouncementsStore().unreadAnnouncementCount : 0,
  149. mergedConfig.showFollowRequestsInExtraNotifications ? rootGetters.followRequestCount : 0
  150. ].reduce((a, c) => a + c, 0)
  151. }