]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/commitdiff
Correctly truncate HTML
authorChocobozzz <me@florianbigard.com>
Thu, 17 Aug 2023 12:26:49 +0000 (14:26 +0200)
committerChocobozzz <me@florianbigard.com>
Thu, 17 Aug 2023 12:34:49 +0000 (14:34 +0200)
We can because we don't use the video truncated description since v5.0

12 files changed:
client/src/app/+videos/+video-edit/shared/video-edit.component.html
client/src/app/+videos/+video-watch/shared/metadata/video-description.component.html
client/src/app/+videos/+video-watch/shared/metadata/video-description.component.scss
client/src/app/+videos/+video-watch/shared/metadata/video-description.component.ts
client/src/app/core/renderer/markdown.service.ts
client/src/app/shared/shared-abuse-list/abuse-details.component.html
client/src/app/shared/shared-abuse-list/abuse-list-table.component.html
client/src/app/shared/shared-abuse-list/abuse-list-table.component.ts
client/src/app/shared/shared-abuse-list/processed-abuse.model.ts
client/src/app/shared/shared-forms/markdown-textarea.component.html
client/src/app/shared/shared-forms/markdown-textarea.component.ts
client/src/sass/class-helpers/_text.scss

index 579b63c6dc85c9d166a386bbeb2fdcedc90f331a..ea99096125828f2ac22a3372fcece93cd7b1f786 100644 (file)
@@ -55,7 +55,7 @@
 
               <my-markdown-textarea
                 formControlName="description" [markdownVideo]="videoToUpdate"
-                [formError]="formErrors.description" [truncate]="250"
+                [formError]="formErrors.description" [truncateTo3Lines]="true"
               ></my-markdown-textarea>
             </div>
           </div>
index 9db3018e613ae16910ee7c7796266b790360f8ec..16f3e38811f1b4fce641af885ce0e99973ef0dd4 100644 (file)
@@ -1,25 +1,26 @@
 <div class="video-info-description">
   <div
+    #descriptionHTML
     class="video-info-description-html"
-    [innerHTML]="getHTMLDescription()"
+    [ngClass]="{ 'ellipsis-multiline-3': !completeDescriptionShown }"
+    [innerHTML]="videoHTMLDescription"
     (timestampClicked)="onTimestampClicked($event)"
     myTimestampRouteTransformer
   ></div>
 
   <button
-    *ngIf="completeDescriptionShown === false && video.description?.length >= 250"
-    (click)="showMoreDescription()" class="video-info-description-more button-unstyle"
+    *ngIf="(hasEllipsis() && !completeDescriptionShown) || completeDescriptionShown"
+    (click)="completeDescriptionShown = !completeDescriptionShown"
+    class="video-info-description-more button-unstyle"
   >
-    <ng-container i18n>Show more</ng-container>
-    <span *ngIf="descriptionLoading === false" class="chevron-down"></span>
-    <my-loader size="sm" class="description-loading" [loading]="descriptionLoading"></my-loader>
-  </button>
+    <ng-container *ngIf="!completeDescriptionShown">
+      <ng-container i18n>Show more</ng-container>
+      <span class="chevron-down"></span>
+    </ng-container>
 
-  <button
-    *ngIf="completeDescriptionShown === true"
-    (click)="showLessDescription()" class="video-info-description-more button-unstyle"
-  >
-    <ng-container i18n>Show less</ng-container>
-    <span *ngIf="descriptionLoading === false" class="chevron-up"></span>
+    <ng-container *ngIf="completeDescriptionShown">
+      <ng-container i18n>Show less</ng-container>
+      <span class="chevron-up"></span>
+    </ng-container>
   </button>
 </div>
index b503a94cb640d13c4d6500167040caa9d55c0489..d799ae2fc3bdc1d319cef481ef59d45a783b306c 100644 (file)
   .video-info-description-html {
     @include peertube-word-wrap;
 
-    ::ng-deep a {
-      text-decoration: none;
+    ::ng-deep {
+      a {
+        text-decoration: none;
+      }
+
+      p:last-child {
+        margin-bottom: 0;
+      }
     }
   }
 
   }
 
   .video-info-description-more {
+    @include font-size(14px);
+
     cursor: pointer;
     font-weight: $font-semibold;
     color: pvar(--greyForegroundColor);
-    font-size: 14px;
+    margin-top: 1rem;
   }
 }
 
