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