Skip to content

Commit

Permalink
feat: unmute indication for autoplay with volume failed (#117)
Browse files Browse the repository at this point in the history
Adding unmute indication in case player tried to autoplay with volume and failed.
  • Loading branch information
Dvir Hazout authored and Dan Ziv committed Nov 6, 2017
1 parent b9b62c8 commit 98a0c5d
Show file tree
Hide file tree
Showing 13 changed files with 246 additions and 17 deletions.
31 changes: 31 additions & 0 deletions src/components/tooltip/_tooltip.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
@import '~styles/variables';

.tooltip {
display: inline-block;
height: 22px;
border-radius: 4px;
background-color: $tooltip-bg-color;
padding: 3px 13px;
color: $tooltip-color;
font-size: 13px;
font-weight: bold;
line-height: 16px;
box-shadow: 0 0 8px 0 rgba(0,0,0,0.3);
position: absolute;
transform: translate3d(-50%, 0, 0);
left: 50%;
bottom: 15px;

&:after {
content: ' ';
display: block;
position: absolute;
width: 10px;
height: 10px;
background-color: #fff;
border-radius: 3px;
transform: rotate(45deg) translate3d(0%, 50%, 0);
left: 50%;
bottom: -1px;
}
}
1 change: 1 addition & 0 deletions src/components/tooltip/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default from './tooltip';
30 changes: 30 additions & 0 deletions src/components/tooltip/tooltip.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//@flow
import style from './_tooltip.scss';
import { h, Component } from 'preact';

/**
* Tooltip component
*
* @class Tooltip
* @example <Tooltip>...</Tooltip>
* @extends {Component}
*/
class Tooltip extends Component {
/**
* render component
*
* @param {*} props - component props
* @returns {React$Element} - component element
* @memberof Tooltip
*/
render(props: any): React$Element<any> {
let className = [style.tooltip];
if (props.out) className.push(style.out);

return (
<div className={className.join(' ')} style={props.left ? {left: props.left} : ''}>{ props.children }</div>
)
}
}

export default Tooltip;
57 changes: 57 additions & 0 deletions src/components/unmute-indication/_unmute-indication.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
.unmute-button-container {
display: inline-block;
position: absolute;
top: 13px; left: 16px;
z-index: 15;

&.show-icon-only {

.btn.unmute-button {
max-width: 64px;

span {
transform: translateX(10px);
opacity: 0;
}
}
}
}
.btn.unmute-button {
font-size: 15px;
max-width: 200px;
transition: max-width 200ms;
padding: 0 16px;
white-space: nowrap;

span {
transform: translateX(0px);
opacity: 1;
transition: transform 100ms, opacity 100ms;
display: inline-block;
}

&.has-top-bar {
transition: 100ms transform;
}
}

.unmute-icon-container {
width: 32px;
height: 32px;
display: inline-block;
vertical-align: top;
position: relative;
margin-right: 3px;

i {
position: absolute;
top: 0;
left: 0;
}
}

.player.hover .unmute-button-container.has-top-bar,
.player.state-paused .unmute-button-container.has-top-bar,
.player.menu-active .unmute-button-container.has-top-bar {
transform: translateY(32px);
}
1 change: 1 addition & 0 deletions src/components/unmute-indication/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default from './unmute-indication';
117 changes: 117 additions & 0 deletions src/components/unmute-indication/unmute-indication.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
//@flow
import style from '../../styles/style.scss';
import {h} from 'preact';
import {connect} from 'preact-redux';
import {bindActions} from '../../utils/bind-actions';
import {actions} from '../../reducers/volume';
import BaseComponent from '../base';
import {default as Icon, IconType} from '../icon';

/**
* The icon only timeout
* @type {number}
* @const
*/
const ICON_ONLY_TIMEOUT = 3000;

@connect(null, bindActions(actions))
/**
* UnmuteIndication component
*
* @class UnmuteIndication
* @example <UnmuteIndication player={this.player} />
* @extends {BaseComponent}
*/
class UnmuteIndication extends BaseComponent {
/**
* The icon only timeout bounded method reference
* @private
* @memberof UnmuteIndication
* @type {Function}
*/
_iconOnlyTimeoutCallback: Function;

/**
* Creates an instance of UnmuteIndication.
* @param {Object} obj obj
* @memberof UnmuteIndication
*/
constructor(obj: Object) {
super({name: 'UnmuteIndication', player: obj.player});
this._iconOnlyTimeoutCallback = this._iconOnlyTimeout.bind(this);
}

/**
* after component mounted, listen to relevant player event for updating the state of the component
*
* @method componentDidMount
* @returns {void}
* @memberof UnmuteIndication
*/
componentDidMount() {
this.player.addEventListener(this.player.Event.MUTE_CHANGE, e => {
this.props.updateMuted(e.payload.mute);

// hide tooltip on user interaction
if (!e.payload.mute && this.state.unmuteHint) {
this.setState({unmuteHint: false});
}
});

this.player.addEventListener(this.player.Event.FALLBACK_TO_MUTED_AUTOPLAY, () => {
this.setState({unmuteHint: true});
this.player.addEventListener(this.player.Event.PLAYING, this._iconOnlyTimeoutCallback);
this.player.addEventListener(this.player.Event.AD_STARTED, this._iconOnlyTimeoutCallback);
});
}

/**
* The icon only timeout handler
* @private
* @memberof UnmuteIndication
* @returns {void}
*/
_iconOnlyTimeout(): void {
this.player.removeEventListener(this.player.Event.PLAYING, this._iconOnlyTimeoutCallback);
this.player.removeEventListener(this.player.Event.AD_STARTED, this._iconOnlyTimeoutCallback);
setTimeout(() => {
this.setState({iconOnly: true});
}, ICON_ONLY_TIMEOUT);
}

/**
* render component
*
* @param {*} props - component props
* @returns {?React$Element} component element
* @memberof UnmuteIndication
*/
render(props: any): ?React$Element<any> {
if (!this.state.unmuteHint) return undefined;

var styleClass = [style.unmuteButtonContainer];
if (props.hasTopBar) styleClass.push(style.hasTopBar);
if (this.state.iconOnly) styleClass.push(style.showIconOnly);

return (
<div
className={styleClass.join(' ')}
onMouseOver={() => this.setState({iconOnly: false})}
onMouseOut={() => this.setState({iconOnly: true})}
onClick={() => this.player.muted = !this.player.muted}
>
<a
className={[style.btn, style.btnDarkTransparent, style.unmuteButton].join(' ')}
>
<div className={style.unmuteIconContainer}>
<Icon type={IconType.VolumeBase}/>
<Icon type={IconType.VolumeMute}/>
</div>
<span>Unmute</span>
</a>
</div>
);
}
}

export default UnmuteIndication;
4 changes: 0 additions & 4 deletions src/components/volume/volume.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,6 @@ class VolumeControl extends BaseComponent {
this.props.updateVolume(this.player.volume);
});

