Browse Source

V2 async (#2163)

* push/pop promise

* tests

* modal

* other

* fix tests

* probably test fix
Roman Kozlov 6 years ago
parent
commit
55cd3f51a7

+ 2
- 1
README.md View File

69
 | dismissOverlay             |  [Contribute](/docs/docs/CONTRIBUTING.md)         |	WIP @cool04ek |
69
 | dismissOverlay             |  [Contribute](/docs/docs/CONTRIBUTING.md)         |	WIP @cool04ek |
70
 | customTransition            |   ✅        |	[Contribute](/docs/docs/CONTRIBUTING.md) |
70
 | customTransition            |   ✅        |	[Contribute](/docs/docs/CONTRIBUTING.md) |
71
 | Screen Visibility        | ✅     |✅|
71
 | Screen Visibility        | ✅     |✅|
72
-| async commands (await push)     |  [Contribute](/docs/docs/CONTRIBUTING.md)        | WIP @cool04ek   |
72
+| async commands (await push)     |  [Contribute](/docs/docs/CONTRIBUTING.md)        | ✅   |
73
+
73
 
74
 
74
 ### Navigation Options
75
 ### Navigation Options
75
 
76
 

+ 1
- 1
lib/android/app/src/main/java/com/reactnativenavigation/parse/LayoutFactory.java View File

89
 	private ViewController createContainerStack(LayoutNode node) {
89
 	private ViewController createContainerStack(LayoutNode node) {
90
 		StackController stackController = new StackController(activity, node.id);
90
 		StackController stackController = new StackController(activity, node.id);
91
 		for (LayoutNode child : node.children) {
91
 		for (LayoutNode child : node.children) {
92
-			stackController.push(create(child));
92
+			stackController.push(create(child), null);
93
 		}
93
 		}
94
 		return stackController;
94
 		return stackController;
95
 	}
95
 	}

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

3
 import android.support.annotation.NonNull;
3
 import android.support.annotation.NonNull;
4
 
4
 
5
 import com.facebook.react.ReactInstanceManager;
5
 import com.facebook.react.ReactInstanceManager;
6
+import com.facebook.react.bridge.Promise;
6
 import com.facebook.react.bridge.ReactApplicationContext;
7
 import com.facebook.react.bridge.ReactApplicationContext;
7
 import com.facebook.react.bridge.ReactContextBaseJavaModule;
8
 import com.facebook.react.bridge.ReactContextBaseJavaModule;
8
 import com.facebook.react.bridge.ReactMethod;
9
 import com.facebook.react.bridge.ReactMethod;
35
 	}
36
 	}
36
 
37
 
37
 	@ReactMethod
38
 	@ReactMethod
38
-	public void setRoot(final ReadableMap rawLayoutTree) {
39
+	public void setRoot(final ReadableMap rawLayoutTree, final Promise promise) {
39
 		final LayoutNode layoutTree = LayoutNodeParser.parse(JSONParser.parse(rawLayoutTree));
40
 		final LayoutNode layoutTree = LayoutNodeParser.parse(JSONParser.parse(rawLayoutTree));
40
 		handle(new Runnable() {
41
 		handle(new Runnable() {
41
 			@Override
42
 			@Override
42
 			public void run() {
43
 			public void run() {
43
 				final ViewController viewController = newLayoutFactory().create(layoutTree);
44
 				final ViewController viewController = newLayoutFactory().create(layoutTree);
44
-				navigator().setRoot(viewController);
45
+				navigator().setRoot(viewController, promise);
45
 			}
46
 			}
46
 		});
47
 		});
47
 	}
48
 	}
58
 	}
59
 	}
59
 
60
 
60
 	@ReactMethod
61
 	@ReactMethod
61
-	public void push(final String onContainerId, final ReadableMap rawLayoutTree) {
62
+	public void push(final String onContainerId, final ReadableMap rawLayoutTree, final Promise promise) {
62
 		final LayoutNode layoutTree = LayoutNodeParser.parse(JSONParser.parse(rawLayoutTree));
63
 		final LayoutNode layoutTree = LayoutNodeParser.parse(JSONParser.parse(rawLayoutTree));
63
 		handle(new Runnable() {
64
 		handle(new Runnable() {
64
 			@Override
65
 			@Override
65
 			public void run() {
66
 			public void run() {
66
 				final ViewController viewController = newLayoutFactory().create(layoutTree);
67
 				final ViewController viewController = newLayoutFactory().create(layoutTree);
67
-				navigator().push(onContainerId, viewController);
68
+				navigator().push(onContainerId, viewController, promise);
68
 			}
69
 			}
69
 		});
70
 		});
70
 	}
71
 	}
71
 
72
 
