diff --git a/.yarnrc.yml b/.yarnrc.yml index 01c9770bb3e..f7eb46f9f56 100644 --- a/.yarnrc.yml +++ b/.yarnrc.yml @@ -8,6 +8,6 @@ npmMinimalAgeGate: 14d nodeLinker: node-modules -npmRegistryServer: "https://npm.dev.wixpress.com" +npmRegistryServer: "https://registry.npmjs.org" yarnPath: .yarn/releases/yarn-4.12.0.cjs diff --git a/android/build.gradle b/android/build.gradle index 51f8f029a7a..bec9808bfbf 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -130,6 +130,7 @@ dependencies { implementation "androidx.constraintlayout:constraintlayout:2.0.4" implementation 'androidx.appcompat:appcompat:1.7.0' + implementation 'androidx.activity:activity:1.9.0' implementation 'androidx.annotation:annotation:1.2.0' implementation 'com.google.android.material:material:1.2.0-alpha03' diff --git a/android/src/main/java/com/reactnativenavigation/NavigationActivity.java b/android/src/main/java/com/reactnativenavigation/NavigationActivity.java index f3f0d5a57ee..9a06c87704e 100644 --- a/android/src/main/java/com/reactnativenavigation/NavigationActivity.java +++ b/android/src/main/java/com/reactnativenavigation/NavigationActivity.java @@ -7,6 +7,7 @@ import android.os.Bundle; import android.view.KeyEvent; import android.view.View; +import android.view.ViewGroup; import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler; import com.facebook.react.modules.core.PermissionAwareActivity; @@ -17,10 +18,12 @@ import com.reactnativenavigation.react.JsDevReloadHandler; import com.reactnativenavigation.react.ReactGateway; import com.reactnativenavigation.react.CommandListenerAdapter; +import com.reactnativenavigation.utils.SystemUiUtils; import com.reactnativenavigation.viewcontrollers.child.ChildControllersRegistry; import com.reactnativenavigation.viewcontrollers.modal.ModalStack; import com.reactnativenavigation.viewcontrollers.navigator.Navigator; +import androidx.activity.EdgeToEdge; import androidx.activity.OnBackPressedCallback; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -36,6 +39,7 @@ public class NavigationActivity extends AppCompatActivity implements DefaultHard @Override protected void onCreate(@Nullable Bundle savedInstanceState) { + enableEdgeToEdge(); super.onCreate(savedInstanceState); if (isFinishing()) { return; @@ -63,7 +67,9 @@ public void onConfigurationChanged(@NonNull Configuration newConfig) { @Override public void onPostCreate(@Nullable Bundle savedInstanceState) { super.onPostCreate(savedInstanceState); - navigator.setContentLayout(findViewById(android.R.id.content)); + ViewGroup contentLayout = findViewById(android.R.id.content); + navigator.setContentLayout(contentLayout); + SystemUiUtils.setupSystemBarBackgrounds(this, contentLayout); } @Override @@ -88,6 +94,7 @@ protected void onPause() { @Override protected void onDestroy() { super.onDestroy(); + SystemUiUtils.tearDown(); if (navigator != null) { navigator.destroy(); } @@ -146,6 +153,14 @@ public void onReload() { navigator.destroyViews(); } + /** + * Override to disable or customize edge-to-edge behavior. + * Called at the start of onCreate, before super.onCreate. + */ + protected void enableEdgeToEdge() { + EdgeToEdge.enable(this); + } + protected void addDefaultSplashLayout() { View view = new View(this); setContentView(view); diff --git a/android/src/main/java/com/reactnativenavigation/react/modal/ModalHostLayout.kt b/android/src/main/java/com/reactnativenavigation/react/modal/ModalHostLayout.kt index 88094c33b3a..50e77bd8a64 100644 --- a/android/src/main/java/com/reactnativenavigation/react/modal/ModalHostLayout.kt +++ b/android/src/main/java/com/reactnativenavigation/react/modal/ModalHostLayout.kt @@ -34,8 +34,8 @@ open class ModalHostLayout(reactContext: ThemedReactContext) : ViewGroup(reactCo } @TargetApi(23) - override fun dispatchProvideStructure(structure: ViewStructure) { - mHostView.dispatchProvideStructure(structure) + override fun dispatchProvideStructure(structure: ViewStructure?) { + structure?.let { mHostView.dispatchProvideStructure(it) } } override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {} diff --git a/android/src/main/java/com/reactnativenavigation/utils/ReactTypefaceUtils.java b/android/src/main/java/com/reactnativenavigation/utils/ReactTypefaceUtils.java index 962bf9ef53c..7a208bf85a2 100644 --- a/android/src/main/java/com/reactnativenavigation/utils/ReactTypefaceUtils.java +++ b/android/src/main/java/com/reactnativenavigation/utils/ReactTypefaceUtils.java @@ -18,9 +18,8 @@ import android.text.TextUtils; import androidx.annotation.Nullable; import com.facebook.react.bridge.ReadableArray; -import com.facebook.react.common.ReactConstants; import com.facebook.react.views.text.ReactFontManager; -import com.facebook.react.views.text.ReactTextShadowNode; +import com.facebook.react.common.ReactConstants; import java.util.ArrayList; import java.util.List; diff --git a/android/src/main/java/com/reactnativenavigation/utils/SystemUiUtils.kt b/android/src/main/java/com/reactnativenavigation/utils/SystemUiUtils.kt index 5ac5d2ed932..3baa578a354 100644 --- a/android/src/main/java/com/reactnativenavigation/utils/SystemUiUtils.kt +++ b/android/src/main/java/com/reactnativenavigation/utils/SystemUiUtils.kt @@ -3,10 +3,14 @@ package com.reactnativenavigation.utils import android.app.Activity import android.graphics.Color import android.graphics.Rect +import android.graphics.drawable.ColorDrawable +import android.view.Gravity import android.view.View +import android.view.ViewGroup import android.view.Window +import android.widget.FrameLayout import androidx.annotation.ColorInt -import androidx.core.view.WindowCompat +import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat import androidx.core.view.WindowInsetsControllerCompat import kotlin.math.abs @@ -16,8 +20,14 @@ object SystemUiUtils { private const val STATUS_BAR_HEIGHT_M = 24 internal const val STATUS_BAR_HEIGHT_TRANSLUCENCY = 0.65f private var statusBarHeight = -1 - var navigationBarDefaultColor = -1 - private set + + const val DEFAULT_NAV_BAR_COLOR = Color.BLACK + private const val THREE_BUTTON_NAV_BAR_OPACITY = 0.8f + + private var statusBarBackgroundView: View? = null + private var navBarBackgroundView: View? = null + private var isEdgeToEdgeActive = false + private var isThreeButtonNav = false @JvmStatic fun getStatusBarHeight(activity: Activity?): Int { @@ -45,98 +55,165 @@ object SystemUiUtils { statusBarHeight = height } - @JvmStatic fun getStatusBarHeightDp(activity: Activity?): Int { - return UiUtils.pxToDp(activity, getStatusBarHeight(activity).toFloat()) - .toInt() + return UiUtils.pxToDp(activity, getStatusBarHeight(activity).toFloat()).toInt() } + // region Setup + + /** + * Initializes view-based system bar backgrounds for edge-to-edge. + * Call from Activity.onPostCreate after the navigator content layout is set. + * + * Status bar: reuses the system's android:id/statusBarBackground DecorView child. + * Navigation bar: creates a view in [contentLayout] sized by WindowInsets, + * since the system's navigationBarBackground is not available with EdgeToEdge. + * + * Both fall back to deprecated window APIs when the views are unavailable. + */ @JvmStatic - fun hideNavigationBar(window: Window?, view: View) { - window?.let { - WindowCompat.setDecorFitsSystemWindows(window, false) - WindowInsetsControllerCompat(window, view).let { controller -> - controller.hide(WindowInsetsCompat.Type.navigationBars()) - controller.systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE + fun setupSystemBarBackgrounds(activity: Activity, contentLayout: ViewGroup) { + setupStatusBarBackground(activity) + setupNavigationBarBackground(contentLayout) + } + + private fun setupStatusBarBackground(activity: Activity) { + if (statusBarBackgroundView != null) return + val sbView = activity.window.decorView.findViewById(android.R.id.statusBarBackground) + if (sbView != null) { + sbView.setBackgroundColor(Color.BLACK) + statusBarBackgroundView = sbView + } + } + + private fun setupNavigationBarBackground(contentLayout: ViewGroup) { + if (navBarBackgroundView != null) return + val view = View(contentLayout.context).apply { + setBackgroundColor(Color.BLACK) + } + val params = FrameLayout.LayoutParams( + FrameLayout.LayoutParams.MATCH_PARENT, 0, Gravity.BOTTOM + ) + contentLayout.addView(view, params) + navBarBackgroundView = view + + ViewCompat.setOnApplyWindowInsetsListener(view) { v, insets -> + val navBarHeight = insets.getInsets(WindowInsetsCompat.Type.navigationBars()).bottom + val tappableHeight = insets.getInsets(WindowInsetsCompat.Type.tappableElement()).bottom + val wasThreeButton = isThreeButtonNav + isEdgeToEdgeActive = navBarHeight > 0 + isThreeButtonNav = tappableHeight > 0 + if (isThreeButtonNav != wasThreeButton) { + v.setBackgroundColor(getDefaultNavBarColor()) } + val lp = v.layoutParams + if (lp.height != navBarHeight) { + lp.height = navBarHeight + v.layoutParams = lp + } + insets } + view.requestApplyInsets() } + /** + * Returns the default navigation bar color, applying 80% opacity for 3-button navigation. + * Gesture navigation gets a fully opaque color since the bar is minimal. + */ @JvmStatic - fun showNavigationBar(window: Window?, view: View) { - window?.let { - WindowCompat.setDecorFitsSystemWindows(window, true) - WindowInsetsControllerCompat(window, view).show(WindowInsetsCompat.Type.navigationBars()) - } + fun getDefaultNavBarColor(): Int { + if (!isThreeButtonNav) return DEFAULT_NAV_BAR_COLOR + val alpha = (THREE_BUTTON_NAV_BAR_OPACITY * 255).toInt() + return Color.argb(alpha, Color.red(DEFAULT_NAV_BAR_COLOR), Color.green(DEFAULT_NAV_BAR_COLOR), Color.blue(DEFAULT_NAV_BAR_COLOR)) } + /** + * Clears references to system bar background views. + * Call from Activity.onDestroy to avoid leaking views across activity recreation. + */ + @JvmStatic + fun tearDown() { + statusBarBackgroundView = null + navBarBackgroundView = null + isEdgeToEdgeActive = false + isThreeButtonNav = false + statusBarHeight = -1 + } + + // endregion + + // region Status Bar + @JvmStatic fun setStatusBarColorScheme(window: Window?, view: View, isDark: Boolean) { window?.let { WindowInsetsControllerCompat(window, view).isAppearanceLightStatusBars = isDark - // Workaround: on devices with api 30 status bar icons flickers or get hidden when removing view - //turns out it is a bug on such devices, fixed by using system flags until it is fixed. - var flags = view.systemUiVisibility - flags = if (isDark) { - flags or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR - } else { - flags and View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.inv() - } - - view.systemUiVisibility = flags } } @JvmStatic fun setStatusBarTranslucent(window: Window?) { - window?.let { - setStatusBarColor(window, window.statusBarColor, true) + getStatusBarColor(window)?.let { currentColor -> + setStatusBarColor(window, currentColor, true) } } @JvmStatic fun isTranslucent(window: Window?): Boolean { - return window?.let { - Color.alpha(it.statusBarColor) < 255 - } ?: false + val color = getStatusBarColor(window) ?: return false + return Color.alpha(color) < 255 } @JvmStatic fun clearStatusBarTranslucency(window: Window?) { - window?.let { - setStatusBarColor(it, it.statusBarColor, false) + getStatusBarColor(window)?.let { currentColor -> + setStatusBarColor(window, currentColor, false) } } @JvmStatic - fun setStatusBarColor( - window: Window?, - @ColorInt color: Int, - translucent: Boolean - ) { + fun setStatusBarColor(window: Window?, @ColorInt color: Int, translucent: Boolean) { val colorAlpha = Color.alpha(color) - val alpha = if (translucent && colorAlpha == 255) STATUS_BAR_HEIGHT_TRANSLUCENCY else colorAlpha/255.0f - val red: Int = Color.red(color) - val green: Int = Color.green(color) - val blue: Int = Color.blue(color) - val opaqueColor = Color.argb(ceil(alpha * 255).toInt(), red, green, blue) + val alpha = if (translucent && colorAlpha == 255) STATUS_BAR_HEIGHT_TRANSLUCENCY else colorAlpha / 255.0f + val opaqueColor = Color.argb( + ceil(alpha * 255).toInt(), + Color.red(color), + Color.green(color), + Color.blue(color) + ) setStatusBarColor(window, opaqueColor) } + /** + * Sets the status bar background color. + * Uses the view-based background when available (edge-to-edge), + * falls back to the deprecated window API on older configurations. + */ fun setStatusBarColor(window: Window?, color: Int) { - window?.statusBarColor = color + statusBarBackgroundView?.setBackgroundColor(color) ?: run { + @Suppress("DEPRECATION") + window?.statusBarColor = color + } } + /** + * Gets the current status bar background color. + * Reads from the view-based background when available, + * falls back to the deprecated window API on older configurations. + */ @JvmStatic fun getStatusBarColor(window: Window?): Int? { + statusBarBackgroundView?.let { view -> + (view.background as? ColorDrawable)?.let { return it.color } + } + @Suppress("DEPRECATION") return window?.statusBarColor } @JvmStatic fun hideStatusBar(window: Window?, view: View) { window?.let { - WindowCompat.setDecorFitsSystemWindows(window, false) WindowInsetsControllerCompat(window, view).let { controller -> controller.hide(WindowInsetsCompat.Type.statusBars()) controller.systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE @@ -147,22 +224,48 @@ object SystemUiUtils { @JvmStatic fun showStatusBar(window: Window?, view: View) { window?.let { - WindowCompat.setDecorFitsSystemWindows(window, true) WindowInsetsControllerCompat(window, view).show(WindowInsetsCompat.Type.statusBars()) } } + // endregion + + // region Navigation Bar + @JvmStatic - fun setNavigationBarBackgroundColor(window: Window?, color: Int, lightColor: Boolean) { + fun hideNavigationBar(window: Window?, view: View) { window?.let { - if (navigationBarDefaultColor == -1) { - navigationBarDefaultColor = window.navigationBarColor - } - WindowInsetsControllerCompat(window, window.decorView).let { controller -> - controller.isAppearanceLightNavigationBars = lightColor + WindowInsetsControllerCompat(window, view).let { controller -> + controller.hide(WindowInsetsCompat.Type.navigationBars()) + controller.systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE } - window.navigationBarColor = color } } + @JvmStatic + fun showNavigationBar(window: Window?, view: View) { + window?.let { + WindowInsetsControllerCompat(window, view).show(WindowInsetsCompat.Type.navigationBars()) + } + } + + /** + * Sets the navigation bar background color and icon appearance. + * Uses the view-based background when available (edge-to-edge), + * falls back to the deprecated window API on older configurations. + */ + @JvmStatic + fun setNavigationBarBackgroundColor(window: Window?, color: Int, lightColor: Boolean) { + window?.let { + WindowInsetsControllerCompat(window, window.decorView).isAppearanceLightNavigationBars = lightColor + } + if (isEdgeToEdgeActive) { + navBarBackgroundView?.setBackgroundColor(color) + } else { + @Suppress("DEPRECATION") + window?.navigationBarColor = color + } + } + + // endregion } diff --git a/android/src/main/java/com/reactnativenavigation/viewcontrollers/bottomtabs/BottomTabsController.java b/android/src/main/java/com/reactnativenavigation/viewcontrollers/bottomtabs/BottomTabsController.java index b3deb2cd87f..49324fa2b6d 100644 --- a/android/src/main/java/com/reactnativenavigation/viewcontrollers/bottomtabs/BottomTabsController.java +++ b/android/src/main/java/com/reactnativenavigation/viewcontrollers/bottomtabs/BottomTabsController.java @@ -254,12 +254,13 @@ public boolean onMeasureChild(CoordinatorLayout parent, ViewGroup child, int par @Override public int getBottomInset(ViewController child) { - return presenter.getBottomInset(resolveChildOptions(child)) + perform(getParentController(), 0, p -> p.getBottomInset(this)); + return presenter.getChildrenBottomInset(resolveChildOptions(child)) + perform(getParentController(), 0, p -> p.getBottomInset(this)); } @Override public void applyBottomInset() { - presenter.applyBottomInset(getBottomInset()); + presenter.applyChildrenInset(getBottomInset()); + presenter.applySelfInset(getBottomInset()); super.applyBottomInset(); } diff --git a/android/src/main/java/com/reactnativenavigation/viewcontrollers/bottomtabs/BottomTabsPresenter.kt b/android/src/main/java/com/reactnativenavigation/viewcontrollers/bottomtabs/BottomTabsPresenter.kt index c2e435e8497..0e74dbe7869 100644 --- a/android/src/main/java/com/reactnativenavigation/viewcontrollers/bottomtabs/BottomTabsPresenter.kt +++ b/android/src/main/java/com/reactnativenavigation/viewcontrollers/bottomtabs/BottomTabsPresenter.kt @@ -1,6 +1,7 @@ package com.reactnativenavigation.viewcontrollers.bottomtabs import android.animation.Animator +import android.app.Activity import android.graphics.Color import android.view.ViewGroup import android.view.ViewGroup.LayoutParams.WRAP_CONTENT @@ -12,6 +13,8 @@ import com.reactnativenavigation.RNNToggles.TAB_BAR_TRANSLUCENCE import com.reactnativenavigation.options.Options import com.reactnativenavigation.options.params.BottomTabsLayoutStyle import com.reactnativenavigation.options.params.Fraction +import com.reactnativenavigation.utils.ColorUtils.isColorLight +import com.reactnativenavigation.utils.SystemUiUtils import com.reactnativenavigation.utils.UiUtils import com.reactnativenavigation.viewcontrollers.viewcontroller.ViewController import com.reactnativenavigation.views.bottomtabs.BottomTabs @@ -92,7 +95,9 @@ class BottomTabsPresenter( // Keep this before the translucent check below if (bottomTabsOptions.backgroundColor.hasValue()) { - bottomTabsContainer.setBackgroundColor(bottomTabsOptions.backgroundColor.get()) + val color = bottomTabsOptions.backgroundColor.get() + bottomTabsContainer.setBackgroundColor(color) + syncNavigationBarColor(options, color) } if (RNNFeatureToggles.isEnabled(TAB_BAR_TRANSLUCENCE)) { @@ -212,7 +217,9 @@ class BottomTabsPresenter( bottomTabsContainer.enableBackgroundBlur() } else { bottomTabsContainer.disableBackgroundBlur() - bottomTabsContainer.setBackgroundColor(bottomTabsOptions.backgroundColor.get(Color.WHITE)!!) + val color = bottomTabsOptions.backgroundColor.get(Color.WHITE)!! + bottomTabsContainer.setBackgroundColor(color) + syncNavigationBarColor(options, color) } bottomTabs.setLayoutDirection(options.layout.direction) @@ -282,13 +289,17 @@ class BottomTabsPresenter( bottomTabs.setBehaviorTranslationEnabled(bottomTabsOptions.hideOnScroll[false]) } - fun applyBottomInset(bottomInset: Int) { + fun applyChildrenInset(bottomInset: Int) { (bottomTabsContainer.layoutParams as ViewGroup.MarginLayoutParams).updateMargins(bottom = bottomInset) bottomTabsContainer.requestLayout() } - fun getBottomInset(resolvedOptions: Options): Int { - return if (resolvedOptions.withDefaultOptions(defaultOptions).bottomTabsOptions.isHiddenOrDrawBehind) 0 else bottomTabs.height + fun getChildrenBottomInset(resolvedOptions: Options): Int { + return if (resolvedOptions.withDefaultOptions(defaultOptions).bottomTabsOptions.isHiddenOrDrawBehind) 0 else (bottomTabsContainer.height) + } + + fun applySelfInset(bottomInset: Int) { + bottomTabsContainer.setBottomInset(bottomInset) } fun getPushAnimation(appearingOptions: Options): Animator? { @@ -351,7 +362,9 @@ class BottomTabsPresenter( bottomTabsContainer.disableBackgroundBlur() // TODO Change to bottomTabsContainer.setBackgroundColor()? - bottomTabs.setBackgroundColor(bottomTabsOptions.backgroundColor.get(Color.WHITE)!!) + val color = bottomTabsOptions.backgroundColor.get(Color.WHITE)!! + bottomTabs.setBackgroundColor(color) + syncNavigationBarColor(options, color) } if (bottomTabsOptions.shadowOptions.hasValue()) { @@ -376,4 +389,10 @@ class BottomTabsPresenter( val margin = UiUtils.dpToPx(bottomTabsContainer.context, marginDp.toFloat()).roundToInt() return margin } + + private fun syncNavigationBarColor(options: Options, tabsColor: Int) { + if (options.navigationBar.backgroundColor.hasValue()) return + val window = (bottomTabsContainer.context as? Activity)?.window ?: return + SystemUiUtils.setNavigationBarBackgroundColor(window, tabsColor, isColorLight(tabsColor)) + } } \ No newline at end of file diff --git a/android/src/main/java/com/reactnativenavigation/viewcontrollers/viewcontroller/LayoutDirectionApplier.kt b/android/src/main/java/com/reactnativenavigation/viewcontrollers/viewcontroller/LayoutDirectionApplier.kt index cdfc2e9e020..01843c30ccf 100644 --- a/android/src/main/java/com/reactnativenavigation/viewcontrollers/viewcontroller/LayoutDirectionApplier.kt +++ b/android/src/main/java/com/reactnativenavigation/viewcontrollers/viewcontroller/LayoutDirectionApplier.kt @@ -1,7 +1,6 @@ package com.reactnativenavigation.viewcontrollers.viewcontroller import android.annotation.SuppressLint -import com.facebook.react.ReactInstanceManager import com.facebook.react.modules.i18nmanager.I18nUtil import com.reactnativenavigation.options.Options @@ -16,4 +15,4 @@ class LayoutDirectionApplier { I18nUtil.instance.forceRTL(currentContext, options.layout.direction.isRtl) } } -} \ No newline at end of file +} diff --git a/android/src/main/java/com/reactnativenavigation/viewcontrollers/viewcontroller/Presenter.java b/android/src/main/java/com/reactnativenavigation/viewcontrollers/viewcontroller/Presenter.java index f1e16de088b..7c6b2b8d840 100644 --- a/android/src/main/java/com/reactnativenavigation/viewcontrollers/viewcontroller/Presenter.java +++ b/android/src/main/java/com/reactnativenavigation/viewcontrollers/viewcontroller/Presenter.java @@ -136,14 +136,12 @@ private void applyNavigationBarVisibility(NavigationBarOptions options) { } private void setNavigationBarBackgroundColor(NavigationBarOptions navigationBar) { - int navigationBarDefaultColor = SystemUiUtils.INSTANCE.getNavigationBarDefaultColor(); - navigationBarDefaultColor = navigationBarDefaultColor == -1 ? Color.BLACK : navigationBarDefaultColor; + int defaultColor = SystemUiUtils.getDefaultNavBarColor(); if (navigationBar.backgroundColor.canApplyValue()) { - int color = navigationBar.backgroundColor.get(navigationBarDefaultColor); + int color = navigationBar.backgroundColor.get(defaultColor); SystemUiUtils.setNavigationBarBackgroundColor(activity.getWindow(), color, isColorLight(color)); } else { - SystemUiUtils.setNavigationBarBackgroundColor(activity.getWindow(), navigationBarDefaultColor, isColorLight(navigationBarDefaultColor)); - + SystemUiUtils.setNavigationBarBackgroundColor(activity.getWindow(), defaultColor, isColorLight(defaultColor)); } } diff --git a/android/src/main/java/com/reactnativenavigation/viewcontrollers/viewcontroller/ViewController.java b/android/src/main/java/com/reactnativenavigation/viewcontrollers/viewcontroller/ViewController.java index 0035533a5c5..cd6bfe86306 100644 --- a/android/src/main/java/com/reactnativenavigation/viewcontrollers/viewcontroller/ViewController.java +++ b/android/src/main/java/com/reactnativenavigation/viewcontrollers/viewcontroller/ViewController.java @@ -430,17 +430,13 @@ public boolean onDependentViewChanged(CoordinatorLayout parent, ViewGroup child, return false; } - public void applyTopInset() { - - } + public void applyTopInset() {} public int getTopInset() { return 0; } - public void applyBottomInset() { - - } + public void applyBottomInset() {} public int getBottomInset() { return perform(parentController, 0, p -> p.getBottomInset(this)); diff --git a/android/src/main/java/com/reactnativenavigation/views/bottomtabs/BottomTabsContainer.kt b/android/src/main/java/com/reactnativenavigation/views/bottomtabs/BottomTabsContainer.kt index d992f567313..3a937ac6f0b 100644 --- a/android/src/main/java/com/reactnativenavigation/views/bottomtabs/BottomTabsContainer.kt +++ b/android/src/main/java/com/reactnativenavigation/views/bottomtabs/BottomTabsContainer.kt @@ -12,6 +12,7 @@ import android.widget.FrameLayout.LayoutParams.WRAP_CONTENT import android.widget.LinearLayout import androidx.annotation.RestrictTo import androidx.core.graphics.ColorUtils +import androidx.core.view.updatePadding import com.reactnativenavigation.options.params.Fraction import com.reactnativenavigation.utils.UiUtils.dpToPx import eightbitlab.com.blurview.BlurTarget @@ -176,4 +177,8 @@ class BottomTabsContainer(context: Context, val bottomTabs: BottomTabs) : Shadow fun setElevation(elevation: Fraction) { setElevation(dpToPx(context, elevation.get().toFloat())) } + + fun setBottomInset(bottomInset: Int) { + blurringView.updatePadding(bottom = bottomInset) + } } diff --git a/android/src/test/java/com/reactnativenavigation/viewcontrollers/stack/TopBarControllerTest.kt b/android/src/test/java/com/reactnativenavigation/viewcontrollers/stack/TopBarControllerTest.kt index 2d9b01f91c9..3c4f94f16df 100644 --- a/android/src/test/java/com/reactnativenavigation/viewcontrollers/stack/TopBarControllerTest.kt +++ b/android/src/test/java/com/reactnativenavigation/viewcontrollers/stack/TopBarControllerTest.kt @@ -51,7 +51,6 @@ class TopBarControllerTest : BaseTest() { override fun beforeEach() { super.beforeEach() - activity = newActivity() appearAnimator = spy(TopBarAppearanceAnimator()) colorAnimator = mock() diff --git a/e2e/assets/bottom_tabs.stylized-root.png b/e2e/assets/bottom_tabs.stylized-root.png new file mode 100644 index 00000000000..4000db82ae1 Binary files /dev/null and b/e2e/assets/bottom_tabs.stylized-root.png differ diff --git a/e2e/assets/bottom_tabs.stylized.png b/e2e/assets/bottom_tabs.stylized.png new file mode 100644 index 00000000000..e2235ded5a9 Binary files /dev/null and b/e2e/assets/bottom_tabs.stylized.png differ diff --git a/e2e/assets/side_menu.aboveContent.png b/e2e/assets/side_menu.aboveContent.png new file mode 100644 index 00000000000..f6a1542fa79 Binary files /dev/null and b/e2e/assets/side_menu.aboveContent.png differ diff --git a/e2e/assets/side_menu.pushContent.png b/e2e/assets/side_menu.pushContent.png new file mode 100644 index 00000000000..5a8b71de73b Binary files /dev/null and b/e2e/assets/side_menu.pushContent.png differ diff --git a/e2e/assets/side_menu.undefined.png b/e2e/assets/side_menu.undefined.png new file mode 100644 index 00000000000..c97b3093840 Binary files /dev/null and b/e2e/assets/side_menu.undefined.png differ diff --git a/lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/statusbar/StatusBarPresenter.kt b/lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/statusbar/StatusBarPresenter.kt new file mode 100644 index 00000000000..6b060ed4dc6 --- /dev/null +++ b/lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/statusbar/StatusBarPresenter.kt @@ -0,0 +1,212 @@ +package com.reactnativenavigation.viewcontrollers.statusbar + +import android.animation.Animator +import android.app.Activity +import android.graphics.Color +import android.view.View +import android.view.Window +import com.reactnativenavigation.RNNFeatureToggles.isEnabled +import com.reactnativenavigation.RNNToggles +import com.reactnativenavigation.options.Options +import com.reactnativenavigation.options.StatusBarOptions +import com.reactnativenavigation.options.StatusBarOptions.TextColorScheme +import com.reactnativenavigation.options.params.Bool +import com.reactnativenavigation.utils.ColorUtils.isColorLight +import com.reactnativenavigation.utils.StubAnimationListener.Companion.onAnimatorEnd +import com.reactnativenavigation.utils.SystemUiUtils.clearStatusBarTranslucency +import com.reactnativenavigation.utils.SystemUiUtils.getStatusBarColor +import com.reactnativenavigation.utils.SystemUiUtils.hideStatusBar +import com.reactnativenavigation.utils.SystemUiUtils.isTranslucent +import com.reactnativenavigation.utils.SystemUiUtils.setStatusBarColor +import com.reactnativenavigation.utils.SystemUiUtils.setStatusBarColorScheme +import com.reactnativenavigation.utils.SystemUiUtils.setStatusBarTranslucent +import com.reactnativenavigation.utils.SystemUiUtils.showStatusBar +import com.reactnativenavigation.viewcontrollers.viewcontroller.StatusBarColorAnimator +import com.reactnativenavigation.viewcontrollers.viewcontroller.ViewController +import java.lang.ref.WeakReference + +class StatusBarPresenter private constructor( + activity: Activity, + private val sbColorAnimator: StatusBarColorAnimator = StatusBarColorAnimator(activity)) { + + private val window = WeakReference(activity.window) + private var hasPendingColorAnim = false + + fun applyOptions(viewController: ViewController<*>, options: StatusBarOptions) { + if (!hasPendingColorAnim) { + setStatusBarBackgroundColor(options) + setTranslucent(options) + } + setTextColorScheme(options) + setStatusBarVisible(viewController, options.visible) + } + + fun mergeOptions(view: View, statusBar: StatusBarOptions) { + mergeStatusBarBackgroundColor(statusBar) + mergeTextColorScheme(statusBar) + mergeTranslucent(statusBar) + mergeStatusBarVisible(view, statusBar.visible) + } + + fun onConfigurationChanged(options: StatusBarOptions) { + setStatusBarBackgroundColor(options) + setTextColorScheme(options) + } + + fun bindViewController(newOptions: StatusBarOptions) { + if (!isEnabled(RNNToggles.TOP_BAR_COLOR_ANIMATION__TABS)) { + return + } + + if (newOptions.backgroundColor.canApplyValue()) { + val currentColor = getCurrentStatusBarBackgroundColor() ?: return + val newColor = getStatusBarBackgroundColor(newOptions) + createStatusBarColorAnimation( + from = currentColor, + to = newColor, + translucent = newOptions.translucent.isTrue, + ).start() + } + } + + fun getStatusBarPushAnimation(appearingOptions: Options): Animator? = + if (isEnabled(RNNToggles.TOP_BAR_COLOR_ANIMATION__PUSH)) { + getStatusBarColorAnimation(appearingOptions.statusBar) + } else null + + fun getStatusBarPopAnimation(appearingOptions: Options, disappearingOptions: Options): Animator? = + if (isEnabled(RNNToggles.TOP_BAR_COLOR_ANIMATION__PUSH)) { + getStatusBarColorAnimation(appearingOptions.statusBar) + } else null + + private fun setStatusBarBackgroundColor(statusBar: StatusBarOptions) { + if (statusBar.backgroundColor.canApplyValue()) { + val statusBarBackgroundColor: Int = getStatusBarBackgroundColor(statusBar) + setStatusBarBackgroundColor(statusBarBackgroundColor, statusBar.translucent.isTrue) + } + } + + private fun setStatusBarBackgroundColor(color: Int, translucent: Boolean) { + setStatusBarColor(window.get(), color, translucent) + } + + private fun getStatusBarBackgroundColor(statusBar: StatusBarOptions): Int { + val defaultColor = + if (statusBar.visible.isTrueOrUndefined) Color.BLACK else Color.TRANSPARENT + return statusBar.backgroundColor.get(defaultColor)!! + } + + private fun setTextColorScheme(statusBar: StatusBarOptions) { + val view = window.get()?.decorView + //View.post is a Workaround, added to solve internal Samsung + //Android 9 issues. For more info see https://github.com/wix/react-native-navigation/pull/7231 + view?.post { + setStatusBarColorScheme( + window.get(), + view, + isDarkTextColorScheme(statusBar) + ) + } + } + + private fun setTranslucent(options: StatusBarOptions) { + val window = window.get() + if (options.translucent.isTrue) { + setStatusBarTranslucent(window) + } else if (isTranslucent(window)) { + clearStatusBarTranslucency(window) + } + } + + private fun setStatusBarVisible(viewController: ViewController<*>, visible: Bool) { + val window = window.get() ?: return + val view = if (viewController.view != null) viewController.view else window.decorView + if (visible.isFalse) { + hideStatusBar(window, view) + } else { + showStatusBar(window, view) + } + } + + private fun mergeStatusBarBackgroundColor(statusBar: StatusBarOptions) { + if (statusBar.backgroundColor.hasValue()) { + val statusBarBackgroundColor = getStatusBarBackgroundColor(statusBar) + setStatusBarColor( + window.get(), statusBarBackgroundColor, + statusBar.translucent.isTrue + ) + } + } + + private fun mergeTextColorScheme(statusBar: StatusBarOptions) { + if (!statusBar.textColorScheme.hasValue()) return + setTextColorScheme(statusBar) + } + + private fun mergeTranslucent(options: StatusBarOptions) { + val window: Window = window.get() ?: return + if (options.translucent.isTrue) { + setStatusBarTranslucent(window) + } else if (options.translucent.isFalse && isTranslucent(window)) { + clearStatusBarTranslucency(window) + } + } + + private fun mergeStatusBarVisible(view: View, visible: Bool) { + if (visible.hasValue()) { + if (visible.isTrue) { + showStatusBar(window.get(), view) + } else { + hideStatusBar(window.get(), view) + } + } + } + + private fun isDarkTextColorScheme(statusBar: StatusBarOptions): Boolean { + if (statusBar.textColorScheme == TextColorScheme.Dark) { + return true + } + + if (statusBar.textColorScheme == TextColorScheme.Light) { + return false + } + return isColorLight(getStatusBarBackgroundColor(statusBar)) + } + + private fun getStatusBarColorAnimation(statusBarOptions: StatusBarOptions): Animator? { + if (isEnabled(RNNToggles.TOP_BAR_COLOR_ANIMATION__TABS)) { + getCurrentStatusBarBackgroundColor()?.let { currentColor -> + val targetColor = statusBarOptions.backgroundColor + + if (targetColor.hasValue()) { + val translucent = statusBarOptions.translucent.isTrue + return createStatusBarColorAnimation( + from = currentColor, + to = targetColor.get(), + translucent = translucent, + ) + } + } + } + return null + } + + private fun createStatusBarColorAnimation(from: Int, to: Int, translucent: Boolean): Animator = + sbColorAnimator.getAnimator(from, to, translucent).apply { + addListener(onAnimatorEnd { + hasPendingColorAnim = false + }) + hasPendingColorAnim = true + } + + private fun getCurrentStatusBarBackgroundColor() = + getStatusBarColor(window.get()) + + companion object { + lateinit var instance: StatusBarPresenter + + fun init(activity: Activity) { + instance = StatusBarPresenter(activity) + } + } +} diff --git a/package.json b/package.json index d5d366e79f6..21d9582d2ea 100644 --- a/package.json +++ b/package.json @@ -95,6 +95,7 @@ "@babel/runtime": "^7.25.0", "@babel/types": "7.25.0", "@d11/react-native-fast-image": "^8.13.0", + "@react-native-community/blur": "^4.4.1", "@react-native-community/cli": "20.0.0", "@react-native-community/cli-platform-android": "20.0.0", "@react-native-community/cli-platform-ios": "20.0.0", @@ -182,4 +183,4 @@ ] ] } -} \ No newline at end of file +} diff --git a/playground/android/gradle.properties b/playground/android/gradle.properties index 5b8b5878d26..97cc74f1fbb 100644 --- a/playground/android/gradle.properties +++ b/playground/android/gradle.properties @@ -19,6 +19,7 @@ org.gradle.jvmargs=-Xmx2048m # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects # org.gradle.parallel=true android.useAndroidX=true +android.enableJetifier=true android.jetifier.ignorelist=bcprov hermesEnabled=true newArchEnabled=true diff --git a/playground/android/settings.gradle b/playground/android/settings.gradle index 503cdf852e1..869220cd271 100644 --- a/playground/android/settings.gradle +++ b/playground/android/settings.gradle @@ -1,6 +1,8 @@ -pluginManagement { includeBuild("../node_modules/@react-native/gradle-plugin") } +pluginManagement { includeBuild("../../node_modules/@react-native/gradle-plugin") } plugins { id("com.facebook.react.settings") } extensions.configure(com.facebook.react.ReactSettingsExtension){ ex -> ex.autolinkLibrariesFromCommand() } +includeBuild('../../node_modules/@react-native/gradle-plugin') rootProject.name = 'Playground' include ':app' -includeBuild('../node_modules/@react-native/gradle-plugin') +include ':react-native-navigation' +project(':react-native-navigation').projectDir = new File(rootProject.projectDir, '../../android/') diff --git a/playground/e2e/assets/bottom_tabs.stylized-root.png b/playground/e2e/assets/bottom_tabs.stylized-root.png index 2f9c3116e64..4000db82ae1 100644 Binary files a/playground/e2e/assets/bottom_tabs.stylized-root.png and b/playground/e2e/assets/bottom_tabs.stylized-root.png differ diff --git a/playground/e2e/assets/bottom_tabs.stylized.png b/playground/e2e/assets/bottom_tabs.stylized.png index 3d9fa4554c1..e2235ded5a9 100644 Binary files a/playground/e2e/assets/bottom_tabs.stylized.png and b/playground/e2e/assets/bottom_tabs.stylized.png differ diff --git a/playground/package.json b/playground/package.json index d89eb2868e6..a8a0d6510bf 100644 --- a/playground/package.json +++ b/playground/package.json @@ -33,6 +33,7 @@ "@babel/preset-env": "^7.25.3", "@babel/runtime": "^7.25.0", "@babel/types": "7.25.0", + "@d11/react-native-fast-image": "^8.13.0", "@react-native-community/cli": "20.0.0", "@react-native-community/cli-platform-android": "20.0.0", "@react-native-community/cli-platform-ios": "20.0.0", @@ -61,7 +62,6 @@ "eslint-plugin-jest": "^28.11.0", "eslint-plugin-prettier": "3.1.4", "github-release-notes": "https://github.com/yogevbd/github-release-notes/tarball/e601b3dba72dcd6cba323c1286ea6dd0c0110b58", - "identity-obj-proxy": "3.0.0", "jest": "^29.6.3", "lint-staged": "10.2.11", @@ -70,7 +70,6 @@ "prettier": "2.8.8", "react": "19.2.3", "react-native": "0.84.0", - "@d11/react-native-fast-image": "^8.13.0", "react-native-gesture-handler": "^2.29.1", "react-native-monorepo-config": "^0.3.0", "react-native-reanimated": "4.2.2", @@ -85,7 +84,6 @@ "typedoc": "0.x.x", "typescript": "^5.8.3" }, - "lint-staged": { "*.{js,ts,tsx}": "eslint --fix", "*.{h,m,mm}": "node ./scripts/check-clang-format" diff --git a/playground/src/components/PressableScale.tsx b/playground/src/components/PressableScale.tsx index 5247ee1debe..a13adc5d394 100644 --- a/playground/src/components/PressableScale.tsx +++ b/playground/src/components/PressableScale.tsx @@ -24,8 +24,9 @@ export interface PressableScaleProps weight?: 'light' | 'medium' | 'heavy'; } -const ReanimatedTouchableWithoutFeedback = - Reanimated.createAnimatedComponent(TouchableWithoutFeedback); +const ReanimatedTouchableWithoutFeedback = Reanimated.createAnimatedComponent( + TouchableWithoutFeedback +); /** * A Pressable that scales down when pressed. Uses the JS Pressability API. diff --git a/playground/src/screens/OrientationScreen.tsx b/playground/src/screens/OrientationScreen.tsx index 6a066d40004..70575377ca1 100644 --- a/playground/src/screens/OrientationScreen.tsx +++ b/playground/src/screens/OrientationScreen.tsx @@ -5,8 +5,11 @@ import Button from '../components/Button'; import Screens from './Screens'; import testIDs from '../testIDs'; -const { LANDSCAPE_PORTRAIT_ORIENTATION_BTN, LANDSCAPE_ORIENTATION_BTN, PORTRAIT_ORIENTATION_BTN } = - testIDs; +const { + LANDSCAPE_PORTRAIT_ORIENTATION_BTN, + LANDSCAPE_ORIENTATION_BTN, + PORTRAIT_ORIENTATION_BTN, +} = testIDs; export default class OrientationScreen extends React.Component { render() { diff --git a/playground/src/screens/OverlayAlert.tsx b/playground/src/screens/OverlayAlert.tsx index b1727d479f9..a3ffcce8fe8 100644 --- a/playground/src/screens/OverlayAlert.tsx +++ b/playground/src/screens/OverlayAlert.tsx @@ -5,8 +5,12 @@ import { component } from '../commons/Layouts'; import Screens from './Screens'; import testIDs from '../testIDs'; -const { OVERLAY_ALERT_HEADER, DISMISS_BTN, SET_INTERCEPT_TOUCH, DISMISS_ALL_OVERLAYS_BUTTON } = - testIDs; +const { + OVERLAY_ALERT_HEADER, + DISMISS_BTN, + SET_INTERCEPT_TOUCH, + DISMISS_ALL_OVERLAYS_BUTTON, +} = testIDs; interface Props extends NavigationProps { incrementDismissedOverlays: any; diff --git a/playground/src/screens/StackCommandsScreen.tsx b/playground/src/screens/StackCommandsScreen.tsx index 03b7061bc12..ab8d4401e9f 100644 --- a/playground/src/screens/StackCommandsScreen.tsx +++ b/playground/src/screens/StackCommandsScreen.tsx @@ -42,7 +42,9 @@ export default class StackCommandsScreen extends NavigationComponent new Promise((resolve) => setTimeout(() => resolve(pushId), 100))) + .then( + (pushId) => new Promise((resolve) => setTimeout(() => resolve(pushId), 100)) + ) .then((pushId) => { this.setState({ pushPromiseResult: `push promise resolved with: ${pushId}`, diff --git a/playground/src/screens/sharedElementCarDealer/CarCard.tsx b/playground/src/screens/sharedElementCarDealer/CarCard.tsx index 7febbcba7a2..c125e4057e9 100644 --- a/playground/src/screens/sharedElementCarDealer/CarCard.tsx +++ b/playground/src/screens/sharedElementCarDealer/CarCard.tsx @@ -1,3 +1,4 @@ +import { BlurView } from '@react-native-community/blur'; import React, { useCallback, useEffect, useMemo, useRef } from 'react'; import { StyleSheet, Text, Dimensions, ViewProps, Platform } from 'react-native'; import Reanimated, { EasingNode, useValue } from 'react-native-reanimated'; @@ -76,6 +77,7 @@ export default function CarCard({ resizeMode="cover" /> + {Platform.OS === 'ios' && } + { it('exposes appLaunched event', () => { const subscription = {}; const cb = jest.fn(); - ( - mockNativeEventsReceiver.registerAppLaunchedListener as jest.MockedFunction - ).mockReturnValueOnce(subscription); + (mockNativeEventsReceiver.registerAppLaunchedListener as jest.MockedFunction< + any + >).mockReturnValueOnce(subscription); const result = uut.registerAppLaunchedListener(cb); diff --git a/yarn.lock b/yarn.lock index c904e2b1826..930b4285da0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1663,6 +1663,16 @@ __metadata: languageName: node linkType: hard +"@d11/react-native-fast-image@npm:^8.13.0": + version: 8.13.0 + resolution: "@d11/react-native-fast-image@npm:8.13.0" + peerDependencies: + react: "*" + react-native: "*" + checksum: 10/bbd3ed3db96ca54c86173ae9a0de8c254448a5ff495d9b4a33fe491bee8ad03b655ed2d7d862996af8817ee5562c2031e7e2969880229cac133e84fd1c3e9f78 + languageName: node + linkType: hard + "@dabh/diagnostics@npm:^2.0.8": version: 2.0.8 resolution: "@dabh/diagnostics@npm:2.0.8" @@ -2250,6 +2260,16 @@ __metadata: languageName: node linkType: hard +"@react-native-community/blur@npm:^4.4.1": + version: 4.4.1 + resolution: "@react-native-community/blur@npm:4.4.1" + peerDependencies: + react: "*" + react-native: "*" + checksum: 10/6fa84036db480af765df08d680955e9b5acaa631a54678b8eef970799fc04bdc7f906cb06a94f2da9af097c52ddb56ca4cff2172372d1d5f7717864f43965e0a + languageName: node + linkType: hard + "@react-native-community/cli-clean@npm:20.0.0": version: 20.0.0 resolution: "@react-native-community/cli-clean@npm:20.0.0" @@ -5016,13 +5036,6 @@ __metadata: languageName: node linkType: hard -"compare-versions@npm:^3.6.0": - version: 3.6.0 - resolution: "compare-versions@npm:3.6.0" - checksum: 10/7492a50cdaa2c27f5254eee7c4b38856e1c164991bab3d98d7fd067fe4b570d47123ecb92523b78338be86aa221668fd3868bfe8caa5587dc3ebbe1a03d52b5d - languageName: node - linkType: hard - "compressible@npm:~2.0.18": version: 2.0.18 resolution: "compressible@npm:2.0.18" @@ -6794,15 +6807,6 @@ __metadata: languageName: node linkType: hard -"find-versions@npm:^3.2.0": - version: 3.2.0 - resolution: "find-versions@npm:3.2.0" - dependencies: - semver-regex: "npm:^2.0.0" - checksum: 10/f010e00f9dedd5b83206762d668b4b3b86bbb81f3c2d957e2559969b9eadb6124297c4a2a1d51c5efea3d79557b19660a2758c77bb6a5ba5ce7750fba9847082 - languageName: node - linkType: hard - "flat-cache@npm:^3.0.4": version: 3.2.0 resolution: "flat-cache@npm:3.2.0" @@ -7733,27 +7737,6 @@ __metadata: languageName: node linkType: hard -"husky@npm:4.2.5": - version: 4.2.5 - resolution: "husky@npm:4.2.5" - dependencies: - chalk: "npm:^4.0.0" - ci-info: "npm:^2.0.0" - compare-versions: "npm:^3.6.0" - cosmiconfig: "npm:^6.0.0" - find-versions: "npm:^3.2.0" - opencollective-postinstall: "npm:^2.0.2" - pkg-dir: "npm:^4.2.0" - please-upgrade-node: "npm:^3.2.0" - slash: "npm:^3.0.0" - which-pm-runs: "npm:^1.0.0" - bin: - husky-run: bin/run.js - husky-upgrade: lib/upgrader/bin.js - checksum: 10/57fb1723f5c6e28edffd79896a5b10a69b4c55f89ddfa1fe6354cf37cf2dab27d7159213d4fc90e9bef2ea10ea1ca60d2003985b77480b4fa719f29d0e89153f - languageName: node - linkType: hard - "iconv-lite@npm:^0.4.17, iconv-lite@npm:~0.4.24": version: 0.4.24 resolution: "iconv-lite@npm:0.4.24" @@ -11430,15 +11413,6 @@ __metadata: languageName: node linkType: hard -"opencollective-postinstall@npm:^2.0.2": - version: 2.0.3 - resolution: "opencollective-postinstall@npm:2.0.3" - bin: - opencollective-postinstall: index.js - checksum: 10/69d63778087cd10c9d707d9ed360556780cfdd0cd6241ded0e26632f467f1d5a064f4a9aec19a30c187770c17adba034d988f7684b226f3a73e79f44e73fab0e - languageName: node - linkType: hard - "opener@npm:^1.5.2": version: 1.5.2 resolution: "opener@npm:1.5.2" @@ -12278,16 +12252,6 @@ __metadata: languageName: node linkType: hard -"react-native-fast-image@npm:^8.6.3": - version: 8.6.3 - resolution: "react-native-fast-image@npm:8.6.3" - peerDependencies: - react: ^17 || ^18 - react-native: ">=0.60.0" - checksum: 10/ed340c2b6c2e76658fa0a899a45132871017018bec0a03423b7456610810e79b2844dfa16063c05d98b89a7613eeb0de2fdf3ad978cdfa4d488a4e01228a7349 - languageName: node - linkType: hard - "react-native-gesture-handler@npm:^2.29.1": version: 2.30.0 resolution: "react-native-gesture-handler@npm:2.30.0" @@ -12332,6 +12296,7 @@ __metadata: "@babel/preset-env": "npm:^7.25.3" "@babel/runtime": "npm:^7.25.0" "@babel/types": "npm:7.25.0" + "@d11/react-native-fast-image": "npm:^8.13.0" "@react-native-community/cli": "npm:20.0.0" "@react-native-community/cli-platform-android": "npm:20.0.0" "@react-native-community/cli-platform-ios": "npm:20.0.0" @@ -12361,7 +12326,6 @@ __metadata: eslint-plugin-prettier: "npm:3.1.4" github-release-notes: "https://github.com/yogevbd/github-release-notes/tarball/e601b3dba72dcd6cba323c1286ea6dd0c0110b58" hoist-non-react-statics: "npm:^3.3.2" - husky: "npm:4.2.5" identity-obj-proxy: "npm:3.0.0" jest: "npm:^29.6.3" lint-staged: "npm:10.2.11" @@ -12373,7 +12337,6 @@ __metadata: react: "npm:19.2.3" react-lifecycles-compat: "npm:^3.0.4" react-native: "npm:0.84.0" - react-native-fast-image: "npm:^8.6.3" react-native-gesture-handler: "npm:^2.29.1" react-native-monorepo-config: "npm:^0.3.0" react-native-reanimated: "npm:4.2.2" @@ -12405,6 +12368,8 @@ __metadata: "@babel/preset-env": "npm:^7.25.3" "@babel/runtime": "npm:^7.25.0" "@babel/types": "npm:7.25.0" + "@d11/react-native-fast-image": "npm:^8.13.0" + "@react-native-community/blur": "npm:^4.4.1" "@react-native-community/cli": "npm:20.0.0" "@react-native-community/cli-platform-android": "npm:20.0.0" "@react-native-community/cli-platform-ios": "npm:20.0.0" @@ -12435,7 +12400,6 @@ __metadata: eslint-plugin-prettier: "npm:3.1.4" github-release-notes: "https://github.com/yogevbd/github-release-notes/tarball/e601b3dba72dcd6cba323c1286ea6dd0c0110b58" hoist-non-react-statics: "npm:3.3.2" - husky: "npm:4.2.5" identity-obj-proxy: "npm:3.0.0" jest: "npm:^29.6.3" lint-staged: "npm:10.2.11" @@ -12448,7 +12412,6 @@ __metadata: react-lifecycles-compat: "npm:^3.0.4" react-native: "npm:0.84.0" react-native-builder-bob: "npm:^0.40.13" - react-native-fast-image: "npm:^8.6.3" react-native-gesture-handler: "npm:^2.29.1" react-native-reanimated: "npm:4.2.2" react-native-worklets: "npm:0.7.4" @@ -13307,13 +13270,6 @@ __metadata: languageName: node linkType: hard -"semver-regex@npm:^2.0.0": - version: 2.0.0 - resolution: "semver-regex@npm:2.0.0" - checksum: 10/da7d6f5ceae80e2097933b1e4ea2815c2cfa2c50c6501db1a3d435a6063c0f23d66bc25fe8d06755048f3d7588d85339db6471446b2c91fea907e5c2ada5b0df - languageName: node - linkType: hard - "semver@npm:2 || 3 || 4 || 5, semver@npm:2.x || 3.x || 4 || 5, semver@npm:5.x.x, semver@npm:^2.3.0 || 3.x || 4 || 5, semver@npm:^5.0.3, semver@npm:^5.1.0, semver@npm:^5.4.1, semver@npm:^5.5.1, semver@npm:^5.6.0, semver@npm:^5.7.1": version: 5.7.2 resolution: "semver@npm:5.7.2" @@ -15100,13 +15056,6 @@ __metadata: languageName: node linkType: hard -"which-pm-runs@npm:^1.0.0": - version: 1.1.0 - resolution: "which-pm-runs@npm:1.1.0" - checksum: 10/39a56ee50886fb33ec710e3b36dc9fe3d0096cac44850d9ca0c6186c4cb824d6c8125f013e0562e7c94744e1e8e4a6ab695592cdb12555777c7a4368143d822c - languageName: node - linkType: hard - "which-typed-array@npm:^1.1.16, which-typed-array@npm:^1.1.19": version: 1.1.20 resolution: "which-typed-array@npm:1.1.20"