Skip to content

Commit

Permalink
[RNMobile] Add Featured Banner to Image Block (Android Only) (#30806)
Browse files Browse the repository at this point in the history
* Add "set as featured image" button to image block

This commit adds a "set as featured image" button to the image block's settings and also styles it.

* Add "setFeaturedImage" function in bridge code

With this commit, a "setFeaturedImage" function has been added to the React Native bridge code.

* Add setFeaturedImage function to MainApplication

This commit adds the setFeaturedImage function to the MainApplication file, so that it works as expected with the demo apps.

* Add "OnSetFeaturedImageListener" listener

This commit sets up a listener called "OnSetFeaturedImageListener" in order to allow communication with the native apps.

* Update edit.native.js with small syntax correction

This commit updates edit.native.js with small syntax correction, there was a missing comma at the end of a statement.

* Set up "OnSetFeaturedImageListener" in WPAndroidGlue.java

This commit updates WPAndroidGlue.java in order to set up a "OnSetFeaturedImageListener" function that will communicate with the Android app when a user clicks a "set as featured image" button.

* Passing featured image ID from native (Android) to Gutenberg

This commit seeks to send information about a post's featured image ID to Gutenberg by utlising functions from Android.

* Restore missing curly brace

Accidentally removed a curly brace with the last commit, so restoring it with this one.

* Updates to featured-banner.native.js

This commit including updates to featured-banner.native.js, a component for the "featured" banner that will overlay the image block when an image is featured. "payload.featuredImageId" logs the ID of a featured image after it's updated.

* Attempts to select featured image using getEditedPostAttribute()

This commit includes attempts to select a post's featured image using getEditedPostAttribute().

* Update featured-banner.native.js

With this commit, I'm updating featured-banner.native.js with the latest changes to that component's UI.

* Add featured banner to image/index.native.js

With this commit, a featured banner is added to the image component.

* Update featured image banner and button styles

This commit updates the styles for the featured image banner and button that overlay the image compoonent.

* Create getFeaturedImageId function and cleanup

This commit includes a new getFeaturedImageId function, for grabbing a post's featured image when a component is mounted and also cleans up some unused code.

* Tidy up edit.native.js

* Update AndroidGlueCode

Update AndroidGlueCode to include OnFocalPointPickerTooltipShownEventListener.

* retrigger checks

* Update setup.js

* retrigger checks

* Update function name from "featuredImageIdChange" to "featuredImageIdCurrent"

With this commit, I'm updating a function's name from "featuredImageIdChange" to "featuredImageIdCurrent" in order to more accurately reflect its purpose.

* Rename "featuredImageIdNotifier" to "onRequestFeaturedImageId"

This commit renames "featuredImageIdNotifier" to "onRequestFeaturedImageId" in order to clarify the function's purpose.

* Create OnSetFeaturedImageListener

This commit introduces an "OnSetFeaturedImageListener" and moves new featured image related functions to it from "OnMediaLibraryButtonListener".

* Dismiss bottomsheet when "setFeaturedImage" is called

* Introduce "onRemoveFeatured" button and tidying up names

This commit introduces a "onRemoveFeatured" button that displays in the image block's settings when an image is featured. It also changes the name of the "onGetFeaturedImageId" function to "checkIfFeaturedImage", to better reflect its current functionality.

* Updates to function names

This commit updates "onRequestFeaturedImageId" to "sendToJSFeaturedImageId" and "getFeaturedImageId" to "checkIfFeaturedImage". In both cases, this has been done to clarify the purpose of the functions.

* Convert FeaturedBanner to Badge component

This commit converts the FeaturedBanner component to a more generic Badge component. This is to make it more re-usable. There are also some small styling tweaks included in this commit, including changes to the component's border-radius.

* Update edit.native.js to correct merge conflict

* Correct merge conflict with GutenbergBridgeJS2Parent.java

This commit is an attempt to correct this PR's merge conflict with GutenbergBridgeJS2Parent.java.

* retrigger checks

* retrigger checks

* retrigger checks

* Update edit.native.js to fix merge conflict

* Update edit.native.js to check if an image is featured uppn changes

This commit fixes a bug, where the "featured" banner did not update if an image was replaced directly within a block, using the available settings to the upper right.

* Update styles.native.scss to add border to the top of featured button

This commit also tidies up some of the code around styling to account for dark mode.

* Update edit.native.js

This commit moves the "checkIfFeaturedImage" check into its own function.

* Introduce "featuredMedia" prop for use when editor loads

This commit introduces a "featuredMedia" prop that is intended to be used when Gutenberg first loads. The prop is going to be passed over the bridge so that we can get information about a post's featured image ID from the app.

* Fetch ID of post's initial featured image

This commit introduces some changes in react-native-editor/src/index.js that enable getEditedPostAttribute( 'featured_media' ) to fetch the initial ID of a post's featured image. This is then used in the image block's componentDidMount function to pinpoint a featured image when it's first loaded in the editor.

* Updated featured state when image is replaced within block

This commit uses "setAttributes" to define a "currentFeaturedImageId" attribute. This attribute is then used in "componentDidUpdate" to check if an image is featured if it's replaced directly within the image block. There are also some smaller tweaks/improvements to the code in this commit, such as changing an if/else statement to use ES6's shorthand.

* Remove checkIfFeaturedImage function

This function has been replaced with functionality to grab a post's initial ID, as can be seen in the previous couple of commits.

* Fix merge conflict

* Fix merge conflict

* Add "setFeaturedImage" and "featuredImageIdCurrent" functions to Swift files

* Simplify this.state.isFeaturedImage by converting to a boolean insider render()

This commit simplifies the process of determining whether an image is featured (currently done via by setting an isFeaturedImage state) by converting it to a boolean insider the render() function.

* Comment out iOS-specific changes to the bridge

This commit comments out iOS-specific changes to the bridge in ordet to test whether these changes are the reason behind a failing performance test.

* Add iOS functions to bridge

* Flag featured image when editor mounts and comment out iOS methods from bridge

This commit adds a check to "componentDidMount" so that a featured image is flagged when the editor first mounts (fixes previous regression). It also comments out iOS methods over the bridge, a temporary change.

* Comment out a bridge function for iOS (missing from previous commit).

* Add set featured image functionality behind dev flag

* Move "set as featured" button behind devOnly and androidOnly flag

This commit also changes the "featured" banner so that it's accessible to all.

* Remove redundant code

This PR branches off #28854 in order to simplify PRs for a new feature that makes it easier for users to set a featured image from the image block. With this commit, I'm removing redundant code that isn't necessary for this specific PR (which will only include code that adds a "featured" banner to the image block).

* Remove import of android.util.Log

* Update CHANGELOG.md

* Update Badge component for re-usability

This commit updates the Badge component so that it works by wrapping around another component. This is designed to make the component easier to re-use.

* Merge branch 'add-featured-badge' of https://github.com/SiobhyB/gutenberg into add-featured-badge

* Correct merge conflict

* Lift subscribeFeaturedImageIdCurrent from image/edit.native.js to post-edit/edit.native.js

This components lifts the subscribeFeaturedImageIdCurrent function so that the data isn't managed directly from within the image component.

* Remove commented out functions from iOS bridge

This commit also removes a redundant empty space in a bridge file.

* Remove androidOnly const

The androidOnly const is not in use and can be removed.

* Correct typo in README

"add" should have been "adds". This commit corrects that.

* Replace call to 'core' with 'coreStore"

This commit replaces the direct call to the 'core' store with a call to 'coreStore', in line with preferred patterns followed elsewhere in the project.

* Rename featuredMedia to featuredImageId

* Destructure props

This commit destructures props in the componentDidMount function to improve readability

* Rename "featuredImageIdCurrent" to "featuredImageIdNativeUpdated"

Following discussion, this commit renames "featuredImageIdCurrent" to "featuredImageIdNativeUpdated" in order to more clearly describe the function's purpose.

* Limit subscribeFeaturedImageIdNativeUpdated to Android-only

subscribeFeaturedImageIdNativeUpdated is currently only supported in the Android app, so this commit limits its usage to prevent errors on iOS.

* Update CHANGELOG.md
  • Loading branch information
SiobhyB authored Apr 20, 2021
1 parent 326554b commit a88a3b5
Show file tree
Hide file tree
Showing 15 changed files with 143 additions and 4 deletions.
11 changes: 9 additions & 2 deletions packages/block-library/src/image/edit.native.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
LinkSettingsNavigation,
BottomSheetTextControl,
FooterMessageLink,
Badge,
} from '@wordpress/components';
import {
BlockCaption,
Expand Down Expand Up @@ -437,6 +438,7 @@ export class ImageEdit extends Component {
image,
clientId,
imageDefaultSize,
featuredImageId,
} = this.props;
const { align, url, alt, id, sizeSlug, className } = attributes;

Expand All @@ -445,6 +447,8 @@ export class ImageEdit extends Component {
imageDefaultSize,
] );

