Browse Source

Fix closing sideMenu when pushing a screen (#4491)

When pushing a screen into a stack in the centre controller with `sideMenu.left/right.visible: false` - side menu was not closed as expected.
This happened because when either side menus is open, it’s considered the current child and when options are resolved. the centre controller’s options were ignored.

This commit fixes this issue on Android. When resolving options, the centre controllers options are resolved as well.
Related to #4267
Guy Carmeli 5 years ago
parent
commit
dc739dee33
No account linked to committer's email address

+ 11
- 12
lib/android/app/src/main/java/com/reactnativenavigation/parse/SideMenuOptions.java View File

@@ -28,17 +28,16 @@ public class SideMenuOptions {
28 28
     }
29 29
 
30 30
     public void mergeWith(SideMenuOptions other) {
31
-        if (other.visible.hasValue()) {
32
-            visible = other.visible;
33
-        }
34
-        if (other.enabled.hasValue()) {
35
-            enabled = other.enabled;
36
-        }
37
-        if (other.height.hasValue()) {
38
-            height = other.height;
39
-        }
40
-        if (other.width.hasValue()) {
41
-            width = other.width;
42
-        }
31
+        if (other.visible.hasValue()) visible = other.visible;
32
+        if (other.enabled.hasValue()) enabled = other.enabled;
33
+        if (other.height.hasValue()) height = other.height;
34
+        if (other.width.hasValue()) width = other.width;
35
+    }
36
+
37
+    public void mergeWithDefault(SideMenuOptions defaultOptions) {
38
+        if (!visible.hasValue()) visible = defaultOptions.visible;
39
+        if (!enabled.hasValue()) enabled = defaultOptions.enabled;
40
+        if (!height.hasValue()) height = defaultOptions.height;
41
+        if (!width.hasValue()) width = defaultOptions.width;
43 42
     }
44 43
 }

+ 43
- 32
lib/android/app/src/main/java/com/reactnativenavigation/presentation/SideMenuPresenter.java View File

@@ -3,6 +3,7 @@ package com.reactnativenavigation.presentation;
3 3
 import android.support.v4.widget.DrawerLayout;
4 4
 import android.view.Gravity;
5 5
 
6
+import com.reactnativenavigation.parse.Options;
6 7
 import com.reactnativenavigation.parse.SideMenuRootOptions;
7 8
 
8 9
 public class SideMenuPresenter {
@@ -13,56 +14,66 @@ public class SideMenuPresenter {
13 14
         this.sideMenu = sideMenu;
14 15
     }
15 16
 
16
-    /**
17
-     * Called when initializing the sideMenu DrawerLayout.
18
-     *
19
-     * @param options Side menu options
20
-     */
21
-    public void applyInitialOptions(SideMenuRootOptions options) {
22
-        if (options.left.enabled.isFalse()) {
23
-            sideMenu.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED, Gravity.LEFT);
17
+    public boolean handleBack() {
18
+        if (sideMenu.isDrawerOpen(Gravity.LEFT)) {
19
+            sideMenu.closeDrawer(Gravity.LEFT);
20
+            return true;
24 21
         }
25
-        else  if (options.left.enabled.isTrue()) {
26
-            sideMenu.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED, Gravity.LEFT);
22
+        if (sideMenu.isDrawerOpen(Gravity.RIGHT)) {
23
+            sideMenu.closeDrawer(Gravity.RIGHT);
24
+            return true;
27 25
         }
26
+        return false;
27
+    }
28 28
 
29
-        if (options.right.enabled.isFalse()) {
30
-            sideMenu.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED, Gravity.RIGHT);
31
-        }
32
-        else  if (options.right.enabled.isTrue()) {
33
-            sideMenu.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED, Gravity.RIGHT);
34
-        }
29
+    public void mergeOptions(SideMenuRootOptions options) {
30
+        mergeLockMode(options);
31
+        mergeVisibility(options);
32
+    }
33
+
34
+    public void mergeChildOptions(SideMenuRootOptions options) {
35
+        mergeLockMode(options);
36
+        mergeVisibility(options);
37
+    }
38
+
39
+    public void applyChildOptions(Options options) {
40
+        applyLockMode(options.sideMenuRootOptions);
41
+        mergeVisibility(options.sideMenuRootOptions);
35 42
     }
