1 import { Injectable } from '@angular/core'
2 import { AuthService } from '@app/core/auth'
3 import { ServerService } from '@app/core/server'
4 import { environment } from '../../../environments/environment'
5 import { PluginService } from '@app/core/plugins/plugin.service'
6 import { ServerConfigTheme } from '@shared/models'
7 import { peertubeLocalStorage } from '@app/shared/misc/peertube-local-storage'
10 export class ThemeService {
12 private static KEYS = {
13 LAST_ACTIVE_THEME: 'last_active_theme'
16 private oldThemeName: string
17 private themes: ServerConfigTheme[] = []
19 private themeFromLocalStorage: ServerConfigTheme
20 private themeDOMLinksFromLocalStorage: HTMLLinkElement[] = []
23 private auth: AuthService,
24 private pluginService: PluginService,
25 private server: ServerService
29 // Try to load from local storage first, so we don't have to wait network requests
30 this.loadAndSetFromLocalStorage()
32 this.server.configLoaded
34 const themes = this.server.getConfig().theme.registered
36 this.removeThemeFromLocalStorageIfNeeded(themes)
37 this.injectThemes(themes)
39 this.listenUserTheme()
43 private injectThemes (themes: ServerConfigTheme[], fromLocalStorage = false) {
46 console.log('Injecting %d themes.', this.themes.length)
48 const head = this.getHeadElement()
50 for (const theme of this.themes) {
51 // Already added this theme?
52 if (fromLocalStorage === false && this.themeFromLocalStorage && this.themeFromLocalStorage.name === theme.name) continue
54 for (const css of theme.css) {
55 const link = document.createElement('link')
57 const href = environment.apiUrl + `/themes/${theme.name}/${theme.version}/css/${css}`
58 link.setAttribute('href', href)
59 link.setAttribute('rel', 'alternate stylesheet')
60 link.setAttribute('type', 'text/css')
61 link.setAttribute('title', theme.name)
62 link.setAttribute('disabled', '')
64 if (fromLocalStorage === true) this.themeDOMLinksFromLocalStorage.push(link)
66 head.appendChild(link)
71 private getCurrentTheme () {
72 if (this.themeFromLocalStorage) return this.themeFromLocalStorage.name
74 if (this.auth.isLoggedIn()) {
75 const theme = this.auth.getUser().theme
76 if (theme !== 'instance-default') return theme
79 return this.server.getConfig().theme.default
82 private loadTheme (name: string) {
83 const links = document.getElementsByTagName('link')
84 for (let i = 0; i < links.length; i++) {
85 const link = links[ i ]
86 if (link.getAttribute('rel').indexOf('style') !== -1 && link.getAttribute('title')) {
87 link.disabled = link.getAttribute('title') !== name
92 private updateCurrentTheme () {
93 if (this.oldThemeName) this.removeThemePlugins(this.oldThemeName)
95 const currentTheme = this.getCurrentTheme()
97 console.log('Enabling %s theme.', currentTheme)
99 this.loadTheme(currentTheme)
101 const theme = this.getTheme(currentTheme)
103 console.log('Adding scripts of theme %s.', currentTheme)
104 this.pluginService.addPlugin(theme, true)
106 this.pluginService.reloadLoadedScopes()
108 peertubeLocalStorage.setItem(ThemeService.KEYS.LAST_ACTIVE_THEME, JSON.stringify(theme))
110 peertubeLocalStorage.removeItem(ThemeService.KEYS.LAST_ACTIVE_THEME)
113 this.oldThemeName = currentTheme
116 private listenUserTheme () {
117 // We don't need them anymore
118 this.themeFromLocalStorage = undefined
119 this.themeDOMLinksFromLocalStorage = []
121 if (!this.auth.isLoggedIn()) {
122 this.updateCurrentTheme()
125 this.auth.userInformationLoaded
126 .subscribe(() => this.updateCurrentTheme())
129 private loadAndSetFromLocalStorage () {
130 const lastActiveThemeString = peertubeLocalStorage.getItem(ThemeService.KEYS.LAST_ACTIVE_THEME)
131 if (!lastActiveThemeString) return
134 const lastActiveTheme = JSON.parse(lastActiveThemeString)
135 this.themeFromLocalStorage = lastActiveTheme
137 this.injectThemes([ lastActiveTheme ], true)
138 this.updateCurrentTheme()
140 console.error('Cannot parse last active theme.', err)
145 private removeThemePlugins (themeName: string) {
146 const oldTheme = this.getTheme(themeName)
148 console.log('Removing scripts of old theme %s.', themeName)
149 this.pluginService.removePlugin(oldTheme)
153 private removeThemeFromLocalStorageIfNeeded (themes: ServerConfigTheme[]) {
154 if (!this.themeFromLocalStorage) return
156 const loadedTheme = themes.find(t => t.name === this.themeFromLocalStorage.name)
157 if (!loadedTheme || loadedTheme.version !== this.themeFromLocalStorage.version) {
158 // Need to remove this theme: we loaded an old version or a theme that does not exist anymore
159 this.removeThemePlugins(this.themeFromLocalStorage.name)
160 this.oldThemeName = undefined
162 const head = this.getHeadElement()
163 for (const htmlLinkElement of this.themeDOMLinksFromLocalStorage) {
164 head.removeChild(htmlLinkElement)
167 this.themeFromLocalStorage = undefined
168 this.themeDOMLinksFromLocalStorage = []
172 private getHeadElement () {
173 return document.getElementsByTagName('head')[0]
176 private getTheme (name: string) {
177 return this.themes.find(t => t.name === name)