Selaa lähdekoodia

Command (#3175)

* Assign commandId to async commands

* CommandListener refactor

Pull listener out of Navigator and implement NativeCommandListener for native commands

* Emit onNavigationEvent when commands complete

* Rename timestamp to completionTime

* Rename NavigationEvent to EventEmitter

* Rename sendOnNavigationButtonPressed to emitOnNavigationButtonPressed

* Emit navigationEvent on bottom tab select

* rebase fix

* Emit commandCompleted event instead of nativeEvent

* fix native ios commands

* wait show and dismiss Overlay
Guy Carmeli 6 vuotta sitten
vanhempi
commit
07050db9f9
No account linked to committer's email address
29 muutettua tiedostoa jossa 396 lisäystä ja 239 poistoa
  1. 5
    2
      lib/android/app/src/main/java/com/reactnativenavigation/parse/LayoutFactory.java
  2. 5
    2
      lib/android/app/src/main/java/com/reactnativenavigation/presentation/BottomTabsOptionsPresenter.java
  3. 11
    3
      lib/android/app/src/main/java/com/reactnativenavigation/presentation/OverlayManager.java
  4. 31
    18
      lib/android/app/src/main/java/com/reactnativenavigation/react/EventEmitter.java
  5. 34
    45
      lib/android/app/src/main/java/com/reactnativenavigation/react/NavigationModule.java
  6. 1
    1
      lib/android/app/src/main/java/com/reactnativenavigation/react/NavigationReactInitializer.java
  7. 3
    3
      lib/android/app/src/main/java/com/reactnativenavigation/react/ReactView.java
  8. 7
    0
      lib/android/app/src/main/java/com/reactnativenavigation/utils/CommandListener.java
  9. 1
    4
      lib/android/app/src/main/java/com/reactnativenavigation/utils/CommandListenerAdapter.java
  10. 31
    0
      lib/android/app/src/main/java/com/reactnativenavigation/utils/NativeCommandListener.java
  11. 7
    0
      lib/android/app/src/main/java/com/reactnativenavigation/utils/Now.java
  12. 9
    14
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/Navigator.java
  13. 1
    1
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/StackController.java
  14. 2
    1
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/ViewController.java
  15. 19
    11
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/bottomtabs/BottomTabsController.java
  16. 5
    0
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/bottomtabs/TabSelector.java
  17. 1
    1
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/modal/ModalPresenter.java
  18. 1
    1
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/modal/ModalStack.java
  19. 58
    0
      lib/android/app/src/test/java/com/reactnativenavigation/utils/NativeCommandListenerTest.java
  20. 24
    6
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/BottomTabsControllerTest.java
  21. 36
    36
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/NavigatorTest.java
  22. 1
    1
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/modal/ModalPresenterTest.java
  23. 1
    1
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/modal/ModalStackTest.java
  24. 11
    11
      lib/ios/RNNBridgeModule.m
  25. 1
    1
      lib/src/Navigation.ts
  26. 22
    22
      lib/src/adapters/NativeCommandsSender.ts
  27. 30
    29
      lib/src/commands/Commands.test.ts
  28. 36
    23
      lib/src/commands/Commands.ts
  29. 2
    2
      playground/src/screens/OptionsScreen.js

+ 5
- 2
lib/android/app/src/main/java/com/reactnativenavigation/parse/LayoutFactory.java Näytä tiedosto

@@ -3,6 +3,7 @@ package com.reactnativenavigation.parse;
3 3
 import android.app.Activity;
4 4
 
5 5
 import com.facebook.react.ReactInstanceManager;
6
+import com.reactnativenavigation.react.EventEmitter;
6 7
 import com.reactnativenavigation.utils.CommandListenerAdapter;
7 8
 import com.reactnativenavigation.utils.ImageLoader;
8 9
 import com.reactnativenavigation.utils.TypefaceLoader;
@@ -31,13 +32,15 @@ public class LayoutFactory {
31 32
 
32 33
 	private final Activity activity;
33 34
 	private final ReactInstanceManager reactInstanceManager;
35
+    private EventEmitter eventEmitter;
34 36
     private Map<String, ExternalComponentCreator> externalComponentCreators;
35 37
     private Options defaultOptions;
36 38
     private final TypefaceLoader typefaceManager;
37 39
 
38
-    public LayoutFactory(Activity activity, final ReactInstanceManager reactInstanceManager, Map<String, ExternalComponentCreator> externalComponentCreators, Options defaultOptions) {
40
+    public LayoutFactory(Activity activity, final ReactInstanceManager reactInstanceManager, EventEmitter eventEmitter, Map<String, ExternalComponentCreator> externalComponentCreators, Options defaultOptions) {
39 41
 		this.activity = activity;
40 42
 		this.reactInstanceManager = reactInstanceManager;
43
+        this.eventEmitter = eventEmitter;
41 44
         this.externalComponentCreators = externalComponentCreators;
42 45
         this.defaultOptions = defaultOptions;
43 46
         typefaceManager = new TypefaceLoader(activity);
@@ -143,7 +146,7 @@ public class LayoutFactory {
143 146
     }
144 147
 
145 148
     private ViewController createBottomTabs(LayoutNode node) {
146
-        final BottomTabsController tabsComponent = new BottomTabsController(activity, new ImageLoader(), node.id, getOptions(node));
149
+        final BottomTabsController tabsComponent = new BottomTabsController(activity, eventEmitter, new ImageLoader(), node.id, getOptions(node));
147 150
 		List<ViewController> tabs = new ArrayList<>();
148 151
 		for (int i = 0; i < node.children.size(); i++) {
149 152
             tabs.add(create(node.children.get(i)));

+ 5
- 2
lib/android/app/src/main/java/com/reactnativenavigation/presentation/BottomTabsOptionsPresenter.java Näytä tiedosto

@@ -6,15 +6,18 @@ import com.reactnativenavigation.parse.BottomTabOptions;
6 6
 import com.reactnativenavigation.parse.BottomTabsOptions;
7 7
 import com.reactnativenavigation.parse.Options;
8 8
 import com.reactnativenavigation.viewcontrollers.bottomtabs.BottomTabFinder;
9
+import com.reactnativenavigation.viewcontrollers.bottomtabs.TabSelector;
9 10
 import com.reactnativenavigation.views.BottomTabs;
10 11
 
11 12
 public class BottomTabsOptionsPresenter {
12 13
     private BottomTabs bottomTabs;
14
+    private TabSelector tabSelector;
13 15
     private BottomTabFinder bottomTabFinder;
14 16
     private BottomTabsAnimator animator;
15 17
 
16
-    public BottomTabsOptionsPresenter(BottomTabs bottomTabs, BottomTabFinder bottomTabFinder) {
18
+    public BottomTabsOptionsPresenter(BottomTabs bottomTabs, TabSelector tabSelector, BottomTabFinder bottomTabFinder) {
17 19
         this.bottomTabs = bottomTabs;
20
+        this.tabSelector = tabSelector;
18 21
         this.bottomTabFinder = bottomTabFinder;
19 22
         animator = new BottomTabsAnimator(bottomTabs);
20 23
     }
@@ -39,7 +42,7 @@ public class BottomTabsOptionsPresenter {
39 42
         }
40 43
         if (options.currentTabIndex.hasValue()) {
41 44
             int tabIndex = options.currentTabIndex.get();
42
-            if (tabIndex >= 0) bottomTabs.setCurrentItem(tabIndex);
45
+            if (tabIndex >= 0) tabSelector.selectTab(tabIndex);
43 46
         }
44 47
         if (options.testId.hasValue()) {
45 48
             bottomTabs.setTag(options.testId.get());

+ 11
- 3
lib/android/app/src/main/java/com/reactnativenavigation/presentation/OverlayManager.java Näytä tiedosto

@@ -2,6 +2,7 @@ package com.reactnativenavigation.presentation;
2 2
 
3 3
 import android.view.ViewGroup;
4 4
 
5
+import com.reactnativenavigation.utils.CommandListener;
5 6
 import com.reactnativenavigation.viewcontrollers.ViewController;
6 7
 
7 8
 import java.util.HashMap;
@@ -9,13 +10,20 @@ import java.util.HashMap;
9 10
 public class OverlayManager {
10 11
     private final HashMap<String, ViewController> overlayRegistry = new HashMap<>();
11 12
 
12
-    public void show(ViewGroup root, ViewController overlay) {
13
+    public void show(ViewGroup root, ViewController overlay, CommandListener listener) {
13 14
         overlayRegistry.put(overlay.getId(), overlay);
14 15
         root.addView(overlay.getView());
16
+        listener.onSuccess(overlay.getId());
15 17
     }
16 18
 
17
-    public void dismiss(ViewGroup root, String componentId) {
18
-        root.removeView(overlayRegistry.get(componentId).getView());
19
+    public void dismiss(ViewGroup root, String componentId, CommandListener listener) {
20
+        ViewGroup overlay = overlayRegistry.get(componentId).getView();
21
+        if (overlay.getParent() == root) {
22
+            root.removeView(overlay);
23
+            listener.onSuccess(componentId);
24
+        } else {
25
+            listener.onError("Overlay is not attached");
26
+        }
19 27
     }
20 28
 
21 29
     public void destroy() {

lib/android/app/src/main/java/com/reactnativenavigation/react/NavigationEvent.java → lib/android/app/src/main/java/com/reactnativenavigation/react/EventEmitter.java Näytä tiedosto

@@ -7,16 +7,17 @@ import com.facebook.react.modules.core.DeviceEventManagerModule;
7 7
 
8 8
 import static com.facebook.react.modules.core.DeviceEventManagerModule.RCTDeviceEventEmitter;
9 9
 
10
-public class NavigationEvent {
10
+public class EventEmitter {
11 11
 	private static final String onAppLaunched = "RNN.appLaunched";
12 12
 	private static final String componentDidAppear = "RNN.componentDidAppear";
13 13
 	private static final String componentDidDisappear = "RNN.componentDidDisappear";
14 14
 	private static final String nativeEvent = "RNN.nativeEvent";
15
+    private static final String commandCompleted = "RNN.commandCompleted";
15 16
     private static final String buttonPressedEvent = "buttonPressed";
16 17
 
17
-	private final RCTDeviceEventEmitter emitter;
18
+    private final RCTDeviceEventEmitter emitter;
18 19
 
19
-	public NavigationEvent(ReactContext reactContext) {
20
+    EventEmitter(ReactContext reactContext) {
20 21
 		this.emitter = reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class);
21 22
 	}
22 23
 
@@ -25,32 +26,44 @@ public class NavigationEvent {
25 26
 	}
26 27
 
27 28
 	public void componentDidDisappear(String id, String componentName) {
28
-		WritableMap map = Arguments.createMap();
29
-		map.putString("componentId", id);
30
-		map.putString("componentName", componentName);
31
-
32
-		emit(componentDidDisappear, map);
29
+		WritableMap data = Arguments.createMap();
30
+		data.putString("componentId", id);
31
+		data.putString("componentName", componentName);
32
+		emit(componentDidDisappear, data);
33 33
 	}
34 34
 
35 35
 	public void componentDidAppear(String id, String componentName) {
36
-		WritableMap map = Arguments.createMap();
37
-		map.putString("componentId", id);
38
-		map.putString("componentName", componentName);
39
-		emit(componentDidAppear, map);
36
+		WritableMap data = Arguments.createMap();
37
+		data.putString("componentId", id);
38
+		data.putString("componentName", componentName);
39
+		emit(componentDidAppear, data);
40 40
 	}
41 41
 
42
-    public void sendOnNavigationButtonPressed(String id, String buttonId) {
42
+    public void emitOnNavigationButtonPressed(String id, String buttonId) {
43 43
 		WritableMap params = Arguments.createMap();
44 44
 		params.putString("componentId", id);
45 45
 		params.putString("buttonId", buttonId);
46 46
 
47
-        WritableMap map = Arguments.createMap();
48
-        map.putString("name", buttonPressedEvent);
49
-        map.putMap("params", params);
50
-
51
-		emit(nativeEvent, map);
47
+        WritableMap data = Arguments.createMap();
48
+        data.putString("name", buttonPressedEvent);
49
+        data.putMap("params", params);
50
+		emit(nativeEvent, data);
52 51
 	}
53 52
 
53
+    public void emitBottomTabSelected(int unselectedTabIndex, int selectedTabIndex) {
54
+        WritableMap data = Arguments.createMap();
55
+        data.putInt("unselectedTabIndex", unselectedTabIndex);
56
+        data.putInt("selectedTabIndex", selectedTabIndex);
57
+        emit(nativeEvent, data);
58
+    }
59
+
60
+    public void emitCommandCompletedEvent(String commandId, long completionTime) {
61
+        WritableMap map = Arguments.createMap();
62
+        map.putString("commandId", commandId);
63
+        map.putDouble("completionTime", completionTime);
64
+        emit(commandCompleted, map);
65
+    }
66
+
54 67
 	private void emit(String eventName) {
55 68
 		emit(eventName, Arguments.createMap());
56 69
 	}

+ 34
- 45
lib/android/app/src/main/java/com/reactnativenavigation/react/NavigationModule.java Näytä tiedosto

@@ -14,6 +14,8 @@ import com.reactnativenavigation.parse.LayoutFactory;
14 14
 import com.reactnativenavigation.parse.LayoutNode;
15 15
 import com.reactnativenavigation.parse.Options;
16 16
 import com.reactnativenavigation.parse.parsers.LayoutNodeParser;
17
+import com.reactnativenavigation.utils.NativeCommandListener;
18
+import com.reactnativenavigation.utils.Now;
17 19
 import com.reactnativenavigation.utils.TypefaceLoader;
18 20
 import com.reactnativenavigation.utils.UiThread;
19 21
 import com.reactnativenavigation.viewcontrollers.Navigator;
@@ -26,13 +28,17 @@ import java.util.Map;
26 28
 
27 29
 public class NavigationModule extends ReactContextBaseJavaModule {
28 30
 	private static final String NAME = "RNNBridgeModule";
31
+
32
+    private final Now now = new Now();
29 33
 	private final ReactInstanceManager reactInstanceManager;
34
+    private EventEmitter eventEmitter;
30 35
 
31
-	@SuppressWarnings("WeakerAccess")
32
-    public NavigationModule(final ReactApplicationContext reactContext, final ReactInstanceManager reactInstanceManager) {
36
+    @SuppressWarnings("WeakerAccess")
37
+    public NavigationModule(ReactApplicationContext reactContext, ReactInstanceManager reactInstanceManager) {
33 38
 		super(reactContext);
34 39
 		this.reactInstanceManager = reactInstanceManager;
35
-	}
40
+		reactInstanceManager.addReactInstanceEventListener(context -> eventEmitter = new EventEmitter(context));
41
+    }
36 42
 
37 43
 	@Override
38 44
 	public String getName() {
@@ -40,90 +46,90 @@ public class NavigationModule extends ReactContextBaseJavaModule {
40 46
 	}
41 47
 
42 48
 	@ReactMethod
43
-	public void setRoot(final ReadableMap rawLayoutTree, final Promise promise) {
49
+	public void setRoot(String commandId, ReadableMap rawLayoutTree, Promise promise) {
44 50
 		final LayoutNode layoutTree = LayoutNodeParser.parse(new JSONObject(rawLayoutTree.toHashMap()));
45 51
 		handle(() -> {
46 52
             final ViewController viewController = newLayoutFactory().create(layoutTree);
47
-            navigator().setRoot(viewController, promise);
53
+            navigator().setRoot(viewController, new NativeCommandListener(commandId, promise, eventEmitter, now));
48 54
         });
49 55
 	}
50 56
 
51 57
 	@ReactMethod
52
-	public void setDefaultOptions(final ReadableMap options) {
58
+	public void setDefaultOptions(ReadableMap options) {
53 59
         final Options defaultOptions = Options.parse(new TypefaceLoader(activity()), new JSONObject(options.toHashMap()));
54 60
         handle(() -> navigator().setDefaultOptions(defaultOptions));
55 61
     }
56 62
 
57 63
 	@ReactMethod
58
-	public void mergeOptions(final String onComponentId, final ReadableMap options) {
64
+	public void mergeOptions(String onComponentId, ReadableMap options) {
59 65
 		final Options navOptions = Options.parse(new TypefaceLoader(activity()), new JSONObject(options.toHashMap()));
60 66
 		handle(() -> navigator().mergeOptions(onComponentId, navOptions));
61 67
 	}
62 68
 
63 69
 	@ReactMethod
64
-	public void push(final String onComponentId, final ReadableMap rawLayoutTree, final Promise promise) {
70
+	public void push(String commandId, String onComponentId, ReadableMap rawLayoutTree, Promise promise) {
65 71
 		final LayoutNode layoutTree = LayoutNodeParser.parse(new JSONObject(rawLayoutTree.toHashMap()));
66 72
 		handle(() -> {
67 73
             final ViewController viewController = newLayoutFactory().create(layoutTree);
68
-            navigator().push(onComponentId, viewController, new CommandListenerAdapter(promise));
74
+            navigator().push(onComponentId, viewController, new NativeCommandListener(commandId, promise, eventEmitter, now));
69 75
         });
70 76
 	}
71 77
 
72 78
     @ReactMethod
73
-    public void setStackRoot(final String onComponentId, final ReadableMap rawLayoutTree, final Promise promise) {
79
+    public void setStackRoot(String commandId, String onComponentId, ReadableMap rawLayoutTree, Promise promise) {
74 80
         final LayoutNode layoutTree = LayoutNodeParser.parse(new JSONObject(rawLayoutTree.toHashMap()));
75 81
         handle(() -> {
76 82
             final ViewController viewController = newLayoutFactory().create(layoutTree);
77
-            navigator().setStackRoot(onComponentId, viewController, new CommandListenerAdapter(promise));
83
+            navigator().setStackRoot(onComponentId, viewController, new NativeCommandListener(commandId, promise, eventEmitter, now));
78 84
         });
79 85
     }
80 86
 
81 87
 	@ReactMethod
82
-	public void pop(final String onComponentId, final ReadableMap options, final Promise promise) {
83
-		handle(() -> navigator().popSpecific(onComponentId, new CommandListenerAdapter(promise)));
88
+	public void pop(String commandId, String onComponentId, ReadableMap options, Promise promise) {
89
+		handle(() -> navigator().popSpecific(onComponentId, new NativeCommandListener(commandId, promise, eventEmitter, now)));
84 90
 	}
85 91
 
86 92
 	@ReactMethod
87
-	public void popTo(final String componentId, final Promise promise) {
88
-		handle(() -> navigator().popTo(componentId, new CommandListenerAdapter(promise)));
93
+	public void popTo(String commandId, String componentId, Promise promise) {
94
+		handle(() -> navigator().popTo(componentId, new NativeCommandListener(commandId, promise, eventEmitter, now)));
89 95
 	}
90 96
 
91 97
 	@ReactMethod
92
-	public void popToRoot(final String componentId, final Promise promise) {
93
-		handle(() -> navigator().popToRoot(componentId, new CommandListenerAdapter(promise)));
98
+	public void popToRoot(String commandId, String componentId, Promise promise) {
99
+		handle(() -> navigator().popToRoot(componentId, new NativeCommandListener(commandId, promise, eventEmitter, now)));
94 100
 	}
95 101
 
96 102
 	@ReactMethod
97
-	public void showModal(final ReadableMap rawLayoutTree, final Promise promise) {
103
+	public void showModal(String commandId, ReadableMap rawLayoutTree, Promise promise) {
98 104
 		final LayoutNode layoutTree = LayoutNodeParser.parse(new JSONObject(rawLayoutTree.toHashMap()));
99 105
 		handle(() -> {
100 106
             final ViewController viewController = newLayoutFactory().create(layoutTree);
101
-            navigator().showModal(viewController, new CommandListenerAdapter(promise));
107
+            navigator().showModal(viewController, new NativeCommandListener(commandId, promise, eventEmitter, now));
102 108
         });
103 109
 	}
104 110
 
105 111
 	@ReactMethod
106
-	public void dismissModal(final String componentId, final Promise promise) {
107
-		handle(() -> navigator().dismissModal(componentId, new CommandListenerAdapter(promise)));
112
+	public void dismissModal(String commandId, String componentId, Promise promise) {
113
+		handle(() -> navigator().dismissModal(componentId, new NativeCommandListener(commandId, promise, eventEmitter, now)));
108 114
 	}
109 115
 
110 116
 	@ReactMethod
111
-	public void dismissAllModals(final Promise promise) {
112
-		handle(() -> navigator().dismissAllModals(new CommandListenerAdapter(promise)));
117
+	public void dismissAllModals(String commandId, Promise promise) {
118
+		handle(() -> navigator().dismissAllModals(new NativeCommandListener(commandId, promise, eventEmitter, now)));
113 119
 	}
114 120
 
115 121
 	@ReactMethod
116
-	public void showOverlay(final ReadableMap rawLayoutTree) {
122
+	public void showOverlay(String commandId, ReadableMap rawLayoutTree, Promise promise) {
117 123
         final LayoutNode layoutTree = LayoutNodeParser.parse(new JSONObject(rawLayoutTree.toHashMap()));
118 124
         handle(() -> {
119 125
             final ViewController viewController = newLayoutFactory().create(layoutTree);
120
-            navigator().showOverlay(viewController);
126
+            navigator().showOverlay(viewController, new NativeCommandListener(commandId, promise, eventEmitter, now));
121 127
         });
122 128
 	}
123 129
 
124 130
 	@ReactMethod
125
-	public void dismissOverlay(final String componentId) {
126
-		handle(() -> navigator().dismissOverlay(componentId));
131
+	public void dismissOverlay(String commandId, String componentId, Promise promise) {
132
+		handle(() -> navigator().dismissOverlay(componentId, new NativeCommandListener(commandId, promise, eventEmitter, now)));
127 133
 	}
128 134
 
129 135
 	private Navigator navigator() {
@@ -134,6 +140,7 @@ public class NavigationModule extends ReactContextBaseJavaModule {
134 140
 	private LayoutFactory newLayoutFactory() {
135 141
 		return new LayoutFactory(activity(),
136 142
                 reactInstanceManager,
143
+                eventEmitter,
137 144
                 externalComponentCreator(),
138 145
                 navigator().getDefaultOptions()
139 146
         );
@@ -151,22 +158,4 @@ public class NavigationModule extends ReactContextBaseJavaModule {
151 158
     private NavigationActivity activity() {
152 159
         return (NavigationActivity) getCurrentActivity();
153 160
     }
154
-
155
-    private class CommandListenerAdapter implements Navigator.CommandListener {
156
-        private Promise promise;
157
-
158
-        CommandListenerAdapter(Promise promise) {
159
-            this.promise = promise;
160
-        }
161
-
162
-        @Override
163
-        public void onSuccess(String childId) {
164
-            promise.resolve(childId);
165
-        }
166
-
167
-        @Override
168
-        public void onError(String message) {
169
-            promise.reject(new Throwable(message));
170
-        }
171
-    }
172 161
 }

+ 1
- 1
lib/android/app/src/main/java/com/reactnativenavigation/react/NavigationReactInitializer.java Näytä tiedosto

@@ -52,7 +52,7 @@ public class NavigationReactInitializer implements ReactInstanceManager.ReactIns
52 52
 
53 53
 	private void emitAppLaunched(ReactContext context) {
54 54
 		waitingForAppLaunchEvent = false;
55
-		new NavigationEvent(context).appLaunched();
55
+		new EventEmitter(context).appLaunched();
56 56
 	}
57 57
 
58 58
 	private boolean shouldCreateContext() {

+ 3
- 3
lib/android/app/src/main/java/com/reactnativenavigation/react/ReactView.java Näytä tiedosto

@@ -60,17 +60,17 @@ public class ReactView extends ReactRootView implements IReactView {
60 60
 
61 61
 	@Override
62 62
 	public void sendComponentStart() {
63
-		new NavigationEvent(reactInstanceManager.getCurrentReactContext()).componentDidAppear(componentId, componentName);
63
+		new EventEmitter(reactInstanceManager.getCurrentReactContext()).componentDidAppear(componentId, componentName);
64 64
 	}
65 65
 
66 66
 	@Override
67 67
 	public void sendComponentStop() {
68
-		new NavigationEvent(reactInstanceManager.getCurrentReactContext()).componentDidDisappear(componentId, componentName);
68
+		new EventEmitter(reactInstanceManager.getCurrentReactContext()).componentDidDisappear(componentId, componentName);
69 69
 	}
70 70
 
71 71
     @Override
72 72
 	public void sendOnNavigationButtonPressed(String buttonId) {
73
-		new NavigationEvent(reactInstanceManager.getCurrentReactContext()).sendOnNavigationButtonPressed(componentId, buttonId);
73
+		new EventEmitter(reactInstanceManager.getCurrentReactContext()).emitOnNavigationButtonPressed(componentId, buttonId);
74 74
 	}
75 75
 
76 76
     @Override

+ 7
- 0
lib/android/app/src/main/java/com/reactnativenavigation/utils/CommandListener.java Näytä tiedosto

@@ -0,0 +1,7 @@
1
+package com.reactnativenavigation.utils;
2
+
3
+public interface CommandListener {
4
+    void onSuccess(String childId);
5
+
6
+    void onError(String message);
7
+}

+ 1
- 4
lib/android/app/src/main/java/com/reactnativenavigation/utils/CommandListenerAdapter.java Näytä tiedosto

@@ -1,8 +1,6 @@
1 1
 package com.reactnativenavigation.utils;
2 2
 
3
-import com.reactnativenavigation.viewcontrollers.Navigator;
4
-
5
-public class CommandListenerAdapter implements Navigator.CommandListener {
3
+public class CommandListenerAdapter implements CommandListener {
6 4
     @Override
7 5
     public void onSuccess(String childId) {
8 6
 
@@ -13,4 +11,3 @@ public class CommandListenerAdapter implements Navigator.CommandListener {
13 11
 
14 12
     }
15 13
 }
16
-

+ 31
- 0
lib/android/app/src/main/java/com/reactnativenavigation/utils/NativeCommandListener.java Näytä tiedosto

@@ -0,0 +1,31 @@
1
+package com.reactnativenavigation.utils;
2
+
3
+import android.support.annotation.Nullable;
4
+
5
+import com.facebook.react.bridge.Promise;
6
+import com.reactnativenavigation.react.EventEmitter;
7
+
8
+public class NativeCommandListener extends CommandListenerAdapter {
9
+    private String commandId;
10
+    @Nullable private Promise promise;
11
+    private EventEmitter eventEmitter;
12
+    private Now now;
13
+
14
+    public NativeCommandListener(String commandId, @Nullable Promise promise, EventEmitter eventEmitter, Now now) {
15
+        this.commandId = commandId;
16
+        this.promise = promise;
17
+        this.eventEmitter = eventEmitter;
18
+        this.now = now;
19
+    }
20
+
21
+    @Override
22
+    public void onSuccess(String childId) {
23
+        if (promise != null) promise.resolve(childId);
24
+        eventEmitter.emitCommandCompletedEvent(commandId, now.now());
25
+    }
26
+
27
+    @Override
28
+    public void onError(String message) {
29
+        if (promise != null) promise.reject(new Throwable(message));
30
+    }
31
+}

+ 7
- 0
lib/android/app/src/main/java/com/reactnativenavigation/utils/Now.java Näytä tiedosto

@@ -0,0 +1,7 @@
1
+package com.reactnativenavigation.utils;
2
+
3
+public class Now {
4
+    public long now() {
5
+        return System.currentTimeMillis();
6
+    }
7
+}

+ 9
- 14
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/Navigator.java Näytä tiedosto

@@ -8,15 +8,16 @@ import android.support.annotation.Nullable;
8 8
 import android.view.ViewGroup;
9 9
 import android.widget.FrameLayout;
10 10
 
11
-import com.facebook.react.bridge.Promise;
12 11
 import com.reactnativenavigation.anim.ModalAnimator;
13 12
 import com.reactnativenavigation.anim.NavigationAnimator;
14 13
 import com.reactnativenavigation.parse.Options;
15 14
 import com.reactnativenavigation.presentation.NavigationOptionsListener;
16 15
 import com.reactnativenavigation.presentation.OverlayManager;
17 16
 import com.reactnativenavigation.react.JsDevReloadHandler;
17
+import com.reactnativenavigation.utils.CommandListener;
18 18
 import com.reactnativenavigation.utils.CommandListenerAdapter;
19 19
 import com.reactnativenavigation.utils.CompatUtils;
20
+import com.reactnativenavigation.utils.NativeCommandListener;
20 21
 import com.reactnativenavigation.viewcontrollers.modal.ModalPresenter;
21 22
 import com.reactnativenavigation.viewcontrollers.modal.ModalStack;
22 23
 
@@ -25,12 +26,6 @@ import java.util.Collections;
25 26
 
26 27
 public class Navigator extends ParentController implements JsDevReloadHandler.ReloadListener {
27 28
 
28
-    public interface CommandListener {
29
-        void onSuccess(String childId);
30
-
31
-        void onError(String message);
32
-    }
33
-
34 29
     private final ModalStack modalStack;
35 30
     private ViewController root;
36 31
     private FrameLayout rootLayout;
@@ -97,7 +92,7 @@ public class Navigator extends ParentController implements JsDevReloadHandler.Re
97 92
 
98 93
     }
99 94
 
100
-    public void setRoot(final ViewController viewController, Promise promise) {
95
+    public void setRoot(final ViewController viewController, CommandListener commandListener) {
101 96
         destroyRoot();
102 97
         root = viewController;
103 98
         contentLayout.addView(viewController.getView());
@@ -106,11 +101,11 @@ public class Navigator extends ParentController implements JsDevReloadHandler.Re
106 101
                     .animateStartApp(viewController.getView(), new AnimatorListenerAdapter() {
107 102
                         @Override
108 103
                         public void onAnimationEnd(Animator animation) {
109
-                            promise.resolve(viewController.getId());
104
+                            commandListener.onSuccess(viewController.getId());
110 105
                         }
111 106
                     });
112 107
         } else {
113
-            promise.resolve(viewController.getId());
108
+            commandListener.onSuccess(viewController.getId());
114 109
         }
115 110
     }
116 111
 
@@ -196,12 +191,12 @@ public class Navigator extends ParentController implements JsDevReloadHandler.Re
196 191
         modalStack.dismissAllModals(listener, root);
197 192
     }
198 193
 
199
-    public void showOverlay(ViewController overlay) {
200
-        overlayManager.show(rootLayout, overlay);
194
+    public void showOverlay(ViewController overlay, NativeCommandListener listener) {
195
+        overlayManager.show(rootLayout, overlay, listener);
201 196
     }
202 197
 
203
-    public void dismissOverlay(final String componentId) {
204
-        overlayManager.dismiss(getView(), componentId);
198
+    public void dismissOverlay(final String componentId, CommandListener listener) {
199
+        overlayManager.dismiss(getView(), componentId, listener);
205 200
     }
206 201
 
207 202
     @Nullable

+ 1
- 1
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/StackController.java Näytä tiedosto

@@ -7,8 +7,8 @@ import android.support.v4.view.ViewPager;
7 7
 
8 8
 import com.reactnativenavigation.anim.NavigationAnimator;
9 9
 import com.reactnativenavigation.parse.Options;
10
+import com.reactnativenavigation.utils.CommandListener;
10 11
 import com.reactnativenavigation.utils.CommandListenerAdapter;
11
-import com.reactnativenavigation.viewcontrollers.Navigator.CommandListener;
12 12
 import com.reactnativenavigation.viewcontrollers.topbar.TopBarBackgroundViewController;
13 13
 import com.reactnativenavigation.viewcontrollers.topbar.TopBarController;
14 14
 import com.reactnativenavigation.views.Component;

+ 2
- 1
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/ViewController.java Näytä tiedosto

@@ -12,6 +12,7 @@ import android.view.ViewTreeObserver;
12 12
 
13 13
 import com.reactnativenavigation.parse.Options;
14 14
 import com.reactnativenavigation.presentation.FabOptionsPresenter;
15
+import com.reactnativenavigation.utils.CommandListener;
15 16
 import com.reactnativenavigation.utils.StringUtils;
16 17
 import com.reactnativenavigation.utils.Task;
17 18
 import com.reactnativenavigation.utils.UiUtils;
@@ -62,7 +63,7 @@ public abstract class ViewController<T extends ViewGroup> implements ViewTreeObs
62 63
         getView();
63 64
     }
64 65
 
65
-    public boolean handleBack(Navigator.CommandListener listener) {
66
+    public boolean handleBack(CommandListener listener) {
66 67
         return false;
67 68
     }
68 69
 

+ 19
- 11
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/bottomtabs/BottomTabsController.java Näytä tiedosto

@@ -12,8 +12,9 @@ import com.reactnativenavigation.parse.BottomTabOptions;
12 12
 import com.reactnativenavigation.parse.Options;
13 13
 import com.reactnativenavigation.presentation.BottomTabsOptionsPresenter;
14 14
 import com.reactnativenavigation.presentation.NavigationOptionsListener;
15
+import com.reactnativenavigation.react.EventEmitter;
16
+import com.reactnativenavigation.utils.CommandListener;
15 17
 import com.reactnativenavigation.utils.ImageLoader;
16
-import com.reactnativenavigation.viewcontrollers.Navigator.CommandListener;
17 18
 import com.reactnativenavigation.viewcontrollers.ParentController;
18 19
 import com.reactnativenavigation.viewcontrollers.ViewController;
19 20
 import com.reactnativenavigation.views.BottomTabs;
@@ -27,14 +28,18 @@ import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
27 28
 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
28 29
 import static android.widget.RelativeLayout.ALIGN_PARENT_BOTTOM;
29 30
 
30
-public class BottomTabsController extends ParentController implements AHBottomNavigation.OnTabSelectedListener, NavigationOptionsListener {
31
+public class BottomTabsController extends ParentController implements AHBottomNavigation.OnTabSelectedListener, NavigationOptionsListener, TabSelector {
32
+
31 33
 	private BottomTabs bottomTabs;
32 34
 	private List<ViewController> tabs = new ArrayList<>();
35
+    private EventEmitter eventEmitter;
33 36
     private ImageLoader imageLoader;
34
-    private BottomTabFinder bottomTabFinder = new BottomTabFinder();
37
+    private BottomTabsOptionsPresenter presenter;
38
+    private final BottomTabFinder bottomTabFinder = new BottomTabFinder();
35 39
 
36
-    public BottomTabsController(final Activity activity, ImageLoader imageLoader, final String id, Options initialOptions) {
40
+    public BottomTabsController(final Activity activity, EventEmitter eventEmitter, ImageLoader imageLoader, final String id, Options initialOptions) {
37 41
 		super(activity, id, initialOptions);
42
+        this.eventEmitter = eventEmitter;
38 43
         this.imageLoader = imageLoader;
39 44
     }
40 45
 
@@ -43,6 +48,7 @@ public class BottomTabsController extends ParentController implements AHBottomNa
43 48
 	protected ViewGroup createView() {
44 49
 		RelativeLayout root = new RelativeLayout(getActivity());
45 50
 		bottomTabs = new BottomTabs(getActivity());
51
+        presenter = new BottomTabsOptionsPresenter(bottomTabs, this, bottomTabFinder);
46 52
         bottomTabs.setOnTabSelectedListener(this);
47 53
 		RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT);
48 54
 		lp.addRule(ALIGN_PARENT_BOTTOM);
@@ -53,14 +59,14 @@ public class BottomTabsController extends ParentController implements AHBottomNa
53 59
     @Override
54 60
     public void applyOptions(Options options) {
55 61
         super.applyOptions(options);
56
-        new BottomTabsOptionsPresenter(bottomTabs, bottomTabFinder).present(options);
62
+        presenter.present(options);
57 63
     }
58 64
 
59 65
     @Override
60 66
     public void applyChildOptions(Options options, Component child) {
61 67
         super.applyChildOptions(options, child);
62 68
         final int tabIndex = bottomTabFinder.findByComponent(child);
63
-        if (tabIndex >= 0) new BottomTabsOptionsPresenter(bottomTabs, bottomTabFinder).present(this.options, tabIndex);
69
+        if (tabIndex >= 0) presenter.present(this.options, tabIndex);
64 70
         applyOnParentController(parentController ->
65 71
                 ((ParentController) parentController).applyChildOptions(this.options.copy().clearBottomTabsOptions().clearBottomTabOptions(), child)
66 72
         );
@@ -70,7 +76,7 @@ public class BottomTabsController extends ParentController implements AHBottomNa
70 76
     public void mergeChildOptions(Options options, Component child) {
71 77
         super.mergeChildOptions(options, child);
72 78
         final int tabIndex = bottomTabFinder.findByComponent(child);
73
-        new BottomTabsOptionsPresenter(bottomTabs, bottomTabFinder).present(options, tabIndex);
79
+        presenter.present(options, tabIndex);
74 80
         applyOnParentController(parentController ->
75 81
                 ((ParentController) parentController).mergeChildOptions(options.copy().clearBottomTabsOptions(), child)
76 82
         );
@@ -93,7 +99,8 @@ public class BottomTabsController extends ParentController implements AHBottomNa
93 99
     @Override
94 100
     public boolean onTabSelected(int index, boolean wasSelected) {
95 101
         if (wasSelected) return false;
96
-        selectTabAtIndex(index);
102
+        eventEmitter.emitBottomTabSelected(bottomTabs.getCurrentItem(), index);
103
+        selectTab(index);
97 104
         return true;
98 105
 	}
99 106
 	
@@ -108,7 +115,7 @@ public class BottomTabsController extends ParentController implements AHBottomNa
108 115
 		    tabs.get(i).setParentController(this);
109 116
 			createTab(i, tabs.get(i).options.bottomTabOptions);
110 117
 		}
111
-		selectTabAtIndex(0);
118
+		selectTab(0);
112 119
 	}
113 120
 
114 121
 	private void createTab(int index, final BottomTabOptions tabOptions) {
@@ -143,10 +150,11 @@ public class BottomTabsController extends ParentController implements AHBottomNa
143 150
 	@Override
144 151
 	public void mergeOptions(Options options) {
145 152
         this.options = this.options.mergeWith(options);
146
-        new BottomTabsOptionsPresenter(bottomTabs, bottomTabFinder).present(this.options);
153
+        presenter.present(this.options);
147 154
     }
148 155
 
149
-    public void selectTabAtIndex(final int newIndex) {
156
+    @Override
157
+    public void selectTab(final int newIndex) {
150 158
         getView().removeView(getCurrentView());
151 159
         bottomTabs.setCurrentItem(newIndex, false);
152 160
         RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT);

+ 5
- 0
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/bottomtabs/TabSelector.java Näytä tiedosto

@@ -0,0 +1,5 @@
1
+package com.reactnativenavigation.viewcontrollers.bottomtabs;
2
+
3
+public interface TabSelector {
4
+    void selectTab(int index);
5
+}

+ 1
- 1
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/modal/ModalPresenter.java Näytä tiedosto

@@ -6,7 +6,7 @@ import android.view.ViewGroup;
6 6
 
7 7
 import com.reactnativenavigation.anim.ModalAnimator;
8 8
 import com.reactnativenavigation.parse.ModalPresentationStyle;
9
-import com.reactnativenavigation.viewcontrollers.Navigator.CommandListener;
9
+import com.reactnativenavigation.utils.CommandListener;
10 10
 import com.reactnativenavigation.viewcontrollers.ViewController;
11 11
 
12 12
 public class ModalPresenter {

+ 1
- 1
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/modal/ModalStack.java Näytä tiedosto

@@ -2,7 +2,7 @@ package com.reactnativenavigation.viewcontrollers.modal;
2 2
 
3 3
 import android.view.ViewGroup;
4 4
 
5
-import com.reactnativenavigation.viewcontrollers.Navigator.CommandListener;
5
+import com.reactnativenavigation.utils.CommandListener;
6 6
 import com.reactnativenavigation.viewcontrollers.ViewController;
7 7
 
8 8
 import java.util.ArrayList;

+ 58
- 0
lib/android/app/src/test/java/com/reactnativenavigation/utils/NativeCommandListenerTest.java Näytä tiedosto

@@ -0,0 +1,58 @@
1
+package com.reactnativenavigation.utils;
2
+
3
+import com.facebook.react.bridge.Promise;
4
+import com.reactnativenavigation.BaseTest;
5
+import com.reactnativenavigation.react.EventEmitter;
6
+
7
+import org.junit.Test;
8
+import org.mockito.ArgumentCaptor;
9
+import org.mockito.Mockito;
10
+
11
+import static org.assertj.core.api.Java6Assertions.assertThat;
12
+import static org.mockito.Mockito.times;
13
+import static org.mockito.Mockito.verify;
14
+import static org.mockito.Mockito.when;
15
+
16
+public class NativeCommandListenerTest extends BaseTest {
17
+    private static final String COMMAND_ID = "someCommand";
18
+    private static final String CHILD_ID = "someChild";
19
+    private static final long NOW = 1535374334;
20
+
21
+    private EventEmitter eventEmitter;
22
+    private Promise promise;
23
+
24
+    private NativeCommandListener uut;
25
+
26
+    @Override
27
+    public void beforeEach() {
28
+        promise = Mockito.mock(Promise.class);
29
+        eventEmitter = Mockito.mock(EventEmitter.class);
30
+        uut = new NativeCommandListener(COMMAND_ID, promise, eventEmitter, mockNow());
31
+    }
32
+
33
+    @Test
34
+    public void onSuccess() {
35
+        uut.onSuccess(CHILD_ID);
36
+        verify(promise, times(1)).resolve(CHILD_ID);
37
+    }
38
+
39
+    @Test
40
+    public void onSuccess_emitsNavigationEvent() {
41
+        uut.onSuccess(CHILD_ID);
42
+        verify(eventEmitter, times(1)).emitCommandCompletedEvent(COMMAND_ID, NOW);
43
+    }
44
+
45
+    @Test
46
+    public void onError() {
47
+        uut.onError("something which is wrong");
48
+        ArgumentCaptor<Throwable> captor = ArgumentCaptor.forClass(Throwable.class);
49
+        verify(promise, times(1)).reject(captor.capture());
50
+        assertThat(captor.getValue().getMessage().equals("something which is wrong"));
51
+    }
52
+
53
+    private Now mockNow() {
54
+        Now now = Mockito.mock(Now.class);
55
+        when(now.now()).then(i -> NOW);
56
+        return now;
57
+    }
58
+}

+ 24
- 6
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/BottomTabsControllerTest.java Näytä tiedosto

@@ -13,6 +13,7 @@ import com.reactnativenavigation.mocks.TopBarButtonCreatorMock;
13 13
 import com.reactnativenavigation.parse.Options;
14 14
 import com.reactnativenavigation.parse.params.Color;
15 15
 import com.reactnativenavigation.parse.params.Number;
16
+import com.reactnativenavigation.react.EventEmitter;
16 17
 import com.reactnativenavigation.utils.CommandListenerAdapter;
17 18
 import com.reactnativenavigation.utils.ImageLoader;
18 19
 import com.reactnativenavigation.utils.OptionHelper;
@@ -24,6 +25,7 @@ import com.reactnativenavigation.views.ReactComponent;
24 25
 
25 26
 import org.junit.Test;
26 27
 import org.mockito.ArgumentCaptor;
28
+import org.mockito.Mockito;
27 29
 
28 30
 import java.util.Arrays;
29 31
 import java.util.Collections;
@@ -47,12 +49,14 @@ public class BottomTabsControllerTest extends BaseTest {
47 49
     private ViewController child5;
48 50
     private Options tabOptions = OptionHelper.createBottomTabOptions();
49 51
     private ImageLoader imageLoaderMock = ImageLoaderMock.mock();
52
+    private EventEmitter eventEmitter;
50 53
 
51 54
     @Override
52 55
     public void beforeEach() {
53 56
         super.beforeEach();
54 57
         activity = newActivity();
55
-        uut = spy(new BottomTabsController(activity, imageLoaderMock, "uut", new Options()));
58
+        eventEmitter = Mockito.mock(EventEmitter.class);
59
+        uut = spy(new BottomTabsController(activity, eventEmitter, imageLoaderMock, "uut", new Options()));
56 60
         child1 = spy(new SimpleViewController(activity, "child1", tabOptions));
57 61
         child2 = spy(new SimpleViewController(activity, "child2", tabOptions));
58 62
         child3 = spy(new SimpleViewController(activity, "child3", tabOptions));
@@ -91,14 +95,27 @@ public class BottomTabsControllerTest extends BaseTest {
91 95
     }
92 96
 
93 97
     @Test
94
-    public void selectTabAtIndex() {
98
+    public void onTabSelected() {
95 99
         uut.setTabs(createTabs());
96 100
         assertThat(uut.getSelectedIndex()).isZero();
97 101
 
98
-        uut.selectTabAtIndex(3);
102
+        uut.onTabSelected(3, false);
99 103
 
100 104
         assertThat(uut.getSelectedIndex()).isEqualTo(3);
101 105
         assertThat(((ViewController) ((List) uut.getChildControllers()).get(0)).getView().getParent()).isNull();
106
+        verify(eventEmitter, times(1)).emitBottomTabSelected(0, 3);
107
+    }
108
+
109
+    @Test
110
+    public void onTabReSelected() {
111
+        uut.setTabs(createTabs());
112
+        assertThat(uut.getSelectedIndex()).isZero();
113
+
114
+        uut.onTabSelected(0, false);
115
+
116
+        assertThat(uut.getSelectedIndex()).isEqualTo(0);
117
+        assertThat(((ViewController) ((List) uut.getChildControllers()).get(0)).getView().getParent()).isNotNull();
118
+        verify(eventEmitter, times(1)).emitBottomTabSelected(0, 0);
102 119
     }
103 120
 
104 121
     @Test
@@ -123,7 +140,7 @@ public class BottomTabsControllerTest extends BaseTest {
123 140
         uut.setTabs(tabs);
124 141
 
125 142
         assertThat(uut.handleBack(new CommandListenerAdapter())).isFalse();
126
-        uut.selectTabAtIndex(2);
143
+        uut.selectTab(2);
127 144
         assertThat(uut.handleBack(new CommandListenerAdapter())).isTrue();
128 145
 
129 146
         verify(spy, times(1)).handleBack(any());
@@ -157,14 +174,15 @@ public class BottomTabsControllerTest extends BaseTest {
157 174
         Options options = new Options();
158 175
         options.bottomTabsOptions.currentTabIndex = new Number(1);
159 176
         uut.mergeOptions(options);
160
-        verify(uut, times(1)).selectTabAtIndex(1);
177
+        verify(uut, times(1)).selectTab(1);
178
+        verify(eventEmitter, times(0)).emitBottomTabSelected(any(Integer.class), any(Integer.class));
161 179
     }
162 180
 
163 181
     @Test
164 182
     public void buttonPressInvokedOnCurrentTab() {
165 183
         uut.setTabs(createTabs());
166 184
         uut.ensureViewIsCreated();
167
-        uut.selectTabAtIndex(1);
185
+        uut.selectTab(1);
168 186
 
169 187
         uut.sendOnNavigationButtonPressed("btn1");
170 188
         verify(child2, times(1)).sendOnNavigationButtonPressed("btn1");

+ 36
- 36
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/NavigatorTest.java Näytä tiedosto

@@ -5,7 +5,6 @@ import android.support.annotation.NonNull;
5 5
 import com.reactnativenavigation.BaseTest;
6 6
 import com.reactnativenavigation.TestActivity;
7 7
 import com.reactnativenavigation.mocks.ImageLoaderMock;
8
-import com.reactnativenavigation.mocks.MockPromise;
9 8
 import com.reactnativenavigation.mocks.SimpleComponentViewController;
10 9
 import com.reactnativenavigation.mocks.SimpleViewController;
11 10
 import com.reactnativenavigation.mocks.TitleBarReactViewCreatorMock;
@@ -15,6 +14,8 @@ import com.reactnativenavigation.parse.Options;
15 14
 import com.reactnativenavigation.parse.params.Bool;
16 15
 import com.reactnativenavigation.parse.params.Text;
17 16
 import com.reactnativenavigation.presentation.OverlayManager;
17
+import com.reactnativenavigation.react.EventEmitter;
18
+import com.reactnativenavigation.utils.CommandListener;
18 19
 import com.reactnativenavigation.utils.CommandListenerAdapter;
19 20
 import com.reactnativenavigation.utils.CompatUtils;
20 21
 import com.reactnativenavigation.utils.ImageLoader;
@@ -29,8 +30,6 @@ import org.robolectric.android.controller.ActivityController;
29 30
 
30 31
 import java.util.Arrays;
31 32
 
32
-import javax.annotation.Nullable;
33
-
34 33
 import static org.assertj.core.api.Java6Assertions.assertThat;
35 34
 import static org.mockito.ArgumentMatchers.any;
36 35
 import static org.mockito.Mockito.spy;
@@ -51,10 +50,11 @@ public class NavigatorTest extends BaseTest {
51 50
     private ImageLoader imageLoaderMock;
52 51
     private ActivityController<TestActivity> activityController;
53 52
     private OverlayManager overlayManager;
53
+    private EventEmitter eventEmitter;
54 54
 
55 55
     @Override
56 56
     public void beforeEach() {
57
-        super.beforeEach();
57
+        eventEmitter = Mockito.mock(EventEmitter.class);
58 58
         overlayManager = Mockito.mock(OverlayManager.class);
59 59
         imageLoaderMock = ImageLoaderMock.mock();
60 60
         activityController = newActivityController(TestActivity.class);
@@ -77,14 +77,14 @@ public class NavigatorTest extends BaseTest {
77 77
     @Test
78 78
     public void setRoot_AddsChildControllerView() {
79 79
         assertThat(uut.getContentLayout().getChildCount()).isZero();
80
-        uut.setRoot(child1, new MockPromise());
80
+        uut.setRoot(child1, new CommandListenerAdapter());
81 81
         assertIsChild(uut.getContentLayout(), child1.getView());
82 82
     }
83 83
 
84 84
     @Test
85 85
     public void setRoot_ReplacesExistingChildControllerViews() {
86
-        uut.setRoot(child1, new MockPromise());
87
-        uut.setRoot(child2, new MockPromise());
86
+        uut.setRoot(child1, new CommandListenerAdapter());
87
+        uut.setRoot(child2, new CommandListenerAdapter());
88 88
         assertIsChild(uut.getContentLayout(), child2.getView());
89 89
     }
90 90
 
@@ -98,7 +98,7 @@ public class NavigatorTest extends BaseTest {
98 98
     public void push() {
99 99
         StackController stackController = newStack();
100 100
         stackController.push(child1, new CommandListenerAdapter());
101
-        uut.setRoot(stackController, new MockPromise());
101
+        uut.setRoot(stackController, new CommandListenerAdapter());
102 102
 
103 103
         assertIsChild(uut.getView(), stackController.getView());
104 104
         assertIsChild(stackController.getView(), child1.getView());
@@ -111,7 +111,7 @@ public class NavigatorTest extends BaseTest {
111 111
 
112 112
     @Test
113 113
     public void push_InvalidPushWithoutAStack_DoesNothing() {
114
-        uut.setRoot(child1, new MockPromise());
114
+        uut.setRoot(child1, new CommandListenerAdapter());
115 115
         uut.push(child1.getId(), child2, new CommandListenerAdapter());
116 116
         assertIsChild(uut.getView(), child1.getView());
117 117
     }
@@ -124,7 +124,7 @@ public class NavigatorTest extends BaseTest {
124 124
         stack1.push(child1, new CommandListenerAdapter());
125 125
         stack2.push(child2, new CommandListenerAdapter());
126 126
         bottomTabsController.setTabs(Arrays.asList(stack1, stack2));
127
-        uut.setRoot(bottomTabsController, new MockPromise());
127
+        uut.setRoot(bottomTabsController, new CommandListenerAdapter());
128 128
 
129 129
         SimpleViewController newChild = new SimpleViewController(activity, "new child", tabOptions);
130 130
         uut.push(child2.getId(), newChild, new CommandListenerAdapter());
@@ -136,7 +136,7 @@ public class NavigatorTest extends BaseTest {
136 136
     @Test
137 137
     public void pop_InvalidDoesNothing() {
138 138
         uut.pop("123", new CommandListenerAdapter());
139
-        uut.setRoot(child1, new MockPromise());
139
+        uut.setRoot(child1, new CommandListenerAdapter());
140 140
         uut.pop(child1.getId(), new CommandListenerAdapter());
141 141
         assertThat(uut.getChildControllers()).hasSize(1);
142 142
     }
@@ -147,7 +147,7 @@ public class NavigatorTest extends BaseTest {
147 147
         StackController stack1 = newStack();
148 148
         StackController stack2 = newStack();
149 149
         bottomTabsController.setTabs(Arrays.asList(stack1, stack2));
150
-        uut.setRoot(bottomTabsController, new MockPromise());
150
+        uut.setRoot(bottomTabsController, new CommandListenerAdapter());
151 151
         stack1.push(child1, new CommandListenerAdapter());
152 152
         stack2.push(child2, new CommandListenerAdapter());
153 153
         stack2.push(child3, new CommandListenerAdapter() {
@@ -175,7 +175,7 @@ public class NavigatorTest extends BaseTest {
175 175
         stack2.push(child3, new CommandListenerAdapter());
176 176
         stack2.push(child4, new CommandListenerAdapter());
177 177
         bottomTabsController.setTabs(Arrays.asList(stack1, stack2));
178
-        uut.setRoot(bottomTabsController, new MockPromise());
178
+        uut.setRoot(bottomTabsController, new CommandListenerAdapter());
179 179
 
180 180
         uut.popSpecific(child2.getId(), new CommandListenerAdapter());
181 181
 
@@ -188,7 +188,7 @@ public class NavigatorTest extends BaseTest {
188 188
         StackController stack1 = newStack();
189 189
         StackController stack2 = newStack();
190 190
         bottomTabsController.setTabs(Arrays.asList(stack1, stack2));
191
-        uut.setRoot(bottomTabsController, new MockPromise());
191
+        uut.setRoot(bottomTabsController, new CommandListenerAdapter());
192 192
 
193 193
         stack1.push(child1, new CommandListenerAdapter());
194 194
         stack2.push(child2, new CommandListenerAdapter());
@@ -209,7 +209,7 @@ public class NavigatorTest extends BaseTest {
209 209
         StackController stack1 = newStack();
210 210
         StackController stack2 = newStack();
211 211
         bottomTabsController.setTabs(Arrays.asList(stack1, stack2));
212
-        uut.setRoot(bottomTabsController, new MockPromise());
212
+        uut.setRoot(bottomTabsController, new CommandListenerAdapter());
213 213
 
214 214
         stack1.push(child1, new CommandListenerAdapter());
215 215
         stack2.push(child2, new CommandListenerAdapter());
@@ -229,7 +229,7 @@ public class NavigatorTest extends BaseTest {
229 229
         disablePushAnimation(child1, child2, child3);
230 230
 
231 231
         StackController stack = newStack();
232
-        uut.setRoot(stack, new MockPromise());
232
+        uut.setRoot(stack, new CommandListenerAdapter());
233 233
 
234 234
         stack.push(child1, new CommandListenerAdapter());
235 235
         stack.push(child2, new CommandListenerAdapter());
@@ -241,8 +241,8 @@ public class NavigatorTest extends BaseTest {
241 241
     @Test
242 242
     public void handleBack_DelegatesToRoot() {
243 243
         ViewController root = spy(child1);
244
-        uut.setRoot(root, new MockPromise());
245
-        when(root.handleBack(any(Navigator.CommandListener.class))).thenReturn(true);
244
+        uut.setRoot(root, new CommandListenerAdapter());
245
+        when(root.handleBack(any(CommandListener.class))).thenReturn(true);
246 246
         assertThat(uut.handleBack(new CommandListenerAdapter())).isTrue();
247 247
         verify(root, times(1)).handleBack(any());
248 248
     }
@@ -250,7 +250,7 @@ public class NavigatorTest extends BaseTest {
250 250
     @Test
251 251
     public void handleBack_modalTakePrecedenceOverRoot() {
252 252
         ViewController root = spy(child1);
253
-        uut.setRoot(root, new MockPromise());
253
+        uut.setRoot(root, new CommandListenerAdapter());
254 254
         uut.showModal(child2, new CommandListenerAdapter());
255 255
         verify(root, times(0)).handleBack(new CommandListenerAdapter());
256 256
     }
@@ -260,7 +260,7 @@ public class NavigatorTest extends BaseTest {
260 260
         ComponentViewController componentVc = new SimpleComponentViewController(activity, "theId", new Options());
261 261
         componentVc.setParentController(parentController);
262 262
         assertThat(componentVc.options.topBar.title.text.get("")).isEmpty();
263
-        uut.setRoot(componentVc, new MockPromise());
263
+        uut.setRoot(componentVc, new CommandListenerAdapter());
264 264
 
265 265
         Options options = new Options();
266 266
         options.topBar.title.text = new Text("new title");
@@ -276,7 +276,7 @@ public class NavigatorTest extends BaseTest {
276 276
 
277 277
     @NonNull
278 278
     private BottomTabsController newTabs() {
279
-        return new BottomTabsController(activity, imageLoaderMock, "tabsController", new Options());
279
+        return new BottomTabsController(activity, eventEmitter, imageLoaderMock, "tabsController", new Options());
280 280
     }
281 281
 
282 282
     @NonNull
@@ -295,7 +295,7 @@ public class NavigatorTest extends BaseTest {
295 295
     public void push_promise() {
296 296
         final StackController stackController = newStack();
297 297
         stackController.push(child1, new CommandListenerAdapter());
298
-        uut.setRoot(stackController, new MockPromise());
298
+        uut.setRoot(stackController, new CommandListenerAdapter());
299 299
 
300 300
         assertIsChild(uut.getView(), stackController.getView());
301 301
         assertIsChild(stackController.getView(), child1.getView());
@@ -311,7 +311,7 @@ public class NavigatorTest extends BaseTest {
311 311
 
312 312
     @Test
313 313
     public void push_InvalidPushWithoutAStack_DoesNothing_Promise() {
314
-        uut.setRoot(child1, new MockPromise());
314
+        uut.setRoot(child1, new CommandListenerAdapter());
315 315
         uut.push(child1.getId(), child2, new CommandListenerAdapter() {
316 316
             @Override
317 317
             public void onError(String message) {
@@ -324,7 +324,7 @@ public class NavigatorTest extends BaseTest {
324 324
     @Test
325 325
     public void pop_InvalidDoesNothing_Promise() {
326 326
         uut.pop("123", new CommandListenerAdapter());
327
-        uut.setRoot(child1, new MockPromise());
327
+        uut.setRoot(child1, new CommandListenerAdapter());
328 328
         uut.pop(child1.getId(), new CommandListenerAdapter() {
329 329
             @Override
330 330
             public void onError(String reason) {
@@ -339,7 +339,7 @@ public class NavigatorTest extends BaseTest {
339 339
         StackController stack1 = newStack();
340 340
         final StackController stack2 = newStack();
341 341
         bottomTabsController.setTabs(Arrays.asList(stack1, stack2));
342
-        uut.setRoot(bottomTabsController, new MockPromise());
342
+        uut.setRoot(bottomTabsController, new CommandListenerAdapter());
343 343
 
344 344
         stack1.push(child1, new CommandListenerAdapter());
345 345
         stack2.push(child2, new CommandListenerAdapter());
@@ -355,7 +355,7 @@ public class NavigatorTest extends BaseTest {
355 355
 
356 356
     @Test
357 357
     public void pushIntoModal() {
358
-        uut.setRoot(parentController, new MockPromise());
358
+        uut.setRoot(parentController, new CommandListenerAdapter());
359 359
         StackController stackController = newStack();
360 360
         stackController.push(child1, new CommandListenerAdapter());
361 361
         uut.showModal(stackController, new CommandListenerAdapter());
@@ -369,7 +369,7 @@ public class NavigatorTest extends BaseTest {
369 369
         child2.options.animations.push.enable = new Bool(false);
370 370
         StackController parent = newStack();
371 371
         parent.ensureViewIsCreated();
372
-        uut.setRoot(parent, new MockPromise());
372
+        uut.setRoot(parent, new CommandListenerAdapter());
373 373
         parent.push(parentController, new CommandListenerAdapter());
374 374
 
375 375
         parentController.push(child1, new CommandListenerAdapter());
@@ -378,7 +378,7 @@ public class NavigatorTest extends BaseTest {
378 378
         child1.ensureViewIsCreated();
379 379
         child2.ensureViewIsCreated();
380 380
 
381
-        Navigator.CommandListener listener = new CommandListenerAdapter() {
381
+        CommandListenerAdapter listener = new CommandListenerAdapter() {
382 382
             @Override
383 383
             public void onSuccess(String childId) {
384 384
                 assertThat(parentController.getChildControllers().size()).isEqualTo(1);
@@ -390,9 +390,9 @@ public class NavigatorTest extends BaseTest {
390 390
 
391 391
     @Test
392 392
     public void showModal_onViewDisappearIsInvokedOnRoot() {
393
-        uut.setRoot(parentController, new MockPromise() {
393
+        uut.setRoot(parentController, new CommandListenerAdapter() {
394 394
             @Override
395
-            public void resolve(@Nullable Object value) {
395
+            public void onSuccess(String childId) {
396 396
                 uut.showModal(child1, new CommandListenerAdapter() {
397 397
                     @Override
398 398
                     public void onSuccess(String childId) {
@@ -409,7 +409,7 @@ public class NavigatorTest extends BaseTest {
409 409
         disableShowModalAnimation(child1, child2);
410 410
         disableDismissModalAnimation(child1, child2);
411 411
 
412
-        uut.setRoot(parentController, new MockPromise());
412
+        uut.setRoot(parentController, new CommandListenerAdapter());
413 413
         uut.showModal(child1, new CommandListenerAdapter());
414 414
         uut.showModal(child2, new CommandListenerAdapter());
415 415
 
@@ -430,7 +430,7 @@ public class NavigatorTest extends BaseTest {
430 430
         uut.dismissAllModals(new CommandListenerAdapter());
431 431
         verify(parentController, times(0)).onViewAppeared();
432 432
 
433
-        uut.setRoot(parentController, new MockPromise());
433
+        uut.setRoot(parentController, new CommandListenerAdapter());
434 434
         verify(parentController, times(1)).onViewAppeared();
435 435
         uut.showModal(child1, new CommandListenerAdapter());
436 436
         uut.dismissAllModals(new CommandListenerAdapter());
@@ -442,7 +442,7 @@ public class NavigatorTest extends BaseTest {
442 442
     public void handleBack_onViewAppearedInvokedOnRoot() {
443 443
         disableShowModalAnimation(child1, child2);
444 444
 
445
-        uut.setRoot(parentController, new MockPromise());
445
+        uut.setRoot(parentController, new CommandListenerAdapter());
446 446
         uut.showModal(child1, new CommandListenerAdapter());
447 447
         uut.showModal(child2, new CommandListenerAdapter());
448 448
 
@@ -461,21 +461,21 @@ public class NavigatorTest extends BaseTest {
461 461
     @Test
462 462
     public void destroy_destroyedRoot() {
463 463
         parentController.options.animations.startApp.enable = new Bool(false);
464
-        uut.setRoot(parentController, new MockPromise());
464
+        uut.setRoot(parentController, new CommandListenerAdapter());
465 465
         activityController.destroy();
466 466
         verify(parentController, times(1)).destroy();
467 467
     }
468 468
 
469 469
     @Test
470 470
     public void destroy_destroyOverlayManager() {
471
-        uut.setRoot(parentController, new MockPromise());
471
+        uut.setRoot(parentController, new CommandListenerAdapter());
472 472
         activityController.destroy();
473 473
         verify(overlayManager, times(1)).destroy();
474 474
     }
475 475
 
476 476
     @Test
477 477
     public void reload_navigatorIsDestroyedOnReload() {
478
-        uut.setRoot(parentController, new MockPromise());
478
+        uut.setRoot(parentController, new CommandListenerAdapter());
479 479
         uut.onReload();
480 480
         verify(parentController, times(1)).destroy();
481 481
         verify(overlayManager, times(1)).destroy();

+ 1
- 1
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/modal/ModalPresenterTest.java Näytä tiedosto

@@ -9,8 +9,8 @@ import com.reactnativenavigation.anim.ModalAnimator;
9 9
 import com.reactnativenavigation.mocks.SimpleViewController;
10 10
 import com.reactnativenavigation.parse.ModalPresentationStyle;
11 11
 import com.reactnativenavigation.parse.Options;
12
+import com.reactnativenavigation.utils.CommandListener;
12 13
 import com.reactnativenavigation.utils.CommandListenerAdapter;
13
-import com.reactnativenavigation.viewcontrollers.Navigator.CommandListener;
14 14
 import com.reactnativenavigation.viewcontrollers.ParentController;
15 15
 import com.reactnativenavigation.viewcontrollers.ViewController;
16 16
 

+ 1
- 1
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/modal/ModalStackTest.java Näytä tiedosto

@@ -8,8 +8,8 @@ import com.reactnativenavigation.BaseTest;
8 8
 import com.reactnativenavigation.anim.ModalAnimator;
9 9
 import com.reactnativenavigation.mocks.SimpleViewController;
10 10
 import com.reactnativenavigation.parse.Options;
11
+import com.reactnativenavigation.utils.CommandListener;
11 12
 import com.reactnativenavigation.utils.CommandListenerAdapter;
12
-import com.reactnativenavigation.viewcontrollers.Navigator.CommandListener;
13 13
 import com.reactnativenavigation.viewcontrollers.ParentController;
14 14
 import com.reactnativenavigation.viewcontrollers.ViewController;
15 15
 

+ 11
- 11
lib/ios/RNNBridgeModule.m Näytä tiedosto

@@ -18,7 +18,7 @@ RCT_EXPORT_MODULE();
18 18
 
19 19
 #pragma mark - JS interface
20 20
 
21
-RCT_EXPORT_METHOD(setRoot:(NSDictionary*)layout resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
21
+RCT_EXPORT_METHOD(setRoot:(NSString*)commandId layout:(NSDictionary*)layout resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
22 22
 	[_commandsHandler setRoot:layout completion:^{
23 23
 		resolve(layout);
24 24
 	}];
@@ -36,61 +36,61 @@ RCT_EXPORT_METHOD(setDefaultOptions:(NSDictionary*)options resolver:(RCTPromiseR
36 36
 	}];
37 37
 }
38 38
 
39
-RCT_EXPORT_METHOD(push:(NSString*)componentId layout:(NSDictionary*)layout resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
39
+RCT_EXPORT_METHOD(push:(NSString*)commandId componentId:(NSString*)componentId layout:(NSDictionary*)layout resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
40 40
 	[_commandsHandler push:componentId layout:layout completion:^{
41 41
 		resolve(componentId);
42 42
 	} rejection:reject];
43 43
 }
44 44
 
45
-RCT_EXPORT_METHOD(pop:(NSString*)componentId options:(NSDictionary*)options resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
45
+RCT_EXPORT_METHOD(pop:(NSString*)commandId componentId:(NSString*)componentId options:(NSDictionary*)options resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
46 46
 	[_commandsHandler pop:componentId options:(NSDictionary*)options completion:^{
47 47
 		resolve(componentId);
48 48
 	} rejection:reject];
49 49
 }
50 50
 
51
-RCT_EXPORT_METHOD(setStackRoot:(NSString*)componentId layout:(NSDictionary*)layout resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
51
+RCT_EXPORT_METHOD(setStackRoot:(NSString*)commandId componentId:(NSString*)componentId layout:(NSDictionary*)layout resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
52 52
 	[_commandsHandler setStackRoot:componentId layout:layout completion:^{
53 53
 		resolve(componentId);
54 54
 	} rejection:reject];
55 55
 }
56 56
 
57
-RCT_EXPORT_METHOD(popTo:(NSString*)componentId resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
57
+RCT_EXPORT_METHOD(popTo:(NSString*)commandId componentId:(NSString*)componentId resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
58 58
 	[_commandsHandler popTo:componentId completion:^{
59 59
 		resolve(componentId);
60 60
 	} rejection:reject];
61 61
 }
62 62
 
63
-RCT_EXPORT_METHOD(popToRoot:(NSString*)componentId resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
63
+RCT_EXPORT_METHOD(popToRoot:(NSString*)commandId componentId:(NSString*)componentId resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
64 64
 	[_commandsHandler popToRoot:componentId completion:^{
65 65
 		resolve(componentId);
66 66
 	} rejection:reject];
67 67
 }
68 68
 
69
-RCT_EXPORT_METHOD(showModal:(NSDictionary*)layout resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
69
+RCT_EXPORT_METHOD(showModal:(NSString*)commandId layout:(NSDictionary*)layout resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
70 70
 	[_commandsHandler showModal:layout completion:^{
71 71
 		resolve(nil);
72 72
 	}];
73 73
 }
74 74
 
75
-RCT_EXPORT_METHOD(dismissModal:(NSString*)componentId resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
75
+RCT_EXPORT_METHOD(dismissModal:(NSString*)commandId componentId:(NSString*)componentId resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
76 76
 	[_commandsHandler dismissModal:componentId completion:^{
77 77
 		resolve(componentId);
78 78
 	}];
79 79
 }
80 80
 
81
-RCT_EXPORT_METHOD(dismissAllModals:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
81
+RCT_EXPORT_METHOD(dismissAllModals:(NSString*)commandId resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
82 82
 	[_commandsHandler dismissAllModalsWithCompletion:^{
83 83
 		resolve(nil);
84 84
 	}];
85 85
 }
86 86
 
87
-RCT_EXPORT_METHOD(showOverlay:(NSDictionary*)layout resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
87
+RCT_EXPORT_METHOD(showOverlay:(NSString*)commandId layout:(NSDictionary*)layout resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
88 88
 	[_commandsHandler showOverlay:layout completion:^{
89 89
 		resolve(layout[@"id"]);
90 90
 	}];
91 91
 }
92 92
 
93
-RCT_EXPORT_METHOD(dismissOverlay:(NSString*)componentId resolve:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
93
+RCT_EXPORT_METHOD(dismissOverlay:(NSString*)commandId componentId:(NSString*)componentId resolve:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
94 94
 	[_commandsHandler dismissOverlay:componentId completion:^{
95 95
 		resolve(@(1));
96 96
 	}];

+ 1
- 1
lib/src/Navigation.ts Näytä tiedosto

@@ -38,7 +38,7 @@ export class Navigation {
38 38
     this.layoutTreeCrawler = new LayoutTreeCrawler(this.uniqueIdProvider, this.store);
39 39
     this.nativeCommandsSender = new NativeCommandsSender();
40 40
     this.commandsObserver = new CommandsObserver();
41
-    this.commands = new Commands(this.nativeCommandsSender, this.layoutTreeParser, this.layoutTreeCrawler, this.commandsObserver);
41
+    this.commands = new Commands(this.nativeCommandsSender, this.layoutTreeParser, this.layoutTreeCrawler, this.commandsObserver, this.uniqueIdProvider);
42 42
     this.eventsRegistry = new EventsRegistry(this.nativeEventsReceiver, this.commandsObserver);
43 43
     this.componentEventsObserver = new ComponentEventsObserver(this.eventsRegistry, this.store);
44 44
 

+ 22
- 22
lib/src/adapters/NativeCommandsSender.ts Näytä tiedosto

@@ -6,8 +6,8 @@ export class NativeCommandsSender {
6 6
     this.nativeCommandsModule = NativeModules.RNNBridgeModule;
7 7
   }
8 8
 
9
-  setRoot(layoutTree: object) {
10
-    return this.nativeCommandsModule.setRoot(layoutTree);
9
+  setRoot(commandId: string, layoutTree: object) {
10
+    return this.nativeCommandsModule.setRoot(commandId, layoutTree);
11 11
   }
12 12
 
13 13
   setDefaultOptions(options: object) {
@@ -18,43 +18,43 @@ export class NativeCommandsSender {
18 18
     return this.nativeCommandsModule.mergeOptions(componentId, options);
19 19
   }
20 20
 
21
-  push(onComponentId: string, layout: object) {
22
-    return this.nativeCommandsModule.push(onComponentId, layout);
21
+  push(commandId: string, onComponentId: string, layout: object) {
22
+    return this.nativeCommandsModule.push(commandId, onComponentId, layout);
23 23
   }
24 24
 
25
-  pop(componentId: string, options: object) {
26
-    return this.nativeCommandsModule.pop(componentId, options);
25
+  pop(commandId: string, componentId: string, options: object) {
26
+    return this.nativeCommandsModule.pop(commandId, componentId, options);
27 27
   }
28 28
 
29
-  popTo(componentId: string) {
30
-    return this.nativeCommandsModule.popTo(componentId);
29
+  popTo(commandId: string, componentId: string) {
30
+    return this.nativeCommandsModule.popTo(commandId, componentId);
31 31
   }
32 32
 
33
-  popToRoot(componentId: string) {
34
-    return this.nativeCommandsModule.popToRoot(componentId);
33
+  popToRoot(commandId: string, componentId: string) {
34
+    return this.nativeCommandsModule.popToRoot(commandId, componentId);
35 35
   }
36 36
 
37
-  setStackRoot(onComponentId: string, layout: object) {
38
-    return this.nativeCommandsModule.setStackRoot(onComponentId, layout);
37
+  setStackRoot(commandId: string, onComponentId: string, layout: object) {
38
+    return this.nativeCommandsModule.setStackRoot(commandId, onComponentId, layout);
39 39
   }
40 40
 
41
-  showModal(layout: object) {
42
-    return this.nativeCommandsModule.showModal(layout);
41
+  showModal(commandId: string, layout: object) {
42
+    return this.nativeCommandsModule.showModal(commandId, layout);
43 43
   }
44 44
 
45
-  dismissModal(componentId: string) {
46
-    return this.nativeCommandsModule.dismissModal(componentId);
45
+  dismissModal(commandId: string, componentId: string) {
46
+    return this.nativeCommandsModule.dismissModal(commandId, componentId);
47 47
   }
48 48
 
49
-  dismissAllModals() {
50
-    return this.nativeCommandsModule.dismissAllModals();
49
+  dismissAllModals(commandId: string) {
50
+    return this.nativeCommandsModule.dismissAllModals(commandId);
51 51
   }
52 52
 
53
-  showOverlay(layout: object) {
54
-    return this.nativeCommandsModule.showOverlay(layout);
53
+  showOverlay(commandId: string, layout: object) {
54
+    return this.nativeCommandsModule.showOverlay(commandId, layout);
55 55
   }
56 56
 
57
-  dismissOverlay(componentId: string) {
58
-    return this.nativeCommandsModule.dismissOverlay(componentId);
57
+  dismissOverlay(commandId: string, componentId: string) {
58
+    return this.nativeCommandsModule.dismissOverlay(commandId, componentId);
59 59
   }
60 60
 }

+ 30
- 29
lib/src/commands/Commands.test.ts Näytä tiedosto

@@ -22,7 +22,8 @@ describe('Commands', () => {
22 22
       mockCommandsSender,
23 23
       new LayoutTreeParser(),
24 24
       new LayoutTreeCrawler(new UniqueIdProvider(), store),
25
-      commandsObserver
25
+      commandsObserver,
26
+      new UniqueIdProvider()
26 27
     );
27 28
   });
28 29
 
@@ -34,7 +35,7 @@ describe('Commands', () => {
34 35
         }
35 36
       });
36 37
       expect(mockCommandsSender.setRoot).toHaveBeenCalledTimes(1);
37
-      expect(mockCommandsSender.setRoot).toHaveBeenCalledWith({
38
+      expect(mockCommandsSender.setRoot).toHaveBeenCalledWith('setRoot+UNIQUE_ID', {
38 39
         type: 'Component',
39 40
         id: 'Component+UNIQUE_ID',
40 41
         children: [],
@@ -48,7 +49,7 @@ describe('Commands', () => {
48 49
     it('deep clones input to avoid mutation errors', () => {
49 50
       const obj = {};
50 51
       uut.setRoot({ component: { name: 'bla', inner: obj } });
51
-      expect(mockCommandsSender.setRoot.mock.calls[0][0].data.inner).not.toBe(obj);
52
+      expect(mockCommandsSender.setRoot.mock.calls[0][1].data.inner).not.toBe(obj);
52 53
     });
53 54
 
54 55
     it('passProps into components', () => {
@@ -98,7 +99,7 @@ describe('Commands', () => {
98 99
         }
99 100
       });
100 101
       expect(mockCommandsSender.showModal).toHaveBeenCalledTimes(1);
101
-      expect(mockCommandsSender.showModal).toHaveBeenCalledWith({
102
+      expect(mockCommandsSender.showModal).toHaveBeenCalledWith('showModal+UNIQUE_ID', {
102 103
         type: 'Component',
103 104
         id: 'Component+UNIQUE_ID',
104 105
         data: {
@@ -112,7 +113,7 @@ describe('Commands', () => {
112 113
     it('deep clones input to avoid mutation errors', () => {
113 114
       const obj = {};
114 115
       uut.showModal({ component: { name: 'name', inner: obj } });
115
-      expect(mockCommandsSender.showModal.mock.calls[0][0].data.inner).not.toBe(obj);
116
+      expect(mockCommandsSender.showModal.mock.calls[0][1].data.inner).not.toBe(obj);
116 117
     });
117 118
 
118 119
     it('passProps into components', () => {
@@ -138,7 +139,7 @@ describe('Commands', () => {
138 139
     it('sends command to native', () => {
139 140
       uut.dismissModal('myUniqueId');
140 141
       expect(mockCommandsSender.dismissModal).toHaveBeenCalledTimes(1);
141
-      expect(mockCommandsSender.dismissModal).toHaveBeenCalledWith('myUniqueId');
142
+      expect(mockCommandsSender.dismissModal).toHaveBeenCalledWith('dismissModal+UNIQUE_ID', 'myUniqueId');
142 143
     });
143 144
 
144 145
     it('returns a promise with the id', async () => {
@@ -152,7 +153,7 @@ describe('Commands', () => {
152 153
     it('sends command to native', () => {
153 154
       uut.dismissAllModals();
154 155
       expect(mockCommandsSender.dismissAllModals).toHaveBeenCalledTimes(1);
155
-      expect(mockCommandsSender.dismissAllModals).toHaveBeenCalledWith();
156
+      expect(mockCommandsSender.dismissAllModals).toHaveBeenCalledWith('dismissAllModals+UNIQUE_ID');
156 157
     });
157 158
 
158 159
     it('returns a promise with the id', async () => {
@@ -166,7 +167,7 @@ describe('Commands', () => {
166 167
     it('deep clones input to avoid mutation errors', () => {
167 168
       const obj = {};
168 169
       uut.push('theComponentId', { component: { name: 'name', passProps: { foo: obj } } });
169
-      expect(mockCommandsSender.push.mock.calls[0][1].data.passProps.foo).not.toBe(obj);
170
+      expect(mockCommandsSender.push.mock.calls[0][2].data.passProps.foo).not.toBe(obj);
170 171
     });
171 172
 
172 173
     it('resolves with the parsed layout', async () => {
@@ -178,7 +179,7 @@ describe('Commands', () => {
178 179
     it('parses into correct layout node and sends to native', () => {
179 180
       uut.push('theComponentId', { component: { name: 'com.example.MyScreen' } });
180 181
       expect(mockCommandsSender.push).toHaveBeenCalledTimes(1);
181
-      expect(mockCommandsSender.push).toHaveBeenCalledWith('theComponentId', {
182
+      expect(mockCommandsSender.push).toHaveBeenCalledWith('push+UNIQUE_ID', 'theComponentId', {
182 183
         type: 'Component',
183 184
         id: 'Component+UNIQUE_ID',
184 185
         data: {
@@ -194,7 +195,7 @@ describe('Commands', () => {
194 195
     it('pops a component, passing componentId', () => {
195 196
       uut.pop('theComponentId', {});
196 197
       expect(mockCommandsSender.pop).toHaveBeenCalledTimes(1);
197
-      expect(mockCommandsSender.pop).toHaveBeenCalledWith('theComponentId', {});
198
+      expect(mockCommandsSender.pop).toHaveBeenCalledWith('pop+UNIQUE_ID', 'theComponentId', {});
198 199
     });
199 200
     it('pops a component, passing componentId and options', () => {
200 201
       const options = {
@@ -207,7 +208,7 @@ describe('Commands', () => {
207 208
       };
208 209
       uut.pop('theComponentId', options);
209 210
       expect(mockCommandsSender.pop).toHaveBeenCalledTimes(1);
210
-      expect(mockCommandsSender.pop).toHaveBeenCalledWith('theComponentId', options);
211
+      expect(mockCommandsSender.pop).toHaveBeenCalledWith('pop+UNIQUE_ID', 'theComponentId', options);
211 212
     });
212 213
 
213 214
     it('pop returns a promise that resolves to componentId', async () => {
@@ -221,7 +222,7 @@ describe('Commands', () => {
221 222
     it('pops all components until the passed Id is top', () => {
222 223
       uut.popTo('theComponentId');
223 224
       expect(mockCommandsSender.popTo).toHaveBeenCalledTimes(1);
224
-      expect(mockCommandsSender.popTo).toHaveBeenCalledWith('theComponentId');
225
+      expect(mockCommandsSender.popTo).toHaveBeenCalledWith('popTo+UNIQUE_ID', 'theComponentId');
225 226
     });
226 227
 
227 228
     it('returns a promise that resolves to targetId', async () => {
@@ -235,7 +236,7 @@ describe('Commands', () => {
235 236
     it('pops all components to root', () => {
236 237
       uut.popToRoot('theComponentId');
237 238
       expect(mockCommandsSender.popToRoot).toHaveBeenCalledTimes(1);
238
-      expect(mockCommandsSender.popToRoot).toHaveBeenCalledWith('theComponentId');
239
+      expect(mockCommandsSender.popToRoot).toHaveBeenCalledWith('popToRoot+UNIQUE_ID', 'theComponentId');
239 240
     });
240 241
 
241 242
     it('returns a promise that resolves to targetId', async () => {
@@ -249,7 +250,7 @@ describe('Commands', () => {
249 250
     it('parses into correct layout node and sends to native', () => {
250 251
       uut.setStackRoot('theComponentId', { component: { name: 'com.example.MyScreen' } });
251 252
       expect(mockCommandsSender.setStackRoot).toHaveBeenCalledTimes(1);
252
-      expect(mockCommandsSender.setStackRoot).toHaveBeenCalledWith('theComponentId', {
253
+      expect(mockCommandsSender.setStackRoot).toHaveBeenCalledWith('setStackRoot+UNIQUE_ID', 'theComponentId', {
253 254
         type: 'Component',
254 255
         id: 'Component+UNIQUE_ID',
255 256
         data: {
@@ -269,7 +270,7 @@ describe('Commands', () => {
269 270
         }
270 271
       });
271 272
       expect(mockCommandsSender.showOverlay).toHaveBeenCalledTimes(1);
272
-      expect(mockCommandsSender.showOverlay).toHaveBeenCalledWith({
273
+      expect(mockCommandsSender.showOverlay).toHaveBeenCalledWith('showOverlay+UNIQUE_ID', {
273 274
         type: 'Component',
274 275
         id: 'Component+UNIQUE_ID',
275 276
         data: {
@@ -283,7 +284,7 @@ describe('Commands', () => {
283 284
     it('deep clones input to avoid mutation errors', () => {
284 285
       const obj = {};
285 286
       uut.showOverlay({ component: { name: 'name', inner: obj } });
286
-      expect(mockCommandsSender.showOverlay.mock.calls[0][0].data.inner).not.toBe(obj);
287
+      expect(mockCommandsSender.showOverlay.mock.calls[0][1].data.inner).not.toBe(obj);
287 288
     });
288 289
 
289 290
     it('resolves with the component id', async () => {
@@ -304,7 +305,7 @@ describe('Commands', () => {
304 305
     it('send command to native with componentId', () => {
305 306
       uut.dismissOverlay('Component1');
306 307
       expect(mockCommandsSender.dismissOverlay).toHaveBeenCalledTimes(1);
307
-      expect(mockCommandsSender.dismissOverlay).toHaveBeenCalledWith('Component1');
308
+      expect(mockCommandsSender.dismissOverlay).toHaveBeenCalledWith('dismissOverlay+UNIQUE_ID', 'Component1');
308 309
     });
309 310
   });
310 311
 
@@ -316,7 +317,7 @@ describe('Commands', () => {
316 317
       const mockParser = { parse: () => 'parsed' };
317 318
       const mockCrawler = { crawl: (x) => x, processOptions: (x) => x };
318 319
       commandsObserver.register(cb);
319
-      uut = new Commands(mockCommandsSender, mockParser, mockCrawler, commandsObserver);
320
+      uut = new Commands(mockCommandsSender, mockParser, mockCrawler, commandsObserver, new UniqueIdProvider());
320 321
     });
321 322
 
322 323
     function getAllMethodsOfUut() {
@@ -373,19 +374,19 @@ describe('Commands', () => {
373 374
         dismissOverlay: ['id'],
374 375
       };
375 376
       const paramsForMethodName = {
376
-        setRoot: { layout: 'parsed' },
377
+        setRoot: { commandId: 'setRoot+UNIQUE_ID', layout: 'parsed' },
377 378
         setDefaultOptions: { options: {} },
378 379
         mergeOptions: { componentId: 'id', options: {} },
379
-        showModal: { layout: 'parsed' },
380
-        dismissModal: { componentId: 'id' },
381
-        dismissAllModals: {},
382
-        push: { componentId: 'id', layout: 'parsed' },
383
-        pop: { componentId: 'id', options: {} },
384
-        popTo: { componentId: 'id' },
385
-        popToRoot: { componentId: 'id' },
386
-        setStackRoot: { componentId: 'id', layout: 'parsed' },
387
-        showOverlay: { layout: 'parsed' },
388
-        dismissOverlay: { componentId: 'id' },
380
+        showModal: { commandId: 'showModal+UNIQUE_ID', layout: 'parsed' },
381
+        dismissModal: { commandId: 'dismissModal+UNIQUE_ID', componentId: 'id' },
382
+        dismissAllModals: {commandId: 'dismissAllModals+UNIQUE_ID'},
383
+        push: { commandId: 'push+UNIQUE_ID', componentId: 'id', layout: 'parsed' },
384
+        pop: { commandId: 'pop+UNIQUE_ID', componentId: 'id', options: {} },
385
+        popTo: { commandId: 'popTo+UNIQUE_ID', componentId: 'id' },
386
+        popToRoot: { commandId: 'popToRoot+UNIQUE_ID', componentId: 'id' },
387
+        setStackRoot: { commandId: 'setStackRoot+UNIQUE_ID', componentId: 'id', layout: 'parsed' },
388
+        showOverlay: { commandId: 'showOverlay+UNIQUE_ID', layout: 'parsed' },
389
+        dismissOverlay: { commandId: 'dismissOverlay+UNIQUE_ID', componentId: 'id' },
389 390
       };
390 391
       _.forEach(getAllMethodsOfUut(), (m) => {
391 392
         it(`for ${m}`, () => {

+ 36
- 23
lib/src/commands/Commands.ts Näytä tiedosto

@@ -1,13 +1,15 @@
1 1
 import * as _ from 'lodash';
2 2
 import { CommandsObserver } from '../events/CommandsObserver';
3 3
 import { NativeCommandsSender } from '../adapters/NativeCommandsSender';
4
+import { UniqueIdProvider } from '../adapters/UniqueIdProvider';
4 5
 
5 6
 export class Commands {
6 7
   constructor(
7 8
     private readonly nativeCommandsSender: NativeCommandsSender,
8 9
     private readonly layoutTreeParser,
9 10
     private readonly layoutTreeCrawler,
10
-    private readonly commandsObserver: CommandsObserver) {
11
+    private readonly commandsObserver: CommandsObserver,
12
+    private readonly uniqueIdProvider: UniqueIdProvider) {
11 13
   }
12 14
 
13 15
   public setRoot(simpleApi) {
@@ -15,8 +17,9 @@ export class Commands {
15 17
     const layout = this.layoutTreeParser.parse(input);
16 18
     this.layoutTreeCrawler.crawl(layout);
17 19
 
18
-    const result = this.nativeCommandsSender.setRoot(layout);
19
-    this.commandsObserver.notify('setRoot', { layout });
20
+    const commandId = this.uniqueIdProvider.generate('setRoot');
21
+    const result = this.nativeCommandsSender.setRoot(commandId, layout);
22
+    this.commandsObserver.notify('setRoot', { commandId, layout });
20 23
     return result;
21 24
   }
22 25
 
@@ -41,20 +44,23 @@ export class Commands {
41 44
     const layout = this.layoutTreeParser.parse(input);
42 45
     this.layoutTreeCrawler.crawl(layout);
43 46
 
44
-    const result = this.nativeCommandsSender.showModal(layout);
45
-    this.commandsObserver.notify('showModal', { layout });
47
+    const commandId = this.uniqueIdProvider.generate('showModal');
48
+    const result = this.nativeCommandsSender.showModal(commandId, layout);
49
+    this.commandsObserver.notify('showModal', { commandId, layout });
46 50
     return result;
47 51
   }
48 52
 
49 53
   public dismissModal(componentId) {
50
-    const result = this.nativeCommandsSender.dismissModal(componentId);
51
-    this.commandsObserver.notify('dismissModal', { componentId });
54
+    const commandId = this.uniqueIdProvider.generate('dismissModal');
55
+    const result = this.nativeCommandsSender.dismissModal(commandId, componentId);
56
+    this.commandsObserver.notify('dismissModal', { commandId, componentId });
52 57
     return result;
53 58
   }
54 59
 
55 60
   public dismissAllModals() {
56
-    const result = this.nativeCommandsSender.dismissAllModals();
57
-    this.commandsObserver.notify('dismissAllModals', {});
61
+    const commandId = this.uniqueIdProvider.generate('dismissAllModals');
62
+    const result = this.nativeCommandsSender.dismissAllModals(commandId);
63
+    this.commandsObserver.notify('dismissAllModals', { commandId });
58 64
     return result;
59 65
   }
60 66
 
@@ -64,26 +70,30 @@ export class Commands {
64 70
     const layout = this.layoutTreeParser.parse(input);
65 71
     this.layoutTreeCrawler.crawl(layout);
66 72
 
67
-    const result = this.nativeCommandsSender.push(componentId, layout);
68
-    this.commandsObserver.notify('push', { componentId, layout });
73
+    const commandId = this.uniqueIdProvider.generate('push');
74
+    const result = this.nativeCommandsSender.push(commandId, componentId, layout);
75
+    this.commandsObserver.notify('push', { commandId, componentId, layout });
69 76
     return result;
70 77
   }
71 78
 
72 79
   public pop(componentId, options) {
73
-    const result = this.nativeCommandsSender.pop(componentId, options);
74
-    this.commandsObserver.notify('pop', { componentId, options });
80
+    const commandId = this.uniqueIdProvider.generate('pop');
81
+    const result = this.nativeCommandsSender.pop(commandId, componentId, options);
82
+    this.commandsObserver.notify('pop', { commandId, componentId, options });
75 83
     return result;
76 84
   }
77 85
 
78 86
   public popTo(componentId) {
79
-    const result = this.nativeCommandsSender.popTo(componentId);
80
-    this.commandsObserver.notify('popTo', { componentId });
87
+    const commandId = this.uniqueIdProvider.generate('popTo');
88
+    const result = this.nativeCommandsSender.popTo(commandId, componentId);
89
+    this.commandsObserver.notify('popTo', { commandId, componentId });
81 90
     return result;
82 91
   }
83 92
 
84 93
   public popToRoot(componentId) {
85
-    const result = this.nativeCommandsSender.popToRoot(componentId);
86
-    this.commandsObserver.notify('popToRoot', { componentId });
94
+    const commandId = this.uniqueIdProvider.generate('popToRoot');
95
+    const result = this.nativeCommandsSender.popToRoot(commandId, componentId);
96
+    this.commandsObserver.notify('popToRoot', { commandId, componentId });
87 97
     return result;
88 98
   }
89 99
 
@@ -93,8 +103,9 @@ export class Commands {
93 103
     const layout = this.layoutTreeParser.parse(input);
94 104
     this.layoutTreeCrawler.crawl(layout);
95 105
 
96
-    const result = this.nativeCommandsSender.setStackRoot(componentId, layout);
97
-    this.commandsObserver.notify('setStackRoot', { componentId, layout });
106
+    const commandId = this.uniqueIdProvider.generate('setStackRoot');
107
+    const result = this.nativeCommandsSender.setStackRoot(commandId, componentId, layout);
108
+    this.commandsObserver.notify('setStackRoot', { commandId, componentId, layout });
98 109
     return result;
99 110
   }
100 111
 
@@ -104,14 +115,16 @@ export class Commands {
104 115
     const layout = this.layoutTreeParser.parse(input);
105 116
     this.layoutTreeCrawler.crawl(layout);
106 117
 
107
-    const result = this.nativeCommandsSender.showOverlay(layout);
108
-    this.commandsObserver.notify('showOverlay', { layout });
118
+    const commandId = this.uniqueIdProvider.generate('showOverlay');
119
+    const result = this.nativeCommandsSender.showOverlay(commandId, layout);
120
+    this.commandsObserver.notify('showOverlay', { commandId, layout });
109 121
     return result;
110 122
   }
111 123
 
112 124
   public dismissOverlay(componentId) {
113
-    const result = this.nativeCommandsSender.dismissOverlay(componentId);
114
-    this.commandsObserver.notify('dismissOverlay', { componentId });
125
+    const commandId = this.uniqueIdProvider.generate('dismissOverlay');
126
+    const result = this.nativeCommandsSender.dismissOverlay(commandId, componentId);
127
+    this.commandsObserver.notify('dismissOverlay', { commandId, componentId });
115 128
     return result;
116 129
   }
117 130
 }

+ 2
- 2
playground/src/screens/OptionsScreen.js Näytä tiedosto

@@ -238,8 +238,8 @@ class OptionsScreen extends Component {
238 238
     });
239 239
   }
240 240
 
241
-  onClickShowOverlay = (interceptTouchOutside) => {
242
-    Navigation.showOverlay({
241
+  onClickShowOverlay = async (interceptTouchOutside) => {
242
+    await Navigation.showOverlay({
243 243
       component: {
244 244
         name: 'navigation.playground.CustomDialog',
245 245
         options: {