feat(gift): 수령지 입력을 추가한다

This commit is contained in:
Yu Sung
2026-10-08 12:32:59 +09:00
parent 52f96f29ca
commit b0336902e6
4 changed files with 445 additions and 0 deletions
@@ -0,0 +1,98 @@
import Combine
import Foundation
final class GiftRecipientAddressViewModel: ObservableObject {
@Published var recipientName: String = ""
@Published var recipientPhoneNumber: String = ""
@Published var recipientZipCode: String = ""
@Published var recipientAddress: String = ""
@Published var recipientAddressDetail: String = ""
@Published var recipientTermsAgreed: Bool = false
@Published var recipientPrivacyAgreed: Bool = false
@Published private(set) var isSubmitting = false
@Published var errorMessage = ""
@Published var isShowPopup = false
@Published var isShowSuccessToast = false
let successMessage = "배송지 입력 완료"
private let applicationNo: String
private let onSubmitted: () -> Void
private let repository = GiftRepository()
private var mutationSubscription: AnyCancellable?
@Published private(set) var hasSubmitted = false
private var hasNotifiedSubmission = false
init(applicationNo: String, onSubmitted: @escaping () -> Void) {
self.applicationNo = applicationNo
self.onSubmitted = onSubmitted
}
var canSubmit: Bool {
!isSubmitting && !hasSubmitted && recipientTermsAgreed && recipientPrivacyAgreed
&& GiftContactInputRule.name.acceptsSubmission(recipientName)
&& GiftContactInputRule.phoneNumber.acceptsSubmission(recipientPhoneNumber)
&& [recipientZipCode, recipientAddress].allSatisfy {
!$0.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
}
}
func submit() {
guard canSubmit else { return }
isSubmitting = true
errorMessage = ""
isShowPopup = false
let request = SubmitGiftRecipientAddressRequest(
recipientName: recipientName.trimmingCharacters(in: .whitespacesAndNewlines),
recipientPhoneNumber: recipientPhoneNumber.trimmingCharacters(in: .whitespacesAndNewlines),
recipientZipCode: recipientZipCode.trimmingCharacters(in: .whitespacesAndNewlines),
recipientAddress: recipientAddress.trimmingCharacters(in: .whitespacesAndNewlines),
recipientAddressDetail: recipientAddressDetail.trimmingCharacters(in: .whitespacesAndNewlines),
recipientTermsAgreed: recipientTermsAgreed,
recipientPrivacyAgreed: recipientPrivacyAgreed
)
mutationSubscription = repository.submitRecipientAddress(applicationNo: applicationNo, request: request)
.receive(on: DispatchQueue.main)
.sink { [weak self] result in
guard let self, !self.hasSubmitted else { return }
if case .failure(let error) = result {
self.finishFailure(data: error.response?.data)
}
} receiveValue: { [weak self] response in
guard let self, !self.hasSubmitted else { return }
do {
let decoded = try JSONDecoder().decode(ApiResponseWithoutData.self, from: response.data)
guard decoded.success else {
self.finishFailure(data: response.data)
return
}
self.hasSubmitted = true
self.isSubmitting = false
self.isShowSuccessToast = true
} catch {
self.finishFailure(data: response.data)
}
}
}
func successToastDidDismiss() {
guard hasSubmitted, !isShowSuccessToast, !hasNotifiedSubmission else { return }
hasNotifiedSubmission = true
onSubmitted()
}
private func finishFailure(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) ?? ""
isSubmitting = false
errorMessage = message.isEmpty ? I18n.Common.commonError : message
isShowPopup = true
}
}