Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions e2e/BottomTabs.test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import Utils from './Utils';
import TestIDs from '../playground/src/testIDs';
import Android from './AndroidUtils';

const { elementByLabel, elementById } = Utils;

Expand Down Expand Up @@ -113,4 +114,28 @@ describe('BottomTabs', () => {
await elementByLabel('OK').tap();
await expect(elementByLabel('First Tab')).toBeVisible();
});

it.e2e(':android: hardware back tab selection history', async () => {
await elementById(TestIDs.SECOND_TAB_BAR_BTN).tap();
await elementById(TestIDs.FIRST_TAB_BAR_BUTTON).tap();
await elementById(TestIDs.SECOND_TAB_BAR_BTN).tap();
await elementById(TestIDs.SECOND_TAB_BAR_BTN).tap();
await elementById(TestIDs.FIRST_TAB_BAR_BUTTON).tap();

Android.pressBack();
await expect(elementByLabel('Second Tab')).toBeVisible();

Android.pressBack();
await expect(elementByLabel('First Tab')).toBeVisible();

Android.pressBack();
await expect(elementByLabel('Second Tab')).toBeVisible();

Android.pressBack();
await expect(elementByLabel('First Tab')).toBeVisible();

Android.pressBack();
await expect(elementByLabel('First Tab')).toBeNotVisible();
await expect(elementByLabel('Second Tab')).toBeNotVisible();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,40 @@ import com.reactnativenavigation.options.parsers.BoolParser
import org.json.JSONObject


sealed class HwBackBottomTabsBehaviour {
object Undefined : HwBackBottomTabsBehaviour() {
override fun hasValue(): Boolean = false
}

object Exit : HwBackBottomTabsBehaviour()
object PrevSelection : HwBackBottomTabsBehaviour()
object JumpToFirst : HwBackBottomTabsBehaviour()

open fun hasValue(): Boolean = true

companion object {
private const val BEHAVIOUR_EXIT = "exit"
private const val BEHAVIOUR_PREV = "previous"
private const val BEHAVIOUR_FIRST = "first"
fun fromString(behaviour: String?): HwBackBottomTabsBehaviour {
return when (behaviour) {
BEHAVIOUR_PREV -> PrevSelection
BEHAVIOUR_FIRST -> JumpToFirst
BEHAVIOUR_EXIT -> Exit
else -> Undefined
}
}
}
}

open class HardwareBackButtonOptions(json: JSONObject? = null) {

@JvmField var dismissModalOnPress: Bool = NullBool()
@JvmField var popStackOnPress: Bool = NullBool()
@JvmField
var dismissModalOnPress: Bool = NullBool()

@JvmField
var popStackOnPress: Bool = NullBool()
var bottomTabOnPress: HwBackBottomTabsBehaviour = HwBackBottomTabsBehaviour.Undefined

init {
parse(json)
Expand All @@ -18,16 +48,19 @@ open class HardwareBackButtonOptions(json: JSONObject? = null) {
fun mergeWith(other: HardwareBackButtonOptions) {
if (other.dismissModalOnPress.hasValue()) dismissModalOnPress = other.dismissModalOnPress
if (other.popStackOnPress.hasValue()) popStackOnPress = other.popStackOnPress
if (other.bottomTabOnPress.hasValue()) bottomTabOnPress = other.bottomTabOnPress
}

fun mergeWithDefault(defaultOptions: HardwareBackButtonOptions) {
if (!dismissModalOnPress.hasValue()) dismissModalOnPress = defaultOptions.dismissModalOnPress
if (!popStackOnPress.hasValue()) popStackOnPress = defaultOptions.popStackOnPress
if (!bottomTabOnPress.hasValue()) bottomTabOnPress = defaultOptions.bottomTabOnPress
}

private fun parse(json: JSONObject?) {
json ?: return
dismissModalOnPress = BoolParser.parse(json, "dismissModalOnPress")
popStackOnPress = BoolParser.parse(json, "popStackOnPress")
bottomTabOnPress = HwBackBottomTabsBehaviour.fromString(json.optString("bottomTabsOnPress"))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import com.aurelhubert.ahbottomnavigation.AHBottomNavigation;
import com.aurelhubert.ahbottomnavigation.AHBottomNavigationItem;
import com.reactnativenavigation.options.BottomTabOptions;
import com.reactnativenavigation.options.HwBackBottomTabsBehaviour;
import com.reactnativenavigation.options.Options;
import com.reactnativenavigation.react.CommandListener;
import com.reactnativenavigation.react.CommandListenerAdapter;
Expand All @@ -29,6 +30,8 @@
import com.reactnativenavigation.views.bottomtabs.BottomTabsLayout;

import java.util.Collection;
import java.util.Deque;
import java.util.LinkedList;
import java.util.List;

import static com.reactnativenavigation.utils.CollectionUtils.forEach;
Expand All @@ -39,6 +42,7 @@ public class BottomTabsController extends ParentController<BottomTabsLayout> imp

private BottomTabsContainer bottomTabsContainer;
private BottomTabs bottomTabs;
private final Deque<Integer> selectionStack;
private final List<ViewController<?>> tabs;
private final EventEmitter eventEmitter;
private final ImageLoader imageLoader;
Expand Down Expand Up @@ -66,6 +70,7 @@ public BottomTabsController(Activity activity, List<ViewController<?>> tabs, Chi
this.presenter = bottomTabsPresenter;
this.tabPresenter = bottomTabPresenter;
forEach(tabs, tab -> tab.setParentController(this));
selectionStack = new LinkedList<>();
}

@Override
Expand Down Expand Up @@ -156,7 +161,27 @@ public void mergeChildOptions(Options options, ViewController<?> child) {

@Override
public boolean handleBack(CommandListener listener) {
return !tabs.isEmpty() && tabs.get(bottomTabs.getCurrentItem()).handleBack(listener);
final boolean childBack = !tabs.isEmpty() && tabs.get(bottomTabs.getCurrentItem()).handleBack(listener);
final Options options = resolveCurrentOptions();
if (!childBack) {
if (options.hardwareBack.getBottomTabOnPress() instanceof HwBackBottomTabsBehaviour.PrevSelection) {
if (!selectionStack.isEmpty()) {
final int prevSelectedTabIndex = selectionStack.poll();
selectTab(prevSelectedTabIndex, false);
return true;
}
} else if (options.hardwareBack.getBottomTabOnPress() instanceof HwBackBottomTabsBehaviour.JumpToFirst) {
if (getSelectedIndex() != 0) {
selectTab(0, false);
return true;
} else {
return false;
}
} else {
return false;
}
}
return childBack;
}

@Override
Expand Down Expand Up @@ -203,7 +228,7 @@ private List<AHBottomNavigationItem> createTabs() {
});
}

int getSelectedIndex() {
public int getSelectedIndex() {
return bottomTabs.getCurrentItem();
}

Expand Down Expand Up @@ -239,13 +264,28 @@ public void destroy() {

@Override
public void selectTab(final int newIndex) {
final boolean enableSelectionHistory = resolveCurrentOptions().hardwareBack.getBottomTabOnPress() instanceof HwBackBottomTabsBehaviour.PrevSelection;
selectTab(newIndex, enableSelectionHistory);
}

private void selectTab(int newIndex, boolean enableSelectionHistory) {
saveTabSelection(newIndex, enableSelectionHistory);
tabsAttacher.onTabSelected(tabs.get(newIndex));
getCurrentView().setVisibility(View.INVISIBLE);
bottomTabs.setCurrentItem(newIndex, false);
getCurrentView().setVisibility(View.VISIBLE);
getCurrentChild().onViewDidAppear();
}

private void saveTabSelection(int newIndex, boolean enableSelectionHistory) {
if (enableSelectionHistory) {
if (selectionStack.isEmpty()
|| selectionStack.peek() != newIndex
|| bottomTabs.getCurrentItem() != newIndex)
selectionStack.offerFirst(bottomTabs.getCurrentItem());
}
}

@NonNull
private ViewGroup getCurrentView() {
return tabs.get(bottomTabs.getCurrentItem()).getView();
Expand Down
Loading