Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RNMobile] Android player controls #29990

Closed
wants to merge 38 commits into from
Closed
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
7a5839e
Disable player controls on Android
jhnstn Apr 7, 2023
c144a88
Add player icons
jhnstn Apr 7, 2023
c1b5662
Add manual controls to Android player
jhnstn Apr 7, 2023
9e59021
changelog
jhnstn Apr 7, 2023
4c51bb5
[not verified] Simplify how the embed is rendered
jhnstn Apr 12, 2023
937ea9d
[not verified] Continue requesting the preview until its ready to render
jhnstn Apr 12, 2023
213f2c5
[not verified] changelog
jhnstn Apr 13, 2023
850139d
Add attempts to VP url to force a request
jhnstn Apr 14, 2023
ebfe916
Update preview update flow
jhnstn Apr 18, 2023
26d8150
fix loading copy
jhnstn Apr 18, 2023
ead3b29
Merge branch 'rnmobile/fix/resize-after-upload' into rnmobile/add-and…
jhnstn Apr 19, 2023
722bd10
Move player controls to a seperate component
jhnstn Apr 20, 2023
ebbdd5f
Hide controls while video is processed
jhnstn Apr 20, 2023
d801b7a
Merge branch 'trunk' into rnmobile/add-android-controls
jhnstn Apr 20, 2023
8454aba
Update embed loading screen
jhnstn Apr 21, 2023
455389e
Prevent white background flicker
jhnstn Apr 21, 2023
3c024d3
Add changelog
jhnstn Apr 21, 2023
ec07fea
Add dynamic height to loading views
jhnstn Apr 25, 2023
97dd1d3
Merge branch 'trunk' into rnmobile/update/vp-loading-screen
jhnstn Apr 25, 2023
eb41925
Rename constant
jhnstn Apr 25, 2023
8052a31
fix linting
jhnstn Apr 25, 2023
a415583
Merge branch 'rnmobile/update/vp-loading-screen' into rnmobile/add-an…
jhnstn Apr 25, 2023
2a119e1
Update version
jhnstn Apr 25, 2023
62b3add
Merge branch 'rnmobile/update/vp-loading-screen' into rnmobile/add-an…
jhnstn Apr 25, 2023
cca47df
Use overlay for loading screen
jhnstn Apr 26, 2023
e4b69eb
Clean up unused state
jhnstn Apr 26, 2023
80888eb
Linting
jhnstn Apr 26, 2023
52053c9
Merge branch 'rnmobile/update/vp-loading-screen' into rnmobile/add-an…
jhnstn Apr 26, 2023
6b29912
Update enable controls boolean
jhnstn Apr 26, 2023
c35d99a
Remove console statement
jhnstn Apr 26, 2023
4905b22
Merge branch 'trunk' into rnmobile/add-android-controls
jhnstn Apr 28, 2023
c4d0115
Fix syntax & linting issues
fluiddot Apr 28, 2023
732f7d9
Add ref to Sandbox
jhnstn Apr 28, 2023
fb35d68
Move player control icons
jhnstn Apr 28, 2023
b4311cc
Pause player when the block loses selection
jhnstn Apr 28, 2023
8be5571
Add empty dependency to onWindowMessage
jhnstn Apr 28, 2023
07e1395
Improve play ended state management
jhnstn Apr 28, 2023
83c0024
Start playing when pressing the rewind button
jhnstn Apr 28, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: fixed

Add manual controls to the player on Android
fluiddot marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/**
* WordPress dependencies
*/
import { Path, SVG } from '@wordpress/components';

