Add notifications in the client
[oweals/peertube.git] / client / src / app / shared / users / user-notification.service.ts
1 import { Injectable } from '@angular/core'
2 import { HttpClient, HttpParams } from '@angular/common/http'
3 import { RestExtractor, RestService } from '@app/shared/rest'
4 import { catchError, map, tap } from 'rxjs/operators'
5 import { environment } from '../../../environments/environment'
6 import { ResultList, UserNotification as UserNotificationServer, UserNotificationSetting } from '../../../../../shared'
7 import { UserNotification } from '@app/shared/users/user-notification.model'
8 import { Subject } from 'rxjs'
9 import * as io from 'socket.io-client'
10 import { AuthService } from '@app/core'
11 import { ComponentPagination } from '@app/shared/rest/component-pagination.model'
12 import { User } from '@app/shared'
13
14 @Injectable()
15 export class UserNotificationService {
16   static BASE_NOTIFICATIONS_URL = environment.apiUrl + '/api/v1/users/me/notifications'
17   static BASE_NOTIFICATION_SETTINGS = environment.apiUrl + '/api/v1/users/me/notification-settings'
18
19   private notificationSubject = new Subject<{ type: 'new' | 'read' | 'read-all', notification?: UserNotification }>()
20
21   private socket: SocketIOClient.Socket
22
23   constructor (
24     private auth: AuthService,
25     private authHttp: HttpClient,
26     private restExtractor: RestExtractor,
27     private restService: RestService
28   ) {}
29
30   listMyNotifications (pagination: ComponentPagination, unread?: boolean, ignoreLoadingBar = false) {
31     let params = new HttpParams()
32     params = this.restService.addRestGetParams(params, this.restService.componentPaginationToRestPagination(pagination))
33
34     if (unread) params = params.append('unread', `${unread}`)
35
36     const headers = ignoreLoadingBar ? { ignoreLoadingBar: '' } : undefined
37
38     return this.authHttp.get<ResultList<UserNotification>>(UserNotificationService.BASE_NOTIFICATIONS_URL, { params, headers })
39                .pipe(
40                  map(res => this.restExtractor.convertResultListDateToHuman(res)),
41                  map(res => this.restExtractor.applyToResultListData(res, this.formatNotification.bind(this))),
42                  catchError(err => this.restExtractor.handleError(err))
43                )
44   }
45
46   countUnreadNotifications () {
47     return this.listMyNotifications({ currentPage: 1, itemsPerPage: 0 }, true)
48       .pipe(map(n => n.total))
49   }
50
51   getMyNotificationsSocket () {
52     const socket = this.getSocket()
53
54     socket.on('new-notification', (n: UserNotificationServer) => {
55       this.notificationSubject.next({ type: 'new', notification: new UserNotification(n) })
56     })
57
58     return this.notificationSubject.asObservable()
59   }
60
61   markAsRead (notification: UserNotification) {
62     const url = UserNotificationService.BASE_NOTIFICATIONS_URL + '/read'
63
64     const body = { ids: [ notification.id ] }
65     const headers = { ignoreLoadingBar: '' }
66
67     return this.authHttp.post(url, body, { headers })
68                .pipe(
69                  map(this.restExtractor.extractDataBool),
70                  tap(() => this.notificationSubject.next({ type: 'read' })),
71                  catchError(res => this.restExtractor.handleError(res))
72                )
73   }
74
75   markAllAsRead () {
76     const url = UserNotificationService.BASE_NOTIFICATIONS_URL + '/read-all'
77     const headers = { ignoreLoadingBar: '' }
78
79     return this.authHttp.post(url, {}, { headers })
80                .pipe(
81                  map(this.restExtractor.extractDataBool),
82                  tap(() => this.notificationSubject.next({ type: 'read-all' })),
83                  catchError(res => this.restExtractor.handleError(res))
84                )
85   }
86
87   updateNotificationSettings (user: User, settings: UserNotificationSetting) {
88     const url = UserNotificationService.BASE_NOTIFICATION_SETTINGS
89
90     return this.authHttp.put(url, settings)
91                .pipe(
92                  map(this.restExtractor.extractDataBool),
93                  catchError(res => this.restExtractor.handleError(res))
94                )
95   }
96
97   private getSocket () {
98     if (this.socket) return this.socket
99
100     this.socket = io(environment.apiUrl + '/user-notifications', {
101       query: { accessToken: this.auth.getAccessToken() }
102     })
103
104     return this.socket
105   }
106
107   private formatNotification (notification: UserNotificationServer) {
108     return new UserNotification(notification)
109   }
110 }