1 import { SortMeta } from 'primeng/api'
2 import { from, Observable } from 'rxjs'
3 import { catchError, concatMap, map, toArray } from 'rxjs/operators'
4 import { HttpClient, HttpParams } from '@angular/common/http'
5 import { Injectable } from '@angular/core'
6 import { ComponentPaginationLight, RestExtractor, RestPagination, RestService } from '@app/core'
7 import { objectLineFeedToHtml } from '@app/helpers'
12 VideoComment as VideoCommentServerModel,
15 VideoCommentThreadTree as VideoCommentThreadTreeServerModel
16 } from '@shared/models'
17 import { environment } from '../../../environments/environment'
18 import { VideoCommentThreadTree } from './video-comment-thread-tree.model'
19 import { VideoComment } from './video-comment.model'
22 export class VideoCommentService {
23 static BASE_FEEDS_URL = environment.apiUrl + '/feeds/video-comments.'
25 private static BASE_VIDEO_URL = environment.apiUrl + '/api/v1/videos/'
28 private authHttp: HttpClient,
29 private restExtractor: RestExtractor,
30 private restService: RestService
33 addCommentThread (videoId: number | string, comment: VideoCommentCreate) {
34 const url = VideoCommentService.BASE_VIDEO_URL + videoId + '/comment-threads'
35 const normalizedComment = objectLineFeedToHtml(comment, 'text')
37 return this.authHttp.post<{ comment: VideoCommentServerModel }>(url, normalizedComment)
39 map(data => this.extractVideoComment(data.comment)),
40 catchError(err => this.restExtractor.handleError(err))
44 addCommentReply (videoId: number | string, inReplyToCommentId: number, comment: VideoCommentCreate) {
45 const url = VideoCommentService.BASE_VIDEO_URL + videoId + '/comments/' + inReplyToCommentId
46 const normalizedComment = objectLineFeedToHtml(comment, 'text')
48 return this.authHttp.post<{ comment: VideoCommentServerModel }>(url, normalizedComment)
50 map(data => this.extractVideoComment(data.comment)),
51 catchError(err => this.restExtractor.handleError(err))
55 getAdminVideoComments (options: {
56 pagination: RestPagination,
59 }): Observable<ResultList<VideoCommentAdmin>> {
60 const { pagination, sort, search } = options
61 const url = VideoCommentService.BASE_VIDEO_URL + 'comments'
63 let params = new HttpParams()
64 params = this.restService.addRestGetParams(params, pagination, sort)
67 params = this.buildParamsFromSearch(search, params)
70 return this.authHttp.get<ResultList<VideoCommentAdmin>>(url, { params })
72 catchError(res => this.restExtractor.handleError(res))
76 getVideoCommentThreads (parameters: {
77 videoId: number | string,
78 componentPagination: ComponentPaginationLight,
80 }): Observable<ThreadsResultList<VideoComment>> {
81 const { videoId, componentPagination, sort } = parameters
83 const pagination = this.restService.componentPaginationToRestPagination(componentPagination)
85 let params = new HttpParams()
86 params = this.restService.addRestGetParams(params, pagination, sort)
88 const url = VideoCommentService.BASE_VIDEO_URL + videoId + '/comment-threads'
89 return this.authHttp.get<ThreadsResultList<VideoComment>>(url, { params })
91 map(result => this.extractVideoComments(result)),
92 catchError(err => this.restExtractor.handleError(err))
96 getVideoThreadComments (parameters: {
97 videoId: number | string,
99 }): Observable<VideoCommentThreadTree> {
100 const { videoId, threadId } = parameters
101 const url = `${VideoCommentService.BASE_VIDEO_URL + videoId}/comment-threads/${threadId}`
104 .get<VideoCommentThreadTreeServerModel>(url)
106 map(tree => this.extractVideoCommentTree(tree)),
107 catchError(err => this.restExtractor.handleError(err))
111 deleteVideoComment (videoId: number | string, commentId: number) {
112 const url = `${VideoCommentService.BASE_VIDEO_URL + videoId}/comments/${commentId}`
117 map(this.restExtractor.extractDataBool),
118 catchError(err => this.restExtractor.handleError(err))
122 deleteVideoComments (comments: { videoId: number | string, commentId: number }[]) {
123 return from(comments)
125 concatMap(c => this.deleteVideoComment(c.videoId, c.commentId)),
130 getVideoCommentsFeeds (videoUUID?: string) {
133 format: FeedFormat.RSS,
135 url: VideoCommentService.BASE_FEEDS_URL + FeedFormat.RSS.toLowerCase()
138 format: FeedFormat.ATOM,
140 url: VideoCommentService.BASE_FEEDS_URL + FeedFormat.ATOM.toLowerCase()
143 format: FeedFormat.JSON,
145 url: VideoCommentService.BASE_FEEDS_URL + FeedFormat.JSON.toLowerCase()
149 if (videoUUID !== undefined) {
150 for (const feed of feeds) {
151 feed.url += '?videoId=' + videoUUID
158 private extractVideoComment (videoComment: VideoCommentServerModel) {
159 return new VideoComment(videoComment)
162 private extractVideoComments (result: ThreadsResultList<VideoCommentServerModel>) {
163 const videoCommentsJson = result.data
164 const totalComments = result.total
165 const comments: VideoComment[] = []
167 for (const videoCommentJson of videoCommentsJson) {
168 comments.push(new VideoComment(videoCommentJson))
171 return { data: comments, total: totalComments, totalNotDeletedComments: result.totalNotDeletedComments }
174 private extractVideoCommentTree (serverTree: VideoCommentThreadTreeServerModel): VideoCommentThreadTree {
175 if (!serverTree) return null
178 comment: new VideoComment(serverTree.comment),
179 children: serverTree.children.map(c => this.extractVideoCommentTree(c))
182 const hasDisplayedChildren = tree.children.length === 0
183 ? !tree.comment.isDeleted
184 : tree.children.some(c => c.hasDisplayedChildren)
186 return Object.assign(tree, { hasDisplayedChildren })
189 private buildParamsFromSearch (search: string, params: HttpParams) {
190 const filters = this.restService.parseQueryStringFilter(search, {
196 searchAccount: { prefix: 'account:' },
197 searchVideo: { prefix: 'video:' }
200 return this.restService.addObjectParams(params, filters)