Skip to content

Commit

Permalink
Add parentProduct: POSParentProduct case to POSItem enum, parse p…
Browse files Browse the repository at this point in the history
…roducts based on product type in item service, and show new `ParentProductCardView` for parent products.
  • Loading branch information
jaclync committed Dec 25, 2024
1 parent efe8572 commit 09768cc
Show file tree
Hide file tree
Showing 7 changed files with 133 additions and 7 deletions.
6 changes: 6 additions & 0 deletions WooCommerce/Classes/POS/Presentation/ItemListView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,12 @@ private extension ItemListView {
} label: {
SimpleProductCardView(product: simpleProduct)
}
case .parentProduct(let parentProduct):
Button {
print("tapped parent product")
} label: {
ParentProductCardView(parentProduct: parentProduct)
}
}
}
}
Expand Down
77 changes: 77 additions & 0 deletions WooCommerce/Classes/POS/Presentation/ParentProductCardView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import SwiftUI
import struct Yosemite.POSParentProduct

struct ParentProductCardView: View {
private let parentProduct: POSParentProduct

@ScaledMetric private var scale: CGFloat = 1.0
@Environment(\.dynamicTypeSize) var dynamicTypeSize

init(parentProduct: POSParentProduct) {
self.parentProduct = parentProduct
}

var body: some View {
HStack(spacing: Constants.cardSpacing) {
if let imageSource = parentProduct.productImageSource {
ProductImageThumbnail(productImageURL: URL(string: imageSource),
productImageSize: Constants.productCardSize * scale,
scale: scale,
foregroundColor: .clear,
cachesOriginalImage: true)
.frame(width: min(Constants.productCardSize * scale, Constants.maximumProductCardSize),
height: Constants.productCardSize * scale)
.clipped()
} else {
Rectangle()
.frame(width: min(Constants.productCardSize * scale, Constants.maximumProductCardSize),
height: Constants.productCardSize * scale)
.foregroundColor(Color(.secondarySystemFill))
}

DynamicHStack(spacing: Constants.textSpacing) {
Spacer().renderedIf(dynamicTypeSize.isAccessibilitySize)
Text(parentProduct.name)
.lineLimit(2)
.foregroundStyle(Color.posPrimaryText)
.multilineTextAlignment(.leading)
.font(Constants.itemNameFont)
Spacer().renderedIf(dynamicTypeSize.isAccessibilitySize)
}
.padding(.horizontal, Constants.horizontalTextPadding * (1 / scale))
.padding(.vertical, Constants.verticalTextPadding * (1 / scale))
Spacer()
}
.frame(maxWidth: .infinity, idealHeight: Constants.productCardSize * scale)
.background(Color.posSecondaryBackground)
.overlay {
RoundedRectangle(cornerRadius: Constants.productCardCornerRadius)
.stroke(Color.black, lineWidth: Constants.nilOutline)
}
.clipShape(RoundedRectangle(cornerRadius: Constants.productCardCornerRadius))
.shadow(color: Color.black.opacity(0.08), radius: 4, y: 2)
}
}

private extension ParentProductCardView {
enum Constants {
static let productCardSize: CGFloat = 112
static let maximumProductCardSize: CGFloat = Constants.productCardSize * 2
static let productCardCornerRadius: CGFloat = 8
// The use of stroke means the shape is rendered as an outline (border) rather than a filled shape,
// since we still have to give it a value, we use 0 so it renders no border but it's shaped as one.
static let nilOutline: CGFloat = 0
static let cardSpacing: CGFloat = 0
static let textSpacing: CGFloat = 8
static let horizontalTextPadding: CGFloat = 32
static let verticalTextPadding: CGFloat = 8
static let itemNameFont: POSFontStyle = .posBodyEmphasized
}
}

