Skip to content

Commit

Permalink
Squashed commit of the following:
Browse files Browse the repository at this point in the history
commit 131d6ba
Author: Danilo Bürger <info@danilobuerger.de>
Date:   Mon Nov 8 22:13:38 2021 +0100

    Fix default font handling on android (#7333)

    * Pass null font family into typeface loader if not set

    ReactTypefaceUtils handles null font families to fall back to the default typeface

    * Provide the default typeface when calling typeface loader

    * Feed paint typeface as default typeface into typeface loader to support default fonts

    * Support default font face via AHTextView in bottom tabs

    * Move defaultTypeFace to typeFace loader

    Co-authored-by: Ward Abbass <swabbass@gmail.com>

commit 5136a2e
Author: Ward Abbass <swabbass@gmail.com>
Date:   Mon Nov 8 15:22:57 2021 +0200

    React Native 66 (#7305)

    * RN 65

    React Native upgrade + babel unit tests running as expected

    * RN66

    - Normal unit tests working
    - Upgrade to RN66.
    - Move jest to jest config file.
    - Use babel-test transformer to make unit tests working
    - e2e tests that can run as unit wont work.

    * disable running e2es as units

    * use Hermes + Fix Reanimated

    * update reanimated and use babel plugin

    * Enable coverage

    * Upgrade jest and babel to 27

    * enable JS e2e add animatable to transform ignore

    * use babel jest as transformer instead of the react native one

    * update android to 30

    * Fix overlay insets and use non deprecated methods

    * Kotlin convert

    * tmp status

    * Update Detox to support iOS 15

    * Fix e2e

    * Revert "tmp status"

    This reverts commit aff2b87.

    * prevent ReactRootViews from having ids

    * status bar utils static fields

    * Squashed commit of the following:

    commit 5959638
    Author: Ward Abbass <warda@wix.com>
    Date:   Wed Oct 27 13:04:00 2021 +0300

        Keyboard demo playground  (#7331)

        Adding a simple demo that can be used to demonstrate Keyboard show/dismiss when showing/dismissing modal when clicking on submit

        Co-authored-by: Yogev Ben David <yogev132@gmail.com>

    * wait for stack/modal to be shown to send didAppear

    this happens since RN 64

    * Fix tests

    * Ensure modal is not already presented

    * use 0.66.2

    Co-authored-by: svbutko <svbutko@hotmail.com>
    Co-authored-by: Yogev Ben David <yogev132@gmail.com>

commit 9647520
Author: Ward Abbass <swabbass@gmail.com>
Date:   Mon Nov 8 15:05:00 2021 +0200

    Android context crash when parsing options (#7342)

    # Issue:

    When parsing options, `ReactInstanceManager.getCurrentContext()` might return null, which caused color parsing to throw null exception since it assumes that context is not null.

    # Diagnose:

    This can be the case when Activity was killed but the Application still alive (Don't keep activity for example), and since js is bound to the Application lifecycle, opening the activity triggers recreating context in bg, in the meantime calling a command that requires reactContext can cause such crashes to unwanted behaviours when react context is needed.
    This happens internally and it looks like it is some flow where calling navigation command when react is not ready.

    # Fix:

    Since the issue did not happen before, and the crash occurred in parsing options that are independent of `ReactContext`, we can use Activity or Application as the context for such a case.

commit f35d410
Author: Yogev Ben David <yogev132@gmail.com>
Date:   Sun Nov 7 16:59:59 2021 +0200

    Remove manually dismissing all modals on setRoot (#7340)

    Currently we manually dismiss all modals on `setRoot`, we should instead clear it from the modal manager so that those controllers will be automatically released by the reference count system.

commit 7347ed6
Author: Yogev Ben David <yogev132@gmail.com>
Date:   Thu Oct 28 12:50:23 2021 +0300

    Fix missing props after setRoot for components with identical predefined id (#7329)

    * Destroy buttons along with the main react view

    * Revert "Destroy buttons along with the main react view"

    This reverts commit 27e604d.

    * Add pending props

    * Fix android button components cache

    Co-authored-by: Ward Abbass <warda@wix.com>
    Co-authored-by: Ward Abbass <swabbass@gmail.com>

commit 3401467
Author: Ward Abbass <warda@wix.com>
Date:   Wed Oct 27 18:00:22 2021 +0300

    MergeOptions, buttons got cleared when animation enabled [Android] (#7330)

    # Issue:
    When having `animateLeftButtons` or `animateRightButtons` enabled, calling `mergeOptions` frequently to update button state like enabled, colour and text etc, caused the menu animation to mess out the views inside the menu.

    # Diagnoses:

    - Calling `mergeOptions` inside `componentDidUpdate`, which can be called frequently without any options changes deducted from props like calling setState for example or changing a prop that does not have something to do the merge options call.
    - Every `mergeOptions` with buttons in it, would remove and re-add buttons to the toolbars which caused the animation to run, even if the change was updating button colour or enabled state.

    # Fix:

    - Introduce a more efficient way to detect updates and update each button in case there are no structural changes.
    - Structural changes can be: reordering, removing, adding buttons or changing buttons with the same `button.id` but a different component with no stable `componentId`.
    - Other changes mentioned above will call update buttons in place with no remove and re-add and no animation.
    - Refactor the code and enhance single responsibilities for each controller.
    - Added more test cases for such flows.

    Note:

    Android built-in menu, has its limits in terms of updating and rearranging action views inside it, it would be possible if we built a custom menu with an overflow that can support, order changes, and in-place updates, and that is a long road to walk.

    Co-authored-by: Yogev Ben David <yogev132@gmail.com>

commit 5959638
Author: Ward Abbass <warda@wix.com>
Date:   Wed Oct 27 13:04:00 2021 +0300

    Keyboard demo playground  (#7331)

    Adding a simple demo that can be used to demonstrate Keyboard show/dismiss when showing/dismissing modal when clicking on submit

    Co-authored-by: Yogev Ben David <yogev132@gmail.com>
  • Loading branch information
swabbass committed Nov 9, 2021
1 parent c0d459a commit 030a3d0
Show file tree
Hide file tree
Showing 56 changed files with 1,043 additions and 396 deletions.
28 changes: 28 additions & 0 deletions e2e/Keyboard.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { default as TestIDs, default as testIDs } from '../playground/src/testIDs';
import Android from './AndroidUtils';
import Utils from './Utils';

const { elementByLabel, elementById } = Utils;

describe('Keyboard', () => {
beforeEach(async () => {
await device.launchApp({ newInstance: true });
await elementById(TestIDs.KEYBOARD_SCREEN_BTN).tap();
});

it('Push - should close keyboard when Back clicked', async () => {
await elementById(TestIDs.TEXT_INPUT1).tap();
await expect(elementByLabel("Keyboard Demo")).not.toBeVisible();
await elementById(TestIDs.BACK_BUTTON).tap();
await expect(elementById(testIDs.MAIN_BOTTOM_TABS)).toBeVisible();
});

it('Modal - should close keyboard when close clicked', async () => {
await elementById(TestIDs.MODAL_BTN).tap();
await elementById(TestIDs.TEXT_INPUT1).tap();
await expect(elementByLabel("Keyboard Demo")).not.toBeVisible();
await elementById(TestIDs.DISMISS_MODAL_TOPBAR_BTN).tap();
await expect(elementById(testIDs.MAIN_BOTTOM_TABS)).toBeVisible();
});

});
14 changes: 13 additions & 1 deletion e2e/SetRoot.test.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Utils from './Utils';
import TestIDs from '../playground/src/testIDs';

const { elementById } = Utils;
const { elementById, elementByLabel } = Utils;

describe('SetRoot', () => {
beforeEach(async () => {
Expand Down Expand Up @@ -33,4 +33,16 @@ describe('SetRoot', () => {
await elementById(TestIDs.SET_ROOT_WITHOUT_STACK_HIDES_BOTTOM_TABS_BTN).tap();
await expect(elementById(TestIDs.LAYOUTS_TAB)).toBeNotVisible();
});

it('set root should not override props for component with identical id', async () => {
await expect(elementByLabel('Two')).toBeVisible();
await elementById(TestIDs.ROUND_BUTTON).tap();
await expect(elementByLabel('Times created: 1')).toBeVisible();
await elementById(TestIDs.OK_BUTTON).tap();
await elementById(TestIDs.SET_ROOT_WITH_BUTTONS).tap();
await expect(elementByLabel('Two')).toBeVisible();
await elementById(TestIDs.ROUND_BUTTON).tap();
await expect(elementByLabel('Times created: 1')).toBeVisible();
await elementById(TestIDs.OK_BUTTON).tap();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,11 @@ class FontOptions {

@JvmOverloads fun getTypeface(typefaceLoader: TypefaceLoader, defaultTypeface: Typeface? = null): Typeface? {
if (isDirty) {
_typeface = typefaceLoader.getTypeFace(fontFamily.get(""), fontStyle.get(""), fontWeight.get(""))
_typeface = typefaceLoader.getTypeFace(fontFamily.get(null), fontStyle.get(""), fontWeight.get(""), defaultTypeface)
isDirty = false
}
return _typeface
?: defaultTypeface?.let { typefaceLoader.getTypeFace(fontFamily.get(""), fontStyle.get(""), fontWeight.get(""), it) }
?: defaultTypeface?.let { typefaceLoader.getTypeFace(fontFamily.get(null), fontStyle.get(""), fontWeight.get(""), it) }
}

fun mergeWith(other: FontOptions) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package com.reactnativenavigation.options;

import android.app.Activity;
import android.content.Context;

import com.facebook.react.ReactInstanceManager;
import com.facebook.react.bridge.ReactContext;
import com.reactnativenavigation.NavigationApplication;
import com.reactnativenavigation.options.parsers.TypefaceLoader;
import com.reactnativenavigation.react.events.EventEmitter;
import com.reactnativenavigation.utils.Assertions;
Expand Down Expand Up @@ -45,6 +47,8 @@
import static com.reactnativenavigation.options.Options.parse;
import static com.reactnativenavigation.utils.CollectionUtils.*;

import org.json.JSONObject;

public class LayoutFactory {
private Activity activity;
private ChildControllersRegistry childRegistry;
Expand Down Expand Up @@ -75,33 +79,33 @@ public ViewController<?> create(final LayoutNode node) {
final ReactContext context = reactInstanceManager.getCurrentReactContext();
switch (node.type) {
case Component:
return createComponent(context, node);
return createComponent(node);
case ExternalComponent:
return createExternalComponent(context, node);
case Stack:
return createStack(context, node);
return createStack(node);
case BottomTabs:
return createBottomTabs(context, node);
return createBottomTabs(node);
case SideMenuRoot:
return createSideMenuRoot(context, node);
return createSideMenuRoot(node);
case SideMenuCenter:
return createSideMenuContent(node);
case SideMenuLeft:
return createSideMenuLeft(node);
case SideMenuRight:
return createSideMenuRight(node);
case TopTabs:
return createTopTabs(context, node);
return createTopTabs(node);
default:
throw new IllegalArgumentException("Invalid node type: " + node.type);
}
}

private ViewController<?> createSideMenuRoot(ReactContext context, LayoutNode node) {
private ViewController<?> createSideMenuRoot(LayoutNode node) {
SideMenuController sideMenuController = new SideMenuController(activity,
childRegistry,
node.id,
parse(context, typefaceManager, node.getOptions()),
parseOptions( node.getOptions()),
new SideMenuPresenter(),
new Presenter(activity, defaultOptions)
);
Expand Down Expand Up @@ -153,15 +157,15 @@ private ViewController<?> createSideMenuRight(LayoutNode node) {
return create(node.children.get(0));
}

private ViewController<?> createComponent(ReactContext context, LayoutNode node) {
private ViewController<?> createComponent(LayoutNode node) {
String id = node.id;
String name = node.data.optString("name");
return new ComponentViewController(activity,
childRegistry,
id,
name,
new ComponentViewCreator(reactInstanceManager),
parse(context, typefaceManager, node.getOptions()),
parseOptions(node.getOptions()),
new Presenter(activity, defaultOptions),
new ComponentPresenter(defaultOptions)
);
Expand All @@ -178,17 +182,17 @@ private ViewController<?> createExternalComponent(ReactContext context, LayoutNo
reactInstanceManager,
new EventEmitter(context),
new ExternalComponentPresenter(),
parse(context, typefaceManager, node.getOptions())
parseOptions(node.getOptions())
);
}

private ViewController<?> createStack(ReactContext context, LayoutNode node) {
private ViewController<?> createStack(LayoutNode node) {
return new StackControllerBuilder(activity, eventEmitter)
.setChildren(createChildren(node.children))
.setChildRegistry(childRegistry)
.setTopBarController(new TopBarController())
.setId(node.id)
.setInitialOptions(parse(context, typefaceManager, node.getOptions()))
.setInitialOptions(parseOptions(node.getOptions()))
.setStackPresenter(new StackPresenter(activity,
new TitleBarReactViewCreator(reactInstanceManager),
new TopBarBackgroundViewCreator(reactInstanceManager),
Expand All @@ -210,7 +214,7 @@ private List<ViewController<?>> createChildren(List<LayoutNode> children) {
return result;
}

private ViewController<?> createBottomTabs(ReactContext context, LayoutNode node) {
private ViewController<?> createBottomTabs(LayoutNode node) {
List<ViewController<?>> tabs = map(node.children, this::create);
BottomTabsPresenter bottomTabsPresenter = new BottomTabsPresenter(tabs, defaultOptions, new BottomTabsAnimator());
return new BottomTabsController(activity,
Expand All @@ -219,24 +223,35 @@ private ViewController<?> createBottomTabs(ReactContext context, LayoutNode node
eventEmitter,
new ImageLoader(),
node.id,
parse(context, typefaceManager, node.getOptions()),
parseOptions( node.getOptions()),
new Presenter(activity, defaultOptions),
new BottomTabsAttacher(tabs, bottomTabsPresenter, defaultOptions),
bottomTabsPresenter,
new BottomTabPresenter(activity, tabs, new ImageLoader(), new TypefaceLoader(activity), defaultOptions));
}

private ViewController<?> createTopTabs(ReactContext context, LayoutNode node) {
private ViewController<?> createTopTabs(LayoutNode node) {
final List<ViewController<?>> tabs = new ArrayList<>();
for (int i = 0; i < node.children.size(); i++) {
ViewController<?> tabController = create(node.children.get(i));
Options options = parse(context, typefaceManager, node.children.get(i).getOptions());
Options options = parseOptions(node.children.get(i).getOptions());
options.setTopTabIndex(i);
tabs.add(tabController);
}
return new TopTabsController(activity, childRegistry, node.id, tabs, new TopTabsLayoutCreator(activity, tabs), parse(context, typefaceManager, node.getOptions()), new Presenter(activity, defaultOptions));
return new TopTabsController(activity, childRegistry, node.id, tabs, new TopTabsLayoutCreator(activity, tabs)
, parseOptions(node.getOptions()), new Presenter(activity, defaultOptions));
}

private Options parseOptions(JSONObject jsonOptions) {
Context context = reactInstanceManager.getCurrentReactContext();
if (context == null) {
context = activity == null ? NavigationApplication.instance : activity;
}
if (typefaceManager == null) {
typefaceManager = new TypefaceLoader(context);
}
return parse(context, typefaceManager, jsonOptions);
}
@NonNull
@RestrictTo(RestrictTo.Scope.TESTS)
public Options getDefaultOptions() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public class Options {
public static final Options EMPTY = new Options();

@NonNull
public static Options parse(Context context, TypefaceLoader typefaceManager, JSONObject json) {
public static Options parse(@NonNull Context context, TypefaceLoader typefaceManager, JSONObject json) {
Options result = new Options();
if (json == null) return result;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,27 @@ package com.reactnativenavigation.options.parsers

import android.content.Context
import android.graphics.Typeface
import com.aurelhubert.ahbottomnavigation.AHTextView
import com.reactnativenavigation.utils.ReactTypefaceUtils

open class TypefaceLoader(private val context: Context) {
@JvmOverloads open fun getTypeFace(
fontFamilyName: String?,
fontStyle: String?,
fontWeight: String?,
defaultTypeFace: Typeface? = null
val defaultTypeFace: Typeface by lazy {
AHTextView(context).typeface ?: Typeface.DEFAULT
}

@JvmOverloads
open fun getTypeFace(
fontFamilyName: String?,
fontStyle: String?,
fontWeight: String?,
defaultTypeFace: Typeface? = null
): Typeface? {
return ReactTypefaceUtils.applyStyles(
defaultTypeFace,
ReactTypefaceUtils.parseFontStyle(fontStyle),
ReactTypefaceUtils.parseFontWeight(fontWeight),
fontFamilyName,
context.assets
defaultTypeFace,
ReactTypefaceUtils.parseFontStyle(fontStyle),
ReactTypefaceUtils.parseFontWeight(fontWeight),
fontFamilyName,
context.assets
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ class ModalFrameLayout(context: ReactContext) : FrameLayout(context) {
addView(modalContentLayout, MarginLayoutParams(MarginLayoutParams.WRAP_CONTENT, MarginLayoutParams.WRAP_CONTENT)
.apply {
val translucent = context.currentActivity?.window?.let {
StatusBarUtils.isTranslucent(context.currentActivity?.window)
StatusBarUtils.isTranslucent(it)
} ?: false
topMargin = if (translucent) 0 else StatusBarUtils.getStatusBarHeight(context.currentActivity)
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat
import kotlin.math.abs


object StatusBarUtils {
private const val STATUS_BAR_HEIGHT_M = 24
private const val STATUS_BAR_HEIGHT_L = 25
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

import androidx.annotation.NonNull;

import com.aurelhubert.ahbottomnavigation.AHTextView;
import com.aurelhubert.ahbottomnavigation.notification.AHNotification;
import com.reactnativenavigation.options.BottomTabOptions;
import com.reactnativenavigation.options.DotIndicatorOptions;
Expand All @@ -27,6 +28,7 @@ public class BottomTabPresenter {
private final Context context;
private final ImageLoader imageLoader;
private final TypefaceLoader typefaceLoader;
private final Typeface defaultTypeface;
private Options defaultOptions;
private final BottomTabFinder bottomTabFinder;
private final LateInit<BottomTabs> bottomTabs = new LateInit<>();
Expand All @@ -39,6 +41,7 @@ public BottomTabPresenter(Context context, List<ViewController<?>> tabs, ImageLo
this.bottomTabFinder = new BottomTabFinder(tabs);
this.imageLoader = imageLoader;
this.typefaceLoader = typefaceLoader;
this.defaultTypeface = typefaceLoader.getDefaultTypeFace();
this.defaultOptions = defaultOptions;
defaultDotIndicatorSize = dpToPx(context, 6);
}
Expand All @@ -57,7 +60,7 @@ public void applyOptions() {
BottomTabOptions tab = tabs.get(i).resolveCurrentOptions(defaultOptions).bottomTabOptions;
bottomTabs.setIconWidth(i, tab.iconWidth.get(null));
bottomTabs.setIconHeight(i, tab.iconHeight.get(null));
bottomTabs.setTitleTypeface(i, tab.font.getTypeface(typefaceLoader, Typeface.DEFAULT));
bottomTabs.setTitleTypeface(i, tab.font.getTypeface(typefaceLoader, defaultTypeface));
if (tab.selectedIconColor.canApplyValue()) bottomTabs.setIconActiveColor(i, tab.selectedIconColor.get(null));
if (tab.iconColor.canApplyValue()) bottomTabs.setIconInactiveColor(i, tab.iconColor.get(null));
bottomTabs.setTitleActiveColor(i, tab.selectedTextColor.get(null));
Expand Down Expand Up @@ -86,7 +89,7 @@ public void mergeChildOptions(Options options, ViewController<?> child) {
BottomTabOptions tab = options.bottomTabOptions;
if (tab.iconWidth.hasValue()) bottomTabs.setIconWidth(index, tab.iconWidth.get(null));
if (tab.iconHeight.hasValue()) bottomTabs.setIconHeight(index, tab.iconHeight.get(null));
if (tab.font.hasValue()) bottomTabs.setTitleTypeface(index, tab.font.getTypeface(typefaceLoader, Typeface.DEFAULT));
if (tab.font.hasValue()) bottomTabs.setTitleTypeface(index, tab.font.getTypeface(typefaceLoader, defaultTypeface));
if (canMergeColor(tab.selectedIconColor)) bottomTabs.setIconActiveColor(index, tab.selectedIconColor.get());
if (canMergeColor(tab.iconColor)) bottomTabs.setIconInactiveColor(index, tab.iconColor.get());
if (tab.selectedTextColor.hasValue()) bottomTabs.setTitleActiveColor(index, tab.selectedTextColor.get());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,12 @@ public void onCancel() {
}

private void onShowModalEnd(ViewController<?> toAdd, @Nullable ViewController<?> toRemove, CommandListener listener) {
toAdd.onViewDidAppear();
if (toRemove != null && toAdd.resolveCurrentOptions(defaultOptions).modal.presentationStyle != ModalPresentationStyle.OverCurrentContext) {
toRemove.detachView();
}
toAdd.addOnAppearedListener(()->{
toAdd.onViewDidAppear();
if (toRemove != null && toAdd.resolveCurrentOptions(defaultOptions).modal.presentationStyle != ModalPresentationStyle.OverCurrentContext) {
toRemove.detachView();
}
});
listener.onSuccess(toAdd.getId());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ public StackController(Activity activity, List<ViewController<?>> children, Chil
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
presenter.onConfigurationChanged(resolveCurrentOptions());
presenter.onConfigurationChanged(resolveCurrentOptions(), getCurrentChild());
fabPresenter.onConfigurationChanged(resolveCurrentOptions());
}

Expand Down Expand Up @@ -176,9 +176,7 @@ public void push(ViewController<?> child, CommandListener listener) {
presenter.getAdditionalPushAnimations(this, child, resolvedOptions),
() -> onPushAnimationComplete(child, toRemove, listener));
} else {
child.onViewDidAppear();
getView().removeView(toRemove.getView());
listener.onSuccess(child.getId());
onPushAnimationComplete(child, toRemove, listener);
}
} else {
listener.onSuccess(child.getId());
Expand All @@ -192,8 +190,10 @@ public void destroy() {
}

private void onPushAnimationComplete(ViewController<?> toAdd, ViewController<?> toRemove, CommandListener listener) {
toAdd.onViewDidAppear();
if (!peek().equals(toRemove)) getView().removeView(toRemove.getView());
toAdd.addOnAppearedListener(() -> {
toAdd.onViewDidAppear();
if (!peek().equals(toRemove)) getView().removeView(toRemove.getView());
});
listener.onSuccess(toAdd.getId());
}

Expand Down Expand Up @@ -429,11 +429,10 @@ private void addInitialChild(StackLayout stackLayout) {
}

private void setChildId(ViewGroup child) {
//In RN > 64 ReactRootView ids are managed by ReactNative
// see: https://github.com/facebook/react-native/blob/main/ReactAndroid/src/main/java/com/facebook/react/ReactRootView.java#L676
if(!(child instanceof ReactRootView)){
//From RN > 64 we can't set id to child that is ReactRootView
//see:https://github.com/facebook/react-native/blob/main/ReactAndroid/src/main/java/com/facebook/react/ReactRootView.java#L676
if (!(child instanceof ReactRootView))
child.setId(CompatUtils.generateViewId());
}
}

private void startChildrenBellowTopChild() {
Expand Down
Loading

0 comments on commit 030a3d0

Please sign in to comment.