diff --git a/ui/component/autoplayCountdown/index.js b/ui/component/autoplayCountdown/index.js new file mode 100644 index 000000000..e7e53d559 --- /dev/null +++ b/ui/component/autoplayCountdown/index.js @@ -0,0 +1,22 @@ +import * as SETTINGS from 'constants/settings'; +import { connect } from 'react-redux'; +import { makeSelectClaimForUri } from 'lbry-redux'; +import { makeSelectNextUnplayedRecommended } from 'redux/selectors/content'; +import { makeSelectClientSetting } from 'redux/selectors/settings'; +import RecommendedVideos from './view'; + +const select = (state, props) => { + const nextRecommendedUri = makeSelectNextUnplayedRecommended(props.uri)(state); + return { + nextRecommendedUri, + nextRecommendedClaim: makeSelectClaimForUri(nextRecommendedUri)(state), + autoplay: makeSelectClientSetting(SETTINGS.AUTOPLAY)(state), + }; +}; + +const perform = (dispatch, ownProps) => ({}); + +export default connect( + select, + perform +)(RecommendedVideos); diff --git a/ui/component/autoplayCountdown/view.jsx b/ui/component/autoplayCountdown/view.jsx new file mode 100644 index 000000000..645cbed1f --- /dev/null +++ b/ui/component/autoplayCountdown/view.jsx @@ -0,0 +1,66 @@ +// @flow +import React from 'react'; +import Button from 'component/button'; +import UriIndicator from 'component/uriIndicator'; +import { formatLbryUrlForWeb } from 'util/url'; +import { withRouter } from 'react-router'; + +type Props = { + history: { push: string => void }, + nextRecommendedClaim: ?StreamClaim, + nextRecommendedUri: string, +}; + +function AutoplayCountdown(props: Props) { + const { + nextRecommendedUri, + nextRecommendedClaim, + history: { push }, + } = props; + const nextTitle = nextRecommendedClaim && nextRecommendedClaim.value && nextRecommendedClaim.value.title; + const [timer, setTimer] = React.useState(5); + const [timerCanceled, setTimerCanceled] = React.useState(false); + + let navigateUrl; + if (nextTitle) { + navigateUrl = formatLbryUrlForWeb(nextRecommendedUri); + } + + React.useEffect(() => { + let interval; + if (!timerCanceled) { + interval = setInterval(() => { + const newTime = timer - 1; + if (newTime === 0) { + push(navigateUrl); + } else { + setTimer(timer - 1); + } + }, 1000); + } + return () => { + clearInterval(interval); + }; + }, [timer, navigateUrl, push, timerCanceled]); + + if (timerCanceled) { + return null; + } + + return ( + <div className="video-overlay__wrapper"> + <div className="video-overlay__subtitle">Up Next</div> + <div className="video-overlay__title">{nextTitle}</div> + <UriIndicator link uri={nextRecommendedUri} /> + + <div className="video-overlay__actions"> + <div className="video-overlay__subtitle">Playing in {timer} seconds</div> + <div className="section__actions--centered"> + <Button label={__('Cancel')} button="secondary" onClick={() => setTimerCanceled(true)} /> + </div> + </div> + </div> + ); +} + +export default withRouter(AutoplayCountdown); diff --git a/ui/component/fileRender/index.js b/ui/component/fileRender/index.js index f99740d5a..99f1979e1 100644 --- a/ui/component/fileRender/index.js +++ b/ui/component/fileRender/index.js @@ -8,13 +8,13 @@ import { makeSelectDownloadPathForUri, makeSelectFileNameForUri, } from 'lbry-redux'; -import { THEME, AUTOPLAY } from 'constants/settings'; +import * as SETTINGS from 'constants/settings'; import { makeSelectClientSetting } from 'redux/selectors/settings'; -import { makeSelectNextUnplayedRecommended, makeSelectIsText } from 'redux/selectors/content'; +import { makeSelectIsText } from 'redux/selectors/content'; import FileRender from './view'; const select = (state, props) => ({ - currentTheme: makeSelectClientSetting(THEME)(state), + currentTheme: makeSelectClientSetting(SETTINGS.THEME)(state), claim: makeSelectClaimForUri(props.uri)(state), mediaType: makeSelectMediaTypeForUri(props.uri)(state), thumbnail: makeSelectThumbnailForUri(props.uri)(state), @@ -22,8 +22,6 @@ const select = (state, props) => ({ downloadPath: makeSelectDownloadPathForUri(props.uri)(state), fileName: makeSelectFileNameForUri(props.uri)(state), streamingUrl: makeSelectStreamingUrlForUri(props.uri)(state), - autoplay: makeSelectClientSetting(AUTOPLAY)(state), - nextUnplayed: makeSelectNextUnplayedRecommended(props.uri)(state), isText: makeSelectIsText(props.uri)(state), }); diff --git a/ui/component/fileRender/view.jsx b/ui/component/fileRender/view.jsx index cf00f12f8..e2250db0b 100644 --- a/ui/component/fileRender/view.jsx +++ b/ui/component/fileRender/view.jsx @@ -8,7 +8,7 @@ import ImageViewer from 'component/viewers/imageViewer'; import AppViewer from 'component/viewers/appViewer'; import Button from 'component/button'; import { withRouter } from 'react-router-dom'; -import { formatLbryUrlForWeb } from 'util/url'; +import AutoplayCountdown from 'component/autoplayCountdown'; // @if TARGET='web' import { generateStreamUrl } from 'util/lbrytv'; // @endif @@ -36,16 +36,20 @@ type Props = { currentTheme: string, downloadPath: string, fileName: string, - autoplay: boolean, - nextFileToPlay: string, - nextUnplayed: string, - history: { push: string => void }, }; -class FileRender extends React.PureComponent<Props> { +type State = { + showAutoplayCountdown: boolean, +}; + +class FileRender extends React.PureComponent<Props, State> { constructor(props: Props) { super(props); + this.state = { + showAutoplayCountdown: false, + }; + (this: any).escapeListener = this.escapeListener.bind(this); (this: any).onEndedCb = this.onEndedCb.bind(this); } @@ -73,10 +77,7 @@ class FileRender extends React.PureComponent<Props> { } onEndedCb() { - const { autoplay, nextUnplayed, history } = this.props; - if (autoplay && nextUnplayed) { - history.push(formatLbryUrlForWeb(nextUnplayed)); - } + this.setState({ showAutoplayCountdown: true }); } renderViewer() { @@ -188,10 +189,12 @@ class FileRender extends React.PureComponent<Props> { } render() { - const { isText } = this.props; + const { isText, uri } = this.props; + const { showAutoplayCountdown } = this.state; return ( <div className={classnames('file-render', { 'file-render--document': isText })}> + {showAutoplayCountdown && <AutoplayCountdown uri={uri} />} <Suspense fallback={<div />}>{this.renderViewer()}</Suspense> </div> ); diff --git a/ui/component/viewers/videoViewer/view.jsx b/ui/component/viewers/videoViewer/view.jsx index b54858be9..cd48c50ed 100644 --- a/ui/component/viewers/videoViewer/view.jsx +++ b/ui/component/viewers/videoViewer/view.jsx @@ -38,7 +38,6 @@ type Props = { changeVolume: number => void, savePosition: (string, number) => void, changeMute: boolean => void, - setPlayingUri: (string | null) => void, source: string, contentType: string, thumbnail: string, @@ -48,18 +47,7 @@ type Props = { }; function VideoViewer(props: Props) { - const { - contentType, - source, - setPlayingUri, - onEndedCB, - changeVolume, - changeMute, - volume, - muted, - thumbnail, - claim, - } = props; + const { contentType, source, onEndedCB, changeVolume, changeMute, volume, muted, thumbnail, claim } = props; const claimId = claim && claim.claim_id; const videoRef = useRef(); const isAudio = contentType.includes('audio'); @@ -85,13 +73,13 @@ function VideoViewer(props: Props) { } function doEnded() { - // clear position - setPlayingUri(null); onEndedCB(); } + function doPause(e: Event) { // store position e.target.currentTime } + function doVolume(e: Event) { // $FlowFixMe volume is missing in EventTarget changeVolume(e.target.volume); diff --git a/ui/scss/component/_file-render.scss b/ui/scss/component/_file-render.scss index c96698934..1613db1d5 100644 --- a/ui/scss/component/_file-render.scss +++ b/ui/scss/component/_file-render.scss @@ -153,3 +153,36 @@ } } } + +.video-overlay__wrapper { + position: absolute; + left: auto; + right: auto; + height: 100%; + width: 100%; + background-color: rgba(0, 0, 0, 0.9); + z-index: 999; + color: var(--color-white); + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + + .button--uri-indicator { + color: var(--color-gray-3); + } +} + +.video-overlay__title { + @extend .section__title; + margin-top: var(--spacing-medium); + margin-bottom: var(--spacing-small); +} + +.video-overlay__subtitle { + color: var(--color-gray-3); + margin: var(--spacing-medium) 0; +} +.video-overlay__actions { + margin-top: var(--spacing-large); +} diff --git a/ui/scss/component/section.scss b/ui/scss/component/section.scss index bfe266c44..16c3584c5 100644 --- a/ui/scss/component/section.scss +++ b/ui/scss/component/section.scss @@ -92,6 +92,11 @@ margin-top: var(--spacing-medium); } +.section__actions--centered { + @extend .section__actions; + justify-content: center; +} + @media (max-width: $breakpoint-small) { .section__actions { flex-wrap: wrap;