diff options
Diffstat (limited to 'src/components/notifications')
| -rw-r--r-- | src/components/notifications/notification_filters.vue | 37 | ||||
| -rw-r--r-- | src/components/notifications/notifications.js | 60 | ||||
| -rw-r--r-- | src/components/notifications/notifications.scss | 36 | ||||
| -rw-r--r-- | src/components/notifications/notifications.vue | 23 |
4 files changed, 109 insertions, 47 deletions
diff --git a/src/components/notifications/notification_filters.vue b/src/components/notifications/notification_filters.vue index 1315b51a..497a5156 100644 --- a/src/components/notifications/notification_filters.vue +++ b/src/components/notifications/notification_filters.vue @@ -8,65 +8,74 @@ <template #content> <div class="dropdown-menu"> <button - class="button-default dropdown-item" + class="menu-item dropdown-item" @click="toggleNotificationFilter('likes')" > <span - class="menu-checkbox" + class="input menu-checkbox" :class="{ 'menu-checkbox-checked': filters.likes }" />{{ $t('settings.notification_visibility_likes') }} </button> <button - class="button-default dropdown-item" + class="menu-item dropdown-item" @click="toggleNotificationFilter('repeats')" > <span - class="menu-checkbox" + class="input menu-checkbox" :class="{ 'menu-checkbox-checked': filters.repeats }" />{{ $t('settings.notification_visibility_repeats') }} </button> <button - class="button-default dropdown-item" + class="menu-item dropdown-item" @click="toggleNotificationFilter('follows')" > <span - class="menu-checkbox" + class="input menu-checkbox" :class="{ 'menu-checkbox-checked': filters.follows }" />{{ $t('settings.notification_visibility_follows') }} </button> <button - class="button-default dropdown-item" + class="menu-item dropdown-item" @click="toggleNotificationFilter('mentions')" > <span - class="menu-checkbox" + class="input menu-checkbox" :class="{ 'menu-checkbox-checked': filters.mentions }" />{{ $t('settings.notification_visibility_mentions') }} </button> <button - class="button-default dropdown-item" + class="menu-item dropdown-item" + @click="toggleNotificationFilter('statuses')" + > + <span + class="input menu-checkbox" + :class="{ 'menu-checkbox-checked': filters.statuses }" + />{{ $t('settings.notification_visibility_statuses') }} + </button> + <button + class="menu-item dropdown-item" @click="toggleNotificationFilter('emojiReactions')" > <span - class="menu-checkbox" + class="input menu-checkbox" :class="{ 'menu-checkbox-checked': filters.emojiReactions }" />{{ $t('settings.notification_visibility_emoji_reactions') }} </button> <button - class="button-default dropdown-item" + class="menu-item dropdown-item" @click="toggleNotificationFilter('moves')" > <span - class="menu-checkbox" + class="input menu-checkbox" :class="{ 'menu-checkbox-checked': filters.moves }" />{{ $t('settings.notification_visibility_moves') }} </button> <button - class="button-default dropdown-item" + class="menu-item dropdown-item" @click="toggleNotificationFilter('polls')" > <span - class="menu-checkbox" + class="input menu-checkbox" :class="{ 'menu-checkbox-checked': filters.polls }" />{{ $t('settings.notification_visibility_polls') }} </button> diff --git a/src/components/notifications/notifications.js b/src/components/notifications/notifications.js index d499d3d6..85d3662e 100644 --- a/src/components/notifications/notifications.js +++ b/src/components/notifications/notifications.js @@ -1,12 +1,15 @@ import { computed } from 'vue' import { mapGetters } from 'vuex' import Notification from '../notification/notification.vue' +import ExtraNotifications from '../extra_notifications/extra_notifications.vue' import NotificationFilters from './notification_filters.vue' import notificationsFetcher from '../../services/notifications_fetcher/notifications_fetcher.service.js' import { notificationsFromStore, filteredNotificationsFromStore, - unseenNotificationsFromStore + unseenNotificationsFromStore, + 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' @@ -23,14 +26,20 @@ const DEFAULT_SEEN_TO_DISPLAY_COUNT = 30 const Notifications = { components: { Notification, - NotificationFilters + NotificationFilters, + ExtraNotifications }, props: { // Disables panel styles, unread mark, potentially other notification-related actions // meant for "Interactions" timeline minimalMode: Boolean, - // Custom filter mode, an array of strings, possible values 'mention', 'repeat', 'like', 'follow', used to override global filter for use in "Interactions" timeline + // Custom filter mode, an array of strings, possible values 'mention', 'status', 'repeat', 'like', 'follow', used to override global filter for use in "Interactions" timeline filterMode: Array, + // Do not show extra notifications + noExtra: { + type: Boolean, + default: false + }, // Disable teleporting (i.e. for /users/user/notifications) disableTeleport: Boolean }, @@ -57,22 +66,36 @@ 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 ? '*' : ''}` }, unseenCount () { return this.unseenNotifications.length }, + ignoreInactionableSeen () { return this.$store.getters.mergedConfig.ignoreInactionableSeen }, + extraNotificationsCount () { + return countExtraNotifications(this.$store) + }, unseenCountTitle () { - return this.unseenCount + (this.unreadChatCount) + this.unreadAnnouncementCount + 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 @@ -94,6 +117,10 @@ 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 + }, ...mapGetters(['unreadChatCount', 'unreadAnnouncementCount']) }, mounted () { @@ -137,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.scss b/src/components/notifications/notifications.scss index 41cfcef0..cfc1f3d6 100644 --- a/src/components/notifications/notifications.scss +++ b/src/components/notifications/notifications.scss @@ -1,5 +1,3 @@ -@import "../../variables"; - .Notifications { &:not(.minimal) { // a bit of a hack to allow scrolling below notifications @@ -7,8 +5,7 @@ } .loadmore-error { - color: $fallback--text; - color: var(--text, $fallback--text); + color: var(--text); } .notification { @@ -25,7 +22,7 @@ &.unseen { .notification-overlay { - background-image: linear-gradient(135deg, var(--badgeNotification, $fallback--cRed) 4px, transparent 10px); + background-image: linear-gradient(135deg, var(--badgeNotification) 4px, transparent 10px); } } } @@ -35,6 +32,11 @@ .notification { box-sizing: border-box; + /* TODO cleanup this */ + .Status { + flex: 1; + } + &:hover .animated.Avatar { canvas { display: none; @@ -60,24 +62,17 @@ width: 32px; height: 32px; } - - .faint { - --link: var(--faintLink); - --text: var(--faint); - } } .follow-request-accept { &:hover { - color: $fallback--text; - color: var(--text, $fallback--text); + color: var(--text); } } .follow-request-reject { &:hover { - color: $fallback--cRed; - color: var(--cRed, $fallback--cRed); + color: var(--cRed); } } @@ -97,11 +92,6 @@ } } - /* TODO cleanup this */ - .Status { - flex: 1; - } - time { white-space: nowrap; } @@ -129,6 +119,14 @@ .emoji-reaction-emoji { font-size: 1.3em; + max-width: 1.25em; + height: 1.25em; + width: auto; + } + + .emoji-reaction-emoji-image { + vertical-align: middle; + object-fit: contain; } .notification-details { diff --git a/src/components/notifications/notifications.vue b/src/components/notifications/notifications.vue index 633efca6..87e7b68d 100644 --- a/src/components/notifications/notifications.vue +++ b/src/components/notifications/notifications.vue @@ -17,9 +17,9 @@ <div class="title"> {{ $t('notifications.notifications') }} <span - v-if="unseenCount" - class="badge badge-notification unseen-count" - >{{ unseenCount }}</span> + v-if="unseenCountBadgeText" + class="badge -notification unseen-count" + >{{ unseenCountBadgeText }}</span> </div> <div v-if="showScrollTop" @@ -55,14 +55,25 @@ role="feed" > <div + v-if="showExtraNotifications" + role="listitem" + class="notification" + > + <extra-notifications /> + </div> + <div v-for="notification in notificationsToDisplay" :key="notification.id" role="listitem" class="notification" - :class="{unseen: !minimalMode && !notification.seen}" + :class="{unseen: !minimalMode && shouldShowUnseen(notification)}" + @click="e => notificationClicked(notification)" > <div class="notification-overlay" /> - <notification :notification="notification" /> + <notification + :notification="notification" + @interacted="e => notificationInteracted(notification)" + /> </div> </div> <div class="panel-footer"> @@ -74,7 +85,7 @@ </div> <button v-else-if="!loading" - class="button-unstyled -link -fullwidth" + class="button-unstyled -link text-center" @click.prevent="fetchOlderNotifications()" > <div class="new-status-notification text-center"> |
