Skip to content

Commit

Permalink
feat: One-off workflows
Browse files Browse the repository at this point in the history
Closes #74
  • Loading branch information
Alorel committed Dec 3, 2022
1 parent cdf6b93 commit 06b4b42
Show file tree
Hide file tree
Showing 5 changed files with 93 additions and 32 deletions.
8 changes: 6 additions & 2 deletions src/lib/data/workflow.mts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {JsonProp, Serialisable} from '../decorators/to-json.mjs';
import type {ReadonlyBehaviorSubject} from '../registries/workflow-registry.mjs';
import {WorkflowStep} from './workflow-step.mjs';

type Init = Partial<Pick<Workflow, 'name' | 'steps'>>;
type Init = Partial<Pick<Workflow, 'name' | 'rm' | 'steps'>>;

export interface WorkflowJson extends Pick<Workflow, 'name'> {
steps: CompressedJsonArray<WorkflowStep>;
Expand All @@ -33,12 +33,16 @@ export class Workflow {
@JsonProp()
public name: string;

@JsonProp()
public rm: boolean;

public readonly steps$: ReadonlyBehaviorSubject<WorkflowStep[]>;

private readonly _steps$: BehaviorSubject<WorkflowStep[]>;

public constructor({name, steps}: Init = {}) {
public constructor({name, rm, steps}: Init = {}) {
this.name = name ?? '';
this.rm = rm ?? false;
this.steps$ = this._steps$ = new BehaviorSubject<WorkflowStep[]>(steps?.length ? steps : [new WorkflowStep()]);
}

Expand Down
7 changes: 6 additions & 1 deletion src/lib/execution/workflow-execution.mts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import type {WorkflowStep} from '../data/workflow-step.mjs';
import type {Workflow} from '../data/workflow.mjs';
import AutoIncrement from '../decorators/auto-increment.mjs';
import PersistClassName from '../decorators/PersistClassName.mjs';
import WorkflowRegistry from '../registries/workflow-registry.mjs';
import {debugLog, errorLog} from '../util/log.mjs';
import prependErrorWith from '../util/rxjs/prepend-error-with.mjs';
import ShareReplayLike from '../util/share-replay-like-observable.mjs';
Expand Down Expand Up @@ -176,7 +177,11 @@ export class WorkflowExecution extends ShareReplayLike<Out> {
}
}

this.patchOnInitEventsOnEnd();
if (this.workflow.rm) {
WorkflowRegistry.inst.rmByListId(this.workflow.listId);
} else {
this.patchOnInitEventsOnEnd();
}
}

/**
Expand Down
5 changes: 5 additions & 0 deletions src/lib/registries/workflow-registry.mts
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,11 @@ export default class WorkflowRegistry {
this._workflows$.next(out);
}

public rmByListId(listId: number): void {
const idx = this.workflows.findIndex(w => w.listId === listId);
this.rmByIdx(idx);
}

/** Save the workflows to mod storage */
public save(): void {
store(this.workflows);
Expand Down
90 changes: 61 additions & 29 deletions src/ui/components/workflow-editor/header-block.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type {Signal} from '@preact/signals';
import {useSignal} from '@preact/signals';
import {memo} from 'preact/compat';
import {useCallback} from 'preact/hooks';
Expand All @@ -12,48 +13,79 @@ export interface WorkflowEditorHeaderBlockProps {
onSave(e: Event): void;
}

const WorkflowEditorHeaderBlock = memo<WorkflowEditorHeaderBlockProps>(({
children,
onSave,
}) => {
const WorkflowEditorHeaderBlock = memo<WorkflowEditorHeaderBlockProps>(({children, onSave}) => (
<div className={EDITOR_SECTION_CLASS}>
<BorderedBlock kind={'agility'} size={2}>
<WorkflowNameEditor/>
<WorkflowRemovableEditor/>

<div className={'text-right mt-3'}>
{children}
<Btn kind={'success'} size={'sm'} onClick={onSave}>{'Save'}</Btn>
</div>
</BorderedBlock>
</div>
));
WorkflowEditorHeaderBlock.displayName = 'WorkflowEditorHeaderBlock';

export default WorkflowEditorHeaderBlock;

const WorkflowNameEditor = memo(function WorkflowNameEditor() {
const [touched$, onBlur] = useTouched();
const workflow$ = useWorkflow();
const inpTouched = useSignal(false);
const reRender = useReRender();

const onBlur = useCallback(() => {
inpTouched.value = true;
}, EMPTY_ARR);
const onChange = useCallback((e: Event): void => {
workflow$.peek().name = (e.target as HTMLInputElement).value;
reRender();
}, [workflow$]);

const workflow = workflow$.value;
const name = workflow$.value.name;

return (
<div className={EDITOR_SECTION_CLASS}>
<BorderedBlock kind={'agility'} size={2}>
<div className={mkClass('row mb-3', inpTouched.value && 'ActionWorkflowsCore-touched')}>
<span className={'font-size-sm pr-1 col-auto'}>{'Workflow name'}</span>
<div className={mkClass('col-auto', !workflow.name.trim() && 'ActionWorkflowsCore-f-invalid')}>
<input className={'form-control form-control-sm'}
value={workflow.name}
onBlur={onBlur}
onInput={onChange}
required/>
</div>
</div>

<div className={'text-right'}>
{children}
<Btn kind={'success'} size={'sm'} onClick={onSave}>{'Save'}</Btn>
</div>
</BorderedBlock>
<div className={mkClass('row', touched$.value && 'ActionWorkflowsCore-touched')}>
<span className={'font-size-sm pr-1 col-auto'}>{'Workflow name'}</span>
<div className={mkClass('col-auto', !name.trim() && 'ActionWorkflowsCore-f-invalid')}>
<input className={'form-control form-control-sm'}
value={name}
onBlur={onBlur}
onInput={onChange}
required/>
</div>
</div>
);
});
WorkflowEditorHeaderBlock.displayName = 'WorkflowEditorHeaderBlock';

export default WorkflowEditorHeaderBlock;
const WorkflowRemovableEditor = memo(function WorkflowRemovableEditor() {
const workflow$ = useWorkflow();
const reRender = useReRender();

const onChange = useCallback((e: Event): void => {
workflow$.peek().rm = (e.target as HTMLInputElement).checked;
reRender();
}, [workflow$]);

return (
<div class={'row mt-1'}>
<label class={'col-auto font-weight-normal'}>
<input type={'checkbox'}
class={'mr-1'}
checked={workflow$.value.rm}
onChange={onChange}
required/>
<span>Delete workflow on completion</span>
</label>
</div>
);
});

function useTouched(): [Signal<boolean>, () => void] {
const touched$ = useSignal(false);
const onBlur = useCallback(() => {
touched$.value = true;
}, EMPTY_ARR);

return [touched$, onBlur];
}


15 changes: 15 additions & 0 deletions src/ui/pages/workflows-dashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,19 @@ export default function WorkflowsDashboard(): VNode {
);
}

function useWorkflowRemovalCorrector(
workflows: Workflow[],
activeWorkflow$: Signal<Workflow | undefined>
): void {
const activeId = activeWorkflow$.value?.listId;

useEffect(() => {
if (activeId !== undefined && !workflows.some(wf => wf.listId === activeId)) {
activeWorkflow$.value = undefined;
}
}, [activeId, workflows]);
}

interface EditorProps {
workflows: Workflow[];
}
Expand All @@ -89,6 +102,8 @@ function DashboardShell({workflows}: EditorProps): VNode {
};
});

useWorkflowRemovalCorrector(workflows, activeWorkflow);

return (
<ProvideActiveStepIdx>
<ProvideWorkflow>
Expand Down

0 comments on commit 06b4b42

Please sign in to comment.