Skip to content

Commit

Permalink
feat: movable steps
Browse files Browse the repository at this point in the history
  • Loading branch information
Alorel committed Nov 25, 2022
1 parent 0aaf3ad commit 26c694e
Show file tree
Hide file tree
Showing 11 changed files with 360 additions and 84 deletions.
6 changes: 6 additions & 0 deletions src/lib/execution/workflow-event.mts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import type {Workflow} from '../data/workflow.mjs';
export const enum WorkflowEventType {
ACTION_COMPLETE,
STEP_LISTENING,
STEP_NOT_LISTENING,
STEP_COMPLETE,
WORKFLOW_START,
WORKFLOW_COMPLETE,
Expand All @@ -14,6 +15,7 @@ export type WorkflowEvent = WorkflowCompleteEvent
| WorkflowStartEvent
| ActionExecutionEvent
| StepListeningEvent
| StepNotListeningEvent
| StepCompleteEvent;

export type WorkflowCompleteEvent = WorkflowEventBase & Result<void> & {
Expand All @@ -32,6 +34,10 @@ export interface StepListeningEvent extends StepEventBase {
type: WorkflowEventType.STEP_LISTENING;
}

export interface StepNotListeningEvent extends StepEventBase {
type: WorkflowEventType.STEP_NOT_LISTENING;
}

export type ActionExecutionEvent = Result<void> & Ref & {
type: WorkflowEventType.ACTION_COMPLETE;
}
Expand Down
45 changes: 41 additions & 4 deletions src/lib/execution/workflow-execution.mts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
scheduled,
startWith,
takeUntil,
tap,
throwError
} from 'rxjs';
import {switchMap, take} from 'rxjs/operators';
Expand Down Expand Up @@ -44,11 +45,15 @@ export class WorkflowExecution extends ShareReplayLike<Out> {

private readonly _activeStepIdx$ = new BehaviorSubject<number>(0);

private incrementActiveStep = true;

private mainSub?: Subscription;

private readonly mainSubObserver: Observer<Out> = {
complete: () => {
++this.activeStepIdx;
if (this.incrementActiveStep) {
++this.activeStepIdx;
}
this.mainSub = asapScheduler.schedule(this.tick);
},
error: (e: Error) => {
Expand All @@ -74,7 +79,7 @@ export class WorkflowExecution extends ShareReplayLike<Out> {
return this._activeStepIdx$.value;
}

public set activeStepIdx(v: number) {
private set activeStepIdx(v: number) {
if (v === this.activeStepIdx) {
return;
}
Expand All @@ -85,6 +90,12 @@ export class WorkflowExecution extends ShareReplayLike<Out> {
return this.workflow.steps[this.activeStepIdx];
}

public setActiveStepIdx(v: number): void {
this.incrementActiveStep = false;
this.activeStepIdx = v;
this.incrementActiveStep = true;
}

/** @inheritDoc */
protected override cleanup(): void {
this.mainSub?.unsubscribe();
Expand Down Expand Up @@ -201,13 +212,39 @@ export class WorkflowExecution extends ShareReplayLike<Out> {
});
});

return concat(exec$, onSuccess$).pipe(
this.whileStepIs(idx),
const src$: Observable<Out> = concat(exec$, onSuccess$).pipe(
startWith<StepListeningEvent>({
...baseEvt,
type: WorkflowEventType.STEP_LISTENING,
})
);

return this
.ifIncomplete(src$, {
...baseEvt,
type: WorkflowEventType.STEP_NOT_LISTENING,
})
.pipe(this.whileStepIs(idx));
}

private ifIncomplete<V>(src: Observable<V>, thenEmitEvent: WorkflowEvent): Observable<V> {
return new Observable<V>(subscriber => {
let completed = false;
const flagComplete = (): void => {
completed = true;
};
const sub = src
.pipe(tap({complete: flagComplete, error: flagComplete}))
.subscribe(subscriber);

return () => {
if (!completed) {
this.next(thenEmitEvent);
}

sub.unsubscribe();
};
});
}

private mkCompleteEvent(ok: true): WorkflowCompleteEvent;
Expand Down
4 changes: 4 additions & 0 deletions src/lib/registries/workflow-registry.mts
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,10 @@ export default class WorkflowRegistry {
this.workflows$.next(out);
}

public save(): void {
store(this.workflows);
}

public setPrimaryExecution(workflow?: Workflow): void {
if (workflow?.listId !== this.primaryExecution$.value?.workflow.listId) {
this.primaryExecution$.next(workflow ? new WorkflowExecution(workflow) : undefined);
Expand Down
11 changes: 9 additions & 2 deletions src/setup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ import {TRIGGER_REGISTRY} from './lib/registries/trigger-registry.mjs';
import {errorLog} from './lib/util/log.mjs';
import './option-types/option-types.mjs';
import './triggers/index.mjs';
import SidenavIcon from './ui/components/sidenav-icon';
import makePageContainer from './ui/make-page-container.mjs';
import NewWorkflow, {NEW_WORKFLOW_PAGE_ID} from './ui/pages/new-workflow';
import WorkflowsDashboard, {WORKFLOWS_DASHBOARD_ID} from './ui/pages/workflows-dashboard';
import {SIDENAV_ITEM} from './ui/sidebar-mgr.mjs';
import {mainIcon} from './ui/ui.mjs';

// ctx.api<Readonly<Api>>(api); // rollup will freeze it
Expand All @@ -28,10 +30,15 @@ ctx.onInterfaceAvailable(() => {
});

ctx.onInterfaceReady(() => {
sidebar
.category('')
sidebar.category('')
.item('Action Workflows', {
after: 'melvorD:Bank',
icon: mainIcon,
});

const iconContainer = SIDENAV_ITEM.value.iconEl;
iconContainer.classList.remove('nav-img');
iconContainer.innerHTML = '';

render(<SidenavIcon/>, iconContainer);
});
8 changes: 6 additions & 2 deletions src/ui/components/btn.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
import type {VNode} from 'preact';
import type {HTMLAttributes} from 'preact/compat';
import type {Ref} from 'preact/hooks';

interface Props extends Omit<HTMLAttributes<HTMLButtonElement>, 'kind' | 'size'> {
btnRef?: Ref<HTMLButtonElement>;

kind?: string;

size?: string;
}
export {Props as BtnProps};

export default function Btn({kind, size, ...rest}: Props): VNode {
export default function Btn({btnRef, kind, size, ...rest}: Props): VNode {
let clazz = 'btn';
if (kind) {
clazz += ` btn-outline-${kind}`;
Expand All @@ -16,5 +20,5 @@ export default function Btn({kind, size, ...rest}: Props): VNode {
clazz += ` btn-${size}`;
}

return <button type={'button'} className={clazz} {...rest}></button>;
return <button type={'button'} className={clazz} ref={btnRef} {...rest}></button>;
}
29 changes: 29 additions & 0 deletions src/ui/components/sidenav-icon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import {distinctWithInitial} from '@aloreljs/rxutils/operators';
import {memo} from 'preact/compat';
import {useEffect, useState} from 'preact/hooks';
import {map, skip} from 'rxjs';
import WorkflowRegistry from '../../lib/registries/workflow-registry.mjs';
import {EMPTY_ARR} from '../../lib/util.mjs';
import {PauseSvg, PlaySvg} from './svg';

const SidenavIcon = memo(() => {
const [running, setRunning] = useState(false);
useEffect(() => {
const sub = WorkflowRegistry.inst.primaryExecution$
.pipe(
map(Boolean),
distinctWithInitial(running),
skip(1)
)
.subscribe(setRunning);
return () => {
sub.unsubscribe();
};
}, EMPTY_ARR);

const Comp = running ? PlaySvg : PauseSvg;

return <Comp class={'nav-img'}/>;
});

export default SidenavIcon;
33 changes: 31 additions & 2 deletions src/ui/components/svg.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type {VNode} from 'preact';
import type {FunctionComponent} from 'preact/compat';
import type {JSXInternal} from 'preact/src/jsx';

type SvgProps = Omit<BaseProps, 'viewBox'>;
type SvgProps = Omit<BaseProps, 'viewBox' | 'children'>;

type ChevronProps = Omit<SvgProps, 'd'>;
export const ChevronLeftSvg = staticComponent<ChevronProps>(props => (
Expand All @@ -21,7 +21,8 @@ const ChevronBase: FunctionComponent<ChevronProps & {d: string}> = ({d, ...rest}
</BaseSvg>
);

export const BinSvg = staticComponent<Omit<SvgProps, 'xmlSpace'>>(props => (
type NoSpaceProps = Omit<SvgProps, 'xmlSpace'>;
export const BinSvg = staticComponent<NoSpaceProps>(props => (
<BaseSvg xmlSpace={'preserve'} viewBox={'0 0 512 512'} {...props}>
<path fill={'#6c994e'} d={'M314.412,113.127H197.588c-10.886,0-19.711-8.825-19.711-19.711v-38.72\tC177.877,24.536,202.413,0,232.573,0h46.854c30.159,0,54.697,24.536,54.697,54.695v38.719\tC334.123,104.302,325.298,113.127,314.412,113.127z M217.299,73.705h77.401V54.695c0-8.422-6.852-15.273-15.274-15.273h-46.854\tc-8.422,0-15.273,6.852-15.273,15.273L217.299,73.705L217.299,73.705z'}/>
<path fill={'#7eb35b'} d={'M345.472,72.19H166.528c-31.038,0-56.198,25.16-56.198,56.198v22.857h291.341v-22.857\tC401.67,97.35,376.51,72.19,345.472,72.19z'}/>
Expand All @@ -33,6 +34,34 @@ export const BinSvg = staticComponent<Omit<SvgProps, 'xmlSpace'>>(props => (
</BaseSvg>
));

export const TargetSvg = staticComponent<NoSpaceProps>(props => (
<BaseSvg xmlSpace={'preserve'} viewBox={'0 0 512 512'} {...props}>
<path fill={'#bfe4f8'} d={'M256,42.667C138.368,42.667,42.667,138.368,42.667,256S138.368,469.333,256,469.333\tS469.333,373.632,469.333,256S373.632,42.667,256,42.667z M256,432.762c-97.467,0-176.762-79.295-176.762-176.762\tS158.533,79.238,256,79.238S432.762,158.533,432.762,256S353.467,432.762,256,432.762z'}/>
<path fill={'#ff5023'} d={'M207 238H305V275H207z'}/>
<path fill={'#802812'} d={'M256 238H305V275H256z'}/>
<path fill={'#93c7ef'} d={'M256,42.667v36.571c97.467,0,176.762,79.295,176.762,176.762S353.467,432.762,256,432.762v36.571\tc117.632,0,213.333-95.701,213.333-213.333S373.632,42.667,256,42.667z'}/>
<path fill={'#ff5023'} d={'M238 0H275V134H238zM0 238H134V275H0zM238 378H275V512H238z'}/>
<path fill={'#802812'} d={'M256 0H274V134H256zM256 378H274V512H256zM378 238H512V275H378z'}/>
</BaseSvg>
));

export const PlaySvg = staticComponent<NoSpaceProps>(props => (
<BaseSvg xmlSpace={'preserve'} viewBox={'0 0 58 58'} {...props}>
<circle cx={29} cy={29} r={29} fill={'#30c78d'}/>
<g fill={'#fff'}>
<path d={'M44 29 22 44 22 29 22 14z'}/>
<path d={'M22,45c-0.16,0-0.321-0.038-0.467-0.116C21.205,44.711,21,44.371,21,44V14 c0-0.371,0.205-0.711,0.533-0.884c0.328-0.174,0.724-0.15,1.031,0.058l22,15C44.836,28.36,45,28.669,45,29s-0.164,0.64-0.437,0.826 l-22,15C22.394,44.941,22.197,45,22,45z M23,15.893v26.215L42.225,29L23,15.893z'}/>
</g>
</BaseSvg>
));

export const PauseSvg = staticComponent<NoSpaceProps>(props => (
<BaseSvg xmlSpace={'preserve'} viewBox={'0 0 496 496'} {...props}>
<path fill={'#e56767'} d={'M496.158,248.085c0-137.021-111.07-248.082-248.076-248.082C111.07,0.002,0,111.062,0,248.085\tc0,137.002,111.07,248.071,248.083,248.071C385.088,496.155,496.158,385.086,496.158,248.085z'}/>
<path fill={'#fff'} d={'M223 121h-60c-6 0-10 4-10 10v234c0 6 4 10 10 10h60c6 0 10-4 10-10V131C233 126 229 121 223 121zM333 121h-60c-6 0-10 4-10 10v234c0 6 4 10 10 10h60c6 0 10-4 10-10V131C343 126 339 121 333 121z'}/>
</BaseSvg>
));

type BaseProps = Omit<JSXInternal.SVGAttributes<SVGSVGElement>, 'xmlns'>;
function BaseSvg(props: BaseProps): VNode {
return <svg xmlns={'http://www.w3.org/2000/svg'} style={'width:1rem;height:1rem'} {...props}/>;
Expand Down
Loading

0 comments on commit 26c694e

Please sign in to comment.