浏览代码

Handle rejections - pop and dismissModal (#4168)

* Handle pop and dismissModal rejections - #3914

* Added e2e

* Update TextScreen.js
Yogev Ben David 6 年前
父节点
当前提交
55ef525f6a
没有帐户链接到提交者的电子邮件

+ 7
- 0
e2e/Modals.test.js 查看文件

@@ -131,4 +131,11 @@ describe('modal', () => {
131 131
     await elementById(testIDs.DISMISS_MODAL_BUTTON).tap();
132 132
     await expect(elementByLabel('Modal Stack Position: 1')).toBeVisible();
133 133
   });
134
+
135
+  test('dismiss modal on non-modal component should not deatch component', async () => {
136
+    await elementById(testIDs.TAB_BASED_APP_BUTTON).tap();
137
+    await elementById(testIDs.DISMISS_MODAL_BUTTON).tap();
138
+    await elementById(testIDs.PUSH_BUTTON).tap();
139
+    await expect(elementById(testIDs.PUSHED_SCREEN_HEADER)).toBeVisible();
140
+  });
134 141
 });

+ 7
- 0
e2e/ScreenStack.test.js 查看文件

@@ -96,4 +96,11 @@ describe('screen stack', () => {
96 96
     await elementById(testIDs.POP_BUTTON).tap();
97 97
     await expect(elementByLabel('This is a side menu center screen tab 1')).toBeVisible();
98 98
   });
99
+
100
+  test('pop component should not deatch component if can`t pop', async () => {
101
+    await elementById(testIDs.TAB_BASED_APP_BUTTON).tap();
102
+    await elementById(testIDs.POP_BUTTON).tap();
103
+    await elementById(testIDs.PUSH_BUTTON).tap();
104
+    await expect(elementById(testIDs.PUSHED_SCREEN_HEADER)).toBeVisible();
105
+  });
99 106
 });

+ 1
- 1
lib/ios/RNNBridgeModule.m 查看文件

@@ -76,7 +76,7 @@ RCT_EXPORT_METHOD(showModal:(NSString*)commandId layout:(NSDictionary*)layout re
76 76
 RCT_EXPORT_METHOD(dismissModal:(NSString*)commandId componentId:(NSString*)componentId mergeOptions:(NSDictionary*)options resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
77 77
 	[_commandsHandler dismissModal:componentId mergeOptions:options completion:^{
78 78
 		resolve(componentId);
79
-	}];
79
+	} rejection:reject];
80 80
 }
81 81
 
82 82
 RCT_EXPORT_METHOD(dismissAllModals:(NSString*)commandId mergeOptions:(NSDictionary*)options resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {

+ 1
- 1
lib/ios/RNNCommandsHandler.h 查看文件

@@ -26,7 +26,7 @@
26 26
 
27 27
 - (void)showModal:(NSDictionary*)layout completion:(RNNTransitionWithComponentIdCompletionBlock)completion;
28 28
 
29
-- (void)dismissModal:(NSString*)componentId mergeOptions:(NSDictionary*)options completion:(RNNTransitionCompletionBlock)completion;
29
+- (void)dismissModal:(NSString*)componentId mergeOptions:(NSDictionary*)options completion:(RNNTransitionCompletionBlock)completion rejection:(RNNTransitionRejectionBlock)reject;
30 30
 
31 31
 - (void)dismissAllModals:(NSDictionary *)options completion:(RNNTransitionCompletionBlock)completion;
32 32
 

+ 14
- 8
lib/ios/RNNCommandsHandler.m 查看文件

@@ -9,6 +9,7 @@
9 9
 #import "React/RCTUIManager.h"
10 10
 #import "RNNErrorHandler.h"
11 11
 #import "RNNDefaultOptionsHelper.h"
12
+#import "UIViewController+RNNOptions.h"
12 13
 
13 14
 static NSString* const setRoot	= @"setRoot";
14 15
 static NSString* const setStackRoot	= @"setStackRoot";
@@ -188,9 +189,7 @@ static NSString* const setDefaultOptions	= @"setDefaultOptions";
188 189
 		[_store removeComponent:componentId];
189 190
 		[_eventEmitter sendOnNavigationCommandCompletion:pop params:@{@"componentId": componentId}];
190 191
 		completion();
191
-	} rejection:^(NSString *code, NSString *message, NSError *error) {
192
-		
193
-	}];
192
+	} rejection:rejection];
194 193
 }
195 194
 
196 195
 - (void)popTo:(NSString*)componentId mergeOptions:(NSDictionary *)mergeOptions completion:(RNNTransitionCompletionBlock)completion rejection:(RCTPromiseRejectBlock)rejection {
@@ -240,19 +239,26 @@ static NSString* const setDefaultOptions	= @"setDefaultOptions";
240 239
 	}];
241 240
 }
242 241
 
243
-- (void)dismissModal:(NSString*)componentId mergeOptions:(NSDictionary *)mergeOptions completion:(RNNTransitionCompletionBlock)completion {
242
+- (void)dismissModal:(NSString*)componentId mergeOptions:(NSDictionary *)mergeOptions completion:(RNNTransitionCompletionBlock)completion rejection:(RNNTransitionRejectionBlock)reject {
244 243
 	[self assertReady];
245 244
 	
246
-	[CATransaction begin];
247
-	[CATransaction setCompletionBlock:^{
248
-		[_eventEmitter sendOnNavigationCommandCompletion:dismissModal params:@{@"componentId": componentId}];
249
-	}];
250 245
 	UIViewController<RNNParentProtocol> *modalToDismiss = (UIViewController<RNNParentProtocol>*)[_store findComponentForId:componentId];
246
+	
247
+	if (!modalToDismiss.isModal) {
248
+		[RNNErrorHandler reject:reject withErrorCode:1013 errorDescription:@"component is not a modal"];
249
+		return;
250
+	}
251
+	
251 252
 	RNNNavigationOptions *options = [[RNNNavigationOptions alloc] initWithDict:mergeOptions];
252 253
 	[modalToDismiss.getCurrentChild.options overrideOptions:options];
253 254
 	
254 255
 	[self removePopedViewControllers:modalToDismiss.navigationController.viewControllers];
255 256
 	
257
+	[CATransaction begin];
258
+	[CATransaction setCompletionBlock:^{
259
+		[_eventEmitter sendOnNavigationCommandCompletion:dismissModal params:@{@"componentId": componentId}];
260
+	}];
261
+	
256 262
 	[_modalManager dismissModal:modalToDismiss completion:completion];
257 263
 	
258 264
 	[CATransaction commit];

+ 9
- 2
lib/ios/RNNNavigationStackManager.m 查看文件

@@ -25,9 +25,16 @@ typedef void (^RNNAnimationBlock)(void);
25 25
 		animated = NO;
26 26
 	}
27 27
 	
28
+	__block UIViewController *poppedVC = nil;
28 29
 	[self performAnimationBlock:^{
29
-		[viewController.navigationController popViewControllerAnimated:animated];
30
-	} completion:completion];
30
+		poppedVC = [viewController.navigationController popViewControllerAnimated:animated];
31
+	} completion:^{
32
+		if (poppedVC) {
33
+			completion();
34
+		} else {
35
+			[RNNErrorHandler reject:rejection withErrorCode:1012 errorDescription:@"popping component failed"];
36
+		}
37
+	}];
31 38
 }
32 39
 
33 40
 - (void)popTo:(UIViewController *)viewController animated:(BOOL)animated completion:(RNNPopCompletionBlock)completion rejection:(RNNTransitionRejectionBlock)rejection; {

+ 2
- 0
lib/ios/UIViewController+RNNOptions.h 查看文件

@@ -32,4 +32,6 @@
32 32
 
33 33
 - (void)rnn_setBackgroundColor:(UIColor *)backgroundColor;
34 34
 
35
+- (BOOL)isModal;
36
+
35 37
 @end

+ 11
- 0
lib/ios/UIViewController+RNNOptions.m 查看文件

@@ -134,5 +134,16 @@ const NSInteger BLUR_STATUS_TAG = 78264801;
134 134
 	return animated ? kStatusBarAnimationDuration : CGFLOAT_MIN;
135 135
 }
136 136
 
137
+- (BOOL)isModal {
138
+	if([self presentingViewController])
139
+		return YES;
140
+	if([[[self navigationController] presentingViewController] presentedViewController] == [self navigationController])
141
+		return YES;
142
+	if([[[self tabBarController] presentingViewController] isKindOfClass:[UITabBarController class]])
143
+		return YES;
144
+	
145
+	return NO;
146
+}
147
+
137 148
 
138 149
 @end

+ 5
- 0
playground/src/screens/TextScreen.js 查看文件

@@ -37,6 +37,7 @@ class TextScreen extends Component {
37 37
           <Button title='Show Right Side Menu' testID={testIDs.SHOW_RIGHT_SIDE_MENU_BUTTON} onPress={() => this.showSideMenu('right')} />
38 38
           <Button title='Push' testID={testIDs.PUSH_BUTTON} onPress={this.onClickPush} />
39 39
           <Button title='Pop' testID={testIDs.POP_BUTTON} onPress={this.onClickPop} />
40
+          <Button title='Dismiss modal' testID={testIDs.DISMISS_MODAL_BUTTON} onPress={this.onClickDismissModal} />
40 41
         </View>
41 42
       </Bounds>
42 43
     );
@@ -58,6 +59,10 @@ class TextScreen extends Component {
58 59
     });
59 60
   }
60 61
 
62
+  onClickDismissModal = () => {
63
+    Navigation.dismissModal(this.props.componentId);
64
+  }
65
+
61 66
   onClickPop = async () => {
62 67
     await Navigation.pop(this.props.componentId);
63 68
   }