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
2 changes: 1 addition & 1 deletion Projects/App/Project.swift
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ private let commonBuildSettings: SettingsDictionary = [
"KAKAO_APP_KEY": "fb2997e54bfe080cc5c1d9706d1251f4",
"GOOGLE_CLIENT_ID": "48737424560-adiebqu29lsflj85v9vrd4e4a3cp6sa3.apps.googleusercontent.com",
"GOOGLE_REVERSED_CLIENT_ID": "com.googleusercontent.apps.48737424560-adiebqu29lsflj85v9vrd4e4a3cp6sa3",
"DEEPLINK_HOST": "keepiluv.jiyong.xyz",
"DEEPLINK_HOST": "keepiluv.teamtwix.com",
"API_BASE_URL": "https://api.dev.teamtwix.com"
]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,20 @@ import SharedDesignSystem
public struct OnboardingDdayReducer {
@Dependency(\.onboardingClient)
private var onboardingClient
@Dependency(\.continuousClock)
private var clock

public enum CancelID: Hashable {
case polling
}

@ObservableState
public struct State: Equatable {
var selectedDate: TXCalendarDate
var showCalendarSheet: Bool = false
var isLoading: Bool = false
var toast: TXToastType?
var modal: TXModalStyle?

public init() {
self.selectedDate = TXCalendarDate()
Expand All @@ -42,6 +49,9 @@ public struct OnboardingDdayReducer {
// MARK: - Binding
case binding(BindingAction<State>)

// MARK: - LifeCycle
case onAppear

// MARK: - User Action
case backButtonTapped
case dateSelectorTapped
Expand All @@ -53,6 +63,13 @@ public struct OnboardingDdayReducer {
// MARK: - API Response
case setAnniversaryResponse(Result<Void, Error>)

// MARK: - Partner Polling
case pollingTick
case pollingResult(Result<OnboardingStatus, Error>)

// MARK: - Modal
case modalConfirmTapped

// MARK: - Delegate
case delegate(Delegate)

Expand All @@ -71,6 +88,14 @@ public struct OnboardingDdayReducer {
case .binding:
return .none

case .onAppear:
return .run { [clock] send in
for await _ in clock.timer(interval: .seconds(3)) {
await send(.pollingTick)
}
}
.cancellable(id: CancelID.polling, cancelInFlight: true)

case .backButtonTapped:
return .send(.delegate(.navigateBack))

Expand All @@ -85,29 +110,66 @@ public struct OnboardingDdayReducer {
case .completeButtonTapped:
guard let date = state.selectedDate.date, !state.isLoading else { return .none }
state.isLoading = true
return .run { send in
do {
try await onboardingClient.setAnniversary(date)
await send(.setAnniversaryResponse(.success(())))
} catch {
await send(.setAnniversaryResponse(.failure(error)))
return .merge(
.cancel(id: CancelID.polling),
.run { send in
do {
try await onboardingClient.setAnniversary(date)
await send(.setAnniversaryResponse(.success(())))
} catch {
await send(.setAnniversaryResponse(.failure(error)))
}
}
}
)

case .setAnniversaryResponse(.success):
state.isLoading = false
return .send(.delegate(.ddayCompleted))

case let .setAnniversaryResponse(.failure(error)):
state.isLoading = false
// 이미 온보딩이 완료된 경우 (G4000), 성공과 동일하게 처리
if let onboardingError = error as? OnboardingError,
onboardingError == .alreadyOnboarded {
return .send(.delegate(.ddayCompleted))
state.modal = .info(
image: .Icon.Illustration.heart,
title: "메이트가 기념일을 등록했어요!",
subtitle: "이미 우리의 기념일이 저장됐어요.\n이제 함께 시작해봐요 :)",
leftButtonText: "확인",
rightButtonText: "시작하기"
)
return .cancel(id: CancelID.polling)
}
state.toast = .fit(message: "기념일 등록에 실패했어요. 다시 시도해주세요")
return .none

case .pollingTick:
return .run { [onboardingClient] send in
do {
let status = try await onboardingClient.fetchStatus()
await send(.pollingResult(.success(status)))
} catch {
await send(.pollingResult(.failure(error)))
}
}

case let .pollingResult(.success(status)):
guard status == .completed else { return .none }
state.modal = .info(
image: .Icon.Illustration.heart,
title: "메이트가 기념일을 등록했어요!",
subtitle: "이미 우리의 기념일이 저장됐어요.\n이제 함께 시작해봐요 :)",
leftButtonText: "확인",
rightButtonText: "시작하기"
)
return .cancel(id: CancelID.polling)

case .pollingResult(.failure):
return .none

case .modalConfirmTapped:
state.modal = nil
return .send(.delegate(.ddayCompleted))

case .delegate:
return .none
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,13 @@ public struct OnboardingDdayView: View {
}
)
}
.onAppear {
store.send(.onAppear)
}
.txLoading(isPresented: store.isLoading)
.txModal(item: $store.modal) { _ in
store.send(.modalConfirmTapped)
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -391,7 +391,7 @@ public struct OnboardingCoordinator {
case .dday(.delegate(.navigateBack)):
popLastRoute(&state.routes)
state.dday = nil
return .none
return .cancel(id: OnboardingDdayReducer.CancelID.polling)

case .dday(.delegate(.ddayCompleted)):
return .send(.startNotificationPermission)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ private extension TXCalendarBottomSheet {
func datePickerView(height: CGFloat) -> some View {
HStack(spacing: 0) {
Picker("Year", selection: $selectedDate.year) {
ForEach(2026...2099, id: \.self) { year in
ForEach(1940...2099, id: \.self) { year in
Text(verbatim: "\(year)년").tag(year)
}
}
Expand Down
Loading