Merge remote-tracking branch 'origin/develop' into migrate/vuex-to-pinia
This commit is contained in:
commit
58e18d48df
489 changed files with 31167 additions and 9871 deletions
|
|
@ -7,69 +7,94 @@
|
|||
>
|
||||
<template #content>
|
||||
<div class="dropdown-menu">
|
||||
<button
|
||||
class="button-default dropdown-item"
|
||||
@click="toggleNotificationFilter('likes')"
|
||||
>
|
||||
<span
|
||||
class="menu-checkbox"
|
||||
:class="{ 'menu-checkbox-checked': filters.likes }"
|
||||
/>{{ $t('settings.notification_visibility_likes') }}
|
||||
</button>
|
||||
<button
|
||||
class="button-default dropdown-item"
|
||||
@click="toggleNotificationFilter('repeats')"
|
||||
>
|
||||
<span
|
||||
class="menu-checkbox"
|
||||
:class="{ 'menu-checkbox-checked': filters.repeats }"
|
||||
/>{{ $t('settings.notification_visibility_repeats') }}
|
||||
</button>
|
||||
<button
|
||||
class="button-default dropdown-item"
|
||||
@click="toggleNotificationFilter('follows')"
|
||||
>
|
||||
<span
|
||||
class="menu-checkbox"
|
||||
:class="{ 'menu-checkbox-checked': filters.follows }"
|
||||
/>{{ $t('settings.notification_visibility_follows') }}
|
||||
</button>
|
||||
<button
|
||||
class="button-default dropdown-item"
|
||||
@click="toggleNotificationFilter('mentions')"
|
||||
>
|
||||
<span
|
||||
class="menu-checkbox"
|
||||
:class="{ 'menu-checkbox-checked': filters.mentions }"
|
||||
/>{{ $t('settings.notification_visibility_mentions') }}
|
||||
</button>
|
||||
<button
|
||||
class="button-default dropdown-item"
|
||||
@click="toggleNotificationFilter('emojiReactions')"
|
||||
>
|
||||
<span
|
||||
class="menu-checkbox"
|
||||
:class="{ 'menu-checkbox-checked': filters.emojiReactions }"
|
||||
/>{{ $t('settings.notification_visibility_emoji_reactions') }}
|
||||
</button>
|
||||
<button
|
||||
class="button-default dropdown-item"
|
||||
@click="toggleNotificationFilter('moves')"
|
||||
>
|
||||
<span
|
||||
class="menu-checkbox"
|
||||
:class="{ 'menu-checkbox-checked': filters.moves }"
|
||||
/>{{ $t('settings.notification_visibility_moves') }}
|
||||
</button>
|
||||
<button
|
||||
class="button-default dropdown-item"
|
||||
@click="toggleNotificationFilter('polls')"
|
||||
>
|
||||
<span
|
||||
class="menu-checkbox"
|
||||
:class="{ 'menu-checkbox-checked': filters.polls }"
|
||||
/>{{ $t('settings.notification_visibility_polls') }}
|
||||
</button>
|
||||
<div class="menu-item dropdown-item -icon">
|
||||
<button
|
||||
class="main-button"
|
||||
@click="toggleNotificationFilter('likes')"
|
||||
>
|
||||
<span
|
||||
class="input menu-checkbox"
|
||||
:class="{ 'menu-checkbox-checked': filters.likes }"
|
||||
/>{{ $t('settings.notification_visibility_likes') }}
|
||||
</button>
|
||||
</div>
|
||||
<div class="menu-item dropdown-item -icon">
|
||||
<button
|
||||
class="main-button"
|
||||
@click="toggleNotificationFilter('repeats')"
|
||||
>
|
||||
<span
|
||||
class="input menu-checkbox"
|
||||
:class="{ 'menu-checkbox-checked': filters.repeats }"
|
||||
/>{{ $t('settings.notification_visibility_repeats') }}
|
||||
</button>
|
||||
</div>
|
||||
<div class="menu-item dropdown-item -icon">
|
||||
<button
|
||||
class="main-button"
|
||||
@click="toggleNotificationFilter('follows')"
|
||||
>
|
||||
<span
|
||||
class="input menu-checkbox"
|
||||
:class="{ 'menu-checkbox-checked': filters.follows }"
|
||||
/>{{ $t('settings.notification_visibility_follows') }}
|
||||
</button>
|
||||
</div>
|
||||
<div class="menu-item dropdown-item -icon">
|
||||
<button
|
||||
class="main-button"
|
||||
@click="toggleNotificationFilter('mentions')"
|
||||
>
|
||||
<span
|
||||
class="input menu-checkbox"
|
||||
:class="{ 'menu-checkbox-checked': filters.mentions }"
|
||||
/>{{ $t('settings.notification_visibility_mentions') }}
|
||||
</button>
|
||||
</div>
|
||||
<div class="menu-item dropdown-item -icon">
|
||||
<button
|
||||
class="main-button"
|
||||
@click="toggleNotificationFilter('statuses')"
|
||||
>
|
||||
<span
|
||||
class="input menu-checkbox"
|
||||
:class="{ 'menu-checkbox-checked': filters.statuses }"
|
||||
/>{{ $t('settings.notification_visibility_statuses') }}
|
||||
</button>
|
||||
</div>
|
||||
<div class="menu-item dropdown-item -icon">
|
||||
<button
|
||||
class="main-button"
|
||||
@click="toggleNotificationFilter('emojiReactions')"
|
||||
>
|
||||
<span
|
||||
class="input menu-checkbox"
|
||||
:class="{ 'menu-checkbox-checked': filters.emojiReactions }"
|
||||
/>{{ $t('settings.notification_visibility_emoji_reactions') }}
|
||||
</button>
|
||||
</div>
|
||||
<div class="menu-item dropdown-item -icon">
|
||||
<button
|
||||
class="main-button"
|
||||
@click="toggleNotificationFilter('moves')"
|
||||
>
|
||||
<span
|
||||
class="input menu-checkbox"
|
||||
:class="{ 'menu-checkbox-checked': filters.moves }"
|
||||
/>{{ $t('settings.notification_visibility_moves') }}
|
||||
</button>
|
||||
</div>
|
||||
<div class="menu-item dropdown-item -icon">
|
||||
<button
|
||||
class="main-button"
|
||||
@click="toggleNotificationFilter('polls')"
|
||||
>
|
||||
<span
|
||||
class="input menu-checkbox"
|
||||
:class="{ 'menu-checkbox-checked': filters.polls }"
|
||||
/>{{ $t('settings.notification_visibility_polls') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #trigger>
|
||||
|
|
|
|||
|
|
@ -2,12 +2,15 @@ import { computed } from 'vue'
|
|||
import { mapGetters } from 'vuex'
|
||||
import { mapState } from 'pinia'
|
||||
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'
|
||||
|
|
@ -26,14 +29,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
|
||||
},
|
||||
|
|
@ -60,22 +69,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 } = useInterfaceStore()
|
||||
|
|
@ -97,6 +120,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
|
||||
},
|
||||
...mapState(useAnnouncementsStore, ['unreadAnnouncementCount']),
|
||||
...mapGetters(['unreadChatCount'])
|
||||
},
|
||||
|
|
@ -141,11 +168,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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -136,6 +126,7 @@
|
|||
|
||||
.emoji-reaction-emoji-image {
|
||||
vertical-align: middle;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.notification-details {
|
||||
|
|
|
|||
|
|
@ -14,13 +14,13 @@
|
|||
v-if="!noHeading"
|
||||
class="notifications-heading panel-heading -sticky"
|
||||
>
|
||||
<div class="title">
|
||||
<h1 class="title">
|
||||
{{ $t('notifications.notifications') }}
|
||||
<span
|
||||
v-if="unseenCount"
|
||||
class="badge badge-notification unseen-count"
|
||||
>{{ unseenCount }}</span>
|
||||
</div>
|
||||
v-if="unseenCountBadgeText"
|
||||
class="badge -notification unseen-count"
|
||||
>{{ unseenCountBadgeText }}</span>
|
||||
</h1>
|
||||
<div
|
||||
v-if="showScrollTop"
|
||||
class="rightside-button"
|
||||
|
|
@ -54,15 +54,26 @@
|
|||
class="panel-body"
|
||||
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">
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue