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
Original file line number Diff line number Diff line change
Expand Up @@ -374,14 +374,14 @@ private extension StoreCreationCoordinator {
categoryName: String?,
countryCode: SiteAddress.CountryCode?,
planToPurchase: WPComPlanProduct) {
let domainSelector = DomainSelectorHostingController(viewModel: .init(initialSearchTerm: storeName),
onDomainSelection: { [weak self] domain in
let domainSelector = FreeDomainSelectorHostingController(viewModel: .init(initialSearchTerm: storeName, dataProvider: FreeDomainSelectorDataProvider()),
onDomainSelection: { [weak self] domain in
guard let self else { return }
await self.createStoreAndContinueToStoreSummary(from: navigationController,
name: storeName,
categoryName: categoryName,
countryCode: countryCode,
domain: domain,
domain: domain.name,
planToPurchase: planToPurchase)
}, onSupport: { [weak self] in
self?.showSupport(from: navigationController)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,14 @@ struct DomainRowViewModel {
let name: String
/// Attributed name to be displayed in the row.
let attributedName: AttributedString
/// Attributed detail to be displayed in the row.
let attributedDetail: AttributedString?
/// Whether the domain is selected.
let isSelected: Bool

init(domainName: String, searchQuery: String, isSelected: Bool) {
init(domainName: String, attributedDetail: AttributedString?, searchQuery: String, isSelected: Bool) {
self.name = domainName
self.attributedDetail = attributedDetail
self.isSelected = isSelected
self.attributedName = {
var attributedName = AttributedString(domainName)
Expand Down Expand Up @@ -38,7 +41,12 @@ struct DomainRowView: View {

var body: some View {
HStack {
Text(viewModel.attributedName)
VStack(alignment: .leading, spacing: Layout.spacingBetweenNameAndDetail) {
Text(viewModel.attributedName)
if let attributedDetail = viewModel.attributedDetail {
Text(attributedDetail)
}
}
if viewModel.isSelected {
Spacer()
Image(uiImage: .checkmarkImage)
Expand All @@ -52,14 +60,21 @@ struct DomainRowView: View {
private extension DomainRowView {
enum Layout {
static let insets: EdgeInsets = .init(top: 10, leading: 16, bottom: 10, trailing: 16)
static let spacingBetweenNameAndDetail: CGFloat = 4
}
}

struct DomainRowView_Previews: PreviewProvider {
static var previews: some View {
VStack(alignment: .leading) {
DomainRowView(viewModel: .init(domainName: "whitechristmastrees.mywc.mysite", searchQuery: "White Christmas Trees", isSelected: true))
DomainRowView(viewModel: .init(domainName: "whitechristmastrees.mywc.mysite", searchQuery: "White Christmas", isSelected: false))
DomainRowView(viewModel: .init(domainName: "whitechristmastrees.mywc.mysite",
attributedDetail: nil,
searchQuery: "White Christmas Trees",
isSelected: true))
DomainRowView(viewModel: .init(domainName: "whitechristmastrees.mywc.mysite",
attributedDetail: nil,
searchQuery: "White Christmas",
isSelected: false))
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import Foundation
import Yosemite

/// Provides domain suggestions data of a generic type.
/// The generic type allows different domain suggestion schemas, like free and paid domains.
protocol DomainSelectorDataProvider {
associatedtype DomainSuggestion

/// Loads domain suggestions async from the remote.
/// - Parameter query: Search query for the domain suggestions.
/// - Returns: A list of domain suggestions.
func loadDomainSuggestions(query: String) async throws -> [DomainSuggestion]
}

/// View model for free domain suggestion UI that shows the domain name.
struct FreeDomainSuggestionViewModel: DomainSuggestionViewProperties, Equatable {
let name: String
let attributedDetail: AttributedString? = nil

init(domainSuggestion: FreeDomainSuggestion) {
self.name = domainSuggestion.name
}
}

/// Provides domain suggestions that are free.
final class FreeDomainSelectorDataProvider: DomainSelectorDataProvider {
private let stores: StoresManager

init(stores: StoresManager = ServiceLocator.stores) {
self.stores = stores
}

@MainActor
func loadDomainSuggestions(query: String) async throws -> [FreeDomainSuggestionViewModel] {
try await withCheckedThrowingContinuation { continuation in
stores.dispatch(DomainAction.loadFreeDomainSuggestions(query: query) { result in
continuation.resume(with: result.map { $0
.filter { $0.isFree }
.map { FreeDomainSuggestionViewModel(domainSuggestion: $0) }
})
})
}
}
}

/// View model for paid domain suggestion UI that shows the domain name and attributed price info.
/// The product ID is for creating a cart after a domain is selected.
struct PaidDomainSuggestionViewModel: DomainSuggestionViewProperties, Equatable {
let name: String
let attributedDetail: AttributedString?
let productID: Int64

init(domainSuggestion: PaidDomainSuggestion) {
self.name = domainSuggestion.name
// TODO: 8558 - attributed price info
self.attributedDetail = .init("\(domainSuggestion.saleCost ?? "no sale") / \(domainSuggestion.cost) / \(domainSuggestion.term)")
self.productID = domainSuggestion.productID
}
}

/// Provides domain suggestions that are paid.
final class PaidDomainSelectorDataProvider: DomainSelectorDataProvider {
private let stores: StoresManager

init(stores: StoresManager = ServiceLocator.stores) {
self.stores = stores
}

@MainActor
func loadDomainSuggestions(query: String) async throws -> [PaidDomainSuggestionViewModel] {
try await withCheckedThrowingContinuation { continuation in
stores.dispatch(DomainAction.loadPaidDomainSuggestions(query: query) { result in
continuation.resume(with: result.map { $0.map { PaidDomainSuggestionViewModel(domainSuggestion: $0) } })
})
}
}
}
Loading