Browse Source

Fix title component not being replaced via mergeOptions (#6066)

This commit fixes a bug related to setting a new react component as title via mergeOptions. When a title component was set, and the user tried to replace the existing title with a new title via mergeOptions, the new title was not created and instead the current title was reapplied.
Fixes #5377
Guy Carmeli 4 years ago
parent
commit
b0e8a824f5
No account linked to committer's email address

+ 21
- 12
lib/android/app/src/main/java/com/reactnativenavigation/presentation/StackPresenter.java View File

177
 
177
 
178
         if (topBarOptions.title.component.hasValue()) {
178
         if (topBarOptions.title.component.hasValue()) {
179
             if (titleControllers.containsKey(component)) {
179
             if (titleControllers.containsKey(component)) {
180
-                topBar.setTitleComponent(titleControllers.get(component).getView());
180
+                topBarController.setTitleComponent(titleControllers.get(component));
181
             } else {
181
             } else {
182
-                TitleBarReactViewController controller = new TitleBarReactViewController(activity, titleViewCreator);
182
+                TitleBarReactViewController controller = new TitleBarReactViewController(activity, titleViewCreator, topBarOptions.title.component);
183
                 controller.setWaitForRender(topBarOptions.title.component.waitForRender);
183
                 controller.setWaitForRender(topBarOptions.title.component.waitForRender);
184
                 titleControllers.put(component, controller);
184
                 titleControllers.put(component, controller);
185
-                controller.setComponent(topBarOptions.title.component);
186
                 controller.getView().setLayoutParams(getComponentLayoutParams(topBarOptions.title.component));
185
                 controller.getView().setLayoutParams(getComponentLayoutParams(topBarOptions.title.component));
187
-                topBar.setTitleComponent(controller.getView());
186
+                topBarController.setTitleComponent(controller);
188
             }
187
             }
189
         }
188
         }
190
 
189
 
415
         }
414
         }
416
 
415
 
417
         if (topBarOptions.title.height.hasValue()) topBar.setTitleHeight(topBarOptions.title.height.get());
416
         if (topBarOptions.title.height.hasValue()) topBar.setTitleHeight(topBarOptions.title.height.get());
418
-        if (topBarOptions.title.text.hasValue()) topBar.setTitle(topBarOptions.title.text.get());
419
         if (topBarOptions.title.topMargin.hasValue()) topBar.setTitleTopMargin(topBarOptions.title.topMargin.get());
417
         if (topBarOptions.title.topMargin.hasValue()) topBar.setTitleTopMargin(topBarOptions.title.topMargin.get());
420
 
418
 
421
         if (topBarOptions.title.component.hasValue()) {
419
         if (topBarOptions.title.component.hasValue()) {
422
-            if (titleControllers.containsKey(component)) {
423
-                topBar.setTitleComponent(titleControllers.get(component).getView());
424
-            } else {
425
-                TitleBarReactViewController controller = new TitleBarReactViewController(activity, titleViewCreator);
426
-                titleControllers.put(component, controller);
427
-                controller.setComponent(topBarOptions.title.component);
420
+            TitleBarReactViewController controller = findTitleComponent(topBarOptions.title.component);
421
+            if (controller == null) {
422
+                controller = new TitleBarReactViewController(activity, titleViewCreator, topBarOptions.title.component);
423
+                perform(titleControllers.put(component, controller), ViewController::destroy);
428
                 controller.getView().setLayoutParams(getComponentLayoutParams(topBarOptions.title.component));
424
                 controller.getView().setLayoutParams(getComponentLayoutParams(topBarOptions.title.component));
429
-                topBar.setTitleComponent(controller.getView());
430
             }
425
             }
426
+            topBarController.setTitleComponent(controller);
427
+        } else if (topBarOptions.title.text.hasValue()) {
428
+            perform(titleControllers.remove(component), ViewController::destroy);
429
+            topBar.setTitle(topBarOptions.title.text.get());
431
         }
430
         }
432
 
431
 
433
         if (topBarOptions.title.color.hasValue()) topBar.setTitleTextColor(topBarOptions.title.color.get());
432
         if (topBarOptions.title.color.hasValue()) topBar.setTitleTextColor(topBarOptions.title.color.get());
478
         }
477
         }
479
     }
478
     }
480
 
479
 
