diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b68274cc..6aded0520 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,8 @@ and adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). - https://github.com/Shopify/flash-list/pull/1076 - Fix stale reference to onScroll and onLoad - https://github.com/Shopify/flash-list/pull/1112 +- New architecture support + - https://github.com/Shopify/flash-list/pull/550 - Upgrade recyclerlistview to v4.2.1 - https://github.com/Shopify/flash-list/pull/1236 diff --git a/RNFlashList.podspec b/RNFlashList.podspec index 38ff0299c..d2a71a5bf 100644 --- a/RNFlashList.podspec +++ b/RNFlashList.podspec @@ -1,6 +1,7 @@ require 'json' package = JSON.parse(File.read(File.join(__dir__, 'package.json'))) +new_arch_enabled = ENV['RCT_NEW_ARCH_ENABLED'] Pod::Spec.new do |s| s.name = 'RNFlashList' @@ -15,8 +16,18 @@ Pod::Spec.new do |s| s.requires_arc = true s.swift_version = '5.0' - # Dependencies - s.dependency 'React-Core' + if new_arch_enabled + s.pod_target_xcconfig = { + 'OTHER_SWIFT_FLAGS' => '-D RCT_NEW_ARCH_ENABLED', + } + end + + # install_modules_dependencies is available since RN 0.71 + if respond_to?(:install_modules_dependencies, true) + install_modules_dependencies(s) + else + s.dependency "React-Core" + end # Tests spec s.test_spec 'Tests' do |test_spec| diff --git a/android/build.gradle b/android/build.gradle index bed08cd57..32f308988 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,7 +1,15 @@ +def isNewArchitectureEnabled() { + return project.hasProperty("newArchEnabled") && project.newArchEnabled == "true" +} + apply plugin: 'com.android.library' apply plugin: 'kotlin-android' +if (isNewArchitectureEnabled()) { + apply plugin: 'com.facebook.react' +} + def _ext = rootProject.ext def _reactNativeVersion = _ext.has('reactNative') ? _ext.reactNative : '+' @@ -40,11 +48,16 @@ android { debug.java.srcDirs += 'src/debug/kotlin' test.java.srcDirs += 'src/test/kotlin' androidTest.java.srcDirs += 'src/androidTest/kotlin' + + if (!isNewArchitectureEnabled()) { + main.java.srcDirs += ['src/paper/java'] + } } defaultConfig { minSdkVersion _minSdkVersion targetSdkVersion _targetSdkVersion + buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString() versionCode 1 versionName "1.0" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" diff --git a/android/src/main/kotlin/com/shopify/reactnative/flash_list/AutoLayoutView.kt b/android/src/main/kotlin/com/shopify/reactnative/flash_list/AutoLayoutView.kt index 457179850..8636aa72a 100644 --- a/android/src/main/kotlin/com/shopify/reactnative/flash_list/AutoLayoutView.kt +++ b/android/src/main/kotlin/com/shopify/reactnative/flash_list/AutoLayoutView.kt @@ -11,6 +11,8 @@ import android.widget.ScrollView import com.facebook.react.bridge.Arguments import com.facebook.react.bridge.ReactContext import com.facebook.react.bridge.WritableMap +import com.facebook.react.uimanager.UIManagerHelper +import com.facebook.react.uimanager.events.EventDispatcher import com.facebook.react.uimanager.events.RCTEventEmitter import com.facebook.react.views.view.ReactViewGroup @@ -137,14 +139,20 @@ class AutoLayoutView(context: Context) : ReactViewGroup(context) { } - /** TODO: Check migration to Fabric */ private fun emitBlankAreaEvent() { - val event: WritableMap = Arguments.createMap() - event.putDouble("offsetStart", alShadow.blankOffsetAtStart / pixelDensity) - event.putDouble("offsetEnd", alShadow.blankOffsetAtEnd / pixelDensity) - val reactContext = context as ReactContext - reactContext - .getJSModule(RCTEventEmitter::class.java) - .receiveEvent(id, "onBlankAreaEvent", event) + val eventDispatcher: EventDispatcher? = + UIManagerHelper.getEventDispatcherForReactTag(context as ReactContext, id) + + if (eventDispatcher != null) { + val surfaceId = UIManagerHelper.getSurfaceId(context as ReactContext) + eventDispatcher.dispatchEvent( + BlankAreaEvent( + surfaceId, + viewTag = id, + offsetStart = alShadow.blankOffsetAtStart / pixelDensity, + offsetEnd = alShadow.blankOffsetAtEnd / pixelDensity + ) + ) + } } } diff --git a/android/src/main/kotlin/com/shopify/reactnative/flash_list/AutoLayoutViewManager.kt b/android/src/main/kotlin/com/shopify/reactnative/flash_list/AutoLayoutViewManager.kt index b646f09c1..b7f3d6506 100644 --- a/android/src/main/kotlin/com/shopify/reactnative/flash_list/AutoLayoutViewManager.kt +++ b/android/src/main/kotlin/com/shopify/reactnative/flash_list/AutoLayoutViewManager.kt @@ -3,15 +3,18 @@ package com.shopify.reactnative.flash_list import com.facebook.react.module.annotations.ReactModule import com.facebook.react.uimanager.ThemedReactContext import com.facebook.react.uimanager.annotations.ReactProp -import com.facebook.react.views.view.ReactViewGroup -import com.facebook.react.views.view.ReactViewManager -import com.facebook.react.common.MapBuilder +import com.facebook.react.uimanager.ViewGroupManager +import com.facebook.react.uimanager.ViewManagerDelegate +import com.facebook.react.viewmanagers.AutoLayoutViewManagerDelegate +import com.facebook.react.viewmanagers.AutoLayoutViewManagerInterface import kotlin.math.roundToInt /** ViewManager for AutoLayoutView - Container for all RecyclerListView children. Automatically removes all gaps and overlaps for GridLayouts with flexible spans. * Note: This cannot work for masonry layouts i.e, pinterest like layout */ @ReactModule(name = AutoLayoutViewManager.REACT_CLASS) -class AutoLayoutViewManager: ReactViewManager() { +class AutoLayoutViewManager: ViewGroupManager(), AutoLayoutViewManagerInterface { + private val mDelegate: AutoLayoutViewManagerDelegate = + AutoLayoutViewManagerDelegate(this) companion object { const val REACT_CLASS = "AutoLayoutView" @@ -21,45 +24,43 @@ class AutoLayoutViewManager: ReactViewManager() { return REACT_CLASS } - override fun createViewInstance(context: ThemedReactContext): ReactViewGroup { + override fun getDelegate(): ViewManagerDelegate = mDelegate + + override fun createViewInstance(context: ThemedReactContext): AutoLayoutView { return AutoLayoutView(context).also { it.pixelDensity = context.resources.displayMetrics.density.toDouble() } } - override fun getExportedCustomDirectEventTypeConstants(): MutableMap { - return MapBuilder.builder().put( - "onBlankAreaEvent", - MapBuilder.of( - "registrationName", "onBlankAreaEvent") - ).build(); - } + override fun getExportedCustomDirectEventTypeConstants() = mutableMapOf( + "onBlankAreaEvent" to mutableMapOf("registrationName" to "onBlankAreaEvent"), + ) @ReactProp(name = "horizontal") - fun setHorizontal(view: AutoLayoutView, isHorizontal: Boolean) { + override fun setHorizontal(view: AutoLayoutView, isHorizontal: Boolean) { view.alShadow.horizontal = isHorizontal } @ReactProp(name = "disableAutoLayout") - fun setDisableAutoLayout(view: AutoLayoutView, disableAutoLayout: Boolean) { + override fun setDisableAutoLayout(view: AutoLayoutView, disableAutoLayout: Boolean) { view.disableAutoLayout = disableAutoLayout } @ReactProp(name = "scrollOffset") - fun setScrollOffset(view: AutoLayoutView, scrollOffset: Double) { + override fun setScrollOffset(view: AutoLayoutView, scrollOffset: Double) { view.alShadow.scrollOffset = convertToPixelLayout(scrollOffset, view.pixelDensity) } @ReactProp(name = "windowSize") - fun setWindowSize(view: AutoLayoutView, windowSize: Double) { + override fun setWindowSize(view: AutoLayoutView, windowSize: Double) { view.alShadow.windowSize = convertToPixelLayout(windowSize, view.pixelDensity) } @ReactProp(name = "renderAheadOffset") - fun setRenderAheadOffset(view: AutoLayoutView, renderOffset: Double) { + override fun setRenderAheadOffset(view: AutoLayoutView, renderOffset: Double) { view.alShadow.renderOffset = convertToPixelLayout(renderOffset, view.pixelDensity) } @ReactProp(name = "enableInstrumentation") - fun setEnableInstrumentation(view: AutoLayoutView, enableInstrumentation: Boolean) { + override fun setEnableInstrumentation(view: AutoLayoutView, enableInstrumentation: Boolean) { view.enableInstrumentation = enableInstrumentation } diff --git a/android/src/main/kotlin/com/shopify/reactnative/flash_list/BlankAreaEvent.kt b/android/src/main/kotlin/com/shopify/reactnative/flash_list/BlankAreaEvent.kt new file mode 100644 index 000000000..ed74c4358 --- /dev/null +++ b/android/src/main/kotlin/com/shopify/reactnative/flash_list/BlankAreaEvent.kt @@ -0,0 +1,29 @@ +@file:Suppress("DEPRECATION") // We want to use RCTEventEmitter for interop purposes +package com.shopify.reactnative.flash_list + +import com.facebook.react.bridge.Arguments +import com.facebook.react.bridge.WritableMap +import com.facebook.react.uimanager.events.Event +import com.facebook.react.uimanager.events.RCTEventEmitter + +class BlankAreaEvent( + surfaceId: Int, + viewTag: Int, + private val offsetStart: Double, + private val offsetEnd: Double +): Event(surfaceId, viewTag) { + override fun getEventName() = EVENT_NAME + + override fun getEventData(): WritableMap = Arguments.createMap().apply { + putDouble("offsetStart", offsetStart) + putDouble("offsetEnd", offsetEnd) + } + + override fun dispatch(rctEventEmitter: RCTEventEmitter) { + rctEventEmitter.receiveEvent(viewTag, eventName, eventData) + } + + companion object { + const val EVENT_NAME: String = "onBlankAreaEvent" + } +} \ No newline at end of file diff --git a/android/src/main/kotlin/com/shopify/reactnative/flash_list/CellContainerManager.kt b/android/src/main/kotlin/com/shopify/reactnative/flash_list/CellContainerManager.kt index 1434caaae..372cb0a23 100644 --- a/android/src/main/kotlin/com/shopify/reactnative/flash_list/CellContainerManager.kt +++ b/android/src/main/kotlin/com/shopify/reactnative/flash_list/CellContainerManager.kt @@ -2,12 +2,17 @@ package com.shopify.reactnative.flash_list import com.facebook.react.module.annotations.ReactModule import com.facebook.react.uimanager.ThemedReactContext +import com.facebook.react.uimanager.ViewGroupManager +import com.facebook.react.uimanager.ViewManagerDelegate import com.facebook.react.uimanager.annotations.ReactProp -import com.facebook.react.views.view.ReactViewGroup -import com.facebook.react.views.view.ReactViewManager +import com.facebook.react.viewmanagers.CellContainerManagerDelegate +import com.facebook.react.viewmanagers.CellContainerManagerInterface @ReactModule(name = AutoLayoutViewManager.REACT_CLASS) -class CellContainerManager: ReactViewManager() { +class CellContainerManager: ViewGroupManager(), CellContainerManagerInterface { + private val mDelegate: CellContainerManagerDelegate + = CellContainerManagerDelegate(this) + companion object { const val REACT_CLASS = "CellContainer" } @@ -16,12 +21,14 @@ class CellContainerManager: ReactViewManager() { return REACT_CLASS } - override fun createViewInstance(context: ThemedReactContext): ReactViewGroup { + override fun getDelegate(): ViewManagerDelegate = mDelegate + + override fun createViewInstance(context: ThemedReactContext): CellContainerImpl { return CellContainerImpl(context) } @ReactProp(name = "index") - fun setIndex(view: CellContainerImpl, index: Int) { + override fun setIndex(view: CellContainerImpl, index: Int) { view.index = index } } diff --git a/android/src/paper/java/com/facebook/react/viewmanagers/AutoLayoutViewManagerDelegate.java b/android/src/paper/java/com/facebook/react/viewmanagers/AutoLayoutViewManagerDelegate.java new file mode 100644 index 000000000..4c9080790 --- /dev/null +++ b/android/src/paper/java/com/facebook/react/viewmanagers/AutoLayoutViewManagerDelegate.java @@ -0,0 +1,46 @@ +/** +* This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen). +* +* Do not edit this file as changes may cause incorrect behavior and will be lost +* once the code is regenerated. +* +* @generated by codegen project: GeneratePropsJavaDelegate.js +*/ + +package com.facebook.react.viewmanagers; + +import android.view.View; +import androidx.annotation.Nullable; +import com.facebook.react.uimanager.BaseViewManagerDelegate; +import com.facebook.react.uimanager.BaseViewManagerInterface; + +public class AutoLayoutViewManagerDelegate & AutoLayoutViewManagerInterface> extends BaseViewManagerDelegate { + public AutoLayoutViewManagerDelegate(U viewManager) { + super(viewManager); + } + @Override + public void setProperty(T view, String propName, @Nullable Object value) { + switch (propName) { + case "horizontal": + mViewManager.setHorizontal(view, value == null ? false : (boolean) value); + break; + case "scrollOffset": + mViewManager.setScrollOffset(view, value == null ? 0f : ((Double) value).doubleValue()); + break; + case "windowSize": + mViewManager.setWindowSize(view, value == null ? 0f : ((Double) value).doubleValue()); + break; + case "renderAheadOffset": + mViewManager.setRenderAheadOffset(view, value == null ? 0f : ((Double) value).doubleValue()); + break; + case "enableInstrumentation": + mViewManager.setEnableInstrumentation(view, value == null ? false : (boolean) value); + break; + case "disableAutoLayout": + mViewManager.setDisableAutoLayout(view, value == null ? false : (boolean) value); + break; + default: + super.setProperty(view, propName, value); + } + } +} diff --git a/android/src/paper/java/com/facebook/react/viewmanagers/AutoLayoutViewManagerInterface.java b/android/src/paper/java/com/facebook/react/viewmanagers/AutoLayoutViewManagerInterface.java new file mode 100644 index 000000000..8ad2622a1 --- /dev/null +++ b/android/src/paper/java/com/facebook/react/viewmanagers/AutoLayoutViewManagerInterface.java @@ -0,0 +1,21 @@ +/** +* This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen). +* +* Do not edit this file as changes may cause incorrect behavior and will be lost +* once the code is regenerated. +* +* @generated by codegen project: GeneratePropsJavaInterface.js +*/ + +package com.facebook.react.viewmanagers; + +import android.view.View; + +public interface AutoLayoutViewManagerInterface { + void setHorizontal(T view, boolean value); + void setScrollOffset(T view, double value); + void setWindowSize(T view, double value); + void setRenderAheadOffset(T view, double value); + void setEnableInstrumentation(T view, boolean value); + void setDisableAutoLayout(T view, boolean value); +} diff --git a/android/src/paper/java/com/facebook/react/viewmanagers/CellContainerManagerDelegate.java b/android/src/paper/java/com/facebook/react/viewmanagers/CellContainerManagerDelegate.java new file mode 100644 index 000000000..2b64af7d5 --- /dev/null +++ b/android/src/paper/java/com/facebook/react/viewmanagers/CellContainerManagerDelegate.java @@ -0,0 +1,31 @@ +/** +* This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen). +* +* Do not edit this file as changes may cause incorrect behavior and will be lost +* once the code is regenerated. +* +* @generated by codegen project: GeneratePropsJavaDelegate.js +*/ + +package com.facebook.react.viewmanagers; + +import android.view.View; +import androidx.annotation.Nullable; +import com.facebook.react.uimanager.BaseViewManagerDelegate; +import com.facebook.react.uimanager.BaseViewManagerInterface; + +public class CellContainerManagerDelegate & CellContainerManagerInterface> extends BaseViewManagerDelegate { + public CellContainerManagerDelegate(U viewManager) { + super(viewManager); + } + @Override + public void setProperty(T view, String propName, @Nullable Object value) { + switch (propName) { + case "index": + mViewManager.setIndex(view, value == null ? 0 : ((Double) value).intValue()); + break; + default: + super.setProperty(view, propName, value); + } + } +} diff --git a/android/src/paper/java/com/facebook/react/viewmanagers/CellContainerManagerInterface.java b/android/src/paper/java/com/facebook/react/viewmanagers/CellContainerManagerInterface.java new file mode 100644 index 000000000..b37ddbd5b --- /dev/null +++ b/android/src/paper/java/com/facebook/react/viewmanagers/CellContainerManagerInterface.java @@ -0,0 +1,16 @@ +/** +* This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen). +* +* Do not edit this file as changes may cause incorrect behavior and will be lost +* once the code is regenerated. +* +* @generated by codegen project: GeneratePropsJavaInterface.js +*/ + +package com.facebook.react.viewmanagers; + +import android.view.View; + +public interface CellContainerManagerInterface { + void setIndex(T view, int value); +} diff --git a/fixture/react-native/ios/FlatListPro.xcodeproj/project.pbxproj b/fixture/react-native/ios/FlatListPro.xcodeproj/project.pbxproj index b257b965a..3a9a493cb 100644 --- a/fixture/react-native/ios/FlatListPro.xcodeproj/project.pbxproj +++ b/fixture/react-native/ios/FlatListPro.xcodeproj/project.pbxproj @@ -10,8 +10,8 @@ 13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.mm */; }; 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; - 4C28D4195347110DD7EFF250 /* libPods-FlatListPro.a in Frameworks */ = {isa = PBXBuildFile; fileRef = B5C2CC4776DE30CFFD3D9728 /* libPods-FlatListPro.a */; }; 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; }; + E446F9835C9F45BAEF26E55A /* Pods_FlatListPro.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1BD677735D2BD8E1B86817A1 /* Pods_FlatListPro.framework */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -21,10 +21,10 @@ 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = FlatListPro/Images.xcassets; sourceTree = ""; }; 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = FlatListPro/Info.plist; sourceTree = ""; }; 13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = FlatListPro/main.m; sourceTree = ""; tabWidth = 4; }; + 1BD677735D2BD8E1B86817A1 /* Pods_FlatListPro.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_FlatListPro.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 5715F3B3165A79E0A40F6237 /* Pods-FlatListPro.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-FlatListPro.debug.xcconfig"; path = "Target Support Files/Pods-FlatListPro/Pods-FlatListPro.debug.xcconfig"; sourceTree = ""; }; 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = FlatListPro/LaunchScreen.storyboard; sourceTree = ""; }; 906B19594009D131502608CA /* Pods-FlatListPro.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-FlatListPro.release.xcconfig"; path = "Target Support Files/Pods-FlatListPro/Pods-FlatListPro.release.xcconfig"; sourceTree = ""; }; - B5C2CC4776DE30CFFD3D9728 /* libPods-FlatListPro.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-FlatListPro.a"; sourceTree = BUILT_PRODUCTS_DIR; }; ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; /* End PBXFileReference section */ @@ -33,7 +33,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 4C28D4195347110DD7EFF250 /* libPods-FlatListPro.a in Frameworks */, + E446F9835C9F45BAEF26E55A /* Pods_FlatListPro.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -57,7 +57,7 @@ isa = PBXGroup; children = ( ED297162215061F000B7C4FE /* JavaScriptCore.framework */, - B5C2CC4776DE30CFFD3D9728 /* libPods-FlatListPro.a */, + 1BD677735D2BD8E1B86817A1 /* Pods_FlatListPro.framework */, ); name = Frameworks; sourceTree = ""; @@ -375,7 +375,15 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + "${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon-Samples/ReactCommon_Samples.framework/Headers", + "${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers/react/nativemodule/core", + "${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon-Samples/ReactCommon_Samples.framework/Headers/platform/ios", + "${PODS_CONFIGURATION_BUILD_DIR}/React-NativeModulesApple/React_NativeModulesApple.framework/Headers", + "${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers/react/renderer/graphics/platform/ios", + ); + IPHONEOS_DEPLOYMENT_TARGET = 12.4; LD_RUNPATH_SEARCH_PATHS = ( /usr/lib/swift, "$(inherited)", @@ -387,8 +395,14 @@ ); MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; - OTHER_CFLAGS = "$(inherited)"; - OTHER_CPLUSPLUSFLAGS = "$(inherited)"; + OTHER_CFLAGS = ( + "$(inherited)", + " ", + ); + OTHER_CPLUSPLUSFLAGS = ( + "$(inherited)", + " ", + ); OTHER_LDFLAGS = ( "$(inherited)", " ", @@ -444,7 +458,15 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + "${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon-Samples/ReactCommon_Samples.framework/Headers", + "${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers/react/nativemodule/core", + "${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon-Samples/ReactCommon_Samples.framework/Headers/platform/ios", + "${PODS_CONFIGURATION_BUILD_DIR}/React-NativeModulesApple/React_NativeModulesApple.framework/Headers", + "${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers/react/renderer/graphics/platform/ios", + ); + IPHONEOS_DEPLOYMENT_TARGET = 12.4; LD_RUNPATH_SEARCH_PATHS = ( /usr/lib/swift, "$(inherited)", @@ -455,8 +477,14 @@ "\"$(inherited)\"", ); MTL_ENABLE_DEBUG_INFO = NO; - OTHER_CFLAGS = "$(inherited)"; - OTHER_CPLUSPLUSFLAGS = "$(inherited)"; + OTHER_CFLAGS = ( + "$(inherited)", + " ", + ); + OTHER_CPLUSPLUSFLAGS = ( + "$(inherited)", + " ", + ); OTHER_LDFLAGS = ( "$(inherited)", " ", diff --git a/fixture/react-native/ios/Podfile b/fixture/react-native/ios/Podfile index d068f850f..3574f83e3 100644 --- a/fixture/react-native/ios/Podfile +++ b/fixture/react-native/ios/Podfile @@ -29,6 +29,9 @@ if linkage != nil use_frameworks! :linkage => linkage.to_sym end +# Shouldn't be necessary in newer versions of React Native +use_frameworks! :linkage => :static + target 'FlatListPro' do config = use_native_modules! diff --git a/fixture/react-native/ios/Podfile.lock b/fixture/react-native/ios/Podfile.lock index 78f5c1d14..b81f7836a 100644 --- a/fixture/react-native/ios/Podfile.lock +++ b/fixture/react-native/ios/Podfile.lock @@ -444,8 +444,10 @@ PODS: - SDWebImage (~> 5.11.1) - SDWebImageWebPCoder (~> 0.8.4) - RNFlashList (1.6.4): + - RCT-Folly (= 2021.07.22.00) - React-Core - RNFlashList/Tests (1.6.4): + - RCT-Folly (= 2021.07.22.00) - React-Core - RNGestureHandler (2.15.0): - RCT-Folly (= 2021.07.22.00) @@ -633,44 +635,44 @@ SPEC CHECKSUMS: RCTTypeSafety: a4551b3d338c96435f63bf06d564055c1d3cc0ac React: 66caa2a8192a35d7ba466a5fdf5dc06ee4a5f6dd React-callinvoker: e5b55e46894c2dd1bcdc19d4f82b0f7f631d1237 - React-Codegen: 0cf41e00026c5eba61f6bdcabd6e4bf659754f33 - React-Core: 2ce84187a00913f287b96753c56c7819ed7d90d5 - React-CoreModules: 893e7c5eed1ef8fe9e1ee1d913581c946e55b305 + React-Codegen: a0bef31e52be718d5feb05804b482f225a022c38 + React-Core: 345ae766c749f0051c1f41d9a228ddf690e7dd13 + React-CoreModules: 965a80829f789cb6a99308e99352369809f01690 React-cxxreact: 075d98dc664c0e9607cc0c45d41dc052bcc7313b - React-debug: abc6213dcb9eafcf5242cbb194fef4c70c91871f + React-debug: c61caaf4d8d74bad779deed1d94faf27b823f290 React-hermes: 133cfa220ef836406f693ed7db56a509032ce433 React-jsi: 9b45fd040d575f8ae6771bf1960641a58eb0bdd4 React-jsiexecutor: 45ef2ec6dcde31b90469175ec76ddac77b91dfc3 React-jsinspector: de0198127395fec3058140a20c045167f761bb16 React-logger: dc3a2b174d79c2da635059212747d8d929b54e06 react-native-safe-area-context: dcab599c527c2d7de2d76507a523d20a0b83823d - React-NativeModulesApple: c3e696ff867e4bc212266cbdf7e862e48a0166fd + React-NativeModulesApple: 10093349d4cfd7cc1aabeadba47549920d19bc2d React-perflogger: 43287389ea08993c300897a46f95cfac04bb6c1a React-RCTActionSheet: 923afe77f9bb89da7c1f98e2730bfc9dde0eed6d - React-RCTAnimation: afd4d94c5e1f731e32ac99800850be06564ac642 - React-RCTAppDelegate: fb2e1447d014557f29e214fe2eb777442f808a3b - React-RCTBlob: 167e2c6c3643f093058c51e76ecc653fc8236033 - React-RCTImage: 867de82a17630a08a3fa64b0cd6677dd19bf6eaf - React-RCTLinking: 885dde8bc5d397c3e72c76315f1f9b5030b3a70e - React-RCTNetwork: efec71102220b96ac8605d0253debd859ca0c817 - React-RCTSettings: 077065d0a4e925b017fe8538afa574d8fb52391f + React-RCTAnimation: b88e8c2c7911eef712d512aed65d1f073724c121 + React-RCTAppDelegate: 209ea0b209e93e7b1958ba52b149ebf7583efac8 + React-RCTBlob: 0f2b9ab34280eda89cbb1f233d5308a32443e87a + React-RCTImage: 9b5e844dc14086707a55d92f72b997635be8f39a + React-RCTLinking: 2fd7420b1266f51a1dc73be0c92dab656d7c7fb4 + React-RCTNetwork: 16b60cd813d69bb1cd28429bdcdc544f9f69d713 + React-RCTSettings: 7070fe45328477283e93a883bb0df0d008aee2a3 React-RCTText: 7adddb518ac362b2398fedf0c64105e0dab29441 - React-RCTVibration: de6b7218e415d82788e0965f278dddb2ef88b372 - React-rncore: f0d8c23481a6c263a343fa7fd3816d943754b720 + React-RCTVibration: ac1f64d2459fe335b2fe431eb033830326864d55 + React-rncore: 9414d658e6bbcee9d9d05836e1c766b0296fb9f5 React-runtimeexecutor: 2b2c09edbca4c9a667428e8c93959f66b3b53140 - React-runtimescheduler: 6ca43e8deadf01ff06b3f01abf8f0e4d508e23c3 - React-utils: 372b83030a74347331636909278bf0a60ec30d59 - ReactCommon: 38824bfffaf4c51fbe03a2730b4fd874ef34d67b + React-runtimescheduler: 68353a05c25b32d442610ef19af377f2b198f3fe + React-utils: a715392dfaaa383668566e400f40493093c08d7e + ReactCommon: a655a8b61cc80c7cabd8db83f69e6f498d4d0894 RNFastImage: 5c9c9fed9c076e521b3f509fe79e790418a544e8 - RNFlashList: b521ebdd7f9352673817f1d98e8bdc0c8cf8545b - RNGestureHandler: 7909c50383a18f0cb10ce1db7262b9a6da504c03 - RNReanimated: f4798af7dede23d41039f0bc372710ead86e90c7 - RNScreens: 93ae3be2f119d955620f9bbb39ad372adb53b7a9 + RNFlashList: f6580844c62eb84bfd4db6b5722bc988b016dd86 + RNGestureHandler: 6a0bca5c5b2fb62211e9a75d5f4ed877817c4412 + RNReanimated: 87c5d25efb8f4b122e16e992b6f1bfbb2a9bbab3 + RNScreens: 396c07678c1c35614bc5feba74a56df387bf1965 SDWebImage: a7f831e1a65eb5e285e3fb046a23fcfbf08e696d SDWebImageWebPCoder: 908b83b6adda48effe7667cd2b7f78c897e5111d SocketRocket: f32cd54efbe0f095c4d7594881e52619cfe80b17 Yoga: d0003f849d2b5224c072cef6568b540d8bb15cd3 -PODFILE CHECKSUM: d220e29016967a52102ee4217d022b80765e8b8f +PODFILE CHECKSUM: ba61270332c77e5ec6cbe8f630331bf40fc3cfe5 COCOAPODS: 1.15.2 diff --git a/fixture/react-native/package.json b/fixture/react-native/package.json index 964bfa985..358be3d1d 100644 --- a/fixture/react-native/package.json +++ b/fixture/react-native/package.json @@ -11,7 +11,8 @@ "e2e:test:ios": "detox test -c ios.sim.release --artifacts-location /tmp/detox_artifacts/", "e2e:build:ios": "detox build -c ios.sim.release", "e2e:test:android": "detox test -c android.emu.release --artifacts-location /tmp/detox_artifacts/", - "e2e:build:android": "detox build -c android.emu.release" + "e2e:build:android": "detox build -c android.emu.release", + "postinstall": "patch-package" }, "dependencies": { "@react-navigation/native": "^6.0.10", diff --git a/fixture/react-native/patches/react-native-safe-area-context+4.10.1.patch b/fixture/react-native/patches/react-native-safe-area-context+4.10.1.patch new file mode 100644 index 000000000..1d135f786 --- /dev/null +++ b/fixture/react-native/patches/react-native-safe-area-context+4.10.1.patch @@ -0,0 +1,288 @@ +diff --git a/node_modules/react-native-safe-area-context/android/src/main/jni/CMakeLists.txt b/node_modules/react-native-safe-area-context/android/src/main/jni/CMakeLists.txt +index 4ee1c8f..c89c4fb 100644 +--- a/node_modules/react-native-safe-area-context/android/src/main/jni/CMakeLists.txt ++++ b/node_modules/react-native-safe-area-context/android/src/main/jni/CMakeLists.txt +@@ -1,5 +1,5 @@ + cmake_minimum_required(VERSION 3.13) +-set(CMAKE_VERBOSE_MAKEFILE ON) ++set(CMAKE_VERBOSE_MAKEFILE on) + + set(LIB_LITERAL safeareacontext) + set(LIB_TARGET_NAME react_codegen_${LIB_LITERAL}) +@@ -12,7 +12,7 @@ set(LIB_ANDROID_GENERATED_COMPONENTS_DIR ${LIB_ANDROID_GENERATED_JNI_DIR}/react/ + add_compile_options( + -fexceptions + -frtti +- -std=c++20 ++ -std=c++17 + -Wall + -Wpedantic + -Wno-gnu-zero-variadic-macro-arguments +@@ -50,8 +50,6 @@ target_link_libraries( + react_render_debug + react_render_graphics + react_render_mapbuffer +- react_render_componentregistry +- react_utils + rrc_view + turbomodulejsijni + yoga +@@ -63,7 +61,7 @@ target_compile_options( + -DLOG_TAG=\"ReactNative\" + -fexceptions + -frtti +- -std=c++20 ++ -std=c++17 + -Wall + ) + +diff --git a/node_modules/react-native-safe-area-context/common/cpp/react/renderer/components/safeareacontext/RNCSafeAreaViewComponentDescriptor.h b/node_modules/react-native-safe-area-context/common/cpp/react/renderer/components/safeareacontext/RNCSafeAreaViewComponentDescriptor.h +index a11321d..fd11385 100644 +--- a/node_modules/react-native-safe-area-context/common/cpp/react/renderer/components/safeareacontext/RNCSafeAreaViewComponentDescriptor.h ++++ b/node_modules/react-native-safe-area-context/common/cpp/react/renderer/components/safeareacontext/RNCSafeAreaViewComponentDescriptor.h +@@ -12,10 +12,11 @@ namespace react { + class RNCSafeAreaViewComponentDescriptor final + : public ConcreteComponentDescriptor { + using ConcreteComponentDescriptor::ConcreteComponentDescriptor; +- void adopt(ShadowNode &shadowNode) const override { +- auto &concreteShadowNode = +- static_cast(shadowNode); +- concreteShadowNode.adjustLayoutWithState(); ++ void adopt(ShadowNode::Unshared const &shadowNode) const override { ++ auto concreteShadowNode = ++ std::static_pointer_cast(shadowNode); ++ ++ concreteShadowNode->adjustLayoutWithState(); + + ConcreteComponentDescriptor::adopt(shadowNode); + } +diff --git a/node_modules/react-native-safe-area-context/common/cpp/react/renderer/components/safeareacontext/RNCSafeAreaViewShadowNode.cpp b/node_modules/react-native-safe-area-context/common/cpp/react/renderer/components/safeareacontext/RNCSafeAreaViewShadowNode.cpp +index 8cb4077..bdb3308 100644 +--- a/node_modules/react-native-safe-area-context/common/cpp/react/renderer/components/safeareacontext/RNCSafeAreaViewShadowNode.cpp ++++ b/node_modules/react-native-safe-area-context/common/cpp/react/renderer/components/safeareacontext/RNCSafeAreaViewShadowNode.cpp +@@ -6,126 +6,113 @@ + #include + + namespace facebook { +-namespace react { ++ namespace react { + +-using namespace yoga; ++ extern const char RNCSafeAreaViewComponentName[] = "RNCSafeAreaView"; + +-extern const char RNCSafeAreaViewComponentName[] = "RNCSafeAreaView"; ++ inline YGValue valueFromEdges(YGStyle::Edges edges, YGEdge edge, YGEdge axis) { ++ YGValue edgeValue = edges[edge]; ++ if (edgeValue.unit != YGUnitUndefined) { ++ return edgeValue; ++ } ++ YGValue axisValue = edges[axis]; ++ if (axisValue.unit != YGUnitUndefined) { ++ return axisValue; ++ } ++ return edges[YGEdgeAll]; ++ } + +-inline Style::Length valueFromEdges( +- Style::Length edge, +- Style::Length axis, +- Style::Length defaultValue) { +- if (edge.unit() != Unit::Undefined) { +- return edge; +- } +- if (axis.unit() != Unit::Undefined) { +- return axis; +- } +- return defaultValue; +-} ++ inline float ++ getEdgeValue(std::string edgeMode, float insetValue, float edgeValue) { ++ if (edgeMode == "off") { ++ return edgeValue; ++ } else if (edgeMode == "maximum") { ++ return fmax(insetValue, edgeValue); ++ } else { ++ return insetValue + edgeValue; ++ } ++ } + +-inline float +-getEdgeValue(std::string edgeMode, float insetValue, float edgeValue) { +- if (edgeMode == "off") { +- return edgeValue; +- } else if (edgeMode == "maximum") { +- return fmax(insetValue, edgeValue); +- } else { +- return insetValue + edgeValue; +- } +-} ++ void RNCSafeAreaViewShadowNode::adjustLayoutWithState() { ++ ensureUnsealed(); + +-void RNCSafeAreaViewShadowNode::adjustLayoutWithState() { +- ensureUnsealed(); ++ auto props = getConcreteProps(); ++ auto state = ++ std::static_pointer_cast( ++ getState()); ++ auto stateData = state->getData(); ++ auto edges = props.edges; + +- auto &props = getConcreteProps(); +- auto state = +- std::static_pointer_cast( +- getState()); +- auto stateData = state->getData(); +- auto edges = props.edges; ++ // Get the current values for padding / margin. The only caveat here is that ++ // percent values are not supported. Also might need to add support for start ++ // / end. ++ YGValue top, left, right, bottom; ++ if (props.mode == RNCSafeAreaViewMode::Padding) { ++ top = valueFromEdges(props.yogaStyle.padding(), YGEdgeTop, YGEdgeVertical); ++ left = ++ valueFromEdges(props.yogaStyle.padding(), YGEdgeLeft, YGEdgeHorizontal); ++ bottom = ++ valueFromEdges(props.yogaStyle.padding(), YGEdgeBottom, YGEdgeVertical); ++ right = valueFromEdges( ++ props.yogaStyle.padding(), YGEdgeRight, YGEdgeHorizontal); ++ } else { ++ top = valueFromEdges(props.yogaStyle.margin(), YGEdgeTop, YGEdgeVertical); ++ left = ++ valueFromEdges(props.yogaStyle.margin(), YGEdgeLeft, YGEdgeHorizontal); ++ bottom = ++ valueFromEdges(props.yogaStyle.margin(), YGEdgeBottom, YGEdgeVertical); ++ right = ++ valueFromEdges(props.yogaStyle.margin(), YGEdgeRight, YGEdgeHorizontal); ++ } + +- // Get the current values for padding / margin. The only caveat here is that +- // percent values are not supported. Also might need to add support for start +- // / end. +- Style::Length top, left, right, bottom; +- if (props.mode == RNCSafeAreaViewMode::Padding) { +- auto defaultPadding = props.yogaStyle.padding(Edge::All); +- top = valueFromEdges( +- props.yogaStyle.padding(Edge::Top), +- props.yogaStyle.padding(Edge::Vertical), +- defaultPadding); +- left = valueFromEdges( +- props.yogaStyle.padding(Edge::Left), +- props.yogaStyle.padding(Edge::Horizontal), +- defaultPadding); +- bottom = valueFromEdges( +- props.yogaStyle.padding(Edge::Bottom), +- props.yogaStyle.padding(Edge::Vertical), +- defaultPadding); +- right = valueFromEdges( +- props.yogaStyle.padding(Edge::Right), +- props.yogaStyle.padding(Edge::Horizontal), +- defaultPadding); +- } else { +- auto defaultMargin = props.yogaStyle.margin(Edge::All); +- top = valueFromEdges( +- props.yogaStyle.margin(Edge::Top), +- props.yogaStyle.margin(Edge::Vertical), +- defaultMargin); +- left = valueFromEdges( +- props.yogaStyle.margin(Edge::Left), +- props.yogaStyle.margin(Edge::Horizontal), +- defaultMargin); +- bottom = valueFromEdges( +- props.yogaStyle.margin(Edge::Bottom), +- props.yogaStyle.margin(Edge::Vertical), +- defaultMargin); +- right = valueFromEdges( +- props.yogaStyle.margin(Edge::Right), +- props.yogaStyle.margin(Edge::Horizontal), +- defaultMargin); +- } ++ top = yogaStyleValueFromFloat(getEdgeValue( ++ edges.top, ++ stateData.insets.top, ++ (top.unit == YGUnitPoint ? top.value : 0))); ++ left = yogaStyleValueFromFloat(getEdgeValue( ++ edges.left, ++ stateData.insets.left, ++ (left.unit == YGUnitPoint ? left.value : 0))); ++ right = yogaStyleValueFromFloat(getEdgeValue( ++ edges.right, ++ stateData.insets.right, ++ (right.unit == YGUnitPoint ? right.value : 0))); ++ bottom = yogaStyleValueFromFloat(getEdgeValue( ++ edges.bottom, ++ stateData.insets.bottom, ++ (bottom.unit == YGUnitPoint ? bottom.value : 0))); + +- top.points(getEdgeValue( +- edges.top, stateData.insets.top, top.value().unwrapOrDefault(0))); +- left.points(getEdgeValue( +- edges.left, stateData.insets.left, left.value().unwrapOrDefault(0))); +- right.points(getEdgeValue( +- edges.right, stateData.insets.right, right.value().unwrapOrDefault(0))); +- bottom.points(getEdgeValue( +- edges.bottom, +- stateData.insets.bottom, +- bottom.value().unwrapOrDefault(0))); ++ YGStyle adjustedStyle = getConcreteProps().yogaStyle; ++ if (props.mode == RNCSafeAreaViewMode::Padding) { ++ adjustedStyle.padding()[YGEdgeTop] = top; ++ adjustedStyle.padding()[YGEdgeLeft] = left; ++ adjustedStyle.padding()[YGEdgeRight] = right; ++ adjustedStyle.padding()[YGEdgeBottom] = bottom; ++ } else { ++ adjustedStyle.margin()[YGEdgeTop] = top; ++ adjustedStyle.margin()[YGEdgeLeft] = left; ++ adjustedStyle.margin()[YGEdgeRight] = right; ++ adjustedStyle.margin()[YGEdgeBottom] = bottom; ++ } + +- yoga::Style adjustedStyle = getConcreteProps().yogaStyle; +- if (props.mode == RNCSafeAreaViewMode::Padding) { +- adjustedStyle.setPadding(Edge::Top, top); +- adjustedStyle.setPadding(Edge::Left, left); +- adjustedStyle.setPadding(Edge::Right, right); +- adjustedStyle.setPadding(Edge::Bottom, bottom); +- } else { +- adjustedStyle.setMargin(Edge::Top, top); +- adjustedStyle.setMargin(Edge::Left, left); +- adjustedStyle.setMargin(Edge::Right, right); +- adjustedStyle.setMargin(Edge::Bottom, bottom); +- } ++ auto currentStyle = yogaNode_.getStyle(); ++ if (adjustedStyle.padding()[YGEdgeTop] != currentStyle.padding()[YGEdgeTop] || ++ adjustedStyle.padding()[YGEdgeLeft] != ++ currentStyle.padding()[YGEdgeLeft] || ++ adjustedStyle.padding()[YGEdgeRight] != ++ currentStyle.padding()[YGEdgeRight] || ++ adjustedStyle.padding()[YGEdgeBottom] != ++ currentStyle.padding()[YGEdgeBottom] || ++ adjustedStyle.margin()[YGEdgeTop] != currentStyle.margin()[YGEdgeTop] || ++ adjustedStyle.margin()[YGEdgeLeft] != currentStyle.margin()[YGEdgeLeft] || ++ adjustedStyle.margin()[YGEdgeRight] != ++ currentStyle.margin()[YGEdgeRight] || ++ adjustedStyle.margin()[YGEdgeBottom] != ++ currentStyle.margin()[YGEdgeBottom]) { ++ yogaNode_.setStyle(adjustedStyle); ++ yogaNode_.setDirty(true); ++ } ++ } + +- auto currentStyle = yogaNode_.style(); +- if (adjustedStyle.padding(Edge::Top) != currentStyle.padding(Edge::Top) || +- adjustedStyle.padding(Edge::Left) != currentStyle.padding(Edge::Left) || +- adjustedStyle.padding(Edge::Right) != currentStyle.padding(Edge::Right) || +- adjustedStyle.padding(Edge::Bottom) != +- currentStyle.padding(Edge::Bottom) || +- adjustedStyle.margin(Edge::Top) != currentStyle.margin(Edge::Top) || +- adjustedStyle.margin(Edge::Left) != currentStyle.margin(Edge::Left) || +- adjustedStyle.margin(Edge::Right) != currentStyle.margin(Edge::Right) || +- adjustedStyle.margin(Edge::Bottom) != currentStyle.margin(Edge::Bottom)) { +- yogaNode_.setStyle(adjustedStyle); +- yogaNode_.setDirty(true); +- } +-} +- +-} // namespace react ++ } // namespace react + } // namespace facebook diff --git a/fixture/react-native/patches/react-native-screens+3.31.1.patch b/fixture/react-native/patches/react-native-screens+3.31.1.patch new file mode 100644 index 000000000..8111c52df --- /dev/null +++ b/fixture/react-native/patches/react-native-screens+3.31.1.patch @@ -0,0 +1,126 @@ +diff --git a/node_modules/react-native-screens/android/src/main/jni/CMakeLists.txt b/node_modules/react-native-screens/android/src/main/jni/CMakeLists.txt +index ec34a20..ef088f9 100644 +--- a/node_modules/react-native-screens/android/src/main/jni/CMakeLists.txt ++++ b/node_modules/react-native-screens/android/src/main/jni/CMakeLists.txt +@@ -12,7 +12,7 @@ set(LIB_ANDROID_GENERATED_COMPONENTS_DIR ${LIB_ANDROID_GENERATED_JNI_DIR}/react/ + add_compile_options( + -fexceptions + -frtti +- -std=c++20 ++ -std=c++17 + -Wall + -Wpedantic + -Wno-gnu-zero-variadic-macro-arguments +@@ -50,8 +50,6 @@ target_link_libraries( + react_render_debug + react_render_graphics + react_render_mapbuffer +- react_render_componentregistry +- react_utils + rrc_view + turbomodulejsijni + yoga +@@ -63,7 +61,7 @@ target_compile_options( + -DLOG_TAG=\"ReactNative\" + -fexceptions + -frtti +- -std=c++20 ++ -std=c++17 + -Wall + ) + +diff --git a/node_modules/react-native-screens/common/cpp/react/renderer/components/rnscreens/RNSModalScreenComponentDescriptor.h b/node_modules/react-native-screens/common/cpp/react/renderer/components/rnscreens/RNSModalScreenComponentDescriptor.h +index 1c6fa19..95429db 100644 +--- a/node_modules/react-native-screens/common/cpp/react/renderer/components/rnscreens/RNSModalScreenComponentDescriptor.h ++++ b/node_modules/react-native-screens/common/cpp/react/renderer/components/rnscreens/RNSModalScreenComponentDescriptor.h +@@ -12,29 +12,29 @@ class RNSModalScreenComponentDescriptor final + public: + using ConcreteComponentDescriptor::ConcreteComponentDescriptor; + +- void adopt(ShadowNode& shadowNode) const override { +- react_native_assert( +- dynamic_cast(&shadowNode)); +- auto& screenShadowNode = +- static_cast(shadowNode); +- +- react_native_assert( +- dynamic_cast(&screenShadowNode)); +- auto& layoutableShadowNode = +- dynamic_cast(screenShadowNode); +- +- auto state = +- std::static_pointer_cast( +- shadowNode.getState()); +- auto stateData = state->getData(); +- +- if (stateData.frameSize.width != 0 && stateData.frameSize.height != 0) { +- layoutableShadowNode.setSize( +- Size{stateData.frameSize.width, stateData.frameSize.height}); +- } +- +- ConcreteComponentDescriptor::adopt(shadowNode); +- } ++ void adopt(ShadowNode::Unshared const &shadowNode) const override { ++ react_native_assert( ++ std::dynamic_pointer_cast(shadowNode)); ++ auto screenShadowNode = ++ std::static_pointer_cast(shadowNode); ++ ++ react_native_assert( ++ std::dynamic_pointer_cast(screenShadowNode)); ++ auto layoutableShadowNode = ++ std::static_pointer_cast(screenShadowNode); ++ ++ auto state = ++ std::static_pointer_cast( ++ shadowNode->getState()); ++ auto stateData = state->getData(); ++ ++ if (stateData.frameSize.width != 0 && stateData.frameSize.height != 0) { ++ layoutableShadowNode->setSize( ++ Size{stateData.frameSize.width, stateData.frameSize.height}); ++ } ++ ++ ConcreteComponentDescriptor::adopt(shadowNode); ++ } + }; + + } // namespace react +diff --git a/node_modules/react-native-screens/common/cpp/react/renderer/components/rnscreens/RNSScreenComponentDescriptor.h b/node_modules/react-native-screens/common/cpp/react/renderer/components/rnscreens/RNSScreenComponentDescriptor.h +index 67194d3..40a3c0a 100644 +--- a/node_modules/react-native-screens/common/cpp/react/renderer/components/rnscreens/RNSScreenComponentDescriptor.h ++++ b/node_modules/react-native-screens/common/cpp/react/renderer/components/rnscreens/RNSScreenComponentDescriptor.h +@@ -12,24 +12,24 @@ class RNSScreenComponentDescriptor final + public: + using ConcreteComponentDescriptor::ConcreteComponentDescriptor; + +- void adopt(ShadowNode& shadowNode) const override { ++ void adopt(ShadowNode::Unshared const &shadowNode) const override { + react_native_assert( +- dynamic_cast(&shadowNode)); +- auto& screenShadowNode = +- static_cast(shadowNode); ++ std::dynamic_pointer_cast(shadowNode)); ++ auto screenShadowNode = ++ std::static_pointer_cast(shadowNode); + + react_native_assert( +- dynamic_cast(&screenShadowNode)); +- auto& layoutableShadowNode = +- dynamic_cast(screenShadowNode); ++ std::dynamic_pointer_cast(screenShadowNode)); ++ auto layoutableShadowNode = ++ std::static_pointer_cast(screenShadowNode); + + auto state = + std::static_pointer_cast( +- shadowNode.getState()); ++ shadowNode->getState()); + auto stateData = state->getData(); + + if (stateData.frameSize.width != 0 && stateData.frameSize.height != 0) { +- layoutableShadowNode.setSize( ++ layoutableShadowNode->setSize( + Size{stateData.frameSize.width, stateData.frameSize.height}); + } + diff --git a/ios/Sources/AutoLayoutView.swift b/ios/Sources/AutoLayoutView.swift index 32371aa16..dccf86fa9 100644 --- a/ios/Sources/AutoLayoutView.swift +++ b/ios/Sources/AutoLayoutView.swift @@ -4,31 +4,35 @@ import UIKit /// Container for all RecyclerListView children. This will automatically remove all gaps and overlaps for GridLayouts with flexible spans. /// Note: This cannot work for masonry layouts i.e, pinterest like layout -@objc class AutoLayoutView: UIView { +@objc public class AutoLayoutView: UIView { + #if RCT_NEW_ARCH_ENABLED + @objc public var onBlankAreaEventHandler: ((CGFloat, CGFloat) -> Void)? + #endif + @objc(onBlankAreaEvent) var onBlankAreaEvent: RCTDirectEventBlock? - @objc func setHorizontal(_ horizontal: Bool) { + @objc public func setHorizontal(_ horizontal: Bool) { self.horizontal = horizontal } - @objc func setScrollOffset(_ scrollOffset: Int) { + @objc public func setScrollOffset(_ scrollOffset: Int) { self.scrollOffset = CGFloat(scrollOffset) } - @objc func setWindowSize(_ windowSize: Int) { + @objc public func setWindowSize(_ windowSize: Int) { self.windowSize = CGFloat(windowSize) } - @objc func setRenderAheadOffset(_ renderAheadOffset: Int) { + @objc public func setRenderAheadOffset(_ renderAheadOffset: Int) { self.renderAheadOffset = CGFloat(renderAheadOffset) } - @objc func setEnableInstrumentation(_ enableInstrumentation: Bool) { + @objc public func setEnableInstrumentation(_ enableInstrumentation: Bool) { self.enableInstrumentation = enableInstrumentation } - @objc func setDisableAutoLayout(_ disableAutoLayout: Bool) { + @objc public func setDisableAutoLayout(_ disableAutoLayout: Bool) { self.disableAutoLayout = disableAutoLayout } @@ -46,7 +50,7 @@ import UIKit /// Tracks where first pixel is drawn in the visible window private var lastMinBound: CGFloat = 0 - override func layoutSubviews() { + override public func layoutSubviews() { fixLayout() super.layoutSubviews() @@ -68,13 +72,17 @@ import UIKit distanceFromWindowStart: distanceFromWindowStart, distanceFromWindowEnd: distanceFromWindowEnd ) - + + #if RCT_NEW_ARCH_ENABLED + onBlankAreaEventHandler?(blankOffsetStart, blankOffsetEnd) + #else onBlankAreaEvent?( [ "offsetStart": blankOffsetStart, "offsetEnd": blankOffsetEnd, ] ) + #endif } func getScrollView() -> UIScrollView? { @@ -91,8 +99,8 @@ import UIKit !disableAutoLayout else { return } let cellContainers = subviews - .compactMap { subview -> CellContainer? in - if let cellContainer = subview as? CellContainer { + .compactMap { subview -> CellContainerComponentView? in + if let cellContainer = subview as? CellContainerComponentView { return cellContainer } else { assertionFailure("CellRendererComponent outer view should always be CellContainer. Learn more here: https://shopify.github.io/flash-list/docs/usage#cellrenderercomponent.") @@ -106,7 +114,7 @@ import UIKit /// Checks for overlaps or gaps between adjacent items and then applies a correction. /// Performance: RecyclerListView renders very small number of views and this is not going to trigger multiple layouts on the iOS side. - private func clearGaps(for cellContainers: [CellContainer]) { + private func clearGaps(for cellContainers: [CellContainerComponentView]) { var maxBound: CGFloat = 0 var minBound: CGFloat = CGFloat(Int.max) var maxBoundNextCell: CGFloat = 0 @@ -195,7 +203,7 @@ import UIKit lastMinBound = minBound } - private func updateLastMaxBoundOverall(currentCell: CellContainer, nextCell: CellContainer) { + private func updateLastMaxBoundOverall(currentCell: CellContainerComponentView, nextCell: CellContainerComponentView) { lastMaxBoundOverall = max(lastMaxBoundOverall, horizontal ? currentCell.frame.maxX : currentCell.frame.maxY, horizontal ? nextCell.frame.maxX : nextCell.frame.maxY) } @@ -220,7 +228,7 @@ import UIKit /// It's important to avoid correcting views outside the render window. An item that isn't being recycled might still remain in the view tree. If views outside get considered then gaps between unused items will cause algorithm to fail. func isWithinBounds( - _ cellContainer: CellContainer, + _ cellContainer: CellContainerComponentView, scrollOffset: CGFloat, renderAheadOffset: CGFloat, windowSize: CGFloat, @@ -274,6 +282,13 @@ import UIKit } private func footer() -> UIView? { - return superview?.subviews.first(where:{($0 as? CellContainer)?.index == -1}) + // On the new arch, AutoLayoutView is wrapped with AutoLayoutViewComponentView, so we need to go up one more level + #if RCT_NEW_ARCH_ENABLED + let parentSubviews = superview?.superview?.subviews + #else + let parentSubviews = superview?.subviews + #endif + + return parentSubviews?.first(where:{($0 as? CellContainerComponentView)?.index == -1}) } } diff --git a/ios/Sources/AutoLayoutViewComponentView.h b/ios/Sources/AutoLayoutViewComponentView.h new file mode 100644 index 000000000..1ae0b6676 --- /dev/null +++ b/ios/Sources/AutoLayoutViewComponentView.h @@ -0,0 +1,16 @@ +#ifndef AutoLayoutViewComponentView_h +#define AutoLayoutViewComponentView_h + +#ifdef RCT_NEW_ARCH_ENABLED + +#import +#import + +@interface AutoLayoutViewComponentView : RCTViewComponentView + +@end + + +#endif /* RCT_NEW_ARCH_ENABLED */ + +#endif /* AutoLayoutViewComponentView_h */ diff --git a/ios/Sources/AutoLayoutViewComponentView.mm b/ios/Sources/AutoLayoutViewComponentView.mm new file mode 100644 index 000000000..9a1e2789c --- /dev/null +++ b/ios/Sources/AutoLayoutViewComponentView.mm @@ -0,0 +1,90 @@ +#ifdef RCT_NEW_ARCH_ENABLED +#import "AutoLayoutViewComponentView.h" +#import +#import + +#import +#import +#import +#import + +#import "RCTFabricComponentsPlugins.h" + +#if __has_include() +#import +#else +#import "RNFlashList-Swift.h" +#endif + +using namespace facebook::react; + +@interface AutoLayoutViewComponentView () +@end + +@implementation AutoLayoutViewComponentView +{ + AutoLayoutView *_autoLayoutView; +} + +- (instancetype)initWithFrame:(CGRect)frame +{ + if (self = [super initWithFrame:frame]) { + static const auto defaultProps = std::make_shared(); + _props = defaultProps; + _autoLayoutView = [[AutoLayoutView alloc] initWithFrame:self.bounds]; + self.contentView = _autoLayoutView; + + __weak AutoLayoutViewComponentView* weakSelf = self; + _autoLayoutView.onBlankAreaEventHandler = ^(CGFloat start, CGFloat end) { + AutoLayoutViewComponentView *strongSelf = weakSelf; + if (strongSelf != nullptr && strongSelf->_eventEmitter != nullptr) { + std::dynamic_pointer_cast(strongSelf->_eventEmitter) + ->onBlankAreaEvent(facebook::react::AutoLayoutViewEventEmitter::OnBlankAreaEvent{ + .offsetStart = (int) floor(start), + .offsetEnd = (int) floor(end), + }); + } + }; + } + + return self; +} + +- (void)mountChildComponentView:(UIView *)childComponentView index:(NSInteger)index +{ + [_autoLayoutView mountChildComponentView:childComponentView index:index]; +} + +- (void)unmountChildComponentView:(UIView *)childComponentView index:(NSInteger)index +{ + [_autoLayoutView unmountChildComponentView:childComponentView index:index]; +} + +#pragma mark - RCTComponentViewProtocol + ++ (ComponentDescriptorProvider)componentDescriptorProvider +{ + return concreteComponentDescriptorProvider(); +} + +- (void)updateProps:(const Props::Shared &)props oldProps:(const Props::Shared &)oldProps +{ + const auto &newProps = *std::static_pointer_cast(props); + + [_autoLayoutView setHorizontal:newProps.horizontal]; + [_autoLayoutView setScrollOffset:newProps.scrollOffset]; + [_autoLayoutView setWindowSize:newProps.windowSize]; + [_autoLayoutView setRenderAheadOffset:newProps.renderAheadOffset]; + [_autoLayoutView setEnableInstrumentation:newProps.enableInstrumentation]; + [_autoLayoutView setDisableAutoLayout:newProps.disableAutoLayout]; + + [super updateProps:props oldProps:oldProps]; +} +@end + +Class AutoLayoutViewCls(void) +{ + return AutoLayoutViewComponentView.class; +} + +#endif /* RCT_NEW_ARCH_ENABLED */ diff --git a/ios/Sources/AutoLayoutViewManager.m b/ios/Sources/AutoLayoutViewManager.mm similarity index 100% rename from ios/Sources/AutoLayoutViewManager.m rename to ios/Sources/AutoLayoutViewManager.mm diff --git a/ios/Sources/CellContainer.swift b/ios/Sources/CellContainer.swift deleted file mode 100644 index 7f09ce767..000000000 --- a/ios/Sources/CellContainer.swift +++ /dev/null @@ -1,9 +0,0 @@ -import Foundation - -@objc class CellContainer: UIView { - var index: Int = -1 - - @objc func setIndex(_ index: Int) { - self.index = index - } -} diff --git a/ios/Sources/CellContainerComponentView.h b/ios/Sources/CellContainerComponentView.h new file mode 100644 index 000000000..1dd0df6f5 --- /dev/null +++ b/ios/Sources/CellContainerComponentView.h @@ -0,0 +1,18 @@ +#ifndef CellContainer_h +#define CellContainer_h + +#import + +#ifdef RCT_NEW_ARCH_ENABLED +#import + +@interface CellContainerComponentView : RCTViewComponentView +#else +@interface CellContainerComponentView : UIView +#endif // RCT_NEW_ARCH_ENABLED + +@property int64_t index; + +@end + +#endif /* CellContainer_h */ diff --git a/ios/Sources/CellContainerComponentView.mm b/ios/Sources/CellContainerComponentView.mm new file mode 100644 index 000000000..7a6f46ecc --- /dev/null +++ b/ios/Sources/CellContainerComponentView.mm @@ -0,0 +1,62 @@ +#import "CellContainerComponentView.h" + +#ifdef RCT_NEW_ARCH_ENABLED +#import + +#import +#import +#import +#import + +#import "RCTFabricComponentsPlugins.h" + +#if __has_include() +#import +#else +#import "RNFlashList-Swift.h" +#endif + +using namespace facebook::react; + +@interface CellContainerComponentView () +@end + +@implementation CellContainerComponentView + +- (instancetype)initWithFrame:(CGRect)frame +{ + if (self = [super initWithFrame:frame]) { + static const auto defaultProps = std::make_shared(); + _props = defaultProps; + + self.userInteractionEnabled = true; + } + + return self; +} + +#pragma mark - RCTComponentViewProtocol + ++ (ComponentDescriptorProvider)componentDescriptorProvider +{ + return concreteComponentDescriptorProvider(); +} + +- (void)updateProps:(const Props::Shared &)props oldProps:(const Props::Shared &)oldProps +{ + const auto &newProps = *std::static_pointer_cast(props); + + self.index = newProps.index; + + [super updateProps:props oldProps:oldProps]; +} +@end + +Class CellContainerCls(void) +{ + return CellContainerComponentView.class; +} +#else +@implementation CellContainerComponentView +@end +#endif /* RCT_NEW_ARCH_ENABLED */ diff --git a/ios/Sources/CellContainerManager.m b/ios/Sources/CellContainerManager.mm similarity index 100% rename from ios/Sources/CellContainerManager.m rename to ios/Sources/CellContainerManager.mm diff --git a/ios/Sources/CellContainerManager.swift b/ios/Sources/CellContainerManager.swift index a36fccd07..dbe6c1483 100644 --- a/ios/Sources/CellContainerManager.swift +++ b/ios/Sources/CellContainerManager.swift @@ -3,7 +3,7 @@ import Foundation @objc(CellContainerManager) class CellContainerManager: RCTViewManager { override func view() -> UIView! { - return CellContainer() + return CellContainerComponentView() } override static func requiresMainQueueSetup() -> Bool { diff --git a/ios/Sources/FlatListPro-Bridging-Header.h b/ios/Sources/FlatListPro-Bridging-Header.h index e3e23d59c..7ed83ce1f 100644 --- a/ios/Sources/FlatListPro-Bridging-Header.h +++ b/ios/Sources/FlatListPro-Bridging-Header.h @@ -4,5 +4,8 @@ #import #import #import +#import + +#import "CellContainerComponentView.h" #endif /* FlatListPro_Bridging_Header_h */ diff --git a/package.json b/package.json index 892646648..869395651 100644 --- a/package.json +++ b/package.json @@ -87,5 +87,10 @@ "dependencies": { "recyclerlistview": "4.2.1", "tslib": "2.4.0" + }, + "codegenConfig": { + "name": "rnflashlist", + "type": "components", + "jsSrcsDir": "./src/specs" } } diff --git a/src/specs/AutoLayoutNativeComponent.ts b/src/specs/AutoLayoutNativeComponent.ts new file mode 100644 index 000000000..93750de95 --- /dev/null +++ b/src/specs/AutoLayoutNativeComponent.ts @@ -0,0 +1,24 @@ +import codegenNativeComponent from "react-native/Libraries/Utilities/codegenNativeComponent"; +import type { ViewProps } from "react-native"; +import type { + Int32, + Double, + DirectEventHandler, +} from "react-native/Libraries/Types/CodegenTypes"; + +type BlankAreaEvent = Readonly<{ + offsetStart: Int32; + offsetEnd: Int32; +}>; + +interface NativeProps extends ViewProps { + horizontal?: boolean; + scrollOffset?: Double; + windowSize?: Double; + renderAheadOffset?: Double; + enableInstrumentation?: boolean; + disableAutoLayout?: boolean; + onBlankAreaEvent?: DirectEventHandler; +} + +export default codegenNativeComponent("AutoLayoutView"); diff --git a/src/specs/CellContainerNativeComponent.ts b/src/specs/CellContainerNativeComponent.ts new file mode 100644 index 000000000..dd284ac4c --- /dev/null +++ b/src/specs/CellContainerNativeComponent.ts @@ -0,0 +1,9 @@ +import codegenNativeComponent from "react-native/Libraries/Utilities/codegenNativeComponent"; +import type { Int32 } from "react-native/Libraries/Types/CodegenTypes"; +import type { ViewProps } from "react-native"; + +interface NativeProps extends ViewProps { + index?: Int32; +} + +export default codegenNativeComponent("CellContainer");