Skip to content

Commit

Permalink
Bottom tabs selection stack (HW button Android) (wix#7380)
Browse files Browse the repository at this point in the history
* Bottom tabs test to kt

* implement flows of tab selection

* Update BottomTabsControllerTest.kt

* Squashed commit of the following:

commit 3368cda
Author: Ward Abbass <swabbass@gmail.com>
Date:   Mon Nov 29 09:56:50 2021 +0200

    Upgrade unit tests dependencies on android

commit 7fe8421
Author: Ward Abbass <swabbass@gmail.com>
Date:   Mon Nov 29 09:20:55 2021 +0200

    tmp

commit bcf0edb
Author: Yogev Ben David <yogev132@gmail.com>
Date:   Thu Nov 25 13:32:27 2021 +0200

    Fix playground reanimated animations (wix#7375)

commit 623c239
Author: Yogev Ben David <yogev132@gmail.com>
Date:   Thu Nov 25 11:37:42 2021 +0200

    Fix missing options processing (wix#7370)

    We missed `mergeOptions` processing on the following commands: `pop`, `popTo`, `popToRoot`, `dismissModal` and `dismissAllModals`. That resulted in a bug where using the old animations syntax didn't work.

commit e0da767
Author: Yogev Ben David <yogev132@gmail.com>
Date:   Thu Nov 25 11:37:13 2021 +0200

    Fix statusBar blur option in dark mode (wix#7372)

commit 74beb27
Author: Yogev Ben David <yogev132@gmail.com>
Date:   Thu Nov 25 11:36:56 2021 +0200

    Update documentation (wix#7373)

commit 6452157
Author: Yogev Ben David <yogev132@gmail.com>
Date:   Wed Nov 24 23:20:11 2021 +0200

    Fix fast refresh crash (wix#7371)

    Closes wix#6176

commit 709ddb8
Author: Yogev Ben David <yogev132@gmail.com>
Date:   Wed Nov 24 21:37:22 2021 +0200

    Fix dismissing modals with sideMenu (wix#7369)

    Dismissing a modal that has a `sideMenu` layout exposed a bug we have where we look for the component in the top ViewController presented children, with `sideMenu` it's not enough because its children are not components.
    Instead, We should recursively look for that component in all of the top viewController children tree.

    Closes wix#7367

commit 5cc967b
Author: Yogev Ben David <yogev132@gmail.com>
Date:   Wed Nov 24 15:26:08 2021 +0200

    [Navigation Mock] Fix bottomTabPressed event (wix#7368)

* add e2e

* add js option

* add more testing

* docs

* Squashed commit of the following:

commit 29ac5e4
Author: Ward Abbass <swabbass@gmail.com>
Date:   Mon Nov 29 10:40:14 2021 +0200

    Upgrade Android unit tests tools (wix#7379)

    * tmp

    * Upgrade unit tests dependencies on android

commit bcf0edb
Author: Yogev Ben David <yogev132@gmail.com>
Date:   Thu Nov 25 13:32:27 2021 +0200

    Fix playground reanimated animations (wix#7375)

commit 623c239
Author: Yogev Ben David <yogev132@gmail.com>
Date:   Thu Nov 25 11:37:42 2021 +0200

    Fix missing options processing (wix#7370)

    We missed `mergeOptions` processing on the following commands: `pop`, `popTo`, `popToRoot`, `dismissModal` and `dismissAllModals`. That resulted in a bug where using the old animations syntax didn't work.

commit e0da767
Author: Yogev Ben David <yogev132@gmail.com>
Date:   Thu Nov 25 11:37:13 2021 +0200

    Fix statusBar blur option in dark mode (wix#7372)

commit 74beb27
Author: Yogev Ben David <yogev132@gmail.com>
Date:   Thu Nov 25 11:36:56 2021 +0200

    Update documentation (wix#7373)

commit 6452157
Author: Yogev Ben David <yogev132@gmail.com>
Date:   Wed Nov 24 23:20:11 2021 +0200

    Fix fast refresh crash (wix#7371)

    Closes wix#6176

commit 709ddb8
Author: Yogev Ben David <yogev132@gmail.com>
Date:   Wed Nov 24 21:37:22 2021 +0200

    Fix dismissing modals with sideMenu (wix#7369)

    Dismissing a modal that has a `sideMenu` layout exposed a bug we have where we look for the component in the top ViewController presented children, with `sideMenu` it's not enough because its children are not components.
    Instead, We should recursively look for that component in all of the top viewController children tree.

    Closes wix#7367

commit 5cc967b
Author: Yogev Ben David <yogev132@gmail.com>
Date:   Wed Nov 24 15:26:08 2021 +0200

    [Navigation Mock] Fix bottomTabPressed event (wix#7368)

* Update BottomTabs.test.js

* Update BottomTabs.test.js

* Update lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/bottomtabs/BottomTabsController.java

Co-authored-by: aliraza-noon <73536024+aliraza-noon@users.noreply.github.com>

* Squashed commit of the following:

commit eae5831
Author: Ward Abbass <swabbass@gmail.com>
Date:   Tue Nov 30 08:55:13 2021 +0200

    Android API 30 fixes (wix#7343)

    Upgrading android target SDK to 30, to support android 12 changes.

    - Refactor StatusBarUtil to SystemUIUtils to include status and navigation bars.
    - Migrate deprecated code using flags to a modern one.
    - Calculating WindowInsets per component where all views are laid as full screen.
    - Keyboard enhancements to show hide keyboard.
    - Fix applying Options of a child taking parent Options into consideration.
    - Fix fixed status bar height and use a more elegant way.
    - Add more tests for SystemUiUtils.
    - Add a SystemUi screen to demonstrate system UI capabilities.
    - Migrate Reanimated usage to support Reanimated 2.

    Closes wix#7339.
    Closes wix#7225.
    Closes wix#7358.
    Closes wix#7199.
    Closes wix#7171.
    Closes wix#7111.
    Closes wix#6988.
    Closes wix#4258.
    Closes wix#7360.

    Demo:

    https://user-images.githubusercontent.com/7227793/142203865-d65b6910-21f8-4617-812e-b5576a6b58e4.mov

    Co-authored-by: Ward Abbass <swabbass@gmail.com>
    Co-authored-by: Yogev Ben David <yogev132@gmail.com>
    Co-authored-by: svbutko <svbutko@hotmail.com>
    Co-authored-by: Ward Abbass <warda@wix.com>

* Update BottomTabsControllerTest.kt

Co-authored-by: aliraza-noon <73536024+aliraza-noon@users.noreply.github.com>
  • Loading branch information
rahimrahman and aliraza-noon committed Feb 25, 2022
1 parent ba3ee16 commit db4e91d
Show file tree
Hide file tree
Showing 9 changed files with 703 additions and 515 deletions.
32 changes: 32 additions & 0 deletions e2e/BottomTabs.test.js
@@ -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 @@ -106,4 +107,35 @@ describe('BottomTabs', () => {
await elementById(TestIDs.POP_BTN).tap();
await expect(elementById(TestIDs.BOTTOM_TABS)).toBeVisible();
});

it('invoke bottomTabPressed event', async () => {
await elementById(TestIDs.THIRD_TAB_BAR_BTN).tap();
await expect(elementByLabel('BottomTabPressed')).toBeVisible();
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();
});
});
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"))
}
}
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

0 comments on commit db4e91d

Please sign in to comment.