diff --git a/src/main/data/middlewares/settings.ts b/src/main/data/middlewares/settings.ts index 755c809b..6499a6b8 100644 --- a/src/main/data/middlewares/settings.ts +++ b/src/main/data/middlewares/settings.ts @@ -12,6 +12,7 @@ import { EngineProperty, TtsEngineProperty, TtsVoice, + migrateSettings, } from 'shared/types' import { RootState } from 'shared/types/store' import { resolveUnpacked } from 'shared/utils' @@ -23,38 +24,29 @@ import { propertyToXml } from 'shared/parser/pipelineXmlConverter/propertyToXml' const settingsFile = resolve(app.getPath('userData'), 'settings.json') export function readSettings() { - let settings = { - // Default folder to download the results on the user disk + let settings: ApplicationSettings = { + settingsVersion: '1.3.0', downloadFolder: pathToFileURL( resolve(app.getPath('home'), 'Documents', 'DAISY Pipeline results') ).href, - // Local pipeline server - // - Run or not a local pipeline server - runLocalPipeline: true, - // - Local pipeline settings - localPipelineProps: { - localPipelineHome: resolveUnpacked('resources', 'daisy-pipeline'), - jrePath: resolveUnpacked('resources', 'daisy-pipeline', 'jre'), - // Note : [49152 ; 65535] is the range of dynamic port, 0 is reserved for error case + pipelineInstanceProps: { + pipelineType: 'embedded', webservice: { - // Note : localhost resolve as ipv6 ':::' in nodejs, but we need ipv4 for the pipeline + // Notes : + // - [49152 ; 65535] is the range of dynamic port, 0 is reserved for error case + // - localhost resolve as ipv6 ':::' in nodejs, but we need ipv4 for the pipeline host: '127.0.0.1', port: 0, path: '/ws', }, + pipelineHome: resolveUnpacked('resources', 'daisy-pipeline'), + jrePath: resolveUnpacked('resources', 'daisy-pipeline', 'jre'), appDataFolder: app.getPath('userData'), logsFolder: resolve(app.getPath('userData'), 'pipeline-logs'), }, - // Remote pipeline settings - // - Use a remote pipeline instead of the local one - useRemotePipeline: false, - // - Remote pipeline connection settings to be defined - /*remotePipelineWebservice: { - - }*/ colorScheme: 'system', - appStateOnClosingMainWindow: undefined, - jobsStateOnClosingMainWindow: 'close', + onClosingMainWindow: undefined, // Undeterminate to display the app-opening dialog + editJobOnNewTab: true, ttsConfig: { preferredVoices: [], xmlFilepath: pathToFileURL( @@ -63,21 +55,21 @@ export function readSettings() { ttsEngineProperties: [], }, autoCheckUpdate: true, - } as ApplicationSettings + } try { if (existsSync(settingsFile)) { - const loaded: ApplicationSettings = JSON.parse( - readFileSync(settingsFile, 'utf8') - ) as ApplicationSettings + const loaded: ApplicationSettings = migrateSettings( + JSON.parse(readFileSync(settingsFile, 'utf8')) + ) settings = { ...settings, ...loaded, - localPipelineProps: { - ...settings.localPipelineProps, - ...loaded?.localPipelineProps, + pipelineInstanceProps: { + ...settings.pipelineInstanceProps, + ...loaded?.pipelineInstanceProps, webservice: { - ...settings.localPipelineProps.webservice, - ...loaded?.localPipelineProps?.webservice, + ...settings.pipelineInstanceProps.webservice, + ...loaded?.pipelineInstanceProps?.webservice, }, }, ttsConfig: { @@ -115,7 +107,7 @@ export function readSettings() { } // Remove pipeline props loading for dev - if (ENVIRONMENT.IS_DEV) settings.localPipelineProps = undefined + if (ENVIRONMENT.IS_DEV) settings.pipelineInstanceProps = undefined return settings } diff --git a/src/main/factories/ipcs/pipeline.ts b/src/main/factories/ipcs/pipeline.ts index 21ba1e0f..673d32a6 100644 --- a/src/main/factories/ipcs/pipeline.ts +++ b/src/main/factories/ipcs/pipeline.ts @@ -4,7 +4,6 @@ import { Webservice, PipelineStatus, PipelineState, - ApplicationSettings, PipelineInstanceProperties, } from 'shared/types' import { ENVIRONMENT, IPC } from 'shared/constants' @@ -12,13 +11,9 @@ import { spawn, ChildProcessWithoutNullStreams } from 'child_process' import { existsSync, mkdirSync, readFileSync, rmSync } from 'fs' import { getAvailablePort, Pipeline2Error, walk } from './utils' - import { resolveUnpacked } from 'shared/utils' - import { info, error } from 'electron-log' - import { pathToFileURL } from 'url' - import { store } from 'main/data/store' import { selectPipeline, @@ -52,13 +47,13 @@ export class PipelineInstance { */ constructor(props?: PipelineInstanceProperties) { this.props = { - localPipelineHome: + pipelineHome: // Add `(props && props.pipelineType == 'system' && props.localPipelineHome) ||` if system wide pipeline control requested - // (!props || props.pipelineType == 'embedded') && + (!props || props.pipelineType == 'embedded') && resolveUnpacked('resources', 'daisy-pipeline'), jrePath: // Add `(props && props.pipelineType == 'system' && props.jrePath) ||` if system wide pipeline control requested - // (!props || props.pipelineType == 'embedded') && + (!props || props.pipelineType == 'embedded') && resolveUnpacked('resources', 'daisy-pipeline', 'jre'), // Note : [49152 ; 65535] is the range of dynamic port, 0 is reserved for error case webservice: (props && @@ -130,7 +125,7 @@ export class PipelineInstance { } if ( - this.props.localPipelineHome === null || + this.props.pipelineHome === null || !existsSync(this.props.jrePath) ) { throw new Pipeline2Error( @@ -253,8 +248,8 @@ Then close the program using the port and restart this application.`, `Launching pipeline on ${this.props.webservice.host}:${this.props.webservice.port}` ) let ClassFolders = [ - resolve(this.props.localPipelineHome, 'system'), - resolve(this.props.localPipelineHome, 'modules'), + resolve(this.props.pipelineHome, 'system'), + resolve(this.props.pipelineHome, 'modules'), ] let jarFiles = ClassFolders.reduce( (acc: Array, path: string) => { @@ -271,7 +266,7 @@ Then close the program using the port and restart this application.`, let relativeJarFiles = jarFiles.reduce( (acc: Array, path: string) => { let relativeDirPath = relative( - this.props.localPipelineHome, + this.props.pipelineHome, path ) if (!acc.includes(relativeDirPath)) { @@ -314,7 +309,7 @@ Then close the program using the port and restart this application.`, let SystemProps = [ '-Dorg.daisy.pipeline.properties="' + resolve( - this.props.localPipelineHome, + this.props.pipelineHome, 'etc', 'pipeline.properties' ) + @@ -322,11 +317,7 @@ Then close the program using the port and restart this application.`, // Logback configuration file '-Dlogback.configurationFile=' + pathToFileURL( - resolve( - this.props.localPipelineHome, - 'etc', - 'logback.xml' - ) + resolve(this.props.pipelineHome, 'etc', 'logback.xml') ).href + '', // XMLCalabash base configuration file @@ -340,17 +331,17 @@ Then close the program using the port and restart this application.`, // Updater configuration '-Dorg.daisy.pipeline.updater.bin="' + resolve( - this.props.localPipelineHome, + this.props.pipelineHome, 'updater', 'pipeline-updater' ).replaceAll('\\', '/') + '"', '-Dorg.daisy.pipeline.updater.deployPath="' + - this.props.localPipelineHome.replaceAll('\\', '/') + + this.props.pipelineHome.replaceAll('\\', '/') + '/"', '-Dorg.daisy.pipeline.updater.releaseDescriptor="' + resolve( - this.props.localPipelineHome, + this.props.pipelineHome, 'etc', 'releaseDescriptor.xml' ).replaceAll('\\', '/') + @@ -367,7 +358,7 @@ Then close the program using the port and restart this application.`, '-Dorg.daisy.pipeline.ws.authentication=false', '-Dorg.daisy.pipeline.ws.host=' + this.props.webservice.host, '-Dorg.daisy.pipeline.ws.cors=true', - '-Dorg.daisy.pipeline.home=' + this.props.localPipelineHome, + '-Dorg.daisy.pipeline.home=' + this.props.pipelineHome, '-Dorg.daisy.pipeline.tts.host.protection=false', // so we can send TTS engine properties ] if (this.props.webservice.path) { @@ -416,7 +407,7 @@ Then close the program using the port and restart this application.`, ${command} ${args.join(' ')}` ) this.instance = spawn(command, args, { - cwd: this.props.localPipelineHome, + cwd: this.props.pipelineHome, }) // NP Replace stdout analysis by webservice monitoring this.instance.stdout.on('data', (data) => { diff --git a/src/main/factories/windows/create.ts b/src/main/factories/windows/create.ts index 71e3179b..1dac1997 100644 --- a/src/main/factories/windows/create.ts +++ b/src/main/factories/windows/create.ts @@ -6,6 +6,7 @@ import { selectPipeline } from 'shared/data/slices/pipeline' import { selectSettings } from 'shared/data/slices/settings' import { WindowProps } from 'shared/types' import { APP_CONFIG } from '~/app.config' + import { PipelineInstance } from '../ipcs/pipeline' /** diff --git a/src/main/windows/Main/index.ts b/src/main/windows/Main/index.ts index 5bd5f4ed..e989dc39 100644 --- a/src/main/windows/Main/index.ts +++ b/src/main/windows/Main/index.ts @@ -5,12 +5,11 @@ import { ENVIRONMENT, IPC } from 'shared/constants' import { createWindow } from 'main/factories' import { APP_CONFIG } from '~/app.config' import { store } from 'main/data/store' -import { ClosingMainWindowActionForApp } from 'shared/types' +import { ClosingMainWindowAction } from 'shared/types' import { save, - selectClosingActionForApp, - setClosingMainWindowActionForApp, - selectClosingActionForJobs, + selectClosingAction, + setClosingMainWindowAction, } from 'shared/data/slices/settings' import { addJob, @@ -150,71 +149,83 @@ export async function MainWindow() { MainWindowInstance.on('close', (event) => { event.preventDefault() - const closingActionForJobs = selectClosingActionForJobs( - store.getState() - ) - // Check for confirmation on closing the app or not - const closingActionForApp = selectClosingActionForApp( - store.getState() - ) - if (!closingActionForApp || closingActionForApp == 'ask') { - dialog - .showMessageBox(MainWindowInstance, { - message: `The application can keep running in the tray to keep the engine running and reload the application faster. + const closingAction: keyof typeof ClosingMainWindowAction = + selectClosingAction(store.getState()) || 'ask' + switch (closingAction) { + case 'ask': + dialog + .showMessageBox(MainWindowInstance, { + message: `The application can keep running in the tray to keep the engine running and reload the application faster. + + Do you want to stop the engine and quit the application on closing this window ?`, + checkboxLabel: + 'Remember my choice (can be changed in settings)', + checkboxChecked: false, + title: 'Keep the application in tray ?', + type: 'info', + buttons: [ + 'Keep the application running in tray with all jobs opened', + 'Close all jobs but keep the application running in tray', + 'Quit the application', + 'Cancel', + ], + }) + .then((result) => { + if (result.response < 3) { + let action: keyof typeof ClosingMainWindowAction = + result.response == 0 + ? 'keepall' + : result.response == 1 + ? 'keepengine' + : result.response == 2 + ? 'close' + : 'ask' -Do you want to stop the engine and quit the application on closing this window ?`, - checkboxLabel: - 'Remember my choice (can be changed in settings)', - checkboxChecked: false, - title: 'Keep the application in tray ?', - type: 'info', - buttons: [ - 'Keep the application in tray', - 'Stop the application', - 'Cancel', - ], - }) - .then((result) => { - if (result.response < 2) { - let action: keyof typeof ClosingMainWindowActionForApp = - result.response == 0 ? 'keep' : 'close' - if (result.checkboxChecked) { - store.dispatch( - setClosingMainWindowActionForApp(action) + if (result.checkboxChecked) { + store.dispatch( + setClosingMainWindowAction(action) + ) + store.dispatch(save()) + // Save result + } + switch (action) { + case 'close': + closeApplication() + break + case 'keepengine': + if (removeNonRunningJobs()) + MainWindowInstance.destroy() + break + case 'keepall': + default: + MainWindowInstance.destroy() + break + } + } else if ( + selectJobs(store.getState()).length == 0 + ) { + const _emptyJob = newJob( + selectPipeline(store.getState()) ) - store.dispatch(save()) - // Save result + store.dispatch(addJob(_emptyJob)) + store.dispatch(selectJob(_emptyJob)) } - if (action == 'close') { - closeApplication() - } else { - MainWindowInstance.destroy() - } - } else if ( - (!closingActionForJobs || - closingActionForJobs == 'close') && - selectJobs(store.getState()).length == 0 - ) { - const _emptyJob = newJob( - selectPipeline(store.getState()) - ) - store.dispatch(addJob(_emptyJob)) - store.dispatch(selectJob(_emptyJob)) - } - }) - } else if (closingActionForApp == 'close') { - closeApplication() - } else if ( - !closingActionForJobs || - closingActionForJobs == 'close' - ) { - if (removeNonRunningJobs()) MainWindowInstance.destroy() - } else { - MainWindowInstance.destroy() + }) + break + case 'close': + closeApplication() + break + case 'keepengine': + if (removeNonRunningJobs()) MainWindowInstance.destroy() + break + case 'keepall': + default: + MainWindowInstance.destroy() + break } }) - if (selectClosingActionForApp(store.getState()) == undefined) { + if (selectClosingAction(store.getState()) == undefined) { dialog .showMessageBox(MainWindowInstance, { message: `This application runs in the tray to keep the DAISY pipeline engine running in the background, and reload the application faster. @@ -228,7 +239,7 @@ If you want to change this behaviour and also close the engine when closing the }) .then((result) => { if (result.checkboxChecked) { - store.dispatch(setClosingMainWindowActionForApp('ask')) + store.dispatch(setClosingMainWindowAction('ask')) store.dispatch(save()) // Save result } diff --git a/src/renderer/components/SettingsView/index.tsx b/src/renderer/components/SettingsView/index.tsx index b02dddb7..7884d959 100644 --- a/src/renderer/components/SettingsView/index.tsx +++ b/src/renderer/components/SettingsView/index.tsx @@ -2,19 +2,18 @@ import { useEffect, useState } from 'react' import { useWindowStore } from 'renderer/store' import { ApplicationSettings, - ClosingMainWindowActionForApp, - ClosingMainWindowActionForJobs, + ClosingMainWindowAction, ColorScheme, } from 'shared/types' import { FileOrFolderInput } from '../Fields/FileOrFolderInput' import { save, setTtsConfig, - setClosingMainWindowActionForApp, setDownloadPath, setColorScheme, setAutoCheckUpdate, - setClosingMainWindowActionForJobs, + setClosingMainWindowAction, + setEditJobOnNewTab, } from 'shared/data/slices/settings' import { TtsVoicesConfigPane } from '../TtsVoicesConfig' import { TtsEnginesConfigPane } from '../TtsEnginesConfig' @@ -39,10 +38,7 @@ export function SettingsView() { // (without affecting the rest of the app) const [newSettings, setNewSettings] = useState({ ...settings, - appStateOnClosingMainWindow: - settings.appStateOnClosingMainWindow ?? 'ask', // defaults to ask in form - jobsStateOnClosingMainWindow: - settings.jobsStateOnClosingMainWindow ?? 'close', // defaults to ask in form + onClosingMainWindow: settings.onClosingMainWindow ?? 'ask', // defaults to ask in form ttsConfig: { ...settings.ttsConfig, }, @@ -52,10 +48,7 @@ export function SettingsView() { // Reload settings from store if it has changed setNewSettings({ ...settings, - appStateOnClosingMainWindow: - settings.appStateOnClosingMainWindow ?? 'ask', // defaults to ask in form - jobsStateOnClosingMainWindow: - settings.jobsStateOnClosingMainWindow ?? 'close', // defaults to ask in form + onClosingMainWindow: settings.onClosingMainWindow ?? 'ask', // defaults to ask in form ttsConfig: { ...settings.ttsConfig, }, @@ -82,34 +75,24 @@ export function SettingsView() { App.store.dispatch(save()) //setSaved(true) } - const AppClosingActionChanged = (e) => { + const ClosingActionChanged = (e) => { App.store.dispatch( - setClosingMainWindowActionForApp( - Object.keys(ClosingMainWindowActionForApp)[ + setClosingMainWindowAction( + Object.keys(ClosingMainWindowAction)[ e.target.selectedIndex - ] as keyof typeof ClosingMainWindowActionForApp + ] as keyof typeof ClosingMainWindowAction ) ) App.store.dispatch(save()) - //setSaved(true) } - const autoCheckUpdateChanged = (e) => { App.store.dispatch(setAutoCheckUpdate(e.target.checked)) App.store.dispatch(save()) - //setSaved(true) } - const JobsClosingActionChanged = (e) => { - App.store.dispatch( - setClosingMainWindowActionForJobs( - Object.keys(ClosingMainWindowActionForJobs)[ - e.target.selectedIndex - ] as keyof typeof ClosingMainWindowActionForJobs - ) - ) + const editJobOnNewTabChanged = (e) => { + App.store.dispatch(setEditJobOnNewTab(e.target.checked)) App.store.dispatch(save()) - //setSaved(true) } const onTtsVoicesPreferenceChange = (voices) => { @@ -311,55 +294,42 @@ export function SettingsView() { ) : selectedSection == SelectedMenuItem.Behavior ? ( <>
-
-