Skip to content

Commit 2365e02

Browse files
authored
Set stack root refactor (#4460)
This PR adds support for setting stack root with multiple children, which is to be expected as stack can be initialised with multiple children. Closes #4441
1 parent 62f6dba commit 2365e02

File tree

21 files changed

+247
-100
lines changed

21 files changed

+247
-100
lines changed

lib/android/app/src/main/java/com/reactnativenavigation/react/NavigationModule.java

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import com.facebook.react.bridge.ReactApplicationContext;
1010
import com.facebook.react.bridge.ReactContextBaseJavaModule;
1111
import com.facebook.react.bridge.ReactMethod;
12+
import com.facebook.react.bridge.ReadableArray;
1213
import com.facebook.react.bridge.ReadableMap;
1314
import com.facebook.react.bridge.WritableMap;
1415
import com.reactnativenavigation.NavigationActivity;
@@ -27,6 +28,7 @@
2728
import com.reactnativenavigation.viewcontrollers.externalcomponent.ExternalComponentCreator;
2829
import com.reactnativenavigation.viewcontrollers.navigator.Navigator;
2930

31+
import java.util.ArrayList;
3032
import java.util.Map;
3133

3234
public class NavigationModule extends ReactContextBaseJavaModule {
@@ -89,11 +91,14 @@ public void push(String commandId, String onComponentId, ReadableMap rawLayoutTr
8991
}
9092

9193
@ReactMethod
92-
public void setStackRoot(String commandId, String onComponentId, ReadableMap rawLayoutTree, Promise promise) {
93-
final LayoutNode layoutTree = LayoutNodeParser.parse(JSONParser.parse(rawLayoutTree));
94+
public void setStackRoot(String commandId, String onComponentId, ReadableArray children, Promise promise) {
9495
handle(() -> {
95-
final ViewController viewController = newLayoutFactory().create(layoutTree);
96-
navigator().setStackRoot(onComponentId, viewController, new NativeCommandListener(commandId, promise, eventEmitter, now));
96+
ArrayList<ViewController> _children = new ArrayList();
97+
for (int i = 0; i < children.size(); i++) {
98+
final LayoutNode layoutTree = LayoutNodeParser.parse(JSONParser.parse(children.getMap(i)));
99+
_children.add(newLayoutFactory().create(layoutTree));
100+
}
101+
navigator().setStackRoot(onComponentId, _children, new NativeCommandListener(commandId, promise, eventEmitter, now));
97102
});
98103
}
99104

lib/android/app/src/main/java/com/reactnativenavigation/utils/CollectionUtils.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,14 @@ public static <T> void forEach(@Nullable Collection<T> items, Apply<T> apply) {
8383
return null;
8484
}
8585

86+
public static <T> T last(@Nullable List<T> items) {
87+
return CollectionUtils.isNullOrEmpty(items) ? null : items.get(items.size() - 1);
88+
}
89+
90+
public static <T> T removeLast(@NonNull List<T> items) {
91+
return items.remove(items.size() - 1);
92+
}
93+
8694
private static @NonNull <T> Collection<T> get(@Nullable Collection<T> t) {
8795
return t == null ? Collections.EMPTY_LIST : t;
8896
}

lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/IdStack.java

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,33 +4,42 @@
44

55
import com.reactnativenavigation.utils.StringUtils;
66

7-
import java.util.ArrayDeque;
7+
import java.util.ArrayList;
88
import java.util.Collection;
99
import java.util.HashMap;
1010
import java.util.Iterator;
11+
import java.util.Map;
12+
13+
import static com.reactnativenavigation.utils.CollectionUtils.last;
14+
import static com.reactnativenavigation.utils.CollectionUtils.removeLast;
1115

1216
public class IdStack<E> implements Iterable<String> {
1317

14-
private final ArrayDeque<String> deque = new ArrayDeque<>();
15-
private final HashMap<String, E> map = new HashMap<>();
18+
private final ArrayList<String> deque = new ArrayList();
19+
private final Map<String, E> map = new HashMap<>();
1620

1721
public void push(String id, E item) {
18-
deque.push(id);
22+
deque.add(id);
1923
map.put(id, item);
2024
}
2125

26+
public void set(String id, E item, int index) {
27+
deque.add(index, id);
28+
map.put(id, item);
29+
}
30+
2231
public E peek() {
2332
if (isEmpty()) {
2433
return null;
2534
}
26-
return map.get(deque.peek());
35+
return map.get(last(deque));
2736
}
2837

2938
public E pop() {
3039
if (isEmpty()) {
3140
return null;
3241
}
33-
return map.remove(deque.pop());
42+
return map.remove(removeLast(deque));
3443
}
3544

3645
public boolean isEmpty() {
@@ -42,7 +51,7 @@ public int size() {
4251
}
4352

4453
public String peekId() {
45-
return deque.peek();
54+
return last(deque);
4655
}
4756

4857
public void clear() {
@@ -54,6 +63,10 @@ public E get(final String id) {
5463
return map.get(id);
5564
}
5665

66+
public E get(final int index) {
67+
return map.get(deque.get(index));
68+
}
69+
5770
public boolean containsId(final String id) {
5871
return deque.contains(id);
5972
}
@@ -80,4 +93,9 @@ public Iterator<String> iterator() {
8093
public Collection<E> values() {
8194
return map.values();
8295
}
96+
97+
public void remove(Iterator<String> iterator, String id) {
98+
iterator.remove();
99+
map.remove(id);
100+
}
83101
}

lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/navigator/Navigator.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88
import android.widget.FrameLayout;
99

1010
import com.reactnativenavigation.parse.Options;
11-
import com.reactnativenavigation.presentation.Presenter;
1211
import com.reactnativenavigation.presentation.OverlayManager;
12+
import com.reactnativenavigation.presentation.Presenter;
1313
import com.reactnativenavigation.react.EventEmitter;
1414
import com.reactnativenavigation.utils.CommandListener;
1515
import com.reactnativenavigation.utils.CompatUtils;
@@ -22,6 +22,7 @@
2222

2323
import java.util.Collection;
2424
import java.util.Collections;
25+
import java.util.List;
2526

2627
public class Navigator extends ParentController {
2728

@@ -148,8 +149,8 @@ public void push(final String id, final ViewController viewController, CommandLi
148149
applyOnStack(id, listener, stack -> stack.push(viewController, listener));
149150
}
150151

151-
public void setStackRoot(String id, ViewController viewController, CommandListener listener) {
152-
applyOnStack(id, listener, stack -> stack.setRoot(viewController, listener));
152+
public void setStackRoot(String id, List<ViewController> children, CommandListener listener) {
153+
applyOnStack(id, listener, stack -> stack.setRoot(children, listener));
153154
}
154155

155156
public void pop(String id, Options mergeOptions, CommandListener listener) {

lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/stack/StackController.java

Lines changed: 44 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import com.reactnativenavigation.presentation.Presenter;
1515
import com.reactnativenavigation.presentation.StackPresenter;
1616
import com.reactnativenavigation.react.Constants;
17+
import com.reactnativenavigation.utils.CollectionUtils;
1718
import com.reactnativenavigation.utils.CommandListener;
1819
import com.reactnativenavigation.utils.CommandListenerAdapter;
1920
import com.reactnativenavigation.viewcontrollers.ChildControllersRegistry;
@@ -183,23 +184,43 @@ private void addChildToStack(ViewController child, View view, Options resolvedOp
183184
getView().addView(view, getView().getChildCount() - 1);
184185
}
185186

186-
public void setRoot(ViewController child, CommandListener listener) {
187-
backButtonHelper.clear(child);
188-
push(child, new CommandListenerAdapter() {
189-
@Override
190-
public void onSuccess(String childId) {
191-
removeChildrenBellowTop();
192-
listener.onSuccess(childId);
193-
}
194-
});
187+
public void setRoot(List<ViewController> children, CommandListener listener) {
188+
if (children.size() == 1) {
189+
backButtonHelper.clear(CollectionUtils.last(children));
190+
push(CollectionUtils.last(children), new CommandListenerAdapter() {
191+
@Override
192+
public void onSuccess(String childId) {
193+
removeChildrenBellowTop();
194+
listener.onSuccess(childId);
195+
}
196+
});
197+
} else {
198+
push(CollectionUtils.last(children), new CommandListenerAdapter() {
199+
@Override
200+
public void onSuccess(String childId) {
201+
removeChildrenBellowTop();
202+
for (int i = 0; i < children.size() - 1; i++) {
203+
stack.set(children.get(i).getId(), children.get(i), i);
204+
children.get(i).setParentController(StackController.this);
205+
if (i == 0) {
206+
backButtonHelper.clear(children.get(i));
207+
} else {
208+
backButtonHelper.addToPushedChild(children.get(i));
209+
}
210+
}
211+
listener.onSuccess(childId);
212+
}
213+
});
214+
}
195215
}
196216

197217
private void removeChildrenBellowTop() {
198218
Iterator<String> iterator = stack.iterator();
199219
while (stack.size() > 1) {
200220
ViewController controller = stack.get(iterator.next());
201221
if (!stack.isTop(controller.getId())) {
202-
removeAndDestroyController(controller);
222+
stack.remove(iterator, controller.getId());
223+
controller.destroy();
203224
}
204225
}
205226
}
@@ -247,15 +268,17 @@ public void popTo(ViewController viewController, Options mergeOptions, CommandLi
247268
return;
248269
}
249270

250-
Iterator<String> iterator = stack.iterator();
251-
String currentControlId = iterator.next();
252-
while (!viewController.getId().equals(currentControlId)) {
253-
if (stack.isTop(currentControlId)) {
254-
currentControlId = iterator.next();
255-
continue;
271+
272+
String currentControlId;
273+
for (int i = stack.size() - 2; i >= 0; i--) {
274+
currentControlId = stack.get(i).getId();
275+
if (currentControlId.equals(viewController.getId())) {
276+
break;
256277
}
257-
removeAndDestroyController(stack.get(currentControlId));
258-
currentControlId = iterator.next();
278+
279+
ViewController controller = stack.get(currentControlId);
280+
stack.remove(controller.getId());
281+
controller.destroy();
259282
}
260283

261284
pop(mergeOptions, listener);
@@ -268,10 +291,12 @@ public void popToRoot(Options mergeOptions, CommandListener listener) {
268291
}
269292

270293
Iterator<String> iterator = stack.iterator();
294+
iterator.next();
271295
while (stack.size() > 2) {
272296
ViewController controller = stack.get(iterator.next());
273297
if (!stack.isTop(controller.getId())) {
274-
removeAndDestroyController(controller);
298+
stack.remove(iterator, controller.getId());
299+
controller.destroy();
275300
}
276301
}
277302

lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/IdStackTest.java

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,67 +17,67 @@ public void beforeEach() {
1717
}
1818

1919
@Test
20-
public void isEmpty() throws Exception {
20+
public void isEmpty() {
2121
assertThat(uut.isEmpty()).isTrue();
2222
uut.push("123", 123);
2323
assertThat(uut.isEmpty()).isFalse();
2424
}
2525

2626
@Test
27-
public void size() throws Exception {
27+
public void size() {
2828
assertThat(uut.size()).isEqualTo(0);
2929
uut.push("123", 123);
3030
assertThat(uut.size()).isEqualTo(1);
3131
}
3232

3333
@Test
34-
public void peek() throws Exception {
34+
public void peek() {
3535
assertThat(uut.peek()).isNull();
3636
uut.push("123", 123);
3737
uut.push("456", 456);
3838
assertThat(uut.peek()).isEqualTo(456);
3939
}
4040

4141
@Test
42-
public void pop() throws Exception {
42+
public void pop() {
4343
assertThat(uut.pop()).isNull();
4444
uut.push("123", 123);
4545
uut.push("456", 456);
4646
assertThat(uut.pop()).isEqualTo(456);
4747
}
4848

4949
@Test
50-
public void peekId() throws Exception {
50+
public void peekId() {
5151
assertThat(uut.peekId()).isNull();
5252
uut.push("123", 123);
5353
assertThat(uut.peekId()).isEqualTo("123");
5454
}
5555

5656
@Test
57-
public void clear() throws Exception {
57+
public void clear() {
5858
uut.push("123", 123);
5959
uut.push("456", 456);
6060
uut.clear();
6161
assertThat(uut.isEmpty()).isTrue();
6262
}
6363

6464
@Test
65-
public void getById() throws Exception {
65+
public void getById() {
6666
assertThat(uut.get("123")).isNull();
6767
uut.push("123", 123);
6868
uut.push("456", 456);
6969
assertThat(uut.get("123")).isEqualTo(123);
7070
}
7171

7272
@Test
73-
public void containsId() throws Exception {
73+
public void containsId() {
7474
assertThat(uut.containsId("123")).isFalse();
7575
uut.push("123", 123);
7676
assertThat(uut.containsId("123")).isTrue();
7777
}
7878

7979
@Test
80-
public void remove() throws Exception {
80+
public void remove() {
8181
assertThat(uut.remove("123")).isNull();
8282

8383
uut.push("123", 123);
@@ -87,16 +87,16 @@ public void remove() throws Exception {
8787
}
8888

8989
@Test
90-
public void iterableIds() throws Exception {
90+
public void iterableIds() {
9191
assertThat(uut).isInstanceOf(Iterable.class);
9292
assertThat(uut).isEmpty();
9393
uut.push("123", 123);
9494
uut.push("456", 456);
95-
assertThat(uut).containsExactly("456", "123");
95+
assertThat(uut).containsExactly("123", "456");
9696
}
9797

9898
@Test
99-
public void isTop() throws Exception {
99+
public void isTop() {
100100
assertThat(uut.isTop("123")).isFalse();
101101
uut.push("123", 123);
102102
assertThat(uut.isTop("123")).isTrue();
@@ -105,7 +105,7 @@ public void isTop() throws Exception {
105105
}
106106

107107
@Test
108-
public void values() throws Exception {
108+
public void values() {
109109
assertThat(uut.values()).isNotNull().isEmpty();
110110
uut.push("123", 123);
111111
uut.push("456", 456);

lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/navigator/NavigatorTest.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import org.robolectric.annotation.Config;
3939

4040
import java.util.Arrays;
41+
import java.util.Collections;
4142
import java.util.List;
4243

4344
import static org.assertj.core.api.Java6Assertions.assertThat;
@@ -301,7 +302,7 @@ public void setStackRoot() {
301302

302303
stack.push(child1, new CommandListenerAdapter());
303304
stack.push(child2, new CommandListenerAdapter());
304-
stack.setRoot(child3, new CommandListenerAdapter());
305+
stack.setRoot(Collections.singletonList(child3), new CommandListenerAdapter());
305306

306307
assertThat(stack.getChildControllers()).containsOnly(child3);
307308
}

0 commit comments

Comments
 (0)