72
 	@ReactMethod
73
 	@ReactMethod
73
-	public void pop(final String onContainerId) {
74
+	public void pop(final String onContainerId, final Promise promise) {
74
 		handle(new Runnable() {
75
 		handle(new Runnable() {
75
 			@Override
76
 			@Override
76
 			public void run() {
77
 			public void run() {
77
-				navigator().popSpecific(onContainerId);
78
+				navigator().popSpecific(onContainerId, promise);
78
 			}
79
 			}
79
 		});
80
 		});
80
 	}
81
 	}
81
 
82
 
82
 	@ReactMethod
83
 	@ReactMethod
83
-	public void popTo(final String containerId) {
84
+	public void popTo(final String containerId, final Promise promise) {
84
 		handle(new Runnable() {
85
 		handle(new Runnable() {
85
 			@Override
86
 			@Override
86
 			public void run() {
87
 			public void run() {
87
-				navigator().popTo(containerId);
88
+				navigator().popTo(containerId, promise);
88
 			}
89
 			}
89
 		});
90
 		});
90
 	}
91
 	}
91
 
92
 
92
 	@ReactMethod
93
 	@ReactMethod
93
-	public void popToRoot(final String containerId) {
94
+	public void popToRoot(final String containerId, final Promise promise) {
94
 		handle(new Runnable() {
95
 		handle(new Runnable() {
95
 			@Override
96
 			@Override
96
 			public void run() {
97
 			public void run() {
97
-				navigator().popToRoot(containerId);
98
+				navigator().popToRoot(containerId, promise);
98
 			}
99
 			}
99
 		});
100
 		});
100
 	}
101
 	}
101
 
102
 
102
 	@ReactMethod
103
 	@ReactMethod
103
-	public void showModal(final ReadableMap rawLayoutTree) {
104
+	public void showModal(final ReadableMap rawLayoutTree, final Promise promise) {
104
 		final LayoutNode layoutTree = LayoutNodeParser.parse(JSONParser.parse(rawLayoutTree));
105
 		final LayoutNode layoutTree = LayoutNodeParser.parse(JSONParser.parse(rawLayoutTree));
105
 		handle(new Runnable() {
106
 		handle(new Runnable() {
106
 			@Override
107
 			@Override
107
 			public void run() {
108
 			public void run() {
108
 				final ViewController viewController = newLayoutFactory().create(layoutTree);
109
 				final ViewController viewController = newLayoutFactory().create(layoutTree);
109
-				navigator().showModal(viewController);
110
+				navigator().showModal(viewController, promise);
110
 			}
111
 			}
111
 		});
112
 		});
112
 	}
113
 	}
113
 
114
 
114
 	@ReactMethod
115
 	@ReactMethod
115
-	public void dismissModal(final String containerId) {
116
+	public void dismissModal(final String containerId, final Promise promise) {
116
 		handle(new Runnable() {
117
 		handle(new Runnable() {
117
 			@Override
118
 			@Override
118
 			public void run() {
119
 			public void run() {
119
-				navigator().dismissModal(containerId);
120
+				navigator().dismissModal(containerId, promise);
120
 			}
121
 			}
121
 		});
122
 		});
122
 	}
123
 	}
123
 
124
 
124
 	@ReactMethod
125
 	@ReactMethod
125
-	public void dismissAllModals() {
126
+	public void dismissAllModals(final Promise promise) {
126
 		handle(new Runnable() {
127
 		handle(new Runnable() {
127
 			@Override
128
 			@Override
128
 			public void run() {
129
 			public void run() {
129
-				navigator().dismissAllModals();
130
+				navigator().dismissAllModals(promise);
130
 			}
131
 			}
131
 		});
132
 		});
132
 	}
133
 	}
133
 
134
 
134
 	@ReactMethod
135
 	@ReactMethod
135
-	public void showOverlay(final String type, final ReadableMap options) {
136
+	public void showOverlay(final String type, final ReadableMap options, final Promise promise) {
136
 		final OverlayOptions overlayOptions = OverlayOptions.parse(JSONParser.parse(options));
137
 		final OverlayOptions overlayOptions = OverlayOptions.parse(JSONParser.parse(options));
137
 		handle(new Runnable() {
138
 		handle(new Runnable() {
138
 			@Override
139
 			@Override
139
 			public void run() {
140
 			public void run() {
140
-				navigator().showOverlay(type, overlayOptions);
141
+				navigator().showOverlay(type, overlayOptions, promise);
141
 			}
142
 			}
142
 		});
143
 		});
143
 	}
144
 	}

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

4
 import android.support.annotation.Nullable;
4
 import android.support.annotation.Nullable;
5
 import android.view.View;
5
 import android.view.View;
6
 
