Skip to content

Commit

Permalink
feat(FEC-8452): playlist (#276)
Browse files Browse the repository at this point in the history
add playlist.next/prev buttons
  • Loading branch information
yairans committed Oct 25, 2018
1 parent 02f4cd8 commit 564694f
Show file tree
Hide file tree
Showing 16 changed files with 207 additions and 8 deletions.
4 changes: 3 additions & 1 deletion docs/translations.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ A sample English dictionary may look like:
"rewind": "Rewind",
"vrStereo": "vrStereo",
"live": "Live",
"unmute": "Unmute"
"unmute": "Unmute",
"next": "Next",
"prev": "Prev"
},
"settings": {
"quality": "Quality",
Expand Down
4 changes: 4 additions & 0 deletions src/components/engine-connector/engine-connector.js
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,10 @@ class EngineConnector extends BaseComponent {
const {available} = e.payload;
this.props.updateIsCastAvailable(available);
});

this.eventManager.listen(this.player, this.player.Event.Playlist.PLAYLIST_ITEM_CHANGED, () => {
this.props.updatePlaylist({next: this.player.playlist.next, prev: this.player.playlist.prev});
});
}

/**
Expand Down
18 changes: 17 additions & 1 deletion src/components/icon/icon.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,11 @@ const IconType = {
vrStereo: 'vr-stereo',
vrStereoFull: 'vr-stereo-full',
Cast: 'cast',
CastBrand: 'cast-brand'
CastBrand: 'cast-brand',
Next: 'next',
NextDisabled: 'next-disabled',
Prev: 'prev',
PrevDisabled: 'prev-disabled'
};

/**
Expand Down Expand Up @@ -170,6 +174,18 @@ class Icon extends Component {
case IconType.CastBrand:
return <i className={[style.icon, style.iconChromecastBrand].join(' ')} />;

case IconType.Next:
return <i className={[style.icon, style.iconNext].join(' ')} />;

case IconType.NextDisabled:
return <i className={[style.icon, style.iconNextDisabled].join(' ')} />;

case IconType.Prev:
return <i className={[style.icon, style.iconPrev].join(' ')} />;

case IconType.PrevDisabled:
return <i className={[style.icon, style.iconPrevDisabled].join(' ')} />;

default:
break;
}
Expand Down
23 changes: 22 additions & 1 deletion src/components/icon/icon.scss
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,12 @@
'<path fill="#{$color}" d="M864 255.996c53.019 0 96 42.981 96 96v384.004c0 53.019-42.981 96-96 96h-219.764c-33.721 0-64.97-17.693-82.319-46.608l-49.917-83.195-49.917 83.195c-17.349 28.916-48.598 46.608-82.319 46.608h-219.764c-53.019 0-96-42.981-96-96v-384.003c0-53.019 42.981-96 96-96h704zM128 351.997v384.003c0 17.673 14.327 32 32 32h219.764c11.24 0 21.657-5.898 27.44-15.536l49.917-83.195c5.405-9.008 12.944-16.547 21.952-21.952 30.309-18.185 69.622-8.357 87.807 21.952l49.917 83.195c5.783 9.639 16.199 15.536 27.44 15.536h219.764c17.673 0 32-14.327 32-32v-384.004c0-17.673-14.327-32-32-32h-704c-17.673 0-32 14.327-32 32zM304 624c-44.183 0-80-35.817-80-80s35.817-80 80-80c44.183 0 80 35.817 80 80s-35.817 80-80 80zM720 624c-44.183 0-80-35.817-80-80s35.817-80 80-80c44.183 0 80 35.817 80 80s-35.817 80-80 80z"></path>',
vrStereoFull:
'<path fill="#{$color}" d="M864 255.996c53.019 0 96 42.981 96 96v384.004c0 53.019-42.981 96-96 96h-219.764c-33.721 0-64.97-17.693-82.319-46.608l-49.917-83.195-49.917 83.195c-17.349 28.916-48.598 46.608-82.319 46.608h-219.764c-53.019 0-96-42.981-96-96v-384.003c0-53.019 42.981-96 96-96h704zM304 624c44.183 0 80-35.817 80-80s-35.817-80-80-80c-44.183 0-80 35.817-80 80s35.817 80 80 80zM720 624c44.183 0 80-35.817 80-80s-35.817-80-80-80c-44.183 0-80 35.817-80 80s35.817 80 80 80z"></path>',
chromecast: '<path fill="#{$color}" d="M160 704v96h96c0-53.12-42.88-96-96-96zM160 576v64c88.32 0 160 71.68 160 160h64c0-123.84-100.16-224-224-224zM736 352h-448v52.16c126.72 40.96 226.88 141.12 267.84 267.84h180.16v-320zM160 448v64c159.040 0 288 128.96 288 288h64c0-194.56-157.76-352-352-352zM800 224h-576c-35.2 0-64 28.8-64 64v96h64v-96h576v448h-224v64h224c35.2 0 64-28.8 64-64v-448c0-35.2-28.8-64-64-64z"></path>'
chromecast:
'<path fill="#{$color}" d="M160 704v96h96c0-53.12-42.88-96-96-96zM160 576v64c88.32 0 160 71.68 160 160h64c0-123.84-100.16-224-224-224zM736 352h-448v52.16c126.72 40.96 226.88 141.12 267.84 267.84h180.16v-320zM160 448v64c159.040 0 288 128.96 288 288h64c0-194.56-157.76-352-352-352zM800 224h-576c-35.2 0-64 28.8-64 64v96h64v-96h576v448h-224v64h224c35.2 0 64-28.8 64-64v-448c0-35.2-28.8-64-64-64z"></path>',
next:
'<path fill="#{$color}" d="M640 549.333l-264.982 154.573c-30.386 17.725-55.018 3.388-55.018-32.094v-319.625c0-35.45 24.605-49.835 55.018-32.094l264.982 154.573v-154.448c0-17.794 14.204-32.219 32-32.219 17.673 0 32 14.398 32 32.219v383.562c0 17.794-14.204 32.219-32 32.219-17.673 0-32-14.398-32-32.219v-154.448z"></path>',
prev:
'<path fill="#{$color}" d="M384 549.333l264.982 154.573c30.386 17.725 55.018 3.388 55.018-32.094v-319.625c0-35.45-24.605-49.835-55.018-32.094l-264.982 154.573v-154.448c0-17.794-14.204-32.219-32-32.219-17.673 0-32 14.398-32 32.219v383.562c0 17.794 14.204 32.219 32 32.219 17.673 0 32-14.398 32-32.219v-154.448z"></path>'
);
$icon: map-get($icons, $icon-name);
$svg-encoded-icon: svg-url($icon);
Expand Down Expand Up @@ -270,3 +275,19 @@
.icon-chromecast-brand {
background-image: icon(chromecast, $brand-color)
}

.icon-next {
background-image: icon(next, '#fff')
}

.icon-next-disabled {
background-image: icon(next, 'graytext')
}

.icon-prev {
background-image: icon(prev, '#fff')
}

.icon-prev-disabled {
background-image: icon(prev, 'graytext')
}
1 change: 1 addition & 0 deletions src/components/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,4 @@ export {CastControl} from './cast';
export {CastOverlay} from './cast-overlay';
export {VrStereoToggleControl} from './vr-stereo-toggle';
export {CastBeforePlay, CastAfterPlay} from './cast-on-tv';
export {PlaylistButton} from './playlist-button';
41 changes: 41 additions & 0 deletions src/components/playlist-button/_playlist-button.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
.control-button-container.control-playlist-button {

.control-button {

.icon-prev {
display: block;
}
.icon-next {
display: block;
}
.icon-prev-disabled {
display: none;
}
.icon-next-disabled {
display: none;
}

&:disabled {

.icon-prev {
display: none;
}
.icon-next {
display: none;
}
.icon-prev-disabled {
display: block;
}
.icon-next-disabled {
display: block;
}
}
}
}

.touch {
.control-button-container.control-playlist-button {
display: none;
}
}

1 change: 1 addition & 0 deletions src/components/playlist-button/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export {PlaylistButton} from './playlist-button';
87 changes: 87 additions & 0 deletions src/components/playlist-button/playlist-button.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
//@flow
import style from '../../styles/style.scss';
import {h} from 'preact';
import {Localizer, Text} from 'preact-i18n';
import BaseComponent from '../base';
import {default as Icon, IconType} from '../icon';
import {KeyMap} from '../../utils/key-map';
import {connect} from 'preact-redux';

/**
* mapping state to props
* @param {*} state - redux store state
* @returns {Object} - mapped state to this component
*/
const mapStateToProps = state => ({
playlist: state.engine.playlist
});

