Support bulk registration request removal

This commit is contained in:
Chocobozzz 2023-01-20 14:58:05 +01:00
parent 9436936cf6
commit cd940f40cb
No known key found for this signature in database
GPG Key ID: 583A612D890159BE
17 changed files with 107 additions and 78 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -7,12 +7,20 @@
[value]="registrations" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [first]="pagination.start" [value]="registrations" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [first]="pagination.start"
[rowsPerPageOptions]="rowsPerPageOptions" [sortField]="sort.field" [sortOrder]="sort.order" dataKey="id" [rowsPerPageOptions]="rowsPerPageOptions" [sortField]="sort.field" [sortOrder]="sort.order" dataKey="id"
[lazy]="true" (onLazyLoad)="loadLazy($event)" [lazyLoadOnInit]="false" [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" currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} registrations"
[expandedRowKeys]="expandedRows" [expandedRowKeys]="expandedRows"
> >
<ng-template pTemplate="caption"> <ng-template pTemplate="caption">
<div class="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"> <div class="ms-auto">
<my-advanced-input-filter (search)="onSearch($event)"></my-advanced-input-filter> <my-advanced-input-filter (search)="onSearch($event)"></my-advanced-input-filter>
</div> </div>
@ -21,6 +29,9 @@
<ng-template pTemplate="header"> <ng-template pTemplate="header">
<tr> <!-- 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: 40px;"></th>
<th style="width: 150px;"></th> <th style="width: 150px;"></th>
<th i18n>Account</th> <th i18n>Account</th>
@ -34,7 +45,11 @@
</ng-template> </ng-template>
<ng-template pTemplate="body" let-expanded="expanded" let-registration> <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"> <td class="expand-cell" [pRowToggler]="registration">
<my-table-expander-icon [expanded]="expanded"></my-table-expander-icon> <my-table-expander-icon [expanded]="expanded"></my-table-expander-icon>
</td> </td>

View File

@ -1,7 +1,8 @@
import { SortMeta } from 'primeng/api' import { SortMeta } from 'primeng/api'
import { Component, OnInit, ViewChild } from '@angular/core' import { Component, OnInit, ViewChild } from '@angular/core'
import { ActivatedRoute, Router } from '@angular/router' 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 { AdvancedInputFilter } from '@app/shared/shared-forms'
import { DropdownAction } from '@app/shared/shared-main' import { DropdownAction } from '@app/shared/shared-main'
import { UserRegistration, UserRegistrationState } from '@shared/models' import { UserRegistration, UserRegistrationState } from '@shared/models'
@ -13,7 +14,7 @@ import { ProcessRegistrationModalComponent } from './process-registration-modal.
templateUrl: './registration-list.component.html', templateUrl: './registration-list.component.html',
styleUrls: [ '../../../shared/shared-moderation/moderation.scss', './registration-list.component.scss' ] 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 @ViewChild('processRegistrationModal', { static: true }) processRegistrationModal: ProcessRegistrationModalComponent
registrations: (UserRegistration & { registrationReasonHTML?: string, moderationResponseHTML?: string })[] = [] registrations: (UserRegistration & { registrationReasonHTML?: string, moderationResponseHTML?: string })[] = []
@ -22,6 +23,7 @@ export class RegistrationListComponent extends RestTable implements OnInit {
pagination: RestPagination = { count: this.rowsPerPage, start: 0 } pagination: RestPagination = { count: this.rowsPerPage, start: 0 }
registrationActions: DropdownAction<UserRegistration>[][] = [] registrationActions: DropdownAction<UserRegistration>[][] = []
bulkActions: DropdownAction<UserRegistration[]>[] = []
inputFilters: AdvancedInputFilter[] = [] inputFilters: AdvancedInputFilter[] = []
@ -33,6 +35,7 @@ export class RegistrationListComponent extends RestTable implements OnInit {
private server: ServerService, private server: ServerService,
private notifier: Notifier, private notifier: Notifier,
private markdownRenderer: MarkdownService, private markdownRenderer: MarkdownService,
private confirmService: ConfirmService,
private adminRegistrationService: AdminRegistrationService private adminRegistrationService: AdminRegistrationService
) { ) {
super() super()
@ -40,22 +43,28 @@ export class RegistrationListComponent extends RestTable implements OnInit {
this.registrationActions = [ this.registrationActions = [
[ [
{ {
label: $localize`Accept this registration`, label: $localize`Accept this request`,
handler: registration => this.openRegistrationRequestProcessModal(registration, 'accept'), handler: registration => this.openRegistrationRequestProcessModal(registration, 'accept'),
isDisplayed: registration => registration.state.id === UserRegistrationState.PENDING isDisplayed: registration => registration.state.id === UserRegistrationState.PENDING
}, },
{ {
label: $localize`Reject this registration`, label: $localize`Reject this request`,
handler: registration => this.openRegistrationRequestProcessModal(registration, 'reject'), handler: registration => this.openRegistrationRequestProcessModal(registration, 'reject'),
isDisplayed: registration => registration.state.id === UserRegistrationState.PENDING isDisplayed: registration => registration.state.id === UserRegistrationState.PENDING
}, },
{ {
label: $localize`Remove this registration request`, label: $localize`Remove this request`,
handler: registration => this.removeRegistration(registration), handler: registration => this.removeRegistrations([ registration ])
isDisplayed: registration => registration.state.id !== UserRegistrationState.PENDING
} }
] ]
] ]
this.bulkActions = [
{
label: $localize`Delete`,
handler: registrations => this.removeRegistrations(registrations)
}
]
} }
ngOnInit () { ngOnInit () {
@ -107,11 +116,28 @@ export class RegistrationListComponent extends RestTable implements OnInit {
this.processRegistrationModal.openModal(registration, mode) this.processRegistrationModal.openModal(registration, mode)
} }
private removeRegistration (registration: UserRegistration) { private async removeRegistrations (registrations: UserRegistration[]) {
this.adminRegistrationService.removeRegistration(registration) 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({ .subscribe({
next: () => { 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() this.reloadData()
}, },

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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