diff options
8 files changed, 161 insertions, 117 deletions
diff --git a/client/src/app/+videos/+video-watch/video-description.component.html b/client/src/app/+videos/+video-watch/video-description.component.html new file mode 100644 index 000000000..57f682899 --- /dev/null +++ b/client/src/app/+videos/+video-watch/video-description.component.html | |||
@@ -0,0 +1,19 @@ | |||
1 | <div class="video-info-description"> | ||
2 | <div | ||
3 | class="video-info-description-html" | ||
4 | [innerHTML]="videoHTMLDescription" | ||
5 | (timestampClicked)="onTimestampClicked($event)" | ||
6 | timestampRouteTransformer | ||
7 | ></div> | ||
8 | |||
9 | <div class="video-info-description-more" *ngIf="completeDescriptionShown === false && video.description?.length >= 250" (click)="showMoreDescription()"> | ||
10 | <ng-container i18n>Show more</ng-container> | ||
11 | <span *ngIf="descriptionLoading === false" class="glyphicon glyphicon-menu-down"></span> | ||
12 | <my-small-loader class="description-loading" [loading]="descriptionLoading"></my-small-loader> | ||
13 | </div> | ||
14 | |||
15 | <div *ngIf="completeDescriptionShown === true" (click)="showLessDescription()" class="video-info-description-more"> | ||
16 | <ng-container i18n>Show less</ng-container> | ||
17 | <span *ngIf="descriptionLoading === false" class="glyphicon glyphicon-menu-up"></span> | ||
18 | </div> | ||
19 | </div> | ||
diff --git a/client/src/app/+videos/+video-watch/video-description.component.scss b/client/src/app/+videos/+video-watch/video-description.component.scss new file mode 100644 index 000000000..fc8b4574c --- /dev/null +++ b/client/src/app/+videos/+video-watch/video-description.component.scss | |||
@@ -0,0 +1,46 @@ | |||
1 | @use '_variables' as *; | ||
2 | @use '_mixins' as *; | ||
3 | |||
4 | .video-info-description { | ||
5 | @include margin-left($video-watch-info-margin-left); | ||
6 | @include margin-right(0); | ||
7 | |||
8 | margin-top: 20px; | ||
9 | margin-bottom: 20px; | ||
10 | font-size: 15px; | ||
11 | |||
12 | .video-info-description-html { | ||
13 | @include peertube-word-wrap; | ||
14 | |||
15 | ::ng-deep a { | ||
16 | text-decoration: none; | ||
17 | } | ||
18 | } | ||
19 | |||
20 | .glyphicon, | ||
21 | .description-loading { | ||
22 | @include margin-left(3px); | ||
23 | } | ||
24 | |||
25 | .description-loading { | ||
26 | display: inline-block; | ||
27 | } | ||
28 | |||
29 | .video-info-description-more { | ||
30 | cursor: pointer; | ||
31 | font-weight: $font-semibold; | ||
32 | color: pvar(--greyForegroundColor); | ||
33 | font-size: 14px; | ||
34 | |||
35 | .glyphicon { | ||
36 | position: relative; | ||
37 | top: 2px; | ||
38 | } | ||
39 | } | ||
40 | } | ||
41 | |||
42 | @media screen and (max-width: 450px) { | ||
43 | .video-info-description { | ||
44 | font-size: 14px !important; | ||
45 | } | ||
46 | } | ||
diff --git a/client/src/app/+videos/+video-watch/video-description.component.ts b/client/src/app/+videos/+video-watch/video-description.component.ts new file mode 100644 index 000000000..2ea3b206f --- /dev/null +++ b/client/src/app/+videos/+video-watch/video-description.component.ts | |||
@@ -0,0 +1,87 @@ | |||
1 | import { Component, EventEmitter, Inject, Input, LOCALE_ID, OnChanges, Output } from '@angular/core' | ||
2 | import { MarkdownService, Notifier } from '@app/core' | ||
3 | import { VideoDetails, VideoService } from '@app/shared/shared-main' | ||
4 | |||
5 | |||
6 | @Component({ | ||
7 | selector: 'my-video-description', | ||
8 | templateUrl: './video-description.component.html', | ||
9 | styleUrls: [ './video-description.component.scss' ] | ||
10 | }) | ||
11 | export class VideoDescriptionComponent implements OnChanges { | ||
12 | @Input() video: VideoDetails | ||
13 | |||
14 | @Output() timestampClicked = new EventEmitter<number>() | ||
15 | |||
16 | descriptionLoading = false | ||
17 | completeDescriptionShown = false | ||
18 | completeVideoDescription: string | ||
19 | shortVideoDescription: string | ||
20 | videoHTMLDescription = '' | ||
21 | |||
22 | constructor ( | ||
23 | private videoService: VideoService, | ||
24 | private notifier: Notifier, | ||
25 | private markdownService: MarkdownService, | ||
26 | @Inject(LOCALE_ID) private localeId: string | ||
27 | ) { } | ||
28 | |||
29 | ngOnChanges () { | ||
30 | this.descriptionLoading = false | ||
31 | this.completeDescriptionShown = false | ||
32 | this.completeVideoDescription = undefined | ||
33 | |||
34 | this.setVideoDescriptionHTML() | ||
35 | } | ||
36 | |||
37 | showMoreDescription () { | ||
38 | if (this.completeVideoDescription === undefined) { | ||
39 | return this.loadCompleteDescription() | ||
40 | } | ||
41 | |||
42 | this.updateVideoDescription(this.completeVideoDescription) | ||
43 | this.completeDescriptionShown = true | ||
44 | } | ||
45 | |||
46 | showLessDescription () { | ||
47 | this.updateVideoDescription(this.shortVideoDescription) | ||
48 | this.completeDescriptionShown = false | ||
49 | } | ||
50 | |||
51 | loadCompleteDescription () { | ||
52 | this.descriptionLoading = true | ||
53 | |||
54 | this.videoService.loadCompleteDescription(this.video.descriptionPath) | ||
55 | .subscribe( | ||
56 | description => { | ||
57 | this.completeDescriptionShown = true | ||
58 | this.descriptionLoading = false | ||
59 | |||
60 | this.shortVideoDescription = this.video.description | ||
61 | this.completeVideoDescription = description | ||
62 | |||
63 | this.updateVideoDescription(this.completeVideoDescription) | ||
64 | }, | ||
65 | |||
66 | error => { | ||
67 | this.descriptionLoading = false | ||
68 | this.notifier.error(error.message) | ||
69 | } | ||
70 | ) | ||
71 | } | ||
72 | |||
73 | onTimestampClicked (timestamp: number) { | ||
74 | this.timestampClicked.emit(timestamp) | ||
75 | } | ||
76 | |||
77 | private updateVideoDescription (description: string) { | ||
78 | this.video.description = description | ||
79 | this.setVideoDescriptionHTML() | ||
80 | .catch(err => console.error(err)) | ||
81 | } | ||
82 | |||
83 | private async setVideoDescriptionHTML () { | ||
84 | const html = await this.markdownService.textMarkdownToHTML(this.video.description) | ||
85 | this.videoHTMLDescription = this.markdownService.processVideoTimestamps(html) | ||
86 | } | ||
87 | } | ||
diff --git a/client/src/app/+videos/+video-watch/video-watch.component.html b/client/src/app/+videos/+video-watch/video-watch.component.html index a659a7db1..40efa7b35 100644 --- a/client/src/app/+videos/+video-watch/video-watch.component.html +++ b/client/src/app/+videos/+video-watch/video-watch.component.html | |||
@@ -186,25 +186,7 @@ | |||
186 | 186 | ||
187 | </div> | 187 | </div> |
188 | 188 | ||
189 | <div class="video-info-description"> | 189 | <my-video-description [video]="video"></my-video-description> |
190 | <div | ||
191 | class="video-info-description-html" | ||
192 | [innerHTML]="videoHTMLDescription" | ||
193 | (timestampClicked)="handleTimestampClicked($event)" | ||
194 | timestampRouteTransformer | ||
195 | ></div> | ||
196 | |||
197 | <div class="video-info-description-more" *ngIf="completeDescriptionShown === false && video.description?.length >= 250" (click)="showMoreDescription()"> | ||
198 | <ng-container i18n>Show more</ng-container> | ||
199 | <span *ngIf="descriptionLoading === false" class="glyphicon glyphicon-menu-down"></span> | ||
200 | <my-small-loader class="description-loading" [loading]="descriptionLoading"></my-small-loader> | ||
201 | </div> | ||
202 | |||
203 | <div *ngIf="completeDescriptionShown === true" (click)="showLessDescription()" class="video-info-description-more"> | ||
204 | <ng-container i18n>Show less</ng-container> | ||
205 | <span *ngIf="descriptionLoading === false" class="glyphicon glyphicon-menu-up"></span> | ||
206 | </div> | ||
207 | </div> | ||
208 | 190 | ||
209 | <div class="video-attributes mb-3"> | 191 | <div class="video-attributes mb-3"> |
210 | <div class="video-attribute"> | 192 | <div class="video-attribute"> |
diff --git a/client/src/app/+videos/+video-watch/video-watch.component.scss b/client/src/app/+videos/+video-watch/video-watch.component.scss index 4d68504f5..f25f3bf31 100644 --- a/client/src/app/+videos/+video-watch/video-watch.component.scss +++ b/client/src/app/+videos/+video-watch/video-watch.component.scss | |||
@@ -4,15 +4,12 @@ | |||
4 | @use '_bootstrap-variables'; | 4 | @use '_bootstrap-variables'; |
5 | @use '_miniature' as *; | 5 | @use '_miniature' as *; |
6 | 6 | ||
7 | $player-factor: math.div(16, 9); | ||
8 | $video-info-margin-left: 44px; | ||
9 | |||
10 | @function getPlayerHeight ($width) { | 7 | @function getPlayerHeight ($width) { |
11 | @return calc(#{$width} / #{$player-factor}); | 8 | @return calc(#{$width} / #{$video-watch-player-factor}); |
12 | } | 9 | } |
13 | 10 | ||
14 | @function getPlayerWidth ($height) { | 11 | @function getPlayerWidth ($height) { |
15 | @return calc(#{$height} * #{$player-factor}); | 12 | @return calc(#{$height} * #{$video-watch-player-factor}); |
16 | } | 13 | } |
17 | 14 | ||
18 | @mixin playlist-below-player { | 15 | @mixin playlist-below-player { |
@@ -316,46 +313,8 @@ $video-info-margin-left: 44px; | |||
316 | } | 313 | } |
317 | } | 314 | } |
318 | 315 | ||
319 | .video-info-description { | ||
320 | @include margin-left($video-info-margin-left); | ||
321 | @include margin-right(0); | ||
322 | |||
323 | margin-top: 20px; | ||
324 | margin-bottom: 20px; | ||
325 | font-size: 15px; | ||
326 | |||
327 | .video-info-description-html { | ||
328 | @include peertube-word-wrap; | ||
329 | |||
330 | ::ng-deep a { | ||
331 | text-decoration: none; | ||
332 | } | ||
333 | } | ||
334 | |||
335 | .glyphicon, | ||
336 | .description-loading { | ||
337 | @include margin-left(3px); | ||
338 | } | ||
339 | |||
340 | .description-loading { | ||
341 | display: inline-block; | ||
342 | } | ||
343 | |||
344 | .video-info-description-more { | ||
345 | cursor: pointer; | ||
346 | font-weight: $font-semibold; | ||
347 | color: pvar(--greyForegroundColor); | ||
348 | font-size: 14px; | ||
349 | |||
350 | .glyphicon { | ||
351 | position: relative; | ||
352 | top: 2px; | ||
353 | } | ||
354 | } | ||
355 | } | ||
356 | |||
357 | .video-attributes { | 316 | .video-attributes { |
358 | @include margin-left($video-info-margin-left); | 317 | @include margin-left($video-watch-info-margin-left); |
359 | } | 318 | } |
360 | 319 | ||
361 | .video-attributes .video-attribute { | 320 | .video-attributes .video-attribute { |
@@ -555,10 +514,6 @@ my-video-comments { | |||
555 | margin-top: 10px; | 514 | margin-top: 10px; |
556 | } | 515 | } |
557 | } | 516 | } |
558 | |||
559 | .video-info-description { | ||
560 | font-size: 14px !important; | ||
561 | } | ||
562 | } | 517 | } |
563 | } | 518 | } |
564 | 519 | ||
diff --git a/client/src/app/+videos/+video-watch/video-watch.component.ts b/client/src/app/+videos/+video-watch/video-watch.component.ts index e2fbf7524..e6b353220 100644 --- a/client/src/app/+videos/+video-watch/video-watch.component.ts +++ b/client/src/app/+videos/+video-watch/video-watch.component.ts | |||
@@ -8,7 +8,6 @@ import { | |||
8 | AuthService, | 8 | AuthService, |
9 | AuthUser, | 9 | AuthUser, |
10 | ConfirmService, | 10 | ConfirmService, |
11 | MarkdownService, | ||
12 | MetaService, | 11 | MetaService, |
13 | Notifier, | 12 | Notifier, |
14 | PeerTubeSocket, | 13 | PeerTubeSocket, |
@@ -139,7 +138,6 @@ export class VideoWatchComponent implements OnInit, OnDestroy { | |||
139 | private serverService: ServerService, | 138 | private serverService: ServerService, |
140 | private restExtractor: RestExtractor, | 139 | private restExtractor: RestExtractor, |
141 | private notifier: Notifier, | 140 | private notifier: Notifier, |
142 | private markdownService: MarkdownService, | ||
143 | private zone: NgZone, | 141 | private zone: NgZone, |
144 | private redirectService: RedirectService, | 142 | private redirectService: RedirectService, |
145 | private videoCaptionService: VideoCaptionService, | 143 | private videoCaptionService: VideoCaptionService, |
@@ -228,20 +226,6 @@ export class VideoWatchComponent implements OnInit, OnDestroy { | |||
228 | this.hotkeysService.remove(this.hotkeys) | 226 | this.hotkeysService.remove(this.hotkeys) |
229 | } | 227 | } |
230 | 228 | ||
231 | showMoreDescription () { | ||
232 | if (this.completeVideoDescription === undefined) { | ||
233 | return this.loadCompleteDescription() | ||
234 | } | ||
235 | |||
236 | this.updateVideoDescription(this.completeVideoDescription) | ||
237 | this.completeDescriptionShown = true | ||
238 | } | ||
239 | |||
240 | showLessDescription () { | ||
241 | this.updateVideoDescription(this.shortVideoDescription) | ||
242 | this.completeDescriptionShown = false | ||
243 | } | ||
244 | |||
245 | showDownloadModal () { | 229 | showDownloadModal () { |
246 | this.videoDownloadModal.show(this.video, this.videoCaptions) | 230 | this.videoDownloadModal.show(this.video, this.videoCaptions) |
247 | } | 231 | } |
@@ -250,28 +234,6 @@ export class VideoWatchComponent implements OnInit, OnDestroy { | |||
250 | return this.video && this.video instanceof VideoDetails && this.video.downloadEnabled && !this.video.isLive | 234 | return this.video && this.video instanceof VideoDetails && this.video.downloadEnabled && !this.video.isLive |
251 | } | 235 | } |
252 | 236 | ||
253 | loadCompleteDescription () { | ||
254 | this.descriptionLoading = true | ||
255 | |||
256 | this.videoService.loadCompleteDescription(this.video.descriptionPath) | ||
257 | .subscribe( | ||
258 | description => { | ||
259 | this.completeDescriptionShown = true | ||
260 | this.descriptionLoading = false | ||
261 | |||
262 | this.shortVideoDescription = this.video.description | ||
263 | this.completeVideoDescription = description | ||
264 | |||
265 | this.updateVideoDescription(this.completeVideoDescription) | ||
266 | }, | ||
267 | |||
268 | error => { | ||
269 | this.descriptionLoading = false | ||
270 | this.notifier.error(error.message) | ||
271 | } | ||
272 | ) | ||
273 | } | ||
274 | |||
275 | showSupportModal () { | 237 | showSupportModal () { |
276 | this.supportModal.show() | 238 | this.supportModal.show() |
277 | } | 239 | } |
@@ -492,17 +454,6 @@ export class VideoWatchComponent implements OnInit, OnDestroy { | |||
492 | }) | 454 | }) |
493 | } | 455 | } |
494 | 456 | ||
495 | private updateVideoDescription (description: string) { | ||
496 | this.video.description = description | ||
497 | this.setVideoDescriptionHTML() | ||
498 | .catch(err => console.error(err)) | ||
499 | } | ||
500 | |||
501 | private async setVideoDescriptionHTML () { | ||
502 | const html = await this.markdownService.textMarkdownToHTML(this.video.description) | ||
503 | this.videoHTMLDescription = this.markdownService.processVideoTimestamps(html) | ||
504 | } | ||
505 | |||
506 | private setVideoLikesBarTooltipText () { | 457 | private setVideoLikesBarTooltipText () { |
507 | this.likesBarTooltipText = `${this.video.likes} likes / ${this.video.dislikes} dislikes` | 458 | this.likesBarTooltipText = `${this.video.likes} likes / ${this.video.dislikes} dislikes` |
508 | } | 459 | } |
@@ -552,7 +503,6 @@ export class VideoWatchComponent implements OnInit, OnDestroy { | |||
552 | this.buildPlayer(urlOptions) | 503 | this.buildPlayer(urlOptions) |
553 | .catch(err => console.error('Cannot build the player', err)) | 504 | .catch(err => console.error('Cannot build the player', err)) |
554 | 505 | ||
555 | this.setVideoDescriptionHTML() | ||
556 | this.setVideoLikesBarTooltipText() | 506 | this.setVideoLikesBarTooltipText() |
557 | 507 | ||
558 | this.setOpenGraphTags() | 508 | this.setOpenGraphTags() |
diff --git a/client/src/app/+videos/+video-watch/video-watch.module.ts b/client/src/app/+videos/+video-watch/video-watch.module.ts index 57f36c723..93b143542 100644 --- a/client/src/app/+videos/+video-watch/video-watch.module.ts +++ b/client/src/app/+videos/+video-watch/video-watch.module.ts | |||
@@ -19,6 +19,7 @@ import { PlayerStylesComponent } from './player-styles.component' | |||
19 | import { RecommendationsModule } from './recommendations/recommendations.module' | 19 | import { RecommendationsModule } from './recommendations/recommendations.module' |
20 | import { TimestampRouteTransformerDirective } from './timestamp-route-transformer.directive' | 20 | import { TimestampRouteTransformerDirective } from './timestamp-route-transformer.directive' |
21 | import { VideoAvatarChannelComponent } from './video-avatar-channel.component' | 21 | import { VideoAvatarChannelComponent } from './video-avatar-channel.component' |
22 | import { VideoDescriptionComponent } from './video-description.component' | ||
22 | import { VideoRateComponent } from './video-rate.component' | 23 | import { VideoRateComponent } from './video-rate.component' |
23 | import { VideoWatchPlaylistComponent } from './video-watch-playlist.component' | 24 | import { VideoWatchPlaylistComponent } from './video-watch-playlist.component' |
24 | import { VideoWatchRoutingModule } from './video-watch-routing.module' | 25 | import { VideoWatchRoutingModule } from './video-watch-routing.module' |
@@ -47,6 +48,7 @@ import { VideoWatchComponent } from './video-watch.component' | |||
47 | VideoWatchComponent, | 48 | VideoWatchComponent, |
48 | VideoWatchPlaylistComponent, | 49 | VideoWatchPlaylistComponent, |
49 | VideoRateComponent, | 50 | VideoRateComponent, |
51 | VideoDescriptionComponent, | ||
50 | 52 | ||
51 | VideoCommentsComponent, | 53 | VideoCommentsComponent, |
52 | VideoCommentAddComponent, | 54 | VideoCommentAddComponent, |
diff --git a/client/src/sass/include/_variables.scss b/client/src/sass/include/_variables.scss index 8f3d3c3b4..39e81f270 100644 --- a/client/src/sass/include/_variables.scss +++ b/client/src/sass/include/_variables.scss | |||
@@ -95,6 +95,9 @@ $activated-action-button-color: #000; | |||
95 | 95 | ||
96 | $focus-box-shadow-form: 0 0 0 .2rem; | 96 | $focus-box-shadow-form: 0 0 0 .2rem; |
97 | 97 | ||
98 | $video-watch-player-factor: math.div(16, 9); | ||
99 | $video-watch-info-margin-left: 44px; | ||
100 | |||
98 | /*** map theme ***/ | 101 | /*** map theme ***/ |
99 | 102 | ||
100 | // pass variables into a sass map, | 103 | // pass variables into a sass map, |