menuEntries: TopMenuDropdownParam[] = []
user: AuthUser
-
private serverConfig: HTMLServerConfig
constructor (
ngOnInit () {
this.serverConfig = this.serverService.getHTMLConfig()
-
this.serverService.getVideoCategories().subscribe(categories => this.videoCategories = categories)
this.serverService.getVideoLicences().subscribe(licences => this.videoLicences = licences)
this.serverService.getVideoLanguages().subscribe(languages => this.videoLanguages = languages)
)
}
- private getDefaultSearchTarget(): SearchTargetType {
+ private getDefaultSearchTarget (): SearchTargetType {
const searchIndexConfig = this.serverConfig.search.searchIndex
if (searchIndexConfig.enabled && (searchIndexConfig.isDefaultSearch || searchIndexConfig.disableLocalSearch)) {
}
})
-
this.serverConfig = this.serverService.getHTMLConfig()
this.initialVideoCaptions = this.videoCaptions.map(c => c.language.id)
this.hasAlreadyAcceptedPrivacyConcern = true
}
-
this.paramsSub = this.route.params.subscribe(routeParams => {
const videoId = routeParams[ 'videoId' ]
if (videoId) this.loadVideo(videoId)
import { CustomReuseStrategy } from '@app/core/routing/custom-reuse-strategy'
import { MenuGuards } from '@app/core/routing/menu-guard.service'
import { POSSIBLE_LOCALES } from '@shared/core-utils/i18n'
-import { MetaGuard, PreloadSelectedModulesList } from './core'
+import { HomepageRedirectComponent, MetaGuard, PreloadSelectedModulesList } from './core'
import { EmptyComponent } from './empty.component'
import { USER_USERNAME_REGEX_CHARACTERS } from './shared/form-validators/user-validators'
import { ActorRedirectGuard } from './shared/shared-main'
{
path: '',
- component: EmptyComponent // Avoid 404, app component will redirect dynamically
+ component: HomepageRedirectComponent
}
]
for (const locale of POSSIBLE_LOCALES) {
routes.push({
path: locale,
- component: EmptyComponent
+ component: HomepageRedirectComponent
})
}
import { Hotkey, HotkeysService } from 'angular2-hotkeys'
-import { concat } from 'rxjs'
-import { filter, first, map, pairwise, switchMap } from 'rxjs/operators'
+import { filter, map, pairwise, switchMap } from 'rxjs/operators'
import { DOCUMENT, PlatformLocation, ViewportScroller } from '@angular/common'
import { AfterViewInit, Component, Inject, LOCALE_ID, OnInit, ViewChild } from '@angular/core'
import { DomSanitizer, SafeHtml } from '@angular/platform-browser'
import { NgbConfig, NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { LoadingBarService } from '@ngx-loading-bar/core'
import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage'
-import { getShortLocale, is18nPath } from '@shared/core-utils/i18n'
+import { getShortLocale } from '@shared/core-utils/i18n'
import { BroadcastMessageLevel, HTMLServerConfig, ServerConfig, UserRole } from '@shared/models'
import { MenuService } from './core/menu/menu.service'
import { POP_STATE_MODAL_DISMISS } from './helpers'
this.serverConfig = this.serverService.getHTMLConfig()
- this.loadPlugins()
+ this.hooks.runAction('action:application.init', 'common')
this.themeService.initialize()
this.authService.loadClientCredentials()
}
})
- // Homepage redirection
- navigationEndEvent.pipe(
- map(() => window.location.pathname),
- filter(pathname => !pathname || pathname === '/' || is18nPath(pathname))
- ).subscribe(() => this.redirectService.redirectToHomepage(true))
-
// Plugin hooks
navigationEndEvent.subscribe(e => {
this.hooks.runAction('action:router.navigation-end', 'common', { path: e.url })
}
}
- private async loadPlugins () {
- this.pluginService.initializePlugins()
-
- this.hooks.runAction('action:application.init', 'common')
- }
-
private async openModalsIfNeeded () {
this.authService.userInformationLoaded
.pipe(
import 'focus-visible'
+import { tap } from 'rxjs/operators'
import { environment } from 'src/environments/environment'
import { APP_BASE_HREF, registerLocaleData } from '@angular/common'
import { APP_INITIALIZER, NgModule } from '@angular/core'
import localeOc from '@app/helpers/locales/oc'
import { AppRoutingModule } from './app-routing.module'
import { AppComponent } from './app.component'
-import { CoreModule, ServerService } from './core'
+import { CoreModule, PluginService, ServerService } from './core'
import { EmptyComponent } from './empty.component'
import { HeaderComponent, SearchTypeaheadComponent, SuggestionComponent } from './header'
import { HighlightPipe } from './header/highlight.pipe'
registerLocaleData(localeOc, 'oc')
-export function loadConfigFactory (server: ServerService) {
- return () => server.loadHTMLConfig()
+export function loadConfigFactory (server: ServerService, pluginService: PluginService) {
+ return () => {
+ const result = server.loadHTMLConfig()
+
+ if (result) return result.pipe(tap(() => pluginService.initializePlugins()))
+
+ return pluginService.initializePlugins()
+ }
}
@NgModule({
{
provide: APP_INITIALIZER,
useFactory: loadConfigFactory,
- deps: [ ServerService ],
+ deps: [ ServerService, PluginService ],
multi: true
- }
+ }
]
})
export class AppModule {}
import { Notifier } from './notification'
import { HtmlRendererService, LinkifierService, MarkdownService } from './renderer'
import { RestExtractor, RestService } from './rest'
-import { LoginGuard, MetaGuard, MetaService, RedirectService, UnloggedGuard, UserRightGuard } from './routing'
+import { HomepageRedirectComponent, LoginGuard, MetaGuard, MetaService, RedirectService, UnloggedGuard, UserRightGuard } from './routing'
import { CanDeactivateGuard } from './routing/can-deactivate-guard.service'
import { ServerConfigResolver } from './routing/server-config-resolver.service'
import { ScopedTokensService } from './scoped-tokens'
],
declarations: [
- CheatSheetComponent
+ CheatSheetComponent,
+ HomepageRedirectComponent
],
exports: [
ToastModule,
- CheatSheetComponent
+ CheatSheetComponent,
+ HomepageRedirectComponent
],
providers: [
+import * as debug from 'debug'
import { Observable, of, ReplaySubject } from 'rxjs'
import { catchError, first, map, shareReplay } from 'rxjs/operators'
import { HttpClient } from '@angular/common/http'
} from '@shared/models'
import { environment } from '../../../environments/environment'
import { RegisterClientHelpers } from '../../../types/register-client-option.model'
-import * as debug from 'debug'
const logger = debug('peertube:plugins')
private static BASE_PLUGIN_API_URL = environment.apiUrl + '/api/v1/plugins'
private static BASE_PLUGIN_URL = environment.apiUrl + '/plugins'
- pluginsBuilt = new ReplaySubject<boolean>(1)
-
pluginsLoaded: { [ scope in PluginClientScope ]: ReplaySubject<boolean> } = {
common: new ReplaySubject<boolean>(1),
'admin-plugin': new ReplaySubject<boolean>(1),
}
initializePlugins () {
- logger('Building plugin configuration')
+ const config = this.server.getHTMLConfig()
+ this.plugins = config.plugin.registered
- this.server.getConfig()
- .subscribe(config => {
- this.plugins = config.plugin.registered
+ this.buildScopeStruct()
- this.buildScopeStruct()
-
- this.pluginsBuilt.next(true)
-
- logger('Plugin configuration built')
- })
+ this.ensurePluginsAreLoaded('common')
}
initializeCustomModal (customModal: CustomModalComponent) {
this.customModal = customModal
}
- ensurePluginsAreBuilt () {
- return this.pluginsBuilt.asObservable()
- .pipe(first(), shareReplay())
- .toPromise()
- }
-
ensurePluginsAreLoaded (scope: PluginClientScope) {
this.loadPluginsByScope(scope)
logger('Loading scope %s', scope)
try {
- await this.ensurePluginsAreBuilt()
-
if (!isReload) this.loadedScopes.push(scope)
const toLoad = this.scopes[ scope ]
--- /dev/null
+import { Component, OnInit } from '@angular/core'
+import { ActivatedRoute } from '@angular/router'
+import { is18nPath } from '@shared/core-utils/i18n/i18n'
+import { RedirectService } from './redirect.service'
+
+/*
+ * We have to use a component instead of an homepage because of a weird issue when using router.navigate in guard
+ *
+ * Since we also want to use the `skipLocationChange` option, we cannot use a guard that returns a UrlTree
+ * See https://github.com/angular/angular/issues/27148
+*/
+
+@Component({
+ template: ''
+})
+export class HomepageRedirectComponent implements OnInit {
+
+ constructor (
+ private route: ActivatedRoute,
+ private redirectService: RedirectService
+ ) { }
+
+ ngOnInit () {
+ const url = this.route.snapshot.url
+
+ if (url.length === 0 || is18nPath('/' + url[0])) {
+ this.redirectService.redirectToHomepage(true)
+ }
+ }
+}
export * from './can-deactivate-guard.service'
export * from './custom-reuse-strategy'
export * from './disable-for-reuse-hook'
+export * from './homepage-redirect.component'
export * from './login-guard.service'
export * from './menu-guard.service'
export * from './meta-guard.service'
return this.defaultRoute
}
-
getDefaultTrendingAlgorithm () {
return this.defaultTrendingAlgorithm
}
this.bannerFormat = $localize`ratio 6/1, recommended size: 1920x317, max size: ${getBytes(this.maxBannerSize)}, extensions: ${this.bannerExtensions}`
}
-
onBannerChange (input: HTMLInputElement) {
this.bannerfileInput = new ElementRef(input)
RegisterClientVideoFieldOptions,
ServerConfigPlugin
} from '../../../shared/models'
+import { environment } from '../environments/environment'
import { ClientScript as ClientScriptModule } from '../types/client-script.model'
-import { importModule } from './utils'
interface HookStructValue extends RegisterClientHookOptions {
plugin: ServerConfigPlugin
console.log('Loading script %s of plugin %s.', clientScript.script, plugin.name)
- return importModule(clientScript.script)
+ const absURL = (environment.apiUrl || window.location.origin) + clientScript.script
+ return import(/* webpackIgnore: true */ absURL)
.then((script: ClientScriptModule) => script.register({ registerHook, registerVideoField, registerSettingsScript, peertubeHelpers }))
.then(() => sortHooksByPriority(hooks))
.catch(err => console.error('Cannot import or register plugin %s.', pluginInfo.plugin.name, err))
-import { environment } from '../environments/environment'
-
function objectToUrlEncoded (obj: any) {
const str: string[] = []
for (const key of Object.keys(obj)) {
document.body.removeChild(el)
}
-// Thanks: https://github.com/uupaa/dynamic-import-polyfill
-function importModule (path: string) {
- return new Promise((resolve, reject) => {
- const vector = '$importModule$' + Math.random().toString(32).slice(2)
- const script = document.createElement('script')
-
- const destructor = () => {
- delete window[ vector ]
- script.onerror = null
- script.onload = null
- script.remove()
- URL.revokeObjectURL(script.src)
- script.src = ''
- }
-
- script.defer = true
- script.type = 'module'
-
- script.onerror = () => {
- reject(new Error(`Failed to import: ${path}`))
- destructor()
- }
- script.onload = () => {
- resolve(window[ vector ])
- destructor()
- }
- const absURL = (environment.apiUrl || window.location.origin) + path
- const loader = `import * as m from "${absURL}"; window.${vector} = m;` // export Module
- const blob = new Blob([ loader ], { type: 'text/javascript' })
- script.src = URL.createObjectURL(blob)
-
- document.head.appendChild(script)
- })
-}
-
function wait (ms: number) {
return new Promise<void>(res => {
setTimeout(() => res(), ms)
export {
copyToClipboard,
- importModule,
objectToUrlEncoded,
wait
}