Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Foundation
import Models

extension SelectionProduct {
extension SelectedProduct {
var sizeText: String {
var sizeValue: String = ""
if let size {
Expand Down
8 changes: 6 additions & 2 deletions Alfie/Alfie/Navigation/Coordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -135,11 +135,15 @@ final class Coordinator: ObservableObject, CoordinatorProtocol {
// MARK: - Product Details

public func openDetails(for productId: String) {
navigationAdapter.push(.productDetails(.id(productId)))
navigationAdapter.push(.productDetails(configuration: .id(productId)))
}

public func openDetails(for product: Product) {
navigationAdapter.push(.productDetails(.product(product)))
navigationAdapter.push(.productDetails(configuration: .product(product)))
}

public func openDetails(for selectedProduct: SelectedProduct) {
navigationAdapter.push(.productDetails(configuration: .selectedProduct(selectedProduct)))
}

// MARK: - Brands
Expand Down
2 changes: 1 addition & 1 deletion Alfie/Alfie/Navigation/DeepLinking/DeepLinkHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ final class DeepLinkHandler: DeepLinkHandlerProtocol {

case .productDetail(let productId, _, _, _):
// TODO: currently the API does not support fetching a product by the StyleNumber (that is parsed from the URL), just by ProductID, so all requests will return "not found"
target = Screen.productDetails(.id(productId))
target = Screen.productDetails(configuration: .id(productId))

case .webView(let url):
target = Screen.webView(url: url, title: "")
Expand Down
9 changes: 5 additions & 4 deletions Alfie/Alfie/Navigation/Screen.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ enum Screen: ScreenProtocol {
case recentSearches
case forceAppUpdate
case debugMenu
case productDetails(_ type: ThemedProductDetailsScreen)
case productDetails(configuration: ProductDetailsConfiguration)
case productListing(configuration: ProductListingScreenConfiguration)
case categoryList(_ categories: [NavigationItem], title: String)

Expand All @@ -35,17 +35,18 @@ enum TabScreen: ScreenProtocol {
static let allCases: [TabScreen] = [.home(), .shop(), .wishlist, .bag]
}

enum HomeTabConfig: Equatable, Hashable {
enum HomeTabConfig: Hashable {
case loggedIn(username: String, memberSince: Int)
case loggedOut
}

enum ThemedProductDetailsScreen: Equatable, Hashable {
enum ProductDetailsConfiguration: Hashable {
case id(_ id: String)
case product(_ product: Product)
case selectedProduct(_ selectedProduct: SelectedProduct)
}

struct ProductListingScreenConfiguration: Equatable, Hashable {
struct ProductListingScreenConfiguration: Hashable {
let category: String?
let searchText: String?
let urlQueryParameters: [String: String]?
Expand Down
3 changes: 3 additions & 0 deletions Alfie/Alfie/Navigation/TabCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,9 @@ final class TabCoordinator: TabCoordinatorProtocol, ObservableObject {

case .product(let product):
coordinator.openDetails(for: product)

case .selectedProduct(let selectedProduct):
coordinator.openDetails(for: selectedProduct)
}

case .categoryList(let categories, let title):
Expand Down
44 changes: 12 additions & 32 deletions Alfie/Alfie/Navigation/ViewFactory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -135,40 +135,20 @@ final class ViewFactory: ViewFactoryProtocol {
wishlistView
.withToolbar(for: .wishlist)

case .productDetails(let type):
switch type {
case .id(let id):
ProductDetailsView(
viewModel: ProductDetailsViewModel(
productId: id,
product: nil,
dependencies: ProductDetailsDependencyContainer(
productService: serviceProvider.productService,
webUrlProvider: serviceProvider.webUrlProvider,
bagService: serviceProvider.bagService,
wishlistService: serviceProvider.wishlistService,
configurationService: serviceProvider.configurationService,
analytics: serviceProvider.analytics
)
)
)

case .product(let product):
ProductDetailsView(
viewModel: ProductDetailsViewModel(
productId: product.id,
product: product,
dependencies: ProductDetailsDependencyContainer(
productService: serviceProvider.productService,
webUrlProvider: serviceProvider.webUrlProvider,
bagService: serviceProvider.bagService,
wishlistService: serviceProvider.wishlistService,
configurationService: serviceProvider.configurationService,
analytics: serviceProvider.analytics
)
case .productDetails(let configuration):
ProductDetailsView(
viewModel: ProductDetailsViewModel(
configuration: configuration,
dependencies: ProductDetailsDependencyContainer(
productService: serviceProvider.productService,
webUrlProvider: serviceProvider.webUrlProvider,
bagService: serviceProvider.bagService,
wishlistService: serviceProvider.wishlistService,
configurationService: serviceProvider.configurationService,
analytics: serviceProvider.analytics
)
)
}
)

case .recentSearches:
let viewModel = RecentSearchesViewModel(recentsService: serviceProvider.recentsService)
Expand Down
10 changes: 9 additions & 1 deletion Alfie/Alfie/Views/BagView/BagView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import Mocks
#endif

struct BagView<ViewModel: BagViewModelProtocol>: View {
@EnvironmentObject var coordinador: Coordinator
@StateObject private var viewModel: ViewModel

init(viewModel: ViewModel) {
Expand All @@ -15,7 +16,14 @@ struct BagView<ViewModel: BagViewModelProtocol>: View {
var body: some View {
List {
ForEach(viewModel.products) { product in
HorizontalProductCard(viewModel: viewModel.productCardViewModel(for: product))
Button(
action: { coordinador.openDetails(for: product) },
label: {
HorizontalProductCard(viewModel: viewModel.productCardViewModel(for: product))
.contentShape(Rectangle())
}
)
.buttonStyle(.plain)
.listRowInsets(EdgeInsets())
}
.onDelete { offsets in
Expand Down
22 changes: 11 additions & 11 deletions Alfie/Alfie/Views/BagView/BagViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import Models
import SharedUI

final class BagViewModel: BagViewModelProtocol {
@Published private(set) var products: [SelectionProduct]
@Published private(set) var products: [SelectedProduct]

private let dependencies: BagDependencyContainer

Expand All @@ -18,22 +18,22 @@ final class BagViewModel: BagViewModelProtocol {
products = dependencies.bagService.getBagContent()
}

func didSelectDelete(for product: SelectionProduct) {
dependencies.bagService.removeProduct(product)
func didSelectDelete(for selectedProduct: SelectedProduct) {
dependencies.bagService.removeProduct(selectedProduct)
products = dependencies.bagService.getBagContent()
dependencies.analytics.trackRemoveFromBag(productID: product.id)
dependencies.analytics.trackRemoveFromBag(productID: selectedProduct.product.id)
}

func productCardViewModel(for product: SelectionProduct) -> HorizontalProductCardViewModel {
func productCardViewModel(for selectedProduct: SelectedProduct) -> HorizontalProductCardViewModel {
.init(
image: product.media.first?.asImage?.url,
designer: product.brand.name,
name: product.name,
image: selectedProduct.media.first?.asImage?.url,
designer: selectedProduct.brand.name,
name: selectedProduct.name,
colorTitle: L10n.Product.Color.title + ":",
color: product.colour?.name ?? "",
color: selectedProduct.colour?.name ?? "",
sizeTitle: L10n.Product.Size.title + ":",
size: product.size == nil ? L10n.Product.OneSize.title : product.sizeText,
priceType: product.priceType
size: selectedProduct.size == nil ? L10n.Product.OneSize.title : selectedProduct.sizeText,
priceType: selectedProduct.priceType
)
}
}
2 changes: 1 addition & 1 deletion Alfie/Alfie/Views/ProductDetails/ProductDetailsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ struct ProductDetailsView<ViewModel: ProductDetailsViewModelProtocol>: View {
.writingSize(to: $viewSize)
}
}
.withToolbar(for: .productDetails(.id(viewModel.productId)))
.withToolbar(for: .productDetails(configuration: .id(viewModel.productId)))
.toolbar {
if !viewModel.state.didFail {
ToolbarItem(placement: .principal) {
Expand Down
53 changes: 38 additions & 15 deletions Alfie/Alfie/Views/ProductDetails/ProductDetailsViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ final class ProductDetailsViewModel: ProductDetailsViewModelProtocol {
private(set) var colorSelectionConfiguration: ColorAndSizingSelectorConfiguration<ColorSwatch> = .init(items: [])
private(set) var sizingSelectionConfiguration: ColorAndSizingSelectorConfiguration<SizingSwatch> = .init(items: [])
public let productId: String
private let initialSelectedProduct: SelectedProduct?

private var product: Product? {
guard case .success(let model) = state else {
Expand All @@ -27,7 +28,7 @@ final class ProductDetailsViewModel: ProductDetailsViewModelProtocol {

private var selectedVariant: Product.Variant? {
guard case .success(let model) = state else {
return baseProduct?.defaultVariant
return initialSelectedProduct?.selectedVariant ?? baseProduct?.defaultVariant
}

return model.selectedVariant
Expand Down Expand Up @@ -88,15 +89,36 @@ final class ProductDetailsViewModel: ProductDetailsViewModelProtocol {
product?.priceType
}

init(productId: String, product: Product?, dependencies: ProductDetailsDependencyContainer) {
self.productId = productId
baseProduct = product
init(
configuration: ProductDetailsConfiguration,
dependencies: ProductDetailsDependencyContainer
) {
self.dependencies = dependencies

if let baseProduct {
switch configuration {
case .id(let productId):
self.productId = productId
self.initialSelectedProduct = nil
self.baseProduct = nil

case .product(let product):
self.productId = product.id
self.initialSelectedProduct = nil
self.baseProduct = product

buildColorAndSizingSelectionConfigurations(
product: baseProduct,
selectedVariant: baseProduct.defaultVariant
product: product,
selectedVariant: product.defaultVariant
)

case .selectedProduct(let selectedProduct):
self.productId = selectedProduct.product.id
self.initialSelectedProduct = selectedProduct
baseProduct = selectedProduct.product

buildColorAndSizingSelectionConfigurations(
product: selectedProduct.product,
selectedVariant: selectedProduct.selectedVariant
)
}
}
Expand Down Expand Up @@ -205,13 +227,14 @@ final class ProductDetailsViewModel: ProductDetailsViewModelProtocol {
return
}

buildColorAndSizingSelectionConfigurations(product: product, selectedVariant: product.defaultVariant)
state = .success(.init(product: product, selectedVariant: product.defaultVariant))
let selectedVariant = initialSelectedProduct?.selectedVariant ?? product.defaultVariant
buildColorAndSizingSelectionConfigurations(product: product, selectedVariant: selectedVariant)
state = .success(.init(product: product, selectedVariant: selectedVariant))
}

private func buildColorAndSizingSelectionConfigurations(product: Product, selectedVariant: Product.Variant?) {
buildColorSelectionConfiguration(product: product, selectedVariant: product.defaultVariant)
buildSizingSelectionConfiguration(product: product, selectedVariant: product.defaultVariant)
private func buildColorAndSizingSelectionConfigurations(product: Product, selectedVariant: Product.Variant) {
buildColorSelectionConfiguration(product: product, selectedVariant: selectedVariant)
buildSizingSelectionConfiguration(product: product, selectedVariant: selectedVariant)
}

private func buildColorSelectionConfiguration(product: Product, selectedVariant: Product.Variant?) {
Expand Down Expand Up @@ -364,14 +387,14 @@ final class ProductDetailsViewModel: ProductDetailsViewModelProtocol {
state = .success(.init(product: product, selectedVariant: variant))
}

private var selectedProduct: SelectionProduct? {
private var selectedProduct: SelectedProduct? {
guard
let product,
let selectedVariant
else {
return nil
return initialSelectedProduct
}

return SelectionProduct(product: product, selectedVariant: selectedVariant)
return SelectedProduct(product: product, selectedVariant: selectedVariant)
}
}
11 changes: 6 additions & 5 deletions Alfie/Alfie/Views/ProductListing/ProductListingViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ final class ProductListingViewModel: ProductListingViewModelProtocol {
@Published var style: ProductListingListStyle
@Published var showRefine = false
@Published var sortOption: String?
@Published private(set) var wishlistContent: [SelectionProduct]
@Published private(set) var wishlistContent: [SelectedProduct]
@Published private(set) var state: PaginatedViewState<ProductListingViewStateModel, ProductListingViewErrorType>

private enum Constants {
Expand Down Expand Up @@ -79,16 +79,17 @@ final class ProductListingViewModel: ProductListingViewModelProtocol {
func didSelect(_: Product) {}

func isFavoriteState(for product: Product) -> Bool {
wishlistContent.contains { $0.id == product.defaultVariant.sku }
wishlistContent.contains { $0.product.defaultVariant.sku == product.defaultVariant.sku }
}

func didTapAddToWishlist(for product: Product, isFavorite: Bool) {
if !isFavorite {
let selectedProduct = SelectionProduct(product: product)
let selectedProduct = SelectedProduct(product: product)
dependencies.wishlistService.addProduct(selectedProduct)
dependencies.analytics.trackAddToWishlist(productID: selectedProduct.id)
dependencies.analytics.trackAddToWishlist(productID: product.id)
} else {
dependencies.wishlistService.removeProduct(product.defaultVariant.sku)
let selectedProduct = SelectedProduct(product: product)
dependencies.wishlistService.removeProduct(selectedProduct)
dependencies.analytics.trackRemoveFromWishlist(productID: product.id)
}
wishlistContent = dependencies.wishlistService.getWishlistContent()
Expand Down
20 changes: 14 additions & 6 deletions Alfie/Alfie/Views/WishlistView/WishlistView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import Mocks
#endif

struct WishlistView<ViewModel: WishlistViewModelProtocol>: View {
@EnvironmentObject var coordinador: Coordinator
@StateObject private var viewModel: ViewModel

init(viewModel: ViewModel) {
Expand All @@ -22,11 +23,18 @@ struct WishlistView<ViewModel: WishlistViewModelProtocol>: View {
spacing: Spacing.space200
) {
ForEach(viewModel.products) { product in
VerticalProductCard(
viewModel: viewModel.productCardViewModel(for: product)
) { _, type in
handleUserAction(forProduct: product, actionType: type)
}
Button(
action: { coordinador.openDetails(for: product) },
label: {
VerticalProductCard(
viewModel: viewModel.productCardViewModel(for: product)
) { _, type in
handleUserAction(forProduct: product, actionType: type)
}
}
)
.buttonStyle(.plain)
.listRowInsets(EdgeInsets())
}
}
.padding(.horizontal, Spacing.space200)
Expand All @@ -41,7 +49,7 @@ struct WishlistView<ViewModel: WishlistViewModelProtocol>: View {
// MARK: - Private Methods

private extension WishlistView {
func handleUserAction(forProduct product: SelectionProduct, actionType: VerticalProductCard.ProductUserActionType) {
func handleUserAction(forProduct product: SelectedProduct, actionType: VerticalProductCard.ProductUserActionType) {
// swiftlint:disable vertical_whitespace_between_cases
switch actionType {
case .remove:
Expand Down
Loading