aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src/app/shared/users
diff options
context:
space:
mode:
Diffstat (limited to 'client/src/app/shared/users')
-rw-r--r--client/src/app/shared/users/user-notification.model.ts37
-rw-r--r--client/src/app/shared/users/user-notification.service.ts2
-rw-r--r--client/src/app/shared/users/user-notifications.component.html82
-rw-r--r--client/src/app/shared/users/user-notifications.component.scss46
-rw-r--r--client/src/app/shared/users/user-notifications.component.ts14
5 files changed, 119 insertions, 62 deletions
diff --git a/client/src/app/shared/users/user-notification.model.ts b/client/src/app/shared/users/user-notification.model.ts
index 5ff816fb8..c5996a8a1 100644
--- a/client/src/app/shared/users/user-notification.model.ts
+++ b/client/src/app/shared/users/user-notification.model.ts
@@ -1,4 +1,5 @@
1import { UserNotification as UserNotificationServer, UserNotificationType, VideoInfo } from '../../../../../shared' 1import { UserNotification as UserNotificationServer, UserNotificationType, VideoInfo, ActorInfo } from '../../../../../shared'
2import { Actor } from '@app/shared/actor/actor.model'
2 3
3export class UserNotification implements UserNotificationServer { 4export class UserNotification implements UserNotificationServer {
4 id: number 5 id: number
@@ -6,10 +7,7 @@ export class UserNotification implements UserNotificationServer {
6 read: boolean 7 read: boolean
7 8
8 video?: VideoInfo & { 9 video?: VideoInfo & {
9 channel: { 10 channel: ActorInfo & { avatarUrl?: string }
10 id: number
11 displayName: string
12 }
13 } 11 }
14 12
15 videoImport?: { 13 videoImport?: {
@@ -23,10 +21,7 @@ export class UserNotification implements UserNotificationServer {
23 comment?: { 21 comment?: {
24 id: number 22 id: number
25 threadId: number 23 threadId: number
26 account: { 24 account: ActorInfo & { avatarUrl?: string }
27 id: number
28 displayName: string
29 }
30 video: VideoInfo 25 video: VideoInfo
31 } 26 }
32 27
@@ -40,18 +35,11 @@ export class UserNotification implements UserNotificationServer {
40 video: VideoInfo 35 video: VideoInfo
41 } 36 }
42 37
43 account?: { 38 account?: ActorInfo & { avatarUrl?: string }
44 id: number
45 displayName: string
46 name: string
47 }
48 39
49 actorFollow?: { 40 actorFollow?: {
50 id: number 41 id: number
51 follower: { 42 follower: ActorInfo & { avatarUrl?: string }
52 name: string
53 displayName: string
54 }
55 following: { 43 following: {
56 type: 'account' | 'channel' 44 type: 'account' | 'channel'
57 name: string 45 name: string
@@ -76,12 +64,22 @@ export class UserNotification implements UserNotificationServer {
76 this.read = hash.read 64 this.read = hash.read
77 65
78 this.video = hash.video 66 this.video = hash.video
67 if (this.video) this.setAvatarUrl(this.video.channel)
68
79 this.videoImport = hash.videoImport 69 this.videoImport = hash.videoImport
70
80 this.comment = hash.comment 71 this.comment = hash.comment
72 if (this.comment) this.setAvatarUrl(this.comment.account)
73
81 this.videoAbuse = hash.videoAbuse 74 this.videoAbuse = hash.videoAbuse
75
82 this.videoBlacklist = hash.videoBlacklist 76 this.videoBlacklist = hash.videoBlacklist
77
83 this.account = hash.account 78 this.account = hash.account
79 if (this.account) this.setAvatarUrl(this.account)
80
84 this.actorFollow = hash.actorFollow 81 this.actorFollow = hash.actorFollow
82 if (this.actorFollow) this.setAvatarUrl(this.actorFollow.follower)
85 83
86 this.createdAt = hash.createdAt 84 this.createdAt = hash.createdAt
87 this.updatedAt = hash.updatedAt 85 this.updatedAt = hash.updatedAt
@@ -150,4 +148,7 @@ export class UserNotification implements UserNotificationServer {
150 return videoImport.targetUrl || videoImport.magnetUri || videoImport.torrentName 148 return videoImport.targetUrl || videoImport.magnetUri || videoImport.torrentName
151 } 149 }
152 150
151 private setAvatarUrl (actor: { avatarUrl?: string, avatar?: { path: string } }) {
152 actor.avatarUrl = Actor.GET_ACTOR_AVATAR_URL(actor)
153 }
153} 154}
diff --git a/client/src/app/shared/users/user-notification.service.ts b/client/src/app/shared/users/user-notification.service.ts
index 67ed8f74e..f8a30955d 100644
--- a/client/src/app/shared/users/user-notification.service.ts
+++ b/client/src/app/shared/users/user-notification.service.ts
@@ -15,8 +15,6 @@ export class UserNotificationService {
15 static BASE_NOTIFICATIONS_URL = environment.apiUrl + '/api/v1/users/me/notifications' 15 static BASE_NOTIFICATIONS_URL = environment.apiUrl + '/api/v1/users/me/notifications'
16 static BASE_NOTIFICATION_SETTINGS = environment.apiUrl + '/api/v1/users/me/notification-settings' 16 static BASE_NOTIFICATION_SETTINGS = environment.apiUrl + '/api/v1/users/me/notification-settings'
17 17
18 private socket: SocketIOClient.Socket
19
20 constructor ( 18 constructor (
21 private auth: AuthService, 19 private auth: AuthService,
22 private authHttp: HttpClient, 20 private authHttp: HttpClient,
diff --git a/client/src/app/shared/users/user-notifications.component.html b/client/src/app/shared/users/user-notifications.component.html
index 86379d941..caa518e7f 100644
--- a/client/src/app/shared/users/user-notifications.component.html
+++ b/client/src/app/shared/users/user-notifications.component.html
@@ -1,61 +1,101 @@
1<div *ngIf="componentPagination.totalItems === 0" class="no-notification" i18n>You don't have notifications.</div> 1<div *ngIf="componentPagination.totalItems === 0" class="no-notification" i18n>You don't have notifications.</div>
2 2
3<div class="notifications" myInfiniteScroller [autoInit]="true" (nearOfBottom)="onNearOfBottom()"> 3<div class="notifications" myInfiniteScroller [autoInit]="true" (nearOfBottom)="onNearOfBottom()">
4 <div *ngFor="let notification of notifications" class="notification" [ngClass]="{ unread: !notification.read }"> 4 <div *ngFor="let notification of notifications" class="notification" [ngClass]="{ unread: !notification.read }" (click)="markAsRead(notification)">
5 5
6 <div [ngSwitch]="notification.type"> 6 <ng-container [ngSwitch]="notification.type">
7 <ng-container i18n *ngSwitchCase="UserNotificationType.NEW_VIDEO_FROM_SUBSCRIPTION"> 7 <ng-container i18n *ngSwitchCase="UserNotificationType.NEW_VIDEO_FROM_SUBSCRIPTION">
8 {{ notification.video.channel.displayName }} published a <a (click)="markAsRead(notification)" [routerLink]="notification.videoUrl">new video</a> 8 <img alt="" aria-labelledby="avatar" class="avatar" [src]="notification.video.channel.avatarUrl" />
9
10 <div class="message">
11 {{ notification.video.channel.displayName }} published a <a (click)="markAsRead(notification)" [routerLink]="notification.videoUrl">new video</a>
12 </div>
9 </ng-container> 13 </ng-container>
10 14
11 <ng-container i18n *ngSwitchCase="UserNotificationType.UNBLACKLIST_ON_MY_VIDEO"> 15 <ng-container i18n *ngSwitchCase="UserNotificationType.UNBLACKLIST_ON_MY_VIDEO">
12 Your video <a (click)="markAsRead(notification)" [routerLink]="notification.videoUrl">{{ notification.video.name }}</a> has been unblacklisted 16 <my-global-icon iconName="undo"></my-global-icon>
17
18 <div class="message">
19 Your video <a (click)="markAsRead(notification)" [routerLink]="notification.videoUrl">{{ notification.video.name }}</a> has been unblacklisted
20 </div>
13 </ng-container> 21 </ng-container>
14 22
15 <ng-container i18n *ngSwitchCase="UserNotificationType.BLACKLIST_ON_MY_VIDEO"> 23 <ng-container i18n *ngSwitchCase="UserNotificationType.BLACKLIST_ON_MY_VIDEO">
16 Your video <a (click)="markAsRead(notification)" [routerLink]="notification.videoUrl">{{ notification.videoBlacklist.video.name }}</a> has been blacklisted 24 <my-global-icon iconName="no"></my-global-icon>
25
26 <div class="message">
27 Your video <a (click)="markAsRead(notification)" [routerLink]="notification.videoUrl">{{ notification.videoBlacklist.video.name }}</a> has been blacklisted
28 </div>
17 </ng-container> 29 </ng-container>
18 30
19 <ng-container i18n *ngSwitchCase="UserNotificationType.NEW_VIDEO_ABUSE_FOR_MODERATORS"> 31 <ng-container i18n *ngSwitchCase="UserNotificationType.NEW_VIDEO_ABUSE_FOR_MODERATORS">
20 <a (click)="markAsRead(notification)" [routerLink]="notification.videoAbuseUrl">A new video abuse</a> has been created on video <a (click)="markAsRead(notification)" [routerLink]="notification.videoUrl">{{ notification.videoAbuse.video.name }}</a> 32 <my-global-icon iconName="alert"></my-global-icon>
33
34 <div class="message">
35 <a (click)="markAsRead(notification)" [routerLink]="notification.videoAbuseUrl">A new video abuse</a> has been created on video <a (click)="markAsRead(notification)" [routerLink]="notification.videoUrl">{{ notification.videoAbuse.video.name }}</a>
36 </div>
21 </ng-container> 37 </ng-container>
22 38
23 <ng-container i18n *ngSwitchCase="UserNotificationType.NEW_COMMENT_ON_MY_VIDEO"> 39 <ng-container i18n *ngSwitchCase="UserNotificationType.NEW_COMMENT_ON_MY_VIDEO">
24 {{ notification.comment.account.displayName }} commented your video <a (click)="markAsRead(notification)" [routerLink]="notification.commentUrl">{{ notification.comment.video.name }}</a> 40 <img alt="" aria-labelledby="avatar" class="avatar" [src]="notification.comment.account.avatarUrl" />
41
42 <div class="message">
43 {{ notification.comment.account.displayName }} commented your video <a (click)="markAsRead(notification)" [routerLink]="notification.commentUrl">{{ notification.comment.video.name }}</a>
44 </div>
25 </ng-container> 45 </ng-container>
26 46
27 <ng-container i18n *ngSwitchCase="UserNotificationType.MY_VIDEO_PUBLISHED"> 47 <ng-container i18n *ngSwitchCase="UserNotificationType.MY_VIDEO_PUBLISHED">
28 Your video <a (click)="markAsRead(notification)" [routerLink]="notification.videoUrl">{{ notification.video.name }}</a> has been published 48 <my-global-icon iconName="sparkle"></my-global-icon>
49
50 <div class="message">
51 Your video <a (click)="markAsRead(notification)" [routerLink]="notification.videoUrl">{{ notification.video.name }}</a> has been published
52 </div>
29 </ng-container> 53 </ng-container>
30 54
31 <ng-container i18n *ngSwitchCase="UserNotificationType.MY_VIDEO_IMPORT_SUCCESS"> 55 <ng-container i18n *ngSwitchCase="UserNotificationType.MY_VIDEO_IMPORT_SUCCESS">
32 <a (click)="markAsRead(notification)" [routerLink]="notification.videoUrl">Your video import</a> {{ notification.videoImportIdentifier }} succeeded 56 <my-global-icon iconName="cloud-download"></my-global-icon>
57
58 <div class="message">
59 <a (click)="markAsRead(notification)" [routerLink]="notification.videoUrl">Your video import</a> {{ notification.videoImportIdentifier }} succeeded
60 </div>
33 </ng-container> 61 </ng-container>
34 62
35 <ng-container i18n *ngSwitchCase="UserNotificationType.MY_VIDEO_IMPORT_ERROR"> 63 <ng-container i18n *ngSwitchCase="UserNotificationType.MY_VIDEO_IMPORT_ERROR">
36 <a (click)="markAsRead(notification)" [routerLink]="notification.videoImportUrl">Your video import</a> {{ notification.videoImportIdentifier }} failed 64 <my-global-icon iconName="cloud-error"></my-global-icon>
65
66 <div class="message">
67 <a (click)="markAsRead(notification)" [routerLink]="notification.videoImportUrl">Your video import</a> {{ notification.videoImportIdentifier }} failed
68 </div>
37 </ng-container> 69 </ng-container>
38 70
39 <ng-container i18n *ngSwitchCase="UserNotificationType.NEW_USER_REGISTRATION"> 71 <ng-container i18n *ngSwitchCase="UserNotificationType.NEW_USER_REGISTRATION">
40 User <a (click)="markAsRead(notification)" [routerLink]="notification.accountUrl">{{ notification.account.name }} registered</a> on your instance 72 <my-global-icon iconName="user-add"></my-global-icon>
73
74 <div class="message">
75 User <a (click)="markAsRead(notification)" [routerLink]="notification.accountUrl">{{ notification.account.name }} registered</a> on your instance
76 </div>
41 </ng-container> 77 </ng-container>
42 78
43 <ng-container i18n *ngSwitchCase="UserNotificationType.NEW_FOLLOW"> 79 <ng-container i18n *ngSwitchCase="UserNotificationType.NEW_FOLLOW">
44 <a (click)="markAsRead(notification)" [routerLink]="notification.accountUrl">{{ notification.actorFollow.follower.displayName }}</a> is following 80 <img alt="" aria-labelledby="avatar" class="avatar" [src]="notification.actorFollow.follower.avatarUrl" />
45 81
46 <ng-container *ngIf="notification.actorFollow.following.type === 'channel'"> 82 <div class="message">
47 your channel {{ notification.actorFollow.following.displayName }} 83 <a (click)="markAsRead(notification)" [routerLink]="notification.accountUrl">{{ notification.actorFollow.follower.displayName }}</a> is following
48 </ng-container> 84
49 <ng-container *ngIf="notification.actorFollow.following.type === 'account'">your account</ng-container> 85 <ng-container *ngIf="notification.actorFollow.following.type === 'channel'">your channel {{ notification.actorFollow.following.displayName }}</ng-container>
86 <ng-container *ngIf="notification.actorFollow.following.type === 'account'">your account</ng-container>
87 </div>
50 </ng-container> 88 </ng-container>
51 89
52 <ng-container i18n *ngSwitchCase="UserNotificationType.COMMENT_MENTION"> 90 <ng-container i18n *ngSwitchCase="UserNotificationType.COMMENT_MENTION">
53 {{ notification.comment.account.displayName }} mentioned you on <a (click)="markAsRead(notification)" [routerLink]="notification.commentUrl">video {{ notification.comment.video.name }}</a> 91 <img alt="" aria-labelledby="avatar" class="avatar" [src]="notification.comment.account.avatarUrl" />
92
93 <div class="message">
94 {{ notification.comment.account.displayName }} mentioned you on <a (click)="markAsRead(notification)" [routerLink]="notification.commentUrl">video {{ notification.comment.video.name }}</a>
95 </div>
54 </ng-container> 96 </ng-container>
55 </div> 97 </ng-container>
56 98
57 <div i18n title="Mark as read" class="mark-as-read"> 99 <div class="from-date">{{ notification.createdAt | myFromNow }}</div>
58 <div class="glyphicon glyphicon-ok" (click)="markAsRead(notification)"></div>
59 </div>
60 </div> 100 </div>
61</div> 101</div>
diff --git a/client/src/app/shared/users/user-notifications.component.scss b/client/src/app/shared/users/user-notifications.component.scss
index 0ae26ea39..315d504c9 100644
--- a/client/src/app/shared/users/user-notifications.component.scss
+++ b/client/src/app/shared/users/user-notifications.component.scss
@@ -1,3 +1,6 @@
1@import '_variables';
2@import '_mixins';
3
1.no-notification { 4.no-notification {
2 display: flex; 5 display: flex;
3 justify-content: center; 6 justify-content: center;
@@ -7,31 +10,42 @@
7 10
8.notification { 11.notification {
9 display: flex; 12 display: flex;
10 justify-content: space-between;
11 align-items: center; 13 align-items: center;
12 font-size: inherit; 14 font-size: inherit;
13 padding: 15px 10px; 15 padding: 15px 5px 15px 10px;
14 border-bottom: 1px solid rgba(0, 0, 0, 0.10); 16 border-bottom: 1px solid rgba(0, 0, 0, 0.10);
15 17
16 .mark-as-read { 18 &.unread {
17 min-width: 35px; 19 background-color: rgba(0, 0, 0, 0.05);
20 }
21
22 my-global-icon {
23 width: 24px;
24 margin-right: 11px;
25 margin-left: 3px;
18 26
19 .glyphicon { 27 @include apply-svg-color(#333);
20 display: none;
21 cursor: pointer;
22 color: rgba(20, 20, 20, 0.5)
23 }
24 } 28 }
25 29
26 &.unread { 30 .avatar {
27 background-color: rgba(0, 0, 0, 0.05); 31 @include avatar(30px);
32
33 margin-right: 10px;
34 }
28 35
29 &:hover .mark-as-read .glyphicon { 36 .message {
30 display: block; 37 flex-grow: 1;
31 38
32 &:hover { 39 a {
33 color: rgba(20, 20, 20, 0.8); 40 font-weight: $font-semibold;
34 }
35 } 41 }
36 } 42 }
43
44 .from-date {
45 font-size: 0.85em;
46 color: $grey-foreground-color;
47 padding-left: 5px;
48 min-width: 70px;
49 text-align: right;
50 }
37} 51}
diff --git a/client/src/app/shared/users/user-notifications.component.ts b/client/src/app/shared/users/user-notifications.component.ts
index e3913ba56..b5f9fd399 100644
--- a/client/src/app/shared/users/user-notifications.component.ts
+++ b/client/src/app/shared/users/user-notifications.component.ts
@@ -20,11 +20,7 @@ export class UserNotificationsComponent implements OnInit {
20 // So we can access it in the template 20 // So we can access it in the template
21 UserNotificationType = UserNotificationType 21 UserNotificationType = UserNotificationType
22 22
23 componentPagination: ComponentPagination = { 23 componentPagination: ComponentPagination
24 currentPage: 1,
25 itemsPerPage: this.itemsPerPage,
26 totalItems: null
27 }
28 24
29 constructor ( 25 constructor (
30 private userNotificationService: UserNotificationService, 26 private userNotificationService: UserNotificationService,
@@ -32,6 +28,12 @@ export class UserNotificationsComponent implements OnInit {
32 ) { } 28 ) { }
33 29
34 ngOnInit () { 30 ngOnInit () {
31 this.componentPagination = {
32 currentPage: 1,
33 itemsPerPage: this.itemsPerPage, // Reset items per page, because of the @Input() variable
34 totalItems: null
35 }
36
35 this.loadMoreNotifications() 37 this.loadMoreNotifications()
36 } 38 }
37 39
@@ -58,6 +60,8 @@ export class UserNotificationsComponent implements OnInit {
58 } 60 }
59 61
60 markAsRead (notification: UserNotification) { 62 markAsRead (notification: UserNotification) {
63 if (notification.read) return
64
61 this.userNotificationService.markAsRead(notification) 65 this.userNotificationService.markAsRead(notification)
62 .subscribe( 66 .subscribe(
63 () => { 67 () => {