add options for marking single notification as read

This commit is contained in:
Henry Jameson 2023-11-13 17:29:25 +02:00
parent c059f4a7ee
commit ec2937ec1f
11 changed files with 85 additions and 4 deletions

View File

@ -50,6 +50,7 @@ const Notification = {
} }
}, },
props: ['notification'], props: ['notification'],
emits: ['interacted'],
components: { components: {
StatusContent, StatusContent,
UserAvatar, UserAvatar,
@ -72,6 +73,9 @@ const Notification = {
getUser (notification) { getUser (notification) {
return this.$store.state.users.usersObject[notification.from_profile.id] return this.$store.state.users.usersObject[notification.from_profile.id]
}, },
interacted () {
this.$emit('interacted')
},
toggleMute () { toggleMute () {
this.unmuted = !this.unmuted this.unmuted = !this.unmuted
}, },
@ -95,6 +99,7 @@ const Notification = {
} }
}, },
doApprove () { doApprove () {
this.$emit('interacted')
this.$store.state.api.backendInteractor.approveUser({ id: this.user.id }) this.$store.state.api.backendInteractor.approveUser({ id: this.user.id })
this.$store.dispatch('removeFollowRequest', this.user) this.$store.dispatch('removeFollowRequest', this.user)
this.$store.dispatch('markSingleNotificationAsSeen', { id: this.notification.id }) this.$store.dispatch('markSingleNotificationAsSeen', { id: this.notification.id })
@ -114,6 +119,7 @@ const Notification = {
} }
}, },
doDeny () { doDeny () {
this.$emit('interacted')
this.$store.state.api.backendInteractor.denyUser({ id: this.user.id }) this.$store.state.api.backendInteractor.denyUser({ id: this.user.id })
.then(() => { .then(() => {
this.$store.dispatch('dismissNotificationLocal', { id: this.notification.id }) this.$store.dispatch('dismissNotificationLocal', { id: this.notification.id })

View File

@ -6,6 +6,7 @@
class="Notification" class="Notification"
:compact="true" :compact="true"
:statusoid="notification.status" :statusoid="notification.status"
@interacted="interacted"
/> />
</article> </article>
<article v-else> <article v-else>

View File

@ -159,6 +159,26 @@ const Notifications = {
updateScrollPosition () { updateScrollPosition () {
this.showScrollTop = this.$refs.root.offsetTop < this.scrollerRef.scrollTop 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 () { markAsSeen () {
this.$store.dispatch('markNotificationsAsSeen') this.$store.dispatch('markNotificationsAsSeen')
this.seenToDisplayCount = DEFAULT_SEEN_TO_DISPLAY_COUNT this.seenToDisplayCount = DEFAULT_SEEN_TO_DISPLAY_COUNT

View File

@ -67,9 +67,13 @@
role="listitem" role="listitem"
class="notification" class="notification"
:class="{unseen: !minimalMode && !notification.seen}" :class="{unseen: !minimalMode && !notification.seen}"
@click="e => notificationClicked(notification)"
> >
<div class="notification-overlay" /> <div class="notification-overlay" />
<notification :notification="notification" /> <notification
:notification="notification"
@interacted="e => notificationInteracted(notification)"
/>
</div> </div>
</div> </div>
<div class="panel-footer"> <div class="panel-footer">

View File

@ -154,6 +154,7 @@ const Status = {
'controlledSetMediaPlaying', 'controlledSetMediaPlaying',
'dive' 'dive'
], ],
emits: ['interacted'],
data () { data () {
return { return {
uncontrolledReplying: false, uncontrolledReplying: false,
@ -442,9 +443,11 @@ const Status = {
this.error = error this.error = error
}, },
clearError () { clearError () {
this.$emit('interacted')
this.error = undefined this.error = undefined
}, },
toggleReplying () { toggleReplying () {
this.$emit('interacted')
controlledOrUncontrolledToggle(this, 'replying') controlledOrUncontrolledToggle(this, 'replying')
}, },
gotoOriginal (id) { gotoOriginal (id) {

View File

@ -508,14 +508,17 @@
:visibility="status.visibility" :visibility="status.visibility"
:logged-in="loggedIn" :logged-in="loggedIn"
:status="status" :status="status"
@click="$emit('interacted')"
/> />
<favorite-button <favorite-button
:logged-in="loggedIn" :logged-in="loggedIn"
:status="status" :status="status"
@click="$emit('interacted')"
/> />
<ReactButton <ReactButton
v-if="loggedIn" v-if="loggedIn"
:status="status" :status="status"
@click="$emit('interacted')"
/> />
<extra-buttons <extra-buttons
:status="status" :status="status"

View File

@ -17,6 +17,10 @@ import {
isValidNotification, isValidNotification,
maybeShowNotification maybeShowNotification
} from '../services/notification_utils/notification_utils.js' } 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' import apiService from '../services/api/api.service.js'
const emptyTl = (userId = 0) => ({ const emptyTl = (userId = 0) => ({
@ -726,6 +730,8 @@ const statuses = {
apiService.markNotificationsAsSeen({ apiService.markNotificationsAsSeen({
id: rootState.statuses.notifications.maxId, id: rootState.statuses.notifications.maxId,
credentials: rootState.users.currentUser.credentials credentials: rootState.users.currentUser.credentials
}).then(() => {
closeAllDesktopNotifications(rootState)
}) })
}, },
markSingleNotificationAsSeen ({ rootState, commit }, { id }) { markSingleNotificationAsSeen ({ rootState, commit }, { id }) {
@ -734,6 +740,8 @@ const statuses = {
single: true, single: true,
id, id,
credentials: rootState.users.currentUser.credentials credentials: rootState.users.currentUser.credentials
}).then(() => {
closeDesktopNotification(rootState, id)
}) })
}, },
dismissNotificationLocal ({ rootState, commit }, { id }) { dismissNotificationLocal ({ rootState, commit }, { id }) {

View File

@ -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 } const state = { failCreateNotif: false }
export const showDesktopNotification = (rootState, desktopNotificationOpts) => { 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()
}
}

View File

@ -1,6 +1,7 @@
import { filter, sortBy, includes } from 'lodash' import { filter, sortBy, includes } from 'lodash'
import { muteWordHits } from '../status_parser/status_parser.js' import { muteWordHits } from '../status_parser/status_parser.js'
import { showDesktopNotification } from '../desktop_notification_utils/desktop_notification_utils.js' import { showDesktopNotification } from '../desktop_notification_utils/desktop_notification_utils.js'
import FaviconService from 'src/services/favicon_service/favicon_service.js' import FaviconService from 'src/services/favicon_service/favicon_service.js'
let cachedBadgeUrl = null let cachedBadgeUrl = null
@ -68,8 +69,8 @@ export const maybeShowNotification = (store, notification) => {
export const filteredNotificationsFromStore = (store, types) => { export const filteredNotificationsFromStore = (store, types) => {
// map is just to clone the array since sort mutates it and it causes some issues // map is just to clone the array since sort mutates it and it causes some issues
let sortedNotifications = notificationsFromStore(store).map(_ => _).sort(sortById) const sortedNotifications = notificationsFromStore(store).map(_ => _).sort(sortById)
sortedNotifications = sortBy(sortedNotifications, 'seen') // TODO implement sorting elsewhere and make it optional
return sortedNotifications.filter( return sortedNotifications.filter(
(notification) => (types || visibleTypes(store)).includes(notification.type) (notification) => (types || visibleTypes(store)).includes(notification.type)
) )

View File

@ -96,6 +96,15 @@ export async function showDesktopNotification (content) {
sw.postMessage({ type: 'desktopNotification', 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 () { export async function updateFocus () {
const { active: sw } = await window.navigator.serviceWorker.getRegistration() const { active: sw } = await window.navigator.serviceWorker.getRegistration()
sw.postMessage({ type: 'updateFocus' }) sw.postMessage({ type: 'updateFocus' })

View File

@ -66,6 +66,12 @@ self.addEventListener('message', async (event) => {
self.registration.showNotification(title, rest) 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') { if (type === 'updateFocus') {
state.lastFocused = event.source.id state.lastFocused = event.source.id