import {combine, state, StateStream} from 'lstream'; import initializeBySchema from '../schema/initializeBySchema'; import {clone} from 'gems/objects'; import materializeParams from '../schema/materializeParams'; import {createFunctionList} from 'gems/func'; import {CraftHistory, OperationRequest} from "cad/craft/craftPlugin"; import { NewOperationCall, ParamsPath, ValueUpdater, WizardService, WizardState, WorkingRequest } from "cad/craft/wizard/wizardTypes"; import _ from "lodash"; import {OperationParamValue} from "cad/craft/schema/schema"; import {ApplicationContext} from "context"; import {Operation} from "cad/craft/operationPlugin"; import produce from "immer" export function activate(ctx: ApplicationContext) { let {streams, services} = ctx; const insertOperation$ = state(null); let REQUEST_COUNTER = 1; const workingRequest$: StateStream = combine<[NewOperationCall, CraftHistory]>( insertOperation$, ctx.craftService.modifications$ ).map(([insertOperationReq, mods]) => { let operation; let params; if (insertOperationReq !== null) { operation = ctx.operationService.get(insertOperationReq.type); params = initializeBySchema(operation.schema, ctx); if (insertOperationReq.initialOverrides) { applyOverrides(params, insertOperationReq.initialOverrides); } return { type: operation.id, params, requestKey: REQUEST_COUNTER++ } } else { const {pointer, history, hints} = mods if (pointer !== history.length - 1) { let {type, params} = history[pointer + 1]; return { type, params: clone(params), hints, requestKey: REQUEST_COUNTER++ }; } else { return null; } } }).remember(null); const materializedWorkingRequest$ = workingRequest$.map(req => { if (req == null) { return null; } let params = {}; let errors = []; let operation = ctx.services.operation.get(req.type); materializeParams(ctx, req.params, operation.schema, params, errors); if (errors.length !== 0) { return null; } return { type: req.type, params }; }).remember(null).filter(r => r !== null).throttle(500); const state$ = state({}); let disposerList = createFunctionList(); // reset effect workingRequest$.pairwise().attach(([old, curr]) => { if (old !== null && old.requestKey !== curr?.requestKey) { disposerList.call(); disposerList = createFunctionList(); state$.next({}); } }) const updateParams = mutator => workingRequest$.update((req: WorkingRequest) => produce(req, draft => { mutator(draft.params) })); const updateState = mutator => state$.update((state: WizardState) => produce(state, draft => { mutator(draft); })); const updateParam = (path: ParamsPath, value: OperationParamValue) => { updateParams(params => { // if (operation.onParamsUpdate) { // operation.onParamsUpdate(params, name, value, params[name]); // } if (!Array.isArray(path)) { path = [path] } _.set(params, path, value); }); }; const readParam = (path: ParamsPath) => { return _.get(workingRequest$.value.params, path); }; const getWorkingRequest = () => workingRequest$.value; //legacy streams.wizard = { insertOperation: insertOperation$, }; const cancel = () => { insertOperation$.next(null); }; const wizardService: WizardService = { open: (type: string, initialOverrides: NewOperationCall) => { streams.wizard.insertOperation.next({ type, initialOverrides }); }, cancel, applyWorkingRequest: () => { let {type, params} = getWorkingRequest(); let request = clone({type, params}); const setError = error => updateState(state => state.error = error); if (insertOperation$.value) { ctx.craftService.modify(request, cancel, setError); } else { ctx.craftService.modifyInHistoryAndStep(request, () => {}, setError); } }, isInProgress: () => getWorkingRequest() !== null, get workingRequest() { return getWorkingRequest(); }, get operation(): Operation { const req = getWorkingRequest(); if (!req) { return null; } return ctx.operationService.get(req.type); }, workingRequest$, materializedWorkingRequest$, state$, updateParams, updateParam, readParam, updateState, addDisposer: (disposer) => disposerList.add(disposer) }; ctx.wizardService = services.wizard = wizardService; } export interface WizardPluginContext { wizardService: WizardService } declare module 'context' { interface ApplicationContext extends WizardPluginContext { } } function applyOverrides(params, initialOverrides) { Object.assign(params, initialOverrides); }