Browse Source

Remove support from popping specific screens in a stack

While arguable being convenient, this is behaviour goes against the Stack api.

Some commands, such as push and pop, can be applied on stacks by passing the
stack's componentId.
This commit also fixes those stack commands which were always applied on parent stacks.
Guy Carmeli 6 years ago
parent
commit
2e80068e59

+ 0
- 11
e2e/ScreenStack.test.js View File

23
     await expect(elementById(testIDs.WELCOME_SCREEN_HEADER)).toBeVisible();
23
     await expect(elementById(testIDs.WELCOME_SCREEN_HEADER)).toBeVisible();
24
   });
24
   });
25
 
25
 
26
-  test('pop screen deep in the stack', async () => {
27
-    await elementById(testIDs.PUSH_BUTTON).tap();
28
-    await expect(elementByLabel('Stack Position: 1')).toBeVisible();
29
-    await elementById(testIDs.PUSH_BUTTON).tap();
30
-    await expect(elementByLabel('Stack Position: 2')).toBeVisible();
31
-    await elementById(testIDs.POP_PREVIOUS_BUTTON).tap();
32
-    await expect(elementByLabel('Stack Position: 2')).toBeVisible();
33
-    await elementById(testIDs.POP_BUTTON).tap();
34
-    await expect(elementById(testIDs.WELCOME_SCREEN_HEADER)).toBeVisible();
35
-  });
36
-
37
   test('pop to specific id', async () => {
26
   test('pop to specific id', async () => {
38
     await elementById(testIDs.PUSH_BUTTON).tap();
27
     await elementById(testIDs.PUSH_BUTTON).tap();
39
     await elementById(testIDs.PUSH_BUTTON).tap();
28
     await elementById(testIDs.PUSH_BUTTON).tap();

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

98
     }
98
     }
99
 
99
 
100
 	@ReactMethod
100
 	@ReactMethod
101
-	public void pop(String commandId, String onComponentId, ReadableMap options, Promise promise) {
102
-		handle(() -> navigator().popSpecific(onComponentId, new NativeCommandListener(commandId, promise, eventEmitter, now)));
101
+	public void pop(String commandId, String componentId, ReadableMap options, Promise promise) {
102
+		handle(() -> navigator().pop(componentId, new NativeCommandListener(commandId, promise, eventEmitter, now)));
103
 	}
103
 	}
104
 
104
 
105
 	@ReactMethod
105
 	@ReactMethod

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

15
 import com.reactnativenavigation.react.EventEmitter;
15
 import com.reactnativenavigation.react.EventEmitter;
16
 import com.reactnativenavigation.utils.CommandListener;
16
 import com.reactnativenavigation.utils.CommandListener;
17
 import com.reactnativenavigation.utils.CompatUtils;
17
 import com.reactnativenavigation.utils.CompatUtils;
18
+import com.reactnativenavigation.utils.Task;
18
 import com.reactnativenavigation.viewcontrollers.modal.ModalStack;
19
 import com.reactnativenavigation.viewcontrollers.modal.ModalStack;
19
 import com.reactnativenavigation.viewcontrollers.stack.StackController;
20
 import com.reactnativenavigation.viewcontrollers.stack.StackController;
20
 import com.reactnativenavigation.views.element.ElementTransitionManager;
21
 import com.reactnativenavigation.views.element.ElementTransitionManager;
147
         }
148
         }
148
     }
149
     }
149
 
150
 
150
-    public void push(final String fromId, final ViewController viewController, CommandListener listener) {
151
-        ViewController from = findControllerById(fromId);
152
-        if (from != null) {
153
-            from.performOnParentStack(
154
-                    stack -> ((StackController) stack).push(viewController, listener),
155
-                    () -> rejectPush(fromId, viewController, listener)
156
-            );
157
-        } else {
158
-            rejectPush(fromId, viewController, listener);
159
-        }
151
+    public void push(final String id, final ViewController viewController, CommandListener listener) {
152
+        applyOnStack(id, listener, stack -> stack.push(viewController, listener));
160
     }
153
     }
161
 
154
 
162
-    public void setStackRoot(String fromId, ViewController viewController, CommandListener listener) {
163
-        ViewController from = findControllerById(fromId);
164
-        if (from != null) {
165
-            from.performOnParentStack(stack -> ((StackController) stack).setRoot(viewController, listener));
166
-        }
167
-    }
168
-
169
-    public void pop(final String fromId, CommandListener listener) {
170
-        ViewController from = findControllerById(fromId);
171
-        if (from != null) {
172
-            from.performOnParentStack(stack -> ((StackController) stack).pop(listener));
173
-        }
155
+    public void setStackRoot(String id, ViewController viewController, CommandListener listener) {
156
+        applyOnStack(id, listener, stack -> stack.setRoot(viewController, listener));
174
     }
157
     }
