Skip to content

Commit ff94ea3

Browse files
committed
Merge pull request code-dot-org#8462 from code-dot-org/animationmodule
animationModule
2 parents d46abad + e0585f9 commit ff94ea3

File tree

10 files changed

+245
-182
lines changed

10 files changed

+245
-182
lines changed

apps/src/gamelab/AnimationPicker/animationPickerModule.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
'use strict';
33

44
import _ from '../../lodash';
5-
import gamelabActions from '../actions';
5+
import {addAnimation} from '../animationModule';
66
import { makeEnum, createUuid } from '../../utils';
77
import { sourceUrlFromKey } from '../animationMetadata';
88

@@ -119,7 +119,7 @@ export function handleUploadComplete(result) {
119119
});
120120

121121
if (goal === Goal.NEW_ANIMATION) {
122-
dispatch(gamelabActions.addAnimation(animation));
122+
dispatch(addAnimation(animation));
123123
} else if (goal === Goal.NEW_FRAME) {
124124
// TODO (bbuchanan): Implement after integrating Piskel
125125
}
@@ -170,7 +170,7 @@ export function pickLibraryAnimation(animation) {
170170
return (dispatch, getState) => {
171171
const goal = getState().animationPicker.goal;
172172
if (goal === Goal.NEW_ANIMATION) {
173-
dispatch(gamelabActions.addAnimation(Object.assign({}, animation, {
173+
dispatch(addAnimation(Object.assign({}, animation, {
174174
key: createUuid()
175175
})));
176176
} else if (goal === Goal.NEW_FRAME) {

apps/src/gamelab/AnimationTab/AnimationListItem.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import Radium from 'radium';
55
import { connect } from 'react-redux';
66
import color from '../../color';
7-
import actions from '../actions';
7+
import * as actions from '../animationModule';
88
import { METADATA_SHAPE } from '../animationMetadata';
99
import { selectAnimation } from './animationTabModule';
1010
import ListItemButtons from './ListItemButtons';

apps/src/gamelab/AnimationTab/animationTabModule.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
'use strict';
33

44
import { combineReducers } from 'redux';
5-
import { ActionType as GameLabActionType } from '../actions';
5+
import {ADD_ANIMATION_AT} from '../animationModule';
66

77
const SELECT_ANIMATION = 'AnimationTab/SELECT_ANIMATION';
88
const SET_COLUMN_SIZES = 'AnimationTab/SET_COLUMN_SIZES';
@@ -15,7 +15,7 @@ export default combineReducers({
1515
function selectedAnimation(state, action) {
1616
state = state || '';
1717
switch (action.type) {
18-
case GameLabActionType.ADD_ANIMATION_AT:
18+
case ADD_ANIMATION_AT:
1919
return action.animationProps.key;
2020
case SELECT_ANIMATION:
2121
return action.animationKey;

apps/src/gamelab/GameLab.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ var ErrorLevel = errorHandler.ErrorLevel;
2626
var dom = require('../dom');
2727
var experiments = require('../experiments');
2828

29-
var actions = require('./actions');
29+
import {setInitialAnimationMetadata} from './animationModule';
3030
var reducers = require('./reducers');
3131
var GameLabView = require('./GameLabView');
3232
var Provider = require('react-redux').Provider;
@@ -215,7 +215,7 @@ GameLab.prototype.init = function (config) {
215215

216216
// Push project-sourced animation metadata into store
217217
if (typeof config.initialAnimationMetadata !== 'undefined') {
218-
this.studioApp_.reduxStore.dispatch(actions.setInitialAnimationMetadata(config.initialAnimationMetadata));
218+
this.studioApp_.reduxStore.dispatch(setInitialAnimationMetadata(config.initialAnimationMetadata));
219219
}
220220

221221
ReactDOM.render((

apps/src/gamelab/GameLabVisualizationHeader.jsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/** @file Row of controls above the visualization. */
2-
var actions = require('./actions');
2+
import {changeInterfaceMode} from './actions';
33
var connect = require('react-redux').connect;
44
var GameLabInterfaceMode = require('./constants').GameLabInterfaceMode;
55
var msg = require('../locale');
@@ -49,7 +49,7 @@ module.exports = connect(function propsFromStore(state) {
4949
}, function propsFromDispatch(dispatch) {
5050
return {
5151
onInterfaceModeChange: function (mode) {
52-
dispatch(actions.changeInterfaceMode(mode));
52+
dispatch(changeInterfaceMode(mode));
5353
}
5454
};
5555
})(GameLabVisualizationHeader);

apps/src/gamelab/actions.js

Lines changed: 3 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,10 @@
22
* @see http://redux.js.org/docs/basics/Actions.html */
33
'use strict';
44

5-
var _ = require('../lodash');
6-
var animationsApi = require('../clientApi').animations;
7-
var reportError = require('./errorDialogStackModule').reportError;
8-
var utils = require('../utils');
5+
import utils from '../utils';
96

107
/** @enum {string} */
11-
var ActionType = module.exports.ActionType = utils.makeEnum(
12-
'ADD_ANIMATION_AT',
13-
'DELETE_ANIMATION',
14-
'SET_ANIMATION_NAME',
15-
'SET_INITIAL_ANIMATION_METADATA',
16-
'CHANGE_INTERFACE_MODE'
17-
);
8+
export const CHANGE_INTERFACE_MODE = 'CHANGE_INTERFACE_MODE';
189

1910
/**
2011
* Change the interface mode between Code Mode and the Animation Tab
@@ -25,116 +16,8 @@ module.exports.changeInterfaceMode = function (interfaceMode) {
2516
return function (dispatch) {
2617
$(window).trigger('appModeChanged');
2718
dispatch({
28-
type: ActionType.CHANGE_INTERFACE_MODE,
19+
type: CHANGE_INTERFACE_MODE,
2920
interfaceMode: interfaceMode
3021
});
3122
};
3223
};
33-
34-
/**
35-
* Push full animation metadata into the store, usually on first load
36-
* from the sources API.
37-
*
38-
* @param {Object} metadata
39-
* @returns {{type: ActionType, metadata: Object}}
40-
*/
41-
module.exports.setInitialAnimationMetadata = function (metadata) {
42-
return {
43-
type: ActionType.SET_INITIAL_ANIMATION_METADATA,
44-
metadata: metadata
45-
};
46-
};
47-
48-
module.exports.addAnimation = function (animationProps) {
49-
// TODO: Validate animationProps?
50-
return function (dispatch, getState) {
51-
dispatch({
52-
type: ActionType.ADD_ANIMATION_AT,
53-
index: getState().animations.length,
54-
animationProps: animationProps
55-
});
56-
// TODO: Save project after adding an animation?
57-
};
58-
};
59-
60-
module.exports.cloneAnimation = function (animationKey) {
61-
return function (dispatch, getState) {
62-
var animations = getState().animations;
63-
64-
var onCloneError = function (errorMessage) {
65-
dispatch(reportError(
66-
'Error copying object ' + animationKey + ': ' + errorMessage));
67-
};
68-
69-
// Track down the source animation and its index in the collection
70-
var sourceIndex = animations.map(function (a) { return a.key; }).indexOf(animationKey);
71-
if (sourceIndex < 0) {
72-
onCloneError('Animation not found');
73-
return;
74-
}
75-
var sourceAnimation = animations[sourceIndex];
76-
var newAnimationKey = utils.createUuid();
77-
78-
animationsApi.ajax(
79-
'PUT',
80-
newAnimationKey + '.png?src=' + animationKey + '.png',
81-
function success(xhr) {
82-
try {
83-
var response = JSON.parse(xhr.responseText);
84-
dispatch({
85-
type: ActionType.ADD_ANIMATION_AT,
86-
index: sourceIndex + 1,
87-
animationProps: _.assign({}, sourceAnimation, {
88-
key: newAnimationKey,
89-
name: sourceAnimation.name + '_copy', // TODO: better generated names
90-
version: response.versionId
91-
})
92-
});
93-
} catch (e) {
94-
onCloneError(e.message);
95-
}
96-
},
97-
function error(xhr) {
98-
onCloneError(xhr.status + ' ' + xhr.statusText);
99-
});
100-
};
101-
};
102-
103-
/**
104-
* Delete the specified animation from the project.
105-
* @param {string} animationKey
106-
* @returns {function}
107-
*/
108-
module.exports.deleteAnimation = function (animationKey) {
109-
return function (dispatch) {
110-
animationsApi.ajax(
111-
'DELETE',
112-
animationKey + '.png',
113-
function success() {
114-
dispatch({
115-
type: ActionType.DELETE_ANIMATION,
116-
animationKey: animationKey
117-
});
118-
// TODO: Save project after deleting an animation?
119-
},
120-
function error(xhr) {
121-
dispatch(reportError(
122-
'Error deleting object ' + animationKey + ': ' +
123-
xhr.status + ' ' + xhr.statusText));
124-
});
125-
};
126-
};
127-
128-
/**
129-
* Set the display name of the specified animation.
130-
* @param {string} animationKey
131-
* @param {string} name
132-
* @returns {{type: ActionType, animationKey: string, name: string}}
133-
*/
134-
module.exports.setAnimationName = function (animationKey, name) {
135-
return {
136-
type: ActionType.SET_ANIMATION_NAME,
137-
animationKey: animationKey,
138-
name: name
139-
};
140-
};
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
/** @file redux actions and reducers for project animation metadata. */
2+
3+
import _ from '../lodash';
4+
import utils from '../utils';
5+
import {animations as animationsApi} from '../clientApi';
6+
import {reportError} from './errorDialogStackModule';
7+
8+
export const ADD_ANIMATION_AT = 'ADD_ANIMATION_AT';
9+
const DELETE_ANIMATION = 'DELETE_ANIMATION';
10+
const SET_ANIMATION_NAME = 'SET_ANIMATION_NAME';
11+
const SET_INITIAL_ANIMATION_METADATA = 'SET_INITIAL_ANIMATION_METADATA';
12+
13+
14+
export default function animations(state, action) {
15+
state = state || [];
16+
17+
switch (action.type) {
18+
19+
case ADD_ANIMATION_AT:
20+
return [].concat(
21+
state.slice(0, action.index),
22+
action.animationProps,
23+
state.slice(action.index));
24+
25+
case DELETE_ANIMATION:
26+
return state.filter(function (animation) {
27+
return animation.key !== action.animationKey;
28+
});
29+
30+
case SET_INITIAL_ANIMATION_METADATA:
31+
return action.metadata;
32+
33+
case SET_ANIMATION_NAME:
34+
return state.map(function (animState) {
35+
return animation(animState, action);
36+
});
37+
38+
default:
39+
return state;
40+
}
41+
}
42+
43+
function animation(state, action) {
44+
state = state || { key: utils.createUuid() };
45+
46+
switch (action.type) {
47+
case SET_ANIMATION_NAME:
48+
if (state.key === action.animationKey) {
49+
return _.assign({}, state, {
50+
name: action.name
51+
});
52+
}
53+
return state;
54+
55+
default:
56+
return state;
57+
}
58+
}
59+
60+
/**
61+
* Push full animation metadata into the store, usually on first load
62+
* from the sources API.
63+
*
64+
* @param {Object} metadata
65+
* @returns {{type: ActionType, metadata: Object}}
66+
*/
67+
export function setInitialAnimationMetadata(metadata) {
68+
return {
69+
type: SET_INITIAL_ANIMATION_METADATA,
70+
metadata: metadata
71+
};
72+
}
73+
74+
export function addAnimation(animationProps) {
75+
// TODO: Validate animationProps?
76+
return function (dispatch, getState) {
77+
dispatch({
78+
type: ADD_ANIMATION_AT,
79+
index: getState().animations.length,
80+
animationProps: animationProps
81+
});
82+
// TODO: Save project after adding an animation?
83+
};
84+
}
85+
86+
export function cloneAnimation(animationKey) {
87+
return function (dispatch, getState) {
88+
var animations = getState().animations;
89+
90+
var onCloneError = function (errorMessage) {
91+
dispatch(reportError(
92+
'Error copying object ' + animationKey + ': ' + errorMessage));
93+
};
94+
95+
// Track down the source animation and its index in the collection
96+
var sourceIndex = animations.map(function (a) { return a.key; }).indexOf(animationKey);
97+
if (sourceIndex < 0) {
98+
onCloneError('Animation not found');
99+
return;
100+
}
101+
var sourceAnimation = animations[sourceIndex];
102+
var newAnimationKey = utils.createUuid();
103+
104+
animationsApi.ajax(
105+
'PUT',
106+
newAnimationKey + '.png?src=' + animationKey + '.png',
107+
function success(xhr) {
108+
try {
109+
var response = JSON.parse(xhr.responseText);
110+
dispatch({
111+
type: ADD_ANIMATION_AT,
112+
index: sourceIndex + 1,
113+
animationProps: _.assign({}, sourceAnimation, {
114+
key: newAnimationKey,
115+
name: sourceAnimation.name + '_copy', // TODO: better generated names
116+
version: response.versionId
117+
})
118+
});
119+
} catch (e) {
120+
onCloneError(e.message);
121+
}
122+
},
123+
function error(xhr) {
124+
onCloneError(xhr.status + ' ' + xhr.statusText);
125+
});
126+
};
127+
}
128+
129+
/**
130+
* Delete the specified animation from the project.
131+
* @param {string} animationKey
132+
* @returns {function}
133+
*/
134+
export function deleteAnimation(animationKey) {
135+
return function (dispatch) {
136+
animationsApi.ajax(
137+
'DELETE',
138+
animationKey + '.png',
139+
function success() {
140+
dispatch({
141+
type: DELETE_ANIMATION,
142+
animationKey: animationKey
143+
});
144+
// TODO: Save project after deleting an animation?
145+
},
146+
function error(xhr) {
147+
dispatch(reportError(
148+
'Error deleting object ' + animationKey + ': ' +
149+
xhr.status + ' ' + xhr.statusText));
150+
});
151+
};
152+
}
153+
154+
/**
155+
* Set the display name of the specified animation.
156+
* @param {string} animationKey
157+
* @param {string} name
158+
* @returns {{type: ActionType, animationKey: string, name: string}}
159+
*/
160+
export function setAnimationName(animationKey, name) {
161+
return {
162+
type: SET_ANIMATION_NAME,
163+
animationKey: animationKey,
164+
name: name
165+
};
166+
}

0 commit comments

Comments
 (0)