Correctly truncate HTML

We can because we don't use the video truncated description since v5.0
This commit is contained in:
Chocobozzz 2023-08-17 14:26:49 +02:00
parent e4f82eaa8b
commit c5f8dc0533
No known key found for this signature in database
GPG Key ID: 583A612D890159BE
12 changed files with 58 additions and 96 deletions

View File

@ -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>

View File

@ -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>

View File

@ -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;
} }
} }

View File

@ -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)
}
} }
} }

View File

@ -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>')
}
} }

View File

@ -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>

View File

@ -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>

View File

@ -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 })
} }
} }

View File

@ -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'] & {

View File

@ -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>

View File

@ -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)
} }

View File

@ -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;
}