From 228393302d98136d4dc35c5f197edc8cebd5d64f Mon Sep 17 00:00:00 2001 From: Rigel Kent Date: Mon, 15 Jun 2020 13:18:22 +0200 Subject: factorize account/server blocklists for users and instance (#2875) --- .../blocklist/account-blocklist.component.html | 64 +++++++++++++ .../blocklist/account-blocklist.component.scss | 16 ++++ .../blocklist/account-blocklist.component.ts | 79 ++++++++++++++++ .../src/app/shared/blocklist/blocklist.service.ts | 18 +++- client/src/app/shared/blocklist/index.ts | 2 + .../blocklist/server-blocklist.component.html | 59 ++++++++++++ .../blocklist/server-blocklist.component.scss | 34 +++++++ .../shared/blocklist/server-blocklist.component.ts | 101 +++++++++++++++++++++ client/src/app/shared/shared.module.ts | 7 +- 9 files changed, 374 insertions(+), 6 deletions(-) create mode 100644 client/src/app/shared/blocklist/account-blocklist.component.html create mode 100644 client/src/app/shared/blocklist/account-blocklist.component.scss create mode 100644 client/src/app/shared/blocklist/account-blocklist.component.ts create mode 100644 client/src/app/shared/blocklist/server-blocklist.component.html create mode 100644 client/src/app/shared/blocklist/server-blocklist.component.scss create mode 100644 client/src/app/shared/blocklist/server-blocklist.component.ts (limited to 'client/src/app/shared') diff --git a/client/src/app/shared/blocklist/account-blocklist.component.html b/client/src/app/shared/blocklist/account-blocklist.component.html new file mode 100644 index 000000000..486785f35 --- /dev/null +++ b/client/src/app/shared/blocklist/account-blocklist.component.html @@ -0,0 +1,64 @@ + + +
+
+ + + Clear filters +
+
+
+ + + + Account + Muted at + + + + + + + + +
+ Avatar +
+ {{ accountBlock.blockedAccount.displayName }} + {{ accountBlock.blockedAccount.nameWithHost }} +
+
+
+ + + {{ accountBlock.createdAt | date: 'short' }} + + + + +
+ + + + +
+ No account found matching current filters. + No account found. +
+ + +
+
diff --git a/client/src/app/shared/blocklist/account-blocklist.component.scss b/client/src/app/shared/blocklist/account-blocklist.component.scss new file mode 100644 index 000000000..aa8363ff4 --- /dev/null +++ b/client/src/app/shared/blocklist/account-blocklist.component.scss @@ -0,0 +1,16 @@ +@import '_variables'; +@import '_mixins'; + +.caption { + justify-content: flex-end; + + input { + @include peertube-input-text(250px); + flex-grow: 1; + } +} + +.unblock-button { + @include peertube-button; + @include grey-button; +} \ No newline at end of file diff --git a/client/src/app/shared/blocklist/account-blocklist.component.ts b/client/src/app/shared/blocklist/account-blocklist.component.ts new file mode 100644 index 000000000..dc5ac4044 --- /dev/null +++ b/client/src/app/shared/blocklist/account-blocklist.component.ts @@ -0,0 +1,79 @@ +import { OnInit } from '@angular/core' +import { Notifier } from '@app/core' +import { I18n } from '@ngx-translate/i18n-polyfill' +import { RestPagination, RestTable } from '@app/shared/rest' +import { SortMeta } from 'primeng/api' +import { AccountBlock } from './account-block.model' +import { BlocklistService, BlocklistComponentType } from './blocklist.service' +import { Actor } from '@app/shared/actor/actor.model' + +export class GenericAccountBlocklistComponent extends RestTable implements OnInit { + // @ts-ignore: "Abstract methods can only appear within an abstract class" + abstract mode: BlocklistComponentType + + blockedAccounts: AccountBlock[] = [] + totalRecords = 0 + sort: SortMeta = { field: 'createdAt', order: -1 } + pagination: RestPagination = { count: this.rowsPerPage, start: 0 } + + constructor ( + private notifier: Notifier, + private blocklistService: BlocklistService, + private i18n: I18n + ) { + super() + } + + // @ts-ignore: "Abstract methods can only appear within an abstract class" + abstract getIdentifier (): string + + ngOnInit () { + this.initialize() + } + + switchToDefaultAvatar ($event: Event) { + ($event.target as HTMLImageElement).src = Actor.GET_DEFAULT_AVATAR_URL() + } + + unblockAccount (accountBlock: AccountBlock) { + const blockedAccount = accountBlock.blockedAccount + const operation = this.mode === BlocklistComponentType.Account + ? this.blocklistService.unblockAccountByUser(blockedAccount) + : this.blocklistService.unblockAccountByInstance(blockedAccount) + + operation.subscribe( + () => { + this.notifier.success( + this.mode === BlocklistComponentType.Account + ? this.i18n('Account {{nameWithHost}} unmuted.', { nameWithHost: blockedAccount.nameWithHost }) + : this.i18n('Account {{nameWithHost}} unmuted by your instance.', { nameWithHost: blockedAccount.nameWithHost }) + ) + + this.loadData() + } + ) + } + + protected loadData () { + const operation = this.mode === BlocklistComponentType.Account + ? this.blocklistService.getUserAccountBlocklist({ + pagination: this.pagination, + sort: this.sort, + search: this.search + }) + : this.blocklistService.getInstanceAccountBlocklist({ + pagination: this.pagination, + sort: this.sort, + search: this.search + }) + + return operation.subscribe( + resultList => { + this.blockedAccounts = resultList.data + this.totalRecords = resultList.total + }, + + err => this.notifier.error(err.message) + ) + } +} diff --git a/client/src/app/shared/blocklist/blocklist.service.ts b/client/src/app/shared/blocklist/blocklist.service.ts index 5cf265bc1..c70a8173a 100644 --- a/client/src/app/shared/blocklist/blocklist.service.ts +++ b/client/src/app/shared/blocklist/blocklist.service.ts @@ -8,6 +8,8 @@ import { AccountBlock as AccountBlockServer, ResultList, ServerBlock } from '../ import { Account } from '@app/shared/account/account.model' import { AccountBlock } from '@app/shared/blocklist/account-block.model' +export enum BlocklistComponentType { Account, Instance } + @Injectable() export class BlocklistService { static BASE_USER_BLOCKLIST_URL = environment.apiUrl + '/api/v1/users/me/blocklist' @@ -21,10 +23,14 @@ export class BlocklistService { /*********************** User -> Account blocklist ***********************/ - getUserAccountBlocklist (pagination: RestPagination, sort: SortMeta) { + getUserAccountBlocklist (options: { pagination: RestPagination, sort: SortMeta, search?: string }) { + const { pagination, sort, search } = options + let params = new HttpParams() params = this.restService.addRestGetParams(params, pagination, sort) + if (search) params = params.append('search', search) + return this.authHttp.get>(BlocklistService.BASE_USER_BLOCKLIST_URL + '/accounts', { params }) .pipe( map(res => this.restExtractor.convertResultListDateToHuman(res)), @@ -49,10 +55,14 @@ export class BlocklistService { /*********************** User -> Server blocklist ***********************/ - getUserServerBlocklist (pagination: RestPagination, sort: SortMeta) { + getUserServerBlocklist (options: { pagination: RestPagination, sort: SortMeta, search?: string }) { + const { pagination, sort, search } = options + let params = new HttpParams() params = this.restService.addRestGetParams(params, pagination, sort) + if (search) params = params.append('search', search) + return this.authHttp.get>(BlocklistService.BASE_USER_BLOCKLIST_URL + '/servers', { params }) .pipe( map(res => this.restExtractor.convertResultListDateToHuman(res)), @@ -76,7 +86,7 @@ export class BlocklistService { /*********************** Instance -> Account blocklist ***********************/ - getInstanceAccountBlocklist (options: { pagination: RestPagination, sort: SortMeta, search: string }) { + getInstanceAccountBlocklist (options: { pagination: RestPagination, sort: SortMeta, search?: string }) { const { pagination, sort, search } = options let params = new HttpParams() @@ -108,7 +118,7 @@ export class BlocklistService { /*********************** Instance -> Server blocklist ***********************/ - getInstanceServerBlocklist (options: { pagination: RestPagination, sort: SortMeta, search: string }) { + getInstanceServerBlocklist (options: { pagination: RestPagination, sort: SortMeta, search?: string }) { const { pagination, sort, search } = options let params = new HttpParams() diff --git a/client/src/app/shared/blocklist/index.ts b/client/src/app/shared/blocklist/index.ts index 5886ca07e..188057b19 100644 --- a/client/src/app/shared/blocklist/index.ts +++ b/client/src/app/shared/blocklist/index.ts @@ -1,2 +1,4 @@ export * from './blocklist.service' export * from './account-block.model' +export * from './server-blocklist.component' +export * from './account-blocklist.component' diff --git a/client/src/app/shared/blocklist/server-blocklist.component.html b/client/src/app/shared/blocklist/server-blocklist.component.html new file mode 100644 index 000000000..977e0e141 --- /dev/null +++ b/client/src/app/shared/blocklist/server-blocklist.component.html @@ -0,0 +1,59 @@ + + +
+
+ + + Clear filters +
+ + + Mute domain + +
+
+ + + + Instance + Muted at + + + + + + + + + {{ serverBlock.blockedServer.host }} + + + + {{ serverBlock.createdAt | date: 'short' }} + + + + + + + + + +
+ No server found matching current filters. + No server found. +
+ + +
+
+ + diff --git a/client/src/app/shared/blocklist/server-blocklist.component.scss b/client/src/app/shared/blocklist/server-blocklist.component.scss new file mode 100644 index 000000000..9ddb76850 --- /dev/null +++ b/client/src/app/shared/blocklist/server-blocklist.component.scss @@ -0,0 +1,34 @@ +@import '_variables'; +@import '_mixins'; + +a { + @include disable-default-a-behaviour; + display: inline-block; + + &, &:hover { + color: pvar(--mainForegroundColor); + } + + span { + font-size: 80%; + color: pvar(--inputPlaceholderColor); + } +} + +.caption { + justify-content: flex-end; + + input { + @include peertube-input-text(250px); + flex-grow: 1; + } +} + +.unblock-button { + @include peertube-button; + @include grey-button; +} + +.block-button { + @include create-button; +} diff --git a/client/src/app/shared/blocklist/server-blocklist.component.ts b/client/src/app/shared/blocklist/server-blocklist.component.ts new file mode 100644 index 000000000..f2b36badc --- /dev/null +++ b/client/src/app/shared/blocklist/server-blocklist.component.ts @@ -0,0 +1,101 @@ +import { OnInit, ViewChild } from '@angular/core' +import { Notifier } from '@app/core' +import { I18n } from '@ngx-translate/i18n-polyfill' +import { RestPagination, RestTable } from '@app/shared/rest' +import { SortMeta } from 'primeng/api' +import { BlocklistService, BlocklistComponentType } from './blocklist.service' +import { ServerBlock } from '../../../../../shared/models/blocklist/server-block.model' +import { BatchDomainsModalComponent } from '@app/+admin/config/shared/batch-domains-modal.component' + +export class GenericServerBlocklistComponent extends RestTable implements OnInit { + @ViewChild('batchDomainsModal') batchDomainsModal: BatchDomainsModalComponent + + // @ts-ignore: "Abstract methods can only appear within an abstract class" + public abstract mode: BlocklistComponentType + + blockedServers: ServerBlock[] = [] + totalRecords = 0 + sort: SortMeta = { field: 'createdAt', order: -1 } + pagination: RestPagination = { count: this.rowsPerPage, start: 0 } + + constructor ( + protected notifier: Notifier, + protected blocklistService: BlocklistService, + protected i18n: I18n + ) { + super() + } + + ngOnInit () { + this.initialize() + } + + // @ts-ignore: "Abstract methods can only appear within an abstract class" + public abstract getIdentifier (): string + + unblockServer (serverBlock: ServerBlock) { + const operation = (host: string) => this.mode === BlocklistComponentType.Account + ? this.blocklistService.unblockServerByUser(host) + : this.blocklistService.unblockServerByInstance(host) + const host = serverBlock.blockedServer.host + + operation(host).subscribe( + () => { + this.notifier.success( + this.mode === BlocklistComponentType.Account + ? this.i18n('Instance {{host}} unmuted.', { host }) + : this.i18n('Instance {{host}} unmuted by your instance.', { host }) + ) + + this.loadData() + } + ) + } + + addServersToBlock () { + this.batchDomainsModal.openModal() + } + + onDomainsToBlock (domains: string[]) { + const operation = (domain: string) => this.mode === BlocklistComponentType.Account + ? this.blocklistService.blockServerByUser(domain) + : this.blocklistService.blockServerByInstance(domain) + + domains.forEach(domain => { + operation(domain).subscribe( + () => { + this.notifier.success( + this.mode === BlocklistComponentType.Account + ? this.i18n('Instance {{domain}} muted.', { domain }) + : this.i18n('Instance {{domain}} muted by your instance.', { domain }) + ) + + this.loadData() + } + ) + }) + } + + protected loadData () { + const operation = this.mode === BlocklistComponentType.Account + ? this.blocklistService.getUserServerBlocklist({ + pagination: this.pagination, + sort: this.sort, + search: this.search + }) + : this.blocklistService.getInstanceServerBlocklist({ + pagination: this.pagination, + sort: this.sort, + search: this.search + }) + + return operation.subscribe( + resultList => { + this.blockedServers = resultList.data + this.totalRecords = resultList.total + }, + + err => this.notifier.error(err.message) + ) + } +} diff --git a/client/src/app/shared/shared.module.ts b/client/src/app/shared/shared.module.ts index 2035097d7..98fab9e16 100644 --- a/client/src/app/shared/shared.module.ts +++ b/client/src/app/shared/shared.module.ts @@ -10,6 +10,7 @@ import { NgModule } from '@angular/core' import { FormsModule, ReactiveFormsModule } from '@angular/forms' import { RouterModule } from '@angular/router' import { BatchDomainsValidatorsService } from '@app/+admin/config/shared/batch-domains-validators.service' +import { BatchDomainsModalComponent } from '@app/+admin/config/shared/batch-domains-modal.component' import { MyAccountInterfaceSettingsComponent } from '@app/+my-account/my-account-settings/my-account-interface' import { MyAccountVideoSettingsComponent } from '@app/+my-account/my-account-settings/my-account-video-settings' import { ActorAvatarInfoComponent } from '@app/+my-account/shared/actor-avatar-info.component' @@ -192,7 +193,8 @@ import { VideoService } from './video/video.service' MyAccountVideoSettingsComponent, MyAccountInterfaceSettingsComponent, - ActorAvatarInfoComponent + ActorAvatarInfoComponent, + BatchDomainsModalComponent ], exports: [ @@ -274,7 +276,8 @@ import { VideoService } from './video/video.service' MyAccountVideoSettingsComponent, MyAccountInterfaceSettingsComponent, - ActorAvatarInfoComponent + ActorAvatarInfoComponent, + BatchDomainsModalComponent ], providers: [ -- cgit v1.2.3