index d0108061103bee479febf21812dd1308796ab53f..911cf72641ae4fcb2b975bc84812e7e3f650c063 100644 (file)
@@ -1,7 +1,6 @@
-import { Component, EventEmitter, Input, OnChanges, Output } from '@angular/core'
-import { MarkdownService, Notifier } from '@app/core'
-import { VideoDetails, VideoService } from '@app/shared/shared-main'
-import { logger } from '@root-helpers/logger'
+import { Component, EventEmitter, Input, OnChanges, Output, ViewChild, ElementRef } from '@angular/core'
+import { MarkdownService } from '@app/core'
+import { VideoDetails } from '@app/shared/shared-main'
 
 @Component({
   selector: 'my-video-description',
@@ -9,36 +8,34 @@ import { logger } from '@root-helpers/logger'
   styleUrls: [ './video-description.component.scss' ]
 })
 export class VideoDescriptionComponent implements OnChanges {
+  @ViewChild('descriptionHTML') descriptionHTML: ElementRef<HTMLElement>
+
   @Input() video: VideoDetails
 
   @Output() timestampClicked = new EventEmitter<number>()
 
-  descriptionLoading = false
   completeDescriptionShown = false
 
-  completeVideoDescriptionLoaded = false
-
-  videoHTMLTruncatedDescription = ''
   videoHTMLDescription = ''
 
   constructor (
-    private videoService: VideoService,
-    private notifier: Notifier,
     private markdownService: MarkdownService
   ) { }
 
   ngOnChanges () {
-    this.descriptionLoading = false
     this.completeDescriptionShown = false
 
     this.setVideoDescriptionHTML()
   }
 
-  showMoreDescription () {
-    if (!this.completeVideoDescriptionLoaded) {
-      return this.loadCompleteDescription()
-    }
+  hasEllipsis () {
+    const el = this.descriptionHTML?.nativeElement
+    if (!el) return false
+
+    return el.offsetHeight < el.scrollHeight
+  }
 
+  showMoreDescription () {
     this.completeDescriptionShown = true
   }
 
@@ -46,51 +43,13 @@ export class VideoDescriptionComponent implements OnChanges {
     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) {
     this.timestampClicked.emit(timestamp)
   }
 
-  getHTMLDescription () {
-    if (this.completeDescriptionShown) {
-      return this.videoHTMLDescription
-    }
-
-    return this.videoHTMLTruncatedDescription
-  }
-
   private async setVideoDescriptionHTML () {
-    {
-      const html = await this.markdownService.textMarkdownToHTML({ markdown: this.video.description })
-
-      this.videoHTMLDescription = this.markdownService.processVideoTimestamps(this.video.shortUUID, html)
-    }
-
-    {
-      const html = await this.markdownService.textMarkdownToHTML({ markdown: this.video.truncatedDescription })
+    const html = await this.markdownService.textMarkdownToHTML({ markdown: this.video.description })
 
-      this.videoHTMLTruncatedDescription = this.markdownService.processVideoTimestamps(this.video.shortUUID, html)
-    }
+    this.videoHTMLDescription = this.markdownService.processVideoTimestamps(this.video.shortUUID, html)
   }
 }
index 907b922327bb5f4165492b4863a53d0782cabed1..499ce520eeb536c42a6c4a7e935f7ccc8f3deb6d 100644 (file)
@@ -138,8 +138,7 @@ export class MarkdownService {
       }
     }
 
-    let html = this.markdownParsers[name].render(markdown)
-    html = this.avoidTruncatedTags(html)
+    const html = this.markdownParsers[name].render(markdown)
 
     if (config.escape) return this.htmlRenderer.toSafeHtml(html, additionalAllowedTags)
 
@@ -181,11 +180,4 @@ export class MarkdownService {
       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>')
-  }
 }
index 2d3e26a25ff69c88255706920e602021330e1c6a..5a4e60fe7c1740478fec23c6294b770ff7d4229d 100644 (file)
@@ -96,7 +96,7 @@
         <strong i18n>Comment:</strong>
       </div>
 
-      <div [innerHTML]="abuse.commentHtml"></div>
+      <div [innerHTML]="abuse.commentHTML"></div>
     </div>
   </div>
 </div>
index 5833e3465cf3d138d8ba951d5f50fef71cedbbc0..2532371226cf4e7c6beb058783111dfe7f7423ec 100644 (file)
@@ -90,7 +90,8 @@
 
       <ng-container *ngIf="abuse.comment">
         <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"
           ></a>
 