@connect(mapStateToProps)
/**
* PlaylistButton component
*
* @class PlaylistButton
* @example <PlaylistButton player={this.player} type="next"/>
* @extends {BaseComponent}
*/
class PlaylistButton extends BaseComponent {
/**
* Creates an instance of PlaylistButton.
* @param {Object} obj obj
* @memberof PlaylistButton
*/
constructor(obj: Object) {
super({name: `PlaybackButton-${obj.type}`, player: obj.player});
}

/**
* playlist button click handler
*
* @returns {void}
* @memberof PlaylistButton
*/
onClick(): void {
this.props.type === 'prev' ? this.player.playlist.playPrev() : this.player.playlist.playNext();
}

/**
* render component
*
* @param {*} props - component props
* @returns {React$Element} - component element
* @memberof PlaylistButton
*/
render(props: any): React$Element<any> | void {
return (
<div className={[style.controlButtonContainer, style.controlPlaylistButton].join(' ')}>
<Localizer>
<button
disabled={!props.playlist[props.type]}
tabIndex="0"
aria-label={<Text id={`controls.${props.type}`} />}
className={`${style.controlButton}`}
onClick={() => this.onClick()}
onKeyDown={e => {
if (e.keyCode === KeyMap.ENTER) {
this.onClick();
}
}}>
{props.type === 'prev' ? (
<div>
<Icon type={IconType.Prev} />
<Icon type={IconType.PrevDisabled} />
</div>
) : (
<div>
<Icon type={IconType.Next} />
<Icon type={IconType.NextDisabled} />
</div>
)}
</button>
</Localizer>
</div>
);
}
}

export {PlaylistButton};
3 changes: 2 additions & 1 deletion src/player-gui.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ const mapStateToProps = state => ({
isLive: state.engine.isLive,
hasError: state.engine.hasError,
isIdle: state.engine.isIdle,
isVr: state.engine.isVr
isVr: state.engine.isVr,
playlist: state.engine.playlist
}
},
config: state.config
Expand Down
15 changes: 12 additions & 3 deletions src/reducers/engine.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ export const types = {
UPDATE_VR_STEREO_MODE: `${component}/UPDATE_VR_STEREO_MODE`,
UPDATE_IS_CASTING: `${component}/UPDATE_IS_CASTING`,
UPDATE_CAST_SESSION: `${component}/UPDATE_CAST_SESSION`,
UPDATE_IS_CAST_AVAILABLE: `${component}/UPDATE_IS_CAST_AVAILABLE`
UPDATE_IS_CAST_AVAILABLE: `${component}/UPDATE_IS_CAST_AVAILABLE`,
UPDATE_PLAYLIST: `${component}/UPDATE_PLAYLIST`
};

export const initialState = {
Expand Down Expand Up @@ -76,7 +77,8 @@ export const initialState = {
vrStereoMode: false,
isCasting: false,
castSession: null,
isCastAvailable: false
isCastAvailable: false,
playlist: null
};

export default (state: Object = initialState, action: Object) => {
Expand Down Expand Up @@ -282,6 +284,12 @@ export default (state: Object = initialState, action: Object) => {
isChangingSource: action.isChangingSource
};

case types.UPDATE_PLAYLIST:
return {
...state,
playlist: action.playlist
};

default:
return state;
}
Expand Down Expand Up @@ -331,5 +339,6 @@ export const actions = {
updateIsChangingSource: (isChangingSource: boolean) => ({
type: types.UPDATE_IS_CHANGING_SOURCE,
isChangingSource
})
}),
updatePlaylist: (playlist: Object) => ({type: types.UPDATE_PLAYLIST, playlist})
};
4 changes: 4 additions & 0 deletions src/styles/_control-button.scss
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@
opacity: 1;
}

