-
Notifications
You must be signed in to change notification settings - Fork 4.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Mobile Stories block (part2: on done) (#25771)
* added mobile StoryUpdateProgress component and bridge code to send/receive save progress * updated WPAndroid bridge DeferredEventEmitter to handle Story save events separately * changed all Save event interface methods to use String ids instead of int, and removed serverMediaId params as these don't apply while saving locally * redefined upload/save state constants * added onStorySaveResult handling to bridge, and renamed STORY_SAVE_STATE_* events to MEDIA_SAVE_STATE_* where appropriate * checking for matches of mediaId in mediaFiles while saving to send save progress updates * added mediaModelCreated() method to the bridge, so a new ID can be assigned to a mediaFile in a Story block * mediaId should always be a string in mediaFiles so, converting to avoid strict comparison to fail * removed commented code * updated documentation * added missing implementation of method storySaveSync() in demo app * fixed prettier warning * Update packages/block-editor/src/components/story-update-progress/README.md Co-authored-by: Joel Dean <[email protected]> * Update packages/block-editor/src/components/story-update-progress/README.md Co-authored-by: Joel Dean <[email protected]> * Mobile Stories block (part3: refactor / rename) (#26005) * renames for generic media files collection block and BlockMediaUpdateProgres * referencing the right props method in finishMediaSaveWithFailure * mistaken renames of parameters in bridge methods * renamed more abstract/generic method names requestMediaFilesEditorLoad * removed extra whtie space * renamed argument type Co-authored-by: Joel Dean <[email protected]>
- Loading branch information
1 parent
71e5d6d
commit 66c15e3
Showing
13 changed files
with
595 additions
and
42 deletions.
There are no files selected for viewing
104 changes: 104 additions & 0 deletions
104
packages/block-editor/src/components/block-media-update-progress/README.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
BlockMediaUpdateProgress | ||
=================== | ||
|
||
`BlockMediaUpdateProgress` shows a progress bar while the media files associated with a media-containing block are being saved first and uploaded later | ||
|
||
## Usage | ||
|
||
Usage example | ||
|
||
```jsx | ||
import { ImageBackground, Text, View } from 'react-native'; | ||
import { | ||
BlockMediaUpdateProgress, | ||
} from '@wordpress/block-editor'; | ||
|
||
function BlockUpdatingProgress( { url, id } ) { | ||
return ( | ||
<BlockMediaUpdateProgress | ||
mediaId={ id } | ||
renderContent={ ( { isSaveFailed, retryMessage } ) => { | ||
return ( | ||
<ImageBackground | ||
resizeMethod="scale" | ||
source={ { uri: url } } | ||
> | ||
{ isSaveFailed && | ||
<View> | ||
<Text>{ retryMessage }</Text> | ||
</View> | ||
} | ||
</ImageBackground> | ||
); | ||
} } | ||
/> | ||
); | ||
} | ||
``` | ||
|
||
## Props | ||
|
||
### mediaFiles | ||
|
||
A collection of media IDs that identify the current story upload. | ||
|
||
- Type: `Array` | ||
- Required: Yes | ||
- Platform: Mobile | ||
|
||
### renderContent | ||
|
||
Content to be rendered along with the progress bar, usually the thumbnail of the media being uploaded. | ||
|
||
- Type: `React components` | ||
- Required: Yes | ||
- Platform: Mobile | ||
|
||
It passes an object containing the following properties: | ||
|
||
`{ isUploadInProgress, isUploadFailed, isSaveInProgress, isSaveFailed, retryMessage }` | ||
|
||
### onUpdateMediaProgress | ||
|
||
Callback called when the progress of the upload is updated. | ||
|
||
- Type: `Function` | ||
- Required: No | ||
- Platform: Mobile | ||
|
||
The argument of the callback is an object containing the following properties: | ||
|
||
`{ mediaId, mediaUrl, progress, state }` | ||
|
||
### onFinishMediaUploadWithSuccess | ||
|
||
Callback called when the media file has been uploaded successfully. | ||
|
||
- Type: `Function` | ||
- Required: No | ||
- Platform: Mobile | ||
|
||
The argument of the callback is an object containing the following properties: | ||
|
||
`{ mediaId, mediaServerId, mediaUrl, progress, state }` | ||
|
||
### onFinishMediaUploadWithFailure | ||
|
||
Callback called when the media file couldn't be uploaded. | ||
|
||
- Type: `Function` | ||
- Required: No | ||
- Platform: Mobile | ||
|
||
The argument of the callback is an object containing the following properties: | ||
|
||
`{ mediaId, progress, state }` | ||
|
||
|
||
### onMediaUploadStateReset | ||
|
||
Callback called when the media upload is reset. | ||
|
||
- Type: `Function` | ||
- Required: No | ||
- Platform: Mobile |
293 changes: 293 additions & 0 deletions
293
packages/block-editor/src/components/block-media-update-progress/index.native.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,293 @@ | ||
/** | ||
* External dependencies | ||
*/ | ||
import React from 'react'; | ||
import { View } from 'react-native'; | ||
|
||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { Spinner } from '@wordpress/components'; | ||
import { __ } from '@wordpress/i18n'; | ||
import { | ||
subscribeMediaUpload, | ||
subscribeMediaSave, | ||
} from '@wordpress/react-native-bridge'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import styles from './styles.scss'; | ||
|
||
export const MEDIA_UPLOAD_STATE_UPLOADING = 1; | ||
export const MEDIA_UPLOAD_STATE_SUCCEEDED = 2; | ||
export const MEDIA_UPLOAD_STATE_FAILED = 3; | ||
export const MEDIA_UPLOAD_STATE_RESET = 4; | ||
|
||
export const MEDIA_SAVE_STATE_SAVING = 5; | ||
export const MEDIA_SAVE_STATE_SUCCEEDED = 6; | ||
export const MEDIA_SAVE_STATE_FAILED = 7; | ||
export const MEDIA_SAVE_STATE_RESET = 8; | ||
export const MEDIA_SAVE_FINAL_STATE_RESULT = 9; | ||
export const MEDIA_SAVE_MEDIAMODEL_CREATED = 10; | ||
|
||
export class BlockMediaUpdateProgress extends React.Component { | ||
constructor( props ) { | ||
super( props ); | ||
|
||
this.state = { | ||
progress: 0, | ||
isSaveInProgress: false, | ||
isSaveFailed: false, | ||
isUploadInProgress: false, | ||
isUploadFailed: false, | ||
}; | ||
|
||
this.mediaUpload = this.mediaUpload.bind( this ); | ||
this.mediaSave = this.mediaSave.bind( this ); | ||
} | ||
|
||
componentDidMount() { | ||
this.addMediaUploadListener(); | ||
this.addMediaSaveListener(); | ||
} | ||
|
||
componentWillUnmount() { | ||
this.removeMediaUploadListener(); | ||
this.removeMediaSaveListener(); | ||
} | ||
|
||
mediaIdContainedInMediaFiles( mediaId, mediaFiles ) { | ||
if ( mediaId !== undefined && mediaFiles !== undefined ) { | ||
for ( let i = 0; i < this.props.mediaFiles.length; i++ ) { | ||
if ( mediaFiles[ i ].id === mediaId.toString() ) { | ||
return true; | ||
} | ||
} | ||
} | ||
return false; | ||
} | ||
|
||
mediaUpload( payload ) { | ||
const { mediaFiles } = this.props; | ||
|
||
if ( | ||
this.mediaIdContainedInMediaFiles( payload.mediaId, mediaFiles ) === | ||
false | ||
) { | ||
return; | ||
} | ||
|
||
switch ( payload.state ) { | ||
case MEDIA_UPLOAD_STATE_UPLOADING: | ||
this.updateMediaUploadProgress( payload ); | ||
break; | ||
case MEDIA_UPLOAD_STATE_SUCCEEDED: | ||
this.finishMediaUploadWithSuccess( payload ); | ||
break; | ||
case MEDIA_UPLOAD_STATE_FAILED: | ||
this.finishMediaUploadWithFailure( payload ); | ||
break; | ||
case MEDIA_UPLOAD_STATE_RESET: | ||
this.mediaUploadStateReset( payload ); | ||
break; | ||
} | ||
} | ||
|
||
mediaSave( payload ) { | ||
const { mediaFiles } = this.props; | ||
|
||
if ( | ||
this.mediaIdContainedInMediaFiles( payload.mediaId, mediaFiles ) === | ||
false | ||
) { | ||
return; | ||
} | ||
|
||
switch ( payload.state ) { | ||
case MEDIA_SAVE_STATE_SAVING: | ||
this.updateMediaSaveProgress( payload ); | ||
break; | ||
case MEDIA_SAVE_STATE_SUCCEEDED: | ||
this.finishMediaSaveWithSuccess( payload ); | ||
break; | ||
case MEDIA_SAVE_STATE_FAILED: | ||
this.finishMediaSaveWithFailure( payload ); | ||
break; | ||
case MEDIA_SAVE_STATE_RESET: | ||
this.mediaSaveStateReset( payload ); | ||
break; | ||
case MEDIA_SAVE_FINAL_STATE_RESULT: | ||
this.finalSaveResult( payload ); | ||
break; | ||
case MEDIA_SAVE_MEDIAMODEL_CREATED: | ||
this.mediaModelCreated( payload ); | ||
break; | ||
} | ||
} | ||
|
||
// ---- Block media save actions | ||
updateMediaSaveProgress( payload ) { | ||
this.setState( { | ||
progress: payload.progress, | ||
isUploadInProgress: false, | ||
isUploadFailed: false, | ||
isSaveInProgress: true, | ||
isSaveFailed: false, | ||
} ); | ||
if ( this.props.onUpdateMediaSaveProgress ) { | ||
this.props.onUpdateMediaSaveProgress( payload ); | ||
} | ||
} | ||
|
||
finishMediaSaveWithSuccess( payload ) { | ||
this.setState( { isSaveInProgress: false } ); | ||
if ( this.props.onFinishMediaSaveWithSuccess ) { | ||
this.props.onFinishMediaSaveWithSuccess( payload ); | ||
} | ||
} | ||
|
||
finishMediaSaveWithFailure( payload ) { | ||
this.setState( { isSaveInProgress: false, isSaveFailed: true } ); | ||
if ( this.props.onFinishMediaSaveWithFailure ) { | ||
this.props.onFinishMediaSaveWithFailure( payload ); | ||
} | ||
} | ||
|
||
mediaSaveStateReset( payload ) { | ||
this.setState( { isUploadInProgress: false, isUploadFailed: false } ); | ||
if ( this.props.onMediaSaveStateReset ) { | ||
this.props.onMediaSaveStateReset( payload ); | ||
} | ||
} | ||
|
||
finalSaveResult( payload ) { | ||
this.setState( { | ||
progress: payload.progress, | ||
isUploadInProgress: false, | ||
isUploadFailed: false, | ||
isSaveInProgress: false, | ||
isSaveFailed: ! payload.success, | ||
} ); | ||
if ( this.props.onFinalSaveResult ) { | ||
this.props.onFinalSaveResult( payload ); | ||
} | ||
} | ||
|
||
mediaModelCreated( payload ) { | ||
this.setState( { | ||
isUploadInProgress: false, | ||
isUploadFailed: false, | ||
isSaveInProgress: false, | ||
isSaveFailed: false, | ||
} ); | ||
if ( this.props.onMediaModelCreated ) { | ||
this.props.onMediaModelCreated( payload ); | ||
} | ||
} | ||
|
||
// ---- Block media upload actions | ||
updateMediaUploadProgress( payload ) { | ||
this.setState( { | ||
progress: payload.progress, | ||
isUploadInProgress: true, | ||
isUploadFailed: false, | ||
isSaveInProgress: false, | ||
isSaveFailed: false, | ||
} ); | ||
if ( this.props.onUpdateMediaUploadProgress ) { | ||
this.props.onUpdateMediaUploadProgress( payload ); | ||
} | ||
} | ||
|
||
finishMediaUploadWithSuccess( payload ) { | ||
this.setState( { isUploadInProgress: false, isSaveInProgress: false } ); | ||
if ( this.props.onFinishMediaUploadWithSuccess ) { | ||
this.props.onFinishMediaUploadWithSuccess( payload ); | ||
} | ||
} | ||
|
||
finishMediaUploadWithFailure( payload ) { | ||
this.setState( { isUploadInProgress: false, isUploadFailed: true } ); | ||
if ( this.props.onFinishMediaUploadWithFailure ) { | ||
this.props.onFinishMediaUploadWithFailure( payload ); | ||
} | ||
} | ||
|
||
mediaUploadStateReset( payload ) { | ||
this.setState( { isUploadInProgress: false, isUploadFailed: false } ); | ||
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(); | ||
} | ||
} | ||
|
||
addMediaSaveListener() { | ||
//if we already have a subscription not worth doing it again | ||
if ( this.subscriptionParentMediaSave ) { | ||
return; | ||
} | ||
this.subscriptionParentMediaSave = subscribeMediaSave( ( payload ) => { | ||
this.mediaSave( payload ); | ||
} ); | ||
} | ||
|
||
removeMediaSaveListener() { | ||
if ( this.subscriptionParentMediaSave ) { | ||
this.subscriptionParentMediaSave.remove(); | ||
} | ||
} | ||
|
||
render() { | ||
const { renderContent = () => null } = this.props; | ||
const { | ||
isUploadInProgress, | ||
isUploadFailed, | ||
isSaveInProgress, | ||
isSaveFailed, | ||
} = this.state; | ||
const showSpinner = | ||
this.state.isUploadInProgress || this.state.isSaveInProgress; | ||
const progress = this.state.progress * 100; | ||
// eslint-disable-next-line @wordpress/i18n-no-collapsible-whitespace | ||
const retryMessage = __( | ||
'Failed to save files.\nPlease tap for options.' | ||
); | ||
|
||
return ( | ||
<View style={ styles.mediaUploadProgress } pointerEvents="box-none"> | ||
{ showSpinner && ( | ||
<View style={ styles.progressBar }> | ||
<Spinner progress={ progress } /> | ||
</View> | ||
) } | ||
{ renderContent( { | ||
isUploadInProgress, | ||
isUploadFailed, | ||
isSaveInProgress, | ||
isSaveFailed, | ||
retryMessage, | ||
} ) } | ||
</View> | ||
); | ||
} | ||
} | ||
|
||
export default BlockMediaUpdateProgress; |
Oops, something went wrong.