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] Use ActivityIndicator to display media upload progress #58759

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
19db7f3
Remove opacity when image is being uploaded
derekblank Feb 5, 2024
f3d0563
Add hideProgressBar prop to MediaUploadProgress component
derekblank Feb 5, 2024
80da66c
Hide progress bar on Image blocks
derekblank Feb 5, 2024
806d3b7
Update spinner-related tests
derekblank Feb 5, 2024
b69c18d
Fix typo and remove unused code
derekblank Feb 5, 2024
48670e6
Use ActivityIndicator for MediaUploadProgress
derekblank Feb 6, 2024
a4e50c5
Add opacity animation to fade upload success checkmark
derekblank Feb 7, 2024
16389a9
Update tests
derekblank Feb 7, 2024
093dc1a
Remove checkmark and opacity animation
derekblank Feb 8, 2024
96c9be4
Update container to use lighter styles
derekblank Feb 8, 2024
e229071
Update CHANGELOG
derekblank Feb 9, 2024
2f0effc
WIP: Use both Offline icon and ActivityIndicator to indicate upload s…
derekblank Feb 14, 2024
56feadf
Update indicator styles
derekblank Feb 15, 2024
c9ea7c2
Merge trunk and resolve conflicts
derekblank Feb 26, 2024
f4af215
Merge trunk and update CHANGELOG
derekblank Mar 6, 2024
eff8720
Merge remote-tracking branch 'origin' into rnmobile/use-activity-indi…
derekblank Mar 8, 2024
b06bdc2
Convert MediaUploadProgress from class to function component
derekblank Mar 8, 2024
a0392df
Update MediaUploadProgress to use light/dark mode styles
derekblank Mar 11, 2024
ccde634
Merge remote-tracking branch 'origin' into rnmobile/use-activity-indi…
derekblank Mar 12, 2024
87d3ae5
Allow displaying both determinate and indeterminate MediaUploadProgre…
derekblank Mar 12, 2024
a46ce62
Update deterministic progress bar styles
derekblank Mar 12, 2024
ca76ada
Add discrete indeterminate and determinate progress indicator styles
derekblank Mar 13, 2024
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
@@ -1,15 +1,17 @@
/**
* External dependencies
*/
import { View } from 'react-native';
import { ActivityIndicator, View } from 'react-native';

/**
* WordPress dependencies
*/
import { Component } from '@wordpress/element';
import { Spinner } from '@wordpress/components';
import { useEffect, useState } from '@wordpress/element';
import { usePreferredColorSchemeStyle } from '@wordpress/compose';
import { __ } from '@wordpress/i18n';
import { Icon } from '@wordpress/components';
import { subscribeMediaUpload } from '@wordpress/react-native-bridge';
import { offline as offlineIcon } from '@wordpress/icons';

