From 0932a0d775caae251ce6738904ada2cb04d92c8e Mon Sep 17 00:00:00 2001 From: Paige Sun Date: Sun, 2 May 2021 15:41:22 -0700 Subject: [PATCH] iOS: 2/5 Remove use of bridge from Modal by dismissing with visible prop Summary: Changelog: [Fabric][iOS][Fix] Remove use of bridge from Modal by dismissing Modal with visible prop Reviewed By: sammy-SC Differential Revision: D28074326 fbshipit-source-id: 0278bfb031db802b59429c553ac62d83838f4cc9 --- Libraries/Modal/Modal.js | 7 ++ .../Modal/RCTModalHostViewNativeComponent.js | 7 ++ .../__snapshots__/Modal-test.js.snap | 2 + .../Modal/RCTModalHostViewComponentView.h | 9 +-- .../Modal/RCTModalHostViewComponentView.mm | 68 ++++++++++++------- React/Views/RCTModalHostViewManager.m | 1 + React/Views/RCTModalManager.h | 7 -- React/Views/RCTModalManager.m | 9 --- .../views/modal/ReactModalHostManager.java | 6 ++ 9 files changed, 68 insertions(+), 48 deletions(-) diff --git a/Libraries/Modal/Modal.js b/Libraries/Modal/Modal.js index 353bb75c699..7786a1e6650 100644 --- a/Libraries/Modal/Modal.js +++ b/Libraries/Modal/Modal.js @@ -193,6 +193,7 @@ class Modal extends React.Component { } componentDidMount() { + // 'modalDismissed' is for the old renderer in iOS only if (ModalEventEmitter) { this._eventSubscription = ModalEventEmitter.addListener( 'modalDismissed', @@ -251,6 +252,12 @@ class Modal extends React.Component { hardwareAccelerated={this.props.hardwareAccelerated} onRequestClose={this.props.onRequestClose} onShow={this.props.onShow} + onDismiss={() => { + if (this.props.onDismiss) { + this.props.onDismiss(); + } + }} + visible={this.props.visible} statusBarTranslucent={this.props.statusBarTranslucent} identifier={this._identifier} style={styles.modal} diff --git a/Libraries/Modal/RCTModalHostViewNativeComponent.js b/Libraries/Modal/RCTModalHostViewNativeComponent.js index 136d6120f64..85f9916ef38 100644 --- a/Libraries/Modal/RCTModalHostViewNativeComponent.js +++ b/Libraries/Modal/RCTModalHostViewNativeComponent.js @@ -92,6 +92,13 @@ type NativeProps = $ReadOnly<{| */ onDismiss?: ?DirectEventHandler, + /** + * The `visible` prop determines whether your modal is visible. + * + * See https://reactnative.dev/docs/modal.html#visible + */ + visible?: WithDefault, + /** * Deprecated. Use the `animationType` prop instead. */ diff --git a/Libraries/Modal/__tests__/__snapshots__/Modal-test.js.snap b/Libraries/Modal/__tests__/__snapshots__/Modal-test.js.snap index d6272a92fb5..b8afa443934 100644 --- a/Libraries/Modal/__tests__/__snapshots__/Modal-test.js.snap +++ b/Libraries/Modal/__tests__/__snapshots__/Modal-test.js.snap @@ -14,6 +14,7 @@ exports[` should render as when not mocked 1`] = ` animationType="none" hardwareAccelerated={false} identifier={1} + onDismiss={[Function]} onStartShouldSetResponder={[Function]} presentationStyle="fullScreen" style={ @@ -21,6 +22,7 @@ exports[` should render as when not mocked 1`] = ` "position": "absolute", } } + visible={true} > (_props); - [[RCTBridge currentBridge].modalManager modalDismissed:@(props.identifier)]; + [modalViewController dismissViewControllerAnimated:animated completion:completion]; } - (void)ensurePresentedOnlyIfNeeded { - BOOL shouldBePresented = !_isPresented && self.window; + BOOL shouldBePresented = !_isPresented && _shouldPresent && self.window; if (shouldBePresented) { _isPresented = YES; [self presentViewController:self.viewController animated:_shouldAnimatePresentation completion:^{ - if (!self->_eventEmitter) { - return; - } + auto eventEmitter = [self modalEventEmitter]; + if (eventEmitter) { + eventEmitter->onShow(ModalHostViewEventEmitter::OnShow{}); - assert(std::dynamic_pointer_cast(self->_eventEmitter)); - auto eventEmitter = - std::static_pointer_cast(self->_eventEmitter); - eventEmitter->onShow(ModalHostViewEventEmitter::OnShow{}); + // A hack so that EventEmitter.cpp's eventTarget_ does not become null when modal is dismissed + eventEmitter->setEnabled(true); + } }]; } - BOOL shouldBeHidden = _isPresented && !self.superview; + BOOL shouldBeHidden = _isPresented && (!_shouldPresent || !self.superview); if (shouldBeHidden) { _isPresented = NO; // To animate dismissal of view controller, snapshot of // view hierarchy needs to be added to the UIViewController. - [self.viewController.view addSubview:_modalContentsSnapshot]; - [self dismissViewController:self.viewController animated:_shouldAnimatePresentation]; + UIView *snapshot = _modalContentsSnapshot; + [self.viewController.view addSubview:snapshot]; + + auto eventEmitter = [self modalEventEmitter]; + [self dismissViewController:self.viewController + animated:_shouldAnimatePresentation + completion:^{ + [snapshot removeFromSuperview]; + + if (eventEmitter) { + eventEmitter->onDismiss(ModalHostViewEventEmitter::OnDismiss{}); + } + }]; } } +- (std::shared_ptr)modalEventEmitter +{ + if (!self->_eventEmitter) { + return nullptr; + } + + assert(std::dynamic_pointer_cast(self->_eventEmitter)); + return std::static_pointer_cast(self->_eventEmitter); +} + #pragma mark - RCTMountingTransactionObserving - (void)mountingTransactionWillMountWithMetadata:(MountingTransactionMetadata const &)metadata @@ -205,10 +219,8 @@ static ModalHostViewEventEmitter::OnOrientationChange onOrientationChangeStruct( - (void)boundsDidChange:(CGRect)newBounds { - if (_eventEmitter) { - assert(std::dynamic_pointer_cast(_eventEmitter)); - - auto eventEmitter = std::static_pointer_cast(_eventEmitter); + auto eventEmitter = [self modalEventEmitter]; + if (eventEmitter) { eventEmitter->onOrientationChange(onOrientationChangeStruct(newBounds)); } @@ -231,6 +243,7 @@ static ModalHostViewEventEmitter::OnOrientationChange onOrientationChangeStruct( _state.reset(); _viewController = nil; _isPresented = NO; + _shouldPresent = NO; } - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps @@ -247,6 +260,9 @@ static ModalHostViewEventEmitter::OnOrientationChange onOrientationChangeStruct( self.viewController.modalPresentationStyle = presentationConfiguration(newProps); + _shouldPresent = newProps.visible; + [self ensurePresentedOnlyIfNeeded]; + [super updateProps:props oldProps:oldProps]; } diff --git a/React/Views/RCTModalHostViewManager.m b/React/Views/RCTModalHostViewManager.m index 335e077e3af..5304d41cf5e 100644 --- a/React/Views/RCTModalHostViewManager.m +++ b/React/Views/RCTModalHostViewManager.m @@ -122,6 +122,7 @@ RCT_EXPORT_VIEW_PROPERTY(supportedOrientations, NSArray) RCT_EXPORT_VIEW_PROPERTY(onOrientationChange, RCTDirectEventBlock) // Fabric only +RCT_EXPORT_VIEW_PROPERTY(visible, BOOL) RCT_EXPORT_VIEW_PROPERTY(onDismiss, RCTDirectEventBlock) @end diff --git a/React/Views/RCTModalManager.h b/React/Views/RCTModalManager.h index a34d801448c..4fbe6dfbd01 100644 --- a/React/Views/RCTModalManager.h +++ b/React/Views/RCTModalManager.h @@ -7,7 +7,6 @@ #import -#import #import #import @@ -16,9 +15,3 @@ - (void)modalDismissed:(NSNumber *)modalID; @end - -@interface RCTBridge (RCTModalManager) - -@property (nonatomic, readonly) RCTModalManager *modalManager; - -@end diff --git a/React/Views/RCTModalManager.m b/React/Views/RCTModalManager.m index 399c025a161..992b73c62db 100644 --- a/React/Views/RCTModalManager.m +++ b/React/Views/RCTModalManager.m @@ -40,12 +40,3 @@ RCT_EXPORT_MODULE(); } @end - -@implementation RCTBridge (RCTDevSettings) - -- (RCTModalManager *)modalManager -{ - return [self moduleForClass:[RCTModalManager class]]; -} - -@end diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostManager.java index e10cb5cd33f..d4f3394269c 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostManager.java @@ -91,6 +91,12 @@ public class ReactModalHostManager extends ViewGroupManager view.setHardwareAccelerated(hardwareAccelerated); } + @Override + @ReactProp(name = "visible") + public void setVisible(ReactModalHostView view, boolean visible) { + // iOS only + } + @Override public void setPresentationStyle(ReactModalHostView view, @Nullable String value) {}