Skip to content

Commit

Permalink
[RNMobile] Refactor native editor initialization (#37073)
Browse files Browse the repository at this point in the history
* Refactor editor initialization

* Update setup locale to be generic

* Fix globals import

* Trigger init callback before editor initialization

* Update package-lock.json

* Set forceRTL in react native setup function

* Add plugin translations to setup locale function

* Remove block-library lazy import

* Remove domain default value

* Add plugin translations to Gutenberg initialization

* Document register Gutenberg function

* Update Gutenberg constructor

* Add default values for register Gutenberg

* Add locale initial prop to Gutenberg demo

* Update react-native-editor changelog

* Add note to react-native-editor index

* Move locale prop definition

* Move lodash import to external deps

* Remove lodash usage from react-native-editor

* Refactor setup locale with code improvements

* Reorder imports in react-native-editor setup

* Move variable definitions close to their functions

* Inline editor initialization call

* Update setup locale comment

* Add default value to setup locale

* Add setup locale tests

* Add test cases for register gutenberg

* Add editor initialization test case

* Revert api-fetch mock

* Mock setFetchHandler of api-fetch package

* Mock setFetchHandler within mock block

* Add eslint rule override in react-native-editor

This override matches the same behavior we have in root ESLint configuration.
Reference: https://github.com/WordPress/gutenberg/blob/ca85ced9298a252d4832438d8408ea4cda95696f/.eslintrc.js#L171-L185

* Add word mock to onModuleImported variables
  • Loading branch information
fluiddot authored Jan 4, 2022
1 parent 028b6b6 commit b7b62d2
Show file tree
Hide file tree
Showing 13 changed files with 530 additions and 211 deletions.
11 changes: 1 addition & 10 deletions packages/edit-post/src/index.native.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,13 @@
*/
import '@wordpress/core-data';
import '@wordpress/format-library';
import { render } from '@wordpress/element';

/**
* Internal dependencies
*/
export { store } from './store';
import Editor from './editor';

let editorInitialized = false;

/**
* Initializes the Editor and returns a componentProvider
* that can be registered with `AppRegistry.registerComponent`
Expand All @@ -22,11 +19,5 @@ let editorInitialized = false;
* @param {Object} postId ID of the post to edit (unused right now)
*/
export function initializeEditor( id, postType, postId ) {
if ( editorInitialized ) {
return;
}

editorInitialized = true;

render( <Editor postId={ postId } postType={ postType } />, id );
return <Editor postId={ postId } postType={ postType } />;
}
50 changes: 0 additions & 50 deletions packages/element/src/react-platform.native.js

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,6 @@ data class GutenbergProps @JvmOverloads constructor(
private const val PROP_INITIAL_HTML_MODE_ENABLED = "initialHtmlModeEnabled"
private const val PROP_POST_TYPE = "postType"
private const val PROP_INITIAL_FEATURED_IMAGE_ID = "featuredImageId"
private const val PROP_LOCALE = "locale"
private const val PROP_TRANSLATIONS = "translations"
private const val PROP_COLORS = "colors"
private const val PROP_GRADIENTS = "gradients"
Expand All @@ -88,6 +87,7 @@ data class GutenbergProps @JvmOverloads constructor(
private const val PROP_IS_FSE_THEME = "isFSETheme"
private const val PROP_GALLERY_WITH_IMAGE_BLOCKS = "galleryWithImageBlocks"

const val PROP_LOCALE = "locale"
const val PROP_CAPABILITIES = "capabilities"
const val PROP_CAPABILITIES_CONTACT_INFO_BLOCK = "contactInfoBlock"
const val PROP_CAPABILITIES_LAYOUT_GRID_BLOCK = "layoutGridBlock"
Expand Down
8 changes: 8 additions & 0 deletions packages/react-native-editor/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,4 +67,12 @@ module.exports = {
},
],
},
overrides: [
{
files: [ '**/*.js' ],
rules: {
'import/no-unresolved': 'off',
},
},
],
};
1 change: 1 addition & 0 deletions packages/react-native-editor/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ For each user feature we should also add a importance categorization label to i
## Unreleased
- [*] Give multi-line block names central alignment in inserter [#37185]
- [**] Fix empty line apperaing when splitting heading blocks on Android 12 [#37279]
- [**] Fix missing translations by refactoring the editor initialization code [#37073]

## 1.68.0
- [**] Fix undo/redo functionality in links when applying text format [#36861]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@

import org.wordpress.mobile.WPAndroidGlue.GutenbergProps;

import java.util.Locale;

public class MainActivity extends ReactActivity {

/**
Expand All @@ -27,6 +29,13 @@ protected ReactActivityDelegate createReactActivityDelegate() {
@Override
protected Bundle getLaunchOptions() {
Bundle bundle = new Bundle();

// Add locale
String languageString = Locale.getDefault().toString();
String localeSlug = languageString.replace("_", "-").toLowerCase(Locale.ENGLISH);
bundle.putString(GutenbergProps.PROP_LOCALE, localeSlug);

// Add capabilities
Bundle capabilities = new Bundle();
capabilities.putBoolean(GutenbergProps.PROP_CAPABILITIES_MENTIONS, true);
capabilities.putBoolean(GutenbergProps.PROP_CAPABILITIES_XPOSTS, true);
Expand Down
5 changes: 3 additions & 2 deletions packages/react-native-editor/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@
* External dependencies
*/
import 'react-native-gesture-handler';

/**
* Internal dependencies
*/
import { doGutenbergNativeSetup } from './src';
import { registerGutenberg } from './src';

doGutenbergNativeSetup();
registerGutenberg();
207 changes: 60 additions & 147 deletions packages/react-native-editor/src/index.js
Original file line number Diff line number Diff line change
@@ -1,170 +1,83 @@
/**
* External dependencies
*/
import { I18nManager, LogBox } from 'react-native';
import 'react-native-gesture-handler';
import { AppRegistry } from 'react-native';

/**
* WordPress dependencies
*/
import { applyFilters, doAction } from '@wordpress/hooks';
import { Component, cloneElement } from '@wordpress/element';

/**
* Internal dependencies
*/
import './globals';
import { getTranslation } from '../i18n-cache';
import initialHtml from './initial-html';
import setupApiFetch from './api-fetch-setup';
import setupLocale from './setup-locale';
import { getTranslation as getGutenbergTranslation } from '../i18n-cache';

/**
* WordPress dependencies
* Register Gutenberg editor to React Native App registry.
*
* @typedef {Object} PluginTranslation
* @property {string} domain Domain of the plugin.
* @property {Function} getTranslation Function for retrieving translations for a locale.
*
* @param {Object} arguments
* @param {Function} arguments.beforeInitCallback Callback executed before the editor initialization.
* @param {PluginTranslation[]} arguments.pluginTranslations Array with plugin translations.
*/
import {
validateThemeColors,
validateThemeGradients,
} from '@wordpress/block-editor';
import { unregisterBlockType, getBlockType } from '@wordpress/blocks';

const reactNativeSetup = () => {
LogBox.ignoreLogs( [
'Require cycle:', // TODO: Refactor to remove require cycles
'lineHeight', // TODO: Remove lineHeight warning from Aztec
/**
* TODO: Migrate to @gorhom/bottom-sheet or replace usage of
* LayoutAnimation to Animated. KeyboardAvoidingView's usage of
* LayoutAnimation collides with both BottomSheet and NavigationContainer
* usage of LayoutAnimation simultaneously https://git.io/J1lZv,
* https://git.io/J1lZY
*/
'Overriding previous layout animation',
] );

I18nManager.forceRTL( false ); // Change to `true` to debug RTL layout easily.
};

const gutenbergSetup = () => {
const wpData = require( '@wordpress/data' );

// wp-data
const userId = 1;
const storageKey = 'WP_DATA_USER_' + userId;
wpData.use( wpData.plugins.persistence, { storageKey } );

setupApiFetch();

const isHermes = () => global.HermesInternal !== null;
// eslint-disable-next-line no-console
console.log( 'Hermes is: ' + isHermes() );

setupInitHooks();

const initializeEditor = require( '@wordpress/edit-post' ).initializeEditor;
initializeEditor( 'gutenberg', 'post', 1 );
};

const setupInitHooks = () => {
const wpHooks = require( '@wordpress/hooks' );

wpHooks.addAction(
'native.pre-render',
'core/react-native-editor',
( props ) => {
setupLocale( props.locale, props.translations );

const capabilities = props.capabilities ?? {};
if (
getBlockType( 'core/block' ) !== undefined &&
capabilities.reusableBlock !== true
) {
unregisterBlockType( 'core/block' );
}
}
);

// Map native props to Editor props
// TODO: normalize props in the bridge (So we don't have to map initialData to initialHtml)
wpHooks.addFilter(
'native.block_editor_props',
'core/react-native-editor',
( props ) => {
const { capabilities = {} } = props;
let {
initialData,
initialTitle,
postType,
featuredImageId,
colors,
gradients,
rawStyles,
rawFeatures,
galleryWithImageBlocks,
locale,
} = props;

if ( initialData === undefined && __DEV__ ) {
initialData = initialHtml;
}
if ( initialTitle === undefined ) {
initialTitle = 'Welcome to Gutenberg!';
}
if ( postType === undefined ) {
postType = 'post';
const registerGutenberg = ( {
beforeInitCallback,
pluginTranslations = [],
} = {} ) => {
class Gutenberg extends Component {
constructor( props ) {
super( props );

// eslint-disable-next-line no-unused-vars
const { rootTag, ...parentProps } = this.props;

// Setup locale
setupLocale(
parentProps.locale,
parentProps.translations,
getGutenbergTranslation,
pluginTranslations
);

if ( beforeInitCallback ) {
beforeInitCallback( parentProps );
}

colors = validateThemeColors( colors );
// We have to lazy import the setup code to prevent executing any code located
// at global scope before the editor is initialized, like translations retrieval.
const setup = require( './setup' ).default;
// Initialize editor
this.editorComponent = setup();

gradients = validateThemeGradients( gradients );
// Dispatch pre-render hooks
doAction( 'native.pre-render', parentProps );

return {
initialHtml: initialData,
initialHtmlModeEnabled: props.initialHtmlModeEnabled,
initialTitle,
postType,
featuredImageId,
capabilities,
colors,
gradients,
rawStyles,
rawFeatures,
galleryWithImageBlocks,
locale,
};
this.filteredProps = applyFilters(
'native.block_editor_props',
parentProps
);
}
);
};

let blocksRegistered = false;

const setupLocale = ( locale, extraTranslations ) => {
const setLocaleData = require( '@wordpress/i18n' ).setLocaleData;

I18nManager.forceRTL( false ); // Change to `true` to debug RTL layout easily.

let gutenbergTranslations = getTranslation( locale );
if ( locale && ! gutenbergTranslations ) {
// Try stripping out the regional
locale = locale.replace( /[-_][A-Za-z]+$/, '' );
gutenbergTranslations = getTranslation( locale );
}
const translations = Object.assign(
{},
gutenbergTranslations,
extraTranslations
);
// eslint-disable-next-line no-console
console.log( 'locale', locale, translations );
// Only change the locale if it's supported by gutenberg
if ( gutenbergTranslations || extraTranslations ) {
setLocaleData( translations );
}
componentDidMount() {
// Dispatch post-render hooks
doAction( 'native.render', this.filteredProps );
}

if ( blocksRegistered ) {
return;
render() {
return cloneElement( this.editorComponent, this.filteredProps );
}
}

const registerCoreBlocks = require( '@wordpress/block-library' )
.registerCoreBlocks;
registerCoreBlocks();
blocksRegistered = true;
AppRegistry.registerComponent( 'gutenberg', () => Gutenberg );
};

export { initialHtml as initialHtmlGutenberg };
export function doGutenbergNativeSetup() {
reactNativeSetup();
gutenbergSetup();
}
export { initialHtml as initialHtmlGutenberg, registerGutenberg, setupLocale };
Loading

0 comments on commit b7b62d2

Please sign in to comment.