Skip to content

Commit

Permalink
New architecture support (#550)
Browse files Browse the repository at this point in the history
* Enable new arch & make newer libs compile under 0.72

* Setup codegen

* Update podspec

* Setup ios code

* Fix AutoLayout subviews and footer

* Implement `onBlankAreaEvent` on iOS

* Update comments

* Implement Android

* Mound children under AutoLayoutView

* Update changelog

* Improve event handling

* Remove context extension

* Disable new arch by default

* Update `podfile.lock`

* update podfile

---------

Co-authored-by: Talha Naqvi <naqvitalha@gmail.com>
  • Loading branch information
j-piasecki and naqvitalha committed Jun 18, 2024
1 parent 43c7eec commit 029fd40
Show file tree
Hide file tree
Showing 30 changed files with 957 additions and 91 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
15 changes: 13 additions & 2 deletions RNFlashList.podspec
Original file line number Diff line number Diff line change
@@ -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'
Expand All @@ -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|
Expand Down
13 changes: 13 additions & 0 deletions android/build.gradle
Original file line number Diff line number Diff line change
@@ -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 : '+'
Expand Down Expand Up @@ -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"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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
)
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<AutoLayoutView>(), AutoLayoutViewManagerInterface<AutoLayoutView> {
private val mDelegate: AutoLayoutViewManagerDelegate<AutoLayoutView, AutoLayoutViewManager> =
AutoLayoutViewManagerDelegate(this)

companion object {
const val REACT_CLASS = "AutoLayoutView"
Expand All @@ -21,45 +24,43 @@ class AutoLayoutViewManager: ReactViewManager() {
return REACT_CLASS
}

override fun createViewInstance(context: ThemedReactContext): ReactViewGroup {
override fun getDelegate(): ViewManagerDelegate<AutoLayoutView> = mDelegate

override fun createViewInstance(context: ThemedReactContext): AutoLayoutView {
return AutoLayoutView(context).also { it.pixelDensity = context.resources.displayMetrics.density.toDouble() }
}

override fun getExportedCustomDirectEventTypeConstants(): MutableMap<String, Any> {
return MapBuilder.builder<String, Any>().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
}

Expand Down
Original file line number Diff line number Diff line change
@@ -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<BlankAreaEvent>(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"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<CellContainerImpl>(), CellContainerManagerInterface<CellContainerImpl> {
private val mDelegate: CellContainerManagerDelegate<CellContainerImpl, CellContainerManager>
= CellContainerManagerDelegate(this)

companion object {
const val REACT_CLASS = "CellContainer"
}
Expand All @@ -16,12 +21,14 @@ class CellContainerManager: ReactViewManager() {
return REACT_CLASS
}

override fun createViewInstance(context: ThemedReactContext): ReactViewGroup {
override fun getDelegate(): ViewManagerDelegate<CellContainerImpl> = 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
}
}
Original file line number Diff line number Diff line change
@@ -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<T extends View, U extends BaseViewManagerInterface<T> & AutoLayoutViewManagerInterface<T>> extends BaseViewManagerDelegate<T, U> {
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);
}
}
}
Original file line number Diff line number Diff line change
@@ -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<T extends View> {
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);
}
Original file line number Diff line number Diff line change
@@ -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<T extends View, U extends BaseViewManagerInterface<T> & CellContainerManagerInterface<T>> extends BaseViewManagerDelegate<T, U> {
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);
}
}
}
Original file line number Diff line number Diff line change
@@ -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<T extends View> {
void setIndex(T view, int value);
}
Loading

0 comments on commit 029fd40

Please sign in to comment.