diff --git a/packages/block-editor/src/components/media-upload-progress/index.native.js b/packages/block-editor/src/components/media-upload-progress/index.native.js index cb5a25d0bb866..4b1feb381b379 100644 --- a/packages/block-editor/src/components/media-upload-progress/index.native.js +++ b/packages/block-editor/src/components/media-upload-progress/index.native.js @@ -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 @@ -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 ( - - - { showSpinner && ( - - ) } + }; + + 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 ( + + { isUploadPaused && ( + + - { renderContent( { - isUploadPaused: - uploadState === MEDIA_UPLOAD_STATE_PAUSED && - this.props.enablePausedUploads, - isUploadInProgress, - isUploadFailed, - retryMessage, - } ) } - - ); - } -} + ) } + { showProgress && + props.progressType && + props.progressType === 'determinate' && + progress !== 100 && ( + + + + ) } + { showProgress && + props.progressType && + props.progressType === 'indeterminate' && ( + + + + ) } + { renderContent( { + isUploadPaused, + isUploadInProgress, + isUploadFailed, + retryMessage, + } ) } + + ); +}; export default MediaUploadProgress; diff --git a/packages/block-editor/src/components/media-upload-progress/styles.native.scss b/packages/block-editor/src/components/media-upload-progress/styles.native.scss index 3a6f831d0154e..97224da62e7da 100644 --- a/packages/block-editor/src/components/media-upload-progress/styles.native.scss +++ b/packages/block-editor/src/components/media-upload-progress/styles.native.scss @@ -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; -} diff --git a/packages/block-library/src/gallery/test/index.native.js b/packages/block-library/src/gallery/test/index.native.js index fd95053bea62c..2765214f430b2 100644 --- a/packages/block-library/src/gallery/test/index.native.js +++ b/packages/block-library/src/gallery/test/index.native.js @@ -489,14 +489,18 @@ describe( 'Gallery block', () => { // Cancel uploads fireEvent.press( galleryItem1 ); - fireEvent.press( within( galleryItem1 ).getByTestId( 'spinner' ) ); + fireEvent.press( + within( galleryItem1 ).getByTestId( 'progress-container' ) + ); expect( requestImageUploadCancelDialog ).toHaveBeenCalledWith( media[ 0 ].localId ); await notifyResetState( media[ 0 ] ); fireEvent.press( galleryItem2 ); - fireEvent.press( within( galleryItem2 ).getByTestId( 'spinner' ) ); + fireEvent.press( + within( galleryItem2 ).getByTestId( 'progress-container' ) + ); expect( requestImageUploadCancelDialog ).toHaveBeenCalledWith( media[ 1 ].localId ); diff --git a/packages/block-library/src/image/edit.native.js b/packages/block-library/src/image/edit.native.js index 010ec4cd59694..8fe17775d4707 100644 --- a/packages/block-library/src/image/edit.native.js +++ b/packages/block-library/src/image/edit.native.js @@ -815,6 +815,7 @@ export class ImageEdit extends Component { enablePausedUploads coverUrl={ url } mediaId={ id } + progressType="indeterminate" onUpdateMediaProgress={ this.updateMediaProgress } onFinishMediaUploadWithSuccess={ this.finishMediaUploadWithSuccess diff --git a/packages/components/src/mobile/image/index.native.js b/packages/components/src/mobile/image/index.native.js index c3e010c40c3b8..6913b364463bf 100644 --- a/packages/components/src/mobile/image/index.native.js +++ b/packages/components/src/mobile/image/index.native.js @@ -306,7 +306,6 @@ const ImageComponent = ( { { networkImageLoaded && networkURL && ( ## Unreleased +- [**] Update Image block progress indicator to use ActivityIndicator [#58759] - [*] Improve consistency of the block outline indicating the currently selected block [#59415] - [*] Gallery block items with in-progress, paused, or failed media uploads correctly display an highlight outline when selected [#59423] - [**] [internal] Upgrade React Native to version 0.73.3 [#58475]