import { actions as coreActions , DocumentRecordProps , selectors , } from "@nteract/core" ; import { ipcRenderer as ipc , IpcRendererEvent , MessageBoxReturnValue } from "electron" ; import { RecordOf } from "immutable" ; import { ofType , StateObservable } from "redux-observable" ; import { concat , empty , Observable , Observer , of , zip } from "rxjs" ; import { catchError , concatMap , exhaustMap , filter , take , tap , timeout , } from "rxjs/operators" ; import * as actions from "../actions" ; import { CLOSE_NOTEBOOK , CloseNotebook } from "../actionTypes" ; import { DESKTOP_NOTEBOOK_CLOSING_NOT_STARTED , DESKTOP_NOTEBOOK_CLOSING_READY_TO_CLOSE , DesktopNotebookAppState , } from "../state" ; import { KernelRecord , KernelRef } from "@nteract/types" ; import { Actions } from "../actions" ; export const closeNotebookEpic = ( action$ , state$ ) => action$ . pipe ( ofType ( CLOSE_NOTEBOOK ) , exhaustMap ( ( action ) => { const contentRef = action . payload . contentRef ; const state = state$ . value ; const model = selectors . model ( state , { contentRef , } ) as RecordOf < DocumentRecordProps > ; let dirtyPromptObservable ; if ( selectors . notebook . isDirty ( model ) ) { dirtyPromptObservable = Observable . create ( ( observer ) => { const promptDialog = { type : "question" , buttons : [ "Quit" , "Cancel" ] , title : "Confirm" , message : "Unsaved data will be lost. Are you sure you want to quit?" , } ; ipc . once ( "show-message-box-response" , ( _event , arg ) => { observer . next ( arg . response === 0 ) ; observer . complete ( ) ; } ) ; ipc . send ( "show-message-box" , promptDialog ) ; } ) ; } else { dirtyPromptObservable = of ( true ) ; } const killKernelActions = [ ] ; const killKernelAwaits = [ ] ; state . core . entities . kernels . byRef . forEach ( ( kernel , kernelRef ) => { if ( ! kernel || ! kernel . type ) { return ; } if ( kernel . type === "zeromq" ) { killKernelActions . push ( coreActions . killKernel ( { restarting : false , kernelRef , } ) ) ; killKernelAwaits . push ( action$ . pipe ( ofType ( coreActions . KILL_KERNEL_SUCCESSFUL , coreActions . KILL_KERNEL_FAILED ) , filter ( ( action ) => action . payload . kernelRef === kernelRef ) , take ( 1 ) ) ) ; } else if ( kernel . type === "websocket" ) { console . log ( "Need to implement a way to shutdown websocket kernels on desktop" ) ; } } ) ; const killKernels = of ( ... killKernelActions ) ; const awaitKillKernelResults = zip ( ... killKernelAwaits ) . pipe ( timeout ( 1000 * 5 ) , tap ( ( result ) => { for ( const r of result ) { console . log ( JSON . stringify ( r ) ) ; } } ) , concatMap ( ( ) => empty ( ) ) , catchError ( ( error ) => { console . log ( "One or more kernels failed to shutdown properly in the allocated time." ) ; console . log ( error . message ) ; return empty ( ) ; } ) ) ; const updateClosingState = of ( actions . closeNotebookProgress ( { newState : DESKTOP_NOTEBOOK_CLOSING_READY_TO_CLOSE , } ) ) ; const initiateClose = Observable . create ( ( observer ) => { if ( action . payload . reloading ) { console . log ( "Kernel shutdown complete; reloading notebook window..." ) ; ipc . send ( "reload" ) ; } else { console . log ( "Kernel shutdown complete; closing notebook window..." ) ; window . close ( ) ; } observer . complete ( ) ; } ) ; return dirtyPromptObservable . pipe ( concatMap ( ( proceed ) => { if ( ! proceed ) { ipc . send ( "close-notebook-canceled" ) ; return of ( actions . closeNotebookProgress ( { newState : DESKTOP_NOTEBOOK_CLOSING_NOT_STARTED , } ) ) ; } return concat ( killKernels , awaitKillKernelResults , updateClosingState , initiateClose ) ; } ) ) ; } ) ) ;