mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-23 06:35:51 +00:00
Fix Pager Node Issues (#3028)
* Fix pager node and deprecate zeroContentInsets flag * Do it with the visible state callback * There we are * Put viewController in node debug description
This commit is contained in:
@@ -327,6 +327,10 @@
|
|||||||
CC4C2A791D88E3BF0039ACAB /* ASTraceEvent.m in Sources */ = {isa = PBXBuildFile; fileRef = CC4C2A761D88E3BF0039ACAB /* ASTraceEvent.m */; };
|
CC4C2A791D88E3BF0039ACAB /* ASTraceEvent.m in Sources */ = {isa = PBXBuildFile; fileRef = CC4C2A761D88E3BF0039ACAB /* ASTraceEvent.m */; };
|
||||||
CC54A81C1D70079800296A24 /* ASDispatch.h in Headers */ = {isa = PBXBuildFile; fileRef = CC54A81B1D70077A00296A24 /* ASDispatch.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
CC54A81C1D70079800296A24 /* ASDispatch.h in Headers */ = {isa = PBXBuildFile; fileRef = CC54A81B1D70077A00296A24 /* ASDispatch.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||||
CC54A81E1D7008B300296A24 /* ASDispatchTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CC54A81D1D7008B300296A24 /* ASDispatchTests.m */; };
|
CC54A81E1D7008B300296A24 /* ASDispatchTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CC54A81D1D7008B300296A24 /* ASDispatchTests.m */; };
|
||||||
|
CC55A70D1E529FA200594372 /* UIResponder+AsyncDisplayKit.h in Headers */ = {isa = PBXBuildFile; fileRef = CC55A70B1E529FA200594372 /* UIResponder+AsyncDisplayKit.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||||
|
CC55A70E1E529FA200594372 /* UIResponder+AsyncDisplayKit.m in Sources */ = {isa = PBXBuildFile; fileRef = CC55A70C1E529FA200594372 /* UIResponder+AsyncDisplayKit.m */; };
|
||||||
|
CC55A7111E52A0F200594372 /* ASResponderChainEnumerator.h in Headers */ = {isa = PBXBuildFile; fileRef = CC55A70F1E52A0F200594372 /* ASResponderChainEnumerator.h */; };
|
||||||
|
CC55A7121E52A0F200594372 /* ASResponderChainEnumerator.m in Sources */ = {isa = PBXBuildFile; fileRef = CC55A7101E52A0F200594372 /* ASResponderChainEnumerator.m */; };
|
||||||
CC57EAF71E3939350034C595 /* ASCollectionView+Undeprecated.h in Headers */ = {isa = PBXBuildFile; fileRef = CC2E317F1DAC353700EEE891 /* ASCollectionView+Undeprecated.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
CC57EAF71E3939350034C595 /* ASCollectionView+Undeprecated.h in Headers */ = {isa = PBXBuildFile; fileRef = CC2E317F1DAC353700EEE891 /* ASCollectionView+Undeprecated.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||||
CC57EAF81E3939450034C595 /* ASTableView+Undeprecated.h in Headers */ = {isa = PBXBuildFile; fileRef = CC512B841DAC45C60054848E /* ASTableView+Undeprecated.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
CC57EAF81E3939450034C595 /* ASTableView+Undeprecated.h in Headers */ = {isa = PBXBuildFile; fileRef = CC512B841DAC45C60054848E /* ASTableView+Undeprecated.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||||
CC58AA4B1E398E1D002C8CB4 /* ASBlockTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = CC58AA4A1E398E1D002C8CB4 /* ASBlockTypes.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
CC58AA4B1E398E1D002C8CB4 /* ASBlockTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = CC58AA4A1E398E1D002C8CB4 /* ASBlockTypes.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||||
@@ -711,6 +715,10 @@
|
|||||||
CC512B841DAC45C60054848E /* ASTableView+Undeprecated.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ASTableView+Undeprecated.h"; sourceTree = "<group>"; };
|
CC512B841DAC45C60054848E /* ASTableView+Undeprecated.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ASTableView+Undeprecated.h"; sourceTree = "<group>"; };
|
||||||
CC54A81B1D70077A00296A24 /* ASDispatch.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ASDispatch.h; sourceTree = "<group>"; };
|
CC54A81B1D70077A00296A24 /* ASDispatch.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ASDispatch.h; sourceTree = "<group>"; };
|
||||||
CC54A81D1D7008B300296A24 /* ASDispatchTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = ASDispatchTests.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
|
CC54A81D1D7008B300296A24 /* ASDispatchTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = ASDispatchTests.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
|
||||||
|
CC55A70B1E529FA200594372 /* UIResponder+AsyncDisplayKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIResponder+AsyncDisplayKit.h"; sourceTree = "<group>"; };
|
||||||
|
CC55A70C1E529FA200594372 /* UIResponder+AsyncDisplayKit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIResponder+AsyncDisplayKit.m"; sourceTree = "<group>"; };
|
||||||
|
CC55A70F1E52A0F200594372 /* ASResponderChainEnumerator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASResponderChainEnumerator.h; sourceTree = "<group>"; };
|
||||||
|
CC55A7101E52A0F200594372 /* ASResponderChainEnumerator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASResponderChainEnumerator.m; sourceTree = "<group>"; };
|
||||||
CC57EAF91E394EA40034C595 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
CC57EAF91E394EA40034C595 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
CC58AA4A1E398E1D002C8CB4 /* ASBlockTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASBlockTypes.h; sourceTree = "<group>"; };
|
CC58AA4A1E398E1D002C8CB4 /* ASBlockTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASBlockTypes.h; sourceTree = "<group>"; };
|
||||||
CC7FD9DC1BB5E962005CCB2B /* ASPhotosFrameworkImageRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASPhotosFrameworkImageRequest.h; sourceTree = "<group>"; };
|
CC7FD9DC1BB5E962005CCB2B /* ASPhotosFrameworkImageRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASPhotosFrameworkImageRequest.h; sourceTree = "<group>"; };
|
||||||
@@ -927,6 +935,8 @@
|
|||||||
DB55C2651C641AE4004EDCF5 /* ASContextTransitioning.h */,
|
DB55C2651C641AE4004EDCF5 /* ASContextTransitioning.h */,
|
||||||
68FC85E71CE29C7D00EDD713 /* ASVisibilityProtocols.h */,
|
68FC85E71CE29C7D00EDD713 /* ASVisibilityProtocols.h */,
|
||||||
68FC85E81CE29C7D00EDD713 /* ASVisibilityProtocols.m */,
|
68FC85E81CE29C7D00EDD713 /* ASVisibilityProtocols.m */,
|
||||||
|
CC55A70B1E529FA200594372 /* UIResponder+AsyncDisplayKit.h */,
|
||||||
|
CC55A70C1E529FA200594372 /* UIResponder+AsyncDisplayKit.m */,
|
||||||
690ED5911E36D118000627C0 /* tvOS */,
|
690ED5911E36D118000627C0 /* tvOS */,
|
||||||
DE89C1691DCEB9CC00D49D74 /* Debug */,
|
DE89C1691DCEB9CC00D49D74 /* Debug */,
|
||||||
058D09E1195D050800B7D73C /* Details */,
|
058D09E1195D050800B7D73C /* Details */,
|
||||||
@@ -1120,6 +1130,8 @@
|
|||||||
058D0A01195D050800B7D73C /* Private */ = {
|
058D0A01195D050800B7D73C /* Private */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
CC55A70F1E52A0F200594372 /* ASResponderChainEnumerator.h */,
|
||||||
|
CC55A7101E52A0F200594372 /* ASResponderChainEnumerator.m */,
|
||||||
6947B0BB1E36B4E30007C478 /* Layout */,
|
6947B0BB1E36B4E30007C478 /* Layout */,
|
||||||
CCE04B2A1E313EDA006AEBBB /* Collection Data Adapter */,
|
CCE04B2A1E313EDA006AEBBB /* Collection Data Adapter */,
|
||||||
058D0A03195D050800B7D73C /* _ASCoreAnimationExtras.h */,
|
058D0A03195D050800B7D73C /* _ASCoreAnimationExtras.h */,
|
||||||
@@ -1398,6 +1410,7 @@
|
|||||||
B35062151B010EFD0018CF92 /* ASBatchContext.h in Headers */,
|
B35062151B010EFD0018CF92 /* ASBatchContext.h in Headers */,
|
||||||
B35061F31B010EFD0018CF92 /* ASCellNode.h in Headers */,
|
B35061F31B010EFD0018CF92 /* ASCellNode.h in Headers */,
|
||||||
34EFC7631B701CBF00AD841F /* ASCenterLayoutSpec.h in Headers */,
|
34EFC7631B701CBF00AD841F /* ASCenterLayoutSpec.h in Headers */,
|
||||||
|
CC55A7111E52A0F200594372 /* ASResponderChainEnumerator.h in Headers */,
|
||||||
18C2ED7F1B9B7DE800F627B3 /* ASCollectionNode.h in Headers */,
|
18C2ED7F1B9B7DE800F627B3 /* ASCollectionNode.h in Headers */,
|
||||||
B35061F51B010EFD0018CF92 /* ASCollectionView.h in Headers */,
|
B35061F51B010EFD0018CF92 /* ASCollectionView.h in Headers */,
|
||||||
ACE87A2C1D73696800D7FF06 /* ASSectionContext.h in Headers */,
|
ACE87A2C1D73696800D7FF06 /* ASSectionContext.h in Headers */,
|
||||||
@@ -1441,6 +1454,7 @@
|
|||||||
254C6B771BF94DF4003EC431 /* ASTextKitAttributes.h in Headers */,
|
254C6B771BF94DF4003EC431 /* ASTextKitAttributes.h in Headers */,
|
||||||
254C6B7D1BF94DF4003EC431 /* ASTextKitShadower.h in Headers */,
|
254C6B7D1BF94DF4003EC431 /* ASTextKitShadower.h in Headers */,
|
||||||
690ED58E1E36BCA6000627C0 /* ASLayoutElementStylePrivate.h in Headers */,
|
690ED58E1E36BCA6000627C0 /* ASLayoutElementStylePrivate.h in Headers */,
|
||||||
|
CC55A70D1E529FA200594372 /* UIResponder+AsyncDisplayKit.h in Headers */,
|
||||||
254C6B731BF94DF4003EC431 /* ASTextKitCoreTextAdditions.h in Headers */,
|
254C6B731BF94DF4003EC431 /* ASTextKitCoreTextAdditions.h in Headers */,
|
||||||
254C6B7A1BF94DF4003EC431 /* ASTextKitRenderer.h in Headers */,
|
254C6B7A1BF94DF4003EC431 /* ASTextKitRenderer.h in Headers */,
|
||||||
69CB62AC1CB8165900024920 /* _ASDisplayViewAccessiblity.h in Headers */,
|
69CB62AC1CB8165900024920 /* _ASDisplayViewAccessiblity.h in Headers */,
|
||||||
@@ -1827,6 +1841,7 @@
|
|||||||
18C2ED831B9B7DE800F627B3 /* ASCollectionNode.mm in Sources */,
|
18C2ED831B9B7DE800F627B3 /* ASCollectionNode.mm in Sources */,
|
||||||
E55D86331CA8A14000A0C26F /* ASLayoutElement.mm in Sources */,
|
E55D86331CA8A14000A0C26F /* ASLayoutElement.mm in Sources */,
|
||||||
68FC85EC1CE29C7D00EDD713 /* ASVisibilityProtocols.m in Sources */,
|
68FC85EC1CE29C7D00EDD713 /* ASVisibilityProtocols.m in Sources */,
|
||||||
|
CC55A7121E52A0F200594372 /* ASResponderChainEnumerator.m in Sources */,
|
||||||
68B8A4E41CBDB958007E4543 /* ASWeakProxy.m in Sources */,
|
68B8A4E41CBDB958007E4543 /* ASWeakProxy.m in Sources */,
|
||||||
9C70F20A1CDBE949007D6C76 /* ASTableNode.mm in Sources */,
|
9C70F20A1CDBE949007D6C76 /* ASTableNode.mm in Sources */,
|
||||||
69CB62AE1CB8165900024920 /* _ASDisplayViewAccessiblity.mm in Sources */,
|
69CB62AE1CB8165900024920 /* _ASDisplayViewAccessiblity.mm in Sources */,
|
||||||
@@ -1905,6 +1920,7 @@
|
|||||||
254C6B871BF94F8A003EC431 /* ASTextKitEntityAttribute.m in Sources */,
|
254C6B871BF94F8A003EC431 /* ASTextKitEntityAttribute.m in Sources */,
|
||||||
34566CB31BC1213700715E6B /* ASPhotosFrameworkImageRequest.m in Sources */,
|
34566CB31BC1213700715E6B /* ASPhotosFrameworkImageRequest.m in Sources */,
|
||||||
254C6B831BF94F8A003EC431 /* ASTextKitCoreTextAdditions.m in Sources */,
|
254C6B831BF94F8A003EC431 /* ASTextKitCoreTextAdditions.m in Sources */,
|
||||||
|
CC55A70E1E529FA200594372 /* UIResponder+AsyncDisplayKit.m in Sources */,
|
||||||
697796611D8AC8D3007E93D7 /* ASLayoutSpec+Subclasses.mm in Sources */,
|
697796611D8AC8D3007E93D7 /* ASLayoutSpec+Subclasses.mm in Sources */,
|
||||||
B350623B1B010EFD0018CF92 /* NSMutableAttributedString+TextKitAdditions.m in Sources */,
|
B350623B1B010EFD0018CF92 /* NSMutableAttributedString+TextKitAdditions.m in Sources */,
|
||||||
044284FD1BAA365100D16268 /* UICollectionViewLayout+ASConvenience.m in Sources */,
|
044284FD1BAA365100D16268 /* UICollectionViewLayout+ASConvenience.m in Sources */,
|
||||||
|
|||||||
@@ -111,6 +111,10 @@ NS_ASSUME_NONNULL_BEGIN
|
|||||||
*/
|
*/
|
||||||
@property (nonatomic, assign) BOOL inverted;
|
@property (nonatomic, assign) BOOL inverted;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface ASCollectionView (Deprecated)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Forces the .contentInset to be UIEdgeInsetsZero.
|
* Forces the .contentInset to be UIEdgeInsetsZero.
|
||||||
*
|
*
|
||||||
@@ -119,11 +123,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||||||
* automaticallyAdjustsScrollViewInsets, which may not be accessible. ASPagerNode uses this to ensure
|
* automaticallyAdjustsScrollViewInsets, which may not be accessible. ASPagerNode uses this to ensure
|
||||||
* its flow layout behaves predictably and does not log undefined layout warnings.
|
* its flow layout behaves predictably and does not log undefined layout warnings.
|
||||||
*/
|
*/
|
||||||
@property (nonatomic) BOOL zeroContentInsets;
|
@property (nonatomic) BOOL zeroContentInsets ASDISPLAYNODE_DEPRECATED_MSG("Set automaticallyAdjustsScrollViewInsets=NO on your view controller instead.");
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@interface ASCollectionView (Deprecated)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The object that acts as the asynchronous delegate of the collection view
|
* The object that acts as the asynchronous delegate of the collection view
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
#import <AsyncDisplayKit/_ASCollectionViewCell.h>
|
#import <AsyncDisplayKit/_ASCollectionViewCell.h>
|
||||||
#import <AsyncDisplayKit/_ASDisplayLayer.h>
|
#import <AsyncDisplayKit/_ASDisplayLayer.h>
|
||||||
#import <AsyncDisplayKit/ASCollectionViewLayoutFacilitatorProtocol.h>
|
#import <AsyncDisplayKit/ASCollectionViewLayoutFacilitatorProtocol.h>
|
||||||
|
#import <AsyncDisplayKit/ASPagerNode.h>
|
||||||
#import <AsyncDisplayKit/ASSectionContext.h>
|
#import <AsyncDisplayKit/ASSectionContext.h>
|
||||||
#import <AsyncDisplayKit/ASCollectionView+Undeprecated.h>
|
#import <AsyncDisplayKit/ASCollectionView+Undeprecated.h>
|
||||||
#import <AsyncDisplayKit/_ASHierarchyChangeSet.h>
|
#import <AsyncDisplayKit/_ASHierarchyChangeSet.h>
|
||||||
@@ -93,6 +94,8 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier";
|
|||||||
NSMutableSet *_registeredSupplementaryKinds;
|
NSMutableSet *_registeredSupplementaryKinds;
|
||||||
|
|
||||||
CGPoint _deceleratingVelocity;
|
CGPoint _deceleratingVelocity;
|
||||||
|
|
||||||
|
BOOL _zeroContentInsets;
|
||||||
|
|
||||||
ASCollectionViewInvalidationStyle _nextLayoutInvalidationStyle;
|
ASCollectionViewInvalidationStyle _nextLayoutInvalidationStyle;
|
||||||
|
|
||||||
@@ -584,6 +587,16 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier";
|
|||||||
return [_rangeController tuningParametersForRangeMode:rangeMode rangeType:rangeType];
|
return [_rangeController tuningParametersForRangeMode:rangeMode rangeType:rangeType];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)setZeroContentInsets:(BOOL)zeroContentInsets
|
||||||
|
{
|
||||||
|
_zeroContentInsets = zeroContentInsets;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)zeroContentInsets
|
||||||
|
{
|
||||||
|
return _zeroContentInsets;
|
||||||
|
}
|
||||||
|
|
||||||
- (CGSize)calculatedSizeForNodeAtIndexPath:(NSIndexPath *)indexPath
|
- (CGSize)calculatedSizeForNodeAtIndexPath:(NSIndexPath *)indexPath
|
||||||
{
|
{
|
||||||
return [[self nodeForItemAtIndexPath:indexPath] calculatedSize];
|
return [[self nodeForItemAtIndexPath:indexPath] calculatedSize];
|
||||||
@@ -1911,19 +1924,11 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier";
|
|||||||
BOOL changedInNonScrollingDirection = (fixedHorizontally && newBounds.size.width != lastUsedSize.width) || (fixedVertically && newBounds.size.height != lastUsedSize.height);
|
BOOL changedInNonScrollingDirection = (fixedHorizontally && newBounds.size.width != lastUsedSize.width) || (fixedVertically && newBounds.size.height != lastUsedSize.height);
|
||||||
|
|
||||||
if (changedInNonScrollingDirection) {
|
if (changedInNonScrollingDirection) {
|
||||||
#pragma clang diagnostic push
|
[_dataController relayoutAllNodes];
|
||||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
[_dataController waitUntilAllUpdatesAreCommitted];
|
||||||
// This actually doesn't perform an animation, but prevents the transaction block from being processed in the
|
|
||||||
// data controller's prevent animation block that would interrupt an interrupted relayout happening in an animation block
|
|
||||||
// ie. ASCollectionView bounds change on rotation or multi-tasking split view resize.
|
|
||||||
[self performBatchAnimated:YES updates:^{
|
|
||||||
[_dataController relayoutAllNodes];
|
|
||||||
} completion:nil];
|
|
||||||
// We need to ensure the size requery is done before we update our layout.
|
// We need to ensure the size requery is done before we update our layout.
|
||||||
[self waitUntilAllUpdatesAreCommitted];
|
|
||||||
[self.collectionViewLayout invalidateLayout];
|
[self.collectionViewLayout invalidateLayout];
|
||||||
}
|
}
|
||||||
#pragma clang diagnostic pop
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -36,6 +36,7 @@
|
|||||||
#import <AsyncDisplayKit/ASRunLoopQueue.h>
|
#import <AsyncDisplayKit/ASRunLoopQueue.h>
|
||||||
#import <AsyncDisplayKit/ASTraitCollection.h>
|
#import <AsyncDisplayKit/ASTraitCollection.h>
|
||||||
#import <AsyncDisplayKit/ASWeakProxy.h>
|
#import <AsyncDisplayKit/ASWeakProxy.h>
|
||||||
|
#import <AsyncDisplayKit/ASResponderChainEnumerator.h>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Assert if the current thread owns a mutex.
|
* Assert if the current thread owns a mutex.
|
||||||
@@ -3784,6 +3785,20 @@ ASDISPLAYNODE_INLINE BOOL nodeIsInRasterizedTree(ASDisplayNode *node) {
|
|||||||
[result addObject:@{ @"frameInWindow" : [NSValue valueWithCGRect:windowFrame] }];
|
[result addObject:@{ @"frameInWindow" : [NSValue valueWithCGRect:windowFrame] }];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Attempt to find view controller.
|
||||||
|
// Note that the convenience method asdk_associatedViewController has an assertion
|
||||||
|
// that it's run on main. Since this is a debug method, let's bypass the assertion
|
||||||
|
// and run up the chain ourselves.
|
||||||
|
if (_view != nil) {
|
||||||
|
for (UIResponder *responder in [_view asdk_responderChainEnumerator]) {
|
||||||
|
UIViewController *vc = ASDynamicCast(responder, UIViewController);
|
||||||
|
if (vc) {
|
||||||
|
[result addObject:@{ @"viewController" : ASObjectDescriptionMakeTiny(vc) }];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (_view != nil) {
|
if (_view != nil) {
|
||||||
[result addObject:@{ @"frame" : [NSValue valueWithCGRect:_view.frame] }];
|
[result addObject:@{ @"frame" : [NSValue valueWithCGRect:_view.frame] }];
|
||||||
} else if (_layer != nil) {
|
} else if (_layer != nil) {
|
||||||
|
|||||||
@@ -11,62 +11,43 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
#import <AsyncDisplayKit/ASPagerFlowLayout.h>
|
#import <AsyncDisplayKit/ASPagerFlowLayout.h>
|
||||||
|
#import <AsyncDisplayKit/ASCellNode.h>
|
||||||
|
#import <AsyncDisplayKit/ASCollectionView.h>
|
||||||
|
|
||||||
@interface ASPagerFlowLayout () {
|
@interface ASPagerFlowLayout () {
|
||||||
BOOL _didRotate;
|
__weak ASCellNode *_currentCellNode;
|
||||||
CGRect _cachedCollectionViewBounds;
|
|
||||||
NSIndexPath *_currentIndexPath;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation ASPagerFlowLayout
|
@implementation ASPagerFlowLayout
|
||||||
|
|
||||||
- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity
|
- (ASCollectionView *)asCollectionView
|
||||||
{
|
{
|
||||||
NSInteger currentPage = ceil(proposedContentOffset.x / self.collectionView.bounds.size.width);
|
// Dynamic cast is too slow and not worth it.
|
||||||
_currentIndexPath = [NSIndexPath indexPathForItem:currentPage inSection:0];
|
return (ASCollectionView *)self.collectionView;
|
||||||
|
|
||||||
return [super targetContentOffsetForProposedContentOffset:proposedContentOffset withScrollingVelocity:velocity];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)prepareLayout
|
||||||
- (void)prepareForAnimatedBoundsChange:(CGRect)oldBounds
|
|
||||||
{
|
{
|
||||||
// Cache the current page if a rotation did happen. This happens before the rotation animation
|
[super prepareLayout];
|
||||||
// is occuring and the bounds changed so we use this as an opportunity to cache the current index path
|
if (_currentCellNode == nil) {
|
||||||
if (_cachedCollectionViewBounds.size.width != self.collectionView.bounds.size.width) {
|
[self _updateCurrentNode];
|
||||||
_cachedCollectionViewBounds = self.collectionView.bounds;
|
}
|
||||||
|
|
||||||
// Figurring out current page based on the old bounds visible space
|
|
||||||
CGRect visibleRect = oldBounds;
|
|
||||||
|
|
||||||
CGFloat visibleXCenter = CGRectGetMidX(visibleRect);
|
|
||||||
NSArray<UICollectionViewLayoutAttributes *> *layoutAttributes = [self layoutAttributesForElementsInRect:visibleRect];
|
|
||||||
for (UICollectionViewLayoutAttributes *attributes in layoutAttributes) {
|
|
||||||
if ([attributes representedElementCategory] == UICollectionElementCategoryCell && attributes.center.x == visibleXCenter) {
|
|
||||||
_currentIndexPath = attributes.indexPath;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_didRotate = YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
[super prepareForAnimatedBoundsChange:oldBounds];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset
|
- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset
|
||||||
{
|
{
|
||||||
// Don't mess around if the user is interacting with the page node. Although if just a rotation happened we should
|
// Don't mess around if the user is interacting with the page node. Although if just a rotation happened we should
|
||||||
// try to use the current index path to not end up setting the target content offset to something in between pages
|
// try to use the current index path to not end up setting the target content offset to something in between pages
|
||||||
if (_didRotate || (!self.collectionView.isDecelerating && !self.collectionView.isTracking)) {
|
if (!self.collectionView.decelerating && !self.collectionView.tracking) {
|
||||||
_didRotate = NO;
|
NSIndexPath *indexPath = [self.asCollectionView indexPathForNode:_currentCellNode];
|
||||||
if (_currentIndexPath) {
|
if (indexPath) {
|
||||||
return [self _targetContentOffsetForItemAtIndexPath:_currentIndexPath proposedContentOffset:proposedContentOffset];
|
return [self _targetContentOffsetForItemAtIndexPath:indexPath proposedContentOffset:proposedContentOffset];
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return [super targetContentOffsetForProposedContentOffset:proposedContentOffset];
|
|
||||||
|
return [super targetContentOffsetForProposedContentOffset:proposedContentOffset];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CGPoint)_targetContentOffsetForItemAtIndexPath:(NSIndexPath *)indexPath proposedContentOffset:(CGPoint)proposedContentOffset
|
- (CGPoint)_targetContentOffsetForItemAtIndexPath:(NSIndexPath *)indexPath proposedContentOffset:(CGPoint)proposedContentOffset
|
||||||
@@ -75,19 +56,49 @@
|
|||||||
return proposedContentOffset;
|
return proposedContentOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
UICollectionViewLayoutAttributes *attributes = [self layoutAttributesForItemAtIndexPath:_currentIndexPath];
|
UICollectionViewLayoutAttributes *attributes = [self layoutAttributesForItemAtIndexPath:indexPath];
|
||||||
if (attributes == nil) {
|
if (attributes == nil) {
|
||||||
return proposedContentOffset;
|
return proposedContentOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
CGFloat xOffset = (CGRectGetWidth(self.collectionView.bounds) - CGRectGetWidth(attributes.frame)) / 2.0;
|
CGFloat xOffset = (CGRectGetWidth(self.collectionView.bounds) - CGRectGetWidth(attributes.frame)) / 2.0;
|
||||||
return CGPointMake(attributes.frame.origin.x - xOffset, proposedContentOffset.y);
|
return CGPointMake(attributes.frame.origin.x - xOffset, proposedContentOffset.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)_dataSourceIsEmpty
|
- (BOOL)_dataSourceIsEmpty
|
||||||
{
|
{
|
||||||
return ([self.collectionView numberOfSections] == 0 ||
|
return ([self.collectionView numberOfSections] == 0 ||
|
||||||
[self.collectionView numberOfItemsInSection:0] == 0);
|
[self.collectionView numberOfItemsInSection:0] == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)_updateCurrentNode
|
||||||
|
{
|
||||||
|
// Never change node during an animated bounds change (rotation)
|
||||||
|
// NOTE! Listening for -prepareForAnimatedBoundsChange and -finalizeAnimatedBoundsChange
|
||||||
|
// isn't sufficient here! It's broken!
|
||||||
|
NSArray *animKeys = self.collectionView.layer.animationKeys;
|
||||||
|
for (NSString *key in animKeys) {
|
||||||
|
if ([key hasPrefix:@"bounds"]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CGRect bounds = self.collectionView.bounds;
|
||||||
|
CGRect rect = CGRectMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds), 1, 1);
|
||||||
|
|
||||||
|
NSIndexPath *indexPath = [self layoutAttributesForElementsInRect:rect].firstObject.indexPath;
|
||||||
|
if (indexPath) {
|
||||||
|
ASCellNode *node = [self.asCollectionView nodeForItemAtIndexPath:indexPath];
|
||||||
|
if (node) {
|
||||||
|
_currentCellNode = node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds
|
||||||
|
{
|
||||||
|
[self _updateCurrentNode];
|
||||||
|
return [super shouldInvalidateLayoutForBoundsChange:newBounds];
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -120,6 +120,24 @@ NS_ASSUME_NONNULL_BEGIN
|
|||||||
*/
|
*/
|
||||||
- (NSInteger)indexOfPageWithNode:(ASCellNode *)node;
|
- (NSInteger)indexOfPageWithNode:(ASCellNode *)node;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tells the pager node to allow its view controller to automatically adjust its content insets.
|
||||||
|
*
|
||||||
|
* @see UIViewController.automaticallyAdjustsScrollViewInsets
|
||||||
|
*
|
||||||
|
* @discussion ASPagerNode should usually not have its content insets automatically adjusted
|
||||||
|
* because it scrolls horizontally, and flow layout will log errors because the pages
|
||||||
|
* do not fit between the top & bottom insets of the collection view.
|
||||||
|
*
|
||||||
|
* The default value is NO, which means that ASPagerNode expects that its view controller will
|
||||||
|
* have automaticallyAdjustsScrollViewInsets=NO.
|
||||||
|
*
|
||||||
|
* If this property is NO, but your view controller has automaticallyAdjustsScrollViewInsets=YES,
|
||||||
|
* the pager node will set the property to NO and log a warning message. In the future,
|
||||||
|
* the pager node will just log the warning, and you'll need to configure your view controller on your own.
|
||||||
|
*/
|
||||||
|
@property (nonatomic, assign) BOOL allowsAutomaticInsetsAdjustment;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_END
|
NS_ASSUME_NONNULL_END
|
||||||
|
|||||||
@@ -12,11 +12,13 @@
|
|||||||
|
|
||||||
#import <AsyncDisplayKit/ASPagerNode.h>
|
#import <AsyncDisplayKit/ASPagerNode.h>
|
||||||
#import <AsyncDisplayKit/ASDelegateProxy.h>
|
#import <AsyncDisplayKit/ASDelegateProxy.h>
|
||||||
|
#import <AsyncDisplayKit/ASDisplayNode+FrameworkPrivate.h>
|
||||||
#import <AsyncDisplayKit/ASDisplayNode+Subclasses.h>
|
#import <AsyncDisplayKit/ASDisplayNode+Subclasses.h>
|
||||||
#import <AsyncDisplayKit/ASPagerFlowLayout.h>
|
#import <AsyncDisplayKit/ASPagerFlowLayout.h>
|
||||||
#import <AsyncDisplayKit/ASAssert.h>
|
#import <AsyncDisplayKit/ASAssert.h>
|
||||||
#import <AsyncDisplayKit/ASCellNode.h>
|
#import <AsyncDisplayKit/ASCellNode.h>
|
||||||
#import <AsyncDisplayKit/ASCollectionView+Undeprecated.h>
|
#import <AsyncDisplayKit/ASCollectionView+Undeprecated.h>
|
||||||
|
#import <AsyncDisplayKit/UIResponder+AsyncDisplayKit.h>
|
||||||
|
|
||||||
@interface ASPagerNode () <ASCollectionDataSource, ASCollectionDelegate, ASCollectionDelegateFlowLayout, ASDelegateProxyInterceptor>
|
@interface ASPagerNode () <ASCollectionDataSource, ASCollectionDelegate, ASCollectionDelegateFlowLayout, ASDelegateProxyInterceptor>
|
||||||
{
|
{
|
||||||
@@ -80,11 +82,6 @@
|
|||||||
cv.allowsSelection = NO;
|
cv.allowsSelection = NO;
|
||||||
cv.showsVerticalScrollIndicator = NO;
|
cv.showsVerticalScrollIndicator = NO;
|
||||||
cv.showsHorizontalScrollIndicator = NO;
|
cv.showsHorizontalScrollIndicator = NO;
|
||||||
|
|
||||||
// Zeroing contentInset is important, as UIKit will set the top inset for the navigation bar even though
|
|
||||||
// our view is only horizontally scrollable. This causes UICollectionViewFlowLayout to log a warning.
|
|
||||||
// From here we cannot disable this directly (UIViewController's automaticallyAdjustsScrollViewInsets).
|
|
||||||
cv.zeroContentInsets = YES;
|
|
||||||
|
|
||||||
ASRangeTuningParameters minimumRenderParams = { .leadingBufferScreenfuls = 0.0, .trailingBufferScreenfuls = 0.0 };
|
ASRangeTuningParameters minimumRenderParams = { .leadingBufferScreenfuls = 0.0, .trailingBufferScreenfuls = 0.0 };
|
||||||
ASRangeTuningParameters minimumPreloadParams = { .leadingBufferScreenfuls = 1.0, .trailingBufferScreenfuls = 1.0 };
|
ASRangeTuningParameters minimumPreloadParams = { .leadingBufferScreenfuls = 1.0, .trailingBufferScreenfuls = 1.0 };
|
||||||
@@ -211,4 +208,21 @@
|
|||||||
[self setDelegate:nil];
|
[self setDelegate:nil];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)didEnterVisibleState
|
||||||
|
{
|
||||||
|
[super didEnterVisibleState];
|
||||||
|
|
||||||
|
// Check that our view controller does not automatically set our content insets
|
||||||
|
// It would be better to have a -didEnterHierarchy hook to put this in, but
|
||||||
|
// such a hook doesn't currently exist, and in every use case I can imagine,
|
||||||
|
// the pager is not hosted inside a range-managed node.
|
||||||
|
if (_allowsAutomaticInsetsAdjustment == NO) {
|
||||||
|
UIViewController *vc = [self.view asdk_associatedViewController];
|
||||||
|
if (vc.automaticallyAdjustsScrollViewInsets) {
|
||||||
|
NSLog(@"AsyncDisplayKit: ASPagerNode is setting automaticallyAdjustsScrollViewInsets=NO on its owning view controller %@. This automatic behavior will be disabled in the future. Set allowsAutomaticInsetsAdjustment=YES on the pager node to suppress this behavior.", vc);
|
||||||
|
vc.automaticallyAdjustsScrollViewInsets = NO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -106,6 +106,7 @@
|
|||||||
#import <AsyncDisplayKit/UIImage+ASConvenience.h>
|
#import <AsyncDisplayKit/UIImage+ASConvenience.h>
|
||||||
#import <AsyncDisplayKit/NSArray+Diffing.h>
|
#import <AsyncDisplayKit/NSArray+Diffing.h>
|
||||||
#import <AsyncDisplayKit/ASObjectDescriptionHelpers.h>
|
#import <AsyncDisplayKit/ASObjectDescriptionHelpers.h>
|
||||||
|
#import <AsyncDisplayKit/UIResponder+AsyncDisplayKit.h>
|
||||||
|
|
||||||
#import <AsyncDisplayKit/AsyncDisplayKit+Debug.h>
|
#import <AsyncDisplayKit/AsyncDisplayKit+Debug.h>
|
||||||
#import <AsyncDisplayKit/ASDisplayNode+Deprecated.h>
|
#import <AsyncDisplayKit/ASDisplayNode+Deprecated.h>
|
||||||
|
|||||||
28
AsyncDisplayKit/Private/ASResponderChainEnumerator.h
Normal file
28
AsyncDisplayKit/Private/ASResponderChainEnumerator.h
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
//
|
||||||
|
// ASResponderChainEnumerator.h
|
||||||
|
// AsyncDisplayKit
|
||||||
|
//
|
||||||
|
// Created by Adlai Holler on 2/13/17.
|
||||||
|
// Copyright © 2017 Facebook. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <UIKit/UIResponder.h>
|
||||||
|
#import <AsyncDisplayKit/ASBaseDefines.h>
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
AS_SUBCLASSING_RESTRICTED
|
||||||
|
@interface ASResponderChainEnumerator : NSEnumerator
|
||||||
|
|
||||||
|
- (instancetype)initWithResponder:(UIResponder *)responder;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface UIResponder (ASResponderChainEnumerator)
|
||||||
|
|
||||||
|
- (ASResponderChainEnumerator *)asdk_responderChainEnumerator;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
||||||
41
AsyncDisplayKit/Private/ASResponderChainEnumerator.m
Normal file
41
AsyncDisplayKit/Private/ASResponderChainEnumerator.m
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
//
|
||||||
|
// ASResponderChainEnumerator.m
|
||||||
|
// AsyncDisplayKit
|
||||||
|
//
|
||||||
|
// Created by Adlai Holler on 2/13/17.
|
||||||
|
// Copyright © 2017 Facebook. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "ASResponderChainEnumerator.h"
|
||||||
|
|
||||||
|
@implementation ASResponderChainEnumerator {
|
||||||
|
UIResponder *_currentResponder;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)initWithResponder:(UIResponder *)responder
|
||||||
|
{
|
||||||
|
if (self = [super init]) {
|
||||||
|
_currentResponder = responder;
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - NSEnumerator
|
||||||
|
|
||||||
|
- (id)nextObject
|
||||||
|
{
|
||||||
|
id result = [_currentResponder nextResponder];
|
||||||
|
_currentResponder = result;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation UIResponder (ASResponderChainEnumerator)
|
||||||
|
|
||||||
|
- (NSEnumerator *)asdk_responderChainEnumerator
|
||||||
|
{
|
||||||
|
return [[ASResponderChainEnumerator alloc] initWithResponder:self];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
24
AsyncDisplayKit/UIResponder+AsyncDisplayKit.h
Normal file
24
AsyncDisplayKit/UIResponder+AsyncDisplayKit.h
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
//
|
||||||
|
// UIResponder+AsyncDisplayKit.h
|
||||||
|
// AsyncDisplayKit
|
||||||
|
//
|
||||||
|
// Created by Adlai Holler on 2/13/17.
|
||||||
|
// Copyright © 2017 Facebook. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <UIKit/UIKit.h>
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
@interface UIResponder (AsyncDisplayKit)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The nearest view controller above this responder, if one exists.
|
||||||
|
*
|
||||||
|
* This property must be accessed on the main thread.
|
||||||
|
*/
|
||||||
|
@property (nonatomic, nullable, readonly) __kindof UIViewController *asdk_associatedViewController;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
||||||
31
AsyncDisplayKit/UIResponder+AsyncDisplayKit.m
Normal file
31
AsyncDisplayKit/UIResponder+AsyncDisplayKit.m
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
//
|
||||||
|
// UIResponder+AsyncDisplayKit.m
|
||||||
|
// AsyncDisplayKit
|
||||||
|
//
|
||||||
|
// Created by Adlai Holler on 2/13/17.
|
||||||
|
// Copyright © 2017 Facebook. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "UIResponder+AsyncDisplayKit.h"
|
||||||
|
|
||||||
|
#import <AsyncDisplayKit/ASAssert.h>
|
||||||
|
#import <AsyncDisplayKit/ASBaseDefines.h>
|
||||||
|
#import <AsyncDisplayKit/ASResponderChainEnumerator.h>
|
||||||
|
|
||||||
|
@implementation UIResponder (AsyncDisplayKit)
|
||||||
|
|
||||||
|
- (__kindof UIViewController *)asdk_associatedViewController
|
||||||
|
{
|
||||||
|
ASDisplayNodeAssertMainThread();
|
||||||
|
|
||||||
|
for (UIResponder *responder in [self asdk_responderChainEnumerator]) {
|
||||||
|
UIViewController *vc = ASDynamicCast(responder, UIViewController);
|
||||||
|
if (vc) {
|
||||||
|
return vc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
@@ -21,9 +21,9 @@
|
|||||||
|
|
||||||
@implementation PageNode
|
@implementation PageNode
|
||||||
|
|
||||||
- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize
|
- (CGSize)calculateSizeThatFits:(CGSize)constrainedSize
|
||||||
{
|
{
|
||||||
return [ASLayout layoutWithLayoutElement:self size:constrainedSize.max];
|
return constrainedSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)fetchData
|
- (void)fetchData
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ static UIColor *randomColor() {
|
|||||||
|
|
||||||
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"Next" style:UIBarButtonItemStylePlain target:self action:@selector(scrollToNextPage:)];
|
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"Next" style:UIBarButtonItemStylePlain target:self action:@selector(scrollToNextPage:)];
|
||||||
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"Previous" style:UIBarButtonItemStylePlain target:self action:@selector(scrollToPreviousPage:)];
|
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"Previous" style:UIBarButtonItemStylePlain target:self action:@selector(scrollToPreviousPage:)];
|
||||||
|
self.automaticallyAdjustsScrollViewInsets = NO;
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user