6
 
7
+import com.facebook.react.bridge.Promise;
7
 import com.reactnativenavigation.R;
8
 import com.reactnativenavigation.R;
8
 
9
 
9
 import java.util.ArrayList;
10
 import java.util.ArrayList;
16
 
17
 
17
 	private List<Modal> modals = new ArrayList<>();
18
 	private List<Modal> modals = new ArrayList<>();
18
 
19
 
19
-	public void showModal(final ViewController viewController) {
20
+	public void showModal(final ViewController viewController, Promise promise) {
20
 		Modal modal = new Modal(viewController);
21
 		Modal modal = new Modal(viewController);
21
 		modals.add(modal);
22
 		modals.add(modal);
22
 		modal.show();
23
 		modal.show();
24
+		if (promise != null) {
25
+			promise.resolve(viewController.getId());
26
+		}
23
 	}
27
 	}
24
 
28
 
25
-	public void dismissModal(final String containerId) {
29
+	public void dismissModal(final String containerId, Promise promise) {
26
 		Modal modal = findModalByContainerId(containerId);
30
 		Modal modal = findModalByContainerId(containerId);
27
 		if (modal != null) {
31
 		if (modal != null) {
28
 			modal.dismiss();
32
 			modal.dismiss();
29
 			modals.remove(modal);
33
 			modals.remove(modal);
34
+			if (promise != null) {
35
+				promise.resolve(containerId);
36
+			}
37
+		} else {
38
+			Navigator.rejectPromise(promise);
30
 		}
39
 		}
31
 	}
40
 	}
32
 
41
 
33
-	public void dismissAll() {
42
+	public void dismissAll(Promise promise) {
34
 		for (Modal modal : modals) {
43
 		for (Modal modal : modals) {
35
 			modal.dismiss();
44
 			modal.dismiss();
36
 		}
45
 		}
37
 		modals.clear();
46
 		modals.clear();
47
+		if (promise != null) {
48
+			promise.resolve(true);
49
+		}
38
 	}
50
 	}
39
 
51
 
40
 	@Nullable
52
 	@Nullable

+ 56
- 13
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/Navigator.java View File

5
 import android.view.ViewGroup;
5
 import android.view.ViewGroup;
6
 import android.widget.FrameLayout;
6
 import android.widget.FrameLayout;
7
 
7
 
8
+import com.facebook.react.bridge.Promise;
8
 import com.reactnativenavigation.parse.NavigationOptions;
9
 import com.reactnativenavigation.parse.NavigationOptions;
9
 import com.reactnativenavigation.parse.OverlayOptions;
10
 import com.reactnativenavigation.parse.OverlayOptions;
10
 import com.reactnativenavigation.presentation.OverlayPresenter;
11
 import com.reactnativenavigation.presentation.OverlayPresenter;
43
 
44
 
44
 	@Override
45
 	@Override
45
 	public void destroy() {
46
 	public void destroy() {
46
-		modalStack.dismissAll();
47
+		modalStack.dismissAll(null);
47
 		super.destroy();
48
 		super.destroy();
48
 	}
49
 	}
49
 
50
 
52
 	 */
53
 	 */
53
 
54
 
54
 	public void setRoot(final ViewController viewController) {
55
 	public void setRoot(final ViewController viewController) {
56
+		setRoot(viewController, null);
57
+	}
58
+
59
+	public void setRoot(final ViewController viewController, Promise promise) {
55
 		if (root != null) {
60
 		if (root != null) {
56
 			root.destroy();
61
 			root.destroy();
57
 		}
62
 		}
58
 
63
 
59
 		root = viewController;
64
 		root = viewController;
60
 		getView().addView(viewController.getView());
65
 		getView().addView(viewController.getView());
66
+		if (promise != null) {
67
+			promise.resolve(viewController.getId());
68
+		}
61
 	}
69
 	}
62
 
70
 
63
 	public void setOptions(final String containerId, NavigationOptions options) {
71
 	public void setOptions(final String containerId, NavigationOptions options) {
68
 	}
76
 	}
69
 
77
 
70
 	public void push(final String fromId, final ViewController viewController) {
78
 	public void push(final String fromId, final ViewController viewController) {
79
+		push(fromId, viewController, null);
80
+	}
81
+
82
+	public void push(final String fromId, final ViewController viewController, Promise promise) {
71
 		ViewController from = findControllerById(fromId);
83
 		ViewController from = findControllerById(fromId);
72
 		if (from != null) {
84
 		if (from != null) {
73
 			StackController parentStackController = from.getParentStackController();
85
 			StackController parentStackController = from.getParentStackController();
74
 			if (parentStackController != null) {
86
 			if (parentStackController != null) {
75
-				parentStackController.push(viewController);
87
+				parentStackController.push(viewController, promise);
76
 			}
88
 			}
77
 		}
89
 		}
78
 	}
90
 	}
