Add comment filtering by reply count
authorRigel Kent <sendmemail@rigelk.eu>
Fri, 27 Dec 2019 16:02:34 +0000 (17:02 +0100)
committerRigel Kent <sendmemail@rigelk.eu>
Fri, 27 Dec 2019 16:02:44 +0000 (17:02 +0100)
client/src/app/shared/video/sort-field.type.ts
client/src/app/videos/+video-watch/comment/video-comment.service.ts
client/src/app/videos/+video-watch/comment/video-comments.component.html
client/src/app/videos/+video-watch/comment/video-comments.component.scss
client/src/app/videos/+video-watch/comment/video-comments.component.ts
server/initializers/constants.ts
server/models/utils.ts
server/models/video/video-comment.ts

index d1088d24495e35f3cbd8bac0f88ce8dc34ef1867..65b24d9463ee57e4fb608280ea6f912fa4543fcf 100644 (file)
@@ -5,3 +5,6 @@ export type VideoSortField = 'name' | '-name'
                       | 'views' | '-views'
                       | 'likes' | '-likes'
                       | 'trending' | '-trending'
+
+export type CommentSortField = 'createdAt' | '-createdAt'
+                          | 'totalReplies' | '-totalReplies'
index 550d42fa86552c65a2b100ff8691a6b4a26c2f14..72fbf5d2596042315ccbb13c687801af1b691740 100644 (file)
@@ -12,7 +12,7 @@ import {
 import { environment } from '../../../../environments/environment'
 import { RestExtractor, RestService } from '../../../shared/rest'
 import { ComponentPagination } from '../../../shared/rest/component-pagination.model'
-import { VideoSortField } from '../../../shared/video/sort-field.type'
+import { CommentSortField } from '../../../shared/video/sort-field.type'
 import { VideoComment } from './video-comment.model'
 
 @Injectable()
@@ -51,7 +51,7 @@ export class VideoCommentService {
   getVideoCommentThreads (parameters: {
     videoId: number | string,
     componentPagination: ComponentPagination,
-    sort: VideoSortField
+    sort: CommentSortField
   }): Observable<ResultList<VideoComment>> {
     const { videoId, componentPagination, sort } = parameters
 
index 5fabb7dfe70a62df015e88980eeb70b239532bd7..e284eab0a1e3203bb30c78d9356217de1cc4d9e3 100644 (file)
     </div>
 
     <my-feed [syndicationItems]="syndicationItems"></my-feed>
+
+    <div ngbDropdown class="d-inline-block ml-4">
+      <button class="btn btn-sm btn-outline-secondary" id="dropdownSortComments" ngbDropdownToggle i18n>
+        Sort by
+      </button>
+      <div ngbDropdownMenu aria-labelledby="dropdownSortComments">
+        <button (click)="handleSortChange('-createdAt')" ngbDropdownItem i18n>Most recent first (default)</button>
+        <button (click)="handleSortChange('-totalReplies')" ngbDropdownItem i18n>Most replies first</button>
+      </div>
+    </div>
   </div>
 
   <ng-template [ngIf]="video.commentsEnabled === true">
index dde10b068c110f319191c5eb7503b16f395a0a6b..9e368229528fc88993ae5df4e7b0404a1b093dfb 100644 (file)
   margin-right: 0;
 }
 
+#dropdownSortComments {
+  font-weight: 600;
+  text-transform: uppercase;
+  border: none;
+}
+
 my-feed {
   display: inline-block;
   margin-left: 5px;
index e814015534614c4e6bd9f8c805a9e7914bd3d8ca..974c61d6cc3f0a598b93ae9b8f79ca08b9e3bb30 100644 (file)
@@ -6,7 +6,7 @@ import { VideoCommentThreadTree } from '../../../../../../shared/models/videos/v
 import { AuthService } from '../../../core/auth'
 import { ComponentPagination, hasMoreItems } from '../../../shared/rest/component-pagination.model'
 import { User } from '../../../shared/users'
-import { VideoSortField } from '../../../shared/video/sort-field.type'
+import { CommentSortField } from '../../../shared/video/sort-field.type'
 import { VideoDetails } from '../../../shared/video/video-details.model'
 import { VideoComment } from './video-comment.model'
 import { VideoCommentService } from './video-comment.service'
@@ -28,7 +28,7 @@ export class VideoCommentsComponent implements OnInit, OnChanges, OnDestroy {
 
   comments: VideoComment[] = []
   highlightedThread: VideoComment
-  sort: VideoSortField = '-createdAt'
+  sort: CommentSortField = '-createdAt'
   componentPagination: ComponentPagination = {
     currentPage: 1,
     itemsPerPage: 10,
@@ -152,6 +152,13 @@ export class VideoCommentsComponent implements OnInit, OnChanges, OnDestroy {
     this.viewReplies(commentTree.comment.id)
   }
 
+  handleSortChange (sort: CommentSortField) {
+    if (this.sort === sort) return
+
+    this.sort = sort
+    this.resetVideo()
+  }
+
   handleTimestampClicked (timestamp: number) {
     this.timestampClicked.emit(timestamp)
   }
index 79fcd0edf5aeab242766359d8249922c646cb875..1b7b94d744a320b3aa2f2fa51834115fbc7c200a 100644 (file)
@@ -46,7 +46,7 @@ const SORTABLE_COLUMNS = {
   VIDEO_ABUSES: [ 'id', 'createdAt', 'state' ],
   VIDEO_CHANNELS: [ 'id', 'name', 'updatedAt', 'createdAt' ],
   VIDEO_IMPORTS: [ 'createdAt' ],
-  VIDEO_COMMENT_THREADS: [ 'createdAt' ],
+  VIDEO_COMMENT_THREADS: [ 'createdAt', 'totalReplies' ],
   VIDEO_RATES: [ 'createdAt' ],
   BLACKLISTS: [ 'id', 'name', 'duration', 'views', 'likes', 'dislikes', 'uuid', 'createdAt' ],
   FOLLOWERS: [ 'createdAt', 'state', 'score' ],
index b53a52a053f9e86276a2632a3185ef2665044832..4199cc4430c2a371dce1dce33eb3f7e31f2a447b 100644 (file)
@@ -22,6 +22,19 @@ function getSort (value: string, lastSort: OrderItem = [ 'id', 'ASC' ]): OrderIt
   return [ [ finalField, direction ], lastSort ]
 }
 
+function getCommentSort (value: string, lastSort: OrderItem = [ 'id', 'ASC' ]): OrderItem[] {
+  const { direction, field } = buildDirectionAndField(value)
+
+  if (field === 'totalReplies') {
+    return [
+      [ Sequelize.literal('"totalReplies"'), direction ],
+      lastSort
+    ]
+  }
+
+  return getSort(value, lastSort)
+}
+
 function getVideoSort (value: string, lastSort: OrderItem = [ 'id', 'ASC' ]): OrderItem[] {
   const { direction, field } = buildDirectionAndField(value)
 
@@ -167,6 +180,7 @@ export {
   SortType,
   buildLocalAccountIdsIn,
   getSort,
+  getCommentSort,
   getVideoSort,
   getBlacklistSort,
   createSimilarityAttribute,
index 869a42afef0f14197fa8bf4643c7e87058651017..28f011b03b3a1b13a6fc7b577d886f09b488098e 100644 (file)
@@ -6,7 +6,7 @@ import { isActivityPubUrlValid } from '../../helpers/custom-validators/activityp
 import { CONSTRAINTS_FIELDS, WEBSERVER } from '../../initializers/constants'
 import { AccountModel } from '../account/account'
 import { ActorModel } from '../activitypub/actor'
-import { buildBlockedAccountSQL, buildLocalAccountIdsIn, getSort, throwIfNotValid } from '../utils'
+import { buildBlockedAccountSQL, buildLocalAccountIdsIn, getCommentSort, throwIfNotValid } from '../utils'
 import { VideoModel } from './video'
 import { VideoChannelModel } from './video-channel'
 import { getServerActor } from '../../helpers/utils'
@@ -259,7 +259,7 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
     const query = {
       offset: start,
       limit: count,
-      order: getSort(sort),
+      order: getCommentSort(sort),
       where: {
         videoId,
         inReplyToCommentId: null,