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

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 (21) hide show
  1. package/jest.config.js +40 -0
  2. package/lib/android/app/build.gradle +14 -12
  3. package/lib/android/app/src/main/java/com/reactnativenavigation/options/FontOptions.kt +2 -2
  4. package/lib/android/app/src/main/java/com/reactnativenavigation/options/parsers/TypefaceLoader.kt +16 -10
  5. package/lib/android/app/src/main/java/com/reactnativenavigation/react/modal/ModalFrameLayout.kt +1 -1
  6. package/lib/android/app/src/main/java/com/reactnativenavigation/utils/StatusBarUtils.kt +39 -0
  7. package/lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/bottomtabs/BottomTabPresenter.java +5 -2
  8. package/lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/component/ComponentViewController.java +7 -6
  9. package/lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/modal/ModalPresenter.java +6 -4
  10. package/lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/stack/StackController.java +14 -6
  11. package/lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/stack/topbar/button/ButtonSpan.kt +1 -1
  12. package/lib/android/app/src/reactNative51/java/com/reactnativenavigation/react/NavigationReactNativeHost.java +2 -1
  13. package/lib/android/app/src/test/java/com/reactnativenavigation/mocks/TypefaceLoaderMock.kt +4 -0
  14. package/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/modal/ModalPresenterTest.java +4 -0
  15. package/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/modal/ModalStackTest.java +13 -1
  16. package/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/navigator/NavigatorTest.java +2 -2
  17. package/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/stack/StackControllerTest.kt +10 -5
  18. package/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/stack/TopBarControllerTest.kt +10 -9
  19. package/lib/ios/RNNModalManager.m +8 -6
  20. package/package.json +12 -53
  21. package/lib/android/app/src/main/java/com/reactnativenavigation/utils/StatusBarUtils.java +0 -41
package/jest.config.js ADDED
@@ -0,0 +1,40 @@
1
+ module.exports = {
2
+ preset: 'react-native',
3
+ transformIgnorePatterns: [
4
+ 'node_modules/(?!(@react-native|react-native|react-native-ui-lib|react-native-animatable)/)',
5
+ ],
6
+ transform: {
7
+ '\\.[jt]sx?$': 'babel-jest',
8
+ },
9
+ roots: [
10
+ '<rootDir>/lib/src/',
11
+ '<rootDir>/playground/src/',
12
+ '<rootDir>/integration/',
13
+ '<rootDir>/scripts/',
14
+ '<rootDir>/e2e/',
15
+ ],
16
+ setupFilesAfterEnv: ['./jest-setup.js'],
17
+ testPathIgnorePatterns: ['/node_modules/'],
18
+ moduleNameMapper: {
19
+ 'react-native-navigation/Mock': '<rootDir>/lib/src/Mock',
20
+ 'react-native-navigation': '<rootDir>/lib/src',
21
+ '\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$':
22
+ '<rootDir>/playground/img/layouts@2x.png',
23
+ },
24
+ collectCoverageFrom: [
25
+ 'lib/src/**/*.ts',
26
+ 'lib/src/**/*.tsx',
27
+ 'integration/**/*.js',
28
+ '!lib/dist/index.js',
29
+ '!lib/dist/Navigation.js',
30
+ '!lib/dist/adapters/**/*',
31
+ '!lib/dist/interfaces/**/*',
32
+ '!lib/dist/**/*.test.*',
33
+ '!integration/**/*.test.*',
34
+ '!integration/*.test.*',
35
+ '!e2e/**/*test.js',
36
+ ],
37
+ resetMocks: true,
38
+ resetModules: true,
39
+ coverageReporters: ['json', 'lcov', 'text', 'html'],
40
+ };
@@ -3,27 +3,29 @@ import org.gradle.api.tasks.testing.logging.TestExceptionFormat
3
3
 
4
4
  apply plugin: 'com.android.library'
5
5
  apply plugin: 'kotlin-android'
6
- apply plugin: 'kotlin-android-extensions'
7
6
 
8
7
  def safeExtGet(prop, fallback) {
9
8
  rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
10
9
  }
11
10
 
12
- def DEFAULT_COMPILE_SDK_VERSION = 29
13
- def DEFAULT_MIN_SDK_VERSION = 19
14
- def DEFAULT_TARGET_SDK_VERSION = 29
15
- def kotlinVersion = rootProject.ext.get("RNNKotlinVersion")
16
- def kotlinStdlib = safeExtGet('RNNKotlinStdlib', 'kotlin-stdlib-jdk8')
17
- def kotlinCoroutinesCore = safeExtGet('RNNKotlinCoroutinesCore', '1.4.3')
11
+ def safeExtGetFallbackLowerBound(prop, fallback) {
12
+ Math.max(safeExtGet(prop,fallback),fallback)
13
+ }
18
14
 