79
 
91
 
80
 	public void pop(final String fromId) {
92
 	public void pop(final String fromId) {
93
+		pop(fromId, null);
94
+	}
95
+
96
+	public void pop(final String fromId, Promise promise) {
81
 		ViewController from = findControllerById(fromId);
97
 		ViewController from = findControllerById(fromId);
82
 		if (from != null) {
98
 		if (from != null) {
83
 			StackController parentStackController = from.getParentStackController();
99
 			StackController parentStackController = from.getParentStackController();
84
 			if (parentStackController != null) {
100
 			if (parentStackController != null) {
85
-				parentStackController.pop();
101
+				parentStackController.pop(promise);
86
 			}
102
 			}
87
 		}
103
 		}
88
 	}
104
 	}
89
 
105
 
90
 	public void popSpecific(final String id) {
106
 	public void popSpecific(final String id) {
107
+		popSpecific(id, null);
108
+	}
109
+
110
+	public void popSpecific(final String id, Promise promise) {
91
 		ViewController from = findControllerById(id);
111
 		ViewController from = findControllerById(id);
92
 		if (from != null) {
112
 		if (from != null) {
93
 			StackController parentStackController = from.getParentStackController();
113
 			StackController parentStackController = from.getParentStackController();
94
 			if (parentStackController != null) {
114
 			if (parentStackController != null) {
95
-				parentStackController.popSpecific(from);
115
+				parentStackController.popSpecific(from, promise);
116
+			} else {
117
+				rejectPromise(promise);
96
 			}
118
 			}
119
+		} else {
120
+			rejectPromise(promise);
97
 		}
121
 		}
98
 	}
122
 	}
99
 
123
 
100
 	public void popToRoot(final String id) {
124
 	public void popToRoot(final String id) {
125
+		popToRoot(id, null);
126
+	}
127
+
128
+	public void popToRoot(final String id, Promise promise) {
101
 		ViewController from = findControllerById(id);
129
 		ViewController from = findControllerById(id);
102
 		if (from != null) {
130
 		if (from != null) {
103
 			StackController parentStackController = from.getParentStackController();
131
 			StackController parentStackController = from.getParentStackController();
104
 			if (parentStackController != null) {
132
 			if (parentStackController != null) {
105
-				parentStackController.popToRoot();
133
+				parentStackController.popToRoot(promise);
106
 			}
134
 			}
107
 		}
135
 		}
108
 	}
136
 	}
109
 
137
 
110
 	public void popTo(final String containerId) {
138
 	public void popTo(final String containerId) {
139
+		popTo(containerId, null);
140
+	}
141
+
142
+	public void popTo(final String containerId, Promise promise) {
111
 		ViewController target = findControllerById(containerId);
143
 		ViewController target = findControllerById(containerId);
112
 		if (target != null) {
144
 		if (target != null) {
113
 			StackController parentStackController = target.getParentStackController();
145
 			StackController parentStackController = target.getParentStackController();
114
 			if (parentStackController != null) {
146
 			if (parentStackController != null) {
115
-				parentStackController.popTo(target);
147
+				parentStackController.popTo(target, promise);
148
+			} else {
149
+				rejectPromise(promise);
116
 			}
150
 			}
151
+		} else {
152
+			rejectPromise(promise);
117
 		}
153
 		}
118
 	}
154
 	}
119
 
155
 
120
-	public void showModal(final ViewController viewController) {
121
-		modalStack.showModal(viewController);
156
+	public void showModal(final ViewController viewController, Promise promise) {
157
+		modalStack.showModal(viewController, promise);
122
 	}
158
 	}
123
 
159
 
124
-	public void dismissModal(final String containerId) {
125
-		modalStack.dismissModal(containerId);
160
+	public void dismissModal(final String containerId, Promise promise) {
161
+		modalStack.dismissModal(containerId, promise);
126
 	}
162
 	}
127
 
163
 
128
-	public void dismissAllModals() {
129
-		modalStack.dismissAll();
164
+	public void dismissAllModals(Promise promise) {
165
+		modalStack.dismissAll(promise);
130
 	}
166
 	}
131
 
167
 
132
-	public void showOverlay(String type, OverlayOptions options) {
168
+	public void showOverlay(String type, OverlayOptions options, Promise promise) {
133
 		new OverlayPresenter(getActivity(), type, options).show();
169
 		new OverlayPresenter(getActivity(), type, options).show();
170
+		promise.resolve(true);
171
+	}
172
+
173
+	static void rejectPromise(Promise promise) {
174
+		if (promise != null) {
175
+			promise.reject(new Throwable("Nothing to pop"));
176
+		}
134
 	}
177
 	}
