Add support to video support on client
This commit is contained in:
parent
dddf58c8ce
commit
07fa4c97ca
|
@ -27,8 +27,8 @@ export class AboutComponent implements OnInit {
|
||||||
this.serverService.getAbout()
|
this.serverService.getAbout()
|
||||||
.subscribe(
|
.subscribe(
|
||||||
res => {
|
res => {
|
||||||
this.descriptionHTML = this.markdownService.markdownToHTML(res.instance.description)
|
this.descriptionHTML = this.markdownService.textMarkdownToHTML(res.instance.description)
|
||||||
this.termsHTML = this.markdownService.markdownToHTML(res.instance.terms)
|
this.termsHTML = this.markdownService.textMarkdownToHTML(res.instance.terms)
|
||||||
},
|
},
|
||||||
|
|
||||||
err => this.notificationsService.error('Error', err)
|
err => this.notificationsService.error('Error', err)
|
||||||
|
|
|
@ -49,7 +49,6 @@
|
||||||
|
|
||||||
#peertube-title {
|
#peertube-title {
|
||||||
@include disable-default-a-behaviour;
|
@include disable-default-a-behaviour;
|
||||||
width: 100%;
|
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
font-weight: $font-bold;
|
font-weight: $font-bold;
|
||||||
color: inherit !important;
|
color: inherit !important;
|
||||||
|
@ -79,6 +78,10 @@
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 350px) {
|
||||||
|
flex: auto;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.header-right {
|
.header-right {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { Component, OnInit } from '@angular/core'
|
import { Component, OnInit } from '@angular/core'
|
||||||
import { GuardsCheckStart, NavigationEnd, Router } from '@angular/router'
|
import { GuardsCheckStart, Router } from '@angular/router'
|
||||||
import { AuthService, ServerService } from '@app/core'
|
import { AuthService, ServerService } from '@app/core'
|
||||||
import { isInSmallView } from '@app/shared/misc/utils'
|
import { isInSmallView } from '@app/shared/misc/utils'
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
width: calc(100% - 150px);
|
width: calc(100% - 150px);
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 400px) {
|
@media screen and (max-width: 600px) {
|
||||||
width: calc(100% - 70px);
|
width: calc(100% - 70px);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -50,7 +50,7 @@
|
||||||
margin-right: 6px;
|
margin-right: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 400px) {
|
@media screen and (max-width: 600px) {
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
padding: 0 10px;
|
padding: 0 10px;
|
||||||
|
|
||||||
|
|
|
@ -44,10 +44,10 @@ export const VIDEO_CHANNEL = {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const VIDEO_DESCRIPTION = {
|
export const VIDEO_DESCRIPTION = {
|
||||||
VALIDATORS: [ Validators.minLength(3), Validators.maxLength(3000) ],
|
VALIDATORS: [ Validators.minLength(3), Validators.maxLength(10000) ],
|
||||||
MESSAGES: {
|
MESSAGES: {
|
||||||
'minlength': 'Video description must be at least 3 characters long.',
|
'minlength': 'Video description must be at least 3 characters long.',
|
||||||
'maxlength': 'Video description cannot be more than 3000 characters long.'
|
'maxlength': 'Video description cannot be more than 10000 characters long.'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,3 +58,11 @@ export const VIDEO_TAGS = {
|
||||||
'maxlength': 'A tag should be less than 30 characters long.'
|
'maxlength': 'A tag should be less than 30 characters long.'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const VIDEO_SUPPORT = {
|
||||||
|
VALIDATORS: [ Validators.minLength(3), Validators.maxLength(300) ],
|
||||||
|
MESSAGES: {
|
||||||
|
'minlength': 'Video support must be at least 3 characters long.',
|
||||||
|
'maxlength': 'Video support cannot be more than 300 characters long.'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
<div class="root" [ngStyle]="{ 'flex-direction': flexDirection }">
|
<div class="root" [ngStyle]="{ 'flex-direction': flexDirection }">
|
||||||
<textarea
|
<textarea
|
||||||
[(ngModel)]="description" (ngModelChange)="onModelChange()"
|
[(ngModel)]="content" (ngModelChange)="onModelChange()"
|
||||||
[ngClass]="classes" [ngStyle]="{ width: textareaWidth, height: textareaHeight, 'margin-right': textareaMarginRight }"
|
[ngClass]="classes" [ngStyle]="{ width: textareaWidth, height: textareaHeight, 'margin-right': textareaMarginRight }"
|
||||||
id="description" name="description">
|
id="description" name="description">
|
||||||
</textarea>
|
</textarea>
|
||||||
|
|
||||||
<tabset *ngIf="arePreviewsDisplayed()" class="previews">
|
<tabset *ngIf="arePreviewsDisplayed()" class="previews">
|
||||||
<tab *ngIf="truncate !== undefined" heading="Truncated description preview" [innerHTML]="truncatedDescriptionHTML"></tab>
|
<tab *ngIf="truncate !== undefined" heading="Truncated preview" [innerHTML]="truncatedPreviewHTML"></tab>
|
||||||
<tab heading="Complete description preview" [innerHTML]="descriptionHTML"></tab>
|
<tab heading="Complete preview" [innerHTML]="previewHTML"></tab>
|
||||||
</tabset>
|
</tabset>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
min-height: 75px;
|
min-height: 75px;
|
||||||
padding: 15px;
|
padding: 15px;
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
|
word-wrap: break-word;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,29 +21,30 @@ import truncate from 'lodash-es/truncate'
|
||||||
})
|
})
|
||||||
|
|
||||||
export class MarkdownTextareaComponent implements ControlValueAccessor, OnInit {
|
export class MarkdownTextareaComponent implements ControlValueAccessor, OnInit {
|
||||||
@Input() description = ''
|
@Input() content = ''
|
||||||
@Input() classes: string[] = []
|
@Input() classes: string[] = []
|
||||||
@Input() textareaWidth = '100%'
|
@Input() textareaWidth = '100%'
|
||||||
@Input() textareaHeight = '150px'
|
@Input() textareaHeight = '150px'
|
||||||
@Input() previewColumn = false
|
@Input() previewColumn = false
|
||||||
@Input() truncate: number
|
@Input() truncate: number
|
||||||
|
@Input() markdownType: 'text' | 'enhanced' = 'text'
|
||||||
|
|
||||||
textareaMarginRight = '0'
|
textareaMarginRight = '0'
|
||||||
flexDirection = 'column'
|
flexDirection = 'column'
|
||||||
truncatedDescriptionHTML = ''
|
truncatedPreviewHTML = ''
|
||||||
descriptionHTML = ''
|
previewHTML = ''
|
||||||
|
|
||||||
private descriptionChanged = new Subject<string>()
|
private contentChanged = new Subject<string>()
|
||||||
|
|
||||||
constructor (private markdownService: MarkdownService) {}
|
constructor (private markdownService: MarkdownService) {}
|
||||||
|
|
||||||
ngOnInit () {
|
ngOnInit () {
|
||||||
this.descriptionChanged
|
this.contentChanged
|
||||||
.debounceTime(150)
|
.debounceTime(150)
|
||||||
.distinctUntilChanged()
|
.distinctUntilChanged()
|
||||||
.subscribe(() => this.updateDescriptionPreviews())
|
.subscribe(() => this.updatePreviews())
|
||||||
|
|
||||||
this.descriptionChanged.next(this.description)
|
this.contentChanged.next(this.content)
|
||||||
|
|
||||||
if (this.previewColumn) {
|
if (this.previewColumn) {
|
||||||
this.flexDirection = 'row'
|
this.flexDirection = 'row'
|
||||||
|
@ -54,9 +55,9 @@ export class MarkdownTextareaComponent implements ControlValueAccessor, OnInit {
|
||||||
propagateChange = (_: any) => { /* empty */ }
|
propagateChange = (_: any) => { /* empty */ }
|
||||||
|
|
||||||
writeValue (description: string) {
|
writeValue (description: string) {
|
||||||
this.description = description
|
this.content = description
|
||||||
|
|
||||||
this.descriptionChanged.next(this.description)
|
this.contentChanged.next(this.content)
|
||||||
}
|
}
|
||||||
|
|
||||||
registerOnChange (fn: (_: any) => void) {
|
registerOnChange (fn: (_: any) => void) {
|
||||||
|
@ -68,19 +69,25 @@ export class MarkdownTextareaComponent implements ControlValueAccessor, OnInit {
|
||||||
}
|
}
|
||||||
|
|
||||||
onModelChange () {
|
onModelChange () {
|
||||||
this.propagateChange(this.description)
|
this.propagateChange(this.content)
|
||||||
|
|
||||||
this.descriptionChanged.next(this.description)
|
this.contentChanged.next(this.content)
|
||||||
}
|
}
|
||||||
|
|
||||||
arePreviewsDisplayed () {
|
arePreviewsDisplayed () {
|
||||||
return isInSmallView() === false
|
return isInSmallView() === false
|
||||||
}
|
}
|
||||||
|
|
||||||
private updateDescriptionPreviews () {
|
private updatePreviews () {
|
||||||
if (this.description === null || this.description === undefined) return
|
if (this.content === null || this.content === undefined) return
|
||||||
|
|
||||||
this.truncatedDescriptionHTML = this.markdownService.markdownToHTML(truncate(this.description, { length: this.truncate }))
|
this.truncatedPreviewHTML = this.markdownRender(truncate(this.content, { length: this.truncate }))
|
||||||
this.descriptionHTML = this.markdownService.markdownToHTML(this.description)
|
this.previewHTML = this.markdownRender(this.content)
|
||||||
|
}
|
||||||
|
|
||||||
|
private markdownRender (text: string) {
|
||||||
|
if (this.markdownType === 'text') return this.markdownService.textMarkdownToHTML(text)
|
||||||
|
|
||||||
|
return this.markdownService.enhancedMarkdownToHTML(text)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,6 +57,7 @@ export class VideoDetails extends Video implements VideoDetailsServerModel {
|
||||||
this.channel = hash.channel
|
this.channel = hash.channel
|
||||||
this.account = hash.account
|
this.account = hash.account
|
||||||
this.tags = hash.tags
|
this.tags = hash.tags
|
||||||
|
this.support = hash.support
|
||||||
this.commentsEnabled = hash.commentsEnabled
|
this.commentsEnabled = hash.commentsEnabled
|
||||||
|
|
||||||
this.likesPercent = (this.likes / (this.likes + this.dislikes)) * 100
|
this.likesPercent = (this.likes / (this.likes + this.dislikes)) * 100
|
||||||
|
|
|
@ -12,6 +12,7 @@ export class VideoEdit {
|
||||||
commentsEnabled: boolean
|
commentsEnabled: boolean
|
||||||
channel: number
|
channel: number
|
||||||
privacy: VideoPrivacy
|
privacy: VideoPrivacy
|
||||||
|
support: string
|
||||||
thumbnailfile?: any
|
thumbnailfile?: any
|
||||||
previewfile?: any
|
previewfile?: any
|
||||||
thumbnailUrl: string
|
thumbnailUrl: string
|
||||||
|
@ -33,6 +34,7 @@ export class VideoEdit {
|
||||||
this.commentsEnabled = videoDetails.commentsEnabled
|
this.commentsEnabled = videoDetails.commentsEnabled
|
||||||
this.channel = videoDetails.channel.id
|
this.channel = videoDetails.channel.id
|
||||||
this.privacy = videoDetails.privacy
|
this.privacy = videoDetails.privacy
|
||||||
|
this.support = videoDetails.support
|
||||||
this.thumbnailUrl = videoDetails.thumbnailUrl
|
this.thumbnailUrl = videoDetails.thumbnailUrl
|
||||||
this.previewUrl = videoDetails.previewUrl
|
this.previewUrl = videoDetails.previewUrl
|
||||||
}
|
}
|
||||||
|
@ -50,6 +52,7 @@ export class VideoEdit {
|
||||||
licence: this.licence,
|
licence: this.licence,
|
||||||
language: this.language,
|
language: this.language,
|
||||||
description: this.description,
|
description: this.description,
|
||||||
|
support: this.support,
|
||||||
name: this.name,
|
name: this.name,
|
||||||
tags: this.tags,
|
tags: this.tags,
|
||||||
nsfw: this.nsfw,
|
nsfw: this.nsfw,
|
||||||
|
|
|
@ -62,6 +62,7 @@ export class VideoService {
|
||||||
tags: video.tags,
|
tags: video.tags,
|
||||||
nsfw: video.nsfw,
|
nsfw: video.nsfw,
|
||||||
commentsEnabled: video.commentsEnabled,
|
commentsEnabled: video.commentsEnabled,
|
||||||
|
support: video.support,
|
||||||
thumbnailfile: video.thumbnailfile,
|
thumbnailfile: video.thumbnailfile,
|
||||||
previewfile: video.previewfile
|
previewfile: video.previewfile
|
||||||
}
|
}
|
||||||
|
|
|
@ -111,7 +111,7 @@
|
||||||
</tab>
|
</tab>
|
||||||
|
|
||||||
<tab heading="Advanced settings">
|
<tab heading="Advanced settings">
|
||||||
<div class="col-md-12">
|
<div class="col-md-12 advanced-settings">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<my-video-image
|
<my-video-image
|
||||||
inputLabel="Upload thumbnail" inputName="thumbnailfile" formControlName="thumbnailfile"
|
inputLabel="Upload thumbnail" inputName="thumbnailfile" formControlName="thumbnailfile"
|
||||||
|
@ -125,6 +125,17 @@
|
||||||
previewWidth="360px" previewHeight="200px"
|
previewWidth="360px" previewHeight="200px"
|
||||||
></my-video-image>
|
></my-video-image>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="support">Support (markdown)</label>
|
||||||
|
<my-markdown-textarea
|
||||||
|
id="support" formControlName="support" textareaWidth="500px" [previewColumn]="true" markdownType="enhanced"
|
||||||
|
[classes]="{ 'input-error': formErrors['support'] }"
|
||||||
|
></my-markdown-textarea>
|
||||||
|
<div *ngIf="formErrors.support" class="form-error">
|
||||||
|
{{ formErrors.support }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</tab>
|
</tab>
|
||||||
|
|
||||||
|
|
|
@ -59,6 +59,10 @@
|
||||||
padding: 0 15px !important;
|
padding: 0 15px !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.advanced-settings .form-group {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.submit-container {
|
.submit-container {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { Component, Input, OnInit } from '@angular/core'
|
import { Component, Input, OnInit } from '@angular/core'
|
||||||
import { FormBuilder, FormControl, FormGroup } from '@angular/forms'
|
import { FormBuilder, FormControl, FormGroup } from '@angular/forms'
|
||||||
import { ActivatedRoute, Router } from '@angular/router'
|
import { ActivatedRoute, Router } from '@angular/router'
|
||||||
import { VIDEO_IMAGE } from '@app/shared'
|
import { VIDEO_IMAGE, VIDEO_SUPPORT } from '@app/shared'
|
||||||
import { NotificationsService } from 'angular2-notifications'
|
import { NotificationsService } from 'angular2-notifications'
|
||||||
import 'rxjs/add/observable/forkJoin'
|
import 'rxjs/add/observable/forkJoin'
|
||||||
import { ServerService } from '../../../core/server'
|
import { ServerService } from '../../../core/server'
|
||||||
|
@ -60,6 +60,7 @@ export class VideoEditComponent implements OnInit {
|
||||||
this.formErrors['description'] = ''
|
this.formErrors['description'] = ''
|
||||||
this.formErrors['thumbnailfile'] = ''
|
this.formErrors['thumbnailfile'] = ''
|
||||||
this.formErrors['previewfile'] = ''
|
this.formErrors['previewfile'] = ''
|
||||||
|
this.formErrors['support'] = ''
|
||||||
|
|
||||||
this.validationMessages['name'] = VIDEO_NAME.MESSAGES
|
this.validationMessages['name'] = VIDEO_NAME.MESSAGES
|
||||||
this.validationMessages['privacy'] = VIDEO_PRIVACY.MESSAGES
|
this.validationMessages['privacy'] = VIDEO_PRIVACY.MESSAGES
|
||||||
|
@ -70,6 +71,7 @@ export class VideoEditComponent implements OnInit {
|
||||||
this.validationMessages['description'] = VIDEO_DESCRIPTION.MESSAGES
|
this.validationMessages['description'] = VIDEO_DESCRIPTION.MESSAGES
|
||||||
this.validationMessages['thumbnailfile'] = VIDEO_IMAGE.MESSAGES
|
this.validationMessages['thumbnailfile'] = VIDEO_IMAGE.MESSAGES
|
||||||
this.validationMessages['previewfile'] = VIDEO_IMAGE.MESSAGES
|
this.validationMessages['previewfile'] = VIDEO_IMAGE.MESSAGES
|
||||||
|
this.validationMessages['support'] = VIDEO_SUPPORT.MESSAGES
|
||||||
|
|
||||||
this.form.addControl('name', new FormControl('', VIDEO_NAME.VALIDATORS))
|
this.form.addControl('name', new FormControl('', VIDEO_NAME.VALIDATORS))
|
||||||
this.form.addControl('privacy', new FormControl('', VIDEO_PRIVACY.VALIDATORS))
|
this.form.addControl('privacy', new FormControl('', VIDEO_PRIVACY.VALIDATORS))
|
||||||
|
@ -83,6 +85,7 @@ export class VideoEditComponent implements OnInit {
|
||||||
this.form.addControl('tags', new FormControl(''))
|
this.form.addControl('tags', new FormControl(''))
|
||||||
this.form.addControl('thumbnailfile', new FormControl(''))
|
this.form.addControl('thumbnailfile', new FormControl(''))
|
||||||
this.form.addControl('previewfile', new FormControl(''))
|
this.form.addControl('previewfile', new FormControl(''))
|
||||||
|
this.form.addControl('support', new FormControl(''))
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit () {
|
ngOnInit () {
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
@import '_mixins';
|
@import '_mixins';
|
||||||
|
|
||||||
.root {
|
.root {
|
||||||
height: 150px;
|
height: auto;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
|
|
|
@ -61,8 +61,7 @@ export class VideoUpdateComponent extends FormReactive implements OnInit {
|
||||||
.switchMap(video => {
|
.switchMap(video => {
|
||||||
return this.videoService
|
return this.videoService
|
||||||
.loadCompleteDescription(video.descriptionPath)
|
.loadCompleteDescription(video.descriptionPath)
|
||||||
.do(description => video.description = description)
|
.map(description => Object.assign(video, { description }))
|
||||||
.map(() => video)
|
|
||||||
})
|
})
|
||||||
.subscribe(
|
.subscribe(
|
||||||
video => {
|
video => {
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
<div bsModal #modal="bs-modal" class="modal" tabindex="-1">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
|
||||||
|
<div class="modal-header">
|
||||||
|
<span class="close" aria-hidden="true" (click)="hide()"></span>
|
||||||
|
<h4 class="modal-title">Support</h4>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal-body">
|
||||||
|
|
||||||
|
<div [innerHTML]="videoHTMLSupport"></div>
|
||||||
|
|
||||||
|
<div class="form-group inputs">
|
||||||
|
<span class="action-button action-button-cancel" (click)="hide()">
|
||||||
|
Cancel
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -0,0 +1,3 @@
|
||||||
|
.action-button-cancel {
|
||||||
|
margin-right: 0 !important;
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
import { Component, Input, ViewChild } from '@angular/core'
|
||||||
|
import { MarkdownService } from '@app/videos/shared'
|
||||||
|
|
||||||
|
import { ModalDirective } from 'ngx-bootstrap/modal'
|
||||||
|
import { VideoDetails } from '../../../shared/video/video-details.model'
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'my-video-support',
|
||||||
|
templateUrl: './video-support.component.html',
|
||||||
|
styleUrls: [ './video-support.component.scss' ]
|
||||||
|
})
|
||||||
|
export class VideoSupportComponent {
|
||||||
|
@Input() video: VideoDetails = null
|
||||||
|
|
||||||
|
@ViewChild('modal') modal: ModalDirective
|
||||||
|
|
||||||
|
videoHTMLSupport = ''
|
||||||
|
|
||||||
|
constructor (private markdownService: MarkdownService) {
|
||||||
|
// empty
|
||||||
|
}
|
||||||
|
|
||||||
|
show () {
|
||||||
|
this.modal.show()
|
||||||
|
|
||||||
|
if (this.video.support) {
|
||||||
|
this.videoHTMLSupport = this.markdownService.enhancedMarkdownToHTML(this.video.support)
|
||||||
|
} else {
|
||||||
|
this.videoHTMLSupport = ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hide () {
|
||||||
|
this.modal.hide()
|
||||||
|
}
|
||||||
|
}
|
|
@ -44,9 +44,14 @@
|
||||||
<span class="icon icon-dislike" title="Dislike this video"></span>
|
<span class="icon icon-dislike" title="Dislike this video"></span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div (click)="showSupportModal()" class="action-button action-button-support">
|
||||||
|
<span class="icon icon-support"></span>
|
||||||
|
<span class="icon-text">Support</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div (click)="showShareModal()" class="action-button action-button-share">
|
<div (click)="showShareModal()" class="action-button action-button-share">
|
||||||
<span class="icon icon-share"></span>
|
<span class="icon icon-share"></span>
|
||||||
Share
|
<span class="icon-text">Share</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="action-more" dropdown dropup="true" placement="right">
|
<div class="action-more" dropdown dropup="true" placement="right">
|
||||||
|
@ -175,6 +180,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ng-template [ngIf]="video !== null">
|
<ng-template [ngIf]="video !== null">
|
||||||
|
<my-video-support #videoSupportModal [video]="video"></my-video-support>
|
||||||
<my-video-share #videoShareModal [video]="video"></my-video-share>
|
<my-video-share #videoShareModal [video]="video"></my-video-share>
|
||||||
<my-video-download #videoDownloadModal [video]="video"></my-video-download>
|
<my-video-download #videoDownloadModal [video]="video"></my-video-download>
|
||||||
<my-video-report #videoReportModal [video]="video"></my-video-report>
|
<my-video-report #videoReportModal [video]="video"></my-video-report>
|
||||||
|
|
|
@ -99,6 +99,7 @@
|
||||||
font-weight: $font-semibold;
|
font-weight: $font-semibold;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
padding: 0 10px 0 10px;
|
padding: 0 10px 0 10px;
|
||||||
|
white-space: nowrap;
|
||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
@include icon(21px);
|
@include icon(21px);
|
||||||
|
@ -114,6 +115,10 @@
|
||||||
background-image: url('../../../assets/images/video/dislike-grey.svg');
|
background-image: url('../../../assets/images/video/dislike-grey.svg');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.icon-support {
|
||||||
|
background-image: url('../../../assets/images/video/heart.svg');
|
||||||
|
}
|
||||||
|
|
||||||
&.icon-share {
|
&.icon-share {
|
||||||
background-image: url('../../../assets/images/video/share.svg');
|
background-image: url('../../../assets/images/video/share.svg');
|
||||||
}
|
}
|
||||||
|
@ -249,11 +254,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@media screen and (max-width: 1300px) {
|
@media screen and (max-width: 1600px) {
|
||||||
.other-videos {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.video-bottom {
|
.video-bottom {
|
||||||
.video-info {
|
.video-info {
|
||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
|
@ -288,6 +289,12 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 1200px) {
|
||||||
|
.other-videos {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 600px) {
|
@media screen and (max-width: 600px) {
|
||||||
.video-bottom {
|
.video-bottom {
|
||||||
margin: 20px 0 0 0;
|
margin: 20px 0 0 0;
|
||||||
|
@ -304,3 +311,9 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 450px) {
|
||||||
|
.video-bottom .action-button .icon-text {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { Component, ElementRef, NgZone, OnDestroy, OnInit, ViewChild } from '@angular/core'
|
import { Component, ElementRef, NgZone, OnDestroy, OnInit, ViewChild } from '@angular/core'
|
||||||
import { ActivatedRoute, Router } from '@angular/router'
|
import { ActivatedRoute, Router } from '@angular/router'
|
||||||
|
import { VideoSupportComponent } from '@app/videos/+video-watch/modal/video-support.component'
|
||||||
import { MetaService } from '@ngx-meta/core'
|
import { MetaService } from '@ngx-meta/core'
|
||||||
import { NotificationsService } from 'angular2-notifications'
|
import { NotificationsService } from 'angular2-notifications'
|
||||||
import { Observable } from 'rxjs/Observable'
|
import { Observable } from 'rxjs/Observable'
|
||||||
|
@ -28,6 +29,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
|
||||||
@ViewChild('videoDownloadModal') videoDownloadModal: VideoDownloadComponent
|
@ViewChild('videoDownloadModal') videoDownloadModal: VideoDownloadComponent
|
||||||
@ViewChild('videoShareModal') videoShareModal: VideoShareComponent
|
@ViewChild('videoShareModal') videoShareModal: VideoShareComponent
|
||||||
@ViewChild('videoReportModal') videoReportModal: VideoReportComponent
|
@ViewChild('videoReportModal') videoReportModal: VideoReportComponent
|
||||||
|
@ViewChild('videoSupportModal') videoSupportModal: VideoSupportComponent
|
||||||
|
|
||||||
otherVideosDisplayed: Video[] = []
|
otherVideosDisplayed: Video[] = []
|
||||||
|
|
||||||
|
@ -189,6 +191,10 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
|
||||||
this.videoReportModal.show()
|
this.videoReportModal.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
showSupportModal () {
|
||||||
|
this.videoSupportModal.show()
|
||||||
|
}
|
||||||
|
|
||||||
showShareModal () {
|
showShareModal () {
|
||||||
this.videoShareModal.show()
|
this.videoShareModal.show()
|
||||||
}
|
}
|
||||||
|
@ -264,7 +270,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
this.videoHTMLDescription = this.markdownService.markdownToHTML(this.video.description)
|
this.videoHTMLDescription = this.markdownService.textMarkdownToHTML(this.video.description)
|
||||||
}
|
}
|
||||||
|
|
||||||
private setVideoLikesBarTooltipText () {
|
private setVideoLikesBarTooltipText () {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { NgModule } from '@angular/core'
|
import { NgModule } from '@angular/core'
|
||||||
|
import { VideoSupportComponent } from '@app/videos/+video-watch/modal/video-support.component'
|
||||||
import { TooltipModule } from 'ngx-bootstrap/tooltip'
|
import { TooltipModule } from 'ngx-bootstrap/tooltip'
|
||||||
import { ClipboardModule } from 'ngx-clipboard'
|
import { ClipboardModule } from 'ngx-clipboard'
|
||||||
import { SharedModule } from '../../shared'
|
import { SharedModule } from '../../shared'
|
||||||
|
@ -29,6 +30,7 @@ import { VideoWatchComponent } from './video-watch.component'
|
||||||
VideoDownloadComponent,
|
VideoDownloadComponent,
|
||||||
VideoShareComponent,
|
VideoShareComponent,
|
||||||
VideoReportComponent,
|
VideoReportComponent,
|
||||||
|
VideoSupportComponent,
|
||||||
VideoCommentsComponent,
|
VideoCommentsComponent,
|
||||||
VideoCommentAddComponent,
|
VideoCommentAddComponent,
|
||||||
VideoCommentComponent
|
VideoCommentComponent
|
||||||
|
|
|
@ -4,33 +4,51 @@ import * as MarkdownIt from 'markdown-it'
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class MarkdownService {
|
export class MarkdownService {
|
||||||
private markdownIt: MarkdownIt.MarkdownIt
|
private textMarkdownIt: MarkdownIt.MarkdownIt
|
||||||
private linkifier: MarkdownIt.MarkdownIt
|
private linkifier: MarkdownIt.MarkdownIt
|
||||||
|
private enhancedMarkdownIt: MarkdownIt.MarkdownIt
|
||||||
|
|
||||||
constructor () {
|
constructor () {
|
||||||
this.markdownIt = new MarkdownIt('zero', { linkify: true, breaks: true })
|
this.textMarkdownIt = new MarkdownIt('zero', { linkify: true, breaks: true })
|
||||||
.enable('linkify')
|
.enable('linkify')
|
||||||
.enable('autolink')
|
.enable('autolink')
|
||||||
.enable('emphasis')
|
.enable('emphasis')
|
||||||
.enable('link')
|
.enable('link')
|
||||||
.enable('newline')
|
.enable('newline')
|
||||||
.enable('list')
|
.enable('list')
|
||||||
this.setTargetToLinks(this.markdownIt)
|
this.setTargetToLinks(this.textMarkdownIt)
|
||||||
|
|
||||||
|
this.enhancedMarkdownIt = new MarkdownIt('zero', { linkify: true, breaks: true })
|
||||||
|
.enable('linkify')
|
||||||
|
.enable('autolink')
|
||||||
|
.enable('emphasis')
|
||||||
|
.enable('link')
|
||||||
|
.enable('newline')
|
||||||
|
.enable('list')
|
||||||
|
.enable('image')
|
||||||
|
this.setTargetToLinks(this.enhancedMarkdownIt)
|
||||||
|
|
||||||
this.linkifier = new MarkdownIt('zero', { linkify: true })
|
this.linkifier = new MarkdownIt('zero', { linkify: true })
|
||||||
.enable('linkify')
|
.enable('linkify')
|
||||||
this.setTargetToLinks(this.linkifier)
|
this.setTargetToLinks(this.linkifier)
|
||||||
}
|
}
|
||||||
|
|
||||||
markdownToHTML (markdown: string) {
|
textMarkdownToHTML (markdown: string) {
|
||||||
const html = this.markdownIt.render(markdown)
|
const html = this.textMarkdownIt.render(markdown)
|
||||||
|
|
||||||
// Avoid linkify truncated links
|
return this.avoidTruncatedLinks(html)
|
||||||
return html.replace(/<a[^>]+>([^<]+)<\/a>\s*...(<\/p>)?$/mi, '$1...')
|
}
|
||||||
|
|
||||||
|
enhancedMarkdownToHTML (markdown: string) {
|
||||||
|
const html = this.enhancedMarkdownIt.render(markdown)
|
||||||
|
|
||||||
|
return this.avoidTruncatedLinks(html)
|
||||||
}
|
}
|
||||||
|
|
||||||
linkify (text: string) {
|
linkify (text: string) {
|
||||||
return this.linkifier.render(text)
|
const html = this.linkifier.render(text)
|
||||||
|
|
||||||
|
return this.avoidTruncatedLinks(html)
|
||||||
}
|
}
|
||||||
|
|
||||||
private setTargetToLinks (markdownIt: MarkdownIt.MarkdownIt) {
|
private setTargetToLinks (markdownIt: MarkdownIt.MarkdownIt) {
|
||||||
|
@ -53,4 +71,8 @@ export class MarkdownService {
|
||||||
return defaultRender(tokens, idx, options, env, self)
|
return defaultRender(tokens, idx, options, env, self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private avoidTruncatedLinks (html) {
|
||||||
|
return html.replace(/<a[^>]+>([^<]+)<\/a>\s*...(<\/p>)?$/mi, '$1...')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<defs></defs>
|
||||||
|
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||||
|
<g id="Artboard-4" transform="translate(-48.000000, -1046.000000)" fill-rule="nonzero" fill="#585858">
|
||||||
|
<g id="Extras" transform="translate(48.000000, 1046.000000)">
|
||||||
|
<g id="heart">
|
||||||
|
<path d="M12.0174466,21 L20.9041801,11.3556763 C22.6291961,9.13778099 22.2795957,5.90145416 20.1233257,4.12713796 C17.9670557,2.35282175 14.8206518,2.71241362 13.0956358,4.93030888 L12.0174465,6.5 L10.9043642,4.93030888 C9.17934824,2.71241362 6.0329443,2.35282175 3.87667432,4.12713796 C1.72040435,5.90145416 1.37080391,9.13778099 3.09581989,11.3556763 L12.0174466,21 Z"></path>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 967 B |
|
@ -24,6 +24,10 @@ body {
|
||||||
color: #000;
|
color: #000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
strong {
|
||||||
|
font-weight: $font-semibold;
|
||||||
|
}
|
||||||
|
|
||||||
input.readonly {
|
input.readonly {
|
||||||
/* Force blank on readonly inputs */
|
/* Force blank on readonly inputs */
|
||||||
background-color: #fff !important;
|
background-color: #fff !important;
|
||||||
|
|
|
@ -24,7 +24,7 @@ $control-bar-height: 34px;
|
||||||
|
|
||||||
.vjs-big-play-button {
|
.vjs-big-play-button {
|
||||||
outline: 0;
|
outline: 0;
|
||||||
font-size: 7em;
|
font-size: 6em;
|
||||||
|
|
||||||
$big-play-width: 1.5em;
|
$big-play-width: 1.5em;
|
||||||
$big-play-height: 1em;
|
$big-play-height: 1em;
|
||||||
|
@ -340,7 +340,7 @@ $control-bar-height: 34px;
|
||||||
|
|
||||||
@media screen and (max-width: 550px) {
|
@media screen and (max-width: 550px) {
|
||||||
.vjs-big-play-button {
|
.vjs-big-play-button {
|
||||||
font-size: 6.5em;
|
font-size: 5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.vjs-webtorrent {
|
.vjs-webtorrent {
|
||||||
|
@ -358,7 +358,7 @@ $control-bar-height: 34px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.vjs-big-play-button {
|
.vjs-big-play-button {
|
||||||
font-size: 5em;
|
font-size: 4em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.vjs-volume-control {
|
.vjs-volume-control {
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
|
|
||||||
```
|
```
|
||||||
$ sudo apt update
|
$ sudo apt update
|
||||||
$ sudo apt install nginx ffmpeg postgresql openssl g++ make redis-server
|
$ sudo apt install nginx ffmpeg postgresql openssl g++ make redis-server git
|
||||||
```
|
```
|
||||||
|
|
||||||
## Arch Linux
|
## Arch Linux
|
||||||
|
@ -18,7 +18,7 @@ $ sudo apt install nginx ffmpeg postgresql openssl g++ make redis-server
|
||||||
1. Run:
|
1. Run:
|
||||||
|
|
||||||
```
|
```
|
||||||
$ sudo pacman -S nodejs yarn ffmpeg postgresql openssl redis
|
$ sudo pacman -S nodejs yarn ffmpeg postgresql openssl redis git
|
||||||
```
|
```
|
||||||
|
|
||||||
## CentOS 7
|
## CentOS 7
|
||||||
|
@ -36,7 +36,7 @@ $ sudo pacman -S nodejs yarn ffmpeg postgresql openssl redis
|
||||||
$ sudo yum update
|
$ sudo yum update
|
||||||
$ sudo yum install epel-release
|
$ sudo yum install epel-release
|
||||||
$ sudo yum update
|
$ sudo yum update
|
||||||
$ sudo yum install nginx postgresql postgresql-server openssl gcc make redis
|
$ sudo yum install nginx postgresql postgresql-server openssl gcc make redis git
|
||||||
```
|
```
|
||||||
|
|
||||||
## Other distributions
|
## Other distributions
|
||||||
|
|
Loading…
Reference in New Issue