mirror of https://github.com/Chocobozzz/PeerTube
Add support to video support on client
parent
dddf58c8ce
commit
07fa4c97ca
|
@ -27,8 +27,8 @@ export class AboutComponent implements OnInit {
|
|||
this.serverService.getAbout()
|
||||
.subscribe(
|
||||
res => {
|
||||
this.descriptionHTML = this.markdownService.markdownToHTML(res.instance.description)
|
||||
this.termsHTML = this.markdownService.markdownToHTML(res.instance.terms)
|
||||
this.descriptionHTML = this.markdownService.textMarkdownToHTML(res.instance.description)
|
||||
this.termsHTML = this.markdownService.textMarkdownToHTML(res.instance.terms)
|
||||
},
|
||||
|
||||
err => this.notificationsService.error('Error', err)
|
||||
|
|
|
@ -49,7 +49,6 @@
|
|||
|
||||
#peertube-title {
|
||||
@include disable-default-a-behaviour;
|
||||
width: 100%;
|
||||
font-size: 20px;
|
||||
font-weight: $font-bold;
|
||||
color: inherit !important;
|
||||
|
@ -79,6 +78,10 @@
|
|||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 350px) {
|
||||
flex: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.header-right {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
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 { isInSmallView } from '@app/shared/misc/utils'
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
width: calc(100% - 150px);
|
||||
}
|
||||
|
||||
@media screen and (max-width: 400px) {
|
||||
@media screen and (max-width: 600px) {
|
||||
width: calc(100% - 70px);
|
||||
}
|
||||
}
|
||||
|
@ -50,7 +50,7 @@
|
|||
margin-right: 6px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 400px) {
|
||||
@media screen and (max-width: 600px) {
|
||||
margin-right: 10px;
|
||||
padding: 0 10px;
|
||||
|
||||
|
|
|
@ -44,10 +44,10 @@ export const VIDEO_CHANNEL = {
|
|||
}
|
||||
|
||||
export const VIDEO_DESCRIPTION = {
|
||||
VALIDATORS: [ Validators.minLength(3), Validators.maxLength(3000) ],
|
||||
VALIDATORS: [ Validators.minLength(3), Validators.maxLength(10000) ],
|
||||
MESSAGES: {
|
||||
'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.'
|
||||
}
|
||||
}
|
||||
|
||||
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 }">
|
||||
<textarea
|
||||
[(ngModel)]="description" (ngModelChange)="onModelChange()"
|
||||
[ngClass]="classes" [ngStyle]="{ width: textareaWidth, height: textareaHeight, 'margin-right': textareaMarginRight }"
|
||||
id="description" name="description">
|
||||
[(ngModel)]="content" (ngModelChange)="onModelChange()"
|
||||
[ngClass]="classes" [ngStyle]="{ width: textareaWidth, height: textareaHeight, 'margin-right': textareaMarginRight }"
|
||||
id="description" name="description">
|
||||
</textarea>
|
||||
|
||||
<tabset *ngIf="arePreviewsDisplayed()" class="previews">
|
||||
<tab *ngIf="truncate !== undefined" heading="Truncated description preview" [innerHTML]="truncatedDescriptionHTML"></tab>
|
||||
<tab heading="Complete description preview" [innerHTML]="descriptionHTML"></tab>
|
||||
<tab *ngIf="truncate !== undefined" heading="Truncated preview" [innerHTML]="truncatedPreviewHTML"></tab>
|
||||
<tab heading="Complete preview" [innerHTML]="previewHTML"></tab>
|
||||
</tabset>
|
||||
</div>
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
min-height: 75px;
|
||||
padding: 15px;
|
||||
font-size: 15px;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,29 +21,30 @@ import truncate from 'lodash-es/truncate'
|
|||
})
|
||||
|
||||
export class MarkdownTextareaComponent implements ControlValueAccessor, OnInit {
|
||||
@Input() description = ''
|
||||
@Input() content = ''
|
||||
@Input() classes: string[] = []
|
||||
@Input() textareaWidth = '100%'
|
||||
@Input() textareaHeight = '150px'
|
||||
@Input() previewColumn = false
|
||||
@Input() truncate: number
|
||||
@Input() markdownType: 'text' | 'enhanced' = 'text'
|
||||
|
||||
textareaMarginRight = '0'
|
||||
flexDirection = 'column'
|
||||
truncatedDescriptionHTML = ''
|
||||
descriptionHTML = ''
|
||||
truncatedPreviewHTML = ''
|
||||
previewHTML = ''
|
||||
|
||||
private descriptionChanged = new Subject<string>()
|
||||
private contentChanged = new Subject<string>()
|
||||
|
||||
constructor (private markdownService: MarkdownService) {}
|
||||
|
||||
ngOnInit () {
|
||||
this.descriptionChanged
|
||||
this.contentChanged
|
||||
.debounceTime(150)
|
||||
.distinctUntilChanged()
|
||||
.subscribe(() => this.updateDescriptionPreviews())
|
||||
.subscribe(() => this.updatePreviews())
|
||||
|
||||
this.descriptionChanged.next(this.description)
|
||||
this.contentChanged.next(this.content)
|
||||
|
||||
if (this.previewColumn) {
|
||||
this.flexDirection = 'row'
|
||||
|
@ -54,9 +55,9 @@ export class MarkdownTextareaComponent implements ControlValueAccessor, OnInit {
|
|||
propagateChange = (_: any) => { /* empty */ }
|
||||
|
||||
writeValue (description: string) {
|
||||
this.description = description
|
||||
this.content = description
|
||||
|
||||
this.descriptionChanged.next(this.description)
|
||||
this.contentChanged.next(this.content)
|
||||
}
|
||||
|
||||
registerOnChange (fn: (_: any) => void) {
|
||||
|
@ -68,19 +69,25 @@ export class MarkdownTextareaComponent implements ControlValueAccessor, OnInit {
|
|||
}
|
||||
|
||||
onModelChange () {
|
||||
this.propagateChange(this.description)
|
||||
this.propagateChange(this.content)
|
||||
|
||||
this.descriptionChanged.next(this.description)
|
||||
this.contentChanged.next(this.content)
|
||||
}
|
||||
|
||||
arePreviewsDisplayed () {
|
||||
return isInSmallView() === false
|
||||
}
|
||||
|
||||
private updateDescriptionPreviews () {
|
||||
if (this.description === null || this.description === undefined) return
|
||||
private updatePreviews () {
|
||||
if (this.content === null || this.content === undefined) return
|
||||
|
||||
this.truncatedDescriptionHTML = this.markdownService.markdownToHTML(truncate(this.description, { length: this.truncate }))
|
||||
this.descriptionHTML = this.markdownService.markdownToHTML(this.description)
|
||||
this.truncatedPreviewHTML = this.markdownRender(truncate(this.content, { length: this.truncate }))
|
||||
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.account = hash.account
|
||||
this.tags = hash.tags
|
||||
this.support = hash.support
|
||||
this.commentsEnabled = hash.commentsEnabled
|
||||
|
||||
this.likesPercent = (this.likes / (this.likes + this.dislikes)) * 100
|
||||
|
|
|
@ -12,6 +12,7 @@ export class VideoEdit {
|
|||
commentsEnabled: boolean
|
||||
channel: number
|
||||
privacy: VideoPrivacy
|
||||
support: string
|
||||
thumbnailfile?: any
|
||||
previewfile?: any
|
||||
thumbnailUrl: string
|
||||
|
@ -33,6 +34,7 @@ export class VideoEdit {
|
|||
this.commentsEnabled = videoDetails.commentsEnabled
|
||||
this.channel = videoDetails.channel.id
|
||||
this.privacy = videoDetails.privacy
|
||||
this.support = videoDetails.support
|
||||
this.thumbnailUrl = videoDetails.thumbnailUrl
|
||||
this.previewUrl = videoDetails.previewUrl
|
||||
}
|
||||
|
@ -50,6 +52,7 @@ export class VideoEdit {
|
|||
licence: this.licence,
|
||||
language: this.language,
|
||||
description: this.description,
|
||||
support: this.support,
|
||||
name: this.name,
|
||||
tags: this.tags,
|
||||
nsfw: this.nsfw,
|
||||
|
|
|
@ -62,6 +62,7 @@ export class VideoService {
|
|||
tags: video.tags,
|
||||
nsfw: video.nsfw,
|
||||
commentsEnabled: video.commentsEnabled,
|
||||
support: video.support,
|
||||
thumbnailfile: video.thumbnailfile,
|
||||
previewfile: video.previewfile
|
||||
}
|
||||
|
|
|
@ -111,7 +111,7 @@
|
|||
</tab>
|
||||
|
||||
<tab heading="Advanced settings">
|
||||
<div class="col-md-12">
|
||||
<div class="col-md-12 advanced-settings">
|
||||
<div class="form-group">
|
||||
<my-video-image
|
||||
inputLabel="Upload thumbnail" inputName="thumbnailfile" formControlName="thumbnailfile"
|
||||
|
@ -125,6 +125,17 @@
|
|||
previewWidth="360px" previewHeight="200px"
|
||||
></my-video-image>
|
||||
</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>
|
||||
</tab>
|
||||
|
||||
|
|
|
@ -59,6 +59,10 @@
|
|||
padding: 0 15px !important;
|
||||
}
|
||||
}
|
||||
|
||||
.advanced-settings .form-group {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.submit-container {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { Component, Input, OnInit } from '@angular/core'
|
||||
import { FormBuilder, FormControl, FormGroup } from '@angular/forms'
|
||||
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 'rxjs/add/observable/forkJoin'
|
||||
import { ServerService } from '../../../core/server'
|
||||
|
@ -60,6 +60,7 @@ export class VideoEditComponent implements OnInit {
|
|||
this.formErrors['description'] = ''
|
||||
this.formErrors['thumbnailfile'] = ''
|
||||
this.formErrors['previewfile'] = ''
|
||||
this.formErrors['support'] = ''
|
||||
|
||||
this.validationMessages['name'] = VIDEO_NAME.MESSAGES
|
||||
this.validationMessages['privacy'] = VIDEO_PRIVACY.MESSAGES
|
||||
|
@ -70,6 +71,7 @@ export class VideoEditComponent implements OnInit {
|
|||
this.validationMessages['description'] = VIDEO_DESCRIPTION.MESSAGES
|
||||
this.validationMessages['thumbnailfile'] = 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('privacy', new FormControl('', VIDEO_PRIVACY.VALIDATORS))
|
||||
|
@ -83,6 +85,7 @@ export class VideoEditComponent implements OnInit {
|
|||
this.form.addControl('tags', new FormControl(''))
|
||||
this.form.addControl('thumbnailfile', new FormControl(''))
|
||||
this.form.addControl('previewfile', new FormControl(''))
|
||||
this.form.addControl('support', new FormControl(''))
|
||||
}
|
||||
|
||||
ngOnInit () {
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
@import '_mixins';
|
||||
|
||||
.root {
|
||||
height: 150px;
|
||||
height: auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
|
|
|
@ -61,8 +61,7 @@ export class VideoUpdateComponent extends FormReactive implements OnInit {
|
|||
.switchMap(video => {
|
||||
return this.videoService
|
||||
.loadCompleteDescription(video.descriptionPath)
|
||||
.do(description => video.description = description)
|
||||
.map(() => video)
|
||||
.map(description => Object.assign(video, { description }))
|
||||
})
|
||||
.subscribe(
|
||||
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>
|
||||
</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">
|
||||
<span class="icon icon-share"></span>
|
||||
Share
|
||||
<span class="icon-text">Share</span>
|
||||
</div>
|
||||
|
||||
<div class="action-more" dropdown dropup="true" placement="right">
|
||||
|
@ -175,6 +180,7 @@
|
|||
</div>
|
||||
|
||||
<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-download #videoDownloadModal [video]="video"></my-video-download>
|
||||
<my-video-report #videoReportModal [video]="video"></my-video-report>
|
||||
|
|
|
@ -99,6 +99,7 @@
|
|||
font-weight: $font-semibold;
|
||||
display: inline-block;
|
||||
padding: 0 10px 0 10px;
|
||||
white-space: nowrap;
|
||||
|
||||
.icon {
|
||||
@include icon(21px);
|
||||
|
@ -114,6 +115,10 @@
|
|||
background-image: url('../../../assets/images/video/dislike-grey.svg');
|
||||
}
|
||||
|
||||
&.icon-support {
|
||||
background-image: url('../../../assets/images/video/heart.svg');
|
||||
}
|
||||
|
||||
&.icon-share {
|
||||
background-image: url('../../../assets/images/video/share.svg');
|
||||
}
|
||||
|
@ -249,11 +254,7 @@
|
|||
}
|
||||
|
||||
|
||||
@media screen and (max-width: 1300px) {
|
||||
.other-videos {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1600px) {
|
||||
.video-bottom {
|
||||
.video-info {
|
||||
margin-right: 0;
|
||||
|
@ -288,6 +289,12 @@
|
|||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1200px) {
|
||||
.other-videos {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 600px) {
|
||||
.video-bottom {
|
||||
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 { ActivatedRoute, Router } from '@angular/router'
|
||||
import { VideoSupportComponent } from '@app/videos/+video-watch/modal/video-support.component'
|
||||
import { MetaService } from '@ngx-meta/core'
|
||||
import { NotificationsService } from 'angular2-notifications'
|
||||
import { Observable } from 'rxjs/Observable'
|
||||
|
@ -28,6 +29,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
|
|||
@ViewChild('videoDownloadModal') videoDownloadModal: VideoDownloadComponent
|
||||
@ViewChild('videoShareModal') videoShareModal: VideoShareComponent
|
||||
@ViewChild('videoReportModal') videoReportModal: VideoReportComponent
|
||||
@ViewChild('videoSupportModal') videoSupportModal: VideoSupportComponent
|
||||
|
||||
otherVideosDisplayed: Video[] = []
|
||||
|
||||
|
@ -189,6 +191,10 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
|
|||
this.videoReportModal.show()
|
||||
}
|
||||
|
||||
showSupportModal () {
|
||||
this.videoSupportModal.show()
|
||||
}
|
||||
|
||||
showShareModal () {
|
||||
this.videoShareModal.show()
|
||||
}
|
||||
|
@ -264,7 +270,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
|
|||
return
|
||||
}
|
||||
|
||||
this.videoHTMLDescription = this.markdownService.markdownToHTML(this.video.description)
|
||||
this.videoHTMLDescription = this.markdownService.textMarkdownToHTML(this.video.description)
|
||||
}
|
||||
|
||||
private setVideoLikesBarTooltipText () {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { NgModule } from '@angular/core'
|
||||
import { VideoSupportComponent } from '@app/videos/+video-watch/modal/video-support.component'
|
||||
import { TooltipModule } from 'ngx-bootstrap/tooltip'
|
||||
import { ClipboardModule } from 'ngx-clipboard'
|
||||
import { SharedModule } from '../../shared'
|
||||
|
@ -29,6 +30,7 @@ import { VideoWatchComponent } from './video-watch.component'
|
|||
VideoDownloadComponent,
|
||||
VideoShareComponent,
|
||||
VideoReportComponent,
|
||||
VideoSupportComponent,
|
||||
VideoCommentsComponent,
|
||||
VideoCommentAddComponent,
|
||||
VideoCommentComponent
|
||||
|
|
|
@ -4,33 +4,51 @@ import * as MarkdownIt from 'markdown-it'
|
|||
|
||||
@Injectable()
|
||||
export class MarkdownService {
|
||||
private markdownIt: MarkdownIt.MarkdownIt
|
||||
private textMarkdownIt: MarkdownIt.MarkdownIt
|
||||
private linkifier: MarkdownIt.MarkdownIt
|
||||
private enhancedMarkdownIt: MarkdownIt.MarkdownIt
|
||||
|
||||
constructor () {
|
||||
this.markdownIt = new MarkdownIt('zero', { linkify: true, breaks: true })
|
||||
this.textMarkdownIt = new MarkdownIt('zero', { linkify: true, breaks: true })
|
||||
.enable('linkify')
|
||||
.enable('autolink')
|
||||
.enable('emphasis')
|
||||
.enable('link')
|
||||
.enable('newline')
|
||||
.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 })
|
||||
.enable('linkify')
|
||||
this.setTargetToLinks(this.linkifier)
|
||||
}
|
||||
|
||||
markdownToHTML (markdown: string) {
|
||||
const html = this.markdownIt.render(markdown)
|
||||
textMarkdownToHTML (markdown: string) {
|
||||
const html = this.textMarkdownIt.render(markdown)
|
||||
|
||||
// Avoid linkify truncated links
|
||||
return html.replace(/<a[^>]+>([^<]+)<\/a>\s*...(<\/p>)?$/mi, '$1...')
|
||||
return this.avoidTruncatedLinks(html)
|
||||
}
|
||||
|
||||
enhancedMarkdownToHTML (markdown: string) {
|
||||
const html = this.enhancedMarkdownIt.render(markdown)
|
||||
|
||||
return this.avoidTruncatedLinks(html)
|
||||
}
|
||||
|
||||
linkify (text: string) {
|
||||
return this.linkifier.render(text)
|
||||
const html = this.linkifier.render(text)
|
||||
|
||||
return this.avoidTruncatedLinks(html)
|
||||
}
|
||||
|
||||
private setTargetToLinks (markdownIt: MarkdownIt.MarkdownIt) {
|
||||
|
@ -53,4 +71,8 @@ export class MarkdownService {
|
|||
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;
|
||||
}
|
||||
|
||||
strong {
|
||||
font-weight: $font-semibold;
|
||||
}
|
||||
|
||||
input.readonly {
|
||||
/* Force blank on readonly inputs */
|
||||
background-color: #fff !important;
|
||||
|
|
|
@ -24,7 +24,7 @@ $control-bar-height: 34px;
|
|||
|
||||
.vjs-big-play-button {
|
||||
outline: 0;
|
||||
font-size: 7em;
|
||||
font-size: 6em;
|
||||
|
||||
$big-play-width: 1.5em;
|
||||
$big-play-height: 1em;
|
||||
|
@ -340,7 +340,7 @@ $control-bar-height: 34px;
|
|||
|
||||
@media screen and (max-width: 550px) {
|
||||
.vjs-big-play-button {
|
||||
font-size: 6.5em;
|
||||
font-size: 5em;
|
||||
}
|
||||
|
||||
.vjs-webtorrent {
|
||||
|
@ -358,7 +358,7 @@ $control-bar-height: 34px;
|
|||
}
|
||||
|
||||
.vjs-big-play-button {
|
||||
font-size: 5em;
|
||||
font-size: 4em;
|
||||
}
|
||||
|
||||
.vjs-volume-control {
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
```
|
||||
$ 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
|
||||
|
@ -18,7 +18,7 @@ $ sudo apt install nginx ffmpeg postgresql openssl g++ make redis-server
|
|||
1. Run:
|
||||
|
||||
```
|
||||
$ sudo pacman -S nodejs yarn ffmpeg postgresql openssl redis
|
||||
$ sudo pacman -S nodejs yarn ffmpeg postgresql openssl redis git
|
||||
```
|
||||
|
||||
## CentOS 7
|
||||
|
@ -36,7 +36,7 @@ $ sudo pacman -S nodejs yarn ffmpeg postgresql openssl redis
|
|||
$ sudo yum update
|
||||
$ sudo yum install epel-release
|
||||
$ 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
|
||||
|
|
Loading…
Reference in New Issue