135
 }
178
 }

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

1
 package com.reactnativenavigation.viewcontrollers;
1
 package com.reactnativenavigation.viewcontrollers;
2
 
2
 
3
 import android.app.Activity;
3
 import android.app.Activity;
4
-import android.graphics.Color;
5
 import android.support.annotation.NonNull;
4
 import android.support.annotation.NonNull;
6
-import android.util.Log;
7
 import android.view.View;
5
 import android.view.View;
8
 import android.view.ViewGroup;
6
 import android.view.ViewGroup;
9
 import android.widget.FrameLayout;
7
 import android.widget.FrameLayout;
10
-import android.widget.LinearLayout;
11
-import android.widget.RelativeLayout;
12
 
8
 
9
+import com.facebook.react.bridge.Promise;
13
 import com.reactnativenavigation.anim.StackAnimator;
10
 import com.reactnativenavigation.anim.StackAnimator;
14
-import com.reactnativenavigation.utils.CompatUtils;
15
-import com.reactnativenavigation.views.TopBar;
16
 
11
 
17
 import java.util.Collection;
12
 import java.util.Collection;
18
-
19
-import static android.widget.RelativeLayout.BELOW;
13
+import java.util.Iterator;
20
 
14
 
21
 public class StackController extends ParentController {
15
 public class StackController extends ParentController {
22
 
16
 
33
 	}
27
 	}
34
 
28
 
35
 	public void push(final ViewController child) {
29
 	public void push(final ViewController child) {
30
+		push(child, null);
31
+	}
32
+
33
+	public void push(final ViewController child, final Promise promise) {
36
 		final ViewController previousTop = peek();
34
 		final ViewController previousTop = peek();
37
 
35
 
38
 		child.setParentStackController(this);
36
 		child.setParentStackController(this);
46
 				@Override
44
 				@Override
47
 				public void onAnimationEnd() {
45
 				public void onAnimationEnd() {
48
 					getView().removeView(previousTop.getView());
46
 					getView().removeView(previousTop.getView());
47
+					if (promise != null) {
48
+						promise.resolve(child.getId());
49
+					}
49
 				}
50
 				}
50
 			});
51
 			});
51
-
52
+		} else if (promise != null) {
53
+			promise.resolve(child.getId());
52
 		}
54
 		}
53
 	}
55
 	}
54
 
56
 
55
-	public boolean canPop() {
57
+	boolean canPop() {
56
 		return stack.size() > 1;
58
 		return stack.size() > 1;
57
 	}
59
 	}
58
 
60
 
59
-	public void pop() {
60
-		if (!canPop()) return;
61
+	void pop(Promise promise) {
62
+		pop(true, promise);
63
+	}
64
+
65
+	void pop() {
66
+		pop(true, null);
67
+	}
68
+
69
+	private void pop(boolean animate, final Promise promise) {
70
+		if (!canPop()) {
71
+			Navigator.rejectPromise(promise);
72
+			return;
73
+		}
61
 
74
 
62
 		final ViewController poppedTop = stack.pop();
75
 		final ViewController poppedTop = stack.pop();
63
 		ViewController newTop = peek();
76
 		ViewController newTop = peek();
66
 		final View exitingView = poppedTop.getView();
79
 		final View exitingView = poppedTop.getView();
67
 		getView().addView(enteringView, getView().getChildCount() - 1);
80
 		getView().addView(enteringView, getView().getChildCount() - 1);
68
 
81
 
69
-		//TODO animatePush only when needed
70
-		animator.animatePop(exitingView, new StackAnimator.StackAnimationListener() {
71
-			@Override
72
-			public void onAnimationEnd() {
73
-				getView().removeView(exitingView);
74
-				poppedTop.destroy();
75
-			}
76
-		});
82
+		if (animate) {
83
+			animator.animatePop(exitingView, new StackAnimator.StackAnimationListener() {
84
+				@Override
85
+				public void onAnimationEnd() {
86
+					finishPopping(exitingView, poppedTop, promise);
87
+				}
88
+			});
89
+		} else {
90
+			finishPopping(exitingView, poppedTop, promise);
91
+		}
92
+	}
93
+
94
+	private void finishPopping(View exitingView, ViewController poppedTop, Promise promise) {
95
+		getView().removeView(exitingView);
96
+		poppedTop.destroy();
97
+		if (promise != null) {
98
+			promise.resolve(poppedTop.getId());
99
+		}
100
+	}
101
+
102
+	void popSpecific(final ViewController childController) {
103
+		popSpecific(childController, null);
77
 	}
104
 	}
78
 
