diff --git a/React/Fabric/Mounting/ComponentViews/InteropView/RCTLegacyViewManagerInteropComponentView.mm b/React/Fabric/Mounting/ComponentViews/InteropView/RCTLegacyViewManagerInteropComponentView.mm index 2044763056d..3d769fd9107 100644 --- a/React/Fabric/Mounting/ComponentViews/InteropView/RCTLegacyViewManagerInteropComponentView.mm +++ b/React/Fabric/Mounting/ComponentViews/InteropView/RCTLegacyViewManagerInteropComponentView.mm @@ -7,6 +7,7 @@ #import "RCTLegacyViewManagerInteropComponentView.h" +#import #import #import #import @@ -31,19 +32,22 @@ using namespace facebook::react; + (BOOL)isSupported:(NSString *)componentName { - static NSSet *supportedComponents = [NSSet setWithObjects:@"ActivityIndicatorView", nil]; + static NSSet *supportedComponents = [NSSet setWithObjects:@"Picker", @"DatePicker", nil]; return [supportedComponents containsObject:componentName]; } -#pragma mark - RCTComponentViewProtocol - -- (void)prepareForRecycle +- (RCTLegacyViewManagerInteropCoordinator *)coordinator { - [super prepareForRecycle]; - [_view removeFromSuperview]; - _view = NULL; + if (_state != nullptr) { + const auto &state = _state->getData(); + return unwrapManagedObject(state.coordinator); + } else { + return NULL; + } } +#pragma mark - RCTComponentViewProtocol + + (ComponentDescriptorProvider)componentDescriptorProvider { return concreteComponentDescriptorProvider(); @@ -58,18 +62,24 @@ using namespace facebook::react; { [super finalizeUpdates:updateMask]; assert(_props && _state); - const auto &state = _state->getData(); - RCTLegacyViewManagerInteropCoordinator *coordinator = unwrapManagedObject(state.coordinator); if (!_view) { - UIView *view = [coordinator view]; + __weak __typeof(self) weakSelf = self; + UIView *view = [self.coordinator viewWithInterceptor:^(std::string eventName, folly::dynamic event) { + if (weakSelf) { + __typeof(self) strongSelf = weakSelf; + auto eventEmitter = + std::static_pointer_cast(strongSelf->_eventEmitter); + eventEmitter->dispatchEvent(eventName, event); + } + }]; self.contentView = view; _view = view; } if (updateMask & RNComponentViewUpdateMaskProps) { const auto &newProps = *std::static_pointer_cast(_props); - [coordinator setProps:newProps.otherProps forView:_view]; + [self.coordinator setProps:newProps.otherProps forView:_view]; } } diff --git a/React/Views/RCTComponentData.h b/React/Views/RCTComponentData.h index 7b1cf485c91..dd777ef2fbc 100644 --- a/React/Views/RCTComponentData.h +++ b/React/Views/RCTComponentData.h @@ -29,6 +29,8 @@ - (void)setProps:(NSDictionary *)props forView:(id)view; - (void)setProps:(NSDictionary *)props forShadowView:(RCTShadowView *)shadowView; +@property (nonatomic, copy, nullable) void (^eventInterceptor)(NSString *eventName, NSDictionary *event, id sender); + - (NSDictionary *)viewConfig; @end diff --git a/React/Views/RCTComponentData.m b/React/Views/RCTComponentData.m index c9a3b502e66..261141f8262 100644 --- a/React/Views/RCTComponentData.m +++ b/React/Views/RCTComponentData.m @@ -20,6 +20,7 @@ typedef void (^RCTPropBlock)(id view, id json); typedef NSMutableDictionary RCTPropBlockDictionary; +typedef void (^InterceptorBlock)(NSString *eventName, NSDictionary *event, id sender); /** * Get the converter function for the specified type @@ -101,7 +102,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init) } } -static RCTPropBlock createEventSetter(NSString *propName, SEL setter, RCTBridge *bridge) +static RCTPropBlock createEventSetter(NSString *propName, SEL setter,InterceptorBlock eventInterceptor, RCTBridge *bridge) { __weak RCTBridge *weakBridge = bridge; return ^(id target, id json) { @@ -115,10 +116,14 @@ static RCTPropBlock createEventSetter(NSString *propName, SEL setter, RCTBridge return; } - RCTComponentEvent *componentEvent = [[RCTComponentEvent alloc] initWithName:propName - viewTag:strongTarget.reactTag - body:event]; - [weakBridge.eventDispatcher sendEvent:componentEvent]; + if (eventInterceptor) { + eventInterceptor(propName, event, strongTarget); + } else { + RCTComponentEvent *componentEvent = [[RCTComponentEvent alloc] initWithName:propName + viewTag:strongTarget.reactTag + body:event]; + [weakBridge.eventDispatcher sendEvent:componentEvent]; + } }; } ((void (*)(id, SEL, id))objc_msgSend)(target, setter, eventHandler); @@ -244,7 +249,7 @@ static RCTPropBlock createNSInvocationSetter(NSMethodSignature *typeSignature, S if (type == NSSelectorFromString(@"RCTBubblingEventBlock:") || type == NSSelectorFromString(@"RCTDirectEventBlock:")) { // Special case for event handlers - setterBlock = createEventSetter(name, setter, _bridge); + setterBlock = createEventSetter(name, setter, self.eventInterceptor, _bridge); } else { // Ordinary property handlers NSMethodSignature *typeSignature = [[RCTConvert class] methodSignatureForSelector:type]; diff --git a/ReactCommon/fabric/components/legacyviewmanagerinterop/LegacyViewManagerInteropShadowNode.h b/ReactCommon/fabric/components/legacyviewmanagerinterop/LegacyViewManagerInteropShadowNode.h index 990605adcd5..8e109460876 100644 --- a/ReactCommon/fabric/components/legacyviewmanagerinterop/LegacyViewManagerInteropShadowNode.h +++ b/ReactCommon/fabric/components/legacyviewmanagerinterop/LegacyViewManagerInteropShadowNode.h @@ -8,6 +8,7 @@ #pragma once #include +#include #include #include @@ -19,7 +20,7 @@ extern const char LegacyViewManagerInteropComponentName[]; using LegacyViewManagerInteropShadowNode = ConcreteViewShadowNode< LegacyViewManagerInteropComponentName, LegacyViewManagerInteropViewProps, - ViewEventEmitter, + LegacyViewManagerInteropViewEventEmitter, LegacyViewManagerInteropState>; } // namespace react diff --git a/ReactCommon/fabric/components/legacyviewmanagerinterop/LegacyViewManagerInteropViewEventEmitter.cpp b/ReactCommon/fabric/components/legacyviewmanagerinterop/LegacyViewManagerInteropViewEventEmitter.cpp new file mode 100644 index 00000000000..afa8461ed3a --- /dev/null +++ b/ReactCommon/fabric/components/legacyviewmanagerinterop/LegacyViewManagerInteropViewEventEmitter.cpp @@ -0,0 +1,20 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "LegacyViewManagerInteropViewEventEmitter.h" +#include + +namespace facebook { +namespace react { +void LegacyViewManagerInteropViewEventEmitter::dispatchEvent( + std::string const &type, + folly::dynamic const &payload) const { + EventEmitter::dispatchEvent(type, payload); +} + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/fabric/components/legacyviewmanagerinterop/LegacyViewManagerInteropViewEventEmitter.h b/ReactCommon/fabric/components/legacyviewmanagerinterop/LegacyViewManagerInteropViewEventEmitter.h new file mode 100644 index 00000000000..f7679f8612f --- /dev/null +++ b/ReactCommon/fabric/components/legacyviewmanagerinterop/LegacyViewManagerInteropViewEventEmitter.h @@ -0,0 +1,32 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +#pragma once + +#include + +#include +#include +#include + +namespace facebook { +namespace react { + +class LegacyViewManagerInteropViewEventEmitter; + +using SharedLegacyViewManagerInteropViewEventEmitter = + std::shared_ptr; + +class LegacyViewManagerInteropViewEventEmitter : public ViewEventEmitter { + public: + using ViewEventEmitter::ViewEventEmitter; + + void dispatchEvent(std::string const &type, folly::dynamic const &payload) + const; +}; + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/fabric/components/legacyviewmanagerinterop/RCTLegacyViewManagerInteropCoordinator.h b/ReactCommon/fabric/components/legacyviewmanagerinterop/RCTLegacyViewManagerInteropCoordinator.h index c5ebd29054d..7a44725cb39 100644 --- a/ReactCommon/fabric/components/legacyviewmanagerinterop/RCTLegacyViewManagerInteropCoordinator.h +++ b/ReactCommon/fabric/components/legacyviewmanagerinterop/RCTLegacyViewManagerInteropCoordinator.h @@ -13,11 +13,13 @@ NS_ASSUME_NONNULL_BEGIN @class RCTComponentData; +typedef void (^InterceptorBlock)(std::string eventName, folly::dynamic event); + @interface RCTLegacyViewManagerInteropCoordinator : NSObject - (instancetype)initWithComponentData:(RCTComponentData *)componentData; -- (UIView *)view; +- (UIView *)viewWithInterceptor:(InterceptorBlock)block; - (void)setProps:(folly::dynamic const &)props forView:(UIView *)view; diff --git a/ReactCommon/fabric/components/legacyviewmanagerinterop/RCTLegacyViewManagerInteropCoordinator.mm b/ReactCommon/fabric/components/legacyviewmanagerinterop/RCTLegacyViewManagerInteropCoordinator.mm index 30ce6294780..9a41afa46ec 100644 --- a/ReactCommon/fabric/components/legacyviewmanagerinterop/RCTLegacyViewManagerInteropCoordinator.mm +++ b/ReactCommon/fabric/components/legacyviewmanagerinterop/RCTLegacyViewManagerInteropCoordinator.mm @@ -5,38 +5,52 @@ * LICENSE file in the root directory of this source tree. */ -#import "RCTLegacyViewManagerInteropCoordinator.h" +#include "RCTLegacyViewManagerInteropCoordinator.h" #include +#include #include -static NSDictionary *something(folly::dynamic const &props) -{ - auto json = folly::toJson(props); - NSData *data = [NSData dataWithBytes:json.c_str() length:json.size()]; - NSDictionary *result = [NSJSONSerialization JSONObjectWithData:data options:0 error:NULL]; - return result; -} +using namespace facebook::react; @implementation RCTLegacyViewManagerInteropCoordinator { RCTComponentData *_componentData; + /* + Each instnace of `RCTLegacyViewManagerInteropComponentView` registers a block to which events are dispatched. + This is the container that maps unretained UIView pointer to a block to which the event is dispatched. + */ + NSMutableDictionary *_eventInterceptors; } - (instancetype)initWithComponentData:(RCTComponentData *)componentData; { if (self = [super init]) { _componentData = componentData; + + _eventInterceptors = [NSMutableDictionary new]; + + __weak __typeof(self) weakSelf = self; + _componentData.eventInterceptor = ^(NSString *eventName, NSDictionary *event, id sender) { + __typeof(self) strongSelf = weakSelf; + InterceptorBlock block = + [strongSelf->_eventInterceptors objectForKey:[NSValue valueWithNonretainedObject:sender]]; + if (block) { + block(std::string([RCTNormalizeInputEventName(eventName) UTF8String]), convertIdToFollyDynamic(event)); + } + }; } return self; } -- (UIView *)view +- (UIView *)viewWithInterceptor:(InterceptorBlock)block { - return [_componentData createViewWithTag:NULL]; + UIView *view = [_componentData createViewWithTag:NULL]; + [_eventInterceptors setObject:block forKey:[NSValue valueWithNonretainedObject:view]]; + return view; } - (void)setProps:(folly::dynamic const &)props forView:(UIView *)view { - NSDictionary *convertedProps = something(props); + NSDictionary *convertedProps = convertFollyDynamicToId(props); [_componentData setProps:convertedProps forView:view]; }