Skip to content

Commit add4336

Browse files
committed
Bind to order for customer data and delete customer sync
1 parent 0161a0c commit add4336

File tree

6 files changed

+95
-111
lines changed

6 files changed

+95
-111
lines changed
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import Foundation
2+
import Yosemite
3+
import Storage
4+
5+
final class BookingDetailsResultsController {
6+
private let storageManager: StorageManagerType
7+
private let booking: Yosemite.Booking
8+
9+
private lazy var orderResultsController: ResultsController<StorageOrder> = {
10+
let predicate = NSPredicate(
11+
format: "siteID = %ld AND orderID = %ld",
12+
booking.siteID,
13+
booking.orderID
14+
)
15+
return ResultsController<StorageOrder>(
16+
storageManager: storageManager,
17+
matching: predicate,
18+
sortedBy: []
19+
)
20+
}()
21+
22+
var order: Yosemite.Order? {
23+
orderResultsController.fetchedObjects.first
24+
}
25+
26+
init(booking: Yosemite.Booking, storageManager: StorageManagerType = ServiceLocator.storageManager) {
27+
self.booking = booking
28+
self.storageManager = storageManager
29+
}
30+
31+
func configure(onReload: @escaping () -> Void) {
32+
orderResultsController.onDidChangeContent = {
33+
onReload()
34+
}
35+
36+
orderResultsController.onDidResetContent = { [weak self] in
37+
try? self?.orderResultsController.performFetch()
38+
onReload()
39+
}
40+
41+
do {
42+
try orderResultsController.performFetch()
43+
} catch {
44+
DDLogError("⛔️ Unable to fetch Order for Booking: \(error)")
45+
}
46+
}
47+
}

WooCommerce/Classes/ViewModels/Booking Details/BookingDetailsViewModel.swift

Lines changed: 44 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,19 @@ import SwiftUI // Added for withAnimation
44

55
final class BookingDetailsViewModel: ObservableObject {
66
private let stores: StoresManager
7+
private lazy var resultsController = BookingDetailsResultsController(booking: booking)
78

89
private let booking: Booking
910
private let headerContent: HeaderContent
1011
private let customerContent = CustomerContent()
11-
private(set) var order: Order?
12+
private(set) var order: Order? {
13+
didSet {
14+
guard let order else {
15+
return
16+
}
17+
updateCustomerInfo(from: order)
18+
}
19+
}
1220

1321
let navigationTitle: String
1422
@Published private(set) var sections: [Section] = []
@@ -19,9 +27,14 @@ final class BookingDetailsViewModel: ObservableObject {
1927
self.headerContent = HeaderContent(booking)
2028
navigationTitle = Self.navigationTitle(for: booking)
2129
setupSections()
30+
configureResultsController()
2231
}
32+
}
33+
34+
// MARK: Private
2335