480
+    private TitleBarReactViewController findTitleComponent(com.reactnativenavigation.parse.Component component) {
481
+        for (TitleBarReactViewController controller : titleControllers.values()) {
482
+            if (ObjectUtils.equalsNotNull(controller.getComponent().name.get(null), component.name.get(null)) &&
483
+                ObjectUtils.equalsNotNull(controller.getComponent().componentId.get(null), component.componentId.get(null))) {
484
+                return controller;
485
+            }
486
+        }
487
+        return null;
488
+    }
489
+
481
     private void mergeTopTabsOptions(TopTabsOptions options) {
490
     private void mergeTopTabsOptions(TopTabsOptions options) {
482
         if (options.selectedTabColor.hasValue() && options.unselectedTabColor.hasValue()) topBar.applyTopTabsColors(options.selectedTabColor, options.unselectedTabColor);
491
         if (options.selectedTabColor.hasValue() && options.unselectedTabColor.hasValue()) topBar.applyTopTabsColors(options.selectedTabColor, options.unselectedTabColor);
483
         if (options.fontSize.hasValue()) topBar.applyTopTabsFontSize(options.fontSize);
492
         if (options.fontSize.hasValue()) topBar.applyTopTabsFontSize(options.fontSize);

+ 7
- 6
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/TitleBarReactViewController.java View File

13
 public class TitleBarReactViewController extends ViewController<TitleBarReactView> {
13
 public class TitleBarReactViewController extends ViewController<TitleBarReactView> {
14
 
14
 
15
     private final TitleBarReactViewCreator reactViewCreator;
15
     private final TitleBarReactViewCreator reactViewCreator;
16
-    private Component component;
16
+    private final Component component;
17
 
17
 
18
-    public TitleBarReactViewController(Activity activity, TitleBarReactViewCreator reactViewCreator) {
18
+    public Component getComponent() {
19
+        return component;
20
+    }
21
+
22
+    public TitleBarReactViewController(Activity activity, TitleBarReactViewCreator reactViewCreator, Component component) {
19
         super(activity, CompatUtils.generateViewId() + "", new YellowBoxDelegate(), new Options(), new ViewControllerOverlay(activity));
23
         super(activity, CompatUtils.generateViewId() + "", new YellowBoxDelegate(), new Options(), new ViewControllerOverlay(activity));
20
         this.reactViewCreator = reactViewCreator;
24
         this.reactViewCreator = reactViewCreator;
25
+        this.component = component;
21
     }
26
     }
22
 
27
 
23
     @Override
28
     @Override
49
     public String getCurrentComponentName() {
54
     public String getCurrentComponentName() {
50
         return null;
55
         return null;
51
     }
56
     }
52
-
53
-    public void setComponent(Component component) {
54
-        this.component = component;
55
-    }
56
 }
57
 }

+ 5
- 0
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/topbar/TopBarController.java View File

6
 import com.reactnativenavigation.anim.TopBarAnimator;
6
 import com.reactnativenavigation.anim.TopBarAnimator;
7
 import com.reactnativenavigation.parse.AnimationOptions;
7
 import com.reactnativenavigation.parse.AnimationOptions;
8
 import com.reactnativenavigation.utils.CompatUtils;
8
 import com.reactnativenavigation.utils.CompatUtils;
9
+import com.reactnativenavigation.viewcontrollers.TitleBarReactViewController;
9
 import com.reactnativenavigation.views.StackLayout;
10
 import com.reactnativenavigation.views.StackLayout;
10
 import com.reactnativenavigation.views.topbar.TopBar;
11
 import com.reactnativenavigation.views.topbar.TopBar;
11
 
12
 
93
         topBar.setRotationY(0);
94
         topBar.setRotationY(0);
94
         topBar.setRotation(0);
95
         topBar.setRotation(0);
95
     }
96
     }
97
+
98
+    public void setTitleComponent(TitleBarReactViewController component) {
99
+        topBar.setTitleComponent(component.getView());
100
+    }
96
 }
101
 }

+ 3
- 1
lib/android/app/src/main/java/com/reactnativenavigation/views/titlebar/TitleBar.java View File

29
 import static com.reactnativenavigation.utils.UiUtils.runOnPreDrawOnce;
29
 import static com.reactnativenavigation.utils.UiUtils.runOnPreDrawOnce;
30
 import static com.reactnativenavigation.utils.ViewUtils.findChildByClass;
30
 import static com.reactnativenavigation.utils.ViewUtils.findChildByClass;
31
 import static com.reactnativenavigation.utils.ViewUtils.findChildrenByClass;
31
 import static com.reactnativenavigation.utils.ViewUtils.findChildrenByClass;
32
+import static com.reactnativenavigation.utils.ViewUtils.removeFromParent;
32
 
33
 
33
 @SuppressLint("ViewConstructor")
34
 @SuppressLint("ViewConstructor")
34
 public class TitleBar extends Toolbar {
35
 public class TitleBar extends Toolbar {
70
     }
71
     }
71
 
72
 
