Last active
October 16, 2021 12:30
-
-
Save pyoner/da38cab7da0a52f6c3b3386564217828 to your computer and use it in GitHub Desktop.
Generated by XState Viz: https://xstate.js.org/viz
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
interface Event { | |
type: string | |
executor: 'client' | 'provider' | 'admin' | 'validator' | |
sender: 'admin' | 'client' | 'provider' | |
} | |
**/ | |
const guards = { | |
// executors | |
isAdmin: (_, {executor}) => executor === 'admin', | |
isValidator: (_, {executor}) => executor === 'validator', | |
isClient: (_, {executor}) => executor === 'client', | |
isProvider: (_, {executor}) => executor === 'provider', | |
// sender / validator roles | |
asAdmin: (_, {sender}) => sender === 'admin', | |
asClient: (_, {sender}) => sender === 'client', | |
asProvider: (_, {sender}) => sender === 'provider', | |
canCloseJob: (context, event, meta) => !meta.state.matches('fundsInEscrow'), | |
canStartDispute: (context, event, meta) => !meta.state.matches('dispute'), | |
canAcceptOffer: ({offer}, {sender}) => offer.from !== sender, | |
canMakeCounterOffer: ({offer}, {sender}) => offer.from !== sender, | |
} | |
const proxyGuards = new Proxy(guards, { | |
get (target, prop) { | |
if (prop in target) { | |
return target[prop] | |
} | |
return (ctx, event, meta) => meta.cond.guards.every(key => target[key](ctx, event, meta)) | |
} | |
}) | |
const every = (...fn_names) => { | |
const name = fn_names.join(', ') | |
return { | |
type: name, | |
guards: fn_names | |
} | |
} | |
const asyncState = (src, onDone, onError = 'err') => { | |
const err = onError === 'err' ? { | |
on: { | |
RETRY: 'await' | |
} | |
} : null; | |
const state = { | |
initial: 'await', | |
states: { | |
await: { | |
invoke: { | |
src, | |
onDone, | |
onError | |
} | |
} | |
} | |
} | |
if (err) { | |
state.states.err = err | |
} | |
return state; | |
} | |
const jobMachine = Machine({ | |
id: 'job', | |
initial: 'init', | |
states: { | |
init: { | |
meta: { | |
DRAFT: 'Create a job draft', | |
PUBLISH: 'Publish a job' | |
}, | |
on: { | |
DRAFT: [{ | |
target: 'asyncDraft', | |
cond: 'isClient' | |
}, { | |
target: 'draft', | |
cond: every('isValidator', 'asClient') | |
}], | |
PUBLISH: [{ | |
target: 'asyncPublish', | |
cond: 'isClient' | |
}, { | |
target: 'public', | |
cond: every('isValidator', 'asClient') | |
}], | |
} | |
}, | |
asyncDraft: asyncState('jobDraft', '#draft'), | |
draft: { | |
id: 'draft', | |
meta: { | |
CLOSE: 'Close the job', | |
PUBLISH: 'Publish the job' | |
}, | |
on: { | |
PUBLISH: [{ | |
target: 'asyncPublish', | |
cond: 'isClient' | |
}, { | |
target: '#public', | |
cond: every('isValidator', 'asClient') | |
}], | |
CLOSE: [{ | |
target: '#job.asyncClose', | |
cond: 'isClient' | |
}, { | |
target: '#closed', | |
cond: every('isValidator', 'asClient') | |
}], | |
} | |
}, | |
asyncPublish: asyncState('jobPublish', '#public'), | |
public: { | |
id: 'public', | |
initial: 'init', | |
meta: { | |
CLOSE: 'Close the job', | |
}, | |
on: { | |
CLOSE: [{ | |
target: '#job.asyncClose', | |
cond: every('isClient', 'canCloseJob') | |
}, { | |
target: '#closed', | |
cond: every('isValidator', 'asClient', 'canCloseJob') | |
}], | |
}, | |
states: { | |
init: { | |
meta: { | |
BID: 'Make a bid', | |
}, | |
on: { | |
BID: [{ | |
target: 'asyncBid', | |
cond: 'isProvider' | |
}, { | |
target: 'bid', | |
cond: every('isValidator', 'asProvider') | |
}], | |
} | |
}, | |
asyncBid: asyncState('makeBid', '#public.bid'), | |
bid: { | |
meta: { | |
ACCEPT: 'Accept the bid', | |
}, | |
on: { | |
ACCEPT: [{ | |
target: 'asyncOffer', | |
cond: 'isClient' | |
}, { | |
target: '#offer', | |
cond: every('isValidator', 'asClient') | |
}] | |
} | |
}, | |
asyncOffer: asyncState('jobOffer', '#offer'), | |
offer: { | |
id: 'offer', | |
initial: 'init', | |
meta: { | |
ACCEPT: 'Accept the offer', | |
COUNTER_OFFER: 'Make a counter offer' | |
}, | |
states: { | |
init: { | |
on: { | |
ACCEPT: [{ | |
target: 'asyncAcceptOffer', | |
cond: every('isProvider', 'canAcceptOffer') | |
}, { | |
target: 'asyncAcceptOffer', | |
cond: every('isClient', 'canAcceptOffer') | |
}, { | |
target: '#public.offer.accepted', | |
cond: every('isValidator', 'asProvider', 'canAcceptOffer') | |
}, { | |
target: '#public.offer.accepted', | |
cond: every('isValidator', 'asClient', 'canAcceptOffer') | |
}], | |
COUNTER_OFFER: [{ | |
target: '#public.asyncOffer', | |
cond: every('isClient', 'canMakeCounterOffer') | |
}, { | |
target: '#public.asyncOffer', | |
cond: every('isProvider', 'canMakeCounterOffer') | |
}, { | |
cond: every('isValidator', 'asProvider', 'canMakeCounterOffer') | |
}, { | |
cond: every('isValidator', 'asClient', 'canMakeCounterOffer') | |
}] | |
}, | |
}, | |
asyncAcceptOffer: asyncState('acceptOffer', '#offer.accepted'), | |
accepted: { | |
initial: 'init', | |
states: { | |
init: { | |
meta: { | |
MAKE_ESCROW: 'Please make escrow', | |
}, | |
on: { | |
MAKE_ESCROW: [{ | |
cond: 'isClient', | |
target: 'asyncDeclareFundsInEscrow' | |
}, { | |
cond: every('isValidator', 'asClient'), | |
target: 'declareFundsInEscrow' | |
}] | |
} | |
}, | |
asyncDeclareFundsInEscrow: asyncState('makeEscrow', '#offer.accepted.declareFundsInEscrow'), | |
declareFundsInEscrow: { | |
initial: 'init', | |
states: { | |
init: { | |
meta: { | |
CHECK: 'Click to check the funds in the escrow', | |
}, | |
on: { | |
CHECK: [{ | |
target: 'checking', | |
cond: 'isValidator' | |
}, { | |
target: 'asyncChecking', | |
cond: 'isClient' | |
}, { | |
target: 'asyncChecking', | |
cond: 'isProvider' | |
}] | |
} | |
}, | |
asyncChecking: asyncState('checkEscrow', '#offer.accepted.fundsInEscrow.init'), | |
checking: { | |
invoke: { | |
src: 'checkEscrow', | |
onDone: '#offer.accepted.fundsInEscrow', | |
onError: '#offer.accepted.declareFundsInEscrow.init' | |
} | |
}, | |
} | |
}, | |
fundsInEscrow: { | |
initial: 'init', | |
meta: { | |
DISPUTE: 'Open a dispute', | |
}, | |
on: { | |
DISPUTE: [{ | |
target: '.asyncDispute', | |
cond: every('isClient', 'canStartDispute') | |
}, { | |
target: '.asyncDispute', | |
cond: every('isProvider', 'canStartDispute') | |
}, { | |
target: '.dispute', | |
cond: every('isValidator', 'asClient', 'canStartDispute') | |
}, { | |
target: '.dispute', | |
cond: every('isValidator', 'asProvider', 'canStartDispute') | |
}] | |
}, | |
states: { | |
init: { | |
meta: { | |
JOB_COMPLETE: 'Click to complete the job', | |
}, | |
on: { | |
JOB_COMPLETE: [{ | |
target: 'pendingCompletion', | |
cond: every('isValidator', 'asProvider') | |
}, { | |
target: 'pendingCompletion', | |
cond: 'isProvider' | |
}] | |
} | |
}, | |
asyncDispute: asyncState('startDispute', '#offer.accepted.fundsInEscrow.dispute'), | |
dispute: { | |
meta: { | |
RELEASE_ESCROW: 'Would you like to release the escrow', | |
REFUND_ESCROW: 'Return back the funds to a client' | |
}, | |
on: { | |
RELEASE_ESCROW: [{ | |
target: 'escrow.released', | |
cond: every('isValidator', 'asAdmin') | |
}, { | |
target: 'asyncReleaseEscrow', | |
cond: 'isAdmin' | |
}], | |
REFUND_ESCROW: [{ | |
target: 'escrow.refunded', | |
cond: every('isValidator', 'asAdmin') | |
}, { | |
target: 'asyncRefundEscrow', | |
cond: 'isAdmin' | |
}] | |
} | |
}, | |
asyncReleaseEscrow: asyncState('releaseEscrow', '#offer.accepted.fundsInEscrow.escrow.released'), | |
asyncRefundEscrow: asyncState('refundEscrow', '#offer.accepted.fundsInEscrow.escrow.refunded'), | |
escrow: { | |
meta: { | |
REVIEW: 'Please make a review', | |
}, | |
on: { | |
REVIEW: [{ | |
target: 'review', | |
cond: every('isValidator', 'asClient') | |
}, { | |
target: 'review', | |
cond: every('isValidator', 'asProvider') | |
}, { | |
target: 'asyncReview', | |
cond: 'isClient' | |
}, { | |
target: 'asyncReview', | |
cond: 'isProvider' | |
}] | |
}, | |
states: { | |
released: {}, | |
refunded: {} | |
} | |
}, | |
asyncPendingCompletion: asyncState('jobComplete', '#offer.accepted.fundsInEscrow.pendingCompletion'), | |
pendingCompletion: { | |
meta: { | |
RELEASE_ESCROW: 'Would you like to release the escrow', | |
}, | |
on: { | |
RELEASE_ESCROW: [{ | |
target: 'escrow.released', | |
cond: every('isValidator', 'asClient') | |
}, { | |
target: 'asyncReleaseEscrow', | |
cond: 'isClient' | |
}] | |
} | |
}, | |
asyncReview: asyncState('jobReview', '#offer.accepted.fundsInEscrow.review'), | |
review: { | |
type: 'parallel', | |
entry: raise('REVIEW'), | |
meta: { | |
REVIEW: 'Review', | |
}, | |
on: { | |
REVIEW: [{ | |
target: '.client', | |
cond: 'isClient' | |
}, { | |
target: '.provider', | |
cond: 'isProvider' | |
}, { | |
target: '.client', | |
cond: every('isValidator', 'asClient') | |
}, { | |
target: '.provider', | |
cond: every('isValidator', 'asProvider') | |
}] | |
}, | |
states: { | |
client: {}, | |
provider: {} | |
} | |
} | |
} | |
} | |
} | |
} | |
} | |
} | |
} | |
}, | |
asyncClose: asyncState('closeJob', '#closed'), | |
closed: { | |
id: 'closed' | |
} | |
} | |
}, { | |
guards: proxyGuards | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment