From: Kim <1877318+kimsible@users.noreply.github.com> Date: Wed, 15 Apr 2020 13:35:41 +0000 (+0200) Subject: Add custom modal to plugin helpers (#2631) X-Git-Tag: v2.2.0-rc.1~189 X-Git-Url: https://git.librecmc.org/?a=commitdiff_plain;h=437e8e06eb32ffe21111f0946115aae0e4c33381;p=oweals%2Fpeertube.git Add custom modal to plugin helpers (#2631) * Add custom modal component * Add custom modal to app and plugins helpers * Fixes custom modal component * Add doc for custom modal * Fix newline end of file html and scss files * Move my-custom-modal component outside component for UserLoggedIn modals * Move initializeCustomModal to ngAfterViewInit() * Wrap events and conditionnals * Replace ng-show with ngIf* * Add modalRef to open only one modal + onCloseClick * Refacto + Fix access methods of custom modal * Fix methods names custom-modal.component * Fix implement AfterViewInit & no default boolean Co-authored-by: kimsible --- diff --git a/client/src/app/app.component.html b/client/src/app/app.component.html index d1eb1646f..84cff4812 100644 --- a/client/src/app/app.component.html +++ b/client/src/app/app.component.html @@ -54,3 +54,5 @@ + + diff --git a/client/src/app/app.component.ts b/client/src/app/app.component.ts index 1d077646c..12c0efd8a 100644 --- a/client/src/app/app.component.ts +++ b/client/src/app/app.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit, ViewChild } from '@angular/core' +import { Component, OnInit, ViewChild, AfterViewInit } from '@angular/core' import { DomSanitizer, SafeHtml } from '@angular/platform-browser' import { Event, GuardsCheckStart, NavigationEnd, Router, Scroll } from '@angular/router' import { AuthService, RedirectService, ServerService, ThemeService } from '@app/core' @@ -14,6 +14,7 @@ import { NgbModal } from '@ng-bootstrap/ng-bootstrap' import { POP_STATE_MODAL_DISMISS } from '@app/shared/misc/constants' import { WelcomeModalComponent } from '@app/modal/welcome-modal.component' import { InstanceConfigWarningModalComponent } from '@app/modal/instance-config-warning-modal.component' +import { CustomModalComponent } from '@app/modal/custom-modal.component' import { ServerConfig, UserRole } from '@shared/models' import { User } from '@app/shared' import { InstanceService } from '@app/shared/instance/instance.service' @@ -24,9 +25,10 @@ import { MenuService } from './core/menu/menu.service' templateUrl: './app.component.html', styleUrls: [ './app.component.scss' ] }) -export class AppComponent implements OnInit { +export class AppComponent implements OnInit, AfterViewInit { @ViewChild('welcomeModal') welcomeModal: WelcomeModalComponent @ViewChild('instanceConfigWarningModal') instanceConfigWarningModal: InstanceConfigWarningModalComponent + @ViewChild('customModal') customModal: CustomModalComponent customCSS: SafeHtml @@ -87,6 +89,10 @@ export class AppComponent implements OnInit { this.openModalsIfNeeded() } + ngAfterViewInit () { + this.pluginService.initializeCustomModal(this.customModal) + } + isUserLoggedIn () { return this.authService.isLoggedIn() } diff --git a/client/src/app/app.module.ts b/client/src/app/app.module.ts index ef23c9655..5a3b109da 100644 --- a/client/src/app/app.module.ts +++ b/client/src/app/app.module.ts @@ -20,6 +20,7 @@ import { InstanceConfigWarningModalComponent } from '@app/modal/instance-config- import { buildFileLocale, getCompleteLocale, isDefaultLocale } from '@shared/models' import { APP_BASE_HREF } from '@angular/common' import { QuickSettingsModalComponent } from '@app/modal/quick-settings-modal.component' +import { CustomModalComponent } from '@app/modal/custom-modal.component' export function metaFactory (serverService: ServerService): MetaLoader { return new MetaStaticLoader({ @@ -47,6 +48,7 @@ export function metaFactory (serverService: ServerService): MetaLoader { SuggestionsComponent, SuggestionComponent, + CustomModalComponent, WelcomeModalComponent, InstanceConfigWarningModalComponent ], diff --git a/client/src/app/core/plugins/plugin.service.ts b/client/src/app/core/plugins/plugin.service.ts index aa6823060..b4ed56cbe 100644 --- a/client/src/app/core/plugins/plugin.service.ts +++ b/client/src/app/core/plugins/plugin.service.ts @@ -20,6 +20,7 @@ import { getDevLocale, isOnDevLocale } from '@app/shared/i18n/i18n-utils' import { RegisterClientHelpers } from '../../../types/register-client-option.model' import { PluginTranslation } from '@shared/models/plugins/plugin-translation.model' import { importModule } from '@app/shared/misc/utils' +import { CustomModalComponent } from '@app/modal/custom-modal.component' interface HookStructValue extends RegisterClientHookOptions { plugin: ServerConfigPlugin @@ -49,6 +50,8 @@ export class PluginService implements ClientHook { translationsObservable: Observable + customModal: CustomModalComponent + private plugins: ServerConfigPlugin[] = [] private scopes: { [ scopeName: string ]: PluginInfo[] } = {} private loadedScripts: { [ script: string ]: boolean } = {} @@ -81,6 +84,10 @@ export class PluginService implements ClientHook { }) } + initializeCustomModal (customModal: CustomModalComponent) { + this.customModal = customModal + } + ensurePluginsAreBuilt () { return this.pluginsBuilt.asObservable() .pipe(first(), shareReplay()) @@ -279,6 +286,16 @@ export class PluginService implements ClientHook { success: (text: string, title?: string, timeout?: number) => this.notifier.success(text, title, timeout) }, + showModal: (input: { + title: string, + content: string, + close?: boolean, + cancel?: { value: string, action?: () => void }, + confirm?: { value: string, action?: () => void } + }) => { + this.customModal.show(input) + }, + translate: (value: string) => { return this.translationsObservable .pipe(map(allTranslations => allTranslations[npmName])) diff --git a/client/src/app/modal/custom-modal.component.html b/client/src/app/modal/custom-modal.component.html new file mode 100644 index 000000000..06ecc2743 --- /dev/null +++ b/client/src/app/modal/custom-modal.component.html @@ -0,0 +1,20 @@ + + + + + + + diff --git a/client/src/app/modal/custom-modal.component.scss b/client/src/app/modal/custom-modal.component.scss new file mode 100644 index 000000000..a7fa30cf5 --- /dev/null +++ b/client/src/app/modal/custom-modal.component.scss @@ -0,0 +1,20 @@ +@import '_mixins'; +@import '_variables'; + +.modal-body { + font-size: 15px; +} + +li { + margin-bottom: 10px; +} + +.action-button-cancel { + @include peertube-button; + @include grey-button; +} + +.action-button-confirm { + @include peertube-button; + @include orange-button; +} diff --git a/client/src/app/modal/custom-modal.component.ts b/client/src/app/modal/custom-modal.component.ts new file mode 100644 index 000000000..a98579085 --- /dev/null +++ b/client/src/app/modal/custom-modal.component.ts @@ -0,0 +1,93 @@ +import { Component, ElementRef, ViewChild, Input } from '@angular/core' +import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap' + +@Component({ + selector: 'my-custom-modal', + templateUrl: './custom-modal.component.html', + styleUrls: [ './custom-modal.component.scss' ] +}) +export class CustomModalComponent { + @ViewChild('modal', { static: true }) modal: ElementRef + + @Input() title: string + @Input() content: string + @Input() close?: boolean + @Input() cancel?: { value: string, action?: () => void } + @Input() confirm?: { value: string, action?: () => void } + + private modalRef: NgbModalRef + + constructor ( + private modalService: NgbModal + ) { } + + show (input: { + title: string, + content: string, + close?: boolean, + cancel?: { value: string, action?: () => void }, + confirm?: { value: string, action?: () => void } + }) { + if (this.modalRef instanceof NgbModalRef && this.modalService.hasOpenModals()) { + console.error('Cannot open another custom modal, one is already opened.') + return + } + + const { title, content, close, cancel, confirm } = input + + this.title = title + this.content = content + this.close = close + this.cancel = cancel + this.confirm = confirm + + this.modalRef = this.modalService.open(this.modal, { + centered: true, + backdrop: 'static', + keyboard: false, + size: 'lg' + }) + } + + onCancelClick () { + this.modalRef.close() + + if (typeof this.cancel.action === 'function') { + this.cancel.action() + } + + this.destroy() + } + + onCloseClick () { + this.modalRef.close() + this.destroy() + } + + onConfirmClick () { + this.modalRef.close() + + if (typeof this.confirm.action === 'function') { + this.confirm.action() + } + + this.destroy() + } + + hasCancel () { + return typeof this.cancel !== 'undefined' + } + + hasConfirm () { + return typeof this.confirm !== 'undefined' + } + + private destroy () { + delete this.modalRef + delete this.title + delete this.content + delete this.close + delete this.cancel + delete this.confirm + } +} diff --git a/client/src/types/register-client-option.model.ts b/client/src/types/register-client-option.model.ts index b64652a0f..1c235107a 100644 --- a/client/src/types/register-client-option.model.ts +++ b/client/src/types/register-client-option.model.ts @@ -19,5 +19,13 @@ export type RegisterClientHelpers = { success: (text: string, title?: string, timeout?: number) => void } + showModal: (input: { + title: string, + content: string, + close?: boolean, + cancel?: { value: string, action?: () => void }, + confirm?: { value: string, action?: () => void } + }) => void + translate: (toTranslate: string) => Promise } diff --git a/support/doc/plugins/guide.md b/support/doc/plugins/guide.md index 5251ce48a..e6870ce17 100644 --- a/support/doc/plugins/guide.md +++ b/support/doc/plugins/guide.md @@ -216,6 +216,24 @@ notifier.success('Success message content.') notifier.error('Error message content.') ``` +#### Custom Modal + +To show a custom modal: + +```js + peertubeHelpers.showModal({ + title: 'My custom modal title', + content: '

My custom modal content

', + // Optionals parameters : + // show close icon + close: true, + // show cancel button and call action() after hiding modal + cancel: { value: 'cancel', action: () => {} }, + // show confirm button and call action() after hiding modal + confirm: { value: 'confirm', action: () => {} }, + }) +``` + #### Translate You can translate some strings of your plugin (PeerTube will use your `translations` object of your `package.json` file):