diff --git a/client/src/app/app.component.ts b/client/src/app/app.component.ts index 736ee7359..228de4fc8 100644 --- a/client/src/app/app.component.ts +++ b/client/src/app/app.component.ts @@ -1,7 +1,7 @@ -import { delay, forkJoin } from 'rxjs' +import { delay, forkJoin, from } from 'rxjs' import { filter, first, map } from 'rxjs/operators' import { DOCUMENT, getLocaleDirection, PlatformLocation, NgIf, NgClass } from '@angular/common' -import { AfterViewInit, Component, Inject, LOCALE_ID, OnInit, ViewChild } from '@angular/core' +import { AfterViewInit, Component, Inject, LOCALE_ID, OnDestroy, OnInit, ViewChild } from '@angular/core' import { DomSanitizer, SafeHtml } from '@angular/platform-browser' import { Event, GuardsCheckStart, RouteConfigLoadEnd, RouteConfigLoadStart, Router, RouterLink, RouterOutlet } from '@angular/router' import { @@ -65,7 +65,7 @@ import { InstanceService } from './shared/shared-main/instance/instance.service' CustomModalComponent ] }) -export class AppComponent implements OnInit, AfterViewInit { +export class AppComponent implements OnInit, AfterViewInit, OnDestroy { private static BROADCAST_MESSAGE_KEY = 'app-broadcast-message-dismissed' @ViewChild('accountSetupWarningModal') accountSetupWarningModal: AccountSetupWarningModalComponent @@ -152,12 +152,28 @@ export class AppComponent implements OnInit, AfterViewInit { this.document.documentElement.lang = getShortLocale(this.localeId) this.document.documentElement.dir = getLocaleDirection(this.localeId) + + this.pluginService.addAction('application:increment-loader', () => { + this.loadingBar.useRef('plugins').start() + + return from([]) + }) + this.pluginService.addAction('application:decrement-loader', () => { + this.loadingBar.useRef('plugins').complete() + + return from([]) + }) } ngAfterViewInit () { this.pluginService.initializeCustomModal(this.customModal) } + ngOnDestroy () { + this.pluginService.removeAction('application:increment-loader') + this.pluginService.removeAction('application:decrement-loader') + } + // --------------------------------------------------------------------------- getDefaultRoute () { diff --git a/client/src/app/core/plugins/plugin.service.ts b/client/src/app/core/plugins/plugin.service.ts index 43f10e02e..2707c1ba2 100644 --- a/client/src/app/core/plugins/plugin.service.ts +++ b/client/src/app/core/plugins/plugin.service.ts @@ -12,6 +12,8 @@ import { getDevLocale, isOnDevLocale } from '@app/helpers' import { CustomModalComponent } from '@app/modal/custom-modal.component' import { getCompleteLocale, getKeys, isDefaultLocale, peertubeTranslate } from '@peertube/peertube-core-utils' import { + ClientDoActionCallback, + ClientDoActionName, ClientHook, ClientHookName, PluginClientScope, @@ -28,6 +30,7 @@ import { import { PluginInfo, PluginsManager } from '@root-helpers/plugins-manager' import { environment } from '../../../environments/environment' import { RegisterClientHelpers } from '../../../types/register-client-option.model' +import { logger } from '@root-helpers/logger' type FormFields = { video: { @@ -58,6 +61,8 @@ export class PluginService implements ClientHook { private pluginsManager: PluginsManager + private actions: { [name in ClientDoActionName]?: ClientDoActionCallback } = {} + constructor ( private authService: AuthService, private notifier: Notifier, @@ -71,6 +76,7 @@ export class PluginService implements ClientHook { this.loadTranslations() this.pluginsManager = new PluginsManager({ + doAction: this.doAction.bind(this), peertubeHelpersFactory: this.buildPeerTubeHelpers.bind(this), onFormFields: this.onFormFields.bind(this), onSettingsScripts: this.onSettingsScripts.bind(this), @@ -78,6 +84,14 @@ export class PluginService implements ClientHook { }) } + addAction (actionName: ClientDoActionName, callback: ClientDoActionCallback) { + this.actions[actionName] = callback + } + + removeAction (actionName: ClientDoActionName) { + delete this.actions[actionName] + } + initializePlugins () { this.pluginsManager.loadPluginsList(this.server.getHTMLConfig()) @@ -184,6 +198,14 @@ export class PluginService implements ClientHook { return firstValueFrom(obs) } + private doAction (actionName: ClientDoActionName) { + try { + return this.actions[actionName]() + } catch (err: any) { + logger.warn(`Plugin tried to do unknown action: ${actionName}`) + } + } + private onFormFields ( pluginInfo: PluginInfo, commonOptions: RegisterClientFormFieldOptions, diff --git a/client/src/root-helpers/plugins-manager.ts b/client/src/root-helpers/plugins-manager.ts index a11723357..3bb792ae8 100644 --- a/client/src/root-helpers/plugins-manager.ts +++ b/client/src/root-helpers/plugins-manager.ts @@ -5,6 +5,7 @@ import { first, shareReplay } from 'rxjs/operators' import { RegisterClientHelpers } from 'src/types/register-client-option.model' import { getExternalAuthHref, getHookType, internalRunHook } from '@peertube/peertube-core-utils' import { + ClientDoAction, ClientHookName, clientHookObject, ClientScriptJSON, @@ -76,17 +77,20 @@ class PluginsManager { 'admin-comments': new ReplaySubject(1) } + private readonly doAction: ClientDoAction private readonly peertubeHelpersFactory: PeertubeHelpersFactory private readonly onFormFields: OnFormFields private readonly onSettingsScripts: OnSettingsScripts private readonly onClientRoute: OnClientRoute constructor (options: { + doAction: ClientDoAction peertubeHelpersFactory: PeertubeHelpersFactory onFormFields?: OnFormFields onSettingsScripts?: OnSettingsScripts onClientRoute?: OnClientRoute }) { + this.doAction = options.doAction this.peertubeHelpersFactory = options.peertubeHelpersFactory this.onFormFields = options.onFormFields this.onSettingsScripts = options.onSettingsScripts @@ -270,6 +274,8 @@ class PluginsManager { return this.onClientRoute(options) } + const doAction = this.doAction + const peertubeHelpers = this.peertubeHelpersFactory(pluginInfo) logger.info(`Loading script ${clientScript.script} of plugin ${plugin.name}`) @@ -278,6 +284,7 @@ class PluginsManager { return dynamicImport(absURL) .then((script: ClientScript) => { return script.register({ + doAction, registerHook, registerVideoField, registerSettingsScript, diff --git a/client/src/types/register-client-option.model.ts b/client/src/types/register-client-option.model.ts index 01b668cec..47b8a0e1c 100644 --- a/client/src/types/register-client-option.model.ts +++ b/client/src/types/register-client-option.model.ts @@ -1,4 +1,5 @@ import { + ClientDoAction, MyUser, RegisterClientFormFieldOptions, RegisterClientHookOptions, @@ -9,6 +10,8 @@ import { } from '@peertube/peertube-models' export type RegisterClientOptions = { + doAction: ClientDoAction + registerHook: (options: RegisterClientHookOptions) => void registerVideoField: (commonOptions: RegisterClientFormFieldOptions, videoFormOptions: RegisterClientVideoFieldOptions) => void diff --git a/packages/models/src/plugins/client/client-hook.model.ts b/packages/models/src/plugins/client/client-hook.model.ts index 9c8ee61f8..cd9a54f41 100644 --- a/packages/models/src/plugins/client/client-hook.model.ts +++ b/packages/models/src/plugins/client/client-hook.model.ts @@ -1,3 +1,5 @@ +import { Observable } from 'rxjs' + // Data from API hooks: {hookType}:api.{location}.{elementType}.{actionType}.{target} // Data in internal functions: {hookType}:{location}.{elementType}.{actionType}.{target} @@ -208,3 +210,13 @@ export type ClientHookName = keyof typeof clientHookObject export interface ClientHook { runHook (hookName: ClientHookName, result?: T, params?: any): Promise } + +export const clientDoActionObject = { + 'application:increment-loader': true, + 'application:decrement-loader': true +} + +export type ClientDoActionName = keyof typeof clientDoActionObject + +export type ClientDoActionCallback = () => Observable +export type ClientDoAction = (actionName: ClientDoActionName) => Observable