Browse Source

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 years ago
parent
commit
07050db9f9
No account linked to committer's email address
29 changed files with 396 additions and 239 deletions
  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 View File

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

+ 5
- 2
lib/android/app/src/main/java/com/reactnativenavigation/presentation/BottomTabsOptionsPresenter.java View File

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

+ 11
- 3
lib/android/app/src/main/java/com/reactnativenavigation/presentation/OverlayManager.java View File

2
 
2
 
3
 import android.view.ViewGroup;
3
 import android.view.ViewGroup;
4
 
4
 
5
+import com.reactnativenavigation.utils.CommandListener;
5
 import com.reactnativenavigation.viewcontrollers.ViewController;
6
 import com.reactnativenavigation.viewcontrollers.ViewController;
6
 
7
 
7
 import java.util.HashMap;
8
 import java.util.HashMap;
9
 public class OverlayManager {
10
 public class OverlayManager {
10
     private final HashMap<String, ViewController> overlayRegistry = new HashMap<>();
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
         overlayRegistry.put(overlay.getId(), overlay);
14
         overlayRegistry.put(overlay.getId(), overlay);
14
         root.addView(overlay.getView());
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
     public void destroy() {
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 View File

7
 
7
 
8
 import static com.facebook.react.modules.core.DeviceEventManagerModule.RCTDeviceEventEmitter;
8
 import static com.facebook.react.modules.core.DeviceEventManagerModule.RCTDeviceEventEmitter;
9
 
9
 
10
-public class NavigationEvent {
10
+public class EventEmitter {
11
 	private static final String onAppLaunched = "RNN.appLaunched";
11
 	private static final String onAppLaunched = "RNN.appLaunched";
12
 	private static final String componentDidAppear = "RNN.componentDidAppear";
12
 	private static final String componentDidAppear = "RNN.componentDidAppear";
13
 	private static final String componentDidDisappear = "RNN.componentDidDisappear";
13
 	private static final String componentDidDisappear = "RNN.componentDidDisappear";
14
 	private static final String nativeEvent = "RNN.nativeEvent";
14
 	private static final String nativeEvent = "RNN.nativeEvent";
15
+    private static final String commandCompleted = "RNN.commandCompleted";
15
     private static final String buttonPressedEvent = "buttonPressed";
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
 		this.emitter = reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class);
21
 		this.emitter = reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class);
21
 	}
22
 	}
22
 
23
 
25
 	}
26
 	}
26
 
27
 