index c38e1286f83223208fd65d92b61c6f18527b9125..691859584b54a7b4197a92603fac0cdd1cc1d192 100644 (file)
@@ -1,5 +1,4 @@
 import * as debug from 'debug'
-import truncate from 'lodash-es/truncate'
 import { SortMeta } from 'primeng/api'
 import { Component, Input, OnInit, ViewChild } from '@angular/core'
 import { ActivatedRoute, Router } from '@angular/router'
@@ -211,11 +210,9 @@ export class AbuseListTableComponent extends RestTable implements OnInit {
 
           if (abuse.comment) {
             if (abuse.comment.deleted) {
-              abuse.truncatedCommentHtml = abuse.commentHtml = $localize`Deleted comment`
+              abuse.commentHTML = $localize`Deleted comment`
             } else {
-              const truncated = truncate(abuse.comment.text, { length: 100 })
-              abuse.truncatedCommentHtml = await this.markdownRenderer.textMarkdownToHTML({ markdown: truncated, withHtml: true })
-              abuse.commentHtml = await this.markdownRenderer.textMarkdownToHTML({ markdown: abuse.comment.text, withHtml: true })
+              abuse.commentHTML = await this.markdownRenderer.textMarkdownToHTML({ markdown: abuse.comment.text, withHtml: true })
             }
           }
 
index 076ccb40b760921b30a5120dadc732f13146570a..c51d6877ef09a1eff8608b53b437d955da84c120 100644 (file)
@@ -12,8 +12,7 @@ export type ProcessedAbuse = AdminAbuse & {
   reporterAccount?: Account
   flaggedAccount?: Account
 
-  truncatedCommentHtml?: string
-  commentHtml?: string
+  commentHTML?: string
 
   video: AdminAbuse['video'] & {
     channel: AdminAbuse['video']['channel'] & {
index 63dcdc5975ad6a5e4b397b6daa27335f7d76e231..ac2dfd17c55b8eae6022c4b1d05c194a0c1e78e3 100644 (file)
@@ -8,11 +8,11 @@
   </textarea>
 
   <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>
 
       <ng-template ngbNavContent>
-        <div [innerHTML]="truncatedPreviewHTML"></div>
+        <div class="ellipsis-multiline-3" [innerHTML]="previewHTML"></div>
       </ng-template>
     </ng-container>
 
index 7edcf868ca78e6b7c3b7de115a2343e73ee8ad7d..169be39d10b3c6c79a0059b8e091b5c3cc80b9c0 100644 (file)
@@ -1,4 +1,3 @@
-import truncate from 'lodash-es/truncate'
 import { Subject } from 'rxjs'
 import { debounceTime, distinctUntilChanged } from 'rxjs/operators'
 import { ViewportScroller } from '@angular/common'
@@ -26,7 +25,7 @@ export class MarkdownTextareaComponent implements ControlValueAccessor, OnInit {
 
   @Input() formError: string
 
-  @Input() truncate: number
+  @Input() truncateTo3Lines: boolean
 
   @Input() markdownType: 'text' | 'enhanced' | 'to-unsafe-html' = 'text'
   @Input() customMarkdownRenderer?: (text: string) => Promise<string | HTMLElement>
@@ -42,7 +41,6 @@ export class MarkdownTextareaComponent implements ControlValueAccessor, OnInit {
   @ViewChild('textarea') textareaElement: ElementRef
   @ViewChild('previewElement') previewElement: ElementRef
 
-  truncatedPreviewHTML: SafeHtml | string = ''
   previewHTML: SafeHtml | string = ''
 
   isMaximized = false
@@ -129,7 +127,6 @@ export class MarkdownTextareaComponent implements ControlValueAccessor, OnInit {
   private async updatePreviews () {
     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)
   }
 
index 0fe698d4f7cea7e7507bfd0bf0710b6a1ccad7a4..2231219fde2de86d00e39a298879309042ace9c3 100644 (file)
   overflow: hidden;
 }
 
+.ellipsis-multiline-1 {
+  @include ellipsis-multiline(1);
+}
+
 .ellipsis-multiline-2 {
   @include ellipsis-multiline(2);
 }
@@ -45,3 +49,7 @@
 }
 
 // ---------------------------------------------------------------------------
+
+.ellipsis {
+  @include ellipsis;
+}