/**
* Internal dependencies
Expand All @@ -24,190 +26,176 @@ import {
MEDIA_UPLOAD_STATE_PAUSED,
} from './constants';

export class MediaUploadProgress extends Component {
constructor( props ) {
super( props );
const MediaUploadProgress = ( props ) => {
const [ uploadState, setUploadState ] = useState( MEDIA_UPLOAD_STATE_IDLE );
const [ progress, setProgress ] = useState( 0 );
const [ isUploadInProgress, setIsUploadInProgress ] = useState( false );
const [ isUploadFailed, setIsUploadFailed ] = useState( false );

this.state = {
uploadState: MEDIA_UPLOAD_STATE_IDLE,
progress: 0,
isUploadInProgress: false,
isUploadFailed: false,
};

this.mediaUpload = this.mediaUpload.bind( this );
this.getRetryMessage = this.getRetryMessage.bind( this );
}

componentDidMount() {
this.addMediaUploadListener();
}

componentWillUnmount() {
this.removeMediaUploadListener();
}

mediaUpload( payload ) {
const { mediaId } = this.props;
const mediaUpload = ( payload ) => {
const { mediaId } = props;

if (
payload.mediaId !== mediaId ||
( payload.state === this.state.uploadState &&
payload.progress === this.state.progress )
( payload.state === uploadState && payload.progress === progress )
) {
return;
}

switch ( payload.state ) {
case MEDIA_UPLOAD_STATE_UPLOADING:
this.updateMediaProgress( payload );
updateMediaProgress( payload );
break;
case MEDIA_UPLOAD_STATE_SUCCEEDED:
this.finishMediaUploadWithSuccess( payload );
finishMediaUploadWithSuccess( payload );
break;
case MEDIA_UPLOAD_STATE_PAUSED:
this.finishMediaUploadWithPause( payload );
finishMediaUploadWithPause( payload );
break;
case MEDIA_UPLOAD_STATE_FAILED:
this.finishMediaUploadWithFailure( payload );
finishMediaUploadWithFailure( payload );
break;
case MEDIA_UPLOAD_STATE_RESET:
this.mediaUploadStateReset( payload );
mediaUploadStateReset( payload );
break;
}
}

updateMediaProgress( payload ) {
this.setState( {
progress: payload.progress,
uploadState: payload.state,
isUploadInProgress: true,
isUploadFailed: false,
} );
if ( this.props.onUpdateMediaProgress ) {
this.props.onUpdateMediaProgress( payload );
};

const updateMediaProgress = ( payload ) => {
setProgress( payload.progress );
setUploadState( payload.state );
setIsUploadInProgress( true );
setIsUploadFailed( false );
if ( props.onUpdateMediaProgress ) {
props.onUpdateMediaProgress( payload );
}
}
};

finishMediaUploadWithSuccess( payload ) {
this.setState( {
uploadState: payload.state,
isUploadInProgress: false,
} );
if ( this.props.onFinishMediaUploadWithSuccess ) {
this.props.onFinishMediaUploadWithSuccess( payload );
const finishMediaUploadWithSuccess = ( payload ) => {
setUploadState( payload.state );
setIsUploadInProgress( false );
if ( props.onFinishMediaUploadWithSuccess ) {
props.onFinishMediaUploadWithSuccess( payload );
}
}
};

finishMediaUploadWithPause( payload ) {
if ( ! this.props.enablePausedUploads ) {
this.finishMediaUploadWithFailure( payload );
const finishMediaUploadWithPause = ( payload ) => {
if ( ! props.enablePausedUploads ) {
finishMediaUploadWithFailure( payload );
return;
}

this.setState( {
uploadState: payload.state,
isUploadInProgress: true,
isUploadFailed: false,
} );
if ( this.props.onFinishMediaUploadWithFailure ) {
this.props.onFinishMediaUploadWithFailure( payload );
setUploadState( payload.state );
setIsUploadInProgress( true );
setIsUploadFailed( false );
if ( props.onFinishMediaUploadWithPause ) {
props.onFinishMediaUploadWithPause( payload );
}
}

finishMediaUploadWithFailure( payload ) {
this.setState( {
uploadState: payload.state,
isUploadInProgress: false,
isUploadFailed: true,
} );
if ( this.props.onFinishMediaUploadWithFailure ) {
this.props.onFinishMediaUploadWithFailure( payload );
};

const finishMediaUploadWithFailure = ( payload ) => {
setUploadState( payload.state );
setIsUploadInProgress( false );
setIsUploadFailed( true );
if ( props.onFinishMediaUploadWithFailure ) {
props.onFinishMediaUploadWithFailure( payload );
}
}
};

const mediaUploadStateReset = ( payload ) => {
setUploadState( payload.state );
setIsUploadInProgress( false );
setIsUploadFailed( false );
if ( props.onMediaUploadStateReset ) {
props.onMediaUploadStateReset( payload );
}
};

mediaUploadStateReset( payload ) {
this.setState( {
uploadState: payload.state,
isUploadInProgress: false,
isUploadFailed: false,
useEffect( () => {
const subscription = subscribeMediaUpload( ( payload ) => {
mediaUpload( payload );
} );
if ( this.props.onMediaUploadStateReset ) {
this.props.onMediaUploadStateReset( payload );
}
}

addMediaUploadListener() {
// If we already have a subscription not worth doing it again.
if ( this.subscriptionParentMediaUpload ) {
return;
}
this.subscriptionParentMediaUpload = subscribeMediaUpload(
( payload ) => {
this.mediaUpload( payload );
}
);
}

removeMediaUploadListener() {
if ( this.subscriptionParentMediaUpload ) {
this.subscriptionParentMediaUpload.remove();
}
}
return () => subscription.remove();
}, [ mediaUpload ] );

getRetryMessage() {
const getRetryMessage = () => {
if (
this.state.uploadState === MEDIA_UPLOAD_STATE_PAUSED &&
this.props.enablePausedUploads
uploadState === MEDIA_UPLOAD_STATE_PAUSED &&
props.enablePausedUploads
) {
return __( 'Waiting for connection' );
return;
}

// eslint-disable-next-line @wordpress/i18n-no-collapsible-whitespace
return __( 'Failed to insert media.\nTap for more info.' );
}

render() {
const { renderContent = () => null } = this.props;
const { isUploadInProgress, isUploadFailed, uploadState } = this.state;
const showSpinner = this.state.isUploadInProgress;
const progress = this.state.progress * 100;
const retryMessage = this.getRetryMessage();

const progressBarStyle = [
styles.progressBar,
showSpinner || styles.progressBarHidden,
this.props.progressBarStyle,
];

return (
<View
style={ [
styles.mediaUploadProgress,
this.props.containerStyle,
] }
pointerEvents="box-none"
>
<View style={ progressBarStyle }>
{ showSpinner && (
<Spinner
progress={ progress }
style={ this.props.spinnerStyle }
testID="spinner"
/>
) }
};

const retryMessage = getRetryMessage();
const isUploadPaused =
uploadState === MEDIA_UPLOAD_STATE_PAUSED && props.enablePausedUploads;
const showProgress =
isUploadInProgress && ! isUploadPaused && ! isUploadFailed;
const { renderContent = () => null } = props;

const indicatorContainerStyle = usePreferredColorSchemeStyle(
styles.indicator__container,
styles[ 'indicator__container--dark' ]
);

const indicatorIconStyle = usePreferredColorSchemeStyle(
styles.indicator__icon,
styles[ 'indicator__icon--dark' ]
);

return (
<View
style={ styles.mediaUploadProgress }
pointerEvents="box-none"
testID="progress-container"
>
{ isUploadPaused && (
<View style={ indicatorContainerStyle }>
<Icon
fill="#111"
size="20"
icon={ offlineIcon }
style={ indicatorIconStyle }
/>
</View>
{ renderContent( {
isUploadPaused:
uploadState === MEDIA_UPLOAD_STATE_PAUSED &&
this.props.enablePausedUploads,
isUploadInProgress,
isUploadFailed,
retryMessage,
} ) }
</View>
);
}
}
) }
{ showProgress &&
props.progressType &&
props.progressType === 'determinate' &&
progress !== 100 && (
<View style={ styles.progress }>
<View
style={ [
styles.progressBar,
{ width: `${ progress }%` },
] }
/>
</View>
) }
{ showProgress &&
props.progressType &&
props.progressType === 'indeterminate' && (
<View style={ indicatorContainerStyle }>
<ActivityIndicator
style={ indicatorIconStyle }
size={ 20 }
color="#111"
/>
</View>
) }
{ renderContent( {
isUploadPaused,
isUploadInProgress,
isUploadFailed,
retryMessage,
} ) }
</View>
);
};

export default MediaUploadProgress;
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,32 @@
z-index: 1;
}

.indicator__container {
background-color: rgba(255, 255, 255, 0.8);
position: absolute;
bottom: 6px;
right: 6px;
z-index: 1;
border-radius: 6px;
}

.indicator__container--dark {
background-color: rgba(0, 0, 0, 0.8);
position: absolute;
bottom: 6px;
right: 6px;
z-index: 1;
border-radius: 6px;
}

.indicator__icon {
margin: 3px;
}


.progressBar {
background-color: $gray-lighten-30;
z-index: 1;
height: 6px;
margin-bottom: -6px;
}

.progressBarHidden {
background-color: transparent;
}
Loading
Loading