#if DEBUG
#Preview {
let parentProduct = POSParentProduct(id: UUID(), name: "Parent product 1", productImageSource: nil, productID: 42, type: .variable)
ParentProductCardView(parentProduct: parentProduct)
}
#endif
4 changes: 4 additions & 0 deletions WooCommerce/WooCommerce.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,7 @@
0279F0E4252DC9670098D7DE /* ProductVariationLoadUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0279F0E3252DC9670098D7DE /* ProductVariationLoadUseCase.swift */; };
027A2E142513124E00DA6ACB /* Keychain+Entries.swift in Sources */ = {isa = PBXBuildFile; fileRef = 027A2E132513124E00DA6ACB /* Keychain+Entries.swift */; };
027A2E162513356100DA6ACB /* AppleIDCredentialChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 027A2E152513356100DA6ACB /* AppleIDCredentialChecker.swift */; };
027ADB6E2D1BF5E3009608DB /* ParentProductCardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 027ADB6D2D1BF5E3009608DB /* ParentProductCardView.swift */; };
027B8BB823FE0CB30040944E /* DefaultProductUIImageLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 027B8BB723FE0CB30040944E /* DefaultProductUIImageLoader.swift */; };
027B8BBD23FE0DE10040944E /* ProductImageActionHandlerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 027B8BBC23FE0DE10040944E /* ProductImageActionHandlerTests.swift */; };
027B8BBF23FE0F850040944E /* MockMediaStoresManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 027B8BBE23FE0F850040944E /* MockMediaStoresManager.swift */; };
Expand Down Expand Up @@ -3526,6 +3527,7 @@
0279F0E3252DC9670098D7DE /* ProductVariationLoadUseCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProductVariationLoadUseCase.swift; sourceTree = "<group>"; };
027A2E132513124E00DA6ACB /* Keychain+Entries.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Keychain+Entries.swift"; sourceTree = "<group>"; };
027A2E152513356100DA6ACB /* AppleIDCredentialChecker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppleIDCredentialChecker.swift; sourceTree = "<group>"; };
027ADB6D2D1BF5E3009608DB /* ParentProductCardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParentProductCardView.swift; sourceTree = "<group>"; };
027B8BB723FE0CB30040944E /* DefaultProductUIImageLoader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultProductUIImageLoader.swift; sourceTree = "<group>"; };
027B8BBC23FE0DE10040944E /* ProductImageActionHandlerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProductImageActionHandlerTests.swift; sourceTree = "<group>"; };
027B8BBE23FE0F850040944E /* MockMediaStoresManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockMediaStoresManager.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -6989,6 +6991,7 @@
68D8FBD02BFEF9C700477C42 /* TotalsView.swift */,
68AF3C3A2D01481A006F1ED2 /* POSReceiptEligibilityBanner.swift */,
DA1D68C12C36F0980097859A /* PointOfSaleAssets.swift */,
027ADB6D2D1BF5E3009608DB /* ParentProductCardView.swift */,
);
path = Presentation;
sourceTree = "<group>";
Expand Down Expand Up @@ -14988,6 +14991,7 @@
B509FED121C041DF000076A9 /* Locale+Woo.swift in Sources */,
B5DB01B52114AB2D00A4F797 /* WooCrashLoggingStack.swift in Sources */,
205B7EBF2C19FBCA00D14A36 /* PointOfSaleCardPresentPaymentRequiredReaderUpdateInProgressAlertViewModel.swift in Sources */,
027ADB6E2D1BF5E3009608DB /* ParentProductCardView.swift in Sources */,
26ED9660274328BC00FA00A1 /* SimplePaymentsSummaryViewModel.swift in Sources */,
024DF31E23743045006658FE /* TextList+AztecFormatting.swift in Sources */,
CEEF742E2B9A0BAA00B03948 /* SessionsReportCardViewModel.swift in Sources */,
Expand Down
4 changes: 4 additions & 0 deletions Yosemite/Yosemite.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
026CF62C237D92DC009563D4 /* ProductVariationAttribute+ReadOnlyConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = 026CF62B237D92DC009563D4 /* ProductVariationAttribute+ReadOnlyConvertible.swift */; };
026D52C0238235930092AE05 /* ProductVariationStoreTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 026D52BF238235930092AE05 /* ProductVariationStoreTests.swift */; };
0271E1662509CF0100633F7A /* AnyError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0271E1652509CF0100633F7A /* AnyError.swift */; };
027ADB6C2D1BF3AD009608DB /* POSParentProduct.swift in Sources */ = {isa = PBXBuildFile; fileRef = 027ADB6B2D1BF3AD009608DB /* POSParentProduct.swift */; };
027CC11129F7AAEA00614B6E /* MockGenerativeContentRemote.swift in Sources */ = {isa = PBXBuildFile; fileRef = 027CC11029F7AAEA00614B6E /* MockGenerativeContentRemote.swift */; };
0286A1B82A0CBDC40099EF94 /* FeatureFlagStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0286A1B72A0CBDC40099EF94 /* FeatureFlagStore.swift */; };
0286A1BA2A0CBE1B0099EF94 /* FeatureFlagAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0286A1B92A0CBE1B0099EF94 /* FeatureFlagAction.swift */; };
Expand Down Expand Up @@ -596,6 +597,7 @@
026CF62B237D92DC009563D4 /* ProductVariationAttribute+ReadOnlyConvertible.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ProductVariationAttribute+ReadOnlyConvertible.swift"; sourceTree = "<group>"; };
026D52BF238235930092AE05 /* ProductVariationStoreTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProductVariationStoreTests.swift; sourceTree = "<group>"; };
0271E1652509CF0100633F7A /* AnyError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnyError.swift; sourceTree = "<group>"; };
027ADB6B2D1BF3AD009608DB /* POSParentProduct.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = POSParentProduct.swift; sourceTree = "<group>"; };
027CC11029F7AAEA00614B6E /* MockGenerativeContentRemote.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockGenerativeContentRemote.swift; sourceTree = "<group>"; };
0286A1B72A0CBDC40099EF94 /* FeatureFlagStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeatureFlagStore.swift; sourceTree = "<group>"; };
0286A1B92A0CBE1B0099EF94 /* FeatureFlagAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeatureFlagAction.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1516,6 +1518,7 @@
children = (
6898F3732C0842150039F10A /* PointOfSaleItemServiceProtocol.swift */,
68EA25332C08734800C49AE2 /* POSSimpleProduct.swift */,
027ADB6B2D1BF3AD009608DB /* POSParentProduct.swift */,
68EA25372C0876DF00C49AE2 /* PointOfSaleProductService.swift */,
);
path = PointOfSale;
Expand Down Expand Up @@ -2599,6 +2602,7 @@
450106872399AB3F00E24722 /* TaxClass+ReadOnlyConvertible.swift in Sources */,
B9AECD462851DBED00E78584 /* Order+CurrencyFormattedValues.swift in Sources */,
45182D1F27B54D3000B4C05C /* InboxNote+ReadOnlyConvertible.swift in Sources */,
027ADB6C2D1BF3AD009608DB /* POSParentProduct.swift in Sources */,
022F00C224726090008CD97F /* SiteNotificationCountFileContents.swift in Sources */,
247CE7BC2582DC1E00F9D9D1 /* MockCustomer.swift in Sources */,
CE0FBB1D2D0C5DE3008B7789 /* WooShippingCarrierPredefinedOptions+ReadOnlyConvertible.swift in Sources */,
Expand Down
21 changes: 21 additions & 0 deletions Yosemite/Yosemite/PointOfSale/POSParentProduct.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import Foundation

