aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src/app/shared/shared-main/angular/defer-loading.directive.ts
blob: 9a10e90e3c4be8c1f4d59ad45e99176f07c9cd89 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
import * as debug from 'debug'
import {
  AfterViewInit,
  ChangeDetectorRef,
  ContentChild,
  Directive,
  ElementRef,
  EmbeddedViewRef,
  EventEmitter,
  OnDestroy,
  Output,
  TemplateRef,
  ViewContainerRef
} from '@angular/core'

const logger = debug('peertube:main:DeferLoadingDirective')

@Directive({
  selector: '[myDeferLoading]'
})
export class DeferLoadingDirective implements AfterViewInit, OnDestroy {
  @ContentChild(TemplateRef) template: TemplateRef<any>

  @Output() loaded: EventEmitter<any> = new EventEmitter()

  view: EmbeddedViewRef<any>

  private observer: IntersectionObserver

  constructor (
    private el: ElementRef,
    private viewContainer: ViewContainerRef,
    private cd: ChangeDetectorRef
  ) { }

  ngAfterViewInit () {
    if (this.hasIncompatibleBrowser()) {
      return this.load()
    }

    this.observer = new IntersectionObserver(entries => {
      const entry = entries[0]
      if (!entry.isIntersecting || entry.target !== this.el.nativeElement) return

      this.observer.unobserve(this.el.nativeElement)
      this.load()
    }, { threshold: 0.1 })

    this.observer.observe(this.el.nativeElement)
  }

  load () {
    if (this.isLoaded()) return

    logger('Loading component')

    this.viewContainer.clear()
    this.view = this.viewContainer.createEmbeddedView(this.template, {}, 0)
    this.loaded.emit()
    this.cd.detectChanges()
  }

  isLoaded () {
    return this.view != null
  }

  ngOnDestroy () {
    this.view = null

    if (this.observer) this.observer.disconnect()
  }

  private hasIncompatibleBrowser () {
    return !('IntersectionObserver' in window)
  }
}