mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Convert UIManagerModuleConstantsHelper to Kotlin (#52358)
Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/52358 Another class going from Java to Kotlin. This is quite involved due to the amount of Raw generics we were using so I'd appreaciate a couple of further eyes here. Changelog: [Android] [Changed] - Convert UIManagerModuleConstantsHelper to Kotlin Reviewed By: mdvacca, javache Differential Revision: D77589975 fbshipit-source-id: 477c1e2a8dfd31db60047fd1252f6d47c177f5c7
This commit is contained in:
committed by
Facebook GitHub Bot
parent
6cb8dc37c7
commit
45fd7feb9f
@@ -4428,13 +4428,6 @@ public abstract interface class com/facebook/react/uimanager/UIManagerModule$Cus
|
||||
public abstract fun resolveCustomEventName (Ljava/lang/String;)Ljava/lang/String;
|
||||
}
|
||||
|
||||
public class com/facebook/react/uimanager/UIManagerModuleConstantsHelper {
|
||||
public fun <init> ()V
|
||||
public static fun createConstants (Ljava/util/List;Ljava/util/Map;Ljava/util/Map;)Ljava/util/Map;
|
||||
public static fun createConstantsForViewManager (Lcom/facebook/react/uimanager/ViewManager;Ljava/util/Map;Ljava/util/Map;Ljava/util/Map;Ljava/util/Map;)Ljava/util/Map;
|
||||
public static fun getDefaultExportableEventTypes ()Ljava/util/Map;
|
||||
}
|
||||
|
||||
public abstract interface class com/facebook/react/uimanager/UIManagerModuleListener {
|
||||
public abstract fun willDispatchViewUpdates (Lcom/facebook/react/uimanager/UIManagerModule;)V
|
||||
}
|
||||
|
||||
+5
-7
@@ -203,7 +203,7 @@ internal class ReactInstance(
|
||||
// initialized.
|
||||
// This happens inside getTurboModuleManagerDelegate getter.
|
||||
if (ReactNativeFeatureFlags.useNativeViewConfigsInBridgelessMode()) {
|
||||
val customDirectEvents: Map<String, Any> = HashMap()
|
||||
val customDirectEvents: MutableMap<String, Any> = HashMap()
|
||||
|
||||
UIConstantsProviderBinding.install(
|
||||
// Use unbuffered RuntimeExecutor to install binding
|
||||
@@ -214,9 +214,7 @@ internal class ReactInstance(
|
||||
// 2. genericBubblingEventTypes.
|
||||
// 3. genericDirectEventTypes.
|
||||
// We want to match this beahavior.
|
||||
{
|
||||
Arguments.makeNativeMap(UIManagerModuleConstantsHelper.getDefaultExportableEventTypes())
|
||||
},
|
||||
{ Arguments.makeNativeMap(UIManagerModuleConstantsHelper.defaultExportableEventTypes) },
|
||||
ConstantsForViewManagerProvider { viewManagerName: String ->
|
||||
val viewManager =
|
||||
viewManagerResolver.getViewManager(viewManagerName)
|
||||
@@ -573,8 +571,8 @@ internal class ReactInstance(
|
||||
}
|
||||
|
||||
private fun createConstants(
|
||||
viewManagers: List<ViewManager<*, *>>,
|
||||
customDirectEvents: Map<String, Any>?
|
||||
viewManagers: List<ViewManager<in Nothing, in Nothing>>,
|
||||
customDirectEvents: MutableMap<String, Any>?
|
||||
): MutableMap<String, Any> {
|
||||
ReactMarker.logMarker(ReactMarkerConstants.CREATE_UI_MANAGER_MODULE_CONSTANTS_START)
|
||||
SystraceMessage.beginSection(Systrace.TRACE_TAG_REACT, "CreateUIManagerConstants")
|
||||
@@ -591,7 +589,7 @@ internal class ReactInstance(
|
||||
|
||||
private fun getConstantsForViewManager(
|
||||
viewManager: ViewManager<*, *>,
|
||||
customDirectEvents: Map<String, Any>
|
||||
customDirectEvents: MutableMap<String, Any>
|
||||
): NativeMap {
|
||||
SystraceMessage.beginSection(
|
||||
Systrace.TRACE_TAG_REACT, "ReactInstance.getConstantsForViewManager")
|
||||
|
||||
+3
-3
@@ -248,7 +248,7 @@ public class UIManagerModule extends ReactContextBaseJavaModule
|
||||
.arg("Lazy", true)
|
||||
.flush();
|
||||
try {
|
||||
return UIManagerModuleConstantsHelper.createConstants(viewManagerResolver);
|
||||
return UIManagerModuleConstantsHelper.internal_createConstants(viewManagerResolver);
|
||||
} finally {
|
||||
Systrace.endSection(Systrace.TRACE_TAG_REACT);
|
||||
ReactMarker.logMarker(CREATE_UI_MANAGER_MODULE_CONSTANTS_END);
|
||||
@@ -264,7 +264,7 @@ public class UIManagerModule extends ReactContextBaseJavaModule
|
||||
.arg("Lazy", false)
|
||||
.flush();
|
||||
try {
|
||||
return UIManagerModuleConstantsHelper.createConstants(
|
||||
return UIManagerModuleConstantsHelper.internal_createConstants(
|
||||
viewManagers, customBubblingEvents, customDirectEvents);
|
||||
} finally {
|
||||
Systrace.endSection(Systrace.TRACE_TAG_REACT);
|
||||
@@ -291,7 +291,7 @@ public class UIManagerModule extends ReactContextBaseJavaModule
|
||||
.flush();
|
||||
try {
|
||||
Map<String, Object> viewManagerConstants =
|
||||
UIManagerModuleConstantsHelper.createConstantsForViewManager(
|
||||
UIManagerModuleConstantsHelper.internal_createConstantsForViewManager(
|
||||
viewManager, null, null, null, customDirectEvents);
|
||||
if (viewManagerConstants != null) {
|
||||
return Arguments.makeNativeMap(viewManagerConstants);
|
||||
|
||||
-244
@@ -1,244 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.react.uimanager;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import com.facebook.common.logging.FLog;
|
||||
import com.facebook.infer.annotation.Nullsafe;
|
||||
import com.facebook.react.common.MapBuilder;
|
||||
import com.facebook.react.common.build.ReactBuildConfig;
|
||||
import com.facebook.react.internal.featureflags.ReactNativeNewArchitectureFeatureFlags;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Helps generate constants map for {@link UIManagerModule} by collecting and merging constants from
|
||||
* registered view managers.
|
||||
*/
|
||||
@Nullsafe(Nullsafe.Mode.LOCAL)
|
||||
public class UIManagerModuleConstantsHelper {
|
||||
private static final String TAG = "UIManagerModuleConstantsHelper";
|
||||
private static final String BUBBLING_EVENTS_KEY = "bubblingEventTypes";
|
||||
private static final String DIRECT_EVENTS_KEY = "directEventTypes";
|
||||
|
||||
/**
|
||||
* Generates a lazy discovery enabled version of {@link UIManagerModule} constants. It only
|
||||
* contains a list of view manager names, so that JS side is aware of the managers there are.
|
||||
* Actual ViewManager instantiation happens when {@code
|
||||
* UIManager.getViewManagerConfig('SpecificViewManager')} call happens. The View Manager is then
|
||||
* registered on the JS side with the help of {@code UIManagerModule.getConstantsForViewManager}.
|
||||
*/
|
||||
/* package */ static Map<String, Object> createConstants(ViewManagerResolver resolver) {
|
||||
Map<String, Object> constants = UIManagerModuleConstants.constants;
|
||||
constants.put("ViewManagerNames", new ArrayList<>(resolver.getViewManagerNames()));
|
||||
constants.put("LazyViewManagersEnabled", true);
|
||||
return constants;
|
||||
}
|
||||
|
||||
public static Map<String, Object> getDefaultExportableEventTypes() {
|
||||
return MapBuilder.<String, Object>of(
|
||||
BUBBLING_EVENTS_KEY, UIManagerModuleConstants.bubblingEventTypeConstants,
|
||||
DIRECT_EVENTS_KEY, UIManagerModuleConstants.directEventTypeConstants);
|
||||
}
|
||||
|
||||
private static void validateDirectEventNames(
|
||||
String viewManagerName, @Nullable Map<String, Object> directEvents) {
|
||||
if (!ReactBuildConfig.DEBUG || directEvents == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (String key : directEvents.keySet()) {
|
||||
Object value = directEvents.get(key);
|
||||
if (value != null && (value instanceof Map)) {
|
||||
String regName = (String) ((Map) value).get("registrationName");
|
||||
if (regName != null
|
||||
&& key.startsWith("top")
|
||||
&& regName.startsWith("on")
|
||||
&& !key.substring(3).equals(regName.substring(2))) {
|
||||
FLog.e(
|
||||
TAG,
|
||||
String.format(
|
||||
"Direct event name for '%s' doesn't correspond to the naming convention,"
|
||||
+ " expected 'topEventName'->'onEventName', got '%s'->'%s'",
|
||||
viewManagerName, key, regName));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates map of constants that is then exposed by {@link UIManagerModule}. Provided list of
|
||||
* {@param viewManagers} is then used to populate content of those predefined fields using {@link
|
||||
* ViewManager#getExportedCustomBubblingEventTypeConstants} and {@link
|
||||
* ViewManager#getExportedCustomDirectEventTypeConstants} respectively. Each view manager is in
|
||||
* addition allowed to expose viewmanager-specific constants that are placed under the key that
|
||||
* corresponds to the view manager's name (see {@link ViewManager#getName}). Constants are merged
|
||||
* into the map of {@link UIManagerModule} base constants that is stored in {@link
|
||||
* UIManagerModuleConstants}. TODO(6845124): Create a test for this
|
||||
*/
|
||||
// NOTE: When converted to Kotlin this method should be `internal` due to
|
||||
// visibility restriction for `ReactInstance`
|
||||
public static Map<String, Object> createConstants(
|
||||
List<ViewManager> viewManagers,
|
||||
@Nullable Map<String, Object> allBubblingEventTypes,
|
||||
@Nullable Map<String, Object> allDirectEventTypes) {
|
||||
Map<String, Object> constants = UIManagerModuleConstants.constants;
|
||||
|
||||
// Generic/default event types:
|
||||
// All view managers are capable of dispatching these events.
|
||||
// They will be automatically registered with React Fiber.
|
||||
Map genericBubblingEventTypes = UIManagerModuleConstants.bubblingEventTypeConstants;
|
||||
Map genericDirectEventTypes = UIManagerModuleConstants.directEventTypeConstants;
|
||||
|
||||
// Cumulative event types:
|
||||
// View manager specific event types are collected as views are loaded.
|
||||
// This information is used later when events are dispatched.
|
||||
if (allBubblingEventTypes != null) {
|
||||
allBubblingEventTypes.putAll(genericBubblingEventTypes);
|
||||
}
|
||||
if (allDirectEventTypes != null) {
|
||||
allDirectEventTypes.putAll(genericDirectEventTypes);
|
||||
}
|
||||
|
||||
for (ViewManager viewManager : viewManagers) {
|
||||
final String viewManagerName = viewManager.getName();
|
||||
|
||||
Map viewManagerConstants =
|
||||
createConstantsForViewManager(
|
||||
viewManager, null, null, allBubblingEventTypes, allDirectEventTypes);
|
||||
if (!viewManagerConstants.isEmpty()) {
|
||||
constants.put(viewManagerName, viewManagerConstants);
|
||||
}
|
||||
}
|
||||
|
||||
constants.put("genericBubblingEventTypes", genericBubblingEventTypes);
|
||||
constants.put("genericDirectEventTypes", genericDirectEventTypes);
|
||||
return constants;
|
||||
}
|
||||
|
||||
// NOTE: When converted to Kotlin this method should be `internal` due to
|
||||
// visibility restriction for `ReactInstance`
|
||||
public static Map<String, Object> createConstantsForViewManager(
|
||||
ViewManager viewManager,
|
||||
@Nullable Map defaultBubblingEvents,
|
||||
@Nullable Map defaultDirectEvents,
|
||||
@Nullable Map cumulativeBubblingEventTypes,
|
||||
@Nullable Map cumulativeDirectEventTypes) {
|
||||
Map<String, Object> viewManagerConstants = MapBuilder.newHashMap();
|
||||
|
||||
Map viewManagerBubblingEvents = viewManager.getExportedCustomBubblingEventTypeConstants();
|
||||
if (viewManagerBubblingEvents != null) {
|
||||
if (ReactNativeNewArchitectureFeatureFlags.enableFabricRenderer()
|
||||
&& ReactNativeNewArchitectureFeatureFlags.useFabricInterop()) {
|
||||
// For Fabric, events needs to be fired with a "top" prefix.
|
||||
// For the sake of Fabric Interop, here we normalize events adding "top" in their
|
||||
// name if the user hasn't provided it.
|
||||
viewManagerBubblingEvents = normalizeEventTypes(viewManagerBubblingEvents);
|
||||
}
|
||||
recursiveMerge(cumulativeBubblingEventTypes, viewManagerBubblingEvents);
|
||||
recursiveMerge(viewManagerBubblingEvents, defaultBubblingEvents);
|
||||
viewManagerConstants.put(BUBBLING_EVENTS_KEY, viewManagerBubblingEvents);
|
||||
} else if (defaultBubblingEvents != null) {
|
||||
viewManagerConstants.put(BUBBLING_EVENTS_KEY, defaultBubblingEvents);
|
||||
}
|
||||
|
||||
Map viewManagerDirectEvents = viewManager.getExportedCustomDirectEventTypeConstants();
|
||||
validateDirectEventNames(viewManager.getName(), viewManagerDirectEvents);
|
||||
if (viewManagerDirectEvents != null) {
|
||||
if (ReactNativeNewArchitectureFeatureFlags.enableFabricRenderer()
|
||||
&& ReactNativeNewArchitectureFeatureFlags.useFabricInterop()) {
|
||||
// For Fabric, events needs to be fired with a "top" prefix.
|
||||
// For the sake of Fabric Interop, here we normalize events adding "top" in their
|
||||
// name if the user hasn't provided it.
|
||||
viewManagerDirectEvents = normalizeEventTypes(viewManagerDirectEvents);
|
||||
}
|
||||
recursiveMerge(cumulativeDirectEventTypes, viewManagerDirectEvents);
|
||||
recursiveMerge(viewManagerDirectEvents, defaultDirectEvents);
|
||||
viewManagerConstants.put(DIRECT_EVENTS_KEY, viewManagerDirectEvents);
|
||||
} else if (defaultDirectEvents != null) {
|
||||
viewManagerConstants.put(DIRECT_EVENTS_KEY, defaultDirectEvents);
|
||||
}
|
||||
|
||||
Map customViewConstants = viewManager.getExportedViewConstants();
|
||||
if (customViewConstants != null) {
|
||||
viewManagerConstants.put("Constants", customViewConstants);
|
||||
}
|
||||
Map viewManagerCommands = viewManager.getCommandsMap();
|
||||
if (viewManagerCommands != null) {
|
||||
viewManagerConstants.put("Commands", viewManagerCommands);
|
||||
}
|
||||
Map<String, String> viewManagerNativeProps = viewManager.getNativeProps();
|
||||
if (!viewManagerNativeProps.isEmpty()) {
|
||||
viewManagerConstants.put("NativeProps", viewManagerNativeProps);
|
||||
}
|
||||
|
||||
return viewManagerConstants;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
/* package */ static @Nullable Map normalizeEventTypes(@Nullable Map events) {
|
||||
if (events == null) {
|
||||
return null;
|
||||
}
|
||||
Set<String> keysToNormalize = new HashSet<>();
|
||||
for (Object key : events.keySet()) {
|
||||
if (key instanceof String) {
|
||||
String keyString = (String) key;
|
||||
if (!keyString.startsWith("top")) {
|
||||
keysToNormalize.add(keyString);
|
||||
}
|
||||
}
|
||||
}
|
||||
// When providing one event in Kotlin, it will create a SingletonMap by default
|
||||
// which will throw on trying to add new element to it
|
||||
if (!(events instanceof HashMap)) {
|
||||
events = new HashMap(events);
|
||||
}
|
||||
for (String oldKey : keysToNormalize) {
|
||||
Object value = events.get(oldKey);
|
||||
String baseKey = "";
|
||||
if (oldKey.startsWith("on")) {
|
||||
// Drop "on" prefix.
|
||||
baseKey = oldKey.substring(2);
|
||||
} else {
|
||||
// Capitalize first letter.
|
||||
baseKey = oldKey.substring(0, 1).toUpperCase() + oldKey.substring(1);
|
||||
}
|
||||
String newKey = "top" + baseKey;
|
||||
events.put(newKey, value);
|
||||
}
|
||||
return events;
|
||||
}
|
||||
|
||||
/** Merges {@param source} map into {@param dest} map recursively */
|
||||
private static void recursiveMerge(@Nullable Map dest, @Nullable Map source) {
|
||||
if (dest == null || source == null || source.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (Object key : source.keySet()) {
|
||||
Object sourceValue = source.get(key);
|
||||
Object destValue = dest.get(key);
|
||||
if (destValue != null && (sourceValue instanceof Map) && (destValue instanceof Map)) {
|
||||
// Since event maps are client based Map interface, it could be immutable
|
||||
if (!(destValue instanceof HashMap)) {
|
||||
destValue = new HashMap((Map) destValue);
|
||||
dest.replace(key, (Map) destValue);
|
||||
}
|
||||
recursiveMerge((Map) destValue, (Map) sourceValue);
|
||||
} else {
|
||||
dest.put(key, sourceValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+238
@@ -0,0 +1,238 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.react.uimanager
|
||||
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import com.facebook.common.logging.FLog
|
||||
import com.facebook.react.common.build.ReactBuildConfig
|
||||
import com.facebook.react.internal.featureflags.ReactNativeNewArchitectureFeatureFlags.enableFabricRenderer
|
||||
import com.facebook.react.internal.featureflags.ReactNativeNewArchitectureFeatureFlags.useFabricInterop
|
||||
import java.util.Locale
|
||||
|
||||
/**
|
||||
* Helps generate constants map for [UIManagerModule] by collecting and merging constants from
|
||||
* registered view managers.
|
||||
*/
|
||||
internal object UIManagerModuleConstantsHelper {
|
||||
private const val TAG = "UIManagerModuleConstantsHelper"
|
||||
private const val BUBBLING_EVENTS_KEY = "bubblingEventTypes"
|
||||
private const val DIRECT_EVENTS_KEY = "directEventTypes"
|
||||
|
||||
/**
|
||||
* Generates a lazy discovery enabled version of [UIManagerModule] constants. It only contains a
|
||||
* list of view manager names, so that JS side is aware of the managers there are. Actual
|
||||
* ViewManager instantiation happens when `UIManager.getViewManagerConfig('SpecificViewManager')`
|
||||
* call happens. The View Manager is then registered on the JS side with the help of
|
||||
* `UIManagerModule.getConstantsForViewManager`.
|
||||
*/
|
||||
@JvmStatic
|
||||
@JvmName("internal_createConstants")
|
||||
internal fun createConstants(resolver: ViewManagerResolver): Map<String, Any> =
|
||||
UIManagerModuleConstants.constants.plus(
|
||||
mapOf(
|
||||
"ViewManagerNames" to ArrayList<String?>(resolver.getViewManagerNames()),
|
||||
"LazyViewManagersEnabled" to true))
|
||||
|
||||
@JvmStatic
|
||||
val defaultExportableEventTypes: Map<String, Any>
|
||||
get() =
|
||||
mapOf(
|
||||
BUBBLING_EVENTS_KEY to UIManagerModuleConstants.bubblingEventTypeConstants,
|
||||
DIRECT_EVENTS_KEY to UIManagerModuleConstants.directEventTypeConstants)
|
||||
|
||||
private fun validateDirectEventNames(
|
||||
viewManagerName: String,
|
||||
directEvents: MutableMap<String, Any>?
|
||||
) {
|
||||
if (!ReactBuildConfig.DEBUG || directEvents == null) {
|
||||
return
|
||||
}
|
||||
|
||||
for ((key, value) in directEvents) {
|
||||
if (value is MutableMap<*, *>) {
|
||||
val regName = value["registrationName"] as String?
|
||||
if (regName != null &&
|
||||
key.startsWith("top") &&
|
||||
regName.startsWith("on") &&
|
||||
(key.substring(3) != regName.substring(2))) {
|
||||
FLog.e(
|
||||
TAG,
|
||||
"Direct event name for '$viewManagerName' doesn't correspond to the naming convention," +
|
||||
" expected 'topEventName'->'onEventName', got '$key'->'$regName'")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates map of constants that is then exposed by [UIManagerModule]. Provided list of {@param
|
||||
* viewManagers} is then used to populate content of those predefined fields using
|
||||
* [ViewManager.getExportedCustomBubblingEventTypeConstants] and
|
||||
* [ViewManager.getExportedCustomDirectEventTypeConstants] respectively. Each view manager is in
|
||||
* addition allowed to expose viewmanager-specific constants that are placed under the key that
|
||||
* corresponds to the view manager's name (see [ViewManager.getName]). Constants are merged into
|
||||
* the map of [UIManagerModule] base constants that is stored in [UIManagerModuleConstants].
|
||||
*
|
||||
* TODO(6845124): Create a test for this
|
||||
*/
|
||||
@JvmStatic
|
||||
@JvmName("internal_createConstants")
|
||||
internal fun createConstants(
|
||||
viewManagers: List<ViewManager<in Nothing, in Nothing>>,
|
||||
allBubblingEventTypes: MutableMap<String, Any>?,
|
||||
allDirectEventTypes: MutableMap<String, Any>?
|
||||
): MutableMap<String, Any> {
|
||||
val constants: MutableMap<String, Any> = UIManagerModuleConstants.constants.toMutableMap()
|
||||
|
||||
// Generic/default event types:
|
||||
// All view managers are capable of dispatching these events.
|
||||
// They will be automatically registered with React Fiber.
|
||||
val genericBubblingEventTypes: Map<String, Any> =
|
||||
UIManagerModuleConstants.bubblingEventTypeConstants
|
||||
val genericDirectEventTypes: Map<String, Any> =
|
||||
UIManagerModuleConstants.directEventTypeConstants
|
||||
|
||||
// Cumulative event types:
|
||||
// View manager specific event types are collected as views are loaded.
|
||||
// This information is used later when events are dispatched.
|
||||
allBubblingEventTypes?.putAll(genericBubblingEventTypes)
|
||||
allDirectEventTypes?.putAll(genericDirectEventTypes)
|
||||
|
||||
for (viewManager in viewManagers) {
|
||||
val viewManagerName = viewManager.getName()
|
||||
|
||||
val viewManagerConstants: MutableMap<*, *> =
|
||||
createConstantsForViewManager(
|
||||
viewManager, null, null, allBubblingEventTypes, allDirectEventTypes)
|
||||
if (!viewManagerConstants.isEmpty()) {
|
||||
constants[viewManagerName] = viewManagerConstants
|
||||
}
|
||||
}
|
||||
|
||||
constants["genericBubblingEventTypes"] = genericBubblingEventTypes
|
||||
constants["genericDirectEventTypes"] = genericDirectEventTypes
|
||||
return constants
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@JvmName("internal_createConstantsForViewManager")
|
||||
internal fun createConstantsForViewManager(
|
||||
viewManager: ViewManager<in Nothing, in Nothing>,
|
||||
defaultBubblingEvents: MutableMap<String, Any>?,
|
||||
defaultDirectEvents: MutableMap<String, Any>?,
|
||||
cumulativeBubblingEventTypes: MutableMap<String, Any>?,
|
||||
cumulativeDirectEventTypes: MutableMap<String, Any>?
|
||||
): MutableMap<String, Any> {
|
||||
val viewManagerConstants: MutableMap<String, Any> = mutableMapOf()
|
||||
|
||||
var viewManagerBubblingEvents: MutableMap<String, Any>? =
|
||||
viewManager.exportedCustomBubblingEventTypeConstants
|
||||
if (viewManagerBubblingEvents != null) {
|
||||
if (enableFabricRenderer() && useFabricInterop()) {
|
||||
// For Fabric, events needs to be fired with a "top" prefix.
|
||||
// For the sake of Fabric Interop, here we normalize events adding "top" in their
|
||||
// name if the user hasn't provided it.
|
||||
viewManagerBubblingEvents = normalizeEventTypes(viewManagerBubblingEvents)
|
||||
}
|
||||
recursiveMerge(cumulativeBubblingEventTypes, viewManagerBubblingEvents)
|
||||
recursiveMerge(viewManagerBubblingEvents, defaultBubblingEvents)
|
||||
viewManagerConstants.put(BUBBLING_EVENTS_KEY, viewManagerBubblingEvents)
|
||||
} else if (defaultBubblingEvents != null) {
|
||||
viewManagerConstants.put(BUBBLING_EVENTS_KEY, defaultBubblingEvents)
|
||||
}
|
||||
|
||||
var viewManagerDirectEvents: MutableMap<String, Any>? =
|
||||
viewManager.exportedCustomDirectEventTypeConstants
|
||||
validateDirectEventNames(viewManager.getName(), viewManagerDirectEvents)
|
||||
if (viewManagerDirectEvents != null) {
|
||||
if (enableFabricRenderer() && useFabricInterop()) {
|
||||
// For Fabric, events needs to be fired with a "top" prefix.
|
||||
// For the sake of Fabric Interop, here we normalize events adding "top" in their
|
||||
// name if the user hasn't provided it.
|
||||
viewManagerDirectEvents = normalizeEventTypes(viewManagerDirectEvents)
|
||||
}
|
||||
recursiveMerge(cumulativeDirectEventTypes, viewManagerDirectEvents)
|
||||
recursiveMerge(viewManagerDirectEvents, defaultDirectEvents)
|
||||
viewManagerConstants.put(DIRECT_EVENTS_KEY, viewManagerDirectEvents)
|
||||
} else if (defaultDirectEvents != null) {
|
||||
viewManagerConstants.put(DIRECT_EVENTS_KEY, defaultDirectEvents)
|
||||
}
|
||||
|
||||
val customViewConstants: MutableMap<String, Any>? = viewManager.exportedViewConstants
|
||||
if (customViewConstants != null) {
|
||||
viewManagerConstants.put("Constants", customViewConstants)
|
||||
}
|
||||
val viewManagerCommands: MutableMap<String, Int>? = viewManager.commandsMap
|
||||
if (viewManagerCommands != null) {
|
||||
viewManagerConstants.put("Commands", viewManagerCommands)
|
||||
}
|
||||
val viewManagerNativeProps = viewManager.nativeProps
|
||||
if (!viewManagerNativeProps.isEmpty()) {
|
||||
viewManagerConstants.put("NativeProps", viewManagerNativeProps)
|
||||
}
|
||||
|
||||
return viewManagerConstants
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
internal fun normalizeEventTypes(
|
||||
eventsToNormalize: MutableMap<String, Any>
|
||||
): MutableMap<String, Any> {
|
||||
var events = eventsToNormalize
|
||||
val keysToNormalize: MutableSet<String> = hashSetOf()
|
||||
for (key in events.keys) {
|
||||
val keyString = key
|
||||
if (!keyString.startsWith("top")) {
|
||||
keysToNormalize.add(keyString)
|
||||
}
|
||||
}
|
||||
// When providing one event in Kotlin, it will create a SingletonMap by default
|
||||
// which will throw on trying to add new element to it
|
||||
if (events !is HashMap<*, *>) {
|
||||
events = HashMap(events)
|
||||
}
|
||||
for (oldKey in keysToNormalize) {
|
||||
val value = checkNotNull(events[oldKey])
|
||||
val baseKey =
|
||||
if (oldKey.startsWith("on")) {
|
||||
// Drop "on" prefix.
|
||||
oldKey.substring(2)
|
||||
} else {
|
||||
// Capitalize first letter.
|
||||
oldKey.substring(0, 1).uppercase(Locale.getDefault()) + oldKey.substring(1)
|
||||
}
|
||||
val newKey = "top$baseKey"
|
||||
events.put(newKey, value)
|
||||
}
|
||||
return events
|
||||
}
|
||||
|
||||
/** Merges [source] map into [dest] map recursively */
|
||||
private fun recursiveMerge(dest: MutableMap<String, Any>?, source: MutableMap<String, Any>?) {
|
||||
if (dest == null || source == null || source.isEmpty()) {
|
||||
return
|
||||
}
|
||||
|
||||
for ((key, sourceValue) in source) {
|
||||
var destValue = dest[key]
|
||||
if (destValue != null &&
|
||||
(sourceValue is MutableMap<*, *>) &&
|
||||
(destValue is MutableMap<*, *>)) {
|
||||
// Since event maps are client based Map interface, it could be immutable
|
||||
if (destValue !is HashMap<*, *>) {
|
||||
destValue = HashMap(destValue)
|
||||
dest.replace(key, destValue as MutableMap<*, *>)
|
||||
}
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
recursiveMerge(destValue as MutableMap<String, Any>, sourceValue as MutableMap<String, Any>)
|
||||
} else {
|
||||
dest.put(key, sourceValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+6
-11
@@ -19,27 +19,22 @@ class UIManagerModuleConstantsHelperTest {
|
||||
ReactNativeFeatureFlagsForTests.setUp()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun normalizeEventTypes_withNull_doesNothing() {
|
||||
assertThat(UIManagerModuleConstantsHelper.normalizeEventTypes(null)).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun normalizeEventTypes_withEmptyMap_doesNothing() {
|
||||
val emptyMap = mutableMapOf<String, Any?>()
|
||||
val emptyMap = mutableMapOf<String, Any>()
|
||||
assertThat(UIManagerModuleConstantsHelper.normalizeEventTypes(emptyMap)).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun normalizeEventTypes_withOnEvent_doesNormalize() {
|
||||
val onClickMap = mutableMapOf("onClick" to "¯\\_(ツ)_/¯")
|
||||
val onClickMap = mutableMapOf<String, Any>("onClick" to "¯\\_(ツ)_/¯")
|
||||
assertThat(UIManagerModuleConstantsHelper.normalizeEventTypes(onClickMap))
|
||||
.containsKeys("topClick", "onClick")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun normalizeEventTypes_withTopEvent_doesNormalize() {
|
||||
val onClickMap = mutableMapOf("topOnClick" to "¯\\_(ツ)_/¯")
|
||||
val onClickMap = mutableMapOf<String, Any>("topOnClick" to "¯\\_(ツ)_/¯")
|
||||
assertThat(UIManagerModuleConstantsHelper.normalizeEventTypes(onClickMap))
|
||||
.containsKey("topOnClick")
|
||||
.doesNotContainKey("onClick")
|
||||
@@ -49,11 +44,11 @@ class UIManagerModuleConstantsHelperTest {
|
||||
@Test
|
||||
fun normalizeEventTypes_withNestedObjects_doesNotLoseThem() {
|
||||
val nestedObjects =
|
||||
mutableMapOf(
|
||||
mutableMapOf<String, Any>(
|
||||
"onColorChanged" to
|
||||
mutableMapOf(
|
||||
mutableMapOf<String, Any>(
|
||||
"phasedRegistrationNames" to
|
||||
mutableMapOf(
|
||||
mutableMapOf<String, Any>(
|
||||
"bubbled" to "onColorChanged",
|
||||
"captured" to "onColorChangedCapture",
|
||||
)))
|
||||
|
||||
Reference in New Issue
Block a user