175
 
158
 
176
-    public void popSpecific(final String id, CommandListener listener) {
177
-        ViewController from = findControllerById(id);
178
-        if (from != null) {
179
-            from.performOnParentStack(stack -> ((StackController) stack).popSpecific(from, listener), () -> listener.onError("Nothing to pop"));
180
-        } else {
181
-            listener.onError("Nothing to pop");
182
-        }
159
+    public void pop(String id, CommandListener listener) {
160
+        applyOnStack(id, listener, stack -> stack.pop(listener));
183
     }
161
     }
184
 
162
 
185
     public void popToRoot(final String id, CommandListener listener) {
163
     public void popToRoot(final String id, CommandListener listener) {
186
-        ViewController from = findControllerById(id);
187
-        if (from != null) {
188
-            from.performOnParentStack(stack -> ((StackController) stack).popToRoot(listener));
189
-        }
164
+        applyOnStack(id, listener, stack -> stack.popToRoot(listener));
190
     }
165
     }
191
 
166
 
192
-    public void popTo(final String componentId, CommandListener listener) {
193
-        ViewController target = findControllerById(componentId);
167
+    public void popTo(final String id, CommandListener listener) {
168
+        ViewController target = findControllerById(id);
194
         if (target != null) {
169
         if (target != null) {
195
-            target.performOnParentStack(stack -> ((StackController) stack).popTo(target, listener), () -> listener.onError("Nothing to pop"));
170
+            target.performOnParentStack(stack -> ((StackController) stack).popTo(target, listener));
196
         } else {
171
         } else {
197
-            listener.onError("Nothing to pop");
172
+            listener.onError("Failed to execute stack command. Stack by " + id + " not found.");
198
         }
173
         }
199
     }
174
     }
200
 
175
 
229
         return controllerById != null ? controllerById : modalStack.findControllerById(id);
204
         return controllerById != null ? controllerById : modalStack.findControllerById(id);
230
     }
205
     }
231
 
206
 
232
-    private void rejectPush(String fromId, ViewController viewController, CommandListener listener) {
233
-        listener.onError("Could not push component: " +
234
-                         viewController.getId() +
235
-                         ". Stack with id " +
236
-                         fromId +
237
-                         " was not found.");
207
+    private void applyOnStack(String fromId, CommandListener listener, Task<StackController> task) {
208
+        ViewController from = findControllerById(fromId);
209
+        if (from != null) {
210
+            if (from instanceof StackController) {
211
+                task.run((StackController) from);
212
+            } else {
213
+                from.performOnParentStack(stack -> task.run((StackController) stack) );
214
+            }
215
+        } else {
216
+            listener.onError("Failed to execute stack command. Stack " + fromId + " not found.");
217
+        }
238
     }
218
     }
239
 
219
 