const isFeaturedImage = featuredImageId === attributes.id;

const getToolbarEditButton = ( open ) => (
<BlockControls>
<ToolbarGroup>
Expand Down Expand Up @@ -507,7 +511,7 @@ export class ImageEdit extends Component {
};

const getImageComponent = ( openMediaOptions, getMediaOptions ) => (
<>
<Badge label={ __( 'Featured' ) } show={ isFeaturedImage }>
<TouchableWithoutFeedback
accessible={ ! isSelected }
onPress={ this.onImagePressed }
Expand Down Expand Up @@ -571,7 +575,7 @@ export class ImageEdit extends Component {
onBlur={ this.props.onBlur } // always assign onBlur as props
insertBlocksAfter={ this.props.insertBlocksAfter }
/>
</>
</Badge>
);

return (
Expand All @@ -591,12 +595,14 @@ export default compose( [
withSelect( ( select, props ) => {
const { getMedia } = select( coreStore );
const { getSettings } = select( blockEditorStore );
const { getEditedPostAttribute } = select( 'core/editor' );
const {
attributes: { id, url },
isSelected,
} = props;
const { imageSizes, imageDefaultSize } = getSettings();
const isNotFileUrl = id && getProtocol( url ) !== 'file:';
const featuredImageId = getEditedPostAttribute( 'featured_media' );

const shouldGetMedia =
( isSelected && isNotFileUrl ) ||
Expand All @@ -610,6 +616,7 @@ export default compose( [
image: shouldGetMedia ? getMedia( id ) : null,
imageSizes,
imageDefaultSize,
featuredImageId,
};
} ),
withPreferredColorScheme,
Expand Down
1 change: 1 addition & 0 deletions packages/components/src/index.native.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ export { default as ImageEditingButton } from './mobile/image/image-editing-butt
export { default as InserterButton } from './mobile/inserter-button';
export { setClipboard, getClipboard } from './mobile/clipboard';
export { default as AudioPlayer } from './mobile/audio-player';
export { default as Badge } from './mobile/badge';

// Utils
export { colorsUtils } from './mobile/color-settings/utils';
Expand Down
31 changes: 31 additions & 0 deletions packages/components/src/mobile/badge/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Badge

The Badge component is designed to be wrapped around another component. It adds a "badge" with some text to the child component's upper left.

An example can be found in the image block. After setting an image as featured, a "featured" badge overlays the block.

### Usage

```jsx
return
<Badge label={ __( 'Hello World!' ) } show={ optionalBoolean }/>
<View></View>
</Badge>;
```

### Props

#### label

The text that will be displayed within the Badge component.

- Type: `String`
- Required: Yes

#### show

An optional boolean to determine whether the badge is displayed.

- Type: `Boolean`
- Required: No
- Default: `true`
27 changes: 27 additions & 0 deletions packages/components/src/mobile/badge/index.native.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/**
* External dependencies
*/
import { View, Text } from 'react-native';

/**
* WordPress dependencies
*/
import { withPreferredColorScheme } from '@wordpress/compose';

/**
* Internal dependencies
*/
import styles from './style.scss';

const Badge = ( { label, children, show = true } ) => {
return (
<>
{ children }
<View style={ styles.badgeContainer }>
{ show && <Text style={ styles.badge }>{ label }</Text> }
</View>
</>
);
};

export default withPreferredColorScheme( Badge );
15 changes: 15 additions & 0 deletions packages/components/src/mobile/badge/style.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
.badgeContainer {
position: absolute;
top: 0;
left: 0;
z-index: 2;
}

.badge {
padding: 10px;
margin: 8px;
color: #fff;
border-radius: 3px;
background-color: $gray-70;
border-color: $gray-70;
}
24 changes: 23 additions & 1 deletion packages/edit-post/src/editor.native.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,12 @@ import { EditorProvider } from '@wordpress/editor';
import { parse, serialize, store as blocksStore } from '@wordpress/blocks';
import { withDispatch, withSelect } from '@wordpress/data';
import { compose } from '@wordpress/compose';
import { subscribeSetFocusOnTitle } from '@wordpress/react-native-bridge';
import {
subscribeSetFocusOnTitle,
subscribeFeaturedImageIdNativeUpdated,
} from '@wordpress/react-native-bridge';
import { SlotFillProvider } from '@wordpress/components';
import { store as coreStore } from '@wordpress/core-data';

/**
* Internal dependencies
Expand Down Expand Up @@ -77,19 +81,33 @@ class Editor extends Component {
}

componentDidMount() {
const { editEntityRecord, postType, postId } = this.props;

this.subscriptionParentSetFocusOnTitle = subscribeSetFocusOnTitle(
() => {
if ( this.postTitleRef ) {
this.postTitleRef.focus();
}
}
);

this.subscriptionParentFeaturedImageIdNativeUpdated = subscribeFeaturedImageIdNativeUpdated(
( payload ) => {
editEntityRecord( 'postType', postType, postId, {
featured_media: payload.featuredImageId,
} );
}
);
}

componentWillUnmount() {
if ( this.subscriptionParentSetFocusOnTitle ) {
this.subscriptionParentSetFocusOnTitle.remove();
}

if ( this.subscribeFeaturedImageIdNativeUpdated ) {
this.subscribeFeaturedImageIdNativeUpdated.remove();
}
}

setTitleRef( titleRef ) {
Expand All @@ -107,6 +125,7 @@ class Editor extends Component {
post,
postId,
postType,
featuredImageId,
initialHtml,
...props
} = this.props;
Expand All @@ -124,6 +143,7 @@ class Editor extends Component {
title: {
raw: props.initialTitle || '',
},
featured_media: featuredImageId,
content: {
// make sure the post content is in sync with gutenberg store
// to avoid marking the post as modified when simply loaded
Expand Down Expand Up @@ -173,8 +193,10 @@ export default compose( [
} ),
withDispatch( ( dispatch ) => {
const { switchEditorMode } = dispatch( editPostStore );
const { editEntityRecord } = dispatch( coreStore );
return {
switchEditorMode,
editEntityRecord,
};
} ),
] )( Editor );
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ interface MediaSaveEventEmitter {
void onReplaceMediaFilesEditedBlock(final String mediaFiles, final String blockId);
}

interface FeaturedImageEmitter {
void sendToJSFeaturedImageId(int mediaId);
}

interface ReplaceUnsupportedBlockCallback {
void replaceUnsupportedBlock(String content, String blockId);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ public class RNReactNativeGutenbergBridgeModule extends ReactContextBaseJavaModu
private static final String MAP_KEY_REPLACE_BLOCK_HTML = "html";
private static final String MAP_KEY_REPLACE_BLOCK_BLOCK_ID = "clientId";

public static final String MAP_KEY_FEATURED_IMAGE_ID = "featuredImageId";

private boolean mIsDarkMode;

public RNReactNativeGutenbergBridgeModule(ReactApplicationContext reactContext,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

import org.wordpress.mobile.ReactNativeGutenbergBridge.GutenbergBridgeJS2Parent.MediaUploadEventEmitter;
import org.wordpress.mobile.ReactNativeGutenbergBridge.GutenbergBridgeJS2Parent.MediaSaveEventEmitter;
import org.wordpress.mobile.ReactNativeGutenbergBridge.GutenbergBridgeJS2Parent.FeaturedImageEmitter;

import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
Expand All @@ -18,8 +19,9 @@
import static org.wordpress.mobile.ReactNativeGutenbergBridge.RNReactNativeGutenbergBridgeModule.MAP_KEY_MEDIA_FILE_UPLOAD_MEDIA_NEW_ID;
import static org.wordpress.mobile.ReactNativeGutenbergBridge.RNReactNativeGutenbergBridgeModule.MAP_KEY_MEDIA_FILE_UPLOAD_MEDIA_URL;
import static org.wordpress.mobile.ReactNativeGutenbergBridge.RNReactNativeGutenbergBridgeModule.MAP_KEY_MEDIA_FINAL_SAVE_RESULT_SUCCESS_VALUE;
import static org.wordpress.mobile.ReactNativeGutenbergBridge.RNReactNativeGutenbergBridgeModule.MAP_KEY_FEATURED_IMAGE_ID;

public class DeferredEventEmitter implements MediaUploadEventEmitter, MediaSaveEventEmitter {
public class DeferredEventEmitter implements MediaUploadEventEmitter, MediaSaveEventEmitter, FeaturedImageEmitter {
public interface JSEventEmitter {
void emitToJS(String eventName, @Nullable WritableMap data);
}
Expand All @@ -40,6 +42,8 @@ public interface JSEventEmitter {
private static final String EVENT_NAME_MEDIA_SAVE = "mediaSave";
private static final String EVENT_NAME_MEDIA_REPLACE_BLOCK = "replaceBlock";

private static final String EVENT_FEATURED_IMAGE_ID_NATIVE_UPDATED = "featuredImageIdNativeUpdated";

private static final String MAP_KEY_MEDIA_FILE_STATE = "state";
private static final String MAP_KEY_MEDIA_FILE_MEDIA_ACTION_PROGRESS = "progress";
private static final String MAP_KEY_MEDIA_FILE_MEDIA_SERVER_ID = "mediaServerId";
Expand Down Expand Up @@ -205,6 +209,12 @@ public void onMediaCollectionSaveResult(String firstMediaIdInCollection, boolean
}
}

public void sendToJSFeaturedImageId(int mediaId) {
WritableMap writableMap = new WritableNativeMap();
writableMap.putInt(MAP_KEY_FEATURED_IMAGE_ID, mediaId);
queueActionToJS(EVENT_FEATURED_IMAGE_ID_NATIVE_UPDATED, writableMap);
}

@Override public void onReplaceMediaFilesEditedBlock(String mediaFiles, String blockId) {
WritableMap writableMap = new WritableNativeMap();
writableMap.putString(MAP_KEY_REPLACE_BLOCK_HTML, mediaFiles);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ data class GutenbergProps @JvmOverloads constructor(
val enableAudioBlock: Boolean,
val localeSlug: String,
val postType: String,
val featuredImageId: Int,
val editorTheme: Bundle?,
val translations: Bundle,
val isDarkMode: Boolean,
Expand All @@ -23,6 +24,7 @@ data class GutenbergProps @JvmOverloads constructor(
putString(PROP_INITIAL_TITLE, "")
putString(PROP_LOCALE, localeSlug)
putString(PROP_POST_TYPE, postType)
putInt(PROP_INITIAL_FEATURED_IMAGE_ID, featuredImageId)
putBundle(PROP_TRANSLATIONS, translations)
putBoolean(PROP_INITIAL_HTML_MODE_ENABLED, htmlModeEnabled)

Expand Down Expand Up @@ -56,6 +58,7 @@ data class GutenbergProps @JvmOverloads constructor(
private const val PROP_INITIAL_TITLE = "initialTitle"
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"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -956,6 +956,10 @@ public void mediaIdChanged(final String oldId, final String newId, final String
mDeferredEventEmitter.onMediaIdChanged(oldId, newId, oldUrl);
}

public void sendToJSFeaturedImageId(int mediaId) {
mDeferredEventEmitter.sendToJSFeaturedImageId(mediaId);
}

public void replaceUnsupportedBlock(String content, String blockId) {
if (mReplaceUnsupportedBlockCallback != null) {
mReplaceUnsupportedBlockCallback.replaceUnsupportedBlock(content, blockId);
Expand Down
9 changes: 9 additions & 0 deletions packages/react-native-bridge/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,15 @@ export function subscribeUpdateHtml( callback ) {
return gutenbergBridgeEvents.addListener( 'updateHtml', callback );
}

export function subscribeFeaturedImageIdNativeUpdated( callback ) {
return isAndroid
? gutenbergBridgeEvents.addListener(
'featuredImageIdNativeUpdated',
callback
)
: undefined;
}

/**
* Request to subscribe to mediaUpload events
*
Expand Down
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

- [*] Image block: Improve text entry for long alt text. [#29670]
- [*] Image block: Add a "featured" banner. (Android only) [#30806]

## 1.50.0

Expand Down
2 changes: 2 additions & 0 deletions packages/react-native-editor/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ const setupInitHooks = () => {
initialData,
initialTitle,
postType,
featuredImageId,
colors,
gradients,
} = props;
Expand All @@ -93,6 +94,7 @@ const setupInitHooks = () => {
initialHtmlModeEnabled: props.initialHtmlModeEnabled,
initialTitle,
postType,
featuredImageId,
capabilities,
colors,
gradients,
Expand Down
1 change: 1 addition & 0 deletions test/native/setup.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ jest.mock( '@wordpress/react-native-bridge', () => {
subscribeSetTitle: jest.fn(),
subscribeSetFocusOnTitle: jest.fn(),
subscribeUpdateHtml: jest.fn(),
subscribeFeaturedImageIdNativeUpdated: jest.fn(),
subscribeMediaAppend: jest.fn(),
subscribeAndroidModalClosed: jest.fn(),
subscribeUpdateTheme: jest.fn(),
Expand Down

0 comments on commit a88a3b5

Please sign in to comment.