diff --git a/packages/react-native-bridge/android/src/main/java/org/wordpress/mobile/ReactNativeGutenbergBridge/GutenbergBridgeJS2Parent.java b/packages/react-native-bridge/android/src/main/java/org/wordpress/mobile/ReactNativeGutenbergBridge/GutenbergBridgeJS2Parent.java index f912598e0f498b..6c94893d143df9 100644 --- a/packages/react-native-bridge/android/src/main/java/org/wordpress/mobile/ReactNativeGutenbergBridge/GutenbergBridgeJS2Parent.java +++ b/packages/react-native-bridge/android/src/main/java/org/wordpress/mobile/ReactNativeGutenbergBridge/GutenbergBridgeJS2Parent.java @@ -166,7 +166,9 @@ void gutenbergDidRequestUnsupportedBlockFallback(ReplaceUnsupportedBlockCallback void gutenbergDidSendButtonPressedAction(String buttonType); - void onAddMention(Consumer onSuccess); + void onShowUserSuggestions(Consumer onResult); + + void onShowXpostSuggestions(Consumer onResult); void setStarterPageTemplatesTooltipShown(boolean tooltipShown); diff --git a/packages/react-native-bridge/android/src/main/java/org/wordpress/mobile/ReactNativeGutenbergBridge/RNReactNativeGutenbergBridgeModule.java b/packages/react-native-bridge/android/src/main/java/org/wordpress/mobile/ReactNativeGutenbergBridge/RNReactNativeGutenbergBridgeModule.java index 4a6b844b2f5c10..d00dc5a566a74a 100644 --- a/packages/react-native-bridge/android/src/main/java/org/wordpress/mobile/ReactNativeGutenbergBridge/RNReactNativeGutenbergBridgeModule.java +++ b/packages/react-native-bridge/android/src/main/java/org/wordpress/mobile/ReactNativeGutenbergBridge/RNReactNativeGutenbergBridgeModule.java @@ -323,8 +323,13 @@ private OtherMediaOptionsReceivedCallback getNewOtherMediaReceivedCallback(final } @ReactMethod - public void addMention(Promise promise) { - mGutenbergBridgeJS2Parent.onAddMention(promise::resolve); + public void showUserSuggestions(Promise promise) { + mGutenbergBridgeJS2Parent.onShowUserSuggestions(promise::resolve); + } + + @ReactMethod + public void showXpostSuggestions(Promise promise) { + mGutenbergBridgeJS2Parent.onShowXpostSuggestions(promise::resolve); } @ReactMethod diff --git a/packages/react-native-bridge/android/src/main/java/org/wordpress/mobile/WPAndroidGlue/AddMentionUtil.java b/packages/react-native-bridge/android/src/main/java/org/wordpress/mobile/WPAndroidGlue/AddMentionUtil.java deleted file mode 100644 index 837f10e1562835..00000000000000 --- a/packages/react-native-bridge/android/src/main/java/org/wordpress/mobile/WPAndroidGlue/AddMentionUtil.java +++ /dev/null @@ -1,7 +0,0 @@ -package org.wordpress.mobile.WPAndroidGlue; - -import androidx.core.util.Consumer; - -public interface AddMentionUtil { - void getMention(Consumer onResult); -} diff --git a/packages/react-native-bridge/android/src/main/java/org/wordpress/mobile/WPAndroidGlue/GutenbergProps.kt b/packages/react-native-bridge/android/src/main/java/org/wordpress/mobile/WPAndroidGlue/GutenbergProps.kt index f9f65dd9c20838..7049bc099c8221 100644 --- a/packages/react-native-bridge/android/src/main/java/org/wordpress/mobile/WPAndroidGlue/GutenbergProps.kt +++ b/packages/react-native-bridge/android/src/main/java/org/wordpress/mobile/WPAndroidGlue/GutenbergProps.kt @@ -5,6 +5,7 @@ import android.os.Bundle data class GutenbergProps @JvmOverloads constructor( val enableMediaFilesCollectionBlocks: Boolean, val enableMentions: Boolean, + val enableXPosts: Boolean, val enableUnsupportedBlockEditor: Boolean, val canEnableUnsupportedBlockEditor: Boolean, val localeSlug: String, @@ -38,6 +39,7 @@ data class GutenbergProps @JvmOverloads constructor( fun getUpdatedCapabilitiesProps() = Bundle().apply { putBoolean(PROP_CAPABILITIES_MENTIONS, enableMentions) + putBoolean(PROP_CAPABILITIES_XPOSTS, enableXPosts) putBoolean(PROP_CAPABILITIES_MEDIAFILES_COLLECTION_BLOCK, enableMediaFilesCollectionBlocks) putBoolean(PROP_CAPABILITIES_UNSUPPORTED_BLOCK_EDITOR, enableUnsupportedBlockEditor) putBoolean(PROP_CAPABILITIES_CAN_ENABLE_UNSUPPORTED_BLOCK_EDITOR, canEnableUnsupportedBlockEditor) @@ -68,6 +70,7 @@ data class GutenbergProps @JvmOverloads constructor( const val PROP_CAPABILITIES = "capabilities" const val PROP_CAPABILITIES_MEDIAFILES_COLLECTION_BLOCK = "mediaFilesCollectionBlock" const val PROP_CAPABILITIES_MENTIONS = "mentions" + const val PROP_CAPABILITIES_XPOSTS = "xposts" const val PROP_CAPABILITIES_UNSUPPORTED_BLOCK_EDITOR = "unsupportedBlockEditor" const val PROP_CAPABILITIES_CAN_ENABLE_UNSUPPORTED_BLOCK_EDITOR = "canEnableUnsupportedBlockEditor" const val PROP_CAPABILITIES_MODAL_LAYOUT_PICKER = "modalLayoutPicker" diff --git a/packages/react-native-bridge/android/src/main/java/org/wordpress/mobile/WPAndroidGlue/ShowSuggestionsUtil.java b/packages/react-native-bridge/android/src/main/java/org/wordpress/mobile/WPAndroidGlue/ShowSuggestionsUtil.java new file mode 100644 index 00000000000000..cd78ded5b6b242 --- /dev/null +++ b/packages/react-native-bridge/android/src/main/java/org/wordpress/mobile/WPAndroidGlue/ShowSuggestionsUtil.java @@ -0,0 +1,8 @@ +package org.wordpress.mobile.WPAndroidGlue; + +import androidx.core.util.Consumer; + +public interface ShowSuggestionsUtil { + void showUserSuggestions(Consumer onResult); + void showXpostSuggestions(Consumer onResult); +} diff --git a/packages/react-native-bridge/android/src/main/java/org/wordpress/mobile/WPAndroidGlue/WPAndroidGlueCode.java b/packages/react-native-bridge/android/src/main/java/org/wordpress/mobile/WPAndroidGlue/WPAndroidGlueCode.java index 9c1de6ab757722..6feeb61581b287 100644 --- a/packages/react-native-bridge/android/src/main/java/org/wordpress/mobile/WPAndroidGlue/WPAndroidGlueCode.java +++ b/packages/react-native-bridge/android/src/main/java/org/wordpress/mobile/WPAndroidGlue/WPAndroidGlueCode.java @@ -109,7 +109,7 @@ public class WPAndroidGlueCode { private CountDownLatch mGetContentCountDownLatch; private WeakReference mLastFocusedView = null; private RequestExecutor mRequestExecutor; - private AddMentionUtil mAddMentionUtil; + private ShowSuggestionsUtil mShowSuggestionsUtil; private @Nullable Bundle mEditorTheme = null; private static OkHttpHeaderInterceptor sAddCookiesInterceptor = new OkHttpHeaderInterceptor(); @@ -411,8 +411,12 @@ public void gutenbergDidSendButtonPressedAction(String buttonType) { } @Override - public void onAddMention(Consumer onSuccess) { - mAddMentionUtil.getMention(onSuccess); + public void onShowUserSuggestions(Consumer onResult) { + mShowSuggestionsUtil.showUserSuggestions(onResult); + } + + @Override public void onShowXpostSuggestions(Consumer onResult) { + mShowSuggestionsUtil.showXpostSuggestions(onResult); } @Override @@ -531,7 +535,7 @@ public void attachToContainer(ViewGroup viewGroup, OnLogGutenbergUserEventListener onLogGutenbergUserEventListener, OnGutenbergDidRequestUnsupportedBlockFallbackListener onGutenbergDidRequestUnsupportedBlockFallbackListener, OnGutenbergDidSendButtonPressedActionListener onGutenbergDidSendButtonPressedActionListener, - AddMentionUtil addMentionUtil, + ShowSuggestionsUtil showSuggestionsUtil, OnStarterPageTemplatesTooltipShownEventListener onStarterPageTemplatesTooltipListener, OnMediaFilesCollectionBasedBlockEditorListener onMediaFilesCollectionBasedBlockEditorListener, boolean isDarkMode) { @@ -549,7 +553,7 @@ public void attachToContainer(ViewGroup viewGroup, mOnLogGutenbergUserEventListener = onLogGutenbergUserEventListener; mOnGutenbergDidRequestUnsupportedBlockFallbackListener = onGutenbergDidRequestUnsupportedBlockFallbackListener; mOnGutenbergDidSendButtonPressedActionListener = onGutenbergDidSendButtonPressedActionListener; - mAddMentionUtil = addMentionUtil; + mShowSuggestionsUtil = showSuggestionsUtil; mOnStarterPageTemplatesTooltipShownListener = onStarterPageTemplatesTooltipListener; mOnMediaFilesCollectionBasedBlockEditorListener = onMediaFilesCollectionBasedBlockEditorListener; diff --git a/packages/react-native-bridge/index.js b/packages/react-native-bridge/index.js index f7d76c91d53cd5..e5bc1db51a0ab2 100644 --- a/packages/react-native-bridge/index.js +++ b/packages/react-native-bridge/index.js @@ -279,8 +279,12 @@ export function logUserEvent( event, properties ) { return RNReactNativeGutenbergBridge.logUserEvent( event, properties ); } -export function addMention() { - return RNReactNativeGutenbergBridge.addMention(); +export function showUserSuggestions() { + return RNReactNativeGutenbergBridge.showUserSuggestions(); +} + +export function showXpostSuggestions() { + return RNReactNativeGutenbergBridge.showXpostSuggestions(); } export function requestStarterPageTemplatesTooltipShown( callback ) { diff --git a/packages/react-native-bridge/ios/GutenbergBridgeDelegate.swift b/packages/react-native-bridge/ios/GutenbergBridgeDelegate.swift index 2e624791346908..753515f600a7e3 100644 --- a/packages/react-native-bridge/ios/GutenbergBridgeDelegate.swift +++ b/packages/react-native-bridge/ios/GutenbergBridgeDelegate.swift @@ -18,6 +18,7 @@ public struct MediaInfo: Encodable { public enum Capabilities: String { case mediaFilesCollectionBlock case mentions + case xposts case unsupportedBlockEditor case canEnableUnsupportedBlockEditor case modalLayoutPicker @@ -225,6 +226,10 @@ public protocol GutenbergBridgeDelegate: class { /// - Parameter callback: Completion handler to be called with an user mention or an error func gutenbergDidRequestMention(callback: @escaping (Swift.Result) -> Void) + /// Tells the delegate that the editor requested a mention + /// - Parameter callback: Completion handler to be called with an xpost or an error + func gutenbergDidRequestXpost(callback: @escaping (Swift.Result) -> Void) + /// Tells the delegate that the editor requested to show the tooltip func gutenbergDidRequestStarterPageTemplatesTooltipShown() -> Bool diff --git a/packages/react-native-bridge/ios/RNReactNativeGutenbergBridge.m b/packages/react-native-bridge/ios/RNReactNativeGutenbergBridge.m index aafdce5c869aa0..9443cae473c1f3 100644 --- a/packages/react-native-bridge/ios/RNReactNativeGutenbergBridge.m +++ b/packages/react-native-bridge/ios/RNReactNativeGutenbergBridge.m @@ -20,7 +20,8 @@ @interface RCT_EXTERN_MODULE(RNReactNativeGutenbergBridge, NSObject) RCT_EXTERN_METHOD(requestMediaEditor:(NSString *)mediaUrl callback:(RCTResponseSenderBlock)callback) RCT_EXTERN_METHOD(logUserEvent:(NSString *)event properties:(NSDictionary *)properties) RCT_EXTERN_METHOD(requestUnsupportedBlockFallback:(NSString *)content blockId:(NSString *)blockId blockName:(NSString *)blockName blockTitle:(NSString *)blockTitle) -RCT_EXTERN_METHOD(addMention:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)rejecter) +RCT_EXTERN_METHOD(showUserSuggestions:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)rejecter) +RCT_EXTERN_METHOD(showXpostSuggestions:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)rejecter) RCT_EXTERN_METHOD(requestStarterPageTemplatesTooltipShown:(RCTResponseSenderBlock)callback) RCT_EXTERN_METHOD(setStarterPageTemplatesTooltipShown:(BOOL)tooltipShown) RCT_EXTERN_METHOD(requestMediaFilesEditorLoad:(NSArray *)mediaFiles blockId:(NSString *)blockId) diff --git a/packages/react-native-bridge/ios/RNReactNativeGutenbergBridge.swift b/packages/react-native-bridge/ios/RNReactNativeGutenbergBridge.swift index 3d8168ba17e8d4..927a59eb8e0fbe 100644 --- a/packages/react-native-bridge/ios/RNReactNativeGutenbergBridge.swift +++ b/packages/react-native-bridge/ios/RNReactNativeGutenbergBridge.swift @@ -267,7 +267,7 @@ public class RNReactNativeGutenbergBridge: RCTEventEmitter { } @objc - func addMention(_ resolver: @escaping RCTPromiseResolveBlock, rejecter: @escaping RCTPromiseRejectBlock) { + func showUserSuggestions(_ resolver: @escaping RCTPromiseResolveBlock, rejecter: @escaping RCTPromiseRejectBlock) { self.delegate?.gutenbergDidRequestMention(callback: { (result) in switch result { case .success(let mention): @@ -278,6 +278,18 @@ public class RNReactNativeGutenbergBridge: RCTEventEmitter { }) } + @objc + func showXpostSuggestions(_ resolver: @escaping RCTPromiseResolveBlock, rejecter: @escaping RCTPromiseRejectBlock) { + self.delegate?.gutenbergDidRequestXpost(callback: { (result) in + switch result { + case .success(let mention): + resolver([mention]) + case .failure(let error): + rejecter(error.domain, "\(error.code)", error) + } + }) + } + @objc func requestStarterPageTemplatesTooltipShown(_ callback: @escaping RCTResponseSenderBlock) { callback([self.delegate?.gutenbergDidRequestStarterPageTemplatesTooltipShown() ?? false]) diff --git a/packages/react-native-editor/CHANGELOG.md b/packages/react-native-editor/CHANGELOG.md index c99f4ac6e477f9..095ed5bbc8fd57 100644 --- a/packages/react-native-editor/CHANGELOG.md +++ b/packages/react-native-editor/CHANGELOG.md @@ -24,6 +24,10 @@ For each user feature we should also add a importance categorization label to i * [*] Fix theme colors syncing with the editor [#26821] * [**] Fix issue where a blocks would disappear when deleting all of the text inside without requiring the extra backspace to remove the block. [#27583] +## 1.44.0 + +* [***] Add support for cross-posting between sites + ## 1.41.0 * [***] Faster editor start and overall operation on Android [#26732] diff --git a/packages/react-native-editor/android/app/src/main/java/com/gutenberg/MainActivity.java b/packages/react-native-editor/android/app/src/main/java/com/gutenberg/MainActivity.java index 83bbea9e5d5a15..ed3fdb7616676a 100644 --- a/packages/react-native-editor/android/app/src/main/java/com/gutenberg/MainActivity.java +++ b/packages/react-native-editor/android/app/src/main/java/com/gutenberg/MainActivity.java @@ -29,6 +29,7 @@ protected Bundle getLaunchOptions() { Bundle bundle = new Bundle(); Bundle capabilities = new Bundle(); capabilities.putBoolean(GutenbergProps.PROP_CAPABILITIES_MENTIONS, true); + capabilities.putBoolean(GutenbergProps.PROP_CAPABILITIES_XPOSTS, true); capabilities.putBoolean(GutenbergProps.PROP_CAPABILITIES_UNSUPPORTED_BLOCK_EDITOR, true); bundle.putBundle(GutenbergProps.PROP_CAPABILITIES, capabilities); return bundle; diff --git a/packages/react-native-editor/android/app/src/main/java/com/gutenberg/MainApplication.java b/packages/react-native-editor/android/app/src/main/java/com/gutenberg/MainApplication.java index 628feb8b8b6dbb..afa269ee7ce1c6 100644 --- a/packages/react-native-editor/android/app/src/main/java/com/gutenberg/MainApplication.java +++ b/packages/react-native-editor/android/app/src/main/java/com/gutenberg/MainApplication.java @@ -189,8 +189,13 @@ public void gutenbergDidRequestUnsupportedBlockFallback(ReplaceUnsupportedBlockC } @Override - public void onAddMention(Consumer onSuccess) { - onSuccess.accept("matt"); + public void onShowUserSuggestions(Consumer onResult) { + onResult.accept("matt"); + } + + @Override + public void onShowXpostSuggestions(Consumer onResult) { + onResult.accept("ma.tt"); } @Override diff --git a/packages/react-native-editor/ios/GutenbergDemo/GutenbergViewController.swift b/packages/react-native-editor/ios/GutenbergDemo/GutenbergViewController.swift index 06ba6a6704bb32..b0002e0e7b5917 100644 --- a/packages/react-native-editor/ios/GutenbergDemo/GutenbergViewController.swift +++ b/packages/react-native-editor/ios/GutenbergDemo/GutenbergViewController.swift @@ -225,6 +225,10 @@ extension GutenbergViewController: GutenbergBridgeDelegate { callback(.success("matt")) } + func gutenbergDidRequestXpost(callback: @escaping (Result) -> Void) { + callback(.success("ma.tt")) + } + func gutenbergDidRequestStarterPageTemplatesTooltipShown() -> Bool { return false; } @@ -298,6 +302,7 @@ extension GutenbergViewController: GutenbergBridgeDataSource { func gutenbergCapabilities() -> [Capabilities : Bool] { return [ .mentions: true, + .xposts: true, .unsupportedBlockEditor: unsupportedBlockEnabled, .canEnableUnsupportedBlockEditor: unsupportedBlockCanBeActivated, .mediaFilesCollectionBlock: true, diff --git a/packages/rich-text/src/component/index.native.js b/packages/rich-text/src/component/index.native.js index 3ecede565bbde8..2d3f3819dbac13 100644 --- a/packages/rich-text/src/component/index.native.js +++ b/packages/rich-text/src/component/index.native.js @@ -8,7 +8,10 @@ */ import RCTAztecView from '@wordpress/react-native-aztec'; import { View, Platform } from 'react-native'; -import { addMention } from '@wordpress/react-native-bridge'; +import { + showUserSuggestions, + showXpostSuggestions, +} from '@wordpress/react-native-bridge'; import { get, pickBy, debounce } from 'lodash'; import memize from 'memize'; @@ -17,14 +20,13 @@ import memize from 'memize'; */ import { BlockFormatControls } from '@wordpress/block-editor'; import { Component } from '@wordpress/element'; -import { Toolbar, ToolbarButton } from '@wordpress/components'; import { compose, withPreferredColorScheme } from '@wordpress/compose'; import { withSelect } from '@wordpress/data'; import { childrenBlock } from '@wordpress/blocks'; import { decodeEntities } from '@wordpress/html-entities'; import { BACKSPACE, DELETE, ENTER } from '@wordpress/keycodes'; import { isURL } from '@wordpress/url'; -import { Icon, atSymbol } from '@wordpress/icons'; +import { atSymbol, plus } from '@wordpress/icons'; import { __ } from '@wordpress/i18n'; /** @@ -43,6 +45,7 @@ import { removeLineSeparator } from '../remove-line-separator'; import { isCollapsed } from '../is-collapsed'; import { remove } from '../remove'; import styles from './style.scss'; +import ToolbarButtonWithOptions from './toolbar-button-with-options'; import { store as richTextStore } from '../store'; const unescapeSpaces = ( text ) => { @@ -83,7 +86,6 @@ export class RichText extends Component { this.onKeyDown = this.onKeyDown.bind( this ); this.handleEnter = this.handleEnter.bind( this ); this.handleDelete = this.handleDelete.bind( this ); - this.handleMention = this.handleMention.bind( this ); this.onPaste = this.onPaste.bind( this ); this.onFocus = this.onFocus.bind( this ); this.onBlur = this.onBlur.bind( this ); @@ -101,7 +103,16 @@ export class RichText extends Component { ); this.valueToFormat = this.valueToFormat.bind( this ); this.getHtmlToRender = this.getHtmlToRender.bind( this ); - this.showMention = this.showMention.bind( this ); + this.handleSuggestionFunc = this.handleSuggestionFunc.bind( this ); + this.handleUserSuggestion = this.handleSuggestionFunc( + showUserSuggestions, + '@' + ).bind( this ); + this.handleXpostSuggestion = this.handleSuggestionFunc( + showXpostSuggestions, + '+' + ).bind( this ); + this.suggestionOptions = this.suggestionOptions.bind( this ); this.insertString = this.insertString.bind( this ); this.state = { activeFormats: [], @@ -322,7 +333,7 @@ export class RichText extends Component { this.handleDelete( event ); this.handleEnter( event ); - this.handleMention( event ); + this.handleTriggerKeyCodes( event ); } handleEnter( event ) { @@ -401,33 +412,66 @@ export class RichText extends Component { this.lastAztecEventType = 'input'; } - handleMention( event ) { + handleTriggerKeyCodes( event ) { const { keyCode } = event; + const triggeredOption = this.suggestionOptions().find( ( option ) => { + const triggeredKeyCode = option.triggerChar.charCodeAt( 0 ); + return triggeredKeyCode === keyCode; + } ); - if ( keyCode !== '@'.charCodeAt( 0 ) ) { - return; - } - const record = this.getRecord(); - const text = getTextContent( record ); - // Only start the mention UI if the selection is on the start of text or the character before is a space - if ( - text.length === 0 || - record.start === 0 || - text.charAt( record.start - 1 ) === ' ' - ) { - this.showMention(); - } else { - this.insertString( record, '@' ); + if ( triggeredOption ) { + const record = this.getRecord(); + const text = getTextContent( record ); + // Only respond to the trigger if the selection is on the start of text or line + // or if the character before is a space + const useTrigger = + text.length === 0 || + record.start === 0 || + text.charAt( record.start - 1 ) === '\n' || + text.charAt( record.start - 1 ) === ' '; + + if ( useTrigger && triggeredOption.onClick ) { + triggeredOption.onClick(); + } else { + this.insertString( record, triggeredOption.triggerChar ); + } } } - showMention() { - const record = this.getRecord(); - addMention() - .then( ( mentionUserId ) => { - this.insertString( record, `@${ mentionUserId } ` ); - } ) - .catch( () => {} ); + suggestionOptions() { + const { areMentionsSupported, areXPostsSupported } = this.props; + const allOptions = [ + { + supported: areMentionsSupported, + title: __( 'Insert mention' ), + onClick: this.handleUserSuggestion, + triggerChar: '@', + value: 'mention', + label: __( 'Mention' ), + icon: atSymbol, + }, + { + supported: areXPostsSupported, + title: __( 'Insert crosspost' ), + onClick: this.handleXpostSuggestion, + triggerChar: '+', + value: 'crosspost', + label: __( 'Crosspost' ), + icon: plus, + }, + ]; + return allOptions.filter( ( op ) => op.supported ); + } + + handleSuggestionFunc( suggestionFunction, prefix ) { + return () => { + const record = this.getRecord(); + suggestionFunction() + .then( ( suggestion ) => { + this.insertString( record, `${ prefix }${ suggestion } ` ); + } ) + .catch( () => {} ); + }; } /** @@ -758,7 +802,6 @@ export class RichText extends Component { withoutInteractiveFormatting, accessibilityLabel, disableEditingMenu = false, - isMentionsSupported, } = this.props; const record = this.getRecord(); @@ -874,11 +917,9 @@ export class RichText extends Component { onFocus={ this.onFocus } onBlur={ this.onBlur } onKeyDown={ this.onKeyDown } - triggerKeyCodes={ - disableEditingMenu === false && isMentionsSupported - ? [ '@' ] - : [] - } + triggerKeyCodes={ this.suggestionOptions().map( + ( op ) => op.triggerChar + ) } onPaste={ this.onPaste } activeFormats={ this.getActiveFormatNames( record ) } onContentSizeChange={ this.onContentSizeChange } @@ -919,18 +960,9 @@ export class RichText extends Component { onFocus={ () => {} } /> - { - // eslint-disable-next-line no-undef - isMentionsSupported && ( - - } - onClick={ this.showMention } - /> - - ) - } + ) } @@ -957,8 +989,9 @@ export default compose( [ return { formatTypes: select( richTextStore ).getFormatTypes(), - isMentionsSupported: + areMentionsSupported: getSettings( 'capabilities' ).mentions === true, + areXPostsSupported: getSettings( 'capabilities' ).xposts === true, ...{ parentBlockStyles }, }; } ), diff --git a/packages/rich-text/src/component/toolbar-button-with-options.native.js b/packages/rich-text/src/component/toolbar-button-with-options.native.js new file mode 100644 index 00000000000000..81c80f3825c1ac --- /dev/null +++ b/packages/rich-text/src/component/toolbar-button-with-options.native.js @@ -0,0 +1,58 @@ +/** + * WordPress dependencies + */ +import { Picker, ToolbarGroup, ToolbarButton } from '@wordpress/components'; +import { useRef } from '@wordpress/element'; +import { Icon } from '@wordpress/icons'; + +/** + * Toolbar button component that, upon a long press, opens a Picker + * to allow selecting from among multiple options. + */ +function ToolbarButtonWithOptions( { options } ) { + const picker = useRef(); + + function presentPicker() { + if ( picker.current ) { + picker.current.presentPicker(); + } + } + + function onValueSelected( selectedValue ) { + const selectedOption = options.find( + ( op ) => op.value === selectedValue + ); + if ( selectedOption ) { + selectedOption.onClick(); + } + } + + if ( ! options || options.length === 0 ) { + return null; + } + const firstOption = options[ 0 ]; + const enablePicker = options.length > 1; + + return ( + <> + + } + onClick={ firstOption.onClick } + onLongPress={ enablePicker && presentPicker } + /> + + { enablePicker && ( + + ) } + + ); +} + +export default ToolbarButtonWithOptions;