Skip to content

Commit

Permalink
Implement bold, italic, and add/edit link shortcuts
Browse files Browse the repository at this point in the history
  • Loading branch information
kmdupr33 committed Jan 11, 2020
1 parent 6344967 commit 34a1ea4
Show file tree
Hide file tree
Showing 13 changed files with 196 additions and 28 deletions.
2 changes: 1 addition & 1 deletion .gitmodules
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[submodule "gutenberg"]
path = gutenberg
url = ../../WordPress/gutenberg.git
url = https://github.com/kmdupr33/gutenberg
[submodule "react-native-aztec"]
path = react-native-aztec-old-submodule
url = ../react-native-aztec.git
1 change: 1 addition & 0 deletions RELEASE-NOTES.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
1.20.0
------
* Fix bug where image placeholders would sometimes not be shown
* Add keyboard shortcuts for bold, italics, and insert/edit link

1.19.0
------
Expand Down
36 changes: 36 additions & 0 deletions android/app/src/main/java/com/gutenberg/MainActivity.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
package com.gutenberg;

import android.os.Bundle;
import android.view.KeyEvent;

import com.facebook.react.ReactActivity;
import com.facebook.react.ReactApplication;

import org.wordpress.mobile.ReactNativeGutenbergBridge.RNReactNativeGutenbergBridgeModule;
import org.wordpress.mobile.ReactNativeGutenbergBridge.RNReactNativeGutenbergBridgeModule.Shortcut;

