Correctly truncate HTML
We can because we don't use the video truncated description since v5.0
This commit is contained in:
parent
e4f82eaa8b
commit
c5f8dc0533
|
@ -55,7 +55,7 @@
|
||||||
|
|
||||||
<my-markdown-textarea
|
<my-markdown-textarea
|
||||||
formControlName="description" [markdownVideo]="videoToUpdate"
|
formControlName="description" [markdownVideo]="videoToUpdate"
|
||||||
[formError]="formErrors.description" [truncate]="250"
|
[formError]="formErrors.description" [truncateTo3Lines]="true"
|
||||||
></my-markdown-textarea>
|
></my-markdown-textarea>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,25 +1,26 @@
|
||||||
<div class="video-info-description">
|
<div class="video-info-description">
|
||||||
<div
|
<div
|
||||||
|
#descriptionHTML
|
||||||
class="video-info-description-html"
|
class="video-info-description-html"
|
||||||
[innerHTML]="getHTMLDescription()"
|
[ngClass]="{ 'ellipsis-multiline-3': !completeDescriptionShown }"
|
||||||
|
[innerHTML]="videoHTMLDescription"
|
||||||
(timestampClicked)="onTimestampClicked($event)"
|
(timestampClicked)="onTimestampClicked($event)"
|
||||||
myTimestampRouteTransformer
|
myTimestampRouteTransformer
|
||||||
></div>
|
></div>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
*ngIf="completeDescriptionShown === false && video.description?.length >= 250"
|
*ngIf="(hasEllipsis() && !completeDescriptionShown) || completeDescriptionShown"
|
||||||
(click)="showMoreDescription()" class="video-info-description-more button-unstyle"
|
(click)="completeDescriptionShown = !completeDescriptionShown"
|
||||||
|
class="video-info-description-more button-unstyle"
|
||||||
>
|
>
|
||||||
<ng-container i18n>Show more</ng-container>
|
<ng-container *ngIf="!completeDescriptionShown">
|
||||||
<span *ngIf="descriptionLoading === false" class="chevron-down"></span>
|
<ng-container i18n>Show more</ng-container>
|
||||||
<my-loader size="sm" class="description-loading" [loading]="descriptionLoading"></my-loader>
|
<span class="chevron-down"></span>
|
||||||
</button>
|
</ng-container>
|
||||||
|
|
||||||
<button
|
<ng-container *ngIf="completeDescriptionShown">
|
||||||
*ngIf="completeDescriptionShown === true"
|
<ng-container i18n>Show less</ng-container>
|
||||||
(click)="showLessDescription()" class="video-info-description-more button-unstyle"
|
<span class="chevron-up"></span>
|
||||||
>
|
</ng-container>
|
||||||
<ng-container i18n>Show less</ng-container>
|
|
||||||
<span *ngIf="descriptionLoading === false" class="chevron-up"></span>
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -11,8 +11,14 @@
|
||||||
.video-info-description-html {
|
.video-info-description-html {
|
||||||
@include peertube-word-wrap;
|
@include peertube-word-wrap;
|
||||||
|
|
||||||
::ng-deep a {
|
::ng-deep {
|
||||||
text-decoration: none;
|
a {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
p:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,10 +27,12 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.video-info-description-more {
|
.video-info-description-more {
|
||||||
|
@include font-size(14px);
|
||||||
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-weight: $font-semibold;
|
font-weight: $font-semibold;
|
||||||
color: pvar(--greyForegroundColor);
|
color: pvar(--greyForegroundColor);
|
||||||
font-size: 14px;
|
margin-top: 1rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import { Component, EventEmitter, Input, OnChanges, Output } from '@angular/core'
|
import { Component, EventEmitter, Input, OnChanges, Output, ViewChild, ElementRef } from '@angular/core'
|
||||||
import { MarkdownService, Notifier } from '@app/core'
|
import { MarkdownService } from '@app/core'
|
||||||
import { VideoDetails, VideoService } from '@app/shared/shared-main'
|
import { VideoDetails } from '@app/shared/shared-main'
|
||||||
import { logger } from '@root-helpers/logger'
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'my-video-description',
|
selector: 'my-video-description',
|
||||||
|
@ -9,36 +8,34 @@ import { logger } from '@root-helpers/logger'
|
||||||
styleUrls: [ './video-description.component.scss' ]
|
styleUrls: [ './video-description.component.scss' ]
|
||||||
})
|
})
|
||||||
export class VideoDescriptionComponent implements OnChanges {
|
export class VideoDescriptionComponent implements OnChanges {
|
||||||
|
@ViewChild('descriptionHTML') descriptionHTML: ElementRef<HTMLElement>
|
||||||
|
|
||||||
@Input() video: VideoDetails
|
@Input() video: VideoDetails
|
||||||
|
|
||||||
@Output() timestampClicked = new EventEmitter<number>()
|
@Output() timestampClicked = new EventEmitter<number>()
|
||||||
|
|
||||||
descriptionLoading = false
|
|
||||||
completeDescriptionShown = false
|
completeDescriptionShown = false
|
||||||
|
|
||||||
completeVideoDescriptionLoaded = false
|
|
||||||
|
|
||||||
videoHTMLTruncatedDescription = ''
|
|
||||||
videoHTMLDescription = ''
|
videoHTMLDescription = ''
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
private videoService: VideoService,
|
|
||||||
private notifier: Notifier,
|
|
||||||
private markdownService: MarkdownService
|
private markdownService: MarkdownService
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
ngOnChanges () {
|
ngOnChanges () {
|
||||||
this.descriptionLoading = false
|
|
||||||
this.completeDescriptionShown = false
|
this.completeDescriptionShown = false
|
||||||
|
|
||||||
this.setVideoDescriptionHTML()
|
this.setVideoDescriptionHTML()
|
||||||
}
|
}
|
||||||
|
|
||||||
showMoreDescription () {
|
hasEllipsis () {
|
||||||
if (!this.completeVideoDescriptionLoaded) {
|
const el = this.descriptionHTML?.nativeElement
|
||||||
return this.loadCompleteDescription()
|
if (!el) return false
|
||||||
}
|
|
||||||
|
|
||||||
|
return el.offsetHeight < el.scrollHeight
|
||||||
|
}
|
||||||
|
|
||||||
|
showMoreDescription () {
|
||||||
this.completeDescriptionShown = true
|
this.completeDescriptionShown = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,51 +43,13 @@ export class VideoDescriptionComponent implements OnChanges {
|
||||||
this.completeDescriptionShown = false
|
this.completeDescriptionShown = false
|
||||||
}
|
}
|
||||||
|
|
||||||
loadCompleteDescription () {
|
|
||||||
this.descriptionLoading = true
|
|
||||||
|
|
||||||
this.videoService.loadCompleteDescription(this.video.descriptionPath)
|
|
||||||
.subscribe({
|
|
||||||
next: description => {
|
|
||||||
this.completeDescriptionShown = true
|
|
||||||
this.descriptionLoading = false
|
|
||||||
|
|
||||||
this.video.description = description
|
|
||||||
|
|
||||||
this.setVideoDescriptionHTML()
|
|
||||||
.catch(err => logger.error(err))
|
|
||||||
},
|
|
||||||
|
|
||||||
error: err => {
|
|
||||||
this.descriptionLoading = false
|
|
||||||
this.notifier.error(err.message)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
onTimestampClicked (timestamp: number) {
|
onTimestampClicked (timestamp: number) {
|
||||||
this.timestampClicked.emit(timestamp)
|
this.timestampClicked.emit(timestamp)
|
||||||
}
|
}
|
||||||
|
|
||||||
getHTMLDescription () {
|
|
||||||
if (this.completeDescriptionShown) {
|
|
||||||
return this.videoHTMLDescription
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.videoHTMLTruncatedDescription
|
|
||||||
}
|
|
||||||
|
|
||||||
private async setVideoDescriptionHTML () {
|
private async setVideoDescriptionHTML () {
|
||||||
{
|
const html = await this.markdownService.textMarkdownToHTML({ markdown: this.video.description })
|
||||||
const html = await this.markdownService.textMarkdownToHTML({ markdown: this.video.description })
|
|
||||||
|
|
||||||
this.videoHTMLDescription = this.markdownService.processVideoTimestamps(this.video.shortUUID, html)
|
this.videoHTMLDescription = this.markdownService.processVideoTimestamps(this.video.shortUUID, html)
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
const html = await this.markdownService.textMarkdownToHTML({ markdown: this.video.truncatedDescription })
|
|
||||||
|
|
||||||
this.videoHTMLTruncatedDescription = this.markdownService.processVideoTimestamps(this.video.shortUUID, html)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -138,8 +138,7 @@ export class MarkdownService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let html = this.markdownParsers[name].render(markdown)
|
const html = this.markdownParsers[name].render(markdown)
|
||||||
html = this.avoidTruncatedTags(html)
|
|
||||||
|
|
||||||
if (config.escape) return this.htmlRenderer.toSafeHtml(html, additionalAllowedTags)
|
if (config.escape) return this.htmlRenderer.toSafeHtml(html, additionalAllowedTags)
|
||||||
|
|
||||||
|
@ -181,11 +180,4 @@ export class MarkdownService {
|
||||||
return defaultRender(tokens, index, options, env, self)
|
return defaultRender(tokens, index, options, env, self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private avoidTruncatedTags (html: string) {
|
|
||||||
return html.replace(/\*\*?([^*]+)$/, '$1')
|
|
||||||
.replace(/<a[^>]+>([^<]+)<\/a>\s*...((<\/p>)|(<\/li>)|(<\/strong>))?$/mi, '$1...')
|
|
||||||
.replace(/\[[^\]]+\]\(([^)]+)$/m, '$1')
|
|
||||||
.replace(/\s?\[[^\]]+\]?[.]{3}<\/p>$/m, '...</p>')
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -96,7 +96,7 @@
|
||||||
<strong i18n>Comment:</strong>
|
<strong i18n>Comment:</strong>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div [innerHTML]="abuse.commentHtml"></div>
|
<div [innerHTML]="abuse.commentHTML"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -90,7 +90,8 @@
|
||||||
|
|
||||||
<ng-container *ngIf="abuse.comment">
|
<ng-container *ngIf="abuse.comment">
|
||||||
<td>
|
<td>
|
||||||
<a [href]="getCommentUrl(abuse)" [innerHTML]="abuse.truncatedCommentHtml" class="table-comment-link"
|
<a
|
||||||
|
[href]="getCommentUrl(abuse)" [innerHTML]="abuse.commentHTML" class="table-comment-link ellipsis-multiline-1"
|
||||||
[title]="abuse.comment.video.name" target="_blank" rel="noopener noreferrer"
|
[title]="abuse.comment.video.name" target="_blank" rel="noopener noreferrer"
|
||||||
></a>
|
></a>
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import * as debug from 'debug'
|
import * as debug from 'debug'
|
||||||
import truncate from 'lodash-es/truncate'
|
|
||||||
import { SortMeta } from 'primeng/api'
|
import { SortMeta } from 'primeng/api'
|
||||||
import { Component, Input, OnInit, ViewChild } from '@angular/core'
|
import { Component, Input, OnInit, ViewChild } from '@angular/core'
|
||||||
import { ActivatedRoute, Router } from '@angular/router'
|
import { ActivatedRoute, Router } from '@angular/router'
|
||||||
|
@ -211,11 +210,9 @@ export class AbuseListTableComponent extends RestTable implements OnInit {
|
||||||
|
|
||||||
if (abuse.comment) {
|
if (abuse.comment) {
|
||||||
if (abuse.comment.deleted) {
|
if (abuse.comment.deleted) {
|
||||||
abuse.truncatedCommentHtml = abuse.commentHtml = $localize`Deleted comment`
|
abuse.commentHTML = $localize`Deleted comment`
|
||||||
} else {
|
} else {
|
||||||
const truncated = truncate(abuse.comment.text, { length: 100 })
|
abuse.commentHTML = await this.markdownRenderer.textMarkdownToHTML({ markdown: abuse.comment.text, withHtml: true })
|
||||||
abuse.truncatedCommentHtml = await this.markdownRenderer.textMarkdownToHTML({ markdown: truncated, withHtml: true })
|
|
||||||
abuse.commentHtml = await this.markdownRenderer.textMarkdownToHTML({ markdown: abuse.comment.text, withHtml: true })
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,8 +12,7 @@ export type ProcessedAbuse = AdminAbuse & {
|
||||||
reporterAccount?: Account
|
reporterAccount?: Account
|
||||||
flaggedAccount?: Account
|
flaggedAccount?: Account
|
||||||
|
|
||||||
truncatedCommentHtml?: string
|
commentHTML?: string
|
||||||
commentHtml?: string
|
|
||||||
|
|
||||||
video: AdminAbuse['video'] & {
|
video: AdminAbuse['video'] & {
|
||||||
channel: AdminAbuse['video']['channel'] & {
|
channel: AdminAbuse['video']['channel'] & {
|
||||||
|
|
|
@ -8,11 +8,11 @@
|
||||||
</textarea>
|
</textarea>
|
||||||
|
|
||||||
<div ngbNav #nav="ngbNav" class="nav-pills nav-preview">
|
<div ngbNav #nav="ngbNav" class="nav-pills nav-preview">
|
||||||
<ng-container ngbNavItem *ngIf="truncate !== undefined">
|
<ng-container ngbNavItem *ngIf="truncateTo3Lines">
|
||||||
<a ngbNavLink i18n>Truncated preview</a>
|
<a ngbNavLink i18n>Truncated preview</a>
|
||||||
|
|
||||||
<ng-template ngbNavContent>
|
<ng-template ngbNavContent>
|
||||||
<div [innerHTML]="truncatedPreviewHTML"></div>
|
<div class="ellipsis-multiline-3" [innerHTML]="previewHTML"></div>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import truncate from 'lodash-es/truncate'
|
|
||||||
import { Subject } from 'rxjs'
|
import { Subject } from 'rxjs'
|
||||||
import { debounceTime, distinctUntilChanged } from 'rxjs/operators'
|
import { debounceTime, distinctUntilChanged } from 'rxjs/operators'
|
||||||
import { ViewportScroller } from '@angular/common'
|
import { ViewportScroller } from '@angular/common'
|
||||||
|
@ -26,7 +25,7 @@ export class MarkdownTextareaComponent implements ControlValueAccessor, OnInit {
|
||||||
|
|
||||||
@Input() formError: string
|
@Input() formError: string
|
||||||
|
|
||||||
@Input() truncate: number
|
@Input() truncateTo3Lines: boolean
|
||||||
|
|
||||||
@Input() markdownType: 'text' | 'enhanced' | 'to-unsafe-html' = 'text'
|
@Input() markdownType: 'text' | 'enhanced' | 'to-unsafe-html' = 'text'
|
||||||
@Input() customMarkdownRenderer?: (text: string) => Promise<string | HTMLElement>
|
@Input() customMarkdownRenderer?: (text: string) => Promise<string | HTMLElement>
|
||||||
|
@ -42,7 +41,6 @@ export class MarkdownTextareaComponent implements ControlValueAccessor, OnInit {
|
||||||
@ViewChild('textarea') textareaElement: ElementRef
|
@ViewChild('textarea') textareaElement: ElementRef
|
||||||
@ViewChild('previewElement') previewElement: ElementRef
|
@ViewChild('previewElement') previewElement: ElementRef
|
||||||
|
|
||||||
truncatedPreviewHTML: SafeHtml | string = ''
|
|
||||||
previewHTML: SafeHtml | string = ''
|
previewHTML: SafeHtml | string = ''
|
||||||
|
|
||||||
isMaximized = false
|
isMaximized = false
|
||||||
|
@ -129,7 +127,6 @@ export class MarkdownTextareaComponent implements ControlValueAccessor, OnInit {
|
||||||
private async updatePreviews () {
|
private async updatePreviews () {
|
||||||
if (this.content === null || this.content === undefined) return
|
if (this.content === null || this.content === undefined) return
|
||||||
|
|
||||||
this.truncatedPreviewHTML = await this.markdownRender(truncate(this.content, { length: this.truncate }))
|
|
||||||
this.previewHTML = await this.markdownRender(this.content)
|
this.previewHTML = await this.markdownRender(this.content)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -36,6 +36,10 @@
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ellipsis-multiline-1 {
|
||||||
|
@include ellipsis-multiline(1);
|
||||||
|
}
|
||||||
|
|
||||||
.ellipsis-multiline-2 {
|
.ellipsis-multiline-2 {
|
||||||
@include ellipsis-multiline(2);
|
@include ellipsis-multiline(2);
|
||||||
}
|
}
|
||||||
|
@ -45,3 +49,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
.ellipsis {
|
||||||
|
@include ellipsis;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue