Merge branch 'master' into develop
[oweals/peertube.git] / client / src / app / shared / forms / markdown-textarea.component.ts
1 import { debounceTime, distinctUntilChanged } from 'rxjs/operators'
2 import { Component, forwardRef, Input, OnInit } from '@angular/core'
3 import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'
4 import { Subject } from 'rxjs'
5 import truncate from 'lodash-es/truncate'
6 import { ScreenService } from '@app/shared/misc/screen.service'
7 import { MarkdownService } from '@app/shared/renderer'
8
9 @Component({
10   selector: 'my-markdown-textarea',
11   templateUrl: './markdown-textarea.component.html',
12   styleUrls: [ './markdown-textarea.component.scss' ],
13   providers: [
14     {
15       provide: NG_VALUE_ACCESSOR,
16       useExisting: forwardRef(() => MarkdownTextareaComponent),
17       multi: true
18     }
19   ]
20 })
21
22 export class MarkdownTextareaComponent implements ControlValueAccessor, OnInit {
23   @Input() content = ''
24   @Input() classes: string[] | { [klass: string]: any[] | any } = []
25   @Input() textareaWidth = '100%'
26   @Input() textareaHeight = '150px'
27   @Input() previewColumn = false
28   @Input() truncate: number
29   @Input() markdownType: 'text' | 'enhanced' = 'text'
30   @Input() markdownVideo = false
31   @Input() name = 'description'
32
33   textareaMarginRight = '0'
34   flexDirection = 'column'
35   truncatedPreviewHTML = ''
36   previewHTML = ''
37
38   private contentChanged = new Subject<string>()
39
40   constructor (
41     private screenService: ScreenService,
42     private markdownService: MarkdownService
43 ) {}
44
45   ngOnInit () {
46     this.contentChanged
47         .pipe(
48           debounceTime(150),
49           distinctUntilChanged()
50         )
51         .subscribe(() => this.updatePreviews())
52
53     this.contentChanged.next(this.content)
54
55     if (this.previewColumn) {
56       this.flexDirection = 'row'
57       this.textareaMarginRight = '15px'
58     }
59   }
60
61   propagateChange = (_: any) => { /* empty */ }
62
63   writeValue (description: string) {
64     this.content = description
65
66     this.contentChanged.next(this.content)
67   }
68
69   registerOnChange (fn: (_: any) => void) {
70     this.propagateChange = fn
71   }
72
73   registerOnTouched () {
74     // Unused
75   }
76
77   onModelChange () {
78     this.propagateChange(this.content)
79
80     this.contentChanged.next(this.content)
81   }
82
83   arePreviewsDisplayed () {
84     return this.screenService.isInSmallView() === false
85   }
86
87   private async updatePreviews () {
88     if (this.content === null || this.content === undefined) return
89
90     this.truncatedPreviewHTML = await this.markdownRender(truncate(this.content, { length: this.truncate }))
91     this.previewHTML = await this.markdownRender(this.content)
92   }
93
94   private async markdownRender (text: string) {
95     const html = this.markdownType === 'text' ?
96       await this.markdownService.textMarkdownToHTML(text) :
97       await this.markdownService.enhancedMarkdownToHTML(text)
98
99     return this.markdownVideo ? this.markdownService.processVideoTimestamps(html) : html
100   }
101 }