diff --git a/client/src/app/shared/shared-custom-markup/custom-markup-container.component.html b/client/src/app/shared/shared-custom-markup/custom-markup-container.component.html
index 6bf2294a3..685efc9ab 100644
--- a/client/src/app/shared/shared-custom-markup/custom-markup-container.component.html
+++ b/client/src/app/shared/shared-custom-markup/custom-markup-container.component.html
@@ -1 +1 @@
-
+
diff --git a/client/src/app/shared/shared-custom-markup/custom-markup-container.component.ts b/client/src/app/shared/shared-custom-markup/custom-markup-container.component.ts
index 3d49c6768..4e802b14d 100644
--- a/client/src/app/shared/shared-custom-markup/custom-markup-container.component.ts
+++ b/client/src/app/shared/shared-custom-markup/custom-markup-container.component.ts
@@ -10,6 +10,8 @@ export class CustomMarkupContainerComponent implements OnChanges {
@Input() content: string
+ displayed = false
+
constructor (
private customMarkupService: CustomMarkupService
) { }
@@ -19,8 +21,13 @@ export class CustomMarkupContainerComponent implements OnChanges {
}
private async buildElement () {
- const element = await this.customMarkupService.buildElement(this.content)
- this.contentWrapper.nativeElement.appendChild(element)
- }
+ if (!this.content) return
+ const { rootElement, componentsLoaded } = await this.customMarkupService.buildElement(this.content)
+ this.contentWrapper.nativeElement.appendChild(rootElement)
+
+ await componentsLoaded
+
+ this.displayed = true
+ }
}
diff --git a/client/src/app/shared/shared-custom-markup/custom-markup.service.ts b/client/src/app/shared/shared-custom-markup/custom-markup.service.ts
index cb1110593..15da94709 100644
--- a/client/src/app/shared/shared-custom-markup/custom-markup.service.ts
+++ b/client/src/app/shared/shared-custom-markup/custom-markup.service.ts
@@ -1,3 +1,4 @@
+import { first } from 'rxjs/operators'
import { ComponentRef, Injectable } from '@angular/core'
import { MarkdownService } from '@app/core'
import {
@@ -19,8 +20,9 @@ import {
VideoMiniatureMarkupComponent,
VideosListMarkupComponent
} from './peertube-custom-tags'
+import { CustomMarkupComponent } from './peertube-custom-tags/shared'
-type AngularBuilderFunction = (el: HTMLElement) => ComponentRef
+type AngularBuilderFunction = (el: HTMLElement) => ComponentRef
type HTMLBuilderFunction = (el: HTMLElement) => HTMLElement
@Injectable()
@@ -45,7 +47,10 @@ export class CustomMarkupService {
private dynamicElementService: DynamicElementService,
private markdown: MarkdownService
) {
- this.customMarkdownRenderer = async (text: string) => this.buildElement(text)
+ this.customMarkdownRenderer = (text: string) => {
+ return this.buildElement(text)
+ .then(({ rootElement }) => rootElement)
+ }
}
getCustomMarkdownRenderer () {
@@ -60,23 +65,30 @@ export class CustomMarkupService {
for (const selector of Object.keys(this.htmlBuilders)) {
rootElement.querySelectorAll(selector)
- .forEach((e: HTMLElement) => {
- try {
- const element = this.execHTMLBuilder(selector, e)
- // Insert as first child
- e.insertBefore(element, e.firstChild)
- } catch (err) {
- console.error('Cannot inject component %s.', selector, err)
- }
- })
+ .forEach((e: HTMLElement) => {
+ try {
+ const element = this.execHTMLBuilder(selector, e)
+ // Insert as first child
+ e.insertBefore(element, e.firstChild)
+ } catch (err) {
+ console.error('Cannot inject component %s.', selector, err)
+ }
+ })
}
+ const loadedPromises: Promise[] = []
+
for (const selector of Object.keys(this.angularBuilders)) {
rootElement.querySelectorAll(selector)
.forEach((e: HTMLElement) => {
try {
const component = this.execAngularBuilder(selector, e)
+ if (component.instance.loaded) {
+ const p = component.instance.loaded.pipe(first()).toPromise()
+ loadedPromises.push(p)
+ }
+
this.dynamicElementService.injectElement(e, component)
} catch (err) {
console.error('Cannot inject component %s.', selector, err)
@@ -84,7 +96,7 @@ export class CustomMarkupService {
})
}
- return rootElement
+ return { rootElement, componentsLoaded: Promise.all(loadedPromises) }
}
private getSupportedTags () {
diff --git a/client/src/app/shared/shared-custom-markup/peertube-custom-tags/button-markup.component.ts b/client/src/app/shared/shared-custom-markup/peertube-custom-tags/button-markup.component.ts
index 987b37d19..1af060548 100644
--- a/client/src/app/shared/shared-custom-markup/peertube-custom-tags/button-markup.component.ts
+++ b/client/src/app/shared/shared-custom-markup/peertube-custom-tags/button-markup.component.ts
@@ -1,5 +1,6 @@
import { Component, Input } from '@angular/core'
import { VideoChannel } from '../../shared-main'
+import { CustomMarkupComponent } from './shared'
/*
* Markup component that creates a button
@@ -10,13 +11,14 @@ import { VideoChannel } from '../../shared-main'
templateUrl: 'button-markup.component.html',
styleUrls: [ 'button-markup.component.scss' ]
})
-export class ButtonMarkupComponent {
+export class ButtonMarkupComponent implements CustomMarkupComponent {
@Input() theme: 'primary' | 'secondary'
@Input() href: string
@Input() label: string
@Input() blankTarget?: boolean
channel: VideoChannel
+ loaded: undefined
getTarget () {
if (this.blankTarget === true) return '_blank'
diff --git a/client/src/app/shared/shared-custom-markup/peertube-custom-tags/channel-miniature-markup.component.ts b/client/src/app/shared/shared-custom-markup/peertube-custom-tags/channel-miniature-markup.component.ts
index 87caec8a5..a91debbef 100644
--- a/client/src/app/shared/shared-custom-markup/peertube-custom-tags/channel-miniature-markup.component.ts
+++ b/client/src/app/shared/shared-custom-markup/peertube-custom-tags/channel-miniature-markup.component.ts
@@ -1,8 +1,9 @@
import { map, switchMap } from 'rxjs/operators'
-import { Component, Input, OnInit } from '@angular/core'
+import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'
import { MarkdownService, UserService } from '@app/core'
import { Video, VideoSortField } from '@shared/models/videos'
import { VideoChannel, VideoChannelService, VideoService } from '../../shared-main'
+import { CustomMarkupComponent } from './shared'
/*
* Markup component that creates a channel miniature only
@@ -13,11 +14,13 @@ import { VideoChannel, VideoChannelService, VideoService } from '../../shared-ma
templateUrl: 'channel-miniature-markup.component.html',
styleUrls: [ 'channel-miniature-markup.component.scss' ]
})
-export class ChannelMiniatureMarkupComponent implements OnInit {
+export class ChannelMiniatureMarkupComponent implements CustomMarkupComponent, OnInit {
@Input() name: string
@Input() displayLatestVideo: boolean
@Input() displayDescription: boolean
+ @Output() loaded = new EventEmitter()
+
channel: VideoChannel
descriptionHTML: string
totalVideos: number
@@ -61,9 +64,13 @@ export class ChannelMiniatureMarkupComponent implements OnInit {
map(user => user.nsfwPolicy),
switchMap(nsfwPolicy => this.videoService.getVideoChannelVideos({ ...videoOptions, nsfwPolicy }))
)
- .subscribe(({ total, data }) => {
- this.totalVideos = total
- this.video = data[0]
+ .subscribe({
+ next: ({ total, data }) => {
+ this.totalVideos = total
+ this.video = data[0]
+ },
+
+ complete: () => this.loaded.emit(true)
})
}
}
diff --git a/client/src/app/shared/shared-custom-markup/peertube-custom-tags/embed-markup.component.ts b/client/src/app/shared/shared-custom-markup/peertube-custom-tags/embed-markup.component.ts
index a854d89f6..4462903db 100644
--- a/client/src/app/shared/shared-custom-markup/peertube-custom-tags/embed-markup.component.ts
+++ b/client/src/app/shared/shared-custom-markup/peertube-custom-tags/embed-markup.component.ts
@@ -1,15 +1,18 @@
import { buildPlaylistLink, buildVideoLink, buildVideoOrPlaylistEmbed } from 'src/assets/player/utils'
import { environment } from 'src/environments/environment'
import { Component, ElementRef, Input, OnInit } from '@angular/core'
+import { CustomMarkupComponent } from './shared'
@Component({
selector: 'my-embed-markup',
template: ''
})
-export class EmbedMarkupComponent implements OnInit {
+export class EmbedMarkupComponent implements CustomMarkupComponent, OnInit {
@Input() uuid: string
@Input() type: 'video' | 'playlist' = 'video'
+ loaded: undefined
+
constructor (private el: ElementRef) { }
ngOnInit () {
diff --git a/client/src/app/shared/shared-custom-markup/peertube-custom-tags/playlist-miniature-markup.component.ts b/client/src/app/shared/shared-custom-markup/peertube-custom-tags/playlist-miniature-markup.component.ts
index eddc3636e..42a42d711 100644
--- a/client/src/app/shared/shared-custom-markup/peertube-custom-tags/playlist-miniature-markup.component.ts
+++ b/client/src/app/shared/shared-custom-markup/peertube-custom-tags/playlist-miniature-markup.component.ts
@@ -1,6 +1,7 @@
-import { Component, Input, OnInit } from '@angular/core'
+import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'
import { MiniatureDisplayOptions } from '../../shared-video-miniature'
import { VideoPlaylist, VideoPlaylistService } from '../../shared-video-playlist'
+import { CustomMarkupComponent } from './shared'
/*
* Markup component that creates a playlist miniature only
@@ -11,9 +12,11 @@ import { VideoPlaylist, VideoPlaylistService } from '../../shared-video-playlist
templateUrl: 'playlist-miniature-markup.component.html',
styleUrls: [ 'playlist-miniature-markup.component.scss' ]
})
-export class PlaylistMiniatureMarkupComponent implements OnInit {
+export class PlaylistMiniatureMarkupComponent implements CustomMarkupComponent, OnInit {
@Input() uuid: string
+ @Output() loaded = new EventEmitter()
+
playlist: VideoPlaylist
displayOptions: MiniatureDisplayOptions = {
@@ -33,6 +36,10 @@ export class PlaylistMiniatureMarkupComponent implements OnInit {
ngOnInit () {
this.playlistService.getVideoPlaylist(this.uuid)
- .subscribe(playlist => this.playlist = playlist)
+ .subscribe({
+ next: playlist => this.playlist = playlist,
+
+ complete: () => this.loaded.emit(true)
+ })
}
}
diff --git a/client/src/app/shared/shared-custom-markup/peertube-custom-tags/shared/custom-markup.component.ts b/client/src/app/shared/shared-custom-markup/peertube-custom-tags/shared/custom-markup.component.ts
new file mode 100644
index 000000000..adfd48c55
--- /dev/null
+++ b/client/src/app/shared/shared-custom-markup/peertube-custom-tags/shared/custom-markup.component.ts
@@ -0,0 +1,5 @@
+import { EventEmitter } from '@angular/core'
+
+export interface CustomMarkupComponent {
+ loaded: EventEmitter | undefined
+}
diff --git a/client/src/app/shared/shared-custom-markup/peertube-custom-tags/shared/index.ts b/client/src/app/shared/shared-custom-markup/peertube-custom-tags/shared/index.ts
new file mode 100644
index 000000000..a6a7bbd92
--- /dev/null
+++ b/client/src/app/shared/shared-custom-markup/peertube-custom-tags/shared/index.ts
@@ -0,0 +1 @@
+export * from './custom-markup.component'
diff --git a/client/src/app/shared/shared-custom-markup/peertube-custom-tags/video-miniature-markup.component.ts b/client/src/app/shared/shared-custom-markup/peertube-custom-tags/video-miniature-markup.component.ts
index dfb4c497f..6ee5123e0 100644
--- a/client/src/app/shared/shared-custom-markup/peertube-custom-tags/video-miniature-markup.component.ts
+++ b/client/src/app/shared/shared-custom-markup/peertube-custom-tags/video-miniature-markup.component.ts
@@ -1,7 +1,8 @@
-import { Component, Input, OnInit } from '@angular/core'
+import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'
import { AuthService } from '@app/core'
import { Video, VideoService } from '../../shared-main'
import { MiniatureDisplayOptions } from '../../shared-video-miniature'
+import { CustomMarkupComponent } from './shared'
/*
* Markup component that creates a video miniature only
@@ -12,10 +13,12 @@ import { MiniatureDisplayOptions } from '../../shared-video-miniature'
templateUrl: 'video-miniature-markup.component.html',
styleUrls: [ 'video-miniature-markup.component.scss' ]
})
-export class VideoMiniatureMarkupComponent implements OnInit {
+export class VideoMiniatureMarkupComponent implements CustomMarkupComponent, OnInit {
@Input() uuid: string
@Input() onlyDisplayTitle: boolean
+ @Output() loaded = new EventEmitter()
+
video: Video
displayOptions: MiniatureDisplayOptions = {
@@ -46,6 +49,10 @@ export class VideoMiniatureMarkupComponent implements OnInit {
}
this.videoService.getVideo({ videoId: this.uuid })
- .subscribe(video => this.video = video)
+ .subscribe({
+ next: video => this.video = video,
+
+ complete: () => this.loaded.emit(true)
+ })
}
}
diff --git a/client/src/app/shared/shared-custom-markup/peertube-custom-tags/videos-list-markup.component.ts b/client/src/app/shared/shared-custom-markup/peertube-custom-tags/videos-list-markup.component.ts
index d4402dd9f..02738022e 100644
--- a/client/src/app/shared/shared-custom-markup/peertube-custom-tags/videos-list-markup.component.ts
+++ b/client/src/app/shared/shared-custom-markup/peertube-custom-tags/videos-list-markup.component.ts
@@ -1,8 +1,9 @@
-import { Component, Input, OnInit } from '@angular/core'
+import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'
import { AuthService } from '@app/core'
import { VideoFilter, VideoSortField } from '@shared/models'
import { Video, VideoService } from '../../shared-main'
import { MiniatureDisplayOptions } from '../../shared-video-miniature'
+import { CustomMarkupComponent } from './shared'
/*
* Markup component list videos depending on criterias
@@ -13,7 +14,7 @@ import { MiniatureDisplayOptions } from '../../shared-video-miniature'
templateUrl: 'videos-list-markup.component.html',
styleUrls: [ 'videos-list-markup.component.scss' ]
})
-export class VideosListMarkupComponent implements OnInit {
+export class VideosListMarkupComponent implements CustomMarkupComponent, OnInit {
@Input() sort: string
@Input() categoryOneOf: number[]
@Input() languageOneOf: string[]
@@ -22,6 +23,8 @@ export class VideosListMarkupComponent implements OnInit {
@Input() filter: VideoFilter
@Input() maxRows: number
+ @Output() loaded = new EventEmitter()
+
videos: Video[]
displayOptions: MiniatureDisplayOptions = {
@@ -73,6 +76,10 @@ export class VideosListMarkupComponent implements OnInit {
}
this.videoService.getVideos(options)
- .subscribe(({ data }) => this.videos = data)
+ .subscribe({
+ next: ({ data }) => this.videos = data,
+
+ complete: () => this.loaded.emit(true)
+ })
}
}