aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--client/src/app/+videos/+video-edit/shared/video-edit.component.html2
-rw-r--r--client/src/app/+videos/+video-watch/shared/metadata/video-description.component.html27
-rw-r--r--client/src/app/+videos/+video-watch/shared/metadata/video-description.component.scss14
-rw-r--r--client/src/app/+videos/+video-watch/shared/metadata/video-description.component.ts69
-rw-r--r--client/src/app/core/renderer/markdown.service.ts10
-rw-r--r--client/src/app/shared/shared-abuse-list/abuse-details.component.html2
-rw-r--r--client/src/app/shared/shared-abuse-list/abuse-list-table.component.html3
-rw-r--r--client/src/app/shared/shared-abuse-list/abuse-list-table.component.ts7
-rw-r--r--client/src/app/shared/shared-abuse-list/processed-abuse.model.ts3
-rw-r--r--client/src/app/shared/shared-forms/markdown-textarea.component.html4
-rw-r--r--client/src/app/shared/shared-forms/markdown-textarea.component.ts5
-rw-r--r--client/src/sass/class-helpers/_text.scss8
12 files changed, 58 insertions, 96 deletions
diff --git a/client/src/app/+videos/+video-edit/shared/video-edit.component.html b/client/src/app/+videos/+video-edit/shared/video-edit.component.html
index 579b63c6d..ea9909612 100644
--- a/client/src/app/+videos/+video-edit/shared/video-edit.component.html
+++ b/client/src/app/+videos/+video-edit/shared/video-edit.component.html
@@ -55,7 +55,7 @@
55 55
56 <my-markdown-textarea 56 <my-markdown-textarea
57 formControlName="description" [markdownVideo]="videoToUpdate" 57 formControlName="description" [markdownVideo]="videoToUpdate"
58 [formError]="formErrors.description" [truncate]="250" 58 [formError]="formErrors.description" [truncateTo3Lines]="true"
59 ></my-markdown-textarea> 59 ></my-markdown-textarea>
60 </div> 60 </div>
61 </div> 61 </div>
diff --git a/client/src/app/+videos/+video-watch/shared/metadata/video-description.component.html b/client/src/app/+videos/+video-watch/shared/metadata/video-description.component.html
index 9db3018e6..16f3e3881 100644
--- a/client/src/app/+videos/+video-watch/shared/metadata/video-description.component.html
+++ b/client/src/app/+videos/+video-watch/shared/metadata/video-description.component.html
@@ -1,25 +1,26 @@
1<div class="video-info-description"> 1<div class="video-info-description">
2 <div 2 <div
3 #descriptionHTML
3 class="video-info-description-html" 4 class="video-info-description-html"
4 [innerHTML]="getHTMLDescription()" 5 [ngClass]="{ 'ellipsis-multiline-3': !completeDescriptionShown }"
6 [innerHTML]="videoHTMLDescription"
5 (timestampClicked)="onTimestampClicked($event)" 7 (timestampClicked)="onTimestampClicked($event)"
6 myTimestampRouteTransformer 8 myTimestampRouteTransformer
7 ></div> 9 ></div>
8 10
9 <button 11 <button
10 *ngIf="completeDescriptionShown === false && video.description?.length >= 250" 12 *ngIf="(hasEllipsis() && !completeDescriptionShown) || completeDescriptionShown"
11 (click)="showMoreDescription()" class="video-info-description-more button-unstyle" 13 (click)="completeDescriptionShown = !completeDescriptionShown"
14 class="video-info-description-more button-unstyle"
12 > 15 >
13 <ng-container i18n>Show more</ng-container> 16 <ng-container *ngIf="!completeDescriptionShown">
14 <span *ngIf="descriptionLoading === false" class="chevron-down"></span> 17 <ng-container i18n>Show more</ng-container>
15 <my-loader size="sm" class="description-loading" [loading]="descriptionLoading"></my-loader> 18 <span class="chevron-down"></span>
16 </button> 19 </ng-container>
17 20
18 <button 21 <ng-container *ngIf="completeDescriptionShown">
19 *ngIf="completeDescriptionShown === true" 22 <ng-container i18n>Show less</ng-container>
20 (click)="showLessDescription()" class="video-info-description-more button-unstyle" 23 <span class="chevron-up"></span>
21 > 24 </ng-container>
22 <ng-container i18n>Show less</ng-container>
23 <span *ngIf="descriptionLoading === false" class="chevron-up"></span>
24 </button> 25 </button>
25</div> 26</div>
diff --git a/client/src/app/+videos/+video-watch/shared/metadata/video-description.component.scss b/client/src/app/+videos/+video-watch/shared/metadata/video-description.component.scss
index b503a94cb..d799ae2fc 100644
--- a/client/src/app/+videos/+video-watch/shared/metadata/video-description.component.scss
+++ b/client/src/app/+videos/+video-watch/shared/metadata/video-description.component.scss
@@ -11,8 +11,14 @@
11 .video-info-description-html { 11 .video-info-description-html {
12 @include peertube-word-wrap; 12 @include peertube-word-wrap;
13 13
14 ::ng-deep a { 14 ::ng-deep {
15 text-decoration: none; 15 a {
16 text-decoration: none;
17 }
18
19 p:last-child {
20 margin-bottom: 0;
21 }
16 } 22 }
17 } 23 }
18 24
@@ -21,10 +27,12 @@
21 } 27 }
22 28
23 .video-info-description-more { 29 .video-info-description-more {
30 @include font-size(14px);
31
24 cursor: pointer; 32 cursor: pointer;
25 font-weight: $font-semibold; 33 font-weight: $font-semibold;
26 color: pvar(--greyForegroundColor); 34 color: pvar(--greyForegroundColor);
27 font-size: 14px; 35 margin-top: 1rem;
28 } 36 }
29} 37}
30 38
diff --git a/client/src/app/+videos/+video-watch/shared/metadata/video-description.component.ts b/client/src/app/+videos/+video-watch/shared/metadata/video-description.component.ts
index d01080611..911cf7264 100644
--- a/client/src/app/+videos/+video-watch/shared/metadata/video-description.component.ts
+++ b/client/src/app/+videos/+video-watch/shared/metadata/video-description.component.ts
@@ -1,7 +1,6 @@
1import { Component, EventEmitter, Input, OnChanges, Output } from '@angular/core' 1import { Component, EventEmitter, Input, OnChanges, Output, ViewChild, ElementRef } from '@angular/core'
2import { MarkdownService, Notifier } from '@app/core' 2import { MarkdownService } from '@app/core'
3import { VideoDetails, VideoService } from '@app/shared/shared-main' 3import { VideoDetails } from '@app/shared/shared-main'
4import { logger } from '@root-helpers/logger'
5 4
6@Component({ 5@Component({
7 selector: 'my-video-description', 6 selector: 'my-video-description',
@@ -9,36 +8,34 @@ import { logger } from '@root-helpers/logger'
9 styleUrls: [ './video-description.component.scss' ] 8 styleUrls: [ './video-description.component.scss' ]
10}) 9})
11export class VideoDescriptionComponent implements OnChanges { 10export class VideoDescriptionComponent implements OnChanges {
11 @ViewChild('descriptionHTML') descriptionHTML: ElementRef<HTMLElement>
12
12 @Input() video: VideoDetails 13 @Input() video: VideoDetails
13 14
14 @Output() timestampClicked = new EventEmitter<number>() 15 @Output() timestampClicked = new EventEmitter<number>()
15 16
16 descriptionLoading = false
17 completeDescriptionShown = false 17 completeDescriptionShown = false
18 18
19 completeVideoDescriptionLoaded = false
20
21 videoHTMLTruncatedDescription = ''
22 videoHTMLDescription = '' 19 videoHTMLDescription = ''
23 20
24 constructor ( 21 constructor (
25 private videoService: VideoService,
26 private notifier: Notifier,
27 private markdownService: MarkdownService 22 private markdownService: MarkdownService
28 ) { } 23 ) { }
29 24
30 ngOnChanges () { 25 ngOnChanges () {
31 this.descriptionLoading = false
32 this.completeDescriptionShown = false 26 this.completeDescriptionShown = false
33 27
34 this.setVideoDescriptionHTML() 28 this.setVideoDescriptionHTML()
35 } 29 }
36 30
37 showMoreDescription () { 31 hasEllipsis () {
38 if (!this.completeVideoDescriptionLoaded) { 32 const el = this.descriptionHTML?.nativeElement
39 return this.loadCompleteDescription() 33 if (!el) return false
40 } 34
35 return el.offsetHeight < el.scrollHeight
36 }
41 37
38 showMoreDescription () {
42 this.completeDescriptionShown = true 39 this.completeDescriptionShown = true
43 } 40 }
44 41
@@ -46,51 +43,13 @@ export class VideoDescriptionComponent implements OnChanges {
46 this.completeDescriptionShown = false 43 this.completeDescriptionShown = false
47 } 44 }
48 45
49 loadCompleteDescription () {
50 this.descriptionLoading = true
51
52 this.videoService.loadCompleteDescription(this.video.descriptionPath)
53 .subscribe({
54 next: description => {
55 this.completeDescriptionShown = true
56 this.descriptionLoading = false
57
58 this.video.description = description
59
60 this.setVideoDescriptionHTML()
61 .catch(err => logger.error(err))
62 },
63
64 error: err => {
65 this.descriptionLoading = false
66 this.notifier.error(err.message)
67 }
68 })
69 }
70
71 onTimestampClicked (timestamp: number) { 46 onTimestampClicked (timestamp: number) {
72 this.timestampClicked.emit(timestamp) 47 this.timestampClicked.emit(timestamp)
73 } 48 }
74 49
75 getHTMLDescription () {
76 if (this.completeDescriptionShown) {
77 return this.videoHTMLDescription
78 }
79
80 return this.videoHTMLTruncatedDescription
81 }
82
83 private async setVideoDescriptionHTML () { 50 private async setVideoDescriptionHTML () {
84 { 51 const html = await this.markdownService.textMarkdownToHTML({ markdown: this.video.description })
85 const html = await this.markdownService.textMarkdownToHTML({ markdown: this.video.description })
86
87 this.videoHTMLDescription = this.markdownService.processVideoTimestamps(this.video.shortUUID, html)
88 }
89
90 {
91 const html = await this.markdownService.textMarkdownToHTML({ markdown: this.video.truncatedDescription })
92 52
93 this.videoHTMLTruncatedDescription = this.markdownService.processVideoTimestamps(this.video.shortUUID, html) 53 this.videoHTMLDescription = this.markdownService.processVideoTimestamps(this.video.shortUUID, html)
94 }
95 } 54 }
96} 55}
diff --git a/client/src/app/core/renderer/markdown.service.ts b/client/src/app/core/renderer/markdown.service.ts
index 907b92232..499ce520e 100644
--- a/client/src/app/core/renderer/markdown.service.ts
+++ b/client/src/app/core/renderer/markdown.service.ts
@@ -138,8 +138,7 @@ export class MarkdownService {
138 } 138 }
139 } 139 }
140 140
141 let html = this.markdownParsers[name].render(markdown) 141 const html = this.markdownParsers[name].render(markdown)
142 html = this.avoidTruncatedTags(html)
143 142
144 if (config.escape) return this.htmlRenderer.toSafeHtml(html, additionalAllowedTags) 143 if (config.escape) return this.htmlRenderer.toSafeHtml(html, additionalAllowedTags)
145 144
@@ -181,11 +180,4 @@ export class MarkdownService {
181 return defaultRender(tokens, index, options, env, self) 180 return defaultRender(tokens, index, options, env, self)
182 } 181 }
183 } 182 }
184
185 private avoidTruncatedTags (html: string) {
186 return html.replace(/\*\*?([^*]+)$/, '$1')
187 .replace(/<a[^>]+>([^<]+)<\/a>\s*...((<\/p>)|(<\/li>)|(<\/strong>))?$/mi, '$1...')
188 .replace(/\[[^\]]+\]\(([^)]+)$/m, '$1')
189 .replace(/\s?\[[^\]]+\]?[.]{3}<\/p>$/m, '...</p>')
190 }
191} 183}
diff --git a/client/src/app/shared/shared-abuse-list/abuse-details.component.html b/client/src/app/shared/shared-abuse-list/abuse-details.component.html
index 2d3e26a25..5a4e60fe7 100644
--- a/client/src/app/shared/shared-abuse-list/abuse-details.component.html
+++ b/client/src/app/shared/shared-abuse-list/abuse-details.component.html
@@ -96,7 +96,7 @@
96 <strong i18n>Comment:</strong> 96 <strong i18n>Comment:</strong>
97 </div> 97 </div>
98 98
99 <div [innerHTML]="abuse.commentHtml"></div> 99 <div [innerHTML]="abuse.commentHTML"></div>
100 </div> 100 </div>
101 </div> 101 </div>
102</div> 102</div>
diff --git a/client/src/app/shared/shared-abuse-list/abuse-list-table.component.html b/client/src/app/shared/shared-abuse-list/abuse-list-table.component.html
index 5833e3465..253237122 100644
--- a/client/src/app/shared/shared-abuse-list/abuse-list-table.component.html
+++ b/client/src/app/shared/shared-abuse-list/abuse-list-table.component.html
@@ -90,7 +90,8 @@
90 90
91 <ng-container *ngIf="abuse.comment"> 91 <ng-container *ngIf="abuse.comment">
92 <td> 92 <td>
93 <a [href]="getCommentUrl(abuse)" [innerHTML]="abuse.truncatedCommentHtml" class="table-comment-link" 93 <a
94 [href]="getCommentUrl(abuse)" [innerHTML]="abuse.commentHTML" class="table-comment-link ellipsis-multiline-1"
94 [title]="abuse.comment.video.name" target="_blank" rel="noopener noreferrer" 95 [title]="abuse.comment.video.name" target="_blank" rel="noopener noreferrer"
95 ></a> 96 ></a>
96 97
diff --git a/client/src/app/shared/shared-abuse-list/abuse-list-table.component.ts b/client/src/app/shared/shared-abuse-list/abuse-list-table.component.ts
index c38e1286f..691859584 100644
--- a/client/src/app/shared/shared-abuse-list/abuse-list-table.component.ts
+++ b/client/src/app/shared/shared-abuse-list/abuse-list-table.component.ts
@@ -1,5 +1,4 @@
1import * as debug from 'debug' 1import * as debug from 'debug'
2import truncate from 'lodash-es/truncate'
3import { SortMeta } from 'primeng/api' 2import { SortMeta } from 'primeng/api'
4import { Component, Input, OnInit, ViewChild } from '@angular/core' 3import { Component, Input, OnInit, ViewChild } from '@angular/core'
5import { ActivatedRoute, Router } from '@angular/router' 4import { ActivatedRoute, Router } from '@angular/router'
@@ -211,11 +210,9 @@ export class AbuseListTableComponent extends RestTable implements OnInit {
211 210
212 if (abuse.comment) { 211 if (abuse.comment) {
213 if (abuse.comment.deleted) { 212 if (abuse.comment.deleted) {
214 abuse.truncatedCommentHtml = abuse.commentHtml = $localize`Deleted comment` 213 abuse.commentHTML = $localize`Deleted comment`
215 } else { 214 } else {
216 const truncated = truncate(abuse.comment.text, { length: 100 }) 215 abuse.commentHTML = await this.markdownRenderer.textMarkdownToHTML({ markdown: abuse.comment.text, withHtml: true })
217 abuse.truncatedCommentHtml = await this.markdownRenderer.textMarkdownToHTML({ markdown: truncated, withHtml: true })
218 abuse.commentHtml = await this.markdownRenderer.textMarkdownToHTML({ markdown: abuse.comment.text, withHtml: true })
219 } 216 }
220 } 217 }
221 218
diff --git a/client/src/app/shared/shared-abuse-list/processed-abuse.model.ts b/client/src/app/shared/shared-abuse-list/processed-abuse.model.ts
index 076ccb40b..c51d6877e 100644
--- a/client/src/app/shared/shared-abuse-list/processed-abuse.model.ts
+++ b/client/src/app/shared/shared-abuse-list/processed-abuse.model.ts
@@ -12,8 +12,7 @@ export type ProcessedAbuse = AdminAbuse & {
12 reporterAccount?: Account 12 reporterAccount?: Account
13 flaggedAccount?: Account 13 flaggedAccount?: Account
14 14
15 truncatedCommentHtml?: string 15 commentHTML?: string
16 commentHtml?: string
17 16
18 video: AdminAbuse['video'] & { 17 video: AdminAbuse['video'] & {
19 channel: AdminAbuse['video']['channel'] & { 18 channel: AdminAbuse['video']['channel'] & {
diff --git a/client/src/app/shared/shared-forms/markdown-textarea.component.html b/client/src/app/shared/shared-forms/markdown-textarea.component.html
index 63dcdc597..ac2dfd17c 100644
--- a/client/src/app/shared/shared-forms/markdown-textarea.component.html
+++ b/client/src/app/shared/shared-forms/markdown-textarea.component.html
@@ -8,11 +8,11 @@
8 </textarea> 8 </textarea>
9 9
10 <div ngbNav #nav="ngbNav" class="nav-pills nav-preview"> 10 <div ngbNav #nav="ngbNav" class="nav-pills nav-preview">
11 <ng-container ngbNavItem *ngIf="truncate !== undefined"> 11 <ng-container ngbNavItem *ngIf="truncateTo3Lines">
12 <a ngbNavLink i18n>Truncated preview</a> 12 <a ngbNavLink i18n>Truncated preview</a>
13 13
14 <ng-template ngbNavContent> 14 <ng-template ngbNavContent>
15 <div [innerHTML]="truncatedPreviewHTML"></div> 15 <div class="ellipsis-multiline-3" [innerHTML]="previewHTML"></div>
16 </ng-template> 16 </ng-template>
17 </ng-container> 17 </ng-container>
18 18
diff --git a/client/src/app/shared/shared-forms/markdown-textarea.component.ts b/client/src/app/shared/shared-forms/markdown-textarea.component.ts
index 7edcf868c..169be39d1 100644
--- a/client/src/app/shared/shared-forms/markdown-textarea.component.ts
+++ b/client/src/app/shared/shared-forms/markdown-textarea.component.ts
@@ -1,4 +1,3 @@
1import truncate from 'lodash-es/truncate'
2import { Subject } from 'rxjs' 1import { Subject } from 'rxjs'
3import { debounceTime, distinctUntilChanged } from 'rxjs/operators' 2import { debounceTime, distinctUntilChanged } from 'rxjs/operators'
4import { ViewportScroller } from '@angular/common' 3import { ViewportScroller } from '@angular/common'
@@ -26,7 +25,7 @@ export class MarkdownTextareaComponent implements ControlValueAccessor, OnInit {
26 25
27 @Input() formError: string 26 @Input() formError: string
28 27
29 @Input() truncate: number 28 @Input() truncateTo3Lines: boolean
30 29
31 @Input() markdownType: 'text' | 'enhanced' | 'to-unsafe-html' = 'text' 30 @Input() markdownType: 'text' | 'enhanced' | 'to-unsafe-html' = 'text'
32 @Input() customMarkdownRenderer?: (text: string) => Promise<string | HTMLElement> 31 @Input() customMarkdownRenderer?: (text: string) => Promise<string | HTMLElement>
@@ -42,7 +41,6 @@ export class MarkdownTextareaComponent implements ControlValueAccessor, OnInit {
42 @ViewChild('textarea') textareaElement: ElementRef 41 @ViewChild('textarea') textareaElement: ElementRef
43 @ViewChild('previewElement') previewElement: ElementRef 42 @ViewChild('previewElement') previewElement: ElementRef
44 43
45 truncatedPreviewHTML: SafeHtml | string = ''
46 previewHTML: SafeHtml | string = '' 44 previewHTML: SafeHtml | string = ''
47 45
48 isMaximized = false 46 isMaximized = false
@@ -129,7 +127,6 @@ export class MarkdownTextareaComponent implements ControlValueAccessor, OnInit {
129 private async updatePreviews () { 127 private async updatePreviews () {
130 if (this.content === null || this.content === undefined) return 128 if (this.content === null || this.content === undefined) return
131 129
132 this.truncatedPreviewHTML = await this.markdownRender(truncate(this.content, { length: this.truncate }))
133 this.previewHTML = await this.markdownRender(this.content) 130 this.previewHTML = await this.markdownRender(this.content)
134 } 131 }
135 132
diff --git a/client/src/sass/class-helpers/_text.scss b/client/src/sass/class-helpers/_text.scss
index 0fe698d4f..2231219fd 100644
--- a/client/src/sass/class-helpers/_text.scss
+++ b/client/src/sass/class-helpers/_text.scss
@@ -36,6 +36,10 @@
36 overflow: hidden; 36 overflow: hidden;
37} 37}
38 38
39.ellipsis-multiline-1 {
40 @include ellipsis-multiline(1);
41}
42
39.ellipsis-multiline-2 { 43.ellipsis-multiline-2 {
40 @include ellipsis-multiline(2); 44 @include ellipsis-multiline(2);
41} 45}
@@ -45,3 +49,7 @@
45} 49}
46 50
47// --------------------------------------------------------------------------- 51// ---------------------------------------------------------------------------
52
53.ellipsis {
54 @include ellipsis;
55}