Add abuse message management in admin
This commit is contained in:
parent
edbc932546
commit
441e453ae5
17 changed files with 326 additions and 29 deletions
|
@ -46,6 +46,7 @@
|
|||
<th i18n>Video/Comment/Account</th>
|
||||
<th style="width: 150px;" i18n pSortableColumn="createdAt">Created <p-sortIcon field="createdAt"></p-sortIcon></th>
|
||||
<th i18n pSortableColumn="state" style="width: 80px;">State <p-sortIcon field="state"></p-sortIcon></th>
|
||||
<th i18n style="width: 80px;">Messages</th>
|
||||
<th style="width: 150px;"></th>
|
||||
</tr>
|
||||
</ng-template>
|
||||
|
@ -157,6 +158,12 @@
|
|||
<span *ngIf="abuse.moderationComment" container="body" placement="left auto" [ngbTooltip]="abuse.moderationComment" class="glyphicon glyphicon-comment"></span>
|
||||
</td>
|
||||
|
||||
<td class="c-hand abuse-messages" (click)="openAbuseMessagesModal(abuse)">
|
||||
{{ abuse.countMessages }}
|
||||
|
||||
<my-global-icon iconName="message-circle"></my-global-icon>
|
||||
</td>
|
||||
|
||||
<td class="action-cell">
|
||||
<my-action-dropdown
|
||||
[ngClass]="{ 'show': expanded }" placement="bottom-right top-right left auto" container="body"
|
||||
|
@ -187,3 +194,4 @@
|
|||
</p-table>
|
||||
|
||||
<my-moderation-comment-modal #moderationCommentModal (commentUpdated)="onModerationCommentUpdated()"></my-moderation-comment-modal>
|
||||
<my-abuse-message-modal #abuseMessagesModal (countMessagesUpdated)="onCountMessagesUpdated($event)"></my-abuse-message-modal>
|
||||
|
|
|
@ -21,3 +21,12 @@
|
|||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.abuse-messages {
|
||||
my-global-icon {
|
||||
width: 22px;
|
||||
margin-left: 3px;
|
||||
position: relative;
|
||||
top: -2px;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,17 +8,17 @@ import { DomSanitizer, SafeHtml } from '@angular/platform-browser'
|
|||
import { ActivatedRoute, Params, Router } from '@angular/router'
|
||||
import { ConfirmService, MarkdownService, Notifier, RestPagination, RestTable } from '@app/core'
|
||||
import { Account, Actor, DropdownAction, Video, VideoService } from '@app/shared/shared-main'
|
||||
import { AbuseService, BlocklistService, VideoBlockService } from '@app/shared/shared-moderation'
|
||||
import { AbuseService, BlocklistService, VideoBlockService, AbuseMessageModalComponent } from '@app/shared/shared-moderation'
|
||||
import { VideoCommentService } from '@app/shared/shared-video-comment'
|
||||
import { I18n } from '@ngx-translate/i18n-polyfill'
|
||||
import { Abuse, AbuseState } from '@shared/models'
|
||||
import { AdminAbuse, AbuseState } from '@shared/models'
|
||||
import { ModerationCommentModalComponent } from './moderation-comment-modal.component'
|
||||
|
||||
const logger = debug('peertube:moderation:AbuseListComponent')
|
||||
|
||||
// Don't use an abuse model because we need external services to compute some properties
|
||||
// And this model is only used in this component
|
||||
export type ProcessedAbuse = Abuse & {
|
||||
export type ProcessedAbuse = AdminAbuse & {
|
||||
moderationCommentHtml?: string,
|
||||
reasonHtml?: string
|
||||
embedHtml?: SafeHtml
|
||||
|
@ -31,8 +31,8 @@ export type ProcessedAbuse = Abuse & {
|
|||
truncatedCommentHtml?: string
|
||||
commentHtml?: string
|
||||
|
||||
video: Abuse['video'] & {
|
||||
channel: Abuse['video']['channel'] & {
|
||||
video: AdminAbuse['video'] & {
|
||||
channel: AdminAbuse['video']['channel'] & {
|
||||
ownerAccount: Account
|
||||
}
|
||||
}
|
||||
|
@ -45,6 +45,7 @@ export type ProcessedAbuse = Abuse & {
|
|||
})
|
||||
export class AbuseListComponent extends RestTable implements OnInit, AfterViewInit {
|
||||
@ViewChild('moderationCommentModal', { static: true }) moderationCommentModal: ModerationCommentModalComponent
|
||||
@ViewChild('abuseMessagesModal', { static: true }) abuseMessagesModal: AbuseMessageModalComponent
|
||||
|
||||
abuses: ProcessedAbuse[] = []
|
||||
totalRecords = 0
|
||||
|
@ -104,7 +105,7 @@ export class AbuseListComponent extends RestTable implements OnInit, AfterViewIn
|
|||
return 'AbuseListComponent'
|
||||
}
|
||||
|
||||
openModerationCommentModal (abuse: Abuse) {
|
||||
openModerationCommentModal (abuse: AdminAbuse) {
|
||||
this.moderationCommentModal.openModal(abuse)
|
||||
}
|
||||
|
||||
|
@ -132,19 +133,19 @@ export class AbuseListComponent extends RestTable implements OnInit, AfterViewIn
|
|||
}
|
||||
/* END Table filter functions */
|
||||
|
||||
isAbuseAccepted (abuse: Abuse) {
|
||||
isAbuseAccepted (abuse: AdminAbuse) {
|
||||
return abuse.state.id === AbuseState.ACCEPTED
|
||||
}
|
||||
|
||||
isAbuseRejected (abuse: Abuse) {
|
||||
isAbuseRejected (abuse: AdminAbuse) {
|
||||
return abuse.state.id === AbuseState.REJECTED
|
||||
}
|
||||
|
||||
getVideoUrl (abuse: Abuse) {
|
||||
getVideoUrl (abuse: AdminAbuse) {
|
||||
return Video.buildClientUrl(abuse.video.uuid)
|
||||
}
|
||||
|
||||
getCommentUrl (abuse: Abuse) {
|
||||
getCommentUrl (abuse: AdminAbuse) {
|
||||
return Video.buildClientUrl(abuse.comment.video.uuid) + ';threadId=' + abuse.comment.threadId
|
||||
}
|
||||
|
||||
|
@ -152,7 +153,7 @@ export class AbuseListComponent extends RestTable implements OnInit, AfterViewIn
|
|||
return '/accounts/' + abuse.flaggedAccount.nameWithHost
|
||||
}
|
||||
|
||||
getVideoEmbed (abuse: Abuse) {
|
||||
getVideoEmbed (abuse: AdminAbuse) {
|
||||
return buildVideoEmbed(
|
||||
buildVideoLink({
|
||||
baseUrl: `${environment.embedUrl}/videos/embed/${abuse.video.uuid}`,
|
||||
|
@ -168,7 +169,7 @@ export class AbuseListComponent extends RestTable implements OnInit, AfterViewIn
|
|||
($event.target as HTMLImageElement).src = Actor.GET_DEFAULT_AVATAR_URL()
|
||||
}
|
||||
|
||||
async removeAbuse (abuse: Abuse) {
|
||||
async removeAbuse (abuse: AdminAbuse) {
|
||||
const res = await this.confirmService.confirm(this.i18n('Do you really want to delete this abuse report?'), this.i18n('Delete'))
|
||||
if (res === false) return
|
||||
|
||||
|
@ -182,7 +183,7 @@ export class AbuseListComponent extends RestTable implements OnInit, AfterViewIn
|
|||
)
|
||||
}
|
||||
|
||||
updateAbuseState (abuse: Abuse, state: AbuseState) {
|
||||
updateAbuseState (abuse: AdminAbuse, state: AbuseState) {
|
||||
this.abuseService.updateAbuse(abuse, { state })
|
||||
.subscribe(
|
||||
() => this.loadData(),
|
||||
|
@ -191,10 +192,25 @@ export class AbuseListComponent extends RestTable implements OnInit, AfterViewIn
|
|||
)
|
||||
}
|
||||
|
||||
onCountMessagesUpdated (event: { abuseId: number, countMessages: number }) {
|
||||
const abuse = this.abuses.find(a => a.id === event.abuseId)
|
||||
|
||||
if (!abuse) {
|
||||
console.error('Cannot find abuse %d.', event.abuseId)
|
||||
return
|
||||
}
|
||||
|
||||
abuse.countMessages = event.countMessages
|
||||
}
|
||||
|
||||
openAbuseMessagesModal (abuse: AdminAbuse) {
|
||||
this.abuseMessagesModal.openModal(abuse)
|
||||
}
|
||||
|
||||
protected loadData () {
|
||||
logger('Load data.')
|
||||
|
||||
return this.abuseService.getAbuses({
|
||||
return this.abuseService.getAdminAbuses({
|
||||
pagination: this.pagination,
|
||||
sort: this.sort,
|
||||
search: this.search
|
||||
|
@ -257,7 +273,11 @@ export class AbuseListComponent extends RestTable implements OnInit, AfterViewIn
|
|||
handler: abuse => this.removeAbuse(abuse)
|
||||
},
|
||||
{
|
||||
label: this.i18n('Add note'),
|
||||
label: this.i18n('Messages'),
|
||||
handler: abuse => this.openAbuseMessagesModal(abuse)
|
||||
},
|
||||
{
|
||||
label: this.i18n('Add internal note'),
|
||||
handler: abuse => this.openModerationCommentModal(abuse),
|
||||
isDisplayed: abuse => !abuse.moderationComment
|
||||
},
|
||||
|
|
|
@ -5,7 +5,7 @@ import { AbuseService } from '@app/shared/shared-moderation'
|
|||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
||||
import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref'
|
||||
import { I18n } from '@ngx-translate/i18n-polyfill'
|
||||
import { Abuse } from '@shared/models'
|
||||
import { AdminAbuse } from '@shared/models'
|
||||
|
||||
@Component({
|
||||
selector: 'my-moderation-comment-modal',
|
||||
|
@ -16,7 +16,7 @@ export class ModerationCommentModalComponent extends FormReactive implements OnI
|
|||
@ViewChild('modal', { static: true }) modal: NgbModal
|
||||
@Output() commentUpdated = new EventEmitter<string>()
|
||||
|
||||
private abuseToComment: Abuse
|
||||
private abuseToComment: AdminAbuse
|
||||
private openedModal: NgbModalRef
|
||||
|
||||
constructor (
|
||||
|
@ -36,7 +36,7 @@ export class ModerationCommentModalComponent extends FormReactive implements OnI
|
|||
})
|
||||
}
|
||||
|
||||
openModal (abuseToComment: Abuse) {
|
||||
openModal (abuseToComment: AdminAbuse) {
|
||||
this.abuseToComment = abuseToComment
|
||||
this.openedModal = this.modalService.open(this.modal, { centered: true })
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ import { BuildFormValidator } from './form-validator.service'
|
|||
export class AbuseValidatorsService {
|
||||
readonly ABUSE_REASON: BuildFormValidator
|
||||
readonly ABUSE_MODERATION_COMMENT: BuildFormValidator
|
||||
readonly ABUSE_MESSAGE: BuildFormValidator
|
||||
|
||||
constructor (private i18n: I18n) {
|
||||
this.ABUSE_REASON = {
|
||||
|
@ -26,5 +27,14 @@ export class AbuseValidatorsService {
|
|||
'maxlength': this.i18n('Moderation comment cannot be more than 3000 characters long.')
|
||||
}
|
||||
}
|
||||
|
||||
this.ABUSE_MESSAGE = {
|
||||
VALIDATORS: [ Validators.required, Validators.minLength(2), Validators.maxLength(3000) ],
|
||||
MESSAGES: {
|
||||
'required': this.i18n('Abuse message is required.'),
|
||||
'minlength': this.i18n('Abuse message must be at least 2 characters long.'),
|
||||
'maxlength': this.i18n('Abuse message cannot be more than 3000 characters long.')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -64,8 +64,7 @@ const icons = {
|
|||
'go': require('!!raw-loader?!../../../assets/images/feather/arrow-up-right.svg').default,
|
||||
'cross': require('!!raw-loader?!../../../assets/images/feather/x.svg').default,
|
||||
'tick': require('!!raw-loader?!../../../assets/images/feather/check.svg').default,
|
||||
'repeat': require('!!raw-loader?!../../../assets/images/feather/repeat.svg').default,
|
||||
'columns': require('!!raw-loader?!../../../assets/images/feather/columns.svg').default
|
||||
'message-circle': require('!!raw-loader?!../../../assets/images/feather/message-circle.svg').default
|
||||
}
|
||||
|
||||
export type GlobalIconName = keyof typeof icons
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
<ng-template #modal>
|
||||
<div class="modal-header">
|
||||
<h4 i18n class="modal-title">Messages</h4>
|
||||
|
||||
<my-global-icon iconName="cross" aria-label="Close" role="button" (click)="hide()"></my-global-icon>
|
||||
</div>
|
||||
|
||||
<div class="modal-body">
|
||||
<div class="messages" #messagesBlock>
|
||||
<div
|
||||
*ngFor="let message of abuseMessages"
|
||||
class="message-block" [ngClass]="{ 'by-moderator': message.byModerator, 'by-me': isMessageByMe(message) }"
|
||||
>
|
||||
|
||||
<div class="author">{{ message.account.name }}</div>
|
||||
|
||||
<div class="bubble">
|
||||
<div class="content">{{ message.message }}</div>
|
||||
<div class="date">{{ message.createdAt | date }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form novalidate [formGroup]="form" (ngSubmit)="addMessage()">
|
||||
<div class="form-group">
|
||||
<textarea formControlName="message" ngbAutofocus [ngClass]="{ 'input-error': formErrors['message'] }" class="form-control"></textarea>
|
||||
|
||||
<div *ngIf="formErrors.message" class="form-error">
|
||||
{{ formErrors.message }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group inputs">
|
||||
<input type="submit" i18n-value value="Add message" class="action-button-submit" [disabled]="!form.valid || sendingMessage">
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
</ng-template>
|
|
@ -0,0 +1,57 @@
|
|||
@import 'variables';
|
||||
@import 'mixins';
|
||||
|
||||
form {
|
||||
margin: 20px 20px 0 0;
|
||||
}
|
||||
|
||||
textarea {
|
||||
@include peertube-textarea(100%, 70px);
|
||||
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.messages {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow-y: scroll;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.message-block {
|
||||
margin-bottom: 10px;
|
||||
max-width: 60%;
|
||||
|
||||
.author {
|
||||
color: var(--greyForegroundColor);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.bubble {
|
||||
color: var(--mainForegroundColor);
|
||||
background-color: var(--greyBackgroundColor);
|
||||
border-radius: 10px;
|
||||
padding: 5px 10px;
|
||||
|
||||
&.by-me {
|
||||
color: var(--mainForegroundColor);
|
||||
background-color: var(--secondaryColor);
|
||||
}
|
||||
|
||||
&.by-moderator {
|
||||
color: #fff;
|
||||
background-color: var(--mainColor);
|
||||
|
||||
align-self: flex-end;
|
||||
}
|
||||
|
||||
.content {
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.date {
|
||||
font-size: 13px;
|
||||
color: var(--greyForegroundColor);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,115 @@
|
|||
import { Component, ElementRef, EventEmitter, Output, ViewChild, OnInit } from '@angular/core'
|
||||
import { Notifier, AuthService } from '@app/core'
|
||||
import { FormReactive, FormValidatorService, AbuseValidatorsService } from '@app/shared/shared-forms'
|
||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
||||
import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref'
|
||||
import { I18n } from '@ngx-translate/i18n-polyfill'
|
||||
import { AbuseMessage, UserAbuse } from '@shared/models'
|
||||
import { AbuseService } from './abuse.service'
|
||||
|
||||
@Component({
|
||||
selector: 'my-abuse-message-modal',
|
||||
templateUrl: './abuse-message-modal.component.html',
|
||||
styleUrls: [ './abuse-message-modal.component.scss' ]
|
||||
})
|
||||
export class AbuseMessageModalComponent extends FormReactive implements OnInit {
|
||||
@ViewChild('modal', { static: true }) modal: NgbModal
|
||||
@ViewChild('messagesBlock', { static: false }) messagesBlock: ElementRef
|
||||
|
||||
@Output() countMessagesUpdated = new EventEmitter<{ abuseId: number, countMessages: number }>()
|
||||
|
||||
abuseMessages: AbuseMessage[] = []
|
||||
textareaMessage: string
|
||||
sendingMessage = false
|
||||
|
||||
private openedModal: NgbModalRef
|
||||
private abuse: UserAbuse
|
||||
|
||||
constructor (
|
||||
protected formValidatorService: FormValidatorService,
|
||||
private abuseValidatorsService: AbuseValidatorsService,
|
||||
private modalService: NgbModal,
|
||||
private auth: AuthService,
|
||||
private notifier: Notifier,
|
||||
private i18n: I18n,
|
||||
private abuseService: AbuseService
|
||||
) {
|
||||
super()
|
||||
}
|
||||
|
||||
ngOnInit () {
|
||||
this.buildForm({
|
||||
message: this.abuseValidatorsService.ABUSE_MESSAGE
|
||||
})
|
||||
}
|
||||
|
||||
openModal (abuse: UserAbuse) {
|
||||
this.abuse = abuse
|
||||
|
||||
this.openedModal = this.modalService.open(this.modal, { centered: true })
|
||||
|
||||
this.loadMessages()
|
||||
}
|
||||
|
||||
hide () {
|
||||
this.abuseMessages = []
|
||||
this.openedModal.close()
|
||||
}
|
||||
|
||||
addMessage () {
|
||||
this.sendingMessage = true
|
||||
|
||||
this.abuseService.addAbuseMessage(this.abuse, this.form.value['message'])
|
||||
.subscribe(
|
||||
() => {
|
||||
this.form.reset()
|
||||
this.sendingMessage = false
|
||||
this.countMessagesUpdated.emit({ abuseId: this.abuse.id, countMessages: this.abuseMessages.length + 1 })
|
||||
|
||||
this.loadMessages()
|
||||
},
|
||||
|
||||
err => {
|
||||
this.sendingMessage = false
|
||||
console.error(err)
|
||||
this.notifier.error('Sorry but you cannot send this message. Please retry later')
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
deleteMessage (abuseMessage: AbuseMessage) {
|
||||
this.abuseService.deleteAbuseMessage(this.abuse, abuseMessage)
|
||||
.subscribe(
|
||||
() => {
|
||||
this.countMessagesUpdated.emit({ abuseId: this.abuse.id, countMessages: this.abuseMessages.length - 1 })
|
||||
|
||||
this.abuseMessages = this.abuseMessages.filter(m => m.id !== abuseMessage.id)
|
||||
},
|
||||
|
||||
err => this.notifier.error(err.message)
|
||||
)
|
||||
}
|
||||
|
||||
isMessageByMe (abuseMessage: AbuseMessage) {
|
||||
return this.auth.getUser().account.id === abuseMessage.account.id
|
||||
}
|
||||
|
||||
private loadMessages () {
|
||||
this.abuseService.listAbuseMessages(this.abuse)
|
||||
.subscribe(
|
||||
res => {
|
||||
this.abuseMessages = res.data
|
||||
|
||||
setTimeout(() => {
|
||||
if (!this.messagesBlock) return
|
||||
|
||||
const element = this.messagesBlock.nativeElement as HTMLElement
|
||||
element.scrollIntoView({ block: 'end', inline: 'nearest' })
|
||||
})
|
||||
},
|
||||
|
||||
err => this.notifier.error(err.message)
|
||||
)
|
||||
}
|
||||
|
||||
}
|
|
@ -5,7 +5,7 @@ import { catchError, map } from 'rxjs/operators'
|
|||
import { HttpClient, HttpParams } from '@angular/common/http'
|
||||
import { Injectable } from '@angular/core'
|
||||
import { RestExtractor, RestPagination, RestService } from '@app/core'
|
||||
import { Abuse, AbuseCreate, AbuseFilter, AbusePredefinedReasonsString, AbuseState, AbuseUpdate, ResultList } from '@shared/models'
|
||||
import { AdminAbuse, AbuseCreate, AbuseFilter, AbusePredefinedReasonsString, AbuseState, AbuseUpdate, ResultList, UserAbuse, AbuseMessage } from '@shared/models'
|
||||
import { environment } from '../../../environments/environment'
|
||||
import { I18n } from '@ngx-translate/i18n-polyfill'
|
||||
|
||||
|
@ -20,11 +20,11 @@ export class AbuseService {
|
|||
private restExtractor: RestExtractor
|
||||
) { }
|
||||
|
||||
getAbuses (options: {
|
||||
getAdminAbuses (options: {
|
||||
pagination: RestPagination,
|
||||
sort: SortMeta,
|
||||
search?: string
|
||||
}): Observable<ResultList<Abuse>> {
|
||||
}): Observable<ResultList<AdminAbuse>> {
|
||||
const { pagination, sort, search } = options
|
||||
const url = AbuseService.BASE_ABUSE_URL
|
||||
|
||||
|
@ -61,7 +61,7 @@ export class AbuseService {
|
|||
params = this.restService.addObjectParams(params, filters)
|
||||
}
|
||||
|
||||
return this.authHttp.get<ResultList<Abuse>>(url, { params })
|
||||
return this.authHttp.get<ResultList<AdminAbuse>>(url, { params })
|
||||
.pipe(
|
||||
catchError(res => this.restExtractor.handleError(res))
|
||||
)
|
||||
|
@ -79,7 +79,7 @@ export class AbuseService {
|
|||
)
|
||||
}
|
||||
|
||||
updateAbuse (abuse: Abuse, abuseUpdate: AbuseUpdate) {
|
||||
updateAbuse (abuse: AdminAbuse, abuseUpdate: AbuseUpdate) {
|
||||
const url = AbuseService.BASE_ABUSE_URL + '/' + abuse.id
|
||||
|
||||
return this.authHttp.put(url, abuseUpdate)
|
||||
|
@ -89,7 +89,7 @@ export class AbuseService {
|
|||
)
|
||||
}
|
||||
|
||||
removeAbuse (abuse: Abuse) {
|
||||
removeAbuse (abuse: AdminAbuse) {
|
||||
const url = AbuseService.BASE_ABUSE_URL + '/' + abuse.id
|
||||
|
||||
return this.authHttp.delete(url)
|
||||
|
@ -99,6 +99,35 @@ export class AbuseService {
|
|||
)
|
||||
}
|
||||
|
||||
addAbuseMessage (abuse: UserAbuse, message: string) {
|
||||
const url = AbuseService.BASE_ABUSE_URL + '/' + abuse.id + '/messages'
|
||||
|
||||
return this.authHttp.post(url, { message })
|
||||
.pipe(
|
||||
map(this.restExtractor.extractDataBool),
|
||||
catchError(res => this.restExtractor.handleError(res))
|
||||
)
|
||||
}
|
||||
|
||||
listAbuseMessages (abuse: UserAbuse) {
|
||||
const url = AbuseService.BASE_ABUSE_URL + '/' + abuse.id + '/messages'
|
||||
|
||||
return this.authHttp.get<ResultList<AbuseMessage>>(url)
|
||||
.pipe(
|
||||
catchError(res => this.restExtractor.handleError(res))
|
||||
)
|
||||
}
|
||||
|
||||
deleteAbuseMessage (abuse: UserAbuse, abuseMessage: AbuseMessage) {
|
||||
const url = AbuseService.BASE_ABUSE_URL + '/' + abuse.id + '/messages/' + abuseMessage.id
|
||||
|
||||
return this.authHttp.delete(url)
|
||||
.pipe(
|
||||
map(this.restExtractor.extractDataBool),
|
||||
catchError(res => this.restExtractor.handleError(res))
|
||||
)
|
||||
}
|
||||
|
||||
getPrefefinedReasons (type: AbuseFilter) {
|
||||
let reasons: { id: AbusePredefinedReasonsString, label: string, description?: string, help?: string }[] = [
|
||||
{
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
export * from './report-modals'
|
||||
|
||||
export * from './abuse-message-modal.component'
|
||||
export * from './abuse.service'
|
||||
export * from './account-block.model'
|
||||
export * from './account-blocklist.component'
|
||||
|
|
|
@ -4,15 +4,16 @@ import { SharedFormModule } from '../shared-forms/shared-form.module'
|
|||
import { SharedGlobalIconModule } from '../shared-icons'
|
||||
import { SharedMainModule } from '../shared-main/shared-main.module'
|
||||
import { SharedVideoCommentModule } from '../shared-video-comment'
|
||||
import { AbuseMessageModalComponent } from './abuse-message-modal.component'
|
||||
import { AbuseService } from './abuse.service'
|
||||
import { BatchDomainsModalComponent } from './batch-domains-modal.component'
|
||||
import { BlocklistService } from './blocklist.service'
|
||||
import { BulkService } from './bulk.service'
|
||||
import { AccountReportComponent, CommentReportComponent, VideoReportComponent } from './report-modals'
|
||||
import { UserBanModalComponent } from './user-ban-modal.component'
|
||||
import { UserModerationDropdownComponent } from './user-moderation-dropdown.component'
|
||||
import { VideoBlockComponent } from './video-block.component'
|
||||
import { VideoBlockService } from './video-block.service'
|
||||
import { VideoReportComponent, AccountReportComponent, CommentReportComponent } from './report-modals'
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
|
@ -29,7 +30,8 @@ import { VideoReportComponent, AccountReportComponent, CommentReportComponent }
|
|||
VideoReportComponent,
|
||||
BatchDomainsModalComponent,
|
||||
CommentReportComponent,
|
||||
AccountReportComponent
|
||||
AccountReportComponent,
|
||||
AbuseMessageModalComponent
|
||||
],
|
||||
|
||||
exports: [
|
||||
|
@ -39,7 +41,8 @@ import { VideoReportComponent, AccountReportComponent, CommentReportComponent }
|
|||
VideoReportComponent,
|
||||
BatchDomainsModalComponent,
|
||||
CommentReportComponent,
|
||||
AccountReportComponent
|
||||
AccountReportComponent,
|
||||
AbuseMessageModalComponent
|
||||
],
|
||||
|
||||
providers: [
|
||||
|
|
1
client/src/assets/images/feather/message-circle.svg
Normal file
1
client/src/assets/images/feather/message-circle.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-message-circle"><path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z"></path></svg>
|
After Width: | Height: | Size: 428 B |
|
@ -32,6 +32,7 @@ body {
|
|||
--secondaryColor: #{$secondary-color};
|
||||
|
||||
--greyForegroundColor: #{$grey-foreground-color};
|
||||
--greyBackgroundColor: #{$grey-background-color};
|
||||
|
||||
--menuBackgroundColor: #{$menu-background};
|
||||
--menuForegroundColor: #{$menu-color};
|
||||
|
|
|
@ -92,6 +92,7 @@ $variables: (
|
|||
--secondaryColor: var(--secondaryColor),
|
||||
|
||||
--greyForegroundColor: var(--greyForegroundColor),
|
||||
--greyBackgroundColor: var(--greyBackgroundColor),
|
||||
|
||||
--menuBackgroundColor: var(--menuBackgroundColor),
|
||||
--menuForegroundColor: var(--menuForegroundColor),
|
||||
|
|
|
@ -94,6 +94,8 @@ export class AbuseMessageModel extends Model<AbuseMessageModel> {
|
|||
|
||||
return {
|
||||
id: this.id,
|
||||
createdAt: this.createdAt,
|
||||
|
||||
byModerator: this.byModerator,
|
||||
message: this.message,
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ export interface AbuseMessage {
|
|||
id: number
|
||||
message: string
|
||||
byModerator: boolean
|
||||
createdAt: Date | string
|
||||
|
||||
account: AccountSummary
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue