Skip to content

Instantly share code, notes, and snippets.

@enjoylife
Last active September 27, 2019 05:11
Show Gist options
  • Save enjoylife/307c0fc78310fb5af004568a0df8ca3b to your computer and use it in GitHub Desktop.
Save enjoylife/307c0fc78310fb5af004568a0df8ca3b to your computer and use it in GitHub Desktop.
Generated by XState Viz: https://xstate.js.org/viz
const resetableEditableContext = () => {
return {
pushing: {
updates: [],
error: null,
lastSuccess: null,
},
pulling: {
error: null,
lastSuccess: null,
updates: [],
},
};
};
const editableMachineError = key => {
return assign({
[key]: (context, event) => {
// using an immutable pattern
return {
...context[key],
error: event.error,
};
},
});
};
const editableMachineSuccess = key => {
return assign({
[key]: (context, event) => {
// using an immutable pattern
return {
...context[key],
lastSuccess: new Date().getTime(),
error: null,
updates: [],
};
},
});
};
// A editableMachine provides a state machine to manage a component which
// can take on a viewable or editable state. More importantly when
// edits do occur the process and constraints to make sure no changes are lost are provided by the state machine.
const editableMachine = Machine(
{
id: 'editable',
strict: true,
initial: 'viewing',
context: {
canEdit: true,
...resetableEditableContext(),
},
states: {
viewing: {
on: {EDIT: {target: 'editing', cond: 'canEdit'}},
},
unsaved_prompt: {
exit: ['resetContext'],
on: {
DONE: {target: 'viewing'},
},
},
editing: {
type: 'parallel',
on: {
DONE: [
{target: 'viewing', cond: 'isSaved' /*actions: 'flush', */},
{target: 'unsaved_prompt', cond: 'waitingOnSave'},
],
CHANGE: {
target: 'editing',
actions: assign({
pushing: (context, event) => {
// using an immutable pattern
return {
...context.pushing,
updates: [...context.pushing.updates, event.change],
};
},
}),
},
},
states: {
pushing: {
initial: 'idle',
states: {
idle: {
on: {
PUSH: {target: 'sync'},
},
},
sync: {
on: {
SUCCESS: {target: 'idle', actions: ['pushingSuccess']},
FAILURE: {target: 'idle', actions: ['pushingError']},
},
},
},
},
pulling: {
initial: 'idle',
states: {
idle: {
on: {
PULL: {target: 'sync'},
},
},
sync: {
on: {
SUCCESS: {target: 'idle', actions: ['pullingSuccess']},
FAILURE: {target: 'idle', actions: ['pullingError']},
},
},
},
},
},
// invoke: {
// id: 'syncMachine',
// src: syncMachine,
// // Deriving child context from parent context
// data: {
// duration: (context, event) => context.customDuration
// }
// }
},
// TODO: state waiting for confirmation that FORCE_DONE is ok
},
},
{
actions: {
resetContext: assign({
...resetableEditableContext(),
}),
pushingError: editableMachineError('pushing'),
pullingError: editableMachineError('pulling'),
pushingSuccess: editableMachineSuccess('pushing'),
pullingSuccess: editableMachineSuccess('pulling'),
},
guards: {
canEdit: (context, event) => context.canEdit,
isSaved: (context, event) =>
context.pushing.updates.length === 0 && !context.error,
waitingOnSave: (context, event) =>
context.pushing.updates.length !== 0 &&
!context.pushing.error &&
!context.pulling.error,
},
}
);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment