Created
February 6, 2020 15:36
-
-
Save sibljon/2d4fee5a4322c93be1fe4e60fee40090 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 mapInviteTypeToConnectionType = inviteType => { | |
switch (inviteType) { | |
case "COLLEAGUE": | |
return "COLLEAGUE"; | |
case "ORGANIZATION_CODE": // Legacy type from <=3.X | |
case "PATIENT": // Legacy type from <=3.X | |
case "SECURE_MESSAGE": | |
return "SECURE_MESSAGE"; | |
case "UNKNOWN": | |
default: | |
console.error(`Unexpected inviteType ${inviteType}`); | |
return "SECURE_MESSAGE"; | |
} | |
}; | |
const associateInvite = ( | |
accountCreationIntent, | |
accountInviteClientID, | |
inviteCode | |
) => | |
new Promise(resolve => | |
setTimeout(() => { | |
resolve({ | |
data: { | |
associateInvite: { | |
confirmationScreen: { | |
body: "", | |
buttonText: "Continue", | |
entityProfile: { | |
url: "https://invite.spruce-dev.com/e/18M23V27KJ800" | |
}, | |
imageURL: | |
"https://msg-media.spruce-dev.com/media/e867db5a-ba30-9dba-7cc0-38cb-f9887032?mimetype=image%2Fpng", | |
photoStyle: "BORDERED_CIRCLE", | |
title: "You're joining Sara Brown, PsyD" | |
}, | |
errorCode: null, | |
errorMessage: null, | |
// inviteType: "ORGANIZATION_CODE", | |
// inviteType: "SECURE_MESSAGE", | |
inviteType: "COLLEAGUE", | |
phoneNumberVerificationText: null, | |
values: [ | |
{ | |
key: "client_data", | |
value: | |
'{"patient_invite":{"greeting":{"title":"You\'re Joining Sara Brown, PsyD","message":"Let\'s create your account so you can start securely messaging with Sara Brown, PsyD.","button_text":"Get Started"},"org_id":"entity_18M23V27KJ800","org_name":"Sara Brown, PsyD"},"practice_invite":{"greeting":{"title":"You\'re Joining Sara Brown, PsyD","message":"Let\'s create your account so you can start securely messaging with Sara Brown, PsyD.","button_text":"Get Started"},"org_id":"entity_18M23V27KJ800","org_name":"Sara Brown, PsyD","join_as_teammate":false}}' | |
}, | |
{ | |
key: "invite_type", | |
value: "ORGANIZATION_CODE" | |
}, | |
{ | |
key: "$desktop_url", | |
value: "https://app.spruce-dev.com/?invite=sara-brown" | |
}, | |
{ | |
key: "invite_token", | |
value: "sara-brown" | |
} | |
], | |
verifyPhoneNumber: true | |
} | |
} | |
}); | |
}, 1000) | |
); | |
const verifyPhoneNumber = phoneNumber => | |
Promise.resolve({ | |
data: { | |
verifyPhoneNumberForAccountCreation: { | |
errorCode: null, | |
errorMessage: null, | |
message: "A verification code has been sent to (630) 915-9986", | |
token: "-XPIFGaWcwroulS8i_5zqQ" | |
} | |
} | |
}); | |
// const checkVerificationCode = verificationCode => | |
// Promise.resolve({ | |
// data: { | |
// checkVerificationCode: { | |
// account: null, | |
// errorCode: "CODE_EXPIRED", | |
// errorMessage: | |
// "The entered code has expired. Please request a new code.", | |
// inviteType: null, | |
// success: false, | |
// verifiedEntityInfo: null | |
// } | |
// } | |
// }); | |
const checkVerificationCode = verificationCode => | |
Promise.resolve({ | |
data: { | |
checkVerificationCode: { | |
account: null, | |
confirmationScreen: null, | |
errorCode: null, | |
errorMessage: null, | |
inviteType: null, | |
success: true, | |
verifiedEntityInfo: null | |
} | |
} | |
}); | |
const detectPresenceOfInviteCode = () => | |
new Promise(resolve => | |
setTimeout(() => { | |
resolve("sara-brown"); | |
}, 200) | |
); | |
const fetchMachine = Machine({ | |
id: "account-creation", | |
initial: "initialize", | |
context: { | |
inviteCode: "", | |
accountCreationIntent: "PROVIDER", | |
connectionType: "", | |
phoneVerificationToken: "", | |
inviteConfirmationScreen: null, | |
accountInviteClientID: "", | |
firstName: "", | |
lastName: "", | |
shortTitle: "", | |
organizationName: "", | |
dob: {}, | |
gender: "", | |
genderDetail: "", | |
email: "", | |
password: "", | |
errorMessage: "" | |
}, | |
states: { | |
initialize: { | |
initial: "detectPresenceOfInviteCode", | |
states: { | |
detectPresenceOfInviteCode: { | |
invoke: { | |
id: "detectPresenceOfInviteCode", | |
src: (context, event) => detectPresenceOfInviteCode(), | |
onDone: { | |
target: "callingAssociateInvite", | |
internal: true, | |
actions: assign({ | |
inviteCode: (context, event) => event.data | |
}) | |
}, | |
onError: { | |
target: "#choosePatientOrProvider", | |
actions: assign({ | |
error: (context, event) => { | |
// In practice, there will never be an error, since we're simply checking the query params for the token and that can't fail | |
console.error(event); | |
} | |
}) | |
} | |
} | |
}, | |
callingAssociateInvite: { | |
invoke: { | |
id: "associateInvite", | |
src: (context, event) => | |
associateInvite( | |
context.accountCreationIntent, | |
context.accountInviteClientID, | |
context.inviteCode | |
), | |
onDone: [ | |
{ | |
target: "#choosePatientOrProvider", | |
actions: assign({ | |
connectionType: (context, event) => | |
mapInviteTypeToConnectionType( | |
event.data.data.associateInvite.inviteType | |
) | |
}), | |
cond: (context, event) => { | |
if ( | |
!event || | |
!event.data || | |
!event.data.data || | |
!event.data.data.associateInvite || | |
!event.data.data.associateInvite.inviteType | |
) { | |
return false; | |
} | |
const connectionType = mapInviteTypeToConnectionType( | |
event.data.data.associateInvite.inviteType | |
); | |
switch (connectionType) { | |
case "SECURE_MESSAGE": | |
return true; | |
case "COLLEAGUE": | |
return false; | |
} | |
} | |
}, | |
{ | |
target: "#confirmPracticeBeingJoined", | |
actions: assign({ | |
connectionType: (context, event) => | |
mapInviteTypeToConnectionType( | |
event.data.data.associateInvite.inviteType | |
), | |
confirmationScreen: (context, event) => | |
mapInviteTypeToConnectionType( | |
event.data.data.associateInvite.confirmationScreen | |
) | |
}), | |
cond: (context, event) => { | |
if ( | |
!event || | |
!event.data || | |
!event.data.data || | |
!event.data.data.associateInvite || | |
!event.data.data.associateInvite.inviteType | |
) { | |
return false; | |
} | |
const connectionType = mapInviteTypeToConnectionType( | |
event.data.data.associateInvite.inviteType | |
); | |
switch (connectionType) { | |
case "SECURE_MESSAGE": | |
return false; | |
case "COLLEAGUE": | |
return true; | |
} | |
} | |
} | |
], | |
onError: { | |
target: "#choosePatientOrProvider", | |
actions: assign({ | |
error: (context, event) => { | |
console.error(event); | |
} | |
}) | |
} | |
} | |
} | |
} | |
}, | |
choosePatientOrProvider: { | |
id: "choosePatientOrProvider", | |
on: { | |
PATIENT: { | |
target: "enterPhoneNumber", | |
actions: "markAsPatient" | |
}, | |
PROVIDER: [ | |
{ | |
target: "enterPhoneNumber", | |
actions: "markAsProvider", | |
cond: context => context.inviteType === "COLLEAGUE" | |
}, | |
{ | |
target: "chooseJoinExistingClinicOrCreateNewClinic", | |
actions: "markAsProvider" | |
} | |
] | |
} | |
}, | |
chooseJoinExistingClinicOrCreateNewClinic: { | |
id: "chooseJoinExistingClinicOrCreateNewClinic", | |
on: { | |
JOIN_EXISTING: { | |
target: "joinExistingClinic", | |
actions: "markAsPatient" | |
}, | |
CREATE_NEW: { | |
target: "enterPhoneNumber", | |
actions: "markAsProvider" | |
} | |
} | |
}, | |
// The joinExistingClinic state is just going to explain how to join an existing practice and link to the help center | |
joinExistingClinic: { | |
on: { | |
BACK: { | |
target: "chooseJoinExistingClinicOrCreateNewClinic" | |
} | |
} | |
}, | |
enterPhoneNumber: { | |
id: "enterPhoneNumber", | |
initial: "awaitPhoneNumberEntry", | |
states: { | |
awaitPhoneNumberEntry: { | |
on: { | |
CHANGE: { | |
actions: assign({ | |
phoneNumber: (ctx, e) => e.data | |
}) | |
}, | |
SUBMIT: { target: "sendingPhoneVerificationCode", internal: true }, | |
BACK: "#choosePatientOrProvider" | |
} | |
}, | |
sendingPhoneVerificationCode: { | |
invoke: { | |
id: "verifyPhoneNumber", | |
src: (context, event) => verifyPhoneNumber(context.phoneNumber), | |
onDone: { | |
target: "#enterPhoneVerificationCode", | |
internal: false | |
}, | |
onError: { | |
target: "error", | |
actions: assign({ | |
error: (context, event) => { | |
console.error(event); | |
} | |
}) | |
} | |
} | |
}, | |
error: { type: "final" } | |
} | |
}, | |
enterPhoneVerificationCode: { | |
id: "enterPhoneVerificationCode", | |
initial: "awaitVerificationCodeEntry", | |
states: { | |
awaitVerificationCodeEntry: { | |
on: { | |
CHANGE: { | |
actions: assign({ | |
verificationCode: (ctx, e) => e.data | |
}) | |
}, | |
SUBMIT: { | |
target: "verifyingCode", | |
internal: true | |
} | |
} | |
}, | |
verifyingCode: { | |
invoke: { | |
id: "checkVerificationCode", | |
src: (context, event) => | |
checkVerificationCode(context.verificationCode), | |
onDone: [ | |
{ | |
target: "awaitVerificationCodeEntry", | |
internal: true, | |
actions: assign({ | |
errorMessage: (context, event) => | |
event.data.data.checkVerificationCode.errorMessage | |
}), | |
cond: (context, event) => | |
event && | |
event.data && | |
event.data.data && | |
event.data.data.checkVerificationCode && | |
event.data.data.checkVerificationCode.errorMessage | |
}, | |
{ | |
target: "#confirmPracticeBeingJoined", | |
internal: false, | |
actions: assign({ | |
confirmationScreen: (context, event) => | |
event.data.data.checkVerificationCode.confirmationScreen | |
}), | |
cond: (context, event) => | |
event && | |
event.data && | |
event.data.data && | |
event.data.data.checkVerificationCode && | |
event.data.data.checkVerificationCode.confirmationScreen | |
}, | |
{ | |
target: "#enterPatientDemographics", | |
cond: (context, event) => | |
context.accountCreationIntent === "PATIENT" | |
}, | |
{ | |
target: "#enterOrgName", | |
cond: (context, event) => | |
context.accountCreationIntent === "PROVIDER" && | |
context.connectionType === "SECURE_MESSAGE" | |
}, | |
{ | |
target: "#enterProviderDemographics", | |
cond: (context, event) => | |
context.accountCreationIntent === "PROVIDER" && | |
context.connectionType === "COLLEAGUE" | |
} | |
], | |
onError: { | |
target: "awaitVerificationCodeEntry", | |
internal: true, | |
actions: assign({ | |
error: (context, event) => { | |
console.error(event); | |
} | |
}) | |
} | |
} | |
} | |
} | |
}, | |
confirmPracticeBeingJoined: { | |
id: "confirmPracticeBeingJoined", | |
on: { | |
CONFIRM: [ | |
{ | |
target: "enterPhoneNumber", | |
cond: ctx => ctx.connectionType === "COLLEAGUE" | |
}, | |
{ | |
target: "enterProviderDemographics", | |
cond: ctx => ctx.accountCreationIntent === "PROVIDER" | |
}, | |
{ | |
target: "enterPatientDemographics", | |
cond: ctx => ctx.accountCreationIntent === "PATIENT" | |
} | |
] | |
} | |
}, | |
enterOrgName: { | |
id: "enterOrgName", | |
on: { | |
CHANGE: { | |
actions: assign({ | |
organizationName: (ctx, e) => e.data | |
}) | |
}, | |
SUBMIT: "enterProviderDemographics", | |
BACK: "enterPhoneVerificationCode" | |
} | |
}, | |
enterPatientDemographics: { | |
id: "enterPatientDemographics", | |
on: { | |
CHANGE_FIRST_NAME: { | |
actions: assign({ | |
firstName: (ctx, e) => e.data | |
}) | |
}, | |
CHANGE_LAST_NAME: { | |
actions: assign({ | |
lastName: (ctx, e) => e.data | |
}) | |
}, | |
CHANGE_DOB: { | |
actions: assign({ | |
dob: (ctx, e) => e.data | |
}) | |
}, | |
CHANGE_GENDER: { | |
actions: assign({ | |
gender: (ctx, e) => e.data | |
}) | |
}, | |
CHANGE_GENDER_DETAIL: { | |
actions: assign({ | |
genderDetail: (ctx, e) => e.data | |
}) | |
}, | |
SUBMIT: "enterEmailAndPassword", | |
BACK: "enterPhoneVerificationCode" | |
} | |
}, | |
enterProviderDemographics: { | |
id: "enterProviderDemographics", | |
on: { | |
CHANGE_FIRST_NAME: { | |
actions: assign({ | |
firstName: (ctx, e) => e.data | |
}) | |
}, | |
CHANGE_LAST_NAME: { | |
actions: assign({ | |
lastName: (ctx, e) => e.data | |
}) | |
}, | |
CHANGE_SHORT_TITLE: { | |
actions: assign({ | |
shortTitle: (ctx, e) => e.data | |
}) | |
}, | |
SUBMIT: "enterEmailAndPassword", | |
BACK: "enterPhoneVerificationCode" | |
} | |
}, | |
enterEmailAndPassword: { | |
on: { | |
CHANGE_EMAIL: { | |
actions: assign({ | |
email: (ctx, e) => e.data | |
}) | |
}, | |
CHANGE_PASSWORD: { | |
actions: assign({ | |
password: (ctx, e) => e.data | |
}) | |
}, | |
SUBMIT: [ | |
{ | |
target: "providerAccountCreated", | |
cond: ctx => | |
ctx.connectionType === "COLLEAGUE" || | |
ctx.accountCreationIntent === "PROVIDER" | |
}, | |
{ | |
target: "patientAccountCreated", | |
cond: ctx => ctx.accountCreationIntent === "PATIENT" | |
} | |
] | |
} | |
}, | |
patientAccountCreated: { | |
type: "final" | |
}, | |
providerAccountCreated: { | |
type: "final" | |
} | |
}, | |
actions: { | |
markAsProvider: assign({ | |
accountCreationIntent: "PROVIDER" | |
}), | |
markAsPatient: assign({ | |
accountCreationIntent: "PATIENT" | |
}) | |
} | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment