feat(gift): 선물 발송 화면을 추가한다
This commit is contained in:
@@ -0,0 +1,168 @@
|
||||
import Foundation
|
||||
import Combine
|
||||
|
||||
final class GiftSendViewModel: ObservableObject {
|
||||
let recipientMemberId: Int
|
||||
let recipientCreatorNickname: String
|
||||
|
||||
private let repository = GiftRepository()
|
||||
private let onCreated: (String) -> Void
|
||||
private var subscription = Set<AnyCancellable>()
|
||||
private var hasLoaded = false
|
||||
|
||||
@Published private(set) var sizes = [GiftSizeOptionResponse]()
|
||||
@Published private(set) var categories = [GiftCategoryOptionResponse]()
|
||||
@Published var selectedSize: GiftSizeOptionResponse?
|
||||
@Published private(set) var selectedCategory: GiftCategoryOptionResponse?
|
||||
@Published var senderName = ""
|
||||
@Published var senderPhoneNumber = ""
|
||||
@Published var senderZipCode = ""
|
||||
@Published var senderAddress = ""
|
||||
@Published var senderAddressDetail = ""
|
||||
@Published var senderTermsAgreed = false
|
||||
@Published var senderPrivacyAgreed = false
|
||||
@Published var damageWaiverAgreed = false
|
||||
@Published private(set) var isLoading = false
|
||||
@Published private(set) var isSubmitting = false
|
||||
@Published var errorMessage = ""
|
||||
@Published var isShowPopup = false
|
||||
|
||||
var isRecipientContextValid: Bool {
|
||||
recipientMemberId > 0
|
||||
&& !recipientCreatorNickname.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
|
||||
}
|
||||
|
||||
var canSubmit: Bool {
|
||||
!isSubmitting
|
||||
&& isRecipientContextValid
|
||||
&& selectedSize != nil
|
||||
&& selectedCategory != nil
|
||||
&& GiftContactInputRule.name.acceptsSubmission(senderName)
|
||||
&& GiftContactInputRule.phoneNumber.acceptsSubmission(senderPhoneNumber)
|
||||
&& [senderZipCode, senderAddress].allSatisfy {
|
||||
!$0.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
|
||||
}
|
||||
&& senderTermsAgreed
|
||||
&& senderPrivacyAgreed
|
||||
&& (selectedCategory?.requiresDamageWaiver != true || damageWaiverAgreed)
|
||||
}
|
||||
|
||||
init(recipientMemberId: Int, recipientCreatorNickname: String, onCreated: @escaping (String) -> Void) {
|
||||
self.recipientMemberId = recipientMemberId
|
||||
self.recipientCreatorNickname = recipientCreatorNickname
|
||||
self.onCreated = onCreated
|
||||
}
|
||||
|
||||
func selectCategory(_ category: GiftCategoryOptionResponse) {
|
||||
if selectedCategory?.categoryId != category.categoryId || !category.requiresDamageWaiver {
|
||||
damageWaiverAgreed = false
|
||||
}
|
||||
selectedCategory = category
|
||||
}
|
||||
|
||||
func submitGift() {
|
||||
guard canSubmit, let request = makeRequest() else { return }
|
||||
|
||||
isSubmitting = true
|
||||
errorMessage = ""
|
||||
isShowPopup = false
|
||||
|
||||
repository.createGift(request: request)
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { [weak self] result in
|
||||
if case .failure(let error) = result {
|
||||
self?.showSubmissionError(data: error.response?.data)
|
||||
}
|
||||
} receiveValue: { [weak self] response in
|
||||
guard let self else { return }
|
||||
|
||||
do {
|
||||
let decoded = try JSONDecoder().decode(ApiResponse<CreateGiftResponse>.self, from: response.data)
|
||||
guard decoded.success, let data = decoded.data else {
|
||||
self.showSubmissionError(data: response.data)
|
||||
return
|
||||
}
|
||||
self.onCreated(data.applicationNo)
|
||||
} catch {
|
||||
self.showSubmissionError(data: response.data)
|
||||
}
|
||||
}
|
||||
.store(in: &subscription)
|
||||
}
|
||||
|
||||
private func makeRequest() -> CreateGiftRequest? {
|
||||
guard let selectedSize, let selectedCategory else { return nil }
|
||||
|
||||
return CreateGiftRequest(
|
||||
recipientMemberId: recipientMemberId,
|
||||
senderName: senderName.trimmingCharacters(in: .whitespacesAndNewlines),
|
||||
senderPhoneNumber: senderPhoneNumber.trimmingCharacters(in: .whitespacesAndNewlines),
|
||||
senderZipCode: senderZipCode.trimmingCharacters(in: .whitespacesAndNewlines),
|
||||
senderAddress: senderAddress.trimmingCharacters(in: .whitespacesAndNewlines),
|
||||
senderAddressDetail: senderAddressDetail.trimmingCharacters(in: .whitespacesAndNewlines),
|
||||
sizeCode: selectedSize.sizeCode.trimmingCharacters(in: .whitespacesAndNewlines),
|
||||
categoryId: selectedCategory.categoryId,
|
||||
senderTermsAgreed: senderTermsAgreed,
|
||||
senderPrivacyAgreed: senderPrivacyAgreed,
|
||||
damageWaiverAgreed: selectedCategory.requiresDamageWaiver && damageWaiverAgreed
|
||||
)
|
||||
}
|
||||
|
||||
private func showSubmissionError(data: Data?) {
|
||||
struct ErrorResponse: Decodable {
|
||||
let message: String?
|
||||
}
|
||||
|
||||
let decoded = data.flatMap { try? JSONDecoder().decode(ErrorResponse.self, from: $0) }
|
||||
let message = decoded?.message?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
|
||||
errorMessage = message.isEmpty ? I18n.Common.commonError : message
|
||||
isSubmitting = false
|
||||
isShowPopup = true
|
||||
}
|
||||
|
||||
func fetchFormOptions() {
|
||||
guard isRecipientContextValid else {
|
||||
errorMessage = I18n.Common.commonError
|
||||
isShowPopup = true
|
||||
return
|
||||
}
|
||||
guard !isLoading, !hasLoaded else { return }
|
||||
|
||||
isLoading = true
|
||||
errorMessage = ""
|
||||
isShowPopup = false
|
||||
|
||||
repository.getFormOptions()
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { [weak self] result in
|
||||
guard let self else { return }
|
||||
|
||||
self.isLoading = false
|
||||
if case .failure = result {
|
||||
self.errorMessage = I18n.Common.commonError
|
||||
self.isShowPopup = true
|
||||
}
|
||||
} receiveValue: { [weak self] response in
|
||||
guard let self else { return }
|
||||
|
||||
do {
|
||||
let decoded = try JSONDecoder().decode(ApiResponse<GiftFormOptionsResponse>.self, from: response.data)
|
||||
|
||||
if let data = decoded.data, decoded.success {
|
||||
self.sizes = data.sizes
|
||||
self.categories = data.categories
|
||||
self.selectedSize = data.sizes.first
|
||||
self.selectedCategory = nil
|
||||
self.hasLoaded = true
|
||||
} else {
|
||||
self.errorMessage = decoded.message ?? I18n.Common.commonError
|
||||
self.isShowPopup = true
|
||||
}
|
||||
} catch {
|
||||
self.errorMessage = I18n.Common.commonError
|
||||
self.isShowPopup = true
|
||||
}
|
||||
}
|
||||
.store(in: &subscription)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user