Skip to content

Commit

Permalink
feat(FEC-8084): add loading spinner while preforming change media (#209)
Browse files Browse the repository at this point in the history
Build a reset preset which will allow us to change and extend the player UI when stopped - for now only the loading component appear there.
Fix logic in loading & pre playback components.
create util methods in component-config.js to allow components reducers to handle config actions.
Add listeners in loading reducer to config change actions.
  • Loading branch information
Dan Ziv committed Apr 9, 2018
1 parent 91df91d commit 2c702cd
Show file tree
Hide file tree
Showing 9 changed files with 74 additions and 29 deletions.
4 changes: 2 additions & 2 deletions src/components/engine-connector/engine-connector.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class EngineConnector extends BaseComponent {
const TrackType = this.player.Track;

this.player.addEventListener(this.player.Event.PLAYER_RESET, () => {
this.props.updateIsStopped(true);
this.props.updateIsIdle(true);
});

this.player.addEventListener(this.player.Event.SOURCE_SELECTED, () => {
Expand All @@ -49,7 +49,7 @@ class EngineConnector extends BaseComponent {

this.player.addEventListener(this.player.Event.CHANGE_SOURCE_ENDED, () => {
this.props.updatePlayerPoster(this.player.poster);
this.props.updateIsStopped(false);
this.props.updateIsIdle(false);
});

this.player.addEventListener(this.player.Event.PLAYER_STATE_CHANGED, (e) => {
Expand Down
10 changes: 4 additions & 6 deletions src/components/loading/loading.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,18 +38,14 @@ class Loading extends BaseComponent {

/**
* after component mounted, set event listener to player state change and update the state of loading spinner accordingly.
* initially, if not mobile and autoplay is on, show the loading spinner without dependency on the player state.
* if is mobile and mobile autoplay is on, show the loading spinner without dependency on the player state.
*
* @returns {void}
* @memberof Loading
*/
componentDidMount() {
this.player.addEventListener(this.player.Event.PLAYER_STATE_CHANGED, e => {
if (!this.state.afterPlayingEvent) return;
const StateType = this.player.State;
if (!this.state.afterPlayingEvent) {
return;
}
if (e.payload.newState.type === StateType.IDLE
|| e.payload.newState.type === StateType.PLAYING
|| e.payload.newState.type === StateType.PAUSED) {
Expand All @@ -60,8 +56,10 @@ class Loading extends BaseComponent {
});

this.player.addEventListener(this.player.Event.SOURCE_SELECTED, () => {
if (this.player.config.autoplay) {
if (this.player.config.playback.autoplay) {
this.props.updateLoadingSpinnerState(true);
} else {
this.props.updateLoadingSpinnerState(false);
}
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ const mapStateToProps = state => ({
prePlayback: state.shell.prePlayback,
poster: state.engine.poster,
isMobile: state.shell.isMobile,
isEnded: state.engine.isEnded
isEnded: state.engine.isEnded,
loading: state.loading.show
});

@connect(mapStateToProps, bindActions(Object.assign(actions, loadingActions)))
Expand Down Expand Up @@ -125,7 +126,7 @@ class PrePlaybackPlayOverlay extends BaseComponent {
* @memberof PrePlaybackPlayOverlay
*/
render(props: any): React$Element<any> | void {
if ((!props.isEnded && !props.prePlayback) || (!props.isEnded && this.autoplay)) {
if ((!props.isEnded && !props.prePlayback) || (!props.isEnded && this.autoplay) || props.loading) {
return undefined;
}
let rootStyle = {},
Expand Down
5 changes: 2 additions & 3 deletions src/player-gui.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@ const mapStateToProps = state => ({
engine: {
adBreak: state.engine.adBreak,
isLive: state.engine.isLive,
hasError: state.engine.hasError,
isStopped: state.engine.isStopped
hasError: state.engine.hasError
}
},
config: state.config
Expand Down Expand Up @@ -57,7 +56,7 @@ class PlayerGUI extends Component {
*/
render(props: any): React$Element<any> | void {
let uiToRender;
if (this.props.uis.length > 0 && !props.state.engine.isStopped) {
if (this.props.uis.length > 0) {
uiToRender = this.getMatchedUI(props.uis, props.state);
return uiToRender ? uiToRender.template(props) : this.props.uis[0].template(props);
} else {
Expand Down
10 changes: 5 additions & 5 deletions src/reducers/engine.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ export const types = {
UPDATE_IS_LIVE: 'engine/UPDATE_IS_LIVE',
UPDATE_IS_DVR: 'engine/UPDATE_IS_DVR',
UPDATE_ERROR: 'engine/ERROR',
UPDATE_IS_STOPPED: 'engine/UPDATE_IS_STOPPED'
UPDATE_IS_IDLE: 'engine/UPDATE_IS_IDLE'
};

export const initialState = {
isStopped: false,
isIdle: false,
isPlaying: false,
isEnded: false,
metadataLoaded: false,
Expand Down Expand Up @@ -193,10 +193,10 @@ export default (state: Object = initialState, action: Object) => {
isDvr: action.isDvr
};

case types.UPDATE_IS_STOPPED:
case types.UPDATE_IS_IDLE:
return {
...state,
isStopped: action.stopped
isIdle: action.IsIdle
};


Expand Down Expand Up @@ -237,5 +237,5 @@ export const actions = {
updatePlayerPoster: (poster: string) => ({type: types.UPDATE_PLAYER_POSTER, poster}),
updateIsLive: (isLive: boolean) => ({type: types.UPDATE_IS_LIVE, isLive}),
updateIsDvr: (isDvr: boolean) => ({type: types.UPDATE_IS_DVR, isDvr}),
updateIsStopped: (stopped: boolean) => ({type: types.UPDATE_IS_STOPPED, stopped})
updateIsIdle: (IsIdle: boolean) => ({type: types.UPDATE_IS_IDLE, IsIdle: IsIdle})
};
13 changes: 12 additions & 1 deletion src/reducers/loading.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
//@flow
import {types as configReducerTypes} from './config'
import {getComponentStateFromConfig, getComponentStateFromComponentConfig} from '../utils/component-config'

const component = 'loading';

export const types = {
UPDATE_LOADING_SPINNER_STATE: 'loading/UPDATE_LOADING_SPINNER_STATE'
UPDATE_LOADING_SPINNER_STATE: `${component}/UPDATE_LOADING_SPINNER_STATE`
};

export const initialState = {
Expand All @@ -9,6 +14,12 @@ export const initialState = {

export default (state: Object = initialState, action: Object) => {
switch (action.type) {
case configReducerTypes.UPDATE:
return getComponentStateFromConfig(component, state, action);

case configReducerTypes.UPDATE_COMPONENT:
return getComponentStateFromComponentConfig(component, state, action);

case types.UPDATE_LOADING_SPINNER_STATE:
return {
...state,
Expand Down
2 changes: 2 additions & 0 deletions src/ui-manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import adsUI from './ui-presets/ads';
import playbackUI from './ui-presets/playback';
import liveUI from './ui-presets/live';
import errorUI from './ui-presets/error';
import idleUI from './ui-presets/idle'

import './styles/style.scss';

Expand Down Expand Up @@ -95,6 +96,7 @@ export default class UIManager {
*/
buildDefaultUI(): void {
const uis = [
{template: props => idleUI(props), condition: state => state.engine.isIdle},
{template: props => errorUI(props), condition: state => state.engine.hasError},
{template: props => adsUI(props), condition: state => state.engine.adBreak},
{template: props => liveUI(props), condition: state => state.engine.isLive},
Expand Down
19 changes: 19 additions & 0 deletions src/ui-presets/idle.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//@flow
import {h} from 'preact';
import style from '../styles/style.scss';
import Loading from '../components/loading';

/**
* Idle ui interface
*
* @export
* @param {*} props component props
* @returns {React$Element} player ui tree
*/
export default function idleUI(props: any): React$Element<any> {
return (
<div className={style.playbackGuiWWrapper}>
<Loading player={props.player}/>
</div>
)
}
35 changes: 25 additions & 10 deletions src/utils/component-config.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,31 @@
//@flow
import {Utils} from 'playkit-js'

/**
* @param {string} component - The component name.
* @param {string} oldState - The component old state.
* @param {string} action - The action object.
* @returns {Object} - The component updated state.
*/
function getComponentStateFromConfig(component: string, oldState: Object, action: Object): Object {
const componentConfig = action.config.components && action.config.components[component];
if (componentConfig) {
return Utils.Object.mergeDeep(oldState, componentConfig);
}
return oldState;
}

/**
* Gets config param value
* @param {*} config property name
* @param {string} alias component name alias
* @returns {Object} component config object
* @param {string} component - The component name.
* @param {string} oldState - The component old state.
* @param {string} action - The action object.
* @returns {Object} - The component updated state.
*/
function getComponentConfig(config: any, alias: string): Object {
try {
return config.components[alias];
} catch (error) {
return {};
function getComponentStateFromComponentConfig(component: string, oldState: Object, action: Object): Object {
if (action.componentAlias === component) {
return Utils.Object.mergeDeep(oldState, action.config);
}
return oldState;
}

/**
Expand All @@ -25,4 +40,4 @@ function shouldRenderComponent(config: Object, alias: string) {
componentConfig.constructor === Object);
}

export {shouldRenderComponent, getComponentConfig};
export {shouldRenderComponent, getComponentStateFromConfig, getComponentStateFromComponentConfig};

0 comments on commit 2c702cd

Please sign in to comment.