diff options
Diffstat (limited to 'client/src/app/app.component.ts')
-rw-r--r-- | client/src/app/app.component.ts | 131 |
1 files changed, 95 insertions, 36 deletions
diff --git a/client/src/app/app.component.ts b/client/src/app/app.component.ts index c5c5a8f66..ad0588b99 100644 --- a/client/src/app/app.component.ts +++ b/client/src/app/app.component.ts | |||
@@ -1,13 +1,14 @@ | |||
1 | import { Component, OnInit } from '@angular/core' | 1 | import { Component, OnInit } from '@angular/core' |
2 | import { DomSanitizer, SafeHtml } from '@angular/platform-browser' | 2 | import { DomSanitizer, SafeHtml } from '@angular/platform-browser' |
3 | import { GuardsCheckStart, NavigationEnd, Router } from '@angular/router' | 3 | import { Event, GuardsCheckStart, NavigationEnd, Router, Scroll } from '@angular/router' |
4 | import { AuthService, RedirectService, ServerService, ThemeService } from '@app/core' | 4 | import { AuthService, RedirectService, ServerService, ThemeService } from '@app/core' |
5 | import { is18nPath } from '../../../shared/models/i18n' | 5 | import { is18nPath } from '../../../shared/models/i18n' |
6 | import { ScreenService } from '@app/shared/misc/screen.service' | 6 | import { ScreenService } from '@app/shared/misc/screen.service' |
7 | import { skip, debounceTime } from 'rxjs/operators' | 7 | import { debounceTime, filter, map, pairwise, skip } from 'rxjs/operators' |
8 | import { HotkeysService, Hotkey } from 'angular2-hotkeys' | 8 | import { Hotkey, HotkeysService } from 'angular2-hotkeys' |
9 | import { I18n } from '@ngx-translate/i18n-polyfill' | 9 | import { I18n } from '@ngx-translate/i18n-polyfill' |
10 | import { fromEvent } from 'rxjs' | 10 | import { fromEvent } from 'rxjs' |
11 | import { ViewportScroller } from '@angular/common' | ||
11 | 12 | ||
12 | @Component({ | 13 | @Component({ |
13 | selector: 'my-app', | 14 | selector: 'my-app', |
@@ -22,6 +23,7 @@ export class AppComponent implements OnInit { | |||
22 | 23 | ||
23 | constructor ( | 24 | constructor ( |
24 | private i18n: I18n, | 25 | private i18n: I18n, |
26 | private viewportScroller: ViewportScroller, | ||
25 | private router: Router, | 27 | private router: Router, |
26 | private authService: AuthService, | 28 | private authService: AuthService, |
27 | private serverService: ServerService, | 29 | private serverService: ServerService, |
@@ -52,15 +54,6 @@ export class AppComponent implements OnInit { | |||
52 | ngOnInit () { | 54 | ngOnInit () { |
53 | document.getElementById('incompatible-browser').className += ' browser-ok' | 55 | document.getElementById('incompatible-browser').className += ' browser-ok' |
54 | 56 | ||
55 | this.router.events.subscribe(e => { | ||
56 | if (e instanceof NavigationEnd) { | ||
57 | const pathname = window.location.pathname | ||
58 | if (!pathname || pathname === '/' || is18nPath(pathname)) { | ||
59 | this.redirectService.redirectToHomepage(true) | ||
60 | } | ||
61 | } | ||
62 | }) | ||
63 | |||
64 | this.authService.loadClientCredentials() | 57 | this.authService.loadClientCredentials() |
65 | 58 | ||
66 | if (this.isUserLoggedIn()) { | 59 | if (this.isUserLoggedIn()) { |
@@ -81,15 +74,94 @@ export class AppComponent implements OnInit { | |||
81 | this.isMenuDisplayed = false | 74 | this.isMenuDisplayed = false |
82 | } | 75 | } |
83 | 76 | ||
84 | this.router.events.subscribe( | 77 | this.initRouteEvents() |
85 | e => { | 78 | this.injectJS() |
86 | // User clicked on a link in the menu, change the page | 79 | this.injectCSS() |
87 | if (e instanceof GuardsCheckStart && this.screenService.isInSmallView()) { | 80 | |
88 | this.isMenuDisplayed = false | 81 | this.initHotkeys() |
89 | } | 82 | |
83 | fromEvent(window, 'resize') | ||
84 | .pipe(debounceTime(200)) | ||
85 | .subscribe(() => this.onResize()) | ||
86 | } | ||
87 | |||
88 | isUserLoggedIn () { | ||
89 | return this.authService.isLoggedIn() | ||
90 | } | ||
91 | |||
92 | toggleMenu () { | ||
93 | this.isMenuDisplayed = !this.isMenuDisplayed | ||
94 | this.isMenuChangedByUser = true | ||
95 | } | ||
96 | |||
97 | onResize () { | ||
98 | this.isMenuDisplayed = window.innerWidth >= 800 && !this.isMenuChangedByUser | ||
99 | } | ||
100 | |||
101 | private initRouteEvents () { | ||
102 | let resetScroll = true | ||
103 | const eventsObs = this.router.events | ||
104 | |||
105 | const scrollEvent = eventsObs.pipe(filter((e: Event): e is Scroll => e instanceof Scroll)) | ||
106 | const navigationEndEvent = eventsObs.pipe(filter((e: Event): e is NavigationEnd => e instanceof NavigationEnd)) | ||
107 | |||
108 | scrollEvent.subscribe(e => { | ||
109 | if (e.position) { | ||
110 | return this.viewportScroller.scrollToPosition(e.position) | ||
90 | } | 111 | } |
91 | ) | ||
92 | 112 | ||
113 | if (e.anchor) { | ||
114 | return this.viewportScroller.scrollToAnchor(e.anchor) | ||
115 | } | ||
116 | |||
117 | if (resetScroll) { | ||
118 | return this.viewportScroller.scrollToPosition([ 0, 0 ]) | ||
119 | } | ||
120 | }) | ||
121 | |||
122 | // When we add the a-state parameter, we don't want to alter the scroll | ||
123 | navigationEndEvent.pipe(pairwise()) | ||
124 | .subscribe(([ e1, e2 ]) => { | ||
125 | try { | ||
126 | resetScroll = false | ||
127 | |||
128 | const previousUrl = new URL(window.location.origin + e1.url) | ||
129 | const nextUrl = new URL(window.location.origin + e2.url) | ||
130 | |||
131 | if (previousUrl.pathname !== nextUrl.pathname) { | ||
132 | resetScroll = true | ||
133 | return | ||
134 | } | ||
135 | |||
136 | const nextSearchParams = nextUrl.searchParams | ||
137 | nextSearchParams.delete('a-state') | ||
138 | |||
139 | const previousSearchParams = previousUrl.searchParams | ||
140 | |||
141 | nextSearchParams.sort() | ||
142 | previousSearchParams.sort() | ||
143 | |||
144 | if (nextSearchParams.toString() !== previousSearchParams.toString()) { | ||
145 | resetScroll = true | ||
146 | } | ||
147 | } catch (e) { | ||
148 | console.error('Cannot parse URL to check next scroll.', e) | ||
149 | resetScroll = true | ||
150 | } | ||
151 | }) | ||
152 | |||
153 | navigationEndEvent.pipe( | ||
154 | map(() => window.location.pathname), | ||
155 | filter(pathname => !pathname || pathname === '/' || is18nPath(pathname)) | ||
156 | ).subscribe(() => this.redirectService.redirectToHomepage(true)) | ||
157 | |||
158 | eventsObs.pipe( | ||
159 | filter((e: Event): e is GuardsCheckStart => e instanceof GuardsCheckStart), | ||
160 | filter(() => this.screenService.isInSmallView()) | ||
161 | ).subscribe(() => this.isMenuDisplayed = false) // User clicked on a link in the menu, change the page | ||
162 | } | ||
163 | |||
164 | private injectJS () { | ||
93 | // Inject JS | 165 | // Inject JS |
94 | this.serverService.configLoaded | 166 | this.serverService.configLoaded |
95 | .subscribe(() => { | 167 | .subscribe(() => { |
@@ -104,7 +176,9 @@ export class AppComponent implements OnInit { | |||
104 | } | 176 | } |
105 | } | 177 | } |
106 | }) | 178 | }) |
179 | } | ||
107 | 180 | ||
181 | private injectCSS () { | ||
108 | // Inject CSS if modified (admin config settings) | 182 | // Inject CSS if modified (admin config settings) |
109 | this.serverService.configLoaded | 183 | this.serverService.configLoaded |
110 | .pipe(skip(1)) // We only want to subscribe to reloads, because the CSS is already injected by the server | 184 | .pipe(skip(1)) // We only want to subscribe to reloads, because the CSS is already injected by the server |
@@ -120,7 +194,9 @@ export class AppComponent implements OnInit { | |||
120 | this.customCSS = this.domSanitizer.bypassSecurityTrustHtml(styleTag) | 194 | this.customCSS = this.domSanitizer.bypassSecurityTrustHtml(styleTag) |
121 | } | 195 | } |
122 | }) | 196 | }) |
197 | } | ||
123 | 198 | ||
199 | private initHotkeys () { | ||
124 | this.hotkeysService.add([ | 200 | this.hotkeysService.add([ |
125 | new Hotkey(['/', 's'], (event: KeyboardEvent): boolean => { | 201 | new Hotkey(['/', 's'], (event: KeyboardEvent): boolean => { |
126 | document.getElementById('search-video').focus() | 202 | document.getElementById('search-video').focus() |
@@ -155,22 +231,5 @@ export class AppComponent implements OnInit { | |||
155 | return false | 231 | return false |
156 | }, undefined, this.i18n('Toggle Dark theme')) | 232 | }, undefined, this.i18n('Toggle Dark theme')) |
157 | ]) | 233 | ]) |
158 | |||
159 | fromEvent(window, 'resize') | ||
160 | .pipe(debounceTime(200)) | ||
161 | .subscribe(() => this.onResize()) | ||
162 | } | ||
163 | |||
164 | isUserLoggedIn () { | ||
165 | return this.authService.isLoggedIn() | ||
166 | } | ||
167 | |||
168 | toggleMenu () { | ||
169 | this.isMenuDisplayed = !this.isMenuDisplayed | ||
170 | this.isMenuChangedByUser = true | ||
171 | } | ||
172 | |||
173 | onResize () { | ||
174 | this.isMenuDisplayed = window.innerWidth >= 800 && !this.isMenuChangedByUser | ||
175 | } | 234 | } |
176 | } | 235 | } |