Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue 331/mention support on @ keypress #22119

Merged
Merged
Show file tree
Hide file tree
Changes from 41 commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
e10e226
Add '@' button for inserting mentions on mobile
mchowning Apr 15, 2020
e519b00
Merge branch 'master' into rnmobile/issue_331/mention_support
SergioEstevao Apr 20, 2020
f9b8548
Move react-native-azted dependency to external dependencies section
mchowning Apr 20, 2020
17f3196
Merge branch 'master' into rnmobile/issue_331/mention_support
SergioEstevao Apr 21, 2020
c862074
Handle promise rejection.
SergioEstevao Apr 21, 2020
f42daee
Fix focus issue.
SergioEstevao Apr 21, 2020
31da259
Add space after mention
SergioEstevao Apr 22, 2020
7ecbb7c
Update selection onFocus.
SergioEstevao Apr 23, 2020
cbb14e4
Merge branch 'master' into rnmobile/issue_331/mention_support
SergioEstevao Apr 24, 2020
4877a9b
Check for site capabilities for mentions support
SergioEstevao Apr 24, 2020
30052c6
Merge branch 'rnmobile/release-1.27.0' into rnmobile/issue_331/mentio…
SergioEstevao Apr 27, 2020
a833d4b
Add the mention button inside a toolbar.
SergioEstevao Apr 27, 2020
3487ffc
Use HOC for site capabilities.
SergioEstevao Apr 27, 2020
aa52210
Merge branch 'master' into rnmobile/issue_331/mention_support
SergioEstevao Apr 28, 2020
c968ab7
Only include space after mention on iOS
mchowning Apr 28, 2020
b737677
Merge branch 'master' into rnmobile/issue_331/mention_support
SergioEstevao Apr 30, 2020
7f3cee1
Use onKeyDown instead of onEnter and onBackspace.
SergioEstevao May 5, 2020
2fd1dfd
Intercept @ keypress to trigger mention UI.
SergioEstevao May 5, 2020
46665ab
Intercept @ keypress to trigger mention UI.
SergioEstevao May 5, 2020
0ec60af
Trigger the UI for mentions only when there is a space before the @
SergioEstevao May 6, 2020
94568a8
Put mentions behind the DEV flag.
SergioEstevao May 8, 2020
29d5f68
Merge branch 'rnmobile/issue_331/mention_support' into rnmobile/issue…
SergioEstevao May 8, 2020
0b76315
Only trigger @ keypress on DEV builds.
SergioEstevao May 8, 2020
e86a5a6
Merge branch 'rnmobile/stable' into rnmobile/issue_331/mention_support
SergioEstevao May 8, 2020
5492f4f
Merge branch 'rnmobile/issue_331/mention_support' into rnmobile/issue…
SergioEstevao May 8, 2020
d908f90
Remove DEV flag
SergioEstevao May 8, 2020
8a825c3
Only make mention keypress available when capabilities and editing me…
SergioEstevao May 9, 2020
884dc63
Merge branch 'rnmobile/release-1.28.0' into rnmobile/issue_331/mentio…
SergioEstevao May 11, 2020
e26a93b
Merge branch 'rnmobile/issue_331/mention_support' into rnmobile/issue…
SergioEstevao May 11, 2020
bc0770d
Merge branch 'master' into rnmobile/issue_331/mention_support_trigger…
SergioEstevao May 20, 2020
cd41576
Merge remote-tracking branch 'origin/master' into rnmobile/issue_331/…
mchowning Jun 4, 2020
29cf645
Enable space after mention on Android
mchowning Jun 5, 2020
297a9ba
Merge branch 'master' into rnmobile/issue_331/mention_support_trigger…
SergioEstevao Jun 10, 2020
6973e45
Remove DEV flag for toolbar mention button.
SergioEstevao Jun 12, 2020
30179ec
Merge branch 'master' into rnmobile/issue_331/mention_support_trigger…
SergioEstevao Jun 12, 2020
0c61b43
Merge branch 'rnmobile/master_2' into rnmobile/issue_331/mention_supp…
SergioEstevao Jun 19, 2020
4920c52
Merge branch 'master' into rnmobile/issue_331/mention_support_trigger…
SergioEstevao Jun 24, 2020
6b74a0d
Bring changes from gb-mobile to the monorepo structure.
SergioEstevao Jun 24, 2020
6c5e679
Check triggerKeyCodes on Android
mchowning Jun 25, 2020
31d0007
Add newline to end of file
mchowning Jun 25, 2020
75b608c
Merge branch 'master' into rnmobile/issue_331/mention_support_trigger…
SergioEstevao Jun 26, 2020
4217be8
Update code to use keycodes.
SergioEstevao Jun 29, 2020
036b7d9
Update GB main reference.
SergioEstevao Jun 29, 2020
a17dbc2
Merge branch 'master' into rnmobile/issue_331/mention_support_trigger…
SergioEstevao Jun 29, 2020
2348709
Update dependencies.
SergioEstevao Jun 29, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package org.wordpress.mobile.ReactNativeAztec

