Skip to content

Instantly share code, notes, and snippets.

@airandopal
Last active May 3, 2021 01:27
Show Gist options
  • Save airandopal/ac06dbaf108091de1490cfb2fa4c1bf6 to your computer and use it in GitHub Desktop.
Save airandopal/ac06dbaf108091de1490cfb2fa4c1bf6 to your computer and use it in GitHub Desktop.

Revisions

  1. airandopal revised this gist May 3, 2021. No changes.
  2. airandopal revised this gist May 3, 2021. 1 changed file with 1 addition and 0 deletions.
    1 change: 1 addition & 0 deletions machine.js
    Original file line number Diff line number Diff line change
    @@ -189,6 +189,7 @@ const dashboardStates = {
    on: {
    ACTIONS_COMPLETE: 'idle',
    BAD_REQUEST: 'error.badRequest',
    // rn, this doesnt work
    STRIPE_ERROR: 'error.stripe',

    // how to make it so i can just pass in the code and it puts it at the end of this, so i dont have to write out an ON for each code
  3. airandopal revised this gist May 3, 2021. 1 changed file with 14 additions and 3 deletions.
    17 changes: 14 additions & 3 deletions machine.js
    Original file line number Diff line number Diff line change
    @@ -51,7 +51,8 @@ const setupData = {
    // cant do const actions because it's already in use here
    actions: {
    'logError': (...args) => {
    console.log(`on entry of ERROR state`, args)
    const [context, event, ...theRest] = args
    console.log(`on entry of ERROR state`, { context, event, theRest })
    },
    'getUserProfile': () => {
    // do get profile and send something towards "actions completing", maybe other things have to be completed. when all actions have been completed then move to idle
    @@ -88,7 +89,6 @@ const addGlobalEntry = ({ states }) => {
    const [key, value] = curr
    if(value.states) {
    let result = performUpdates(value.states)
    console.log('see result', result)
    acc[key] = { states: result }
    } else {
    acc[key] = {
    @@ -103,14 +103,22 @@ const addGlobalEntry = ({ states }) => {
    const updatedStates = performUpdates(states)
    return { states: updatedStates }
    }
    // actions on entry with conditions? like send pager if its xyz type of errors (array based)
    const errorStatesObj = {
    states: {
    // doesnt work here
    entry: ['logError'],
    // entry: ['logError'],
    badRequest: {},
    unknown: {},
    stripe: {
    entry: ['logError'],
    on: {
    '': {
    entry: ['logError'],
    }
    },
    states: {
    '': {},
    200: {},
    400: {},
    401: {},
    @@ -182,6 +190,9 @@ const dashboardStates = {
    ACTIONS_COMPLETE: 'idle',
    BAD_REQUEST: 'error.badRequest',
    STRIPE_ERROR: 'error.stripe',

    // how to make it so i can just pass in the code and it puts it at the end of this, so i dont have to write out an ON for each code
    STRIPE_ERROR_500: 'error.stripe.500',
    UNKNOWN: 'error.unknown'
    }
    },
  4. airandopal revised this gist May 3, 2021. 1 changed file with 66 additions and 1 deletion.
    67 changes: 66 additions & 1 deletion machine.js
    Original file line number Diff line number Diff line change
    @@ -50,6 +50,9 @@ const setupData = {
    guards,
    // cant do const actions because it's already in use here
    actions: {
    'logError': (...args) => {
    console.log(`on entry of ERROR state`, args)
    },
    'getUserProfile': () => {
    // do get profile and send something towards "actions completing", maybe other things have to be completed. when all actions have been completed then move to idle
    // or send error [error getting profile]
    @@ -68,6 +71,64 @@ const setupData = {
    }
    }

    // 200 - OK Everything worked as expected.
    // 400 - Bad Request The request was unacceptable, often due to missing a required parameter.
    // 401 - Unauthorized No valid API key provided.
    // 402 - Request Failed The parameters were valid but the request failed.
    // 403 - Forbidden The API key doesn't have permissions to perform the request.
    // 404 - Not Found The requested resource doesn't exist.
    // 409 - Conflict The request conflicts with another request (perhaps due to using the same idempotent key).
    // 429 - Too Many Requests Too many requests hit the API too quickly. We recommend an exponential backoff of your requests.
    // 500, 502, 503, 504 - Server Errors Something went wrong on Stripe's end.

    const addGlobalEntry = ({ states }) => {
    // todo: make recursive, so liek stripe.states all the ones in there ge tit to
    const performUpdates = (states) => {
    return Object.entries(states).reduce((acc, curr) => {
    const [key, value] = curr
    if(value.states) {
    let result = performUpdates(value.states)
    console.log('see result', result)
    acc[key] = { states: result }
    } else {
    acc[key] = {
    entry: ['logError'],
    ...value
    }
    }
    return acc

    }, {})
    }
    const updatedStates = performUpdates(states)
    return { states: updatedStates }
    }
    const errorStatesObj = {
    states: {
    // doesnt work here
    entry: ['logError'],
    badRequest: {},
    unknown: {},
    stripe: {
    states: {
    200: {},
    400: {},
    401: {},
    402: {},
    403: {},
    404: {},
    409: {},
    429: {},
    // how to handle 500-504
    500: {},
    }
    },
    ui: {},
    api: {},
    }
    }
    // todo: really should have a "transformations" step, where i can do this for any objectis in the machine
    const errorStates = addGlobalEntry(errorStatesObj)
    const appStates = {
    'loading': {
    on: {
    @@ -118,7 +179,10 @@ const dashboardStates = {
    entry: ['getUserProfile'],
    exit: [],
    on: {
    ACTIONS_COMPLETE: 'idle'
    ACTIONS_COMPLETE: 'idle',
    BAD_REQUEST: 'error.badRequest',
    STRIPE_ERROR: 'error.stripe',
    UNKNOWN: 'error.unknown'
    }
    },
    idle: {},
    @@ -127,6 +191,7 @@ const dashboardStates = {
    returned: {},
    // i.e .typing or clicking something
    takingAction: {},
    error: errorStates
    }
    const states = {
    app: appStates,
  5. airandopal revised this gist May 2, 2021. 1 changed file with 1 addition and 0 deletions.
    1 change: 1 addition & 0 deletions machine.js
    Original file line number Diff line number Diff line change
    @@ -24,6 +24,7 @@
    // FIRST TASK OF DAY
    // read: https://xstate.js.org/docs/guides/communication.html
    // then: https://egghead.io/lessons/react-handle-http-request-state-with-xstate
    // then: https://xstate.js.org/docs/tutorials/reddit.html#splitting-machines
    // how to handle hte "Switcher" with x state
    // handle: if on page like profile and is session is no longer valid, redirect to login page or show login modal
    // session tampered scenario - send alert to server and mark profile
  6. airandopal revised this gist May 2, 2021. 1 changed file with 1 addition and 0 deletions.
    1 change: 1 addition & 0 deletions machine.js
    Original file line number Diff line number Diff line change
    @@ -23,6 +23,7 @@

    // FIRST TASK OF DAY
    // read: https://xstate.js.org/docs/guides/communication.html
    // then: https://egghead.io/lessons/react-handle-http-request-state-with-xstate
    // how to handle hte "Switcher" with x state
    // handle: if on page like profile and is session is no longer valid, redirect to login page or show login modal
    // session tampered scenario - send alert to server and mark profile
  7. airandopal revised this gist May 2, 2021. 1 changed file with 1 addition and 0 deletions.
    1 change: 1 addition & 0 deletions machine.js
    Original file line number Diff line number Diff line change
    @@ -22,6 +22,7 @@


    // FIRST TASK OF DAY
    // read: https://xstate.js.org/docs/guides/communication.html
    // how to handle hte "Switcher" with x state
    // handle: if on page like profile and is session is no longer valid, redirect to login page or show login modal
    // session tampered scenario - send alert to server and mark profile
  8. airandopal revised this gist May 2, 2021. 1 changed file with 4 additions and 1 deletion.
    5 changes: 4 additions & 1 deletion machine.js
    Original file line number Diff line number Diff line change
    @@ -18,6 +18,8 @@
    // seeding data with jotai like profile data
    // for every page, easily see, what actions can be taken from this page

    // add an on exit that logs page route changes


    // FIRST TASK OF DAY
    // how to handle hte "Switcher" with x state
    @@ -33,12 +35,13 @@ const defaults = {

    const guards = {
    isSessionPresent: (context, event) => { return null }, // todo: get info from global state about whether there is a user
    // todo: do i need two different ones, or if it returns true and false is that enough
    sessionNotPresent: (context, event) => {
    console.log({ context, event })
    return null
    // return true

    },
    isValidSubscription: () => {}
    }
    const setupData = {
    guards,
  9. airandopal revised this gist May 2, 2021. 1 changed file with 14 additions and 1 deletion.
    15 changes: 14 additions & 1 deletion machine.js
    Original file line number Diff line number Diff line change
    @@ -20,11 +20,13 @@


    // FIRST TASK OF DAY
    // how to handle hte "Switcher" with x state
    // handle: if on page like profile and is session is no longer valid, redirect to login page or show login modal
    // session tampered scenario - send alert to server and mark profile
    // session expired
    // no previous session found

    //
    const defaults = {
    initialState: 'loading'
    }
    @@ -45,7 +47,18 @@ const setupData = {
    'getUserProfile': () => {
    // do get profile and send something towards "actions completing", maybe other things have to be completed. when all actions have been completed then move to idle
    // or send error [error getting profile]
    }
    },
    'getSubscriptionData': () => {},

    'updateEmail': () => {},

    'pauseSubscription': () => {},
    'resumeSubscription': () => {},
    'cancelSubscription': () => {},

    'deleteDataGDPR': () => {},
    'deleteAccount': () => {},

    }
    }

  10. airandopal revised this gist May 2, 2021. 1 changed file with 123 additions and 116 deletions.
    239 changes: 123 additions & 116 deletions machine.js
    Original file line number Diff line number Diff line change
    @@ -1,14 +1,14 @@

    // Available variables:
    // - Machine
    // - interpret
    // - assign
    // - send
    // - sendParent
    // - spawn
    // - raise
    // - actions
    // - XState (all XState exports)
    // Available variables:
    // - Machine
    // - interpret
    // - assign
    // - send
    // - sendParent
    // - spawn
    // - raise
    // - actions
    // - XState (all XState exports)
    // lib
    // - actions
    // - guards
    @@ -26,131 +26,138 @@
    // no previous session found

    const defaults = {
    initialState: 'loading'
    initialState: 'loading'
    }

    const guards = {
    isSessionPresent: (context, event) => { return null }, // todo: get info from global state about whether there is a user
    sessionNotPresent: (context, event) => {
    console.log({ context, event })
    return null
    // return true
    sessionNotPresent: (context, event) => {
    console.log({ context, event })
    return null
    // return true

    },
    }
    const setupData = {
    guards,
    // cant do const actions because it's already in use here
    actions: {
    'getUserProfile': () => {
    // do get profile and send something towards "actions completing", maybe other things have to be completed. when all actions have been completed then move to idle
    // or send error [error getting profile]
    }
    }
    }

    const appStates = {
    'loading': {
    on: {
    // having this is same as having target at end
    // '': 'checkingForSession',
    // TRANSIENT TRANSITION
    '': [
    { target: 'loggedIn', cond: 'isSessionPresent' },
    { target: 'notLoggedIn', cond: 'sessionNotPresent' },
    // this has to be last
    { target: 'checkingForSession' },
    ],
    }
    },
    // todo: should this be a parallel state or a state inside loading
    // i.e. inside loading, its checking session and also loading other things
    // think about what happens when a page is public vs private - public pages dont need to block content, but for any page, i guess it should know whether or not the user is logged in.
    'checkingForSession': {
    on: {
    SUCCESS: 'loggedIn',
    FAILURE: 'notLoggedIn', // whats the difference between this and error
    ERROR: 'error', // must pass error along
    }
    },
    'loggedIn': {
    on: {
    LOGOUT: 'loggedOut'
    }
    },
    'notLoggedIn': {
    on: {
    LOGIN: 'tryingLogin'
    }
    },
    'tryingLogin': {
    on: {
    SUCCESS: 'loggedIn',
    FAILURE: 'notLoggedIn', // whats the difference between this and error
    ERROR: 'error', // must pass error along
    }
    },
    // 'BAD_LOGIN': {},
    'loggedOut': {},
    'error': {},
    'loading': {
    on: {
    // having this is same as having target at end
    // '': 'checkingForSession',
    // TRANSIENT TRANSITION
    '': [
    { target: 'loggedIn', cond: 'isSessionPresent' },
    { target: 'notLoggedIn', cond: 'sessionNotPresent' },
    // this has to be last
    { target: 'checkingForSession' },
    ],
    }
    },
    // todo: should this be a parallel state or a state inside loading
    // i.e. inside loading, its checking session and also loading other things
    // think about what happens when a page is public vs private - public pages dont need to block content, but for any page, i guess it should know whether or not the user is logged in.
    'checkingForSession': {
    on: {
    SUCCESS: 'loggedIn',
    FAILURE: 'notLoggedIn', // whats the difference between this and error
    ERROR: 'error', // must pass error along
    }
    },
    'loggedIn': {
    on: {
    LOGOUT: 'loggedOut'
    }
    },
    'notLoggedIn': {
    on: {
    LOGIN: 'tryingLogin'
    }
    },
    'tryingLogin': {
    on: {
    SUCCESS: 'loggedIn',
    FAILURE: 'notLoggedIn', // whats the difference between this and error
    ERROR: 'error', // must pass error along
    }
    },
    // 'BAD_LOGIN': {},
    'loggedOut': {},
    'error': {},
    }
    const dashboardStates = {
    loading: {
    on: {
    '': { actions: ['getUserProfile'] }
    }
    },
    idle: {},
    // i.e. from or to stripe. can tell if returned based on url params.
    redirecting: {},
    returned: {},
    // i.e .typing or clicking something
    takingAction: {},
    loading: {
    entry: ['getUserProfile'],
    exit: [],
    on: {
    ACTIONS_COMPLETE: 'idle'
    }
    },
    idle: {},
    // i.e. from or to stripe. can tell if returned based on url params.
    redirecting: {},
    returned: {},
    // i.e .typing or clicking something
    takingAction: {},
    }
    const states = {
    app: appStates,
    dashboard: dashboardStates
    app: appStates,
    dashboard: dashboardStates
    }
    const configs = {
    const configs = {
    app: {
    id: 'app',
    initial: defaults.initialState,
    context: {},
    states: states.app
    },
    pages: {
    index: {
    // aka dashboard page if logged in
    id: 'dashboard',
    initial: defaults.initialState,
    context: {},
    states: states.dashboard
    }
    }
    id: 'app',
    initial: defaults.initialState,
    context: {},
    states: states.app
    },
    pages: {
    index: {
    // aka dashboard page if logged in
    id: 'dashboard',
    initial: defaults.initialState,
    context: {},
    states: states.dashboard
    }
    }
    }
    // Transient transition
    // Will transition to either 'win' or 'lose' immediately upon
    // (re)entering 'playing' state if the condition is met.
    // Transient transition
    // Will transition to either 'win' or 'lose' immediately upon
    // (re)entering 'playing' state if the condition is met.


    const dashbaordMachine = Machine(configs.pages.index);
    const fetchMachine = Machine(configs.app, { guards });
    // how can another machine talk to this one - i.e. the profile machine talk to the parent machine


    // data / context parts
    // email
    // loading
    // error
    // userData
    // session
    // userDetails
    // isReceipt
    // checkedForUser
    // viewType
    // subscription
    const fetchMachine = Machine(configs.app, setupData);
    const dashbaordMachine = Machine(configs.pages.index, setupData);
    // how can another machine talk to this one - i.e. the profile machine talk to the parent machine

    // atoms
    // - providers
    // - get ?
    // - atom With machine

    // data / context parts
    // email
    // loading
    // error
    // userData
    // session
    // userDetails
    // isReceipt
    // checkedForUser
    // viewType
    // subscription

    // modal
    // - open
    // - modalType
    // atoms
    // - providers
    // - get ?
    // - atom With machine





    // modal
    // - open
    // - modalType
  11. airandopal revised this gist May 2, 2021. 1 changed file with 21 additions and 2 deletions.
    23 changes: 21 additions & 2 deletions machine.js
    Original file line number Diff line number Diff line change
    @@ -15,6 +15,20 @@
    // - atoms
    // - pages

    // seeding data with jotai like profile data
    // for every page, easily see, what actions can be taken from this page


    // FIRST TASK OF DAY
    // handle: if on page like profile and is session is no longer valid, redirect to login page or show login modal
    // session tampered scenario - send alert to server and mark profile
    // session expired
    // no previous session found

    const defaults = {
    initialState: 'loading'
    }

    const guards = {
    isSessionPresent: (context, event) => { return null }, // todo: get info from global state about whether there is a user
    sessionNotPresent: (context, event) => {
    @@ -71,6 +85,11 @@ const appStates = {
    'error': {},
    }
    const dashboardStates = {
    loading: {
    on: {
    '': { actions: ['getUserProfile'] }
    }
    },
    idle: {},
    // i.e. from or to stripe. can tell if returned based on url params.
    redirecting: {},
    @@ -85,15 +104,15 @@ const states = {
    const configs = {
    app: {
    id: 'app',
    initial: 'loading',
    initial: defaults.initialState,
    context: {},
    states: states.app
    },
    pages: {
    index: {
    // aka dashboard page if logged in
    id: 'dashboard',
    initial: 'idle',
    initial: defaults.initialState,
    context: {},
    states: states.dashboard
    }
  12. airandopal revised this gist May 2, 2021. 1 changed file with 12 additions and 3 deletions.
    15 changes: 12 additions & 3 deletions machine.js
    Original file line number Diff line number Diff line change
    @@ -9,6 +9,11 @@
    // - raise
    // - actions
    // - XState (all XState exports)
    // lib
    // - actions
    // - guards
    // - atoms
    // - pages

    const guards = {
    isSessionPresent: (context, event) => { return null }, // todo: get info from global state about whether there is a user
    @@ -20,7 +25,7 @@ const guards = {
    },
    }

    const mainStates = {
    const appStates = {
    'loading': {
    on: {
    // having this is same as having target at end
    @@ -72,21 +77,25 @@ const dashboardStates = {
    returned: {},
    // i.e .typing or clicking something
    takingAction: {},
    }
    const states = {
    app: appStates,
    dashboard: dashboardStates
    }
    const configs = {
    app: {
    id: 'app',
    initial: 'loading',
    context: {},
    states: mainStates
    states: states.app
    },
    pages: {
    index: {
    // aka dashboard page if logged in
    id: 'dashboard',
    initial: 'idle',
    context: {},
    states: dashboardStates
    states: states.dashboard
    }
    }
    }
  13. airandopal revised this gist May 2, 2021. 1 changed file with 64 additions and 6 deletions.
    70 changes: 64 additions & 6 deletions machine.js
    Original file line number Diff line number Diff line change
    @@ -12,21 +12,31 @@

    const guards = {
    isSessionPresent: (context, event) => { return null }, // todo: get info from global state about whether there is a user
    sessionNotPresent: (context, event) => { return null },
    sessionNotPresent: (context, event) => {
    console.log({ context, event })
    return null
    // return true

    },
    }

    const mainStates = {
    'loading': {
    on: {
    '': 'checkingForSession',
    // having this is same as having target at end
    // '': 'checkingForSession',
    // TRANSIENT TRANSITION
    '': [
    { target: 'checkingForSession' },
    { target: 'loggedIn', cond: 'isSessionPresent' },
    { target: 'notLoggedIn', cond: 'sessionNotPresent' }
    { target: 'notLoggedIn', cond: 'sessionNotPresent' },
    // this has to be last
    { target: 'checkingForSession' },
    ],
    }
    },
    // todo: should this be a parallel state or a state inside loading
    // i.e. inside loading, its checking session and also loading other things
    // think about what happens when a page is public vs private - public pages dont need to block content, but for any page, i guess it should know whether or not the user is logged in.
    'checkingForSession': {
    on: {
    SUCCESS: 'loggedIn',
    @@ -55,16 +65,64 @@ const mainStates = {
    'loggedOut': {},
    'error': {},
    }
    const config = {
    const dashboardStates = {
    idle: {},
    // i.e. from or to stripe. can tell if returned based on url params.
    redirecting: {},
    returned: {},
    // i.e .typing or clicking something
    takingAction: {},
    }
    const configs = {
    app: {
    id: 'app',
    initial: 'loading',
    context: {},
    states: mainStates
    },
    pages: {
    index: {
    // aka dashboard page if logged in
    id: 'dashboard',
    initial: 'idle',
    context: {},
    states: dashboardStates
    }
    }
    }
    // Transient transition
    // Will transition to either 'win' or 'lose' immediately upon
    // (re)entering 'playing' state if the condition is met.


    const fetchMachine = Machine(config, { guards });
    const dashbaordMachine = Machine(configs.pages.index);
    const fetchMachine = Machine(configs.app, { guards });
    // how can another machine talk to this one - i.e. the profile machine talk to the parent machine


    // data / context parts
    // email
    // loading
    // error
    // userData
    // session
    // userDetails
    // isReceipt
    // checkedForUser
    // viewType
    // subscription

    // atoms
    // - providers
    // - get ?
    // - atom With machine


    // modal
    // - open
    // - modalType





  14. airandopal created this gist May 2, 2021.
    70 changes: 70 additions & 0 deletions machine.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,70 @@

    // Available variables:
    // - Machine
    // - interpret
    // - assign
    // - send
    // - sendParent
    // - spawn
    // - raise
    // - actions
    // - XState (all XState exports)

    const guards = {
    isSessionPresent: (context, event) => { return null }, // todo: get info from global state about whether there is a user
    sessionNotPresent: (context, event) => { return null },
    }

    const mainStates = {
    'loading': {
    on: {
    '': 'checkingForSession',
    // TRANSIENT TRANSITION
    '': [
    { target: 'checkingForSession' },
    { target: 'loggedIn', cond: 'isSessionPresent' },
    { target: 'notLoggedIn', cond: 'sessionNotPresent' }
    ],
    }
    },
    'checkingForSession': {
    on: {
    SUCCESS: 'loggedIn',
    FAILURE: 'notLoggedIn', // whats the difference between this and error
    ERROR: 'error', // must pass error along
    }
    },
    'loggedIn': {
    on: {
    LOGOUT: 'loggedOut'
    }
    },
    'notLoggedIn': {
    on: {
    LOGIN: 'tryingLogin'
    }
    },
    'tryingLogin': {
    on: {
    SUCCESS: 'loggedIn',
    FAILURE: 'notLoggedIn', // whats the difference between this and error
    ERROR: 'error', // must pass error along
    }
    },
    // 'BAD_LOGIN': {},
    'loggedOut': {},
    'error': {},
    }
    const config = {
    id: 'app',
    initial: 'loading',
    context: {},
    states: mainStates
    }
    // Transient transition
    // Will transition to either 'win' or 'lose' immediately upon
    // (re)entering 'playing' state if the condition is met.


    const fetchMachine = Machine(config, { guards });