Increase rows per page, add reporter muting for abuse list
authorRigel Kent <sendmemail@rigelk.eu>
Thu, 16 Apr 2020 15:18:55 +0000 (17:18 +0200)
committerRigel Kent <par@rigelk.eu>
Fri, 1 May 2020 14:41:02 +0000 (16:41 +0200)
client/src/app/+admin/moderation/instance-blocklist/instance-account-blocklist.component.html
client/src/app/+admin/moderation/instance-blocklist/instance-server-blocklist.component.html
client/src/app/+admin/moderation/moderation.component.scss
client/src/app/+admin/moderation/video-abuse-list/video-abuse-list.component.html
client/src/app/+admin/moderation/video-abuse-list/video-abuse-list.component.ts
client/src/app/+admin/moderation/video-blacklist-list/video-blacklist-list.component.html
client/src/app/shared/buttons/action-dropdown.component.html
client/src/app/shared/buttons/action-dropdown.component.ts
client/src/sass/primeng-custom.scss

index e40c29abf9908c3330ab2f32914ad1fb3183d559..6d77c8290fb1fb29c560363b73a9526d2bf563bd 100644 (file)
@@ -8,7 +8,7 @@
   <ng-template pTemplate="header">
     <tr>
       <th i18n>Account</th>
-      <th style="width: 200px;" i18n pSortableColumn="createdAt">Muted at <p-sortIcon field="createdAt"></p-sortIcon></th>
+      <th style="width: 190px;" i18n pSortableColumn="createdAt">Muted at <p-sortIcon field="createdAt"></p-sortIcon></th>
       <th style="width: 100px;"></th> <!-- column for action buttons --> 
     </tr>
   </ng-template>
index bf5c0091895851437663bf72ba6141b6e04ac85f..075be8498aac1ad4bfb62e32119afaa54f8c7105 100644 (file)
@@ -16,7 +16,7 @@
   <ng-template pTemplate="header">
     <tr>
       <th i18n>Instance</th>
-      <th style="width: 200px;" i18n pSortableColumn="createdAt">Muted at <p-sortIcon field="createdAt"></p-sortIcon></th>
+      <th style="width: 190px;" i18n pSortableColumn="createdAt">Muted at <p-sortIcon field="createdAt"></p-sortIcon></th>
       <th style="width: 100px;"></th> <!-- column for action buttons -->
     </tr>
   </ng-template>
index 9af76d2ddcf1570e87d08657bb9467656f26dbed..a015b6d85d75b544bcf3ca6b01166c4dd3f8f1f3 100644 (file)
   }
 }
 
+.glyphicon-trash {
+  font-size: 80%;
+}
+
 .screenratio {
   position: relative;
   width: 100%;
index 3d356dc7ce807a1a413d30bc14f07333c089d282..204cb209eb4a245826aa7a01b5c9ffe0b99f3cea 100644 (file)
@@ -1,15 +1,15 @@
 <p-table
-  [value]="videoAbuses" [lazy]="true" [paginator]="true" [totalRecords]="totalRecords" [rows]="rowsPerPage"
-  [sortField]="sort.field" [sortOrder]="sort.order" (onLazyLoad)="loadLazy($event)" dataKey="id"
+  [value]="videoAbuses" [lazy]="true" [paginator]="true" [totalRecords]="totalRecords" [rows]="rowsPerPage" [rowsPerPageOptions]="rowsPerPageOptions"
+  [sortField]="sort.field" [sortOrder]="sort.order" (onLazyLoad)="loadLazy($event)" dataKey="id" [resizableColumns]="true"
   [showCurrentPageReport]="true" i18n-currentPageReportTemplate
   currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} reports"
 >
   <ng-template pTemplate="header">
     <tr> <!-- header -->
       <th style="width: 40px;"></th>
-      <th i18n>Reporter</th>
-      <th style="width: 200px;" i18n pSortableColumn="createdAt">Created <p-sortIcon field="createdAt"></p-sortIcon></th>
+      <th style="width: 20%;" pResizableColumn i18n>Reporter</th>
       <th i18n>Video</th>
+      <th style="width:190px;" i18n pSortableColumn="createdAt">Created <p-sortIcon field="createdAt"></p-sortIcon></th>
       <th i18n pSortableColumn="state" style="width: 80px;">State <p-sortIcon field="state"></p-sortIcon></th>
       <th style="width: 120px;"></th>
     </tr>
         </a>
       </td>
 