export default (
<SVG width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<Path
d="M5 20C5 20.5523 5.44772 21 6 21H9C9.55229 21 10 20.5523 10 20L10 4C10 3.44772 9.55228 3 9 3H6C5.44771 3 5 3.44772 5 4V20Z M14 20C14 20.5523 14.4477 21 15 21H18C18.5523 21 19 20.5523 19 20V4C19 3.44772 18.5523 3 18 3H15C14.4477 3 14 3.44772 14 4L14 20Z"
fill="white"
/>
</SVG>
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/**
* WordPress dependencies
*/
import { Path, SVG } from '@wordpress/components';

export default (
<SVG width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<Path
d="M4.75725 2.075C4.60279 1.97745 4.41041 1.97489 4.25365 2.06832C4.09689 2.16174 4 2.3367 4 2.52632V21.4737C4 21.6633 4.09689 21.8383 4.25365 21.9317C4.41041 22.0251 4.60279 22.0226 4.75725 21.925L19.7573 12.4513C19.9079 12.3562 20 12.1849 20 12C20 11.8151 19.9079 11.6438 19.7573 11.5487L4.75725 2.075Z"
fill="white"
/>
</SVG>
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/**
* WordPress dependencies
*/
import { Path, SVG } from '@wordpress/components';

export default (
<SVG width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<Path
d="M8.73333 5L5 9.34483M5 9.34483L8.73333 13.2069M5 9.34483C8.11111 9.34483 12.2778 9.34483 15.2644 9.34483C15.2644 9.34483 19 9.34483 19 14.1724C19 19 16.6667 19 11.5333 19"
stroke="white"
stroke-width="1.5"
/>
</SVG>
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/**
* WordPress dependencies
*/
import { Icon } from '@wordpress/components';
import { useState, useEffect, useCallback, useRef } from '@wordpress/element';
/**
* External dependencies
*/
import { View, Pressable } from 'react-native';
/**
* Internal dependencies
*/
import PauseIcon from './icons/icon-pause.native.js';
import PlayIcon from './icons/icon-play.native.js';
import ReplayIcon from './icons/icon-replay.native.js';
import style from './style.scss';

const PlayerControls = ( { isSelected, playEnded, onToggle } ) => {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When I first started working on this, the Player was still a small component. It ballooned a bit more after I added the preview logic. Since the player controls are 1. Android only and 2. hopefully a temporary work around I opted to put as much of the logic into a separate component.

const [ showControlIcon, setShowControlIcon ] = useState( true );
const [ isPlaying, setIsPlaying ] = useState( false );
const [ isFinishedPlaying, setIsFinishedPlaying ] = useState( playEnded );

// Update the state when the video ends.
useEffect( () => {
if ( playEnded ) {
setShowControlIcon( true );
}
setIsFinishedPlaying( playEnded );
}, [ playEnded ] );

// Clear out the state when deselected.
useEffect( () => {
if ( ! isSelected ) {
setIsPlaying( false );
setIsFinishedPlaying( false );
}
}, [ isSelected ] );

// Hide the play/pause button after a short delay.
const hidePauseTimer = useRef();
useEffect( () => {
if ( isPlaying ) {
hidePauseTimer.current = setTimeout( () => {
! isFinishedPlaying && setShowControlIcon( false );
}, 800 );
}
return () => {
clearTimeout( hidePauseTimer.current );
};
}, [ isPlaying ] );

const togglePlayState = useCallback( () => {
setIsFinishedPlaying( false );
setShowControlIcon( true );

onToggle( isPlaying ? 'pause' : 'play' );
setIsPlaying( ! isPlaying );
}, [ isPlaying ] );

let icon = PlayIcon;

if ( isPlaying ) {
icon = PauseIcon;
}

if ( isFinishedPlaying ) {
icon = ReplayIcon;
}

const iconStyle = style[ 'videopress-player__overlay-controls-button-icon' ];
const renderButton = () => (
<View style={ style[ 'videopress-player__overlay-controls-button' ] }>
{ showControlIcon && <Icon icon={ icon } size={ iconStyle.size } style={ iconStyle } /> }
</View>
);

if ( ! isSelected ) {
return renderButton();
}

return (
<View style={ style[ 'videopress-player__overlay-controls' ] }>
<Pressable onPress={ togglePlayState }>{ renderButton() }</Pressable>
</View>
);
};

export default PlayerControls;
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
.videopress-player__overlay-controls {
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 100%;
z-index: 1;
}

.videopress-player__overlay-controls-button {
display: flex;
justify-content: center;
align-items: center;
height: 100%;
width: 100%;
}

.videopress-player__overlay-controls-button-icon {
width: 64px;
height: 64px;
size: 64;
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,15 @@ import { __ } from '@wordpress/i18n';
/**
* External dependencies
*/
import { View, Text } from 'react-native';
import { View, Text, Platform } from 'react-native';
/**
* Internal dependencies
*/
import getMediaToken from '../../../../../lib/get-media-token/index.native';
import { getVideoPressUrl } from '../../../../../lib/url';
import { usePreview } from '../../../../hooks/use-preview';
import addTokenIntoIframeSource from '../../../../utils/add-token-iframe-source';
import PlayerControls from './controls';
import style from './style.scss';

const VIDEO_PREVIEW_ATTEMPTS_LIMIT = 10;
Expand Down Expand Up @@ -51,6 +52,15 @@ export default function Player( { isSelected, attributes } ) {
const [ previewCheckAttempts, setPreviewCheckAttempts ] = useState( 0 );
const previewCheckTimer = useRef();

// Used for Android controls only
const [ playEnded, setPlayEnded ] = useState( false );
const playerRef = useRef();
const onToggleEvent = useCallback( event => {
playerRef.current?.injectJavaScript( `
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I considered passing the playerRef down to the control component but it got wonky trying to manage the events coming from the embed. I then decided to let the Player continue to own the embed

document?.querySelector('iframe')?.contentWindow.postMessage({event: 'videopress_action_${ event }'}, '*');
` );
}, [] );

// Fetch token for a VideoPress GUID
useEffect( () => {
if ( guid ) {
Expand All @@ -62,7 +72,7 @@ export default function Player( { isSelected, attributes } ) {

const videoPressUrl = getVideoPressUrl( guid, {
autoplay: false, // Note: Autoplay is disabled to prevent the video from playing fullscreen when loading the editor.
controls,
controls: Platform.OS === 'ios',
loop,
muted,
playsinline,
Expand Down Expand Up @@ -98,24 +108,58 @@ export default function Player( { isSelected, attributes } ) {
return () => clearTimeout( previewCheckTimer.current );
}, [ preview, isPlayerLoaded, isRequestingEmbedPreview, previewCheckAttempts ] );

const onSandboxMessage = useCallback( message => {
if ( message.event === 'videopress_loading_state' && message.state === 'loaded' ) {
setIsPlayerLoaded( true );
const onSandboxMessage = message => {
switch ( message.event ) {
case 'videopress_loading_state':
if ( message.state === 'loaded' ) {
setIsPlayerLoaded( true );
}
break;

// Events use for the Android controls
case 'videopress_ended':
setPlayEnded( true );
break;
case 'videopress_playing':
if ( playEnded ) {
setPlayEnded( false );
}
break;
}
}, [] );
};

const loadingStyle = {};
if ( ! isPreviewReady( preview ) ) {
loadingStyle.height = 250;
}

const renderOverlay = () => {
// Show custom controls on Android only
if ( Platform.OS === 'android' && isPlayerLoaded ) {
return (
<View style={ style[ 'videopress-player__overlay' ] }>
<PlayerControls
isSelected={ isSelected }
playEnded={ playEnded }
onToggle={ onToggleEvent }
/>
</View>
);
}

if ( ! isSelected ) {
return <View style={ style[ 'videopress-player__overlay' ] } />;
}
};

const renderEmbed = () => {
if ( html ) {
return (
<SandBox
html={ html }
onWindowEvents={ { message: onSandboxMessage } }
viewportProps="user-scalable=0"
ref={ playerRef }
/>
);
}
Expand All @@ -124,7 +168,7 @@ export default function Player( { isSelected, attributes } ) {

return (
<View style={ [ style[ 'videopress-player' ], loadingStyle ] }>
{ ! isSelected && <View style={ style[ 'videopress-player__overlay' ] } /> }
{ renderOverlay() }
{ renderEmbed() }
</View>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,26 @@
width: 100%;
z-index: 1;
}

.videopress-player__overlay-controls {
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 100%;
z-index: 1;
}

.videopress-player__overlay-controls-button {
display: flex;
justify-content: center;
align-items: center;
height: 100%;
width: 100%;
}

.videopress-player__overlay-controls-button-icon {
width: 64px;
height: 64px;
size: 64;
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { store as noticesStore } from '@wordpress/notices';
/**
* External dependencies
*/
import React from 'react';
fluiddot marked this conversation as resolved.
Show resolved Hide resolved
import { View } from 'react-native';
/**
* Internal dependencies
Expand Down