72
     public void setComponent(View component) {
73
     public void setComponent(View component) {
74
+        if (this.component == component) return;
73
         clearTitle();
75
         clearTitle();
74
         clearSubtitle();
76
         clearSubtitle();
75
         this.component = component;
77
         this.component = component;
175
 
177
 
176
     private void clearComponent() {
178
     private void clearComponent() {
177
         if (component != null) {
179
         if (component != null) {
178
-            removeView(component);
180
+            removeFromParent(component);
179
             component = null;
181
             component = null;
180
         }
182
         }
181
     }
183
     }

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

35
         return button;
35
         return button;
36
     }
36
     }
37
 
37
 
38
+    public static Component titleComponent(String componentId) {
39
+        Component component = new Component();
40
+        component.componentId = new Text(componentId);
41
+        component.name = new Text(componentId);
42
+        return component;
43
+    }
44
+
38
     public static Button iconButton(String id, String icon) {
45
     public static Button iconButton(String id, String icon) {
39
         Button button = new Button();
46
         Button button = new Button();
40
         button.id = "someButton";
47
         button.id = "someButton";

+ 56
- 2
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/StackPresenterTest.java View File

84
     private Button textBtn2 = TitleBarHelper.textualButton("btn2");
84
     private Button textBtn2 = TitleBarHelper.textualButton("btn2");
85
     private Button componentBtn1 = TitleBarHelper.reactViewButton("btn1_");
85
     private Button componentBtn1 = TitleBarHelper.reactViewButton("btn1_");
86
     private Button componentBtn2 = TitleBarHelper.reactViewButton("btn2_");
86
     private Button componentBtn2 = TitleBarHelper.reactViewButton("btn2_");
87
+    private Component titleComponent1 = TitleBarHelper.titleComponent("component1");
88
+    private Component titleComponent2 = TitleBarHelper.titleComponent("component2");
87
     private TopBarController topBarController;
89
     private TopBarController topBarController;
88
     private ChildControllersRegistry childRegistry;
90
     private ChildControllersRegistry childRegistry;
89
-    private IconResolver iconResolver;
90
 
91
 
91
     @Override
92
     @Override
92
     public void beforeEach() {
93
     public void beforeEach() {
98
             }
99
             }
99
         };
100
         };
100
         renderChecker = spy(new RenderChecker());
101
         renderChecker = spy(new RenderChecker());
101
-        iconResolver = new IconResolver(activity, ImageLoaderMock.mock());
102
+        IconResolver iconResolver = new IconResolver(activity, ImageLoaderMock.mock());
102
         uut = spy(new StackPresenter(activity, titleViewCreator, new TopBarBackgroundViewCreatorMock(), new TopBarButtonCreatorMock(), iconResolver, renderChecker, new Options()));
103
         uut = spy(new StackPresenter(activity, titleViewCreator, new TopBarBackgroundViewCreatorMock(), new TopBarButtonCreatorMock(), iconResolver, renderChecker, new Options()));
103
         createTopBarController();
104
         createTopBarController();
104
 
105
 
309
         uut.mergeChildOptions(options, EMPTY_OPTIONS, parent, child);
310
         uut.mergeChildOptions(options, EMPTY_OPTIONS, parent, child);
310
     }
311
     }
311
 
312
 
313
+    @Test
314
+    public void applyTopBarOptions_setTitleComponent() {
315
+        Options applyComponent = new Options();
316
+        applyComponent.topBar.title.component.name = new Text("Component1");
317
+        applyComponent.topBar.title.component.componentId = new Text("Component1id");
318
+        uut.applyChildOptions(applyComponent, parent, child);
319
+        verify(topBarController).setTitleComponent(any());
320
+    }
321
+
322
+    @Test
323
+    public void mergeTopBarOptions_settingTitleDestroysComponent() {
324
+        Options componentOptions = new Options();
325
+        componentOptions.topBar.title.component = titleComponent1;
326
+        uut.applyChildOptions(componentOptions, parent, child);
327
+        ArgumentCaptor<TitleBarReactViewController> applyCaptor = ArgumentCaptor.forClass(TitleBarReactViewController.class);
328
+        verify(topBarController).setTitleComponent(applyCaptor.capture());
329
+
330
+        Options titleOptions = new Options();
331
+        titleOptions.topBar.title.text = new Text("Some title");
332
+        uut.mergeChildOptions(titleOptions, Options.EMPTY, parent, child);
333
+        assertThat(applyCaptor.getValue().isDestroyed()).isTrue();
334
+    }
335
+
336
+    @Test
337
+    public void mergeTopBarOptions_doesNotRecreateTitleComponentIfEquals() {
338
+        Options options = new Options();
339
+        options.topBar.title.component = titleComponent1;
340
+        uut.applyChildOptions(options, parent, child);
341
+        ArgumentCaptor<TitleBarReactViewController> applyCaptor = ArgumentCaptor.forClass(TitleBarReactViewController.class);
342
+        verify(topBarController).setTitleComponent(applyCaptor.capture());
343
+
344
+        uut.mergeChildOptions(options, Options.EMPTY, parent, child);
345
+        verify(topBarController, times(2)).setTitleComponent(applyCaptor.getValue());
346
+    }
347
+
348
+    @Test
349
+    public void mergeTopBarOptions_previousTitleComponentIsDestroyed() {
350
+        Options options = new Options();
351
+        options.topBar.title.component = titleComponent1;
352
+        uut.applyChildOptions(options, parent, child);
353
+        ArgumentCaptor<TitleBarReactViewController> applyCaptor = ArgumentCaptor.forClass(TitleBarReactViewController.class);
354
+        verify(topBarController).setTitleComponent(applyCaptor.capture());
355
+
356
+        Options toMerge = new Options();
357
+        toMerge.topBar.title.component = titleComponent2;
358
+        uut.mergeChildOptions(toMerge, Options.EMPTY, parent, child);
359
+        ArgumentCaptor<TitleBarReactViewController> mergeCaptor = ArgumentCaptor.forClass(TitleBarReactViewController.class);
360
+        verify(topBarController, times(2)).setTitleComponent(mergeCaptor.capture());
361
+
362
+        assertThat(applyCaptor.getValue()).isNotEqualTo(mergeCaptor.getValue());
363
+        assertThat(applyCaptor.getValue().isDestroyed()).isTrue();
364
+    }
365
+
312
     @Test
