aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--client/src/app/+my-account/my-account-notifications/my-account-notifications.component.html10
-rw-r--r--client/src/app/+my-account/my-account-notifications/my-account-notifications.component.scss6
-rw-r--r--client/src/app/+my-account/my-account-notifications/my-account-notifications.component.ts4
-rw-r--r--client/src/app/shared/shared-main/users/user-notification.service.ts14
-rw-r--r--client/src/app/shared/shared-main/users/user-notifications.component.ts28
-rw-r--r--client/src/sass/include/_mixins.scss69
-rw-r--r--client/src/sass/primeng-custom.scss5
-rw-r--r--server/initializers/constants.ts4
8 files changed, 128 insertions, 12 deletions
diff --git a/client/src/app/+my-account/my-account-notifications/my-account-notifications.component.html b/client/src/app/+my-account/my-account-notifications/my-account-notifications.component.html
index 8e4480ca6..0727f90e8 100644
--- a/client/src/app/+my-account/my-account-notifications/my-account-notifications.component.html
+++ b/client/src/app/+my-account/my-account-notifications/my-account-notifications.component.html
@@ -5,7 +5,15 @@
5 Notification preferences 5 Notification preferences
6 </a> 6 </a>
7 7
8 <button class="btn" [disabled]="!hasUnreadNotifications()" (click)="markAllAsRead()"> 8 <div class="peertube-select-container peertube-select-button ml-2">
9 <select [(ngModel)]="notificationSortType" (ngModelChange)="onNotificationSortTypeChanged()" class="form-control">
10 <option value="undefined" disabled>Sort by</option>
11 <option value="created" i18n>Newest first</option>
12 <option value="unread-created" i18n>Unread first</option>
13 </select>
14 </div>
15
16 <button class="btn ml-auto" [disabled]="!hasUnreadNotifications()" (click)="markAllAsRead()">
9 <ng-container *ngIf="hasUnreadNotifications()"> 17 <ng-container *ngIf="hasUnreadNotifications()">
10 <my-global-icon iconName="inbox-full" aria-hidden="true"></my-global-icon> 18 <my-global-icon iconName="inbox-full" aria-hidden="true"></my-global-icon>
11 19
diff --git a/client/src/app/+my-account/my-account-notifications/my-account-notifications.component.scss b/client/src/app/+my-account/my-account-notifications/my-account-notifications.component.scss
index 73f7c7b24..d586eeb0d 100644
--- a/client/src/app/+my-account/my-account-notifications/my-account-notifications.component.scss
+++ b/client/src/app/+my-account/my-account-notifications/my-account-notifications.component.scss
@@ -3,7 +3,6 @@
3 3
4.header { 4.header {
5 display: flex; 5 display: flex;
6 justify-content: space-between;
7 font-size: 15px; 6 font-size: 15px;
8 margin-bottom: 20px; 7 margin-bottom: 20px;
9 8
@@ -18,8 +17,13 @@
18 @include grey-button; 17 @include grey-button;
19 @include button-with-icon(20px, 3px, -1px); 18 @include button-with-icon(20px, 3px, -1px);
20 } 19 }
20
21 .peertube-select-container {
22 @include peertube-select-container(auto);
23 }
21} 24}
22 25
26
23my-user-notifications { 27my-user-notifications {
24 font-size: 15px; 28 font-size: 15px;
25} 29}
diff --git a/client/src/app/+my-account/my-account-notifications/my-account-notifications.component.ts b/client/src/app/+my-account/my-account-notifications/my-account-notifications.component.ts
index 0c1427d96..03b91e050 100644
--- a/client/src/app/+my-account/my-account-notifications/my-account-notifications.component.ts
+++ b/client/src/app/+my-account/my-account-notifications/my-account-notifications.component.ts
@@ -8,6 +8,8 @@ import { UserNotificationsComponent } from '@app/shared/shared-main'
8export class MyAccountNotificationsComponent { 8export class MyAccountNotificationsComponent {
9 @ViewChild('userNotification', { static: true }) userNotification: UserNotificationsComponent 9 @ViewChild('userNotification', { static: true }) userNotification: UserNotificationsComponent
10 10
11 notificationSortType = 'created'
12
11 markAllAsRead () { 13 markAllAsRead () {
12 this.userNotification.markAllAsRead() 14 this.userNotification.markAllAsRead()
13 } 15 }
@@ -15,4 +17,6 @@ export class MyAccountNotificationsComponent {
15 hasUnreadNotifications () { 17 hasUnreadNotifications () {
16 return this.userNotification.notifications.filter(n => n.read === false).length !== 0 18 return this.userNotification.notifications.filter(n => n.read === false).length !== 0
17 } 19 }
20
21 onNotificationSortTypeChanged () {}
18} 22}
diff --git a/client/src/app/shared/shared-main/users/user-notification.service.ts b/client/src/app/shared/shared-main/users/user-notification.service.ts
index 8dd9472fe..ecc66ecdb 100644
--- a/client/src/app/shared/shared-main/users/user-notification.service.ts
+++ b/client/src/app/shared/shared-main/users/user-notification.service.ts
@@ -5,6 +5,7 @@ import { ComponentPaginationLight, RestExtractor, RestService, User, UserNotific
5import { ResultList, UserNotification as UserNotificationServer, UserNotificationSetting } from '@shared/models' 5import { ResultList, UserNotification as UserNotificationServer, UserNotificationSetting } from '@shared/models'
6import { environment } from '../../../../environments/environment' 6import { environment } from '../../../../environments/environment'
7import { UserNotification } from './user-notification.model' 7import { UserNotification } from './user-notification.model'
8import { SortMeta } from 'primeng/api'
8 9
9@Injectable() 10@Injectable()
10export class UserNotificationService { 11export class UserNotificationService {
@@ -18,9 +19,16 @@ export class UserNotificationService {
18 private userNotificationSocket: UserNotificationSocket 19 private userNotificationSocket: UserNotificationSocket
19 ) {} 20 ) {}
20 21
21 listMyNotifications (pagination: ComponentPaginationLight, unread?: boolean, ignoreLoadingBar = false) { 22 listMyNotifications (parameters: {
23 pagination: ComponentPaginationLight
24 ignoreLoadingBar?: boolean
25 unread?: boolean,
26 sort?: SortMeta
27 }) {
28 const { pagination, ignoreLoadingBar, unread, sort } = parameters
29
22 let params = new HttpParams() 30 let params = new HttpParams()
23 params = this.restService.addRestGetParams(params, this.restService.componentPaginationToRestPagination(pagination)) 31 params = this.restService.addRestGetParams(params, this.restService.componentPaginationToRestPagination(pagination), sort)
24 32
25 if (unread) params = params.append('unread', `${unread}`) 33 if (unread) params = params.append('unread', `${unread}`)
26 34
@@ -35,7 +43,7 @@ export class UserNotificationService {
35 } 43 }
36 44
37 countUnreadNotifications () { 45 countUnreadNotifications () {
38 return this.listMyNotifications({ currentPage: 1, itemsPerPage: 0 }, true) 46 return this.listMyNotifications({ pagination: { currentPage: 1, itemsPerPage: 0 }, ignoreLoadingBar: true, unread: true })
39 .pipe(map(n => n.total)) 47 .pipe(map(n => n.total))
40 } 48 }
41 49
diff --git a/client/src/app/shared/shared-main/users/user-notifications.component.ts b/client/src/app/shared/shared-main/users/user-notifications.component.ts
index 6abd8b7d8..48be80e3f 100644
--- a/client/src/app/shared/shared-main/users/user-notifications.component.ts
+++ b/client/src/app/shared/shared-main/users/user-notifications.component.ts
@@ -19,6 +19,7 @@ export class UserNotificationsComponent implements OnInit {
19 @Output() notificationsLoaded = new EventEmitter() 19 @Output() notificationsLoaded = new EventEmitter()
20 20
21 notifications: UserNotification[] = [] 21 notifications: UserNotification[] = []
22 sortField = 'createdAt'
22 23
23 // So we can access it in the template 24 // So we can access it in the template
24 UserNotificationType = UserNotificationType 25 UserNotificationType = UserNotificationType
@@ -39,18 +40,25 @@ export class UserNotificationsComponent implements OnInit {
39 totalItems: null 40 totalItems: null
40 } 41 }
41 42
42 this.loadMoreNotifications() 43 this.loadNotifications()
43 44
44 if (this.markAllAsReadSubject) { 45 if (this.markAllAsReadSubject) {
45 this.markAllAsReadSubject.subscribe(() => this.markAllAsRead()) 46 this.markAllAsReadSubject.subscribe(() => this.markAllAsRead())
46 } 47 }
47 } 48 }
48 49
49 loadMoreNotifications () { 50 loadNotifications (reset?: boolean) {
50 this.userNotificationService.listMyNotifications(this.componentPagination, undefined, this.ignoreLoadingBar) 51 this.userNotificationService.listMyNotifications({
52 pagination: this.componentPagination,
53 ignoreLoadingBar: this.ignoreLoadingBar,
54 sort: {
55 field: this.sortField,
56 order: this.sortField === 'createdAt' ? -1 : 1
57 }
58 })
51 .subscribe( 59 .subscribe(
52 result => { 60 result => {
53 this.notifications = this.notifications.concat(result.data) 61 this.notifications = reset ? result.data : this.notifications.concat(result.data)
54 this.componentPagination.totalItems = result.total 62 this.componentPagination.totalItems = result.total
55 63
56 this.notificationsLoaded.emit() 64 this.notificationsLoaded.emit()
@@ -68,7 +76,7 @@ export class UserNotificationsComponent implements OnInit {
68 this.componentPagination.currentPage++ 76 this.componentPagination.currentPage++
69 77
70 if (hasMoreItems(this.componentPagination)) { 78 if (hasMoreItems(this.componentPagination)) {
71 this.loadMoreNotifications() 79 this.loadNotifications()
72 } 80 }
73 } 81 }
74 82
@@ -97,4 +105,14 @@ export class UserNotificationsComponent implements OnInit {
97 err => this.notifier.error(err.message) 105 err => this.notifier.error(err.message)
98 ) 106 )
99 } 107 }
108
109 changeSortColumn (column: string) {
110 this.componentPagination = {
111 currentPage: 1,
112 itemsPerPage: this.itemsPerPage,
113 totalItems: null
114 }
115 this.sortField = column
116 this.loadNotifications(true)
117 }
100} 118}
diff --git a/client/src/sass/include/_mixins.scss b/client/src/sass/include/_mixins.scss
index 3471d694c..2de5ce7f1 100644
--- a/client/src/sass/include/_mixins.scss
+++ b/client/src/sass/include/_mixins.scss
@@ -356,6 +356,17 @@
356 color: #000; 356 color: #000;
357 } 357 }
358 } 358 }
359
360 &.peertube-select-button {
361 @include grey-button;
362
363 select,
364 option {
365 font-weight: $font-semibold;
366 color: pvar(--greyForegroundColor);
367 border: none;
368 }
369 }
359} 370}
360 371
361// Thanks: https://codepen.io/triss90/pen/XNEdRe/ 372// Thanks: https://codepen.io/triss90/pen/XNEdRe/
@@ -454,6 +465,49 @@
454 } 465 }
455} 466}
456 467
468@mixin table-badge {
469 border-radius: 2px;
470 padding: 1/4em 1/2em;
471 text-transform: uppercase;
472 font-weight: $font-bold;
473 font-size: 12px;
474 letter-spacing: 1/3px;
475
476 &.badge-banned,
477 &.badge-red {
478 background-color: #ffcdd2;
479 color: #c63737;
480 }
481
482 &.badge-banned {
483 text-decoration: line-through;
484 }
485
486 &.badge-yellow {
487 background-color: #feedaf;
488 color: #8a5340;
489 }
490
491 &.badge-brown {
492 background-color: #ffd8b2;
493 color: #805b36;
494 }
495
496 &.badge-green {
497 background-color: #c8e6c9;
498 color: #256029;
499 }
500
501 &.badge-blue {
502 background-color: #b3e5fc;
503 color: #23547b;
504 }
505
506 &.badge-purple {
507 background-color: #eccfff;
508 color: #694382;
509 }
510}
457 511
458@mixin avatar ($size) { 512@mixin avatar ($size) {
459 object-fit: cover; 513 object-fit: cover;
@@ -638,6 +692,7 @@
638 overflow: hidden; 692 overflow: hidden;
639 font-size: 0.75rem; 693 font-size: 0.75rem;
640 border-radius: 0.25rem; 694 border-radius: 0.25rem;
695 color: gray;
641 696
642 .progress-bar { 697 .progress-bar {
643 color: pvar(--mainBackgroundColor); 698 color: pvar(--mainBackgroundColor);
@@ -648,11 +703,25 @@
648 text-align: center; 703 text-align: center;
649 white-space: nowrap; 704 white-space: nowrap;
650 transition: width 0.6s ease; 705 transition: width 0.6s ease;
706 isolation: isolate;
707
708 &:after {
709 content: attr(valuenow-formatted);
710 position: absolute;
711 margin-left: .2rem;
712 mix-blend-mode: screen;
713 color: gray;
714 }
651 715
652 &.secondary { 716 &.secondary {
653 background-color: pvar(--secondaryColor); 717 background-color: pvar(--secondaryColor);
654 } 718 }
655 } 719 }
720
721 .progress-bar + span {
722 position: relative;
723 top: -1px;
724 }
656} 725}
657 726
658@mixin breadcrumb { 727@mixin breadcrumb {
diff --git a/client/src/sass/primeng-custom.scss b/client/src/sass/primeng-custom.scss
index 2388c0469..bf49639f5 100644
--- a/client/src/sass/primeng-custom.scss
+++ b/client/src/sass/primeng-custom.scss
@@ -92,6 +92,11 @@ p-table {
92 &:last-child td { 92 &:last-child td {
93 border-bottom: none !important; 93 border-bottom: none !important;
94 } 94 }
95
96 &:focus + tr > td,
97 &:focus > td {
98 box-shadow: none !important;
99 }
95 } 100 }
96 101
97 .expander { 102 .expander {
diff --git a/server/initializers/constants.ts b/server/initializers/constants.ts
index 2e9d3956e..fd5bf5868 100644
--- a/server/initializers/constants.ts
+++ b/server/initializers/constants.ts
@@ -55,7 +55,7 @@ const WEBSERVER = {
55 55
56// Sortable columns per schema 56// Sortable columns per schema
57const SORTABLE_COLUMNS = { 57const SORTABLE_COLUMNS = {
58 USERS: [ 'id', 'username', 'videoQuotaUsed', 'createdAt' ], 58 USERS: [ 'id', 'username', 'videoQuotaUsed', 'createdAt', 'lastLoginDate', 'role' ],
59 USER_SUBSCRIPTIONS: [ 'id', 'createdAt' ], 59 USER_SUBSCRIPTIONS: [ 'id', 'createdAt' ],
60 ACCOUNTS: [ 'createdAt' ], 60 ACCOUNTS: [ 'createdAt' ],
61 JOBS: [ 'createdAt' ], 61 JOBS: [ 'createdAt' ],
@@ -78,7 +78,7 @@ const SORTABLE_COLUMNS = {
78 ACCOUNTS_BLOCKLIST: [ 'createdAt' ], 78 ACCOUNTS_BLOCKLIST: [ 'createdAt' ],
79 SERVERS_BLOCKLIST: [ 'createdAt' ], 79 SERVERS_BLOCKLIST: [ 'createdAt' ],
80 80
81 USER_NOTIFICATIONS: [ 'createdAt' ], 81 USER_NOTIFICATIONS: [ 'createdAt', 'read' ],
82 82
83 VIDEO_PLAYLISTS: [ 'displayName', 'createdAt', 'updatedAt' ], 83 VIDEO_PLAYLISTS: [ 'displayName', 'createdAt', 'updatedAt' ],
84 84