27
 	public void componentDidDisappear(String id, String componentName) {
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
 	public void componentDidAppear(String id, String componentName) {
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
 		WritableMap params = Arguments.createMap();
43
 		WritableMap params = Arguments.createMap();
44
 		params.putString("componentId", id);
44
 		params.putString("componentId", id);
45
 		params.putString("buttonId", buttonId);
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
 	private void emit(String eventName) {
67
 	private void emit(String eventName) {
55
 		emit(eventName, Arguments.createMap());
68
 		emit(eventName, Arguments.createMap());
56
 	}
69
 	}

+ 34
- 45
lib/android/app/src/main/java/com/reactnativenavigation/react/NavigationModule.java View File

14
 import com.reactnativenavigation.parse.LayoutNode;
14
 import com.reactnativenavigation.parse.LayoutNode;
15
 import com.reactnativenavigation.parse.Options;
15
 import com.reactnativenavigation.parse.Options;
16
 import com.reactnativenavigation.parse.parsers.LayoutNodeParser;
16
 import com.reactnativenavigation.parse.parsers.LayoutNodeParser;
17
+import com.reactnativenavigation.utils.NativeCommandListener;
18
+import com.reactnativenavigation.utils.Now;
17
 import com.reactnativenavigation.utils.TypefaceLoader;
19
 import com.reactnativenavigation.utils.TypefaceLoader;
18
 import com.reactnativenavigation.utils.UiThread;
20
 import com.reactnativenavigation.utils.UiThread;
19
 import com.reactnativenavigation.viewcontrollers.Navigator;
21
 import com.reactnativenavigation.viewcontrollers.Navigator;
26
 
28
 
27
 public class NavigationModule extends ReactContextBaseJavaModule {
29
 public class NavigationModule extends ReactContextBaseJavaModule {
28
 	private static final String NAME = "RNNBridgeModule";
30
 	private static final String NAME = "RNNBridgeModule";
31
+
32
+    private final Now now = new Now();
29
 	private final ReactInstanceManager reactInstanceManager;
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
 		super(reactContext);
38
 		super(reactContext);
34
 		this.reactInstanceManager = reactInstanceManager;
39
 		this.reactInstanceManager = reactInstanceManager;
35
-	}
40
+		reactInstanceManager.addReactInstanceEventListener(context -> eventEmitter = new EventEmitter(context));
41
+    }
36
 
42
 
37
 	@Override
43
 	@Override
38
 	public String getName() {
44
 	public String getName() {
40
 	}
46
 	}
41
 
47
 
42
 	@ReactMethod
48
 	@ReactMethod
43
-	public void setRoot(final ReadableMap rawLayoutTree, final Promise promise) {
49
+	public void setRoot(String commandId, ReadableMap rawLayoutTree, Promise promise) {
44
 		final LayoutNode layoutTree = LayoutNodeParser.parse(new JSONObject(rawLayoutTree.toHashMap()));
50
 		final LayoutNode layoutTree = LayoutNodeParser.parse(new JSONObject(rawLayoutTree.toHashMap()));
45
 		handle(() -> {
51
 		handle(() -> {
46
             final ViewController viewController = newLayoutFactory().create(layoutTree);
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
 	@ReactMethod
57
 	@ReactMethod
52
-	public void setDefaultOptions(final ReadableMap options) {
58
+	public void setDefaultOptions(ReadableMap options) {
53
         final Options defaultOptions = Options.parse(new TypefaceLoader(activity()), new JSONObject(options.toHashMap()));
59
         final Options defaultOptions = Options.parse(new TypefaceLoader(activity()), new JSONObject(options.toHashMap()));
54
         handle(() -> navigator().setDefaultOptions(defaultOptions));
60
         handle(() -> navigator().setDefaultOptions(defaultOptions));
55
     }
61
     }
56
 
62
 
57
 	@ReactMethod
63
 	@ReactMethod
58
-	public void mergeOptions(final String onComponentId, final ReadableMap options) {
64
+	public void mergeOptions(String onComponentId, ReadableMap options) {
59
 		final Options navOptions = Options.parse(new TypefaceLoader(activity()), new JSONObject(options.toHashMap()));
65
 		final Options navOptions = Options.parse(new TypefaceLoader(activity()), new JSONObject(options.toHashMap()));
60
 		handle(() -> navigator().mergeOptions(onComponentId, navOptions));
66
 		handle(() -> navigator().mergeOptions(onComponentId, navOptions));
61
 	}
67
 	}
62
 
68
 
63
 	@ReactMethod
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
 		final LayoutNode layoutTree = LayoutNodeParser.parse(new JSONObject(rawLayoutTree.toHashMap()));
71
 		final LayoutNode layoutTree = LayoutNodeParser.parse(new JSONObject(rawLayoutTree.toHashMap()));
66
 		handle(() -> {
72
 		handle(() -> {
67
             final ViewController viewController = newLayoutFactory().create(layoutTree);
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
     @ReactMethod
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
         final LayoutNode layoutTree = LayoutNodeParser.parse(new JSONObject(rawLayoutTree.toHashMap()));
80
         final LayoutNode layoutTree = LayoutNodeParser.parse(new JSONObject(rawLayoutTree.toHashMap()));
75
         handle(() -> {
81
         handle(() -> {
76
             final ViewController viewController = newLayoutFactory().create(layoutTree);
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
 	@ReactMethod
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
 	@ReactMethod
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
 	@ReactMethod
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
 	@ReactMethod
102
 	@ReactMethod
97
-	public void showModal(final ReadableMap rawLayoutTree, final Promise promise) {
103
+	public void showModal(String commandId, ReadableMap rawLayoutTree, Promise promise) {
98
 		final LayoutNode layoutTree = LayoutNodeParser.parse(new JSONObject(rawLayoutTree.toHashMap()));
104
 		final LayoutNode layoutTree = LayoutNodeParser.parse(new JSONObject(rawLayoutTree.toHashMap()));
99
 		handle(() -> {
105
 		handle(() -> {
100
             final ViewController viewController = newLayoutFactory().create(layoutTree);
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
 	@ReactMethod
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
 	@ReactMethod
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
 	@ReactMethod
121
 	@ReactMethod
116
-	public void showOverlay(final ReadableMap rawLayoutTree) {
122
+	public void showOverlay(String commandId, ReadableMap rawLayoutTree, Promise promise) {
117
         final LayoutNode layoutTree = LayoutNodeParser.parse(new JSONObject(rawLayoutTree.toHashMap()));
123
         final LayoutNode layoutTree = LayoutNodeParser.parse(new JSONObject(rawLayoutTree.toHashMap()));
118
         handle(() -> {
124
         handle(() -> {
119
             final ViewController viewController = newLayoutFactory().create(layoutTree);
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
 	@ReactMethod
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
 	private Navigator navigator() {
135
 	private Navigator navigator() {
134
 	private LayoutFactory newLayoutFactory() {
140
 	private LayoutFactory newLayoutFactory() {
135
 		return new LayoutFactory(activity(),
141
 		return new LayoutFactory(activity(),
136
                 reactInstanceManager,
142
                 reactInstanceManager,
143
+                eventEmitter,
137
                 externalComponentCreator(),
144
                 externalComponentCreator(),
138
                 navigator().getDefaultOptions()
145
                 navigator().getDefaultOptions()
139
         );
146
         );
151
     private NavigationActivity activity() {
158
     private NavigationActivity activity() {
152
         return (NavigationActivity) getCurrentActivity();
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 View File

52
 
52
 
53
 	private void emitAppLaunched(ReactContext context) {
53
 	private void emitAppLaunched(ReactContext context) {
54
 		waitingForAppLaunchEvent = false;
54
 		waitingForAppLaunchEvent = false;
55
-		new NavigationEvent(context).appLaunched();
55
+		new EventEmitter(context).appLaunched();
56
 	}
56
 	}
57
 
57
 
58
 	private boolean shouldCreateContext() {
58
 	private boolean shouldCreateContext() {

+ 3
- 3
lib/android/app/src/main/java/com/reactnativenavigation/react/ReactView.java View File

60
 
60
 
61
 	@Override
61
 	@Override
62
 	public void sendComponentStart() {
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
 	@Override
66
 	@Override
67
 	public void sendComponentStop() {
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
     @Override
71
     @Override
72
 	public void sendOnNavigationButtonPressed(String buttonId) {
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
     @Override
76
     @Override

+ 7
- 0
lib/android/app/src/main/java/com/reactnativenavigation/utils/CommandListener.java View File

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 View File

1
 package com.reactnativenavigation.utils;
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
     @Override
4
     @Override
7
     public void onSuccess(String childId) {
5
     public void onSuccess(String childId) {
8
 
6
 
13
 
11
 
14
     }
12
     }
15
 }
13
 }
16
-

+ 31
- 0
lib/android/app/src/main/java/com/reactnativenavigation/utils/NativeCommandListener.java View File

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 View File

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 View File

8
 import android.view.ViewGroup;
8
 import android.view.ViewGroup;
9
 import android.widget.FrameLayout;
9
 import android.widget.FrameLayout;
10
 
10
 
11
-import com.facebook.react.bridge.Promise;
12
 import com.reactnativenavigation.anim.ModalAnimator;
11
 import com.reactnativenavigation.anim.ModalAnimator;
13
 import com.reactnativenavigation.anim.NavigationAnimator;
12
 import com.reactnativenavigation.anim.NavigationAnimator;
14
 import com.reactnativenavigation.parse.Options;
13
 import com.reactnativenavigation.parse.Options;
15
 import com.reactnativenavigation.presentation.NavigationOptionsListener;
14
 import com.reactnativenavigation.presentation.NavigationOptionsListener;
16
 import com.reactnativenavigation.presentation.OverlayManager;
15
 import com.reactnativenavigation.presentation.OverlayManager;
17
 import com.reactnativenavigation.react.JsDevReloadHandler;
16
 import com.reactnativenavigation.react.JsDevReloadHandler;
17
+import com.reactnativenavigation.utils.CommandListener;
18
 import com.reactnativenavigation.utils.CommandListenerAdapter;
18
 import com.reactnativenavigation.utils.CommandListenerAdapter;
19
 import com.reactnativenavigation.utils.CompatUtils;
19
 import com.reactnativenavigation.utils.CompatUtils;
20
+import com.reactnativenavigation.utils.NativeCommandListener;
20
 import com.reactnativenavigation.viewcontrollers.modal.ModalPresenter;
21
 import com.reactnativenavigation.viewcontrollers.modal.ModalPresenter;
21
 import com.reactnativenavigation.viewcontrollers.modal.ModalStack;
22
 import com.reactnativenavigation.viewcontrollers.modal.ModalStack;
22
 
23
 
25
 
26
 
26
 public class Navigator extends ParentController implements JsDevReloadHandler.ReloadListener {
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
     private final ModalStack modalStack;
29
     private final ModalStack modalStack;
35
     private ViewController root;
30
     private ViewController root;
36
     private FrameLayout rootLayout;
31
     private FrameLayout rootLayout;
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
         destroyRoot();
96
         destroyRoot();
102
         root = viewController;
97
         root = viewController;
103
         contentLayout.addView(viewController.getView());
98
         contentLayout.addView(viewController.getView());
106
                     .animateStartApp(viewController.getView(), new AnimatorListenerAdapter() {
101
                     .animateStartApp(viewController.getView(), new AnimatorListenerAdapter() {
107
                         @Override
102
                         @Override
108
                         public void onAnimationEnd(Animator animation) {
103
                         public void onAnimationEnd(Animator animation) {
109
-                            promise.resolve(viewController.getId());
104
+                            commandListener.onSuccess(viewController.getId());
110
                         }
105
                         }
111
                     });
106
                     });
112
         } else {
107
         } else {
113
-            promise.resolve(viewController.getId());
108
+            commandListener.onSuccess(viewController.getId());
114
         }
109
         }
115
     }
110
     }
116
 
111
 
196
         modalStack.dismissAllModals(listener, root);
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
     @Nullable
202
     @Nullable

+ 1
- 1
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/StackController.java View File

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

+ 2
- 1
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/ViewController.java View File

12
 
12
 
13
 import com.reactnativenavigation.parse.Options;
13
 import com.reactnativenavigation.parse.Options;
14
 import com.reactnativenavigation.presentation.FabOptionsPresenter;
14
 import com.reactnativenavigation.presentation.FabOptionsPresenter;
15
+import com.reactnativenavigation.utils.CommandListener;
15
 import com.reactnativenavigation.utils.StringUtils;
16
 import com.reactnativenavigation.utils.StringUtils;
16
 import com.reactnativenavigation.utils.Task;
17
 import com.reactnativenavigation.utils.Task;
17
 import com.reactnativenavigation.utils.UiUtils;
18
 import com.reactnativenavigation.utils.UiUtils;
62
         getView();
63
         getView();
63
     }
64
     }
64
 
65
 
65
-    public boolean handleBack(Navigator.CommandListener listener) {
66
+    public boolean handleBack(CommandListener listener) {
66
         return false;
67
         return false;
67
     }
68
     }
68
 
69
 

+ 19
- 11
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/bottomtabs/BottomTabsController.java View File

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

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 View File

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

+ 1
- 1
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/modal/ModalStack.java View File

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

+ 58
- 0
lib/android/app/src/test/java/com/reactnativenavigation/utils/NativeCommandListenerTest.java View File

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 View File

13
 import com.reactnativenavigation.parse.Options;
13
 import com.reactnativenavigation.parse.Options;
14
 import com.reactnativenavigation.parse.params.Color;
14
 import com.reactnativenavigation.parse.params.Color;
15
 import com.reactnativenavigation.parse.params.Number;
15
 import com.reactnativenavigation.parse.params.Number;
16
+import com.reactnativenavigation.react.EventEmitter;
16
 import com.reactnativenavigation.utils.CommandListenerAdapter;
17
 import com.reactnativenavigation.utils.CommandListenerAdapter;
17
 import com.reactnativenavigation.utils.ImageLoader;
18
 import com.reactnativenavigation.utils.ImageLoader;
18
 import com.reactnativenavigation.utils.OptionHelper;
19
 import com.reactnativenavigation.utils.OptionHelper;
24
 
25
 
25
 import org.junit.Test;
26
 import org.junit.Test;
26
 import org.mockito.ArgumentCaptor;
27
 import org.mockito.ArgumentCaptor;
28
+import org.mockito.Mockito;
27
 
29
 
28
 import java.util.Arrays;
30
 import java.util.Arrays;
29
 import java.util.Collections;
31
 import java.util.Collections;
47
     private ViewController child5;
49
     private ViewController child5;
48
     private Options tabOptions = OptionHelper.createBottomTabOptions();
50
     private Options tabOptions = OptionHelper.createBottomTabOptions();
49
     private ImageLoader imageLoaderMock = ImageLoaderMock.mock();
51
     private ImageLoader imageLoaderMock = ImageLoaderMock.mock();
52
+    private EventEmitter eventEmitter;
50
 
53
 
51
     @Override
54
     @Override
52
     public void beforeEach() {
55
     public void beforeEach() {
53
         super.beforeEach();
56
         super.beforeEach();
54
         activity = newActivity();
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
         child1 = spy(new SimpleViewController(activity, "child1", tabOptions));
60
         child1 = spy(new SimpleViewController(activity, "child1", tabOptions));
57
         child2 = spy(new SimpleViewController(activity, "child2", tabOptions));
61
         child2 = spy(new SimpleViewController(activity, "child2", tabOptions));
58
         child3 = spy(new SimpleViewController(activity, "child3", tabOptions));
62
         child3 = spy(new SimpleViewController(activity, "child3", tabOptions));
91
     }
95
     }
92
 
96
 
93
     @Test
97
     @Test
94
-    public void selectTabAtIndex() {
98
+    public void onTabSelected() {
95
         uut.setTabs(createTabs());
99
         uut.setTabs(createTabs());
96
         assertThat(uut.getSelectedIndex()).isZero();
100
         assertThat(uut.getSelectedIndex()).isZero();
97
 
101
 
98
-        uut.selectTabAtIndex(3);
102
+        uut.onTabSelected(3, false);
99
 
103
 
100
         assertThat(uut.getSelectedIndex()).isEqualTo(3);
104
         assertThat(uut.getSelectedIndex()).isEqualTo(3);
101
         assertThat(((ViewController) ((List) uut.getChildControllers()).get(0)).getView().getParent()).isNull();
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
     @Test
121
     @Test
123
         uut.setTabs(tabs);
140
         uut.setTabs(tabs);
124
 
141
 
125
         assertThat(uut.handleBack(new CommandListenerAdapter())).isFalse();
142
         assertThat(uut.handleBack(new CommandListenerAdapter())).isFalse();
126
-        uut.selectTabAtIndex(2);
143
+        uut.selectTab(2);
127
         assertThat(uut.handleBack(new CommandListenerAdapter())).isTrue();
144
         assertThat(uut.handleBack(new CommandListenerAdapter())).isTrue();
128
 
145
 
129
         verify(spy, times(1)).handleBack(any());
146
         verify(spy, times(1)).handleBack(any());
157
         Options options = new Options();
174
         Options options = new Options();
158
         options.bottomTabsOptions.currentTabIndex = new Number(1);
175
         options.bottomTabsOptions.currentTabIndex = new Number(1);
159
         uut.mergeOptions(options);
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
     @Test
181
     @Test
164
     public void buttonPressInvokedOnCurrentTab() {
182
     public void buttonPressInvokedOnCurrentTab() {
165
         uut.setTabs(createTabs());
183
         uut.setTabs(createTabs());
166
         uut.ensureViewIsCreated();
184
         uut.ensureViewIsCreated();
167
-        uut.selectTabAtIndex(1);
185
+        uut.selectTab(1);
168
 
186
 
169
         uut.sendOnNavigationButtonPressed("btn1");
187
         uut.sendOnNavigationButtonPressed("btn1");
170
         verify(child2, times(1)).sendOnNavigationButtonPressed("btn1");
188
         verify(child2, times(1)).sendOnNavigationButtonPressed("btn1");

+ 36
- 36
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/NavigatorTest.java View File

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

+ 1
- 1
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/modal/ModalPresenterTest.java View File

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

+ 1
- 1
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/modal/ModalStackTest.java View File

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

+ 11
- 11
lib/ios/RNNBridgeModule.m View File

18
 
18
 
19
 #pragma mark - JS interface
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
 	[_commandsHandler setRoot:layout completion:^{
22
 	[_commandsHandler setRoot:layout completion:^{
23
 		resolve(layout);
23
 		resolve(layout);
24
 	}];
24
 	}];
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
 	[_commandsHandler push:componentId layout:layout completion:^{
40
 	[_commandsHandler push:componentId layout:layout completion:^{
41
 		resolve(componentId);
41
 		resolve(componentId);
42
 	} rejection:reject];
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
 	[_commandsHandler pop:componentId options:(NSDictionary*)options completion:^{
46
 	[_commandsHandler pop:componentId options:(NSDictionary*)options completion:^{
47
 		resolve(componentId);
47
 		resolve(componentId);
48
 	} rejection:reject];
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
 	[_commandsHandler setStackRoot:componentId layout:layout completion:^{
52
 	[_commandsHandler setStackRoot:componentId layout:layout completion:^{
53
 		resolve(componentId);
53
 		resolve(componentId);
54
 	} rejection:reject];
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
 	[_commandsHandler popTo:componentId completion:^{
58
 	[_commandsHandler popTo:componentId completion:^{
59
 		resolve(componentId);
59
 		resolve(componentId);
60
 	} rejection:reject];
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
 	[_commandsHandler popToRoot:componentId completion:^{
64
 	[_commandsHandler popToRoot:componentId completion:^{
65
 		resolve(componentId);
65
 		resolve(componentId);
66
 	} rejection:reject];
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
 	[_commandsHandler showModal:layout completion:^{
70
 	[_commandsHandler showModal:layout completion:^{
71
 		resolve(nil);
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
 	[_commandsHandler dismissModal:componentId completion:^{
76
 	[_commandsHandler dismissModal:componentId completion:^{
77
 		resolve(componentId);
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
 	[_commandsHandler dismissAllModalsWithCompletion:^{
82
 	[_commandsHandler dismissAllModalsWithCompletion:^{
83
 		resolve(nil);
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
 	[_commandsHandler showOverlay:layout completion:^{
88
 	[_commandsHandler showOverlay:layout completion:^{
89
 		resolve(layout[@"id"]);
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
 	[_commandsHandler dismissOverlay:componentId completion:^{
94
 	[_commandsHandler dismissOverlay:componentId completion:^{
95
 		resolve(@(1));
95
 		resolve(@(1));
96
 	}];
96
 	}];

+ 1
- 1
lib/src/Navigation.ts View File

38
     this.layoutTreeCrawler = new LayoutTreeCrawler(this.uniqueIdProvider, this.store);
38
     this.layoutTreeCrawler = new LayoutTreeCrawler(this.uniqueIdProvider, this.store);
39
     this.nativeCommandsSender = new NativeCommandsSender();
39
     this.nativeCommandsSender = new NativeCommandsSender();
40
     this.commandsObserver = new CommandsObserver();
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
     this.eventsRegistry = new EventsRegistry(this.nativeEventsReceiver, this.commandsObserver);
42
     this.eventsRegistry = new EventsRegistry(this.nativeEventsReceiver, this.commandsObserver);
43
     this.componentEventsObserver = new ComponentEventsObserver(this.eventsRegistry, this.store);
43
     this.componentEventsObserver = new ComponentEventsObserver(this.eventsRegistry, this.store);
44
 
44
 

+ 22
- 22
lib/src/adapters/NativeCommandsSender.ts View File

6
     this.nativeCommandsModule = NativeModules.RNNBridgeModule;
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
   setDefaultOptions(options: object) {
13
   setDefaultOptions(options: object) {
18
     return this.nativeCommandsModule.mergeOptions(componentId, options);
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 View File

22
       mockCommandsSender,
22
       mockCommandsSender,
23
       new LayoutTreeParser(),
23
       new LayoutTreeParser(),
24
       new LayoutTreeCrawler(new UniqueIdProvider(), store),
24
       new LayoutTreeCrawler(new UniqueIdProvider(), store),
25
-      commandsObserver
25
+      commandsObserver,
26
+      new UniqueIdProvider()
26
     );
27
     );
27
   });
28
   });
28
 
29
 
34
         }
35
         }
35
       });
36
       });
36
       expect(mockCommandsSender.setRoot).toHaveBeenCalledTimes(1);
37
       expect(mockCommandsSender.setRoot).toHaveBeenCalledTimes(1);
37
-      expect(mockCommandsSender.setRoot).toHaveBeenCalledWith({
38
+      expect(mockCommandsSender.setRoot).toHaveBeenCalledWith('setRoot+UNIQUE_ID', {
38
         type: 'Component',
39
         type: 'Component',
39
         id: 'Component+UNIQUE_ID',
40
         id: 'Component+UNIQUE_ID',
40
         children: [],
41
         children: [],
48
     it('deep clones input to avoid mutation errors', () => {
49
     it('deep clones input to avoid mutation errors', () => {
49
       const obj = {};
50
       const obj = {};
50
       uut.setRoot({ component: { name: 'bla', inner: obj } });
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
     it('passProps into components', () => {
55
     it('passProps into components', () => {
98
         }
99
         }
99
       });
100
       });
100
       expect(mockCommandsSender.showModal).toHaveBeenCalledTimes(1);
101
       expect(mockCommandsSender.showModal).toHaveBeenCalledTimes(1);
101
-      expect(mockCommandsSender.showModal).toHaveBeenCalledWith({
102
+      expect(mockCommandsSender.showModal).toHaveBeenCalledWith('showModal+UNIQUE_ID', {
102
         type: 'Component',
103
         type: 'Component',
103
         id: 'Component+UNIQUE_ID',
104
         id: 'Component+UNIQUE_ID',
104
         data: {
105
         data: {
112
     it('deep clones input to avoid mutation errors', () => {
113
     it('deep clones input to avoid mutation errors', () => {
113
       const obj = {};
114
       const obj = {};
114
       uut.showModal({ component: { name: 'name', inner: obj } });
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
     it('passProps into components', () => {
119
     it('passProps into components', () => {
138
     it('sends command to native', () => {
139
     it('sends command to native', () => {
139
       uut.dismissModal('myUniqueId');
140
       uut.dismissModal('myUniqueId');
140
       expect(mockCommandsSender.dismissModal).toHaveBeenCalledTimes(1);
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
     it('returns a promise with the id', async () => {
145
     it('returns a promise with the id', async () => {
152
     it('sends command to native', () => {
153
     it('sends command to native', () => {
153
       uut.dismissAllModals();
154
       uut.dismissAllModals();
154
       expect(mockCommandsSender.dismissAllModals).toHaveBeenCalledTimes(1);
155
       expect(mockCommandsSender.dismissAllModals).toHaveBeenCalledTimes(1);
155
-      expect(mockCommandsSender.dismissAllModals).toHaveBeenCalledWith();
156
+      expect(mockCommandsSender.dismissAllModals).toHaveBeenCalledWith('dismissAllModals+UNIQUE_ID');
156
     });
157
     });
157
 
158
 
158
     it('returns a promise with the id', async () => {
159
     it('returns a promise with the id', async () => {
166
     it('deep clones input to avoid mutation errors', () => {
167
     it('deep clones input to avoid mutation errors', () => {
167
       const obj = {};
168
       const obj = {};
168
       uut.push('theComponentId', { component: { name: 'name', passProps: { foo: obj } } });
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
     it('resolves with the parsed layout', async () => {
173
     it('resolves with the parsed layout', async () => {
178
     it('parses into correct layout node and sends to native', () => {
179
     it('parses into correct layout node and sends to native', () => {
179
       uut.push('theComponentId', { component: { name: 'com.example.MyScreen' } });
180
       uut.push('theComponentId', { component: { name: 'com.example.MyScreen' } });
180
       expect(mockCommandsSender.push).toHaveBeenCalledTimes(1);
181
       expect(mockCommandsSender.push).toHaveBeenCalledTimes(1);
181
-      expect(mockCommandsSender.push).toHaveBeenCalledWith('theComponentId', {
182
+      expect(mockCommandsSender.push).toHaveBeenCalledWith('push+UNIQUE_ID', 'theComponentId', {
182
         type: 'Component',
183
         type: 'Component',
183
         id: 'Component+UNIQUE_ID',
184
         id: 'Component+UNIQUE_ID',
184
         data: {
185
         data: {
194
     it('pops a component, passing componentId', () => {
195
     it('pops a component, passing componentId', () => {
195
       uut.pop('theComponentId', {});
196
       uut.pop('theComponentId', {});
196
       expect(mockCommandsSender.pop).toHaveBeenCalledTimes(1);
197
       expect(mockCommandsSender.pop).toHaveBeenCalledTimes(1);
197
-      expect(mockCommandsSender.pop).toHaveBeenCalledWith('theComponentId', {});
198
+      expect(mockCommandsSender.pop).toHaveBeenCalledWith('pop+UNIQUE_ID', 'theComponentId', {});
198
     });
199
     });
199
     it('pops a component, passing componentId and options', () => {
200
     it('pops a component, passing componentId and options', () => {
200
       const options = {
201
       const options = {
207
       };
208
       };
208
       uut.pop('theComponentId', options);
209
       uut.pop('theComponentId', options);
209
       expect(mockCommandsSender.pop).toHaveBeenCalledTimes(1);
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
     it('pop returns a promise that resolves to componentId', async () => {
214
     it('pop returns a promise that resolves to componentId', async () => {
221
     it('pops all components until the passed Id is top', () => {
222
     it('pops all components until the passed Id is top', () => {
222
       uut.popTo('theComponentId');
223
       uut.popTo('theComponentId');
223
       expect(mockCommandsSender.popTo).toHaveBeenCalledTimes(1);
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
     it('returns a promise that resolves to targetId', async () => {
228
     it('returns a promise that resolves to targetId', async () => {
235
     it('pops all components to root', () => {
236
     it('pops all components to root', () => {
236
       uut.popToRoot('theComponentId');
237
       uut.popToRoot('theComponentId');
237
       expect(mockCommandsSender.popToRoot).toHaveBeenCalledTimes(1);
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
     it('returns a promise that resolves to targetId', async () => {
242
     it('returns a promise that resolves to targetId', async () => {
249
     it('parses into correct layout node and sends to native', () => {
250
     it('parses into correct layout node and sends to native', () => {
250
       uut.setStackRoot('theComponentId', { component: { name: 'com.example.MyScreen' } });
251
       uut.setStackRoot('theComponentId', { component: { name: 'com.example.MyScreen' } });
251
       expect(mockCommandsSender.setStackRoot).toHaveBeenCalledTimes(1);
252
       expect(mockCommandsSender.setStackRoot).toHaveBeenCalledTimes(1);
252
-      expect(mockCommandsSender.setStackRoot).toHaveBeenCalledWith('theComponentId', {
253
+      expect(mockCommandsSender.setStackRoot).toHaveBeenCalledWith('setStackRoot+UNIQUE_ID', 'theComponentId', {
253
         type: 'Component',
254
         type: 'Component',
254
         id: 'Component+UNIQUE_ID',
255
         id: 'Component+UNIQUE_ID',
255
         data: {
256
         data: {
269
         }
270
         }
270
       });
271
       });
271
       expect(mockCommandsSender.showOverlay).toHaveBeenCalledTimes(1);
272
       expect(mockCommandsSender.showOverlay).toHaveBeenCalledTimes(1);
272
-      expect(mockCommandsSender.showOverlay).toHaveBeenCalledWith({
273
+      expect(mockCommandsSender.showOverlay).toHaveBeenCalledWith('showOverlay+UNIQUE_ID', {
273
         type: 'Component',
274
         type: 'Component',
274
         id: 'Component+UNIQUE_ID',
275
         id: 'Component+UNIQUE_ID',
275
         data: {
276
         data: {
283
     it('deep clones input to avoid mutation errors', () => {
284
     it('deep clones input to avoid mutation errors', () => {
284
       const obj = {};
285
       const obj = {};
285
       uut.showOverlay({ component: { name: 'name', inner: obj } });
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
     it('resolves with the component id', async () => {
290
     it('resolves with the component id', async () => {
304
     it('send command to native with componentId', () => {
305
     it('send command to native with componentId', () => {
305
       uut.dismissOverlay('Component1');
306
       uut.dismissOverlay('Component1');
306
       expect(mockCommandsSender.dismissOverlay).toHaveBeenCalledTimes(1);
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
       const mockParser = { parse: () => 'parsed' };
317
       const mockParser = { parse: () => 'parsed' };
317
       const mockCrawler = { crawl: (x) => x, processOptions: (x) => x };
318
       const mockCrawler = { crawl: (x) => x, processOptions: (x) => x };
318
       commandsObserver.register(cb);
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
     function getAllMethodsOfUut() {
323
     function getAllMethodsOfUut() {
373
         dismissOverlay: ['id'],
374
         dismissOverlay: ['id'],
374
       };
375
       };
375
       const paramsForMethodName = {
376
       const paramsForMethodName = {
376
-        setRoot: { layout: 'parsed' },
377
+        setRoot: { commandId: 'setRoot+UNIQUE_ID', layout: 'parsed' },
377
         setDefaultOptions: { options: {} },
378
         setDefaultOptions: { options: {} },
378
         mergeOptions: { componentId: 'id', options: {} },
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
       _.forEach(getAllMethodsOfUut(), (m) => {
391
       _.forEach(getAllMethodsOfUut(), (m) => {
391
         it(`for ${m}`, () => {
392
         it(`for ${m}`, () => {

+ 36
- 23
lib/src/commands/Commands.ts View File

1
 import * as _ from 'lodash';
1
 import * as _ from 'lodash';
2
 import { CommandsObserver } from '../events/CommandsObserver';
2
 import { CommandsObserver } from '../events/CommandsObserver';
3
 import { NativeCommandsSender } from '../adapters/NativeCommandsSender';
3
 import { NativeCommandsSender } from '../adapters/NativeCommandsSender';
4
+import { UniqueIdProvider } from '../adapters/UniqueIdProvider';
4
 
5
 
5
 export class Commands {
6
 export class Commands {
6
   constructor(
7
   constructor(
7
     private readonly nativeCommandsSender: NativeCommandsSender,
8
     private readonly nativeCommandsSender: NativeCommandsSender,
8
     private readonly layoutTreeParser,
9
     private readonly layoutTreeParser,
9
     private readonly layoutTreeCrawler,
10
     private readonly layoutTreeCrawler,
10
-    private readonly commandsObserver: CommandsObserver) {
11
+    private readonly commandsObserver: CommandsObserver,
12
+    private readonly uniqueIdProvider: UniqueIdProvider) {
11
   }
13
   }
12
 
14
 
13
   public setRoot(simpleApi) {
15
   public setRoot(simpleApi) {
15
     const layout = this.layoutTreeParser.parse(input);
17
     const layout = this.layoutTreeParser.parse(input);
16
     this.layoutTreeCrawler.crawl(layout);
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
     return result;
23
     return result;
21
   }
24
   }
22
 
25
 
41
     const layout = this.layoutTreeParser.parse(input);
44
     const layout = this.layoutTreeParser.parse(input);
42
     this.layoutTreeCrawler.crawl(layout);
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
     return result;
50
     return result;
47
   }
51
   }
48
 
52
 
49
   public dismissModal(componentId) {
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
     return result;
57
     return result;
53
   }
58
   }
54
 
59
 
55
   public dismissAllModals() {
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
     return result;
64
     return result;
59
   }
65
   }
60
 
66
 
64
     const layout = this.layoutTreeParser.parse(input);
70
     const layout = this.layoutTreeParser.parse(input);
65
     this.layoutTreeCrawler.crawl(layout);
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
     return result;
76
     return result;
70
   }
77
   }
71
 
78
 
72
   public pop(componentId, options) {
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
     return result;
83
     return result;
76
   }
84
   }
77
 
85
 
78
   public popTo(componentId) {
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
     return result;
90
     return result;
82
   }
91
   }
83
 
92
 
84
   public popToRoot(componentId) {
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
     return result;
97
     return result;
88
   }
98
   }
89
 
99
 
93
     const layout = this.layoutTreeParser.parse(input);
103
     const layout = this.layoutTreeParser.parse(input);
94
     this.layoutTreeCrawler.crawl(layout);
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
     return result;
109
     return result;
99
   }
110
   }
100
 
111
 
104
     const layout = this.layoutTreeParser.parse(input);
115
     const layout = this.layoutTreeParser.parse(input);
105
     this.layoutTreeCrawler.crawl(layout);
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
     return result;
121
     return result;
110
   }
122
   }
111
 
123
 
112
   public dismissOverlay(componentId) {
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
     return result;
128
     return result;
116
   }
129
   }
117
 }
130
 }

+ 2
- 2
playground/src/screens/OptionsScreen.js View File

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