Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[iOS] Fix cannot working Modal's onDismiss. #29882

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 23 additions & 2 deletions Libraries/Modal/Modal.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@

const AppContainer = require('../ReactNative/AppContainer');
const I18nManager = require('../ReactNative/I18nManager');
import NativeEventEmitter from '../EventEmitter/NativeEventEmitter';
import NativeModalManager from './NativeModalManager';
const Platform = require('../Utilities/Platform');
const React = require('react');
const ScrollView = require('../Components/ScrollView/ScrollView');
const StyleSheet = require('../StyleSheet/StyleSheet');
Expand All @@ -26,6 +29,11 @@ import type {DirectEventHandler} from '../Types/CodegenTypes';
import {type EventSubscription} from '../vendor/emitter/EventEmitter';
import RCTModalHostView from './RCTModalHostViewNativeComponent';

const ModalEventEmitter =
Platform.OS === 'ios' && NativeModalManager != null
? new NativeEventEmitter(NativeModalManager)
: null;

/**
* The Modal component is a simple way to present content above an enclosing view.
*
Expand Down Expand Up @@ -161,9 +169,22 @@ class Modal extends React.Component<Props> {
this._identifier = uniqueModalIdentifier++;
}

componentDidMount() {
if (ModalEventEmitter) {
this._eventSubscription = ModalEventEmitter.addListener(
'modalDismissed',
event => {
if (event.modalID === this._identifier && this.props.onDismiss) {
this.props.onDismiss();
}
},
);
}
}

componentWillUnmount() {
if (this.props.onDismiss != null) {
this.props.onDismiss();
if (this._eventSubscription) {
this._eventSubscription.remove();
}
}

Expand Down
9 changes: 9 additions & 0 deletions Libraries/Modal/RCTModalHostViewNativeComponent.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import type {HostComponent} from '../Renderer/shims/ReactNativeTypes';
import type {
WithDefault,
DirectEventHandler,
BubblingEventHandler,
Int32,
} from '../Types/CodegenTypes';

Expand Down Expand Up @@ -86,6 +87,14 @@ type NativeProps = $ReadOnly<{|
*/
onShow?: ?DirectEventHandler<null>,

/**
* The `onDismiss` prop allows passing a function that will be called once
* the modal has been dismissed.
*
* See https://reactnative.dev/docs/modal.html#ondismiss
*/
onDismiss?: ?BubblingEventHandler<null>,

/**
* Deprecated. Use the `animationType` prop instead.
*/
Expand Down
27 changes: 9 additions & 18 deletions 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 @@ -48,8 +49,6 @@ - (void)insertReactSubview:(id<RCTComponent>)subview atIndex:(NSInteger)atIndex

@interface RCTModalHostViewManager () <RCTModalHostViewInteractor>

@property (nonatomic, copy) dispatch_block_t dismissWaitingBlock;

@end

@implementation RCTModalHostViewManager {
Expand Down Expand Up @@ -81,33 +80,25 @@ - (void)presentModalHostView:(RCTModalHostView *)modalHostView
if (_presentationBlock) {
_presentationBlock([modalHostView reactViewController], viewController, animated, completionBlock);
} else {
__weak typeof(self) weakself = self;
[[modalHostView reactViewController] presentViewController:viewController
animated:animated
completion:^{
!completionBlock ?: completionBlock();
__strong typeof(weakself) strongself = weakself;
!strongself.dismissWaitingBlock
?: strongself.dismissWaitingBlock();
strongself.dismissWaitingBlock = nil;
}];
completion:completionBlock];
}
}

- (void)dismissModalHostView:(RCTModalHostView *)modalHostView
withViewController:(RCTModalHostViewController *)viewController
animated:(BOOL)animated
{
dispatch_block_t completionBlock = ^{
if (modalHostView.identifier) {
[[self.bridge moduleForClass:[RCTModalManager class]] modalDismissed:modalHostView.identifier];
}
};
if (_dismissalBlock) {
_dismissalBlock([modalHostView reactViewController], viewController, animated, nil);
_dismissalBlock([modalHostView reactViewController], viewController, animated, completionBlock);
} else {
self.dismissWaitingBlock = ^{
[viewController.presentingViewController dismissViewControllerAnimated:animated completion:nil];
};
if (viewController.presentingViewController) {
self.dismissWaitingBlock();
self.dismissWaitingBlock = nil;
}
[viewController.presentingViewController dismissViewControllerAnimated:animated completion:completionBlock];
}
}

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

#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 React/Views/RCTModalManager.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/**
* 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.
*/

#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