diff --git a/web/client/actions/__tests__/playback-test.js b/web/client/actions/__tests__/playback-test.js index df0b5dd6cd..3ac634c316 100644 --- a/web/client/actions/__tests__/playback-test.js +++ b/web/client/actions/__tests__/playback-test.js @@ -11,7 +11,7 @@ import { PLAY, PAUSE, STOP, SET_FRAMES, SET_CURRENT_FRAME, APPEND_FRAMES, FRAMES_LOADING, SELECT_PLAYBACK_RANGE, CHANGE_SETTING, TOGGLE_ANIMATION_MODE, ANIMATION_STEP_MOVE, UPDATE_METADATA, SET_INTERVAL_DATA, pause, play, stop, setFrames, setCurrentFrame, appendFrames, framesLoading, selectPlaybackRange, - changeSetting, toggleAnimationMode, animationStepMove, updateMetadata, setIntervalData + changeSetting, toggleAnimationMode, animationStepMove, updateMetadata, setIntervalData, onInitPlayback, INIT } from "../playback"; describe('Test playback actions', () => { @@ -99,4 +99,11 @@ describe('Test playback actions', () => { expect(retval.type).toBe(SET_INTERVAL_DATA); expect(retval.timeIntervalData).toBe(true); }); + it('Test init action creator', () => { + const payload = {metadata: "1", settings: "2"}; + const retval = onInitPlayback(payload); + expect(retval).toBeTruthy(); + expect(retval.type).toBe(INIT); + expect(retval.payload).toEqual(payload); + }); }); diff --git a/web/client/actions/playback.js b/web/client/actions/playback.js index 1026866e96..a95bd85bb7 100644 --- a/web/client/actions/playback.js +++ b/web/client/actions/playback.js @@ -9,6 +9,7 @@ export const PLAY = "PLAYBACK:START"; export const PAUSE = "PLAYBACK:PAUSE"; export const STOP = "PLAYBACK:STOP"; +export const INIT = "PLAYBACK:INIT"; export const SET_FRAMES = "PLAYBACK:SET_FRAMES"; export const APPEND_FRAMES = "PLAYBACK:APPEND_FRAMES"; export const FRAMES_LOADING = "PLAYBACK:FRAMES_LOADING"; @@ -88,3 +89,12 @@ export const setIntervalData = (timeIntervalData) => ({ type: SET_INTERVAL_DATA, timeIntervalData }); + +/** + * Initialize the playback data on config load + * @param {object} playback data + */ +export const onInitPlayback = (payload) => ({ + type: INIT, + payload +}); diff --git a/web/client/epics/__tests__/playback-test.js b/web/client/epics/__tests__/playback-test.js index 112a273579..b2cfec4bb3 100644 --- a/web/client/epics/__tests__/playback-test.js +++ b/web/client/epics/__tests__/playback-test.js @@ -17,7 +17,8 @@ import { SET_FRAMES, SET_INTERVAL_DATA, TOGGLE_ANIMATION_MODE, - toggleAnimationMode + toggleAnimationMode, + INIT } from '../../actions/playback'; import { @@ -26,7 +27,8 @@ import { playbackCacheNextPreviousTimes, setIsIntervalData, switchOffSnapToLayer, - playbackToggleGuideLayerToFixedStep + playbackToggleGuideLayerToFixedStep, + updatePlaybackDataOnMapLoad } from '../playback'; import DOMAIN_VALUES_RESPONSE from 'raw-loader!../../test-resources/wmts/DomainValues.xml'; @@ -34,6 +36,7 @@ import DOMAIN_INTERVAL_VALUES_RESPONSE from 'raw-loader!../../test-resources/wmt import { removeNode, CHANGE_LAYER_PROPERTIES, changeLayerProperties } from '../../actions/layers'; import { setCurrentTime, moveTime } from '../../actions/dimension'; import { selectLayer, LOADING, setMapSync, SELECT_LAYER, initializeSelectLayer } from '../../actions/timeline'; +import { configureMap } from '../../actions/config'; import axios from '../../libs/ajax'; import MockAdapter from 'axios-mock-adapter'; const ANIMATION_MOCK_STATE = { @@ -585,4 +588,29 @@ describe('playback Epics', () => { } }, state); }); + it('updatePlaybackDataOnMapLoad on config load', done => { + const _payload = {playback: {metadata: {timeIntervalData: false}, settings: "2"}}; + testEpic(updatePlaybackDataOnMapLoad, 1, configureMap(_payload), ([action]) => { + try { + const { type, payload } = action; + expect(type).toBe(INIT); + expect(payload.metadata.timeIntervalData).toBe(false); + expect(payload.settings).toBe("2"); + done(); + } catch (e) { + done(e); + } + }, ANIMATION_MOCK_STATE); + }); + it('updatePlaybackDataOnMapLoad on no playback config data', done => { + const _payload = {map: {}}; + testEpic(addTimeoutEpic(updatePlaybackDataOnMapLoad, 500), 1, configureMap(_payload), ([action]) => { + try { + expect(action.type).toBe(TEST_TIMEOUT); + done(); + } catch (e) { + done(e); + } + }, ANIMATION_MOCK_STATE); + }); }); diff --git a/web/client/epics/playback.js b/web/client/epics/playback.js index 10ca433592..d3a2dfa469 100644 --- a/web/client/epics/playback.js +++ b/web/client/epics/playback.js @@ -7,7 +7,8 @@ */ import moment from 'moment'; -import { get } from 'lodash'; +import get from 'lodash/get'; +import isEmpty from 'lodash/isEmpty'; import { PLAY, @@ -25,7 +26,8 @@ import { framesLoading, updateMetadata, setIntervalData, - toggleAnimationMode + toggleAnimationMode, + onInitPlayback } from '../actions/playback'; import { moveTime, SET_CURRENT_TIME, MOVE_TIME } from '../actions/dimension'; @@ -79,6 +81,7 @@ import { wrapStartStop } from '../observables/epics'; import { getTimeDomainsObservable } from '../observables/multidim'; import { getDomainValues } from '../api/MultiDim'; import Rx from 'rxjs'; +import { MAP_CONFIG_LOADED } from '../actions/config'; const BUFFER_SIZE = 20; const PRELOAD_BEFORE = 10; @@ -437,6 +440,17 @@ export const playbackStopWhenDeleteLayer = (action$, { getState = () => {} } = { ) .switchMap( () => Rx.Observable.of(stop())); +/** + * Updates playback state on map config load + * @param action$ + * @return {observable} + */ +export const updatePlaybackDataOnMapLoad = (action$) => + action$.ofType(MAP_CONFIG_LOADED) + .filter(({config} = {}) => !isEmpty(config?.playback)) + .switchMap(({config} = {}) => { + return Rx.Observable.of(onInitPlayback(config?.playback)); + }); export default { retrieveFramesForPlayback, @@ -448,5 +462,6 @@ export default { playbackFollowCursor, playbackStopWhenDeleteLayer, setIsIntervalData, - switchOffSnapToLayer + switchOffSnapToLayer, + updatePlaybackDataOnMapLoad }; diff --git a/web/client/plugins/Playback.jsx b/web/client/plugins/Playback.jsx index 6136f8bfea..b93490d468 100644 --- a/web/client/plugins/Playback.jsx +++ b/web/client/plugins/Playback.jsx @@ -17,8 +17,15 @@ import playback from '../epics/playback'; import dimensionReducers from '../reducers/dimension'; import playbackReducers from '../reducers/playback'; import { currentTimeSelector } from '../selectors/dimension'; -import { loadingSelector, statusSelector } from '../selectors/playback'; +import { loadingSelector, playbackMetadataSelector, playbackRangeSelector, playbackSettingsSelector, statusSelector } from '../selectors/playback'; import PlaybackComp from './playback/Playback'; +import { registerCustomSaveHandler } from '../selectors/mapsave'; + +registerCustomSaveHandler('playback', (state) => ({ + settings: playbackSettingsSelector(state), + playbackRange: playbackRangeSelector(state), + metadata: playbackMetadataSelector(state) +})); const Playback = compose( defaultProps({ diff --git a/web/client/reducers/__tests__/playback-test.js b/web/client/reducers/__tests__/playback-test.js index e2dbe1f9b2..acfeef35ea 100644 --- a/web/client/reducers/__tests__/playback-test.js +++ b/web/client/reducers/__tests__/playback-test.js @@ -19,7 +19,8 @@ import { selectPlaybackRange, framesLoading, STATUS, - setIntervalData + setIntervalData, + onInitPlayback } from '../../actions/playback'; import playback from '../playback'; @@ -135,4 +136,12 @@ describe('playback reducer', () => { expect(state).toExist(); expect(state.metadata.timeIntervalData).toBe(false); }); + it('initialize playback data', () => { + const payload = {metadata: {timeIntervalData: false}, settings: "2"}; + const action = onInitPlayback(payload); + const state = playback(undefined, action); + expect(state).toBeTruthy(); + expect(state.metadata.timeIntervalData).toBe(false); + expect(state.settings).toBe("2"); + }); }); diff --git a/web/client/reducers/playback.js b/web/client/reducers/playback.js index 2b3852f691..e4e879bfb9 100644 --- a/web/client/reducers/playback.js +++ b/web/client/reducers/playback.js @@ -3,6 +3,7 @@ import { PAUSE, STOP, STATUS, + INIT, SET_FRAMES, APPEND_FRAMES, FRAMES_LOADING, @@ -25,6 +26,9 @@ const DEFAULT_SETTINGS = { export default (state = { status: STATUS.STOP, currentFrame: -1, settings: DEFAULT_SETTINGS}, action) => { switch (action.type) { + case INIT: { + return {...state, ...action.payload}; + } case PLAY: { return set(`status`, STATUS.PLAY, state); }