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 <kimsible@users.noreply.github.com>
pull/2654/head
Kim 2020-04-15 15:35:41 +02:00 committed by GitHub
parent 45c14ae1b2
commit 437e8e06eb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 188 additions and 2 deletions

View File

@ -54,3 +54,5 @@
<my-welcome-modal #welcomeModal></my-welcome-modal>
<my-instance-config-warning-modal #instanceConfigWarningModal></my-instance-config-warning-modal>
</ng-template>
<my-custom-modal #customModal></my-custom-modal>

View File

@ -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()
}

View File

@ -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
],

View File

@ -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<PluginTranslation>
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]))

View File

@ -0,0 +1,20 @@
<ng-template #modal let-hide="close">
<div class="modal-header">
<h4 class="modal-title">{{title}}</h4>
<my-global-icon *ngIf="close" iconName="cross" aria-label="Close" role="button" (click)="onCloseClick()"></my-global-icon>
</div>
<div class="modal-body" [innerHTML]="content"></div>
<div *ngIf="hasCancel() || hasConfirm()" class="modal-footer inputs">
<input
*ngIf="hasCancel()" type="button" role="button" value="{{cancel.value}}" class="action-button action-button-cancel"
(click)="onCancelClick()" (key.enter)="onCancelClick()"
>
<input
*ngIf="hasConfirm()" type="button" role="button" value="{{confirm.value}}" class="action-button action-button-confirm"
(click)="onConfirmClick()" (key.enter)="onConfirmClick()"
>
</div>
</ng-template>

View File

@ -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;
}

View File

@ -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
}
}

View File

@ -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<string>
}

View File

@ -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: '<p>My custom modal content</p>',
// 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):