Skip to content

Commit

Permalink
Predefined report reasons reporter UI
Browse files Browse the repository at this point in the history
  • Loading branch information
rigelk committed Jun 13, 2020
1 parent 7dfe352 commit 7333edd
Show file tree
Hide file tree
Showing 31 changed files with 499 additions and 62 deletions.
14 changes: 14 additions & 0 deletions client/src/app/+admin/moderation/moderation.component.scss
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,20 @@
}
}

p-calendar {
display: block;

::ng-deep {
.ui-widget-content {
min-width: 400px;
}

input {
@include peertube-input-text(100%);
}
}
}

.screenratio {
div {
@include miniature-thumbnail;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,15 @@
<span class="col-9 moderation-expanded-text" [innerHTML]="videoAbuse.reasonHtml"></span>
</div>

<div *ngIf="predefinedReasons" class="mt-2 d-flex">
<span class="col-3"></span>
<span class="col-9">
<div class="chip bg-secondary text-light" *ngFor="let reason of predefinedReasons">
<div>{{ reason }}</div>
</div>
</span>
</div>

<div class="mt-3 d-flex" *ngIf="videoAbuse.moderationComment">
<span class="col-3 moderation-expanded-label" i18n>Note</span>
<span class="col-9 moderation-expanded-text" [innerHTML]="videoAbuse.moderationCommentHtml"></span>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { Component, Input } from '@angular/core'
import { Account } from '@app/shared/account/account.model'
import { Actor } from '@app/shared/actor/actor.model'
import { VideoAbusePredefinedReasons } from '../../../../../../shared/models/videos/abuse/video-abuse-reason.model'
import { ProcessedVideoAbuse } from './video-abuse-list.component'
import { I18n } from '@ngx-translate/i18n-polyfill'

@Component({
selector: 'my-video-abuse-details',
Expand All @@ -11,6 +12,27 @@ import { ProcessedVideoAbuse } from './video-abuse-list.component'
export class VideoAbuseDetailsComponent {
@Input() videoAbuse: ProcessedVideoAbuse

private predefinedReasonsTranslations: { [key: number]: string }

constructor (
private i18n: I18n
) {
this.predefinedReasonsTranslations = {
[VideoAbusePredefinedReasons.VIOLENT_OR_REPULSIVE]: this.i18n('Violent or Repulsive'),
[VideoAbusePredefinedReasons.HATEFUL_OR_ABUSIVE]: this.i18n('Hateful or Abusive'),
[VideoAbusePredefinedReasons.SPAM_OR_MISLEADING]: this.i18n('Spam or Misleading'),
[VideoAbusePredefinedReasons.PRIVACY]: this.i18n('Privacy'),
[VideoAbusePredefinedReasons.RIGHTS]: this.i18n('Rights'),
[VideoAbusePredefinedReasons.SERVER_RULES]: this.i18n('Server rules'),
[VideoAbusePredefinedReasons.THUMBNAILS]: this.i18n('Thumbnails'),
[VideoAbusePredefinedReasons.CAPTIONS]: this.i18n('Captions')
}
}

get predefinedReasons () {
return this.videoAbuse.predefinedReasons.map(r => this.predefinedReasonsTranslations[r])
}

switchToDefaultAvatar ($event: Event) {
($event.target as HTMLImageElement).src = Actor.GET_DEFAULT_AVATAR_URL()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@ import { ModerationCommentModalComponent } from './moderation-comment-modal.comp
import { Video } from '../../../shared/video/video.model'
import { MarkdownService } from '@app/shared/renderer'
import { Actor } from '@app/shared/actor/actor.model'
import { buildVideoLink, buildVideoEmbed } from 'src/assets/player/utils'
import { getAbsoluteAPIUrl } from '@app/shared/misc/utils'
import { buildVideoEmbed } from 'src/assets/player/utils'
import { DomSanitizer } from '@angular/platform-browser'
import { BlocklistService } from '@app/shared/blocklist'
import { VideoService } from '@app/shared/video/video.service'
import { ActivatedRoute, Params, Router } from '@angular/router'
import { filter } from 'rxjs/operators'
import { environment } from 'src/environments/environment'

export type ProcessedVideoAbuse = VideoAbuse & {
moderationCommentHtml?: string,
Expand Down Expand Up @@ -259,12 +259,7 @@ export class VideoAbuseListComponent extends RestTable implements OnInit, AfterV
}

getVideoEmbed (videoAbuse: VideoAbuse) {
const absoluteAPIUrl = getAbsoluteAPIUrl()
const embedUrl = buildVideoLink({
baseUrl: absoluteAPIUrl + '/videos/embed/' + videoAbuse.video.uuid,
warningTitle: false
})
return buildVideoEmbed(embedUrl)
return buildVideoEmbed(`${environment.embedUrl}/videos/embed/${videoAbuse.video.uuid}`)
}

switchToDefaultAvatar ($event: Event) {
Expand Down
1 change: 1 addition & 0 deletions client/src/app/shared/video-abuse/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './video-abuse.service'
export * from './video-abuse-predefined-reasons.model'
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export enum PredefinedReasons {
violentOrRepulsive = 'violentOrRepulsive',
hatefulOrAbusive = 'hatefulOrAbusive',
spamOrMisleading = 'spamOrMisleading',
privacy = 'privacy',
rights = 'rights',
serverRules = 'serverRules',
thumbnails = 'thumbnails',
captions = 'captions'
}

export type VideoAbusePredefinedReasons = {
[key in PredefinedReasons]?: boolean
}
9 changes: 6 additions & 3 deletions client/src/app/shared/video-abuse/video-abuse.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Observable } from 'rxjs'
import { ResultList, VideoAbuse, VideoAbuseUpdate, VideoAbuseState } from '../../../../../shared'
import { environment } from '../../../environments/environment'
import { RestExtractor, RestPagination, RestService } from '../rest'
import { VideoAbusePredefinedReasons } from './video-abuse-predefined-reasons.model'

@Injectable()
export class VideoAbuseService {
Expand Down Expand Up @@ -63,9 +64,11 @@ export class VideoAbuseService {
)
}

reportVideo (id: number, reason: string) {
const url = VideoAbuseService.BASE_VIDEO_ABUSE_URL + id + '/abuse'
const body = { reason }
reportVideo (parameters: { id: number, reason: string, predefinedReasons?: VideoAbusePredefinedReasons, timestamp: any }) {
const url = VideoAbuseService.BASE_VIDEO_ABUSE_URL + parameters.id + '/abuse'

delete parameters.id
const body = { ...parameters }

return this.authHttp.post(url, body)
.pipe(
Expand Down
4 changes: 2 additions & 2 deletions client/src/app/shared/video/modals/video-block.component.html
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<ng-template #modal>
<div class="modal-header">
<h4 i18n class="modal-title">Blocklist video</h4>
<h4 i18n class="modal-title">Block video "{{ video.name }}"</h4>
<my-global-icon iconName="cross" aria-label="Close" role="button" (click)="hide()"></my-global-icon>
</div>

Expand All @@ -9,7 +9,7 @@ <h4 i18n class="modal-title">Blocklist video</h4>
<form novalidate [formGroup]="form" (ngSubmit)="block()">
<div class="form-group">
<textarea
i18n-placeholder placeholder="Reason..." formControlName="reason"
i18n-placeholder placeholder="Please describe the reason..." formControlName="reason"
[ngClass]="{ 'input-error': formErrors['reason'] }" class="form-control"
></textarea>
<div *ngIf="formErrors.reason" class="form-error">
Expand Down
105 changes: 82 additions & 23 deletions client/src/app/shared/video/modals/video-report.component.html
Original file line number Diff line number Diff line change
@@ -1,38 +1,97 @@
<ng-template #modal>
<div class="modal-header">
<h4 i18n class="modal-title">Report video</h4>
<h4 i18n class="modal-title">Report video "{{ video.name }}"</h4>
<my-global-icon iconName="cross" aria-label="Close" role="button" (click)="hide()"></my-global-icon>
</div>

<div class="modal-body">
<form novalidate [formGroup]="form" (ngSubmit)="report()">

<div i18n class="information">
Your report will be sent to moderators of {{ currentHost }}<ng-container *ngIf="isRemoteVideo()"> and will be forwarded to the video origin ({{ originHost }}) too</ng-container>.
</div>
<div class="row">
<div class="col-5 form-group">

<label i18n for="reportPredefinedReasons">What is the issue?</label>

<div class="ml-2 mt-2 d-flex flex-column">
<ng-container formGroupName="predefinedReasons">
<div class="form-group" *ngFor="let reason of predefinedReasons">
<my-peertube-checkbox formControlName="{{reason.id}}" labelText="{{reason.label}}">
<ng-template *ngIf="reason.help" ptTemplate="help">
<div [innerHTML]="reason.help"></div>
</ng-template>
<ng-container *ngIf="reason.description" ngProjectAs="description">
<div [innerHTML]="reason.description"></div>
</ng-container>
</my-peertube-checkbox>
</div>
</ng-container>
</div>

<form novalidate [formGroup]="form" (ngSubmit)="report()">
<div class="form-group">
<textarea
i18n-placeholder placeholder="Reason..." formControlName="reason"
[ngClass]="{ 'input-error': formErrors['reason'] }" class="form-control"
></textarea>
<div *ngIf="formErrors.reason" class="form-error">
{{ formErrors.reason }}
</div>
</div>

<div class="form-group inputs">
<input
type="button" role="button" i18n-value value="Cancel" class="action-button action-button-cancel"
(click)="hide()" (key.enter)="hide()"
>
<div class="col-7">
<div class="row justify-content-center">
<div class="col-12 col-lg-9 mb-2">
<div class="screenratio">
<div [innerHTML]="embedHtml"></div>
</div>
</div>
</div>

<div class="mb-1 start-at" formGroupName="timestamp">
<my-peertube-checkbox
formControlName="hasStart"
i18n-labelText labelText="Start at"
></my-peertube-checkbox>

<my-timestamp-input
[timestamp]="timestamp.startAt"
[maxTimestamp]="video.duration"
formControlName="startAt"
inputName="startAt"
>
</my-timestamp-input>
</div>

<div class="mb-3 stop-at" formGroupName="timestamp" *ngIf="timestamp.hasStart">
<my-peertube-checkbox
formControlName="hasEnd"
i18n-labelText labelText="Stop at"
></my-peertube-checkbox>

<input
type="submit" i18n-value value="Submit" class="action-button-submit"
[disabled]="!form.valid"
>
<my-timestamp-input
[timestamp]="timestamp.endAt"
[maxTimestamp]="video.duration"
formControlName="endAt"
inputName="endAt"
>
</my-timestamp-input>
</div>

<div i18n class="information">
Your report will be sent to moderators of {{ currentHost }}<ng-container *ngIf="isRemoteVideo()"> and will be forwarded to the video origin ({{ originHost }}) too</ng-container>.
</div>

<div class="form-group">
<textarea
i18n-placeholder placeholder="Please describe the issue..." formControlName="reason" ngbAutofocus
[ngClass]="{ 'input-error': formErrors['reason'] }" class="form-control"
></textarea>
<div *ngIf="formErrors.reason" class="form-error">
{{ formErrors.reason }}
</div>
</div>
</div>
</form>
</div>

<div class="form-group inputs">
<input
type="button" role="button" i18n-value value="Cancel" class="action-button action-button-cancel"
(click)="hide()" (key.enter)="hide()"
>
<input type="submit" i18n-value value="Submit" class="action-button-submit" [disabled]="!form.valid">
</div>

</form>
</div>
</ng-template>
17 changes: 17 additions & 0 deletions client/src/app/shared/video/modals/video-report.component.scss
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,20 @@
textarea {
@include peertube-textarea(100%, 100px);
}

.start-at,
.stop-at {
width: 300px;
display: flex;
align-items: center;

my-timestamp-input {
margin-left: 10px;
}
}

.screenratio {
@include large-screen-ratio($selector: 'div, ::ng-deep iframe') {
left: 0;
};
}
Loading

0 comments on commit 7333edd

Please sign in to comment.