diff --git a/changelog.d/double-notifications.fix b/changelog.d/double-notifications.fix new file mode 100644 index 00000000..24e08c0f --- /dev/null +++ b/changelog.d/double-notifications.fix @@ -0,0 +1 @@ +Fix native notifications appearing as many times as there are open tabs. Clicking on notification will focus last focused tab. diff --git a/changelog.d/focus-clear.add b/changelog.d/focus-clear.add new file mode 100644 index 00000000..70f54ab6 --- /dev/null +++ b/changelog.d/focus-clear.add @@ -0,0 +1 @@ +Focusing into a tab clears all current desktop notifications diff --git a/changelog.d/mobile-chrome-notifs.fix b/changelog.d/mobile-chrome-notifs.fix new file mode 100644 index 00000000..7db10c56 --- /dev/null +++ b/changelog.d/mobile-chrome-notifs.fix @@ -0,0 +1 @@ +Fixed error that appeared on mobile Chrome(ium) (and derivatives) when native notifications are allowed diff --git a/changelog.d/mobile-drawer-notifications.change b/changelog.d/mobile-drawer-notifications.change new file mode 100644 index 00000000..9353c709 --- /dev/null +++ b/changelog.d/mobile-drawer-notifications.change @@ -0,0 +1 @@ +Added option to not mark all notifications when closing notifications drawer on mobile, this creates a new button to mark all as seen. diff --git a/changelog.d/more-notification-types-setting.fix b/changelog.d/more-notification-types-setting.fix new file mode 100644 index 00000000..2d71b599 --- /dev/null +++ b/changelog.d/more-notification-types-setting.fix @@ -0,0 +1 @@ +Fixed being unable to set notification visibility for reports and follow requests diff --git a/changelog.d/native-filtering.add b/changelog.d/native-filtering.add new file mode 100644 index 00000000..82ab9a23 --- /dev/null +++ b/changelog.d/native-filtering.add @@ -0,0 +1 @@ +Added option to toggle what notification types appear in native notifications, by default less important ones (likes, repeats, etc) will no longer show up in native notifications. diff --git a/changelog.d/native-notifications.add b/changelog.d/native-notifications.add new file mode 100644 index 00000000..d896e7c0 --- /dev/null +++ b/changelog.d/native-notifications.add @@ -0,0 +1 @@ +Native notifications now also have "badge" property that matches instance's favicon (visible in Android Chromium at least) diff --git a/changelog.d/noninteractive-ignore-read.add b/changelog.d/noninteractive-ignore-read.add new file mode 100644 index 00000000..5e8710cf --- /dev/null +++ b/changelog.d/noninteractive-ignore-read.add @@ -0,0 +1 @@ +Added option to treat non-interactive notifications (likes, repeats et all) as seen for visual purposes (no read mark, ignored in counters, still can show in native notifications) diff --git a/changelog.d/notification-read.add b/changelog.d/notification-read.add new file mode 100644 index 00000000..e5027a95 --- /dev/null +++ b/changelog.d/notification-read.add @@ -0,0 +1 @@ +Interacting (opening reply box etc) or simply clicking on non-interactive notifications now marks them as read. Clicking on native notifications for non-interactive ones also marks them as seen. diff --git a/changelog.d/notifications-sorting.change b/changelog.d/notifications-sorting.change new file mode 100644 index 00000000..3a616244 --- /dev/null +++ b/changelog.d/notifications-sorting.change @@ -0,0 +1 @@ +Notifications are no longer sorted by "seen" status since interacting with them can change their read status and makes UI jumpy. Old behavior can be restored in settings. diff --git a/changelog.d/serviceworkers.change b/changelog.d/serviceworkers.change new file mode 100644 index 00000000..b3b64f6d --- /dev/null +++ b/changelog.d/serviceworkers.change @@ -0,0 +1 @@ +Notifications are now shown through a serviceworker (since mobile chrome does not allow them otherwise), it's always enabled, even if previously we only enabled it for WebPush notifications only. If you don't like websites "running" while closed, check how to disable them in your browser. Old way to show notifications will be used as a fallback but might not have all the new features. diff --git a/changelog.d/unreads-sync.fix b/changelog.d/unreads-sync.fix new file mode 100644 index 00000000..1eac3364 --- /dev/null +++ b/changelog.d/unreads-sync.fix @@ -0,0 +1 @@ +unread notifications should now properly catch up (eventually) in polling mode diff --git a/changelog.d/web-push-always.add b/changelog.d/web-push-always.add new file mode 100644 index 00000000..f8b8888a --- /dev/null +++ b/changelog.d/web-push-always.add @@ -0,0 +1 @@ +Added option to always "show" notifications when using web push for better compatibility with some browsers (chrome, edge, safari) diff --git a/src/boot/after_store.js b/src/boot/after_store.js index 395d4834..84fea954 100644 --- a/src/boot/after_store.js +++ b/src/boot/after_store.js @@ -16,6 +16,7 @@ import backendInteractorService from '../services/backend_interactor_service/bac import { CURRENT_VERSION } from '../services/theme_data/theme_data.service.js' import { applyTheme, applyConfig } from '../services/style_setter/style_setter.js' import FaviconService from '../services/favicon_service/favicon_service.js' +import { initServiceWorker, updateFocus } from '../services/sw/sw.js' let staticInitialResults = null @@ -344,6 +345,9 @@ const afterStoreSetup = async ({ store, i18n }) => { store.dispatch('setLayoutHeight', windowHeight()) FaviconService.initFaviconService() + initServiceWorker(store) + + window.addEventListener('focus', () => updateFocus()) const overrides = window.___pleromafe_dev_overrides || {} const server = (typeof overrides.target !== 'undefined') ? overrides.target : window.location.origin diff --git a/src/components/mobile_nav/mobile_nav.js b/src/components/mobile_nav/mobile_nav.js index b5325116..8c9261b0 100644 --- a/src/components/mobile_nav/mobile_nav.js +++ b/src/components/mobile_nav/mobile_nav.js @@ -14,7 +14,8 @@ import { faBell, faBars, faArrowUp, - faMinus + faMinus, + faCheckDouble } from '@fortawesome/free-solid-svg-icons' library.add( @@ -22,7 +23,8 @@ library.add( faBell, faBars, faArrowUp, - faMinus + faMinus, + faCheckDouble ) const MobileNav = { @@ -55,6 +57,12 @@ const MobileNav = { unseenNotificationsCount () { return this.unseenNotifications.length + countExtraNotifications(this.$store) }, + unseenCount () { + return this.unseenNotifications.length + }, + unseenCountBadgeText () { + return `${this.unseenCount ? this.unseenCount : ''}` + }, hideSitename () { return this.$store.state.instance.hideSitename }, sitename () { return this.$store.state.instance.name }, isChat () { @@ -67,6 +75,9 @@ const MobileNav = { shouldConfirmLogout () { return this.$store.getters.mergedConfig.modalOnLogout }, + closingDrawerMarksAsSeen () { + return this.$store.getters.mergedConfig.closingDrawerMarksAsSeen + }, ...mapGetters(['unreadChatCount']) }, methods: { @@ -81,7 +92,7 @@ const MobileNav = { // make sure to mark notifs seen only when the notifs were open and not // from close-calls. this.notificationsOpen = false - if (markRead) { + if (markRead && this.closingDrawerMarksAsSeen) { this.markNotificationsAsSeen() } } @@ -117,7 +128,6 @@ const MobileNav = { this.hideConfirmLogout() }, markNotificationsAsSeen () { - // this.$refs.notifications.markAsSeen() this.$store.dispatch('markNotificationsAsSeen') }, onScroll ({ target: { scrollTop, clientHeight, scrollHeight } }) { diff --git a/src/components/mobile_nav/mobile_nav.vue b/src/components/mobile_nav/mobile_nav.vue index c2746abe..f20a509d 100644 --- a/src/components/mobile_nav/mobile_nav.vue +++ b/src/components/mobile_nav/mobile_nav.vue @@ -50,7 +50,13 @@ @touchmove.stop="notificationsTouchMove" >
- {{ $t('notifications.notifications') }} + + {{ $t('notifications.notifications') }} + {{ unseenCountBadgeText }} + +
diff --git a/src/components/notifications/notifications.js b/src/components/notifications/notifications.js index 571df0f1..a9fa8455 100644 --- a/src/components/notifications/notifications.js +++ b/src/components/notifications/notifications.js @@ -8,7 +8,8 @@ import { notificationsFromStore, filteredNotificationsFromStore, unseenNotificationsFromStore, - countExtraNotifications + countExtraNotifications, + ACTIONABLE_NOTIFICATION_TYPES } from '../../services/notification_utils/notification_utils.js' import FaviconService from '../../services/favicon_service/favicon_service.js' import { library } from '@fortawesome/fontawesome-svg-core' @@ -65,13 +66,20 @@ 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) }, filteredNotifications () { - return filteredNotificationsFromStore(this.$store, this.filterMode) + if (this.unseenAtTop) { + return [ + ...filteredNotificationsFromStore(this.$store).filter(n => this.shouldShowUnseen(n)), + ...filteredNotificationsFromStore(this.$store).filter(n => !this.shouldShowUnseen(n)) + ] + } else { + return filteredNotificationsFromStore(this.$store, this.filterMode) + } }, unseenCountBadgeText () { return `${this.unseenCount ? this.unseenCount : ''}${this.extraNotificationsCount ? '*' : ''}` @@ -79,6 +87,7 @@ const Notifications = { unseenCount () { return this.unseenNotifications.length }, + ignoreInactionableSeen () { return this.$store.getters.mergedConfig.ignoreInactionableSeen }, extraNotificationsCount () { return countExtraNotifications(this.$store) }, @@ -86,7 +95,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 @@ -108,6 +117,7 @@ const Notifications = { return this.filteredNotifications.slice(0, this.unseenCount + this.seenToDisplayCount) }, noSticky () { return this.$store.getters.mergedConfig.disableStickyHeaders }, + unseenAtTop () { return this.$store.getters.mergedConfig.unseenAtTop }, showExtraNotifications () { return !this.noExtra }, @@ -154,11 +164,28 @@ const Notifications = { scrollToTop () { const scrollable = this.scrollerRef scrollable.scrollTo({ top: this.$refs.root.offsetTop }) - // this.$refs.root.scrollIntoView({ behavior: 'smooth', block: 'start' }) }, updateScrollPosition () { this.showScrollTop = this.$refs.root.offsetTop < this.scrollerRef.scrollTop }, + shouldShowUnseen (notification) { + if (notification.seen) return false + + const actionable = ACTIONABLE_NOTIFICATION_TYPES.has(notification.type) + return this.ignoreInactionableSeen ? actionable : true + }, + /* "Interacted" really refers to "actionable" notifications that require user input, + * everything else (likes/repeats/reacts) cannot be acted and therefore we just clear + * the "seen" status upon any clicks on them + */ + notificationClicked (notification) { + const { id } = notification + this.$store.dispatch('notificationClicked', { id }) + }, + notificationInteracted (notification) { + const { id } = notification + 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 index 999f8e9c..a0025182 100644 --- a/src/components/notifications/notifications.vue +++ b/src/components/notifications/notifications.vue @@ -66,10 +66,14 @@ :key="notification.id" role="listitem" class="notification" - :class="{unseen: !minimalMode && !notification.seen}" + :class="{unseen: !minimalMode && shouldShowUnseen(notification)}" + @click="e => notificationClicked(notification)" >
- +