this.player.addEventListener(this.player.Event.MUTE_CHANGE, () => {
this.props.updateMuted(this.player.muted);
});

document.addEventListener('mouseup', (e: any) => this.onVolumeProgressBarMouseUp(e));
document.addEventListener('mousemove', (e: any) => this.onVolumeProgressBarMouseMove(e));
}
Expand Down
12 changes: 0 additions & 12 deletions src/styles/_tooltip.scss

This file was deleted.

2 changes: 2 additions & 0 deletions src/styles/_variables.scss
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ $success-color: #009444;
$danger-color: #db1f26;
$ads-color: #F9A71B;
$live-color: #DA1F26;
$tooltip-bg-color: #fff;
$tooltip-color: $grayscale1;
$font-family: sans-serif;
$progress-bar-height: 4px;
$progress-bar-border-radius: $progress-bar-height / 2;
Expand Down
2 changes: 1 addition & 1 deletion src/styles/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
@import 'inputs';
@import 'buttons';
@import 'dropdown';
@import 'tooltip';
@import 'control-button';
@import 'links';
@import 'shell';
Expand All @@ -30,3 +29,4 @@
@import '../components/ad-skip/ad-skip';
@import '../components/live-tag/live-tag';
@import '../components/icon/icon';
@import '../components/unmute-indication/unmute-indication';
2 changes: 2 additions & 0 deletions src/ui-presets/ads.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import AdSkip from '../components/ad-skip';
import AdLearnMore from '../components/ad-learn-more';
import TopBar from '../components/top-bar';
import BottomBar from '../components/bottom-bar';
import UnmuteIndication from '../components/unmute-indication';

/**
* Ads ui interface
Expand All @@ -28,6 +29,7 @@ export default function adsUI(props: any): ?React$Element<any> {
<div className={style.adGuiWrapper}>
<Loading player={props.player}/>
<div className={style.playerGui} id='player-gui'>
<UnmuteIndication player={props.player} hasTopBar />
<div>
<TopBar>
<div className={style.leftControls}>
Expand Down
2 changes: 2 additions & 0 deletions src/ui-presets/live.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import BottomBar from '../components/bottom-bar';
import OverlayPortal from '../components/overlay-portal';
import KeyboardControl from '../components/keyboard';
import LiveTag from '../components/live-tag';
import UnmuteIndication from '../components/unmute-indication';

/**
* Live ui intrface
Expand All @@ -29,6 +30,7 @@ export default function liveUI(props: any): React$Element<any> {
<Loading player={props.player} />
<div className={style.playerGui} id='player-gui'>
<OverlayPortal />
<UnmuteIndication />
<OverlayPlay player={props.player} />
<BottomBar>
<SeekBarLivePlaybackContainer showFramePreview showTimeBubble player={props.player} config={props.config} />
Expand Down
2 changes: 2 additions & 0 deletions src/ui-presets/playback.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import TimeDisplayPlaybackContainer from '../components/time-display-playback-co
import BottomBar from '../components/bottom-bar';
import OverlayPortal from '../components/overlay-portal';
import KeyboardControl from '../components/keyboard';
import UnmuteIndication from '../components/unmute-indication';

/**
* Playback ui interface
Expand All @@ -29,6 +30,7 @@ export default function playbackUI(props: any): React$Element<any> {
<Loading player={props.player} />
<div className={style.playerGui} id='player-gui'>
<OverlayPortal />
<UnmuteIndication player={props.player} />
<OverlayPlay player={props.player} />
<BottomBar>
<SeekBarPlaybackContainer showFramePreview showTimeBubble player={props.player} config={props.config} />
Expand Down

0 comments on commit 98a0c5d

Please sign in to comment.