Skip to content

Commit

Permalink
feat(FEC-10666): add HD/4K indication in quality selector (#628)
Browse files Browse the repository at this point in the history
We wish to add a simple indication that would allow millennials to know the quality they are watching.

Requirements
For resolutions, where height is between 720 (included) and 2160 (excluded) add an HD indication.
For resolutions, where height is between 2160 (included) and 4320 (excluded) add a 4K indication.
For resolutions, where height is above 4320 (included) add a 8K indication.

solves FEC-10666
  • Loading branch information
JonathanTGold committed Aug 10, 2021
1 parent 5cdb597 commit 0535fb0
Show file tree
Hide file tree
Showing 15 changed files with 166 additions and 87 deletions.
1 change: 1 addition & 0 deletions .flowconfig
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
node_modules/kaltura-player-js/flow-typed/
[options]
esproposal.decorators=ignore
esproposal.optional_chaining=enable
module.name_mapper.extension='scss' -> 'empty/object'
module.name_mapper='^utils\/\(.*\)$' -> '<PROJECT_ROOT>/src/utils/\1'
module.name_mapper='^utils$' -> '<PROJECT_ROOT>/src/utils'
Expand Down
15 changes: 0 additions & 15 deletions src/components/badge/_badge.scss

This file was deleted.

27 changes: 0 additions & 27 deletions src/components/badge/badge.js

This file was deleted.

1 change: 0 additions & 1 deletion src/components/badge/index.js

This file was deleted.

23 changes: 12 additions & 11 deletions src/components/dropdown/dropdown.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import style from '../../styles/style.scss';
import {h, Component} from 'preact';
import {connect} from 'react-redux';
import {Menu} from '../menu';
import {default as Icon, IconType} from '../icon';
import {BadgeType, default as Icon, IconType} from '../icon';
import {KeyMap} from '../../utils/key-map';

/**
Expand Down Expand Up @@ -119,18 +119,14 @@ class DropDown extends Component {
};

/**
* get active option label or first option's label
* get active option or first option
*
* @returns {string} - active option label
* @returns {Object} - active option
* @memberof DropDown
*/
getActiveOptionLabel(): string {
let activeOptions = this.props.options.filter(t => t.active);
try {
return activeOptions[0].label;
} catch (e) {
return this.props.options[0].label || 'Unlabled';
}
getActiveOption(): Object {
const activeOption = this.props.options.find(option => option.active);
return activeOption ? activeOption : {label: 'Unlabled'};
}

/**
Expand Down Expand Up @@ -160,6 +156,9 @@ class DropDown extends Component {
*/
render(props: any): React$Element<any> {
const activeOptionId = props.name + 'Active';
const activeOption = this.getActiveOption();
const label = activeOption?.dropdownOptions?.label || activeOption.label;
const badgeType = BadgeType[activeOption.badgeType || activeOption?.dropdownOptions?.badgeType];
return props.isMobile || props.isSmallSize ? (
this.renderNativeSelect(props.name)
) : (
Expand All @@ -181,7 +180,9 @@ class DropDown extends Component {
className={style.dropdownButton}
onClick={this.onClick}
onKeyDown={this.onKeyDown}>
<span id={activeOptionId}>{this.getActiveOptionLabel()}</span>
<span id={activeOptionId} className={badgeType ? [style.labelBadge, badgeType].join(' ') : ''}>
{label}
</span>
<Icon type={IconType.ArrowDown} />
{!this.state.dropMenuActive ? undefined : (
<Menu parentEl={this._el} options={props.options} onMenuChosen={this.onMenuChosen} onClose={this.onClose} />
Expand Down
13 changes: 11 additions & 2 deletions src/components/icon/icon.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,15 @@ const IconType = {
PictureInPictureStop: 'picture-in-picture-stop'
};

const BadgeType = {
qualityHd: `${style.badgeIcon} ${style.iconQualityHd}`,
qualityHdActive: `${style.badgeIcon} ${style.iconQualityHdActive}`,
quality4k: `${style.badgeIcon} ${style.iconQuality4K}`,
quality4kActive: `${style.badgeIcon} ${style.iconQuality4KActive}`,
quality8k: `${style.badgeIcon} ${style.iconQuality8K}`,
quality8kActive: `${style.badgeIcon} ${style.iconQuality8KActive}`
};

const IconState: {[state: string]: number} = {
INACTIVE: 0,
ACTIVE: 1
Expand Down Expand Up @@ -97,7 +106,7 @@ class Icon extends Component {
* @param {string} path - svg path
* @param {number} width - svg width
* @param {number} height - svg height
* @param {string} viewBox - svg height
* @param {string} viewBox - svg viewBox
* @returns {string} - encoded svg url
* @memberof Icon
*/
Expand Down Expand Up @@ -296,4 +305,4 @@ class Icon extends Component {
}

export default Icon;
export {Icon, IconType, IconState};
export {Icon, IconType, BadgeType, IconState};
70 changes: 65 additions & 5 deletions src/components/icon/icon.scss

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/components/icon/index.js
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export default from './icon';
export {Icon, IconType, IconState} from './icon';
export {Icon, IconType, IconState, BadgeType} from './icon';
3 changes: 1 addition & 2 deletions src/components/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,7 @@ export {EngineConnector} from './engine-connector';
export {ErrorOverlay} from './error-overlay';
export * from './event-dispatcher';
export * from './keyboard';
export {Icon, IconType, IconState} from './icon';
export {Badge} from './badge';
export {Icon, IconType, IconState, BadgeType} from './icon';
export {LiveTag} from './live-tag';
export {Loading} from './loading';
export {Menu} from './menu';
Expand Down
7 changes: 4 additions & 3 deletions src/components/menu/menu.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//@flow
import style from '../../styles/style.scss';
import {h, Component} from 'preact';
import {default as Icon, IconType} from '../icon';
import {BadgeType, default as Icon, IconType} from '../icon';
import {connect} from 'react-redux';
import {withKeyboardA11y} from '../../utils/popup-keyboard-accessibility';
import {KeyMap} from 'utils/key-map';
Expand Down Expand Up @@ -249,6 +249,8 @@ class MenuItem extends Component {
* @memberof MenuItem
*/
render(props: any): React$Element<any> {
const badgeType: string | null =
props.data.badgeType && !props.isSelected(props.data) ? BadgeType[props.data.badgeType] : BadgeType[props.data.badgeType + 'Active'];
return (
<div
role="menuitemradio"
Expand All @@ -263,8 +265,7 @@ class MenuItem extends Component {
className={props.isSelected(props.data) ? [style.dropdownMenuItem, style.active].join(' ') : style.dropdownMenuItem}
onClick={this.onClick}
onKeyDown={this.onKeyDown}>
<span>{props.data.label}</span>
{props.data.badge ? props.data.badge : null}
<span className={badgeType ? [style.labelBadge, badgeType].join(' ') : ''}>{props.data.label}</span>
<span className={[style.menuIconContainer, style.active].join(' ')}>
<Icon type={IconType.Check} />
</span>
Expand Down
55 changes: 38 additions & 17 deletions src/components/settings/settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ import style from '../../styles/style.scss';
import {h, Component} from 'preact';
import {withText, Text} from 'preact-i18n';
import {connect} from 'react-redux';
import {bindActions} from '../../utils/bind-actions';
import {actions} from '../../reducers/settings';
import {SmartContainer} from '../smart-container';
import {SmartContainerItem} from '../smart-container/smart-container-item';
import {default as Icon, IconType} from '../icon';
import {bindActions} from 'utils';
import {actions} from 'reducers/settings';
import {SmartContainer} from 'components';
import {SmartContainerItem} from 'components';
import {default as Icon, IconType, BadgeType} from '../icon';
import {withPlayer} from '../player';
import {withEventManager} from 'event/with-event-manager';
import {withEventDispatcher} from 'components/event-dispatcher';
Expand All @@ -19,10 +19,9 @@ import {actions as overlayIconActions} from 'reducers/overlay-action';
import {Tooltip} from 'components/tooltip';
import {Button} from 'components/button';
import {ButtonControl} from 'components/button-control';
import {Badge} from 'components/badge';

const HeightResolution = {
HD: 720,
HD: 1080,
UHD_4K: 2160,
UHD_8K: 4320
};
Expand Down Expand Up @@ -251,22 +250,33 @@ class Settings extends Component {
}

/**
* Prepares the badge of the quality option according to the height of its resolution.
* Determines the badge icon type of the quality option based of the height resolution.
*
* @param {number} videoTrackHeight - video track quality height.
* @returns {Component<Badge>} - the badge withe the appropriate value.
* @returns {Component<Badge>} - the badge icon type.
* @memberof Settings
*/
getBadge(videoTrackHeight: number): Component<Badge> {
let badgeContent = '';
getLabelBadgeType(videoTrackHeight: number): string | null {
const [QHD, , Q4K, , Q8k] = Object.keys(BadgeType);
if (videoTrackHeight >= HeightResolution.HD && videoTrackHeight < HeightResolution.UHD_4K) {
badgeContent = 'HD';
return QHD;
} else if (videoTrackHeight >= HeightResolution.UHD_4K && videoTrackHeight < HeightResolution.UHD_8K) {
badgeContent = '4K';
return Q4K;
} else if (videoTrackHeight >= HeightResolution.UHD_8K) {
badgeContent = '8K';
return Q8k;
}
return badgeContent ? <Badge content={badgeContent} /> : null;
return null;
}

/**
* returns The badge icon type of the active quality option based of the height resolution
*
* @returns {string} - the badge icon type.
* @memberof DropDown
*/
getButtonBadgeType(): string | null {
const activeVideoTrackHeight: Object = this.props.player.getActiveTracks().video.height;
return activeVideoTrackHeight ? this.getLabelBadgeType(activeVideoTrackHeight) : null;
}

/**
Expand Down Expand Up @@ -303,27 +313,38 @@ class Settings extends Component {
label: t.label,
active: !player.isAdaptiveBitrateEnabled() && t.active,
value: t,
badge: this.getBadge(t.height)
badgeType: this.getLabelBadgeType(t.height)
}));

// Progressive playback doesn't support auto
if (qualityOptions.length > 1 && player.streamType !== 'progressive') {
const activeTrack: Object = qualityOptions.find(track => track.value.active === true).value;
qualityOptions.unshift({
label: this.props.qualityAutoLabelText,
dropdownOptions: {
label: this.props.qualityAutoLabelText + ' - ' + activeTrack.label,
badgeType: this.getLabelBadgeType(activeTrack.height)
},
active: player.isAdaptiveBitrateEnabled(),
value: 'auto'
});
}

if (qualityOptions.length <= 1 && speedOptions.length <= 1) return undefined;
if (isLive && qualityOptions.length <= 1) return undefined;
const buttonBadgeType: string = this.getButtonBadgeType() || '';
return (
<ButtonControl name={COMPONENT_NAME} ref={c => (c ? (this._controlSettingsElement = c) : undefined)}>
<Tooltip label={props.buttonLabel}>
<Button
tabIndex="0"
aria-label={props.buttonLabel}
className={this.state.smartContainerOpen ? [style.controlButton, style.active].join(' ') : style.controlButton}
className={[
style.controlButton,
style.buttonBadge,
BadgeType[buttonBadgeType + 'Active'],
this.state.smartContainerOpen ? style.active : ''
].join(' ')}
onClick={this.onControlButtonClick}>
<Icon type={IconType.Settings} />
</Button>
Expand Down
2 changes: 1 addition & 1 deletion src/components/smart-container/_smart-container.scss
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@
align-self: flex-end;
}
.dropdown {
span {
span:not(.badge-icon) {
max-width: 100px;
overflow: hidden;
text-overflow: ellipsis;
Expand Down
9 changes: 9 additions & 0 deletions src/styles/_control-button.scss
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,15 @@
padding: 0;
cursor: pointer;

&.button-badge {
position: relative;

&:after {
top: -4px;
right: -1px;
}
}

i {
width: 32px;
height: 32px;
Expand Down
24 changes: 23 additions & 1 deletion src/styles/_dropdown.scss
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,17 @@
}

.dropdown {
position: relative;
font-size: 15px;

.label-badge {
position: relative;

&:after {
top: -5px;
right: -18px;
}
}

&.active {
.dropdown-menu {
display: block;
Expand All @@ -32,6 +40,7 @@
color: #fff;
cursor: pointer;
padding-left: 20px;
display: flex;

.icon {
width: 16px;
Expand All @@ -41,6 +50,12 @@
transition: 150ms transform;
will-change: transform;
}

& > .label-badge {
&.badge-icon {
margin-right: 13px;
}
}
}
}

Expand All @@ -58,6 +73,7 @@
overflow-y: auto;
font-size: 15px;
text-align: left;
font-weight: normal;

&.top {
margin-bottom: 10px;
Expand Down Expand Up @@ -113,5 +129,11 @@
.menu-icon-container {
opacity: 0;
}

& > .label-badge {
&.badge-icon {
margin-right: 5px;
}
}
}
}
1 change: 0 additions & 1 deletion src/styles/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,3 @@
@import '../components/interactive-area/_interactive-area';
@import '../components/video-area/video-area';
@import '../components/gui-area/gui-area';
@import '../components/badge/badge';

0 comments on commit 0535fb0

Please sign in to comment.