react-native-navigation 7.23.1-snapshot.288 → 7.23.1-snapshot.334

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/lib/android/app/src/main/java/com/reactnativenavigation/options/LayoutFactory.java +32 -17
  2. package/lib/android/app/src/main/java/com/reactnativenavigation/options/Options.java +1 -1
  3. package/lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/stack/StackController.java +1 -1
  4. package/lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/stack/StackPresenter.java +97 -123
  5. package/lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/stack/topbar/TopBarController.kt +183 -22
  6. package/lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/stack/topbar/button/ButtonController.kt +25 -4
  7. package/lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/stack/topbar/button/ButtonPresenter.kt +9 -1
  8. package/lib/android/app/src/main/java/com/reactnativenavigation/views/stack/topbar/titlebar/ButtonBar.kt +7 -12
  9. package/lib/android/app/src/main/java/com/reactnativenavigation/views/stack/topbar/titlebar/TitleAndButtonsContainer.kt +0 -2
  10. package/lib/android/app/src/main/java/com/reactnativenavigation/views/stack/topbar/titlebar/TitleSubTitleLayout.kt +0 -7
  11. package/lib/android/app/src/test/java/com/reactnativenavigation/utils/LayoutFactoryTest.java +25 -2
  12. package/lib/android/app/src/test/java/com/reactnativenavigation/utils/{TitleAndButtonsMeasurer.kt → TitleAndButtonsMeasurerTest.kt} +2 -2
  13. package/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/stack/StackPresenterTest.kt +54 -47
  14. package/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/stack/TopBarControllerTest.kt +217 -40
  15. package/lib/dist/commands/LayoutTreeCrawler.js +1 -1
  16. package/lib/dist/commands/OptionsProcessor.js +2 -2
  17. package/lib/dist/components/Store.d.ts +2 -0
  18. package/lib/dist/components/Store.js +8 -0
  19. package/lib/ios/RNNAssert.h +2 -2
  20. package/lib/ios/RNNBottomTabOptions.m +2 -1
  21. package/lib/ios/RNNButtonBuilder.m +3 -3
  22. package/lib/ios/RNNButtonOptions.m +4 -4
  23. package/lib/ios/RNNCommandsHandler.m +1 -4
  24. package/lib/ios/RNNDotIndicatorPresenter.m +2 -1
  25. package/lib/ios/RNNModalManager.h +2 -0
  26. package/lib/ios/RNNModalManager.m +5 -0
  27. package/lib/ios/RNNSegmentedControl.h +1 -1
  28. package/lib/ios/RNNTabBarItemCreator.m +3 -2
  29. package/lib/ios/RNNUIBarButtonItem.h +1 -1
  30. package/lib/ios/RNNUIBarButtonItem.m +3 -3
  31. package/lib/ios/TopBarPresenter.m +8 -4
  32. package/lib/src/commands/LayoutTreeCrawler.ts +1 -1
  33. package/lib/src/commands/OptionsProcessor.ts +2 -2
  34. package/lib/src/components/Store.ts +9 -0
  35. package/package.json +1 -1
@@ -1,9 +1,11 @@
1
1
  package com.reactnativenavigation.options;
2
2
 
3
3
  import android.app.Activity;
4
+ import android.content.Context;
4
5
 
5
6
  import com.facebook.react.ReactInstanceManager;
6
7
  import com.facebook.react.bridge.ReactContext;
8
+ import com.reactnativenavigation.NavigationApplication;
7
9
  import com.reactnativenavigation.options.parsers.TypefaceLoader;
8
10
  import com.reactnativenavigation.react.events.EventEmitter;
9
11
  import com.reactnativenavigation.utils.Assertions;
@@ -45,6 +47,8 @@ import androidx.annotation.RestrictTo;
45
47
  import static com.reactnativenavigation.options.Options.parse;
46
48
  import static com.reactnativenavigation.utils.CollectionUtils.*;
47
49
 
50
+ import org.json.JSONObject;
51
+
48
52
  public class LayoutFactory {
49
53
  private Activity activity;
50
54
  private ChildControllersRegistry childRegistry;
@@ -75,15 +79,15 @@ public class LayoutFactory {
75
79
  final ReactContext context = reactInstanceManager.getCurrentReactContext();
76
80
  switch (node.type) {
77
81
  case Component:
78
- return createComponent(context, node);
82
+ return createComponent(node);
79
83
  case ExternalComponent:
80
84
  return createExternalComponent(context, node);
81
85
  case Stack:
82
- return createStack(context, node);
86
+ return createStack(node);
83
87
  case BottomTabs:
84
- return createBottomTabs(context, node);
88
+ return createBottomTabs(node);
85
89
  case SideMenuRoot:
86
- return createSideMenuRoot(context, node);
90
+ return createSideMenuRoot(node);
87
91
  case SideMenuCenter:
88
92
  return createSideMenuContent(node);
89
93
  case SideMenuLeft:
@@ -91,17 +95,17 @@ public class LayoutFactory {
91
95
  case SideMenuRight:
92
96
  return createSideMenuRight(node);
93
97
  case TopTabs:
94
- return createTopTabs(context, node);
98
+ return createTopTabs(node);
95
99
  default:
96
100
  throw new IllegalArgumentException("Invalid node type: " + node.type);
97
101
  }
98
102
  }
99
103
 
100
- private ViewController<?> createSideMenuRoot(ReactContext context, LayoutNode node) {
104
+ private ViewController<?> createSideMenuRoot(LayoutNode node) {
101
105
  SideMenuController sideMenuController = new SideMenuController(activity,
102
106
  childRegistry,
103
107
  node.id,
104
- parse(context, typefaceManager, node.getOptions()),
108
+ parseOptions( node.getOptions()),
105
109
  new SideMenuPresenter(),
106
110
  new Presenter(activity, defaultOptions)
107
111
  );
@@ -153,7 +157,7 @@ public class LayoutFactory {
153
157
  return create(node.children.get(0));
154
158
  }
155
159
 
156
- private ViewController<?> createComponent(ReactContext context, LayoutNode node) {
160
+ private ViewController<?> createComponent(LayoutNode node) {
157
161
  String id = node.id;
158
162
  String name = node.data.optString("name");
159
163
  return new ComponentViewController(activity,
@@ -161,7 +165,7 @@ public class LayoutFactory {
161
165
  id,
162
166
  name,
163
167
  new ComponentViewCreator(reactInstanceManager),
164
- parse(context, typefaceManager, node.getOptions()),
168
+ parseOptions(node.getOptions()),
165
169
  new Presenter(activity, defaultOptions),
166
170
  new ComponentPresenter(defaultOptions)
167
171
  );
@@ -178,17 +182,17 @@ public class LayoutFactory {
178
182
  reactInstanceManager,
179
183
  new EventEmitter(context),
180
184
  new ExternalComponentPresenter(),
181
- parse(context, typefaceManager, node.getOptions())
185
+ parseOptions(node.getOptions())
182
186
  );
183
187
  }
184
188
 
185
- private ViewController<?> createStack(ReactContext context, LayoutNode node) {
189
+ private ViewController<?> createStack(LayoutNode node) {
186
190
  return new StackControllerBuilder(activity, eventEmitter)
187
191
  .setChildren(createChildren(node.children))
188
192
  .setChildRegistry(childRegistry)
189
193
  .setTopBarController(new TopBarController())
190
194
  .setId(node.id)
191
- .setInitialOptions(parse(context, typefaceManager, node.getOptions()))
195
+ .setInitialOptions(parseOptions(node.getOptions()))
192
196
  .setStackPresenter(new StackPresenter(activity,
193
197
  new TitleBarReactViewCreator(reactInstanceManager),
194
198
  new TopBarBackgroundViewCreator(reactInstanceManager),
@@ -210,7 +214,7 @@ public class LayoutFactory {
210
214
  return result;
211
215
  }
212
216
 
213
- private ViewController<?> createBottomTabs(ReactContext context, LayoutNode node) {
217
+ private ViewController<?> createBottomTabs(LayoutNode node) {
214
218
  List<ViewController<?>> tabs = map(node.children, this::create);
215
219
  BottomTabsPresenter bottomTabsPresenter = new BottomTabsPresenter(tabs, defaultOptions, new BottomTabsAnimator());
216
220
  return new BottomTabsController(activity,
@@ -219,24 +223,35 @@ public class LayoutFactory {
219
223
  eventEmitter,
220
224
  new ImageLoader(),
221
225
  node.id,
222
- parse(context, typefaceManager, node.getOptions()),
226
+ parseOptions( node.getOptions()),
223
227
  new Presenter(activity, defaultOptions),
224
228
  new BottomTabsAttacher(tabs, bottomTabsPresenter, defaultOptions),
225
229
  bottomTabsPresenter,
226
230
  new BottomTabPresenter(activity, tabs, new ImageLoader(), new TypefaceLoader(activity), defaultOptions));
227
231
  }
228
232
 
229
- private ViewController<?> createTopTabs(ReactContext context, LayoutNode node) {
233
+ private ViewController<?> createTopTabs(LayoutNode node) {
230
234
  final List<ViewController<?>> tabs = new ArrayList<>();
231
235
  for (int i = 0; i < node.children.size(); i++) {
232
236
  ViewController<?> tabController = create(node.children.get(i));
233
- Options options = parse(context, typefaceManager, node.children.get(i).getOptions());
237
+ Options options = parseOptions(node.children.get(i).getOptions());
234
238
  options.setTopTabIndex(i);
235
239
  tabs.add(tabController);
236
240
  }
237
- return new TopTabsController(activity, childRegistry, node.id, tabs, new TopTabsLayoutCreator(activity, tabs), parse(context, typefaceManager, node.getOptions()), new Presenter(activity, defaultOptions));
241
+ return new TopTabsController(activity, childRegistry, node.id, tabs, new TopTabsLayoutCreator(activity, tabs)
242
+ , parseOptions(node.getOptions()), new Presenter(activity, defaultOptions));
238
243
  }
239
244
 
245
+ private Options parseOptions(JSONObject jsonOptions) {
246
+ Context context = reactInstanceManager.getCurrentReactContext();
247
+ if (context == null) {
248
+ context = activity == null ? NavigationApplication.instance : activity;
249
+ }
250
+ if (typefaceManager == null) {
251
+ typefaceManager = new TypefaceLoader(context);
252
+ }
253
+ return parse(context, typefaceManager, jsonOptions);
254
+ }
240
255
  @NonNull
241
256
  @RestrictTo(RestrictTo.Scope.TESTS)
242
257
  public Options getDefaultOptions() {
@@ -15,7 +15,7 @@ public class Options {
15
15
  public static final Options EMPTY = new Options();
16
16
 
17
17
  @NonNull
18
- public static Options parse(Context context, TypefaceLoader typefaceManager, JSONObject json) {
18
+ public static Options parse(@NonNull Context context, TypefaceLoader typefaceManager, JSONObject json) {
19
19
  Options result = new Options();
20
20
  if (json == null) return result;
21
21
 
@@ -68,7 +68,7 @@ public class StackController extends ParentController<StackLayout> {
68
68
  @Override
69
69
  public void onConfigurationChanged(Configuration newConfig) {
70
70
  super.onConfigurationChanged(newConfig);
71
- presenter.onConfigurationChanged(resolveCurrentOptions());
71
+ presenter.onConfigurationChanged(resolveCurrentOptions(), getCurrentChild());
72
72
  fabPresenter.onConfigurationChanged(resolveCurrentOptions());
73
73
  }
74
74
 
@@ -1,9 +1,17 @@
1
1
  package com.reactnativenavigation.viewcontrollers.stack;
2
2
 
3
+ import static com.reactnativenavigation.utils.CollectionUtils.filter;
4
+ import static com.reactnativenavigation.utils.CollectionUtils.forEach;
5
+ import static com.reactnativenavigation.utils.CollectionUtils.isNullOrEmpty;
6
+ import static com.reactnativenavigation.utils.CollectionUtils.merge;
7
+ import static com.reactnativenavigation.utils.ObjectUtils.perform;
8
+ import static com.reactnativenavigation.viewcontrollers.stack.topbar.TopBarControllerKt.DEFAULT_BORDER_COLOR;
9
+
3
10
  import android.animation.Animator;
4
11
  import android.app.Activity;
5
12
  import android.graphics.Color;
6
13
  import android.view.View;
14
+ import android.view.ViewGroup;
7
15
  import android.view.ViewGroup.LayoutParams;
8
16
  import android.view.ViewGroup.MarginLayoutParams;
9
17
  import android.widget.FrameLayout;
@@ -47,24 +55,11 @@ import com.reactnativenavigation.views.stack.topbar.titlebar.TitleBarReactViewCr
47
55
  import java.util.ArrayList;
48
56
  import java.util.Collections;
49
57
  import java.util.HashMap;
50
- import java.util.LinkedHashMap;
51
58
  import java.util.List;
52
59
  import java.util.Map;
53
60
  import java.util.Objects;
54
61
 
55
- import static com.reactnativenavigation.utils.CollectionUtils.difference;
56
- import static com.reactnativenavigation.utils.CollectionUtils.filter;
57
- import static com.reactnativenavigation.utils.CollectionUtils.first;
58
- import static com.reactnativenavigation.utils.CollectionUtils.forEach;
59
- import static com.reactnativenavigation.utils.CollectionUtils.getOrDefault;
60
- import static com.reactnativenavigation.utils.CollectionUtils.isNullOrEmpty;
61
- import static com.reactnativenavigation.utils.CollectionUtils.keyBy;
62
- import static com.reactnativenavigation.utils.CollectionUtils.merge;
63
- import static com.reactnativenavigation.utils.ObjectUtils.perform;
64
- import static com.reactnativenavigation.utils.ObjectUtils.take;
65
-
66
62
  public class StackPresenter {
67
- private static final int DEFAULT_BORDER_COLOR = Color.BLACK;
68
63
  private static final double DEFAULT_ELEVATION = 4d;
69
64
  private final Activity activity;
70
65
 
@@ -79,12 +74,10 @@ public class StackPresenter {
79
74
  private final TitleBarButtonCreator buttonCreator;
80
75
  private Options defaultOptions;
81
76
 
82
- private List<ButtonController> currentRightButtons = new ArrayList<>();
83
- private List<ButtonController> currentLeftButtons = new ArrayList<>();
84
77
  private final Map<View, TitleBarReactViewController> titleControllers = new HashMap();
85
78
  private final Map<View, TopBarBackgroundViewController> backgroundControllers = new HashMap();
86
- private final Map<View, Map<String, ButtonController>> componentRightButtons = new HashMap();
87
- private final Map<View, Map<String, ButtonController>> componentLeftButtons = new HashMap();
79
+ private final Map<View, Map<String, ButtonController>> rightButtonControllers = new HashMap();
80
+ private final Map<View, Map<String, ButtonController>> leftButtonControllers = new HashMap();
88
81
  private final IconResolver iconResolver;
89
82
  private final TypefaceLoader typefaceLoader;
90
83
 
@@ -126,8 +119,8 @@ public class StackPresenter {
126
119
 
127
120
  public boolean isRendered(View component) {
128
121
  ArrayList<ViewController<?>> controllers = new ArrayList<>();
129
- controllers.addAll(perform(componentRightButtons.get(component), new ArrayList<>(), Map::values));
130
- controllers.addAll(perform(componentLeftButtons.get(component), new ArrayList<>(), Map::values));
122
+ controllers.addAll(perform(rightButtonControllers.get(component), new ArrayList<>(), Map::values));
123
+ controllers.addAll(perform(leftButtonControllers.get(component), new ArrayList<>(), Map::values));
131
124
  controllers.add(backgroundControllers.get(component));
132
125
  controllers.add(titleControllers.get(component));
133
126
  return renderChecker.areRendered(filter(controllers, ObjectUtils::notNull));
@@ -136,29 +129,21 @@ public class StackPresenter {
136
129
  public void mergeOptions(Options options, StackController stack, ViewController<?> currentChild) {
137
130
  TopBarOptions resolvedTopBarOptions = options.topBar.copy().mergeWithDefault(stack.resolveChildOptions(currentChild).topBar).mergeWithDefault(defaultOptions.topBar);
138
131
  mergeOrientation(options.layout.orientation);
139
- // mergeButtons(topBar, withDefault.topBar.buttons, child);
140
132
  mergeTopBarOptions(resolvedTopBarOptions, options, stack, currentChild);
141
133
  mergeTopTabsOptions(options.topTabs);
142
134
  mergeTopTabOptions(options.topTabOptions);
143
135
  }
144
136
 
145
- public void onConfigurationChanged(Options options) {
137
+ public void onConfigurationChanged(Options options, ViewController<?> currentChild) {
146
138
  if (topBar == null) return;
147
139
  Options withDefault = options.copy().withDefaultOptions(defaultOptions);
148
- if (currentRightButtons != null && !currentRightButtons.isEmpty())
149
- topBarController.applyRightButtons(currentRightButtons);
150
- if (currentLeftButtons != null && !currentLeftButtons.isEmpty())
151
- topBarController.applyLeftButtons(currentLeftButtons);
152
140
  if (withDefault.topBar.buttons.back.visible.isTrue()) {
153
- topBar.setBackButton(createButtonController(withDefault.topBar.buttons.back));
141
+ topBarController.setBackButton(createButtonController(withDefault.topBar.buttons.back));
154
142
  }
155
- topBar.setOverflowButtonColor(withDefault.topBar.rightButtonColor.get(Color.BLACK));
156
- topBar.applyTopTabsColors(withDefault.topTabs.selectedTabColor,
157
- withDefault.topTabs.unselectedTabColor);
158
- topBar.setBorderColor(withDefault.topBar.borderColor.get(DEFAULT_BORDER_COLOR));
159
- topBar.setBackgroundColor(withDefault.topBar.background.color.get(Color.WHITE));
160
- topBar.setTitleTextColor(withDefault.topBar.title.color.get(TopBar.DEFAULT_TITLE_COLOR));
161
- topBar.setSubtitleColor(withDefault.topBar.subtitle.color.get(TopBar.DEFAULT_TITLE_COLOR));
143
+ topBarController.onConfigurationChanged(withDefault,
144
+ leftButtonControllers.get(currentChild.getView()),
145
+ rightButtonControllers.get(currentChild.getView()));
146
+
162
147
  }
163
148
 
164
149
  public void applyInitialChildLayoutOptions(Options options) {
@@ -183,10 +168,10 @@ public class StackPresenter {
183
168
  public void onChildDestroyed(ViewController<?> child) {
184
169
  perform(titleControllers.remove(child.getView()), TitleBarReactViewController::destroy);
185
170
  perform(backgroundControllers.remove(child.getView()), TopBarBackgroundViewController::destroy);
186
- destroyButtons(componentRightButtons.get(child.getView()));
187
- destroyButtons(componentLeftButtons.get(child.getView()));
188
- componentRightButtons.remove(child.getView());
189
- componentLeftButtons.remove(child.getView());
171
+ destroyButtons(rightButtonControllers.get(child.getView()));
172
+ destroyButtons(leftButtonControllers.get(child.getView()));
173
+ rightButtonControllers.remove(child.getView());
174
+ leftButtonControllers.remove(child.getView());
190
175
  }
191
176
 
192
177
  private void destroyButtons(@Nullable Map<String, ButtonController> buttons) {
@@ -258,7 +243,7 @@ public class StackPresenter {
258
243
  }
259
244
 
260
245
  private void applyStatusBarDrawBehindOptions(TopBarOptions topBarOptions, Options withDefault) {
261
- if(withDefault.statusBar.visible.isTrueOrUndefined() && withDefault.statusBar.drawBehind.isTrue()){
246
+ if (withDefault.statusBar.visible.isTrueOrUndefined() && withDefault.statusBar.drawBehind.isTrue()) {
262
247
  topBar.setTopPadding(StatusBarUtils.getStatusBarHeight(activity));
263
248
  topBar.setHeight(topBarOptions.height.get(UiUtils.getTopBarHeightDp(activity)) + StatusBarUtils.getStatusBarHeightDp(activity));
264
249
  } else {
@@ -268,8 +253,8 @@ public class StackPresenter {
268
253
  }
269
254
 
270
255
  private void mergeStatusBarDrawBehindOptions(TopBarOptions topBarOptions, Options toMerge) {
271
- if(toMerge.statusBar.drawBehind.hasValue()){
272
- if(toMerge.statusBar.visible.isTrueOrUndefined() && toMerge.statusBar.drawBehind.isTrue()){
256
+ if (toMerge.statusBar.drawBehind.hasValue()) {
257
+ if (toMerge.statusBar.visible.isTrueOrUndefined() && toMerge.statusBar.drawBehind.isTrue()) {
273
258
  topBar.setTopPadding(StatusBarUtils.getStatusBarHeight(activity));
274
259
  topBar.setHeight(topBarOptions.height.get(UiUtils.getTopBarHeightDp(activity)) + StatusBarUtils.getStatusBarHeightDp(activity));
275
260
  } else {
@@ -304,59 +289,21 @@ public class StackPresenter {
304
289
  }
305
290
 
306
291
  private void applyButtons(TopBarOptions options, ViewController<?> child) {
307
- if (options.buttons.right != null) {
308
- List<ButtonOptions> rightButtons = mergeButtonsWithColor(options.buttons.right,
309
- options.rightButtonColor
310
- , options.rightButtonDisabledColor);
311
- List<ButtonController> rightButtonControllers = getOrCreateButtonControllersByInstanceId(componentRightButtons.get(child.getView()), rightButtons);
312
- componentRightButtons.put(child.getView(), keyBy(rightButtonControllers, ButtonController::getButtonInstanceId));
313
- if (!CollectionUtils.equals(currentRightButtons, rightButtonControllers)) {
314
- currentRightButtons = rightButtonControllers;
315
- topBarController.applyRightButtons(currentRightButtons);
316
- }
317
- } else {
318
- currentRightButtons = null;
319
- topBar.clearRightButtons();
320
- }
321
-
322
- if (options.buttons.left != null) {
323
- List<ButtonOptions> leftButtons = mergeButtonsWithColor(options.buttons.left,
324
- options.leftButtonColor,
325
- options.leftButtonDisabledColor);
326
- List<ButtonController> leftButtonControllers = getOrCreateButtonControllersByInstanceId(componentLeftButtons.get(child.getView()), leftButtons);
327
- componentLeftButtons.put(child.getView(), keyBy(leftButtonControllers, ButtonController::getButtonInstanceId));
328
- if (!CollectionUtils.equals(currentLeftButtons, leftButtonControllers)) {
329
- currentLeftButtons = leftButtonControllers;
330
- topBarController.applyLeftButtons(currentLeftButtons);
331
- }
332
- } else {
333
- currentLeftButtons = null;
334
- topBar.clearLeftButtons();
335
- }
336
-
337
- if (options.buttons.back.visible.isTrue() && !options.buttons.hasLeftButtons()) {
338
- topBar.setBackButton(createButtonController(options.buttons.back));
339
- }
292
+ //should be at first in order for next actions to be animated
340
293
  if (options.animateRightButtons.hasValue())
341
- topBar.animateRightButtons(options.animateRightButtons.isTrue());
294
+ topBarController.animateRightButtons(options.animateRightButtons.isTrue());
342
295
  if (options.animateLeftButtons.hasValue())
343
- topBar.animateLeftButtons(options.animateLeftButtons.isTrue());
344
- topBar.setOverflowButtonColor(options.rightButtonColor.get(Color.BLACK));
345
- }
296
+ topBarController.animateLeftButtons(options.animateLeftButtons.isTrue());
346
297
 
347
- private List<ButtonController> getOrCreateButtonControllersByInstanceId(@Nullable Map<String, ButtonController> currentButtons, @Nullable List<ButtonOptions> buttons) {
348
- if (buttons == null) return null;
349
- Map<String, ButtonController> result = new LinkedHashMap<>();
350
- forEach(buttons, b -> result.put(b.instanceId, getOrDefault(currentButtons, b.instanceId, () -> createButtonController(b))));
351
- return new ArrayList<>(result.values());
352
- }
298
+ applyRightButtonsOptions(options, child);
353
299
 
354
- private List<ButtonController> getOrCreateButtonControllers(@Nullable Map<String, ButtonController> currentButtons, @NonNull List<ButtonOptions> buttons) {
355
- ArrayList<ButtonController> result = new ArrayList<>();
356
- for (ButtonOptions b : buttons) {
357
- result.add(take(first(perform(currentButtons, null, Map::values), button -> button.getButton().equals(b)), createButtonController(b)));
300
+ applyLeftButtonsOptions(options, child);
301
+
302
+ if (options.buttons.back.visible.isTrue() && !options.buttons.hasLeftButtons()) {
303
+ topBarController.setBackButton(createButtonController(options.buttons.back));
358
304
  }
359
- return result;
305
+
306
+ topBar.setOverflowButtonColor(options.rightButtonColor.get(Color.BLACK));
360
307
  }
361
308
 
362
309
  private ButtonController createButtonController(ButtonOptions button) {
@@ -419,8 +366,13 @@ public class StackPresenter {
419
366
  }
420
367
 
421
368
  private void mergeButtons(TopBarOptions options, TopBarOptions optionsToMerge, View child, StackController stack) {
422
- mergeRightButtons(options, optionsToMerge.buttons, child);
423
- mergeLeftButton(options, optionsToMerge.buttons, child);
369
+ if (optionsToMerge.animateRightButtons.hasValue())
370
+ topBarController.animateRightButtons(optionsToMerge.animateRightButtons.isTrue());
371
+ if (optionsToMerge.animateLeftButtons.hasValue())
372
+ topBarController.animateLeftButtons(optionsToMerge.animateLeftButtons.isTrue());
373
+
374
+ mergeRightButtonsOptions(options, optionsToMerge.buttons, child);
375
+ mergeLeftButtonsOptions(options, optionsToMerge.buttons, child);
424
376
  mergeLeftButtonsColor(child, optionsToMerge.leftButtonColor, optionsToMerge.leftButtonDisabledColor);
425
377
  mergeRightButtonsColor(child, optionsToMerge.rightButtonColor, optionsToMerge.rightButtonDisabledColor);
426
378
  mergeBackButton(optionsToMerge.buttons, stack);
@@ -428,7 +380,7 @@ public class StackPresenter {
428
380
 
429
381
  private void mergeLeftButtonsColor(View child, ThemeColour color, ThemeColour disabledColor) {
430
382
  if (color.hasValue() || disabledColor.hasValue()) {
431
- Map<String, ButtonController> stringButtonControllerMap = componentLeftButtons.get(child);
383
+ Map<String, ButtonController> stringButtonControllerMap = leftButtonControllers.get(child);
432
384
  if (stringButtonControllerMap != null) {
433
385
  forEach(stringButtonControllerMap.values(), (btnController) -> {
434
386
  if (color.hasValue()) {
@@ -444,7 +396,7 @@ public class StackPresenter {
444
396
 
445
397
  private void mergeRightButtonsColor(View child, ThemeColour color, ThemeColour disabledColor) {
446
398
  if (color.hasValue() || disabledColor.hasValue()) {
447
- Map<String, ButtonController> stringButtonControllerMap = componentRightButtons.get(child);
399
+ Map<String, ButtonController> stringButtonControllerMap = rightButtonControllers.get(child);
448
400
  if (stringButtonControllerMap != null) {
449
401
  forEach(stringButtonControllerMap.values(), (btnController) -> {
450
402
  if (color.hasValue()) {
@@ -458,40 +410,65 @@ public class StackPresenter {
458
410
  }
459
411
  }
460
412
 
461
- private void mergeRightButtons(TopBarOptions options, TopBarButtons buttons, View child) {
462
- if (buttons.right == null) return;
463
- List<ButtonOptions> rightButtons = mergeButtonsWithColor(buttons.right, options.rightButtonColor, options.rightButtonDisabledColor);
464
- List<ButtonController> toMerge = getOrCreateButtonControllers(componentRightButtons.get(child), rightButtons);
465
- List<ButtonController> toRemove = difference(currentRightButtons, toMerge, ButtonController::areButtonsEqual);
466
- forEach(toRemove, ButtonController::destroy);
413
+ private void applyLeftButtonsOptions(TopBarOptions options, ViewController<?> child) {
414
+ if (options.buttons.left != null) {
415
+ List<ButtonOptions> leftButtons = mergeButtonsWithColor(options.buttons.left,
416
+ options.leftButtonColor
417
+ , options.leftButtonDisabledColor);
418
+ final ViewGroup childView = child.getView();
419
+ final Map<String, ButtonController> btnControllers = getOrCreateButtonControllerMap(childView, leftButtonControllers);
420
+ topBarController.applyLeftButtonsOptions(btnControllers, leftButtons, this::createButtonController);
421
+ } else {
422
+ topBarController.clearLeftButtons();
423
+ }
424
+ }
467
425
 
468
- if (!CollectionUtils.equals(currentRightButtons, toMerge)) {
469
- componentRightButtons.put(child, keyBy(toMerge, ButtonController::getButtonInstanceId));
470
- topBarController.mergeRightButtons(toMerge, toRemove);
471
- currentRightButtons = toMerge;
426
+ private void applyRightButtonsOptions(TopBarOptions options, ViewController<?> child) {
427
+ if (options.buttons.right != null) {
428
+ List<ButtonOptions> rightButtons = mergeButtonsWithColor(options.buttons.right,
429
+ options.rightButtonColor
430
+ , options.rightButtonDisabledColor);
431
+ final ViewGroup childView = child.getView();
432
+ final Map<String, ButtonController> btnControllers = getOrCreateButtonControllerMap(childView, rightButtonControllers);
433
+ topBarController.applyRightButtonsOptions(btnControllers, rightButtons, this::createButtonController);
434
+ } else {
435
+ topBarController.clearRightButtons();
472
436
  }
437
+ }
438
+
439
+ private void mergeRightButtonsOptions(TopBarOptions options, TopBarButtons buttons, View child) {
440
+ if (buttons.right == null) return;
441
+ List<ButtonOptions> rightButtons = mergeButtonsWithColor(buttons.right, options.rightButtonColor,
442
+ options.rightButtonDisabledColor);
443
+ final Map<String, ButtonController> btnControllers = getOrCreateButtonControllerMap(child, rightButtonControllers);
444
+ topBarController.mergeRightButtonsOptions(btnControllers, rightButtons, this::createButtonController);
473
445
  if (options.rightButtonColor.hasValue()) topBar.setOverflowButtonColor(options.rightButtonColor.get());
474
446
  }
475
447
 
476
- private void mergeLeftButton(TopBarOptions options, TopBarButtons buttons, View child) {
448
+ private void mergeLeftButtonsOptions(TopBarOptions options, TopBarButtons buttons, View child) {
477
449
  if (buttons.left == null) return;
478
- List<ButtonOptions> leftButtons = mergeButtonsWithColor(buttons.left, options.leftButtonColor, options.leftButtonDisabledColor);
479
- List<ButtonController> toMerge = getOrCreateButtonControllers(componentLeftButtons.get(child), leftButtons);
480
- List<ButtonController> toRemove = difference(currentLeftButtons, toMerge, ButtonController::areButtonsEqual);
481
- forEach(toRemove, ButtonController::destroy);
482
- if (!CollectionUtils.equals(currentLeftButtons, toMerge)) {
483
- componentLeftButtons.put(child, keyBy(toMerge, ButtonController::getButtonInstanceId));
484
- topBarController.mergeLeftButtons(toMerge, toRemove);
485
- currentLeftButtons = toMerge;
486
- }
450
+ List<ButtonOptions> leftButtons = mergeButtonsWithColor(buttons.left, options.leftButtonColor,
451
+ options.leftButtonDisabledColor);
452
+ final Map<String, ButtonController> btnControllers = getOrCreateButtonControllerMap(child, leftButtonControllers);
453
+ topBarController.mergeLeftButtonsOptions(btnControllers, leftButtons, this::createButtonController);
454
+ if (options.leftButtonColor.hasValue()) topBar.setOverflowButtonColor(options.leftButtonColor.get());
455
+ }
456
+
457
+ @NonNull
458
+ private Map<String, ButtonController> getOrCreateButtonControllerMap(View child, Map<View, Map<String, ButtonController>> buttonControllers) {
459
+ final Map<String, ButtonController> controllerMap = buttonControllers.get(child);
460
+ final Map<String, ButtonController> btnControllers = controllerMap != null ? controllerMap : new HashMap<>();
461
+ if (controllerMap == null)
462
+ buttonControllers.put(child, btnControllers);
463
+ return btnControllers;
487
464
  }
488
465
 
489
466
  private void mergeBackButton(TopBarButtons buttons, StackController stack) {
490
467
  if (buttons.back.hasValue() && isNullOrEmpty(buttons.left)) {
491
468
  if (buttons.back.visible.isFalse()) {
492
- topBar.clearBackButton();
469
+ topBarController.clearBackButton();
493
470
  } else if (stack.size() > 1) {
494
- topBar.setBackButton(createButtonController(buttons.back));
471
+ topBarController.setBackButton(createButtonController(buttons.back));
495
472
  }
496
473
  }
497
474
  }
@@ -517,13 +494,10 @@ public class StackPresenter {
517
494
  if (topBarOptions.topMargin.hasValue() && topBar.getLayoutParams() instanceof MarginLayoutParams) {
518
495
  ((MarginLayoutParams) topBar.getLayoutParams()).topMargin = UiUtils.dpToPx(activity, topBarOptions.topMargin.get());
519
496
  }
520
- mergeStatusBarDrawBehindOptions(resolveOptions,options);
497
+ mergeStatusBarDrawBehindOptions(resolveOptions, options);
521
498
  if (topBarOptions.title.height.hasValue()) topBar.setTitleHeight(topBarOptions.title.height.get());
522
499
  if (topBarOptions.title.topMargin.hasValue()) topBar.setTitleTopMargin(topBarOptions.title.topMargin.get());
523
- if (topBarOptions.animateLeftButtons.hasValue())
524
- topBar.animateLeftButtons(topBarOptions.animateLeftButtons.isTrue());
525
- if (topBarOptions.animateRightButtons.hasValue())
526
- topBar.animateRightButtons(topBarOptions.animateRightButtons.isTrue());
500
+
527
501
  if (topBarOptions.title.component.hasValue()) {
528
502
  TitleBarReactViewController controller = findTitleComponent(topBarOptions.title.component);
529
503
  if (controller == null) {
@@ -646,8 +620,8 @@ public class StackPresenter {
646
620
 
647
621
  @RestrictTo(RestrictTo.Scope.TESTS)
648
622
  public void setComponentsButtonController(View child, ButtonController rightController, ButtonController leftController) {
649
- forEach(componentLeftButtons.get(child).keySet(), (key) -> componentLeftButtons.get(child).put(key, leftController));
650
- forEach(componentRightButtons.get(child).keySet(), (key) -> componentRightButtons.get(child).put(key, rightController));
623
+ forEach(leftButtonControllers.get(child).keySet(), (key) -> leftButtonControllers.get(child).put(key, leftController));
624
+ forEach(rightButtonControllers.get(child).keySet(), (key) -> rightButtonControllers.get(child).put(key, rightController));
651
625
  }
652
626
 
653
627
 
@@ -657,11 +631,11 @@ public class StackPresenter {
657
631
  }
658
632
 
659
633
  private List<ButtonController> getRightButtons(View child) {
660
- return componentRightButtons.containsKey(child) ? new ArrayList<>(componentRightButtons.get(child).values()) : null;
634
+ return rightButtonControllers.containsKey(child) ? new ArrayList<>(rightButtonControllers.get(child).values()) : null;
661
635
  }
662
636
 
663
637
  private List<ButtonController> getLeftButtons(View child) {
664
- return componentLeftButtons.containsKey(child) ? new ArrayList<>(componentLeftButtons.get(child).values()) : null;
638
+ return leftButtonControllers.containsKey(child) ? new ArrayList<>(leftButtonControllers.get(child).values()) : null;
665
639
  }
666
640
 
667
641
  private void applyStatusBarInsets(StackController stack, ViewController<?> child) {