-      <td>{{ videoAbuse.createdAt }}</td>
-
       <td>
+        <span *ngIf="videoAbuse.video.deleted" i18n-title title="Video was deleted" class="glyphicon glyphicon-trash"></span>
         <a [href]="getVideoUrl(videoAbuse)" i18n-title title="Open video in a new tab" target="_blank" rel="noopener noreferrer">
           {{ videoAbuse.video.name }}
         </a>
       </td>
 
+      <td>{{ videoAbuse.createdAt }}</td>
+
       <td class="c-hand video-abuse-states" [pRowToggler]="videoAbuse">
         <span *ngIf="isVideoAbuseAccepted(videoAbuse)" [title]="videoAbuse.state.label" class="glyphicon glyphicon-ok"></span>
         <span *ngIf="isVideoAbuseRejected(videoAbuse)" [title]="videoAbuse.state.label" class="glyphicon glyphicon-remove"></span>
@@ -55,7 +56,7 @@
       </td>
 
       <td class="action-cell">
-        <my-action-dropdown placement="bottom-right auto" i18n-label label="Actions" [actions]="videoAbuseActions" [entry]="videoAbuse"></my-action-dropdown>
+        <my-action-dropdown placement="bottom-right auto" container="body" i18n-label label="Actions" [actions]="videoAbuseActions" [entry]="videoAbuse"></my-action-dropdown>
       </td>
     </tr>
   </ng-template>
index 5e48cf24fd8e220469e69b6deb641b509fd8bb45..9858cbce2958cdb14432c2dbdaac7343d1656842 100644 (file)
@@ -15,6 +15,7 @@ import { buildVideoLink, buildVideoEmbed } from 'src/assets/player/utils'
 import { getAbsoluteAPIUrl } from '@app/shared/misc/utils'
 import { DomSanitizer } from '@angular/platform-browser'
 import { BlocklistService } from '@app/shared/blocklist'
