Procházet zdrojové kódy

Fix commands handling on overlays (#4239)

Yogev Ben David před 6 roky
rodič
revize
dafe1e076b
No account linked to committer's email address

+ 1
- 5
lib/ios/RNNBridgeManager.m Zobrazit soubor

@@ -23,7 +23,6 @@
23 23
 	RCTBridge* _bridge;
24 24
 
25 25
 	RNNStore* _store;
26
-	RNNStore* _overlayStore;
27 26
 
28 27
 	RNNCommandsHandler* _commandsHandler;
29 28
 }
@@ -35,7 +34,6 @@
35 34
 		_delegate = delegate;
36 35
 		
37 36
 		_store = [RNNStore new];
38
-		_overlayStore = [RNNStore new];
39 37
 		_bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:_launchOptions];
40 38
 
41 39
 		[[NSNotificationCenter defaultCenter] addObserver:self
@@ -78,7 +76,7 @@
78 76
 	id<RNNRootViewCreator> rootViewCreator = [[RNNReactRootViewCreator alloc] initWithBridge:bridge];
79 77
 	RNNControllerFactory *controllerFactory = [[RNNControllerFactory alloc] initWithRootViewCreator:rootViewCreator eventEmitter:eventEmitter andBridge:bridge];
80 78
 	
81
-	_commandsHandler = [[RNNCommandsHandler alloc] initWithStore:_store overlayStore:_overlayStore controllerFactory:controllerFactory eventEmitter:eventEmitter stackManager:[RNNNavigationStackManager new] modalManager:[RNNModalManager new] overlayManager:[RNNOverlayManager new]];
79
+	_commandsHandler = [[RNNCommandsHandler alloc] initWithStore:_store controllerFactory:controllerFactory eventEmitter:eventEmitter stackManager:[RNNNavigationStackManager new] modalManager:[RNNModalManager new] overlayManager:[RNNOverlayManager new]];
82 80
 	RNNBridgeModule *bridgeModule = [[RNNBridgeModule alloc] initWithCommandsHandler:_commandsHandler];
83 81
 
84 82
 	return [@[bridgeModule,eventEmitter] arrayByAddingObjectsFromArray:[self extraModulesFromDelegate]];
@@ -88,12 +86,10 @@
88 86
 
89 87
 - (void)onJavaScriptWillLoad {
90 88
 	[_store clean];
91
-	[_overlayStore clean];
92 89
 }
93 90
 
94 91
 - (void)onJavaScriptLoaded {
95 92
 	[_store setReadyToReceiveCommands:true];
96
-	[_overlayStore setReadyToReceiveCommands:true];
97 93
 	[[_bridge moduleForClass:[RNNEventEmitter class]] sendOnAppLaunched];
98 94
 }
99 95
 

+ 1
- 1
lib/ios/RNNCommandsHandler.h Zobrazit soubor

@@ -9,7 +9,7 @@
9 9
 
10 10
 @interface RNNCommandsHandler : NSObject
11 11
 
12
-- (instancetype)initWithStore:(RNNStore*)store overlayStore:(RNNStore*)overlayStore controllerFactory:(RNNControllerFactory*)controllerFactory eventEmitter:(RNNEventEmitter *)eventEmitter stackManager:(RNNNavigationStackManager *)stackManager modalManager:(RNNModalManager *)modalManager overlayManager:(RNNOverlayManager *)overlayManager;
12
+- (instancetype)initWithStore:(RNNStore*)store controllerFactory:(RNNControllerFactory*)controllerFactory eventEmitter:(RNNEventEmitter *)eventEmitter stackManager:(RNNNavigationStackManager *)stackManager modalManager:(RNNModalManager *)modalManager overlayManager:(RNNOverlayManager *)overlayManager;
13 13
 
14 14
 - (void)setRoot:(NSDictionary*)layout completion:(RNNTransitionCompletionBlock)completion;
15 15
 

+ 4
- 6
lib/ios/RNNCommandsHandler.m Zobrazit soubor

@@ -30,17 +30,15 @@ static NSString* const setDefaultOptions	= @"setDefaultOptions";
30 30
 @implementation RNNCommandsHandler {
31 31
 	RNNControllerFactory *_controllerFactory;
32 32
 	RNNStore *_store;
33
-	RNNStore *_overlayStore;
34 33
 	RNNModalManager* _modalManager;
35 34
 	RNNOverlayManager* _overlayManager;
36 35
 	RNNNavigationStackManager* _stackManager;
37 36
 	RNNEventEmitter* _eventEmitter;
38 37
 }
39 38
 
40
-- (instancetype)initWithStore:(RNNStore*)store overlayStore:(RNNStore*)overlayStore controllerFactory:(RNNControllerFactory*)controllerFactory eventEmitter:(RNNEventEmitter *)eventEmitter stackManager:(RNNNavigationStackManager *)stackManager modalManager:(RNNModalManager *)modalManager overlayManager:(RNNOverlayManager *)overlayManager {
39
+- (instancetype)initWithStore:(RNNStore*)store controllerFactory:(RNNControllerFactory*)controllerFactory eventEmitter:(RNNEventEmitter *)eventEmitter stackManager:(RNNNavigationStackManager *)stackManager modalManager:(RNNModalManager *)modalManager overlayManager:(RNNOverlayManager *)overlayManager {
41 40
 	self = [super init];
42 41
 	_store = store;
43
-	_overlayStore = overlayStore;
44 42
 	_controllerFactory = controllerFactory;
45 43
 	_eventEmitter = eventEmitter;
46 44
 	_modalManager = modalManager;
@@ -56,7 +54,7 @@ static NSString* const setDefaultOptions	= @"setDefaultOptions";
56 54
 	[self assertReady];
57 55
 	
58 56
 	[_modalManager dismissAllModalsAnimated:NO];
59
-	[_store removeAllComponents];
57
+	[_store removeAllComponentsFromWindow:UIApplication.sharedApplication.delegate.window];
60 58
 	
61 59
 	UIViewController *vc = [_controllerFactory createLayout:layout[@"root"] saveToStore:_store];
62 60
 	
@@ -280,7 +278,7 @@ static NSString* const setDefaultOptions	= @"setDefaultOptions";
280 278
 - (void)showOverlay:(NSDictionary *)layout completion:(RNNTransitionCompletionBlock)completion {
281 279
 	[self assertReady];
282 280
 	
283
-	UIViewController<RNNParentProtocol>* overlayVC = [_controllerFactory createLayout:layout saveToStore:_overlayStore];
281
+	UIViewController<RNNParentProtocol>* overlayVC = [_controllerFactory createLayout:layout saveToStore:_store];
284 282
 	[_overlayManager showOverlay:overlayVC];
285 283
 	[_eventEmitter sendOnNavigationCommandCompletion:showOverlay params:@{@"layout": layout}];
286 284
 	completion();
@@ -288,7 +286,7 @@ static NSString* const setDefaultOptions	= @"setDefaultOptions";
288 286
 
289 287
 - (void)dismissOverlay:(NSString*)componentId completion:(RNNTransitionCompletionBlock)completion rejection:(RNNTransitionRejectionBlock)reject {
290 288
 	[self assertReady];
291
-	UIViewController* viewController = [_overlayStore findComponentForId:componentId];
289
+	UIViewController* viewController = [_store findComponentForId:componentId];
292 290
 	if (viewController) {
293 291
 		[_overlayManager dismissOverlay:viewController];
294 292
 		[_eventEmitter sendOnNavigationCommandCompletion:dismissOverlay params:@{@"componentId": componentId}];

+ 1
- 1
lib/ios/RNNStore.h Zobrazit soubor

@@ -15,7 +15,7 @@ typedef void (^RNNTransitionRejectionBlock)(NSString *code, NSString *message, N
15 15
 - (void)removeComponent:(NSString*)componentId;
16 16
 - (void)removeComponentByViewControllerInstance:(UIViewController*)componentInstance;
17 17
 - (void)removeAllComponents;
18
-
18
+- (void)removeAllComponentsFromWindow:(UIWindow *)window;
19 19
 - (void)registerExternalComponent:(NSString *)name callback:(RNNExternalViewCreator)callback;
20 20
 - (UIViewController *)getExternalComponent:(RNNLayoutInfo *)layoutInfo bridge:(RCTBridge *)bridge;
21 21
 

+ 17
- 1
lib/ios/RNNStore.m Zobrazit soubor

@@ -1,4 +1,3 @@
1
-
2 1
 #import "RNNStore.h"
3 2
 
4 3
 @interface RNNStore ()
@@ -47,6 +46,23 @@
47 46
 	[_componentStore removeAllObjects];
48 47
 }
49 48
 
49
+- (void)removeAllComponentsFromWindow:(UIWindow *)window {
50
+	for (NSString *key in [self componentsForWindow:window]) {
51
+		[_componentStore removeObjectForKey:key];
52
+	}
53
+}
54
+
55
+- (NSArray *)componentsForWindow:(UIWindow *)window {
56
+	NSMutableArray* keyWindowComponents = [NSMutableArray new];
57
+	for (NSString* key in _componentStore) {
58
+		UIViewController *component = [_componentStore objectForKey:key];
59
+		if (component.view.window == window) {
60
+			[keyWindowComponents addObject:key];
61
+		}
62
+	}
63
+	
64
+	return keyWindowComponents;
65
+}
50 66
 
51 67
 -(void)setReadyToReceiveCommands:(BOOL)isReady {
52 68
 	_isReadyToReceiveCommands = isReady;

+ 12
- 15
lib/ios/ReactNativeNavigationTests/RNNCommandsHandlerTest.m Zobrazit soubor

@@ -26,8 +26,7 @@
26 26
 
27 27
 @interface RNNCommandsHandlerTest : XCTestCase
28 28
 
29
-@property (nonatomic, strong) RNNStore* store;
30
-@property (nonatomic, strong) id overlayStore;
29
+@property (nonatomic, strong) id store;
31 30
 @property (nonatomic, strong) RNNCommandsHandler* uut;
32 31
 @property (nonatomic, strong) RNNRootViewController* vc1;
33 32
 @property (nonatomic, strong) RNNRootViewController* vc2;
@@ -43,13 +42,11 @@
43 42
 
44 43
 - (void)setUp {
45 44
 	[super setUp];
46
-//	[self.store setReadyToReceiveCommands:true];
47
-	self.store = [[RNNStore alloc] init];
48
-	self.overlayStore = [OCMockObject partialMockForObject:[[RNNStore alloc] init]];
45
+	self.store = [OCMockObject partialMockForObject:[[RNNStore alloc] init]];
49 46
 	self.eventEmmiter = [OCMockObject partialMockForObject:[RNNEventEmitter new]];
50 47
 	self.overlayManager = [OCMockObject partialMockForObject:[RNNOverlayManager new]];
51 48
 	self.controllerFactory = [OCMockObject partialMockForObject:[[RNNControllerFactory alloc] initWithRootViewCreator:nil eventEmitter:self.eventEmmiter andBridge:nil]];
52
-	self.uut = [[RNNCommandsHandler alloc] initWithStore:self.store overlayStore:self.overlayStore controllerFactory:self.controllerFactory eventEmitter:self.eventEmmiter stackManager:[RNNNavigationStackManager new] modalManager:[RNNModalManager new] overlayManager:self.overlayManager];
49
+	self.uut = [[RNNCommandsHandler alloc] initWithStore:self.store controllerFactory:self.controllerFactory eventEmitter:self.eventEmmiter stackManager:[RNNNavigationStackManager new] modalManager:[RNNModalManager new] overlayManager:self.overlayManager];
53 50
 	self.vc1 = [RNNRootViewController new];
54 51
 	self.vc2 = [RNNRootViewController new];
55 52
 	self.vc3 = [RNNRootViewController new];
@@ -76,7 +73,7 @@
76 73
 -(NSArray*) getPublicMethodNamesForObject:(NSObject*)obj{
77 74
 	NSMutableArray* skipMethods = [NSMutableArray new];
78 75
 
79
-	[skipMethods addObject:@"initWithStore:overlayStore:controllerFactory:eventEmitter:stackManager:modalManager:overlayManager:"];
76
+	[skipMethods addObject:@"initWithStore:controllerFactory:eventEmitter:stackManager:modalManager:overlayManager:"];
80 77
 	[skipMethods addObject:@"assertReady"];
81 78
 	[skipMethods addObject:@"removePopedViewControllers:"];
82 79
 	[skipMethods addObject:@".cxx_destruct"];
@@ -198,17 +195,17 @@
198 195
 	OCMStub([self.overlayManager showOverlay:[OCMArg any]]);
199 196
 	NSDictionary* layout = @{};
200 197
 	
201
-	[[self.controllerFactory expect] createLayout:layout saveToStore:self.overlayStore];
198
+	[[self.controllerFactory expect] createLayout:layout saveToStore:self.store];
202 199
 	[self.uut showOverlay:layout completion:^{}];
203 200
 	[self.controllerFactory verify];
204 201
 }
205 202
 
206
-- (void)testShowOverlay_saveToOverlayStore {
203
+- (void)testShowOverlay_saveToStore {
207 204
 	[self.store setReadyToReceiveCommands:true];
208 205
 	OCMStub([self.overlayManager showOverlay:[OCMArg any]]);
209 206
 	OCMStub([self.controllerFactory createLayout:[OCMArg any] saveToStore:[OCMArg any]]);
210 207
 	
211
-	[[self.controllerFactory expect] createLayout:[OCMArg any] saveToStore:self.overlayStore];
208
+	[[self.controllerFactory expect] createLayout:[OCMArg any] saveToStore:self.store];
212 209
 	[self.uut showOverlay:@{} completion:^{}];
213 210
 	[self.overlayManager verify];
214 211
 }
@@ -235,19 +232,19 @@
235 232
 	[self.eventEmmiter verify];
236 233
 }
237 234
 
238
-- (void)testDismissOverlay_findComponentFromOverlayStore {
235
+- (void)testDismissOverlay_findComponentFromStore {
239 236
 	[self.store setReadyToReceiveCommands:true];
240 237
 	NSString* componentId = @"componentId";
241
-	[[self.overlayStore expect] findComponentForId:componentId];
238
+	[[self.store expect] findComponentForId:componentId];
242 239
 	[self.uut dismissOverlay:componentId completion:^{} rejection:^(NSString *code, NSString *message, NSError *error) {}];
243
-	[self.overlayStore verify];
240
+	[self.store verify];
244 241
 }
245 242
 
246 243
 - (void)testDismissOverlay_dismissReturnedViewController {
247 244
 	[self.store setReadyToReceiveCommands:true];
248 245
 	NSString* componentId = @"componentId";
249 246
 	UIViewController* returnedView = [UIViewController new];
250
-	OCMStub([self.overlayStore findComponentForId:componentId]).andReturn(returnedView);
247
+	OCMStub([self.store findComponentForId:componentId]).andReturn(returnedView);
251 248
 	
252 249
 	[[self.overlayManager expect] dismissOverlay:returnedView];
253 250
 	[self.uut dismissOverlay:componentId completion:^{} rejection:^(NSString *code, NSString *message, NSError *error) {}];
@@ -267,7 +264,7 @@
267 264
 - (void)testDismissOverlay_invokeNavigationCommandEvent {
268 265
 	[self.store setReadyToReceiveCommands:true];
269 266
 	NSString* componentId = @"componentId";
270
-	OCMStub([self.overlayStore findComponentForId:componentId]).andReturn([UIViewController new]);
267
+	OCMStub([self.store findComponentForId:componentId]).andReturn([UIViewController new]);
271 268
 	
272 269
 	[[self.eventEmmiter expect] sendOnNavigationCommandCompletion:@"dismissOverlay" params:[OCMArg any]];
273 270
 	[self.uut dismissOverlay:componentId completion:^{

+ 35
- 3
lib/ios/ReactNativeNavigationTests/RNNStoreTest.m Zobrazit soubor

@@ -1,5 +1,6 @@
1 1
 
2 2
 #import <XCTest/XCTest.h>
3
+#import <OCMock/OCMock.h>
3 4
 #import "RNNStore.h"
4 5
 
5 6
 @interface RNNStoreTest : XCTestCase
@@ -67,7 +68,7 @@
67 68
 	XCTAssertNil([self.store findComponentForId:componentId1]);
68 69
 }
69 70
 
70
--(void)testPopWillRemoveVcFromStore {
71
+- (void)testPopWillRemoveVcFromStore {
71 72
 	NSString *vcId = @"cnt_vc_2";
72 73
 	
73 74
 	[self setComponentAndRelease:vcId];
@@ -77,7 +78,7 @@
77 78
 }
78 79
 
79 80
 
80
--(void)testRemoveComponentByInstance {
81
+- (void)testRemoveComponentByInstance {
81 82
 	NSString *componentId1 = @"cntId1";
82 83
 	UIViewController *vc1 = [UIViewController new];
83 84
 	
@@ -87,6 +88,29 @@
87 88
 	XCTAssertNil([self.store findComponentForId:@"cntId1"]);
88 89
 }
89 90
 
91
+- (void)testRemoveAllComponentsFromWindowShouldRemoveComponentsInWindow {
92
+	UIViewController* overlayVC = [self createMockedViewControllerWithWindow:[UIWindow new]];
93
+	
94
+	NSString* overlayId = @"overlayId";
95
+	[self.store setComponent:overlayVC componentId:overlayId];
96
+	[self.store removeAllComponentsFromWindow:overlayVC.view.window];
97
+	XCTAssertNil([self.store findComponentForId:overlayId]);
98
+}
99
+
100
+- (void)testRemoveAllComponentsFromWindowShouldNotRemoveComponentsInOtherWindows {
101
+	UIViewController* overlayVC = [self createMockedViewControllerWithWindow:[UIWindow new]];
102
+	UIViewController* componentVC = [self createMockedViewControllerWithWindow:[UIWindow new]];
103
+	
104
+	NSString* componentId = @"componentId";
105
+	NSString* overlayId = @"overlayId";
106
+	[self.store setComponent:overlayVC componentId:overlayId];
107
+	[self.store setComponent:componentVC componentId:componentId];
108
+	
109
+	[self.store removeAllComponentsFromWindow:componentVC.view.window];
110
+	XCTAssertNil([self.store findComponentForId:componentId]);
111
+	XCTAssertNotNil([self.store findComponentForId:overlayId]);
112
+}
113
+
90 114
 - (void)testGetExternalComponentShouldRetrunSavedComponent {
91 115
 	UIViewController* testVC = [UIViewController new];
92 116
 	RNNLayoutInfo* layoutInfo = [[RNNLayoutInfo alloc] init];
@@ -104,7 +128,7 @@
104 128
 	XCTAssertNil(result);
105 129
 }
106 130
 
107
--(void)testCleanStore {
131
+- (void)testCleanStore {
108 132
 	[self.store clean];
109 133
 	XCTAssertFalse(self.store.isReadyToReceiveCommands);
110 134
 }
@@ -121,6 +145,14 @@
121 145
 	}
122 146
 }
123 147
 
148
+- (UIViewController *)createMockedViewControllerWithWindow:(UIWindow *)window {
149
+	UIWindow* overlayWindow = [UIWindow new];
150
+	id mockedViewController = [OCMockObject partialMockForObject:[UIViewController new]];
151
+	id mockedView = [OCMockObject partialMockForObject:[UIView new]];
152
+	OCMStub([mockedView window]).andReturn(overlayWindow);
153
+	OCMStub([mockedViewController view]).andReturn(mockedView);
154
+	return mockedViewController;
155
+}
124 156
 
125 157
 
126 158
 @end

+ 9
- 0
playground/src/screens/CustomDialog.js Zobrazit soubor

@@ -19,6 +19,7 @@ class CustomDialog extends PureComponent {
19 19
         <Text style={styles.h1} testID={testIDs.DIALOG_HEADER}>Test view</Text>
20 20
         <Button title='OK' testID={testIDs.OK_BUTTON} onPress={() => this.onCLickOk()} />
21 21
         <Button title='Set Root' testID={testIDs.SET_ROOT_BUTTON} onPress={() => this.onCLickSetRoot()} />
22
+        <Button title='Set Intercept touch' testID={testIDs.SET_INTERCEPT_TOUCH} onPress={() => this.onCLickSetInterceptTouch()} />
22 23
       </View>
23 24
     );
24 25
   }
@@ -42,6 +43,14 @@ class CustomDialog extends PureComponent {
42 43
       }
43 44
     });
44 45
   }
46
+
47
+  onCLickSetInterceptTouch() {
48
+    Navigation.mergeOptions(this.props.componentId, {
49
+      overlay: {
50
+        interceptTouchOutside: false
51
+      }
52
+    });
53
+  }
45 54
 }
46 55
 
47 56
 const styles = {

+ 1
- 0
playground/src/testIDs.js Zobrazit soubor

@@ -76,6 +76,7 @@ module.exports = {
76 76
   SIDE_MENU_LAYOUT_INSIDE_BOTTOM_TAB: `SIDE_MENU_LAYOUT_INSIDE_BOTTOM_TAB`,
77 77
   OPEN_SIDE_MENU: `OPEN_SIDE_MENU`,
78 78
   SET_ROOT_BUTTON: `SET_ROOT_BUTTON`,
79
+  SET_INTERCEPT_TOUCH: `SET_INTERCEPT_TOUCH`,
79 80
   PUSH_BOTTOM_TABS_BUTTON: `PUSH_BOTTOM_TABS_BUTTON`,
80 81
   
81 82
   // Elements