allow sorting notifications
This commit is contained in:
parent
292c17b894
commit
654a188f80
8 changed files with 128 additions and 12 deletions
client/src
app
+my-account/my-account-notifications
my-account-notifications.component.htmlmy-account-notifications.component.scssmy-account-notifications.component.ts
shared/shared-main/users
sass
server/initializers
|
@ -5,7 +5,15 @@
|
|||
Notification preferences
|
||||
</a>
|
||||
|
||||
<button class="btn" [disabled]="!hasUnreadNotifications()" (click)="markAllAsRead()">
|
||||
<div class="peertube-select-container peertube-select-button ml-2">
|
||||
<select [(ngModel)]="notificationSortType" (ngModelChange)="onNotificationSortTypeChanged()" class="form-control">
|
||||
<option value="undefined" disabled>Sort by</option>
|
||||
<option value="created" i18n>Newest first</option>
|
||||
<option value="unread-created" i18n>Unread first</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<button class="btn ml-auto" [disabled]="!hasUnreadNotifications()" (click)="markAllAsRead()">
|
||||
<ng-container *ngIf="hasUnreadNotifications()">
|
||||
<my-global-icon iconName="inbox-full" aria-hidden="true"></my-global-icon>
|
||||
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
|
||||
.header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
font-size: 15px;
|
||||
margin-bottom: 20px;
|
||||
|
||||
|
@ -18,8 +17,13 @@
|
|||
@include grey-button;
|
||||
@include button-with-icon(20px, 3px, -1px);
|
||||
}
|
||||
|
||||
.peertube-select-container {
|
||||
@include peertube-select-container(auto);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
my-user-notifications {
|
||||
font-size: 15px;
|
||||
}
|
||||
|
|
|
@ -8,6 +8,8 @@ import { UserNotificationsComponent } from '@app/shared/shared-main'
|
|||
export class MyAccountNotificationsComponent {
|
||||
@ViewChild('userNotification', { static: true }) userNotification: UserNotificationsComponent
|
||||
|
||||
notificationSortType = 'created'
|
||||
|
||||
markAllAsRead () {
|
||||
this.userNotification.markAllAsRead()
|
||||
}
|
||||
|
@ -15,4 +17,6 @@ export class MyAccountNotificationsComponent {
|
|||
hasUnreadNotifications () {
|
||||
return this.userNotification.notifications.filter(n => n.read === false).length !== 0
|
||||
}
|
||||
|
||||
onNotificationSortTypeChanged () {}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import { ComponentPaginationLight, RestExtractor, RestService, User, UserNotific
|
|||
import { ResultList, UserNotification as UserNotificationServer, UserNotificationSetting } from '@shared/models'
|
||||
import { environment } from '../../../../environments/environment'
|
||||
import { UserNotification } from './user-notification.model'
|
||||
import { SortMeta } from 'primeng/api'
|
||||
|
||||
@Injectable()
|
||||
export class UserNotificationService {
|
||||
|
@ -18,9 +19,16 @@ export class UserNotificationService {
|
|||
private userNotificationSocket: UserNotificationSocket
|
||||
) {}
|
||||
|
||||
listMyNotifications (pagination: ComponentPaginationLight, unread?: boolean, ignoreLoadingBar = false) {
|
||||
listMyNotifications (parameters: {
|
||||
pagination: ComponentPaginationLight
|
||||
ignoreLoadingBar?: boolean
|
||||
unread?: boolean,
|
||||
sort?: SortMeta
|
||||
}) {
|
||||
const { pagination, ignoreLoadingBar, unread, sort } = parameters
|
||||
|
||||
let params = new HttpParams()
|
||||
params = this.restService.addRestGetParams(params, this.restService.componentPaginationToRestPagination(pagination))
|
||||
params = this.restService.addRestGetParams(params, this.restService.componentPaginationToRestPagination(pagination), sort)
|
||||
|
||||
if (unread) params = params.append('unread', `${unread}`)
|
||||
|
||||
|
@ -35,7 +43,7 @@ export class UserNotificationService {
|
|||
}
|
||||
|
||||
countUnreadNotifications () {
|
||||
return this.listMyNotifications({ currentPage: 1, itemsPerPage: 0 }, true)
|
||||
return this.listMyNotifications({ pagination: { currentPage: 1, itemsPerPage: 0 }, ignoreLoadingBar: true, unread: true })
|
||||
.pipe(map(n => n.total))
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ export class UserNotificationsComponent implements OnInit {
|
|||
@Output() notificationsLoaded = new EventEmitter()
|
||||
|
||||
notifications: UserNotification[] = []
|
||||
sortField = 'createdAt'
|
||||
|
||||
// So we can access it in the template
|
||||
UserNotificationType = UserNotificationType
|
||||
|
@ -39,18 +40,25 @@ export class UserNotificationsComponent implements OnInit {
|
|||
totalItems: null
|
||||
}
|
||||
|
||||
this.loadMoreNotifications()
|
||||
this.loadNotifications()
|
||||
|
||||
if (this.markAllAsReadSubject) {
|
||||
this.markAllAsReadSubject.subscribe(() => this.markAllAsRead())
|
||||
}
|
||||
}
|
||||
|
||||
loadMoreNotifications () {
|
||||
this.userNotificationService.listMyNotifications(this.componentPagination, undefined, this.ignoreLoadingBar)
|
||||
loadNotifications (reset?: boolean) {
|
||||
this.userNotificationService.listMyNotifications({
|
||||
pagination: this.componentPagination,
|
||||
ignoreLoadingBar: this.ignoreLoadingBar,
|
||||
sort: {
|
||||
field: this.sortField,
|
||||
order: this.sortField === 'createdAt' ? -1 : 1
|
||||
}
|
||||
})
|
||||
.subscribe(
|
||||
result => {
|
||||
this.notifications = this.notifications.concat(result.data)
|
||||
this.notifications = reset ? result.data : this.notifications.concat(result.data)
|
||||
this.componentPagination.totalItems = result.total
|
||||
|
||||
this.notificationsLoaded.emit()
|
||||
|
@ -68,7 +76,7 @@ export class UserNotificationsComponent implements OnInit {
|
|||
this.componentPagination.currentPage++
|
||||
|
||||
if (hasMoreItems(this.componentPagination)) {
|
||||
this.loadMoreNotifications()
|
||||
this.loadNotifications()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -97,4 +105,14 @@ export class UserNotificationsComponent implements OnInit {
|
|||
err => this.notifier.error(err.message)
|
||||
)
|
||||
}
|
||||
|
||||
changeSortColumn (column: string) {
|
||||
this.componentPagination = {
|
||||
currentPage: 1,
|
||||
itemsPerPage: this.itemsPerPage,
|
||||
totalItems: null
|
||||
}
|
||||
this.sortField = column
|
||||
this.loadNotifications(true)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -356,6 +356,17 @@
|
|||
color: #000;
|
||||
}
|
||||
}
|
||||
|
||||
&.peertube-select-button {
|
||||
@include grey-button;
|
||||
|
||||
select,
|
||||
option {
|
||||
font-weight: $font-semibold;
|
||||
color: pvar(--greyForegroundColor);
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Thanks: https://codepen.io/triss90/pen/XNEdRe/
|
||||
|
@ -454,6 +465,49 @@
|
|||
}
|
||||
}
|
||||
|
||||
@mixin table-badge {
|
||||
border-radius: 2px;
|
||||
padding: 1/4em 1/2em;
|
||||
text-transform: uppercase;
|
||||
font-weight: $font-bold;
|
||||
font-size: 12px;
|
||||
letter-spacing: 1/3px;
|
||||
|
||||
&.badge-banned,
|
||||
&.badge-red {
|
||||
background-color: #ffcdd2;
|
||||
color: #c63737;
|
||||
}
|
||||
|
||||
&.badge-banned {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
&.badge-yellow {
|
||||
background-color: #feedaf;
|
||||
color: #8a5340;
|
||||
}
|
||||
|
||||
&.badge-brown {
|
||||
background-color: #ffd8b2;
|
||||
color: #805b36;
|
||||
}
|
||||
|
||||
&.badge-green {
|
||||
background-color: #c8e6c9;
|
||||
color: #256029;
|
||||
}
|
||||
|
||||
&.badge-blue {
|
||||
background-color: #b3e5fc;
|
||||
color: #23547b;
|
||||
}
|
||||
|
||||
&.badge-purple {
|
||||
background-color: #eccfff;
|
||||
color: #694382;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin avatar ($size) {
|
||||
object-fit: cover;
|
||||
|
@ -638,6 +692,7 @@
|
|||
overflow: hidden;
|
||||
font-size: 0.75rem;
|
||||
border-radius: 0.25rem;
|
||||
color: gray;
|
||||
|
||||
.progress-bar {
|
||||
color: pvar(--mainBackgroundColor);
|
||||
|
@ -648,11 +703,25 @@
|
|||
text-align: center;
|
||||
white-space: nowrap;
|
||||
transition: width 0.6s ease;
|
||||
isolation: isolate;
|
||||
|
||||
&:after {
|
||||
content: attr(valuenow-formatted);
|
||||
position: absolute;
|
||||
margin-left: .2rem;
|
||||
mix-blend-mode: screen;
|
||||
color: gray;
|
||||
}
|
||||
|
||||
&.secondary {
|
||||
background-color: pvar(--secondaryColor);
|
||||
}
|
||||
}
|
||||
|
||||
.progress-bar + span {
|
||||
position: relative;
|
||||
top: -1px;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin breadcrumb {
|
||||
|
|
|
@ -92,6 +92,11 @@ p-table {
|
|||
&:last-child td {
|
||||
border-bottom: none !important;
|
||||
}
|
||||
|
||||
&:focus + tr > td,
|
||||
&:focus > td {
|
||||
box-shadow: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.expander {
|
||||
|
|
|
@ -55,7 +55,7 @@ const WEBSERVER = {
|
|||
|
||||
// Sortable columns per schema
|
||||
const SORTABLE_COLUMNS = {
|
||||
USERS: [ 'id', 'username', 'videoQuotaUsed', 'createdAt' ],
|
||||
USERS: [ 'id', 'username', 'videoQuotaUsed', 'createdAt', 'lastLoginDate', 'role' ],
|
||||
USER_SUBSCRIPTIONS: [ 'id', 'createdAt' ],
|
||||
ACCOUNTS: [ 'createdAt' ],
|
||||
JOBS: [ 'createdAt' ],
|
||||
|
@ -78,7 +78,7 @@ const SORTABLE_COLUMNS = {
|
|||
ACCOUNTS_BLOCKLIST: [ 'createdAt' ],
|
||||
SERVERS_BLOCKLIST: [ 'createdAt' ],
|
||||
|
||||
USER_NOTIFICATIONS: [ 'createdAt' ],
|
||||
USER_NOTIFICATIONS: [ 'createdAt', 'read' ],
|
||||
|
||||
VIDEO_PLAYLISTS: [ 'displayName', 'createdAt', 'updatedAt' ],
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue