Skip to content

Commit

Permalink
Back out "Send Modal onDismiss event on iOS (Fabric) and Android" @by…
Browse files Browse the repository at this point in the history
…pass-github-export-checks

Summary:
~~Original commit changeset: f419164032c3

Original Phabricator Diff: D52445670

bypass-github-export-checks

Changelog: [Internal]

Reviewed By: makovkastar

Differential Revision: D52932743

fbshipit-source-id: ea37270998213de0ae732477e0fb99b47aae7cd5
  • Loading branch information
Erica Klein authored and facebook-github-bot committed Jan 20, 2024
1 parent 92b889b commit a58ec07
Show file tree
Hide file tree
Showing 17 changed files with 185 additions and 101 deletions.
9 changes: 5 additions & 4 deletions packages/react-native/Libraries/Modal/Modal.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,6 @@ export interface ModalBaseProps {
* The `onShow` prop allows passing a function that will be called once the modal has been shown.
*/
onShow?: ((event: NativeSyntheticEvent<any>) => void) | undefined;
/**
* The `onDismiss` prop allows passing a function that will be called once the modal has been dismissed.
*/
onDismiss?: (() => void) | undefined;
}

export interface ModalPropsIOS {
Expand Down Expand Up @@ -74,6 +70,11 @@ export interface ModalPropsIOS {
>
| undefined;

/**
* The `onDismiss` prop allows passing a function that will be called once the modal has been dismissed.
*/
onDismiss?: (() => void) | undefined;

/**
* The `onOrientationChange` callback is called when the orientation changes while the modal is being displayed.
* The orientation provided is only 'portrait' or 'landscape'. This callback is also called on initial render, regardless of the current orientation.
Expand Down
70 changes: 53 additions & 17 deletions packages/react-native/Libraries/Modal/Modal.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ import type {ViewProps} from '../Components/View/ViewPropTypes';
import type {RootTag} from '../ReactNative/RootTag';
import type {DirectEventHandler} from '../Types/CodegenTypes';

import NativeEventEmitter from '../EventEmitter/NativeEventEmitter';
import {type EventSubscription} from '../vendor/emitter/EventEmitter';
import ModalInjection from './ModalInjection';
import NativeModalManager from './NativeModalManager';
import RCTModalHostView from './RCTModalHostViewNativeComponent';
import {VirtualizedListContextResetter} from '@react-native/virtualized-lists';

Expand All @@ -22,14 +25,34 @@ const AppContainer = require('../ReactNative/AppContainer');
const I18nManager = require('../ReactNative/I18nManager');
const {RootTagContext} = require('../ReactNative/RootTag');
const StyleSheet = require('../StyleSheet/StyleSheet');
const Platform = require('../Utilities/Platform');
const React = require('react');

type ModalEventDefinitions = {
modalDismissed: [{modalID: number}],
};

const ModalEventEmitter =
Platform.OS === 'ios' && NativeModalManager != null
? new NativeEventEmitter<ModalEventDefinitions>(
// T88715063: NativeEventEmitter only used this parameter on iOS. Now it uses it on all platforms, so this code was modified automatically to preserve its behavior
// If you want to use the native module on other platforms, please remove this condition and test its behavior
Platform.OS !== 'ios' ? null : NativeModalManager,
)
: null;

/**
* The Modal component is a simple way to present content above an enclosing view.
*
* See https://reactnative.dev/docs/modal
*/

// In order to route onDismiss callbacks, we need to uniquely identifier each
// <Modal> on screen. There can be different ones, either nested or as siblings.
// We cannot pass the onDismiss callback to native as the view will be
// destroyed before the callback is fired.
let uniqueModalIdentifier = 0;

type OrientationChangeEvent = $ReadOnly<{|
orientation: 'portrait' | 'landscape',
|}>;
Expand Down Expand Up @@ -136,10 +159,6 @@ export type Props = $ReadOnly<{|
onOrientationChange?: ?DirectEventHandler<OrientationChangeEvent>,
|}>;

type State = {|
isRendering: boolean,
|};

function confirmProps(props: Props) {
if (__DEV__) {
if (
Expand All @@ -154,35 +173,53 @@ function confirmProps(props: Props) {
}
}

class Modal extends React.Component<Props, State> {
class Modal extends React.Component<Props> {
static defaultProps: {|hardwareAccelerated: boolean, visible: boolean|} = {
visible: true,
hardwareAccelerated: false,
};

static contextType: React.Context<RootTag> = RootTagContext;

_identifier: number;
_eventSubscription: ?EventSubscription;

constructor(props: Props) {
super(props);
this.state = {
isRendering: props.visible === true,
};
if (__DEV__) {
confirmProps(props);
}
this._identifier = uniqueModalIdentifier++;
}

componentDidUpdate(prevProps: Props) {
if (prevProps.visible !== true && this.props.visible === true) {
this.setState({isRendering: true});
componentDidMount() {
// 'modalDismissed' is for the old renderer in iOS only
if (ModalEventEmitter) {
this._eventSubscription = ModalEventEmitter.addListener(
'modalDismissed',
event => {
if (event.modalID === this._identifier && this.props.onDismiss) {
this.props.onDismiss();
}
},
);
}
}

componentWillUnmount() {
if (this._eventSubscription) {
this._eventSubscription.remove();
}
}

componentDidUpdate() {
if (__DEV__) {
confirmProps(this.props);
}
}

render(): React.Node {
if (this.props.visible !== true && !this.state.isRendering) {
if (this.props.visible !== true) {
return null;
}

Expand Down Expand Up @@ -216,14 +253,13 @@ class Modal extends React.Component<Props, State> {
onRequestClose={this.props.onRequestClose}
onShow={this.props.onShow}
onDismiss={() => {
this.setState({isRendering: false}, () => {
if (this.props.onDismiss) {
this.props.onDismiss();
}
});
if (this.props.onDismiss) {
this.props.onDismiss();
}
}}
visible={this.props.visible}
statusBarTranslucent={this.props.statusBarTranslucent}
identifier={this._identifier}
style={styles.modal}
// $FlowFixMe[method-unbinding] added when improving typing for this parameters
onStartShouldSetResponder={this._shouldSetResponder}
Expand Down
21 changes: 21 additions & 0 deletions packages/react-native/Libraries/Modal/NativeModalManager.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/**
* 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.
*
* @flow strict
* @format
*/

import type {TurboModule} from '../TurboModule/RCTExport';

import * as TurboModuleRegistry from '../TurboModule/TurboModuleRegistry';

export interface Spec extends TurboModule {
// RCTEventEmitter
+addListener: (eventName: string) => void;
+removeListeners: (count: number) => void;
}

export default (TurboModuleRegistry.get<Spec>('ModalManager'): ?Spec);
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@

import type {ViewProps} from '../Components/View/ViewPropTypes';
import type {HostComponent} from '../Renderer/shims/ReactNativeTypes';
import type {DirectEventHandler, WithDefault} from '../Types/CodegenTypes';
import type {
DirectEventHandler,
Int32,
WithDefault,
} from '../Types/CodegenTypes';

import codegenNativeComponent from '../Utilities/codegenNativeComponent';

Expand Down Expand Up @@ -122,6 +126,11 @@ type NativeProps = $ReadOnly<{|
* See https://reactnative.dev/docs/modal#onorientationchange
*/
onOrientationChange?: ?DirectEventHandler<OrientationChangeEvent>,

/**
* The `identifier` is the unique number for identifying Modal components.
*/
identifier?: WithDefault<Int32, 0>,
|}>;

export default (codegenNativeComponent<NativeProps>('ModalHostView', {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ exports[`<Modal /> should render as <RCTModalHostView> when not mocked 1`] = `
<RCTModalHostView
animationType="none"
hardwareAccelerated={false}
identifier={3}
onDismiss={[Function]}
onStartShouldSetResponder={[Function]}
presentationStyle="fullScreen"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6449,6 +6449,15 @@ exports[`public API should not change unintentionally Libraries/Modal/ModalInjec
"
`;

exports[`public API should not change unintentionally Libraries/Modal/NativeModalManager.js 1`] = `
"export interface Spec extends TurboModule {
+addListener: (eventName: string) => void;
+removeListeners: (count: number) => void;
}
declare export default ?Spec;
"
`;

exports[`public API should not change unintentionally Libraries/Modal/RCTModalHostViewNativeComponent.js 1`] = `
"type OrientationChangeEvent = $ReadOnly<{|
orientation: \\"portrait\\" | \\"landscape\\",
Expand Down Expand Up @@ -6479,6 +6488,7 @@ type NativeProps = $ReadOnly<{|
\\"portrait\\",
>,
onOrientationChange?: ?DirectEventHandler<OrientationChangeEvent>,
identifier?: WithDefault<Int32, 0>,
|}>;
declare export default HostComponent<NativeProps>;
"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#import "RCTModalHostViewComponentView.h"

#import <React/RCTBridge+Private.h>
#import <React/RCTModalManager.h>
#import <React/UIView+React.h>
#import <react/renderer/components/modal/ModalHostViewComponentDescriptor.h>
#import <react/renderer/components/modal/ModalHostViewState.h>
Expand Down
6 changes: 5 additions & 1 deletion packages/react-native/React/Views/RCTModalHostView.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,23 @@
@property (nonatomic, assign, getter=isTransparent) BOOL transparent;

@property (nonatomic, copy) RCTDirectEventBlock onShow;
@property (nonatomic, copy) RCTDirectEventBlock onDismiss;
@property (nonatomic, assign) BOOL visible;

// Android only
@property (nonatomic, assign) BOOL statusBarTranslucent;
@property (nonatomic, assign) BOOL hardwareAccelerated;
@property (nonatomic, assign) BOOL animated;

@property (nonatomic, copy) NSNumber *identifier;

@property (nonatomic, weak) id<RCTModalHostViewInteractor> delegate;

@property (nonatomic, copy) NSArray<NSString *> *supportedOrientations;
@property (nonatomic, copy) RCTDirectEventBlock onOrientationChange;

// Fabric only
@property (nonatomic, copy) RCTDirectEventBlock onDismiss;

- (instancetype)initWithBridge:(RCTBridge *)bridge NS_DESIGNATED_INITIALIZER;

@end
Expand Down
10 changes: 7 additions & 3 deletions packages/react-native/React/Views/RCTModalHostViewManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#import "RCTBridge.h"
#import "RCTModalHostView.h"
#import "RCTModalHostViewController.h"
#import "RCTModalManager.h"
#import "RCTShadowView.h"
#import "RCTUtils.h"

Expand Down Expand Up @@ -90,8 +91,8 @@ - (void)dismissModalHostView:(RCTModalHostView *)modalHostView
animated:(BOOL)animated
{
dispatch_block_t completionBlock = ^{
if (modalHostView.onDismiss) {
modalHostView.onDismiss(nil);
if (modalHostView.identifier) {
[[self.bridge moduleForClass:[RCTModalManager class]] modalDismissed:modalHostView.identifier];
}
};
dispatch_async(dispatch_get_main_queue(), ^{
Expand Down Expand Up @@ -123,10 +124,13 @@ - (void)invalidate
RCT_EXPORT_VIEW_PROPERTY(hardwareAccelerated, BOOL)
RCT_EXPORT_VIEW_PROPERTY(animated, BOOL)
RCT_EXPORT_VIEW_PROPERTY(onShow, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onDismiss, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(identifier, NSNumber)
RCT_EXPORT_VIEW_PROPERTY(supportedOrientations, NSArray)
RCT_EXPORT_VIEW_PROPERTY(onOrientationChange, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(visible, BOOL)
RCT_EXPORT_VIEW_PROPERTY(onRequestClose, RCTDirectEventBlock)

// Fabric only
RCT_EXPORT_VIEW_PROPERTY(onDismiss, RCTDirectEventBlock)

@end
17 changes: 17 additions & 0 deletions packages/react-native/React/Views/RCTModalManager.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*
* 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.
*/

#import <UIKit/UIKit.h>

#import <React/RCTBridgeModule.h>
#import <React/RCTEventEmitter.h>

@interface RCTModalManager : RCTEventEmitter <RCTBridgeModule>

- (void)modalDismissed:(NSNumber *)modalID;

@end
42 changes: 42 additions & 0 deletions packages/react-native/React/Views/RCTModalManager.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* 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.
*/

#import "RCTModalManager.h"

@interface RCTModalManager ()

@property BOOL shouldEmit;

@end

@implementation RCTModalManager

RCT_EXPORT_MODULE();

- (NSArray<NSString *> *)supportedEvents
{
return @[ @"modalDismissed" ];
}

- (void)startObserving
{
_shouldEmit = YES;
}

- (void)stopObserving
{
_shouldEmit = NO;
}

- (void)modalDismissed:(NSNumber *)modalID
{
if (_shouldEmit) {
[self sendEventWithName:@"modalDismissed" body:@{@"modalID" : modalID}];
}
}

@end
6 changes: 3 additions & 3 deletions packages/react-native/ReactAndroid/api/ReactAndroid.api
Original file line number Diff line number Diff line change
Expand Up @@ -5776,6 +5776,7 @@ public abstract interface class com/facebook/react/viewmanagers/ModalHostViewMan
public abstract fun setAnimated (Landroid/view/View;Z)V
public abstract fun setAnimationType (Landroid/view/View;Ljava/lang/String;)V
public abstract fun setHardwareAccelerated (Landroid/view/View;Z)V
public abstract fun setIdentifier (Landroid/view/View;I)V
public abstract fun setPresentationStyle (Landroid/view/View;Ljava/lang/String;)V
public abstract fun setStatusBarTranslucent (Landroid/view/View;Z)V
public abstract fun setSupportedOrientations (Landroid/view/View;Lcom/facebook/react/bridge/ReadableArray;)V
Expand Down Expand Up @@ -6118,6 +6119,8 @@ public class com/facebook/react/views/modal/ReactModalHostManager : com/facebook
public fun setAnimationType (Lcom/facebook/react/views/modal/ReactModalHostView;Ljava/lang/String;)V
public synthetic fun setHardwareAccelerated (Landroid/view/View;Z)V
public fun setHardwareAccelerated (Lcom/facebook/react/views/modal/ReactModalHostView;Z)V
public synthetic fun setIdentifier (Landroid/view/View;I)V
public fun setIdentifier (Lcom/facebook/react/views/modal/ReactModalHostView;I)V
public synthetic fun setPresentationStyle (Landroid/view/View;Ljava/lang/String;)V
public fun setPresentationStyle (Lcom/facebook/react/views/modal/ReactModalHostView;Ljava/lang/String;)V
public synthetic fun setStatusBarTranslucent (Landroid/view/View;Z)V
Expand Down Expand Up @@ -6158,14 +6161,11 @@ public class com/facebook/react/views/modal/ReactModalHostView : android/view/Vi
public fun removeViewAt (I)V
protected fun setAnimationType (Ljava/lang/String;)V
protected fun setHardwareAccelerated (Z)V
protected fun setOnDismissListener (Landroid/content/DialogInterface$OnDismissListener;)V
protected fun setOnRequestCloseListener (Lcom/facebook/react/views/modal/ReactModalHostView$OnRequestCloseListener;)V
protected fun setOnShowListener (Landroid/content/DialogInterface$OnShowListener;)V
public fun setStateWrapper (Lcom/facebook/react/uimanager/StateWrapper;)V
protected fun setStatusBarTranslucent (Z)V
protected fun setTransparent (Z)V
protected fun setVisible (Z)V
protected fun showOrDismiss ()V
protected fun showOrUpdate ()V
public fun updateState (II)V
}
Expand Down
Loading

0 comments on commit a58ec07

Please sign in to comment.