// Available variables: // - Machine // - interpret // - assign // - send // - sendParent // - spawn // - raise // - actions // - XState (all XState exports) const createLinkMachine = Machine( { id: 'shortenLink', context: { linkHistory: [], value: '', errorMessage: null, retries: 0, }, initial: 'idle', states: { idle: { tags: ['showingForm'], on: { CREATE_LINK: 'validateForm', UPDATE_INPUT: { actions: ['updateValue', 'clearErrorMessage'], }, }, }, validateForm: { tags: ['showingForm'], invoke: { id: 'validatingUrl', src: 'validateUrl', onDone: { target: 'creatingLink', actions: ['clearErrorMessage'], }, onError: { target: 'idle', actions: ['setErrorMessage'], }, }, }, creatingLink: { tags: ['showingForm'], always: [{ cond: 'retriesExceeded', target: 'cannotCreateLink' }], invoke: { id: 'creatingLink', src: 'postLink', onDone: { target: 'linkCreated', actions: ['prependLink'], }, onError: { target: 'creatingLink', actions: ['incrementRetries', 'setErrorMessage'], }, }, }, cannotCreateLink: { tags: ['showingForm'], exit: ['resetErrorCount'], on: { CREATE_LINK: 'validateForm', }, }, linkCreated: { tags: ['showingResult'], exit: ['resetContext'], on: { RESTART: 'idle', }, }, }, }, { guards: { retriesExceeded: (ctx) => { return ctx.retries !== 0 && ctx.retries >= 2 }, }, services: { validateUrl: async (ctx) => { // const result = urlFormSchema.validate({ url: ctx.value }) // if (result.error) { // return Promise.reject({ message: 'Must be a valid URL' }) // } return {} }, postLink: async (ctx) => { // // const res = await fetch('/api/link', { // // method: 'POST', // // body: JSON.stringify({ url: ctx.value }), // // headers: { 'Content-Type': 'application/json' }, // // }) // if (res.status === 201) { // const data = res.json() // return data // } // return Promise.reject({ status: res.status, message: 'Unable to create link. Please try again later.' }) return { id: 1234, url: 'https://example.com', slug: 'abc123'} }, }, actions: { updateValue: assign((_ctx, evt) => { if (evt.type !== 'UPDATE_INPUT') return {} return { value: evt.value, } }), resetContext: assign((_ctx, evt) => { if (evt.type !== 'RESTART') return {} return { value: '', errorMessage: null, retries: 0, } }), resetErrorCount: assign((_ctx, evt) => { if (evt.type !== 'CREATE_LINK') return {} return { retries: 0, } }), incrementRetries: assign((ctx) => { return { retries: ctx.retries + 1, } }), prependLink: assign((ctx, evt) => { if (evt.type !== 'done.invoke.creatingLink') return {} return { linkHistory: [evt.data, ...ctx.linkHistory], } }), clearErrorMessage: assign((_ctx, evt) => { if (evt.type !== 'done.invoke.validateUrl') return {} return { errorMessage: null, } }), setErrorMessage: assign((_ctx, evt) => { if (evt.type === 'error.platform.validatingUrl' || evt.type === 'error.platform.creatingLink') { return { errorMessage: evt.data.message, } } return {} }), }, } )