public class MainActivity extends ReactActivity {

Expand All @@ -12,4 +19,33 @@ public class MainActivity extends ReactActivity {
protected String getMainComponentName() {
return "gutenberg";
}

private RNReactNativeGutenbergBridgeModule module;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
((ReactApplication) getApplication()).getReactNativeHost()
.getReactInstanceManager()
.addReactInstanceEventListener(context -> module = context.getNativeModule(RNReactNativeGutenbergBridgeModule.class));
}

@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
if (!event.isCtrlPressed()) {
return super.onKeyUp(keyCode, event);
}
switch (event.getKeyCode()) {
case KeyEvent.KEYCODE_B:
module.emitShortcutEventToJs(Shortcut.Bold);
break;
case KeyEvent.KEYCODE_I:
module.emitShortcutEventToJs(Shortcut.Italic);
break;
case KeyEvent.KEYCODE_K:
module.emitShortcutEventToJs(Shortcut.AddEditLink);
break;
}
return super.onKeyUp(keyCode, event);
}
}
2 changes: 1 addition & 1 deletion gutenberg
10 changes: 10 additions & 0 deletions ios/gutenberg.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
140ED2AC1D01E1AD002B40FF /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 146834041AC3E56700842450 /* libReact.a */; };
146834051AC3E58100842450 /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 146834041AC3E56700842450 /* libReact.a */; };
16AEBAC87CB24520ADA106D7 /* libRNSafeArea.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 69F3DF692A7C4E5994CD3CBB /* libRNSafeArea.a */; };
1EE61FB623C0E47F003727EC /* ShortcutCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1EE61FB523C0E47F003727EC /* ShortcutCoordinator.swift */; };
2D02E4BD1E0B4A84006451C7 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
2D02E4C21E0B4AEC006451C7 /* libRCTAnimation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5E9157351DD0AC6500FF2AA8 /* libRCTAnimation.a */; };
2D02E4C31E0B4AEC006451C7 /* libRCTImage-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3DAD3E841DF850E9000B6D8A /* libRCTImage-tvOS.a */; };
Expand Down Expand Up @@ -419,6 +420,7 @@
13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = gutenberg/Images.xcassets; sourceTree = "<group>"; };
13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = gutenberg/Info.plist; sourceTree = "<group>"; };
146833FF1AC3E56700842450 /* React.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = React.xcodeproj; path = "../node_modules/react-native/React/React.xcodeproj"; sourceTree = "<group>"; };
1EE61FB523C0E47F003727EC /* ShortcutCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = ShortcutCoordinator.swift; path = gutenberg/ShortcutCoordinator.swift; sourceTree = "<group>"; };
24AEC4495B174D4C9E96E630 /* RCTVideo.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = RCTVideo.xcodeproj; path = "../node_modules/react-native-video/ios/RCTVideo.xcodeproj"; sourceTree = "<group>"; };
2D02E47B1E0B4A5D006451C7 /* gutenberg-tvOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "gutenberg-tvOS.app"; sourceTree = BUILT_PRODUCTS_DIR; };
2D02E4901E0B4A5D006451C7 /* gutenberg-tvOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "gutenberg-tvOSTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
Expand Down Expand Up @@ -600,6 +602,7 @@
7EC7328E21907E3F00FED2E6 /* GutenbergViewController.swift */,
FF9A6F1621FA8E2500D36D14 /* MediaPickCoordinator.swift */,
FF6836C722035EAB00A0C562 /* MediaUploadCoordinator.swift */,
1EE61FB523C0E47F003727EC /* ShortcutCoordinator.swift */,
F151983A2100DC3D000F6E97 /* MediaProvider.swift */,
13B07FB51A68108700A75B9A /* Images.xcassets */,
13B07FB61A68108700A75B9A /* Info.plist */,
Expand Down Expand Up @@ -921,6 +924,7 @@
TargetAttributes = {
00E356ED1AD99517003FC87E = {
CreatedOnToolsVersion = 6.2;
LastSwiftMigration = 1130;
TestTargetID = 13B07F861A680F5B00A75B9A;
};
13B07F861A680F5B00A75B9A = {
Expand Down Expand Up @@ -1434,6 +1438,7 @@
F151983C2100DC3D000F6E97 /* AppDelegate.swift in Sources */,
FF83DAA92226905A00A34C93 /* CustomImageLoader.swift in Sources */,
FF9A6F4121FA8E2500D36D14 /* MediaPickCoordinator.swift in Sources */,
1EE61FB623C0E47F003727EC /* ShortcutCoordinator.swift in Sources */,
F151983D2100DC3D000F6E97 /* MediaProvider.swift in Sources */,
FF6836C822035EAB00A0C562 /* MediaUploadCoordinator.swift in Sources */,
7EC7328F21907E3F00FED2E6 /* GutenbergViewController.swift in Sources */,
Expand Down Expand Up @@ -1493,6 +1498,7 @@
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
BUNDLE_LOADER = "$(TEST_HOST)";
CLANG_ENABLE_MODULES = YES;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
Expand Down Expand Up @@ -1522,6 +1528,8 @@
"-lc++",
);
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/gutenberg.app/gutenberg";
};
name = Debug;
Expand All @@ -1531,6 +1539,7 @@
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
BUNDLE_LOADER = "$(TEST_HOST)";
CLANG_ENABLE_MODULES = YES;
COPY_PHASE_STRIP = NO;
HEADER_SEARCH_PATHS = (
"$(inherited)",
Expand All @@ -1557,6 +1566,7 @@
"-lc++",
);
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/gutenberg.app/gutenberg";
};
name = Release;
Expand Down
25 changes: 14 additions & 11 deletions ios/gutenberg.xcodeproj/xcshareddata/xcschemes/gutenberg.xcscheme
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,15 @@
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
BuildableName = "gutenberg.app"
BlueprintName = "gutenberg"
ReferencedContainer = "container:gutenberg.xcodeproj">
</BuildableReference>
</MacroExpansion>
<Testables>
<TestableReference
skipped = "NO">
Expand All @@ -67,17 +76,6 @@
</BuildableReference>
</TestableReference>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
BuildableName = "gutenberg.app"
BlueprintName = "gutenberg"
ReferencedContainer = "container:gutenberg.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
Expand All @@ -100,6 +98,11 @@
</BuildableReference>
</BuildableProductRunnable>
<AdditionalOptions>
<AdditionalOption
key = "NSZombieEnabled"
value = "YES"
isEnabled = "YES">
</AdditionalOption>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
Expand Down
10 changes: 10 additions & 0 deletions ios/gutenberg/GutenbergViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ class GutenbergViewController: UIViewController {
return mediaUploadCoordinator
}()
fileprivate var longPressGesture: UILongPressGestureRecognizer!
fileprivate lazy var shortcutCoordinator: ShortcutCoordinator = {
let shortcutCoordinator = ShortcutCoordinator(gutenberg: gutenberg)
return shortcutCoordinator
}()