public enum POSParentProductType {
case variable
}

public struct POSParentProduct: Equatable, Hashable, Identifiable {
public let id: UUID
public let name: String
public let productImageSource: String?
public let productID: Int64
public let type: POSParentProductType

public init(id: UUID, name: String, productImageSource: String?, productID: Int64, type: POSParentProductType) {
self.id = id
self.name = name
self.productImageSource = productImageSource
self.productID = productID
self.type = type
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@ import struct Networking.PagedItems

public enum POSItem: Equatable, Identifiable, Hashable {
case simpleProduct(POSSimpleProduct)
case parentProduct(POSParentProduct)

public var id: UUID {
switch self {
case .simpleProduct(let product):
return product.id
case .parentProduct(let parentProduct):
return parentProduct.id
}
}
}
Expand Down
25 changes: 18 additions & 7 deletions Yosemite/Yosemite/PointOfSale/PointOfSaleProductService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -65,16 +65,27 @@ public final class PointOfSaleProductService: PointOfSaleItemServiceProtocol {
// - Product thumbnail, if any.
private func mapProductsToPOSItems(products: [Product]) -> [POSItem] {
let currencyFormatter = CurrencyFormatter(currencySettings: currencySettings)
return products.map { product in
return products.compactMap { product in
let formattedPrice = currencyFormatter.formatAmount(product.price) ?? "-"
let thumbnailSource = product.images.first?.src

return .simpleProduct(POSSimpleProduct(id: UUID(),
name: product.name,
formattedPrice: formattedPrice,
productImageSource: thumbnailSource,
productID: product.productID,
price: product.price))
switch product.productType {
case .simple:
return .simpleProduct(POSSimpleProduct(id: UUID(),
name: product.name,
formattedPrice: formattedPrice,
productImageSource: thumbnailSource,
productID: product.productID,
price: product.price))
case .variable:
return .parentProduct(POSParentProduct(id: UUID(),
name: product.name,
productImageSource: thumbnailSource,
productID: product.productID,
type: .variable))
default:
return nil
}
}
}
}
Expand Down

0 comments on commit 09768cc

Please sign in to comment.