15
+ def DEFAULT_COMPILE_SDK_VERSION = 30
16
+ def DEFAULT_MIN_SDK_VERSION = 21
17
+ def DEFAULT_TARGET_SDK_VERSION = 30
18
+ def DEFAULT_KOTLIN_VERSION = "1.5.31"
19
+ def DEFAULT_KOTLIN_STDLIB = 'kotlin-stdlib-jdk8'
20
+ def kotlinVersion = safeExtGet("RNNKotlinVersion", DEFAULT_KOTLIN_VERSION)
21
+ def kotlinStdlib = safeExtGet('RNNKotlinStdlib',DEFAULT_KOTLIN_STDLIB )
22
+ def kotlinCoroutinesCore = safeExtGet('RNNKotlinCoroutinesCore', '1.5.2')
19
23
  android {
20
- compileSdkVersion safeExtGet('compileSdkVersion', DEFAULT_COMPILE_SDK_VERSION)
24
+ compileSdkVersion safeExtGetFallbackLowerBound('compileSdkVersion', DEFAULT_COMPILE_SDK_VERSION)
21
25
 
22
26
  defaultConfig {
23
- minSdkVersion safeExtGet('minSdkVersion', DEFAULT_MIN_SDK_VERSION)
24
- targetSdkVersion safeExtGet('targetSdkVersion', DEFAULT_TARGET_SDK_VERSION)
25
- versionCode 1
26
- versionName "1.0"
27
+ minSdkVersion safeExtGetFallbackLowerBound('minSdkVersion', DEFAULT_MIN_SDK_VERSION)
28
+ targetSdkVersion safeExtGetFallbackLowerBound('targetSdkVersion', DEFAULT_TARGET_SDK_VERSION)
27
29
  }
28
30
  buildTypes {
29
31
  release {
@@ -26,11 +26,11 @@ class FontOptions {
26
26
 
27
27
  @JvmOverloads fun getTypeface(typefaceLoader: TypefaceLoader, defaultTypeface: Typeface? = null): Typeface? {
28
28
  if (isDirty) {
29
- _typeface = typefaceLoader.getTypeFace(fontFamily.get(""), fontStyle.get(""), fontWeight.get(""))
29
+ _typeface = typefaceLoader.getTypeFace(fontFamily.get(null), fontStyle.get(""), fontWeight.get(""), defaultTypeface)
30
30
  isDirty = false
31
31
  }
32
32
  return _typeface
33
- ?: defaultTypeface?.let { typefaceLoader.getTypeFace(fontFamily.get(""), fontStyle.get(""), fontWeight.get(""), it) }
33
+ ?: defaultTypeface?.let { typefaceLoader.getTypeFace(fontFamily.get(null), fontStyle.get(""), fontWeight.get(""), it) }
34
34
  }
35
35
 
36
36
  fun mergeWith(other: FontOptions) {
@@ -2,21 +2,27 @@ package com.reactnativenavigation.options.parsers
2
2
 
3
3
  import android.content.Context
4
4
  import android.graphics.Typeface
5
+ import com.aurelhubert.ahbottomnavigation.AHTextView
5
6
  import com.reactnativenavigation.utils.ReactTypefaceUtils
6
7
 
7
8
  open class TypefaceLoader(private val context: Context) {
8
- @JvmOverloads open fun getTypeFace(
9
- fontFamilyName: String?,
10
- fontStyle: String?,
11
- fontWeight: String?,
12
- defaultTypeFace: Typeface? = null
9
+ open val defaultTypeFace: Typeface by lazy {
10
+ AHTextView(context).typeface ?: Typeface.DEFAULT
11
+ }
12
+
13
+ @JvmOverloads
14
+ open fun getTypeFace(
15
+ fontFamilyName: String?,
16
+ fontStyle: String?,
17
+ fontWeight: String?,
18
+ defaultTypeFace: Typeface? = null
13
19
  ): Typeface? {
14
20
  return ReactTypefaceUtils.applyStyles(
15
- defaultTypeFace,
16
- ReactTypefaceUtils.parseFontStyle(fontStyle),
17
- ReactTypefaceUtils.parseFontWeight(fontWeight),
18
- fontFamilyName,
19
- context.assets
21
+ defaultTypeFace,
22
+ ReactTypefaceUtils.parseFontStyle(fontStyle),
23
+ ReactTypefaceUtils.parseFontWeight(fontWeight),
24
+ fontFamilyName,
25
+ context.assets
20
26
  )
21
27
  }
22
28
  }
@@ -11,7 +11,7 @@ class ModalFrameLayout(context: ReactContext) : FrameLayout(context) {
11
11
  addView(modalContentLayout, MarginLayoutParams(MarginLayoutParams.WRAP_CONTENT, MarginLayoutParams.WRAP_CONTENT)
12
12
  .apply {
13
13
  val translucent = context.currentActivity?.window?.let {
14
- StatusBarUtils.isTranslucent(context.currentActivity?.window)
14
+ StatusBarUtils.isTranslucent(it)
15
15
  } ?: false
16
16
  topMargin = if (translucent) 0 else StatusBarUtils.getStatusBarHeight(context)
17
17
  })
@@ -0,0 +1,39 @@
1
+ package com.reactnativenavigation.utils
2
+
3
+ import android.content.Context
4
+ import android.os.Build
5
+ import android.view.Window
6
+ import android.view.WindowManager
7
+
8
+ object StatusBarUtils {
9
+ private const val STATUS_BAR_HEIGHT_M = 24
10
+ private const val STATUS_BAR_HEIGHT_L = 25
11
+ private var statusBarHeight = -1
12
+ @JvmStatic
13
+ fun saveStatusBarHeight(height: Int) {
14
+ statusBarHeight = height
15
+ }
16
+ @JvmStatic
17
+ fun getStatusBarHeight(context: Context): Int {
18
+ if (statusBarHeight > 0) {
19
+ return statusBarHeight
20
+ }
21
+ val resources = context.resources
22
+ val resourceId = resources.getIdentifier("status_bar_height", "dimen", "android")
23
+ statusBarHeight = if (resourceId > 0) resources.getDimensionPixelSize(resourceId) else UiUtils.dpToPx(
24
+ context,
25
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) STATUS_BAR_HEIGHT_M else STATUS_BAR_HEIGHT_L
26
+ )
27
+ return statusBarHeight
28
+ }
29
+ @JvmStatic
30
+ fun getStatusBarHeightDp(context: Context): Int {
31
+ return UiUtils.pxToDp(context, getStatusBarHeight(context).toFloat())
32
+ .toInt()
33
+ }
34
+ @JvmStatic
35
+ fun isTranslucent(window: Window): Boolean {
36
+ val lp = window.attributes
37
+ return lp != null && lp.flags and WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS == WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS
38
+ }
39
+ }
@@ -9,6 +9,7 @@ import android.graphics.drawable.Drawable;
9
9
 
10
10
  import androidx.annotation.NonNull;
11
11
 
12
+ import com.aurelhubert.ahbottomnavigation.AHTextView;
12
13
  import com.aurelhubert.ahbottomnavigation.notification.AHNotification;
13
14
  import com.reactnativenavigation.options.BottomTabOptions;
14
15
  import com.reactnativenavigation.options.DotIndicatorOptions;
@@ -27,6 +28,7 @@ public class BottomTabPresenter {
27
28
  private final Context context;
28
29
  private final ImageLoader imageLoader;
29
30
  private final TypefaceLoader typefaceLoader;
31
+ private final Typeface defaultTypeface;
30
32
  private Options defaultOptions;
31
33
  private final BottomTabFinder bottomTabFinder;
32
34
  private final LateInit<BottomTabs> bottomTabs = new LateInit<>();
@@ -39,6 +41,7 @@ public class BottomTabPresenter {
39
41
  this.bottomTabFinder = new BottomTabFinder(tabs);
40
42
  this.imageLoader = imageLoader;
41
43
  this.typefaceLoader = typefaceLoader;
44
+ this.defaultTypeface = typefaceLoader.getDefaultTypeFace();
42
45
  this.defaultOptions = defaultOptions;
43
46
  defaultDotIndicatorSize = dpToPx(context, 6);
44
47
  }
@@ -57,7 +60,7 @@ public class BottomTabPresenter {
57
60
  BottomTabOptions tab = tabs.get(i).resolveCurrentOptions(defaultOptions).bottomTabOptions;
58
61
  bottomTabs.setIconWidth(i, tab.iconWidth.get(null));
59
62
  bottomTabs.setIconHeight(i, tab.iconHeight.get(null));
60
- bottomTabs.setTitleTypeface(i, tab.font.getTypeface(typefaceLoader, Typeface.DEFAULT));
63
+ bottomTabs.setTitleTypeface(i, tab.font.getTypeface(typefaceLoader, defaultTypeface));
61
64
  if (tab.selectedIconColor.canApplyValue()) bottomTabs.setIconActiveColor(i, tab.selectedIconColor.get(null));
62
65
  if (tab.iconColor.canApplyValue()) bottomTabs.setIconInactiveColor(i, tab.iconColor.get(null));
63
66
  bottomTabs.setTitleActiveColor(i, tab.selectedTextColor.get(null));
@@ -86,7 +89,7 @@ public class BottomTabPresenter {
86
89
  BottomTabOptions tab = options.bottomTabOptions;
87
90
  if (tab.iconWidth.hasValue()) bottomTabs.setIconWidth(index, tab.iconWidth.get(null));
88
91
  if (tab.iconHeight.hasValue()) bottomTabs.setIconHeight(index, tab.iconHeight.get(null));
89
- if (tab.font.hasValue()) bottomTabs.setTitleTypeface(index, tab.font.getTypeface(typefaceLoader, Typeface.DEFAULT));
92
+ if (tab.font.hasValue()) bottomTabs.setTitleTypeface(index, tab.font.getTypeface(typefaceLoader, defaultTypeface));
90
93
  if (canMergeColor(tab.selectedIconColor)) bottomTabs.setIconActiveColor(index, tab.selectedIconColor.get());
91
94
  if (canMergeColor(tab.iconColor)) bottomTabs.setIconInactiveColor(index, tab.iconColor.get());
92
95
  if (tab.selectedTextColor.hasValue()) bottomTabs.setTitleActiveColor(index, tab.selectedTextColor.get());
@@ -15,6 +15,7 @@ import com.reactnativenavigation.viewcontrollers.viewcontroller.ViewController;
15
15
  import com.reactnativenavigation.views.component.ComponentLayout;
16
16
 
17
17
  import androidx.annotation.NonNull;
18
+ import androidx.core.graphics.Insets;
18
19
  import androidx.core.view.ViewCompat;
19
20
  import androidx.core.view.WindowInsetsCompat;
20
21
 
@@ -137,13 +138,13 @@ public class ComponentViewController extends ChildController<ComponentLayout> {
137
138
 
138
139
  @Override
139
140
  protected WindowInsetsCompat applyWindowInsets(ViewController<?> view, WindowInsetsCompat insets) {
140
- ViewCompat.onApplyWindowInsets(view.getView(), insets.replaceSystemWindowInsets(
141
- insets.getSystemWindowInsetLeft(),
142
- insets.getSystemWindowInsetTop(),
141
+ final WindowInsetsCompat.Builder builder = new WindowInsetsCompat.Builder();
142
+ final WindowInsetsCompat finalInsets = builder.setSystemWindowInsets(Insets.of(insets.getSystemWindowInsetLeft(),
143
+ Math.max(insets.getSystemWindowInsetTop() - getTopInset(), 0),
143
144
  insets.getSystemWindowInsetRight(),
144
- Math.max(insets.getSystemWindowInsetBottom() - getBottomInset(), 0)
145
- ));
146
- return insets;
145
+ Math.max(insets.getSystemWindowInsetBottom() - getBottomInset(), 0))).build();
146
+ ViewCompat.onApplyWindowInsets(view.getView(), finalInsets);
147
+ return finalInsets;
147
148
  }
148
149
 
149
150
  @Override
@@ -90,10 +90,12 @@ public class ModalPresenter {
90
90
  }
91
91
 
92
92
  private void onShowModalEnd(ViewController<?> toAdd, @Nullable ViewController<?> toRemove, CommandListener listener) {
93
- toAdd.onViewDidAppear();
94
- if (toRemove != null && toAdd.resolveCurrentOptions(defaultOptions).modal.presentationStyle != ModalPresentationStyle.OverCurrentContext) {
95
- toRemove.detachView();
96
- }
93
+ toAdd.addOnAppearedListener(()->{
94
+ toAdd.onViewDidAppear();
95
+ if (toRemove != null && toAdd.resolveCurrentOptions(defaultOptions).modal.presentationStyle != ModalPresentationStyle.OverCurrentContext) {
96
+ toRemove.detachView();
97
+ }
98
+ });
97
99
  listener.onSuccess(toAdd.getId());
98
100
  }
99
101
 
@@ -5,6 +5,7 @@ import android.content.res.Configuration;
5
5
  import android.view.View;
6
6
  import android.view.ViewGroup;
7
7
 
8
+ import com.facebook.react.ReactRootView;
8
9
  import com.reactnativenavigation.options.ButtonOptions;
9
10
  import com.reactnativenavigation.options.Options;
10
11
  import com.reactnativenavigation.options.StackAnimationOptions;
@@ -175,9 +176,7 @@ public class StackController extends ParentController<StackLayout> {
175
176
  presenter.getAdditionalPushAnimations(this, child, resolvedOptions),
176
177
  () -> onPushAnimationComplete(child, toRemove, listener));
177
178
  } else {
178
- child.onViewDidAppear();
179
- getView().removeView(toRemove.getView());
180
- listener.onSuccess(child.getId());
179
+ onPushAnimationComplete(child, toRemove, listener);
181
180
  }
182
181
  } else {
183
182
  listener.onSuccess(child.getId());
@@ -191,8 +190,10 @@ public class StackController extends ParentController<StackLayout> {
191
190
  }
192
191
 
193
192
  private void onPushAnimationComplete(ViewController<?> toAdd, ViewController<?> toRemove, CommandListener listener) {
194
- toAdd.onViewDidAppear();
195
- if (!peek().equals(toRemove)) getView().removeView(toRemove.getView());
193
+ toAdd.addOnAppearedListener(() -> {
194
+ toAdd.onViewDidAppear();
195
+ if (!peek().equals(toRemove)) getView().removeView(toRemove.getView());
196
+ });
196
197
  listener.onSuccess(toAdd.getId());
197
198
  }
198
199
 
@@ -421,12 +422,19 @@ public class StackController extends ParentController<StackLayout> {
421
422
  if (isEmpty()) return;
422
423
  ViewController<?> childController = peek();
423
424
  ViewGroup child = childController.getView();
424
- child.setId(CompatUtils.generateViewId());
425
+ setChildId(child);
425
426
  childController.addOnAppearedListener(this::startChildrenBellowTopChild);
426
427
  stackLayout.addView(child, 0, matchParentWithBehaviour(new StackBehaviour(this)));
427
428
  presenter.applyInitialChildLayoutOptions(resolveCurrentOptions());
428
429
  }
429
430
 
431
+ private void setChildId(ViewGroup child) {
432
+ //From RN > 64 we can't set id to child that is ReactRootView
433
+ //see:https://github.com/facebook/react-native/blob/main/ReactAndroid/src/main/java/com/facebook/react/ReactRootView.java#L676
434
+ if (!(child instanceof ReactRootView))
435
+ child.setId(CompatUtils.generateViewId());
436
+ }
437
+
430
438
  private void startChildrenBellowTopChild() {
431
439
  ArrayList<ViewController<?>> children = new ArrayList<>(getChildControllers());
432
440
  for (int i = children.size() - 2; i >= 0; i--) {
@@ -25,7 +25,7 @@ class ButtonSpan(
25
25
 
26
26
  fun apply(paint: Paint) {
27
27
  with(button.font) {
28
- val typeface = getTypeface(typefaceLoader, Typeface.DEFAULT)
28
+ val typeface = getTypeface(typefaceLoader, paint.typeface)
29
29
  val fakeStyle = (paint.typeface?.style ?: 0) and (typeface?.style?.inv() ?: 1)
30
30
  if (fakeStyle and Typeface.BOLD != 0) paint.isFakeBoldText = true
31
31
  if (fakeStyle and Typeface.ITALIC != 0) paint.textSkewX = -0.25f
@@ -8,6 +8,7 @@ import com.facebook.react.ReactPackage;
8
8
  import com.facebook.react.common.LifecycleState;
9
9
  import com.facebook.react.devsupport.interfaces.DevBundleDownloadListener;
10
10
  import com.reactnativenavigation.NavigationApplication;
11
+ import com.facebook.hermes.reactexecutor.HermesExecutorFactory;
11
12
 
12
13
  import androidx.annotation.NonNull;
13
14
  import androidx.annotation.Nullable;
@@ -44,7 +45,7 @@ public abstract class NavigationReactNativeHost extends ReactNativeHost implemen
44
45
  .setJSMainModulePath(getJSMainModuleName())
45
46
  .setUseDeveloperSupport(getUseDeveloperSupport())
46
47
  .setRedBoxHandler(getRedBoxHandler())
47
- .setJavaScriptExecutorFactory(getJavaScriptExecutorFactory())
48
+ .setJavaScriptExecutorFactory(new HermesExecutorFactory())
48
49
  .setUIImplementationProvider(getUIImplementationProvider())
49
50
  .setInitialLifecycleState(LifecycleState.BEFORE_CREATE)
50
51
  .setDevBundleDownloadListener(getDevBundleDownloadListener());
@@ -9,8 +9,12 @@ class TypefaceLoaderMock() : TypefaceLoader(mock()) {
9
9
 
10
10
  constructor(mockTypefaces: Map<String, Typeface>?) : this() {
11
11
  this.mockTypefaces = mockTypefaces
12
+
12
13
  }
13
14
 
15
+ override val defaultTypeFace: Typeface
16
+ get() = Typeface.DEFAULT
17
+
14
18
  override fun getTypeFace(fontFamilyName: String?, fontStyle: String?, fontWeight: String?, defaultTypeFace: Typeface?): Typeface? {
15
19
  return mockTypefaces?.getOrDefault(fontFamilyName, defaultTypeFace) ?: defaultTypeFace
16
20
  }
@@ -50,6 +50,7 @@ public class ModalPresenterTest extends BaseTest {
50
50
 
51
51
  @Override
52
52
  public void beforeEach() {
53
+ super.beforeEach();
53
54
  Activity activity = newActivity();
54
55
  ChildControllersRegistry childRegistry = new ChildControllersRegistry();
55
56
 
@@ -210,6 +211,7 @@ public class ModalPresenterTest extends BaseTest {
210
211
  uut.setRootLayout(spy);
211
212
 
212
213
  uut.showModal(modal1, root, new CommandListenerAdapter());
214
+ idleMainLooper();
213
215
  uut.dismissModal(modal1, root, root, new CommandListenerAdapter());
214
216
 
215
217
  verify(spy).addView(root.getView(), 0);
@@ -237,6 +239,7 @@ public class ModalPresenterTest extends BaseTest {
237
239
  verify(modal1).onViewWillAppear();
238
240
 
239
241
  uut.showModal(modal2, modal1, new CommandListenerAdapter());
242
+ idleMainLooper();
240
243
  assertThat(modal1.getView().getParent()).isNull();
241
244
 
242
245
  Shadows.shadowOf(Looper.getMainLooper()).idle();
@@ -253,6 +256,7 @@ public class ModalPresenterTest extends BaseTest {
253
256
 
254
257
  uut.showModal(modal1, root, new CommandListenerAdapter());
255
258
  uut.showModal(modal2, modal1, new CommandListenerAdapter());
259
+ idleMainLooper();
256
260
  assertThat(modal1.getView().getParent()).isNull();
257
261
  assertThat(root.getView().getParent()).isNull();
258
262
 
@@ -13,7 +13,7 @@ import com.reactnativenavigation.react.events.EventEmitter;
13
13
  import com.reactnativenavigation.viewcontrollers.child.ChildControllersRegistry;
14
14
  import com.reactnativenavigation.viewcontrollers.stack.StackController;
15
15
  import com.reactnativenavigation.viewcontrollers.viewcontroller.ViewController;
16
-
16
+ import com.reactnativenavigation.options.TransitionAnimationOptions;
17
17
  import org.junit.Test;
18
18
  import org.mockito.Mockito;
19
19
 
@@ -54,6 +54,7 @@ public class ModalStackTest extends BaseTest {
54
54
 
55
55
  @Override
56
56
  public void beforeEach() {
57
+ super.beforeEach();
57
58
  activity = newActivity();
58
59
  childRegistry = new ChildControllersRegistry();
59
60
  root = new SimpleViewController(activity, childRegistry, "root", new Options());
@@ -81,6 +82,16 @@ public class ModalStackTest extends BaseTest {
81
82
  .build();
82
83
  }
83
84
 
85
+ @Test
86
+ public void showModal_DidAppearEventShouldWaitForReactViewToBeShown(){
87
+ CommandListener listener = spy(new CommandListenerAdapter());
88
+ uut.showModal(modal1, root, listener);
89
+ verify(modal1).addOnAppearedListener(any());
90
+ verify(listener).onSuccess(modal1.getId());
91
+ idleMainLooper();
92
+ verify(modal1).onViewDidAppear();
93
+ }
94
+
84
95
  @Test
85
96
  public void modalRefIsSaved() {
86
97
  disableShowModalAnimation(modal1);
@@ -94,6 +105,7 @@ public class ModalStackTest extends BaseTest {
94
105
  public void showModal() {
95
106
  CommandListener listener = spy(new CommandListenerAdapter());
96
107
  uut.showModal(modal1, root, listener);
108
+ idleMainLooper();
97
109
  verify(listener).onSuccess(modal1.getId());
98
110
  verify(modal1).onViewDidAppear();
99
111
  assertThat(uut.size()).isOne();
@@ -507,7 +507,7 @@ public class NavigatorTest extends BaseTest {
507
507
  StackController stack2 = newStack(child2, child3, child4);
508
508
  BottomTabsController bottomTabsController = newTabs(Arrays.asList(stack1, stack2));
509
509
  uut.setRoot(bottomTabsController, new CommandListenerAdapter(), reactInstanceManager);
510
-
510
+ idleMainLooper();
511
511
  CommandListenerAdapter listener = spy(new CommandListenerAdapter() {
512
512
  @Override
513
513
  public void onSuccess(String childId) {
@@ -670,7 +670,7 @@ public class NavigatorTest extends BaseTest {
670
670
  final StackController stack2 = newStack(child2, child3);
671
671
  BottomTabsController bottomTabsController = newTabs(Arrays.asList(stack1, stack2));
672
672
  uut.setRoot(bottomTabsController, new CommandListenerAdapter(), reactInstanceManager);
673
-
673
+ idleMainLooper();
674
674
  CommandListenerAdapter listener = spy(new CommandListenerAdapter() {
675
675
  @Override
676
676
  public void onSuccess(String childId) {
@@ -197,7 +197,7 @@ class StackControllerTest : BaseTest() {
197
197
  fun setRoot_pushDuringSetRootAnimationShouldNotCrash() {
198
198
  uut.push(child1, CommandListenerAdapter())
199
199
  uut.push(child2, CommandListenerAdapter())
200
-
200
+ idleMainLooper()
201
201
  uut.setRoot(listOf(child1), CommandListenerAdapter())
202
202
  uut.push(child3, CommandListenerAdapter())
203
203
  assertThat(uut.currentChild).isEqualTo(child3)
@@ -281,6 +281,7 @@ class StackControllerTest : BaseTest() {
281
281
  disablePushAnimation(child1, child2)
282
282
  uut.push(child1, CommandListenerAdapter()) // Initialize stack with a child
283
283
  uut.push(child2, CommandListenerAdapter())
284
+ idleMainLooper()
284
285
  verify(child2).onViewDidAppear()
285
286
  }
286
287
 
@@ -608,8 +609,9 @@ class StackControllerTest : BaseTest() {
608
609
  assertNotChildOf(uut.view, child1.view)
609
610
  uut.push(child1, CommandListenerAdapter())
610
611
  assertIsChild(uut.view, child1.view)
611
-
612
+ idleMainLooper()
612
613
  uut.push(child2, CommandListenerAdapter())
614
+ idleMainLooper()
613
615
  assertIsChild(uut.view, child2)
614
616
  assertNotChildOf(uut.view, child1)
615
617
  }
@@ -663,7 +665,7 @@ class StackControllerTest : BaseTest() {
663
665
  val child1View: View = child1.view
664
666
  uut.push(child1, CommandListenerAdapter())
665
667
  uut.push(child2, CommandListenerAdapter())
666
-
668
+ idleMainLooper()
667
669
  assertIsChild(uut.view, child2View)
668
670
  assertNotChildOf(uut.view, child1View)
669
671
  uut.pop(Options.EMPTY, CommandListenerAdapter())
@@ -715,7 +717,7 @@ class StackControllerTest : BaseTest() {
715
717
  uut.push(child2, CommandListenerAdapter())
716
718
  uut.push(child3, CommandListenerAdapter())
717
719
  uut.push(child4, CommandListenerAdapter())
718
-
720
+ idleMainLooper()
719
721
  uut.popTo(child2, Options.EMPTY, CommandListenerAdapter())
720
722
  verify(animator, never()).pop(any(), eq(child1), any(), any(), any())
721
723
  verify(animator, never()).pop(any(), eq(child2), any(), any(), any())
@@ -729,6 +731,7 @@ class StackControllerTest : BaseTest() {
729
731
  uut.push(child1, mock())
730
732
  uut.push(child2, mock())
731
733
  uut.push(child3, mock())
734
+ idleMainLooper()
732
735
  uut.popTo(child1, Options.EMPTY, mock())
733
736
  animator.endPushAnimation(child3)
734
737
  assertContainsOnlyId(child1.id)
@@ -759,8 +762,8 @@ class StackControllerTest : BaseTest() {
759
762
  disablePushAnimation(child1, child2, child3)
760
763
  uut.push(child1, CommandListenerAdapter())
761
764
  uut.push(child2, CommandListenerAdapter())
762
-
763
765
  uut.push(child3, CommandListenerAdapter())
766
+ idleMainLooper()
764
767
  uut.popToRoot(Options.EMPTY, object : CommandListenerAdapter() {
765
768
  override fun onSuccess(childId: String) {
766
769
  verify(animator).pop(eq(child1), eq(child3), any(), any(), any())
@@ -776,6 +779,7 @@ class StackControllerTest : BaseTest() {
776
779
  uut.push(child1, CommandListenerAdapter())
777
780
  uut.push(child2, CommandListenerAdapter())
778
781
  uut.push(child3, CommandListenerAdapter())
782
+ idleMainLooper()
779
783
  uut.popToRoot(Options.EMPTY, object : CommandListenerAdapter() {
780
784
  override fun onSuccess(childId: String) {
781
785
  verify(child1, never()).destroy()
@@ -811,6 +815,7 @@ class StackControllerTest : BaseTest() {
811
815
  uut.push(child1, mock())
812
816
  uut.push(child2, mock())
813
817
  uut.push(child3, mock())
818
+ idleMainLooper()
814
819
  uut.popToRoot(Options.EMPTY, mock())
815
820
  animator.endPushAnimation(child3)
816
821
  assertContainsOnlyId(child1.id)
@@ -336,11 +336,11 @@ class TopBarControllerTest : BaseTest() {
336
336
  verify(removedControllers[textButton1.id]!!, never()).destroy()
337
337
  verify(removedControllers[textButton2.id]!!, never()).destroy()
338
338
 
339
- verify(rightButtonControllers[textButton1.id]!!, times(1)).addToMenu(any(), any())
340
- verify(rightButtonControllers[textButton2.id]!!, times(1)).addToMenu(any(), any())
339
+ verify(rightButtonControllers[textButton1.id]!!, times(2)).addToMenu(any(), any())
340
+ verify(rightButtonControllers[textButton2.id]!!, times(2)).addToMenu(any(), any())
341
341
  }
342
342
  @Test
343
- fun `mergeRightButtons - should rebuild menu when adding menu items`(){
343
+ fun `mergeRightButtons - should rebuild menu when adding menu items, existing should not be destroyed`(){
344
344
  val controllers = spy(LinkedHashMap<String,ButtonController>())
345
345
  uut.mergeRightButtonsOptions(controllers, listOf(textButton1)) {
346
346
  createButtonController(it)
@@ -351,8 +351,8 @@ class TopBarControllerTest : BaseTest() {
351
351
  createButtonController(it)
352
352
  }
353
353
  assertThat(uut.rightButtonCount).isEqualTo(2)
354
- verify(controllers, times(1)).remove(any())
355
- verify(controllers[textButton1.id]!!, times(1)).addToMenu(any(), any())
354
+ verify(controllers, never()).remove(any())
355
+ verify(controllers[textButton1.id]!!, times(2)).addToMenu(any(), any())
356
356
  }
357
357
  @Test
358
358
  fun `mergeRightButtons - should modify changed buttons`(){
@@ -374,12 +374,13 @@ class TopBarControllerTest : BaseTest() {
374
374
  verify(controllers[textButton1.id]!!, never()).destroy()
375
375
  }
376
376
 
377
- fun `mergeRightButtons - reorder of same menu items should rebuild menu`(){
377
+ @Test
378
+ fun `mergeRightButtons - reorder of same menu items should rebuild menu, not view recreation`(){
378
379
  val controllers = spy(LinkedHashMap<String,ButtonController>())
379
380
  uut.mergeRightButtonsOptions(controllers, listOf(textButton1, textButton2)) {
380
381
  createButtonController(it)
381
382
  }
382
- assertThat(uut.rightButtonCount).isEqualTo(1)
383
+ assertThat(uut.rightButtonCount).isEqualTo(2)
383
384
  verify(controllers[textButton1.id]!!, times(1)).addToMenu(any(), any())
384
385
  verify(controllers[textButton2.id]!!, times(1)).addToMenu(any(), any())
385
386
 
@@ -390,8 +391,8 @@ class TopBarControllerTest : BaseTest() {
390
391
  verify(controllers[textButton2.id]!!, never()).mergeButtonOptions(any(), any())
391
392
  verify(controllers[textButton1.id]!!, times(2)).addToMenu(any(), any())
392
393
  verify(controllers[textButton2.id]!!, times(2)).addToMenu(any(), any())
393
- verify(controllers[textButton1.id]!!, times(1)).destroy()
394
- verify(controllers[textButton2.id]!!, times(1)).destroy()
394
+ verify(controllers[textButton1.id]!!, never()).destroy()
395
+ verify(controllers[textButton2.id]!!, never()).destroy()
395
396
  }
396
397
 
397
398
  private fun createButtonController(it: ButtonOptions) =
@@ -36,12 +36,14 @@
36
36
  modalHostViewManager.presentationBlock =
37
37
  ^(UIViewController *reactViewController, UIViewController *viewController, BOOL animated,
38
38
  dispatch_block_t completionBlock) {
39
- [self showModal:viewController
40
- animated:animated
41
- completion:^(NSString *_Nonnull componentId) {
42
- if (completionBlock)
43
- completionBlock();
44
- }];
39
+ if (reactViewController.presentedViewController != viewController) {
40
+ [self showModal:viewController
41
+ animated:animated
42
+ completion:^(NSString *_Nonnull componentId) {
43
+ if (completionBlock)
44
+ completionBlock();
45
+ }];
46
+ }
45
47
  };
46
48
 
47
49
  modalHostViewManager.dismissalBlock =
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-navigation",
3
- "version": "7.23.1-snapshot.334",
3
+ "version": "7.23.1-snapshot.357",
4
4
  "description": "React Native Navigation - truly native navigation for iOS and Android",
5
5
  "license": "MIT",
6
6
  "nativePackage": true,
@@ -66,10 +66,9 @@
66
66
  "tslib": "1.9.3"
67
67
  },
68
68
  "devDependencies": {
69
- "@babel/core": "7.10.3",
70
69
  "@babel/plugin-proposal-export-default-from": "7.10.1",
71
70
  "@babel/plugin-proposal-export-namespace-from": "7.10.1",
72
- "@babel/types": "7.6.x",
71
+ "@babel/types": "7.15.6",
73
72
  "@react-native-community/blur": "^3.6.0",
74
73
  "@react-native-community/datetimepicker": "^3.4.7",
75
74
  "@react-native-community/eslint-config": "2.0.0",
@@ -79,14 +78,14 @@
79
78
  "@types/detox": "17.14.3",
80
79
  "@types/hoist-non-react-statics": "^3.0.1",
81
80
  "@types/jasmine": "3.5.10",
82
- "@types/jest": "26.0.3",
81
+ "@types/jest": "27.0.2",
83
82
  "@types/lodash": "^4.14.149",
84
83
  "@types/react": "16.9.41",
85
84
  "@types/react-native": "0.63.1",
86
85
  "@types/react-test-renderer": "16.9.2",
87
86
  "@typescript-eslint/eslint-plugin": "3.3.0",
88
87
  "@typescript-eslint/parser": "3.3.0",
89
- "babel-jest": "26.1.0",
88
+ "babel-jest": "^27.2.5",
90
89
  "clang-format": "^1.4.0",
91
90
  "detox": "18.23.1",
92
91
  "eslint": "7.3.0",
@@ -95,16 +94,18 @@
95
94
  "github-release-notes": "https://github.com/yogevbd/github-release-notes/tarball/e601b3dba72dcd6cba323c1286ea6dd0c0110b58",
96
95
  "husky": "4.2.5",
97
96
  "identity-obj-proxy": "3.0.0",
98
- "jest": "26.1.0",
99
- "jest-circus": "26.1.0",
97
+ "@babel/core": "7.15.8",
98
+ "@babel/runtime": "7.15.4",
99
+ "jest": "^27.2.5",
100
+ "jest-circus": "^27.2.5",
100
101
  "lint-staged": "10.2.11",
101
- "metro-react-native-babel-preset": "0.59.0",
102
+ "metro-react-native-babel-preset": "0.66.2",
102
103
  "prettier": "2.1.2",
103
- "react": "16.13.1",
104
- "react-native": "0.63.2",
104
+ "react": "17.0.2",
105
+ "react-native": "0.66.2",
105
106
  "react-native-fast-image": "^8.3.4",
106
107
  "react-native-gesture-handler": "^1.6.1",
107
- "react-native-reanimated": "^1.9.0",
108
+ "react-native-reanimated": "2.3.0-beta.2",
108
109
  "react-native-ui-lib": "5.11.0",
109
110
  "react-redux": "5.x.x",
110
111
  "react-test-renderer": "16.13.1",
@@ -117,48 +118,6 @@
117
118
  "detox-testing-library-rnn-adapter": "2.x.x",
118
119
  "remx": "3.x.x"
119
120
  },
120
- "jest": {
121
- "preset": "react-native",
122
- "transform": {
123
- "^.+\\.js$": "<rootDir>/node_modules/react-native/jest/preprocessor.js"
124
- },
125
- "roots": [
126
- "<rootDir>/lib/src/",
127
- "<rootDir>/playground/src/",
128
- "<rootDir>/integration/",
129
- "<rootDir>/scripts/",
130
- "<rootDir>/e2e/"
131
- ],
132
- "setupFilesAfterEnv": [
133
- "./jest-setup.js"
134
- ],
135
- "moduleNameMapper": {
136
- "react-native-navigation/Mock": "<rootDir>/lib/src/Mock",
137
- "react-native-navigation": "<rootDir>/lib/src",
138
- "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/playground/img/layouts@2x.png"
139
- },
140
- "collectCoverageFrom": [
141
- "lib/src/**/*.ts",
142
- "lib/src/**/*.tsx",
143
- "integration/**/*.js",
144
- "!lib/dist/index.js",
145
- "!lib/dist/Navigation.js",
146
- "!lib/dist/adapters/**/*",
147
- "!lib/dist/interfaces/**/*",
148
- "!lib/dist/**/*.test.*",
149
- "!integration/**/*.test.*",
150
- "!integration/*.test.*",
151
- "!e2e/**/*test.js"
152
- ],
153
- "resetMocks": true,
154
- "resetModules": true,
155
- "coverageReporters": [
156
- "json",
157
- "lcov",
158
- "text",
159
- "html"
160
- ]
161
- },
162
121
  "husky": {
163
122
  "hooks": {
164
123
  "pre-commit": "lint-staged"
@@ -1,41 +0,0 @@
1
- package com.reactnativenavigation.utils;
2
-
3
- import android.content.Context;
4
- import android.content.res.Resources;
5
- import android.os.Build;
6
- import android.view.Window;
7
- import android.view.WindowManager;
8
-
9
- import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
10
- import static com.reactnativenavigation.utils.UiUtils.dpToPx;
11
-
12
- public class StatusBarUtils {
13
- private static final int STATUS_BAR_HEIGHT_M = 24;
14
- private static final int STATUS_BAR_HEIGHT_L = 25;
15
- private static int statusBarHeight = -1;
16
-
17
- public static void saveStatusBarHeight(int height) {
18
- statusBarHeight = height;
19
- }
20
-
21
- public static int getStatusBarHeight(Context context) {
22
- if (statusBarHeight > 0) {
23
- return statusBarHeight;
24
- }
25
- final Resources resources = context.getResources();
26
- final int resourceId = resources.getIdentifier("status_bar_height", "dimen", "android");
27
- statusBarHeight = resourceId > 0 ?
28
- resources.getDimensionPixelSize(resourceId) :
29
- dpToPx(context, Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ? STATUS_BAR_HEIGHT_M : STATUS_BAR_HEIGHT_L);
30
- return statusBarHeight;
31
- }
32
-
33
- public static int getStatusBarHeightDp(Context context) {
34
- return (int) UiUtils.pxToDp(context, getStatusBarHeight(context));
35
- }
36
-
37
- public static boolean isTranslucent(Window window) {
38
- WindowManager.LayoutParams lp = window.getAttributes();
39
- return lp != null && (lp.flags & FLAG_TRANSLUCENT_STATUS) == FLAG_TRANSLUCENT_STATUS;
40
- }
41
- }