.moderation-expanded-label {
font-weight: $font-semibold;
- min-width: 200px;
display: inline-block;
vertical-align: top;
+ text-align: right;
}
.moderation-expanded-text {
display: inline-block;
+ word-wrap: break-word;
+
+ ::ng-deep p:last-child {
+ margin-bottom: 0px !important;
+ }
}
-.moderation-expanded {
- word-wrap: break-word;
- overflow: visible !important;
- text-overflow: unset !important;
- white-space: unset !important;
+.screenratio {
+ position: relative;
+ width: 100%;
+ height: 0;
+ padding-bottom: 56%;
+
+ ::ng-deep iframe {
+ position: absolute;
+ width: 100% !important;
+ height: 100% !important;
+ left: 0;
+ top: 0;
+ }
+}
+
+.chip {
+ @include chip;
}
</td>
<td>
- <a [href]="videoAbuse.reporterAccount.url" i18n-title title="Go to the account" target="_blank" rel="noopener noreferrer">
- {{ createByString(videoAbuse.reporterAccount) }}
+ <a [href]="videoAbuse.reporterAccount.url" i18n-title title="Open account in a new tab" target="_blank" rel="noopener noreferrer">
+ <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>
</td>
<ng-template pTemplate="rowexpansion" let-videoAbuse>
<tr>
- <td class="moderation-expanded" colspan="6">
- <div>
- <span i18n class="moderation-expanded-label">Reason:</span>
- <span class="moderation-expanded-text" [innerHTML]="videoAbuse.reasonHtml"></span>
- </div>
- <div *ngIf="videoAbuse.moderationComment">
- <span i18n class="moderation-expanded-label">Moderation comment:</span>
- <span class="moderation-expanded-text" [innerHTML]="videoAbuse.moderationCommentHtml"></span>
+ <td class="expand-cell" colspan="6">
+ <div class="d-flex">
+ <div class="col-8">
+ <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>
</td>
</tr>
import { ModerationCommentModalComponent } from './moderation-comment-modal.component'
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 { DomSanitizer } from '@angular/platform-browser'
@Component({
selector: 'my-video-abuse-list',
private videoAbuseService: VideoAbuseService,
private confirmService: ConfirmService,
private i18n: I18n,
- private markdownRenderer: MarkdownService
+ private markdownRenderer: MarkdownService,
+ private sanitizer: DomSanitizer
) {
super()
handler: videoAbuse => this.removeVideoAbuse(videoAbuse)
},
{
- label: this.i18n('Update moderation comment'),
- handler: videoAbuse => this.openModerationCommentModal(videoAbuse)
+ label: this.i18n('Add note'),
+ 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'),
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) {
const res = await this.confirmService.confirm(this.i18n('Do you really want to delete this abuse report?'), this.i18n('Delete'))
if (res === false) return
for (const abuse of this.videoAbuses) {
Object.assign(abuse, {
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))
})
}
<ng-template pTemplate="rowexpansion" let-videoBlacklist>
<tr>
- <td class="moderation-expanded" colspan="6">
- <span i18n class="moderation-expanded-label">Blacklist reason:</span>
- <span class="moderation-expanded-text" [innerHTML]="videoBlacklist.reasonHtml"></span>
+ <td class="expand-cell" colspan="6">
+ <span class="col-2 moderation-expanded-label" i18n>Blacklist reason:</span>
+ <span class="col-9 moderation-expanded-text" [innerHTML]="videoBlacklist.reasonHtml"></span>
</td>
</tr>
</ng-template>
<ng-template pTemplate="body" let-expanded="expanded" let-user>
<tr [pSelectableRow]="user" [ngClass]="{ banned: user.blocked }">
- <td class="expand-cell">
+ <td>
<p-tableCheckbox [value]="user"></p-tableCheckbox>
</td>
- <td>
+ <td class="expand-cell">
<span *ngIf="user.blockedReason" class="expander" [pRowToggler]="user">
<i [ngClass]="expanded ? 'glyphicon glyphicon-menu-down' : 'glyphicon glyphicon-menu-right'"></i>
</span>
</td>
<td>
- <a i18n-title title="Go to the account page" target="_blank" rel="noopener noreferrer" [routerLink]="[ '/accounts/' + user.username ]">
- {{ user.username }}
+ <a i18n-title title="Open account in a new tab" target="_blank" rel="noopener noreferrer" [routerLink]="[ '/accounts/' + 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>
</a>
</td>
position: relative;
top: -2.5px;
}
+
+.chip {
+ @include chip;
+}
import { ServerConfig, User } from '../../../../../../shared'
import { UserBanModalComponent } from '@app/shared/moderation'
import { DropdownAction } from '@app/shared/buttons/action-dropdown.component'
+import { Actor } from '@app/shared/actor/actor.model'
@Component({
selector: 'my-user-list',
this.loadData()
}
+ switchToDefaultAvatar ($event: Event) {
+ ($event.target as HTMLImageElement).src = Actor.GET_DEFAULT_AVATAR_URL()
+ }
+
async unbanUsers (users: User[]) {
const message = this.i18n('Do you really want to unban {{num}} users?', { num: users.length })
display: false,
ticks: {
min: Math.max(0, this.videoChannelsMinimumDailyViews - (3 * this.videoChannelsMaximumDailyViews / 100)),
- max: this.videoChannelsMaximumDailyViews
+ max: Math.max(1, this.videoChannelsMaximumDailyViews)
}
}]
},
}
}
}
+
+@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;
+ }
+ }
+}