105
 
79
-	public void popSpecific(final ViewController childController) {
106
+	void popSpecific(final ViewController childController, Promise promise) {
80
 		if (stack.isTop(childController.getId())) {
107
 		if (stack.isTop(childController.getId())) {
81
-			pop();
108
+			pop(promise);
82
 		} else {
109
 		} else {
83
 			stack.remove(childController.getId());
110
 			stack.remove(childController.getId());
84
 			childController.destroy();
111
 			childController.destroy();
112
+			if (promise != null) {
113
+				promise.resolve(childController.getId());
114
+			}
85
 		}
115
 		}
86
 	}
116
 	}
87
 
117
 
88
-	public void popTo(final ViewController viewController) {
118
+	void popTo(ViewController viewController) {
119
+		popTo(viewController, null);
120
+	}
121
+
122
+	void popTo(final ViewController viewController, Promise promise) {
89
 		if (!stack.containsId(viewController.getId())) {
123
 		if (!stack.containsId(viewController.getId())) {
124
+			Navigator.rejectPromise(promise);
90
 			return;
125
 			return;
91
 		}
126
 		}
92
-		while (!stack.isTop(viewController.getId())) {
93
-			pop();
127
+
128
+		Iterator<String> iterator = stack.iterator();
129
+		String currentControlId = iterator.next();
130
+		while (!viewController.getId().equals(currentControlId)) {
131
+			String nextControlId = iterator.next();
132
+			boolean animate = nextControlId.equals(viewController.getId());
133
+			pop(animate, animate ? promise : null);
134
+			currentControlId = nextControlId;
94
 		}
135
 		}
95
 	}
136
 	}
96
 
137
 
97
-	public void popToRoot() {
138
+	void popToRoot() {
139
+		popToRoot(null);
140
+	}
141
+
142
+	void popToRoot(Promise promise) {
98
 		while (canPop()) {
143
 		while (canPop()) {
99
-			pop();
144
+			boolean animate = stack.size() == 2; //first element is root
145
+			pop(animate, animate ? promise : null);
100
 		}
146
 		}
101
 	}
147
 	}
102
 
148
 
103
-	public ViewController peek() {
149
+	ViewController peek() {
104
 		return stack.peek();
150
 		return stack.peek();
105
 	}
151
 	}
106
 
152
 
115
 	@Override
161
 	@Override
