mirror of https://github.com/Chocobozzz/PeerTube
Rich reporter field and video embed in moderation abuse list
parent
2bc9bd08cd
commit
d6af81469b
|
@ -8,18 +8,35 @@
|
||||||
|
|
||||||
.moderation-expanded-label {
|
.moderation-expanded-label {
|
||||||
font-weight: $font-semibold;
|
font-weight: $font-semibold;
|
||||||
min-width: 200px;
|
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
.moderation-expanded-text {
|
.moderation-expanded-text {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
word-wrap: break-word;
|
||||||
|
|
||||||
|
::ng-deep p:last-child {
|
||||||
|
margin-bottom: 0px !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.moderation-expanded {
|
.screenratio {
|
||||||
word-wrap: break-word;
|
position: relative;
|
||||||
overflow: visible !important;
|
width: 100%;
|
||||||
text-overflow: unset !important;
|
height: 0;
|
||||||
white-space: unset !important;
|
padding-bottom: 56%;
|
||||||
|
|
||||||
|
::ng-deep iframe {
|
||||||
|
position: absolute;
|
||||||
|
width: 100% !important;
|
||||||
|
height: 100% !important;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.chip {
|
||||||
|
@include chip;
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,8 +24,19 @@
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td>
|
<td>
|
||||||
<a [href]="videoAbuse.reporterAccount.url" i18n-title title="Go to the account" target="_blank" rel="noopener noreferrer">
|
<a [href]="videoAbuse.reporterAccount.url" i18n-title title="Open account in a new tab" target="_blank" rel="noopener noreferrer">
|
||||||
{{ createByString(videoAbuse.reporterAccount) }}
|
<div class="chip two-lines">
|
||||||
|
<img
|
||||||
|
class="avatar"
|
||||||
|
[src]="videoAbuse.reporterAccount.avatar.path"
|
||||||
|
(error)="switchToDefaultAvatar($event)"
|
||||||
|
alt="Avatar"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
{{ videoAbuse.reporterAccount.displayName }}
|
||||||
|
<span class="text-muted">{{ createByString(videoAbuse.reporterAccount) }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
|
@ -50,14 +61,22 @@
|
||||||
|
|
||||||
<ng-template pTemplate="rowexpansion" let-videoAbuse>
|
<ng-template pTemplate="rowexpansion" let-videoAbuse>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="moderation-expanded" colspan="6">
|
<td class="expand-cell" colspan="6">
|
||||||
<div>
|
<div class="d-flex">
|
||||||
<span i18n class="moderation-expanded-label">Reason:</span>
|
<div class="col-8">
|
||||||
<span class="moderation-expanded-text" [innerHTML]="videoAbuse.reasonHtml"></span>
|
<div class="d-flex">
|
||||||
|
<span class="col-3 moderation-expanded-label" i18n>Reason:</span>
|
||||||
|
<span class="col-9 moderation-expanded-text" [innerHTML]="videoAbuse.reasonHtml"></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>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-4">
|
||||||
|
<div class="screenratio" [innerHTML]="videoAbuse.embedHtml"></div>
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="videoAbuse.moderationComment">
|
|
||||||
<span i18n class="moderation-expanded-label">Moderation comment:</span>
|
|
||||||
<span class="moderation-expanded-text" [innerHTML]="videoAbuse.moderationCommentHtml"></span>
|
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
|
@ -10,6 +10,10 @@ import { ConfirmService } from '../../../core/index'
|
||||||
import { ModerationCommentModalComponent } from './moderation-comment-modal.component'
|
import { ModerationCommentModalComponent } from './moderation-comment-modal.component'
|
||||||
import { Video } from '../../../shared/video/video.model'
|
import { Video } from '../../../shared/video/video.model'
|
||||||
import { MarkdownService } from '@app/shared/renderer'
|
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 { DomSanitizer } from '@angular/platform-browser'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'my-video-abuse-list',
|
selector: 'my-video-abuse-list',
|
||||||
|
@ -32,7 +36,8 @@ export class VideoAbuseListComponent extends RestTable implements OnInit {
|
||||||
private videoAbuseService: VideoAbuseService,
|
private videoAbuseService: VideoAbuseService,
|
||||||
private confirmService: ConfirmService,
|
private confirmService: ConfirmService,
|
||||||
private i18n: I18n,
|
private i18n: I18n,
|
||||||
private markdownRenderer: MarkdownService
|
private markdownRenderer: MarkdownService,
|
||||||
|
private sanitizer: DomSanitizer
|
||||||
) {
|
) {
|
||||||
super()
|
super()
|
||||||
|
|
||||||
|
@ -42,8 +47,14 @@ export class VideoAbuseListComponent extends RestTable implements OnInit {
|
||||||
handler: videoAbuse => this.removeVideoAbuse(videoAbuse)
|
handler: videoAbuse => this.removeVideoAbuse(videoAbuse)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: this.i18n('Update moderation comment'),
|
label: this.i18n('Add note'),
|
||||||
handler: videoAbuse => this.openModerationCommentModal(videoAbuse)
|
handler: videoAbuse => this.openModerationCommentModal(videoAbuse),
|
||||||
|
isDisplayed: videoAbuse => !videoAbuse.moderationComment
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: this.i18n('Update note'),
|
||||||
|
handler: videoAbuse => this.openModerationCommentModal(videoAbuse),
|
||||||
|
isDisplayed: videoAbuse => !!videoAbuse.moderationComment
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: this.i18n('Mark as accepted'),
|
label: this.i18n('Mark as accepted'),
|
||||||
|
@ -90,6 +101,19 @@ export class VideoAbuseListComponent extends RestTable implements OnInit {
|
||||||
return Video.buildClientUrl(videoAbuse.video.uuid)
|
return Video.buildClientUrl(videoAbuse.video.uuid)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getVideoEmbed (videoAbuse: VideoAbuse) {
|
||||||
|
const absoluteAPIUrl = 'http://localhost:9000' || getAbsoluteAPIUrl()
|
||||||
|
const embedUrl = buildVideoLink({
|
||||||
|
baseUrl: absoluteAPIUrl + '/videos/embed/' + videoAbuse.video.uuid,
|
||||||
|
warningTitle: false
|
||||||
|
})
|
||||||
|
return buildVideoEmbed(embedUrl)
|
||||||
|
}
|
||||||
|
|
||||||
|
switchToDefaultAvatar ($event: Event) {
|
||||||
|
($event.target as HTMLImageElement).src = Actor.GET_DEFAULT_AVATAR_URL()
|
||||||
|
}
|
||||||
|
|
||||||
async removeVideoAbuse (videoAbuse: VideoAbuse) {
|
async removeVideoAbuse (videoAbuse: VideoAbuse) {
|
||||||
const res = await this.confirmService.confirm(this.i18n('Do you really want to delete this abuse report?'), this.i18n('Delete'))
|
const res = await this.confirmService.confirm(this.i18n('Do you really want to delete this abuse report?'), this.i18n('Delete'))
|
||||||
if (res === false) return
|
if (res === false) return
|
||||||
|
@ -125,7 +149,8 @@ export class VideoAbuseListComponent extends RestTable implements OnInit {
|
||||||
for (const abuse of this.videoAbuses) {
|
for (const abuse of this.videoAbuses) {
|
||||||
Object.assign(abuse, {
|
Object.assign(abuse, {
|
||||||
reasonHtml: await this.toHtml(abuse.reason),
|
reasonHtml: await this.toHtml(abuse.reason),
|
||||||
moderationCommentHtml: await this.toHtml(abuse.moderationComment)
|
moderationCommentHtml: await this.toHtml(abuse.moderationComment),
|
||||||
|
embedHtml: this.sanitizer.bypassSecurityTrustHtml(this.getVideoEmbed(abuse))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -42,9 +42,9 @@
|
||||||
|
|
||||||
<ng-template pTemplate="rowexpansion" let-videoBlacklist>
|
<ng-template pTemplate="rowexpansion" let-videoBlacklist>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="moderation-expanded" colspan="6">
|
<td class="expand-cell" colspan="6">
|
||||||
<span i18n class="moderation-expanded-label">Blacklist reason:</span>
|
<span class="col-2 moderation-expanded-label" i18n>Blacklist reason:</span>
|
||||||
<span class="moderation-expanded-text" [innerHTML]="videoBlacklist.reasonHtml"></span>
|
<span class="col-9 moderation-expanded-text" [innerHTML]="videoBlacklist.reasonHtml"></span>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
|
@ -51,19 +51,30 @@
|
||||||
<ng-template pTemplate="body" let-expanded="expanded" let-user>
|
<ng-template pTemplate="body" let-expanded="expanded" let-user>
|
||||||
|
|
||||||
<tr [pSelectableRow]="user" [ngClass]="{ banned: user.blocked }">
|
<tr [pSelectableRow]="user" [ngClass]="{ banned: user.blocked }">
|
||||||
<td class="expand-cell">
|
<td>
|
||||||
<p-tableCheckbox [value]="user"></p-tableCheckbox>
|
<p-tableCheckbox [value]="user"></p-tableCheckbox>
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td>
|
<td class="expand-cell">
|
||||||
<span *ngIf="user.blockedReason" class="expander" [pRowToggler]="user">
|
<span *ngIf="user.blockedReason" class="expander" [pRowToggler]="user">
|
||||||
<i [ngClass]="expanded ? 'glyphicon glyphicon-menu-down' : 'glyphicon glyphicon-menu-right'"></i>
|
<i [ngClass]="expanded ? 'glyphicon glyphicon-menu-down' : 'glyphicon glyphicon-menu-right'"></i>
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td>
|
<td>
|
||||||
<a i18n-title title="Go to the account page" target="_blank" rel="noopener noreferrer" [routerLink]="[ '/accounts/' + user.username ]">
|
<a i18n-title title="Open account in a new tab" target="_blank" rel="noopener noreferrer" [routerLink]="[ '/accounts/' + user.username ]">
|
||||||
{{ user.username }}
|
<div class="chip two-lines">
|
||||||
|
<img
|
||||||
|
class="avatar"
|
||||||
|
[src]="user?.account?.avatar?.path"
|
||||||
|
(error)="switchToDefaultAvatar($event)"
|
||||||
|
alt="Avatar"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
{{ user.account.displayName }}
|
||||||
|
<span class="text-muted">{{ user.username }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<span i18n *ngIf="user.blocked" class="banned-info">(banned)</span>
|
<span i18n *ngIf="user.blocked" class="banned-info">(banned)</span>
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
|
|
|
@ -29,3 +29,7 @@ p-tableCheckbox {
|
||||||
position: relative;
|
position: relative;
|
||||||
top: -2.5px;
|
top: -2.5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.chip {
|
||||||
|
@include chip;
|
||||||
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import { I18n } from '@ngx-translate/i18n-polyfill'
|
||||||
import { ServerConfig, User } from '../../../../../../shared'
|
import { ServerConfig, User } from '../../../../../../shared'
|
||||||
import { UserBanModalComponent } from '@app/shared/moderation'
|
import { UserBanModalComponent } from '@app/shared/moderation'
|
||||||
import { DropdownAction } from '@app/shared/buttons/action-dropdown.component'
|
import { DropdownAction } from '@app/shared/buttons/action-dropdown.component'
|
||||||
|
import { Actor } from '@app/shared/actor/actor.model'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'my-user-list',
|
selector: 'my-user-list',
|
||||||
|
@ -105,6 +106,10 @@ export class UserListComponent extends RestTable implements OnInit {
|
||||||
this.loadData()
|
this.loadData()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switchToDefaultAvatar ($event: Event) {
|
||||||
|
($event.target as HTMLImageElement).src = Actor.GET_DEFAULT_AVATAR_URL()
|
||||||
|
}
|
||||||
|
|
||||||
async unbanUsers (users: User[]) {
|
async unbanUsers (users: User[]) {
|
||||||
const message = this.i18n('Do you really want to unban {{num}} users?', { num: users.length })
|
const message = this.i18n('Do you really want to unban {{num}} users?', { num: users.length })
|
||||||
|
|
||||||
|
|
|
@ -56,7 +56,7 @@ export class MyAccountVideoChannelsComponent implements OnInit {
|
||||||
display: false,
|
display: false,
|
||||||
ticks: {
|
ticks: {
|
||||||
min: Math.max(0, this.videoChannelsMinimumDailyViews - (3 * this.videoChannelsMaximumDailyViews / 100)),
|
min: Math.max(0, this.videoChannelsMinimumDailyViews - (3 * this.videoChannelsMaximumDailyViews / 100)),
|
||||||
max: this.videoChannelsMaximumDailyViews
|
max: Math.max(1, this.videoChannelsMaximumDailyViews)
|
||||||
}
|
}
|
||||||
}]
|
}]
|
||||||
},
|
},
|
||||||
|
|
|
@ -796,3 +796,57 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@mixin chip {
|
||||||
|
$avatar-height: 1.2rem;
|
||||||
|
|
||||||
|
align-items: center;
|
||||||
|
border-radius: 5rem;
|
||||||
|
display: inline-flex;
|
||||||
|
font-size: 90%;
|
||||||
|
color: var(--mainForegroundColor);
|
||||||
|
height: $avatar-height;
|
||||||
|
line-height: .8rem;
|
||||||
|
margin: .1rem;
|
||||||
|
max-width: 320px;
|
||||||
|
overflow: hidden;
|
||||||
|
padding: .2rem .4rem;
|
||||||
|
text-decoration: none;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
vertical-align: middle;
|
||||||
|
white-space: nowrap;
|
||||||
|
|
||||||
|
.avatar {
|
||||||
|
margin-left: -.4rem;
|
||||||
|
margin-right: .2rem;
|
||||||
|
height: $avatar-height;
|
||||||
|
width: $avatar-height;
|
||||||
|
|
||||||
|
border-radius: 50%;
|
||||||
|
display: inline-block;
|
||||||
|
line-height: 1.25;
|
||||||
|
position: relative;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.two-lines {
|
||||||
|
$avatar-height: 1.8rem;
|
||||||
|
|
||||||
|
height: $avatar-height;
|
||||||
|
|
||||||
|
.avatar {
|
||||||
|
height: $avatar-height;
|
||||||
|
width: $avatar-height;
|
||||||
|
}
|
||||||
|
|
||||||
|
div {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
font-size: 80%;
|
||||||
|
height: $avatar-height;
|
||||||
|
margin-left: .1rem;
|
||||||
|
margin-right: .1rem;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue