Skip to content

Instantly share code, notes, and snippets.

@amogower
Created January 19, 2021 19:06
Show Gist options
  • Save amogower/a86ecebaef1e53440d4daa13f2f8fdee to your computer and use it in GitHub Desktop.
Save amogower/a86ecebaef1e53440d4daa13f2f8fdee to your computer and use it in GitHub Desktop.
Generated by XState Viz: https://xstate.js.org/viz
const singleBetMachine = Machine({
id: 'singleBet',
initial: 'default',
context: {
isEachWay: false,
stake: '',
},
states: {
default: {
on: {
CHANGE_STAKE: {
actions: 'setStake',
},
TOGGLE_EACH_WAY: {
actions: 'toggleEachWay',
},
},
},
error: {
on: {
CHANGE_STAKE: {
actions: 'setStake',
target: 'default',
},
},
},
},
});
const multipleBetMachine = Machine({
id: 'multipleBet',
initial: 'default',
context: {
isEachWay: false,
stake: '',
},
states: {
default: {
on: {
CHANGE_STAKE: {
actions: 'setStake',
},
TOGGLE_EACH_WAY: {
actions: 'toggleEachWay',
},
},
},
error: {
on: {
CHANGE_STAKE: {
actions: 'setStake',
target: 'default',
},
},
},
},
});
const pollingMachine = Machine({
initial: 'fetching',
states: {
fetching: {
invoke: {
src: 'buildBetslip',
onError: 'failure',
onDone: {
target: 'success',
actions: sendParent('BETSLIP_BUILT'),
},
},
after: { TIMEOUT: 'fetching' },
},
failure: {
after: {
INTERVAL: 'fetching',
},
},
success: {},
},
}, {
delays: { INTERVAL: 5000, TIMEOUT: 10000 }
});
const betslipMachine = Machine({
id: 'ocBetslip',
initial: 'hidden',
context: {
bookie: null,
bookieList: [],
credentials: {
password: null,
username: null,
},
errors: {
global: [],
multiple: [],
single: [],
total: 0,
},
freeBets: [],
multiples: [],
market: {},
receiptNo: null,
selectedBets: {},
singles: [],
totalStake: 10,
totalReturns: 0,
userSelectedBookie: false,
},
states: {
hidden: {
id: 'hidden',
on: {
ADD_BET: {
actions: 'addBet',
target: 'visible',
},
SHOW_BETSLIP: {
target: 'visible',
},
},
},
visible: {
id: 'visible',
initial: 'betslip',
on: {
HIDE_BETSLIP: {
actions: 'hideBetslip',
target: 'hidden',
},
},
states: {
bookieList: {
on: {
CLOSE_BOOKIE_LIST: {
actions: 'selectBookie',
target: '#betslip.initialized',
},
SELECT_BOOKIE: {
actions: 'selectBookie',
target: '#betslip.initialized',
},
},
},
betslip: {
id: 'betslip',
initial: 'initializing',
states: {
initializing: {
on: {
'': [
{
cond: 'hasNoBets',
target: 'empty',
},
{
target: 'fetchingBookmakerData',
},
]
},
},
empty: {
on: {
ADD_BET: {
actions: 'addBet',
target: 'initializing',
},
},
},
fetchingBookmakerData: {
invoke: {
src: 'fetchBookmakerData',
onDone: {
target: 'fetchingBetData',
},
onError: {
target: '#error',
},
}
},
fetchingBetData: {
invoke: {
src: 'fetchSingleBetData',
onDone: {
actions: 'addBets',
target: 'initialized',
},
onError: {
target: '#error',
},
},
},
initialized: {
on: {
'': [
{
cond: 'noBookieSelected',
target: '#visible.bookieList',
},
{
target: 'betting',
},
],
},
},
betting: {
initial: 'loggedOut',
states: {
loggedOut: {
id: 'loggedOut',
on: {
'': [
{
cond: 'bookieLoggedIn',
target: 'loggedIn'
},
],
LOG_IN: {
target: '#login',
},
PLACE_BETSLIP: {
target: '#betslip.placeBetslip',
},
},
},
loggedIn: {
id: 'loggedIn',
invoke: {
id: 'polling',
src: pollingMachine,
},
on: {
'': [
{
cond: 'bookieLoggedOut',
target: 'loggedOut'
},
],
BETSLIP_BUILT: {
actions: 'betslipBuilt',
},
LOG_OUT: {
actions: 'bookmakerLogout',
target: 'loggedOut',
},
PLACE_BETSLIP: [
{
cond: 'hasInsufficientFunds',
target: '#visible.error',
},
{
target: '#betslip.placeBetslip',
},
],
},
},
},
on: {
ADD_BET: {
actions: 'addBet',
target: '#betslip.fetchingBetData',
},
OPEN_BOOKIE_LIST: {
target: '#visible.bookieList',
},
REMOVE_BET: {
actions: 'removeBet',
target: '#betslip.initializing',
},
},
},
placeBetslip: {
on: {
'': [
{
cond: 'bookieLoggedOut',
target: '#login',
},
{
cond: 'hasInsufficientFunds',
target: '#visible.error',
},
{
target: 'placingBetslip',
},
],
},
},
placingBetslip: {
invoke: {
src: 'placeBetslip',
onDone: {
actions: 'cacheReceiptNo',
target: 'receipt',
},
onError: [
{
target: '#error',
},
],
},
},
receipt: {
on: {
DONE: {
target: 'done',
},
REUSE_SELECTIONS: {
target: '#betslip.betting.loggedIn',
},
},
},
done: {
type: 'final',
},
error: {
initial: 'single',
states: {
single: {},
multiple: {},
},
},
hist: {
type: 'history',
},
},
},
login: {
id: 'login',
initial: 'loginOauth',
states: {
loginApi: {
on: {
'': {
cond: 'bookieNotOauth',
target: 'loginApi',
},
CHANGE_USERNAME: {
actions: 'changeUsername',
},
CHANGE_PASSWORD: {
actions: 'changePassword',
},
LOGIN: [
{
cond: 'noUsername',
target: 'error.username',
},
{
cond: 'noPassword',
target: 'error.password',
},
{
target: 'loggingIn',
},
],
},
},
loginOauth: {
on: {
LOGIN_SUCCESS: {
actions: 'bookmakerLoginSuccess',
target: '#betslip.hist',
},
LOGIN_FAILURE: {
target: '#error',
},
},
},
loggingIn: {
invoke: {
src: 'bookmakerLogin',
onDone: {
actions: 'bookmakerLoginSuccess',
target: '#betslip.hist',
},
onError: {
target: '#error',
},
},
},
error: {
states: {
password: {
on: {
CHANGE_PASSWORD: {
actions: 'changePassword',
target: '#login.loginApi',
},
},
},
username: {
on: {
CHANGE_USERNAME: {
actions: 'changeUsername',
target: '#login.loginApi',
},
},
},
},
},
},
},
error: {
id: 'error',
on: {
CLOSE_MODAL: {
actions: 'clearGlobalErrors',
target: 'hist',
},
},
},
hist: {
type: 'history',
history: 'deep',
},
},
},
},
onDone: {
actions: 'hideBetslip',
},
}, {
actions: {
addBet: assign({
bookie: ({ bookie, bookieList }, event) =>
event.bookie ? bookieList.find(b => b.code === event.bookie) : bookie,
selectedBets: ({ selectedBets }, { data }) => ({
...selectedBets,
['1234']: { betId: '1234' },
}),
userSelectedBookie: ({ userSelectedBookie }, event) =>
event.bookie ? true : userSelectedBookie,
}),
addBets: assign({
multiples: ({ multiples }, { data }) => data ? data.multiples : multiples,
singles: ({ singles }, { data }) => data ? data.singles : singles,
}),
betslipBuilt: assign({
freeBets: ({ freeBets }, { data }) => data ? data.ocBetslip.freeBets : freeBets,
multiples: ({ multiples }, { data }) => data ? data.ocBetslip.multiples : multiples,
singles: ({ singles }, { data }) => data ? data.ocBetslip.singles : singles,
}),
bookmakerLoginSuccess: assign({
bookie: ({ bookie }) => ({
...bookie,
balance: 250,
loggedIn: true,
}),
}),
bookmakerLogout: assign({
bookie: () => null,
}),
cacheReceiptNo: assign({
receiptNo: (_, { receiptNo }) => receiptNo ? receiptNo : null,
}),
changePassword: assign({
credentials: ({ credentials }, { target }) => ({
...credentials,
password: target.value,
}),
}),
changeUsername: assign({
credentials: ({ credentials }, { target }) => ({
...credentials,
username: target.value,
}),
}),
clearGlobalError: assign({
errors: ({ errors }) => ({
...errors,
global: errors.global.slice(1, errors.global.length),
}),
}),
clearMultipleError: assign({
errors: ({ errors }, { currentTarget }) => ({
...errors,
multiple: errors.single.filter(error => error.id === currentTarget.dataset.betType),
}),
}),
clearSingleErrors: assign({
errors: ({ errors }, { currentTarget }) => ({
...errors,
single: errors.single.filter(error => error.id === currentTarget.dataset.betId),
}),
}),
hideBetslip: assign({
betslipVisible: false,
}),
removeBet: assign({
selectedBets: ({ selectedBets }, { betId }) => ({ ...selectedBets, [betId]: undefined }),
singles: ({ singles }, { betId }) => singles.filter((single) => single.betId !== betId),
}),
selectBookie: assign({
bookie: (ctx, { bookie }) => {
// return bookie ? bookie : ctx.bookie;
return {
bookieCode: 'B3',
loggedIn: false,
};
},
userSelectedBookie: (_, { bookie }) => !!bookie,
}),
setStake: assign({
singles: ({ singles }, { betId, value }) => singles.map(single => {
if (single.betId !== betId) return single;
return Object.assign({}, single, {
stake: value,
});
}),
}),
showBetslip: assign({
betslipVisible: true,
}),
toggleEachWay: assign((ctx, evt) => {
let key = 'singles';
let attrCheck = 'betId';
if (evt.betType !== 'Single') {
key = 'multiples';
attrCheck = 'betType';
}
return {
[key]: ctx[key].map((bet) => {
if (bet[attrCheck] !== evt[attrCheck]) return bet;
return Object.assign({}, bet, {
eachWay: !ctx.eachWay,
});
}),
};
}),
},
guards: {
bookieLoggedIn: ({ bookie }) => bookie && bookie.loggedIn,
bookieLoggedOut: ({ bookie }) => !bookie || (bookie && !bookie.loggedIn),
bookieNotOauth: ({ bookie }) => bookie && bookie.loginType !== 'oauth',
hasFetchedBookies: ({ bookieList }) => bookieList.length > 0,
hasInsufficientFunds: ({ bookie, singles, totalStake }) => {
if (!bookie) return false;
const hasFunds =
Number(bookie.balance) >= Number(totalStake) ||
singles[0].freeBet;
return !hasFunds;
},
hasMultipleError: ({ errors }) => errors.multiple.length > 0,
hasNoBets: ({ selectedBets }) => Object.keys(selectedBets).length === 0,
hasSingleError: ({ errors }) => errors.single.length > 0,
noBookieSelected: ({ bookie, userSelectedBookie }) => !bookie && !userSelectedBookie,
noPassword: ({ credentials }) => !credentials.password,
noUsername: ({ credentials }) => !credentials.username,
},
services: {
buildBetslip: () => new Promise(resolve => {
setTimeout(() => {
resolve();
}, 1500);
}),
fetchBookmakerData: () => new Promise(resolve => {
setTimeout(() => {
resolve({
bookieList: [
{ bookieCode: 'B3' }
],
});
}, 1500);
}),
fetchSingleBetData: () => new Promise(resolve => {
setTimeout(() => {
resolve({
multiples: [],
singles: [
{ betId: '2345', eachWay: false },
{ betId: '3456', eachWay: true },
],
});
}, 1500);
}),
getCachedBets: () => new Promise(resolve => {
setTimeout(() => {
resolve([1, 2]);
}, 1500);
}),
},
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment