Browse Source

Support bulk registration request removal

Chocobozzz 1 year ago
parent
commit
cd940f40cb

+ 2 - 2
client/src/app/+admin/follows/followers-list/followers-list.component.html

@@ -9,14 +9,14 @@
   [lazy]="true" (onLazyLoad)="loadLazy($event)" [lazyLoadOnInit]="false"
   [showCurrentPageReport]="true" i18n-currentPageReportTemplate
   currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} followers"
-  [(selection)]="selectedFollows"
+  [(selection)]="selectedRows"
 >
   <ng-template pTemplate="caption">
     <div class="caption">
       <div class="left-buttons">
         <my-action-dropdown
           *ngIf="isInSelectionMode()" i18n-label label="Batch actions" theme="orange"
-          [actions]="bulkFollowsActions" [entry]="selectedFollows"
+          [actions]="bulkActions" [entry]="selectedRows"
         >
         </my-action-dropdown>
       </div>

+ 7 - 10
client/src/app/+admin/follows/followers-list/followers-list.component.ts

@@ -12,7 +12,7 @@ import { ActorFollow } from '@shared/models'
   templateUrl: './followers-list.component.html',
   styleUrls: [ './followers-list.component.scss' ]
 })
-export class FollowersListComponent extends RestTable implements OnInit {
+export class FollowersListComponent extends RestTable <ActorFollow> implements OnInit {
   followers: ActorFollow[] = []
   totalRecords = 0
   sort: SortMeta = { field: 'createdAt', order: -1 }
@@ -20,8 +20,7 @@ export class FollowersListComponent extends RestTable implements OnInit {
 
   searchFilters: AdvancedInputFilter[] = []
 
-  selectedFollows: ActorFollow[] = []
-  bulkFollowsActions: DropdownAction<ActorFollow[]>[] = []
+  bulkActions: DropdownAction<ActorFollow[]>[] = []
 
   constructor (
     private confirmService: ConfirmService,
@@ -36,7 +35,7 @@ export class FollowersListComponent extends RestTable implements OnInit {
 
     this.searchFilters = this.followService.buildFollowsListFilters()
 
-    this.bulkFollowsActions = [
+    this.bulkActions = [
       {
         label: $localize`Reject`,
         handler: follows => this.rejectFollower(follows),
@@ -105,12 +104,14 @@ export class FollowersListComponent extends RestTable implements OnInit {
   }
 
   async deleteFollowers (follows: ActorFollow[]) {
+    const icuParams = { count: follows.length, followerName: this.buildFollowerName(follows[0]) }
+
     let message = $localize`Deleted followers will be able to send again a follow request.`
     message += '<br /><br />'
 
     // eslint-disable-next-line max-len
     message += prepareIcu($localize`Do you really want to delete {count, plural, =1 {{followerName} follow request?} other {{count} follow requests?}}`)(
-      { count: follows.length, followerName: this.buildFollowerName(follows[0]) },
+      icuParams,
       $localize`Do you really want to delete these follow requests?`
     )
 
@@ -122,7 +123,7 @@ export class FollowersListComponent extends RestTable implements OnInit {
           next: () => {
             // eslint-disable-next-line max-len
             const message = prepareIcu($localize`Removed {count, plural, =1 {{followerName} follow request} other {{count} follow requests}}`)(
-              { count: follows.length, followerName: this.buildFollowerName(follows[0]) },
+              icuParams,
               $localize`Follow requests removed`
             )
 
@@ -139,10 +140,6 @@ export class FollowersListComponent extends RestTable implements OnInit {
     return follow.follower.name + '@' + follow.follower.host
   }
 
-  isInSelectionMode () {
-    return this.selectedFollows.length !== 0
-  }
-
   protected reloadData () {
     this.followService.getFollowers({ pagination: this.pagination, sort: this.sort, search: this.search })
                       .subscribe({

+ 2 - 2
client/src/app/+admin/follows/following-list/following-list.component.html

@@ -9,14 +9,14 @@
   [lazy]="true" (onLazyLoad)="loadLazy($event)" [lazyLoadOnInit]="false"
   [showCurrentPageReport]="true" i18n-currentPageReportTemplate
   currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} hosts"
-  [(selection)]="selectedFollows"
+  [(selection)]="selectedRows"
 >
   <ng-template pTemplate="caption">
     <div class="caption">
       <div class="left-buttons">
         <my-action-dropdown
           *ngIf="isInSelectionMode()" i18n-label label="Batch actions" theme="orange"
-          [actions]="bulkFollowsActions" [entry]="selectedFollows"
+          [actions]="bulkActions" [entry]="selectedRows"
         >
         </my-action-dropdown>
 

+ 3 - 8
client/src/app/+admin/follows/following-list/following-list.component.ts

@@ -12,7 +12,7 @@ import { prepareIcu } from '@app/helpers'
   templateUrl: './following-list.component.html',
   styleUrls: [ './following-list.component.scss' ]
 })
-export class FollowingListComponent extends RestTable implements OnInit {
+export class FollowingListComponent extends RestTable <ActorFollow> implements OnInit {
   @ViewChild('followModal') followModal: FollowModalComponent
 
   following: ActorFollow[] = []
@@ -22,8 +22,7 @@ export class FollowingListComponent extends RestTable implements OnInit {
 
   searchFilters: AdvancedInputFilter[] = []
 
-  selectedFollows: ActorFollow[] = []
-  bulkFollowsActions: DropdownAction<ActorFollow[]>[] = []
+  bulkActions: DropdownAction<ActorFollow[]>[] = []
 
   constructor (
     private notifier: Notifier,
@@ -38,7 +37,7 @@ export class FollowingListComponent extends RestTable implements OnInit {
 
     this.searchFilters = this.followService.buildFollowsListFilters()
 
-    this.bulkFollowsActions = [
+    this.bulkActions = [
       {
         label: $localize`Delete`,
         handler: follows => this.removeFollowing(follows)
@@ -58,10 +57,6 @@ export class FollowingListComponent extends RestTable implements OnInit {
     return follow.following.name === 'peertube'
   }
 
-  isInSelectionMode () {
-    return this.selectedFollows.length !== 0
-  }
-
   buildFollowingName (follow: ActorFollow) {
     return follow.following.name + '@' + follow.following.host
   }

+ 11 - 5
client/src/app/+admin/moderation/registration-list/admin-registration.service.ts

@@ -1,8 +1,10 @@
 import { SortMeta } from 'primeng/api'
-import { catchError } from 'rxjs/operators'
+import { from } from 'rxjs'
+import { catchError, concatMap, toArray } from 'rxjs/operators'
 import { HttpClient, HttpParams } from '@angular/common/http'
 import { Injectable } from '@angular/core'
 import { RestExtractor, RestPagination, RestService } from '@app/core'
+import { arrayify } from '@shared/core-utils'
 import { ResultList, UserRegistration } from '@shared/models'
 import { environment } from '../../../../environments/environment'
 
@@ -54,10 +56,14 @@ export class AdminRegistrationService {
       .pipe(catchError(res => this.restExtractor.handleError(res)))
   }
 
-  removeRegistration (registration: UserRegistration) {
-    const url = AdminRegistrationService.BASE_REGISTRATION_URL + '/' + registration.id
+  removeRegistrations (registrationsArg: UserRegistration | UserRegistration[]) {
+    const registrations = arrayify(registrationsArg)
 
-    return this.authHttp.delete(url)
-      .pipe(catchError(res => this.restExtractor.handleError(res)))
+    return from(registrations)
+      .pipe(
+        concatMap(r => this.authHttp.delete(AdminRegistrationService.BASE_REGISTRATION_URL + '/' + r.id)),
+        toArray(),
+        catchError(err => this.restExtractor.handleError(err))
+      )
   }
 }

+ 17 - 2
client/src/app/+admin/moderation/registration-list/registration-list.component.html

@@ -7,12 +7,20 @@
   [value]="registrations" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [first]="pagination.start"
   [rowsPerPageOptions]="rowsPerPageOptions" [sortField]="sort.field" [sortOrder]="sort.order" dataKey="id"
   [lazy]="true" (onLazyLoad)="loadLazy($event)" [lazyLoadOnInit]="false"
-  [showCurrentPageReport]="true" i18n-currentPageReportTemplate
+  [(selection)]="selectedRows" [showCurrentPageReport]="true" i18n-currentPageReportTemplate
   currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} registrations"
   [expandedRowKeys]="expandedRows"
 >
   <ng-template pTemplate="caption">
     <div class="caption">
+      <div class="left-buttons">
+        <my-action-dropdown
+          *ngIf="isInSelectionMode()" i18n-label label="Batch actions" theme="orange"
+          [actions]="bulkActions" [entry]="selectedRows"
+        >
+        </my-action-dropdown>
+      </div>
+
       <div class="ms-auto">
         <my-advanced-input-filter (search)="onSearch($event)"></my-advanced-input-filter>
       </div>
@@ -21,6 +29,9 @@
 
   <ng-template pTemplate="header">
     <tr> <!-- header -->
+      <th style="width: 40px">
+        <p-tableHeaderCheckbox ariaLabel="Select all rows" i18n-ariaLabel></p-tableHeaderCheckbox>
+      </th>
       <th style="width: 40px;"></th>
       <th style="width: 150px;"></th>
       <th i18n>Account</th>
@@ -34,7 +45,11 @@
   </ng-template>
 
   <ng-template pTemplate="body" let-expanded="expanded" let-registration>
-    <tr>
+    <tr [pSelectableRow]="registration">
+      <td class="checkbox-cell">
+        <p-tableCheckbox [value]="registration" ariaLabel="Select this row" i18n-ariaLabel></p-tableCheckbox>
+      </td>
+
       <td class="expand-cell" [pRowToggler]="registration">
         <my-table-expander-icon [expanded]="expanded"></my-table-expander-icon>
       </td>

+ 36 - 10
client/src/app/+admin/moderation/registration-list/registration-list.component.ts

@@ -1,7 +1,8 @@
 import { SortMeta } from 'primeng/api'
 import { Component, OnInit, ViewChild } from '@angular/core'
 import { ActivatedRoute, Router } from '@angular/router'
-import { MarkdownService, Notifier, RestPagination, RestTable, ServerService } from '@app/core'
+import { ConfirmService, MarkdownService, Notifier, RestPagination, RestTable, ServerService } from '@app/core'
+import { prepareIcu } from '@app/helpers'
 import { AdvancedInputFilter } from '@app/shared/shared-forms'
 import { DropdownAction } from '@app/shared/shared-main'
 import { UserRegistration, UserRegistrationState } from '@shared/models'
@@ -13,7 +14,7 @@ import { ProcessRegistrationModalComponent } from './process-registration-modal.
   templateUrl: './registration-list.component.html',
   styleUrls: [ '../../../shared/shared-moderation/moderation.scss', './registration-list.component.scss' ]
 })
-export class RegistrationListComponent extends RestTable implements OnInit {
+export class RegistrationListComponent extends RestTable <UserRegistration> implements OnInit {
   @ViewChild('processRegistrationModal', { static: true }) processRegistrationModal: ProcessRegistrationModalComponent
 
   registrations: (UserRegistration & { registrationReasonHTML?: string, moderationResponseHTML?: string })[] = []
@@ -22,6 +23,7 @@ export class RegistrationListComponent extends RestTable implements OnInit {
   pagination: RestPagination = { count: this.rowsPerPage, start: 0 }
 
   registrationActions: DropdownAction<UserRegistration>[][] = []
+  bulkActions: DropdownAction<UserRegistration[]>[] = []
 
   inputFilters: AdvancedInputFilter[] = []
 
@@ -33,6 +35,7 @@ export class RegistrationListComponent extends RestTable implements OnInit {
     private server: ServerService,
     private notifier: Notifier,
     private markdownRenderer: MarkdownService,
+    private confirmService: ConfirmService,
     private adminRegistrationService: AdminRegistrationService
   ) {
     super()
@@ -40,22 +43,28 @@ export class RegistrationListComponent extends RestTable implements OnInit {
     this.registrationActions = [
       [
         {
-          label: $localize`Accept this registration`,
+          label: $localize`Accept this request`,
           handler: registration => this.openRegistrationRequestProcessModal(registration, 'accept'),
           isDisplayed: registration => registration.state.id === UserRegistrationState.PENDING
         },
         {
-          label: $localize`Reject this registration`,
+          label: $localize`Reject this request`,
           handler: registration => this.openRegistrationRequestProcessModal(registration, 'reject'),
           isDisplayed: registration => registration.state.id === UserRegistrationState.PENDING
         },
         {
-          label: $localize`Remove this registration request`,
-          handler: registration => this.removeRegistration(registration),
-          isDisplayed: registration => registration.state.id !== UserRegistrationState.PENDING
+          label: $localize`Remove this request`,
+          handler: registration => this.removeRegistrations([ registration ])
         }
       ]
     ]
+
+    this.bulkActions = [
+      {
+        label: $localize`Delete`,
+        handler: registrations => this.removeRegistrations(registrations)
+      }
+    ]
   }
 
   ngOnInit () {
@@ -107,11 +116,28 @@ export class RegistrationListComponent extends RestTable implements OnInit {
     this.processRegistrationModal.openModal(registration, mode)
   }
 
-  private removeRegistration (registration: UserRegistration) {
-    this.adminRegistrationService.removeRegistration(registration)
+  private async removeRegistrations (registrations: UserRegistration[]) {
+    const icuParams = { count: registrations.length, username: registrations[0].username }
+
+    // eslint-disable-next-line max-len
+    const message = prepareIcu($localize`Do you really want to delete {count, plural, =1 {{username} registration request?} other {{count} registration requests?}}`)(
+      icuParams,
+      $localize`Do you really want to delete these registration requests?`
+    )
+
+    const res = await this.confirmService.confirm(message, $localize`Delete`)
+    if (res === false) return
+
+    this.adminRegistrationService.removeRegistrations(registrations)
       .subscribe({
         next: () => {
-          this.notifier.success($localize`Registration request deleted.`)
+          // eslint-disable-next-line max-len
+          const message = prepareIcu($localize`Removed {count, plural, =1 {{username} registration request} other {{count} registration requests}}`)(
+            icuParams,
+            $localize`Registration requests removed`
+          )
+
+          this.notifier.success(message)
           this.reloadData()
         },
 

+ 2 - 2
client/src/app/+admin/overview/comments/video-comment-list.component.html

@@ -13,14 +13,14 @@
   [lazy]="true" (onLazyLoad)="loadLazy($event)" [lazyLoadOnInit]="false" [selectionPageOnly]="true"
   [showCurrentPageReport]="true" i18n-currentPageReportTemplate
   currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} comments"
-  [expandedRowKeys]="expandedRows" [(selection)]="selectedComments"
+  [expandedRowKeys]="expandedRows" [(selection)]="selectedRows"
 >
   <ng-template pTemplate="caption">
     <div class="caption">
       <div>
         <my-action-dropdown
           *ngIf="isInSelectionMode()" i18n-label label="Batch actions" theme="orange"
-          [actions]="bulkCommentActions" [entry]="selectedComments"
+          [actions]="bulkActions" [entry]="selectedRows"
         >
         </my-action-dropdown>
       </div>

+ 4 - 9
client/src/app/+admin/overview/comments/video-comment-list.component.ts

@@ -14,7 +14,7 @@ import { prepareIcu } from '@app/helpers'
   templateUrl: './video-comment-list.component.html',
   styleUrls: [ '../../../shared/shared-moderation/moderation.scss', './video-comment-list.component.scss' ]
 })
-export class VideoCommentListComponent extends RestTable implements OnInit {
+export class VideoCommentListComponent extends RestTable <VideoCommentAdmin> implements OnInit {
   comments: VideoCommentAdmin[]
   totalRecords = 0
   sort: SortMeta = { field: 'createdAt', order: -1 }
@@ -40,8 +40,7 @@ export class VideoCommentListComponent extends RestTable implements OnInit {
     }
   ]
 
-  selectedComments: VideoCommentAdmin[] = []
-  bulkCommentActions: DropdownAction<VideoCommentAdmin[]>[] = []
+  bulkActions: DropdownAction<VideoCommentAdmin[]>[] = []
 
   inputFilters: AdvancedInputFilter[] = [
     {
@@ -100,7 +99,7 @@ export class VideoCommentListComponent extends RestTable implements OnInit {
   ngOnInit () {
     this.initialize()
 
-    this.bulkCommentActions = [
+    this.bulkActions = [
       {
         label: $localize`Delete`,
         handler: comments => this.removeComments(comments),
@@ -118,10 +117,6 @@ export class VideoCommentListComponent extends RestTable implements OnInit {
     return this.markdownRenderer.textMarkdownToHTML({ markdown: text, withHtml: true, withEmoji: true })
   }
 
-  isInSelectionMode () {
-    return this.selectedComments.length !== 0
-  }
-
   reloadData () {
     this.videoCommentService.getAdminVideoComments({
       pagination: this.pagination,
@@ -162,7 +157,7 @@ export class VideoCommentListComponent extends RestTable implements OnInit {
 
         error: err => this.notifier.error(err.message),
 
-        complete: () => this.selectedComments = []
+        complete: () => this.selectedRows = []
       })
   }
 

+ 2 - 2
client/src/app/+admin/overview/users/user-list/user-list.component.html

@@ -6,7 +6,7 @@
 <p-table
   [value]="users" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [first]="pagination.start"
   [rowsPerPageOptions]="rowsPerPageOptions" [sortField]="sort.field" [sortOrder]="sort.order" dataKey="id" [resizableColumns]="true"
-  [(selection)]="selectedUsers" [lazy]="true" (onLazyLoad)="loadLazy($event)" [lazyLoadOnInit]="false" [selectionPageOnly]="true"
+  [(selection)]="selectedRows" [lazy]="true" (onLazyLoad)="loadLazy($event)" [lazyLoadOnInit]="false" [selectionPageOnly]="true"
   [showCurrentPageReport]="true" i18n-currentPageReportTemplate
   currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} users"
   [expandedRowKeys]="expandedRows"
@@ -16,7 +16,7 @@
       <div class="left-buttons">
         <my-action-dropdown
           *ngIf="isInSelectionMode()" i18n-label label="Batch actions" theme="orange"
-          [actions]="bulkUserActions" [entry]="selectedUsers"
+          [actions]="bulkActions" [entry]="selectedRows"
         >
         </my-action-dropdown>
 

+ 5 - 10
client/src/app/+admin/overview/users/user-list/user-list.component.ts

@@ -22,7 +22,7 @@ type UserForList = User & {
   templateUrl: './user-list.component.html',
   styleUrls: [ './user-list.component.scss' ]
 })
-export class UserListComponent extends RestTable implements OnInit {
+export class UserListComponent extends RestTable <User> implements OnInit {
   private static readonly LOCAL_STORAGE_SELECTED_COLUMNS_KEY = 'admin-user-list-selected-columns'
 
   @ViewChild('userBanModal', { static: true }) userBanModal: UserBanModalComponent
@@ -35,8 +35,7 @@ export class UserListComponent extends RestTable implements OnInit {
 
   highlightBannedUsers = false
 
-  selectedUsers: User[] = []
-  bulkUserActions: DropdownAction<User[]>[][] = []
+  bulkActions: DropdownAction<User[]>[][] = []
   columns: { id: string, label: string }[]
 
   inputFilters: AdvancedInputFilter[] = [
@@ -95,7 +94,7 @@ export class UserListComponent extends RestTable implements OnInit {
 
     this.initialize()
 
-    this.bulkUserActions = [
+    this.bulkActions = [
       [
         {
           label: $localize`Delete`,
@@ -249,7 +248,7 @@ export class UserListComponent extends RestTable implements OnInit {
     const res = await this.confirmService.confirm(message, $localize`Delete`)
     if (res === false) return
 
-    this.userAdminService.removeUser(users)
+    this.userAdminService.removeUsers(users)
       .subscribe({
         next: () => {
           this.notifier.success(
@@ -284,12 +283,8 @@ export class UserListComponent extends RestTable implements OnInit {
       })
   }
 
-  isInSelectionMode () {
-    return this.selectedUsers.length !== 0
-  }
-
   protected reloadData () {
-    this.selectedUsers = []
+    this.selectedRows = []
 
     this.userAdminService.getUsers({
       pagination: this.pagination,

+ 2 - 2
client/src/app/+admin/overview/videos/video-list.component.html

@@ -6,7 +6,7 @@
 <p-table
   [value]="videos" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [first]="pagination.start"
   [rowsPerPageOptions]="rowsPerPageOptions" [sortField]="sort.field" [sortOrder]="sort.order" dataKey="id" [resizableColumns]="true"
-  [(selection)]="selectedVideos" [lazy]="true" (onLazyLoad)="loadLazy($event)" [lazyLoadOnInit]="false" [selectionPageOnly]="true"
+  [(selection)]="selectedRows" [lazy]="true" (onLazyLoad)="loadLazy($event)" [lazyLoadOnInit]="false" [selectionPageOnly]="true"
   [showCurrentPageReport]="true" i18n-currentPageReportTemplate
   currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} videos"
   [expandedRowKeys]="expandedRows" [ngClass]="{ loading: loading }"
@@ -16,7 +16,7 @@
       <div class="left-buttons">
         <my-action-dropdown
           *ngIf="isInSelectionMode()" i18n-label label="Batch actions" theme="orange"
-          [actions]="bulkVideoActions" [entry]="selectedVideos"
+          [actions]="bulkActions" [entry]="selectedRows"
         >
         </my-action-dropdown>
       </div>

+ 4 - 10
client/src/app/+admin/overview/videos/video-list.component.ts

@@ -17,7 +17,7 @@ import { VideoAdminService } from './video-admin.service'
   templateUrl: './video-list.component.html',
   styleUrls: [ './video-list.component.scss' ]
 })
-export class VideoListComponent extends RestTable implements OnInit {
+export class VideoListComponent extends RestTable <Video> implements OnInit {
   @ViewChild('videoBlockModal') videoBlockModal: VideoBlockComponent
 
   videos: Video[] = []
@@ -26,9 +26,7 @@ export class VideoListComponent extends RestTable implements OnInit {
   sort: SortMeta = { field: 'publishedAt', order: -1 }
   pagination: RestPagination = { count: this.rowsPerPage, start: 0 }
 
-  bulkVideoActions: DropdownAction<Video[]>[][] = []
-
-  selectedVideos: Video[] = []
+  bulkActions: DropdownAction<Video[]>[][] = []
 
   inputFilters: AdvancedInputFilter[]
 
@@ -72,7 +70,7 @@ export class VideoListComponent extends RestTable implements OnInit {
 
     this.inputFilters = this.videoAdminService.buildAdminInputFilter()
 
-    this.bulkVideoActions = [
+    this.bulkActions = [
       [
         {
           label: $localize`Delete`,
@@ -126,10 +124,6 @@ export class VideoListComponent extends RestTable implements OnInit {
     return 'VideoListComponent'
   }
 
-  isInSelectionMode () {
-    return this.selectedVideos.length !== 0
-  }
-
   getPrivacyBadgeClass (video: Video) {
     if (video.privacy.id === VideoPrivacy.PUBLIC) return 'badge-green'
 
@@ -190,7 +184,7 @@ export class VideoListComponent extends RestTable implements OnInit {
   }
 
   reloadData () {
-    this.selectedVideos = []
+    this.selectedRows = []
 
     this.loading = true
 

+ 1 - 1
client/src/app/core/renderer/linkifier.service.ts

@@ -15,7 +15,7 @@ export class LinkifierService {
     },
     formatHref: {
       mention: (href: string) => {
-        return getAbsoluteAPIUrl() + '/services/redirect/accounts/' + href.substr(1)
+        return getAbsoluteAPIUrl() + '/services/redirect/accounts/' + href.substring(1)
       }
     }
   }

+ 7 - 1
client/src/app/core/rest/rest-table.ts

@@ -7,7 +7,7 @@ import { RestPagination } from './rest-pagination'
 
 const debugLogger = debug('peertube:tables:RestTable')
 
-export abstract class RestTable {
+export abstract class RestTable <T = unknown> {
 
   abstract totalRecords: number
   abstract sort: SortMeta
@@ -17,6 +17,8 @@ export abstract class RestTable {
   rowsPerPage = this.rowsPerPageOptions[0]
   expandedRows = {}
 
+  selectedRows: T[] = []
+
   search: string
 
   protected route: ActivatedRoute
@@ -75,6 +77,10 @@ export abstract class RestTable {
     this.reloadData()
   }
 
+  isInSelectionMode () {
+    return this.selectedRows.length !== 0
+  }
+
   protected abstract reloadData (): void
 
   private getSortLocalStorageKey () {

+ 1 - 1
client/src/app/shared/shared-moderation/user-moderation-dropdown.component.ts

@@ -105,7 +105,7 @@ export class UserModerationDropdownComponent implements OnInit, OnChanges {
     const res = await this.confirmService.confirm(message, $localize`Delete ${user.username}`)
     if (res === false) return
 
-    this.userAdminService.removeUser(user)
+    this.userAdminService.removeUsers(user)
       .subscribe({
         next: () => {
           this.notifier.success($localize`User ${user.username} deleted.`)

+ 1 - 1
client/src/app/shared/shared-users/user-admin.service.ts

@@ -64,7 +64,7 @@ export class UserAdminService {
                )
   }
 
-  removeUser (usersArg: UserServerModel | UserServerModel[]) {
+  removeUsers (usersArg: UserServerModel | UserServerModel[]) {
     const users = arrayify(usersArg)
 
     return from(users)