Skip to content

Instantly share code, notes, and snippets.

@marcmartino
Last active June 8, 2020 01:56
Show Gist options
  • Save marcmartino/afcedff8bafcb64109f52b4b23aa8028 to your computer and use it in GitHub Desktop.
Save marcmartino/afcedff8bafcb64109f52b4b23aa8028 to your computer and use it in GitHub Desktop.
Generated by XState Viz: https://xstate.js.org/viz
const setSpeechNodeContext = (lang, text) =>
assign({
speechText: text,
speechLanguage: lang,
});
const setListenNodeContext = (lang, conditions) =>
assign({
listenConditions: conditions,
listenLang: lang,
});
const isMatchingCapture = (x) => (context) => {
const captured = context.transcriptContext?.matched || "";
console.log("matching");
console.log(captured);
console.log(x);
switch (typeof x) {
case "string":
return captured.indexOf(x) > -1;
case "object":
if (x instanceof RegExp) return !!captured.match(x);
default:
return true;
}
};
const dummySpeechPromise = ({
speechText,
speechLanguage,
transcriptContext: prev,
}) =>
new Promise((resolve) => {
console.log("new speech", speechText, speechLanguage);
setTimeout(() => {
resolve({
prev,
questionText: speechText,
actions: [],
actionType: "speaking",
success: true,
});
}, 2000);
});
const dummyListenPromise = (captured) => ({
listenConditions,
listenLanguage,
transcriptContext: prev,
}) =>
new Promise((resolve) => {
console.log("new listen", listenConditions, listenLanguage);
setTimeout(() => {
resolve({
prev,
conditions: listenConditions,
listeningLanguage: listenLanguage,
matched: captured,
transcript: `transcript ${captured}`,
target: "listening",
});
}, 3500);
});
const phrases = [
{ phrase: "cosa", translation: "thing" },
{ phrase: "marcador", translation: "marker" },
{ phrase: "microfono", translation: "microphone" },
{ phrase: "guitara", translation: "guitar" },
{ phrase: "pañuelo de papel", translation: "tissue" },
];
const speechMachine = Machine(
{
id: "Flashcard",
initial: "selectedWordList",
strict: true,
context: {
speechText: "",
speechLanguage: "",
listenConditions: [],
listenLanguage: "",
},
states: {
selectedWordList: {
on: {
FETCH: { target: "card.loadingQueuedWords" },
},
},
// error: {
// id: "error",
// type: "final",
// },
card: {
id: "card",
states: {
loadingQueuedWords: {
on: {
RESOLVE: {
actions: assign({
nextPhrase: () =>
phrases[Math.floor(Math.random() * phrases.length)],
}),
target: "presentFlashcardSpeech",
},
// ERROR: { target: '#error' }
},
},
presentFlashcardSpeech: {
entry: assign({
speechText: ({ nextPhrase }) => nextPhrase.phrase,
speechLanguage: "es-AR",
}),
exit: "clearTranscriptNodes",
invoke: {
id: "speakFlashcard",
src: dummySpeechPromise,
onDone: {
actions: "assignQNAContextResults",
target: "flashcardResponseListen",
},
onError: {},
},
// on: {
// SUCCESS: { target: "flashcardResponseListen" },
// },
},
flashcardResponseListen: {
entry: setListenNodeContext("en-US", [
{ test: /taco/gi, target: "evaluateAnswer" },
{ test: /conj/gi, target: "#cardResources" },
{ test: /help/gi, target: "#help.entry" },
{ test: /next/gi, target: "#appNav" },
]),
invoke: {
id: "flashcardResponse",
src: dummyListenPromise("taco"),
onDone: {
actions: ["assignQNAContextResults"],
target: "flashcardResponseRedirect",
},
onError: {},
},
},
flashcardResponseRedirect: {
exit: "clearTranscriptNodes",
on: {
"": [
...[
{ test: /taco/gi, target: "evaluateAnswer" },
{ test: /conj/gi, target: "#cardResources" },
{ test: /help/gi, target: "#help.entry" },
{ test: /next/gi, target: "#appNav" },
].map(({ test, target }) => ({
target,
cond: isMatchingCapture(test),
})) /* default route here */,
],
},
},
evaluateAnswer: {
on: {
CORRECT: {
target: "correctAnswer",
actions: assign({
transcriptContext: ({ transcriptContext }) => ({
...transcriptContext,
correctAnswer: true,
}),
}),
},
INCORRECT: {
target: "incorrectAnswer",
actions: assign({
transcriptContext: ({ transcriptContext }) => ({
...transcriptContext,
correctAnswer: false,
}),
}),
},
},
},
correctAnswer: {
on: {
"": { target: "optionalReviewIdle" },
},
},
optionalReviewIdle: {
on: {
"": {
target: "loadingQueuedWords",
cond: (context) => context.skipWordReview,
},
NEXT: { target: "loadingQueuedWords" },
},
},
incorrectAnswer: {
on: {
"": {
actions: assign((context) => ({
incorrectAttempts: context.incorrectAttempts + 1,
})),
target: "presentFlashcardSpeech",
},
},
},
},
},
cardResourceMenu: {
id: "cardResources",
initial: "describeCardRoot",
states: {
describeCardRoot: {
on: {
"": { target: "describeCardSpeech" },
},
},
describeCardSpeech: {
entry: setSpeechNodeContext(
"en-US",
"This sentence will recap the card, probably by reiterating the cloze or similar"
),
exit: "clearTranscriptNodes",
on: {
SUCCESS: { target: "describeCardListen" },
// ERROR: { target: '#error' }
},
},
describeCardListen: {
entry: setListenNodeContext("en-US", [
{ test: /prev/gi, target: "REQUEST_PREVIOUS_CARD_DESCRIPTION" },
{
test: /forward/gi,
target: "REQUEST_PROXIMAL_CARD_DESCRIPTION",
},
{ test: /current/gi, target: "REQUEST_CURRENT_CARD_DESCRIPTION" },
{ test: /definition/gi, target: "REQUEST_DEFINITION" },
{ test: /sentence/gi, target: "REQUEST_EXAMPLE_SENTENCES" },
{ test: /conjugation/gi, target: "REQUEST_CONJUGATIONS" },
{ test: /return/gi, target: "RETURN" },
]),
exit: "clearTranscriptNodes",
on: {
REQUEST_PREVIOUS_CARD_DESCRIPTION: {
target: "describeCardSpeech",
},
REQUEST_PROXIMAL_CARD_DESCRIPTION: {
target: "describeCardSpeech",
},
REQUEST_CURRENT_CARD_DESCRIPTION: {
target: "describeCardSpeech",
},
REQUEST_DEFINITION: { target: "cardDefinitionsSpeech" },
REQUEST_EXAMPLE_SENTENCES: { target: "cardSentencesSpeech" },
REQUEST_CONJUGATIONS: { target: "cardConjugationsSpeech" },
RETURN: { target: "#card.presentFlashcardSpeech" },
// ERROR: { target: '#error' }
},
},
cardDefinitionsSpeech: {
entry: setSpeechNodeContext(
"en-US",
"This give the definition of the word"
),
exit: "clearTranscriptNodes",
on: {
SUCCESS: { target: "describeCardListen" },
// ERROR: { target: '#elsewhere' }
},
},
cardSentencesSpeech: {
entry: setSpeechNodeContext(
"en-US",
"This will give the example sentences of the phrase"
),
exit: "clearTranscriptNodes",
on: {
SUCCESS: { target: "describeCardListen" },
// ERROR: { target: '#elsewhere' }
},
},
cardConjugationsSpeech: {
entry: setSpeechNodeContext(
"en-US",
"This will give the conjugations of the phrase"
),
exit: "clearTranscriptNodes",
on: {
SUCCESS: { target: "describeCardListen" },
// ERROR: { target: '#elsewhere' }
},
},
},
},
helpMenu: {
id: "help",
states: {
entry: {
type: "final",
},
},
},
appNavigationMenu: {
id: "appNav",
states: {
root: {
on: {
"": [
{
target: "nextSpeech",
// cond: isMatchingCapture("next"),
},
{
target: "pausedSpeech",
// cond: isMatchingCapture("pause")
},
{
target: "exitSpeech",
// cond: isMatchingCapture("exit")
},
],
},
},
nextSpeech: {
entry: assign({
speechNodeTranscript: {
lang: "en-US",
text: "OK, next!",
},
}),
exit: "clearTranscriptNodes",
on: {
SUCCESS: { target: "#card.loadingQueuedWords" },
//ERROR: {target: "#error",},
},
},
pausedSpeech: {
entry: assign({
speechNodeTranscript: {
lang: "en-US",
text:
"Session paused, please press the button on screen to resume",
},
}),
exit: "clearTranscriptNodes",
on: {
SUCCESS: { target: "pauseIdle" },
//ERROR: {target: "#error",},
},
},
pauseIdle: {
on: {
UNPAUSE: { target: "#card.presentFlashcardSpeech" },
},
},
exitSpeech: {
entry: assign({
speechNodeTranscript: {
lang: "en-US",
text: "Goodbye",
},
}),
exit: "clearTranscriptNodes",
on: {
SUCCESS: { target: "exiting" },
//ERROR: {target: "#error",},
},
},
exiting: {
type: "final",
},
},
},
},
},
{
actions: {
clearTranscriptNodes: assign({
listenConditions: [],
listenLanguage: "",
speechText: "",
speechLanguage: "",
}),
assignQNAContextResults: assign({
transcriptContext: (context, event) => event.data,
}),
},
}
);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment