Skip to content

Commit

Permalink
[RNMobile] Simplify media insertion flow Part 1 - redux changes (#29546)
Browse files Browse the repository at this point in the history
* created actions for adding and clearing last inserted block event.

* added reducer for determining new state based on the action

* added selector to query the state for the last block inserted

* [RNMobile] Simplify media insertion flow Part 2 - media upload (#29547)

* added autoOpenMediaUpload prop to the MediaPlaceholder

* Added the auto-opening capabilities to the MediaUpload component.

* Added documentation for the new autoOpenMediaUpload prop

* renamed autoOpenMediaUpload to autoOpen in the MediaUpload component.

* [RNMobile] Simplify media insertion flow - Part 3 component integration (#29548)

* Track the clientId of the block that is inserted.

* implemented auto opening utilizing last block inserted from the store

* added dismissal support for the auto opening picker to the UI tests.

* Updated Dismiss button in closePicker function to Cancel

* Added release notes for auto-opening.

* [RNMobile] Refactor simplify media flow redux store changes (#30123)

* Moved the last block inserted actions from editor to the block-editor

* Moved the last block inserted reducer from editor to the block-editor

* Moved the last block inserted selector from editor to the block-editor

* Fixed es-lint error.

* Moved last block inserted actions test from editor to the block-editor

* Moved last block inserted reducer test from editor to the block-editor

* Moved last block inserted selector test from editor to the block-editor

* Moved all calls to last block inserted from editor to block-editor

* last block inserter usage in menu native migrated : editor to block-editor

* [RNMobile] Refactor: Simplify media flow redux migration (#30238)

* Add meta argument to insertBlock action

* Add inserter menu source

* Update last block inserted reducer

Instead of using specific actions for tracking the last block inserted, it uses the actions related to the insertion flow.

* Add get last block inserted selector

* Refactor gallery edit component

withSelect and withDispatch logic has been refactored to use useSelect and useDispatch hooks

* Refactor image edit component

wasBlockJustInserted is now calculated with getLastBlockInserted

* Refactor video edit component

wasBlockJustInserted is now calculated with getLastBlockInserted

* Fix reset blocks action in last block inserted reducer

* Add source param to wasBlockJustInserted selector

* Simplify withSelect part of video block

* Removed add/clear last block inserted actions and tests due to refactor

* Removed addLastBlockInserted from the insert menu's onPress.

* rewrote the tests for the lastBlockInserted reducer.

* rewrote tests for wasBlockJustInserted selector.

* optimized clientId

* removed unneeded clientId.

* put the expectedSource inside the test meta object.

* used expectedState insted {} for state related tests.

* used expectedState instead {} for state related tests.

* removed parentheses from describe name.

* return the same state instead of empty state.

* made changes to test name so its intent is clearer.

* made the insertion source optional.

Co-authored-by: Carlos Garcia <[email protected]>

* added wasBlockJustInserted prop needed after merge with trunk.

* removed updateSelection from reducer so it's updated at all times.

Co-authored-by: Carlos Garcia <[email protected]>
  • Loading branch information
jd-alexander and fluiddot authored Apr 20, 2021
1 parent 17b490a commit 3e14c5e
Show file tree
Hide file tree
Showing 19 changed files with 351 additions and 70 deletions.
8 changes: 7 additions & 1 deletion packages/block-editor/src/components/inserter/menu.native.js
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,13 @@ function InserterMenu( {
innerBlocks
);

insertBlock( newBlock, insertionIndex, destinationRootClientId );
insertBlock(
newBlock,
insertionIndex,
destinationRootClientId,
true,
{ source: 'inserter_menu' }
);
},
[ insertBlock, destinationRootClientId, insertionIndex ]
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,15 @@ This property is similar to the `accept` property. The difference is the format
- Required: No
- Platform: Web | Mobile

### autoOpenMediaUpload

If true, the MediaUpload component auto-opens the picker of the respective platform.

- Type: `Boolean`
- Required: No
- Default: `false`
- Platform: Mobile

### className

Class name added to the placeholder.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ function MediaPlaceholder( props ) {
height,
backgroundColor,
hideContent,
autoOpenMediaUpload,
} = props;

// use ref to keep media array current for callbacks during rerenders
Expand Down Expand Up @@ -160,6 +161,7 @@ function MediaPlaceholder( props ) {
}
multiple={ multiple }
isReplacingMedia={ false }
autoOpen={ autoOpenMediaUpload }
render={ ( { open, getMediaOptions } ) => {
return (
<TouchableWithoutFeedback
Expand Down
9 changes: 9 additions & 0 deletions packages/block-editor/src/components/media-upload/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,15 @@ Only applies if `gallery === true`.
- Default: `false`
- Platform: Web

### autoOpen

If true, the picker of the respective platform auto-opens.

- Type: `Boolean`
- Required: No
- Default: `false`
- Platform: Mobile

### gallery

If true, the component will initiate all the states required to represent a gallery. By default, the media modal opens in the gallery edit frame, but that can be changed using the `addToGallery`flag.
Expand Down
32 changes: 28 additions & 4 deletions packages/block-editor/src/components/media-upload/index.native.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
/**
* External dependencies
*/
import { Platform } from 'react-native';

import { delay } from 'lodash';

/**
* WordPress dependencies
*/
import { Component } from '@wordpress/element';
import { Component, React } from '@wordpress/element';
import { __ } from '@wordpress/i18n';
import { Picker } from '@wordpress/components';
import {
Expand All @@ -26,20 +33,21 @@ export const OPTION_TAKE_VIDEO = __( 'Take a Video' );
export const OPTION_TAKE_PHOTO = __( 'Take a Photo' );
export const OPTION_TAKE_PHOTO_OR_VIDEO = __( 'Take a Photo or Video' );

const PICKER_OPENING_DELAY = 200;

export class MediaUpload extends Component {
constructor( props ) {
super( props );
this.onPickerPresent = this.onPickerPresent.bind( this );
this.onPickerSelect = this.onPickerSelect.bind( this );
this.getAllSources = this.getAllSources.bind( this );

this.state = {
otherMediaOptions: [],
};
}

componentDidMount() {
const { allowedTypes = [] } = this.props;
const { allowedTypes = [], autoOpen } = this.props;
getOtherMediaOptions( allowedTypes, ( otherMediaOptions ) => {
const otherMediaOptionsWithIcons = otherMediaOptions.map(
( option ) => {
Expand All @@ -54,6 +62,10 @@ export class MediaUpload extends Component {

this.setState( { otherMediaOptions: otherMediaOptionsWithIcons } );
} );

if ( autoOpen ) {
this.onPickerPresent();
}
}

getAllSources() {
Expand Down Expand Up @@ -136,8 +148,20 @@ export class MediaUpload extends Component {
}

onPickerPresent() {
const { autoOpen } = this.props;
const isIOS = Platform.OS === 'ios';

if ( this.picker ) {
this.picker.presentPicker();
// the delay below is required because on iOS this action sheet gets dismissed by the close event of the Inserter
// so this delay allows the Inserter to be closed fully before presenting action sheet.
if ( autoOpen && isIOS ) {
delay(
() => this.picker.presentPicker(),
PICKER_OPENING_DELAY
);
} else {
this.picker.presentPicker();
}
}
}

Expand Down
13 changes: 11 additions & 2 deletions packages/block-editor/src/store/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -538,16 +538,25 @@ export function* moveBlockToPosition(
* @param {?number} index Index at which block should be inserted.
* @param {?string} rootClientId Optional root client ID of block list on which to insert.
* @param {?boolean} updateSelection If true block selection will be updated. If false, block selection will not change. Defaults to true.
* @param {?Object} meta Optional Meta values to be passed to the action object.
*
* @return {Object} Action object.
*/
export function insertBlock(
block,
index,
rootClientId,
updateSelection = true
updateSelection = true,
meta
) {
return insertBlocks( [ block ], index, rootClientId, updateSelection );
return insertBlocks(
[ block ],
index,
rootClientId,
updateSelection,
0,
meta
);
}

/**
Expand Down
26 changes: 26 additions & 0 deletions packages/block-editor/src/store/reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -1727,6 +1727,31 @@ export function highlightedBlock( state, action ) {
return state;
}

/**
* Reducer returning the block insertion event list state.
*
* @param {Object} state Current state.
* @param {Object} action Dispatched action.
*
* @return {Object} Updated state.
*/
export function lastBlockInserted( state = {}, action ) {
switch ( action.type ) {
case 'INSERT_BLOCKS':
if ( ! action.blocks.length ) {
return state;
}

const clientId = action.blocks[ 0 ].clientId;
const source = action.meta?.source;

return { clientId, source };
case 'RESET_BLOCKS':
return {};
}
return state;
}

export default combineReducers( {
blocks,
isTyping,
Expand All @@ -1748,4 +1773,5 @@ export default combineReducers( {
hasBlockMovingClientId,
automaticChangeStatus,
highlightedBlock,
lastBlockInserted,
} );
16 changes: 16 additions & 0 deletions packages/block-editor/src/store/selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -2193,3 +2193,19 @@ export const __experimentalGetActiveBlockIdByBlockNames = createSelector(
validBlockNames,
]
);

/**
* Tells if the block with the passed clientId was just inserted.
*
* @param {Object} state Global application state.
* @param {Object} clientId Client Id of the block.
* @param {?string} source Optional insertion source of the block.
* @return {boolean} True if the block matches the last block inserted from the specified source.
*/
export function wasBlockJustInserted( state, clientId, source ) {
const { lastBlockInserted } = state;
return (
lastBlockInserted.clientId === clientId &&
lastBlockInserted.source === source
);
}
73 changes: 73 additions & 0 deletions packages/block-editor/src/store/test/reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import {
template,
blockListSettings,
lastBlockAttributesChange,
lastBlockInserted,
} from '../reducer';

describe( 'state', () => {
Expand Down Expand Up @@ -2944,4 +2945,76 @@ describe( 'state', () => {
expect( state ).toBe( null );
} );
} );

describe( 'lastBlockInserted', () => {
it( 'should return client id if last block inserted is called with action INSERT_BLOCKS', () => {
const expectedClientId = '62bfef6e-d5e9-43ba-b7f9-c77cf354141f';

const action = {
blocks: [
{
clientId: expectedClientId,
},
],
meta: {
source: 'inserter_menu',
},
type: 'INSERT_BLOCKS',
};

const state = lastBlockInserted( {}, action );

expect( state.clientId ).toBe( expectedClientId );
} );

it( 'should return inserter_menu source if last block inserted is called with action INSERT_BLOCKS', () => {
const expectedSource = 'inserter_menu';

const action = {
blocks: [
{
clientId: '62bfef6e-d5e9-43ba-b7f9-c77cf354141f',
},
],
meta: {
source: expectedSource,
},
type: 'INSERT_BLOCKS',
};

const state = lastBlockInserted( {}, action );

expect( state.source ).toBe( expectedSource );
} );

it( 'should return state if last block inserted is called with action INSERT_BLOCKS and block list is empty', () => {
const expectedState = {
clientId: '9db792c6-a25a-495d-adbd-97d56a4c4189',
};

const action = {
blocks: [],
meta: {
source: 'inserter_menu',
},
type: 'INSERT_BLOCKS',
};

const state = lastBlockInserted( expectedState, action );

expect( state ).toEqual( expectedState );
} );

it( 'should return empty state if last block inserted is called with action RESET_BLOCKS', () => {
const expectedState = {};

const action = {
type: 'RESET_BLOCKS',
};

const state = lastBlockInserted( expectedState, action );

expect( state ).toEqual( expectedState );
} );
} );
} );
51 changes: 51 additions & 0 deletions packages/block-editor/src/store/test/selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ const {
__experimentalGetPatternsByBlockTypes,
__unstableGetClientIdWithClientIdsTree,
__unstableGetClientIdsTree,
wasBlockJustInserted,
} = selectors;

describe( 'selectors', () => {
Expand Down Expand Up @@ -3481,6 +3482,56 @@ describe( 'selectors', () => {
);
} );
} );

describe( 'wasBlockJustInserted', () => {
it( 'should return true if the client id passed to wasBlockJustInserted is found within the state', () => {
const expectedClientId = '62bfef6e-d5e9-43ba-b7f9-c77cf354141f';
const source = 'inserter_menu';

const state = {
lastBlockInserted: {
clientId: expectedClientId,
source,
},
};

expect(
wasBlockJustInserted( state, expectedClientId, source )
).toBe( true );
} );

it( 'should return false if the client id passed to wasBlockJustInserted is not found within the state', () => {
const expectedClientId = '62bfef6e-d5e9-43ba-b7f9-c77cf354141f';
const unexpectedClientId = '62bfsed4-d5e9-43ba-b7f9-c77cf565756s';
const source = 'inserter_menu';

const state = {
lastBlockInserted: {
clientId: unexpectedClientId,
source,
},
};

expect(
wasBlockJustInserted( state, expectedClientId, source )
).toBe( false );
} );

it( 'should return false if the source passed to wasBlockJustInserted is not found within the state', () => {
const clientId = '62bfef6e-d5e9-43ba-b7f9-c77cf354141f';
const expectedSource = 'inserter_menu';

const state = {
lastBlockInserted: {
clientId,
},
};

expect(
wasBlockJustInserted( state, clientId, expectedSource )
).toBe( false );
} );
} );
} );

describe( '__experimentalGetParsedReusableBlock', () => {
Expand Down
Loading

0 comments on commit 3e14c5e

Please sign in to comment.