Try to fix subscriptions inconsistencies
[oweals/peertube.git] / client / src / app / shared / menu / top-menu-dropdown.component.ts
1 import { Component, Input, OnDestroy, OnInit } from '@angular/core'
2 import { filter, take } from 'rxjs/operators'
3 import { NavigationEnd, Router } from '@angular/router'
4 import { Subscription } from 'rxjs'
5 import { NgbDropdown } from '@ng-bootstrap/ng-bootstrap'
6 import { GlobalIconName } from '@app/shared/images/global-icon.component'
7 import { ScreenService } from '@app/shared/misc/screen.service'
8
9 export type TopMenuDropdownParam = {
10   label: string
11   routerLink?: string
12
13   children?: {
14     label: string
15     routerLink: string
16
17     iconName?: GlobalIconName
18   }[]
19 }
20
21 @Component({
22   selector: 'my-top-menu-dropdown',
23   templateUrl: './top-menu-dropdown.component.html',
24   styleUrls: [ './top-menu-dropdown.component.scss' ]
25 })
26 export class TopMenuDropdownComponent implements OnInit, OnDestroy {
27   @Input() menuEntries: TopMenuDropdownParam[] = []
28
29   suffixLabels: { [ parentLabel: string ]: string }
30   hasIcons = false
31   container: undefined | 'body' = undefined
32
33   private openedOnHover = false
34   private routeSub: Subscription
35
36   constructor (
37     private router: Router,
38     private screen: ScreenService
39   ) {}
40
41   ngOnInit () {
42     this.updateChildLabels(window.location.pathname)
43
44     this.routeSub = this.router.events
45                         .pipe(filter(event => event instanceof NavigationEnd))
46                         .subscribe(() => this.updateChildLabels(window.location.pathname))
47
48     this.hasIcons = this.menuEntries.some(
49       e => e.children && e.children.some(c => !!c.iconName)
50     )
51
52     // FIXME: We have to set body for the container to avoid because of scroll overflow on mobile view
53     // But this break our hovering system
54     if (this.screen.isInMobileView()) {
55       this.container = 'body'
56     }
57   }
58
59   ngOnDestroy () {
60     if (this.routeSub) this.routeSub.unsubscribe()
61   }
62
63   openDropdownOnHover (dropdown: NgbDropdown) {
64     this.openedOnHover = true
65     dropdown.open()
66
67     // Menu was closed
68     dropdown.openChange
69             .pipe(take(1))
70             .subscribe(() => this.openedOnHover = false)
71   }
72
73   dropdownAnchorClicked (dropdown: NgbDropdown) {
74     if (this.openedOnHover) {
75       this.openedOnHover = false
76       return
77     }
78
79     return dropdown.toggle()
80   }
81
82   closeDropdownIfHovered (dropdown: NgbDropdown) {
83     if (this.openedOnHover === false) return
84
85     dropdown.close()
86     this.openedOnHover = false
87   }
88
89   private updateChildLabels (path: string) {
90     this.suffixLabels = {}
91
92     for (const entry of this.menuEntries) {
93       if (!entry.children) continue
94
95       for (const child of entry.children) {
96         if (path.startsWith(child.routerLink)) {
97           this.suffixLabels[entry.label] = child.label
98         }
99       }
100     }
101   }
102 }