import com.facebook.react.bridge.Arguments
import com.facebook.react.bridge.WritableMap
import com.facebook.react.uimanager.events.Event
import com.facebook.react.uimanager.events.RCTEventEmitter

/**
* This event includes all data contained in [com.facebook.react.views.textinput.ReactTextChangedEvent],
* plus some extra info Gutenberg needs from Aztec.
*/
class AztecReactTextChangedEvent(
viewId: Int,
private val mText: String,
private val mEventCount: Int,
private val mMostRecentChar: Char?
) : Event<AztecReactTextChangedEvent>(viewId) {

override fun getEventName(): String = "topAztecChange"

override fun dispatch(rctEventEmitter: RCTEventEmitter) {
rctEventEmitter.receiveEvent(viewTag, eventName, serializeEventData())
}

private fun serializeEventData(): WritableMap =
Arguments.createMap().apply {
putString("text", mText)
putInt("eventCount", mEventCount)
putInt("target", viewTag)
if (mMostRecentChar != null) {
putInt("keyCode", mMostRecentChar.toInt())
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ public Map<String, Object> getExportedCustomBubblingEventTypeConstants() {
MapBuilder.of(
"bubbled", "onSubmitEditing", "captured", "onSubmitEditingCapture")))*/
.put(
"topChange",
"topAztecChange",
MapBuilder.of(
"phasedRegistrationNames",
MapBuilder.of("bubbled", "onChange")))
Expand Down Expand Up @@ -623,13 +623,15 @@ public void onTextChanged(CharSequence s, int start, int before, int count) {
// the text (minus the Enter char itself).
if (!mEditText.isEnterPressedUnderway()) {
int currentEventCount = mEditText.incrementAndGetEventCounter();
boolean singleCharacterHasBeenAdded = count - before == 1;
// The event that contains the event counter and updates it must be sent first.
// TODO: t7936714 merge these events
mEventDispatcher.dispatchEvent(
new ReactTextChangedEvent(
new AztecReactTextChangedEvent(
mEditText.getId(),
mEditText.toHtml(mEditText.getText(), false),
currentEventCount));
currentEventCount,
singleCharacterHasBeenAdded ? s.charAt(start + before) : null));

mEventDispatcher.dispatchEvent(
new ReactTextInputEvent(
Expand Down Expand Up @@ -657,8 +659,7 @@ public void onTextChanged(CharSequence s, int start, int before, int count) {
}

@Override
public void afterTextChanged(Editable s) {
}
public void afterTextChanged(Editable s) {}
}

private class AztecContentSizeWatcher implements com.facebook.react.views.textinput.ContentSizeWatcher {
Expand Down
56 changes: 49 additions & 7 deletions packages/react-native-aztec/ios/RNTAztecView/RCTAztecView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import UIKit
class RCTAztecView: Aztec.TextView {
@objc var onBackspace: RCTBubblingEventBlock? = nil
@objc var onChange: RCTBubblingEventBlock? = nil
@objc var onKeyDown: RCTBubblingEventBlock? = nil
@objc var onEnter: RCTBubblingEventBlock? = nil
@objc var onFocus: RCTBubblingEventBlock? = nil
@objc var onBlur: RCTBubblingEventBlock? = nil
Expand All @@ -14,6 +15,7 @@ class RCTAztecView: Aztec.TextView {
@objc var onSelectionChange: RCTBubblingEventBlock? = nil
@objc var minWidth: CGFloat = 0
@objc var maxWidth: CGFloat = 0
@objc var triggerKeyCodes: NSArray?

@objc var activeFormats: NSSet? = nil {
didSet {
Expand Down Expand Up @@ -304,7 +306,7 @@ class RCTAztecView: Aztec.TextView {
// MARK: - Edits

open override func insertText(_ text: String) {
guard !interceptEnter(text) else {
guard !interceptEnter(text), !interceptTriggersKeyCodes(text) else {
return
}

Expand Down Expand Up @@ -342,12 +344,13 @@ class RCTAztecView: Aztec.TextView {
}

guard text == "\n",
let onEnter = onEnter else {
let onKeyDown = onKeyDown else {
return false
}

let caretData = packCaretDataForRN()
onEnter(caretData)
var eventData = packCaretDataForRN()
eventData = add(keyTrigger: "\r", to: eventData)
onKeyDown(eventData)
return true
}

Expand All @@ -356,19 +359,45 @@ class RCTAztecView: Aztec.TextView {
|| (selectedRange.location == 0 && selectedRange.length == 0)
|| text.count == 1 // send backspace event when cleaning all characters
|| selectedRange == NSRange(location: 0, length: textStorage.length), // send backspace event when deleting all the text
let onBackspace = onBackspace else {
let onKeyDown = onKeyDown else {
return false
}
var range = selectedRange
if text.count == 1 {
range = NSRange(location: 0, length: textStorage.length)
}
let caretData = packCaretDataForRN(overrideRange: range)
var caretData = packCaretDataForRN(overrideRange: range)
onSelectionChange?(caretData)
onBackspace(caretData)
let backSpaceKeyCode:UInt8 = 8
caretData = add(keyCode: backSpaceKeyCode, to: caretData)
onKeyDown(caretData)
return true
}

private func interceptTriggersKeyCodes(_ text: String) -> Bool {
guard let keyCodes = triggerKeyCodes,
keyCodes.count > 0,
let onKeyDown = onKeyDown,
text.count == 1
else {
return false
}
for value in keyCodes {
guard let keyString = value as? String,
let keyCode = keyString.first?.asciiValue,
text.contains(keyString)
else {
continue
}

var eventData = [AnyHashable:Any]()
eventData = add(keyCode: keyCode, to: eventData)
onKeyDown(eventData)
return true
}
return false;
}

private func isNewLineBeforeSelectionAndNotEndOfContent() -> Bool {
guard let currentLocation = text.indexFromLocation(selectedRange.location) else {
return false
Expand Down Expand Up @@ -429,6 +458,19 @@ class RCTAztecView: Aztec.TextView {
return result
}

func add(keyTrigger: String, to pack:[AnyHashable: Any]) -> [AnyHashable: Any] {
guard let keyCode = keyTrigger.first?.asciiValue else {
return pack
}
return add(keyCode: keyCode, to: pack)
}

func add(keyCode: UInt8, to pack:[AnyHashable: Any]) -> [AnyHashable: Any] {
var result = pack
result["keyCode"] = keyCode
return result
}

// MARK: - RN Properties

@objc
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ @interface RCT_EXTERN_MODULE(RCTAztecViewManager, NSObject)
RCT_EXPORT_VIEW_PROPERTY(onBackspace, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onChange, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onEnter, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onKeyDown, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(triggerKeyCodes, NSArray)
RCT_EXPORT_VIEW_PROPERTY(onFocus, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onBlur, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onPaste, RCTBubblingEventBlock)
Expand Down
64 changes: 55 additions & 9 deletions packages/react-native-aztec/src/AztecView.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@ import ReactNative, {
import TextInputState from 'react-native/Libraries/Components/TextInput/TextInputState';

const AztecManager = UIManager.getViewManagerConfig( 'RCTAztecView' );

const BACKSPACE = 8;
class AztecView extends React.Component {
constructor() {
super( ...arguments );
this._onContentSizeChange = this._onContentSizeChange.bind( this );
this._onEnter = this._onEnter.bind( this );
this._onBackspace = this._onBackspace.bind( this );
this._onKeyDown = this._onKeyDown.bind( this );
this._onChange = this._onChange.bind( this );
this._onHTMLContentWithCursor = this._onHTMLContentWithCursor.bind(
this
);
Expand Down Expand Up @@ -57,21 +59,43 @@ class AztecView extends React.Component {
return;
}

if ( ! this.props.onEnter ) {
if ( ! this.props.onKeyDown ) {
return;
}

const { onEnter } = this.props;
onEnter( event );
const { onKeyDown } = this.props;

const newEvent = { ...event, keyCode: 13 };
SergioEstevao marked this conversation as resolved.
Show resolved Hide resolved
onKeyDown( newEvent );
}

_onBackspace( event ) {
if ( ! this.props.onBackspace ) {
if ( ! this.props.onKeyDown ) {
return;
}

const { onBackspace } = this.props;
onBackspace( event );
const { onKeyDown } = this.props;

const newEvent = {
...event,
keyCode: BACKSPACE,
preventDefault: () => {},
};
onKeyDown( newEvent );
}

_onKeyDown( event ) {
if ( ! this.props.onKeyDown ) {
return;
}

const { onKeyDown } = this.props;
const newEvent = {
...event,
keyCode: event.nativeEvent.keyCode,
preventDefault: () => {},
};
onKeyDown( newEvent );
}

_onHTMLContentWithCursor( event ) {
Expand Down Expand Up @@ -107,6 +131,26 @@ class AztecView extends React.Component {
onBlur( event );
}

_onChange( event ) {
// iOS uses the the onKeyDown prop directly from native only when one of the triggerKeyCodes is entered, but
// Android includes the information needed for onKeyDown in the event passed to onChange.
if ( Platform.OS === 'android' ) {
const triggersIncludeEventKeyCode =
this.props.triggerKeyCodes &&
this.props.triggerKeyCodes
.map( ( char ) => char.charCodeAt( 0 ) )
.includes( event.nativeEvent.keyCode );
if ( triggersIncludeEventKeyCode ) {
this._onKeyDown( event );
}
}

const { onChange } = this.props;
if ( onChange ) {
onChange( event );
}
}

_onSelectionChange( event ) {
if ( this.props.onSelectionChange ) {
const { selectionStart, selectionEnd, text } = event.nativeEvent;
Expand Down Expand Up @@ -182,15 +226,17 @@ class AztecView extends React.Component {
style={ style }
onContentSizeChange={ this._onContentSizeChange }
onHTMLContentWithCursor={ this._onHTMLContentWithCursor }
onChange={ this._onChange }
onSelectionChange={ this._onSelectionChange }
onEnter={ this.props.onEnter && this._onEnter }
onEnter={ this.props.onKeyDown && this._onEnter }
onBackspace={ this.props.onKeyDown && this._onBackspace }
onKeyDown={ this.props.onKeyDown && this._onKeyDown }
deleteEnter={ this.props.deleteEnter }
// IMPORTANT: the onFocus events are thrown away as these are handled by onPress() in the upper level.
// It's necessary to do this otherwise onFocus may be set by `{...otherProps}` and thus the onPress + onFocus
// combination generate an infinite loop as described in https://github.com/wordpress-mobile/gutenberg-mobile/issues/302
onFocus={ this._onAztecFocus }
onBlur={ this._onBlur }
onBackspace={ this._onBackspace }
/>
</TouchableWithoutFeedback>
);
Expand Down
Loading