aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src/app/core
diff options
context:
space:
mode:
Diffstat (limited to 'client/src/app/core')
-rw-r--r--client/src/app/core/core.module.ts7
-rw-r--r--client/src/app/core/menu/menu.service.ts58
-rw-r--r--client/src/app/core/renderer/html-renderer.service.ts10
-rw-r--r--client/src/app/core/renderer/markdown.service.ts53
-rw-r--r--client/src/app/core/routing/index.ts2
-rw-r--r--client/src/app/core/routing/meta-guard.service.ts23
-rw-r--r--client/src/app/core/routing/meta.service.ts40
-rw-r--r--client/src/app/core/routing/redirect.service.ts35
-rw-r--r--client/src/app/core/server/server.service.ts32
-rw-r--r--client/src/app/core/theme/theme.service.ts14
10 files changed, 215 insertions, 59 deletions
diff --git a/client/src/app/core/core.module.ts b/client/src/app/core/core.module.ts
index 3152a7003..de3274544 100644
--- a/client/src/app/core/core.module.ts
+++ b/client/src/app/core/core.module.ts
@@ -14,7 +14,7 @@ import { throwIfAlreadyLoaded } from './module-import-guard'
14import { Notifier } from './notification' 14import { Notifier } from './notification'
15import { HtmlRendererService, LinkifierService, MarkdownService } from './renderer' 15import { HtmlRendererService, LinkifierService, MarkdownService } from './renderer'
16import { RestExtractor, RestService } from './rest' 16import { RestExtractor, RestService } from './rest'
17import { LoginGuard, RedirectService, UnloggedGuard, UserRightGuard } from './routing' 17import { LoginGuard, MetaGuard, MetaService, RedirectService, UnloggedGuard, UserRightGuard } from './routing'
18import { CanDeactivateGuard } from './routing/can-deactivate-guard.service' 18import { CanDeactivateGuard } from './routing/can-deactivate-guard.service'
19import { ServerConfigResolver } from './routing/server-config-resolver.service' 19import { ServerConfigResolver } from './routing/server-config-resolver.service'
20import { ScopedTokensService } from './scoped-tokens' 20import { ScopedTokensService } from './scoped-tokens'
@@ -77,7 +77,10 @@ import { LocalStorageService, ScreenService, SessionStorageService } from './wra
77 MessageService, 77 MessageService,
78 PeerTubeSocket, 78 PeerTubeSocket,
79 ServerConfigResolver, 79 ServerConfigResolver,
80 CanDeactivateGuard 80 CanDeactivateGuard,
81
82 MetaService,
83 MetaGuard
81 ] 84 ]
82}) 85})
83export class CoreModule { 86export class CoreModule {
diff --git a/client/src/app/core/menu/menu.service.ts b/client/src/app/core/menu/menu.service.ts
index 502d3bb2f..77592cbb6 100644
--- a/client/src/app/core/menu/menu.service.ts
+++ b/client/src/app/core/menu/menu.service.ts
@@ -1,8 +1,19 @@
1import { fromEvent } from 'rxjs' 1import { fromEvent } from 'rxjs'
2import { debounceTime } from 'rxjs/operators' 2import { debounceTime } from 'rxjs/operators'
3import { Injectable } from '@angular/core' 3import { Injectable } from '@angular/core'
4import { GlobalIconName } from '@app/shared/shared-icons'
5import { sortObjectComparator } from '@shared/core-utils/miscs/miscs'
6import { ServerConfig } from '@shared/models/server'
4import { ScreenService } from '../wrappers' 7import { ScreenService } from '../wrappers'
5 8
9export type MenuLink = {
10 icon: GlobalIconName
11 label: string
12 menuLabel: string
13 path: string
14 priority: number
15}
16
6@Injectable() 17@Injectable()
7export class MenuService { 18export class MenuService {
8 isMenuDisplayed = true 19 isMenuDisplayed = true
@@ -48,6 +59,53 @@ export class MenuService {
48 this.isMenuDisplayed = window.innerWidth >= 800 && !this.isMenuChangedByUser 59 this.isMenuDisplayed = window.innerWidth >= 800 && !this.isMenuChangedByUser
49 } 60 }
50 61
62 buildCommonLinks (config: ServerConfig) {
63 let entries: MenuLink[] = [
64 {
65 icon: 'globe' as 'globe',
66 label: $localize`Discover videos`,
67 menuLabel: $localize`Discover`,
68 path: '/videos/overview',
69 priority: 150
70 },
71 {
72 icon: 'trending' as 'trending',
73 label: $localize`Trending videos`,
74 menuLabel: $localize`Trending`,
75 path: '/videos/trending',
76 priority: 140
77 },
78 {
79 icon: 'recently-added' as 'recently-added',
80 label: $localize`Recently added videos`,
81 menuLabel: $localize`Recently added`,
82 path: '/videos/recently-added',
83 priority: 130
84 },
85 {
86 icon: 'octagon' as 'octagon',
87 label: $localize`Local videos`,
88 menuLabel: $localize`Local videos`,
89 path: '/videos/local',
90 priority: 120
91 }
92 ]
93
94 if (config.homepage.enabled) {
95 entries.push({
96 icon: 'home' as 'home',
97 label: $localize`Home`,
98 menuLabel: $localize`Home`,
99 path: '/home',
100 priority: 160
101 })
102 }
103
104 entries = entries.sort(sortObjectComparator('priority', 'desc'))
105
106 return entries
107 }
108
51 private handleWindowResize () { 109 private handleWindowResize () {
52 // On touch screens, do not handle window resize event since opened menu is handled with a content overlay 110 // On touch screens, do not handle window resize event since opened menu is handled with a content overlay
53 if (this.screenService.isInTouchScreen()) return 111 if (this.screenService.isInTouchScreen()) return
diff --git a/client/src/app/core/renderer/html-renderer.service.ts b/client/src/app/core/renderer/html-renderer.service.ts
index 3176cf6a4..418d8603e 100644
--- a/client/src/app/core/renderer/html-renderer.service.ts
+++ b/client/src/app/core/renderer/html-renderer.service.ts
@@ -1,6 +1,6 @@
1import { Injectable } from '@angular/core' 1import { Injectable } from '@angular/core'
2import { LinkifierService } from './linkifier.service' 2import { LinkifierService } from './linkifier.service'
3import { SANITIZE_OPTIONS } from '@shared/core-utils/renderer/html' 3import { getCustomMarkupSanitizeOptions, getSanitizeOptions } from '@shared/core-utils/renderer/html'
4 4
5@Injectable() 5@Injectable()
6export class HtmlRendererService { 6export class HtmlRendererService {
@@ -20,7 +20,7 @@ export class HtmlRendererService {
20 }) 20 })
21 } 21 }
22 22
23 async toSafeHtml (text: string) { 23 async toSafeHtml (text: string, additionalAllowedTags: string[] = []) {
24 const [ html ] = await Promise.all([ 24 const [ html ] = await Promise.all([
25 // Convert possible markdown to html 25 // Convert possible markdown to html
26 this.linkifier.linkify(text), 26 this.linkifier.linkify(text),
@@ -28,7 +28,11 @@ export class HtmlRendererService {
28 this.loadSanitizeHtml() 28 this.loadSanitizeHtml()
29 ]) 29 ])
30 30
31 return this.sanitizeHtml(html, SANITIZE_OPTIONS) 31 const options = additionalAllowedTags.length !== 0
32 ? getCustomMarkupSanitizeOptions(additionalAllowedTags)
33 : getSanitizeOptions()
34
35 return this.sanitizeHtml(html, options)
32 } 36 }
33 37
34 private async loadSanitizeHtml () { 38 private async loadSanitizeHtml () {
diff --git a/client/src/app/core/renderer/markdown.service.ts b/client/src/app/core/renderer/markdown.service.ts
index edddb0a66..ca1bf4eb9 100644
--- a/client/src/app/core/renderer/markdown.service.ts
+++ b/client/src/app/core/renderer/markdown.service.ts
@@ -17,12 +17,15 @@ type MarkdownParsers = {
17 enhancedMarkdownIt: MarkdownIt 17 enhancedMarkdownIt: MarkdownIt
18 enhancedWithHTMLMarkdownIt: MarkdownIt 18 enhancedWithHTMLMarkdownIt: MarkdownIt
19 19
20 completeMarkdownIt: MarkdownIt 20 unsafeMarkdownIt: MarkdownIt
21
22 customPageMarkdownIt: MarkdownIt
21} 23}
22 24
23type MarkdownConfig = { 25type MarkdownConfig = {
24 rules: string[] 26 rules: string[]
25 html: boolean 27 html: boolean
28 breaks: boolean
26 escape?: boolean 29 escape?: boolean
27} 30}
28 31
@@ -35,18 +38,24 @@ export class MarkdownService {
35 private markdownParsers: MarkdownParsers = { 38 private markdownParsers: MarkdownParsers = {
36 textMarkdownIt: null, 39 textMarkdownIt: null,
37 textWithHTMLMarkdownIt: null, 40 textWithHTMLMarkdownIt: null,
41
38 enhancedMarkdownIt: null, 42 enhancedMarkdownIt: null,
39 enhancedWithHTMLMarkdownIt: null, 43 enhancedWithHTMLMarkdownIt: null,
40 completeMarkdownIt: null 44
45 unsafeMarkdownIt: null,
46
47 customPageMarkdownIt: null
41 } 48 }
42 private parsersConfig: MarkdownParserConfigs = { 49 private parsersConfig: MarkdownParserConfigs = {
43 textMarkdownIt: { rules: TEXT_RULES, html: false }, 50 textMarkdownIt: { rules: TEXT_RULES, breaks: true, html: false },
44 textWithHTMLMarkdownIt: { rules: TEXT_WITH_HTML_RULES, html: true, escape: true }, 51 textWithHTMLMarkdownIt: { rules: TEXT_WITH_HTML_RULES, breaks: true, html: true, escape: true },
45 52
46 enhancedMarkdownIt: { rules: ENHANCED_RULES, html: false }, 53 enhancedMarkdownIt: { rules: ENHANCED_RULES, breaks: true, html: false },
47 enhancedWithHTMLMarkdownIt: { rules: ENHANCED_WITH_HTML_RULES, html: true, escape: true }, 54 enhancedWithHTMLMarkdownIt: { rules: ENHANCED_WITH_HTML_RULES, breaks: true, html: true, escape: true },
48 55
49 completeMarkdownIt: { rules: COMPLETE_RULES, html: true } 56 unsafeMarkdownIt: { rules: COMPLETE_RULES, breaks: true, html: true, escape: false },
57
58 customPageMarkdownIt: { rules: COMPLETE_RULES, breaks: false, html: true, escape: true }
50 } 59 }
51 60
52 private emojiModule: any 61 private emojiModule: any
@@ -54,22 +63,26 @@ export class MarkdownService {
54 constructor (private htmlRenderer: HtmlRendererService) {} 63 constructor (private htmlRenderer: HtmlRendererService) {}
55 64
56 textMarkdownToHTML (markdown: string, withHtml = false, withEmoji = false) { 65 textMarkdownToHTML (markdown: string, withHtml = false, withEmoji = false) {
57 if (withHtml) return this.render('textWithHTMLMarkdownIt', markdown, withEmoji) 66 if (withHtml) return this.render({ name: 'textWithHTMLMarkdownIt', markdown, withEmoji })
58 67
59 return this.render('textMarkdownIt', markdown, withEmoji) 68 return this.render({ name: 'textMarkdownIt', markdown, withEmoji })
60 } 69 }
61 70
62 enhancedMarkdownToHTML (markdown: string, withHtml = false, withEmoji = false) { 71 enhancedMarkdownToHTML (markdown: string, withHtml = false, withEmoji = false) {
63 if (withHtml) return this.render('enhancedWithHTMLMarkdownIt', markdown, withEmoji) 72 if (withHtml) return this.render({ name: 'enhancedWithHTMLMarkdownIt', markdown, withEmoji })
73
74 return this.render({ name: 'enhancedMarkdownIt', markdown, withEmoji })
75 }
64 76
65 return this.render('enhancedMarkdownIt', markdown, withEmoji) 77 unsafeMarkdownToHTML (markdown: string, _trustedInput: true) {
78 return this.render({ name: 'unsafeMarkdownIt', markdown, withEmoji: true })
66 } 79 }
67 80
68 completeMarkdownToHTML (markdown: string) { 81 customPageMarkdownToHTML (markdown: string, additionalAllowedTags: string[]) {
69 return this.render('completeMarkdownIt', markdown, true) 82 return this.render({ name: 'customPageMarkdownIt', markdown, withEmoji: true, additionalAllowedTags })
70 } 83 }
71 84
72 async processVideoTimestamps (html: string) { 85 processVideoTimestamps (html: string) {
73 return html.replace(/((\d{1,2}):)?(\d{1,2}):(\d{1,2})/g, function (str, _, h, m, s) { 86 return html.replace(/((\d{1,2}):)?(\d{1,2}):(\d{1,2})/g, function (str, _, h, m, s) {
74 const t = (3600 * +(h || 0)) + (60 * +(m || 0)) + (+(s || 0)) 87 const t = (3600 * +(h || 0)) + (60 * +(m || 0)) + (+(s || 0))
75 const url = buildVideoLink({ startTime: t }) 88 const url = buildVideoLink({ startTime: t })
@@ -77,7 +90,13 @@ export class MarkdownService {
77 }) 90 })
78 } 91 }
79 92
80 private async render (name: keyof MarkdownParsers, markdown: string, withEmoji = false) { 93 private async render (options: {
94 name: keyof MarkdownParsers
95 markdown: string
96 withEmoji: boolean
97 additionalAllowedTags?: string[]
98 }) {
99 const { name, markdown, withEmoji, additionalAllowedTags } = options
81 if (!markdown) return '' 100 if (!markdown) return ''
82 101
83 const config = this.parsersConfig[ name ] 102 const config = this.parsersConfig[ name ]
@@ -96,7 +115,7 @@ export class MarkdownService {
96 let html = this.markdownParsers[ name ].render(markdown) 115 let html = this.markdownParsers[ name ].render(markdown)
97 html = this.avoidTruncatedTags(html) 116 html = this.avoidTruncatedTags(html)
98 117
99 if (config.escape) return this.htmlRenderer.toSafeHtml(html) 118 if (config.escape) return this.htmlRenderer.toSafeHtml(html, additionalAllowedTags)
100 119
101 return html 120 return html
102 } 121 }
@@ -105,7 +124,7 @@ export class MarkdownService {
105 // FIXME: import('...') returns a struct module, containing a "default" field 124 // FIXME: import('...') returns a struct module, containing a "default" field
106 const MarkdownItClass: typeof import ('markdown-it') = (await import('markdown-it') as any).default 125 const MarkdownItClass: typeof import ('markdown-it') = (await import('markdown-it') as any).default
107 126
108 const markdownIt = new MarkdownItClass('zero', { linkify: true, breaks: true, html: config.html }) 127 const markdownIt = new MarkdownItClass('zero', { linkify: true, breaks: config.breaks, html: config.html })
109 128
110 for (const rule of config.rules) { 129 for (const rule of config.rules) {
111 markdownIt.enable(rule) 130 markdownIt.enable(rule)
diff --git a/client/src/app/core/routing/index.ts b/client/src/app/core/routing/index.ts
index 239c27caf..4314ea475 100644
--- a/client/src/app/core/routing/index.ts
+++ b/client/src/app/core/routing/index.ts
@@ -3,6 +3,8 @@ export * from './custom-reuse-strategy'
3export * from './disable-for-reuse-hook' 3export * from './disable-for-reuse-hook'
4export * from './login-guard.service' 4export * from './login-guard.service'
5export * from './menu-guard.service' 5export * from './menu-guard.service'
6export * from './meta-guard.service'
7export * from './meta.service'
6export * from './preload-selected-modules-list' 8export * from './preload-selected-modules-list'
7export * from './redirect.service' 9export * from './redirect.service'
8export * from './server-config-resolver.service' 10export * from './server-config-resolver.service'
diff --git a/client/src/app/core/routing/meta-guard.service.ts b/client/src/app/core/routing/meta-guard.service.ts
new file mode 100644
index 000000000..bedb3450e
--- /dev/null
+++ b/client/src/app/core/routing/meta-guard.service.ts
@@ -0,0 +1,23 @@
1import { Injectable } from '@angular/core'
2import { ActivatedRouteSnapshot, CanActivate, CanActivateChild, RouterStateSnapshot } from '@angular/router'
3import { MetaService } from './meta.service'
4
5@Injectable()
6export class MetaGuard implements CanActivate, CanActivateChild {
7
8 constructor (private meta: MetaService) { }
9
10 canActivate (route: ActivatedRouteSnapshot): boolean {
11 const metaSettings = route.data?.meta
12
13 if (metaSettings) {
14 this.meta.update(metaSettings)
15 }
16
17 return true
18 }
19
20 canActivateChild (route: ActivatedRouteSnapshot): boolean {
21 return this.canActivate(route)
22 }
23}
diff --git a/client/src/app/core/routing/meta.service.ts b/client/src/app/core/routing/meta.service.ts
new file mode 100644
index 000000000..a5ac778dc
--- /dev/null
+++ b/client/src/app/core/routing/meta.service.ts
@@ -0,0 +1,40 @@
1import { Injectable } from '@angular/core'
2import { Meta, Title } from '@angular/platform-browser'
3import { HTMLServerConfig } from '@shared/models/server'
4import { ServerService } from '../server'
5
6export interface MetaSettings {
7 title?: string
8}
9
10@Injectable()
11export class MetaService {
12 private config: HTMLServerConfig
13
14 constructor (
15 private titleService: Title,
16 private meta: Meta,
17 private server: ServerService
18 ) {
19 this.config = this.server.getTmpConfig()
20 this.server.getConfig()
21 .subscribe(config => this.config = config)
22 }
23
24 setTitle (subTitle?: string) {
25 let title = ''
26 if (subTitle) title += `${subTitle} - `
27
28 title += this.config.instance.name
29
30 this.titleService.setTitle(title)
31 }
32
33 setTag (name: string, value: string) {
34 this.meta.addTag({ name, content: value })
35 }
36
37 update (meta: MetaSettings) {
38 this.setTitle(meta.title)
39 }
40}
diff --git a/client/src/app/core/routing/redirect.service.ts b/client/src/app/core/routing/redirect.service.ts
index 6d26fb504..cf690a4d0 100644
--- a/client/src/app/core/routing/redirect.service.ts
+++ b/client/src/app/core/routing/redirect.service.ts
@@ -6,14 +6,14 @@ import { ServerService } from '../server'
6export class RedirectService { 6export class RedirectService {
7 // Default route could change according to the instance configuration 7 // Default route could change according to the instance configuration
8 static INIT_DEFAULT_ROUTE = '/videos/trending' 8 static INIT_DEFAULT_ROUTE = '/videos/trending'
9 static DEFAULT_ROUTE = RedirectService.INIT_DEFAULT_ROUTE
10 static INIT_DEFAULT_TRENDING_ALGORITHM = 'most-viewed' 9 static INIT_DEFAULT_TRENDING_ALGORITHM = 'most-viewed'
11 static DEFAULT_TRENDING_ALGORITHM = RedirectService.INIT_DEFAULT_TRENDING_ALGORITHM
12 10
13 private previousUrl: string 11 private previousUrl: string
14 private currentUrl: string 12 private currentUrl: string
15 13
16 private redirectingToHomepage = false 14 private redirectingToHomepage = false
15 private defaultTrendingAlgorithm = RedirectService.INIT_DEFAULT_TRENDING_ALGORITHM
16 private defaultRoute = RedirectService.INIT_DEFAULT_ROUTE
17 17
18 constructor ( 18 constructor (
19 private router: Router, 19 private router: Router,
@@ -22,10 +22,10 @@ export class RedirectService {
22 // The config is first loaded from the cache so try to get the default route 22 // The config is first loaded from the cache so try to get the default route
23 const tmpConfig = this.serverService.getTmpConfig() 23 const tmpConfig = this.serverService.getTmpConfig()
24 if (tmpConfig?.instance?.defaultClientRoute) { 24 if (tmpConfig?.instance?.defaultClientRoute) {
25 RedirectService.DEFAULT_ROUTE = tmpConfig.instance.defaultClientRoute 25 this.defaultRoute = tmpConfig.instance.defaultClientRoute
26 } 26 }
27 if (tmpConfig?.trending?.videos?.algorithms?.default) { 27 if (tmpConfig?.trending?.videos?.algorithms?.default) {
28 RedirectService.DEFAULT_TRENDING_ALGORITHM = tmpConfig.trending.videos.algorithms.default 28 this.defaultTrendingAlgorithm = tmpConfig.trending.videos.algorithms.default
29 } 29 }
30 30
31 // Load default route 31 // Load default route
@@ -34,13 +34,8 @@ export class RedirectService {
34 const defaultRouteConfig = config.instance.defaultClientRoute 34 const defaultRouteConfig = config.instance.defaultClientRoute
35 const defaultTrendingConfig = config.trending.videos.algorithms.default 35 const defaultTrendingConfig = config.trending.videos.algorithms.default
36 36
37 if (defaultRouteConfig) { 37 if (defaultRouteConfig) this.defaultRoute = defaultRouteConfig
38 RedirectService.DEFAULT_ROUTE = defaultRouteConfig 38 if (defaultTrendingConfig) this.defaultTrendingAlgorithm = defaultTrendingConfig
39 }
40
41 if (defaultTrendingConfig) {
42 RedirectService.DEFAULT_TRENDING_ALGORITHM = defaultTrendingConfig
43 }
44 }) 39 })
45 40
46 // Track previous url 41 // Track previous url
@@ -53,6 +48,14 @@ export class RedirectService {
53 }) 48 })
54 } 49 }
55 50
51 getDefaultRoute () {
52 return this.defaultRoute
53 }
54
55 getDefaultTrendingAlgorithm () {
56 return this.defaultTrendingAlgorithm
57 }
58
56 redirectToPreviousRoute () { 59 redirectToPreviousRoute () {
57 const exceptions = [ 60 const exceptions = [
58 '/verify-account', 61 '/verify-account',
@@ -72,21 +75,21 @@ export class RedirectService {
72 75
73 this.redirectingToHomepage = true 76 this.redirectingToHomepage = true
74 77
75 console.log('Redirecting to %s...', RedirectService.DEFAULT_ROUTE) 78 console.log('Redirecting to %s...', this.defaultRoute)
76 79
77 this.router.navigateByUrl(RedirectService.DEFAULT_ROUTE, { skipLocationChange }) 80 this.router.navigateByUrl(this.defaultRoute, { skipLocationChange })
78 .then(() => this.redirectingToHomepage = false) 81 .then(() => this.redirectingToHomepage = false)
79 .catch(() => { 82 .catch(() => {
80 this.redirectingToHomepage = false 83 this.redirectingToHomepage = false
81 84
82 console.error( 85 console.error(
83 'Cannot navigate to %s, resetting default route to %s.', 86 'Cannot navigate to %s, resetting default route to %s.',
84 RedirectService.DEFAULT_ROUTE, 87 this.defaultRoute,
85 RedirectService.INIT_DEFAULT_ROUTE 88 RedirectService.INIT_DEFAULT_ROUTE
86 ) 89 )
87 90
88 RedirectService.DEFAULT_ROUTE = RedirectService.INIT_DEFAULT_ROUTE 91 this.defaultRoute = RedirectService.INIT_DEFAULT_ROUTE
89 return this.router.navigateByUrl(RedirectService.DEFAULT_ROUTE, { skipLocationChange }) 92 return this.router.navigateByUrl(this.defaultRoute, { skipLocationChange })
90 }) 93 })
91 94
92 } 95 }
diff --git a/client/src/app/core/server/server.service.ts b/client/src/app/core/server/server.service.ts
index 906191ae1..5b1b7603f 100644
--- a/client/src/app/core/server/server.service.ts
+++ b/client/src/app/core/server/server.service.ts
@@ -3,7 +3,6 @@ import { first, map, share, shareReplay, switchMap, tap } from 'rxjs/operators'
3import { HttpClient } from '@angular/common/http' 3import { HttpClient } from '@angular/common/http'
4import { Inject, Injectable, LOCALE_ID } from '@angular/core' 4import { Inject, Injectable, LOCALE_ID } from '@angular/core'
5import { getDevLocale, isOnDevLocale, sortBy } from '@app/helpers' 5import { getDevLocale, isOnDevLocale, sortBy } from '@app/helpers'
6import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage'
7import { getCompleteLocale, isDefaultLocale, peertubeTranslate } from '@shared/core-utils/i18n' 6import { getCompleteLocale, isDefaultLocale, peertubeTranslate } from '@shared/core-utils/i18n'
8import { SearchTargetType, ServerConfig, ServerStats, VideoConstant } from '@shared/models' 7import { SearchTargetType, ServerConfig, ServerStats, VideoConstant } from '@shared/models'
9import { environment } from '../../../environments/environment' 8import { environment } from '../../../environments/environment'
@@ -16,8 +15,6 @@ export class ServerService {
16 private static BASE_LOCALE_URL = environment.apiUrl + '/client/locales/' 15 private static BASE_LOCALE_URL = environment.apiUrl + '/client/locales/'
17 private static BASE_STATS_URL = environment.apiUrl + '/api/v1/server/stats' 16 private static BASE_STATS_URL = environment.apiUrl + '/api/v1/server/stats'
18 17
19 private static CONFIG_LOCAL_STORAGE_KEY = 'server-config'
20
21 configReloaded = new Subject<ServerConfig>() 18 configReloaded = new Subject<ServerConfig>()
22 19
23 private localeObservable: Observable<any> 20 private localeObservable: Observable<any>
@@ -176,6 +173,9 @@ export class ServerService {
176 disableLocalSearch: false, 173 disableLocalSearch: false,
177 isDefaultSearch: false 174 isDefaultSearch: false
178 } 175 }
176 },
177 homepage: {
178 enabled: false
179 } 179 }
180 } 180 }
181 181
@@ -201,9 +201,7 @@ export class ServerService {
201 this.configReset = true 201 this.configReset = true
202 202
203 // Notify config update 203 // Notify config update
204 this.getConfig().subscribe(() => { 204 return this.getConfig()
205 // empty, to fire a reset config event
206 })
207 } 205 }
208 206
209 getConfig () { 207 getConfig () {
@@ -212,7 +210,6 @@ export class ServerService {
212 if (!this.configObservable) { 210 if (!this.configObservable) {
213 this.configObservable = this.http.get<ServerConfig>(ServerService.BASE_CONFIG_URL) 211 this.configObservable = this.http.get<ServerConfig>(ServerService.BASE_CONFIG_URL)
214 .pipe( 212 .pipe(
215 tap(config => this.saveConfigLocally(config)),
216 tap(config => { 213 tap(config => {
217 this.config = config 214 this.config = config
218 this.configLoaded = true 215 this.configLoaded = true
@@ -343,20 +340,15 @@ export class ServerService {
343 ) 340 )
344 } 341 }
345 342
346 private saveConfigLocally (config: ServerConfig) {
347 peertubeLocalStorage.setItem(ServerService.CONFIG_LOCAL_STORAGE_KEY, JSON.stringify(config))
348 }
349
350 private loadConfigLocally () { 343 private loadConfigLocally () {
351 const configString = peertubeLocalStorage.getItem(ServerService.CONFIG_LOCAL_STORAGE_KEY) 344 const configString = window['PeerTubeServerConfig']
352 345 if (!configString) return
353 if (configString) { 346
354 try { 347 try {
355 const parsed = JSON.parse(configString) 348 const parsed = JSON.parse(configString)
356 Object.assign(this.config, parsed) 349 Object.assign(this.config, parsed)
357 } catch (err) { 350 } catch (err) {
358 console.error('Cannot parse config saved in local storage.', err) 351 console.error('Cannot parse config saved in from index.html.', err)
359 }
360 } 352 }
361 } 353 }
362} 354}
diff --git a/client/src/app/core/theme/theme.service.ts b/client/src/app/core/theme/theme.service.ts
index 4c4611d01..e7a5ae17a 100644
--- a/client/src/app/core/theme/theme.service.ts
+++ b/client/src/app/core/theme/theme.service.ts
@@ -82,7 +82,19 @@ export class ThemeService {
82 : this.userService.getAnonymousUser().theme 82 : this.userService.getAnonymousUser().theme
83 83
84 if (theme !== 'instance-default') return theme 84 if (theme !== 'instance-default') return theme
85 return this.serverConfig.theme.default 85
86 const instanceTheme = this.serverConfig.theme.default
87 if (instanceTheme !== 'default') return instanceTheme
88
89 // Default to dark theme if available and wanted by the user
90 if (
91 this.themes.find(t => t.name === 'dark') &&
92 window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches
93 ) {
94 return 'dark'
95 }
96
97 return instanceTheme
86 } 98 }
87 99
88 private loadTheme (name: string) { 100 private loadTheme (name: string) {