aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src/app/+admin/follows/followers-list
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2022-07-27 13:44:40 +0200
committerChocobozzz <me@florianbigard.com>2022-07-27 13:52:13 +0200
commite3d6c6434f570f77c0532f86c82f78bcafb399ec (patch)
tree65d525f42c8cf55aba871093b3dd65964f5cd967 /client/src/app/+admin/follows/followers-list
parent073deef8862f462de5f159a57877ef415ebe4c69 (diff)
downloadPeerTube-e3d6c6434f570f77c0532f86c82f78bcafb399ec.tar.gz
PeerTube-e3d6c6434f570f77c0532f86c82f78bcafb399ec.tar.zst
PeerTube-e3d6c6434f570f77c0532f86c82f78bcafb399ec.zip
Add bulk action on following/followers
Diffstat (limited to 'client/src/app/+admin/follows/followers-list')
-rw-r--r--client/src/app/+admin/follows/followers-list/followers-list.component.html26
-rw-r--r--client/src/app/+admin/follows/followers-list/followers-list.component.ts105
2 files changed, 99 insertions, 32 deletions
diff --git a/client/src/app/+admin/follows/followers-list/followers-list.component.html b/client/src/app/+admin/follows/followers-list/followers-list.component.html
index 4f11f261d..8fe0d2348 100644
--- a/client/src/app/+admin/follows/followers-list/followers-list.component.html
+++ b/client/src/app/+admin/follows/followers-list/followers-list.component.html
@@ -9,9 +9,18 @@
9 [lazy]="true" (onLazyLoad)="loadLazy($event)" [lazyLoadOnInit]="false" 9 [lazy]="true" (onLazyLoad)="loadLazy($event)" [lazyLoadOnInit]="false"
10 [showCurrentPageReport]="true" i18n-currentPageReportTemplate 10 [showCurrentPageReport]="true" i18n-currentPageReportTemplate
11 currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} followers" 11 currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} followers"
12 [(selection)]="selectedFollows"
12> 13>
13 <ng-template pTemplate="caption"> 14 <ng-template pTemplate="caption">
14 <div class="caption"> 15 <div class="caption">
16 <div class="left-buttons">
17 <my-action-dropdown
18 *ngIf="isInSelectionMode()" i18n-label label="Batch actions" theme="orange"
19 [actions]="bulkFollowsActions" [entry]="selectedFollows"
20 >
21 </my-action-dropdown>
22 </div>
23
15 <div class="ms-auto"> 24 <div class="ms-auto">
16 <my-advanced-input-filter [filters]="searchFilters" (search)="onSearch($event)"></my-advanced-input-filter> 25 <my-advanced-input-filter [filters]="searchFilters" (search)="onSearch($event)"></my-advanced-input-filter>
17 </div> 26 </div>
@@ -20,6 +29,9 @@
20 29
21 <ng-template pTemplate="header"> 30 <ng-template pTemplate="header">
22 <tr> 31 <tr>
32 <th style="width: 40px">
33 <p-tableHeaderCheckbox ariaLabel="Select all rows" i18n-ariaLabel></p-tableHeaderCheckbox>
34 </th>
23 <th style="width: 150px;" i18n>Actions</th> 35 <th style="width: 150px;" i18n>Actions</th>
24 <th i18n>Follower</th> 36 <th i18n>Follower</th>
25 <th style="width: 100px;" i18n pSortableColumn="state">State <p-sortIcon field="state"></p-sortIcon></th> 37 <th style="width: 100px;" i18n pSortableColumn="state">State <p-sortIcon field="state"></p-sortIcon></th>
@@ -30,15 +42,19 @@
30 42
31 <ng-template pTemplate="body" let-follow> 43 <ng-template pTemplate="body" let-follow>
32 <tr> 44 <tr>
45 <td class="checkbox-cell">
46 <p-tableCheckbox [value]="follow" ariaLabel="Select this row" i18n-ariaLabel></p-tableCheckbox>
47 </td>
48
33 <td class="action-cell"> 49 <td class="action-cell">
34 <my-button *ngIf="follow.state !== 'accepted'" i18n-title title="Accept" icon="tick" (click)="acceptFollower(follow)"></my-button> 50 <my-button *ngIf="follow.state !== 'accepted'" i18n-title title="Accept" icon="tick" (click)="acceptFollower([ follow ])"></my-button>
35 <my-button *ngIf="follow.state !== 'rejected'" i18n-title title="Refuse" icon="cross" (click)="rejectFollower(follow)"></my-button> 51 <my-button *ngIf="follow.state !== 'rejected'" i18n-title title="Reject" icon="cross" (click)="rejectFollower([ follow ])"></my-button>
36 52
37 <my-delete-button *ngIf="follow.state === 'rejected'" (click)="deleteFollower(follow)"></my-delete-button> 53 <my-delete-button *ngIf="follow.state === 'rejected'" (click)="deleteFollowers([ follow ])"></my-delete-button>
38 </td> 54 </td>
39 <td> 55 <td>
40 <a [href]="follow.follower.url" i18n-title title="Open actor page in a new tab" target="_blank" rel="noopener noreferrer"> 56 <a [href]="follow.follower.url" i18n-title title="Open actor page in a new tab" target="_blank" rel="noopener noreferrer">
41 {{ follow.follower.name + '@' + follow.follower.host }} 57 {{ buildFollowerName(follow) }}
42 <my-global-icon iconName="external-link"></my-global-icon> 58 <my-global-icon iconName="external-link"></my-global-icon>
43 </a> 59 </a>
44 </td> 60 </td>
@@ -56,7 +72,7 @@
56 72
57 <ng-template pTemplate="emptymessage"> 73 <ng-template pTemplate="emptymessage">
58 <tr> 74 <tr>
59 <td colspan="5"> 75 <td colspan="6">
60 <div class="no-results"> 76 <div class="no-results">
61 <ng-container *ngIf="search" i18n>No follower found matching current filters.</ng-container> 77 <ng-container *ngIf="search" i18n>No follower found matching current filters.</ng-container>
62 <ng-container *ngIf="!search" i18n>Your instance doesn't have any follower.</ng-container> 78 <ng-container *ngIf="!search" i18n>Your instance doesn't have any follower.</ng-container>
diff --git a/client/src/app/+admin/follows/followers-list/followers-list.component.ts b/client/src/app/+admin/follows/followers-list/followers-list.component.ts
index d09e74fef..b2d333e83 100644
--- a/client/src/app/+admin/follows/followers-list/followers-list.component.ts
+++ b/client/src/app/+admin/follows/followers-list/followers-list.component.ts
@@ -1,8 +1,10 @@
1import { SortMeta } from 'primeng/api' 1import { SortMeta } from 'primeng/api'
2import { Component, OnInit } from '@angular/core' 2import { Component, OnInit } from '@angular/core'
3import { ConfirmService, Notifier, RestPagination, RestTable } from '@app/core' 3import { ConfirmService, Notifier, RestPagination, RestTable } from '@app/core'
4import { prepareIcu } from '@app/helpers'
4import { AdvancedInputFilter } from '@app/shared/shared-forms' 5import { AdvancedInputFilter } from '@app/shared/shared-forms'
5import { InstanceFollowService } from '@app/shared/shared-instance' 6import { InstanceFollowService } from '@app/shared/shared-instance'
7import { DropdownAction } from '@app/shared/shared-main'
6import { ActorFollow } from '@shared/models' 8import { ActorFollow } from '@shared/models'
7 9
8@Component({ 10@Component({
@@ -16,7 +18,10 @@ export class FollowersListComponent extends RestTable implements OnInit {
16 sort: SortMeta = { field: 'createdAt', order: -1 } 18 sort: SortMeta = { field: 'createdAt', order: -1 }
17 pagination: RestPagination = { count: this.rowsPerPage, start: 0 } 19 pagination: RestPagination = { count: this.rowsPerPage, start: 0 }
18 20
19 searchFilters: AdvancedInputFilter[] 21 searchFilters: AdvancedInputFilter[] = []
22
23 selectedFollows: ActorFollow[] = []
24 bulkFollowsActions: DropdownAction<ActorFollow[]>[] = []
20 25
21 constructor ( 26 constructor (
22 private confirmService: ConfirmService, 27 private confirmService: ConfirmService,
@@ -24,66 +29,104 @@ export class FollowersListComponent extends RestTable implements OnInit {
24 private followService: InstanceFollowService 29 private followService: InstanceFollowService
25 ) { 30 ) {
26 super() 31 super()
27
28 this.searchFilters = this.followService.buildFollowsListFilters()
29 } 32 }
30 33
31 ngOnInit () { 34 ngOnInit () {
32 this.initialize() 35 this.initialize()
36
37 this.searchFilters = this.followService.buildFollowsListFilters()
38
39 this.bulkFollowsActions = [
40 {
41 label: $localize`Reject`,
42 handler: follows => this.rejectFollower(follows),
43 isDisplayed: follows => follows.every(f => f.state !== 'rejected')
44 },
45 {
46 label: $localize`Accept`,
47 handler: follows => this.acceptFollower(follows),
48 isDisplayed: follows => follows.every(f => f.state !== 'accepted')
49 },
50 {
51 label: $localize`Delete`,
52 handler: follows => this.deleteFollowers(follows),
53 isDisplayed: follows => follows.every(f => f.state === 'rejected')
54 }
55 ]
33 } 56 }
34 57
35 getIdentifier () { 58 getIdentifier () {
36 return 'FollowersListComponent' 59 return 'FollowersListComponent'
37 } 60 }
38 61
39 acceptFollower (follow: ActorFollow) { 62 acceptFollower (follows: ActorFollow[]) {
40 follow.state = 'accepted' 63 this.followService.acceptFollower(follows)
41
42 this.followService.acceptFollower(follow)
43 .subscribe({ 64 .subscribe({
44 next: () => { 65 next: () => {
45 const handle = follow.follower.name + '@' + follow.follower.host 66 // eslint-disable-next-line max-len
46 this.notifier.success($localize`${handle} accepted in instance followers`) 67 const message = prepareIcu($localize`Accepted {count, plural, =1 {{followerName} follow request} other {{count} follow requests}}`)(
68 { count: follows.length, followerName: this.buildFollowerName(follows[0]) },
69 $localize`Follow requests accepted`
70 )
71 this.notifier.success(message)
72
73 this.reloadData()
47 }, 74 },
48 75
49 error: err => { 76 error: err => this.notifier.error(err.message)
50 follow.state = 'pending'
51 this.notifier.error(err.message)
52 }
53 }) 77 })
54 } 78 }
55 79
56 async rejectFollower (follow: ActorFollow) { 80 async rejectFollower (follows: ActorFollow[]) {
57 const message = $localize`Do you really want to reject this follower?` 81 // eslint-disable-next-line max-len
82 const message = prepareIcu($localize`Do you really want to reject {count, plural, =1 {{followerName} follow request?} other {{count} follow requests?}}`)(
83 { count: follows.length, followerName: this.buildFollowerName(follows[0]) },
84 $localize`Do you really want to reject these follow requests?`
85 )
86
58 const res = await this.confirmService.confirm(message, $localize`Reject`) 87 const res = await this.confirmService.confirm(message, $localize`Reject`)
59 if (res === false) return 88 if (res === false) return
60 89
61 this.followService.rejectFollower(follow) 90 this.followService.rejectFollower(follows)
62 .subscribe({ 91 .subscribe({
63 next: () => { 92 next: () => {
64 const handle = follow.follower.name + '@' + follow.follower.host 93 // eslint-disable-next-line max-len
65 this.notifier.success($localize`${handle} rejected from instance followers`) 94 const message = prepareIcu($localize`Rejected {count, plural, =1 {{followerName} follow request} other {{count} follow requests}}`)(
95 { count: follows.length, followerName: this.buildFollowerName(follows[0]) },
96 $localize`Follow requests rejected`
97 )
98 this.notifier.success(message)
66 99
67 this.reloadData() 100 this.reloadData()
68 }, 101 },
69 102
70 error: err => { 103 error: err => this.notifier.error(err.message)
71 follow.state = 'pending'
72 this.notifier.error(err.message)
73 }
74 }) 104 })
75 } 105 }
76 106
77 async deleteFollower (follow: ActorFollow) { 107 async deleteFollowers (follows: ActorFollow[]) {
78 const message = $localize`Do you really want to delete this follower? It will be able to send again another follow request.` 108 let message = $localize`Deleted followers will be able to send again a follow request.`
109 message += '<br /><br />'
110
111 // eslint-disable-next-line max-len
112 message += prepareIcu($localize`Do you really want to delete {count, plural, =1 {{followerName} follow request?} other {{count} follow requests?}}`)(
113 { count: follows.length, followerName: this.buildFollowerName(follows[0]) },
114 $localize`Do you really want to delete these follow requests?`
115 )
116
79 const res = await this.confirmService.confirm(message, $localize`Delete`) 117 const res = await this.confirmService.confirm(message, $localize`Delete`)
80 if (res === false) return 118 if (res === false) return
81 119
82 this.followService.removeFollower(follow) 120 this.followService.removeFollower(follows)
83 .subscribe({ 121 .subscribe({
84 next: () => { 122 next: () => {
85 const handle = follow.follower.name + '@' + follow.follower.host 123 // eslint-disable-next-line max-len
86 this.notifier.success($localize`${handle} removed from instance followers`) 124 const message = prepareIcu($localize`Removed {count, plural, =1 {{followerName} follow request} other {{count} follow requests}}`)(
125 { count: follows.length, followerName: this.buildFollowerName(follows[0]) },
126 $localize`Follow requests removed`
127 )
128
129 this.notifier.success(message)
87 130
88 this.reloadData() 131 this.reloadData()
89 }, 132 },
@@ -92,6 +135,14 @@ export class FollowersListComponent extends RestTable implements OnInit {
92 }) 135 })
93 } 136 }
94 137
138 buildFollowerName (follow: ActorFollow) {
139 return follow.follower.name + '@' + follow.follower.host
140 }
141
142 isInSelectionMode () {
143 return this.selectedFollows.length !== 0
144 }
145
95 protected reloadData () { 146 protected reloadData () {
96 this.followService.getFollowers({ pagination: this.pagination, sort: this.sort, search: this.search }) 147 this.followService.getFollowers({ pagination: this.pagination, sort: this.sort, search: this.search })
97 .subscribe({ 148 .subscribe({