24-
private func setupSections() {
36+
private extension BookingDetailsViewModel {
37+
func setupSections() {
2538
let headerSection = Section(
2639
content: .header(headerContent)
2740
)
@@ -55,151 +68,76 @@ final class BookingDetailsViewModel: ObservableObject {
5568
bookingNotes
5669
]
5770
}
58-
}
5971

60-
// MARK: Local Data
72+
func configureResultsController() {
73+
resultsController.configure { [weak self] in
74+
self?.order = self?.resultsController.order
75+
}
76+
self.order = resultsController.order
77+
}
6178

62-
extension BookingDetailsViewModel {
63-
func loadLocalData() {
64-
loadCustomerData()
79+
func updateCustomerInfo(from order: Order) {
80+
guard let billingAddress = order.billingAddress else {
81+
return
82+
}
83+
84+
customerContent.update(with: billingAddress)
85+
headerContent.update(with: billingAddress)
86+
insertCustomerSectionIfAbsent()
6587
}
66-
}
6788

68-
private extension BookingDetailsViewModel {
69-
func loadCustomerData() {
70-
guard booking.customerID > 0 else {
89+
func insertCustomerSectionIfAbsent() {
90+
// Avoid adding if it already exists
91+
guard !sections.contains(where: { if case .customer = $0.content { return true } else { return false } }) else {
7192
return
7293
}
7394

74-
let action = CustomerAction.loadCustomer(siteID: booking.siteID, customerID: booking.customerID) { [weak self] result in
75-
guard let self = self else { return }
76-
if case .success(let customer) = result {
77-
Task {
78-
await self.updateCustomerSection(with: customer)
79-
await self.updateHeader(with: customer)
80-
}
81-
}
95+
let customerSection = Section(
96+
header: .title(Localization.customerSectionHeaderTitle.uppercased()),
97+
content: .customer(customerContent)
98+
)
99+
withAnimation {
100+
sections.insert(customerSection, at: 2)
82101
}
83-
stores.dispatch(action)
84102
}
85103
}
86104

87105
// MARK: Syncing
88106

89107
extension BookingDetailsViewModel {
90108
func syncData() async {
91-
await withTaskGroup(of: Void.self) { group in
92-
group.addTask { await self.syncCustomer() }
93-
group.addTask { await self.syncOrder() }
94-
}
109+
await syncOrder()
95110
}
96111
}
97112

98113
private extension BookingDetailsViewModel {
99-
func syncCustomer() async {
100-
guard shouldSyncCustomer else {
101-
return
102-
}
103-
104-
do {
105-
let fetchedCustomer = try await retrieveCustomer()
106-
await updateCustomerSection(with: fetchedCustomer)
107-
await updateHeader(with: fetchedCustomer)
108-
} catch {
109-
DDLogError("⛔️ Error synchronizing Customer for Booking: \(error)")
110-
}
111-
}
112-
113114
func syncOrder() async {
114115
guard booking.orderID > 0 else {
115116
return
116117
}
117118

118119
do {
119-
let fetchedOrder = try await retrieveOrder()
120-
await MainActor.run {
121-
self.order = fetchedOrder
122-
}
120+
try await fetchRemoteOrder()
123121
} catch {
124122
DDLogError("⛔️ Error synchronizing Order for Booking: \(error)")
125123
}
126124
}
127125

128126
@MainActor
129-
func retrieveCustomer() async throws -> Customer {
130-
try await withCheckedThrowingContinuation { continuation in
131-
let action = CustomerAction.retrieveCustomer(
132-
siteID: booking.siteID,
133-
customerID: booking.customerID
134-
) { result in
135-
switch result {
136-
case .success(let customer):
137-
continuation.resume(returning: customer)
138-
case .failure(let error):
139-
continuation.resume(throwing: error)
140-
}
141-
}
142-
stores.dispatch(action)
143-
}
144-
}
145-
146-
@MainActor
147-
func retrieveOrder() async throws -> Order {
148-
enum OrderSyncError: Error {
149-
case unknown
150-
}
151-
127+
func fetchRemoteOrder() async throws {
152128
return try await withCheckedThrowingContinuation { continuation in
153129
let action = OrderAction.retrieveOrder(
154130
siteID: booking.siteID,
155131
orderID: booking.orderID
156-
) { order, error in
157-
if let order = order {
158-
continuation.resume(returning: order)
159-
} else if let error = error {
132+
) { _, error in
133+
if let error {
160134
continuation.resume(throwing: error)
161-
} else {
162-
continuation.resume(throwing: OrderSyncError.unknown)
163135
}
136+
continuation.resume(returning: ())
164137
}
165138
stores.dispatch(action)
166139
}
167140
}
168-
169-
@MainActor
170-
func updateCustomerSection(with customer: Customer) {
171-
if let billingAddress = customer.billing {
172-
customerContent.update(with: billingAddress)
173-
}
174-
insertCustomerSectionIfAbsent()
175-
}
176-
177-
@MainActor
178-
func updateHeader(with customer: Customer) {
179-
if let billingAddress = customer.billing {
180-
headerContent.update(with: billingAddress)
181-
}
182-
}
183-
184-
func insertCustomerSectionIfAbsent() {
185-
// Avoid adding if it already exists
186-
guard !sections.contains(where: { if case .customer = $0.content { return true } else { return false } }) else {
187-
return
188-
}
189-
190-
let customerSection = Section(
191-
header: .title(Localization.customerSectionHeaderTitle.uppercased()),
192-
content: .customer(customerContent)
193-
)
194-
withAnimation {
195-
sections.insert(customerSection, at: 2)
196-
}
197-
}
198-
199-
/// Returns true when the `customerID` is non-zero and customer section doesn't exist
200-
var shouldSyncCustomer: Bool {
201-
return booking.customerID > 0
202-
}
203141
}
204142

205143
extension BookingDetailsViewModel {

WooCommerce/Classes/ViewModels/Booking Details/CustomerContent.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ extension BookingDetailsViewModel {
88
@Published var phoneText: String?
99
@Published var billingAddressText: String?
1010

11-
@MainActor
1211
func update(with address: Address) {
1312
let name = [
1413
address.firstName,

WooCommerce/Classes/ViewModels/Booking Details/HeaderContent.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ extension BookingDetailsViewModel {
3131
status = [.booked, .payAtLocation]
3232
}
3333

34-
@MainActor
3534
func update(with address: Address) {
3635
let customerName = [
3736
address.firstName,

WooCommerce/Classes/ViewRelated/Bookings/Booking Details/BookingDetailsView.swift

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,6 @@ struct BookingDetailsView: View {
2929
init(_ viewModel: BookingDetailsViewModel) {
3030
self.viewModel = viewModel
3131

32-
/// Trigger local data load
33-
viewModel.loadLocalData()
34-
3532
/// Trigger remote data sync
3633
Task {
3734
await viewModel.syncData()

WooCommerce/WooCommerce.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -993,6 +993,7 @@
993993
2D05337E2E951A62004111FD /* BookingDetailsViewModel+PriceFormatting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D05337D2E951A62004111FD /* BookingDetailsViewModel+PriceFormatting.swift */; };
994994
2D054A2A2E953E3C004111FD /* BookingDetailsViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D054A292E953E3C004111FD /* BookingDetailsViewModelTests.swift */; };
995995
2D0555812E9693E6004111FD /* HeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D0555802E9693E1004111FD /* HeaderView.swift */; };
996+
2D057B4F2E9D2A95004111FD /* BookingDetailsResultsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D057B4E2E9D2A95004111FD /* BookingDetailsResultsController.swift */; };
996997
2D05D19F2E82D1A8004111FD /* BookingDetailsViewModel+Section.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D05D19E2E82D1A3004111FD /* BookingDetailsViewModel+Section.swift */; };
997998
2D05D1A22E82D235004111FD /* HeaderContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D05D1A12E82D233004111FD /* HeaderContent.swift */; };
998999
2D05D1A42E82D266004111FD /* AppointmentDetailsContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D05D1A32E82D25F004111FD /* AppointmentDetailsContent.swift */; };
@@ -3901,6 +3902,7 @@
39013902
2D05337D2E951A62004111FD /* BookingDetailsViewModel+PriceFormatting.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BookingDetailsViewModel+PriceFormatting.swift"; sourceTree = "<group>"; };
39023903
2D054A292E953E3C004111FD /* BookingDetailsViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookingDetailsViewModelTests.swift; sourceTree = "<group>"; };
39033904
2D0555802E9693E1004111FD /* HeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeaderView.swift; sourceTree = "<group>"; };
3905+
2D057B4E2E9D2A95004111FD /* BookingDetailsResultsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookingDetailsResultsController.swift; sourceTree = "<group>"; };
39043906
2D05D19E2E82D1A3004111FD /* BookingDetailsViewModel+Section.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BookingDetailsViewModel+Section.swift"; sourceTree = "<group>"; };
39053907
2D05D1A02E82D1EF004111FD /* BookingDetailsViewModel+SectionContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BookingDetailsViewModel+SectionContent.swift"; sourceTree = "<group>"; };
39063908
2D05D1A12E82D233004111FD /* HeaderContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeaderContent.swift; sourceTree = "<group>"; };
@@ -7978,6 +7980,7 @@
79787980
2D05D19E2E82D1A3004111FD /* BookingDetailsViewModel+Section.swift */,
79797981
2D05D1A02E82D1EF004111FD /* BookingDetailsViewModel+SectionContent.swift */,
79807982
2D05337D2E951A62004111FD /* BookingDetailsViewModel+PriceFormatting.swift */,
7983+
2D057B4E2E9D2A95004111FD /* BookingDetailsResultsController.swift */,
79817984
);
79827985
path = "Booking Details";
79837986
sourceTree = "<group>";
@@ -15541,6 +15544,7 @@
1554115544
0270F47824D006F60005210A /* ProductFormPresentationStyle.swift in Sources */,
1554215545
EECB7EE62864647F0028C888 /* ProductImagesProductIDUpdater.swift in Sources */,
1554315546
DE96844B2A331AD2000FBF4E /* WooAnalyticsEvent+ProductSharingAI.swift in Sources */,
15547+
2D057B4F2E9D2A95004111FD /* BookingDetailsResultsController.swift in Sources */,
1554415548
FE28F7182684EE6A004465C7 /* RoleErrorViewModel.swift in Sources */,
1554515549
02C0CD2C23B5BC9600F880B1 /* DefaultImageService.swift in Sources */,
1554615550
B9D19A442AE7B66B00D944D8 /* CustomAmountRowView.swift in Sources */,

0 commit comments

Comments
 (0)