diff --git a/src/components/progress-indicator/_progress-indicator.scss b/src/components/progress-indicator/_progress-indicator.scss new file mode 100644 index 000000000..b9ae98143 --- /dev/null +++ b/src/components/progress-indicator/_progress-indicator.scss @@ -0,0 +1,37 @@ +.player .seek-bar { + &.ad-break { + cursor: initial; + + .progress-bar .progress { + background-color: $ads-color; + } + } + + &.live { + .progress-bar .progress { + background-color: $live-color; + } + } + + .progress-bar { + .progress { + position: absolute; + top: 0; + left: 0; + height: 100%; + border-radius: inherit; + background-color: $primary-color; + } + .buffered { + position: absolute; + top: 0; + left: 0; + height: 100%; + border-radius: inherit; + background-color: rgba(255, 255, 255, 0.3); + } + } +} + + + diff --git a/src/components/progress-indicator/index.js b/src/components/progress-indicator/index.js new file mode 100644 index 000000000..0208925d3 --- /dev/null +++ b/src/components/progress-indicator/index.js @@ -0,0 +1 @@ +export {ProgressIndicator} from './progress-indicator'; diff --git a/src/components/progress-indicator/progress-indicator.js b/src/components/progress-indicator/progress-indicator.js new file mode 100644 index 000000000..0044033a4 --- /dev/null +++ b/src/components/progress-indicator/progress-indicator.js @@ -0,0 +1,65 @@ +//@flow +import {h, Fragment, Component} from 'preact'; +import style from '../../styles/style.scss'; +import {connect} from 'react-redux'; +import {withPlayer} from '../player'; + +/** + * mapping state to props + * @param {*} state - redux store state + * @returns {Object} - mapped state to this component + */ +const mapStateToProps = state => ({ + dataLoaded: state.engine.dataLoaded, + currentTime: state.seekbar.currentTime, + duration: state.engine.duration, + isMobile: state.shell.isMobile, + adBreak: state.engine.adBreak +}); + +/** + * ProgressBar component + * + * @class ProgressIndicator + * @extends {Component} + */ +@connect(mapStateToProps) +@withPlayer +class ProgressIndicator extends Component { + /** + * get current buffered percent from the player + * + * @returns {number} - current buffered percent + * @memberof ProgressIndicator + */ + getBufferedPercent(): number { + const {player} = this.props; + if (this.props.duration > 0 && player.buffered.length > 0) { + const buffered = player.isLive() ? player.buffered.end(0) - player.getStartTimeOfDvrWindow() : player.buffered.end(0); + const bufferedPercent = (buffered / this.props.duration) * 100; + return bufferedPercent < 100 ? bufferedPercent : 100; + } + return 0; + } + + /** + * render component + * + * @param {*} props - component props + * @returns {React$Element} - component + * @memberof ProgressIndicator + */ + render(props: any): React$Element { + const bufferedWidth = `${Math.round(this.getBufferedPercent())}%`; + const progressWidth = `${props.player.isLive() && props.player.isOnLiveEdge() ? 100 : (props.currentTime / props.duration) * 100}%`; + return ( + +
+ {props.dataLoaded ?
: undefined} + + ); + } +} + +ProgressIndicator.displayName = 'ProgressIndicator'; +export {ProgressIndicator}; diff --git a/src/components/seekbar/_seekbar.scss b/src/components/seekbar/_seekbar.scss index 40b14fac4..9bb2df92f 100644 --- a/src/components/seekbar/_seekbar.scss +++ b/src/components/seekbar/_seekbar.scss @@ -21,16 +21,6 @@ &.ad-break { cursor: initial; - - .progress-bar .progress { - background-color: $ads-color; - } - } - - &.live { - .progress-bar .progress { - background-color: $live-color; - } } .progress-bar { @@ -40,19 +30,16 @@ border-radius: $progress-bar-border-radius; position: absolute; transition: height 200ms ease, transform 200ms ease; - - .progress { - position: absolute; - top: 0; - left: 0; - height: 100%; - border-radius: inherit; - background-color: $primary-color; + &.chapters { + background-color: transparent; + .virtual-progress-indicator { + height: 8px; + transform: translateY(-2px); + } } .virtual-progress { display: none; } - .buffered, .virtual-progress { position: absolute; top: 0; @@ -61,10 +48,6 @@ border-radius: inherit; } - .buffered { - background-color: rgba(255, 255, 255, 0.3); - } - .virtual-progress-indicator { width: 1px; height: 100%; @@ -77,7 +60,7 @@ cursor: pointer; display: block; top: -8px + calc($progress-bar-height / 2); - right: -8px; + left: -6px; border-radius: 8px; height: 16px; width: 16px; @@ -132,7 +115,7 @@ .player:not(.touch):not(.size-ty) .seek-bar { &:hover, &.hover { - .progress-bar { + .progress-bar:not(.chapters) { height: 2 * $progress-bar-height; border-radius: 2 * $progress-bar-border-radius; transform: translateY(calc($progress-bar-height / -2)); diff --git a/src/components/seekbar/seekbar.js b/src/components/seekbar/seekbar.js index b1087301d..bf50d3777 100644 --- a/src/components/seekbar/seekbar.js +++ b/src/components/seekbar/seekbar.js @@ -15,6 +15,7 @@ import {PlayerArea} from '../player-area'; import {withEventManager} from 'event/with-event-manager'; import {FakeEvent} from 'event/fake-event'; import {SeekBarPreview} from '../seekbar-preview'; +import {ProgressIndicator} from '../progress-indicator'; /** * mapping state to props @@ -26,7 +27,8 @@ const mapStateToProps = state => ({ isMobile: state.shell.isMobile, previewHoverActive: state.seekbar.previewHoverActive, hidePreview: state.seekbar.hidePreview, - hideTimeBubble: state.seekbar.hideTimeBubble + hideTimeBubble: state.seekbar.hideTimeBubble, + segments: state.seekbar.segments }); const COMPONENT_NAME = 'SeekBar'; @@ -448,22 +450,6 @@ class SeekBar extends Component { return time; } - /** - * get current buffered percent from the player - * - * @returns {number} - current buffered percent - * @memberof SeekBar - */ - getBufferedPercent(): number { - const {player} = this.props; - if (this.props.duration > 0 && player.buffered.length > 0) { - const buffered = player.isLive() ? player.buffered.end(0) - player.getStartTimeOfDvrWindow() : player.buffered.end(0); - const bufferedPercent = (buffered / this.props.duration) * 100; - return bufferedPercent < 100 ? bufferedPercent : 100; - } - return 0; - } - /** * get the left position the frame preview element should be in * @@ -561,8 +547,7 @@ class SeekBar extends Component { */ render(props: any, state: Object): React$Element { const virtualProgressWidth = `${(props.virtualTime / props.duration) * 100}%`; - const progressWidth = `${props.forceFullProgress ? 100 : (props.currentTime / props.duration) * 100}%`; - const bufferedWidth = `${Math.round(this.getBufferedPercent())}%`; + const scrubberProgressPosition = `${(props.currentTime / props.duration) * this._seekBarElement?.clientWidth}px`; const seekbarStyleClass = [style.seekBar]; if (props.adBreak) seekbarStyleClass.push(style.adBreak); if (props.isDvr) seekbarStyleClass.push(style.live); @@ -593,12 +578,12 @@ class SeekBar extends Component { {this.renderFramePreview()} {this.renderTimeBubble()} -
- {props.dataLoaded ? ( -
- {props.adBreak ? undefined : } + + {props.adBreak ? undefined : ( +
+
- ) : undefined} + )}
diff --git a/src/reducers/seekbar.js b/src/reducers/seekbar.js index 47d799e01..162035875 100644 --- a/src/reducers/seekbar.js +++ b/src/reducers/seekbar.js @@ -7,7 +7,10 @@ export const types = { UPDATE_HIDE_SEEKBAR_PREVIEW: 'seekbar/UPDATE_HIDE_SEEKBAR_PREVIEW', UPDATE_HIDE_SEEKBAR_TIME_BUBBLE: 'seekbar/UPDATE_HIDE_SEEKBAR_TIME_BUBBLE', UPDATE_CURRENT_TIME: 'seekbar/UPDATE_CURRENT_TIME', - UPDATE_VIRTUAL_TIME: 'seekbar/UPDATE_VIRTUAL_TIME' + UPDATE_VIRTUAL_TIME: 'seekbar/UPDATE_VIRTUAL_TIME', + UPDATE_HOVERED_SEGMENT: 'seekbar/UPDATE_HOVERED_SEGMENT', + UPDATE_SEEKBAR_SEGMENTS: 'seekbar/UPDATE_SEEKBAR_SEGMENTS', + UPDATE_SEGMENT_END_TIME: 'seekbar/UPDATE_SEGMENT_END_TIME' }; export const initialState = { @@ -18,7 +21,8 @@ export const initialState = { previewHoverActive: false, clientRect: {x: 0, y: 0, width: 0, height: 0, top: 0, right: 0, bottom: 0, left: 0}, hidePreview: false, - hideTimeBubble: false + hideTimeBubble: false, + segments: [] }; export default (state: Object = initialState, action: Object) => { @@ -71,6 +75,24 @@ export default (state: Object = initialState, action: Object) => { virtualTime: action.virtualTime }; + case types.UPDATE_HOVERED_SEGMENT: + return { + ...state, + segments: state.segments.map(segment => (segment.id === action.id ? {...segment, isHovered: action.isHovered} : segment)) + }; + + case types.UPDATE_SEEKBAR_SEGMENTS: + return { + ...state, + segments: action.segments + }; + + case types.UPDATE_SEGMENT_END_TIME: + return { + ...state, + segments: state.segments.map(segment => (segment.id === action.id ? {...segment, endTime: action.endTime} : segment)) + }; + default: return state; } @@ -99,5 +121,8 @@ export const actions = { hideTimeBubble }), updateCurrentTime: (currentTime: number) => ({type: types.UPDATE_CURRENT_TIME, currentTime}), - updateVirtualTime: (virtualTime: number) => ({type: types.UPDATE_VIRTUAL_TIME, virtualTime}) + updateVirtualTime: (virtualTime: number) => ({type: types.UPDATE_VIRTUAL_TIME, virtualTime}), + updateHoveredSegment: (id: string, isHovered: boolean) => ({type: types.UPDATE_HOVERED_SEGMENT, id, isHovered}), + updateSegmentEndTime: (id: string, endTime: number) => ({type: types.UPDATE_SEGMENT_END_TIME, id, endTime}), + updateSeekbarSegments: (segments: any[]) => ({type: types.UPDATE_SEEKBAR_SEGMENTS, segments}) }; diff --git a/src/styles/style.scss b/src/styles/style.scss index b01e46bb4..224b6bac5 100644 --- a/src/styles/style.scss +++ b/src/styles/style.scss @@ -21,6 +21,7 @@ @import '../components/volume/volume'; @import '../components/fullscreen/fullscreen'; @import '../components/seekbar/seekbar'; +@import '../components/progress-indicator/progress-indicator'; @import '../components/seekbar-preview/seekbar-preview'; @import '../components/time-display/time-display'; @import '../components/video-playing-title/video-playing-title';