aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src/app/shared
diff options
context:
space:
mode:
Diffstat (limited to 'client/src/app/shared')
-rw-r--r--client/src/app/shared/shared-main/account/actor.model.ts8
-rw-r--r--client/src/app/shared/shared-moderation/blocklist.service.ts15
-rw-r--r--client/src/app/shared/shared-moderation/user-ban-modal.component.html17
-rw-r--r--client/src/app/shared/shared-moderation/user-ban-modal.component.scss5
-rw-r--r--client/src/app/shared/shared-moderation/user-ban-modal.component.ts41
-rw-r--r--client/src/app/shared/shared-moderation/user-moderation-dropdown.component.ts46
6 files changed, 79 insertions, 53 deletions
diff --git a/client/src/app/shared/shared-main/account/actor.model.ts b/client/src/app/shared/shared-main/account/actor.model.ts
index a54f51aa4..bd693860d 100644
--- a/client/src/app/shared/shared-main/account/actor.model.ts
+++ b/client/src/app/shared/shared-main/account/actor.model.ts
@@ -1,4 +1,4 @@
1import { getAbsoluteAPIUrl } from '@app/helpers' 1import { getAbsoluteAPIUrl, getAPIHost } from '@app/helpers'
2import { Actor as ServerActor, ActorImage } from '@shared/models' 2import { Actor as ServerActor, ActorImage } from '@shared/models'
3 3
4export abstract class Actor implements ServerActor { 4export abstract class Actor implements ServerActor {
@@ -32,8 +32,7 @@ export abstract class Actor implements ServerActor {
32 } 32 }
33 33
34 static CREATE_BY_STRING (accountName: string, host: string, forceHostname = false) { 34 static CREATE_BY_STRING (accountName: string, host: string, forceHostname = false) {
35 const absoluteAPIUrl = getAbsoluteAPIUrl() 35 const thisHost = getAPIHost()
36 const thisHost = new URL(absoluteAPIUrl).host
37 36
38 if (host.trim() === thisHost && !forceHostname) return accountName 37 if (host.trim() === thisHost && !forceHostname) return accountName
39 38
@@ -41,8 +40,7 @@ export abstract class Actor implements ServerActor {
41 } 40 }
42 41
43 static IS_LOCAL (host: string) { 42 static IS_LOCAL (host: string) {
44 const absoluteAPIUrl = getAbsoluteAPIUrl() 43 const thisHost = getAPIHost()
45 const thisHost = new URL(absoluteAPIUrl).host
46 44
47 return host.trim() === thisHost 45 return host.trim() === thisHost
48 } 46 }
diff --git a/client/src/app/shared/shared-moderation/blocklist.service.ts b/client/src/app/shared/shared-moderation/blocklist.service.ts
index f4836c6c4..3e92c2831 100644
--- a/client/src/app/shared/shared-moderation/blocklist.service.ts
+++ b/client/src/app/shared/shared-moderation/blocklist.service.ts
@@ -1,5 +1,6 @@
1import { SortMeta } from 'primeng/api' 1import { SortMeta } from 'primeng/api'
2import { catchError, map } from 'rxjs/operators' 2import { from } from 'rxjs'
3import { catchError, concatMap, map, toArray } from 'rxjs/operators'
3import { HttpClient, HttpParams } from '@angular/common/http' 4import { HttpClient, HttpParams } from '@angular/common/http'
4import { Injectable } from '@angular/core' 5import { Injectable } from '@angular/core'
5import { RestExtractor, RestPagination, RestService } from '@app/core' 6import { RestExtractor, RestPagination, RestService } from '@app/core'
@@ -120,11 +121,15 @@ export class BlocklistService {
120 ) 121 )
121 } 122 }
122 123
123 blockAccountByInstance (account: Pick<Account, 'nameWithHost'>) { 124 blockAccountByInstance (accountsArg: Pick<Account, 'nameWithHost'> | Pick<Account, 'nameWithHost'>[]) {
124 const body = { accountName: account.nameWithHost } 125 const accounts = Array.isArray(accountsArg) ? accountsArg : [ accountsArg ]
125 126
126 return this.authHttp.post(BlocklistService.BASE_SERVER_BLOCKLIST_URL + '/accounts', body) 127 return from(accounts)
127 .pipe(catchError(err => this.restExtractor.handleError(err))) 128 .pipe(
129 concatMap(a => this.authHttp.post(BlocklistService.BASE_SERVER_BLOCKLIST_URL + '/accounts', { accountName: a.nameWithHost })),
130 toArray(),
131 catchError(err => this.restExtractor.handleError(err))
132 )
128 } 133 }
129 134
130 unblockAccountByInstance (account: Pick<Account, 'nameWithHost'>) { 135 unblockAccountByInstance (account: Pick<Account, 'nameWithHost'>) {
diff --git a/client/src/app/shared/shared-moderation/user-ban-modal.component.html b/client/src/app/shared/shared-moderation/user-ban-modal.component.html
index 6c83cc9cc..2b6726bdc 100644
--- a/client/src/app/shared/shared-moderation/user-ban-modal.component.html
+++ b/client/src/app/shared/shared-moderation/user-ban-modal.component.html
@@ -1,11 +1,15 @@
1<ng-template #modal> 1<ng-template #modal>
2 <div class="modal-header"> 2 <div class="modal-header">
3 <h4 i18n class="modal-title">Ban</h4> 3 <h4 i18n class="modal-title">{{ getModalTitle() }}</h4>
4 4
5 <my-global-icon iconName="cross" aria-label="Close" role="button" (click)="hide()"></my-global-icon> 5 <my-global-icon iconName="cross" aria-label="Close" role="button" (click)="hide()"></my-global-icon>
6 </div> 6 </div>
7 7
8 <div class="modal-body"> 8 <div class="modal-body">
9 <div class="description" i18n>
10 A banned user will no longer be able to login.
11 </div>
12
9 <form novalidate [formGroup]="form" (ngSubmit)="banUser()"> 13 <form novalidate [formGroup]="form" (ngSubmit)="banUser()">
10 <div class="form-group"> 14 <div class="form-group">
11 <textarea 15 <textarea
@@ -17,15 +21,12 @@
17 </div> 21 </div>
18 </div> 22 </div>
19 23
20 <div i18n>
21 A banned user will no longer be able to login.
22 </div>
23
24 <div class="form-group"> 24 <div class="form-group">
25 <my-peertube-checkbox 25 <my-peertube-checkbox
26 inputName="banMute" formControlName="mute" 26 inputName="banMute" formControlName="mute"
27 i18n-labelText labelText="Mute this account" 27 i18n-labelText labelText="Mute to also hide videos/comments"
28 ></my-peertube-checkbox> 28 >
29 </my-peertube-checkbox>
29 </div> 30 </div>
30 31
31 <div class="form-group inputs"> 32 <div class="form-group inputs">
@@ -34,7 +35,7 @@
34 (click)="hide()" (key.enter)="hide()" 35 (click)="hide()" (key.enter)="hide()"
35 > 36 >
36 37
37 <input type="submit" i18n-value [value]="modalMessage" class="peertube-button orange-button" [disabled]="!form.valid" /> 38 <input type="submit" i18n-value [value]="getModalTitle()" class="peertube-button orange-button" [disabled]="!form.valid" />
38 </div> 39 </div>
39 </form> 40 </form>
40 </div> 41 </div>
diff --git a/client/src/app/shared/shared-moderation/user-ban-modal.component.scss b/client/src/app/shared/shared-moderation/user-ban-modal.component.scss
index 08e072d8f..2c46c3d03 100644
--- a/client/src/app/shared/shared-moderation/user-ban-modal.component.scss
+++ b/client/src/app/shared/shared-moderation/user-ban-modal.component.scss
@@ -1,6 +1,11 @@
1@use '_variables' as *; 1@use '_variables' as *;
2@use '_mixins' as *; 2@use '_mixins' as *;
3 3
4.description {
5 font-size: 15px;
6 margin-bottom: 15px;
7}
8
4textarea { 9textarea {
5 @include peertube-textarea(100%, 60px); 10 @include peertube-textarea(100%, 60px);
6} 11}
diff --git a/client/src/app/shared/shared-moderation/user-ban-modal.component.ts b/client/src/app/shared/shared-moderation/user-ban-modal.component.ts
index b3f03990f..9edfac388 100644
--- a/client/src/app/shared/shared-moderation/user-ban-modal.component.ts
+++ b/client/src/app/shared/shared-moderation/user-ban-modal.component.ts
@@ -1,3 +1,4 @@
1import { forkJoin } from 'rxjs'
1import { Component, EventEmitter, OnInit, Output, ViewChild } from '@angular/core' 2import { Component, EventEmitter, OnInit, Output, ViewChild } from '@angular/core'
2import { Notifier } from '@app/core' 3import { Notifier } from '@app/core'
3import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' 4import { FormReactive, FormValidatorService } from '@app/shared/shared-forms'
@@ -42,9 +43,6 @@ export class UserBanModalComponent extends FormReactive implements OnInit {
42 openModal (user: User | User[]) { 43 openModal (user: User | User[]) {
43 this.usersToBan = user 44 this.usersToBan = user
44 this.openedModal = this.modalService.open(this.modal, { centered: true }) 45 this.openedModal = this.modalService.open(this.modal, { centered: true })
45
46 const isSingleUser = !(Array.isArray(this.usersToBan) && this.usersToBan.length > 1)
47 this.modalMessage = isSingleUser ? $localize`Ban this user` : $localize`Ban these users`
48 } 46 }
49 47
50 hide () { 48 hide () {
@@ -56,7 +54,13 @@ export class UserBanModalComponent extends FormReactive implements OnInit {
56 const reason = this.form.value['reason'] || undefined 54 const reason = this.form.value['reason'] || undefined
57 const mute = this.form.value['mute'] 55 const mute = this.form.value['mute']
58 56
59 this.userAdminService.banUsers(this.usersToBan, reason) 57 const observables = [
58 this.userAdminService.banUsers(this.usersToBan, reason)
59 ]
60
61 if (mute) observables.push(this.muteAccounts())
62
63 forkJoin(observables)
60 .subscribe({ 64 .subscribe({
61 next: () => { 65 next: () => {
62 const message = Array.isArray(this.usersToBan) 66 const message = Array.isArray(this.usersToBan)
@@ -67,22 +71,6 @@ export class UserBanModalComponent extends FormReactive implements OnInit {
67 71
68 this.userBanned.emit(this.usersToBan) 72 this.userBanned.emit(this.usersToBan)
69 73
70 if (mute) {
71 const users = Array.isArray(this.usersToBan) ? this.usersToBan : [ this.usersToBan ]
72 users.forEach(user => {
73 const account = new Account(user.account)
74 this.blocklistService.blockAccountByInstance(account)
75 .subscribe({
76 next: () => {
77 this.notifier.success($localize`Account ${user.username} muted by the instance.`)
78 account.mutedByInstance = true
79 },
80
81 error: err => this.notifier.error(err.message)
82 })
83 })
84 }
85
86 this.hide() 74 this.hide()
87 }, 75 },
88 76
@@ -90,4 +78,17 @@ export class UserBanModalComponent extends FormReactive implements OnInit {
90 }) 78 })
91 } 79 }
92 80
81 getModalTitle () {
82 if (Array.isArray(this.usersToBan)) return $localize`Ban ${this.usersToBan.length} users`
83
84 return $localize`Ban "${this.usersToBan.username}"`
85 }
86
87 private muteAccounts () {
88 const accounts = Array.isArray(this.usersToBan)
89 ? this.usersToBan.map(u => new Account(u.account))
90 : new Account(this.usersToBan.account)
91
92 return this.blocklistService.blockAccountByInstance(accounts)
93 }
93} 94}
diff --git a/client/src/app/shared/shared-moderation/user-moderation-dropdown.component.ts b/client/src/app/shared/shared-moderation/user-moderation-dropdown.component.ts
index 0d19565ef..787318c2c 100644
--- a/client/src/app/shared/shared-moderation/user-moderation-dropdown.component.ts
+++ b/client/src/app/shared/shared-moderation/user-moderation-dropdown.component.ts
@@ -7,6 +7,16 @@ import { BlocklistService } from './blocklist.service'
7import { BulkService } from './bulk.service' 7import { BulkService } from './bulk.service'
8import { UserBanModalComponent } from './user-ban-modal.component' 8import { UserBanModalComponent } from './user-ban-modal.component'
9 9
10export type AccountMutedStatus =
11 Pick<Account, 'id' | 'nameWithHost' | 'host' | 'userId' |
12 'mutedByInstance' | 'mutedByUser' | 'mutedServerByInstance' | 'mutedServerByUser'>
13
14export type UserModerationDisplayType = {
15 myAccount?: boolean
16 instanceAccount?: boolean
17 instanceUser?: boolean
18}
19
10@Component({ 20@Component({
11 selector: 'my-user-moderation-dropdown', 21 selector: 'my-user-moderation-dropdown',
12 templateUrl: './user-moderation-dropdown.component.html' 22 templateUrl: './user-moderation-dropdown.component.html'
@@ -15,8 +25,8 @@ export class UserModerationDropdownComponent implements OnInit, OnChanges {
15 @ViewChild('userBanModal') userBanModal: UserBanModalComponent 25 @ViewChild('userBanModal') userBanModal: UserBanModalComponent
16 26
17 @Input() user: User 27 @Input() user: User
18 @Input() account: Account 28 @Input() account: AccountMutedStatus
19 @Input() prependActions: DropdownAction<{ user: User, account: Account }>[] 29 @Input() prependActions: DropdownAction<{ user: User, account: AccountMutedStatus }>[]
20 30
21 @Input() buttonSize: 'normal' | 'small' = 'normal' 31 @Input() buttonSize: 'normal' | 'small' = 'normal'
22 @Input() buttonStyled = true 32 @Input() buttonStyled = true
@@ -24,10 +34,16 @@ export class UserModerationDropdownComponent implements OnInit, OnChanges {
24 @Input() label: string 34 @Input() label: string
25 @Input() container: 'body' | undefined = undefined 35 @Input() container: 'body' | undefined = undefined
26 36
37 @Input() displayOptions: UserModerationDisplayType = {
38 myAccount: true,
39 instanceAccount: true,
40 instanceUser: true
41 }
42
27 @Output() userChanged = new EventEmitter() 43 @Output() userChanged = new EventEmitter()
28 @Output() userDeleted = new EventEmitter() 44 @Output() userDeleted = new EventEmitter()
29 45
30 userActions: DropdownAction<{ user: User, account: Account }>[][] = [] 46 userActions: DropdownAction<{ user: User, account: AccountMutedStatus }>[][] = []
31 47
32 requiresEmailVerification = false 48 requiresEmailVerification = false
33 49
@@ -111,7 +127,7 @@ export class UserModerationDropdownComponent implements OnInit, OnChanges {
111 }) 127 })
112 } 128 }
113 129
114 blockAccountByUser (account: Account) { 130 blockAccountByUser (account: AccountMutedStatus) {
115 this.blocklistService.blockAccountByUser(account) 131 this.blocklistService.blockAccountByUser(account)
116 .subscribe({ 132 .subscribe({
117 next: () => { 133 next: () => {
@@ -125,7 +141,7 @@ export class UserModerationDropdownComponent implements OnInit, OnChanges {
125 }) 141 })
126 } 142 }
127 143
128 unblockAccountByUser (account: Account) { 144 unblockAccountByUser (account: AccountMutedStatus) {
129 this.blocklistService.unblockAccountByUser(account) 145 this.blocklistService.unblockAccountByUser(account)
130 .subscribe({ 146 .subscribe({
131 next: () => { 147 next: () => {
@@ -167,7 +183,7 @@ export class UserModerationDropdownComponent implements OnInit, OnChanges {
167 }) 183 })
168 } 184 }
169 185
170 blockAccountByInstance (account: Account) { 186 blockAccountByInstance (account: AccountMutedStatus) {
171 this.blocklistService.blockAccountByInstance(account) 187 this.blocklistService.blockAccountByInstance(account)
172 .subscribe({ 188 .subscribe({
173 next: () => { 189 next: () => {
@@ -181,7 +197,7 @@ export class UserModerationDropdownComponent implements OnInit, OnChanges {
181 }) 197 })
182 } 198 }
183 199
184 unblockAccountByInstance (account: Account) { 200 unblockAccountByInstance (account: AccountMutedStatus) {
185 this.blocklistService.unblockAccountByInstance(account) 201 this.blocklistService.unblockAccountByInstance(account)
186 .subscribe({ 202 .subscribe({
187 next: () => { 203 next: () => {
@@ -246,7 +262,7 @@ export class UserModerationDropdownComponent implements OnInit, OnChanges {
246 return user && this.authService.getUser().id === user.id 262 return user && this.authService.getUser().id === user.id
247 } 263 }
248 264
249 private isMyAccount (account: Account) { 265 private isMyAccount (account: AccountMutedStatus) {
250 return account && this.authService.getUser().account.id === account.id 266 return account && this.authService.getUser().account.id === account.id
251 } 267 }
252 268
@@ -267,9 +283,9 @@ export class UserModerationDropdownComponent implements OnInit, OnChanges {
267 } 283 }
268 284
269 private buildMyAccountModerationActions () { 285 private buildMyAccountModerationActions () {
270 if (!this.account || !this.authService.isLoggedIn()) return [] 286 if (!this.account || !this.displayOptions.myAccount || !this.authService.isLoggedIn()) return []
271 287
272 const myAccountActions: DropdownAction<{ user: User, account: Account }>[] = [ 288 const myAccountActions: DropdownAction<{ user: User, account: AccountMutedStatus }>[] = [
273 { 289 {
274 label: $localize`My account moderation`, 290 label: $localize`My account moderation`,
275 class: [ 'red' ], 291 class: [ 'red' ],
@@ -315,9 +331,9 @@ export class UserModerationDropdownComponent implements OnInit, OnChanges {
315 331
316 const authUser = this.authService.getUser() 332 const authUser = this.authService.getUser()
317 333
318 let instanceActions: DropdownAction<{ user: User, account: Account }>[] = [] 334 let instanceActions: DropdownAction<{ user: User, account: AccountMutedStatus }>[] = []
319 335
320 if (this.user && authUser.hasRight(UserRight.MANAGE_USERS) && authUser.canManage(this.user)) { 336 if (this.user && this.displayOptions.instanceUser && authUser.hasRight(UserRight.MANAGE_USERS) && authUser.canManage(this.user)) {
321 instanceActions = instanceActions.concat([ 337 instanceActions = instanceActions.concat([
322 { 338 {
323 label: $localize`Edit user`, 339 label: $localize`Edit user`,
@@ -351,7 +367,7 @@ export class UserModerationDropdownComponent implements OnInit, OnChanges {
351 } 367 }
352 368
353 // Instance actions on account blocklists 369 // Instance actions on account blocklists
354 if (this.account && authUser.hasRight(UserRight.MANAGE_ACCOUNTS_BLOCKLIST)) { 370 if (this.account && this.displayOptions.instanceAccount && authUser.hasRight(UserRight.MANAGE_ACCOUNTS_BLOCKLIST)) {
355 instanceActions = instanceActions.concat([ 371 instanceActions = instanceActions.concat([
356 { 372 {
357 label: $localize`Mute this account`, 373 label: $localize`Mute this account`,
@@ -369,7 +385,7 @@ export class UserModerationDropdownComponent implements OnInit, OnChanges {
369 } 385 }
370 386
371 // Instance actions on server blocklists 387 // Instance actions on server blocklists
372 if (this.account && authUser.hasRight(UserRight.MANAGE_SERVERS_BLOCKLIST)) { 388 if (this.account && this.displayOptions.instanceAccount && authUser.hasRight(UserRight.MANAGE_SERVERS_BLOCKLIST)) {
373 instanceActions = instanceActions.concat([ 389 instanceActions = instanceActions.concat([
374 { 390 {
375 label: $localize`Mute the instance`, 391 label: $localize`Mute the instance`,
@@ -386,7 +402,7 @@ export class UserModerationDropdownComponent implements OnInit, OnChanges {
386 ]) 402 ])
387 } 403 }
388 404
389 if (this.account && authUser.hasRight(UserRight.REMOVE_ANY_VIDEO_COMMENT)) { 405 if (this.account && this.displayOptions.instanceAccount && authUser.hasRight(UserRight.REMOVE_ANY_VIDEO_COMMENT)) {
390 instanceActions = instanceActions.concat([ 406 instanceActions = instanceActions.concat([
391 { 407 {
392 label: $localize`Remove comments from your instance`, 408 label: $localize`Remove comments from your instance`,