Skip to content

Instantly share code, notes, and snippets.

@huang47
Created December 20, 2022 21:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save huang47/764f969b2ec9b58b01292571e98c0b1b to your computer and use it in GitHub Desktop.
Save huang47/764f969b2ec9b58b01292571e98c0b1b to your computer and use it in GitHub Desktop.
contract state machine
import {
createMachine,
assign,
actions as XActions,
EventObject,
} from 'xstate';
const { choose } = XActions;
interface ContractContext {
version: string;
partnerHasExistingContracts: boolean;
partner: string;
metadata: Record<string, unknown>;
updatedAt: number;
}
const context: ContractContext = {
version: '0',
partnerHasExistingContracts: true,
partner: 'foo',
metadata: {},
updatedAt: Date.now(),
};
const CREATE_CONTRACT_MACHINE_ID = 'createContract';
const guards = {
partnerHasNoContracts: ({ partnerHasExistingContracts }: ContractContext) =>
partnerHasExistingContracts,
partnerHasExistingContracts: ({
partnerHasExistingContracts,
}: ContractContext) => !partnerHasExistingContracts,
isDraft: (_, __, meta) => {
const isDraft =
meta.state.value?.creatingNewContract?.creation === 'init' &&
meta.state.value?.creatingNewContract?.lifecycle === 'draft';
console.log(meta.state.value, isDraft);
return isDraft;
},
};
const actions = {
selectPartner: assign<ContractContext>({
partner: (context: ContractContext, event: EventObject) =>
event?.data?.partner || '',
}),
setMetadata: assign<ContractContext>({
metadata: (context: ContractContext, event: EventObject) =>
event?.data?.metadata || {},
}),
toggleHasContract: assign<ContractContext>({
partnerHasExistingContracts: (context: ContractContext) =>
!context.partnerHasExistingContracts,
}),
bumpVersion: assign<ContractContext>({
version: (context: ContractContext) =>
`${parseInt(context.version, 10) + 1}`,
}),
updateUpdatedAt: assign<ContractContext>({
updatedAt: (context: ContractContext) => Date.now(),
}),
};
const contractLifecycleDefinition = {
id: 'contractLifecycle',
initial: 'draft',
states: {
draft: {
on: {
review: { target: 'inReview' },
delete: { target: 'deleted' },
},
},
inReview: {
on: {
activate: { target: 'active' },
revoke: { target: 'draft' },
},
},
active: {
on: {
delete: { target: 'deleted' },
expire: { target: 'expired' },
supersede: { target: 'superceded' },
},
},
deleted: {},
expired: {
type: 'final',
},
superceded: {
type: 'final',
},
},
};
const createContractStateDefinition = {
initial: 'init',
states: {
init: {
on: {
SET_METADATA: {
target: 'metadataSet',
actions: ['setMetadata'],
},
BACK: {
target: `#${CREATE_CONTRACT_MACHINE_ID}.init`,
actions: ['updateUpdatedAt'],
},
},
},
metadataSet: {
on: {
SET_DATES: 'datesSet',
BACK: 'init',
},
},
datesSet: {
on: {
SET_OBLIGATION_TYPES: 'obligationTypesSet',
BACK: 'metadataSet',
},
},
obligationTypesSet: {
on: {
SET_OBLIGATION_DETAILS: 'obligationDetailsSet',
BACK: 'datesSet',
},
},
obligationDetailsSet: {
on: {
CONFIRM: 'confirmed',
BACK: 'obligationTypesSet',
},
},
confirmed: {
type: 'final',
},
},
onDone: `#${CREATE_CONTRACT_MACHINE_ID}.success`,
};
const definitions = {
predictableActionArguments: true,
id: CREATE_CONTRACT_MACHINE_ID,
initial: 'init',
context,
states: {
init: {
on: {
SELECT_PARTNER: {
target: 'partnerSelected',
actions: ['selectPartner'],
},
},
},
partnerSelected: {
always: [
{
target: 'creatingNewContract',
cond: 'partnerHasNoContracts',
actions: ['updateUpdatedAt'],
},
],
},
creatingNewContract: {
type: 'parallel',
states: {
creation: { ...createContractStateDefinition },
lifecycle: { ...contractLifecycleDefinition },
},
},
success: {
type: 'final',
},
},
on: {
TOGGLE_FLOW: {
target: 'init',
actions: ['toggleHasContract'],
},
},
};
const options = {
guards,
actions,
};
const createContractMachine = createMachine<ContractContext>(
definitions,
options
);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment