Federated video streaming platform using ActivityPub and P2P in the web browser with Angular. https://joinpeertube.org/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

118 lines
3.2 KiB

  1. import { ViewportScroller } from '@angular/common'
  2. import truncate from 'lodash-es/truncate'
  3. import { Subject } from 'rxjs'
  4. import { debounceTime, distinctUntilChanged } from 'rxjs/operators'
  5. import { Component, ElementRef, forwardRef, Input, OnInit, ViewChild } from '@angular/core'
  6. import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'
  7. import { MarkdownService } from '@app/core'
  8. @Component({
  9. selector: 'my-markdown-textarea',
  10. templateUrl: './markdown-textarea.component.html',
  11. styleUrls: [ './markdown-textarea.component.scss' ],
  12. providers: [
  13. {
  14. provide: NG_VALUE_ACCESSOR,
  15. useExisting: forwardRef(() => MarkdownTextareaComponent),
  16. multi: true
  17. }
  18. ]
  19. })
  20. export class MarkdownTextareaComponent implements ControlValueAccessor, OnInit {
  21. @Input() content = ''
  22. @Input() classes: string[] | { [klass: string]: any[] | any } = []
  23. @Input() textareaMaxWidth = '100%'
  24. @Input() textareaHeight = '150px'
  25. @Input() truncate: number
  26. @Input() markdownType: 'text' | 'enhanced' = 'text'
  27. @Input() markdownVideo = false
  28. @Input() name = 'description'
  29. @ViewChild('textarea') textareaElement: ElementRef
  30. truncatedPreviewHTML = ''
  31. previewHTML = ''
  32. isMaximized = false
  33. private contentChanged = new Subject<string>()
  34. private scrollPosition: [number, number]
  35. constructor (
  36. private viewportScroller: ViewportScroller,
  37. private markdownService: MarkdownService
  38. ) { }
  39. ngOnInit () {
  40. this.contentChanged
  41. .pipe(
  42. debounceTime(150),
  43. distinctUntilChanged()
  44. )
  45. .subscribe(() => this.updatePreviews())
  46. this.contentChanged.next(this.content)
  47. }
  48. propagateChange = (_: any) => { /* empty */ }
  49. writeValue (description: string) {
  50. this.content = description
  51. this.contentChanged.next(this.content)
  52. }
  53. registerOnChange (fn: (_: any) => void) {
  54. this.propagateChange = fn
  55. }
  56. registerOnTouched () {
  57. // Unused
  58. }
  59. onModelChange () {
  60. this.propagateChange(this.content)
  61. this.contentChanged.next(this.content)
  62. }
  63. onMaximizeClick () {
  64. this.isMaximized = !this.isMaximized
  65. // Make sure textarea have the focus
  66. this.textareaElement.nativeElement.focus()
  67. // Make sure the window has no scrollbars
  68. if (!this.isMaximized) {
  69. this.unlockBodyScroll()
  70. } else {
  71. this.lockBodyScroll()
  72. }
  73. }
  74. private lockBodyScroll () {
  75. this.scrollPosition = this.viewportScroller.getScrollPosition()
  76. document.getElementById('content').classList.add('lock-scroll')
  77. }
  78. private unlockBodyScroll () {
  79. document.getElementById('content').classList.remove('lock-scroll')
  80. this.viewportScroller.scrollToPosition(this.scrollPosition)
  81. }
  82. private async updatePreviews () {
  83. if (this.content === null || this.content === undefined) return
  84. this.truncatedPreviewHTML = await this.markdownRender(truncate(this.content, { length: this.truncate }))
  85. this.previewHTML = await this.markdownRender(this.content)
  86. }
  87. private async markdownRender (text: string) {
  88. const html = this.markdownType === 'text' ?
  89. await this.markdownService.textMarkdownToHTML(text) :
  90. await this.markdownService.enhancedMarkdownToHTML(text)
  91. return this.markdownVideo ? this.markdownService.processVideoTimestamps(html) : html
  92. }
  93. }