cleanup and fix flow issues

This commit is contained in:
Sean Yesmunt 2020-04-28 15:33:30 -04:00
parent 0754bb1a7f
commit 767c69e132
4 changed files with 53 additions and 26 deletions

View file

@ -2,7 +2,7 @@ import { connect } from 'react-redux';
import { makeSelectClaimForUri, makeSelectFileInfoForUri, makeSelectThumbnailForUri } from 'lbry-redux'; import { makeSelectClaimForUri, makeSelectFileInfoForUri, makeSelectThumbnailForUri } from 'lbry-redux';
import { doChangeVolume, doChangeMute, doAnalyticsView } from 'redux/actions/app'; import { doChangeVolume, doChangeMute, doAnalyticsView } from 'redux/actions/app';
import { selectVolume, selectMute } from 'redux/selectors/app'; import { selectVolume, selectMute } from 'redux/selectors/app';
import { savePosition, doSetPlayingUri } from 'redux/actions/content'; import { savePosition } from 'redux/actions/content';
import VideoViewer from './view'; import VideoViewer from './view';
import { withRouter } from 'react-router'; import { withRouter } from 'react-router';
import { doClaimEligiblePurchaseRewards } from 'lbryinc'; import { doClaimEligiblePurchaseRewards } from 'lbryinc';
@ -31,7 +31,6 @@ const perform = dispatch => ({
changeVolume: volume => dispatch(doChangeVolume(volume)), changeVolume: volume => dispatch(doChangeVolume(volume)),
savePosition: (uri, position) => dispatch(savePosition(uri, position)), savePosition: (uri, position) => dispatch(savePosition(uri, position)),
changeMute: muted => dispatch(doChangeMute(muted)), changeMute: muted => dispatch(doChangeMute(muted)),
setPlayingUri: uri => dispatch(doSetPlayingUri(uri)),
doAnalyticsView: (uri, timeToStart) => dispatch(doAnalyticsView(uri, timeToStart)), doAnalyticsView: (uri, timeToStart) => dispatch(doAnalyticsView(uri, timeToStart)),
claimRewards: () => dispatch(doClaimEligiblePurchaseRewards()), claimRewards: () => dispatch(doClaimEligiblePurchaseRewards()),
}); });

View file