override func loadView() {
view = gutenberg.rootView
Expand Down Expand Up @@ -42,6 +46,12 @@ class GutenbergViewController: UIViewController {
@objc func handleLongPress() {
NotificationCenter.default.post(Notification(name: MediaUploadCoordinator.failUpload ))
}

override var next: UIResponder? {
let originalNext = super.next
shortcutCoordinator.responder = originalNext
return shortcutCoordinator
}
}

extension GutenbergViewController: GutenbergBridgeDelegate {
Expand Down
47 changes: 47 additions & 0 deletions ios/gutenberg/ShortcutCoordinator.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import UIKit
import RNReactNativeGutenbergBridge

class ShortcutCoordinator: UIResponder {
let gutenberg: Gutenberg
var responder: UIResponder?
init(gutenberg: Gutenberg) {
self.gutenberg = gutenberg
}

@objc func toggleBold() {
gutenberg.sendShortcut(.bold)
}

@objc func toggleItalic() {
gutenberg.sendShortcut(.italic)
}

@objc func addEditLink() {
gutenberg.sendShortcut(.addEditLink)
}

override var next: UIResponder? {
return responder
}

override var keyCommands: [UIKeyCommand]? {
return [
UIKeyCommand(input:"B",
modifierFlags: .command,
action:#selector(toggleBold),
discoverabilityTitle:NSLocalizedString("Bold", comment: "Discoverability title for bold formatting keyboard shortcut.")
),
UIKeyCommand(input:"I",
modifierFlags: .command,
action:#selector(toggleItalic),
discoverabilityTitle:NSLocalizedString("Italic", comment: "Discoverability title for italic formatting keyboard shortcut.")
),
UIKeyCommand(input: "K",
modifierFlags: .command,
action:#selector(addEditLink),
discoverabilityTitle:NSLocalizedString("Add/Edit Link", comment: "Discoverability title for add/edit link keyboard shortcut")
)
]
}
}

Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
package org.wordpress.mobile.ReactNativeGutenbergBridge;

import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.WritableMap;

import org.wordpress.mobile.WPAndroidGlue.MediaOption;
import org.wordpress.mobile.WPAndroidGlue.RequestExecutor;

import java.util.ArrayList;
import java.util.List;
import org.wordpress.mobile.WPAndroidGlue.MediaOption;
import org.wordpress.mobile.WPAndroidGlue.RequestExecutor;

public interface GutenbergBridgeJS2Parent extends RequestExecutor {
interface RNMedia {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.bridge.WritableNativeArray;
import com.facebook.react.bridge.WritableNativeMap;
import com.facebook.react.module.annotations.ReactModule;
import com.facebook.react.modules.core.DeviceEventManagerModule;

import org.wordpress.mobile.ReactNativeGutenbergBridge.GutenbergBridgeJS2Parent.MediaType;
Expand All @@ -22,6 +23,7 @@
import java.util.ArrayList;
import java.util.List;

@ReactModule(name = "RNReactNativeGutenbergBridge")
public class RNReactNativeGutenbergBridgeModule extends ReactContextBaseJavaModule {
private final ReactApplicationContext mReactContext;
private final GutenbergBridgeJS2Parent mGutenbergBridgeJS2Parent;
Expand All @@ -32,6 +34,9 @@ public class RNReactNativeGutenbergBridgeModule extends ReactContextBaseJavaModu
private static final String EVENT_NAME_FOCUS_TITLE = "setFocusOnTitle";
private static final String EVENT_NAME_MEDIA_UPLOAD = "mediaUpload";
private static final String EVENT_NAME_MEDIA_APPEND = "mediaAppend";
private static final String EVENT_NAME_TOGGLE_BOLD = "toggleBold";
private static final String EVENT_NAME_TOGGLE_ITALIC = "toggleItalic";
private static final String EVENT_NAME_ADD_EDIT_LINK = "addEditLink";

private static final String MAP_KEY_UPDATE_HTML = "html";
private static final String MAP_KEY_UPDATE_TITLE = "title";
Expand All @@ -55,7 +60,7 @@ public class RNReactNativeGutenbergBridgeModule extends ReactContextBaseJavaModu


public RNReactNativeGutenbergBridgeModule(ReactApplicationContext reactContext,
GutenbergBridgeJS2Parent gutenbergBridgeJS2Parent) {
GutenbergBridgeJS2Parent gutenbergBridgeJS2Parent) {
super(reactContext);
mReactContext = reactContext;
mGutenbergBridgeJS2Parent = gutenbergBridgeJS2Parent;
Expand All @@ -70,8 +75,36 @@ private void emitToJS(String eventName, @Nullable WritableMap data) {
mReactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit(eventName, data);
}

private void emitToJS(String eventName) {
emitToJS(eventName, null);
}

public void getHtmlFromJS() {
emitToJS(EVENT_NAME_REQUEST_GET_HTML, null);
emitToJS(EVENT_NAME_REQUEST_GET_HTML);
}

public enum Shortcut {
Bold,
Italic,
AddEditLink
}

public void emitShortcutEventToJs(Shortcut shortcut) {
String eventName;
switch (shortcut) {
case Bold:
eventName = EVENT_NAME_TOGGLE_BOLD;
break;
case Italic:
eventName = EVENT_NAME_TOGGLE_ITALIC;
break;
case AddEditLink:
eventName = EVENT_NAME_ADD_EDIT_LINK;
break;
default:
return;
}
emitToJS(eventName);
}

public void setHtmlInJS(String html) {
Expand Down Expand Up @@ -132,7 +165,7 @@ private MediaType getMediaTypeFromFilter(ReadableArray filter) {
MediaType filter1 = MediaType.getEnum(filter.getString(1));

if ((filter0.equals(MediaType.VIDEO) && filter1.equals(MediaType.IMAGE))
|| (filter0.equals(MediaType.IMAGE) && filter1.equals(MediaType.VIDEO))) {
|| (filter0.equals(MediaType.IMAGE) && filter1.equals(MediaType.VIDEO))) {
return MediaType.MEDIA;
}
default:
Expand All @@ -147,7 +180,7 @@ public void requestMediaImport(String url, final Callback onUploadMediaSelected)

@ReactMethod
public void mediaUploadSync() {
mGutenbergBridgeJS2Parent.mediaUploadSync(getNewUploadMediaCallback(false,null));
mGutenbergBridgeJS2Parent.mediaUploadSync(getNewUploadMediaCallback(false, null));
}

@ReactMethod
Expand Down Expand Up @@ -196,7 +229,8 @@ public void fetchRequest(String path, Promise promise) {

private OtherMediaOptionsReceivedCallback getNewOtherMediaReceivedCallback(final Callback jsCallback) {
return new OtherMediaOptionsReceivedCallback() {
@Override public void onOtherMediaOptionsReceived(ArrayList<MediaOption> mediaOptions) {
@Override
public void onOtherMediaOptionsReceived(ArrayList<MediaOption> mediaOptions) {
WritableArray writableArray = new WritableNativeArray();
for (MediaOption mediaOption : mediaOptions) {
writableArray.pushMap(mediaOption.toMap());
Expand Down Expand Up @@ -225,7 +259,8 @@ public void onUploadMediaFileSelected(List<RNMedia> mediaList) {
}
}

@Override public void onUploadMediaFileClear(int mediaId) {
@Override
public void onUploadMediaFileClear(int mediaId) {
setMediaFileUploadDataInJS(MEDIA_UPLOAD_STATE_RESET, mediaId, null, 0);
}

Expand Down
Loading

0 comments on commit 34a1ea4

Please sign in to comment.