diff options
-rw-r--r-- | client/src/app/app.component.ts | 6 | ||||
-rw-r--r-- | client/src/app/core/theme/theme.service.ts | 91 |
2 files changed, 83 insertions, 14 deletions
diff --git a/client/src/app/app.component.ts b/client/src/app/app.component.ts index 6b18e5feb..1724bbd1a 100644 --- a/client/src/app/app.component.ts +++ b/client/src/app/app.component.ts | |||
@@ -71,6 +71,9 @@ export class AppComponent implements OnInit { | |||
71 | ngOnInit () { | 71 | ngOnInit () { |
72 | document.getElementById('incompatible-browser').className += ' browser-ok' | 72 | document.getElementById('incompatible-browser').className += ' browser-ok' |
73 | 73 | ||
74 | this.loadPlugins() | ||
75 | this.themeService.initialize() | ||
76 | |||
74 | this.authService.loadClientCredentials() | 77 | this.authService.loadClientCredentials() |
75 | 78 | ||
76 | if (this.isUserLoggedIn()) { | 79 | if (this.isUserLoggedIn()) { |
@@ -86,9 +89,6 @@ export class AppComponent implements OnInit { | |||
86 | this.serverService.loadVideoPrivacies() | 89 | this.serverService.loadVideoPrivacies() |
87 | this.serverService.loadVideoPlaylistPrivacies() | 90 | this.serverService.loadVideoPlaylistPrivacies() |
88 | 91 | ||
89 | this.loadPlugins() | ||
90 | this.themeService.initialize() | ||
91 | |||
92 | // Do not display menu on small screens | 92 | // Do not display menu on small screens |
93 | if (this.screenService.isInSmallView()) { | 93 | if (this.screenService.isInSmallView()) { |
94 | this.isMenuDisplayed = false | 94 | this.isMenuDisplayed = false |
diff --git a/client/src/app/core/theme/theme.service.ts b/client/src/app/core/theme/theme.service.ts index 012488075..b312e8f7a 100644 --- a/client/src/app/core/theme/theme.service.ts +++ b/client/src/app/core/theme/theme.service.ts | |||
@@ -4,13 +4,21 @@ import { ServerService } from '@app/core/server' | |||
4 | import { environment } from '../../../environments/environment' | 4 | import { environment } from '../../../environments/environment' |
5 | import { PluginService } from '@app/core/plugins/plugin.service' | 5 | import { PluginService } from '@app/core/plugins/plugin.service' |
6 | import { ServerConfigTheme } from '@shared/models' | 6 | import { ServerConfigTheme } from '@shared/models' |
7 | import { peertubeLocalStorage } from '@app/shared/misc/peertube-local-storage' | ||
7 | 8 | ||
8 | @Injectable() | 9 | @Injectable() |
9 | export class ThemeService { | 10 | export class ThemeService { |
10 | 11 | ||
12 | private static KEYS = { | ||
13 | LAST_ACTIVE_THEME: 'last_active_theme' | ||
14 | } | ||
15 | |||
11 | private oldThemeName: string | 16 | private oldThemeName: string |
12 | private themes: ServerConfigTheme[] = [] | 17 | private themes: ServerConfigTheme[] = [] |
13 | 18 | ||
19 | private themeFromLocalStorage: ServerConfigTheme | ||
20 | private themeDOMLinksFromLocalStorage: HTMLLinkElement[] = [] | ||
21 | |||
14 | constructor ( | 22 | constructor ( |
15 | private auth: AuthService, | 23 | private auth: AuthService, |
16 | private pluginService: PluginService, | 24 | private pluginService: PluginService, |
@@ -18,22 +26,30 @@ export class ThemeService { | |||
18 | ) {} | 26 | ) {} |
19 | 27 | ||
20 | initialize () { | 28 | initialize () { |
29 | // Try to load from local storage first, so we don't have to wait network requests | ||
30 | this.loadAndSetFromLocalStorage() | ||
31 | |||
21 | this.server.configLoaded | 32 | this.server.configLoaded |
22 | .subscribe(() => { | 33 | .subscribe(() => { |
23 | this.injectThemes() | 34 | const themes = this.server.getConfig().theme.registered |
35 | |||
36 | this.removeThemeFromLocalStorageIfNeeded(themes) | ||
37 | this.injectThemes(themes) | ||
24 | 38 | ||
25 | this.listenUserTheme() | 39 | this.listenUserTheme() |
26 | }) | 40 | }) |
27 | } | 41 | } |
28 | 42 | ||
29 | private injectThemes () { | 43 | private injectThemes (themes: ServerConfigTheme[], fromLocalStorage = false) { |
30 | this.themes = this.server.getConfig().theme.registered | 44 | this.themes = themes |
31 | 45 | ||
32 | console.log('Injecting %d themes.', this.themes.length) | 46 | console.log('Injecting %d themes.', this.themes.length) |
33 | 47 | ||
34 | const head = document.getElementsByTagName('head')[0] | 48 | const head = this.getHeadElement() |
35 | 49 | ||
36 | for (const theme of this.themes) { | 50 | for (const theme of this.themes) { |
51 | // Already added this theme? | ||
52 | if (fromLocalStorage === false && this.themeFromLocalStorage && this.themeFromLocalStorage.name === theme.name) continue | ||
37 | 53 | ||
38 | for (const css of theme.css) { | 54 | for (const css of theme.css) { |
39 | const link = document.createElement('link') | 55 | const link = document.createElement('link') |
@@ -45,12 +61,16 @@ export class ThemeService { | |||
45 | link.setAttribute('title', theme.name) | 61 | link.setAttribute('title', theme.name) |
46 | link.setAttribute('disabled', '') | 62 | link.setAttribute('disabled', '') |
47 | 63 | ||
64 | if (fromLocalStorage === true) this.themeDOMLinksFromLocalStorage.push(link) | ||
65 | |||
48 | head.appendChild(link) | 66 | head.appendChild(link) |
49 | } | 67 | } |
50 | } | 68 | } |
51 | } | 69 | } |
52 | 70 | ||
53 | private getCurrentTheme () { | 71 | private getCurrentTheme () { |
72 | if (this.themeFromLocalStorage) return this.themeFromLocalStorage.name | ||
73 | |||
54 | if (this.auth.isLoggedIn()) { | 74 | if (this.auth.isLoggedIn()) { |
55 | const theme = this.auth.getUser().theme | 75 | const theme = this.auth.getUser().theme |
56 | if (theme !== 'instance-default') return theme | 76 | if (theme !== 'instance-default') return theme |
@@ -70,13 +90,7 @@ export class ThemeService { | |||
70 | } | 90 | } |
71 | 91 | ||
72 | private updateCurrentTheme () { | 92 | private updateCurrentTheme () { |
73 | if (this.oldThemeName) { | 93 | if (this.oldThemeName) this.removeThemePlugins(this.oldThemeName) |
74 | const oldTheme = this.getTheme(this.oldThemeName) | ||
75 | if (oldTheme) { | ||
76 | console.log('Removing scripts of old theme %s.', this.oldThemeName) | ||
77 | this.pluginService.removePlugin(oldTheme) | ||
78 | } | ||
79 | } | ||
80 | 94 | ||
81 | const currentTheme = this.getCurrentTheme() | 95 | const currentTheme = this.getCurrentTheme() |
82 | 96 | ||
@@ -90,12 +104,20 @@ export class ThemeService { | |||
90 | this.pluginService.addPlugin(theme, true) | 104 | this.pluginService.addPlugin(theme, true) |
91 | 105 | ||
92 | this.pluginService.reloadLoadedScopes() | 106 | this.pluginService.reloadLoadedScopes() |
107 | |||
108 | peertubeLocalStorage.setItem(ThemeService.KEYS.LAST_ACTIVE_THEME, JSON.stringify(theme)) | ||
109 | } else { | ||
110 | peertubeLocalStorage.removeItem(ThemeService.KEYS.LAST_ACTIVE_THEME) | ||
93 | } | 111 | } |
94 | 112 | ||
95 | this.oldThemeName = currentTheme | 113 | this.oldThemeName = currentTheme |
96 | } | 114 | } |
97 | 115 | ||
98 | private listenUserTheme () { | 116 | private listenUserTheme () { |
117 | // We don't need them anymore | ||
118 | this.themeFromLocalStorage = undefined | ||
119 | this.themeDOMLinksFromLocalStorage = [] | ||
120 | |||
99 | if (!this.auth.isLoggedIn()) { | 121 | if (!this.auth.isLoggedIn()) { |
100 | this.updateCurrentTheme() | 122 | this.updateCurrentTheme() |
101 | } | 123 | } |
@@ -104,6 +126,53 @@ export class ThemeService { | |||
104 | .subscribe(() => this.updateCurrentTheme()) | 126 | .subscribe(() => this.updateCurrentTheme()) |
105 | } | 127 | } |
106 | 128 | ||
129 | private loadAndSetFromLocalStorage () { | ||
130 | const lastActiveThemeString = peertubeLocalStorage.getItem(ThemeService.KEYS.LAST_ACTIVE_THEME) | ||
131 | if (!lastActiveThemeString) return | ||
132 | |||
133 | try { | ||
134 | const lastActiveTheme = JSON.parse(lastActiveThemeString) | ||
135 | this.themeFromLocalStorage = lastActiveTheme | ||
136 | |||
137 | this.injectThemes([ lastActiveTheme ], true) | ||
138 | this.updateCurrentTheme() | ||
139 | } catch (err) { | ||
140 | console.error('Cannot parse last active theme.', err) | ||
141 | return | ||
142 | } | ||
143 | } | ||
144 | |||
145 | private removeThemePlugins (themeName: string) { | ||
146 | const oldTheme = this.getTheme(themeName) | ||
147 | if (oldTheme) { | ||
148 | console.log('Removing scripts of old theme %s.', themeName) | ||
149 | this.pluginService.removePlugin(oldTheme) | ||
150 | } | ||
151 | } | ||
152 | |||
153 | private removeThemeFromLocalStorageIfNeeded (themes: ServerConfigTheme[]) { | ||
154 | if (!this.themeFromLocalStorage) return | ||
155 | |||
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 | ||
161 | |||
162 | const head = this.getHeadElement() | ||
163 | for (const htmlLinkElement of this.themeDOMLinksFromLocalStorage) { | ||
164 | head.removeChild(htmlLinkElement) | ||
165 | } | ||
166 | |||
167 | this.themeFromLocalStorage = undefined | ||
168 | this.themeDOMLinksFromLocalStorage = [] | ||
169 | } | ||
170 | } | ||
171 | |||
172 | private getHeadElement () { | ||
173 | return document.getElementsByTagName('head')[0] | ||
174 | } | ||
175 | |||
107 | private getTheme (name: string) { | 176 | private getTheme (name: string) { |
108 | return this.themes.find(t => t.name === name) | 177 | return this.themes.find(t => t.name === name) |
109 | } | 178 | } |