aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src/app/core/routing/custom-reuse-strategy.ts
blob: 1498e221fbf387533c4c9864b5be704045dca14e (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
77
78
79
80
81
82
83
84
85
86
87
88
89
import { ComponentRef, Injectable } from '@angular/core'
import { ActivatedRouteSnapshot, DetachedRouteHandle, RouteReuseStrategy } from '@angular/router'
import { DisableForReuseHook } from './disable-for-reuse-hook'
import { PeerTubeRouterService, RouterSetting } from './peertube-router.service'

@Injectable()
export class CustomReuseStrategy implements RouteReuseStrategy {
  storedRouteHandles = new Map<string, DetachedRouteHandle>()
  recentlyUsed: string

  private readonly MAX_SIZE = 2

  // Decides if the route should be stored
  shouldDetach (route: ActivatedRouteSnapshot): boolean {
    return this.isReuseEnabled(route)
  }

  // Store the information for the route we're destructing
  store (route: ActivatedRouteSnapshot, handle: DetachedRouteHandle): void {
    if (!handle) return

    const key = this.generateKey(route)
    this.recentlyUsed = key

    console.log('Storing component %s to reuse later.', key)

    const componentRef = (handle as any).componentRef as ComponentRef<DisableForReuseHook>
    componentRef.instance.disableForReuse()
    componentRef.changeDetectorRef.detectChanges()

    this.storedRouteHandles.set(key, handle)

    this.gb()
  }

  // Return true if we have a stored route object for the next route
  shouldAttach (route: ActivatedRouteSnapshot): boolean {
    const key = this.generateKey(route)
    return this.isReuseEnabled(route) && this.storedRouteHandles.has(key)
  }

  // If we returned true in shouldAttach(), now return the actual route data for restoration
  retrieve (route: ActivatedRouteSnapshot): DetachedRouteHandle {
    if (!this.isReuseEnabled(route)) return undefined

    const key = this.generateKey(route)
    this.recentlyUsed = key

    console.log('Reusing component %s.', key)

    const handle = this.storedRouteHandles.get(key)
    if (!handle) return handle;

    (handle as any).componentRef.instance.enabledForReuse()

    return handle
  }

  // Reuse the route if we're going to and from the same route
  shouldReuseRoute (future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {
    return future.routeConfig === curr.routeConfig
  }

  private gb () {
    if (this.storedRouteHandles.size >= this.MAX_SIZE) {
      this.storedRouteHandles.forEach((r, key) => {
        if (key === this.recentlyUsed) return

        console.log('Removing stored component %s.', key);

        (r as any).componentRef.destroy()
        this.storedRouteHandles.delete(key)
      })
    }
  }

  private generateKey (route: ActivatedRouteSnapshot) {
    const reuse = route.data.reuse
    if (!reuse) return undefined

    return reuse.key + JSON.stringify(route.queryParams)
  }

  private isReuseEnabled (route: ActivatedRouteSnapshot) {
    // Cannot use peertube router here because of cyclic router dependency
    return route.data.reuse?.enabled &&
      !!(route.queryParams[PeerTubeRouterService.ROUTE_SETTING_NAME] & RouterSetting.REUSE_COMPONENT)
  }
}