]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - client/src/app/videos/+video-watch/comment/video-comments.component.ts
Simplify client syndications
[github/Chocobozzz/PeerTube.git] / client / src / app / videos / +video-watch / comment / video-comments.component.ts
CommitLineData
1263fc4e
C
1import { Component, Input, OnChanges, OnDestroy, OnInit, SimpleChanges } from '@angular/core'
2import { ActivatedRoute } from '@angular/router'
4cb6d457 3import { ConfirmService } from '@app/core'
4635f59d 4import { NotificationsService } from 'angular2-notifications'
1263fc4e
C
5import { Subscription } from 'rxjs/Subscription'
6import { VideoCommentThreadTree } from '../../../../../../shared/models/videos/video-comment.model'
4635f59d
C
7import { AuthService } from '../../../core/auth'
8import { ComponentPagination } from '../../../shared/rest/component-pagination.model'
9import { User } from '../../../shared/users'
10import { SortField } from '../../../shared/video/sort-field.type'
47564bbe 11import { VideoDetails } from '../../../shared/video/video-details.model'
4635f59d
C
12import { VideoComment } from './video-comment.model'
13import { VideoCommentService } from './video-comment.service'
14
15@Component({
16 selector: 'my-video-comments',
17 templateUrl: './video-comments.component.html',
18 styleUrls: ['./video-comments.component.scss']
19})
1263fc4e 20export class VideoCommentsComponent implements OnInit, OnChanges, OnDestroy {
47564bbe 21 @Input() video: VideoDetails
4635f59d
C
22 @Input() user: User
23
24 comments: VideoComment[] = []
5b8072ee 25 highlightedThread: VideoComment
4635f59d
C
26 sort: SortField = '-createdAt'
27 componentPagination: ComponentPagination = {
28 currentPage: 1,
7416fbf3 29 itemsPerPage: 10,
4635f59d
C
30 totalItems: null
31 }
32 inReplyToCommentId: number
33 threadComments: { [ id: number ]: VideoCommentThreadTree } = {}
34 threadLoading: { [ id: number ]: boolean } = {}
1263fc4e
C
35
36 private sub: Subscription
4635f59d
C
37
38 constructor (
39 private authService: AuthService,
40 private notificationsService: NotificationsService,
4cb6d457 41 private confirmService: ConfirmService,
d5b53822 42 private videoCommentService: VideoCommentService,
43 private activatedRoute: ActivatedRoute
4635f59d
C
44 ) {}
45
1263fc4e
C
46 ngOnInit () {
47 // Find highlighted comment in params
48 this.sub = this.activatedRoute.params.subscribe(
49 params => {
5b8072ee
C
50 if (params['threadId']) {
51 const highlightedThreadId = +params['threadId']
52 this.processHighlightedThread(highlightedThreadId)
1263fc4e
C
53 }
54 }
55 )
56 }
57
339632b4
C
58 ngOnChanges (changes: SimpleChanges) {
59 if (changes['video']) {
1263fc4e 60 this.resetVideo()
47564bbe 61 }
4635f59d
C
62 }
63
1263fc4e
C
64 ngOnDestroy () {
65 if (this.sub) this.sub.unsubscribe()
66 }
67
5b8072ee 68 viewReplies (commentId: number, highlightThread = false) {
1263fc4e 69 this.threadLoading[commentId] = true
4635f59d 70
1263fc4e 71 this.videoCommentService.getVideoThreadComments(this.video.id, commentId)
4635f59d
C
72 .subscribe(
73 res => {
1263fc4e
C
74 this.threadComments[commentId] = res
75 this.threadLoading[commentId] = false
76
5b8072ee 77 if (highlightThread) this.highlightedThread = new VideoComment(res.comment)
4635f59d
C
78 },
79
80 err => this.notificationsService.error('Error', err.message)
81 )
82 }
83
7416fbf3
C
84 loadMoreComments () {
85 this.videoCommentService.getVideoCommentThreads(this.video.id, this.componentPagination, this.sort)
86 .subscribe(
87 res => {
88 this.comments = this.comments.concat(res.comments)
89 this.componentPagination.totalItems = res.totalComments
90 },
91
92 err => this.notificationsService.error('Error', err.message)
93 )
94 }
95
4635f59d
C
96 onCommentThreadCreated (comment: VideoComment) {
97 this.comments.unshift(comment)
98 }
99
100 onWantedToReply (comment: VideoComment) {
101 this.inReplyToCommentId = comment.id
102 }
103
104 onResetReply () {
105 this.inReplyToCommentId = undefined
106 }
107
4cb6d457 108 onThreadCreated (commentTree: VideoCommentThreadTree) {
1263fc4e 109 this.viewReplies(commentTree.comment.id)
4cb6d457
C
110 }
111
1f30a185 112 async onWantedToDelete (commentToDelete: VideoComment) {
4cb6d457
C
113 let message = 'Do you really want to delete this comment?'
114 if (commentToDelete.totalReplies !== 0) message += `${commentToDelete.totalReplies} would be deleted too.`
115
1f30a185
C
116 const res = await this.confirmService.confirm(message, 'Delete')
117 if (res === false) return
118
119 this.videoCommentService.deleteVideoComment(commentToDelete.videoId, commentToDelete.id)
120 .subscribe(
121 () => {
122 // Delete the comment in the tree
123 if (commentToDelete.inReplyToCommentId) {
124 const thread = this.threadComments[commentToDelete.threadId]
125 if (!thread) {
126 console.error(`Cannot find thread ${commentToDelete.threadId} of the comment to delete ${commentToDelete.id}`)
127 return
128 }
129
130 this.deleteLocalCommentThread(thread, commentToDelete)
131 return
132 }
133
134 // Delete the thread
135 this.comments = this.comments.filter(c => c.id !== commentToDelete.id)
136 this.componentPagination.totalItems--
137 },
138
139 err => this.notificationsService.error('Error', err.message)
140 )
4cb6d457
C
141 }
142
4635f59d
C
143 isUserLoggedIn () {
144 return this.authService.isLoggedIn()
145 }
7416fbf3
C
146
147 onNearOfBottom () {
148 this.componentPagination.currentPage++
149
150 if (this.hasMoreComments()) {
151 this.loadMoreComments()
152 }
153 }
154
4cb6d457 155 private hasMoreComments () {
7416fbf3
C
156 // No results
157 if (this.componentPagination.totalItems === 0) return false
158
159 // Not loaded yet
160 if (!this.componentPagination.totalItems) return true
161
162 const maxPage = this.componentPagination.totalItems / this.componentPagination.itemsPerPage
163 return maxPage > this.componentPagination.currentPage
164 }
4cb6d457
C
165
166 private deleteLocalCommentThread (parentComment: VideoCommentThreadTree, commentToDelete: VideoComment) {
167 for (const commentChild of parentComment.children) {
168 if (commentChild.comment.id === commentToDelete.id) {
169 parentComment.children = parentComment.children.filter(c => c.comment.id !== commentToDelete.id)
170 return
171 }
172
173 this.deleteLocalCommentThread(commentChild, commentToDelete)
174 }
175 }
339632b4 176
1263fc4e 177 private resetVideo () {
339632b4
C
178 if (this.video.commentsEnabled === true) {
179 // Reset all our fields
5b8072ee 180 this.highlightedThread = null
339632b4
C
181 this.comments = []
182 this.threadComments = {}
183 this.threadLoading = {}
184 this.inReplyToCommentId = undefined
0cd4344f
C
185 this.componentPagination.currentPage = 1
186 this.componentPagination.totalItems = null
339632b4
C
187
188 this.loadMoreComments()
189 }
190 }
1263fc4e 191
5b8072ee
C
192 private processHighlightedThread (highlightedThreadId: number) {
193 this.highlightedThread = this.comments.find(c => c.id === highlightedThreadId)
1263fc4e 194
5b8072ee
C
195 const highlightThread = true
196 this.viewReplies(highlightedThreadId, highlightThread)
1263fc4e 197 }
4635f59d 198}