my fork of the bluesky client
at main 283 lines 12 kB view raw
1diff --git a/node_modules/react-native/Libraries/Blob/RCTFileReaderModule.mm b/node_modules/react-native/Libraries/Blob/RCTFileReaderModule.mm 2index caa5540..c5d4e67 100644 3--- a/node_modules/react-native/Libraries/Blob/RCTFileReaderModule.mm 4+++ b/node_modules/react-native/Libraries/Blob/RCTFileReaderModule.mm 5@@ -73,7 +73,7 @@ @implementation RCTFileReaderModule 6 } else { 7 NSString *type = [RCTConvert NSString:blob[@"type"]]; 8 NSString *text = [NSString stringWithFormat:@"data:%@;base64,%@", 9- type != nil && [type length] > 0 ? type : @"application/octet-stream", 10+ ![type isEqual:[NSNull null]] && [type length] > 0 ? type : @"application/octet-stream", 11 [data base64EncodedStringWithOptions:0]]; 12 13 resolve(text); 14diff --git a/node_modules/react-native/Libraries/Text/TextInput/RCTBaseTextInputView.mm b/node_modules/react-native/Libraries/Text/TextInput/RCTBaseTextInputView.mm 15index b0d71dc..41b9a0e 100644 16--- a/node_modules/react-native/Libraries/Text/TextInput/RCTBaseTextInputView.mm 17+++ b/node_modules/react-native/Libraries/Text/TextInput/RCTBaseTextInputView.mm 18@@ -377,10 +377,6 @@ - (void)textInputDidBeginEditing 19 self.backedTextInputView.attributedText = [NSAttributedString new]; 20 } 21 22- if (_selectTextOnFocus) { 23- [self.backedTextInputView selectAll:nil]; 24- } 25- 26 [_eventDispatcher sendTextEventWithType:RCTTextEventTypeFocus 27 reactTag:self.reactTag 28 text:[self.backedTextInputView.attributedText.string copy] 29@@ -611,6 +607,10 @@ - (UIView *)reactAccessibilityElement 30 - (void)reactFocus 31 { 32 [self.backedTextInputView reactFocus]; 33+ 34+ if (_selectTextOnFocus) { 35+ [self.backedTextInputView selectAll:nil]; 36+ } 37 } 38 39 - (void)reactBlur 40diff --git a/node_modules/react-native/React/Views/RefreshControl/RCTRefreshControl.h b/node_modules/react-native/React/Views/RefreshControl/RCTRefreshControl.h 41index e9b330f..ec5f58c 100644 42--- a/node_modules/react-native/React/Views/RefreshControl/RCTRefreshControl.h 43+++ b/node_modules/react-native/React/Views/RefreshControl/RCTRefreshControl.h 44@@ -15,5 +15,8 @@ 45 @property (nonatomic, copy) NSString *title; 46 @property (nonatomic, copy) RCTDirectEventBlock onRefresh; 47 @property (nonatomic, weak) UIScrollView *scrollView; 48+@property (nonatomic, copy) UIColor *customTintColor; 49+ 50+- (void)forwarderBeginRefreshing; 51 52 @end 53diff --git a/node_modules/react-native/React/Views/RefreshControl/RCTRefreshControl.m b/node_modules/react-native/React/Views/RefreshControl/RCTRefreshControl.m 54index b09e653..d2b4e05 100644 55--- a/node_modules/react-native/React/Views/RefreshControl/RCTRefreshControl.m 56+++ b/node_modules/react-native/React/Views/RefreshControl/RCTRefreshControl.m 57@@ -22,6 +22,7 @@ @implementation RCTRefreshControl { 58 NSString *_title; 59 UIColor *_titleColor; 60 CGFloat _progressViewOffset; 61+ UIColor *_customTintColor; 62 } 63 64 - (instancetype)init 65@@ -56,6 +57,12 @@ - (void)layoutSubviews 66 _isInitialRender = false; 67 } 68 69+- (void)didMoveToSuperview 70+{ 71+ [super didMoveToSuperview]; 72+ [self setTintColor:_customTintColor]; 73+} 74+ 75 - (void)beginRefreshingProgrammatically 76 { 77 UInt64 beginRefreshingTimestamp = _currentRefreshingStateTimestamp; 78@@ -203,4 +210,58 @@ - (void)refreshControlValueChanged 79 } 80 } 81 82+- (void)setCustomTintColor:(UIColor *)customTintColor 83+{ 84+ _customTintColor = customTintColor; 85+ [self setTintColor:customTintColor]; 86+} 87+ 88+// Fix for https://github.com/facebook/react-native/issues/43388 89+// A bug in iOS 17.4 causes the haptic to not play when refreshing if the tintColor 90+// is set before the refresh control gets added to the scrollview. We'll call this 91+// function whenever the superview changes. We'll also call it if the value of customTintColor 92+// changes. 93+- (void)setTintColor:(UIColor *)tintColor 94+{ 95+ if ([self.superview isKindOfClass:[UIScrollView class]] && self.tintColor != tintColor) { 96+ [super setTintColor:tintColor]; 97+ } 98+} 99+ 100+/* 101+ This method is used by Bluesky's ExpoScrollForwarder. This allows other React Native 102+ libraries to perform a refresh of a scrollview and access the refresh control's onRefresh 103+ function. 104+ */ 105+- (void)forwarderBeginRefreshing 106+{ 107+ _refreshingProgrammatically = NO; 108+ 109+ [self sizeToFit]; 110+ 111+ if (!self.scrollView) { 112+ return; 113+ } 114+ 115+ UIScrollView *scrollView = (UIScrollView *)self.scrollView; 116+ 117+ [UIView animateWithDuration:0.3 118+ delay:0 119+ options:UIViewAnimationOptionBeginFromCurrentState 120+ animations:^(void) { 121+ // Whenever we call this method, the scrollview will always be at a position of 122+ // -130 or less. Scrolling back to -65 simulates the default behavior of RCTRefreshControl 123+ [scrollView setContentOffset:CGPointMake(0, -65)]; 124+ } 125+ completion:^(__unused BOOL finished) { 126+ [super beginRefreshing]; 127+ [self setCurrentRefreshingState:super.refreshing]; 128+ 129+ if (self->_onRefresh) { 130+ self->_onRefresh(nil); 131+ } 132+ } 133+ ]; 134+} 135+ 136 @end 137diff --git a/node_modules/react-native/React/Views/RefreshControl/RCTRefreshControlManager.m b/node_modules/react-native/React/Views/RefreshControl/RCTRefreshControlManager.m 138index 40aaf9c..1c60164 100644 139--- a/node_modules/react-native/React/Views/RefreshControl/RCTRefreshControlManager.m 140+++ b/node_modules/react-native/React/Views/RefreshControl/RCTRefreshControlManager.m 141@@ -22,11 +22,12 @@ - (UIView *)view 142 143 RCT_EXPORT_VIEW_PROPERTY(onRefresh, RCTDirectEventBlock) 144 RCT_EXPORT_VIEW_PROPERTY(refreshing, BOOL) 145-RCT_EXPORT_VIEW_PROPERTY(tintColor, UIColor) 146 RCT_EXPORT_VIEW_PROPERTY(title, NSString) 147 RCT_EXPORT_VIEW_PROPERTY(titleColor, UIColor) 148 RCT_EXPORT_VIEW_PROPERTY(progressViewOffset, CGFloat) 149 150+RCT_REMAP_VIEW_PROPERTY(tintColor, customTintColor, UIColor) 151+ 152 RCT_EXPORT_METHOD(setNativeRefreshing : (nonnull NSNumber *)viewTag toRefreshing : (BOOL)refreshing) 153 { 154 [self.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry) { 155diff --git a/node_modules/react-native/React/Views/ScrollView/RCTScrollView.m b/node_modules/react-native/React/Views/ScrollView/RCTScrollView.m 156index 1aead51..39e6244 100644 157--- a/node_modules/react-native/React/Views/ScrollView/RCTScrollView.m 158+++ b/node_modules/react-native/React/Views/ScrollView/RCTScrollView.m 159@@ -159,31 +159,6 @@ - (BOOL)touchesShouldCancelInContentView:(__unused UIView *)view 160 return !shouldDisableScrollInteraction; 161 } 162 163-/* 164- * Automatically centers the content such that if the content is smaller than the 165- * ScrollView, we force it to be centered, but when you zoom or the content otherwise 166- * becomes larger than the ScrollView, there is no padding around the content but it 167- * can still fill the whole view. 168- */ 169-- (void)setContentOffset:(CGPoint)contentOffset 170-{ 171- UIView *contentView = [self contentView]; 172- if (contentView && _centerContent && !CGSizeEqualToSize(contentView.frame.size, CGSizeZero)) { 173- CGSize subviewSize = contentView.frame.size; 174- CGSize scrollViewSize = self.bounds.size; 175- if (subviewSize.width <= scrollViewSize.width) { 176- contentOffset.x = -(scrollViewSize.width - subviewSize.width) / 2.0; 177- } 178- if (subviewSize.height <= scrollViewSize.height) { 179- contentOffset.y = -(scrollViewSize.height - subviewSize.height) / 2.0; 180- } 181- } 182- 183- super.contentOffset = CGPointMake( 184- RCTSanitizeNaNValue(contentOffset.x, @"scrollView.contentOffset.x"), 185- RCTSanitizeNaNValue(contentOffset.y, @"scrollView.contentOffset.y")); 186-} 187- 188 - (void)setFrame:(CGRect)frame 189 { 190 // Preserving and revalidating `contentOffset`. 191@@ -427,6 +402,11 @@ - (void)setRemoveClippedSubviews:(__unused BOOL)removeClippedSubviews 192 // Does nothing 193 } 194 195+- (void)setFrame:(CGRect)frame { 196+ [super setFrame:frame]; 197+ [self centerContentIfNeeded]; 198+} 199+ 200 - (void)insertReactSubview:(UIView *)view atIndex:(NSInteger)atIndex 201 { 202 [super insertReactSubview:view atIndex:atIndex]; 203@@ -444,6 +424,8 @@ - (void)insertReactSubview:(UIView *)view atIndex:(NSInteger)atIndex 204 RCTApplyTransformationAccordingLayoutDirection(_contentView, self.reactLayoutDirection); 205 [_scrollView addSubview:view]; 206 } 207+ 208+ [self centerContentIfNeeded]; 209 } 210 211 - (void)removeReactSubview:(UIView *)subview 212@@ -652,9 +634,46 @@ -(void)delegateMethod : (UIScrollView *)scrollView \ 213 } 214 215 RCT_SCROLL_EVENT_HANDLER(scrollViewWillBeginDecelerating, onMomentumScrollBegin) 216-RCT_SCROLL_EVENT_HANDLER(scrollViewDidZoom, onScroll) 217 RCT_SCROLL_EVENT_HANDLER(scrollViewDidScrollToTop, onScrollToTop) 218 219+-(void)scrollViewDidZoom : (UIScrollView *)scrollView 220+{ 221+ [self centerContentIfNeeded]; 222+ 223+ RCT_SEND_SCROLL_EVENT(onScroll, nil); 224+ RCT_FORWARD_SCROLL_EVENT(scrollViewDidZoom : scrollView); 225+} 226+ 227+/* 228+ * Automatically centers the content such that if the content is smaller than the 229+ * ScrollView, we force it to be centered, but when you zoom or the content otherwise 230+ * becomes larger than the ScrollView, there is no padding around the content but it 231+ * can still fill the whole view. 232+ * 233+ * PATCHED: This deviates from the original React Native implementation to fix two issues: 234+ * 235+ * - The scroll view was swallowing any taps immediately after pinching 236+ * - The scroll view was jittering when crossing the full screen threshold while pinching 237+ * 238+ * This implementation is based on https://petersteinberger.com/blog/2013/how-to-center-uiscrollview/. 239+ */ 240+-(void)centerContentIfNeeded 241+{ 242+ if (_scrollView.centerContent && 243+ !CGSizeEqualToSize(self.contentSize, CGSizeZero) && 244+ !CGSizeEqualToSize(self.bounds.size, CGSizeZero) 245+ ) { 246+ CGFloat top = 0, left = 0; 247+ if (self.contentSize.width < self.bounds.size.width) { 248+ left = (self.bounds.size.width - self.contentSize.width) * 0.5f; 249+ } 250+ if (self.contentSize.height < self.bounds.size.height) { 251+ top = (self.bounds.size.height - self.contentSize.height) * 0.5f; 252+ } 253+ _scrollView.contentInset = UIEdgeInsetsMake(top, left, top, left); 254+ } 255+} 256+ 257 - (void)addScrollListener:(NSObject<UIScrollViewDelegate> *)scrollListener 258 { 259 [_scrollListeners addObject:scrollListener]; 260@@ -913,6 +932,7 @@ - (void)updateContentSizeIfNeeded 261 CGSize contentSize = self.contentSize; 262 if (!CGSizeEqualToSize(_scrollView.contentSize, contentSize)) { 263 _scrollView.contentSize = contentSize; 264+ [self centerContentIfNeeded]; 265 } 266 } 267 268diff --git a/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/core/JavaTimerManager.java b/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/core/JavaTimerManager.java 269index 5f5e1ab..aac00b6 100644 270--- a/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/core/JavaTimerManager.java 271+++ b/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/core/JavaTimerManager.java 272@@ -99,8 +99,9 @@ public class JavaTimerManager { 273 } 274 275 // If the JS thread is busy for multiple frames we cancel any other pending runnable. 276- if (mCurrentIdleCallbackRunnable != null) { 277- mCurrentIdleCallbackRunnable.cancel(); 278+ IdleCallbackRunnable currentRunnable = mCurrentIdleCallbackRunnable; 279+ if (currentRunnable != null) { 280+ currentRunnable.cancel(); 281 } 282 283 mCurrentIdleCallbackRunnable = new IdleCallbackRunnable(frameTimeNanos);