@onekeyfe/react-native-pager-view 3.0.132 → 3.0.134
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -184,10 +184,12 @@ private class CollapsiblePagerHorizontalItemsView(
|
|
|
184
184
|
}
|
|
185
185
|
}
|
|
186
186
|
|
|
187
|
-
private fun selectedBackground(selected: Boolean) = GradientDrawable().apply {
|
|
187
|
+
private fun selectedBackground(selected: Boolean, height: Int) = GradientDrawable().apply {
|
|
188
188
|
shape = GradientDrawable.RECTANGLE
|
|
189
189
|
setColor(if (selected) selectedBackgroundColor else Color.TRANSPARENT)
|
|
190
|
-
|
|
190
|
+
// OneKey patch: match the pill shape of the React category filters.
|
|
191
|
+
// cornerRadius = dp(10.0).toFloat()
|
|
192
|
+
cornerRadius = height / 2f
|
|
191
193
|
}
|
|
192
194
|
|
|
193
195
|
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
|
@@ -278,7 +280,7 @@ private class CollapsiblePagerHorizontalItemsView(
|
|
|
278
280
|
buttons.forEachIndexed { index, button ->
|
|
279
281
|
val selected = index == selectedIndex
|
|
280
282
|
button.setTextColor(if (selected) activeTextColor else inactiveTextColor)
|
|
281
|
-
button.background = selectedBackground(selected)
|
|
283
|
+
button.background = selectedBackground(selected, button.height)
|
|
282
284
|
button.isSelected = selected
|
|
283
285
|
}
|
|
284
286
|
}
|
|
@@ -11,12 +11,39 @@
|
|
|
11
11
|
#import "React/RCTSurfaceTouchHandler.h"
|
|
12
12
|
#import "React/RCTTouchHandler.h"
|
|
13
13
|
|
|
14
|
+
// OneKey patch: deduplicate structural mutation parents before scanning the page.
|
|
15
|
+
#include <unordered_set>
|
|
16
|
+
|
|
14
17
|
using namespace facebook::react;
|
|
15
18
|
|
|
16
19
|
static void *RNCCollapsiblePagerContentOffsetContext = &RNCCollapsiblePagerContentOffsetContext;
|
|
17
20
|
static NSString *const RNCCollapsiblePagerNativeScrollerIDPrefix =
|
|
18
21
|
@"rnc-collapsible-pager-native-scroller:";
|
|
19
22
|
|
|
23
|
+
static const CFTimeInterval RNCCollapsiblePagerFallbackDebounceInterval = 0.5;
|
|
24
|
+
|
|
25
|
+
// OneKey patch: native scroll views can survive a move or be reused for a new
|
|
26
|
+
// Fabric component. Keep weak identity until their page is attached again.
|
|
27
|
+
@interface RNCCollapsiblePagerReleasedScrollState : NSObject
|
|
28
|
+
@property (nonatomic, weak) UIScrollView *scrollView;
|
|
29
|
+
@property (nonatomic, weak) UIView *componentView;
|
|
30
|
+
@property (nonatomic, assign) NSInteger componentTag;
|
|
31
|
+
@property (nonatomic, weak) UIScrollView *pendingFallbackScrollView;
|
|
32
|
+
@property (nonatomic, assign) CFTimeInterval pendingFallbackDetectedAt;
|
|
33
|
+
@end
|
|
34
|
+
|
|
35
|
+
@implementation RNCCollapsiblePagerReleasedScrollState
|
|
36
|
+
@end
|
|
37
|
+
|
|
38
|
+
static UIView *RNCScrollComponentView(UIScrollView *scrollView)
|
|
39
|
+
{
|
|
40
|
+
for (UIView *view = scrollView; view != nil; view = view.superview) {
|
|
41
|
+
// UIView's Fabric category adopts the protocol even for unregistered views.
|
|
42
|
+
if ([view isKindOfClass:RCTViewComponentView.class] && view.tag > 0) return view;
|
|
43
|
+
}
|
|
44
|
+
return nil;
|
|
45
|
+
}
|
|
46
|
+
|
|
20
47
|
typedef void (^RNCCollapsiblePagerNativeTabPressHandler)(NSInteger index, NSString *key);
|
|
21
48
|
|
|
22
49
|
static CGFloat RNCClamp(CGFloat value, CGFloat minimum, CGFloat maximum)
|
|
@@ -716,7 +743,9 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
716
743
|
NSString *key = [item[@"key"] isKindOfClass:NSString.class] ? item[@"key"] : @"";
|
|
717
744
|
BOOL selected = [key isEqualToString:self->_selectedKey];
|
|
718
745
|
button.backgroundColor = selected ? self->_selectedBackgroundColor : UIColor.clearColor;
|
|
719
|
-
|
|
746
|
+
// OneKey patch: match the pill shape of the React category filters.
|
|
747
|
+
// button.layer.cornerRadius = 10;
|
|
748
|
+
button.layer.cornerRadius = CGRectGetHeight(button.bounds) / 2;
|
|
720
749
|
[button setTitleColor:selected ? self->_activeTextColor : self->_inactiveTextColor
|
|
721
750
|
forState:UIControlStateNormal];
|
|
722
751
|
button.accessibilityTraits = selected
|
|
@@ -989,6 +1018,9 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
989
1018
|
NSMapTable<UIScrollView *, NSValue *> *_originalIndicatorInsets;
|
|
990
1019
|
NSMapTable<UIScrollView *, NSNumber *> *_originalAlwaysBounceVertical;
|
|
991
1020
|
NSMapTable<UIScrollView *, NSNumber *> *_originalInsetAdjustmentBehavior;
|
|
1021
|
+
// OneKey patch: pair pre-mount release with recovery, including deferred mounts.
|
|
1022
|
+
NSMutableDictionary<NSString *, RNCCollapsiblePagerReleasedScrollState *> *_releasedPageScrollStates;
|
|
1023
|
+
BOOL _needsScrollObserverReattachAfterMount;
|
|
992
1024
|
__weak UIScrollView *_observedScrollView;
|
|
993
1025
|
__weak UIScrollView *_sharedHeaderScrollView;
|
|
994
1026
|
__weak UIScrollView *_pendingFallbackScrollView;
|
|
@@ -1023,6 +1055,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
1023
1055
|
_logicalChildren = [NSMutableArray new];
|
|
1024
1056
|
_pageControllers = [NSMutableArray new];
|
|
1025
1057
|
_pageOffsets = [NSMutableDictionary new];
|
|
1058
|
+
_releasedPageScrollStates = [NSMutableDictionary new];
|
|
1026
1059
|
_originalInsets = [NSMapTable weakToStrongObjectsMapTable];
|
|
1027
1060
|
_appliedTopInsets = [NSMapTable weakToStrongObjectsMapTable];
|
|
1028
1061
|
_originalIndicatorInsets = [NSMapTable weakToStrongObjectsMapTable];
|
|
@@ -1132,6 +1165,19 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
1132
1165
|
!_pagerScrollView.dragging && !_pagerScrollView.decelerating) {
|
|
1133
1166
|
[self finishPagerScrollEmittingSelection:NO];
|
|
1134
1167
|
}
|
|
1168
|
+
// OneKey patch: a detached pager can return without a new Fabric transaction.
|
|
1169
|
+
// Wait until the current mount has finished before inspecting its new lists.
|
|
1170
|
+
if (self.window != nil &&
|
|
1171
|
+
(_needsScrollObserverReattachAfterMount || _releasedPageScrollStates.count > 0)) {
|
|
1172
|
+
NSUInteger generation = _generation;
|
|
1173
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
1174
|
+
if (self->_generation == generation &&
|
|
1175
|
+
(self->_needsScrollObserverReattachAfterMount || self->_releasedPageScrollStates.count > 0)) {
|
|
1176
|
+
self->_needsScrollObserverReattachAfterMount = YES;
|
|
1177
|
+
[self attachScrollObserverForCurrentPage];
|
|
1178
|
+
}
|
|
1179
|
+
});
|
|
1180
|
+
}
|
|
1135
1181
|
}
|
|
1136
1182
|
|
|
1137
1183
|
- (void)dealloc
|
|
@@ -1375,6 +1421,63 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
1375
1421
|
|
|
1376
1422
|
#pragma mark - React mounting
|
|
1377
1423
|
|
|
1424
|
+
// OneKey patch: Fabric can recycle a removed list as a descendant of our header
|
|
1425
|
+
// within the same transaction. Release native ownership before that reuse can
|
|
1426
|
+
// form a cycle; did-mount recovery runs too late to prevent it.
|
|
1427
|
+
- (void)mountingTransactionWillMount:(const MountingTransaction &)transaction
|
|
1428
|
+
withSurfaceTelemetry:(const SurfaceTelemetry &)surfaceTelemetry
|
|
1429
|
+
{
|
|
1430
|
+
if (_isBeingRecycled || (_originalInsets.count == 0 && _releasedPageScrollStates.count == 0)) return;
|
|
1431
|
+
std::unordered_set<NSInteger> removedTags;
|
|
1432
|
+
std::unordered_set<NSInteger> deletedTags;
|
|
1433
|
+
for (const auto &mutation : transaction.getMutations()) {
|
|
1434
|
+
if ((mutation.type == ShadowViewMutation::Remove ||
|
|
1435
|
+
mutation.type == ShadowViewMutation::Delete) && mutation.oldChildShadowView.tag > 0) {
|
|
1436
|
+
removedTags.insert(mutation.oldChildShadowView.tag);
|
|
1437
|
+
if (mutation.type == ShadowViewMutation::Delete) {
|
|
1438
|
+
deletedTags.insert(mutation.oldChildShadowView.tag);
|
|
1439
|
+
}
|
|
1440
|
+
}
|
|
1441
|
+
}
|
|
1442
|
+
// A transition can defer reattachment across several mounting transactions.
|
|
1443
|
+
for (RNCCollapsiblePagerReleasedScrollState *state in _releasedPageScrollStates.allValues) {
|
|
1444
|
+
if (deletedTags.find(state.componentTag) != deletedTags.end()) state.componentView = nil;
|
|
1445
|
+
}
|
|
1446
|
+
if (removedTags.empty()) return;
|
|
1447
|
+
|
|
1448
|
+
// Inspect only the managed scroll views' ancestor chains, including wrappers
|
|
1449
|
+
// above the pager. Row removals and unrelated commits keep ownership intact.
|
|
1450
|
+
for (UIScrollView *scrollView in _originalInsets.keyEnumerator.allObjects) {
|
|
1451
|
+
for (UIView *ancestor = scrollView; ancestor != nil; ancestor = ancestor.superview) {
|
|
1452
|
+
if (removedTags.find(ancestor.tag) == removedTags.end()) continue;
|
|
1453
|
+
BOOL belongsToPage = NO;
|
|
1454
|
+
for (NSInteger index = 0; index < _pageControllers.count; index++) {
|
|
1455
|
+
if (![scrollView isDescendantOfView:_pageControllers[index].view]) continue;
|
|
1456
|
+
belongsToPage = YES;
|
|
1457
|
+
NSString *pageKey = [self pageKeyForIndex:index];
|
|
1458
|
+
UIView *componentView = RNCScrollComponentView(scrollView);
|
|
1459
|
+
RNCCollapsiblePagerReleasedScrollState *state = [RNCCollapsiblePagerReleasedScrollState new];
|
|
1460
|
+
state.scrollView = scrollView;
|
|
1461
|
+
state.componentTag = componentView.tag;
|
|
1462
|
+
// A deleted component can reuse both the native object and its tag.
|
|
1463
|
+
if (deletedTags.find(componentView.tag) == deletedTags.end()) {
|
|
1464
|
+
state.componentView = componentView;
|
|
1465
|
+
}
|
|
1466
|
+
_releasedPageScrollStates[pageKey] = state;
|
|
1467
|
+
// Retained pages may be visually aligned to the expanded header while
|
|
1468
|
+
// their saved offsets still hold the user's independent list position.
|
|
1469
|
+
if (scrollView == _observedScrollView || _pageOffsets[pageKey] == nil) {
|
|
1470
|
+
_pageOffsets[pageKey] = @(scrollView.contentOffset.y + scrollView.contentInset.top);
|
|
1471
|
+
}
|
|
1472
|
+
break;
|
|
1473
|
+
}
|
|
1474
|
+
_needsScrollObserverReattachAfterMount = YES;
|
|
1475
|
+
[self restoreScrollViewState:scrollView resetSavedOffset:!belongsToPage];
|
|
1476
|
+
break;
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
1479
|
+
}
|
|
1480
|
+
|
|
1378
1481
|
- (void)mountingTransactionDidMount:(const MountingTransaction &)transaction
|
|
1379
1482
|
withSurfaceTelemetry:(const SurfaceTelemetry &)surfaceTelemetry
|
|
1380
1483
|
{
|
|
@@ -1382,16 +1485,55 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
1382
1485
|
_isPagerDragging || _isBeingRecycled || self.window == nil ||
|
|
1383
1486
|
_currentIndex < 0 || _currentIndex >= _pageControllers.count) return;
|
|
1384
1487
|
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1488
|
+
// OneKey patch: a release must recover even when the changed parent is above
|
|
1489
|
+
// the page subtree. Missing lists keep their identity for a later insertion.
|
|
1490
|
+
if (_needsScrollObserverReattachAfterMount) {
|
|
1491
|
+
[self attachScrollObserverForCurrentPage];
|
|
1492
|
+
return;
|
|
1493
|
+
}
|
|
1494
|
+
UIView *page = _pageControllers[_currentIndex].view;
|
|
1495
|
+
BOOL headerIsAttached = [_sharedHeaderHostView isDescendantOfView:self];
|
|
1496
|
+
BOOL currentListIsAttached = _observedScrollView != nil &&
|
|
1497
|
+
[_observedScrollView isDescendantOfView:page] && headerIsAttached;
|
|
1498
|
+
BOOL currentListIsFallback = currentListIsAttached &&
|
|
1499
|
+
[_observedScrollView isKindOfClass:UICollectionView.class];
|
|
1500
|
+
if (currentListIsAttached && !currentListIsFallback &&
|
|
1501
|
+
_releasedPageScrollStates.count == 0) return;
|
|
1502
|
+
if (!headerIsAttached) {
|
|
1503
|
+
[self attachScrollObserverForCurrentPage];
|
|
1504
|
+
return;
|
|
1505
|
+
}
|
|
1506
|
+
// Property-only updates cannot replace a list. Collect changed parents first
|
|
1507
|
+
// so a burst of unrelated mutations scans the current page at most once.
|
|
1508
|
+
std::unordered_set<NSInteger> changedParentTags;
|
|
1509
|
+
for (const auto &mutation : transaction.getMutations()) {
|
|
1510
|
+
if ((mutation.type == ShadowViewMutation::Insert ||
|
|
1511
|
+
mutation.type == ShadowViewMutation::Remove) && mutation.parentTag > 0) {
|
|
1512
|
+
changedParentTags.insert(mutation.parentTag);
|
|
1513
|
+
}
|
|
1514
|
+
}
|
|
1515
|
+
if (changedParentTags.empty()) return;
|
|
1516
|
+
|
|
1517
|
+
// A missing list can return in a later transaction. Inspect only the current
|
|
1518
|
+
// page needing an observer and pages whose released identities are retained.
|
|
1519
|
+
for (NSInteger index = 0; index < _pageControllers.count; index++) {
|
|
1520
|
+
BOOL hasReleasedList = _releasedPageScrollStates[[self pageKeyForIndex:index]] != nil;
|
|
1521
|
+
BOOL currentPageNeedsResolve = index == _currentIndex &&
|
|
1522
|
+
(!currentListIsAttached || currentListIsFallback);
|
|
1523
|
+
if (!hasReleasedList && !currentPageNeedsResolve) continue;
|
|
1524
|
+
NSMutableArray<UIView *> *pendingViews =
|
|
1525
|
+
[NSMutableArray arrayWithObject:_pageControllers[index].view];
|
|
1526
|
+
while (pendingViews.count > 0) {
|
|
1527
|
+
UIView *view = pendingViews.lastObject;
|
|
1528
|
+
[pendingViews removeLastObject];
|
|
1529
|
+
if (changedParentTags.find(view.tag) != changedParentTags.end()) {
|
|
1530
|
+
if (hasReleasedList) _needsScrollObserverReattachAfterMount = YES;
|
|
1531
|
+
[self attachScrollObserverForCurrentPage];
|
|
1532
|
+
return;
|
|
1533
|
+
}
|
|
1534
|
+
[pendingViews addObjectsFromArray:view.subviews];
|
|
1535
|
+
}
|
|
1393
1536
|
}
|
|
1394
|
-
if (shouldResolve) [self attachScrollObserverForCurrentPage];
|
|
1395
1537
|
}
|
|
1396
1538
|
|
|
1397
1539
|
- (void)mountChildComponentView:(UIView<RCTComponentViewProtocol> *)childComponentView
|
|
@@ -1573,6 +1715,8 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
1573
1715
|
{
|
|
1574
1716
|
_isBeingRecycled = YES;
|
|
1575
1717
|
_generation++;
|
|
1718
|
+
_needsScrollObserverReattachAfterMount = NO;
|
|
1719
|
+
[_releasedPageScrollStates removeAllObjects];
|
|
1576
1720
|
[self detachSharedHeaderPressCancellationGesture];
|
|
1577
1721
|
[self detachScrollObserver];
|
|
1578
1722
|
[self restoreSharedHeadersToContainer];
|
|
@@ -1943,6 +2087,18 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
1943
2087
|
|
|
1944
2088
|
- (void)attachScrollObserverForCurrentPage
|
|
1945
2089
|
{
|
|
2090
|
+
[self attachScrollObserverForCurrentPageAfterTransition:NO];
|
|
2091
|
+
}
|
|
2092
|
+
|
|
2093
|
+
// OneKey patch: selection completion restores list state synchronously, before
|
|
2094
|
+
// the transition flags are cleared on the next run loop. Other callers wait.
|
|
2095
|
+
- (void)attachScrollObserverForCurrentPageAfterTransition:(BOOL)selectionSettled
|
|
2096
|
+
{
|
|
2097
|
+
if (_isBeingRecycled) return;
|
|
2098
|
+
if (_needsScrollObserverReattachAfterMount) {
|
|
2099
|
+
if (self.window == nil || ((_transitioning || _isPagerDragging) && !selectionSettled)) return;
|
|
2100
|
+
[self prepareAdjacentPageInsets];
|
|
2101
|
+
}
|
|
1946
2102
|
[self restoreDetachedScrollInsets];
|
|
1947
2103
|
if (_scrollResolveRetryPageIndex != _currentIndex) {
|
|
1948
2104
|
_scrollResolveRetryToken += 1;
|
|
@@ -1956,7 +2112,18 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
1956
2112
|
if (candidate == nil) {
|
|
1957
2113
|
[self detachScrollObserver];
|
|
1958
2114
|
[self restoreSharedHeadersToContainer];
|
|
1959
|
-
|
|
2115
|
+
// Stop retrying released lists on unrelated commits without discarding a
|
|
2116
|
+
// surviving identity or offset. A relevant insertion resumes recovery.
|
|
2117
|
+
if (_needsScrollObserverReattachAfterMount) {
|
|
2118
|
+
_needsScrollObserverReattachAfterMount = NO;
|
|
2119
|
+
_scrollResolveRetryToken += 1;
|
|
2120
|
+
_scrollResolveRetryScheduled = NO;
|
|
2121
|
+
_scrollResolveRetryAttempt = 0;
|
|
2122
|
+
_pendingFallbackScrollView = nil;
|
|
2123
|
+
_pendingFallbackScrollViewDetectedAt = 0;
|
|
2124
|
+
} else {
|
|
2125
|
+
[self scheduleScrollObserverRetryForCurrentPage];
|
|
2126
|
+
}
|
|
1960
2127
|
return;
|
|
1961
2128
|
}
|
|
1962
2129
|
if ([candidate isKindOfClass:UICollectionView.class] &&
|
|
@@ -1966,7 +2133,8 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
1966
2133
|
_pendingFallbackScrollViewDetectedAt = CACurrentMediaTime();
|
|
1967
2134
|
_scrollResolveRetryAttempt = 0;
|
|
1968
2135
|
}
|
|
1969
|
-
if (CACurrentMediaTime() - _pendingFallbackScrollViewDetectedAt <
|
|
2136
|
+
if (CACurrentMediaTime() - _pendingFallbackScrollViewDetectedAt <
|
|
2137
|
+
RNCCollapsiblePagerFallbackDebounceInterval) {
|
|
1970
2138
|
[self scheduleScrollObserverRetryForCurrentPage];
|
|
1971
2139
|
return;
|
|
1972
2140
|
}
|
|
@@ -1983,6 +2151,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
1983
2151
|
[candidate.panGestureRecognizer requireGestureRecognizerToFail:_pagerScrollView.panGestureRecognizer];
|
|
1984
2152
|
}
|
|
1985
2153
|
if (candidate == _observedScrollView) {
|
|
2154
|
+
_needsScrollObserverReattachAfterMount = NO;
|
|
1986
2155
|
[self attachSharedHeadersToScrollView:candidate];
|
|
1987
2156
|
[self updateSharedHeaderPressCancellationGesture];
|
|
1988
2157
|
return;
|
|
@@ -2006,6 +2175,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2006
2175
|
context:RNCCollapsiblePagerContentOffsetContext];
|
|
2007
2176
|
[candidate.panGestureRecognizer addTarget:self action:@selector(observedListPanChanged:)];
|
|
2008
2177
|
_observingContentOffset = YES;
|
|
2178
|
+
_needsScrollObserverReattachAfterMount = NO;
|
|
2009
2179
|
_currentLogicalOffset = candidate.contentOffset.y + candidate.contentInset.top;
|
|
2010
2180
|
[self attachSharedHeadersToScrollView:candidate];
|
|
2011
2181
|
[self updateSharedHeaderPressCancellationGesture];
|
|
@@ -2235,6 +2405,39 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2235
2405
|
}
|
|
2236
2406
|
}
|
|
2237
2407
|
|
|
2408
|
+
// OneKey patch: release observers, headers and saved state while the scroll view
|
|
2409
|
+
// still represents the old list, before Fabric applies its next owner's props.
|
|
2410
|
+
- (void)restoreScrollViewState:(UIScrollView *)scrollView resetSavedOffset:(BOOL)resetSavedOffset
|
|
2411
|
+
{
|
|
2412
|
+
if (scrollView == _observedScrollView) {
|
|
2413
|
+
[self detachScrollObserver];
|
|
2414
|
+
if (resetSavedOffset) _pageOffsets[self.currentPageKey] = @(_headerOffset);
|
|
2415
|
+
}
|
|
2416
|
+
if (scrollView == _sharedHeaderScrollView) {
|
|
2417
|
+
[self restoreSharedHeadersToContainer];
|
|
2418
|
+
}
|
|
2419
|
+
CGFloat logicalOffset = scrollView.contentOffset.y + scrollView.contentInset.top;
|
|
2420
|
+
UIEdgeInsets original = [_originalInsets objectForKey:scrollView].UIEdgeInsetsValue;
|
|
2421
|
+
NSNumber *appliedTop = [_appliedTopInsets objectForKey:scrollView];
|
|
2422
|
+
if (appliedTop != nil && scrollView.refreshControl != nil) {
|
|
2423
|
+
original.top += MAX(0, scrollView.contentInset.top - appliedTop.doubleValue);
|
|
2424
|
+
}
|
|
2425
|
+
scrollView.contentInset = original;
|
|
2426
|
+
scrollView.verticalScrollIndicatorInsets =
|
|
2427
|
+
[_originalIndicatorInsets objectForKey:scrollView].UIEdgeInsetsValue;
|
|
2428
|
+
scrollView.alwaysBounceVertical =
|
|
2429
|
+
[_originalAlwaysBounceVertical objectForKey:scrollView].boolValue;
|
|
2430
|
+
scrollView.contentInsetAdjustmentBehavior = (UIScrollViewContentInsetAdjustmentBehavior)
|
|
2431
|
+
[_originalInsetAdjustmentBehavior objectForKey:scrollView].integerValue;
|
|
2432
|
+
[scrollView setContentOffset:CGPointMake(scrollView.contentOffset.x, logicalOffset - original.top)
|
|
2433
|
+
animated:NO];
|
|
2434
|
+
[_originalInsets removeObjectForKey:scrollView];
|
|
2435
|
+
[_appliedTopInsets removeObjectForKey:scrollView];
|
|
2436
|
+
[_originalIndicatorInsets removeObjectForKey:scrollView];
|
|
2437
|
+
[_originalAlwaysBounceVertical removeObjectForKey:scrollView];
|
|
2438
|
+
[_originalInsetAdjustmentBehavior removeObjectForKey:scrollView];
|
|
2439
|
+
}
|
|
2440
|
+
|
|
2238
2441
|
- (void)restoreDetachedScrollInsets
|
|
2239
2442
|
{
|
|
2240
2443
|
for (UIScrollView *scrollView in _originalInsets.keyEnumerator.allObjects) {
|
|
@@ -2246,32 +2449,8 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2246
2449
|
}
|
|
2247
2450
|
}
|
|
2248
2451
|
if (belongsToPage) continue;
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
_pageOffsets[self.currentPageKey] = @(_headerOffset);
|
|
2252
|
-
}
|
|
2253
|
-
// Fabric can reuse an empty RN ScrollView in another screen. Release all
|
|
2254
|
-
// pager-owned insets before that view is used as a search or dialog list.
|
|
2255
|
-
CGFloat logicalOffset = scrollView.contentOffset.y + scrollView.contentInset.top;
|
|
2256
|
-
UIEdgeInsets original = [_originalInsets objectForKey:scrollView].UIEdgeInsetsValue;
|
|
2257
|
-
NSNumber *appliedTop = [_appliedTopInsets objectForKey:scrollView];
|
|
2258
|
-
if (appliedTop != nil && scrollView.refreshControl != nil) {
|
|
2259
|
-
original.top += MAX(0, scrollView.contentInset.top - appliedTop.doubleValue);
|
|
2260
|
-
}
|
|
2261
|
-
scrollView.contentInset = original;
|
|
2262
|
-
scrollView.verticalScrollIndicatorInsets =
|
|
2263
|
-
[_originalIndicatorInsets objectForKey:scrollView].UIEdgeInsetsValue;
|
|
2264
|
-
scrollView.alwaysBounceVertical =
|
|
2265
|
-
[_originalAlwaysBounceVertical objectForKey:scrollView].boolValue;
|
|
2266
|
-
scrollView.contentInsetAdjustmentBehavior = (UIScrollViewContentInsetAdjustmentBehavior)
|
|
2267
|
-
[_originalInsetAdjustmentBehavior objectForKey:scrollView].integerValue;
|
|
2268
|
-
[scrollView setContentOffset:CGPointMake(scrollView.contentOffset.x, logicalOffset - original.top)
|
|
2269
|
-
animated:NO];
|
|
2270
|
-
[_originalInsets removeObjectForKey:scrollView];
|
|
2271
|
-
[_appliedTopInsets removeObjectForKey:scrollView];
|
|
2272
|
-
[_originalIndicatorInsets removeObjectForKey:scrollView];
|
|
2273
|
-
[_originalAlwaysBounceVertical removeObjectForKey:scrollView];
|
|
2274
|
-
[_originalInsetAdjustmentBehavior removeObjectForKey:scrollView];
|
|
2452
|
+
// OneKey patch: share the full release path with pre-mount recycling.
|
|
2453
|
+
[self restoreScrollViewState:scrollView resetSavedOffset:YES];
|
|
2275
2454
|
}
|
|
2276
2455
|
}
|
|
2277
2456
|
|
|
@@ -2279,6 +2458,57 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2279
2458
|
pageIndex:(NSInteger)pageIndex
|
|
2280
2459
|
restore:(BOOL)restore
|
|
2281
2460
|
{
|
|
2461
|
+
// OneKey patch: a surviving list keeps its full offset. A replacement keeps
|
|
2462
|
+
// only header collapse, even if Fabric reused the same native scroll object.
|
|
2463
|
+
NSString *pageKey = [self pageKeyForIndex:pageIndex];
|
|
2464
|
+
RNCCollapsiblePagerReleasedScrollState *released = _releasedPageScrollStates[pageKey];
|
|
2465
|
+
BOOL deferredReleasedIdentity = NO;
|
|
2466
|
+
if (released != nil) {
|
|
2467
|
+
UIView *componentView = RNCScrollComponentView(scrollView);
|
|
2468
|
+
BOOL sameList = released.scrollView == scrollView && componentView != nil &&
|
|
2469
|
+
released.componentView == componentView && released.componentTag == componentView.tag;
|
|
2470
|
+
// Adjacent Nitro lists are visible only as generic UICollectionViews, so
|
|
2471
|
+
// debounce each replacement candidate independently before rejecting the
|
|
2472
|
+
// released identity. A surviving native list is safe to accept immediately.
|
|
2473
|
+
BOOL needsFallbackDebounce = !sameList &&
|
|
2474
|
+
[scrollView isKindOfClass:UICollectionView.class] && scrollView != _observedScrollView;
|
|
2475
|
+
if (needsFallbackDebounce) {
|
|
2476
|
+
CFTimeInterval now = CACurrentMediaTime();
|
|
2477
|
+
if (released.pendingFallbackScrollView != scrollView) {
|
|
2478
|
+
released.pendingFallbackScrollView = scrollView;
|
|
2479
|
+
released.pendingFallbackDetectedAt = now;
|
|
2480
|
+
__weak __typeof__(self) weakSelf = self;
|
|
2481
|
+
__weak RNCCollapsiblePagerReleasedScrollState *weakReleased = released;
|
|
2482
|
+
__weak UIScrollView *weakScrollView = scrollView;
|
|
2483
|
+
NSString *capturedPageKey = [pageKey copy];
|
|
2484
|
+
NSUInteger capturedGeneration = _generation;
|
|
2485
|
+
dispatch_after(
|
|
2486
|
+
dispatch_time(
|
|
2487
|
+
DISPATCH_TIME_NOW,
|
|
2488
|
+
(int64_t)(RNCCollapsiblePagerFallbackDebounceInterval * NSEC_PER_SEC)
|
|
2489
|
+
),
|
|
2490
|
+
dispatch_get_main_queue(),
|
|
2491
|
+
^{
|
|
2492
|
+
RNCCollapsiblePagerViewComponentView *strongSelf = weakSelf;
|
|
2493
|
+
RNCCollapsiblePagerReleasedScrollState *strongReleased = weakReleased;
|
|
2494
|
+
UIScrollView *strongScrollView = weakScrollView;
|
|
2495
|
+
if (strongSelf == nil || strongReleased == nil || strongScrollView == nil ||
|
|
2496
|
+
strongSelf->_isBeingRecycled || strongSelf.window == nil ||
|
|
2497
|
+
strongSelf->_generation != capturedGeneration ||
|
|
2498
|
+
strongSelf->_releasedPageScrollStates[capturedPageKey] != strongReleased ||
|
|
2499
|
+
strongReleased.pendingFallbackScrollView != strongScrollView) return;
|
|
2500
|
+
[strongSelf prepareAdjacentPageInsets];
|
|
2501
|
+
}
|
|
2502
|
+
);
|
|
2503
|
+
}
|
|
2504
|
+
deferredReleasedIdentity = now - released.pendingFallbackDetectedAt <
|
|
2505
|
+
RNCCollapsiblePagerFallbackDebounceInterval;
|
|
2506
|
+
}
|
|
2507
|
+
if (!deferredReleasedIdentity) {
|
|
2508
|
+
if (!sameList) _pageOffsets[pageKey] = @(_headerOffset);
|
|
2509
|
+
[_releasedPageScrollStates removeObjectForKey:pageKey];
|
|
2510
|
+
}
|
|
2511
|
+
}
|
|
2282
2512
|
NSValue *storedOriginal = [_originalInsets objectForKey:scrollView];
|
|
2283
2513
|
UIEdgeInsets original = storedOriginal == nil
|
|
2284
2514
|
? scrollView.contentInset
|
|
@@ -2327,7 +2557,9 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2327
2557
|
NSNumber *saved = _pageOffsets[[self pageKeyForIndex:pageIndex]];
|
|
2328
2558
|
// The sticky slot can change height between pages. Cache offsets relative
|
|
2329
2559
|
// to content start so a round trip does not add that height difference.
|
|
2330
|
-
CGFloat restoredLogicalOffset = saved == nil
|
|
2560
|
+
CGFloat restoredLogicalOffset = deferredReleasedIdentity || saved == nil
|
|
2561
|
+
? _headerOffset
|
|
2562
|
+
: saved.doubleValue;
|
|
2331
2563
|
// While the shared header is visible, retained pages must agree on its
|
|
2332
2564
|
// collapse position. Deep offsets remain independent after full collapse.
|
|
2333
2565
|
if (_nativeSmoothHeaderScrollEnabled && _headerOffset < _headerHeight) {
|
|
@@ -2483,7 +2715,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2483
2715
|
self->_currentIndex = index;
|
|
2484
2716
|
self->_destinationIndex = index;
|
|
2485
2717
|
[self->_nativeTabBarView setProgress:index];
|
|
2486
|
-
[self
|
|
2718
|
+
[self attachScrollObserverForCurrentPageAfterTransition:YES];
|
|
2487
2719
|
[self emitPageSelected:index];
|
|
2488
2720
|
[self emitDiagnostics:@"page-selected"];
|
|
2489
2721
|
} else if (self->_pendingGoToIndex < 0) {
|
|
@@ -2520,7 +2752,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2520
2752
|
self->_currentIndex = index;
|
|
2521
2753
|
self->_destinationIndex = index;
|
|
2522
2754
|
[self->_nativeTabBarView setProgress:index];
|
|
2523
|
-
[self
|
|
2755
|
+
[self attachScrollObserverForCurrentPageAfterTransition:YES];
|
|
2524
2756
|
[self emitPageSelected:index];
|
|
2525
2757
|
[self emitDiagnostics:@"page-selected"];
|
|
2526
2758
|
} else {
|
|
@@ -2660,7 +2892,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2660
2892
|
0
|
|
2661
2893
|
) animated:NO];
|
|
2662
2894
|
[_nativeTabBarView setProgress:index];
|
|
2663
|
-
[self
|
|
2895
|
+
[self attachScrollObserverForCurrentPageAfterTransition:YES];
|
|
2664
2896
|
if (emitSelection &&
|
|
2665
2897
|
(_pendingGoToIndex < 0 || _pendingGoToIndex == index)) {
|
|
2666
2898
|
[self emitPageSelected:index];
|
|
@@ -2711,7 +2943,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2711
2943
|
_currentIndex = index;
|
|
2712
2944
|
_destinationIndex = index;
|
|
2713
2945
|
[_nativeTabBarView setProgress:index];
|
|
2714
|
-
[self
|
|
2946
|
+
[self attachScrollObserverForCurrentPageAfterTransition:YES];
|
|
2715
2947
|
// A newer tab tap remains authoritative while its queued command runs.
|
|
2716
2948
|
if (emitSelection &&
|
|
2717
2949
|
(_pendingGoToIndex < 0 || _pendingGoToIndex == index)) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onekeyfe/react-native-pager-view",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.134",
|
|
4
4
|
"description": "React Native wrapper for Android and iOS ViewPager",
|
|
5
5
|
"source": "./src/index.tsx",
|
|
6
6
|
"main": "./lib/module/index.js",
|
|
@@ -39,7 +39,6 @@
|
|
|
39
39
|
"prepare": "rm -rf lib && bob build",
|
|
40
40
|
"typecheck": "tsc -b",
|
|
41
41
|
"lint": "eslint \"**/*.{js,ts,tsx}\"",
|
|
42
|
-
"test": "node --test src/__tests__/*.cjs",
|
|
43
42
|
"release": "yarn prepare && npm whoami && npm publish --access public"
|
|
44
43
|
},
|
|
45
44
|
"keywords": [
|
|
@@ -67,7 +66,7 @@
|
|
|
67
66
|
"typescript": "^5.9.2"
|
|
68
67
|
},
|
|
69
68
|
"peerDependencies": {
|
|
70
|
-
"@onekeyfe/react-native-native-logger": "3.0.
|
|
69
|
+
"@onekeyfe/react-native-native-logger": "3.0.134",
|
|
71
70
|
"react": "*",
|
|
72
71
|
"react-native": "*"
|
|
73
72
|
},
|