Create a dedicated component for video description
This commit is contained in:
parent
6ea59f4154
commit
b0c43e36db
|
@ -0,0 +1,19 @@
|
||||||
|
<div class="video-info-description">
|
||||||
|
<div
|
||||||
|
class="video-info-description-html"
|
||||||
|
[innerHTML]="videoHTMLDescription"
|
||||||
|
(timestampClicked)="onTimestampClicked($event)"
|
||||||
|
timestampRouteTransformer
|
||||||
|
></div>
|
||||||
|
|
||||||
|
<div class="video-info-description-more" *ngIf="completeDescriptionShown === false && video.description?.length >= 250" (click)="showMoreDescription()">
|
||||||
|
<ng-container i18n>Show more</ng-container>
|
||||||
|
<span *ngIf="descriptionLoading === false" class="glyphicon glyphicon-menu-down"></span>
|
||||||
|
<my-small-loader class="description-loading" [loading]="descriptionLoading"></my-small-loader>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div *ngIf="completeDescriptionShown === true" (click)="showLessDescription()" class="video-info-description-more">
|
||||||
|
<ng-container i18n>Show less</ng-container>
|
||||||
|
<span *ngIf="descriptionLoading === false" class="glyphicon glyphicon-menu-up"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -0,0 +1,46 @@
|
||||||
|
@use '_variables' as *;
|
||||||
|
@use '_mixins' as *;
|
||||||
|
|
||||||
|
.video-info-description {
|
||||||
|
@include margin-left($video-watch-info-margin-left);
|
||||||
|
@include margin-right(0);
|
||||||
|
|
||||||
|
margin-top: 20px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
font-size: 15px;
|
||||||
|
|
||||||
|
.video-info-description-html {
|
||||||
|
@include peertube-word-wrap;
|
||||||
|
|
||||||
|
::ng-deep a {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.glyphicon,
|
||||||
|
.description-loading {
|
||||||
|
@include margin-left(3px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.description-loading {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-info-description-more {
|
||||||
|
cursor: pointer;
|
||||||
|
font-weight: $font-semibold;
|
||||||
|
color: pvar(--greyForegroundColor);
|
||||||
|
font-size: 14px;
|
||||||
|
|
||||||
|
.glyphicon {
|
||||||
|
position: relative;
|
||||||
|
top: 2px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 450px) {
|
||||||
|
.video-info-description {
|
||||||
|
font-size: 14px !important;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,87 @@
|
||||||
|
import { Component, EventEmitter, Inject, Input, LOCALE_ID, OnChanges, Output } from '@angular/core'
|
||||||
|
import { MarkdownService, Notifier } from '@app/core'
|
||||||
|
import { VideoDetails, VideoService } from '@app/shared/shared-main'
|
||||||
|
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'my-video-description',
|
||||||
|
templateUrl: './video-description.component.html',
|
||||||
|
styleUrls: [ './video-description.component.scss' ]
|
||||||
|
})
|
||||||
|
export class VideoDescriptionComponent implements OnChanges {
|
||||||
|
@Input() video: VideoDetails
|
||||||
|
|
||||||
|
@Output() timestampClicked = new EventEmitter<number>()
|
||||||
|
|
||||||
|
descriptionLoading = false
|
||||||
|
completeDescriptionShown = false
|
||||||
|
completeVideoDescription: string
|
||||||
|
shortVideoDescription: string
|
||||||
|
videoHTMLDescription = ''
|
||||||
|
|
||||||
|
constructor (
|
||||||
|
private videoService: VideoService,
|
||||||
|
private notifier: Notifier,
|
||||||
|
private markdownService: MarkdownService,
|
||||||
|
@Inject(LOCALE_ID) private localeId: string
|
||||||
|
) { }
|
||||||
|
|
||||||
|
ngOnChanges () {
|
||||||
|
this.descriptionLoading = false
|
||||||
|
this.completeDescriptionShown = false
|
||||||
|
this.completeVideoDescription = undefined
|
||||||
|
|
||||||
|
this.setVideoDescriptionHTML()
|
||||||
|
}
|
||||||
|
|
||||||
|
showMoreDescription () {
|
||||||
|
if (this.completeVideoDescription === undefined) {
|
||||||
|
return this.loadCompleteDescription()
|
||||||
|
}
|
||||||
|
|
||||||
|
this.updateVideoDescription(this.completeVideoDescription)
|
||||||
|
this.completeDescriptionShown = true
|
||||||
|
}
|
||||||
|
|
||||||
|
showLessDescription () {
|
||||||
|
this.updateVideoDescription(this.shortVideoDescription)
|
||||||
|
this.completeDescriptionShown = false
|
||||||
|
}
|
||||||
|
|
||||||
|
loadCompleteDescription () {
|
||||||
|
this.descriptionLoading = true
|
||||||
|
|
||||||
|
this.videoService.loadCompleteDescription(this.video.descriptionPath)
|
||||||
|
.subscribe(
|
||||||
|
description => {
|
||||||
|
this.completeDescriptionShown = true
|
||||||
|
this.descriptionLoading = false
|
||||||
|
|
||||||
|
this.shortVideoDescription = this.video.description
|
||||||
|
this.completeVideoDescription = description
|
||||||
|
|
||||||
|
this.updateVideoDescription(this.completeVideoDescription)
|
||||||
|
},
|
||||||
|
|
||||||
|
error => {
|
||||||
|
this.descriptionLoading = false
|
||||||
|
this.notifier.error(error.message)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
onTimestampClicked (timestamp: number) {
|
||||||
|
this.timestampClicked.emit(timestamp)
|
||||||
|
}
|
||||||
|
|
||||||
|
private updateVideoDescription (description: string) {
|
||||||
|
this.video.description = description
|
||||||
|
this.setVideoDescriptionHTML()
|
||||||
|
.catch(err => console.error(err))
|
||||||
|
}
|
||||||
|
|
||||||
|
private async setVideoDescriptionHTML () {
|
||||||
|
const html = await this.markdownService.textMarkdownToHTML(this.video.description)
|
||||||
|
this.videoHTMLDescription = this.markdownService.processVideoTimestamps(html)
|
||||||
|
}
|
||||||
|
}
|
|
@ -186,25 +186,7 @@
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="video-info-description">
|
<my-video-description [video]="video"></my-video-description>
|
||||||
<div
|
|
||||||
class="video-info-description-html"
|
|
||||||
[innerHTML]="videoHTMLDescription"
|
|
||||||
(timestampClicked)="handleTimestampClicked($event)"
|
|
||||||
timestampRouteTransformer
|
|
||||||
></div>
|
|
||||||
|
|
||||||
<div class="video-info-description-more" *ngIf="completeDescriptionShown === false && video.description?.length >= 250" (click)="showMoreDescription()">
|
|
||||||
<ng-container i18n>Show more</ng-container>
|
|
||||||
<span *ngIf="descriptionLoading === false" class="glyphicon glyphicon-menu-down"></span>
|
|
||||||
<my-small-loader class="description-loading" [loading]="descriptionLoading"></my-small-loader>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div *ngIf="completeDescriptionShown === true" (click)="showLessDescription()" class="video-info-description-more">
|
|
||||||
<ng-container i18n>Show less</ng-container>
|
|
||||||
<span *ngIf="descriptionLoading === false" class="glyphicon glyphicon-menu-up"></span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="video-attributes mb-3">
|
<div class="video-attributes mb-3">
|
||||||
<div class="video-attribute">
|
<div class="video-attribute">
|
||||||
|
|
|
@ -4,15 +4,12 @@
|
||||||
@use '_bootstrap-variables';
|
@use '_bootstrap-variables';
|
||||||
@use '_miniature' as *;
|
@use '_miniature' as *;
|
||||||
|
|
||||||
$player-factor: math.div(16, 9);
|
|
||||||
$video-info-margin-left: 44px;
|
|
||||||
|
|
||||||
@function getPlayerHeight ($width) {
|
@function getPlayerHeight ($width) {
|
||||||
@return calc(#{$width} / #{$player-factor});
|
@return calc(#{$width} / #{$video-watch-player-factor});
|
||||||
}
|
}
|
||||||
|
|
||||||
@function getPlayerWidth ($height) {
|
@function getPlayerWidth ($height) {
|
||||||
@return calc(#{$height} * #{$player-factor});
|
@return calc(#{$height} * #{$video-watch-player-factor});
|
||||||
}
|
}
|
||||||
|
|
||||||
@mixin playlist-below-player {
|
@mixin playlist-below-player {
|
||||||
|
@ -316,46 +313,8 @@ $video-info-margin-left: 44px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.video-info-description {
|
|
||||||
@include margin-left($video-info-margin-left);
|
|
||||||
@include margin-right(0);
|
|
||||||
|
|
||||||
margin-top: 20px;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
font-size: 15px;
|
|
||||||
|
|
||||||
.video-info-description-html {
|
|
||||||
@include peertube-word-wrap;
|
|
||||||
|
|
||||||
::ng-deep a {
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.glyphicon,
|
|
||||||
.description-loading {
|
|
||||||
@include margin-left(3px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.description-loading {
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.video-info-description-more {
|
|
||||||
cursor: pointer;
|
|
||||||
font-weight: $font-semibold;
|
|
||||||
color: pvar(--greyForegroundColor);
|
|
||||||
font-size: 14px;
|
|
||||||
|
|
||||||
.glyphicon {
|
|
||||||
position: relative;
|
|
||||||
top: 2px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.video-attributes {
|
.video-attributes {
|
||||||
@include margin-left($video-info-margin-left);
|
@include margin-left($video-watch-info-margin-left);
|
||||||
}
|
}
|
||||||
|
|
||||||
.video-attributes .video-attribute {
|
.video-attributes .video-attribute {
|
||||||
|
@ -555,10 +514,6 @@ my-video-comments {
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.video-info-description {
|
|
||||||
font-size: 14px !important;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,6 @@ import {
|
||||||
AuthService,
|
AuthService,
|
||||||
AuthUser,
|
AuthUser,
|
||||||
ConfirmService,
|
ConfirmService,
|
||||||
MarkdownService,
|
|
||||||
MetaService,
|
MetaService,
|
||||||
Notifier,
|
Notifier,
|
||||||
PeerTubeSocket,
|
PeerTubeSocket,
|
||||||
|
@ -139,7 +138,6 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
|
||||||
private serverService: ServerService,
|
private serverService: ServerService,
|
||||||
private restExtractor: RestExtractor,
|
private restExtractor: RestExtractor,
|
||||||
private notifier: Notifier,
|
private notifier: Notifier,
|
||||||
private markdownService: MarkdownService,
|
|
||||||
private zone: NgZone,
|
private zone: NgZone,
|
||||||
private redirectService: RedirectService,
|
private redirectService: RedirectService,
|
||||||
private videoCaptionService: VideoCaptionService,
|
private videoCaptionService: VideoCaptionService,
|
||||||
|
@ -228,20 +226,6 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
|
||||||
this.hotkeysService.remove(this.hotkeys)
|
this.hotkeysService.remove(this.hotkeys)
|
||||||
}
|
}
|
||||||
|
|
||||||
showMoreDescription () {
|
|
||||||
if (this.completeVideoDescription === undefined) {
|
|
||||||
return this.loadCompleteDescription()
|
|
||||||
}
|
|
||||||
|
|
||||||
this.updateVideoDescription(this.completeVideoDescription)
|
|
||||||
this.completeDescriptionShown = true
|
|
||||||
}
|
|
||||||
|
|
||||||
showLessDescription () {
|
|
||||||
this.updateVideoDescription(this.shortVideoDescription)
|
|
||||||
this.completeDescriptionShown = false
|
|
||||||
}
|
|
||||||
|
|
||||||
showDownloadModal () {
|
showDownloadModal () {
|
||||||
this.videoDownloadModal.show(this.video, this.videoCaptions)
|
this.videoDownloadModal.show(this.video, this.videoCaptions)
|
||||||
}
|
}
|
||||||
|
@ -250,28 +234,6 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
|
||||||
return this.video && this.video instanceof VideoDetails && this.video.downloadEnabled && !this.video.isLive
|
return this.video && this.video instanceof VideoDetails && this.video.downloadEnabled && !this.video.isLive
|
||||||
}
|
}
|
||||||
|
|
||||||
loadCompleteDescription () {
|
|
||||||
this.descriptionLoading = true
|
|
||||||
|
|
||||||
this.videoService.loadCompleteDescription(this.video.descriptionPath)
|
|
||||||
.subscribe(
|
|
||||||
description => {
|
|
||||||
this.completeDescriptionShown = true
|
|
||||||
this.descriptionLoading = false
|
|
||||||
|
|
||||||
this.shortVideoDescription = this.video.description
|
|
||||||
this.completeVideoDescription = description
|
|
||||||
|
|
||||||
this.updateVideoDescription(this.completeVideoDescription)
|
|
||||||
},
|
|
||||||
|
|
||||||
error => {
|
|
||||||
this.descriptionLoading = false
|
|
||||||
this.notifier.error(error.message)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
showSupportModal () {
|
showSupportModal () {
|
||||||
this.supportModal.show()
|
this.supportModal.show()
|
||||||
}
|
}
|
||||||
|
@ -492,17 +454,6 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private updateVideoDescription (description: string) {
|
|
||||||
this.video.description = description
|
|
||||||
this.setVideoDescriptionHTML()
|
|
||||||
.catch(err => console.error(err))
|
|
||||||
}
|
|
||||||
|
|
||||||
private async setVideoDescriptionHTML () {
|
|
||||||
const html = await this.markdownService.textMarkdownToHTML(this.video.description)
|
|
||||||
this.videoHTMLDescription = this.markdownService.processVideoTimestamps(html)
|
|
||||||
}
|
|
||||||
|
|
||||||
private setVideoLikesBarTooltipText () {
|
private setVideoLikesBarTooltipText () {
|
||||||
this.likesBarTooltipText = `${this.video.likes} likes / ${this.video.dislikes} dislikes`
|
this.likesBarTooltipText = `${this.video.likes} likes / ${this.video.dislikes} dislikes`
|
||||||
}
|
}
|
||||||
|
@ -552,7 +503,6 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
|
||||||
this.buildPlayer(urlOptions)
|
this.buildPlayer(urlOptions)
|
||||||
.catch(err => console.error('Cannot build the player', err))
|
.catch(err => console.error('Cannot build the player', err))
|
||||||
|
|
||||||
this.setVideoDescriptionHTML()
|
|
||||||
this.setVideoLikesBarTooltipText()
|
this.setVideoLikesBarTooltipText()
|
||||||
|
|
||||||
this.setOpenGraphTags()
|
this.setOpenGraphTags()
|
||||||
|
|
|
@ -19,6 +19,7 @@ import { PlayerStylesComponent } from './player-styles.component'
|
||||||
import { RecommendationsModule } from './recommendations/recommendations.module'
|
import { RecommendationsModule } from './recommendations/recommendations.module'
|
||||||
import { TimestampRouteTransformerDirective } from './timestamp-route-transformer.directive'
|
import { TimestampRouteTransformerDirective } from './timestamp-route-transformer.directive'
|
||||||
import { VideoAvatarChannelComponent } from './video-avatar-channel.component'
|
import { VideoAvatarChannelComponent } from './video-avatar-channel.component'
|
||||||
|
import { VideoDescriptionComponent } from './video-description.component'
|
||||||
import { VideoRateComponent } from './video-rate.component'
|
import { VideoRateComponent } from './video-rate.component'
|
||||||
import { VideoWatchPlaylistComponent } from './video-watch-playlist.component'
|
import { VideoWatchPlaylistComponent } from './video-watch-playlist.component'
|
||||||
import { VideoWatchRoutingModule } from './video-watch-routing.module'
|
import { VideoWatchRoutingModule } from './video-watch-routing.module'
|
||||||
|
@ -47,6 +48,7 @@ import { VideoWatchComponent } from './video-watch.component'
|
||||||
VideoWatchComponent,
|
VideoWatchComponent,
|
||||||
VideoWatchPlaylistComponent,
|
VideoWatchPlaylistComponent,
|
||||||
VideoRateComponent,
|
VideoRateComponent,
|
||||||
|
VideoDescriptionComponent,
|
||||||
|
|
||||||
VideoCommentsComponent,
|
VideoCommentsComponent,
|
||||||
VideoCommentAddComponent,
|
VideoCommentAddComponent,
|
||||||
|
|
|
@ -95,6 +95,9 @@ $activated-action-button-color: #000;
|
||||||
|
|
||||||
$focus-box-shadow-form: 0 0 0 .2rem;
|
$focus-box-shadow-form: 0 0 0 .2rem;
|
||||||
|
|
||||||
|
$video-watch-player-factor: math.div(16, 9);
|
||||||
|
$video-watch-info-margin-left: 44px;
|
||||||
|
|
||||||
/*** map theme ***/
|
/*** map theme ***/
|
||||||
|
|
||||||
// pass variables into a sass map,
|
// pass variables into a sass map,
|
||||||
|
|
Loading…
Reference in New Issue