import { Inject , Injectable , OnDestroy , Optional } from '@angular/core' ; import { ActivatedRoute , Params , Routes , ROUTES } from '@angular/router' ; import { Observable , Subject } from 'rxjs' ; import { filter , map , takeUntil } from 'rxjs/operators' ; import { KirbyAnimation } from '../../../animation/kirby-animation' ; import { ActionSheetConfig } from '../action-sheet/config/action-sheet-config' ; import { AlertConfig } from '../alert/config/alert-config' ; import { ModalConfig } from '../modal-wrapper/config/modal-config' ; import { ActionSheetHelper } from './action-sheet.helper' ; import { AlertHelper } from './alert.helper' ; import { ModalNavigationService } from './modal-navigation.service' ; import { ModalHelper } from './modal.helper' ; import { ModalRouteActivation , Overlay } from './modal.interfaces' ; @ Injectable ( ) export class ModalController implements OnDestroy { private overlays : Overlay [ ] = [ ] ; private readonly noOverlayRegisteredErrorMessage = 'No modal overlays are currently registered' ; private destroy$ = new Subject < void > ( ) ; constructor ( private modalHelper , private actionSheetHelper , private alertHelper , private modalNavigationService , @ Optional ( ) @ Inject ( ROUTES ) private routeConfig ) { } async initialize ( moduleRootRoutePath ? ) { const modalNavigation = await this . modalNavigationService . getModalNavigation ( this . routeConfig , moduleRootRoutePath ) ; this . onModalRouteActivated ( modalNavigation . activated$ ) ; this . onModalRouteDeactivated ( modalNavigation . deactivated$ ) ; } private onModalRouteActivated ( modalRouteActivated$ ) { const navigateOnWillClose = ( ) => { this . modalNavigationService . navigateOutOfModalOutlet ( ) ; } ; const siblingModalRouteActivated$ = modalRouteActivated$ . pipe ( filter ( ( modalRouteActivation ) => ! modalRouteActivation . isNewModal ) , map ( ( modalRouteActivation ) => modalRouteActivation . route ) ) ; modalRouteActivated$ . pipe ( takeUntil ( this . destroy$ ) , filter ( ( ) => this . overlays . length === 0 ) ) . subscribe ( async ( modalRouteActivation ) => { if ( modalRouteActivation . isNewModal ) { await this . showModalRoute ( modalRouteActivation . route , siblingModalRouteActivated$ , navigateOnWillClose ) ; } } ) ; } private onModalRouteDeactivated ( modalRouteDeactivated$ ) { modalRouteDeactivated$ . pipe ( takeUntil ( this . destroy$ ) , filter ( ( ) => this . overlays . length > 0 ) ) . subscribe ( async ( ) => { await this . hideAll ( ) ; } ) ; } public async showModal ( config , onClose ? ) { if ( config . hasOwnProperty ( 'dim' ) ) { console . warn ( 'ModalConfig.dim is deprecated - please remove from your configuration.' ) ; } await this . showAndRegisterOverlay ( ( ) => this . modalHelper . showModalWindow ( config ) , onClose ) ; } public async navigateToModal ( path , queryParams ? ) { return this . modalNavigationService . navigateToModal ( path , queryParams ) ; } public async navigateWithinModal ( relativePath , queryParams ? ) { return this . modalNavigationService . navigateWithinModal ( relativePath , queryParams ) ; } private async showModalRoute ( modalRoute , siblingModalRouteActivated$ , onWillClose ) { const config = { component : null , modalRoute : modalRoute , siblingModalRouteActivated$ : siblingModalRouteActivated$ , flavor : 'modal' , } ; await this . showAndRegisterOverlay ( ( ) => this . modalHelper . showModalWindow ( config ) , null , onWillClose ) ; } public async showActionSheet ( config , onClose ? ) { await this . showAndRegisterOverlay ( ( ) => this . actionSheetHelper . showActionSheet ( config ) , onClose ) ; } public async showAlert ( config , onClose ? ) { await this . showAndRegisterOverlay ( ( ) => this . alertHelper . showAlert ( config ) , onClose ) ; } private async showAndRegisterOverlay ( showOverlay , onCloseOverlay ? , onWillCloseOverlay ? ) { const overlay = await showOverlay ( ) ; if ( ! overlay ) return ; this . overlays . push ( overlay ) ; if ( typeof onWillCloseOverlay === 'function' ) { overlay . onWillDismiss . then ( ( event ) => onWillCloseOverlay ( event . data ) ) ; } overlay . onDidDismiss . then ( ( event ) => { this . overlays . pop ( ) ; if ( typeof onCloseOverlay === 'function' ) { onCloseOverlay ( event . data ) ; } } ) ; } public registerPresentingElement ( element ) { this . modalHelper . registerPresentingElement ( element ) ; } public async hideTopmost ( data ? ) { const overlay = this . overlays [ this . overlays . length - 1 ] ; if ( ! overlay ) { throw new Error ( this . noOverlayRegisteredErrorMessage ) ; } return overlay . dismiss ( data ) ; } deprecated public scrollToTop ( duration ? ) { console . warn ( 'ModalController.scrollToTop is deprecated - please inject Modal in embedded component and use Modal.scrollToTop instead.' ) ; const overlay = this . overlays [ this . overlays . length - 1 ] ; if ( ! overlay ) { throw new Error ( this . noOverlayRegisteredErrorMessage ) ; } this . modalHelper . scrollToTop ( this . noOverlayRegisteredErrorMessage , duration ) ; } deprecated public scrollToBottom ( duration ? ) { console . warn ( 'ModalController.scrollToBottom is deprecated - please inject Modal in embedded component and use Modal.scrollToBottom instead.' ) ; const overlay = this . overlays [ this . overlays . length - 1 ] ; if ( ! overlay ) { throw new Error ( this . noOverlayRegisteredErrorMessage ) ; } this . modalHelper . scrollToBottom ( this . noOverlayRegisteredErrorMessage , duration ) ; } public async hideAll ( ) { await Promise . all ( this . overlays . map ( async ( overlay ) => { await overlay . dismiss ( ) ; } ) ) ; } ngOnDestroy ( ) { this . destroy$ . next ( ) ; this . destroy$ . complete ( ) ; } }