aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2021-02-19 09:50:13 +0100
committerChocobozzz <me@florianbigard.com>2021-02-19 10:06:52 +0100
commit9d6b9d10ef8cbef39e89bc709285abffb0d8caa1 (patch)
tree3425b22556e00d1b15de15c72b2802cfc9374473
parentfae6e4da8f516a9d6c3bad9bf6f35811ccacbad8 (diff)
downloadPeerTube-9d6b9d10ef8cbef39e89bc709285abffb0d8caa1.tar.gz
PeerTube-9d6b9d10ef8cbef39e89bc709285abffb0d8caa1.tar.zst
PeerTube-9d6b9d10ef8cbef39e89bc709285abffb0d8caa1.zip
Fix video comments display with deleted comments
-rw-r--r--client/src/app/+videos/+video-watch/comment/video-comment.component.html2
-rw-r--r--client/src/app/+videos/+video-watch/comment/video-comment.component.ts9
-rw-r--r--client/src/app/+videos/+video-watch/comment/video-comments.component.html8
-rw-r--r--client/src/app/+videos/+video-watch/comment/video-comments.component.ts10
-rw-r--r--client/src/app/shared/shared-video-comment/video-comment-thread-tree.model.ts1
-rw-r--r--client/src/app/shared/shared-video-comment/video-comment.service.ts25
-rw-r--r--server/controllers/api/videos/comment.ts13
-rw-r--r--server/models/utils.ts2
-rw-r--r--server/models/video/video-comment.ts60
-rw-r--r--server/tests/api/videos/video-comments.ts48
-rw-r--r--shared/models/result-list.model.ts4
11 files changed, 120 insertions, 62 deletions
diff --git a/client/src/app/+videos/+video-watch/comment/video-comment.component.html b/client/src/app/+videos/+video-watch/comment/video-comment.component.html
index 8847753a5..ba41b6f48 100644
--- a/client/src/app/+videos/+video-watch/comment/video-comment.component.html
+++ b/client/src/app/+videos/+video-watch/comment/video-comment.component.html
@@ -1,4 +1,4 @@
1<div *ngIf="isNotDeletedOrDeletedWithReplies()" class="root-comment"> 1<div *ngIf="isCommentDisplayed()" class="root-comment">
2 <div class="left"> 2 <div class="left">
3 <a *ngIf="!comment.isDeleted" [href]="comment.account.url" target="_blank" rel="noopener noreferrer"> 3 <a *ngIf="!comment.isDeleted" [href]="comment.account.url" target="_blank" rel="noopener noreferrer">
4 <img 4 <img
diff --git a/client/src/app/+videos/+video-watch/comment/video-comment.component.ts b/client/src/app/+videos/+video-watch/comment/video-comment.component.ts
index 0958b25c0..5c5d72b22 100644
--- a/client/src/app/+videos/+video-watch/comment/video-comment.component.ts
+++ b/client/src/app/+videos/+video-watch/comment/video-comment.component.ts
@@ -62,6 +62,7 @@ export class VideoCommentComponent implements OnInit, OnChanges {
62 if (!this.commentTree) { 62 if (!this.commentTree) {
63 this.commentTree = { 63 this.commentTree = {
64 comment: this.comment, 64 comment: this.comment,
65 hasDisplayedChildren: false,
65 children: [] 66 children: []
66 } 67 }
67 68
@@ -70,6 +71,7 @@ export class VideoCommentComponent implements OnInit, OnChanges {
70 71
71 this.commentTree.children.unshift({ 72 this.commentTree.children.unshift({
72 comment: createdComment, 73 comment: createdComment,
74 hasDisplayedChildren: false,
73 children: [] 75 children: []
74 }) 76 })
75 77
@@ -133,8 +135,11 @@ export class VideoCommentComponent implements OnInit, OnChanges {
133 ($event.target as HTMLImageElement).src = Account.GET_DEFAULT_AVATAR_URL() 135 ($event.target as HTMLImageElement).src = Account.GET_DEFAULT_AVATAR_URL()
134 } 136 }
135 137
136 isNotDeletedOrDeletedWithReplies () { 138 isCommentDisplayed () {
137 return !this.comment.isDeleted || this.comment.isDeleted && this.comment.totalReplies !== 0 139 // Not deleted
140 return !this.comment.isDeleted ||
141 this.comment.totalReplies !== 0 || // Or root comment thread has replies
142 (this.commentTree?.hasDisplayedChildren) // Or this is a reply that have other replies
138 } 143 }
139 144
140 private getUserIfNeeded (account: Account) { 145 private getUserIfNeeded (account: Account) {
diff --git a/client/src/app/+videos/+video-watch/comment/video-comments.component.html b/client/src/app/+videos/+video-watch/comment/video-comments.component.html
index f9ebfcc1f..4a6426d30 100644
--- a/client/src/app/+videos/+video-watch/comment/video-comments.component.html
+++ b/client/src/app/+videos/+video-watch/comment/video-comments.component.html
@@ -1,10 +1,10 @@
1<div> 1<div>
2 <div class="title-block"> 2 <div class="title-block">
3 <h2 class="title-page title-page-single"> 3 <h2 class="title-page title-page-single">
4 <ng-container *ngIf="componentPagination.totalItems > 0; then hasComments; else noComments"></ng-container> 4 <ng-container *ngIf="totalNotDeletedComments > 0; then hasComments; else noComments"></ng-container>
5 <ng-template #hasComments> 5 <ng-template #hasComments>
6 <ng-container i18n *ngIf="componentPagination.totalItems === 1; else manyComments">1 Comment</ng-container> 6 <ng-container i18n *ngIf="totalNotDeletedComments === 1; else manyComments">1 Comment</ng-container>
7 <ng-template i18n #manyComments>{{ componentPagination.totalItems }} Comments</ng-template> 7 <ng-template i18n #manyComments>{{ totalNotDeletedComments }} Comments</ng-template>
8 </ng-template> 8 </ng-template>
9 <ng-template i18n #noComments>Comments</ng-template> 9 <ng-template i18n #noComments>Comments</ng-template>
10 </h2> 10 </h2>
@@ -30,7 +30,7 @@
30 [textValue]="commentThreadRedraftValue" 30 [textValue]="commentThreadRedraftValue"
31 ></my-video-comment-add> 31 ></my-video-comment-add>
32 32
33 <div *ngIf="componentPagination.totalItems === 0 && comments.length === 0" i18n>No comments.</div> 33 <div *ngIf="totalNotDeletedComments === 0 && comments.length === 0" i18n>No comments.</div>
34 34
35 <div 35 <div
36 class="comment-threads" 36 class="comment-threads"
diff --git a/client/src/app/+videos/+video-watch/comment/video-comments.component.ts b/client/src/app/+videos/+video-watch/comment/video-comments.component.ts
index f83a73ccd..d36dd9e34 100644
--- a/client/src/app/+videos/+video-watch/comment/video-comments.component.ts
+++ b/client/src/app/+videos/+video-watch/comment/video-comments.component.ts
@@ -21,15 +21,20 @@ export class VideoCommentsComponent implements OnInit, OnChanges, OnDestroy {
21 21
22 comments: VideoComment[] = [] 22 comments: VideoComment[] = []
23 highlightedThread: VideoComment 23 highlightedThread: VideoComment
24
24 sort = '-createdAt' 25 sort = '-createdAt'
26
25 componentPagination: ComponentPagination = { 27 componentPagination: ComponentPagination = {
26 currentPage: 1, 28 currentPage: 1,
27 itemsPerPage: 10, 29 itemsPerPage: 10,
28 totalItems: null 30 totalItems: null
29 } 31 }
32 totalNotDeletedComments: number
33
30 inReplyToCommentId: number 34 inReplyToCommentId: number
31 commentReplyRedraftValue: string 35 commentReplyRedraftValue: string
32 commentThreadRedraftValue: string 36 commentThreadRedraftValue: string
37
33 threadComments: { [ id: number ]: VideoCommentThreadTree } = {} 38 threadComments: { [ id: number ]: VideoCommentThreadTree } = {}
34 threadLoading: { [ id: number ]: boolean } = {} 39 threadLoading: { [ id: number ]: boolean } = {}
35 40
@@ -122,8 +127,8 @@ export class VideoCommentsComponent implements OnInit, OnChanges, OnDestroy {
122 obs.subscribe( 127 obs.subscribe(
123 res => { 128 res => {
124 this.comments = this.comments.concat(res.data) 129 this.comments = this.comments.concat(res.data)
125 // Client does not display removed comments 130 this.componentPagination.totalItems = res.total
126 this.componentPagination.totalItems = res.total - this.comments.filter(c => c.isDeleted).length 131 this.totalNotDeletedComments = res.totalNotDeletedComments
127 132
128 this.onDataSubject.next(res.data) 133 this.onDataSubject.next(res.data)
129 this.hooks.runAction('action:video-watch.video-threads.loaded', 'video-watch', { data: this.componentPagination }) 134 this.hooks.runAction('action:video-watch.video-threads.loaded', 'video-watch', { data: this.componentPagination })
@@ -241,6 +246,7 @@ export class VideoCommentsComponent implements OnInit, OnChanges, OnDestroy {
241 this.inReplyToCommentId = undefined 246 this.inReplyToCommentId = undefined
242 this.componentPagination.currentPage = 1 247 this.componentPagination.currentPage = 1
243 this.componentPagination.totalItems = null 248 this.componentPagination.totalItems = null
249 this.totalNotDeletedComments = null
244 250
245 this.syndicationItems = this.videoCommentService.getVideoCommentsFeeds(this.video.uuid) 251 this.syndicationItems = this.videoCommentService.getVideoCommentsFeeds(this.video.uuid)
246 this.loadMoreThreads() 252 this.loadMoreThreads()
diff --git a/client/src/app/shared/shared-video-comment/video-comment-thread-tree.model.ts b/client/src/app/shared/shared-video-comment/video-comment-thread-tree.model.ts
index 7c2aaeadd..9956c88a6 100644
--- a/client/src/app/shared/shared-video-comment/video-comment-thread-tree.model.ts
+++ b/client/src/app/shared/shared-video-comment/video-comment-thread-tree.model.ts
@@ -3,5 +3,6 @@ import { VideoComment } from './video-comment.model'
3 3
4export class VideoCommentThreadTree implements VideoCommentThreadTreeServerModel { 4export class VideoCommentThreadTree implements VideoCommentThreadTreeServerModel {
5 comment: VideoComment 5 comment: VideoComment
6 hasDisplayedChildren: boolean
6 children: VideoCommentThreadTree[] 7 children: VideoCommentThreadTree[]
7} 8}
diff --git a/client/src/app/shared/shared-video-comment/video-comment.service.ts b/client/src/app/shared/shared-video-comment/video-comment.service.ts
index c107a33ab..0f09778df 100644
--- a/client/src/app/shared/shared-video-comment/video-comment.service.ts
+++ b/client/src/app/shared/shared-video-comment/video-comment.service.ts
@@ -8,6 +8,7 @@ import { objectLineFeedToHtml } from '@app/helpers'
8import { 8import {
9 FeedFormat, 9 FeedFormat,
10 ResultList, 10 ResultList,
11 ThreadsResultList,
11 VideoComment as VideoCommentServerModel, 12 VideoComment as VideoCommentServerModel,
12 VideoCommentAdmin, 13 VideoCommentAdmin,
13 VideoCommentCreate, 14 VideoCommentCreate,
@@ -76,7 +77,7 @@ export class VideoCommentService {
76 videoId: number | string, 77 videoId: number | string,
77 componentPagination: ComponentPaginationLight, 78 componentPagination: ComponentPaginationLight,
78 sort: string 79 sort: string
79 }): Observable<ResultList<VideoComment>> { 80 }): Observable<ThreadsResultList<VideoComment>> {
80 const { videoId, componentPagination, sort } = parameters 81 const { videoId, componentPagination, sort } = parameters
81 82
82 const pagination = this.restService.componentPaginationToRestPagination(componentPagination) 83 const pagination = this.restService.componentPaginationToRestPagination(componentPagination)
@@ -85,7 +86,7 @@ export class VideoCommentService {
85 params = this.restService.addRestGetParams(params, pagination, sort) 86 params = this.restService.addRestGetParams(params, pagination, sort)
86 87
87 const url = VideoCommentService.BASE_VIDEO_URL + videoId + '/comment-threads' 88 const url = VideoCommentService.BASE_VIDEO_URL + videoId + '/comment-threads'
88 return this.authHttp.get<ResultList<VideoComment>>(url, { params }) 89 return this.authHttp.get<ThreadsResultList<VideoComment>>(url, { params })
89 .pipe( 90 .pipe(
90 map(result => this.extractVideoComments(result)), 91 map(result => this.extractVideoComments(result)),
91 catchError(err => this.restExtractor.handleError(err)) 92 catchError(err => this.restExtractor.handleError(err))
@@ -158,7 +159,7 @@ export class VideoCommentService {
158 return new VideoComment(videoComment) 159 return new VideoComment(videoComment)
159 } 160 }
160 161
161 private extractVideoComments (result: ResultList<VideoCommentServerModel>) { 162 private extractVideoComments (result: ThreadsResultList<VideoCommentServerModel>) {
162 const videoCommentsJson = result.data 163 const videoCommentsJson = result.data
163 const totalComments = result.total 164 const totalComments = result.total
164 const comments: VideoComment[] = [] 165 const comments: VideoComment[] = []
@@ -167,16 +168,22 @@ export class VideoCommentService {
167 comments.push(new VideoComment(videoCommentJson)) 168 comments.push(new VideoComment(videoCommentJson))
168 } 169 }
169 170
170 return { data: comments, total: totalComments } 171 return { data: comments, total: totalComments, totalNotDeletedComments: result.totalNotDeletedComments }
171 } 172 }
172 173
173 private extractVideoCommentTree (tree: VideoCommentThreadTreeServerModel) { 174 private extractVideoCommentTree (serverTree: VideoCommentThreadTreeServerModel): VideoCommentThreadTree {
174 if (!tree) return tree as VideoCommentThreadTree 175 if (!serverTree) return null
175 176
176 tree.comment = new VideoComment(tree.comment) 177 const tree = {
177 tree.children.forEach(c => this.extractVideoCommentTree(c)) 178 comment: new VideoComment(serverTree.comment),
179 children: serverTree.children.map(c => this.extractVideoCommentTree(c))
180 }
181
182 const hasDisplayedChildren = tree.children.length === 0
183 ? !tree.comment.isDeleted
184 : tree.children.some(c => c.hasDisplayedChildren)
178 185
179 return tree as VideoCommentThreadTree 186 return Object.assign(tree, { hasDisplayedChildren })
180 } 187 }
181 188
182 private buildParamsFromSearch (search: string, params: HttpParams) { 189 private buildParamsFromSearch (search: string, params: HttpParams) {
diff --git a/server/controllers/api/videos/comment.ts b/server/controllers/api/videos/comment.ts
index 752a33596..b21698525 100644
--- a/server/controllers/api/videos/comment.ts
+++ b/server/controllers/api/videos/comment.ts
@@ -1,5 +1,5 @@
1import * as express from 'express' 1import * as express from 'express'
2import { ResultList, UserRight } from '../../../../shared/models' 2import { ResultList, ThreadsResultList, UserRight } from '../../../../shared/models'
3import { VideoCommentCreate } from '../../../../shared/models/videos/video-comment.model' 3import { VideoCommentCreate } from '../../../../shared/models/videos/video-comment.model'
4import { auditLoggerFactory, CommentAuditView, getAuditIdFromRes } from '../../../helpers/audit-logger' 4import { auditLoggerFactory, CommentAuditView, getAuditIdFromRes } from '../../../helpers/audit-logger'
5import { getFormattedObjects } from '../../../helpers/utils' 5import { getFormattedObjects } from '../../../helpers/utils'
@@ -30,6 +30,7 @@ import {
30import { AccountModel } from '../../../models/account/account' 30import { AccountModel } from '../../../models/account/account'
31import { VideoCommentModel } from '../../../models/video/video-comment' 31import { VideoCommentModel } from '../../../models/video/video-comment'
32import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' 32import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
33import { logger } from '@server/helpers/logger'
33 34
34const auditLogger = auditLoggerFactory('comments') 35const auditLogger = auditLoggerFactory('comments')
35const videoCommentRouter = express.Router() 36const videoCommentRouter = express.Router()
@@ -108,7 +109,7 @@ async function listVideoThreads (req: express.Request, res: express.Response) {
108 const video = res.locals.onlyVideo 109 const video = res.locals.onlyVideo
109 const user = res.locals.oauth ? res.locals.oauth.token.User : undefined 110 const user = res.locals.oauth ? res.locals.oauth.token.User : undefined
110 111
111 let resultList: ResultList<VideoCommentModel> 112 let resultList: ThreadsResultList<VideoCommentModel>
112 113
113 if (video.commentsEnabled === true) { 114 if (video.commentsEnabled === true) {
114 const apiOptions = await Hooks.wrapObject({ 115 const apiOptions = await Hooks.wrapObject({
@@ -128,11 +129,15 @@ async function listVideoThreads (req: express.Request, res: express.Response) {
128 } else { 129 } else {
129 resultList = { 130 resultList = {
130 total: 0, 131 total: 0,
132 totalNotDeletedComments: 0,
131 data: [] 133 data: []
132 } 134 }
133 } 135 }
134 136
135 return res.json(getFormattedObjects(resultList.data, resultList.total)) 137 return res.json({
138 ...getFormattedObjects(resultList.data, resultList.total),
139 totalNotDeletedComments: resultList.totalNotDeletedComments
140 })
136} 141}
137 142
138async function listVideoThreadComments (req: express.Request, res: express.Response) { 143async function listVideoThreadComments (req: express.Request, res: express.Response) {
@@ -161,6 +166,8 @@ async function listVideoThreadComments (req: express.Request, res: express.Respo
161 } 166 }
162 } 167 }
163 168
169 logger.info('coucou', { resultList })
170
164 if (resultList.data.length === 0) { 171 if (resultList.data.length === 0) {
165 return res.sendStatus(HttpStatusCode.NOT_FOUND_404) 172 return res.sendStatus(HttpStatusCode.NOT_FOUND_404)
166 } 173 }
diff --git a/server/models/utils.ts b/server/models/utils.ts
index 143c1a23c..5337ae75d 100644
--- a/server/models/utils.ts
+++ b/server/models/utils.ts
@@ -134,7 +134,7 @@ function buildBlockedAccountSQL (blockerIds: number[]) {
134 const blockerIdsString = blockerIds.join(', ') 134 const blockerIdsString = blockerIds.join(', ')
135 135
136 return 'SELECT "targetAccountId" AS "id" FROM "accountBlocklist" WHERE "accountId" IN (' + blockerIdsString + ')' + 136 return 'SELECT "targetAccountId" AS "id" FROM "accountBlocklist" WHERE "accountId" IN (' + blockerIdsString + ')' +
137 ' UNION ALL ' + 137 ' UNION ' +
138 'SELECT "account"."id" AS "id" FROM account INNER JOIN "actor" ON account."actorId" = actor.id ' + 138 'SELECT "account"."id" AS "id" FROM account INNER JOIN "actor" ON account."actorId" = actor.id ' +
139 'INNER JOIN "serverBlocklist" ON "actor"."serverId" = "serverBlocklist"."targetServerId" ' + 139 'INNER JOIN "serverBlocklist" ON "actor"."serverId" = "serverBlocklist"."targetServerId" ' +
140 'WHERE "serverBlocklist"."accountId" IN (' + blockerIdsString + ')' 140 'WHERE "serverBlocklist"."accountId" IN (' + blockerIdsString + ')'
diff --git a/server/models/video/video-comment.ts b/server/models/video/video-comment.ts
index 8d1c38826..cfd1d5b7a 100644
--- a/server/models/video/video-comment.ts
+++ b/server/models/video/video-comment.ts
@@ -414,7 +414,15 @@ export class VideoCommentModel extends Model {
414 414
415 const blockerAccountIds = await VideoCommentModel.buildBlockerAccountIds({ videoId, user, isVideoOwned }) 415 const blockerAccountIds = await VideoCommentModel.buildBlockerAccountIds({ videoId, user, isVideoOwned })
416 416
417 const query = { 417 const accountBlockedWhere = {
418 accountId: {
419 [Op.notIn]: Sequelize.literal(
420 '(' + buildBlockedAccountSQL(blockerAccountIds) + ')'
421 )
422 }
423 }
424
425 const queryList = {
418 offset: start, 426 offset: start,
419 limit: count, 427 limit: count,
420 order: getCommentSort(sort), 428 order: getCommentSort(sort),
@@ -428,13 +436,7 @@ export class VideoCommentModel extends Model {
428 }, 436 },
429 { 437 {
430 [Op.or]: [ 438 [Op.or]: [
431 { 439 accountBlockedWhere,
432 accountId: {
433 [Op.notIn]: Sequelize.literal(
434 '(' + buildBlockedAccountSQL(blockerAccountIds) + ')'
435 )
436 }
437 },
438 { 440 {
439 accountId: null 441 accountId: null
440 } 442 }
@@ -444,19 +446,27 @@ export class VideoCommentModel extends Model {
444 } 446 }
445 } 447 }
446 448
447 const scopes: (string | ScopeOptions)[] = [ 449 const scopesList: (string | ScopeOptions)[] = [
448 ScopeNames.WITH_ACCOUNT_FOR_API, 450 ScopeNames.WITH_ACCOUNT_FOR_API,
449 { 451 {
450 method: [ ScopeNames.ATTRIBUTES_FOR_API, blockerAccountIds ] 452 method: [ ScopeNames.ATTRIBUTES_FOR_API, blockerAccountIds ]
451 } 453 }
452 ] 454 ]
453 455
454 return VideoCommentModel 456 const queryCount = {
455 .scope(scopes) 457 where: {
456 .findAndCountAll(query) 458 videoId,
457 .then(({ rows, count }) => { 459 deletedAt: null,
458 return { total: count, data: rows } 460 ...accountBlockedWhere
459 }) 461 }
462 }
463
464 return Promise.all([
465 VideoCommentModel.scope(scopesList).findAndCountAll(queryList),
466 VideoCommentModel.count(queryCount)
467 ]).then(([ { rows, count }, totalNotDeletedComments ]) => {
468 return { total: count, data: rows, totalNotDeletedComments }
469 })
460 } 470 }
461 471
462 static async listThreadCommentsForApi (parameters: { 472 static async listThreadCommentsForApi (parameters: {
@@ -477,11 +487,18 @@ export class VideoCommentModel extends Model {
477 { id: threadId }, 487 { id: threadId },
478 { originCommentId: threadId } 488 { originCommentId: threadId }
479 ], 489 ],
480 accountId: { 490 [Op.or]: [
481 [Op.notIn]: Sequelize.literal( 491 {
482 '(' + buildBlockedAccountSQL(blockerAccountIds) + ')' 492 accountId: {
483 ) 493 [Op.notIn]: Sequelize.literal(
484 } 494 '(' + buildBlockedAccountSQL(blockerAccountIds) + ')'
495 )
496 }
497 },
498 {
499 accountId: null
500 }
501 ]
485 } 502 }
486 } 503 }
487 504
@@ -492,8 +509,7 @@ export class VideoCommentModel extends Model {
492 } 509 }
493 ] 510 ]
494 511
495 return VideoCommentModel 512 return VideoCommentModel.scope(scopes)
496 .scope(scopes)
497 .findAndCountAll(query) 513 .findAndCountAll(query)
498 .then(({ rows, count }) => { 514 .then(({ rows, count }) => {
499 return { total: count, data: rows } 515 return { total: count, data: rows }
diff --git a/server/tests/api/videos/video-comments.ts b/server/tests/api/videos/video-comments.ts
index 141a80690..615e0ea45 100644
--- a/server/tests/api/videos/video-comments.ts
+++ b/server/tests/api/videos/video-comments.ts
@@ -67,6 +67,7 @@ describe('Test video comments', function () {
67 const res = await getVideoCommentThreads(server.url, videoUUID, 0, 5) 67 const res = await getVideoCommentThreads(server.url, videoUUID, 0, 5)
68 68
69 expect(res.body.total).to.equal(0) 69 expect(res.body.total).to.equal(0)
70 expect(res.body.totalNotDeletedComments).to.equal(0)
70 expect(res.body.data).to.be.an('array') 71 expect(res.body.data).to.be.an('array')
71 expect(res.body.data).to.have.lengthOf(0) 72 expect(res.body.data).to.have.lengthOf(0)
72 }) 73 })
@@ -94,6 +95,7 @@ describe('Test video comments', function () {
94 const res = await getVideoCommentThreads(server.url, videoUUID, 0, 5) 95 const res = await getVideoCommentThreads(server.url, videoUUID, 0, 5)
95 96
96 expect(res.body.total).to.equal(1) 97 expect(res.body.total).to.equal(1)
98 expect(res.body.totalNotDeletedComments).to.equal(1)
97 expect(res.body.data).to.be.an('array') 99 expect(res.body.data).to.be.an('array')
98 expect(res.body.data).to.have.lengthOf(1) 100 expect(res.body.data).to.have.lengthOf(1)
99 101
@@ -172,6 +174,7 @@ describe('Test video comments', function () {
172 const res = await getVideoCommentThreads(server.url, videoUUID, 0, 5, 'createdAt') 174 const res = await getVideoCommentThreads(server.url, videoUUID, 0, 5, 'createdAt')
173 175
174 expect(res.body.total).to.equal(3) 176 expect(res.body.total).to.equal(3)
177 expect(res.body.totalNotDeletedComments).to.equal(6)
175 expect(res.body.data).to.be.an('array') 178 expect(res.body.data).to.be.an('array')
176 expect(res.body.data).to.have.lengthOf(3) 179 expect(res.body.data).to.have.lengthOf(3)
177 180
@@ -186,26 +189,35 @@ describe('Test video comments', function () {
186 it('Should delete a reply', async function () { 189 it('Should delete a reply', async function () {
187 await deleteVideoComment(server.url, server.accessToken, videoId, replyToDeleteId) 190 await deleteVideoComment(server.url, server.accessToken, videoId, replyToDeleteId)
188 191
189 const res = await getVideoThreadComments(server.url, videoUUID, threadId) 192 {
190 193 const res = await getVideoCommentThreads(server.url, videoUUID, 0, 5, 'createdAt')
191 const tree: VideoCommentThreadTree = res.body
192 expect(tree.comment.text).equal('my super first comment')
193 expect(tree.children).to.have.lengthOf(2)
194
195 const firstChild = tree.children[0]
196 expect(firstChild.comment.text).to.equal('my super answer to thread 1')
197 expect(firstChild.children).to.have.lengthOf(1)
198 194
199 const childOfFirstChild = firstChild.children[0] 195 expect(res.body.total).to.equal(3)
200 expect(childOfFirstChild.comment.text).to.equal('my super answer to answer of thread 1') 196 expect(res.body.totalNotDeletedComments).to.equal(5)
201 expect(childOfFirstChild.children).to.have.lengthOf(0) 197 }
202 198
203 const deletedChildOfFirstChild = tree.children[1] 199 {
204 expect(deletedChildOfFirstChild.comment.text).to.equal('') 200 const res = await getVideoThreadComments(server.url, videoUUID, threadId)
205 expect(deletedChildOfFirstChild.comment.isDeleted).to.be.true 201
206 expect(deletedChildOfFirstChild.comment.deletedAt).to.not.be.null 202 const tree: VideoCommentThreadTree = res.body
207 expect(deletedChildOfFirstChild.comment.account).to.be.null 203 expect(tree.comment.text).equal('my super first comment')
208 expect(deletedChildOfFirstChild.children).to.have.lengthOf(0) 204 expect(tree.children).to.have.lengthOf(2)
205
206 const firstChild = tree.children[0]
207 expect(firstChild.comment.text).to.equal('my super answer to thread 1')
208 expect(firstChild.children).to.have.lengthOf(1)
209
210 const childOfFirstChild = firstChild.children[0]
211 expect(childOfFirstChild.comment.text).to.equal('my super answer to answer of thread 1')
212 expect(childOfFirstChild.children).to.have.lengthOf(0)
213
214 const deletedChildOfFirstChild = tree.children[1]
215 expect(deletedChildOfFirstChild.comment.text).to.equal('')
216 expect(deletedChildOfFirstChild.comment.isDeleted).to.be.true
217 expect(deletedChildOfFirstChild.comment.deletedAt).to.not.be.null
218 expect(deletedChildOfFirstChild.comment.account).to.be.null
219 expect(deletedChildOfFirstChild.children).to.have.lengthOf(0)
220 }
209 }) 221 })
210 222
211 it('Should delete a complete thread', async function () { 223 it('Should delete a complete thread', async function () {
diff --git a/shared/models/result-list.model.ts b/shared/models/result-list.model.ts
index 2d5147a86..fcafcfb2f 100644
--- a/shared/models/result-list.model.ts
+++ b/shared/models/result-list.model.ts
@@ -2,3 +2,7 @@ export interface ResultList<T> {
2 total: number 2 total: number
3 data: T[] 3 data: T[]
4} 4}
5
6export interface ThreadsResultList <T> extends ResultList <T> {
7 totalNotDeletedComments: number
8}