116
 	public boolean handleBack() {
162
 	public boolean handleBack() {
117
 		if (canPop()) {
163
 		if (canPop()) {
118
-			pop();
164
+			pop(null);
119
 			return true;
165
 			return true;
120
 		} else {
166
 		} else {
121
 			return false;
167
 			return false;

+ 39
- 0
lib/android/app/src/test/java/com/reactnativenavigation/mocks/MockPromise.java View File

1
+package com.reactnativenavigation.mocks;
2
+
3
+import com.facebook.react.bridge.Promise;
4
+
5
+import javax.annotation.Nullable;
6
+
7
+
8
+public class MockPromise implements Promise {
9
+
10
+	@Override
11
+	public void resolve(@Nullable Object value) {
12
+
13
+	}
14
+
15
+	@Override
16
+	public void reject(String code, String message) {
17
+
18
+	}
19
+
20
+	@Override
21
+	public void reject(String code, Throwable e) {
22
+
23
+	}
24
+
25
+	@Override
26
+	public void reject(String code, String message, Throwable e) {
27
+
28
+	}
29
+
30
+	@Override
31
+	public void reject(String message) {
32
+
33
+	}
34
+
35
+	@Override
36
+	public void reject(Throwable reason) {
37
+
38
+	}
39
+}

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

4
 import android.support.annotation.NonNull;
4
 import android.support.annotation.NonNull;
5
 
5
 
6
 import com.reactnativenavigation.BaseTest;
6
 import com.reactnativenavigation.BaseTest;
7
+import com.reactnativenavigation.mocks.MockPromise;
7
 import com.reactnativenavigation.mocks.SimpleContainerViewController;
8
 import com.reactnativenavigation.mocks.SimpleContainerViewController;
8
 import com.reactnativenavigation.mocks.SimpleViewController;
9
 import com.reactnativenavigation.mocks.SimpleViewController;
9
 import com.reactnativenavigation.mocks.TestStackAnimator;
10
 import com.reactnativenavigation.mocks.TestStackAnimator;
14
 
15
 
15
 import java.util.Arrays;
16
 import java.util.Arrays;
16
 
17
 
18
+import javax.annotation.Nullable;
19
+
17
 import static org.assertj.core.api.Java6Assertions.assertThat;
20
 import static org.assertj.core.api.Java6Assertions.assertThat;
18
 import static org.mockito.Mockito.spy;
21
 import static org.mockito.Mockito.spy;
19
 import static org.mockito.Mockito.times;
22
 import static org.mockito.Mockito.times;
29
 	private ViewController child4;
32
 	private ViewController child4;
30
 	private ViewController child5;
33
 	private ViewController child5;
31
 
34
 
35
+
32
 	@Override
36
 	@Override
33
 	public void beforeEach() {
37
 	public void beforeEach() {
34
 		super.beforeEach();
38
 		super.beforeEach();
216
 	private StackController newStack() {
220
 	private StackController newStack() {
217
 		return new StackController(activity, "stack" + CompatUtils.generateViewId(), new TestStackAnimator());
221
 		return new StackController(activity, "stack" + CompatUtils.generateViewId(), new TestStackAnimator());
218
 	}
222
 	}
223
+
224
+	@Test
225
+	public void push_Promise() throws Exception {
226
+		final StackController stackController = newStack();
227
+		stackController.push(child1);
228
+		uut.setRoot(stackController);
229
+
230
+		assertIsChildById(uut.getView(), stackController.getView());
231
+		assertIsChildById(stackController.getView(), child1.getView());
232
+
233
+		uut.push(child1.getId(), child2, new MockPromise() {
234
+			@Override
235
+			public void resolve(@Nullable Object value) {
236
+				assertIsChildById(uut.getView(), stackController.getView());
237
+				assertIsChildById(stackController.getView(), child2.getView());
238
+			}
239
+		});
240
+	}
241
+
242
+	@Test
243
+	public void push_InvalidPushWithoutAStack_DoesNothing_Promise() throws Exception {
244
+		uut.setRoot(child1);
245
+		uut.push(child1.getId(), child2, new MockPromise() {
246
+			@Override
247
+			public void reject(String code, Throwable e) {
248
+				assertIsChildById(uut.getView(), child1.getView());
249
+			}
250
+		});
251
+
252
+	}
253
+
254
+	@Test
255
+	public void pop_InvalidDoesNothing_Promise() throws Exception {
256
+		uut.pop("123");
257
+		uut.setRoot(child1);
258
+		uut.pop(child1.getId(), new MockPromise() {
259
+			@Override
260
+			public void reject(Throwable reason) {
261
+				assertThat(uut.getChildControllers()).hasSize(1);
262
+			}
263
+		});
264
+	}
265
+
266
+	@Test
267
+	public void pop_FromCorrectStackByFindingChildId_Promise() throws Exception {
268
+		BottomTabsController bottomTabsController = newTabs();
269
+		StackController stack1 = newStack();
270
+		final StackController stack2 = newStack();
271
+		stack1.push(child1);
272
+		stack2.push(child2);
273
+		stack2.push(child3);
274
+		stack2.push(child4);
275
+		bottomTabsController.setTabs(Arrays.<ViewController>asList(stack1, stack2));
276
+		uut.setRoot(bottomTabsController);
277
+
278
+		uut.pop("child4", new MockPromise() {
279
+			@Override
280
+			public void resolve(@Nullable Object value) {
281
+				assertThat(stack2.getChildControllers()).containsOnly(child2, child3);
282
+			}
283
+		});
284
+	}
219
 }
285
 }

+ 9
- 18
lib/src/adapters/NativeCommandsSender.js View File

6
   }
6
   }
7
 
7
 
8
   setRoot(layoutTree) {
8
   setRoot(layoutTree) {
9
-    this.nativeCommandsModule.setRoot(layoutTree);
10
-    return Promise.resolve(layoutTree);
9
+    return this.nativeCommandsModule.setRoot(layoutTree);
11
   }
10
   }
12
 
11
 
13
   setOptions(containerId, options) {
12
   setOptions(containerId, options) {
15
   }
14
   }
16
 
15
 
17
   push(onContainerId, layout) {
16
   push(onContainerId, layout) {
18
-    this.nativeCommandsModule.push(onContainerId, layout);
19
-    return Promise.resolve(layout);
17
+    return this.nativeCommandsModule.push(onContainerId, layout);
20
   }
18
   }
21
 
19
 
22
   pop(containerId) {
20
   pop(containerId) {
23
-    this.nativeCommandsModule.pop(containerId);
24
-    return Promise.resolve(containerId);
21
+    return this.nativeCommandsModule.pop(containerId);
25
   }
22
   }
26
 
23
 
27
   popTo(containerId) {
24
   popTo(containerId) {
28
-    this.nativeCommandsModule.popTo(containerId);
29
-    return Promise.resolve(containerId);
25
+    return this.nativeCommandsModule.popTo(containerId);
30
   }
26
   }
31
 
27
 
32
   popToRoot(containerId) {
28
   popToRoot(containerId) {
33
-    this.nativeCommandsModule.popToRoot(containerId);
34
-    return Promise.resolve(containerId);
29
+    return this.nativeCommandsModule.popToRoot(containerId);
35
   }
30
   }
36
 
31
 
37
   showModal(layout) {
32
   showModal(layout) {
38
-    this.nativeCommandsModule.showModal(layout);
39
-    return Promise.resolve(layout);
33
+    return this.nativeCommandsModule.showModal(layout);
40
   }
34
   }
41
 
35
 
42
   dismissModal(containerId) {
36
   dismissModal(containerId) {
43
-    this.nativeCommandsModule.dismissModal(containerId);
44
-    return Promise.resolve(containerId);
37
+    return this.nativeCommandsModule.dismissModal(containerId);
45
   }
38
   }
46
 
39
 
47
   dismissAllModals() {
40
   dismissAllModals() {
48
-    this.nativeCommandsModule.dismissAllModals();
49
-    return Promise.resolve(true);
41
+    return this.nativeCommandsModule.dismissAllModals();
50
   }
42
   }
51
 
43
 
52
   switchToTab(containerId, tabIndex) {
44
   switchToTab(containerId, tabIndex) {
55
   }
47
   }
56
 
48
 
57
   showOverlay(type, options) {
49
   showOverlay(type, options) {
58
-    this.nativeCommandsModule.showOverlay(type, options);
59
-    return Promise.resolve(type);
50
+    return this.nativeCommandsModule.showOverlay(type, options);
60
   }
51
   }
61
 }
52
 }
