diff --git a/src/main/data/middlewares/pipeline.ts b/src/main/data/middlewares/pipeline.ts index f188d77c..4c452f9a 100644 --- a/src/main/data/middlewares/pipeline.ts +++ b/src/main/data/middlewares/pipeline.ts @@ -24,6 +24,7 @@ import { setProperties, setTtsEngineState, setTtsEngineFeatures, + requestStylesheetParameters, } from 'shared/data/slices/pipeline' import { @@ -367,7 +368,7 @@ export function pipelineMiddleware({ getState, dispatch }) { dispatch(setTtsEngineFeatures(features)) }) .catch((e) => { - error('useWebservice', e) + error('useWebservice', e, e.parsedText) if ( selectStatus(getState()) == PipelineStatus.RUNNING @@ -687,6 +688,80 @@ export function pipelineMiddleware({ getState, dispatch }) { } }, 1000) break + case requestStylesheetParameters.type: + const job = action.payload as Job + const stylesheet = job.jobRequest.options.filter( + (option) => option.name === 'stylesheet' + )[0] + if ( + !stylesheet || + !stylesheet.value || + stylesheet.value == '' + ) { + // No parameters provided, load defaults + dispatch( + updateJob({ + ...job, + stylesheetParameters: [], + }) + ) + } else { + pipelineAPI + .fetchStylesheetParameters(job)(webservice) + .then((parameters) => { + console.log('received parameters', parameters) + // update job options with new parameters + const options = [...job.jobRequest.options] + for (let item of parameters) { + const existingOption = options.find( + (o) => o.name === item.name + ) + if (existingOption !== undefined) { + existingOption.value = item.default + } else { + // For now, only consider non-uri parameters + options.push({ + name: item.name, + value: item.default, + isFile: false, + }) + } + } + // Also send back the parameters to the UI + // for composition of the script options + dispatch( + updateJob({ + ...job, + jobRequest: { + ...job.jobRequest, + options: [...options], + }, + stylesheetParameters: parameters, + }) + ) + }) + .catch((e) => { + error('error fetching stylesheet parameters', e) + dispatch( + updateJob({ + ...job, + jobData: { + ...job.jobData, + status: JobStatus.ERROR, + }, + errors: [ + { + error: + e instanceof ParserException + ? e.parsedText + : String(e), + }, + ], + }) + ) + }) + } + break default: if (action.type.startsWith('settings/')) { // FIXME : check if local pipeline props have changed and diff --git a/src/renderer/components/Fields/FormField.tsx b/src/renderer/components/Fields/FormField.tsx index 1a4932c7..298f1a36 100644 --- a/src/renderer/components/Fields/FormField.tsx +++ b/src/renderer/components/Fields/FormField.tsx @@ -23,26 +23,19 @@ export function FormField({ }: { item: ScriptItemBase idprefix: string - onChange: (string, ScriptItemBase) => void // function to set the value in a parent-level collection. + onChange: (value: any, item: ScriptItemBase) => void // function to set the value in a parent-level collection. initialValue: any // the initial value for the field }) { - const [value, setValue] = useState(initialValue) const [checked, setChecked] = useState(true) let controlId = `${idprefix}-${item.name}` - let onChangeValue = (newValue, scriptItem) => { - setValue(newValue) - onChange(newValue, scriptItem) - } - let dialogOpts = - item.type == 'anyFileURI' - ? ['openFile'] - : item.type == 'anyDirURI' - ? ['openDirectory'] - : ['openFile', 'openDirectory'] + let dialogOpts = ['anyFileURI', 'anyURI'].includes(item.type) + ? ['openFile'] + : item.type == 'anyDirURI' + ? ['openDirectory'] + : ['openFile', 'openDirectory'] const { settings } = useWindowStore() - let matchType = (item) => { let inputType = findInputType(item.type) if (inputType == 'file') { @@ -54,11 +47,11 @@ export function FormField({ elemId={controlId} mediaType={item.mediaType} name={item.name} - onChange={(filenames) => onChangeValue(filenames, item)} + onChange={(filenames) => onChange(filenames, item)} useSystemPath={false} buttonLabel="Browse" required={item.required} - initialValue={value} + initialValue={initialValue} ordered={item.ordered} /> ) @@ -77,13 +70,11 @@ export function FormField({ elemId={controlId} mediaType={item.mediaType} name={item.name} - onChange={(filename) => - onChangeValue(filename, item) - } + onChange={(filename) => onChange(filename, item)} useSystemPath={false} buttonLabel="Browse" required={item.required} - initialValue={value} + initialValue={initialValue} /> ) } @@ -94,9 +85,11 @@ export function FormField({ onChangeValue(e.target.checked, item)} + onChange={(e) => onChange(e.target.checked, item)} id={controlId} - checked={value === 'true' || value === true} + checked={ + initialValue === 'true' || initialValue === true + } > ) @@ -104,8 +97,8 @@ export function FormField({ return ( onChangeValue(newValue, item)} - initialValue={value ?? ''} + onChange={(newValue) => onChange(newValue, item)} + initialValue={initialValue ?? ''} controlId={controlId} /> ) @@ -115,10 +108,9 @@ export function FormField({ onChangeValue(e.target.value, item)} + onChange={(e) => onChange(e.target.value, item)} > ) diff --git a/src/renderer/components/ScriptForm/index.tsx b/src/renderer/components/ScriptForm/index.tsx index ad093b23..8f211a76 100644 --- a/src/renderer/components/ScriptForm/index.tsx +++ b/src/renderer/components/ScriptForm/index.tsx @@ -1,7 +1,13 @@ /* Fill out fields for a new job and submit it */ -import { Job, Script } from 'shared/types' +import { + Job, + NameValue, + Script, + ScriptItemBase, + ScriptOption, +} from 'shared/types' import { useState } from 'react' import { useWindowStore } from 'renderer/store' import { @@ -10,7 +16,11 @@ import { getAllRequired, ID, } from 'renderer/utils/utils' -import { restoreJob, runJob } from 'shared/data/slices/pipeline' +import { + requestStylesheetParameters, + restoreJob, + runJob, +} from 'shared/data/slices/pipeline' import { addJob, removeJob, @@ -24,7 +34,7 @@ import { FormField } from '../Fields/FormField' const { App } = window // update the array and return a new copy of it -let updateArrayValue = (value, data, arr) => { +let updateArrayValue = (value: any, data: ScriptItemBase, arr: NameValue[]) => { let arr2 = arr.map((i) => (i.name == data.name ? { ...i, value } : i)) return arr2 } @@ -37,23 +47,77 @@ export function ScriptForm({ job, script }: { job: Job; script: Script }) { let optional = getAllOptional(script) const { settings } = useWindowStore() - let saveValueInJobRequest = (value, data) => { + // for to-pef scripts + // the job request must be splitted in two step + // First only display the following parameters + // - inputs, + // - stylesheet, + // - page-width + // - page-height + + // Cannot use const isBrailleJob = optional.findIndex((item) => item.name === 'stylesheet') + // as other non braille steps have a stylesheet option + const isBrailleJob = (script && script.id.endsWith('to-pef')) || false + // Filter out the options that are to be defined in the first step of braille script + const filteredOptions = ['stylesheet', 'page-width', 'page-height'] + const hiddenOptions = ['transform', 'stylesheet-parameters'] + if (isBrailleJob) { + optional = optional.filter((item) => + filteredOptions.includes(item.name) + ) + } + + // After requestStylesheetParameters, the engine will return a list of new + // script options. Those are stored separatly in the job.stylesheetParameters + // properties + // When this property is set + if (isBrailleJob && job.stylesheetParameters != null) { + required = [] + optional = [ + ...getAllOptional(script) + .filter((item) => !filteredOptions.includes(item.name)) + .filter((item) => !hiddenOptions.includes(item.name)), + ] + for (let item of job.stylesheetParameters) { + const existingOption = optional.find( + (o) => o.name === item.name + ) as ScriptOption | undefined + if (existingOption !== undefined) { + existingOption.default = item.default + } else { + optional.push(item) + } + } + } + + // Allow the user to go back to first inputs and options set + let previous = async (e) => { + e.preventDefault() + App.store.dispatch( + updateJob({ + ...job, + stylesheetParameters: null, + }) + ) + } + + let saveValueInJobRequest = (value: any, item: ScriptItemBase) => { if (!job.jobRequest) { return } let inputs = [...job.jobRequest.inputs] let options = [...job.jobRequest.options] - if (data.mediaType.includes('text/css')) { + if (item.mediaType?.includes('text/css')) { // the css filenames are already formatted by our file widget as 'file:///'... // so i don't think they need to be modified before getting sent to the engine // but this block is a placeholder just in case we have to change it // i haven't tested this on windows as of now } - if (data.kind == 'input') { - inputs = updateArrayValue(value, data, inputs) + if (item.kind == 'input') { + inputs = updateArrayValue(value, item, inputs) } else { - options = updateArrayValue(value, data, options) + options = updateArrayValue(value, item, options) } App.store.dispatch( @@ -71,15 +135,18 @@ export function ScriptForm({ job, script }: { job: Job; script: Script }) { // submit a job let onSubmit = async (e) => { e.preventDefault() - setSubmitInProgress(true) - - App.store.dispatch( - runJob({ - ...job, - jobRequest: job.jobRequest, - }) - ) - setSubmitInProgress(false) + if (isBrailleJob && job.stylesheetParameters == null) { + App.store.dispatch(requestStylesheetParameters(job)) + } else { + setSubmitInProgress(true) + App.store.dispatch( + runJob({ + ...job, + jobRequest: job.jobRequest, + }) + ) + setSubmitInProgress(false) + } } return ( @@ -112,33 +179,37 @@ export function ScriptForm({ job, script }: { job: Job; script: Script }) { {!submitInProgress ? (
-
-

- Required information -

-
    - {required.map((item, idx) => ( -
  • - -
  • - ))} -
-
+ {required.length > 0 && ( +
+

+ Required information +

+
    + {required.map((item, idx) => ( +
  • + +
  • + ))} +
+
+ )} {optional.length > 0 ? (
    {optional.map((item, idx) => - item.mediaType.includes( + item.mediaType?.includes( 'application/vnd.pipeline.tts-config+xml' ) ? ( '' // skip it, we don't need to provide a visual field for this option, it's set globally ) : ( -
  • - {item.mediaType.includes( - 'text/css' - ) ? ( - - ) : ( - - )} +
  • +
  • ) )} @@ -205,9 +259,20 @@ export function ScriptForm({ job, script }: { job: Job; script: Script }) { )}
- + {isBrailleJob && job.stylesheetParameters != null && ( + + )} + {isBrailleJob && job.stylesheetParameters == null ? ( + + ) : ( + + )}