]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - client/src/app/app.component.ts
Cleanup SASS imports
[github/Chocobozzz/PeerTube.git] / client / src / app / app.component.ts
CommitLineData
fd45e8f4 1import { Component, OnInit } from '@angular/core'
00b5556c 2import { DomSanitizer, SafeHtml } from '@angular/platform-browser'
489290b8 3import { Event, GuardsCheckStart, NavigationEnd, Router, Scroll } from '@angular/router'
1a00c561 4import { AuthService, RedirectService, ServerService, ThemeService } from '@app/core'
989e526a 5import { is18nPath } from '../../../shared/models/i18n'
bbe0f064 6import { ScreenService } from '@app/shared/misc/screen.service'
489290b8
C
7import { debounceTime, filter, map, pairwise, skip } from 'rxjs/operators'
8import { Hotkey, HotkeysService } from 'angular2-hotkeys'
e33f888b 9import { I18n } from '@ngx-translate/i18n-polyfill'
a5858241 10import { fromEvent } from 'rxjs'
489290b8 11import { ViewportScroller } from '@angular/common'
18a6f04c 12import { PluginService } from '@app/core/plugins/plugin.service'
93cae479 13import { HooksService } from '@app/core/plugins/hooks.service'
e2a2d6c8 14
dc8bc31b 15@Component({
3154f382
C
16 selector: 'my-app',
17 templateUrl: './app.component.html',
18 styleUrls: [ './app.component.scss' ]
dc8bc31b 19})
e2a2d6c8 20export class AppComponent implements OnInit {
df98563e 21 isMenuDisplayed = true
a5858241 22 isMenuChangedByUser = false
67167390 23
00b5556c
C
24 customCSS: SafeHtml
25
df98563e 26 constructor (
e33f888b 27 private i18n: I18n,
489290b8 28 private viewportScroller: ViewportScroller,
3154f382 29 private router: Router,
e2a2d6c8 30 private authService: AuthService,
00b5556c 31 private serverService: ServerService,
18a6f04c 32 private pluginService: PluginService,
901637bb 33 private domSanitizer: DomSanitizer,
bbe0f064 34 private redirectService: RedirectService,
ee1fc23a 35 private screenService: ScreenService,
1a00c561 36 private hotkeysService: HotkeysService,
93cae479
C
37 private themeService: ThemeService,
38 private hooks: HooksService
989e526a 39 ) { }
a99593ed 40
915c5bbe
C
41 get serverVersion () {
42 return this.serverService.getConfig().serverVersion
43 }
44
abb2c792
RK
45 get serverCommit () {
46 const commit = this.serverService.getConfig().serverCommit || ''
47 return (commit !== '') ? '...' + commit : commit
48 }
49
36f9424f
C
50 get instanceName () {
51 return this.serverService.getConfig().instance.name
52 }
53
29f9b562
C
54 get defaultRoute () {
55 return RedirectService.DEFAULT_ROUTE
56 }
57
df98563e 58 ngOnInit () {
b3eeb529 59 document.getElementById('incompatible-browser').className += ' browser-ok'
73e09f27 60
d592e0a9
C
61 this.authService.loadClientCredentials()
62
d414207f 63 if (this.isUserLoggedIn()) {
e2a2d6c8 64 // The service will automatically redirect to the login page if the token is not valid anymore
bcd9f81e 65 this.authService.refreshUserInformation()
e2a2d6c8 66 }
6e07c3de 67
db7af09b
C
68 // Load custom data from server
69 this.serverService.loadConfig()
70 this.serverService.loadVideoCategories()
71 this.serverService.loadVideoLanguages()
72 this.serverService.loadVideoLicences()
fd45e8f4 73 this.serverService.loadVideoPrivacies()
830b4faf 74 this.serverService.loadVideoPlaylistPrivacies()
3eeeb87f 75
18a6f04c 76 this.loadPlugins()
ffb321be 77 this.themeService.initialize()
18a6f04c 78
3eeeb87f 79 // Do not display menu on small screens
bbe0f064 80 if (this.screenService.isInSmallView()) {
df98563e 81 this.isMenuDisplayed = false
3eeeb87f 82 }
c8cf5952 83
489290b8
C
84 this.initRouteEvents()
85 this.injectJS()
86 this.injectCSS()
87
88 this.initHotkeys()
89
90 fromEvent(window, 'resize')
91 .pipe(debounceTime(200))
92 .subscribe(() => this.onResize())
93 }
94
95 isUserLoggedIn () {
96 return this.authService.isLoggedIn()
97 }
98
99 toggleMenu () {
100 this.isMenuDisplayed = !this.isMenuDisplayed
101 this.isMenuChangedByUser = true
102 }
103
104 onResize () {
105 this.isMenuDisplayed = window.innerWidth >= 800 && !this.isMenuChangedByUser
106 }
107
108 private initRouteEvents () {
109 let resetScroll = true
110 const eventsObs = this.router.events
111
112 const scrollEvent = eventsObs.pipe(filter((e: Event): e is Scroll => e instanceof Scroll))
113 const navigationEndEvent = eventsObs.pipe(filter((e: Event): e is NavigationEnd => e instanceof NavigationEnd))
114
115 scrollEvent.subscribe(e => {
116 if (e.position) {
117 return this.viewportScroller.scrollToPosition(e.position)
c8cf5952 118 }
00b5556c 119
489290b8
C
120 if (e.anchor) {
121 return this.viewportScroller.scrollToAnchor(e.anchor)
122 }
123
124 if (resetScroll) {
125 return this.viewportScroller.scrollToPosition([ 0, 0 ])
126 }
127 })
128
129 // When we add the a-state parameter, we don't want to alter the scroll
130 navigationEndEvent.pipe(pairwise())
131 .subscribe(([ e1, e2 ]) => {
132 try {
133 resetScroll = false
134
722bca90
C
135 const previousUrl = new URL(window.location.origin + e1.urlAfterRedirects)
136 const nextUrl = new URL(window.location.origin + e2.urlAfterRedirects)
489290b8
C
137
138 if (previousUrl.pathname !== nextUrl.pathname) {
139 resetScroll = true
140 return
141 }
142
143 const nextSearchParams = nextUrl.searchParams
144 nextSearchParams.delete('a-state')
145
146 const previousSearchParams = previousUrl.searchParams
147
148 nextSearchParams.sort()
149 previousSearchParams.sort()
150
151 if (nextSearchParams.toString() !== previousSearchParams.toString()) {
152 resetScroll = true
153 }
154 } catch (e) {
155 console.error('Cannot parse URL to check next scroll.', e)
156 resetScroll = true
157 }
158 })
159
160 navigationEndEvent.pipe(
161 map(() => window.location.pathname),
162 filter(pathname => !pathname || pathname === '/' || is18nPath(pathname))
163 ).subscribe(() => this.redirectService.redirectToHomepage(true))
164
23bdacf8
C
165 navigationEndEvent.subscribe(e => {
166 this.hooks.runAction('action:router.navigation-end', 'common', { path: e.url })
167 })
168
489290b8
C
169 eventsObs.pipe(
170 filter((e: Event): e is GuardsCheckStart => e instanceof GuardsCheckStart),
171 filter(() => this.screenService.isInSmallView())
172 ).subscribe(() => this.isMenuDisplayed = false) // User clicked on a link in the menu, change the page
173 }
174
175 private injectJS () {
e032aec9 176 // Inject JS
00b5556c 177 this.serverService.configLoaded
e032aec9
C
178 .subscribe(() => {
179 const config = this.serverService.getConfig()
180
181 if (config.instance.customizations.javascript) {
182 try {
183 // tslint:disable:no-eval
184 eval(config.instance.customizations.javascript)
185 } catch (err) {
186 console.error('Cannot eval custom JavaScript.', err)
187 }
188 }
189 })
489290b8 190 }
00b5556c 191
489290b8 192 private injectCSS () {
e032aec9
C
193 // Inject CSS if modified (admin config settings)
194 this.serverService.configLoaded
195 .pipe(skip(1)) // We only want to subscribe to reloads, because the CSS is already injected by the server
196 .subscribe(() => {
197 const headStyle = document.querySelector('style.custom-css-style')
198 if (headStyle) headStyle.parentNode.removeChild(headStyle)
00b5556c 199
e032aec9
C
200 const config = this.serverService.getConfig()
201
202 // We test customCSS if the admin removed the css
203 if (this.customCSS || config.instance.customizations.css) {
204 const styleTag = '<style>' + config.instance.customizations.css + '</style>'
205 this.customCSS = this.domSanitizer.bypassSecurityTrustHtml(styleTag)
00b5556c 206 }
e032aec9 207 })
489290b8 208 }
ee1fc23a 209
18a6f04c
C
210 private async loadPlugins () {
211 this.pluginService.initializePlugins()
212
c9e3eeed 213 this.hooks.runAction('action:application.init', 'common')
18a6f04c
C
214 }
215
489290b8 216 private initHotkeys () {
ee1fc23a 217 this.hotkeysService.add([
8542dc33 218 new Hotkey(['/', 's'], (event: KeyboardEvent): boolean => {
ee1fc23a 219 document.getElementById('search-video').focus()
8542dc33 220 return false
e33f888b 221 }, undefined, this.i18n('Focus the search bar')),
8542dc33
RK
222 new Hotkey('b', (event: KeyboardEvent): boolean => {
223 this.toggleMenu()
224 return false
e33f888b 225 }, undefined, this.i18n('Toggle the left menu')),
20d21199 226 new Hotkey('g o', (event: KeyboardEvent): boolean => {
a54991da
RK
227 this.router.navigate([ '/videos/overview' ])
228 return false
79a89941 229 }, undefined, this.i18n('Go to the discover videos page')),
20d21199 230 new Hotkey('g t', (event: KeyboardEvent): boolean => {
ee1fc23a
RK
231 this.router.navigate([ '/videos/trending' ])
232 return false
e33f888b 233 }, undefined, this.i18n('Go to the trending videos page')),
20d21199 234 new Hotkey('g r', (event: KeyboardEvent): boolean => {
ee1fc23a
RK
235 this.router.navigate([ '/videos/recently-added' ])
236 return false
e33f888b 237 }, undefined, this.i18n('Go to the recently added videos page')),
20d21199 238 new Hotkey('g l', (event: KeyboardEvent): boolean => {
ee1fc23a
RK
239 this.router.navigate([ '/videos/local' ])
240 return false
e33f888b 241 }, undefined, this.i18n('Go to the local videos page')),
20d21199 242 new Hotkey('g u', (event: KeyboardEvent): boolean => {
ee1fc23a
RK
243 this.router.navigate([ '/videos/upload' ])
244 return false
ffb321be 245 }, undefined, this.i18n('Go to the videos upload page'))
ee1fc23a 246 ])
67167390 247 }
dc8bc31b 248}