366
     @Test
313
     public void mergeTopTabsOptions() {
367
     public void mergeTopTabsOptions() {
314
         Options options = new Options();
368
         Options options = new Options();

+ 5
- 5
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/TitleBarReactViewControllerTest.java View File

23
     public void beforeEach() {
23
     public void beforeEach() {
24
         viewCreator = spy(new TitleBarReactViewCreatorMock());
24
         viewCreator = spy(new TitleBarReactViewCreatorMock());
25
         activity = newActivity();
25
         activity = newActivity();
26
-        uut = new TitleBarReactViewController(activity, viewCreator);
27
-        createComponent();
28
-        uut.setComponent(component);
26
+        component = createComponent();
27
+        uut = new TitleBarReactViewController(activity, viewCreator, component);
29
     }
28
     }
30
 
29
 
31
     @Test
30
     @Test
34
         verify(viewCreator).create(activity, component.componentId.get(), component.name.get());
33
         verify(viewCreator).create(activity, component.componentId.get(), component.name.get());
35
     }
34
     }
36
 
35
 
37
-    private void createComponent() {
38
-        component = new Component();
36
+    private Component createComponent() {
37
+        Component component = new Component();
39
         component.componentId = new Text("compId");
38
         component.componentId = new Text("compId");
40
         component.name = new Text("compName");
39
         component.name = new Text("compName");
40
+        return component;
41
     }
41
     }
42
 }
42
 }

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

124
         verify(uut).addView(component);
124
         verify(uut).addView(component);
125
     }
125
     }
126
 
126
 
127
+    @Test
128
+    public void setComponent_doesNothingIfComponentIsAlreadyAdded() {
129
+        View component = new View(activity);
130
+        uut.setComponent(component);
131
+
132
+        uut.setComponent(component);
133
+        verify(uut).addView(component);
134
+    }
135
+
127
     @Test
136
     @Test
128
     public void addView_overflowIsEnabledForButtonsContainer() {
137
     public void addView_overflowIsEnabledForButtonsContainer() {
129
         ActionMenuView buttonsContainer = mock(ActionMenuView.class);
138
         ActionMenuView buttonsContainer = mock(ActionMenuView.class);
141
         assertThat(uut.getTitle()).isNullOrEmpty();
150
         assertThat(uut.getTitle()).isNullOrEmpty();
142
         assertThat(uut.getMenu().size()).isZero();
151
         assertThat(uut.getMenu().size()).isZero();
143
         assertThat(uut.getNavigationIcon()).isNull();
152
         assertThat(uut.getNavigationIcon()).isNull();
144
-        verify(uut).removeView(title);
153
+        assertThat(title.getParent()).isNull();
145
     }
154
     }
146
 
155
 
147
     @Test
156
     @Test

+ 0
- 1
playground/src/commons/Options.js View File

3
 const { Dimensions } = require('react-native');
3
 const { Dimensions } = require('react-native');
4
 const height = Math.round(Dimensions.get('window').height);
4
 const height = Math.round(Dimensions.get('window').height);
5
 const width = Math.round(Dimensions.get('window').width);
5
 const width = Math.round(Dimensions.get('window').width);
6
-console.log('guyca', `height: ${height} width: ${width}`);
7
 const {
6
 const {
8
   useCustomAnimations,
7
   useCustomAnimations,
9
   useSlowOpenScreenAnimations,
8
   useSlowOpenScreenAnimations,