From 9d9597df427542eb5c7d3ba8ff5aeb146fab40e2 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Thu, 26 Oct 2017 15:01:47 +0200 Subject: Add markdown support to video description --- client/package.json | 2 ++ .../videos/+video-edit/video-update.component.ts | 2 +- .../videos/+video-watch/video-watch.component.html | 4 +-- .../videos/+video-watch/video-watch.component.ts | 8 +++-- .../app/videos/+video-watch/video-watch.module.ts | 3 +- client/src/app/videos/shared/index.ts | 1 + client/src/app/videos/shared/markdown.service.ts | 40 ++++++++++++++++++++++ client/src/app/videos/shared/video-edit.model.ts | 15 ++++++++ client/src/app/videos/shared/video.service.ts | 2 +- client/yarn.lock | 28 +++++++++++++++ 10 files changed, 97 insertions(+), 8 deletions(-) create mode 100644 client/src/app/videos/shared/markdown.service.ts diff --git a/client/package.json b/client/package.json index 8b949ef80..8d42e0c87 100644 --- a/client/package.json +++ b/client/package.json @@ -35,6 +35,7 @@ "@angularclass/hmr-loader": "^3.0.2", "@ngx-meta/core": "^4.0.1", "@types/core-js": "^0.9.28", + "@types/markdown-it": "^0.0.4", "@types/node": "^8.0.33", "@types/source-map": "^0.5.1", "@types/uglify-js": "^2.0.27", @@ -66,6 +67,7 @@ "inline-manifest-webpack-plugin": "^3.0.1", "intl": "^1.2.4", "json-loader": "^0.5.4", + "markdown-it": "^8.4.0", "ng-router-loader": "^2.0.0", "ngc-webpack": "3.2.2", "ngx-bootstrap": "1.9.3", diff --git a/client/src/app/videos/+video-edit/video-update.component.ts b/client/src/app/videos/+video-edit/video-update.component.ts index 70cb334fd..30390ac05 100644 --- a/client/src/app/videos/+video-edit/video-update.component.ts +++ b/client/src/app/videos/+video-edit/video-update.component.ts @@ -87,7 +87,7 @@ export class VideoUpdateComponent extends FormReactive implements OnInit { this.videoService.getVideo(uuid) .subscribe( video => { - this.video = video + this.video = new VideoEdit(video) this.hydrateFormFromVideo() }, 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 5d5827344..6e502aae2 100644 --- a/client/src/app/videos/+video-watch/video-watch.component.html +++ b/client/src/app/videos/+video-watch/video-watch.component.html @@ -128,9 +128,7 @@ Published on {{ video.createdAt | date:'short' }} -
- {{ video.description }} -
+
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 529e2e84f..2e1adb043 100644 --- a/client/src/app/videos/+video-watch/video-watch.component.ts +++ b/client/src/app/videos/+video-watch/video-watch.component.ts @@ -13,7 +13,7 @@ import { AuthService, ConfirmService } from '../../core' import { VideoDownloadComponent } from './video-download.component' import { VideoShareComponent } from './video-share.component' import { VideoReportComponent } from './video-report.component' -import { VideoDetails, VideoService } from '../shared' +import { VideoDetails, VideoService, MarkdownService } from '../shared' import { VideoBlacklistService } from '../../shared' import { UserVideoRateType, VideoRateType } from '../../../../../shared' @@ -38,6 +38,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy { video: VideoDetails = null videoPlayerLoaded = false videoNotFound = false + videoHTMLDescription = '' private paramsSub: Subscription @@ -50,7 +51,8 @@ export class VideoWatchComponent implements OnInit, OnDestroy { private confirmService: ConfirmService, private metaService: MetaService, private authService: AuthService, - private notificationsService: NotificationsService + private notificationsService: NotificationsService, + private markdownService: MarkdownService ) {} ngOnInit () { @@ -259,6 +261,8 @@ export class VideoWatchComponent implements OnInit, OnDestroy { }) }) + this.videoHTMLDescription = this.markdownService.markdownToHTML(this.video.description) + this.setOpenGraphTags() this.checkUserRating() } 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 c6c1344ce..1b983200d 100644 --- a/client/src/app/videos/+video-watch/video-watch.module.ts +++ b/client/src/app/videos/+video-watch/video-watch.module.ts @@ -1,7 +1,7 @@ import { NgModule } from '@angular/core' import { VideoWatchRoutingModule } from './video-watch-routing.module' -import { VideoService } from '../shared' +import { VideoService, MarkdownService } from '../shared' import { SharedModule } from '../../shared' import { VideoWatchComponent } from './video-watch.component' @@ -28,6 +28,7 @@ import { VideoDownloadComponent } from './video-download.component' ], providers: [ + MarkdownService, VideoService ] }) diff --git a/client/src/app/videos/shared/index.ts b/client/src/app/videos/shared/index.ts index dcaa4e090..09d961dd3 100644 --- a/client/src/app/videos/shared/index.ts +++ b/client/src/app/videos/shared/index.ts @@ -1,4 +1,5 @@ export * from './sort-field.type' +export * from './markdown.service' export * from './video.model' export * from './video-details.model' export * from './video-edit.model' diff --git a/client/src/app/videos/shared/markdown.service.ts b/client/src/app/videos/shared/markdown.service.ts new file mode 100644 index 000000000..d8b5b76b6 --- /dev/null +++ b/client/src/app/videos/shared/markdown.service.ts @@ -0,0 +1,40 @@ +import { Injectable } from '@angular/core' + +import * as MarkdownIt from 'markdown-it' + +@Injectable() +export class MarkdownService { + private markdownIt: MarkdownIt.MarkdownIt + + constructor () { + this.markdownIt = new MarkdownIt('zero', { linkify: true }) + .enable('linkify') + .enable('autolink') + .enable('emphasis') + .enable('link') + .enable('newline') + + // Snippet from markdown-it documentation: https://github.com/markdown-it/markdown-it/blob/master/docs/architecture.md#renderer + const defaultRender = this.markdownIt.renderer.rules.link_open || function (tokens, idx, options, env, self) { + return self.renderToken(tokens, idx, options) + } + + this.markdownIt.renderer.rules.link_open = function (tokens, idx, options, env, self) { + // If you are sure other plugins can't add `target` - drop check below + const aIndex = tokens[idx].attrIndex('target') + + if (aIndex < 0) { + tokens[idx].attrPush(['target', '_blank']) // add new attribute + } else { + tokens[idx].attrs[aIndex][1] = '_blank' // replace value of existing attr + } + + // pass token to default renderer. + return defaultRender(tokens, idx, options, env, self) + } + } + + markdownToHTML (markdown: string) { + return this.markdownIt.render(markdown) + } +} diff --git a/client/src/app/videos/shared/video-edit.model.ts b/client/src/app/videos/shared/video-edit.model.ts index f30d8feba..e0b7bf130 100644 --- a/client/src/app/videos/shared/video-edit.model.ts +++ b/client/src/app/videos/shared/video-edit.model.ts @@ -1,3 +1,5 @@ +import { VideoDetails } from './video-details.model' + export class VideoEdit { category: number licence: number @@ -10,6 +12,19 @@ export class VideoEdit { uuid?: string id?: number + constructor (videoDetails: VideoDetails) { + this.id = videoDetails.id + this.uuid = videoDetails.uuid + this.category = videoDetails.category + this.licence = videoDetails.licence + this.language = videoDetails.language + this.description = videoDetails.description + this.name = videoDetails.name + this.tags = videoDetails.tags + this.nsfw = videoDetails.nsfw + this.channel = videoDetails.channel.id + } + patch (values: Object) { Object.keys(values).forEach((key) => { this[key] = values[key] diff --git a/client/src/app/videos/shared/video.service.ts b/client/src/app/videos/shared/video.service.ts index 06fb3313e..8fdc1f213 100644 --- a/client/src/app/videos/shared/video.service.ts +++ b/client/src/app/videos/shared/video.service.ts @@ -36,7 +36,7 @@ export class VideoService { private restService: RestService ) {} - getVideo (uuid: string) { + getVideo (uuid: string): Observable { return this.authHttp.get(VideoService.BASE_VIDEO_URL + uuid) .map(videoHash => new VideoDetails(videoHash)) .catch((res) => this.restExtractor.handleError(res)) diff --git a/client/yarn.lock b/client/yarn.lock index b63c76e2f..23ab3a3df 100644 --- a/client/yarn.lock +++ b/client/yarn.lock @@ -102,6 +102,10 @@ dependencies: "@types/node" "*" +"@types/markdown-it@^0.0.4": + version "0.0.4" + resolved "https://registry.yarnpkg.com/@types/markdown-it/-/markdown-it-0.0.4.tgz#c5f67365916044b342dae8d702724788ba0b5b74" + "@types/node@*": version "8.0.25" resolved "https://registry.yarnpkg.com/@types/node/-/node-8.0.25.tgz#66ecaf4df93f5281b48427ee96fbcdfc4f0cdce1" @@ -3842,6 +3846,12 @@ levn@^0.3.0, levn@~0.3.0: prelude-ls "~1.1.2" type-check "~0.3.2" +linkify-it@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/linkify-it/-/linkify-it-2.0.3.tgz#d94a4648f9b1c179d64fa97291268bdb6ce9434f" + dependencies: + uc.micro "^1.0.1" + load-ip-set@^1.2.7: version "1.3.1" resolved "https://registry.yarnpkg.com/load-ip-set/-/load-ip-set-1.3.1.tgz#cfd050c6916e7ba0ca85d0b566e7854713eb495e" @@ -4169,6 +4179,16 @@ map-visit@^0.1.5: lazy-cache "^2.0.1" object-visit "^0.3.4" +markdown-it@^8.4.0: + version "8.4.0" + resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-8.4.0.tgz#e2400881bf171f7018ed1bd9da441dac8af6306d" + dependencies: + argparse "^1.0.7" + entities "~1.1.1" + linkify-it "^2.0.0" + mdurl "^1.0.1" + uc.micro "^1.0.3" + marked-terminal@^1.6.2: version "1.7.0" resolved "https://registry.yarnpkg.com/marked-terminal/-/marked-terminal-1.7.0.tgz#c8c460881c772c7604b64367007ee5f77f125904" @@ -4194,6 +4214,10 @@ md5.js@^1.3.4: hash-base "^3.0.0" inherits "^2.0.1" +mdurl@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e" + media-typer@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" @@ -6824,6 +6848,10 @@ typescript@^2.5.2: version "2.5.3" resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.5.3.tgz#df3dcdc38f3beb800d4bc322646b04a3f6ca7f0d" +uc.micro@^1.0.1, uc.micro@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.3.tgz#7ed50d5e0f9a9fb0a573379259f2a77458d50192" + uglify-js@3.0.x, uglify-js@^3.0.6: version "3.0.28" resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.0.28.tgz#96b8495f0272944787b5843a1679aa326640d5f7" -- cgit v1.2.3