diff --git a/HMH_Tuist_iOS/Projects/App/Sources/Common/Custom/PickerView.swift b/HMH_Tuist_iOS/Projects/App/Sources/Common/Custom/PickerView.swift index 3bd83e89..c750a5d3 100644 --- a/HMH_Tuist_iOS/Projects/App/Sources/Common/Custom/PickerView.swift +++ b/HMH_Tuist_iOS/Projects/App/Sources/Common/Custom/PickerView.swift @@ -73,7 +73,7 @@ class PickerCoordinator: NSObject, UIPickerViewDelegate, UIPickerViewDataSource func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) { selectedTime.wrappedValue = times[row] - viewModel.isCompleted = true +// viewModel.isCompleted = true } func pickerView(_ pickerView: UIPickerView, rowHeightForComponent component: Int) -> CGFloat { diff --git a/HMH_Tuist_iOS/Projects/Features/LoginFeature/Sources/Views/LoginUseCase.swift b/HMH_Tuist_iOS/Projects/Features/LoginFeature/Sources/Views/LoginUseCase.swift new file mode 100644 index 00000000..272c858e --- /dev/null +++ b/HMH_Tuist_iOS/Projects/Features/LoginFeature/Sources/Views/LoginUseCase.swift @@ -0,0 +1,62 @@ +// +// LoginUseCase.swift +// LoginFeature +// +// Created by Seonwoo Kim on 11/8/24. +// Copyright © 2024 HMH-iOS. All rights reserved. +// + +import Foundation +import Combine + +import Domain +import Core + +public enum LoginResponseType { + case loginSuccess + case loginFailure + case onboardingNeeded +} + +public protocol LoginUseCaseType { + func login(provider: OAuthProviderType) -> AnyPublisher +} + +public final class LoginUseCase: LoginUseCaseType { + + private let repository: AuthRepositoryType + + public init(repository: AuthRepositoryType) { + self.repository = repository + } + + public func login(provider: OAuthProviderType) -> AnyPublisher { + repository.authorize(provider) + .handleEvents(receiveOutput: { socialToken in + UserManager.shared.socialToken = socialToken + }) + .flatMap { [weak self] _ -> AnyPublisher in + guard let self = self else { + return Fail(error: AuthError.appleAuthrizeError).eraseToAnyPublisher() + } + + return self.repository.socialLogin(socialPlatform: provider.rawValue) + .map { _ in LoginResponseType.loginSuccess } + .catch { error -> AnyPublisher in + switch error { + case .unregisteredUser: + return Just(.onboardingNeeded) + .setFailureType(to: AuthError.self) + .eraseToAnyPublisher() + default: + return Just(.loginFailure) + .setFailureType(to: AuthError.self) + .eraseToAnyPublisher() + } + } + .eraseToAnyPublisher() + } + .eraseToAnyPublisher() + } +} + diff --git a/HMH_Tuist_iOS/Projects/Features/OnboardingFeature/Sources/Models/OnboardingModel.swift b/HMH_Tuist_iOS/Projects/Features/OnboardingFeature/Sources/Models/OnboardingModel.swift index 2dc16a24..246d55ed 100644 --- a/HMH_Tuist_iOS/Projects/Features/OnboardingFeature/Sources/Models/OnboardingModel.swift +++ b/HMH_Tuist_iOS/Projects/Features/OnboardingFeature/Sources/Models/OnboardingModel.swift @@ -6,6 +6,7 @@ // import Foundation +import DSKit struct SurveyButtonInfo: Identifiable { let id = UUID() @@ -13,4 +14,28 @@ struct SurveyButtonInfo: Identifiable { var isSelected: Bool } +extension SurveyButtonInfo { + static func initializeSurveyButtonItems() -> [[SurveyButtonInfo]] { + return [ + [ + SurveyButtonInfo(buttonTitle: StringLiteral.TimeSurveySelect.firstSelect, isSelected: false), + SurveyButtonInfo(buttonTitle: StringLiteral.TimeSurveySelect.secondSelect, isSelected: false), + SurveyButtonInfo(buttonTitle: StringLiteral.TimeSurveySelect.thirdSelect, isSelected: false), + SurveyButtonInfo(buttonTitle: StringLiteral.TimeSurveySelect.fourthSelect, isSelected: false), + ], + [ + SurveyButtonInfo(buttonTitle: StringLiteral.ProblemSurveySelect.firstSelect, isSelected: false), + SurveyButtonInfo(buttonTitle: StringLiteral.ProblemSurveySelect.secondSelect, isSelected: false), + SurveyButtonInfo(buttonTitle: StringLiteral.ProblemSurveySelect.thirdSelect, isSelected: false), + SurveyButtonInfo(buttonTitle: StringLiteral.ProblemSurveySelect.fourthSelect, isSelected: false), + ], + [ + SurveyButtonInfo(buttonTitle: StringLiteral.PeriodSelect.firstSelect, isSelected: false), + SurveyButtonInfo(buttonTitle: StringLiteral.PeriodSelect.secondSelect, isSelected: false), + SurveyButtonInfo(buttonTitle: StringLiteral.PeriodSelect.thirdSelect, isSelected: false), + SurveyButtonInfo(buttonTitle: StringLiteral.PeriodSelect.fourthSelect, isSelected: false), + ] + ] + } +} diff --git a/HMH_Tuist_iOS/Projects/Features/OnboardingFeature/Sources/Refactor/OnboardingState.swift b/HMH_Tuist_iOS/Projects/Features/OnboardingFeature/Sources/Refactor/OnboardingState.swift new file mode 100644 index 00000000..047a985c --- /dev/null +++ b/HMH_Tuist_iOS/Projects/Features/OnboardingFeature/Sources/Refactor/OnboardingState.swift @@ -0,0 +1,60 @@ +// +// OnboardingState.swift +// OnboardingFeature +// +// Created by Seonwoo Kim on 11/23/24. +// Copyright © 2024 HMH-iOS. All rights reserved. +// + +import Foundation +import DSKit + +enum OnboardingState: Int { + case timeSurveySelect = 0 + case problemSurveySelect + case challangePeriodSelect + case goalTimeSelect + case permissionSelect +} + +extension OnboardingState { + var mainTitle: String { + switch self { + case .timeSurveySelect: + return StringLiteral.OnboardigMain.timeSurveySelect + case .problemSurveySelect: + return StringLiteral.OnboardigMain.problemSurveySelect + case .challangePeriodSelect: + return StringLiteral.OnboardigMain.periodSelect + case .permissionSelect: + return StringLiteral.OnboardigMain.permissionSelect + case .goalTimeSelect: + return StringLiteral.OnboardigMain.appGoalTimeSelect + } + } + + var subTitle: String { + switch self { + case .timeSurveySelect: + return "" + case .problemSurveySelect: + return StringLiteral.OnboardigSub.problemSurveySelect + case .challangePeriodSelect: + return StringLiteral.OnboardigSub.periodSelect + case .permissionSelect: + return StringLiteral.OnboardigSub.permissionSelect + case .goalTimeSelect: + return StringLiteral.OnboardigSub.appGoalTimeSelect + } + } + + var nextButtonTitle: String { + switch self { + case .timeSurveySelect, .problemSurveySelect, .challangePeriodSelect, .goalTimeSelect: + return StringLiteral.OnboardingButton.next + case .permissionSelect: + return StringLiteral.OnboardingButton.permission + } + } +} + diff --git a/HMH_Tuist_iOS/Projects/Features/OnboardingFeature/Sources/Refactor/OnboardingUseCase.swift b/HMH_Tuist_iOS/Projects/Features/OnboardingFeature/Sources/Refactor/OnboardingUseCase.swift new file mode 100644 index 00000000..0648be64 --- /dev/null +++ b/HMH_Tuist_iOS/Projects/Features/OnboardingFeature/Sources/Refactor/OnboardingUseCase.swift @@ -0,0 +1,83 @@ +// +// OnboardingUseCase.swift +// OnboardingFeatureInterface +// +// Created by Seonwoo Kim on 11/18/24. +// Copyright © 2024 HMH-iOS. All rights reserved. +// + +import Foundation +import Combine + +import Domain +import Core + +public protocol OnboardingUseCaseType { + func postSignUpData( + socialPlatform: String, + userName: String, + averageUseTime: String, + problems: [String], + period: Int, + goalTime: Int + ) -> AnyPublisher + func calculateGoalTime(hour: String, minute: String) -> Int + func removeLastCharacterAndConvertToInt(from string: String) -> Int? +} + +public final class OnboardingUseCase: OnboardingUseCaseType { + + private let repository: AuthRepositoryType + + public init(repository: AuthRepositoryType) { + self.repository = repository + } + + public func postSignUpData( + socialPlatform: String, + userName: String, + averageUseTime: String, + problems: [String], + period: Int, + goalTime: Int + ) -> AnyPublisher { + let challengeInfo = ChallengeInfo(period: period, goalTime: goalTime, apps: []) + + return repository.signUp( + socialPlatform: socialPlatform, + name: userName, + averageUseTime: averageUseTime, + problem: problems, + challengeInfo: challengeInfo + ) + .map { auth -> Void in +// UserManager.shared.accessToken = auth.accessToken +// UserManager.shared.refreshToken = auth.refreshToken + } + .mapError { error -> Error in + + return error + } + .eraseToAnyPublisher() + } + + public func calculateGoalTime(hour: String, minute: String) -> Int { + let hourInt = Int(hour) ?? 0 + let minuteInt = Int(minute) ?? 0 + + let totalMinutes = hourInt * 60 + minuteInt + let totalMilliseconds = totalMinutes * 60 * 1000 + return totalMilliseconds + } + + public func removeLastCharacterAndConvertToInt(from string: String) -> Int? { + guard !string.isEmpty else { + return nil + } + + let modifiedString = String(string.dropLast()) + + return Int(modifiedString) + } +} + diff --git a/HMH_Tuist_iOS/Projects/Features/OnboardingFeature/Sources/ViewModels/OnboardingViewModel.swift b/HMH_Tuist_iOS/Projects/Features/OnboardingFeature/Sources/ViewModels/OnboardingViewModel.swift index 1d3c21f0..0846d842 100644 --- a/HMH_Tuist_iOS/Projects/Features/OnboardingFeature/Sources/ViewModels/OnboardingViewModel.swift +++ b/HMH_Tuist_iOS/Projects/Features/OnboardingFeature/Sources/ViewModels/OnboardingViewModel.swift @@ -1,398 +1,215 @@ // // OnboardingViewModel.swift -// HMH_iOS +// OnboardingFeatureInterface // -// Created by Seonwoo Kim on 3/24/24. +// Created by Seonwoo Kim on 11/18/24. +// Copyright © 2024 HMH-iOS. All rights reserved. // -import SwiftUI -import FamilyControls import Foundation +import Combine import Core -import DSKit import Domain +import DSKit - -public class OnboardingViewModel: ObservableObject { - - //TODO: 말썽꾸러기 스크린뷰모델 -// var screenViewModel: ScreenTimeViewModel +public final class OnboardingViewModel : ObservableObject { - @Published - var surveyButtonItems: [[SurveyButtonInfo]] - - var problems: [String] - - @Published - var onboardingState: Int - - @Published - public var isCompleted: Bool - - @Published - var isPickerPresented: Bool = false - - @Published - var isOnboardingError : Bool = false - - @Published - var isCompletePresented: Bool = false - - @Published - var selectedGoalTime: String + private let useCase: OnboardingUseCaseType + private var cancelBag = CancelBag() + @Published private(set) var state = State(onboardingState: .timeSurveySelect, surveyButtonItems: [[]], isNextAvailable: false, surveyState: 0) @Published var selectedAppHour: String - @Published var selectedAppMinute: String + var userName: String var averageUseTime: String - + var problems: [String] var period: Int - - var isChallengeMode: Bool - - var goalTime: Int - var appGoalTime: Int - @AppStorage("socialPlatform") private var socialPlatform = "" - @AppStorage("userName") private var userName = "" - - @MainActor func saveOnboardingData() { - print(onboardingState) - switch onboardingState { - case 0: - for index in 0..<4{ - if surveyButtonItems[onboardingState][index].isSelected { - self.averageUseTime = surveyButtonItems[onboardingState][index].buttonTitle - } - } - addOnboardingState() - offIsCompleted() - case 1: - for index in 0..<4{ - if surveyButtonItems[onboardingState][index].isSelected { - self.problems.append(surveyButtonItems[onboardingState][index].buttonTitle) - } - } - addOnboardingState() - offIsCompleted() - case 2: - for index in 0..<4{ - if surveyButtonItems[onboardingState] [index].isSelected { - self.period = removeLastCharacterAndConvertToInt(from: surveyButtonItems[onboardingState] [index].buttonTitle) ?? 0 - - print(surveyButtonItems[onboardingState] [index].buttonTitle) - - } - } - if isChallengeMode { - onboardingState = 6 - } else { - addOnboardingState() - } - case 3: - break - //TODO: 말썽꾸러기 스크린뷰모델 -// screenViewModel.requestAuthorization() -// if screenViewModel.authorizationCenter.authorizationStatus == .approved { -// onboardingState += 1 -// } - case 4: - isPickerPresented = true - case 5: - self.appGoalTime = convertToTotalMilliseconds(hour: selectedAppHour, minute: selectedAppMinute) - if isChallengeMode { - //TODO: 말썽꾸러기 스크린뷰모델 -// screenViewModel.handleStartDeviceActivityMonitoring(interval: appGoalTime) - addOnboardingState() - } else { - addOnboardingState() - } - offIsCompleted() - case 6: - self.goalTime = convertToTotalMilliseconds(hour: selectedGoalTime, minute: "0") - //TODO: 말썽꾸러기 스크린뷰모델 -// screenViewModel.handleTotalDeviceActivityMonitoring(interval: goalTime) - if isChallengeMode { - postCreateChallengeData() - isCompletePresented = true - } else { - postSignUpLoginData() - } - offIsCompleted() - default: - break - } - } - - func alertAction() { - postCreateChallengeData() - addOnboardingState() - isCompletePresented = false - } - - func addOnboardingState() { - onboardingState += 1 - } - - func backButtonTapped() { - switch onboardingState { - case 0: - UserManager.shared.appStateString = "login" - offIsCompleted() - case 1, 2, 3 : - onboardingState -= 1 - offIsCompleted() - resetAllSelections() - case 6: - onboardingState -= 1 - offIsCompleted() - default: - onIsCompleted() - onboardingState -= 1 - } + public init(useCase: OnboardingUseCaseType) { + self.useCase = useCase + self.state = State( + onboardingState: .timeSurveySelect, + surveyButtonItems: SurveyButtonInfo.initializeSurveyButtonItems(), + isNextAvailable: false, + surveyState: 0 + ) + self.userName = "" + self.averageUseTime = "" + self.problems = [] + self.period = 0 + self.selectedAppHour = "" + self.selectedAppMinute = "" + self.appGoalTime = 0 } - func onIsCompleted() { - isCompleted = true - } + // MARK: Action - func offIsCompleted() { - isCompleted = false + enum Action { + case nextButtonTap + case arrowButtonTap + case surveyButtonTap(index: Int) } - func resetOnboardingState() { - onboardingState = 0 - } + // MARK: State - public func getSurveyState() -> Int { - return onboardingState <= 2 ? onboardingState : 0 + struct State { + var onboardingState: OnboardingState + var surveyButtonItems: [[SurveyButtonInfo]] + var isNextAvailable: Bool + var surveyState: Int } - func pushToComplete() { - // if onboardingState == 6 { - // NavigationLink(PermissionView) - // } + func send(action: Action) { + switch action { + case .nextButtonTap: + handleNextButtonTap() + case .arrowButtonTap: + handleBackButtonTap() + case .surveyButtonTap(index: let index): + surveyButtonTap(index: index) + } } - func removeLastCharacterAndConvertToInt(from string: String) -> Int? { - guard !string.isEmpty else { - return nil - } + func surveyButtonTap(index: Int) { + guard state.onboardingState.rawValue < state.surveyButtonItems.count else { return } - let modifiedString = String(string.dropLast()) + let currentSurveyItems = state.surveyButtonItems[state.onboardingState.rawValue] - return Int(modifiedString) + switch state.onboardingState { + case .timeSurveySelect, .challangePeriodSelect, .goalTimeSelect: + for i in 0...self) { data in -// print(data.status) -// if data.status == 201 { -// UserManager.shared.appStateString = "onboardingComplete" -// UserManager.shared.accessToken = data.data?.token.accessToken ?? "" -// UserManager.shared.refreshToken = data.data?.token.refreshToken ?? "" -// } else if data.message == "이미 회원가입된 유저입니다." { -// self.isOnboardingError = true -// } else { -// self.isOnboardingError = true -// } -// } + private func resetSelections(for state: OnboardingState) { + guard state.rawValue < self.state.surveyButtonItems.count else { return } + for i in 0...self) { data in -// print(data.status) -// } + private func handleNextButtonTap() { + switch state.onboardingState { + case .timeSurveySelect: + saveTimeSurvey() + case .problemSurveySelect: + saveProblemSurvey() + case .challangePeriodSelect: + savePeriod() + case .goalTimeSelect: + saveGoalTime() + case .permissionSelect: + savePermission() + } } - func patchApp(appGoalTime: Int) { - //TODO: 네트워크 부분은 의존성 정리한 뒤에 다시 연결해봅시다 -// let applist = [ -// Apps( -// appCode: "#temp", -// goalTime: appGoalTime -// ) -// ] -// let requestDTO = AddAppRequestDTO(apps: applist) -// Providers.challengeProvider.request(target: .addApp(data: requestDTO), -// instance: BaseResponse.self) { result in -// print("result: \(result)") -// } + private func saveTimeSurvey() { + for index in 0..<4{ + if state.surveyButtonItems[state.onboardingState.rawValue][index].isSelected { + self.averageUseTime = state.surveyButtonItems[state.onboardingState.rawValue][index].buttonTitle + } + } + addOnboardingState() + offIsCompleted() } - @MainActor func createAppChallengeData(appGoalTime: Int) { - //TODO: 네트워크 부분은 의존성 정리한 뒤에 다시 연결해봅시다 -// var applist: [Apps] = [] -// // screenViewModel.hashVaule -// applist = [Apps(appCode: "#24333", goalTime: appGoalTime)] -// Providers.challengeProvider.request(target: .addApp(data: AddAppRequestDTO(apps: applist)), instance: BaseResponse.self) { [weak self] result in -// UserManager.shared.appStateString = "home" -// self?.screenViewModel.handleStartDeviceActivityMonitoring(includeUsageThreshold: true, interval: self?.appGoalTime ?? 0) -// } + private func saveProblemSurvey() { + for index in 0..<4{ + if state.surveyButtonItems[state.onboardingState.rawValue][index].isSelected { + self.problems.append(state.surveyButtonItems[state.onboardingState.rawValue][index].buttonTitle) + } + } + addOnboardingState() + offIsCompleted() } - func changeSurveyButtonStatus(num: Int) { - if onboardingState == 1 { - let selectedCount = surveyButtonItems[onboardingState].filter { $0.isSelected }.count - if surveyButtonItems[onboardingState][num].isSelected { - surveyButtonItems[onboardingState][num].isSelected.toggle() - } else if selectedCount < 2 { - surveyButtonItems[onboardingState][num].isSelected = true - } - } else { - for index in 0..<4 { - surveyButtonItems[onboardingState][index].isSelected = (index == num) + private func savePeriod() { + for index in 0..<4{ + if state.surveyButtonItems[state.onboardingState.rawValue] [index].isSelected { + self.period = useCase.removeLastCharacterAndConvertToInt(from: state.surveyButtonItems[state.onboardingState.rawValue] [index].buttonTitle) ?? 0 } } + addOnboardingState() } - - func convertToTotalMilliseconds(hour: String?, minute: String?) -> Int { - let hourInt = Int(hour ?? "") ?? 0 - let minuteInt = Int(minute ?? "") ?? 0 - - let totalMinutes = hourInt * 60 + minuteInt - let totalMilliseconds = totalMinutes * 60 * 1000 - return totalMilliseconds + private func saveGoalTime() { + self.appGoalTime = useCase.calculateGoalTime(hour: selectedAppHour, minute: selectedAppMinute) + addOnboardingState() + print(appGoalTime) } - - func getOnboardigMain() -> String { - switch onboardingState { - case 0: - StringLiteral.OnboardigMain.timeSurveySelect - case 1: - StringLiteral.OnboardigMain.problemSurveySelect - case 2: - StringLiteral.OnboardigMain.periodSelect - case 3: - StringLiteral.OnboardigMain.permissionSelect - case 4: - StringLiteral.OnboardigMain.appSelect - case 5: - StringLiteral.OnboardigMain.appGoalTimeSelect - case 6: - StringLiteral.OnboardigMain.goalTimeSelect - default: - "" - } + private func savePermission() { + // screenViewModel.requestAuthorization() + // if screenViewModel.authorizationCenter.authorizationStatus == .approved { + // addOnboardingState() + // } + completOnboarding() } - func getOnboardigSub() -> String { - switch onboardingState { - case 0: - "" - case 1: - StringLiteral.OnboardigSub.problemSurveySelect - case 2: - StringLiteral.OnboardigSub.periodSelect - case 3: - StringLiteral.OnboardigSub.permissionSelect - case 4: - StringLiteral.OnboardigSub.appSelect - case 5: - StringLiteral.OnboardigSub.appGoalTimeSelect - case 6: - StringLiteral.OnboardigSub.goalTimeSelect - default: - "" + private func addOnboardingState() { + guard let nextState = OnboardingState(rawValue: state.onboardingState.rawValue + 1) else { return } + state.onboardingState = nextState + + if nextState.rawValue <= 2 { + state.surveyState = nextState.rawValue } } - func getNextButton() -> String { - switch onboardingState { - case 0, 1, 2, 5: - StringLiteral.OnboardingButton.next - case 3: - StringLiteral.OnboardingButton.permission - case 4: - StringLiteral.OnboardingButton.appSelect - case 6: - StringLiteral.OnboardingButton.complete + private func handleBackButtonTap() { + switch state.onboardingState { + case .timeSurveySelect: + // UserManager.shared.appStateString = "login" + offIsCompleted() + case .problemSurveySelect, .challangePeriodSelect, .goalTimeSelect: + guard let previousState = OnboardingState(rawValue: state.onboardingState.rawValue - 1) else { return } + state.onboardingState = previousState + state.surveyState = previousState.rawValue + resetSelections(for: previousState) + offIsCompleted() default: - "" + guard let previousState = OnboardingState(rawValue: state.onboardingState.rawValue - 1) else { return } + state.onboardingState = previousState + onIsCompleted() } } - func handleOnAppear() { - if onboardingState == 3 && isChallengeMode { - isPickerPresented = true - } + + private func offIsCompleted() { + state.isNextAvailable = false } - func resetAllSelections() { - for i in 0.. UIPickerView { let pickerView = UIPickerView(frame: CGRect(x: 0, y: 0, width: 100, height: 100)) @@ -30,19 +29,17 @@ struct PickerView: UIViewRepresentable { } func makeCoordinator() -> PickerCoordinator { - return PickerCoordinator(times: times, selectedTime: $selectedTimes, viewModel: viewModel) + return PickerCoordinator(times: times, selectedTime: $selectedTimes) } } class PickerCoordinator: NSObject, UIPickerViewDelegate, UIPickerViewDataSource { - @ObservedObject var viewModel: OnboardingViewModel var times: [String] var selectedTime: Binding - init(times: [String], selectedTime: Binding, viewModel: OnboardingViewModel) { + init(times: [String], selectedTime: Binding) { self.times = times self.selectedTime = selectedTime - self.viewModel = viewModel } func numberOfComponents(in pickerView: UIPickerView) -> Int { @@ -59,7 +56,7 @@ class PickerCoordinator: NSObject, UIPickerViewDelegate, UIPickerViewDataSource func pickerView(_ pickerView: UIPickerView, attributedTitleForRow row: Int, forComponent component: Int) -> NSAttributedString? { let time = UIColor(DSKitAsset.whiteText.swiftUIColor) - let font = UIFont(name: "Pretendard-Medium", size: 22) + let font = DSKitFontFamily.Pretendard.medium.font(size: 22) let attributes: [NSAttributedString.Key: Any] = [ @@ -72,7 +69,7 @@ class PickerCoordinator: NSObject, UIPickerViewDelegate, UIPickerViewDataSource func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) { selectedTime.wrappedValue = times[row] - viewModel.isCompleted = true +// viewModel.state.isNextAvailable = true } func pickerView(_ pickerView: UIPickerView, rowHeightForComponent component: Int) -> CGFloat { diff --git a/HMH_Tuist_iOS/Projects/Features/OnboardingFeature/Sources/Views/GoalTimeView.swift b/HMH_Tuist_iOS/Projects/Features/OnboardingFeature/Sources/Views/GoalTimeView.swift index 234c7294..0e6d064c 100644 --- a/HMH_Tuist_iOS/Projects/Features/OnboardingFeature/Sources/Views/GoalTimeView.swift +++ b/HMH_Tuist_iOS/Projects/Features/OnboardingFeature/Sources/Views/GoalTimeView.swift @@ -21,7 +21,7 @@ struct GoalTimeView: View { // PickerView(times: times, selectedTimes: $viewModel.selectedGoalTime, viewModel: viewModel) // .frame(width: 67) Text("시간") - .font(.text2_medium_20) + .font(DSKitFontFamily.Pretendard.medium.swiftUIFont(size: 20)) .foregroundColor(DSKitAsset.gray2.swiftUIColor) } } diff --git a/HMH_Tuist_iOS/Projects/Features/OnboardingFeature/Sources/Views/NextButtonView.swift b/HMH_Tuist_iOS/Projects/Features/OnboardingFeature/Sources/Views/NextButtonView.swift index eaa656ed..6f45641f 100644 --- a/HMH_Tuist_iOS/Projects/Features/OnboardingFeature/Sources/Views/NextButtonView.swift +++ b/HMH_Tuist_iOS/Projects/Features/OnboardingFeature/Sources/Views/NextButtonView.swift @@ -16,19 +16,19 @@ struct NextButtonView: View { var body: some View { VStack { Button { - viewModel.saveOnboardingData() + viewModel.send(action: .nextButtonTap) } label: { - Text(viewModel.getNextButton()) - .font(.text4_semibold_16) + Text(viewModel.state.onboardingState.nextButtonTitle) + .font(DSKitFontFamily.Pretendard.semiBold.swiftUIFont(size: 16)) .frame(minWidth: 100, maxWidth: .infinity, minHeight: 44, maxHeight: 44, alignment: .center) } .frame(maxWidth: .infinity) .frame(height: 52) .foregroundColor( - viewModel.isCompleted ? DSKitAsset.whiteBtn.swiftUIColor : DSKitAsset.gray2.swiftUIColor) - .background(viewModel.isCompleted ? DSKitAsset.bluePurpleButton.swiftUIColor : DSKitAsset.gray5.swiftUIColor) + viewModel.state.isNextAvailable ? DSKitAsset.whiteBtn.swiftUIColor : DSKitAsset.gray2.swiftUIColor) + .background(viewModel.state.isNextAvailable ? DSKitAsset.bluePurpleButton.swiftUIColor : DSKitAsset.gray5.swiftUIColor) .clipShape(RoundedRectangle(cornerRadius: 4)) - .disabled(!viewModel.isCompleted) + .disabled(!viewModel.state.isNextAvailable) } } } diff --git a/HMH_Tuist_iOS/Projects/Features/OnboardingFeature/Sources/Views/OnboardingCompleteView.swift b/HMH_Tuist_iOS/Projects/Features/OnboardingFeature/Sources/Views/OnboardingCompleteView.swift index 0f1d4725..996438f9 100644 --- a/HMH_Tuist_iOS/Projects/Features/OnboardingFeature/Sources/Views/OnboardingCompleteView.swift +++ b/HMH_Tuist_iOS/Projects/Features/OnboardingFeature/Sources/Views/OnboardingCompleteView.swift @@ -23,11 +23,11 @@ public struct OnboardingCompleteView: View { Spacer() .frame(height: 25) Text(StringLiteral.OnboardingComplete.title) - .font(.title3_semibold_22) + .font(DSKitFontFamily.Pretendard.semiBold.swiftUIFont(size: 22)) .lineSpacing(1.5) .foregroundStyle(DSKitAsset.whiteText.swiftUIColor) Text(StringLiteral.OnboardingComplete.subTitle) - .font(.detail1_regular_14) + .font(DSKitFontFamily.Pretendard.regular.swiftUIFont(size: 14)) .lineSpacing(1.5) .multilineTextAlignment(.center) .foregroundStyle(DSKitAsset.gray2.swiftUIColor) @@ -48,7 +48,7 @@ extension OnboardingCompleteView { VStack { NavigationLink(destination: StoryContentView()) { Text(StringLiteral.OnboardingComplete.button) - .font(.text4_semibold_16) + .font(DSKitFontFamily.Pretendard.semiBold.swiftUIFont(size: 16)) .frame(minWidth: 100, maxWidth: .infinity, minHeight: 44, maxHeight: 44, alignment: .center) } .frame(maxWidth: .infinity) diff --git a/HMH_Tuist_iOS/Projects/Features/OnboardingFeature/Sources/Views/OnboardingContentView.swift b/HMH_Tuist_iOS/Projects/Features/OnboardingFeature/Sources/Views/OnboardingContentView.swift index 0eb46164..616832d0 100644 --- a/HMH_Tuist_iOS/Projects/Features/OnboardingFeature/Sources/Views/OnboardingContentView.swift +++ b/HMH_Tuist_iOS/Projects/Features/OnboardingFeature/Sources/Views/OnboardingContentView.swift @@ -10,25 +10,19 @@ import FamilyControls import DSKit import Core +import Data +import Networks public struct OnboardingContentView: View { //TODO: 말썽꾸러기 스크린뷰모델 // @StateObject var screenViewModel = ScreenTimeViewModel() - @StateObject - var onboardingViewModel: OnboardingViewModel - @State private var selection = FamilyActivitySelection() + @ObservedObject + var viewModel: OnboardingViewModel +// @State private var selection: FamilyActivitySelection - var isChallengeMode: Bool - @Environment(\.presentationMode) var presentationMode - - public init(isChallengeMode: Bool = false, onboardingState: Int = 0) { - //TODO: 말썽꾸러기 스크린뷰모델 - // let screenTimeViewModel = ScreenTimeViewModel() - // _screenViewModel = StateObject(wrappedValue: screenTimeViewModel) - // _onboardingViewModel = StateObject(wrappedValue: OnboardingViewModel(viewModel: screenTimeViewModel, onboardingState: onboardingState, isChallengeMode: isChallengeMode)) - _onboardingViewModel = StateObject(wrappedValue: OnboardingViewModel(onboardingState: onboardingState, isChallengeMode: isChallengeMode)) - self.isChallengeMode = isChallengeMode + public init(viewModel: OnboardingViewModel) { + self.viewModel = viewModel } public var body: some View { @@ -39,9 +33,7 @@ public struct OnboardingContentView: View { VStack { OnboardingNavigationView() .frame(height: 60) - if !isChallengeMode { - OnboardingProgressView() - } + OnboardingProgressView() } OnboardingTitleView() .padding(.top, 31) @@ -49,61 +41,13 @@ public struct OnboardingContentView: View { SurveyContainerView() .frame(maxWidth: .infinity) Spacer() - NextButtonView(viewModel: onboardingViewModel) + NextButtonView(viewModel: viewModel) } } .padding(.horizontal, 20) .padding(.bottom, 20) .background(DSKitAsset.blackground.swiftUIColor) .navigationBarHidden(true) - .onChange(of: onboardingViewModel.onboardingState) { newState in - if isChallengeMode && (newState == 1 || newState == 3 || newState == 7 ) { - self.presentationMode.wrappedValue.dismiss() - onboardingViewModel.resetOnboardingState() - } - } - .familyActivityPicker(isPresented: $onboardingViewModel.isPickerPresented, - selection: $selection) - .onChange(of: selection) { newSelection in - //TODO: 말썽꾸러기 스크린뷰모델 - // screenViewModel.updateSelectedApp(newSelection: newSelection) - } - .onChange(of: onboardingViewModel.isPickerPresented) { isPresented in - if !isPresented { - onboardingViewModel.addOnboardingState() - onboardingViewModel.offIsCompleted() - } - } - .onAppear() { - //TODO: 말썽꾸러기 스크린뷰모델 - // selection = screenViewModel.selectedApp - onboardingViewModel.handleOnAppear() - } - .showToast(toastType: .onboardingWarn, isPresented: $onboardingViewModel.isOnboardingError) - .customAlert( - isPresented: $onboardingViewModel.isCompletePresented, - customAlert: { - CustomAlertView( - alertType: .challengeCreationComplete, - confirmBtn: CustomAlertButtonView( - buttonType: .Confirm, - alertType: .challengeCreationComplete, - isPresented: $onboardingViewModel.isCompletePresented, - action: { - onboardingViewModel.alertAction() - } - ), - cancelBtn: CustomAlertButtonView( - buttonType: .Cancel, - alertType: .challengeCreationComplete, - isPresented: $onboardingViewModel.isCompletePresented, - action: { - onboardingViewModel.alertAction() - } - ), currentPoint: 0, usagePoint: 0 - ) - } - ) } } @@ -111,7 +55,7 @@ extension OnboardingContentView { private func OnboardingNavigationView() -> some View { HStack { Button(action: { - onboardingViewModel.backButtonTapped() + viewModel.send(action: .arrowButtonTap) }, label: { Image(uiImage: DSKitAsset.chevronLeft.image) .frame(width: 24, height: 24) @@ -131,21 +75,21 @@ extension OnboardingContentView { .cornerRadius(1.0) Rectangle() .foregroundColor(DSKitAsset.bluePurpleLine.swiftUIColor) - .frame(width: CGFloat(onboardingViewModel.onboardingState) / CGFloat(6) * 334, height: 4) + .frame(width: CGFloat(viewModel.state.onboardingState.rawValue) / CGFloat(5) * 334, height: 4) .cornerRadius(10.0) - .animation(Animation.spring(duration: 0.5), value: onboardingViewModel.onboardingState) + .animation(Animation.spring(duration: 0.5), value: viewModel.state.onboardingState) } } } private func OnboardingTitleView() -> some View { VStack(alignment: .leading, spacing: 8) { - Text(onboardingViewModel.getOnboardigMain()) - .font(.title3_semibold_22) + Text(viewModel.state.onboardingState.mainTitle) + .font(DSKitFontFamily.Pretendard.semiBold.swiftUIFont(size: 22)) .lineSpacing(1.5) .foregroundStyle(DSKitAsset.whiteText.swiftUIColor) - Text(onboardingViewModel.getOnboardigSub()) - .font(.detail1_regular_14) + Text(viewModel.state.onboardingState.subTitle) + .font(DSKitFontFamily.Pretendard.regular.swiftUIFont(size: 14)) .lineSpacing(1.5) .foregroundStyle(DSKitAsset.gray2.swiftUIColor) } @@ -153,13 +97,11 @@ extension OnboardingContentView { private func SurveyContainerView() -> some View { VStack { - switch onboardingViewModel.onboardingState { - case 0, 1, 2: - SurveyView(viewModel: onboardingViewModel) - case 5: - AppGoalTimeView(viewModel: onboardingViewModel) - case 6: - GoalTimeView(viewModel: onboardingViewModel) + switch viewModel.state.onboardingState { + case .timeSurveySelect, .problemSurveySelect, .challangePeriodSelect: + SurveyView(viewModel: viewModel) + case .goalTimeSelect: + AppGoalTimeView(viewModel: viewModel) default: EmptyView() } diff --git a/HMH_Tuist_iOS/Projects/Features/OnboardingFeature/Sources/Views/StoryContentView.swift b/HMH_Tuist_iOS/Projects/Features/OnboardingFeature/Sources/Views/StoryContentView.swift index 51231f99..25e23241 100644 --- a/HMH_Tuist_iOS/Projects/Features/OnboardingFeature/Sources/Views/StoryContentView.swift +++ b/HMH_Tuist_iOS/Projects/Features/OnboardingFeature/Sources/Views/StoryContentView.swift @@ -22,7 +22,7 @@ struct StoryContentView: View { Spacer() .frame(height: 40) Text("아무데나 눌러서 이동") - .font(.text5_medium_16) + .font(DSKitFontFamily.Pretendard.medium.swiftUIFont(size: 16)) .foregroundColor(DSKitAsset.gray1.swiftUIColor) } diff --git a/HMH_Tuist_iOS/Projects/Features/OnboardingFeature/Sources/Views/SurveyButton.swift b/HMH_Tuist_iOS/Projects/Features/OnboardingFeature/Sources/Views/SurveyButton.swift index f1bef383..d1a844cd 100644 --- a/HMH_Tuist_iOS/Projects/Features/OnboardingFeature/Sources/Views/SurveyButton.swift +++ b/HMH_Tuist_iOS/Projects/Features/OnboardingFeature/Sources/Views/SurveyButton.swift @@ -17,18 +17,18 @@ struct SurveyButton: View { var body: some View { Button { - viewModel.changeSurveyButtonStatus(num: numberOfRow) - viewModel.onIsCompleted() + viewModel.send(action: .surveyButtonTap(index: numberOfRow)) +// viewModel.onIsCompleted() } label: { - Text(viewModel.surveyButtonItems[viewModel.getSurveyState()][numberOfRow].buttonTitle) - .font(.text4_semibold_16) + Text(viewModel.state.surveyButtonItems[viewModel.state.surveyState][numberOfRow].buttonTitle) + .font(DSKitFontFamily.Pretendard.semiBold.swiftUIFont(size: 16)) .frame(minWidth: 100, maxWidth: .infinity, minHeight: 44, maxHeight: 44, alignment: .center) } .frame(maxWidth: .infinity) .frame(height: 62) .foregroundColor(DSKitAsset.whiteBtn.swiftUIColor) - .background(viewModel.surveyButtonItems[viewModel.getSurveyState()][numberOfRow].isSelected ? DSKitAsset.bluePurpleOpacity22.swiftUIColor : DSKitAsset.gray7.swiftUIColor) - .border(viewModel.surveyButtonItems[viewModel.getSurveyState()][numberOfRow].isSelected ? DSKitAsset.bluePurpleLine.swiftUIColor : DSKitAsset.gray7.swiftUIColor, width: /*@START_MENU_TOKEN@*/1/*@END_MENU_TOKEN@*/) + .background(viewModel.state.surveyButtonItems[viewModel.state.surveyState][numberOfRow].isSelected ? DSKitAsset.bluePurpleOpacity22.swiftUIColor : DSKitAsset.gray7.swiftUIColor) + .border(viewModel.state.surveyButtonItems[viewModel.state.surveyState][numberOfRow].isSelected ? DSKitAsset.bluePurpleLine.swiftUIColor : DSKitAsset.gray7.swiftUIColor, width: /*@START_MENU_TOKEN@*/1/*@END_MENU_TOKEN@*/) .clipShape(RoundedRectangle(cornerRadius: 4)) } }