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 6 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
     }
28
     }
29
 
29
 
30
     public void mergeWith(SideMenuOptions other) {
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
 import android.support.v4.widget.DrawerLayout;
3
 import android.support.v4.widget.DrawerLayout;
4
 import android.view.Gravity;
4
 import android.view.Gravity;
5
 
5
 
6
+import com.reactnativenavigation.parse.Options;
6
 import com.reactnativenavigation.parse.SideMenuRootOptions;
7
 import com.reactnativenavigation.parse.SideMenuRootOptions;
7
 
8
 
8
 public class SideMenuPresenter {
9
 public class SideMenuPresenter {
13
         this.sideMenu = sideMenu;
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
         if (options.left.visible.isTrue()) {
53
         if (options.left.visible.isTrue()) {
43
             sideMenu.openDrawer(Gravity.LEFT);
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
             sideMenu.closeDrawer(Gravity.LEFT);
56
             sideMenu.closeDrawer(Gravity.LEFT);
47
         }
57
         }
48
 
58
 
49
         if (options.right.visible.isTrue()) {
59
         if (options.right.visible.isTrue()) {
50
             sideMenu.openDrawer(Gravity.RIGHT);
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
             sideMenu.closeDrawer(Gravity.RIGHT);
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
     @Override
72
     @Override
73
     public void applyChildOptions(Options options, Component child) {
73
     public void applyChildOptions(Options options, Component child) {
74
         super.applyChildOptions(options, child);
74
         super.applyChildOptions(options, child);
75
-        presenter.applyInitialOptions(options.sideMenuRootOptions);
75
+        presenter.applyChildOptions(resolveCurrentOptions());
76
         performOnParentController(parentController ->
76
         performOnParentController(parentController ->
77
                 ((ParentController) parentController).applyChildOptions(this.options, child)
77
                 ((ParentController) parentController).applyChildOptions(this.options, child)
78
         );
78
         );
81
     @Override
81
     @Override
82
     public void mergeChildOptions(Options options, ViewController childController, Component child) {
82
     public void mergeChildOptions(Options options, ViewController childController, Component child) {
83
         super.mergeChildOptions(options, childController, child);
83
         super.mergeChildOptions(options, childController, child);
84
-        presenter.present(options.sideMenuRootOptions);
84
+        presenter.mergeChildOptions(options.sideMenuRootOptions);
85
         performOnParentController(parentController ->
85
         performOnParentController(parentController ->
86
                 ((ParentController) parentController).mergeChildOptions(options.copy().clearSideMenuOptions(), childController, child)
86
                 ((ParentController) parentController).mergeChildOptions(options.copy().clearSideMenuOptions(), childController, child)
87
         );
87
         );
90
     @Override
90
     @Override
91
     public void mergeOptions(Options options) {
91
     public void mergeOptions(Options options) {
92
         super.mergeOptions(options);
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
     @Override
105
     @Override

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

12
 import com.reactnativenavigation.parse.SideMenuOptions;
12
 import com.reactnativenavigation.parse.SideMenuOptions;
13
 import com.reactnativenavigation.parse.params.Bool;
13
 import com.reactnativenavigation.parse.params.Bool;
14
 import com.reactnativenavigation.parse.params.Number;
14
 import com.reactnativenavigation.parse.params.Number;
15
+import com.reactnativenavigation.parse.params.Text;
15
 import com.reactnativenavigation.presentation.Presenter;
16
 import com.reactnativenavigation.presentation.Presenter;
16
 import com.reactnativenavigation.presentation.SideMenuPresenter;
17
 import com.reactnativenavigation.presentation.SideMenuPresenter;
17
 import com.reactnativenavigation.utils.CommandListenerAdapter;
18
 import com.reactnativenavigation.utils.CommandListenerAdapter;
18
 import com.reactnativenavigation.viewcontrollers.ChildControllersRegistry;
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
 import org.junit.Test;
24
 import org.junit.Test;
25
+import org.mockito.Mockito;
21
 
26
 
22
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
27
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
23
 import static org.assertj.core.api.Java6Assertions.assertThat;
28
 import static org.assertj.core.api.Java6Assertions.assertThat;
24
 import static org.mockito.ArgumentMatchers.any;
29
 import static org.mockito.ArgumentMatchers.any;
30
+import static org.mockito.ArgumentMatchers.eq;
25
 import static org.mockito.Mockito.spy;
31
 import static org.mockito.Mockito.spy;
26
 import static org.mockito.Mockito.times;
32
 import static org.mockito.Mockito.times;
27
 import static org.mockito.Mockito.verify;
33
 import static org.mockito.Mockito.verify;
32
     private Activity activity;
38
     private Activity activity;
33
     private ChildControllersRegistry childRegistry;
39
     private ChildControllersRegistry childRegistry;
34
     private SideMenuPresenter presenter;
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
     @Override
48
     @Override
40
     public void beforeEach() {
49
     public void beforeEach() {
41
         activity = newActivity();
50
         activity = newActivity();
42
         childRegistry = new ChildControllersRegistry();
51
         childRegistry = new ChildControllersRegistry();
43
         presenter = spy(new SideMenuPresenter());
52
         presenter = spy(new SideMenuPresenter());
53
+        child = new SimpleComponentViewController(activity, childRegistry, "child", new Options());
44
         left = new SimpleComponentViewController(activity, childRegistry, "left", new Options());
54
         left = new SimpleComponentViewController(activity, childRegistry, "left", new Options());
45
         right = new SimpleComponentViewController(activity, childRegistry, "right", new Options());
55
         right = new SimpleComponentViewController(activity, childRegistry, "right", new Options());
46
         center = spy(new SimpleComponentViewController(activity, childRegistry, "center", new Options()));
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
         uut.setCenterController(center);
64
         uut.setCenterController(center);
65
+        parent = Mockito.mock(ParentController.class);
66
+        uut.setParentController(parent);
49
     }
67
     }
50
 
68
 
51
     @Test
69
     @Test
54
         verify(presenter).bindView(uut.getView());
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
     @Test
82
     @Test
58
     public void mergeOptions_openLeftSideMenu() {
83
     public void mergeOptions_openLeftSideMenu() {
59
         uut.setLeftController(new SimpleComponentViewController(activity, childRegistry, "left", new Options()));
84
         uut.setLeftController(new SimpleComponentViewController(activity, childRegistry, "left", new Options()));
84
         assertThat(uut.options.sideMenuRootOptions).isNotEqualTo(initialOptions.sideMenuRootOptions);
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
     @Test
129
     @Test
88
     public void setLeftController_matchesParentByDefault() {
130
     public void setLeftController_matchesParentByDefault() {
89
         SideMenuOptions options = new SideMenuOptions();
131
         SideMenuOptions options = new SideMenuOptions();
98
         assertThat(params.width).isEqualTo(MATCH_PARENT);
140
         assertThat(params.width).isEqualTo(MATCH_PARENT);
99
         assertThat(params.height).isEqualTo(MATCH_PARENT);
141
         assertThat(params.height).isEqualTo(MATCH_PARENT);
100
     }
142
     }
143
+
101
     @Test
144
     @Test
102
     public void setLeftController_setHeightAndWidthWithOptions() {
145
     public void setLeftController_setHeightAndWidthWithOptions() {
103
         SideMenuOptions options = new SideMenuOptions();
146
         SideMenuOptions options = new SideMenuOptions();
115
         assertThat(params.width).isEqualTo(widthInDp);
158
         assertThat(params.width).isEqualTo(widthInDp);
116
         assertThat(params.height).isEqualTo(heightInDp);
159
         assertThat(params.height).isEqualTo(heightInDp);
117
     }
160
     }
161
+
118
     @Test
162
     @Test
119
     public void setRightController_matchesParentByDefault() {
163
     public void setRightController_matchesParentByDefault() {
120
         SideMenuOptions options = new SideMenuOptions();
164
         SideMenuOptions options = new SideMenuOptions();
129
         assertThat(params.width).isEqualTo(MATCH_PARENT);
173
         assertThat(params.width).isEqualTo(MATCH_PARENT);
130
         assertThat(params.height).isEqualTo(MATCH_PARENT);
174
         assertThat(params.height).isEqualTo(MATCH_PARENT);
131
     }
175
     }
176
+
132
     @Test
177
     @Test
133
     public void setRightController_setHeightAndWidthWithOptions() {
178
     public void setRightController_setHeightAndWidthWithOptions() {
134
         SideMenuOptions options = new SideMenuOptions();
179
         SideMenuOptions options = new SideMenuOptions();

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

1
 const React = require('react');
1
 const React = require('react');
2
 const { Component } = require('react');
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
 const { Navigation } = require('react-native-navigation');
6
 const { Navigation } = require('react-native-navigation');
7
 const testIDs = require('../testIDs');
7
 const testIDs = require('../testIDs');
31
   }
31
   }
32
 
32
 
33
   pushAndCloseSideMenu() {
33
   pushAndCloseSideMenu() {
34
-    this.hideSideMenu();
34
+    if (Platform.OS === 'ios') {
35
+      this.hideSideMenu();
36
+    }
35
     Navigation.push('tab1Stack', {
37
     Navigation.push('tab1Stack', {
36
       component: {
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
   }