An ATproto social media client -- with an independent Appview.
1diff --git a/node_modules/react-native/React/Fabric/Mounting/ComponentViews/ScrollView/RCTPullToRefreshViewComponentView.h b/node_modules/react-native/React/Fabric/Mounting/ComponentViews/ScrollView/RCTPullToRefreshViewComponentView.h
2index 914a249..0deac55 100644
3--- a/node_modules/react-native/React/Fabric/Mounting/ComponentViews/ScrollView/RCTPullToRefreshViewComponentView.h
4+++ b/node_modules/react-native/React/Fabric/Mounting/ComponentViews/ScrollView/RCTPullToRefreshViewComponentView.h
5@@ -19,6 +19,8 @@ NS_ASSUME_NONNULL_BEGIN
6 */
7 @interface RCTPullToRefreshViewComponentView : RCTViewComponentView <RCTCustomPullToRefreshViewProtocol>
8
9+- (void)beginRefreshingProgrammatically;
10+
11 @end
12
13 NS_ASSUME_NONNULL_END
14diff --git a/node_modules/react-native/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm b/node_modules/react-native/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm
15index d029337..0f63ea3 100644
16--- a/node_modules/react-native/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm
17+++ b/node_modules/react-native/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm
18@@ -1038,6 +1038,11 @@ - (void)_adjustForMaintainVisibleContentPosition
19 }
20 }
21
22++ (BOOL)shouldBeRecycled
23+{
24+ return NO;
25+}
26+
27 @end
28
29 Class<RCTComponentViewProtocol> RCTScrollViewCls(void)
30diff --git a/node_modules/react-native/React/Views/RefreshControl/RCTRefreshControl.h b/node_modules/react-native/React/Views/RefreshControl/RCTRefreshControl.h
31index e9b330f..ec5f58c 100644
32--- a/node_modules/react-native/React/Views/RefreshControl/RCTRefreshControl.h
33+++ b/node_modules/react-native/React/Views/RefreshControl/RCTRefreshControl.h
34@@ -15,5 +15,8 @@
35 @property (nonatomic, copy) NSString *title;
36 @property (nonatomic, copy) RCTDirectEventBlock onRefresh;
37 @property (nonatomic, weak) UIScrollView *scrollView;
38+@property (nonatomic, copy) UIColor *customTintColor;
39+
40+- (void)forwarderBeginRefreshing;
41
42 @end
43diff --git a/node_modules/react-native/React/Views/RefreshControl/RCTRefreshControl.m b/node_modules/react-native/React/Views/RefreshControl/RCTRefreshControl.m
44index 53bfd04..ff1b1ed 100644
45--- a/node_modules/react-native/React/Views/RefreshControl/RCTRefreshControl.m
46+++ b/node_modules/react-native/React/Views/RefreshControl/RCTRefreshControl.m
47@@ -23,6 +23,7 @@ @implementation RCTRefreshControl {
48 UIColor *_titleColor;
49 CGFloat _progressViewOffset;
50 BOOL _hasMovedToWindow;
51+ UIColor *_customTintColor;
52 }
53
54 - (instancetype)init
55@@ -58,6 +59,12 @@ - (void)layoutSubviews
56 _isInitialRender = false;
57 }
58
59+- (void)didMoveToSuperview
60+{
61+ [super didMoveToSuperview];
62+ [self setTintColor:_customTintColor];
63+}
64+
65 - (void)didMoveToWindow
66 {
67 [super didMoveToWindow];
68@@ -221,4 +228,50 @@ - (void)refreshControlValueChanged
69 }
70 }
71
72+// Fix for https://github.com/facebook/react-native/issues/43388
73+// A bug in iOS 17.4 causes the haptic to not play when refreshing if the tintColor
74+// is set before the refresh control gets added to the scrollview. We'll call this
75+// function whenever the superview changes. We'll also call it if the value of customTintColor
76+// changes.
77+- (void)setTintColor:(UIColor *)tintColor
78+{
79+ if ([self.superview isKindOfClass:[UIScrollView class]] && self.tintColor != tintColor) {
80+ [super setTintColor:tintColor];
81+ }
82+}
83+
84+// This method is used by Bluesky's ExpoScrollForwarder. This allows other React Native
85+// libraries to perform a refresh of a scrollview and access the refresh control's onRefresh
86+// function.
87+- (void)forwarderBeginRefreshing
88+{
89+ _refreshingProgrammatically = NO;
90+
91+ [self sizeToFit];
92+
93+ if (!self.scrollView) {
94+ return;
95+ }
96+
97+ UIScrollView *scrollView = (UIScrollView *)self.scrollView;
98+
99+ [UIView animateWithDuration:0.3
100+ delay:0
101+ options:UIViewAnimationOptionBeginFromCurrentState
102+ animations:^(void) {
103+ // Whenever we call this method, the scrollview will always be at a position of
104+ // -130 or less. Scrolling back to -65 simulates the default behavior of RCTRefreshControl
105+ [scrollView setContentOffset:CGPointMake(0, -65)];
106+ }
107+ completion:^(__unused BOOL finished) {
108+ [super beginRefreshing];
109+ [self setCurrentRefreshingState:super.refreshing];
110+
111+ if (self->_onRefresh) {
112+ self->_onRefresh(nil);
113+ }
114+ }
115+ ];
116+}
117+
118 @end
119diff --git a/node_modules/react-native/React/Views/RefreshControl/RCTRefreshControlManager.m b/node_modules/react-native/React/Views/RefreshControl/RCTRefreshControlManager.m
120index 40aaf9c..1c60164 100644
121--- a/node_modules/react-native/React/Views/RefreshControl/RCTRefreshControlManager.m
122+++ b/node_modules/react-native/React/Views/RefreshControl/RCTRefreshControlManager.m
123@@ -22,11 +22,12 @@ - (UIView *)view
124
125 RCT_EXPORT_VIEW_PROPERTY(onRefresh, RCTDirectEventBlock)
126 RCT_EXPORT_VIEW_PROPERTY(refreshing, BOOL)
127-RCT_EXPORT_VIEW_PROPERTY(tintColor, UIColor)
128 RCT_EXPORT_VIEW_PROPERTY(title, NSString)
129 RCT_EXPORT_VIEW_PROPERTY(titleColor, UIColor)
130 RCT_EXPORT_VIEW_PROPERTY(progressViewOffset, CGFloat)
131
132+RCT_REMAP_VIEW_PROPERTY(tintColor, customTintColor, UIColor)
133+
134 RCT_EXPORT_METHOD(setNativeRefreshing : (nonnull NSNumber *)viewTag toRefreshing : (BOOL)refreshing)
135 {
136 [self.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry) {