Last active
October 2, 2019 10:37
-
-
Save mikaelkaron/adf20c79b13905d83a994668bcf29dca 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
// Available variables: | |
// - Machine | |
// - interpret | |
// - assign | |
// - send | |
// - sendParent | |
// - spawn | |
// - raise | |
// - actions | |
// - XState (all XState exports) | |
const boot = { | |
id: "boot", | |
initial: "pending", | |
states: { | |
pending: { | |
invoke: { | |
src: "boot", | |
onDone: { target: "success", actions: "login" }, | |
onError: "#error" | |
}, | |
after: { TIMEOUT: "#error" } | |
}, | |
success: { type: "final" }, | |
}, | |
onDone: [{ target: "auth", cond: "isAuth" }, { target: "anon" }], | |
meta: { component: "page-boot" } | |
}; | |
const signup = { | |
invoke: { | |
src: "signup", | |
autoForward: true, | |
onDone: { | |
target: "done", | |
actions: "login" | |
} | |
}, | |
on: { | |
SIGNIN: "signin" | |
}, | |
meta: { component: "page-signup" } | |
}; | |
const signin = { | |
invoke: { | |
src: "signin", | |
autoForward: true, | |
onDone: { | |
target: "done", | |
actions: "login" | |
} | |
}, | |
on: { | |
SIGNUP: "signup" | |
}, | |
meta: { component: "page-signin" } | |
}; | |
const anon = { | |
id: "anon", | |
initial: "go", | |
states: { | |
go: { | |
on: { | |
"": [ | |
{ target: "signup", cond: "goSignup" }, | |
{ target: "signin" } | |
] | |
} | |
}, | |
signup, | |
signin, | |
done: { type: "final" } | |
}, | |
onDone: "#auth", | |
on: { "": { target: "boot", cond: "isAuth" } } | |
}; | |
const AccountPage = ({ id, component = `page-${id}`, incomplete, complete, target }, ...actions) => ({ | |
id, | |
initial: "incomplete", | |
states: { | |
incomplete: { | |
invoke: { | |
src: "account", | |
autoForward: true, | |
data: ({ account }) => account, | |
onDone: { | |
target: "complete", | |
actions: [ "accountUpdate", ...actions ] | |
} | |
}, | |
on: { | |
"": { | |
target: "complete", | |
cond: complete | |
} | |
} | |
}, | |
complete: { type: "final" } | |
}, | |
onDone: [ | |
{ target: ".incomplete", cond: incomplete }, | |
{ target } | |
], | |
meta: { component } | |
}); | |
const welcome = AccountPage({ | |
id: "welcome", | |
incomplete: "welcomePending", | |
complete: "welcomeCompleted", | |
target: "enrolled" | |
}); | |
const onboarding = AccountPage({ | |
id: "onboarding", | |
incomplete: "onboardingPending", | |
complete: "onboardingCompleted", | |
target: "apply" | |
}); | |
const application = { | |
initial: "local", | |
states: { | |
local: { | |
on: { | |
"": [ | |
{ target: "success", cond: "hasApplication" }, | |
{ target: "remote" } | |
] | |
} | |
}, | |
remote: { | |
invoke: { | |
src: "applicationRead", | |
onDone: [ | |
{ target: "create", cond: "noData" }, | |
{ target: "success", actions: "applicationUpdate" } | |
], | |
onError: "#error" | |
}, | |
after: { TIMEOUT: "#error" } | |
}, | |
create: { | |
invoke: { | |
src: "applicationCreate", | |
onDone: { target: "success", actions: "applicationUpdate" }, | |
onError: "#error" | |
}, | |
after: { TIMEOUT: "#error" } | |
}, | |
success: { type: "final" } | |
}, | |
onDone: [ | |
{ target: "evaluation", cond: "evaluationPending" }, | |
{ target: "references", cond: "referencesPending" }, | |
{ target: "profile" } | |
] | |
}; | |
const ApplicationPage = ({ id, component = `page-${id}`, incomplete, complete }, ...actions) => ({ | |
id, | |
initial: "edit", | |
states: { | |
edit: { | |
invoke: { | |
src: "application", | |
autoForward: true, | |
data: ({ application }) => application, | |
onDone: { | |
target: "done", | |
actions: [ "applicationUpdate", ...actions ] | |
} | |
}, | |
initial: "incomplete", | |
states: { | |
incomplete: { | |
on: { | |
"": { | |
target: "complete", | |
cond: complete | |
} | |
} | |
}, | |
complete: { | |
on: { | |
NEXT: "done" | |
} | |
}, | |
done: { type: "final" } | |
}, | |
onDone: "done" | |
}, | |
done: { type: "final" } | |
}, | |
onDone: [ | |
{ target: ".edit", cond: incomplete }, | |
{ target: "application" } | |
], | |
meta: { component } | |
}); | |
const evaluation = ApplicationPage({ | |
id: "evaluation", | |
incomplete: "evaluationPending", | |
complete: "evaluationCompleted" | |
}, "completeEvaluation"); | |
const references = ApplicationPage({ | |
id: "references", | |
incomplete: "referencesPending", | |
complete: "referencesCompleted" | |
}, "completeReferences"); | |
const profile = { | |
meta: { component: "page-profile" } | |
}; | |
const apply = { | |
initial: "application", | |
states: { | |
application, | |
evaluation, | |
references, | |
profile | |
}, | |
on: { | |
EVALUATION: ".evaluation", | |
REFERENCES: { | |
target: ".references", | |
cond: "evaluationCompleted" | |
}, | |
PROFILE: { | |
target: ".profile", | |
cond: "referencesCompleted" | |
} | |
} | |
}; | |
const enrolled = { | |
initial: "onboarding", | |
states: { | |
onboarding, | |
apply | |
}, | |
on: { | |
ACCOUNT: "#account" | |
} | |
}; | |
const active = { | |
initial: "welcome", | |
states: { | |
welcome, | |
enrolled, | |
hist: { | |
id: "hist", | |
type: "history", | |
history: "deep" | |
} | |
} | |
}; | |
const account = { | |
id: "account", | |
initial: "edit", | |
states: { | |
edit: { | |
invoke: { | |
src: "account", | |
autoForward: true, | |
data: ({ account }) => account, | |
onDone: { | |
target: "done", | |
actions: "accountUpdate" | |
} | |
}, | |
on: { | |
CANCEL: "done" | |
} | |
}, | |
done: { type: "final" } | |
}, | |
onDone: "#hist", | |
meta: { component: "page-account" } | |
}; | |
const signout = { | |
initial: "pending", | |
states: { | |
pending: { | |
invoke: { | |
src: "signout", | |
onDone: "success", | |
onError: "failure" | |
}, | |
after: { TIMEOUT: "failure" } | |
}, | |
success: { type: "final" }, | |
failure: { | |
on: { | |
"": "#hist" | |
} | |
} | |
}, | |
onDone: { target: "done", actions: "logout" }, | |
meta: { component: "page-signout" } | |
}; | |
const auth = { | |
id: "auth", | |
initial: "active", | |
states: { | |
active, | |
account, | |
signout, | |
done: { type: "final" } | |
}, | |
onDone: "#anon", | |
on: { | |
"": { | |
target: "#boot", | |
cond: "isAnon" | |
}, | |
SIGNOUT: ".signout" | |
} | |
}; | |
const error = { | |
id: "error", | |
type: "final", | |
meta: { component: "page-error" } | |
}; | |
const config = { | |
id: "app", | |
initial: "boot", | |
entry: "params", | |
context: {}, | |
states: { | |
boot, | |
anon, | |
auth, | |
error | |
}, | |
on: { | |
BOOT: { | |
target: '.boot', | |
actions: 'logout' | |
} | |
} | |
}; | |
const Step = { | |
none: 0, | |
evaluation: 1, | |
references: 2 | |
}; | |
const Flag = { | |
none: 0, | |
tc: 1, | |
onboarding: 2 | |
}; | |
const set = flag => assign({ | |
account: ({ account }) => ({ | |
...account, | |
flags: account.flags | flag | |
}) | |
}); | |
const has = flag => ({ account: { flags } = {} }) => !!(flags & flag); | |
const not = flag => ({ account: { flags } = {} }) => !(flags & flag); | |
const complete = step => assign({ | |
application: ({ application = {} }) => ({ | |
...application, | |
completed: application.completed | step | |
}) | |
}); | |
const completed = step => ({ application: { completed } = {} }) => | |
!!(completed & step); | |
const pending = step => ({ application: { completed } = {} }) => | |
!(completed & step); | |
const InputMachine = (submit, TIMEOUT = 2000) => Machine( | |
{ | |
initial: "ready", | |
states: { | |
ready: { | |
entry: sendParent("READY"), | |
on: { | |
SUBMIT: "pending" | |
} | |
}, | |
pending: { | |
entry: [sendParent("PENDING"), "pending"], | |
invoke: { | |
src: "submit", | |
onDone: "success", | |
onError: { | |
target: "failure", | |
actions: "error" | |
}, | |
data: ctx => ctx | |
}, | |
after: { | |
TIMEOUT: { | |
target: "failure", | |
actions: "timeout" | |
} | |
} | |
}, | |
success: { | |
type: "final", | |
data: (ctx, event) => event.data | |
}, | |
failure: { | |
entry: sendParent(({ error }) => ({ type: "ERROR", error })), | |
on: { | |
"": "ready" | |
} | |
} | |
} | |
}, | |
{ | |
actions: { | |
pending: assign({ error: undefined }), | |
error: assign({ | |
error: (_context, event) => event.data.message | |
}), | |
timeout: assign({ error: "Timeout: No response from backend" }) | |
}, | |
delays: { | |
TIMEOUT | |
}, | |
services: { | |
submit | |
} | |
} | |
); | |
const services = { | |
boot: Promise.resolve({}), | |
signin: InputMachine((ctx, { email }) => Promise.resolve({ account: { email, flags: Flag.tc }, application: { completed: Step.evaluation }})), | |
signup: InputMachine((ctx, { email }) => Promise.resolve({ account: { email, flags: Flag.none }})), | |
signout: Promise.resolve('bye'), | |
account: InputMachine((account, { type, ...event }) => Promise.resolve({ ...account, ...event }) | |
), | |
application: InputMachine((application, { type, ...event }) => Promise.resolve({ ...application, ...event }) | |
), | |
applicationRead: ({ application }) => Promise.resolve(application), | |
applicationCreate: Promise.resolve({ completed: Step.none }) | |
}; | |
const delays = { | |
TIMEOUT: 2000 | |
}; | |
const options = { | |
actions: { | |
login: assign((ctx, { data }) => ({ ...ctx, ...data })), | |
logout: assign({ | |
account: undefined, | |
profile: undefined, | |
application: undefined | |
}), | |
accountUpdate: assign({ account: (ctx, { data }) => ({ ...data }) }), | |
applicationUpdate: assign({ application: (ctx, { data }) => ({ ...data }) }), | |
welcomeComplete: set(Flag.tc), | |
onboardingComplete: set(Flag.onboarding), | |
evaluationComplete: complete(Step.evaluation), | |
referencesComplete: complete(Step.references) | |
}, | |
guards: { | |
isAnon: ctx => !ctx.account, | |
isAuth: ctx => !!ctx.account, | |
noData: (_ctx, event) => !event.data, | |
goSignup: () => false, | |
hasApplication: ctx => !!ctx.application, | |
welcomeCompleted: has(Flag.tc), | |
welcomePending: not(Flag.tc), | |
onboardingCompleted: has(Flag.onboarding), | |
onboardingPending: not(Flag.onboarding), | |
evaluationPending: pending(Step.evaluation), | |
evaluationCompleted: completed(Step.evaluation), | |
referencesPending: pending(Step.references), | |
referencesCompleted: completed(Step.references) | |
}, | |
services, | |
delays | |
}; | |
// debug | |
account.states.edit.on.UPDATE = { | |
actions: send({ type: "SUBMIT", "email": "[email protected]" }) | |
}; | |
signin.on.SIGNIN = { | |
actions: send({ type: "SUBMIT", "email": "[email protected]" }) | |
}; | |
signup.on.SIGNUP = { | |
actions: send({ type: "SUBMIT", "email": "[email protected]" }) | |
}; | |
welcome.states.incomplete.on.UPDATE = { | |
actions: send({ | |
type: "SUBMIT", | |
mobile: 123456, | |
flags: Flag.tc | |
}) | |
}; | |
onboarding.states.incomplete.on.NEXT = { | |
actions: send({ | |
type: "SUBMIT", | |
flags: Flag.onboarding | |
}) | |
}; | |
evaluation.states.edit.on = { SAVE: { | |
actions: send({ | |
type: "SUBMIT", | |
completed: Step.evaluation, | |
evaluation: { | |
"some": "evaluation" | |
} | |
}) | |
} }; | |
references.states.edit.on = { SAVE: { | |
actions: send({ | |
type: "SUBMIT", | |
completed: Step.evaluation | Step.references, | |
references: { | |
"moar": "references" | |
} | |
}) | |
} }; | |
const app = Machine(config, options); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment