Skip to content

Commit f611ff5

Browse files
committed
Add reusable infinite scroll list component
1 parent 2a2fab2 commit f611ff5

File tree

3 files changed

+92
-31
lines changed

3 files changed

+92
-31
lines changed

WooCommerce/Classes/ViewRelated/Orders/Order Creation/ProductsSection/AddProductToOrder.swift

Lines changed: 2 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -16,21 +16,16 @@ struct AddProductToOrder: View {
1616
Group {
1717
switch viewModel.syncStatus {
1818
case .results:
19-
List {
19+
InfiniteScrollList(isLoading: viewModel.shouldShowScrollIndicator,
20+
loadAction: viewModel.syncNextPage) {
2021
ForEach(viewModel.productRows) { rowViewModel in
2122
ProductRow(viewModel: rowViewModel)
2223
.onTapGesture {
2324
viewModel.selectProduct(rowViewModel.productOrVariationID)
2425
isPresented.toggle()
2526
}
2627
}
27-
28-
InfiniteScrollIndicator(showContent: viewModel.shouldShowScrollIndicator)
29-
.onAppear {
30-
viewModel.syncNextPage()
31-
}
3228
}
33-
.listStyle(PlainListStyle())
3429
case .empty:
3530
EmptyState(title: Localization.emptyStateMessage, image: .emptyProductsTabImage)
3631
.frame(maxHeight: .infinity)
@@ -64,30 +59,6 @@ struct AddProductToOrder: View {
6459
}
6560
}
6661

67-
private struct InfiniteScrollIndicator: View {
68-
69-
let showContent: Bool
70-
71-
var body: some View {
72-
if #available(iOS 15.0, *) {
73-
createProgressView()
74-
.listRowSeparator(.hidden, edges: .bottom)
75-
} else {
76-
createProgressView()
77-
}
78-
}
79-
80-
@ViewBuilder func createProgressView() -> some View {
81-
ProgressView()
82-
.frame(maxWidth: .infinity, alignment: .center)
83-
.listRowInsets(EdgeInsets())
84-
.listRowBackground(Color(.listBackground))
85-
.if(!showContent) { progressView in
86-
progressView.hidden() // Hidden but still in view hierarchy so `onAppear` will trigger sync when needed
87-
}
88-
}
89-
}
90-
9162
private extension AddProductToOrder {
9263
enum Localization {
9364
static let title = NSLocalizedString("Add Product", comment: "Title for the screen to add a product to an order")
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import SwiftUI
2+
3+
struct InfiniteScrollList<Content: View>: View {
4+
/// Content to render in the list.
5+
///
6+
private let listContent: Content
7+
8+
/// Whether the list is loading more content. Used to determine whether to show the infinite scroll indicator.
9+
///
10+
private let isLoading: Bool
11+
12+
/// Action to load more content.
13+
///
14+
private let loadAction: () -> Void
15+
16+
/// A list that renders the provided list content with an infinite scroll indicator.
17+
///
18+
/// - Parameters:
19+
/// - isLoading: Whether the list is loading more content. Used to determine whether to show the infinite scroll indicator.
20+
/// - loadAction: Action to load more content.
21+
/// - listContent: Content to render in the list.
22+
///
23+
init(isLoading: Bool,
24+
loadAction: @escaping () -> Void,
25+
@ViewBuilder listContent: () -> Content) {
26+
self.listContent = listContent()
27+
self.isLoading = isLoading
28+
self.loadAction = loadAction
29+
}
30+
31+
var body: some View {
32+
List {
33+
listContent
34+
35+
InfiniteScrollIndicator(showContent: isLoading)
36+
.onAppear {
37+
loadAction()
38+
}
39+
}
40+
.listStyle(PlainListStyle())
41+
}
42+
}
43+
44+
private struct InfiniteScrollIndicator: View {
45+
46+
let showContent: Bool
47+
48+
var body: some View {
49+
if #available(iOS 15.0, *) {
50+
createProgressView()
51+
.listRowSeparator(.hidden, edges: .bottom)
52+
} else {
53+
createProgressView()
54+
}
55+
}
56+
57+
@ViewBuilder func createProgressView() -> some View {
58+
ProgressView()
59+
.frame(maxWidth: .infinity, alignment: .center)
60+
.listRowInsets(EdgeInsets())
61+
.listRowBackground(Color(.listBackground))
62+
.if(!showContent) { progressView in
63+
progressView.hidden() // Hidden but still in view hierarchy so `onAppear` will trigger the load action when needed
64+
}
65+
}
66+
}
67+
68+
struct InfiniteScrollList_Previews: PreviewProvider {
69+
static var previews: some View {
70+
InfiniteScrollList(isLoading: true, loadAction: {}) {
71+
ForEach((0..<6)) { index in
72+
Text("Item \(index)")
73+
}
74+
}
75+
.previewDisplayName("Infinite scroll list: Loading")
76+
.previewLayout(.sizeThatFits)
77+
78+
InfiniteScrollList(isLoading: false, loadAction: {}) {
79+
ForEach((0..<6)) { index in
80+
Text("Item \(index)")
81+
}
82+
}
83+
.previewDisplayName("Infinite scroll list: Loaded")
84+
.previewLayout(.sizeThatFits)
85+
}
86+
}

WooCommerce/WooCommerce.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1180,6 +1180,7 @@
11801180
CCDC49ED24000533003166BA /* TestCredentials.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCDC49EC24000533003166BA /* TestCredentials.swift */; };
11811181
CCE4CD172667EBB100E09FD4 /* ShippingLabelPaymentMethodsViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCE4CD162667EBB100E09FD4 /* ShippingLabelPaymentMethodsViewModelTests.swift */; };
11821182
CCE4CD282669324300E09FD4 /* ShippingLabelPaymentMethodsTopBanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCE4CD272669324300E09FD4 /* ShippingLabelPaymentMethodsTopBanner.swift */; };
1183+
CCF87BBE279047BC00461C43 /* InfiniteScrollList.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCF87BBD279047BC00461C43 /* InfiniteScrollList.swift */; };
11831184
CCFC00B523E9BD1500157A78 /* ScreenshotCredentials.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCFC00B423E9BD1500157A78 /* ScreenshotCredentials.swift */; };
11841185
CCFC00EE23E9BD5500157A78 /* oauth2_token.json in Resources */ = {isa = PBXBuildFile; fileRef = CCFC00BC23E9BD5500157A78 /* oauth2_token.json */; };
11851186
CCFC00EF23E9BD5500157A78 /* auth_options.json in Resources */ = {isa = PBXBuildFile; fileRef = CCFC00BD23E9BD5500157A78 /* auth_options.json */; };
@@ -2772,6 +2773,7 @@
27722773
CCDC49F224006130003166BA /* UITests.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; name = UITests.xctestplan; path = WooCommerceUITests/UITests.xctestplan; sourceTree = SOURCE_ROOT; };
27732774
CCE4CD162667EBB100E09FD4 /* ShippingLabelPaymentMethodsViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShippingLabelPaymentMethodsViewModelTests.swift; sourceTree = "<group>"; };
27742775
CCE4CD272669324300E09FD4 /* ShippingLabelPaymentMethodsTopBanner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShippingLabelPaymentMethodsTopBanner.swift; sourceTree = "<group>"; };
2776+
CCF87BBD279047BC00461C43 /* InfiniteScrollList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InfiniteScrollList.swift; sourceTree = "<group>"; };
27752777
CCFC00B423E9BD1500157A78 /* ScreenshotCredentials.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScreenshotCredentials.swift; sourceTree = "<group>"; };
27762778
CCFC00BC23E9BD5500157A78 /* oauth2_token.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = oauth2_token.json; sourceTree = "<group>"; };
27772779
CCFC00BD23E9BD5500157A78 /* auth_options.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = auth_options.json; sourceTree = "<group>"; };
@@ -4957,6 +4959,7 @@
49574959
AE6DBE3A2732CAAD00957E7A /* AdaptiveStack.swift */,
49584960
26B3EC632745916F0075EAE6 /* BindableTextField.swift */,
49594961
AEACCB6C2785FF4A000D01F0 /* NavigationRow.swift */,
4962+
CCF87BBD279047BC00461C43 /* InfiniteScrollList.swift */,
49604963
);
49614964
path = "SwiftUI Components";
49624965
sourceTree = "<group>";
@@ -8146,6 +8149,7 @@
81468149
6832C7CA26DA5C4500BA4088 /* LabeledTextViewTableViewCell.swift in Sources */,
81478150
DE7842F726F2E9340030C792 /* UIViewController+Connectivity.swift in Sources */,
81488151
E1C47209267A1ECC00D06DA1 /* CrashLoggingStack.swift in Sources */,
8152+
CCF87BBE279047BC00461C43 /* InfiniteScrollList.swift in Sources */,
81498153
D85A3C5226C15DE200C0E026 /* InPersonPaymentsPluginNotSupportedVersionView.swift in Sources */,
81508154
CE583A0B2107937F00D73C1C /* TextViewTableViewCell.swift in Sources */,
81518155
45AE150224A23F03005AA948 /* ProductParentCategoriesViewController.swift in Sources */,

0 commit comments

Comments
 (0)