Bluesky app fork with some witchin' additions 馃挮
at main 16 kB view raw
1diff --git a/node_modules/react-native-screens/ios/RNSScreen.mm b/node_modules/react-native-screens/ios/RNSScreen.mm 2index b62a2e2..cb469db 100644 3--- a/node_modules/react-native-screens/ios/RNSScreen.mm 4+++ b/node_modules/react-native-screens/ios/RNSScreen.mm 5@@ -729,9 +729,26 @@ - (void)notifyTransitionProgress:(double)progress closing:(BOOL)closing goingFor 6 #endif 7 } 8 9-#if !RCT_NEW_ARCH_ENABLED 10+- (void)willMoveToWindow:(UIWindow *)newWindow 11+{ 12+ if (@available(iOS 26, *)) { 13+ // In iOS 26, as soon as another screen appears in transition, it is interactable 14+ // To avoid glitches resulting from clicking buttons mid transition, we temporarily disable all interactions 15+ // Disabling interactions for parent navigation controller won't be enough in case of nested stack 16+ // Furthermore, a stack put inside a modal will exist in an entirely different hierarchy 17+ // To be sure, we block interactions on the whole window. 18+ // Note that newWindows is nil when moving from instead of moving to, and Obj-C handles nil correctly 19+ newWindow.userInteractionEnabled = false; 20+ } 21+} 22+ 23 - (void)presentationControllerWillDismiss:(UIPresentationController *)presentationController 24 { 25+ if (@available(iOS 26, *)) { 26+ // Disable interactions to disallow multiple modals dismissed at once; see willMoveToWindow 27+ presentationController.containerView.window.userInteractionEnabled = false; 28+ } 29+#if !RCT_NEW_ARCH_ENABLED 30 // On Paper, we need to call both "cancel" and "reset" here because RN's gesture 31 // recognizer does not handle the scenario when it gets cancelled by other top 32 // level gesture recognizer. In this case by the modal dismiss gesture. 33@@ -744,8 +761,8 @@ - (void)presentationControllerWillDismiss:(UIPresentationController *)presentati 34 // down. 35 [_touchHandler cancel]; 36 [_touchHandler reset]; 37-} 38 #endif // !RCT_NEW_ARCH_ENABLED 39+} 40 41 - (BOOL)presentationControllerShouldDismiss:(UIPresentationController *)presentationController 42 { 43@@ -757,6 +774,10 @@ - (BOOL)presentationControllerShouldDismiss:(UIPresentationController *)presenta 44 45 - (void)presentationControllerDidAttemptToDismiss:(UIPresentationController *)presentationController 46 { 47+ if (@available(iOS 26, *)) { 48+ // Reenable interactions; see presentationControllerWillDismiss 49+ presentationController.containerView.window.userInteractionEnabled = true; 50+ } 51 // NOTE(kkafar): We should consider depracating the use of gesture cancel here & align 52 // with usePreventRemove API of react-navigation v7. 53 [self notifyGestureCancel]; 54@@ -767,6 +788,11 @@ - (void)presentationControllerDidAttemptToDismiss:(UIPresentationController *)pr 55 56 - (void)presentationControllerDidDismiss:(UIPresentationController *)presentationController 57 { 58+ if (@available(iOS 26, *)) { 59+ // Reenable interactions; see presentationControllerWillDismiss 60+ // Dismissed screen doesn't hold a reference to window, but presentingViewController.view does 61+ presentationController.presentingViewController.view.window.userInteractionEnabled = true; 62+ } 63 if ([_reactSuperview respondsToSelector:@selector(presentationControllerDidDismiss:)]) { 64 [_reactSuperview performSelector:@selector(presentationControllerDidDismiss:) withObject:presentationController]; 65 } 66@@ -1518,6 +1544,10 @@ - (void)viewWillDisappear:(BOOL)animated 67 68 - (void)viewDidAppear:(BOOL)animated 69 { 70+ if (@available(iOS 26, *)) { 71+ // Reenable interactions, see willMoveToWindow 72+ self.view.window.userInteractionEnabled = true; 73+ } 74 [super viewDidAppear:animated]; 75 if (!_isSwiping || _shouldNotify) { 76 // we are going forward or dismissing without swipe 77diff --git a/node_modules/react-native-screens/ios/RNSScreenStack.mm b/node_modules/react-native-screens/ios/RNSScreenStack.mm 78index 229dc58..10b365b 100644 79--- a/node_modules/react-native-screens/ios/RNSScreenStack.mm 80+++ b/node_modules/react-native-screens/ios/RNSScreenStack.mm 81@@ -62,26 +62,6 @@ @interface RNSScreenStackView () < 82 83 @implementation RNSNavigationController 84 85-#if RNS_IPHONE_OS_VERSION_AVAILABLE(26_0) 86-- (void)viewDidLoad 87-{ 88- // iOS 26 introduces new gesture recognizer which replaces our RNSPanGestureRecognizer. 89- // The problem is that we are not able to handle it here for various reasons: 90- // - the new recognizer comes with its own delegate and our current approach is to wire 91- // all recognizers to RNSScreenStackView; to be 100% sure we don't break the logic, 92- // we would have to decorate its delegate and call it after our code, which would 93- // break other recognizers that the stack view is the delegate for 94- // - when RNSScreenStackView.setupGestureHandler method is called, the recognizer hasn't been 95- // loaded yet and there is no other place to configure in a not "hacky" way 96- // - the official docs warn us to not use it for anything other than "setting up failure requirements with it" 97- // - we expose fullScreenGestureEnabled prop to enable/disable the feature, 98- // so we need control over the delegate 99- if (@available(iOS 26.0, *)) { 100- self.interactiveContentPopGestureRecognizer.enabled = NO; 101- } 102-} 103-#endif // iOS 26 104- 105 #if !TARGET_OS_TV 106 - (UIViewController *)childViewControllerForStatusBarStyle 107 { 108@@ -219,50 +199,6 @@ - (bool)onRepeatedTabSelectionOfTabScreenController:(RNSTabsScreenViewController 109 return false; 110 } 111 112-#pragma mark - UINavigationBarDelegate 113- 114-#if RNS_IPHONE_OS_VERSION_AVAILABLE(26_0) 115-- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item 116-{ 117- if (@available(iOS 26, *)) { 118- // To prevent popping multiple screens when back button is pressed repeatedly, 119- // We allow for pop operation to proceed only if no transition is in progress, 120- // which we check indirectly by checking if transitionCoordinator is set. 121- // If it's not, we are safe to proceed. 122- if (self.transitionCoordinator == nil) { 123- // We still need to disable interactions for back button so click effects are not applied, 124- // and there is unfortunately no better place for it currently 125- UIView *button = [navigationBar rnscreens_findBackButtonWrapperView]; 126- if (button != nil) { 127- button.userInteractionEnabled = false; 128- } 129- 130- return true; 131- } 132- 133- return false; 134- } 135- 136- return true; 137-} 138- 139-- (void)navigationBar:(UINavigationBar *)navigationBar didPopItem:(UINavigationItem *)item 140-{ 141- if (@available(iOS 26, *)) { 142- // Reset interactions on back button -> see navigationBar:shouldPopItem 143- // IMPORTANT: This reset won't execute when preventNativeDismiss is on. 144- // However, on iOS 26, unlike in previous versions, the back button instance changes 145- // when handling preventNativeDismiss and userIteractionEnabled is reset. 146- // The instance also changes when regular screen pop happens, but in that case 147- // the value of userInteractionEnabled is carried on, and we reset it here. 148- UIView *button = [navigationBar rnscreens_findBackButtonWrapperView]; 149- if (button != nil) { 150- button.userInteractionEnabled = true; 151- } 152- } 153-} 154-#endif // Check for iOS >= 26 155- 156 #pragma mark - RNSFrameCorrectionProvider 157 158 #ifdef RNS_GAMMA_ENABLED 159@@ -327,7 +263,7 @@ @implementation RNSScreenStackView { 160 UINavigationController *_controller; 161 NSMutableArray<RNSScreenView *> *_reactSubviews; 162 BOOL _invalidated; 163- BOOL _isFullWidthSwiping; 164+ BOOL _isFullWidthSwipingWithPanGesture; // used only for content swipe with RNSPanGestureRecognizer 165 RNSPercentDrivenInteractiveTransition *_interactionController; 166 __weak RNSScreenStackManager *_manager; 167 BOOL _updateScheduled; 168@@ -522,6 +458,11 @@ - (void)reactAddControllerToClosestParent:(UIViewController *)controller 169 [self addSubview:controller.view]; 170 #if !TARGET_OS_TV 171 _controller.interactivePopGestureRecognizer.delegate = self; 172+ #if RNS_IPHONE_OS_VERSION_AVAILABLE(26_0) 173+ if (@available(iOS 26, *)) { 174+ _controller.interactiveContentPopGestureRecognizer.delegate = self; 175+ } 176+#endif // Check for iOS >= 26.0 177 #endif 178 [controller didMoveToParentViewController:parentView.reactViewController]; 179 // On iOS pre 12 we observed that `willShowViewController` delegate method does not always 180@@ -943,7 +884,7 @@ - (void)dismissOnReload 181 // when preventing the native dismiss with back button, we have to return the animator. 182 // Also, we need to return the animator when full width swiping even if the animation is not custom, 183 // otherwise the screen will be just popped immediately due to no animation 184- ((operation == UINavigationControllerOperationPop && shouldCancelDismiss) || _isFullWidthSwiping || 185+ ((operation == UINavigationControllerOperationPop && shouldCancelDismiss) || _isFullWidthSwipingWithPanGesture || 186 [RNSScreenStackAnimator isCustomAnimation:screen.stackAnimation] || _customAnimation)) { 187 return [[RNSScreenStackAnimator alloc] initWithOperation:operation]; 188 } 189@@ -967,23 +908,39 @@ - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer 190 } 191 RNSScreenView *topScreen = _reactSubviews.lastObject; 192 193+ BOOL customAnimationOnSwipePropSetAndSelectedAnimationIsCustom = 194+ topScreen.customAnimationOnSwipe && [RNSScreenStackAnimator isCustomAnimation:topScreen.stackAnimation]; 195+ 196 #if TARGET_OS_TV || TARGET_OS_VISION 197 [self cancelTouchesInParent]; 198 return YES; 199 #else 200- // RNSPanGestureRecognizer will receive events iff topScreen.fullScreenSwipeEnabled == YES; 201- // Events are filtered in gestureRecognizer:shouldReceivePressOrTouchEvent: method 202 if ([gestureRecognizer isKindOfClass:[RNSPanGestureRecognizer class]]) { 203- if ([self isInGestureResponseDistance:gestureRecognizer topScreen:topScreen]) { 204- _isFullWidthSwiping = YES; 205- [self cancelTouchesInParent]; 206- return YES; 207+ // On iOS < 26, we have a custom full screen swipe recognizer that functions similarily 208+ // to interactiveContentPopGestureRecognizer introduced in iOS 26. 209+ // On iOS >= 26, we want to use the native one, but we are unable to handle custom animations 210+ // with native interactiveContentPopGestureRecognizer, so we have to fallback to the old implementation. 211+ // In this case, the old one should behave as close as the new native one, having only the difference 212+ // in animation, and without any customization that is exclusive for it (e.g. gestureResponseDistance). 213+ if (@available(iOS 26, *)) { 214+ if (customAnimationOnSwipePropSetAndSelectedAnimationIsCustom) { 215+ _isFullWidthSwipingWithPanGesture = YES; 216+ [self cancelTouchesInParent]; 217+ return YES; 218+ } 219+ return NO; 220+ } else { 221+ if ([self isInGestureResponseDistance:gestureRecognizer topScreen:topScreen]) { 222+ _isFullWidthSwipingWithPanGesture = YES; 223+ [self cancelTouchesInParent]; 224+ return YES; 225+ } 226+ return NO; 227 } 228- return NO; 229 } 230 231 // Now we're dealing with RNSScreenEdgeGestureRecognizer (or _UIParallaxTransitionPanGestureRecognizer) 232- if (topScreen.customAnimationOnSwipe && [RNSScreenStackAnimator isCustomAnimation:topScreen.stackAnimation]) { 233+ if (customAnimationOnSwipePropSetAndSelectedAnimationIsCustom) { 234 if ([gestureRecognizer isKindOfClass:[RNSScreenEdgeGestureRecognizer class]]) { 235 UIRectEdge edges = ((RNSScreenEdgeGestureRecognizer *)gestureRecognizer).edges; 236 BOOL isRTL = _controller.view.semanticContentAttribute == UISemanticContentAttributeForceRightToLeft; 237@@ -1028,7 +985,9 @@ - (void)setupGestureHandlers 238 rightEdgeSwipeGestureRecognizer.delegate = self; 239 [self addGestureRecognizer:rightEdgeSwipeGestureRecognizer]; 240 241- // gesture recognizer for full width swipe gesture 242+ // Starting from iOS 26, RNSPanGestureRecognizer has been mostly replaced by native 243+ // interactiveContentPopGestureRecognizer. It still needs to handle custom dismiss animations, 244+ // which we are not able to handle with the latter. 245 RNSPanGestureRecognizer *panRecognizer = [[RNSPanGestureRecognizer alloc] initWithTarget:self 246 action:@selector(handleSwipe:)]; 247 panRecognizer.delegate = self; 248@@ -1091,7 +1050,7 @@ - (void)handleSwipe:(UIPanGestureRecognizer *)gestureRecognizer 249 [_interactionController cancelInteractiveTransition]; 250 } 251 _interactionController = nil; 252- _isFullWidthSwiping = NO; 253+ _isFullWidthSwipingWithPanGesture = NO; 254 } 255 default: { 256 break; 257@@ -1225,14 +1184,6 @@ - (BOOL)isScrollViewPanGestureRecognizer:(UIGestureRecognizer *)gestureRecognize 258 // Be careful when adding another type of gesture recognizer. 259 - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceivePressOrTouchEvent:(NSObject *)event 260 { 261- if (@available(iOS 26, *)) { 262- // in iOS 26, you can swipe to pop screen before the previous one finished transitioning; 263- // this prevents from registering the second gesture 264- if ([self isTransitionInProgress]) { 265- return NO; 266- } 267- } 268- 269 RNSScreenView *topScreen = _reactSubviews.lastObject; 270 271 for (RNSScreenView *s in _reactSubviews.reverseObjectEnumerator) { 272@@ -1249,10 +1200,30 @@ - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceive 273 return NO; 274 } 275 276+ BOOL customAnimationOnSwipePropSetAndSelectedAnimationIsCustom = 277+ topScreen.customAnimationOnSwipe && [RNSScreenStackAnimator isCustomAnimation:topScreen.stackAnimation]; 278+#if RNS_IPHONE_OS_VERSION_AVAILABLE(26_0) 279+ if (@available(iOS 26, *)) { 280+ // On iOS 26, fullScreenSwipeEnabled takes no effect, and depending on whether custom animations are on, 281+ // we select either interactiveContentPopGestureRecognizer or RNSPanGestureRecognizer 282+ if (([gestureRecognizer isKindOfClass:[RNSPanGestureRecognizer class]] && 283+ !customAnimationOnSwipePropSetAndSelectedAnimationIsCustom) || 284+ (gestureRecognizer == _controller.interactiveContentPopGestureRecognizer && 285+ customAnimationOnSwipePropSetAndSelectedAnimationIsCustom)) { 286+ return NO; 287+ } 288+ } else { 289+ // We want to pass events to RNSPanGestureRecognizer iff full screen swipe is enabled. 290+ if ([gestureRecognizer isKindOfClass:[RNSPanGestureRecognizer class]]) { 291+ return topScreen.fullScreenSwipeEnabled; 292+ } 293+ } 294+#else // check for iOS >= 26 295 // We want to pass events to RNSPanGestureRecognizer iff full screen swipe is enabled. 296 if ([gestureRecognizer isKindOfClass:[RNSPanGestureRecognizer class]]) { 297 return topScreen.fullScreenSwipeEnabled; 298 } 299+#endif // check for iOS >= 26 300 301 // RNSScreenEdgeGestureRecognizer || _UIParallaxTransitionPanGestureRecognizer 302 return YES; 303@@ -1268,15 +1239,6 @@ - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceive 304 return [self gestureRecognizer:gestureRecognizer shouldReceivePressOrTouchEvent:touch]; 305 } 306 307-- (BOOL)isTransitionInProgress 308-{ 309- if (_controller.transitionCoordinator != nil) { 310- return YES; 311- } 312- 313- return NO; 314-} 315- 316 - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer 317 shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer 318 { 319@@ -1289,7 +1251,6 @@ - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer 320 if (gestureRecognizer.state == UIGestureRecognizerStateBegan || isBackGesture) { 321 return NO; 322 } 323- 324 return YES; 325 } 326 return NO;