240
     private boolean isRootNotCreated() {
220
     private boolean isRootNotCreated() {

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

19
 import com.reactnativenavigation.viewcontrollers.ChildControllersRegistry;
19
 import com.reactnativenavigation.viewcontrollers.ChildControllersRegistry;
20
 import com.reactnativenavigation.viewcontrollers.IdStack;
20
 import com.reactnativenavigation.viewcontrollers.IdStack;
21
 import com.reactnativenavigation.viewcontrollers.ParentController;
21
 import com.reactnativenavigation.viewcontrollers.ParentController;
22
-import com.reactnativenavigation.viewcontrollers.ReactViewCreator;
23
 import com.reactnativenavigation.viewcontrollers.ViewController;
22
 import com.reactnativenavigation.viewcontrollers.ViewController;
24
 import com.reactnativenavigation.viewcontrollers.topbar.TopBarBackgroundViewController;
23
 import com.reactnativenavigation.viewcontrollers.topbar.TopBarBackgroundViewController;
25
 import com.reactnativenavigation.viewcontrollers.topbar.TopBarController;
24
 import com.reactnativenavigation.viewcontrollers.topbar.TopBarController;
38
 
37
 
39
     private final IdStack<ViewController> stack = new IdStack<>();
38
     private final IdStack<ViewController> stack = new IdStack<>();
40
     private final NavigationAnimator animator;
39
     private final NavigationAnimator animator;
41
-    private final ReactViewCreator topBarButtonCreator;
42
     private TopBarBackgroundViewController topBarBackgroundViewController;
40
     private TopBarBackgroundViewController topBarBackgroundViewController;
43
     private TopBarController topBarController;
41
     private TopBarController topBarController;
44
     private BackButtonHelper backButtonHelper;
42
     private BackButtonHelper backButtonHelper;
45
     private final StackOptionsPresenter presenter;
43
     private final StackOptionsPresenter presenter;
46
 
44
 
47
-    public StackController(Activity activity, List<ViewController> children, ChildControllersRegistry childRegistry, ReactViewCreator topBarButtonCreator, TopBarBackgroundViewController topBarBackgroundViewController, TopBarController topBarController, NavigationAnimator animator, String id, Options initialOptions, BackButtonHelper backButtonHelper, StackOptionsPresenter stackPresenter, OptionsPresenter presenter) {
45
+    public StackController(Activity activity, List<ViewController> children, ChildControllersRegistry childRegistry, TopBarBackgroundViewController topBarBackgroundViewController, TopBarController topBarController, NavigationAnimator animator, String id, Options initialOptions, BackButtonHelper backButtonHelper, StackOptionsPresenter stackPresenter, OptionsPresenter presenter) {
48
         super(activity, childRegistry, id, presenter, initialOptions);
46
         super(activity, childRegistry, id, presenter, initialOptions);
49
         this.topBarController = topBarController;
47
         this.topBarController = topBarController;
50
-        this.topBarButtonCreator = topBarButtonCreator;
51
         this.topBarBackgroundViewController = topBarBackgroundViewController;
48
         this.topBarBackgroundViewController = topBarBackgroundViewController;
52
         this.animator = animator;
49
         this.animator = animator;
53
         this.backButtonHelper = backButtonHelper;
50
         this.backButtonHelper = backButtonHelper;
224
         listener.onSuccess(disappearing.getId());
221
         listener.onSuccess(disappearing.getId());
225
     }
222
     }
226
 
223
 
227
-    public void popSpecific(ViewController childController, CommandListener listener) {
228
-        if (stack.isTop(childController.getId())) {
229
-            pop(listener);
230
-        } else {
231
-            removeAndDestroyController(childController);
232
-            listener.onSuccess(childController.getId());
233
-        }
234
-    }
235
-
236
     public void popTo(final ViewController viewController, CommandListener listener) {
224
     public void popTo(final ViewController viewController, CommandListener listener) {
237
         if (!stack.containsId(viewController.getId())) {
225
         if (!stack.containsId(viewController.getId())) {
238
             listener.onError("Nothing to pop");
226
             listener.onError("Nothing to pop");

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

95
         return new StackController(activity,
95
         return new StackController(activity,
96
                 children,
96
                 children,
97
                 childRegistry,
97
                 childRegistry,
98
-                topBarButtonCreator,
99
                 topBarBackgroundViewController,
98
                 topBarBackgroundViewController,
100
                 topBarController,
99
                 topBarController,
101
                 animator,
100
                 animator,

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

24
 import com.reactnativenavigation.utils.CompatUtils;
24
 import com.reactnativenavigation.utils.CompatUtils;
25
 import com.reactnativenavigation.utils.ImageLoader;
25
 import com.reactnativenavigation.utils.ImageLoader;
26
 import com.reactnativenavigation.utils.OptionHelper;
26
 import com.reactnativenavigation.utils.OptionHelper;
27
+import com.reactnativenavigation.utils.ViewUtils;
27
 import com.reactnativenavigation.viewcontrollers.bottomtabs.BottomTabsController;
28
 import com.reactnativenavigation.viewcontrollers.bottomtabs.BottomTabsController;
28
 import com.reactnativenavigation.viewcontrollers.modal.ModalStack;
29
 import com.reactnativenavigation.viewcontrollers.modal.ModalStack;
29
 import com.reactnativenavigation.viewcontrollers.stack.StackController;
30
 import com.reactnativenavigation.viewcontrollers.stack.StackController;
217
     }
218
     }
218
 
219
 
219
     @Test
220
     @Test
220
-    public void popSpecific() {
221
-        disablePushAnimation(child1, child2, child3, child4);
222
-        StackController stack1 = newStack(); stack1.ensureViewIsCreated();
223
-        StackController stack2 = newStack(); stack2.ensureViewIsCreated();
224
-        stack1.push(child1, new CommandListenerAdapter());
225
-        stack2.push(child2, new CommandListenerAdapter());
226
-        stack2.push(child3, new CommandListenerAdapter());
227
-        stack2.push(child4, new CommandListenerAdapter());
228
-        BottomTabsController bottomTabsController = newTabs(Arrays.asList(stack1, stack2));
229
-        uut.setRoot(bottomTabsController, new CommandListenerAdapter());
230
-
231
-        uut.popSpecific(child2.getId(), new CommandListenerAdapter());
221
+    public void pop_byStackId() {
222
+        disablePushAnimation(child1, child2);
223
+        disablePopAnimation(child2, child1);
224
+        StackController stack = newStack(); stack.ensureViewIsCreated();
225
+        uut.setRoot(stack, new CommandListenerAdapter());
226
+        stack.push(child1, new CommandListenerAdapter());
227
+        stack.push(child2, new CommandListenerAdapter());
232
 
228
 
233
-        assertThat(stack2.getChildControllers()).containsOnly(child4, child3);
229
+        uut.pop(stack.getId(), new CommandListenerAdapter());
230
+        assertThat(stack.getChildControllers()).containsOnly(child1);
234
     }
231
     }
235
 
232
 
236
     @Test
233
     @Test
434
                 assertThat(spy.getChildControllers().size()).isEqualTo(1);
431
                 assertThat(spy.getChildControllers().size()).isEqualTo(1);
435
             }
432
             }
436
         };
433
         };
437
-        uut.popSpecific("child2", listener);
438
-        verify(spy, times(1)).popSpecific(child2, listener);
434
+        uut.pop("child2", listener);
435
+        verify(spy, times(1)).pop(listener);
439
     }
436
     }