+import { VideoService } from '@app/shared/video/video.service'
 
 @Component({
   selector: 'my-video-abuse-list',
@@ -26,7 +27,8 @@ export class VideoAbuseListComponent extends RestTable implements OnInit {
 
   videoAbuses: (VideoAbuse & { moderationCommentHtml?: string, reasonHtml?: string })[] = []
   totalRecords = 0
-  rowsPerPage = 10
+  rowsPerPageOptions = [ 20, 50, 100 ]
+  rowsPerPage = this.rowsPerPageOptions[0]
   sort: SortMeta = { field: 'createdAt', order: 1 }
   pagination: RestPagination = { count: this.rowsPerPage, start: 0 }
 
@@ -36,6 +38,7 @@ export class VideoAbuseListComponent extends RestTable implements OnInit {
     private notifier: Notifier,
     private videoAbuseService: VideoAbuseService,
     private blocklistService: BlocklistService,
+    private videoService: VideoService,
     private videoBlacklistService: VideoBlacklistService,
     private confirmService: ConfirmService,
     private i18n: I18n,
@@ -78,10 +81,12 @@ export class VideoAbuseListComponent extends RestTable implements OnInit {
       [
         {
           label: this.i18n('Actions for the video'),
-          isHeader: true
+          isHeader: true,
+          isDisplayed: videoAbuse => !videoAbuse.video.deleted
         },
         {
           label: this.i18n('Blacklist video'),
+          isDisplayed: videoAbuse => !videoAbuse.video.deleted,
           handler: videoAbuse => {
             this.videoBlacklistService.blacklistVideo(videoAbuse.video.id, undefined, true)
               .subscribe(
@@ -91,6 +96,48 @@ export class VideoAbuseListComponent extends RestTable implements OnInit {
                   this.updateVideoAbuseState(videoAbuse, VideoAbuseState.ACCEPTED)
                 },
 
+                err => this.notifier.error(err.message)
+              )
+          }
+        },
+        {
+          label: this.i18n('Delete video'),
+          isDisplayed: videoAbuse => !videoAbuse.video.deleted,
+          handler: async videoAbuse => {
+            const res = await this.confirmService.confirm(this.i18n('Do you really want to delete this video?'), this.i18n('Delete'))
+            if (res === false) return
+
+            this.videoService.removeVideo(videoAbuse.video.id)
+              .subscribe(
+                () => {
+                  this.notifier.success(this.i18n('Video deleted.'))
+
+                  this.updateVideoAbuseState(videoAbuse, VideoAbuseState.ACCEPTED)
+                },
+
+                err => this.notifier.error(err.message)
+              )
+          }
+        }
+      ],
+      [
+        {
+          label: this.i18n('Actions for the reporter'),
+          isHeader: true
+        },
+        {
+          label: this.i18n('Mute reporter'),
+          handler: async videoAbuse => {
+            const account = videoAbuse.reporterAccount as Account
+
+            this.blocklistService.blockAccountByInstance(account)
+              .subscribe(
+                () => {
+                  this.notifier.success(this.i18n('Account {{nameWithHost}} muted by the instance.', { nameWithHost: account.nameWithHost }))
+
+                  account.mutedByInstance = true
+                },
+
                 err => this.notifier.error(err.message)
               )
           }
@@ -180,7 +227,8 @@ export class VideoAbuseListComponent extends RestTable implements OnInit {
                      Object.assign(abuse, {
                        reasonHtml: await this.toHtml(abuse.reason),
                        moderationCommentHtml: await this.toHtml(abuse.moderationComment),
-                       embedHtml: this.sanitizer.bypassSecurityTrustHtml(this.getVideoEmbed(abuse))
+                       embedHtml: this.sanitizer.bypassSecurityTrustHtml(this.getVideoEmbed(abuse)),
+                       reporterAccount: new Account(abuse.reporterAccount)
                      })
                    }
 
index 023f4e7b72444f16f73346aaf493009d6f10b588..2f9fc8ba410a11f2d6221261d3804929e1aaaaf3 100644 (file)
@@ -10,7 +10,7 @@
       <th i18n pSortableColumn="name">Video <p-sortIcon field="name"></p-sortIcon></th>
       <th style="width: 120px;" i18n>Sensitive</th>
       <th style="width: 120px;" i18n>Unfederated</th>
-      <th style="width: 200px;" i18n pSortableColumn="createdAt">Date <p-sortIcon field="createdAt"></p-sortIcon></th>
+      <th style="width: 190px;" i18n pSortableColumn="createdAt">Date <p-sortIcon field="createdAt"></p-sortIcon></th>
       <th style="width: 120px;"></th>
     </tr>
   </ng-template>
index 0efc01d3800899ce6ee09b38d31b3f56ebf4ed5b..952b3b6f80b93769b02cd0ef72f0b358c51ae471 100644 (file)
@@ -1,4 +1,4 @@
-<div class="dropdown-root" ngbDropdown [placement]="placement" container="body" *ngIf="areActionsDisplayed(actions, entry)">
+<div class="dropdown-root" ngbDropdown [placement]="placement" [container]="container" *ngIf="areActionsDisplayed(actions, entry)">
   <div
     class="action-button" [ngClass]="{ small: buttonSize === 'small', grey: theme === 'grey', orange: theme === 'orange', 'button-styled': buttonStyled }"
     ngbDropdownToggle role="button"
index 8fcaa38b968661525207e06ac835fb6bee4791c0..15f9556dc95b89bf035c929705a3f1be4aa0461e 100644 (file)
@@ -27,6 +27,7 @@ export class ActionDropdownComponent<T> {
   @Input() entry: T
 
   @Input() placement = 'bottom-left auto'
+  @Input() container: null | 'body'
 
   @Input() buttonSize: DropdownButtonSize = 'normal'
   @Input() buttonDirection: DropdownDirection = 'horizontal'
index b8e685abaa9f363d5426d6cae797d96ec27ad0b3..9c9a6197d255dc4cffa6ed0963354f0cae7efea9 100644 (file)
@@ -191,12 +191,22 @@ p-table {
 
       .ui-dropdown {
         position: absolute;
+        top: 3px;
         left: 0;
+
+        &.ui-state-focus {
+          box-shadow: #{$focus-box-shadow-form} var(--mainColorLightest);
+        }
+
+        .ui-dropdown-label {
+          color: var(--inputPlaceholderColor);
+        }
       }
 
       .ui-paginator-current {
         position: absolute;
         right: 0;
+        color: var(--inputPlaceholderColor);
       }
 
       .ui-paginator-first,