36 43
 
37
-    public void present(SideMenuRootOptions options) {
38
-        // TODO: Not sure why we call these options when we show the DrawerLayout rather than when initializing it.
39
-        // TODO: (i.e. `setDrawerLockMode()` is supposed to be called when the DrawerLayout is initialized.
40
-        applyInitialOptions(options);
44
+    private void applyLockMode(SideMenuRootOptions options) {
45
+        int leftLockMode = options.left.enabled.isTrueOrUndefined() ? DrawerLayout.LOCK_MODE_UNLOCKED : DrawerLayout.LOCK_MODE_LOCKED_CLOSED;
46
+        sideMenu.setDrawerLockMode(leftLockMode, Gravity.LEFT);
47
+
48
+        int rightLockMode = options.right.enabled.isTrueOrUndefined() ? DrawerLayout.LOCK_MODE_UNLOCKED : DrawerLayout.LOCK_MODE_LOCKED_CLOSED;
49
+        sideMenu.setDrawerLockMode(rightLockMode, Gravity.RIGHT);
50
+    }
41 51
 
52
+    private void mergeVisibility(SideMenuRootOptions options) {
42 53
         if (options.left.visible.isTrue()) {
43 54
             sideMenu.openDrawer(Gravity.LEFT);
44
-
45
-        } else if (options.left.visible.isFalse() && sideMenu.isDrawerOpen(Gravity.LEFT)) {
55
+        } else if (options.left.visible.isFalse()) {
46 56
             sideMenu.closeDrawer(Gravity.LEFT);
47 57
         }
48 58
 
49 59
         if (options.right.visible.isTrue()) {
50 60
             sideMenu.openDrawer(Gravity.RIGHT);
51
-
52
-        } else if (options.right.visible.isFalse() && sideMenu.isDrawerOpen(Gravity.RIGHT)){
61
+        } else if (options.right.visible.isFalse()) {
53 62
             sideMenu.closeDrawer(Gravity.RIGHT);
54 63
         }
55 64
     }
56 65
 
57
-    public boolean handleBack() {
58
-        if (sideMenu.isDrawerOpen(Gravity.LEFT)) {
59
-            sideMenu.closeDrawer(Gravity.LEFT);
60
-            return true;
66
+    private void mergeLockMode(SideMenuRootOptions options) {
67
+        if (options.left.enabled.isFalse()) {
68
+            sideMenu.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED, Gravity.LEFT);
69
+        } else if (options.left.enabled.isTrue()) {
70
+            sideMenu.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED, Gravity.LEFT);
61 71
         }
62
-        if (sideMenu.isDrawerOpen(Gravity.RIGHT)) {
63
-            sideMenu.closeDrawer(Gravity.RIGHT);
64
-            return true;
72
+
73
+        if (options.right.enabled.isFalse()) {
74
+            sideMenu.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED, Gravity.RIGHT);
75
+        } else if (options.right.enabled.isTrue()) {
76
+            sideMenu.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED, Gravity.RIGHT);
65 77
         }
66
-        return false;
67 78
     }
68 79
 }

+ 12
- 3
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/sidemenu/SideMenuController.java View File