&:disabled {
cursor: default;
}

&.control-button-rounded {
width: 36px;
height: 36px;
Expand Down
1 change: 1 addition & 0 deletions src/styles/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,4 @@
@import '../components/cast-overlay/cast-overlay';
@import '../components/cast-on-tv/cast-on-tv';
@import '../components/backdrop/backdrop';
@import '../components/playlist-button/playlist-button';
4 changes: 3 additions & 1 deletion src/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
"rewind": "Rewind",
"vrStereo": "vrStereo",
"live": "Live",
"unmute": "Unmute"
"unmute": "Unmute",
"next": "Next",
"prev": "Prev"
},
"settings": {
"quality": "Quality",
Expand Down
3 changes: 3 additions & 0 deletions src/ui-presets/ads.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {BottomBar} from '../components/bottom-bar';
import {UnmuteIndication} from '../components/unmute-indication';
import {KeyboardControl} from '../components/keyboard';
import {AdNotice} from '../components/ad-notice/ad-notice';
import {PlaylistButton} from '../components/playlist-button/playlist-button';

/**
* Ads ui interface
Expand Down Expand Up @@ -50,7 +51,9 @@ export function adsUI(props: any): ?React$Element<any> {
</div>
<BottomBar>
<div className={style.leftControls}>
{props.state.engine.playlist ? <PlaylistButton player={props.player} type="prev" /> : undefined}
<PlayPauseControl player={props.player} />
{props.state.engine.playlist ? <PlaylistButton player={props.player} type="next" /> : undefined}
<TimeDisplayAdsContainer />
</div>
<div className={style.rightControls}>
Expand Down
3 changes: 3 additions & 0 deletions src/ui-presets/live.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {VrStereoToggleControl} from '../components/vr-stereo-toggle';
import {CastControl} from '../components/cast';
import {CastBeforePlay} from '../components/cast-on-tv/cast-before-play';
import {Backdrop} from '../components/backdrop/backdrop';
import {PlaylistButton} from '../components/playlist-button/playlist-button';

/**
* Live ui intrface
Expand All @@ -41,7 +42,9 @@ export function liveUI(props: any): React$Element<any> {
<BottomBar>
<SeekBarLivePlaybackContainer showFramePreview showTimeBubble player={props.player} playerContainer={props.playerContainer} />
<div className={style.leftControls}>
{props.state.engine.playlist ? <PlaylistButton player={props.player} type="prev" /> : undefined}
<PlayPauseControl player={props.player} />
{props.state.engine.playlist ? <PlaylistButton player={props.player} type="next" /> : undefined}
<LiveTag player={props.player} />
</div>
<div className={style.rightControls}>
Expand Down
3 changes: 3 additions & 0 deletions src/ui-presets/playback.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {shouldRenderComponent} from '../utils/component-config';
import {CastControl} from '../components/cast';
import {CastBeforePlay} from '../components/cast-on-tv/cast-before-play';
import {Backdrop} from '../components/backdrop/backdrop';
import {PlaylistButton} from '../components/playlist-button/playlist-button';

/**
* Playback ui interface
Expand All @@ -42,7 +43,9 @@ export function playbackUI(props: any): React$Element<any> {
<BottomBar>
<SeekBarPlaybackContainer showFramePreview showTimeBubble player={props.player} playerContainer={props.playerContainer} />
<div className={style.leftControls}>
{props.state.engine.playlist ? <PlaylistButton player={props.player} type="prev" /> : undefined}
<PlayPauseControl player={props.player} />
{props.state.engine.playlist ? <PlaylistButton player={props.player} type="next" /> : undefined}
<RewindControl player={props.player} step={10} />
<TimeDisplayPlaybackContainer format="current / total" />
</div>
Expand Down

0 comments on commit 564694f

Please sign in to comment.