Skip to content
This repository was archived by the owner on Feb 2, 2023. It is now read-only.

Commit f71eba7

Browse files
committed
[ASCollectionView] Finish support for interoperability with base-class UICollectionViewCells.
This also supports supplementary nodes. It builds off of Adlai's .interop flag but makes necessary improvements for all of the delegate methods to work in practice with heterogenous cell types.
1 parent e2e797b commit f71eba7

13 files changed

Lines changed: 280 additions & 149 deletions

AsyncDisplayKit.xcodeproj/project.pbxproj

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -311,7 +311,6 @@
311311
CC0F885C1E42807F00576FED /* ASCollectionViewFlowLayoutInspector.h in Headers */ = {isa = PBXBuildFile; fileRef = CC0F885A1E42807F00576FED /* ASCollectionViewFlowLayoutInspector.h */; settings = {ATTRIBUTES = (Private, ); }; };
312312
CC0F885F1E4280B800576FED /* _ASCollectionViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = CC0F885D1E4280B800576FED /* _ASCollectionViewCell.m */; };
313313
CC0F88601E4280B800576FED /* _ASCollectionViewCell.h in Headers */ = {isa = PBXBuildFile; fileRef = CC0F885E1E4280B800576FED /* _ASCollectionViewCell.h */; settings = {ATTRIBUTES = (Private, ); }; };
314-
CC0F88611E4280C900576FED /* ASCollectionInteropProtocols.h in Headers */ = {isa = PBXBuildFile; fileRef = CC6363E11E32C00800D8A8DE /* ASCollectionInteropProtocols.h */; settings = {ATTRIBUTES = (Private, ); }; };
315314
CC0F88621E4281E200576FED /* ASSectionController.h in Headers */ = {isa = PBXBuildFile; fileRef = CCE04B1E1E313EA7006AEBBB /* ASSectionController.h */; settings = {ATTRIBUTES = (Public, ); }; };
316315
CC0F88631E4281E700576FED /* ASSupplementaryNodeSource.h in Headers */ = {isa = PBXBuildFile; fileRef = CCE04B2B1E314A32006AEBBB /* ASSupplementaryNodeSource.h */; settings = {ATTRIBUTES = (Public, ); }; };
317316
CC0F886B1E4286FA00576FED /* ReferenceImages_32 in Resources */ = {isa = PBXBuildFile; fileRef = CC0F88681E4286FA00576FED /* ReferenceImages_32 */; };
@@ -721,7 +720,6 @@
721720
CC54A81D1D7008B300296A24 /* ASDispatchTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = ASDispatchTests.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
722721
CC57EAF91E394EA40034C595 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
723722
CC58AA4A1E398E1D002C8CB4 /* ASBlockTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASBlockTypes.h; sourceTree = "<group>"; };
724-
CC6363E11E32C00800D8A8DE /* ASCollectionInteropProtocols.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASCollectionInteropProtocols.h; sourceTree = "<group>"; };
725723
CC7FD9DC1BB5E962005CCB2B /* ASPhotosFrameworkImageRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASPhotosFrameworkImageRequest.h; sourceTree = "<group>"; };
726724
CC7FD9DD1BB5E962005CCB2B /* ASPhotosFrameworkImageRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASPhotosFrameworkImageRequest.m; sourceTree = "<group>"; };
727725
CC7FD9E01BB5F750005CCB2B /* ASPhotosFrameworkImageRequestTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASPhotosFrameworkImageRequestTests.m; sourceTree = "<group>"; };
@@ -1340,7 +1338,6 @@
13401338
CCE04B2A1E313EDA006AEBBB /* Collection Data Adapter */ = {
13411339
isa = PBXGroup;
13421340
children = (
1343-
CC6363E11E32C00800D8A8DE /* ASCollectionInteropProtocols.h */,
13441341
CCBD05DF1E4147B000D18509 /* ASIGListAdapterBasedDataSource.h */,
13451342
CCBD05DE1E4147B000D18509 /* ASIGListAdapterBasedDataSource.m */,
13461343
);
@@ -1416,7 +1413,6 @@
14161413
A2763D7A1CBDD57D00A9ADBD /* ASPINRemoteImageDownloader.h in Headers */,
14171414
34EFC7611B701C9C00AD841F /* ASBackgroundLayoutSpec.h in Headers */,
14181415
B35062591B010F070018CF92 /* ASBaseDefines.h in Headers */,
1419-
CC0F88611E4280C900576FED /* ASCollectionInteropProtocols.h in Headers */,
14201416
B35062131B010EFD0018CF92 /* ASBasicImageDownloader.h in Headers */,
14211417
B35062151B010EFD0018CF92 /* ASBatchContext.h in Headers */,
14221418
B35061F31B010EFD0018CF92 /* ASCellNode.h in Headers */,

AsyncDisplayKit/ASCellNode+Internal.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,9 @@ NS_ASSUME_NONNULL_BEGIN
6363

6464
@property (nonatomic, copy, nullable) NSIndexPath *cachedIndexPath;
6565

66-
@property (weak, nonatomic, nullable) ASDisplayNode *owningNode;
66+
@property (nonatomic, weak, nullable) ASDisplayNode *owningNode;
67+
68+
@property (nonatomic, assign) BOOL shouldUseUIKitCell;
6769

6870
@end
6971

AsyncDisplayKit/ASCollectionNode.h

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -706,4 +706,39 @@ NS_ASSUME_NONNULL_BEGIN
706706

707707
@end
708708

709+
@protocol ASCollectionDataSourceInterop <ASCollectionDataSource>
710+
711+
/**
712+
* This method offers compatibility with synchronous, standard UICollectionViewCell objects.
713+
* These cells will **not** have the performance benefits of ASCellNodes (like preloading, async layout, and
714+
* async drawing) - even when mixed within the same ASCollectionNode.
715+
*
716+
* In order to use this method, you must:
717+
* 1. Implement it on your ASCollectionDataSource object.
718+
* 2. Call registerCellClass: on the collectionNode.view (in viewDidLoad, or register an onDidLoad: block).
719+
* 3. Return nil from the nodeBlockForItem...: or nodeForItem...: method. NOTE: it is an error to return
720+
* nil from within a nodeBlock, if you have returned a nodeBlock object.
721+
* 4. Lastly, you must implement a method to provide the size for the cell. There are two ways this is done:
722+
* 4a. UICollectionViewFlowLayout (incl. ASPagerNode). Implement
723+
collectionNode:constrainedSizeForItemAtIndexPath:.
724+
* 4b. Custom collection layouts. Set .view.layoutInspector and have it implement
725+
collectionView:constrainedSizeForNodeAtIndexPath:.
726+
*
727+
* For an example of using this method with all steps above (including a custom layout, 4b.),
728+
* see the app in examples/CustomCollectionView and enable kShowUICollectionViewCells = YES.
729+
*/
730+
- (__kindof UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath;
731+
732+
- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath;
733+
734+
@end
735+
736+
@protocol ASCollectionDelegateInterop <ASCollectionDelegate>
737+
738+
- (void)collectionView:(UICollectionView *)collectionView willDisplayCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath;
739+
740+
- (void)collectionView:(UICollectionView *)collectionView didEndDisplayingCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath;
741+
742+
@end
743+
709744
NS_ASSUME_NONNULL_END

AsyncDisplayKit/ASCollectionView.mm

Lines changed: 93 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
#import <AsyncDisplayKit/ASCollectionViewLayoutFacilitatorProtocol.h>
2929
#import <AsyncDisplayKit/ASSectionContext.h>
3030
#import <AsyncDisplayKit/ASCollectionView+Undeprecated.h>
31-
#import <AsyncDisplayKit/ASCollectionInteropProtocols.h>
3231
#import <AsyncDisplayKit/_ASHierarchyChangeSet.h>
3332

3433
/**
@@ -450,7 +449,7 @@ - (void)setAsyncDelegate:(id<ASCollectionDelegate>)asyncDelegate
450449
if (asyncDelegate == nil) {
451450
_asyncDelegate = nil;
452451
_proxyDelegate = _isDeallocating ? nil : [[ASCollectionViewProxy alloc] initWithTarget:nil interceptor:self];
453-
_asyncDataSourceFlags = {};
452+
_asyncDelegateFlags = {};
454453
} else {
455454
_asyncDelegate = asyncDelegate;
456455
_proxyDelegate = [[ASCollectionViewProxy alloc] initWithTarget:_asyncDelegate interceptor:self];
@@ -857,49 +856,79 @@ - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollection
857856
return [[self nodeForItemAtIndexPath:indexPath] calculatedSize];
858857
}
859858

860-
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section
859+
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)layout referenceSizeForHeaderInSection:(NSInteger)section
861860
{
862-
return [self supplementaryNodeForElementKind:UICollectionElementKindSectionHeader atIndexPath:[NSIndexPath indexPathForItem:0 inSection:section]].calculatedSize;
861+
ASCellNode *cell = [self supplementaryNodeForElementKind:UICollectionElementKindSectionHeader
862+
atIndexPath:[NSIndexPath indexPathForItem:0 inSection:section]];
863+
if (cell.shouldUseUIKitCell && _asyncDelegateFlags.interop) {
864+
if ([_asyncDelegate respondsToSelector:@selector(collectionView:layout:referenceSizeForHeaderInSection:)]) {
865+
return [(id)_asyncDelegate collectionView:collectionView layout:layout referenceSizeForHeaderInSection:section];
866+
}
867+
}
868+
return cell.calculatedSize;
863869
}
864870

865-
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout referenceSizeForFooterInSection:(NSInteger)section
871+
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)layout referenceSizeForFooterInSection:(NSInteger)section
866872
{
867-
return [self supplementaryNodeForElementKind:UICollectionElementKindSectionFooter atIndexPath:[NSIndexPath indexPathForItem:0 inSection:section]].calculatedSize;
873+
ASCellNode *cell = [self supplementaryNodeForElementKind:UICollectionElementKindSectionFooter
874+
atIndexPath:[NSIndexPath indexPathForItem:0 inSection:section]];
875+
if (cell.shouldUseUIKitCell && _asyncDelegateFlags.interop) {
876+
if ([_asyncDelegate respondsToSelector:@selector(collectionView:layout:referenceSizeForFooterInSection:)]) {
877+
return [(id)_asyncDelegate collectionView:collectionView layout:layout referenceSizeForFooterInSection:section];
878+
}
879+
}
880+
return cell.calculatedSize;
868881
}
869882

870883
- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
871884
{
872-
UICollectionReusableView *view;
873-
if (_asyncDataSource && _asyncDataSourceFlags.interop) {
885+
UICollectionReusableView *view = nil;
886+
ASCellNode *node = [_dataController supplementaryNodeOfKind:kind atIndexPath:indexPath];
887+
888+
if (node.shouldUseUIKitCell && _asyncDataSourceFlags.interop) {
874889
view = [(id<ASCollectionDataSourceInterop>)_asyncDataSource collectionView:collectionView viewForSupplementaryElementOfKind:kind atIndexPath:indexPath];
875890
} else {
891+
ASDisplayNodeAssert(node != nil, @"Supplementary node should exist. Kind = %@, indexPath = %@, collectionDataSource = %@", kind, indexPath, self);
892+
if ([_registeredSupplementaryKinds containsObject:kind] == NO) {
893+
[self registerSupplementaryNodeOfKind:kind];
894+
}
876895
view = [self dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:kReuseIdentifier forIndexPath:indexPath];
896+
if (node) {
897+
[_rangeController configureContentView:view forCellNode:node];
898+
}
877899
}
878-
879-
ASCellNode *node = [_dataController supplementaryNodeOfKind:kind atIndexPath:indexPath];
880-
ASDisplayNodeAssert(node != nil, @"Supplementary node should exist. Kind = %@, indexPath = %@, collectionDataSource = %@", kind, indexPath, self);
881-
[_rangeController configureContentView:view forCellNode:node];
900+
882901
return view;
883902
}
884903

885904
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
886905
{
887-
_ASCollectionViewCell *cell;
888-
if (_asyncDataSource && _asyncDataSourceFlags.interop) {
906+
UICollectionViewCell *cell = nil;
907+
ASCellNode *node = [self nodeForItemAtIndexPath:indexPath];
908+
909+
if (node.shouldUseUIKitCell && _asyncDataSourceFlags.interop) {
889910
cell = [(id<ASCollectionDataSourceInterop>)_asyncDataSource collectionView:collectionView cellForItemAtIndexPath:indexPath];
890911
} else {
912+
ASDisplayNodeAssert(node != nil, @"Cell node should exist. indexPath = %@, collectionDataSource = %@", indexPath, self);
891913
cell = [self dequeueReusableCellWithReuseIdentifier:kReuseIdentifier forIndexPath:indexPath];
914+
[(_ASCollectionViewCell *)cell setNode:node];
915+
[_rangeController configureContentView:cell.contentView forCellNode:node];
892916
}
893-
894-
ASCellNode *node = [self nodeForItemAtIndexPath:indexPath];
895-
cell.node = node;
896-
[_rangeController configureContentView:cell.contentView forCellNode:node];
897-
898917
return cell;
899918
}
900919

901920
- (void)collectionView:(UICollectionView *)collectionView willDisplayCell:(_ASCollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath
902921
{
922+
// Since _ASCollectionViewCell is not available for subclassing, this is faster than isKindOfClass:
923+
// We must exit early here, because only _ASCollectionViewCell implements the -node accessor method.
924+
if ([cell class] != [_ASCollectionViewCell class]) {
925+
if (_asyncDelegateFlags.interop) {
926+
[(id <ASCollectionDelegateInterop>)_asyncDelegate collectionView:collectionView willDisplayCell:cell forItemAtIndexPath:indexPath];
927+
}
928+
[_rangeController setNeedsUpdate];
929+
return;
930+
}
931+
903932
ASCellNode *cellNode = [cell node];
904933
cellNode.scrollView = collectionView;
905934

@@ -914,12 +943,8 @@ - (void)collectionView:(UICollectionView *)collectionView willDisplayCell:(_ASCo
914943

915944
ASDisplayNodeAssertNotNil(cellNode, @"Expected node associated with cell that will be displayed not to be nil. indexPath: %@", indexPath);
916945

917-
if (_asyncDelegateFlags.interop) {
918-
[(id<ASCollectionDelegateInterop>)_asyncDelegate collectionView:collectionView willDisplayCell:cell forItemAtIndexPath:indexPath];
919-
} else if (_asyncDelegateFlags.collectionNodeWillDisplayItem) {
920-
if (ASCollectionNode *collectionNode = self.collectionNode) {
921-
[_asyncDelegate collectionNode:collectionNode willDisplayItemWithNode:cellNode];
922-
}
946+
if (_asyncDelegateFlags.collectionNodeWillDisplayItem && self.collectionNode != nil) {
947+
[_asyncDelegate collectionNode:self.collectionNode willDisplayItemWithNode:cellNode];
923948
} else if (_asyncDelegateFlags.collectionViewWillDisplayNodeForItem) {
924949
#pragma clang diagnostic push
925950
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
@@ -938,12 +963,20 @@ - (void)collectionView:(UICollectionView *)collectionView willDisplayCell:(_ASCo
938963

939964
- (void)collectionView:(UICollectionView *)collectionView didEndDisplayingCell:(_ASCollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath
940965
{
966+
// Since _ASCollectionViewCell is not available for subclassing, this is faster than isKindOfClass:
967+
// We must exit early here, because only _ASCollectionViewCell implements the -node accessor method.
968+
if ([cell class] != [_ASCollectionViewCell class]) {
969+
if (_asyncDelegateFlags.interop) {
970+
[(id <ASCollectionDelegateInterop>)_asyncDelegate collectionView:collectionView didEndDisplayingCell:cell forItemAtIndexPath:indexPath];
971+
}
972+
[_rangeController setNeedsUpdate];
973+
return;
974+
}
975+
941976
ASCellNode *cellNode = [cell node];
942977
ASDisplayNodeAssertNotNil(cellNode, @"Expected node associated with removed cell not to be nil.");
943978

944-
if (_asyncDelegateFlags.interop) {
945-
[(id<ASCollectionDelegateInterop>)_asyncDelegate collectionView:collectionView didEndDisplayingCell:cell forItemAtIndexPath:indexPath];
946-
} else if (_asyncDelegateFlags.collectionNodeDidEndDisplayingItem) {
979+
if (_asyncDelegateFlags.collectionNodeDidEndDisplayingItem) {
947980
if (ASCollectionNode *collectionNode = self.collectionNode) {
948981
[_asyncDelegate collectionNode:collectionNode didEndDisplayingItemWithNode:cellNode];
949982
}
@@ -1369,46 +1402,49 @@ - (void)_beginBatchFetching
13691402

13701403
#pragma mark - ASDataControllerSource
13711404

1372-
- (ASCellNodeBlock)dataController:(ASDataController *)dataController nodeBlockAtIndexPath:(NSIndexPath *)indexPath {
1405+
- (ASCellNodeBlock)dataController:(ASDataController *)dataController nodeBlockAtIndexPath:(NSIndexPath *)indexPath
1406+
{
13731407
ASCellNodeBlock block = nil;
1408+
ASCellNode *cell = nil;
13741409

13751410
if (_asyncDataSourceFlags.collectionNodeNodeBlockForItem) {
13761411
GET_COLLECTIONNODE_OR_RETURN(collectionNode, ^{ return [[ASCellNode alloc] init]; });
13771412
block = [_asyncDataSource collectionNode:collectionNode nodeBlockForItemAtIndexPath:indexPath];
13781413
} else if (_asyncDataSourceFlags.collectionNodeNodeForItem) {
13791414
GET_COLLECTIONNODE_OR_RETURN(collectionNode, ^{ return [[ASCellNode alloc] init]; });
1380-
ASCellNode *node = [_asyncDataSource collectionNode:collectionNode nodeForItemAtIndexPath:indexPath];
1381-
if ([node isKindOfClass:[ASCellNode class]]) {
1382-
block = ^{
1383-
return node;
1384-
};
1385-
} else {
1386-
ASDisplayNodeFailAssert(@"Data source returned invalid node from tableNode:nodeForRowAtIndexPath:. Node: %@", node);
1387-
}
1388-
} else if (_asyncDataSourceFlags.collectionViewNodeBlockForItem) {
1415+
cell = [_asyncDataSource collectionNode:collectionNode nodeForItemAtIndexPath:indexPath];
13891416
#pragma clang diagnostic push
13901417
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
1418+
} else if (_asyncDataSourceFlags.collectionViewNodeBlockForItem) {
13911419
block = [_asyncDataSource collectionView:self nodeBlockForItemAtIndexPath:indexPath];
13921420
} else if (_asyncDataSourceFlags.collectionViewNodeForItem) {
1393-
ASCellNode *node = [_asyncDataSource collectionView:self nodeForItemAtIndexPath:indexPath];
1421+
cell = [_asyncDataSource collectionView:self nodeForItemAtIndexPath:indexPath];
1422+
}
13941423
#pragma clang diagnostic pop
1395-
if ([node isKindOfClass:[ASCellNode class]]) {
1424+
1425+
// Handle nil node block or cell
1426+
if (cell && [cell isKindOfClass:[ASCellNode class]]) {
1427+
block = ^{
1428+
return cell;
1429+
};
1430+
}
1431+
1432+
if (block == nil) {
1433+
if (_asyncDataSourceFlags.interop) {
13961434
block = ^{
1397-
return node;
1435+
ASCellNode *cell = [[ASCellNode alloc] init];
1436+
cell.shouldUseUIKitCell = YES;
1437+
cell.style.preferredSize = CGSizeZero;
1438+
return cell;
13981439
};
13991440
} else {
1400-
ASDisplayNodeFailAssert(@"Data source returned invalid node from tableView:nodeForRowAtIndexPath:. Node: %@", node);
1441+
ASDisplayNodeFailAssert(@"ASCollection could not get a node block for row at index path %@: %@, %@. If you are trying to display a UICollectionViewCell, make sure your dataSource conforms to the <ASCollectionDataSourceInterop> protocol!", indexPath, cell, block);
1442+
block = ^{
1443+
return [[ASCellNode alloc] init];
1444+
};
14011445
}
14021446
}
14031447

1404-
// Handle nil node block
1405-
if (block == nil) {
1406-
ASDisplayNodeFailAssert(@"ASTableNode could not get a node block for row at index path %@", indexPath);
1407-
block = ^{
1408-
return [[ASCellNode alloc] init];
1409-
};
1410-
}
1411-
14121448
// Wrap the node block
14131449
__weak __typeof__(self) weakSelf = self;
14141450
return ^{
@@ -1419,7 +1455,7 @@ - (ASCellNodeBlock)dataController:(ASDataController *)dataController nodeBlockAt
14191455
node.interactionDelegate = strongSelf;
14201456
}
14211457
if (_inverted) {
1422-
node.transform = CATransform3DMakeScale(1, -1, 1) ;
1458+
node.transform = CATransform3DMakeScale(1, -1, 1) ;
14231459
}
14241460
return node;
14251461
};
@@ -1479,6 +1515,12 @@ - (ASCellNode *)dataController:(ASCollectionDataController *)dataController supp
14791515
node = [_asyncDataSource collectionView:self nodeForSupplementaryElementOfKind:kind atIndexPath:indexPath];
14801516
#pragma clang diagnostic pop
14811517
}
1518+
1519+
if (node == nil && _asyncDataSourceFlags.interop) {
1520+
node = [[ASCellNode alloc] init];
1521+
node.shouldUseUIKitCell = YES;
1522+
}
1523+
14821524
ASDisplayNodeAssert(node != nil, @"A node must be returned for supplementary element of kind '%@' at index path '%@'", kind, indexPath);
14831525
return node;
14841526
}

AsyncDisplayKit/Private/ASCollectionInteropProtocols.h

Lines changed: 0 additions & 35 deletions
This file was deleted.

0 commit comments

Comments
 (0)