@@ -72,7 +72,7 @@ public class SideMenuController extends ParentController<DrawerLayout> {
72 72
     @Override
73 73
     public void applyChildOptions(Options options, Component child) {
74 74
         super.applyChildOptions(options, child);
75
-        presenter.applyInitialOptions(options.sideMenuRootOptions);
75
+        presenter.applyChildOptions(resolveCurrentOptions());
76 76
         performOnParentController(parentController ->
77 77
                 ((ParentController) parentController).applyChildOptions(this.options, child)
78 78
         );
@@ -81,7 +81,7 @@ public class SideMenuController extends ParentController<DrawerLayout> {
81 81
     @Override
82 82
     public void mergeChildOptions(Options options, ViewController childController, Component child) {
83 83
         super.mergeChildOptions(options, childController, child);
84
-        presenter.present(options.sideMenuRootOptions);
84
+        presenter.mergeChildOptions(options.sideMenuRootOptions);
85 85
         performOnParentController(parentController ->
86 86
                 ((ParentController) parentController).mergeChildOptions(options.copy().clearSideMenuOptions(), childController, child)
87 87
         );
@@ -90,7 +90,16 @@ public class SideMenuController extends ParentController<DrawerLayout> {
90 90
     @Override
91 91
     public void mergeOptions(Options options) {
92 92
         super.mergeOptions(options);
93
-        presenter.present(this.options.sideMenuRootOptions);
93
+        presenter.mergeOptions(options.sideMenuRootOptions);
94
+    }
95
+
96
+    @Override
97
+    public Options resolveCurrentOptions() {
98
+        Options options = super.resolveCurrentOptions();
99
+        if (getView().isDrawerOpen(Gravity.LEFT) || getView().isDrawerOpen(Gravity.RIGHT)) {
100
+            options = options.mergeWith(center.resolveCurrentOptions());
101
+        }
102
+        return options;
94 103
     }
95 104
 
96 105
     @Override

+ 49
- 4
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/sidemenu/SideMenuControllerTest.java View File

@@ -12,16 +12,22 @@ import com.reactnativenavigation.parse.Options;
12 12
 import com.reactnativenavigation.parse.SideMenuOptions;
13 13
 import com.reactnativenavigation.parse.params.Bool;
14 14
 import com.reactnativenavigation.parse.params.Number;
15
+import com.reactnativenavigation.parse.params.Text;
15 16
 import com.reactnativenavigation.presentation.Presenter;
16 17
 import com.reactnativenavigation.presentation.SideMenuPresenter;
17 18
 import com.reactnativenavigation.utils.CommandListenerAdapter;
18 19
 import com.reactnativenavigation.viewcontrollers.ChildControllersRegistry;
20
+import com.reactnativenavigation.viewcontrollers.ParentController;
21
+import com.reactnativenavigation.viewcontrollers.ViewController;
22
+import com.reactnativenavigation.views.Component;
19 23
 
20 24
 import org.junit.Test;
25
+import org.mockito.Mockito;
21 26
 
22 27
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
23 28
 import static org.assertj.core.api.Java6Assertions.assertThat;
24 29
 import static org.mockito.ArgumentMatchers.any;
30
+import static org.mockito.ArgumentMatchers.eq;
25 31
 import static org.mockito.Mockito.spy;
26 32
 import static org.mockito.Mockito.times;
27 33
 import static org.mockito.Mockito.verify;
@@ -32,20 +38,32 @@ public class SideMenuControllerTest extends BaseTest {
32 38
     private Activity activity;
33 39
     private ChildControllersRegistry childRegistry;
34 40
     private SideMenuPresenter presenter;
35
-    private SimpleComponentViewController left;
36
-    private SimpleComponentViewController right;
37
-    private SimpleComponentViewController center;
41
+    private ViewController left;
42
+    private ViewController right;
43
+    private ViewController center;
44
+    private ViewController child;
45
+    private ParentController parent;
46
+    private Options resolvedOptions;
38 47
 
39 48
     @Override
40 49
     public void beforeEach() {
41 50
         activity = newActivity();
42 51
         childRegistry = new ChildControllersRegistry();
43 52
         presenter = spy(new SideMenuPresenter());
53
+        child = new SimpleComponentViewController(activity, childRegistry, "child", new Options());
44 54
         left = new SimpleComponentViewController(activity, childRegistry, "left", new Options());
45 55
         right = new SimpleComponentViewController(activity, childRegistry, "right", new Options());
46 56
         center = spy(new SimpleComponentViewController(activity, childRegistry, "center", new Options()));
47
-        uut = new SideMenuController(activity, childRegistry, "sideMenu", new Options(), presenter, new Presenter(activity, new Options()));
57
+        uut = new SideMenuController(activity, childRegistry, "sideMenu", new Options(), presenter, new Presenter(activity, new Options())) {
58
+            @Override
59
+            public Options resolveCurrentOptions() {
60
+                resolvedOptions = super.resolveCurrentOptions();
61
+                return resolvedOptions;
62
+            }
63
+        };
48 64
         uut.setCenterController(center);
65
+        parent = Mockito.mock(ParentController.class);
66
+        uut.setParentController(parent);
49 67
     }
50 68
 
51 69
     @Test
@@ -54,6 +72,13 @@ public class SideMenuControllerTest extends BaseTest {
54 72
         verify(presenter).bindView(uut.getView());
55 73
     }
56 74
 
75
+    @Test
76
+    public void applyChildOptions() {
77
+        uut.applyChildOptions(new Options(), (Component) child.getView());
78
+        verify(presenter).applyChildOptions(eq(resolvedOptions));
79
+        verify(parent).applyChildOptions(uut.options, (Component) child.getView());
80
+    }
81
+
57 82
     @Test
58 83
     public void mergeOptions_openLeftSideMenu() {
59 84
         uut.setLeftController(new SimpleComponentViewController(activity, childRegistry, "left", new Options()));
@@ -84,6 +109,23 @@ public class SideMenuControllerTest extends BaseTest {
84 109
         assertThat(uut.options.sideMenuRootOptions).isNotEqualTo(initialOptions.sideMenuRootOptions);
85 110
     }
86 111
 
112
+    @Test
113
+    public void mergeChildOptions() {
114
+        Options options = new Options();
115
+        uut.mergeChildOptions(options, child, (Component) child.getView());
116
+        verify(presenter).mergeChildOptions(options.sideMenuRootOptions);
117
+    }
118
+
119
+    @Test
120
+    public void resolveCurrentOptions_centerOptionsAreMergedEvenIfDrawerIsOpen() {
121
+        uut.setLeftController(left);
122
+        center.options.topBar.title.text = new Text("Center");
123
+        assertThat(uut.resolveCurrentOptions().topBar.title.text.hasValue()).isTrue();
124
+
125
+        uut.getView().openDrawer(Gravity.LEFT);
126
+        assertThat(uut.resolveCurrentOptions().topBar.title.text.hasValue()).isTrue();
127
+    }
128
+
87 129
     @Test
88 130
     public void setLeftController_matchesParentByDefault() {
89 131
         SideMenuOptions options = new SideMenuOptions();
@@ -98,6 +140,7 @@ public class SideMenuControllerTest extends BaseTest {
98 140
         assertThat(params.width).isEqualTo(MATCH_PARENT);
99 141
         assertThat(params.height).isEqualTo(MATCH_PARENT);
100 142
     }
143
+
101 144
     @Test
102 145
     public void setLeftController_setHeightAndWidthWithOptions() {
103 146
         SideMenuOptions options = new SideMenuOptions();
@@ -115,6 +158,7 @@ public class SideMenuControllerTest extends BaseTest {
115 158
         assertThat(params.width).isEqualTo(widthInDp);
116 159
         assertThat(params.height).isEqualTo(heightInDp);
117 160
     }
161
+
118 162
     @Test
119 163
     public void setRightController_matchesParentByDefault() {
120 164
         SideMenuOptions options = new SideMenuOptions();
@@ -129,6 +173,7 @@ public class SideMenuControllerTest extends BaseTest {
129 173
         assertThat(params.width).isEqualTo(MATCH_PARENT);
130 174
         assertThat(params.height).isEqualTo(MATCH_PARENT);
131 175
     }
176
+
132 177
     @Test
133 178
     public void setRightController_setHeightAndWidthWithOptions() {
134 179
         SideMenuOptions options = new SideMenuOptions();

+ 13
- 3
playground/src/screens/SideMenuScreen.js View File

@@ -1,7 +1,7 @@
1 1
 const React = require('react');
2 2
 const { Component } = require('react');
3 3
 
4
-const { View, Text, Button } = require('react-native');
4
+const { View, Text, Button, Platform } = require('react-native');
5 5
 
6 6
 const { Navigation } = require('react-native-navigation');
7 7
 const testIDs = require('../testIDs');
@@ -31,10 +31,20 @@ class SideMenuScreen extends Component {
31 31
   }
32 32
 
33 33
   pushAndCloseSideMenu() {
34
-    this.hideSideMenu();
34
+    if (Platform.OS === 'ios') {
35
+      this.hideSideMenu();
36
+    }
35 37
     Navigation.push('tab1Stack', {
36 38
       component: {
37
-        name: 'navigation.playground.TextScreen'
39
+        name: 'navigation.playground.TextScreen',
40
+        options: {
41
+          sideMenu: {
42
+            left: {
43
+              visible: false,
44
+              enabled: false
45
+            }
46
+          }
47
+        }
38 48
       }
39 49
     });
40 50
   }