440
 
437
 
441
     @Test
438
     @Test
474
         verify(parentVisibilityListener, times(2)).onViewAppeared(parentController.getView());
471
         verify(parentVisibilityListener, times(2)).onViewAppeared(parentController.getView());
475
     }
472
     }
476
 
473
 
474
+    @Test
475
+    public void dismissModal_reattachedToRoot() {
476
+        disableModalAnimations(child1);
477
+
478
+        uut.setRoot(parentController, new CommandListenerAdapter());
479
+        assertThat(ViewUtils.isChildOf(uut.getRootLayout(), parentController.getView()));
480
+        uut.showModal(child1, new CommandListenerAdapter());
481
+
482
+        uut.dismissModal(child1.getId(), new CommandListenerAdapter());
483
+        assertThat(ViewUtils.isChildOf(uut.getRootLayout(), parentController.getView()));
484
+    }
485
+
477
     @Test
486
     @Test
478
     public void dismissModal_rejectIfRootIsNotSetAndSingleModalIsDisplayed() {
487
     public void dismissModal_rejectIfRootIsNotSetAndSingleModalIsDisplayed() {
479
         disableModalAnimations(child1, child2);
488
         disableModalAnimations(child1, child2);

+ 0
- 41
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/stack/StackControllerTest.java View File

424
         });
424
         });
425
     }
425
     }
426
 
426
 
427
-    @Test
428
-    public void pop_specificWhenTopIsRegularPop() {
429
-        uut.push(child1, new CommandListenerAdapter());
430
-        uut.push(child2, new CommandListenerAdapter() {
431
-            @Override
432
-            public void onSuccess(String childId) {
433
-                uut.popSpecific(child2, new CommandListenerAdapter() {
434
-                    @Override
435
-                    public void onSuccess(String childId) {
436
-                        assertContainsOnlyId(child1.getId());
437
-                        assertIsChild(uut.getView(), child1.getView());
438
-                    }
439
-                });
440
-            }
441
-        });
442
-    }
443
-
444
     @Test
427
     @Test
445
     public void pop_appearingChildHasCorrectLayoutParams() {
428
     public void pop_appearingChildHasCorrectLayoutParams() {
446
         child2.options.animations.pop.enable = new Bool(false);
429
         child2.options.animations.pop.enable = new Bool(false);
460
                 .getHeight());
443
                 .getHeight());
461
     }
444
     }
462
 
445
 
463
-    @Test
464
-    public void popSpecific_deepInStack() {
465
-        uut.push(child1, new CommandListenerAdapter());
466
-        uut.push(child2, new CommandListenerAdapter());
467
-        assertIsChild(uut.getView(), child2.getView());
468
-        uut.popSpecific(child1, new CommandListenerAdapter());
469
-        assertContainsOnlyId(child2.getId());
470
-        assertIsChild(uut.getView(), child2.getView());
471
-    }
472
-
473
     @Test
446
     @Test
474
     public void popTo_PopsTopUntilControllerIsNewTop() {
447
     public void popTo_PopsTopUntilControllerIsNewTop() {
475
         uut.push(child1, new CommandListenerAdapter());
448
         uut.push(child1, new CommandListenerAdapter());
687
         });
660
         });
688
     }
661
     }
689
 
662
 
690
-    @Test
691
-    public void popSpecific_CallsDestroyOnPoppedChild() {
692
-        child1 = spy(child1);
693
-        child2 = spy(child2);
694
-        child3 = spy(child3);
695
-        uut.push(child1, new CommandListenerAdapter());
696
-        uut.push(child2, new CommandListenerAdapter());
697
-        uut.push(child3, new CommandListenerAdapter());
698
-
699
-        verify(child2, times(0)).destroy();
700
-        uut.popSpecific(child2, new CommandListenerAdapter());
701
-        verify(child2, times(1)).destroy();
702
-    }
703
-
704
     @Test
663
     @Test
705
     public void popTo_CallsDestroyOnPoppedChild() {
664
     public void popTo_CallsDestroyOnPoppedChild() {
706
         child1 = spy(child1);
665
         child1 = spy(child1);