Skip to content

Commit

Permalink
[RNMobile] Audio Player UI for audio block (#27467)
Browse files Browse the repository at this point in the history
* Basics of Audio block working

* Add audio support to MediaUpload

* Add handling of file uploads and replace

* WPMediaLibrary support for Audio block

* Avoid removing media info on error state

* Linting

* Added an AUDIO file to the test requestMediaPickFromMediaLibrary func

* Fixed typo in ToolbarButton of Audio Block.

* Removed auto help behavior present on web that's not used on mobile.

* [Android] Wired the click of the Audio Media Library button.

* Added Audio media options for choosing audio file locally.

* [RNMobile] Audio Block: Proper caption field (#27689)

* Audio Player UI for audio block

* Add extension to styles import

* Show file name while loading and retry message on error

* Pass state props to audio player component

* Implement placeholder-ish player UI structure

* added styles for icon, title and subtitle

* added blue-wordpress as link color.

* added sizing based on design specs.

* Fixed object destructuring error.

* added icon styling state for upload in progress or upload failed.

* implemented error style and behavior.

* added styling for upload failed text.

* Override MediaUploadProgress styles

* Update filename extension spseration to handle two dots

* Update UI structure and styles

* Make retry message translateable

* Update snaphots

* Fix lint error

* Set initial file name

* Remove devOnly flag from audio block

* Increase tap target of button

* Add 1px between title and subtitle

* Align title, subtitle and button vertically

* On iOS use VideoPlayer to play audio files in-app

* Revert "On iOS use VideoPlayer to play audio files in-app"

This reverts commit b1eb8dd.

* [RNMobile] Audio Block -  Cancel and Retry Dialog (#28540)

Co-authored-by: Ceyhun Ozugur <[email protected]>

* Rename button title

* Add pill-shaped background to button

* Add padding to title

* Fix file title being empty when selecting from media library

* Use safeDecodeURI

* On iOS use VideoPlayer to play audio files in-app

* Decrease button font size

Co-authored-by: Joel Dean <[email protected]>

* Revert "Remove devOnly flag from audio block"

This reverts commit e6b5b6d.

* Add right padding to title container to fix error state

Co-authored-by: Joel Dean <[email protected]>
Co-authored-by: Ceyhun Ozugur <[email protected]>
  • Loading branch information
3 people authored Feb 1, 2021
1 parent 0ff5cc6 commit aadc511
Show file tree
Hide file tree
Showing 9 changed files with 408 additions and 13 deletions.
2 changes: 2 additions & 0 deletions packages/base-styles/_colors.native.scss
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ $light-opacity-light-700: rgba($white, 0.4);
$alert-yellow: #f0b849;
$alert-red: #d94f4f;
$alert-green: #4ab866;
$red-30: #f86368;
$red-40: #e65054;
$red-50: #d63638;

// Primary Accent (Blues)
$blue-wordpress: #0087be;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,12 +128,24 @@ export class MediaUploadProgress extends Component {
const progressBarStyle = [
styles.progressBar,
showSpinner || styles.progressBarHidden,
this.props.progressBarStyle,
];

return (
<View style={ styles.mediaUploadProgress } pointerEvents="box-none">
<View
style={ [
styles.mediaUploadProgress,
this.props.containerStyle,
] }
pointerEvents="box-none"
>
<View style={ progressBarStyle }>
{ showSpinner && <Spinner progress={ progress } /> }
{ showSpinner && (
<Spinner
progress={ progress }
style={ this.props.spinnerStyle }
/>
) }
</View>
{ renderContent( {
isUploadInProgress,
Expand Down
34 changes: 24 additions & 10 deletions packages/block-library/src/audio/edit.native.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/**
* External dependencies
*/
import { Text, TouchableWithoutFeedback } from 'react-native';
import { TouchableWithoutFeedback } from 'react-native';
import { isEmpty } from 'lodash';

/**
Expand All @@ -15,6 +15,7 @@ import {
withNotices,
ToolbarButton,
ToolbarGroup,
AudioPlayer,
} from '@wordpress/components';
import {
BlockCaption,
Expand All @@ -29,6 +30,11 @@ import { __, sprintf } from '@wordpress/i18n';
import { audio as icon, replace } from '@wordpress/icons';
import { useState } from '@wordpress/element';

/**
* Internal dependencies
*/
import styles from './style.scss';

const ALLOWED_MEDIA_TYPES = [ 'audio' ];

function AudioEdit( {
Expand Down Expand Up @@ -70,7 +76,6 @@ function AudioEdit( {
noticeOperations.createErrorNotice( message );
}

// const { setAttributes, isSelected, noticeUI } = this.props;
function onSelectAudio( media ) {
if ( ! media || ! media.url ) {
// in this case there was an error and we should continue in the editing state
Expand Down Expand Up @@ -133,17 +138,26 @@ function AudioEdit( {
onFinishMediaUploadWithSuccess={ onFileChange }
onFinishMediaUploadWithFailure={ onError }
onMediaUploadStateReset={ onFileChange }
renderContent={ ( { isUploadInProgress, isUploadFailed } ) => {
containerStyle={ styles.progressContainer }
progressBarStyle={ styles.progressBar }
spinnerStyle={ styles.spinner }
renderContent={ ( {
isUploadInProgress,
isUploadFailed,
retryMessage,
} ) => {
return (
<View>
<>
{ ! isCaptionSelected && getBlockControls( open ) }
{ getMediaOptions() }
<Text>
⏯ Audio Player goes here.{ ' ' }
{ isUploadInProgress && 'Uploading...' }
{ isUploadFailed && 'ERROR' }
</Text>
</View>
<AudioPlayer
isUploadInProgress={ isUploadInProgress }
isUploadFailed={ isUploadFailed }
retryMessage={ retryMessage }
attributes={ attributes }
isSelected={ isSelected }
/>
</>
);
} }
/>
Expand Down
13 changes: 13 additions & 0 deletions packages/block-library/src/audio/style.native.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
.progressContainer {
border-radius: 4px;
overflow: hidden;
}

.progressBar {
height: 4px;
margin-bottom: -4px;
}

.spinner {
height: 4px;
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,19 @@
exports[`File block renders file error state without crashing 1`] = `
<View
pointerEvents="box-none"
style={
Array [
undefined,
undefined,
]
}
>
<View
style={
Array [
undefined,
undefined,
undefined,
]
}
/>
Expand Down Expand Up @@ -195,12 +202,19 @@ exports[`File block renders file error state without crashing 1`] = `
exports[`File block renders file without crashing 1`] = `
<View
pointerEvents="box-none"
style={
Array [
undefined,
undefined,
]
}
>
<View
style={
Array [
undefined,
undefined,
undefined,
]
}
/>
Expand Down
1 change: 1 addition & 0 deletions packages/components/src/index.native.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ export { default as ImageEditingButton } from './mobile/image/image-editing-butt
export { default as InserterButton } from './mobile/inserter-button';
export { setClipboard, getClipboard } from './mobile/clipboard';
export { default as Preview } from './mobile/preview';
export { default as AudioPlayer } from './mobile/audio-player';

// Utils
export { colorsUtils } from './mobile/color-settings/utils';
Expand Down
225 changes: 225 additions & 0 deletions packages/components/src/mobile/audio-player/index.native.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
/**
* External dependencies
*/
import {
Text,
TouchableWithoutFeedback,
Linking,
Alert,
Platform,
} from 'react-native';
import { default as VideoPlayer } from 'react-native-video';

/**
* WordPress dependencies
*/
import { View } from '@wordpress/primitives';
import { Icon } from '@wordpress/components';
import { withPreferredColorScheme } from '@wordpress/compose';
import { __ } from '@wordpress/i18n';
import { audio, warning } from '@wordpress/icons';
import {
requestImageFailedRetryDialog,
requestImageUploadCancelDialog,
} from '@wordpress/react-native-bridge';
import { getProtocol, safeDecodeURI } from '@wordpress/url';
import { useState } from '@wordpress/element';

/**
* Internal dependencies
*/
import styles from './styles.scss';

const isIOS = Platform.OS === 'ios';

function Player( {
getStylesFromColorScheme,
isUploadInProgress,
isUploadFailed,
attributes,
isSelected,
} ) {
const { id, src } = attributes;
const [ paused, setPaused ] = useState( true );

const onPressListen = () => {
if ( src ) {
if ( isIOS && this.player ) {
this.player.presentFullscreenPlayer();
return;
}

Linking.canOpenURL( src )
.then( ( supported ) => {
if ( ! supported ) {
Alert.alert(
__( 'Problem opening the audio' ),
__( 'No application can handle this request.' )
);
} else {
return Linking.openURL( src );
}
} )
.catch( () => {
Alert.alert(
__( 'Problem opening the audio' ),
__( 'An unknown error occurred. Please try again.' )
);
} );
}
};

const containerStyle = getStylesFromColorScheme(
styles.container,
styles.containerDark
);

const iconStyle = getStylesFromColorScheme( styles.icon, styles.iconDark );

const iconDisabledStyle = getStylesFromColorScheme(
styles.iconDisabled,
styles.iconDisabledDark
);

const isDisabled = isUploadFailed || isUploadInProgress;

const finalIconStyle = {
...iconStyle,
...( isDisabled && iconDisabledStyle ),
};

const iconContainerStyle = getStylesFromColorScheme(
styles.iconContainer,
styles.iconContainerDark
);

const titleContainerStyle = {
...styles.titleContainer,
...( isIOS ? styles.titleContainerIOS : styles.titleContainerAndroid ),
};

const titleStyle = getStylesFromColorScheme(
styles.title,
styles.titleDark
);

const uploadFailedStyle = getStylesFromColorScheme(
styles.uploadFailed,
styles.uploadFailedDark
);

const subtitleStyle = getStylesFromColorScheme(
styles.subtitle,
styles.subtitleDark
);

const finalSubtitleStyle = {
...subtitleStyle,
...( isUploadFailed && uploadFailedStyle ),
};

const buttonBackgroundStyle = getStylesFromColorScheme(
styles.buttonBackground,
styles.buttonBackgroundDark
);

let title = '';
let extension = '';

if ( src ) {
const decodedURI = safeDecodeURI( src );
const fileName = decodedURI.split( '/' ).pop();
const parts = fileName.split( '.' );
extension = parts.pop().toUpperCase();
title = parts.join( '.' );
}

const getSubtitleValue = () => {
if ( isUploadInProgress ) {
return __( 'Uploading…' );
}
if ( isUploadFailed ) {
return __( 'Failed to insert audio file. Please tap for options.' );
}
return (
extension +
// translators: displays audio file extension. e.g. MP3 audio file
__( ' audio file' )
);
};

function onAudioUploadCancelDialog() {
if ( isUploadInProgress ) {
requestImageUploadCancelDialog( id );
} else if ( id && getProtocol( src ) === 'file:' ) {
requestImageFailedRetryDialog( id );
}
}

return (
<TouchableWithoutFeedback
accessible={ ! isSelected }
disabled={ ! isSelected }
onPress={ onAudioUploadCancelDialog }
>
<View style={ containerStyle }>
<View style={ iconContainerStyle }>
<Icon icon={ audio } style={ finalIconStyle } size={ 24 } />
</View>
<View style={ titleContainerStyle }>
<Text style={ titleStyle }>{ title }</Text>
<View style={ styles.subtitleContainer }>
{ isUploadFailed && (
<Icon
icon={ warning }
style={ {
...styles.errorIcon,
...uploadFailedStyle,
} }
size={ 16 }
/>
) }
<Text style={ finalSubtitleStyle }>
{ getSubtitleValue() }
</Text>
</View>
</View>
{ ! isDisabled && (
<TouchableWithoutFeedback
accessibilityLabel={ __( 'Audio Player' ) }
accessibilityRole={ 'button' }
accessibilityHint={ __(
'Double tap to listen the audio file'
) }
onPress={ onPressListen }
>
<View style={ buttonBackgroundStyle }>
<Text style={ styles.buttonText }>
{ __( 'OPEN' ) }
</Text>
</View>
</TouchableWithoutFeedback>
) }
{ isIOS && (
<VideoPlayer
source={ { uri: src } }
paused={ paused }
ref={ ( ref ) => {
this.player = ref;
} }
controls={ false }
ignoreSilentSwitch={ 'ignore' }
onFullscreenPlayerWillPresent={ () => {
setPaused( false );
} }
onFullscreenPlayerDidDismiss={ () => {
setPaused( true );
} }
/>
) }
</View>
</TouchableWithoutFeedback>
);
}

export default withPreferredColorScheme( Player );
Loading

0 comments on commit aadc511

Please sign in to comment.