diff --git a/src/actions/index.js b/src/actions/index.js index 4e6e298..d2009b8 100644 --- a/src/actions/index.js +++ b/src/actions/index.js @@ -58,6 +58,11 @@ export const addGIF = imageData => ({ payload: { imageData } }); +export const undoBurst = (frames, frameIDs) => ({ + type: types.UNDO_BURST, + payload: { frames, frameIDs } +}); + export const togglePane = pane => { clearTimer(); return { @@ -140,7 +145,17 @@ export const requestFrame = opts => async dispatch => { }; export const requestBurst = opts => async (dispatch, getState) => { - const { idx, min, max, step, width, height, oversample } = opts; + const { + idx, + min, + max, + step, + width, + height, + oversample, + frames, + frameIDs + } = opts; const imageOpts = { width, height, @@ -161,6 +176,9 @@ export const requestBurst = opts => async (dispatch, getState) => { return; } + const prevFrames = { ...frames }; + const prevFrameIDs = [...frameIDs]; + let imageData; let sliderErrorMessage; for (let val = min; val <= max; val += step) { @@ -173,6 +191,8 @@ export const requestBurst = opts => async (dispatch, getState) => { imageData = await getImageData(imageOpts); dispatch(addFrame(imageData)); } + + return { prevFrames, prevFrameIDs }; }; export const startAnimation = () => (dispatch, getState) => { diff --git a/src/components/Burst.css b/src/components/Burst.css index 3e207e5..3dcbaf8 100644 --- a/src/components/Burst.css +++ b/src/components/Burst.css @@ -60,3 +60,8 @@ background: #484848; border-color: #e79600; } + +.capturing { + opacity: 0.5; + pointer-events: none; +} diff --git a/src/components/Burst.js b/src/components/Burst.js index 4b3a7fe..eabcb95 100644 --- a/src/components/Burst.js +++ b/src/components/Burst.js @@ -1,22 +1,34 @@ import React, { Component } from 'react'; import classNames from 'classnames'; +import { getCalcState, setCalcState } from '../lib/calc-helpers'; import { getBurstErrors } from '../lib/input-helpers'; import './Burst.css'; class Burst extends Component { constructor(props) { super(props); - this.state = { idx: 1, min: -10, max: 10, step: 1, + isCapturing: false, + canUndo: false, + prevFrames: {}, + prevFrameIDs: [], + prevCalcState: {}, errors: {} }; this.handleInputUpdate = this.handleInputUpdate.bind(this); this.handleRequestBurst = this.handleRequestBurst.bind(this); + this.handleUndoBurst = this.handleUndoBurst.bind(this); + } + + componentDidUpdate(prevProps) { + if (this.props.frameIDs.length !== prevProps.frameIDs.length) { + this.setState({ canUndo: false }); + } } handleInputUpdate(evt) { @@ -32,9 +44,41 @@ class Burst extends Component { this.setState(newState); } - handleRequestBurst() { - const { requestBurst, expanded, ...imgOpts } = this.props; - requestBurst({ ...this.state, ...imgOpts }); + async handleRequestBurst() { + this.setState({ isCapturing: true, canUndo: false }); + const { requestBurst, expanded, frames, frameIDs, ...imgOpts } = this.props; + const prevCalcState = getCalcState(); + const undoData = await requestBurst({ + ...this.state, + ...imgOpts, + frames, + frameIDs + }); + if (undoData) { + const { prevFrames, prevFrameIDs } = undoData; + this.setState({ + isCapturing: false, + canUndo: true, + prevFrames, + prevFrameIDs, + prevCalcState + }); + } else { + this.setState({ isCapturing: false }); + } + } + + handleUndoBurst() { + const { undoBurst } = this.props; + const { prevFrames, prevFrameIDs, prevCalcState } = this.state; + undoBurst(prevFrames, prevFrameIDs); + setCalcState(prevCalcState); + this.setState({ + canUndo: false, + prevFrames: {}, + prevFrameIDs: [], + prevCalcState: {} + }); } render() { @@ -91,13 +135,26 @@ class Burst extends Component { />
+ {this.state.canUndo ? ( +
+ +
+ ) : null} ); } diff --git a/src/constants/action-types.js b/src/constants/action-types.js index 950302c..cf78a22 100644 --- a/src/constants/action-types.js +++ b/src/constants/action-types.js @@ -11,6 +11,7 @@ export const ADD_FRAME = 'ADD_FRAME'; export const UPDATE_GIF_PROGRESS = 'UPDATE_GIF_PROGRESS'; export const ADD_GIF = 'ADD_GIF'; +export const UNDO_BURST = 'UNDO_BURST'; // UI export const UPDATE_PREVIEW_IDX = 'UPDATE_PREVIEW_IDX'; diff --git a/src/containers/BurstContainer.js b/src/containers/BurstContainer.js index d091fb5..4744f3b 100644 --- a/src/containers/BurstContainer.js +++ b/src/containers/BurstContainer.js @@ -1,23 +1,26 @@ import { connect } from 'react-redux'; import Burst from '../components/Burst'; -import { requestBurst } from '../actions'; +import { requestBurst, undoBurst } from '../actions'; import panes from '../constants/pane-types'; const mapStateToProps = (state, ownProps) => { - const { settings, ui } = state; + const { settings, ui, images } = state; const { width, height, oversample } = settings.image; + const { frames, frameIDs } = images; return { expanded: ui.expandedPane === panes.BURST, width, height, - oversample + oversample, + frames, + frameIDs }; }; const BurstContainer = connect( mapStateToProps, - { requestBurst } + { requestBurst, undoBurst } )(Burst); export default BurstContainer; diff --git a/src/lib/calc-helpers.js b/src/lib/calc-helpers.js index 57cda8c..876d4e2 100644 --- a/src/lib/calc-helpers.js +++ b/src/lib/calc-helpers.js @@ -39,3 +39,11 @@ export const setSliderByIndex = (idx, val) => { const identifier = match[1]; calculator.setExpression({ id, latex: `${identifier}=${val}` }); }; + +export const getCalcState = () => { + return calculator.getState(); +}; + +export const setCalcState = state => { + return calculator.setState(state); +}; diff --git a/src/reducers/images.js b/src/reducers/images.js index fef790c..170dfb7 100644 --- a/src/reducers/images.js +++ b/src/reducers/images.js @@ -12,6 +12,7 @@ import { ADD_FRAME, UPDATE_GIF_PROGRESS, ADD_GIF, + UNDO_BURST, UPDATE_IMAGE_SETTING, UPDATE_BOUNDS_SETTING, UPDATE_STRATEGY, @@ -53,6 +54,13 @@ const images = (state = initialState, { type, payload }) => { gifData: payload.imageData }; + case UNDO_BURST: + return { + ...state, + frames: payload.frames, + frameIDs: payload.frameIDs + }; + case UPDATE_IMAGE_SETTING: case UPDATE_BOUNDS_SETTING: case UPDATE_STRATEGY: