ソースを参照

Currently visible screen (#1581)

* added method for getting the currently visible screen ID. Can be useful to decide if logic should be applied according to screen visibility, for example: set buttons/title dynamically, or even determine that you don’t need to show the same screen again if it’s the current one that’s showing

* Implement getCurrentlyVisibleScreenId on Android

Can be used statically:
Navigation.getCurrentlyVisibleScreenId() - returns the unique screen
instance id

With a navigator instance:
await this.props.navigator.screenIsCurrentlyVisible() - resolves a
promise that checks if the current screen is visible
Guy Carmeli 7 年 前
コミット
0c4d40bd23

+ 5
- 0
android/app/src/main/java/com/reactnativenavigation/bridge/NavigationReactModule.java ファイルの表示

@@ -263,4 +263,9 @@ public class NavigationReactModule extends ReactContextBaseJavaModule {
263 263
     public void isAppLaunched(Promise promise) {
264 264
         NavigationCommandsHandler.isAppLaunched(promise);
265 265
     }
266
+
267
+    @ReactMethod
268
+    public void getCurrentlyVisibleScreenId(Promise promise) {
269
+        NavigationCommandsHandler.getCurrentlyVisibleScreenId(promise);
270
+    }
266 271
 }

+ 4
- 0
android/app/src/main/java/com/reactnativenavigation/controllers/Modal.java ファイルの表示

@@ -96,6 +96,10 @@ public class Modal extends Dialog implements DialogInterface.OnDismissListener,
96 96
         layout.selectTopTabByTabIndex(screenInstanceId, index);
97 97
     }
98 98
 
99
+    String getCurrentlyVisibleScreenId() {
100
+        return layout.getCurrentlyVisibleScreenId();
101
+    }
102
+
99 103
     interface OnModalDismissedListener {
100 104
         void onModalDismissed(Modal modal);
101 105
     }

+ 4
- 0
android/app/src/main/java/com/reactnativenavigation/controllers/ModalController.java ファイルの表示

@@ -183,4 +183,8 @@ class ModalController implements ScreenStackContainer, Modal.OnModalDismissedLis
183 183
             modal.selectTopTabByScreen(screenInstanceId);
184 184
         }
185 185
     }
186
+
187
+    String getCurrentlyVisibleScreenId() {
188
+        return stack.peek().getCurrentlyVisibleScreenId();
189
+    }
186 190
 }

+ 5
- 2
android/app/src/main/java/com/reactnativenavigation/controllers/NavigationActivity.java ファイルの表示

@@ -392,8 +392,7 @@ public class NavigationActivity extends AppCompatActivity implements DefaultHard
392 392
     public void showContextualMenu(String screenInstanceId, ContextualMenuParams params, Callback onButtonClicked) {
393 393
         if (modalController.isShowing()) {
394 394
             modalController.showContextualMenu(screenInstanceId, params, onButtonClicked);
395
-        } else
396
-        {
395
+        } else {
397 396
             layout.showContextualMenu(screenInstanceId, params, onButtonClicked);
398 397
         }
399 398
     }
@@ -449,4 +448,8 @@ public class NavigationActivity extends AppCompatActivity implements DefaultHard
449 448
             mPermissionListener = null;
450 449
         }
451 450
     }
451
+
452
+    public String getCurrentlyVisibleScreenId() {
453
+        return modalController.isShowing() ? modalController.getCurrentlyVisibleScreenId() : layout.getCurrentlyVisibleScreenId();
454
+    }
452 455
 }

+ 18
- 0
android/app/src/main/java/com/reactnativenavigation/controllers/NavigationCommandsHandler.java ファイルの表示

@@ -3,8 +3,10 @@ package com.reactnativenavigation.controllers;
3 3
 import android.content.Intent;
4 4
 import android.os.Bundle;
5 5
 
6
+import com.facebook.react.bridge.Arguments;
6 7
 import com.facebook.react.bridge.Callback;
7 8
 import com.facebook.react.bridge.Promise;
9
+import com.facebook.react.bridge.WritableMap;
8 10
 import com.reactnativenavigation.NavigationApplication;
9 11
 import com.reactnativenavigation.params.ActivityParams;
10 12
 import com.reactnativenavigation.params.ContextualMenuParams;
@@ -540,4 +542,20 @@ public class NavigationCommandsHandler {
540 542
         final boolean isAppLaunched = SplashActivity.isResumed || NavigationActivity.currentActivity != null;
541 543
         promise.resolve(isAppLaunched);
542 544
     }
545
+
546
+    public static void getCurrentlyVisibleScreenId(final Promise promise) {
547
+        final NavigationActivity currentActivity = NavigationActivity.currentActivity;
548
+        if (currentActivity == null) {
549
+            promise.resolve("");
550
+            return;
551
+        }
552
+        NavigationApplication.instance.runOnMainThread(new Runnable() {
553
+            @Override
554
+            public void run() {
555
+                WritableMap map = Arguments.createMap();
556
+                map.putString("screenId", currentActivity.getCurrentlyVisibleScreenId());
557
+                promise.resolve(map);
558
+            }
559
+        });
560
+    }
543 561
 }

+ 5
- 0
android/app/src/main/java/com/reactnativenavigation/layouts/BottomTabsLayout.java ファイルの表示

@@ -211,6 +211,11 @@ public class BottomTabsLayout extends BaseLayout implements AHBottomNavigation.O
211 211
         }
212 212
     }
213 213
 
214
+    @Override
215
+    public String getCurrentlyVisibleScreenId() {
216
+        return getCurrentScreen().getScreenInstanceId();
217
+    }
218
+
214 219
     @Override
215 220
     public void selectTopTabByTabIndex(String screenInstanceId, int index) {
216 221
         for (int i = 0; i < bottomTabs.getItemsCount(); i++) {

+ 2
- 0
android/app/src/main/java/com/reactnativenavigation/layouts/Layout.java ファイルの表示

@@ -66,4 +66,6 @@ public interface Layout extends ScreenStackContainer {
66 66
     void selectTopTabByScreen(String screenInstanceId);
67 67
 
68 68
     void updateScreenStyle(String screenInstanceId, Bundle styleParams);
69
+
70
+    String getCurrentlyVisibleScreenId();
69 71
 }

+ 5
- 0
android/app/src/main/java/com/reactnativenavigation/layouts/SingleScreenLayout.java ファイルの表示

@@ -274,6 +274,11 @@ public class SingleScreenLayout extends BaseLayout {
274 274
         stack.updateScreenStyle(screenInstanceId, styleParams);
275 275
     }
276 276
 
277
+    @Override
278
+    public String getCurrentlyVisibleScreenId() {
279
+        return stack.peek().getScreenInstanceId();
280
+    }
281
+
277 282
     @Override
278 283
     public void showSlidingOverlay(final SlidingOverlayParams params) {
279 284
         slidingOverlaysQueue.add(new SlidingOverlay(this, params));

+ 1
- 0
ios/RCCManager.h ファイルの表示

@@ -15,6 +15,7 @@
15 15
 -(void)registerController:(UIViewController*)controller componentId:(NSString*)componentId componentType:(NSString*)componentType;
16 16
 -(id)getControllerWithId:(NSString*)componentId componentType:(NSString*)componentType;
17 17
 -(void)unregisterController:(UIViewController*)vc;
18
+-(NSString*) getIdForController:(UIViewController*)vc;
18 19
 
19 20
 -(void)clearModuleRegistry;
20 21
 

+ 27
- 0
ios/RCCManager.m ファイルの表示

@@ -1,4 +1,5 @@
1 1
 #import "RCCManager.h"
2
+#import "RCCViewController.h"
2 3
 #import <React/RCTBridge.h>
3 4
 #import <React/RCTRedBox.h>
4 5
 #import <Foundation/Foundation.h>
@@ -116,6 +117,32 @@
116 117
   return component;
117 118
 }
118 119
 
120
+-(NSString*) getIdForController:(UIViewController*)vc
121
+{
122
+  if([vc isKindOfClass:[RCCViewController class]])
123
+  {
124
+    NSString *controllerId = ((RCCViewController*)vc).controllerId;
125
+    if(controllerId != nil)
126
+    {
127
+      return controllerId;
128
+    }
129
+  }
130
+  
131
+  for (NSString *key in [self.modulesRegistry allKeys])
132
+  {
133
+    NSMutableDictionary *componentsDic = self.modulesRegistry[key];
134
+    for (NSString *componentID in [componentsDic allKeys])
135
+    {
136
+      UIViewController *tmpVc = componentsDic[componentID];
137
+      if (tmpVc == vc)
138
+      {
139
+        return componentID;
140
+      }
141
+    }
142
+  }
143
+  return nil;
144
+}
145
+
119 146
 -(void)initBridgeWithBundleURL:(NSURL *)bundleURL
120 147
 {
121 148
   [self initBridgeWithBundleURL :bundleURL launchOptions:nil];

+ 29
- 0
ios/RCCManagerModule.m ファイルの表示

@@ -351,6 +351,35 @@ RCT_EXPORT_METHOD(
351 351
                                                                     completion:^(){ resolve(nil); }];
352 352
 }
353 353
 
354
+- (UIViewController *) getVisibleViewControllerFor:(UIViewController *)vc
355
+{
356
+    if ([vc isKindOfClass:[UINavigationController class]])
357
+    {
358
+        return [self getVisibleViewControllerFor:[((UINavigationController*)vc) visibleViewController]];
359
+    }
360
+    else if ([vc isKindOfClass:[UITabBarController class]])
361
+    {
362
+        return [self getVisibleViewControllerFor:[((UITabBarController*)vc) selectedViewController]];
363
+    }
364
+    else if (vc.presentedViewController)
365
+    {
366
+        return [self getVisibleViewControllerFor:vc.presentedViewController];
367
+    }
368
+    else
369
+    {
370
+        return vc;
371
+    }
372
+}
373
+
374
+RCT_EXPORT_METHOD(getCurrentlyVisibleScreenId:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
375
+{
376
+    UIViewController *rootVC = [UIApplication sharedApplication].delegate.window.rootViewController;
377
+    UIViewController *visibleVC = [self getVisibleViewControllerFor:rootVC];
378
+    NSString *controllerId = [[RCCManager sharedIntance] getIdForController:visibleVC];
379
+    id result = (controllerId != nil) ? @{@"screenId": controllerId} : nil;
380
+    resolve(result);
381
+}
382
+
354 383
 -(BOOL)viewControllerIsModal:(UIViewController*)viewController
355 384
 {
356 385
     BOOL viewControllerIsModal = (viewController.presentingViewController.presentedViewController == viewController)

+ 3
- 0
ios/RCCNavigationController.m ファイルの表示

@@ -34,6 +34,7 @@ NSString const *CALLBACK_ASSOCIATED_ID = @"RCCNavigationController.CALLBACK_ASSO
34 34
   
35 35
   RCCViewController *viewController = [[RCCViewController alloc] initWithComponent:component passProps:passProps navigatorStyle:navigatorStyle globalProps:globalProps bridge:bridge];
36 36
   if (!viewController) return nil;
37
+  viewController.controllerId = props[@"id"];
37 38
   
38 39
   NSArray *leftButtons = props[@"leftButtons"];
39 40
   if (leftButtons)
@@ -106,6 +107,7 @@ NSString const *CALLBACK_ASSOCIATED_ID = @"RCCNavigationController.CALLBACK_ASSO
106 107
     }
107 108
     
108 109
     RCCViewController *viewController = [[RCCViewController alloc] initWithComponent:component passProps:passProps navigatorStyle:navigatorStyle globalProps:nil bridge:bridge];
110
+    viewController.controllerId = passProps[@"screenInstanceID"];
109 111
     
110 112
     [self processTitleView:viewController
111 113
                      props:actionParams
@@ -213,6 +215,7 @@ NSString const *CALLBACK_ASSOCIATED_ID = @"RCCNavigationController.CALLBACK_ASSO
213 215
     NSDictionary *navigatorStyle = actionParams[@"style"];
214 216
     
215 217
     RCCViewController *viewController = [[RCCViewController alloc] initWithComponent:component passProps:passProps navigatorStyle:navigatorStyle globalProps:nil bridge:bridge];
218
+    viewController.controllerId = passProps[@"screenInstanceID"];
216 219
     
217 220
     [self processTitleView:viewController
218 221
                      props:actionParams

+ 1
- 0
ios/RCCViewController.h ファイルの表示

@@ -15,6 +15,7 @@ extern NSString* const RCCViewControllerCancelReactTouchesNotification;
15 15
 
16 16
 @property (nonatomic) NSMutableDictionary *navigatorStyle;
17 17
 @property (nonatomic) BOOL navBarHidden;
18
+@property (nonatomic, strong) NSString *controllerId;
18 19
 @property (nonatomic, strong) NSString *commandType;
19 20
 @property (nonatomic, strong) NSString *timestamp;
20 21
 

+ 5
- 0
ios/RCCViewController.m ファイルの表示

@@ -94,6 +94,11 @@ const NSInteger TRANSPARENT_NAVBAR_TAG = 78264803;
94 94
   if (controller && componentId)
95 95
   {
96 96
     [[RCCManager sharedInstance] registerController:controller componentId:componentId componentType:type];
97
+    
98
+    if([controller isKindOfClass:[RCCViewController class]])
99
+    {
100
+      ((RCCViewController*)controller).controllerId = componentId;
101
+    }
97 102
   }
98 103
   
99 104
   // set background image at root level

+ 5
- 0
src/Navigation.js ファイルの表示

@@ -166,8 +166,13 @@ async function isAppLaunched() {
166 166
   return await platformSpecific.isAppLaunched();
167 167
 }
168 168
 
169
+function getCurrentlyVisibleScreenId() {
170
+  return platformSpecific.getCurrentlyVisibleScreenId();
171
+}
172
+
169 173
 export default {
170 174
   getRegisteredScreen,
175
+  getCurrentlyVisibleScreenId,
171 176
   registerComponent,
172 177
   showModal: showModal,
173 178
   dismissModal: dismissModal,

+ 8
- 0
src/Screen.js ファイルの表示

@@ -165,6 +165,14 @@ class Navigator {
165 165
       Navigation.clearEventHandler(this.navigatorEventID);
166 166
     }
167 167
   }
168
+
169
+  async screenIsCurrentlyVisible() {
170
+    const res = await Navigation.getCurrentlyVisibleScreenId();
171
+    if (!res) {
172
+      return false;
173
+    }
174
+    return res.screenId === this.screenInstanceID;
175
+  }
168 176
 }
169 177
 
170 178
 export default class Screen extends Component {

+ 6
- 0
src/deprecated/controllers/index.js ファイルの表示

@@ -313,6 +313,12 @@ var Controllers = {
313 313
     }
314 314
   },
315 315
 
316
+  ScreenUtils: {
317
+    getCurrentlyVisibleScreenId: async function() {
318
+      return await RCCManager.getCurrentlyVisibleScreenId();
319
+    }
320
+  },
321
+
316 322
   NavigationToolBarIOS: OriginalReactNative.requireNativeComponent('RCCToolBar', null),
317 323
 
318 324
   Constants: Constants

+ 6
- 1
src/deprecated/platformSpecificDeprecated.android.js ファイルの表示

@@ -688,6 +688,10 @@ async function isAppLaunched() {
688 688
   return await newPlatformSpecific.isAppLaunched();
689 689
 }
690 690
 
691
+async function getCurrentlyVisibleScreenId() {
692
+  return await newPlatformSpecific.getCurrentlyVisibleScreenId();
693
+}
694
+
691 695
 export default {
692 696
   startTabBasedApp,
693 697
   startSingleScreenApp,
@@ -718,5 +722,6 @@ export default {
718 722
   dismissSnackbar,
719 723
   showContextualMenu,
720 724
   dismissContextualMenu,
721
-  isAppLaunched
725
+  isAppLaunched,
726
+  getCurrentlyVisibleScreenId
722 727
 };

+ 7
- 2
src/deprecated/platformSpecificDeprecated.ios.js ファイルの表示

@@ -1,6 +1,6 @@
1 1
 /*eslint-disable*/
2 2
 import Navigation from './../Navigation';
3
-import Controllers, {Modal, Notification} from './controllers';
3
+import Controllers, {Modal, Notification, ScreenUtils} from './controllers';
4 4
 const React = Controllers.hijackReact();
5 5
 const {
6 6
   ControllerRegistry,
@@ -620,6 +620,10 @@ function dismissContextualMenu() {
620 620
   // Android only
621 621
 }
622 622
 
623
+async function getCurrentlyVisibleScreenId() {
624
+  return await ScreenUtils.getCurrentlyVisibleScreenId();
625
+}
626
+
623 627
 export default {
624 628
   startTabBasedApp,
625 629
   startSingleScreenApp,
@@ -647,5 +651,6 @@ export default {
647 651
   navigatorSwitchToTab,
648 652
   navigatorToggleNavBar,
649 653
   showContextualMenu,
650
-  dismissContextualMenu
654
+  dismissContextualMenu,
655
+  getCurrentlyVisibleScreenId
651 656
 };

+ 6
- 1
src/platformSpecific.android.js ファイルの表示

@@ -181,6 +181,10 @@ async function isAppLaunched() {
181 181
   return await NativeReactModule.isAppLaunched();
182 182
 }
183 183
 
184
+async function getCurrentlyVisibleScreenId() {
185
+  return await NativeReactModule.getCurrentlyVisibleScreenId();
186
+}
187
+
184 188
 module.exports = {
185 189
   startApp,
186 190
   push,
@@ -215,5 +219,6 @@ module.exports = {
215 219
   showContextualMenu,
216 220
   dismissContextualMenu,
217 221
   setScreenStyle,
218
-  isAppLaunched
222
+  isAppLaunched,
223
+  getCurrentlyVisibleScreenId
219 224
 };