@ -1,17 +1,28 @@
// @flow // @flow
import React, { useEffect, useRef } from 'react'; import React, { useEffect, useRef } from 'react';
import classnames from 'classnames';
import videojs from 'video.js/dist/alt/video.core.novtt.min.js'; import videojs from 'video.js/dist/alt/video.core.novtt.min.js';
import 'video.js/dist/alt/video-js-cdn.min.css'; import 'video.js/dist/alt/video-js-cdn.min.css';
import eventTracking from 'videojs-event-tracking'; import eventTracking from 'videojs-event-tracking';
import isUserTyping from 'util/detect-typing'; import isUserTyping from 'util/detect-typing';
export type Player = {
on: (string, (any) => void) => void,
isFullscreen: () => boolean,
exitFullscreen: () => boolean,
requestFullscreen: () => boolean,
play: () => Promise<any>,
volume: (?number) => number,
muted: (?boolean) => boolean,
dispose: () => void,
currentTime: number => void,
};
type Props = { type Props = {
source: string, source: string,
sourceType: string, sourceType: string,
poster: boolean, poster: ?string,
autoplay: boolean, onPlayerReady: Player => void,
onPlayerReady: () => null,
onVolumeChange: () => null,
isAudio: boolean, isAudio: boolean,
startMuted: boolean, startMuted: boolean,
}; };
@ -27,12 +38,17 @@ type VideoJSOptions = {
}; };
const VIDEO_JS_OPTIONS: VideoJSOptions = { const VIDEO_JS_OPTIONS: VideoJSOptions = {
controls: true,
preload: 'auto', preload: 'auto',
playbackRates: [0.25, 0.5, 0.75, 1, 1.1, 1.25, 1.5, 1.75, 2], playbackRates: [0.25, 0.5, 0.75, 1, 1.1, 1.25, 1.5, 1.75, 2],
responsive: true, responsive: true,
controls: true,
}; };
const IS_IOS =
(/iPad|iPhone|iPod/.test(navigator.platform) ||
(navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1)) &&
!window.MSStream;
const F11_KEYCODE = 122; const F11_KEYCODE = 122;
const SPACE_BAR_KEYCODE = 32; const SPACE_BAR_KEYCODE = 32;
const SMALL_F_KEYCODE = 70; const SMALL_F_KEYCODE = 70;
@ -55,8 +71,9 @@ if (!Object.keys(videojs.getPlugins()).includes('eventTracking')) {
/* /*
properties for this component should be kept to ONLY those that if changed should REQUIRE an entirely new videojs element properties for this component should be kept to ONLY those that if changed should REQUIRE an entirely new videojs element
*/ */
export default React.memo(function VideoJs(props: Props) { export default React.memo<Props>(function VideoJs(props: Props) {
const { startMuted, source, sourceType, poster, isAudio, onPlayerReady } = props; const { startMuted, source, sourceType, poster, isAudio, onPlayerReady } = props;
let player: ?Player;
const containerRef = useRef(); const containerRef = useRef();
const videoJsOptions = { const videoJsOptions = {
...VIDEO_JS_OPTIONS, ...VIDEO_JS_OPTIONS,
@ -74,7 +91,7 @@ export default React.memo(function VideoJs(props: Props) {
videoJsOptions.muted = startMuted; videoJsOptions.muted = startMuted;
function handleKeyDown(e: KeyboardEvent) { function handleKeyDown(e: KeyboardEvent) {
const videoNode = containerRef.current && containerRef.current.querySelector('video, audio'); const videoNode: ?HTMLVideoElement = containerRef.current && containerRef.current.querySelector('video, audio');
if (!videoNode || isUserTyping()) { if (!videoNode || isUserTyping()) {
return; return;
@ -85,8 +102,8 @@ export default React.memo(function VideoJs(props: Props) {
} }
// Fullscreen toggle shortcuts // Fullscreen toggle shortcuts
if (e.keyCode === FULLSCREEN_KEYCODE || e.keyCode === F11_KEYCODE) { if (player && (e.keyCode === FULLSCREEN_KEYCODE || e.keyCode === F11_KEYCODE)) {
if (player && !player.isFullscreen()) { if (!player.isFullscreen()) {
player.requestFullscreen(); player.requestFullscreen();
} else { } else {
player.exitFullscreen(); player.exitFullscreen();
@ -111,21 +128,22 @@ export default React.memo(function VideoJs(props: Props) {
} }
} }
let player;
// Create the video element. Note that a new videojs instantiation will happen on *every* render, so do not add props to this component! // Create the video element. Note that a new videojs instantiation will happen on *every* render, so do not add props to this component!
useEffect(() => { useEffect(() => {
if (containerRef.current) { if (containerRef.current) {
const wrapper = document.createElement('div'); const wrapper = document.createElement('div');
wrapper.setAttribute('data-vjs-player', true); wrapper.setAttribute('data-vjs-player', 'true');
const el = document.createElement(isAudio ? 'audio' : 'video'); const el = document.createElement(isAudio ? 'audio' : 'video');
el.className = 'video-js'; el.className = 'video-js';
el.playsinline = true;
wrapper.appendChild(el); wrapper.appendChild(el);
// $FlowFixMe
containerRef.current.appendChild(wrapper); containerRef.current.appendChild(wrapper);
player = videojs(el, videoJsOptions, () => { player = videojs(el, videoJsOptions, () => {
if (player) {
onPlayerReady(player); onPlayerReady(player);
}
}); });
// fixes #3498 (https://github.com/lbryio/lbry-desktop/issues/3498) // fixes #3498 (https://github.com/lbryio/lbry-desktop/issues/3498)
@ -137,10 +155,14 @@ export default React.memo(function VideoJs(props: Props) {
return () => { return () => {
window.removeEventListener('keydown', handleKeyDown); window.removeEventListener('keydown', handleKeyDown);
if (player) {
player.dispose(); player.dispose();
}
}; };
} }
}); });
return <div className="video-js-parent" ref={containerRef} />; // $FlowFixMe
return <div className={classnames('video-js-parent', { 'video-js-parent--ios': IS_IOS })} ref={containerRef} />;
}); });

View file

@ -1,6 +1,7 @@
// @flow // @flow
import React, { useEffect, useState, useContext, useCallback } from 'react'; import React, { useEffect, useState, useContext, useCallback } from 'react';
import { stopContextMenu } from 'util/context-menu'; import { stopContextMenu } from 'util/context-menu';
import type { Player } from './internal/videojs';
import VideoJs from './internal/videojs'; import VideoJs from './internal/videojs';
import analytics from 'analytics'; import analytics from 'analytics';
@ -13,6 +14,8 @@ import FileViewerEmbeddedEnded from 'lbrytv/component/fileViewerEmbeddedEnded';
import FileViewerEmbeddedTitle from 'lbrytv/component/fileViewerEmbeddedTitle'; import FileViewerEmbeddedTitle from 'lbrytv/component/fileViewerEmbeddedTitle';
import LoadingScreen from 'component/common/loading-screen'; import LoadingScreen from 'component/common/loading-screen';
const PLAY_TIMEOUT_ERROR = 'play_timeout_error';
type Props = { type Props = {
position: number, position: number,
changeVolume: number => void, changeVolume: number => void,
@ -28,7 +31,6 @@ type Props = {
autoplayIfEmbedded: boolean, autoplayIfEmbedded: boolean,
doAnalyticsView: (string, number) => Promise<any>, doAnalyticsView: (string, number) => Promise<any>,
claimRewards: () => void, claimRewards: () => void,
setPlayingUri: (?string) => void,
}; };
/* /*
@ -52,7 +54,6 @@ function VideoViewer(props: Props) {
autoplayIfEmbedded, autoplayIfEmbedded,
doAnalyticsView, doAnalyticsView,
claimRewards, claimRewards,
setPlayingUri,
} = props; } = props;
const claimId = claim && claim.claim_id; const claimId = claim && claim.claim_id;
const isAudio = contentType.includes('audio'); const isAudio = contentType.includes('audio');
@ -109,7 +110,7 @@ function VideoViewer(props: Props) {
setIsPlaying(false); setIsPlaying(false);
} }
const onPlayerReady = useCallback(player => { const onPlayerReady = useCallback((player: Player) => {
if (!embedded) { if (!embedded) {
player.muted(muted); player.muted(muted);
player.volume(volume); player.volume(volume);
@ -120,22 +121,21 @@ function VideoViewer(props: Props) {
if (shouldPlay) { if (shouldPlay) {
const playPromise = player.play(); const playPromise = player.play();
const timeoutPromise = new Promise((resolve, reject) => { const timeoutPromise = new Promise((resolve, reject) => {
setTimeout(() => reject('test'), 1000); setTimeout(() => reject(PLAY_TIMEOUT_ERROR), 1000);
}); });
Promise.race([playPromise, timeoutPromise]).catch(error => { Promise.race([playPromise, timeoutPromise]).catch(error => {
if (error === 'test') { if (error === PLAY_TIMEOUT_ERROR) {
// The player promise hung // The player promise hung
// This is probably in firefox // This is probably in firefox
// Try playing again, it works? // The second attempt usually works
player.play(); player.play();
} else { } else {
// Autoplay was actually blocked by the browser. // Autoplay was actually blocked by the browser
// Reset everything so the user sees the thumbnail/play button and can start it on their own // Reset everything so the user sees the thumbnail/play button and can start it on their own
setIsLoading(false); setIsLoading(false);
setIsPlaying(false); setIsPlaying(false);
} }
console.log('error', error);
}); });
} }

View file

@ -288,6 +288,12 @@
width: 100%; width: 100%;
} }
.video-js-parent--ios {
.vjs-control-bar {
display: none;
}
}
// By default no video js play button // By default no video js play button
.vjs-big-play-button { .vjs-big-play-button {
display: none; display: none;