diff options
-rw-r--r-- | client/src/app/app.component.html | 2 | ||||
-rw-r--r-- | client/src/app/app.component.ts | 10 | ||||
-rw-r--r-- | client/src/app/app.module.ts | 2 | ||||
-rw-r--r-- | client/src/app/core/plugins/plugin.service.ts | 17 | ||||
-rw-r--r-- | client/src/app/modal/custom-modal.component.html | 20 | ||||
-rw-r--r-- | client/src/app/modal/custom-modal.component.scss | 20 | ||||
-rw-r--r-- | client/src/app/modal/custom-modal.component.ts | 93 | ||||
-rw-r--r-- | client/src/types/register-client-option.model.ts | 8 | ||||
-rw-r--r-- | support/doc/plugins/guide.md | 18 |
9 files changed, 188 insertions, 2 deletions
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 @@ | |||
54 | <my-welcome-modal #welcomeModal></my-welcome-modal> | 54 | <my-welcome-modal #welcomeModal></my-welcome-modal> |
55 | <my-instance-config-warning-modal #instanceConfigWarningModal></my-instance-config-warning-modal> | 55 | <my-instance-config-warning-modal #instanceConfigWarningModal></my-instance-config-warning-modal> |
56 | </ng-template> | 56 | </ng-template> |
57 | |||
58 | <my-custom-modal #customModal></my-custom-modal> | ||
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 @@ | |||
1 | import { Component, OnInit, ViewChild } from '@angular/core' | 1 | import { Component, OnInit, ViewChild, AfterViewInit } from '@angular/core' |
2 | import { DomSanitizer, SafeHtml } from '@angular/platform-browser' | 2 | import { DomSanitizer, SafeHtml } from '@angular/platform-browser' |
3 | import { Event, GuardsCheckStart, NavigationEnd, Router, Scroll } from '@angular/router' | 3 | import { Event, GuardsCheckStart, NavigationEnd, Router, Scroll } from '@angular/router' |
4 | import { AuthService, RedirectService, ServerService, ThemeService } from '@app/core' | 4 | import { AuthService, RedirectService, ServerService, ThemeService } from '@app/core' |
@@ -14,6 +14,7 @@ import { NgbModal } from '@ng-bootstrap/ng-bootstrap' | |||
14 | import { POP_STATE_MODAL_DISMISS } from '@app/shared/misc/constants' | 14 | import { POP_STATE_MODAL_DISMISS } from '@app/shared/misc/constants' |
15 | import { WelcomeModalComponent } from '@app/modal/welcome-modal.component' | 15 | import { WelcomeModalComponent } from '@app/modal/welcome-modal.component' |
16 | import { InstanceConfigWarningModalComponent } from '@app/modal/instance-config-warning-modal.component' | 16 | import { InstanceConfigWarningModalComponent } from '@app/modal/instance-config-warning-modal.component' |
17 | import { CustomModalComponent } from '@app/modal/custom-modal.component' | ||
17 | import { ServerConfig, UserRole } from '@shared/models' | 18 | import { ServerConfig, UserRole } from '@shared/models' |
18 | import { User } from '@app/shared' | 19 | import { User } from '@app/shared' |
19 | import { InstanceService } from '@app/shared/instance/instance.service' | 20 | import { InstanceService } from '@app/shared/instance/instance.service' |
@@ -24,9 +25,10 @@ import { MenuService } from './core/menu/menu.service' | |||
24 | templateUrl: './app.component.html', | 25 | templateUrl: './app.component.html', |
25 | styleUrls: [ './app.component.scss' ] | 26 | styleUrls: [ './app.component.scss' ] |
26 | }) | 27 | }) |
27 | export class AppComponent implements OnInit { | 28 | export class AppComponent implements OnInit, AfterViewInit { |
28 | @ViewChild('welcomeModal') welcomeModal: WelcomeModalComponent | 29 | @ViewChild('welcomeModal') welcomeModal: WelcomeModalComponent |
29 | @ViewChild('instanceConfigWarningModal') instanceConfigWarningModal: InstanceConfigWarningModalComponent | 30 | @ViewChild('instanceConfigWarningModal') instanceConfigWarningModal: InstanceConfigWarningModalComponent |
31 | @ViewChild('customModal') customModal: CustomModalComponent | ||
30 | 32 | ||
31 | customCSS: SafeHtml | 33 | customCSS: SafeHtml |
32 | 34 | ||
@@ -87,6 +89,10 @@ export class AppComponent implements OnInit { | |||
87 | this.openModalsIfNeeded() | 89 | this.openModalsIfNeeded() |
88 | } | 90 | } |
89 | 91 | ||
92 | ngAfterViewInit () { | ||
93 | this.pluginService.initializeCustomModal(this.customModal) | ||
94 | } | ||
95 | |||
90 | isUserLoggedIn () { | 96 | isUserLoggedIn () { |
91 | return this.authService.isLoggedIn() | 97 | return this.authService.isLoggedIn() |
92 | } | 98 | } |
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- | |||
20 | import { buildFileLocale, getCompleteLocale, isDefaultLocale } from '@shared/models' | 20 | import { buildFileLocale, getCompleteLocale, isDefaultLocale } from '@shared/models' |
21 | import { APP_BASE_HREF } from '@angular/common' | 21 | import { APP_BASE_HREF } from '@angular/common' |
22 | import { QuickSettingsModalComponent } from '@app/modal/quick-settings-modal.component' | 22 | import { QuickSettingsModalComponent } from '@app/modal/quick-settings-modal.component' |
23 | import { CustomModalComponent } from '@app/modal/custom-modal.component' | ||
23 | 24 | ||
24 | export function metaFactory (serverService: ServerService): MetaLoader { | 25 | export function metaFactory (serverService: ServerService): MetaLoader { |
25 | return new MetaStaticLoader({ | 26 | return new MetaStaticLoader({ |
@@ -47,6 +48,7 @@ export function metaFactory (serverService: ServerService): MetaLoader { | |||
47 | SuggestionsComponent, | 48 | SuggestionsComponent, |
48 | SuggestionComponent, | 49 | SuggestionComponent, |
49 | 50 | ||
51 | CustomModalComponent, | ||
50 | WelcomeModalComponent, | 52 | WelcomeModalComponent, |
51 | InstanceConfigWarningModalComponent | 53 | InstanceConfigWarningModalComponent |
52 | ], | 54 | ], |
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' | |||
20 | import { RegisterClientHelpers } from '../../../types/register-client-option.model' | 20 | import { RegisterClientHelpers } from '../../../types/register-client-option.model' |
21 | import { PluginTranslation } from '@shared/models/plugins/plugin-translation.model' | 21 | import { PluginTranslation } from '@shared/models/plugins/plugin-translation.model' |
22 | import { importModule } from '@app/shared/misc/utils' | 22 | import { importModule } from '@app/shared/misc/utils' |
23 | import { CustomModalComponent } from '@app/modal/custom-modal.component' | ||
23 | 24 | ||
24 | interface HookStructValue extends RegisterClientHookOptions { | 25 | interface HookStructValue extends RegisterClientHookOptions { |
25 | plugin: ServerConfigPlugin | 26 | plugin: ServerConfigPlugin |
@@ -49,6 +50,8 @@ export class PluginService implements ClientHook { | |||
49 | 50 | ||
50 | translationsObservable: Observable<PluginTranslation> | 51 | translationsObservable: Observable<PluginTranslation> |
51 | 52 | ||
53 | customModal: CustomModalComponent | ||
54 | |||
52 | private plugins: ServerConfigPlugin[] = [] | 55 | private plugins: ServerConfigPlugin[] = [] |
53 | private scopes: { [ scopeName: string ]: PluginInfo[] } = {} | 56 | private scopes: { [ scopeName: string ]: PluginInfo[] } = {} |
54 | private loadedScripts: { [ script: string ]: boolean } = {} | 57 | private loadedScripts: { [ script: string ]: boolean } = {} |
@@ -81,6 +84,10 @@ export class PluginService implements ClientHook { | |||
81 | }) | 84 | }) |
82 | } | 85 | } |
83 | 86 | ||
87 | initializeCustomModal (customModal: CustomModalComponent) { | ||
88 | this.customModal = customModal | ||
89 | } | ||
90 | |||
84 | ensurePluginsAreBuilt () { | 91 | ensurePluginsAreBuilt () { |
85 | return this.pluginsBuilt.asObservable() | 92 | return this.pluginsBuilt.asObservable() |
86 | .pipe(first(), shareReplay()) | 93 | .pipe(first(), shareReplay()) |
@@ -279,6 +286,16 @@ export class PluginService implements ClientHook { | |||
279 | success: (text: string, title?: string, timeout?: number) => this.notifier.success(text, title, timeout) | 286 | success: (text: string, title?: string, timeout?: number) => this.notifier.success(text, title, timeout) |
280 | }, | 287 | }, |
281 | 288 | ||
289 | showModal: (input: { | ||
290 | title: string, | ||
291 | content: string, | ||
292 | close?: boolean, | ||
293 | cancel?: { value: string, action?: () => void }, | ||
294 | confirm?: { value: string, action?: () => void } | ||
295 | }) => { | ||
296 | this.customModal.show(input) | ||
297 | }, | ||
298 | |||
282 | translate: (value: string) => { | 299 | translate: (value: string) => { |
283 | return this.translationsObservable | 300 | return this.translationsObservable |
284 | .pipe(map(allTranslations => allTranslations[npmName])) | 301 | .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 @@ | |||
1 | <ng-template #modal let-hide="close"> | ||
2 | <div class="modal-header"> | ||
3 | <h4 class="modal-title">{{title}}</h4> | ||
4 | <my-global-icon *ngIf="close" iconName="cross" aria-label="Close" role="button" (click)="onCloseClick()"></my-global-icon> | ||
5 | </div> | ||
6 | |||
7 | <div class="modal-body" [innerHTML]="content"></div> | ||
8 | |||
9 | <div *ngIf="hasCancel() || hasConfirm()" class="modal-footer inputs"> | ||
10 | <input | ||
11 | *ngIf="hasCancel()" type="button" role="button" value="{{cancel.value}}" class="action-button action-button-cancel" | ||
12 | (click)="onCancelClick()" (key.enter)="onCancelClick()" | ||
13 | > | ||
14 | |||
15 | <input | ||
16 | *ngIf="hasConfirm()" type="button" role="button" value="{{confirm.value}}" class="action-button action-button-confirm" | ||
17 | (click)="onConfirmClick()" (key.enter)="onConfirmClick()" | ||
18 | > | ||
19 | </div> | ||
20 | </ng-template> | ||
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 @@ | |||
1 | @import '_mixins'; | ||
2 | @import '_variables'; | ||
3 | |||
4 | .modal-body { | ||
5 | font-size: 15px; | ||
6 | } | ||
7 | |||
8 | li { | ||
9 | margin-bottom: 10px; | ||
10 | } | ||
11 | |||
12 | .action-button-cancel { | ||
13 | @include peertube-button; | ||
14 | @include grey-button; | ||
15 | } | ||
16 | |||
17 | .action-button-confirm { | ||
18 | @include peertube-button; | ||
19 | @include orange-button; | ||
20 | } | ||
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 @@ | |||
1 | import { Component, ElementRef, ViewChild, Input } from '@angular/core' | ||
2 | import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap' | ||
3 | |||
4 | @Component({ | ||
5 | selector: 'my-custom-modal', | ||
6 | templateUrl: './custom-modal.component.html', | ||
7 | styleUrls: [ './custom-modal.component.scss' ] | ||
8 | }) | ||
9 | export class CustomModalComponent { | ||
10 | @ViewChild('modal', { static: true }) modal: ElementRef | ||
11 | |||
12 | @Input() title: string | ||
13 | @Input() content: string | ||
14 | @Input() close?: boolean | ||
15 | @Input() cancel?: { value: string, action?: () => void } | ||
16 | @Input() confirm?: { value: string, action?: () => void } | ||
17 | |||
18 | private modalRef: NgbModalRef | ||
19 | |||
20 | constructor ( | ||
21 | private modalService: NgbModal | ||
22 | ) { } | ||
23 | |||
24 | show (input: { | ||
25 | title: string, | ||
26 | content: string, | ||
27 | close?: boolean, | ||
28 | cancel?: { value: string, action?: () => void }, | ||
29 | confirm?: { value: string, action?: () => void } | ||
30 | }) { | ||
31 | if (this.modalRef instanceof NgbModalRef && this.modalService.hasOpenModals()) { | ||
32 | console.error('Cannot open another custom modal, one is already opened.') | ||
33 | return | ||
34 | } | ||
35 | |||
36 | const { title, content, close, cancel, confirm } = input | ||
37 | |||
38 | this.title = title | ||
39 | this.content = content | ||
40 | this.close = close | ||
41 | this.cancel = cancel | ||
42 | this.confirm = confirm | ||
43 | |||
44 | this.modalRef = this.modalService.open(this.modal, { | ||
45 | centered: true, | ||
46 | backdrop: 'static', | ||
47 | keyboard: false, | ||
48 | size: 'lg' | ||
49 | }) | ||
50 | } | ||
51 | |||
52 | onCancelClick () { | ||
53 | this.modalRef.close() | ||
54 | |||
55 | if (typeof this.cancel.action === 'function') { | ||
56 | this.cancel.action() | ||
57 | } | ||
58 | |||
59 | this.destroy() | ||
60 | } | ||
61 | |||
62 | onCloseClick () { | ||
63 | this.modalRef.close() | ||
64 | this.destroy() | ||
65 | } | ||
66 | |||
67 | onConfirmClick () { | ||
68 | this.modalRef.close() | ||
69 | |||
70 | if (typeof this.confirm.action === 'function') { | ||
71 | this.confirm.action() | ||
72 | } | ||
73 | |||
74 | this.destroy() | ||
75 | } | ||
76 | |||
77 | hasCancel () { | ||
78 | return typeof this.cancel !== 'undefined' | ||
79 | } | ||
80 | |||
81 | hasConfirm () { | ||
82 | return typeof this.confirm !== 'undefined' | ||
83 | } | ||
84 | |||
85 | private destroy () { | ||
86 | delete this.modalRef | ||
87 | delete this.title | ||
88 | delete this.content | ||
89 | delete this.close | ||
90 | delete this.cancel | ||
91 | delete this.confirm | ||
92 | } | ||
93 | } | ||
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 = { | |||
19 | success: (text: string, title?: string, timeout?: number) => void | 19 | success: (text: string, title?: string, timeout?: number) => void |
20 | } | 20 | } |
21 | 21 | ||
22 | showModal: (input: { | ||
23 | title: string, | ||
24 | content: string, | ||
25 | close?: boolean, | ||
26 | cancel?: { value: string, action?: () => void }, | ||
27 | confirm?: { value: string, action?: () => void } | ||
28 | }) => void | ||
29 | |||
22 | translate: (toTranslate: string) => Promise<string> | 30 | translate: (toTranslate: string) => Promise<string> |
23 | } | 31 | } |
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.') | |||
216 | notifier.error('Error message content.') | 216 | notifier.error('Error message content.') |
217 | ``` | 217 | ``` |
218 | 218 | ||
219 | #### Custom Modal | ||
220 | |||
221 | To show a custom modal: | ||
222 | |||
223 | ```js | ||
224 | peertubeHelpers.showModal({ | ||
225 | title: 'My custom modal title', | ||
226 | content: '<p>My custom modal content</p>', | ||
227 | // Optionals parameters : | ||
228 | // show close icon | ||
229 | close: true, | ||
230 | // show cancel button and call action() after hiding modal | ||
231 | cancel: { value: 'cancel', action: () => {} }, | ||
232 | // show confirm button and call action() after hiding modal | ||
233 | confirm: { value: 'confirm', action: () => {} }, | ||
234 | }) | ||
235 | ``` | ||
236 | |||
219 | #### Translate | 237 | #### Translate |
220 | 238 | ||
221 | You can translate some strings of your plugin (PeerTube will use your `translations` object of your `package.json` file): | 239 | You can translate some strings of your plugin (PeerTube will use your `translations` object of your `package.json` file): |