aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src/app/shared/shared-moderation
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2020-07-27 11:40:30 +0200
committerChocobozzz <chocobozzz@cpy.re>2020-07-31 11:35:19 +0200
commit94148c9028829b5576a5dcbfba2c7fb9cf6443d3 (patch)
tree2774f272329111abd03e8441ff936da11fb1a3f3 /client/src/app/shared/shared-moderation
parent441e453ae53e491b09c9b09b00b041788176ce64 (diff)
downloadPeerTube-94148c9028829b5576a5dcbfba2c7fb9cf6443d3.tar.gz
PeerTube-94148c9028829b5576a5dcbfba2c7fb9cf6443d3.tar.zst
PeerTube-94148c9028829b5576a5dcbfba2c7fb9cf6443d3.zip
Add abuse messages management in my account
Diffstat (limited to 'client/src/app/shared/shared-moderation')
-rw-r--r--client/src/app/shared/shared-moderation/abuse-message-modal.component.html40
-rw-r--r--client/src/app/shared/shared-moderation/abuse-message-modal.component.scss57
-rw-r--r--client/src/app/shared/shared-moderation/abuse-message-modal.component.ts115
-rw-r--r--client/src/app/shared/shared-moderation/abuse.service.ts93
-rw-r--r--client/src/app/shared/shared-moderation/index.ts1
-rw-r--r--client/src/app/shared/shared-moderation/moderation.scss50
-rw-r--r--client/src/app/shared/shared-moderation/server-blocklist.component.scss13
-rw-r--r--client/src/app/shared/shared-moderation/shared-moderation.module.ts7
8 files changed, 129 insertions, 247 deletions
diff --git a/client/src/app/shared/shared-moderation/abuse-message-modal.component.html b/client/src/app/shared/shared-moderation/abuse-message-modal.component.html
deleted file mode 100644
index 67c6a3081..000000000
--- a/client/src/app/shared/shared-moderation/abuse-message-modal.component.html
+++ /dev/null
@@ -1,40 +0,0 @@
1<ng-template #modal>
2 <div class="modal-header">
3 <h4 i18n class="modal-title">Messages</h4>
4
5 <my-global-icon iconName="cross" aria-label="Close" role="button" (click)="hide()"></my-global-icon>
6 </div>
7
8 <div class="modal-body">
9 <div class="messages" #messagesBlock>
10 <div
11 *ngFor="let message of abuseMessages"
12 class="message-block" [ngClass]="{ 'by-moderator': message.byModerator, 'by-me': isMessageByMe(message) }"
13 >
14
15 <div class="author">{{ message.account.name }}</div>
16
17 <div class="bubble">
18 <div class="content">{{ message.message }}</div>
19 <div class="date">{{ message.createdAt | date }}</div>
20 </div>
21 </div>
22 </div>
23
24 <form novalidate [formGroup]="form" (ngSubmit)="addMessage()">
25 <div class="form-group">
26 <textarea formControlName="message" ngbAutofocus [ngClass]="{ 'input-error': formErrors['message'] }" class="form-control"></textarea>
27
28 <div *ngIf="formErrors.message" class="form-error">
29 {{ formErrors.message }}
30 </div>
31 </div>
32
33 <div class="form-group inputs">
34 <input type="submit" i18n-value value="Add message" class="action-button-submit" [disabled]="!form.valid || sendingMessage">
35 </div>
36 </form>
37
38 </div>
39
40</ng-template>
diff --git a/client/src/app/shared/shared-moderation/abuse-message-modal.component.scss b/client/src/app/shared/shared-moderation/abuse-message-modal.component.scss
deleted file mode 100644
index 89d6b88c1..000000000
--- a/client/src/app/shared/shared-moderation/abuse-message-modal.component.scss
+++ /dev/null
@@ -1,57 +0,0 @@
1@import 'variables';
2@import 'mixins';
3
4form {
5 margin: 20px 20px 0 0;
6}
7
8textarea {
9 @include peertube-textarea(100%, 70px);
10
11 margin-top: 20px;
12}
13
14.messages {
15 display: flex;
16 flex-direction: column;
17 overflow-y: scroll;
18 margin-right: 5px;
19}
20
21.message-block {
22 margin-bottom: 10px;
23 max-width: 60%;
24
25 .author {
26 color: var(--greyForegroundColor);
27 font-size: 14px;
28 }
29
30 .bubble {
31 color: var(--mainForegroundColor);
32 background-color: var(--greyBackgroundColor);
33 border-radius: 10px;
34 padding: 5px 10px;
35
36 &.by-me {
37 color: var(--mainForegroundColor);
38 background-color: var(--secondaryColor);
39 }
40
41 &.by-moderator {
42 color: #fff;
43 background-color: var(--mainColor);
44
45 align-self: flex-end;
46 }
47
48 .content {
49 font-size: 15px;
50 }
51
52 .date {
53 font-size: 13px;
54 color: var(--greyForegroundColor);
55 }
56 }
57}
diff --git a/client/src/app/shared/shared-moderation/abuse-message-modal.component.ts b/client/src/app/shared/shared-moderation/abuse-message-modal.component.ts
deleted file mode 100644
index 5822dfe1d..000000000
--- a/client/src/app/shared/shared-moderation/abuse-message-modal.component.ts
+++ /dev/null
@@ -1,115 +0,0 @@
1import { Component, ElementRef, EventEmitter, Output, ViewChild, OnInit } from '@angular/core'
2import { Notifier, AuthService } from '@app/core'
3import { FormReactive, FormValidatorService, AbuseValidatorsService } from '@app/shared/shared-forms'
4import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
5import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref'
6import { I18n } from '@ngx-translate/i18n-polyfill'
7import { AbuseMessage, UserAbuse } from '@shared/models'
8import { AbuseService } from './abuse.service'
9
10@Component({
11 selector: 'my-abuse-message-modal',
12 templateUrl: './abuse-message-modal.component.html',
13 styleUrls: [ './abuse-message-modal.component.scss' ]
14})
15export class AbuseMessageModalComponent extends FormReactive implements OnInit {
16 @ViewChild('modal', { static: true }) modal: NgbModal
17 @ViewChild('messagesBlock', { static: false }) messagesBlock: ElementRef
18
19 @Output() countMessagesUpdated = new EventEmitter<{ abuseId: number, countMessages: number }>()
20
21 abuseMessages: AbuseMessage[] = []
22 textareaMessage: string
23 sendingMessage = false
24
25 private openedModal: NgbModalRef
26 private abuse: UserAbuse
27
28 constructor (
29 protected formValidatorService: FormValidatorService,
30 private abuseValidatorsService: AbuseValidatorsService,
31 private modalService: NgbModal,
32 private auth: AuthService,
33 private notifier: Notifier,
34 private i18n: I18n,
35 private abuseService: AbuseService
36 ) {
37 super()
38 }
39
40 ngOnInit () {
41 this.buildForm({
42 message: this.abuseValidatorsService.ABUSE_MESSAGE
43 })
44 }
45
46 openModal (abuse: UserAbuse) {
47 this.abuse = abuse
48
49 this.openedModal = this.modalService.open(this.modal, { centered: true })
50
51 this.loadMessages()
52 }
53
54 hide () {
55 this.abuseMessages = []
56 this.openedModal.close()
57 }
58
59 addMessage () {
60 this.sendingMessage = true
61
62 this.abuseService.addAbuseMessage(this.abuse, this.form.value['message'])
63 .subscribe(
64 () => {
65 this.form.reset()
66 this.sendingMessage = false
67 this.countMessagesUpdated.emit({ abuseId: this.abuse.id, countMessages: this.abuseMessages.length + 1 })
68
69 this.loadMessages()
70 },
71
72 err => {
73 this.sendingMessage = false
74 console.error(err)
75 this.notifier.error('Sorry but you cannot send this message. Please retry later')
76 }
77 )
78 }
79
80 deleteMessage (abuseMessage: AbuseMessage) {
81 this.abuseService.deleteAbuseMessage(this.abuse, abuseMessage)
82 .subscribe(
83 () => {
84 this.countMessagesUpdated.emit({ abuseId: this.abuse.id, countMessages: this.abuseMessages.length - 1 })
85
86 this.abuseMessages = this.abuseMessages.filter(m => m.id !== abuseMessage.id)
87 },
88
89 err => this.notifier.error(err.message)
90 )
91 }
92
93 isMessageByMe (abuseMessage: AbuseMessage) {
94 return this.auth.getUser().account.id === abuseMessage.account.id
95 }
96
97 private loadMessages () {
98 this.abuseService.listAbuseMessages(this.abuse)
99 .subscribe(
100 res => {
101 this.abuseMessages = res.data
102
103 setTimeout(() => {
104 if (!this.messagesBlock) return
105
106 const element = this.messagesBlock.nativeElement as HTMLElement
107 element.scrollIntoView({ block: 'end', inline: 'nearest' })
108 })
109 },
110
111 err => this.notifier.error(err.message)
112 )
113 }
114
115}
diff --git a/client/src/app/shared/shared-moderation/abuse.service.ts b/client/src/app/shared/shared-moderation/abuse.service.ts
index 652d8370f..c1aa62023 100644
--- a/client/src/app/shared/shared-moderation/abuse.service.ts
+++ b/client/src/app/shared/shared-moderation/abuse.service.ts
@@ -5,13 +5,24 @@ import { catchError, map } from 'rxjs/operators'
5import { HttpClient, HttpParams } from '@angular/common/http' 5import { HttpClient, HttpParams } from '@angular/common/http'
6import { Injectable } from '@angular/core' 6import { Injectable } from '@angular/core'
7import { RestExtractor, RestPagination, RestService } from '@app/core' 7import { RestExtractor, RestPagination, RestService } from '@app/core'
8import { AdminAbuse, AbuseCreate, AbuseFilter, AbusePredefinedReasonsString, AbuseState, AbuseUpdate, ResultList, UserAbuse, AbuseMessage } from '@shared/models'
9import { environment } from '../../../environments/environment'
10import { I18n } from '@ngx-translate/i18n-polyfill' 8import { I18n } from '@ngx-translate/i18n-polyfill'
9import {
10 AbuseCreate,
11 AbuseFilter,
12 AbuseMessage,
13 AbusePredefinedReasonsString,
14 AbuseState,
15 AbuseUpdate,
16 AdminAbuse,
17 ResultList,
18 UserAbuse
19} from '@shared/models'
20import { environment } from '../../../environments/environment'
11 21
12@Injectable() 22@Injectable()
13export class AbuseService { 23export class AbuseService {
14 private static BASE_ABUSE_URL = environment.apiUrl + '/api/v1/abuses' 24 private static BASE_ABUSE_URL = environment.apiUrl + '/api/v1/abuses'
25 private static BASE_MY_ABUSE_URL = environment.apiUrl + '/api/v1/users/me/abuses'
15 26
16 constructor ( 27 constructor (
17 private i18n: I18n, 28 private i18n: I18n,
@@ -32,33 +43,7 @@ export class AbuseService {
32 params = this.restService.addRestGetParams(params, pagination, sort) 43 params = this.restService.addRestGetParams(params, pagination, sort)
33 44
34 if (search) { 45 if (search) {
35 const filters = this.restService.parseQueryStringFilter(search, { 46 params = this.buildParamsFromSearch(search, params)
36 id: { prefix: '#' },
37 state: {
38 prefix: 'state:',
39 handler: v => {
40 if (v === 'accepted') return AbuseState.ACCEPTED
41 if (v === 'pending') return AbuseState.PENDING
42 if (v === 'rejected') return AbuseState.REJECTED
43
44 return undefined
45 }
46 },
47 videoIs: {
48 prefix: 'videoIs:',
49 handler: v => {
50 if (v === 'deleted') return v
51 if (v === 'blacklisted') return v
52
53 return undefined
54 }
55 },
56 searchReporter: { prefix: 'reporter:' },
57 searchReportee: { prefix: 'reportee:' },
58 predefinedReason: { prefix: 'tag:' }
59 })
60
61 params = this.restService.addObjectParams(params, filters)
62 } 47 }
63 48
64 return this.authHttp.get<ResultList<AdminAbuse>>(url, { params }) 49 return this.authHttp.get<ResultList<AdminAbuse>>(url, { params })
@@ -67,6 +52,27 @@ export class AbuseService {
67 ) 52 )
68 } 53 }
69 54
55 getUserAbuses (options: {
56 pagination: RestPagination,
57 sort: SortMeta,
58 search?: string
59 }): Observable<ResultList<UserAbuse>> {
60 const { pagination, sort, search } = options
61 const url = AbuseService.BASE_MY_ABUSE_URL
62
63 let params = new HttpParams()
64 params = this.restService.addRestGetParams(params, pagination, sort)
65
66 if (search) {
67 params = this.buildParamsFromSearch(search, params)
68 }
69
70 return this.authHttp.get<ResultList<UserAbuse>>(url, { params })
71 .pipe(
72 catchError(res => this.restExtractor.handleError(res))
73 )
74 }
75
70 reportVideo (parameters: AbuseCreate) { 76 reportVideo (parameters: AbuseCreate) {
71 const url = AbuseService.BASE_ABUSE_URL 77 const url = AbuseService.BASE_ABUSE_URL
72 78
@@ -180,4 +186,33 @@ export class AbuseService {
180 return reasons 186 return reasons
181 } 187 }
182 188
189 private buildParamsFromSearch (search: string, params: HttpParams) {
190 const filters = this.restService.parseQueryStringFilter(search, {
191 id: { prefix: '#' },
192 state: {
193 prefix: 'state:',
194 handler: v => {
195 if (v === 'accepted') return AbuseState.ACCEPTED
196 if (v === 'pending') return AbuseState.PENDING
197 if (v === 'rejected') return AbuseState.REJECTED
198
199 return undefined
200 }
201 },
202 videoIs: {
203 prefix: 'videoIs:',
204 handler: v => {
205 if (v === 'deleted') return v
206 if (v === 'blacklisted') return v
207
208 return undefined
209 }
210 },
211 searchReporter: { prefix: 'reporter:' },
212 searchReportee: { prefix: 'reportee:' },
213 predefinedReason: { prefix: 'tag:' }
214 })
215
216 return this.restService.addObjectParams(params, filters)
217 }
183} 218}
diff --git a/client/src/app/shared/shared-moderation/index.ts b/client/src/app/shared/shared-moderation/index.ts
index c8082d4b3..41c910ffe 100644
--- a/client/src/app/shared/shared-moderation/index.ts
+++ b/client/src/app/shared/shared-moderation/index.ts
@@ -1,6 +1,5 @@
1export * from './report-modals' 1export * from './report-modals'
2 2
3export * from './abuse-message-modal.component'
4export * from './abuse.service' 3export * from './abuse.service'
5export * from './account-block.model' 4export * from './account-block.model'
6export * from './account-blocklist.component' 5export * from './account-blocklist.component'
diff --git a/client/src/app/shared/shared-moderation/moderation.scss b/client/src/app/shared/shared-moderation/moderation.scss
new file mode 100644
index 000000000..260346dc5
--- /dev/null
+++ b/client/src/app/shared/shared-moderation/moderation.scss
@@ -0,0 +1,50 @@
1@import 'variables';
2@import 'mixins';
3@import 'miniature';
4
5.moderation-expanded {
6 font-size: 90%;
7
8 .moderation-expanded-label {
9 font-weight: $font-semibold;
10 display: inline-block;
11 vertical-align: top;
12 text-align: right;
13 }
14
15 .moderation-expanded-text {
16 display: inline-flex;
17 word-wrap: break-word;
18
19 ::ng-deep p:last-child {
20 margin-bottom: 0px !important;
21 }
22 }
23}
24
25.input-group {
26 @include peertube-input-group(300px);
27
28 .dropdown-toggle::after {
29 margin-left: 0;
30 }
31}
32
33.chip {
34 @include chip;
35}
36
37.caption {
38 justify-content: flex-end;
39
40 input {
41 @include peertube-input-text(250px);
42 flex-grow: 1;
43 }
44}
45
46my-action-dropdown.show {
47 ::ng-deep .dropdown-root {
48 display: block !important;
49 }
50}
diff --git a/client/src/app/shared/shared-moderation/server-blocklist.component.scss b/client/src/app/shared/shared-moderation/server-blocklist.component.scss
index 9ddb76850..31db4d92b 100644
--- a/client/src/app/shared/shared-moderation/server-blocklist.component.scss
+++ b/client/src/app/shared/shared-moderation/server-blocklist.component.scss
@@ -32,3 +32,16 @@ a {
32.block-button { 32.block-button {
33 @include create-button; 33 @include create-button;
34} 34}
35
36.caption {
37 justify-content: flex-end;
38
39 input {
40 @include peertube-input-text(250px);
41 flex-grow: 1;
42 }
43}
44
45.chip {
46 @include chip;
47}
diff --git a/client/src/app/shared/shared-moderation/shared-moderation.module.ts b/client/src/app/shared/shared-moderation/shared-moderation.module.ts
index b5b6daf27..b1b98f8d0 100644
--- a/client/src/app/shared/shared-moderation/shared-moderation.module.ts
+++ b/client/src/app/shared/shared-moderation/shared-moderation.module.ts
@@ -4,7 +4,6 @@ import { SharedFormModule } from '../shared-forms/shared-form.module'
4import { SharedGlobalIconModule } from '../shared-icons' 4import { SharedGlobalIconModule } from '../shared-icons'
5import { SharedMainModule } from '../shared-main/shared-main.module' 5import { SharedMainModule } from '../shared-main/shared-main.module'
6import { SharedVideoCommentModule } from '../shared-video-comment' 6import { SharedVideoCommentModule } from '../shared-video-comment'
7import { AbuseMessageModalComponent } from './abuse-message-modal.component'
8import { AbuseService } from './abuse.service' 7import { AbuseService } from './abuse.service'
9import { BatchDomainsModalComponent } from './batch-domains-modal.component' 8import { BatchDomainsModalComponent } from './batch-domains-modal.component'
10import { BlocklistService } from './blocklist.service' 9import { BlocklistService } from './blocklist.service'
@@ -30,8 +29,7 @@ import { VideoBlockService } from './video-block.service'
30 VideoReportComponent, 29 VideoReportComponent,
31 BatchDomainsModalComponent, 30 BatchDomainsModalComponent,
32 CommentReportComponent, 31 CommentReportComponent,
33 AccountReportComponent, 32 AccountReportComponent
34 AbuseMessageModalComponent
35 ], 33 ],
36 34
37 exports: [ 35 exports: [
@@ -41,8 +39,7 @@ import { VideoBlockService } from './video-block.service'
41 VideoReportComponent, 39 VideoReportComponent,
42 BatchDomainsModalComponent, 40 BatchDomainsModalComponent,
43 CommentReportComponent, 41 CommentReportComponent,
44 AccountReportComponent, 42 AccountReportComponent
45 AbuseMessageModalComponent
46 ], 43 ],
47 44
48 providers: [ 45 providers: [