commit: ec2937ec1f3b0ae153f79604eb35b57ffe0f9af2
parent c059f4a7ee16c0128c348c43c9d468e7cfdb5ef7
Author: Henry Jameson <me@hjkos.com>
Date: Mon, 13 Nov 2023 17:29:25 +0200
add options for marking single notification as read
Diffstat:
11 files changed, 85 insertions(+), 4 deletions(-)
diff --git a/src/components/notification/notification.js b/src/components/notification/notification.js
@@ -50,6 +50,7 @@ const Notification = {
}
},
props: ['notification'],
+ emits: ['interacted'],
components: {
StatusContent,
UserAvatar,
@@ -72,6 +73,9 @@ const Notification = {
getUser (notification) {
return this.$store.state.users.usersObject[notification.from_profile.id]
},
+ interacted () {
+ this.$emit('interacted')
+ },
toggleMute () {
this.unmuted = !this.unmuted
},
@@ -95,6 +99,7 @@ const Notification = {
}
},
doApprove () {
+ this.$emit('interacted')
this.$store.state.api.backendInteractor.approveUser({ id: this.user.id })
this.$store.dispatch('removeFollowRequest', this.user)
this.$store.dispatch('markSingleNotificationAsSeen', { id: this.notification.id })
@@ -114,6 +119,7 @@ const Notification = {
}
},
doDeny () {
+ this.$emit('interacted')
this.$store.state.api.backendInteractor.denyUser({ id: this.user.id })
.then(() => {
this.$store.dispatch('dismissNotificationLocal', { id: this.notification.id })
diff --git a/src/components/notification/notification.vue b/src/components/notification/notification.vue
@@ -6,6 +6,7 @@
class="Notification"
:compact="true"
:statusoid="notification.status"
+ @interacted="interacted"
/>
</article>
<article v-else>
diff --git a/src/components/notifications/notifications.js b/src/components/notifications/notifications.js
@@ -159,6 +159,26 @@ const Notifications = {
updateScrollPosition () {
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)
+ }
+ }
+ },
+ notificationInteracted (notification) {
+ const { id, seen } = notification
+ if (!seen) this.markOneAsSeen(id)
+ },
+ markOneAsSeen (id) {
+ this.$store.dispatch('markSingleNotificationAsSeen', { id })
+ },
markAsSeen () {
this.$store.dispatch('markNotificationsAsSeen')
this.seenToDisplayCount = DEFAULT_SEEN_TO_DISPLAY_COUNT
diff --git a/src/components/notifications/notifications.vue b/src/components/notifications/notifications.vue
@@ -67,9 +67,13 @@
role="listitem"
class="notification"
:class="{unseen: !minimalMode && !notification.seen}"
+ @click="e => notificationClicked(notification)"
>
<div class="notification-overlay" />
- <notification :notification="notification" />
+ <notification
+ :notification="notification"
+ @interacted="e => notificationInteracted(notification)"
+ />
</div>
</div>
<div class="panel-footer">
diff --git a/src/components/status/status.js b/src/components/status/status.js
@@ -154,6 +154,7 @@ const Status = {
'controlledSetMediaPlaying',
'dive'
],
+ emits: ['interacted'],
data () {
return {
uncontrolledReplying: false,
@@ -442,9 +443,11 @@ const Status = {
this.error = error
},
clearError () {
+ this.$emit('interacted')
this.error = undefined
},
toggleReplying () {
+ this.$emit('interacted')
controlledOrUncontrolledToggle(this, 'replying')
},
gotoOriginal (id) {
diff --git a/src/components/status/status.vue b/src/components/status/status.vue
@@ -508,14 +508,17 @@
:visibility="status.visibility"
:logged-in="loggedIn"
:status="status"
+ @click="$emit('interacted')"
/>
<favorite-button
:logged-in="loggedIn"
:status="status"
+ @click="$emit('interacted')"
/>
<ReactButton
v-if="loggedIn"
:status="status"
+ @click="$emit('interacted')"
/>
<extra-buttons
:status="status"
diff --git a/src/modules/statuses.js b/src/modules/statuses.js
@@ -17,6 +17,10 @@ import {
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) => ({
@@ -726,6 +730,8 @@ const statuses = {
apiService.markNotificationsAsSeen({
id: rootState.statuses.notifications.maxId,
credentials: rootState.users.currentUser.credentials
+ }).then(() => {
+ closeAllDesktopNotifications(rootState)
})
},
markSingleNotificationAsSeen ({ rootState, commit }, { id }) {
@@ -734,6 +740,8 @@ const statuses = {
single: true,
id,
credentials: rootState.users.currentUser.credentials
+ }).then(() => {
+ closeDesktopNotification(rootState, id)
})
},
dismissNotificationLocal ({ rootState, commit }, { id }) {
diff --git a/src/services/desktop_notification_utils/desktop_notification_utils.js b/src/services/desktop_notification_utils/desktop_notification_utils.js
@@ -1,4 +1,8 @@
-import { showDesktopNotification as swDesktopNotification, isSWSupported } from '../sw/sw.js'
+import {
+ showDesktopNotification as swDesktopNotification,
+ closeDesktopNotification as swCloseDesktopNotification,
+ isSWSupported
+} from '../sw/sw.js'
const state = { failCreateNotif: false }
export const showDesktopNotification = (rootState, desktopNotificationOpts) => {
@@ -16,3 +20,19 @@ export const showDesktopNotification = (rootState, desktopNotificationOpts) => {
}
}
}
+
+export const closeDesktopNotification = (rootState, id) => {
+ if (!('Notification' in window && window.Notification.permission === 'granted')) return
+
+ if (isSWSupported()) {
+ swCloseDesktopNotification({ id })
+ }
+}
+
+export const closeAllDesktopNotifications = (rootState) => {
+ if (!('Notification' in window && window.Notification.permission === 'granted')) return
+
+ if (isSWSupported()) {
+ swCloseDesktopNotification()
+ }
+}
diff --git a/src/services/notification_utils/notification_utils.js b/src/services/notification_utils/notification_utils.js
@@ -1,6 +1,7 @@
import { filter, sortBy, includes } from 'lodash'
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'
let cachedBadgeUrl = null
@@ -68,8 +69,8 @@ export const maybeShowNotification = (store, notification) => {
export const filteredNotificationsFromStore = (store, types) => {
// map is just to clone the array since sort mutates it and it causes some issues
- let sortedNotifications = notificationsFromStore(store).map(_ => _).sort(sortById)
- sortedNotifications = sortBy(sortedNotifications, 'seen')
+ 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)
)
diff --git a/src/services/sw/sw.js b/src/services/sw/sw.js
@@ -96,6 +96,15 @@ export async function showDesktopNotification (content) {
sw.postMessage({ type: 'desktopNotification', content })
}
+export async function closeDesktopNotification ({ id }) {
+ const { active: sw } = await window.navigator.serviceWorker.getRegistration()
+ if (id >= 0) {
+ sw.postMessage({ type: 'desktopNotificationClose', content: { id } })
+ } else {
+ sw.postMessage({ type: 'desktopNotificationClose', content: { all: true } })
+ }
+}
+
export async function updateFocus () {
const { active: sw } = await window.navigator.serviceWorker.getRegistration()
sw.postMessage({ type: 'updateFocus' })
diff --git a/src/sw.js b/src/sw.js
@@ -66,6 +66,12 @@ self.addEventListener('message', async (event) => {
self.registration.showNotification(title, rest)
}
+ if (type === 'desktopNotificationClose') {
+ const { id, all } = content
+ const search = all ? null : { tag: id }
+ self.registration.getNotifications(search).forEach(n => n.close())
+ }
+
if (type === 'updateFocus') {
state.lastFocused = event.source.id