62
 
53
 

+ 2
- 2
playground/src/containers/ModalScreen.js View File

54
     });
54
     });
55
   }
55
   }
56
 
56
 
57
-  onClickDismissModal() {
58
-    Navigation.dismissModal(this.props.containerId);
57
+  async onClickDismissModal() {
58
+    await Navigation.dismissModal(this.props.containerId);
59
   }
59
   }
60
 
60
 
61
   onClickDismissPreviousModal() {
61
   onClickDismissPreviousModal() {

+ 10
- 10
playground/src/containers/PushedScreen.js View File

33
     );
33
     );
34
   }
34
   }
35
 
35
 
36
-  onClickPush() {
37
-    Navigation.push(this.props.containerId, {
36
+  async onClickPush() {
37
+    await Navigation.push(this.props.containerId, {
38
       name: 'navigation.playground.PushedScreen',
38
       name: 'navigation.playground.PushedScreen',
39
       passProps: {
39
       passProps: {
40
         stackPosition: this.getStackPosition() + 1,
40
         stackPosition: this.getStackPosition() + 1,
43
     });
43
     });
44
   }
44
   }
45
 
45
 
46
-  onClickPop() {
47
-    Navigation.pop(this.props.containerId);
46
+  async onClickPop() {
47
+    await Navigation.pop(this.props.containerId);
48
   }
48
   }
49
 
49
 
50
-  onClickPopPrevious() {
51
-    Navigation.pop(_.last(this.props.previousScreenIds));
50
+  async onClickPopPrevious() {
51
+    await Navigation.pop(_.last(this.props.previousScreenIds));
52
   }
52
   }
53
 
53
 
54
-  onClickPopToFirstPosition() {
55
-    Navigation.popTo(this.props.previousScreenIds[0]);
54
+  async onClickPopToFirstPosition() {
55
+    await Navigation.popTo(this.props.previousScreenIds[0]);
56
   }
56
   }
57
 
57
 
58
-  onClickPopToRoot() {
59
-    Navigation.popToRoot(this.props.containerId);
58
+  async onClickPopToRoot() {
59
+    await Navigation.popToRoot(this.props.containerId);
60
   }
60
   }
61
 
61
 
62
   getStackPosition() {
62
   getStackPosition() {

+ 4
- 4
playground/src/containers/WelcomeScreen.js View File

104
     });
104
     });
105
   }
105
   }
106
 
106
 
107
-  onClickPush() {
108
-    Navigation.push(this.props.containerId, {
107
+  async onClickPush() {
108
+    await Navigation.push(this.props.containerId, {
109
       name: 'navigation.playground.PushedScreen'
109
       name: 'navigation.playground.PushedScreen'
110
     });
110
     });
111
   }
111
   }
116
     });
116
     });
117
   }
117
   }
118
 
118
 
119
-  onClickShowModal() {
120
-    Navigation.showModal({
119
+  async onClickShowModal() {
120
+    await Navigation.showModal({
121
       container: {
121
       container: {
122
         name: 'navigation.playground.ModalScreen'
122
         name: 'navigation.playground.ModalScreen'
123
       }
123
       }