diff --git a/Plugin/DependencyPlugin/ProjectDescriptionHelpers/ModulePaths.swift b/Plugin/DependencyPlugin/ProjectDescriptionHelpers/ModulePaths.swift index 4e1d8af..26a6e22 100644 --- a/Plugin/DependencyPlugin/ProjectDescriptionHelpers/ModulePaths.swift +++ b/Plugin/DependencyPlugin/ProjectDescriptionHelpers/ModulePaths.swift @@ -24,6 +24,7 @@ extension ModulePaths: MicroTargetPathConvertable { public extension ModulePaths { enum Feature: String, MicroTargetPathConvertable { + case FindPasswordFeature case SignupFeature case MainFeature case SigninFeature diff --git a/Projects/App/Project.swift b/Projects/App/Project.swift index b87ffe8..6b237ba 100644 --- a/Projects/App/Project.swift +++ b/Projects/App/Project.swift @@ -5,7 +5,11 @@ import Foundation import ProjectDescription import ProjectDescriptionHelpers -let configurations: [Configuration] = .default +let configurations: [Configuration] = [ + .debug(name: .dev, xcconfig: .relativeToXCConfig(type: .dev, name: env.name)), + .debug(name: .stage, xcconfig: .relativeToXCConfig(type: .stage, name: env.name)), + .release(name: .prod, xcconfig: .relativeToXCConfig(type: .prod, name: env.name)) +] let settings: Settings = .settings( base: env.baseSetting, diff --git a/Projects/App/Sources/Application/DI/AppComponent.swift b/Projects/App/Sources/Application/DI/AppComponent.swift index 353b5f1..9f73f4c 100644 --- a/Projects/App/Sources/Application/DI/AppComponent.swift +++ b/Projects/App/Sources/Application/DI/AppComponent.swift @@ -10,6 +10,9 @@ import SignupFeature import SignupFeatureInterface import MainFeature import MainFeatureInterface +import FindPasswordFeature +import FindPasswordFeatureInterface +>>>>>>> origin/develop public final class AppComponent: BootstrapComponent { // private let _keychain: any Keychain @@ -41,15 +44,18 @@ public extension AppComponent { SigninComponent(parent: self) } // Signup - var signupEmailVerifyFactory: any SignupEmailVerifyFactory { - SignupEmailVerifyComponent(parent: self) + var signupEmailFactory: any SignupEmailFactory { + SignupEmailComponent(parent: self) } - var signupEmailAuthCodeVerifyFactory: any SignupEmailAuthCodeVerifyFactory { - SignupEmailAuthCodeVerifyComponent(parent: self) + var signupVerifyAuthCode: any SignupVerifyAuthCodeFactory { + SignupVerifyAuthCodeComponent(parent: self) } var signupPasswordFactory: any SignupPasswordFactory { SignupPasswordComponent(parent: self) } + var signupUserInfoFlowFactory: any SignupUserInfoFlowFactory { + SignupUserInfoFlowComponent(parent: self) + } var signupNameFactory: any SignupNameFactory { SignupNameComponent(parent: self) } @@ -64,5 +70,14 @@ public extension AppComponent { } var mainFactory: any MainFactory { MainComponent(parent: self) + // Find Password + var inputEmailFactory: any InputEmailFactory { + InputEmailComponent(parent: self) + } + var verifyAuthCodeFactory: any VerifyAuthCodeFactory { + VerifyAuthCodeComponent(parent: self) + } + var inputNewPasswordFactory: any InputNewPasswordFactory { + InputNewPasswordComponent(parent: self) } } diff --git a/Projects/App/Sources/Application/NeedleGenerated.swift b/Projects/App/Sources/Application/NeedleGenerated.swift index c9ea2f0..15d4a56 100644 --- a/Projects/App/Sources/Application/NeedleGenerated.swift +++ b/Projects/App/Sources/Application/NeedleGenerated.swift @@ -1,7 +1,8 @@ - import MainFeature import MainFeatureInterface +import FindPasswordFeature +import FindPasswordFeatureInterface import NeedleFoundation import RootFeature import RootFeatureInterface @@ -37,31 +38,31 @@ private class SplashDependencye0cb7136f2ec3edfd60aProvider: SplashDependency { private func factoryace9f05f51d68f4c0677e3b0c44298fc1c149afb(_ component: NeedleFoundation.Scope) -> AnyObject { return SplashDependencye0cb7136f2ec3edfd60aProvider() } -private class SignupPasswordDependency778bf5389a70d7df6152Provider: SignupPasswordDependency { - var signupNameFactory: any SignupNameFactory { - return appComponent.signupNameFactory +private class SignupVerifyAuthCodeDependencyc5bfc91282e270b5f35aProvider: SignupVerifyAuthCodeDependency { + var signupPasswordFactory: any SignupPasswordFactory { + return appComponent.signupPasswordFactory } private let appComponent: AppComponent init(appComponent: AppComponent) { self.appComponent = appComponent } } -/// ^->AppComponent->SignupPasswordComponent -private func factorye93d1d56840ff97c674af47b58f8f304c97af4d5(_ component: NeedleFoundation.Scope) -> AnyObject { - return SignupPasswordDependency778bf5389a70d7df6152Provider(appComponent: parent1(component) as! AppComponent) +/// ^->AppComponent->SignupVerifyAuthCodeComponent +private func factoryff5803dec4bd48820d95f47b58f8f304c97af4d5(_ component: NeedleFoundation.Scope) -> AnyObject { + return SignupVerifyAuthCodeDependencyc5bfc91282e270b5f35aProvider(appComponent: parent1(component) as! AppComponent) } -private class SignupEmailAuthCodeVerifyDependencyaf9da1ebf0e9e5f1b708Provider: SignupEmailAuthCodeVerifyDependency { - var signupPasswordFactory: any SignupPasswordFactory { - return appComponent.signupPasswordFactory +private class SignupPasswordDependency778bf5389a70d7df6152Provider: SignupPasswordDependency { + var signupUserInfoFlowFactory: any SignupUserInfoFlowFactory { + return appComponent.signupUserInfoFlowFactory } private let appComponent: AppComponent init(appComponent: AppComponent) { self.appComponent = appComponent } } -/// ^->AppComponent->SignupEmailAuthCodeVerifyComponent -private func factoryb06be35aa893adde971bf47b58f8f304c97af4d5(_ component: NeedleFoundation.Scope) -> AnyObject { - return SignupEmailAuthCodeVerifyDependencyaf9da1ebf0e9e5f1b708Provider(appComponent: parent1(component) as! AppComponent) +/// ^->AppComponent->SignupPasswordComponent +private func factorye93d1d56840ff97c674af47b58f8f304c97af4d5(_ component: NeedleFoundation.Scope) -> AnyObject { + return SignupPasswordDependency778bf5389a70d7df6152Provider(appComponent: parent1(component) as! AppComponent) } private class SignupStudentIDDependencyc62f548e51245f5bf42dProvider: SignupStudentIDDependency { var signupGenderFactory: any SignupGenderFactory { @@ -77,17 +78,15 @@ private func factorycca12bec445b4f4f6de7f47b58f8f304c97af4d5(_ component: Needle return SignupStudentIDDependencyc62f548e51245f5bf42dProvider(appComponent: parent1(component) as! AppComponent) } private class SignupGenderDependency65e2a6565372eb41f4acProvider: SignupGenderDependency { - var signupCheckLevelFactory: any SignupCheckLevelFactory { - return appComponent.signupCheckLevelFactory - } - private let appComponent: AppComponent - init(appComponent: AppComponent) { - self.appComponent = appComponent + + + init() { + } } /// ^->AppComponent->SignupGenderComponent -private func factory997eaa831d16af15eee5f47b58f8f304c97af4d5(_ component: NeedleFoundation.Scope) -> AnyObject { - return SignupGenderDependency65e2a6565372eb41f4acProvider(appComponent: parent1(component) as! AppComponent) +private func factory997eaa831d16af15eee5e3b0c44298fc1c149afb(_ component: NeedleFoundation.Scope) -> AnyObject { + return SignupGenderDependency65e2a6565372eb41f4acProvider() } private class SignupCheckLevelDependencye4ec84c7d38354a454aeProvider: SignupCheckLevelDependency { @@ -101,30 +100,50 @@ private func factory5b23127e837152346fdae3b0c44298fc1c149afb(_ component: Needle return SignupCheckLevelDependencye4ec84c7d38354a454aeProvider() } private class SignupNameDependency860534ba64c054146b7bProvider: SignupNameDependency { + + + init() { + + } +} +/// ^->AppComponent->SignupNameComponent +private func factory71e8fa7f4327b1e25ed0e3b0c44298fc1c149afb(_ component: NeedleFoundation.Scope) -> AnyObject { + return SignupNameDependency860534ba64c054146b7bProvider() +} +private class SignupUserInfoFlowDependencyc2b570f167242d15d8e8Provider: SignupUserInfoFlowDependency { + var signupNameFactory: any SignupNameFactory { + return appComponent.signupNameFactory + } var signupStudentIDFactory: any SignupStudentIDFactory { return appComponent.signupStudentIDFactory } + var signupGenderFactory: any SignupGenderFactory { + return appComponent.signupGenderFactory + } + var signupCheckLevelFactory: any SignupCheckLevelFactory { + return appComponent.signupCheckLevelFactory + } private let appComponent: AppComponent init(appComponent: AppComponent) { self.appComponent = appComponent } } -/// ^->AppComponent->SignupNameComponent -private func factory71e8fa7f4327b1e25ed0f47b58f8f304c97af4d5(_ component: NeedleFoundation.Scope) -> AnyObject { - return SignupNameDependency860534ba64c054146b7bProvider(appComponent: parent1(component) as! AppComponent) +/// ^->AppComponent->SignupUserInfoFlowComponent +private func factoryce6f8b9af5f65c8e334ef47b58f8f304c97af4d5(_ component: NeedleFoundation.Scope) -> AnyObject { + return SignupUserInfoFlowDependencyc2b570f167242d15d8e8Provider(appComponent: parent1(component) as! AppComponent) } -private class SignupEmailVerifyDependencyf9d372ac752ee19b78caProvider: SignupEmailVerifyDependency { - var signupEmailAuthCodeVerifyFactory: any SignupEmailAuthCodeVerifyFactory { - return appComponent.signupEmailAuthCodeVerifyFactory +private class SignupEmailDependency844c9e928af75b6aaa7aProvider: SignupEmailDependency { + var signupVerifyAuthCode: any SignupVerifyAuthCodeFactory { + return appComponent.signupVerifyAuthCode } private let appComponent: AppComponent init(appComponent: AppComponent) { self.appComponent = appComponent } } -/// ^->AppComponent->SignupEmailVerifyComponent -private func factory3b1904c76335d70151ebf47b58f8f304c97af4d5(_ component: NeedleFoundation.Scope) -> AnyObject { - return SignupEmailVerifyDependencyf9d372ac752ee19b78caProvider(appComponent: parent1(component) as! AppComponent) +/// ^->AppComponent->SignupEmailComponent +private func factory4d1ddf658c5970ef6b47f47b58f8f304c97af4d5(_ component: NeedleFoundation.Scope) -> AnyObject { + return SignupEmailDependency844c9e928af75b6aaa7aProvider(appComponent: parent1(component) as! AppComponent) } private class MainDependency7c6a5b4738b211b8e155Provider: MainDependency { @@ -157,8 +176,11 @@ private func factory264bfc4d4cb6b0629b40f47b58f8f304c97af4d5(_ component: Needle return RootDependency3944cc797a4a88956fb5Provider(appComponent: parent1(component) as! AppComponent) } private class SigninDependencyde06a9d0b22764487733Provider: SigninDependency { - var signupEmailVerifyFactory: any SignupEmailVerifyFactory { - return appComponent.signupEmailVerifyFactory + var signupEmailFactory: any SignupEmailFactory { + return appComponent.signupEmailFactory + } + var inputEmailFactory: any InputEmailFactory { + return appComponent.inputEmailFactory } private let appComponent: AppComponent init(appComponent: AppComponent) { @@ -169,6 +191,43 @@ private class SigninDependencyde06a9d0b22764487733Provider: SigninDependency { private func factory2882a056d84a613debccf47b58f8f304c97af4d5(_ component: NeedleFoundation.Scope) -> AnyObject { return SigninDependencyde06a9d0b22764487733Provider(appComponent: parent1(component) as! AppComponent) } +private class VerifyAuthCodeDependencya16ab19f97e0892b555bProvider: VerifyAuthCodeDependency { + var inputNewPasswordFactory: any InputNewPasswordFactory { + return appComponent.inputNewPasswordFactory + } + private let appComponent: AppComponent + init(appComponent: AppComponent) { + self.appComponent = appComponent + } +} +/// ^->AppComponent->VerifyAuthCodeComponent +private func factoryed5ce75de1bf576b84adf47b58f8f304c97af4d5(_ component: NeedleFoundation.Scope) -> AnyObject { + return VerifyAuthCodeDependencya16ab19f97e0892b555bProvider(appComponent: parent1(component) as! AppComponent) +} +private class InputNewPasswordDependency1149e32e41e1cfce6f6dProvider: InputNewPasswordDependency { + + + init() { + + } +} +/// ^->AppComponent->InputNewPasswordComponent +private func factory27615059458a0576f404e3b0c44298fc1c149afb(_ component: NeedleFoundation.Scope) -> AnyObject { + return InputNewPasswordDependency1149e32e41e1cfce6f6dProvider() +} +private class InputEmailDependency4102766a436592066e97Provider: InputEmailDependency { + var verifyAuthCodeFactory: any VerifyAuthCodeFactory { + return appComponent.verifyAuthCodeFactory + } + private let appComponent: AppComponent + init(appComponent: AppComponent) { + self.appComponent = appComponent + } +} +/// ^->AppComponent->InputEmailComponent +private func factoryf939e41ba3a1151e88f8f47b58f8f304c97af4d5(_ component: NeedleFoundation.Scope) -> AnyObject { + return InputEmailDependency4102766a436592066e97Provider(appComponent: parent1(component) as! AppComponent) +} #else extension AppComponent: Registration { @@ -176,14 +235,18 @@ extension AppComponent: Registration { localTable["splashFactory-any SplashFactory"] = { [unowned self] in self.splashFactory as Any } localTable["signinFactory-any SigninFactory"] = { [unowned self] in self.signinFactory as Any } - localTable["signupEmailVerifyFactory-any SignupEmailVerifyFactory"] = { [unowned self] in self.signupEmailVerifyFactory as Any } - localTable["signupEmailAuthCodeVerifyFactory-any SignupEmailAuthCodeVerifyFactory"] = { [unowned self] in self.signupEmailAuthCodeVerifyFactory as Any } + localTable["signupEmailFactory-any SignupEmailFactory"] = { [unowned self] in self.signupEmailFactory as Any } + localTable["signupVerifyAuthCode-any SignupVerifyAuthCodeFactory"] = { [unowned self] in self.signupVerifyAuthCode as Any } localTable["signupPasswordFactory-any SignupPasswordFactory"] = { [unowned self] in self.signupPasswordFactory as Any } + localTable["signupUserInfoFlowFactory-any SignupUserInfoFlowFactory"] = { [unowned self] in self.signupUserInfoFlowFactory as Any } localTable["signupNameFactory-any SignupNameFactory"] = { [unowned self] in self.signupNameFactory as Any } localTable["signupStudentIDFactory-any SignupStudentIDFactory"] = { [unowned self] in self.signupStudentIDFactory as Any } localTable["signupGenderFactory-any SignupGenderFactory"] = { [unowned self] in self.signupGenderFactory as Any } localTable["signupCheckLevelFactory-any SignupCheckLevelFactory"] = { [unowned self] in self.signupCheckLevelFactory as Any } localTable["mainFactory-any MainFactory"] = { [unowned self] in self.mainFactory as Any } + localTable["inputEmailFactory-any InputEmailFactory"] = { [unowned self] in self.inputEmailFactory as Any } + localTable["verifyAuthCodeFactory-any VerifyAuthCodeFactory"] = { [unowned self] in self.verifyAuthCodeFactory as Any } + localTable["inputNewPasswordFactory-any InputNewPasswordFactory"] = { [unowned self] in self.inputNewPasswordFactory as Any } } } extension SplashComponent: Registration { @@ -191,14 +254,14 @@ extension SplashComponent: Registration { } } -extension SignupPasswordComponent: Registration { +extension SignupVerifyAuthCodeComponent: Registration { public func registerItems() { - keyPathToName[\SignupPasswordDependency.signupNameFactory] = "signupNameFactory-any SignupNameFactory" + keyPathToName[\SignupVerifyAuthCodeDependency.signupPasswordFactory] = "signupPasswordFactory-any SignupPasswordFactory" } } -extension SignupEmailAuthCodeVerifyComponent: Registration { +extension SignupPasswordComponent: Registration { public func registerItems() { - keyPathToName[\SignupEmailAuthCodeVerifyDependency.signupPasswordFactory] = "signupPasswordFactory-any SignupPasswordFactory" + keyPathToName[\SignupPasswordDependency.signupUserInfoFlowFactory] = "signupUserInfoFlowFactory-any SignupUserInfoFlowFactory" } } extension SignupStudentIDComponent: Registration { @@ -208,7 +271,7 @@ extension SignupStudentIDComponent: Registration { } extension SignupGenderComponent: Registration { public func registerItems() { - keyPathToName[\SignupGenderDependency.signupCheckLevelFactory] = "signupCheckLevelFactory-any SignupCheckLevelFactory" + } } extension SignupCheckLevelComponent: Registration { @@ -218,12 +281,20 @@ extension SignupCheckLevelComponent: Registration { } extension SignupNameComponent: Registration { public func registerItems() { - keyPathToName[\SignupNameDependency.signupStudentIDFactory] = "signupStudentIDFactory-any SignupStudentIDFactory" + + } +} +extension SignupUserInfoFlowComponent: Registration { + public func registerItems() { + keyPathToName[\SignupUserInfoFlowDependency.signupNameFactory] = "signupNameFactory-any SignupNameFactory" + keyPathToName[\SignupUserInfoFlowDependency.signupStudentIDFactory] = "signupStudentIDFactory-any SignupStudentIDFactory" + keyPathToName[\SignupUserInfoFlowDependency.signupGenderFactory] = "signupGenderFactory-any SignupGenderFactory" + keyPathToName[\SignupUserInfoFlowDependency.signupCheckLevelFactory] = "signupCheckLevelFactory-any SignupCheckLevelFactory" } } -extension SignupEmailVerifyComponent: Registration { +extension SignupEmailComponent: Registration { public func registerItems() { - keyPathToName[\SignupEmailVerifyDependency.signupEmailAuthCodeVerifyFactory] = "signupEmailAuthCodeVerifyFactory-any SignupEmailAuthCodeVerifyFactory" + keyPathToName[\SignupEmailDependency.signupVerifyAuthCode] = "signupVerifyAuthCode-any SignupVerifyAuthCodeFactory" } } extension MainComponent: Registration { @@ -240,7 +311,23 @@ extension RootComponent: Registration { } extension SigninComponent: Registration { public func registerItems() { - keyPathToName[\SigninDependency.signupEmailVerifyFactory] = "signupEmailVerifyFactory-any SignupEmailVerifyFactory" + keyPathToName[\SigninDependency.signupEmailFactory] = "signupEmailFactory-any SignupEmailFactory" + keyPathToName[\SigninDependency.inputEmailFactory] = "inputEmailFactory-any InputEmailFactory" + } +} +extension VerifyAuthCodeComponent: Registration { + public func registerItems() { + keyPathToName[\VerifyAuthCodeDependency.inputNewPasswordFactory] = "inputNewPasswordFactory-any InputNewPasswordFactory" + } +} +extension InputNewPasswordComponent: Registration { + public func registerItems() { + + } +} +extension InputEmailComponent: Registration { + public func registerItems() { + keyPathToName[\InputEmailDependency.verifyAuthCodeFactory] = "verifyAuthCodeFactory-any VerifyAuthCodeFactory" } } @@ -261,16 +348,22 @@ private func registerProviderFactory(_ componentPath: String, _ factory: @escapi @inline(never) private func register1() { registerProviderFactory("^->AppComponent", factoryEmptyDependencyProvider) registerProviderFactory("^->AppComponent->SplashComponent", factoryace9f05f51d68f4c0677e3b0c44298fc1c149afb) + registerProviderFactory("^->AppComponent->SignupVerifyAuthCodeComponent", factoryff5803dec4bd48820d95f47b58f8f304c97af4d5) registerProviderFactory("^->AppComponent->SignupPasswordComponent", factorye93d1d56840ff97c674af47b58f8f304c97af4d5) - registerProviderFactory("^->AppComponent->SignupEmailAuthCodeVerifyComponent", factoryb06be35aa893adde971bf47b58f8f304c97af4d5) registerProviderFactory("^->AppComponent->SignupStudentIDComponent", factorycca12bec445b4f4f6de7f47b58f8f304c97af4d5) - registerProviderFactory("^->AppComponent->SignupGenderComponent", factory997eaa831d16af15eee5f47b58f8f304c97af4d5) + registerProviderFactory("^->AppComponent->SignupGenderComponent", factory997eaa831d16af15eee5e3b0c44298fc1c149afb) registerProviderFactory("^->AppComponent->SignupCheckLevelComponent", factory5b23127e837152346fdae3b0c44298fc1c149afb) registerProviderFactory("^->AppComponent->SignupNameComponent", factory71e8fa7f4327b1e25ed0f47b58f8f304c97af4d5) registerProviderFactory("^->AppComponent->SignupEmailVerifyComponent", factory3b1904c76335d70151ebf47b58f8f304c97af4d5) registerProviderFactory("^->AppComponent->MainComponent", factoryc9274e46e78e70f29c54e3b0c44298fc1c149afb) + registerProviderFactory("^->AppComponent->SignupNameComponent", factory71e8fa7f4327b1e25ed0e3b0c44298fc1c149afb) + registerProviderFactory("^->AppComponent->SignupUserInfoFlowComponent", factoryce6f8b9af5f65c8e334ef47b58f8f304c97af4d5) + registerProviderFactory("^->AppComponent->SignupEmailComponent", factory4d1ddf658c5970ef6b47f47b58f8f304c97af4d5) registerProviderFactory("^->AppComponent->RootComponent", factory264bfc4d4cb6b0629b40f47b58f8f304c97af4d5) registerProviderFactory("^->AppComponent->SigninComponent", factory2882a056d84a613debccf47b58f8f304c97af4d5) + registerProviderFactory("^->AppComponent->VerifyAuthCodeComponent", factoryed5ce75de1bf576b84adf47b58f8f304c97af4d5) + registerProviderFactory("^->AppComponent->InputNewPasswordComponent", factory27615059458a0576f404e3b0c44298fc1c149afb) + registerProviderFactory("^->AppComponent->InputEmailComponent", factoryf939e41ba3a1151e88f8f47b58f8f304c97af4d5) } #endif diff --git a/Projects/App/Support/Info.plist b/Projects/App/Support/Info.plist index a8624ed..4172bf3 100644 --- a/Projects/App/Support/Info.plist +++ b/Projects/App/Support/Info.plist @@ -16,8 +16,8 @@ $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString 1.0 - CFBundleVersion - 1 + CFBundleDisplayName + $(BUNDLE_DISPLAY_NAME) LSRequiresIPhoneOS UIApplicationSupportsIndirectInputEvents diff --git a/Projects/Feature/BaseFeature/Sources/EnvironmentValues/RootPresentationMode.swift b/Projects/Feature/BaseFeature/Sources/EnvironmentValues/RootPresentationMode.swift new file mode 100644 index 0000000..619b788 --- /dev/null +++ b/Projects/Feature/BaseFeature/Sources/EnvironmentValues/RootPresentationMode.swift @@ -0,0 +1,20 @@ +import SwiftUI + +public struct RootPresentationModeKey: EnvironmentKey { + public static let defaultValue: Binding = .constant(RootPresentationMode()) +} + +public extension EnvironmentValues { + var rootPresentationMode: Binding { + get { return self[RootPresentationModeKey.self] } + set { self[RootPresentationModeKey.self] = newValue } + } +} + +public typealias RootPresentationMode = Bool + +public extension Binding { + func popToRootView() { + self.wrappedValue.toggle() + } +} diff --git a/Projects/Feature/SignupFeature/Interface/SignupEmailVerifyFactory.swift b/Projects/Feature/FindPasswordFeature/Interface/InputEmailFactory.swift similarity index 66% rename from Projects/Feature/SignupFeature/Interface/SignupEmailVerifyFactory.swift rename to Projects/Feature/FindPasswordFeature/Interface/InputEmailFactory.swift index e2f140f..ce19a83 100644 --- a/Projects/Feature/SignupFeature/Interface/SignupEmailVerifyFactory.swift +++ b/Projects/Feature/FindPasswordFeature/Interface/InputEmailFactory.swift @@ -1,6 +1,6 @@ import SwiftUI -public protocol SignupEmailVerifyFactory { +public protocol InputEmailFactory { associatedtype SomeView: View func makeView() -> SomeView } diff --git a/Projects/Feature/SignupFeature/Interface/SignupEmailAuthCodeVerifyFactory.swift b/Projects/Feature/FindPasswordFeature/Interface/InputNewPasswordFactory.swift similarity index 62% rename from Projects/Feature/SignupFeature/Interface/SignupEmailAuthCodeVerifyFactory.swift rename to Projects/Feature/FindPasswordFeature/Interface/InputNewPasswordFactory.swift index 12eb80d..f12aab8 100644 --- a/Projects/Feature/SignupFeature/Interface/SignupEmailAuthCodeVerifyFactory.swift +++ b/Projects/Feature/FindPasswordFeature/Interface/InputNewPasswordFactory.swift @@ -1,6 +1,6 @@ import SwiftUI -public protocol SignupEmailAuthCodeVerifyFactory { +public protocol InputNewPasswordFactory { associatedtype SomeView: View func makeView() -> SomeView } diff --git a/Projects/Feature/FindPasswordFeature/Interface/VerifyAuthCodeFactory.swift b/Projects/Feature/FindPasswordFeature/Interface/VerifyAuthCodeFactory.swift new file mode 100644 index 0000000..b977a25 --- /dev/null +++ b/Projects/Feature/FindPasswordFeature/Interface/VerifyAuthCodeFactory.swift @@ -0,0 +1,6 @@ +import SwiftUI + +public protocol VerifyAuthCodeFactory { + associatedtype SomeView: View + func makeView() -> SomeView +} diff --git a/Projects/Feature/FindPasswordFeature/Project.swift b/Projects/Feature/FindPasswordFeature/Project.swift new file mode 100644 index 0000000..f654b37 --- /dev/null +++ b/Projects/Feature/FindPasswordFeature/Project.swift @@ -0,0 +1,17 @@ +import DependencyPlugin +import ProjectDescription +import ProjectDescriptionHelpers + +let project = Project.module( + name: ModulePaths.Feature.FindPasswordFeature.rawValue, + targets: [ + .interface(module: .feature(.FindPasswordFeature)), + .implements(module: .feature(.FindPasswordFeature), dependencies: [ + .feature(target: .FindPasswordFeature, type: .interface), + .feature(target: .BaseFeature) + ]), + .tests(module: .feature(.FindPasswordFeature), dependencies: [ + .feature(target: .FindPasswordFeature) + ]) + ] +) diff --git a/Projects/Feature/FindPasswordFeature/Sources/Email/InputEmailComponent.swift b/Projects/Feature/FindPasswordFeature/Sources/Email/InputEmailComponent.swift new file mode 100644 index 0000000..b35cc94 --- /dev/null +++ b/Projects/Feature/FindPasswordFeature/Sources/Email/InputEmailComponent.swift @@ -0,0 +1,16 @@ +import SwiftUI +import NeedleFoundation +import FindPasswordFeatureInterface + +public protocol InputEmailDependency: Dependency { + var verifyAuthCodeFactory: any VerifyAuthCodeFactory { get } +} + +public final class InputEmailComponent: Component, InputEmailFactory { + public func makeView() -> some View { + InputEmailView( + viewModel: .init(), + verifyAuthCodeFactory: dependency.verifyAuthCodeFactory + ) + } +} diff --git a/Projects/Feature/FindPasswordFeature/Sources/Email/InputEmailViewModel.swift b/Projects/Feature/FindPasswordFeature/Sources/Email/InputEmailViewModel.swift new file mode 100644 index 0000000..5831b0f --- /dev/null +++ b/Projects/Feature/FindPasswordFeature/Sources/Email/InputEmailViewModel.swift @@ -0,0 +1,12 @@ +import BaseFeature +import Combine + +final class InputEmailViewModel: BaseViewModel { + @Published var email: String = "" + + @Published var isNavigatedToVerifyAuthCode: Bool = false + + func nextButtonDidTap() { + self.isNavigatedToVerifyAuthCode = true + } +} diff --git a/Projects/Feature/FindPasswordFeature/Sources/Email/InputSignupEmailView.swift b/Projects/Feature/FindPasswordFeature/Sources/Email/InputSignupEmailView.swift new file mode 100644 index 0000000..0b44eac --- /dev/null +++ b/Projects/Feature/FindPasswordFeature/Sources/Email/InputSignupEmailView.swift @@ -0,0 +1,61 @@ +import DesignSystem +import SwiftUI +import BaseFeature +import ViewUtil +import FindPasswordFeatureInterface + +struct InputEmailView: View { + private enum FocusField { + case email + } + @FocusState private var focusField: FocusField? + @StateObject var viewModel: InputEmailViewModel + @Environment(\.rootPresentationMode) var rootPresentationMode + + private let verifyAuthCodeFactory: any VerifyAuthCodeFactory + + init( + viewModel: InputEmailViewModel, + verifyAuthCodeFactory: any VerifyAuthCodeFactory + ) { + _viewModel = StateObject(wrappedValue: viewModel) + self.verifyAuthCodeFactory = verifyAuthCodeFactory + } + + var body: some View { + VStack(spacing: 0) { + NavigationTitleView( + title: "이메일을 입력해 주세요", + description: "이메일로 인증번호를 전송해 드릴게요" + ) + + KGTextField( + "이메일을 입력해주세요", + text: $viewModel.email, + title: "이메일", + isError: viewModel.isErrorOccurred, + errorMessage: viewModel.errorMessage, + onCommit: viewModel.nextButtonDidTap + ) + .textContentType(.emailAddress) + .keyboardType(.emailAddress) + .focused($focusField, equals: .email) + + Spacer() + } + .bottomButton( + text: "다음", + isEditing: focusField != nil, + isDisabled: viewModel.email.isEmpty, + action: viewModel.nextButtonDidTap + ) + .navigationBackButton() + .kgBackground() + .hideKeyboardWhenTap() + .navigate( + to: verifyAuthCodeFactory.makeView().eraseToAnyView() + .environment(\.rootPresentationMode, rootPresentationMode), + when: $viewModel.isNavigatedToVerifyAuthCode + ) + } +} diff --git a/Projects/Feature/FindPasswordFeature/Sources/NewPassword/InputNewPasswordComponent.swift b/Projects/Feature/FindPasswordFeature/Sources/NewPassword/InputNewPasswordComponent.swift new file mode 100644 index 0000000..742f013 --- /dev/null +++ b/Projects/Feature/FindPasswordFeature/Sources/NewPassword/InputNewPasswordComponent.swift @@ -0,0 +1,13 @@ +import SwiftUI +import NeedleFoundation +import FindPasswordFeatureInterface + +public protocol InputNewPasswordDependency: Dependency {} + +public final class InputNewPasswordComponent: Component, InputNewPasswordFactory { + public func makeView() -> some View { + InputNewPasswordView( + viewModel: .init() + ) + } +} diff --git a/Projects/Feature/FindPasswordFeature/Sources/NewPassword/InputNewPasswordView.swift b/Projects/Feature/FindPasswordFeature/Sources/NewPassword/InputNewPasswordView.swift new file mode 100644 index 0000000..194f8b9 --- /dev/null +++ b/Projects/Feature/FindPasswordFeature/Sources/NewPassword/InputNewPasswordView.swift @@ -0,0 +1,71 @@ +import DesignSystem +import SwiftUI +import BaseFeature +import SignupFeatureInterface +import ViewUtil + +struct InputNewPasswordView: View { + private enum FocusField { + case password + case checkPassword + } + @FocusState private var focusField: FocusField? + @StateObject var viewModel: InputNewPasswordViewModel + @Environment(\.rootPresentationMode) var rootPresentationMode + + init( + viewModel: InputNewPasswordViewModel + ) { + _viewModel = StateObject(wrappedValue: viewModel) + } + + var body: some View { + VStack(spacing: 0) { + NavigationTitleView( + title: "새 비밀번호를 입력해주세요", + description: "비밀번호는 영어와 숫자를 조합해 만들어 주세요" + ) + + KGTextField( + "비밀번호(8~12자)를 입력해 주세요", + text: $viewModel.password, + title: "비밀번호", + isError: viewModel.isErrorOccurred, + errorMessage: viewModel.errorMessage, + isSecure: true + ) { + self.focusField = .checkPassword + } + .textContentType(.password) + .focused($focusField, equals: .password) + + KGTextField( + "비밀번호 다시 입력해 주세요", + text: $viewModel.checkPassword, + title: "비밀번호 확인", + isError: viewModel.isErrorOccurred, + errorMessage: viewModel.errorMessage, + isSecure: true, + onCommit: viewModel.nextButtonDidTap + ) + .textContentType(.password) + .focused($focusField, equals: .checkPassword) + + Spacer() + } + .bottomButton( + text: "다음", + isEditing: focusField != nil, + isDisabled: viewModel.password.isEmpty || viewModel.checkPassword.isEmpty, + action: viewModel.nextButtonDidTap + ) + .navigationBar() + .kgBackground() + .hideKeyboardWhenTap() + .onSuccess(of: viewModel.isSuccessToChangePassword) { + DispatchQueue.main.async { + self.rootPresentationMode.popToRootView() + } + } + } +} diff --git a/Projects/Feature/FindPasswordFeature/Sources/NewPassword/InputNewPasswordViewModel.swift b/Projects/Feature/FindPasswordFeature/Sources/NewPassword/InputNewPasswordViewModel.swift new file mode 100644 index 0000000..c0aa1a4 --- /dev/null +++ b/Projects/Feature/FindPasswordFeature/Sources/NewPassword/InputNewPasswordViewModel.swift @@ -0,0 +1,13 @@ +import BaseFeature +import Combine + +final class InputNewPasswordViewModel: BaseViewModel { + @Published var password: String = "" + @Published var checkPassword: String = "" + + @Published var isSuccessToChangePassword: Bool = false + + func nextButtonDidTap() { + self.isSuccessToChangePassword = true + } +} diff --git a/Projects/Feature/FindPasswordFeature/Sources/VerifyAuthCode/VerifyAuthCodeComponent.swift b/Projects/Feature/FindPasswordFeature/Sources/VerifyAuthCode/VerifyAuthCodeComponent.swift new file mode 100644 index 0000000..d713b57 --- /dev/null +++ b/Projects/Feature/FindPasswordFeature/Sources/VerifyAuthCode/VerifyAuthCodeComponent.swift @@ -0,0 +1,17 @@ +import SwiftUI +import NeedleFoundation +import FindPasswordFeatureInterface + +public protocol VerifyAuthCodeDependency: Dependency { + var inputNewPasswordFactory: any InputNewPasswordFactory { get } +} + +public final class VerifyAuthCodeComponent: Component, + VerifyAuthCodeFactory { + public func makeView() -> some View { + VerifyAuthCodeView( + viewModel: .init(), + inputNewPasswordFactory: dependency.inputNewPasswordFactory + ) + } +} diff --git a/Projects/Feature/FindPasswordFeature/Sources/VerifyAuthCode/VerifyAuthCodeView.swift b/Projects/Feature/FindPasswordFeature/Sources/VerifyAuthCode/VerifyAuthCodeView.swift new file mode 100644 index 0000000..9d0777d --- /dev/null +++ b/Projects/Feature/FindPasswordFeature/Sources/VerifyAuthCode/VerifyAuthCodeView.swift @@ -0,0 +1,70 @@ +import DesignSystem +import SwiftUI +import BaseFeature +import ViewUtil +import FindPasswordFeatureInterface + +struct VerifyAuthCodeView: View { + private enum FocusField { + case authCode + } + @FocusState private var focusField: FocusField? + @StateObject var viewModel: VerifyAuthCodeViewModel + @Environment(\.rootPresentationMode) var rootPresentationMode + + private let inputNewPasswordFactory: any InputNewPasswordFactory + + init( + viewModel: VerifyAuthCodeViewModel, + inputNewPasswordFactory: any InputNewPasswordFactory + ) { + _viewModel = StateObject(wrappedValue: viewModel) + self.inputNewPasswordFactory = inputNewPasswordFactory + } + + var body: some View { + VStack(spacing: 0) { + NavigationTitleView( + title: "인증번호를 입력해 주세요", + description: "입력해주신 이메일로 인증번호를 전송해 드렸어요" + ) + + VerifyCodeTextField( + $viewModel.authCode, + isError: viewModel.isErrorOccurred, + errorMessage: viewModel.errorMessage, + onCommit: viewModel.nextButtonDidTap + ) + .focused($focusField, equals: .authCode) + + HStack { + Text("5:00") + .kgFont(.label, weight: .regular, color: .Greens.main) + + Spacer() + + Text("인증번호 재전송") + .kgFont(.label, weight: .regular, color: .Grays.gray400) + .underlineText(color: .Grays.gray400) + } + .padding(.vertical, 16) + .padding(.horizontal, 24) + + Spacer() + } + .bottomButton( + text: "인증", + isEditing: focusField != nil, + isDisabled: viewModel.authCode.count < 4, + action: viewModel.nextButtonDidTap + ) + .navigationBackButton() + .kgBackground() + .hideKeyboardWhenTap() + .navigate( + to: inputNewPasswordFactory.makeView().eraseToAnyView() + .environment(\.rootPresentationMode, rootPresentationMode), + when: $viewModel.isNavigatedToNewPassword + ) + } +} diff --git a/Projects/Feature/FindPasswordFeature/Sources/VerifyAuthCode/VerifyAuthCodeViewModel.swift b/Projects/Feature/FindPasswordFeature/Sources/VerifyAuthCode/VerifyAuthCodeViewModel.swift new file mode 100644 index 0000000..9828a4e --- /dev/null +++ b/Projects/Feature/FindPasswordFeature/Sources/VerifyAuthCode/VerifyAuthCodeViewModel.swift @@ -0,0 +1,13 @@ +import BaseFeature +import Foundation +import Combine + +final class VerifyAuthCodeViewModel: BaseViewModel { + @Published var authCode: String = "" + + @Published var isNavigatedToNewPassword: Bool = false + + func nextButtonDidTap() { + self.isNavigatedToNewPassword = true + } +} diff --git a/Projects/Feature/FindPasswordFeature/Tests/FindPasswordFeatureTest.swift b/Projects/Feature/FindPasswordFeature/Tests/FindPasswordFeatureTest.swift new file mode 100644 index 0000000..ee17017 --- /dev/null +++ b/Projects/Feature/FindPasswordFeature/Tests/FindPasswordFeatureTest.swift @@ -0,0 +1,11 @@ +import XCTest + +final class FindPasswordFeatureTests: XCTestCase { + override func setUpWithError() throws {} + + override func tearDownWithError() throws {} + + func testExample() { + XCTAssertEqual(1, 1) + } +} diff --git a/Projects/Feature/SigninFeature/Project.swift b/Projects/Feature/SigninFeature/Project.swift index 7bde5f1..048b497 100644 --- a/Projects/Feature/SigninFeature/Project.swift +++ b/Projects/Feature/SigninFeature/Project.swift @@ -9,7 +9,8 @@ let project = Project.module( .implements(module: .feature(.SigninFeature), dependencies: [ .feature(target: .SigninFeature, type: .interface), .feature(target: .BaseFeature), - .feature(target: .SignupFeature, type: .interface) + .feature(target: .SignupFeature, type: .interface), + .feature(target: .FindPasswordFeature, type: .interface) ]), .tests(module: .feature(.SigninFeature), dependencies: [ .feature(target: .SigninFeature) diff --git a/Projects/Feature/SigninFeature/Sources/SigninComponent.swift b/Projects/Feature/SigninFeature/Sources/SigninComponent.swift index 93828b4..1c3e783 100644 --- a/Projects/Feature/SigninFeature/Sources/SigninComponent.swift +++ b/Projects/Feature/SigninFeature/Sources/SigninComponent.swift @@ -2,16 +2,19 @@ import SwiftUI import NeedleFoundation import SigninFeatureInterface import SignupFeatureInterface +import FindPasswordFeatureInterface public protocol SigninDependency: Dependency { - var signupEmailVerifyFactory: any SignupEmailVerifyFactory { get } + var signupEmailFactory: any SignupEmailFactory { get } + var inputEmailFactory: any InputEmailFactory { get } } public final class SigninComponent: Component, SigninFactory { public func makeView() -> some View { SigninView( viewModel: .init(), - signupEmailVerifyFactory: dependency.signupEmailVerifyFactory + signupEmailFactory: dependency.signupEmailFactory, + inputEmailFactory: dependency.inputEmailFactory ) } } diff --git a/Projects/Feature/SigninFeature/Sources/SigninView.swift b/Projects/Feature/SigninFeature/Sources/SigninView.swift index fc5c7d5..1a53054 100644 --- a/Projects/Feature/SigninFeature/Sources/SigninView.swift +++ b/Projects/Feature/SigninFeature/Sources/SigninView.swift @@ -3,6 +3,7 @@ import SwiftUI import BaseFeature import ViewUtil import SignupFeatureInterface +import FindPasswordFeatureInterface struct SigninView: View { private enum FocusField { @@ -13,14 +14,17 @@ struct SigninView: View { @FocusState private var focusField: FocusField? @StateObject var viewModel: SigninViewModel - private let signupEmailVerifyFactory: any SignupEmailVerifyFactory + private let signupEmailFactory: any SignupEmailFactory + private let inputEmailFactory: any InputEmailFactory init( viewModel: SigninViewModel, - signupEmailVerifyFactory: any SignupEmailVerifyFactory + signupEmailFactory: any SignupEmailFactory, + inputEmailFactory: any InputEmailFactory ) { _viewModel = StateObject(wrappedValue: viewModel) - self.signupEmailVerifyFactory = signupEmailVerifyFactory + self.signupEmailFactory = signupEmailFactory + self.inputEmailFactory = inputEmailFactory } var body: some View { @@ -64,16 +68,31 @@ struct SigninView: View { Color.Grays.gray700 .frame(width: 1, height: 16) - Text("비밀번호 찾기") - .kgFont(.label, weight: .regular, color: .Grays.gray700) + Button(action: viewModel.findPasswordButtonDidTap) { + Text("비밀번호 찾기") + .kgFont(.label, weight: .regular, color: .Grays.gray700) + } } } Spacer() } +<<<<<<< HEAD .navigate(to: signupEmailVerifyFactory.makeView().eraseToAnyView(), when: $viewModel.isNavigatedToSignup) .onChange(of: viewModel.isSuccessToSignin) { _ in self.appState.sceneFlow = .main } +======= + .hideKeyboardWhenTap() + .navigate( + to: signupEmailFactory.makeView().eraseToAnyView(), + when: $viewModel.isNavigatedToSignup + ) + .navigate( + to: inputEmailFactory.makeView().eraseToAnyView() + .environment(\.rootPresentationMode, $viewModel.isNavigatedToFindPassword), + when: $viewModel.isNavigatedToFindPassword + ) +>>>>>>> origin/develop } } diff --git a/Projects/Feature/SigninFeature/Sources/SigninViewModel.swift b/Projects/Feature/SigninFeature/Sources/SigninViewModel.swift index 49f4f48..cebe659 100644 --- a/Projects/Feature/SigninFeature/Sources/SigninViewModel.swift +++ b/Projects/Feature/SigninFeature/Sources/SigninViewModel.swift @@ -6,6 +6,7 @@ final class SigninViewModel: BaseViewModel { @Published var password: String = "" @Published var isSuccessToSignin: Bool = false @Published var isNavigatedToSignup: Bool = false + @Published var isNavigatedToFindPassword: Bool = false func signinButtonDidTap() { self.isSuccessToSignin = true @@ -14,4 +15,8 @@ final class SigninViewModel: BaseViewModel { func signupButtonDidTap() { self.isNavigatedToSignup = true } + + func findPasswordButtonDidTap() { + self.isNavigatedToFindPassword = true + } } diff --git a/Projects/Feature/SignupFeature/Interface/SignupEmailFactory.swift b/Projects/Feature/SignupFeature/Interface/SignupEmailFactory.swift new file mode 100644 index 0000000..556aafa --- /dev/null +++ b/Projects/Feature/SignupFeature/Interface/SignupEmailFactory.swift @@ -0,0 +1,6 @@ +import SwiftUI + +public protocol SignupEmailFactory { + associatedtype SomeView: View + func makeView() -> SomeView +} diff --git a/Projects/Feature/SignupFeature/Interface/SignupVerifyAuthCodeFactory.swift b/Projects/Feature/SignupFeature/Interface/SignupVerifyAuthCodeFactory.swift new file mode 100644 index 0000000..4689581 --- /dev/null +++ b/Projects/Feature/SignupFeature/Interface/SignupVerifyAuthCodeFactory.swift @@ -0,0 +1,6 @@ +import SwiftUI + +public protocol SignupVerifyAuthCodeFactory { + associatedtype SomeView: View + func makeView() -> SomeView +} diff --git a/Projects/Feature/SignupFeature/Interface/UserInfo/SignupCheckLevelFactory.swift b/Projects/Feature/SignupFeature/Interface/UserInfo/SignupCheckLevelFactory.swift index d05004f..8a401fd 100644 --- a/Projects/Feature/SignupFeature/Interface/UserInfo/SignupCheckLevelFactory.swift +++ b/Projects/Feature/SignupFeature/Interface/UserInfo/SignupCheckLevelFactory.swift @@ -2,5 +2,5 @@ import SwiftUI public protocol SignupCheckLevelFactory { associatedtype SomeView: View - func makeView() -> SomeView + func makeView(nextViewNavigateAction: @escaping () -> Void) -> SomeView } diff --git a/Projects/Feature/SignupFeature/Interface/UserInfo/SignupGenderFactory.swift b/Projects/Feature/SignupFeature/Interface/UserInfo/SignupGenderFactory.swift index a169b8f..e5dce8e 100644 --- a/Projects/Feature/SignupFeature/Interface/UserInfo/SignupGenderFactory.swift +++ b/Projects/Feature/SignupFeature/Interface/UserInfo/SignupGenderFactory.swift @@ -2,5 +2,5 @@ import SwiftUI public protocol SignupGenderFactory { associatedtype SomeView: View - func makeView() -> SomeView + func makeView(nextViewNavigateAction: @escaping () -> Void) -> SomeView } diff --git a/Projects/Feature/SignupFeature/Interface/UserInfo/SignupNameFactory.swift b/Projects/Feature/SignupFeature/Interface/UserInfo/SignupNameFactory.swift index cffaf15..7823758 100644 --- a/Projects/Feature/SignupFeature/Interface/UserInfo/SignupNameFactory.swift +++ b/Projects/Feature/SignupFeature/Interface/UserInfo/SignupNameFactory.swift @@ -2,5 +2,5 @@ import SwiftUI public protocol SignupNameFactory { associatedtype SomeView: View - func makeView() -> SomeView + func makeView(nextViewNavigateAction: @escaping () -> Void) -> SomeView } diff --git a/Projects/Feature/SignupFeature/Interface/UserInfo/SignupStudentIDFactory.swift b/Projects/Feature/SignupFeature/Interface/UserInfo/SignupStudentIDFactory.swift index e9b1832..a7d11ed 100644 --- a/Projects/Feature/SignupFeature/Interface/UserInfo/SignupStudentIDFactory.swift +++ b/Projects/Feature/SignupFeature/Interface/UserInfo/SignupStudentIDFactory.swift @@ -2,5 +2,5 @@ import SwiftUI public protocol SignupStudentIDFactory { associatedtype SomeView: View - func makeView() -> SomeView + func makeView(nextViewNavigateAction: @escaping () -> Void) -> SomeView } diff --git a/Projects/Feature/SignupFeature/Interface/UserInfo/SignupUserInfoFlowFactory.swift b/Projects/Feature/SignupFeature/Interface/UserInfo/SignupUserInfoFlowFactory.swift new file mode 100644 index 0000000..1791c5f --- /dev/null +++ b/Projects/Feature/SignupFeature/Interface/UserInfo/SignupUserInfoFlowFactory.swift @@ -0,0 +1,6 @@ +import SwiftUI + +public protocol SignupUserInfoFlowFactory { + associatedtype SomeView: View + func makeView() -> SomeView +} diff --git a/Projects/Feature/SignupFeature/Sources/Email/SignupEmailComponent.swift b/Projects/Feature/SignupFeature/Sources/Email/SignupEmailComponent.swift new file mode 100644 index 0000000..8db734b --- /dev/null +++ b/Projects/Feature/SignupFeature/Sources/Email/SignupEmailComponent.swift @@ -0,0 +1,16 @@ +import SwiftUI +import NeedleFoundation +import SignupFeatureInterface + +public protocol SignupEmailDependency: Dependency { + var signupVerifyAuthCode: any SignupVerifyAuthCodeFactory { get } +} + +public final class SignupEmailComponent: Component, SignupEmailFactory { + public func makeView() -> some View { + SignupEmailView( + viewModel: .init(), + signupVerifyAuthCode: dependency.signupVerifyAuthCode + ) + } +} diff --git a/Projects/Feature/SignupFeature/Sources/EmailVerify/SignupEmailVerifyView.swift b/Projects/Feature/SignupFeature/Sources/Email/SignupEmailView.swift similarity index 71% rename from Projects/Feature/SignupFeature/Sources/EmailVerify/SignupEmailVerifyView.swift rename to Projects/Feature/SignupFeature/Sources/Email/SignupEmailView.swift index d246056..abe8b3d 100644 --- a/Projects/Feature/SignupFeature/Sources/EmailVerify/SignupEmailVerifyView.swift +++ b/Projects/Feature/SignupFeature/Sources/Email/SignupEmailView.swift @@ -4,21 +4,21 @@ import BaseFeature import ViewUtil import SignupFeatureInterface -struct SignupEmailVerifyView: View { +struct SignupEmailView: View { private enum FocusField { case email } @FocusState private var focusField: FocusField? - @StateObject var viewModel: SignupEmailVerifyViewModel + @StateObject var viewModel: SignupEmailViewModel - private let signupEmailAuthCodeVerifyFactory: any SignupEmailAuthCodeVerifyFactory + private let signupVerifyAuthCode: any SignupVerifyAuthCodeFactory init( - viewModel: SignupEmailVerifyViewModel, - signupEmailAuthCodeVerifyFactory: any SignupEmailAuthCodeVerifyFactory + viewModel: SignupEmailViewModel, + signupVerifyAuthCode: any SignupVerifyAuthCodeFactory ) { _viewModel = StateObject(wrappedValue: viewModel) - self.signupEmailAuthCodeVerifyFactory = signupEmailAuthCodeVerifyFactory + self.signupVerifyAuthCode = signupVerifyAuthCode } var body: some View { @@ -52,8 +52,8 @@ struct SignupEmailVerifyView: View { .kgBackground() .hideKeyboardWhenTap() .navigate( - to: signupEmailAuthCodeVerifyFactory.makeView().eraseToAnyView(), - when: $viewModel.isNavigatedToEmailAuthCodeVerify + to: signupVerifyAuthCode.makeView().eraseToAnyView(), + when: $viewModel.isNavigatedToVerifyAuthCode ) } } diff --git a/Projects/Feature/SignupFeature/Sources/Email/SignupEmailViewModel.swift b/Projects/Feature/SignupFeature/Sources/Email/SignupEmailViewModel.swift new file mode 100644 index 0000000..bcd69c1 --- /dev/null +++ b/Projects/Feature/SignupFeature/Sources/Email/SignupEmailViewModel.swift @@ -0,0 +1,12 @@ +import BaseFeature +import Combine + +final class SignupEmailViewModel: BaseViewModel { + @Published var email: String = "" + + @Published var isNavigatedToVerifyAuthCode: Bool = false + + func nextButtonDidTap() { + self.isNavigatedToVerifyAuthCode = true + } +} diff --git a/Projects/Feature/SignupFeature/Sources/EmailVerify/SignupEmailVerifyComponent.swift b/Projects/Feature/SignupFeature/Sources/EmailVerify/SignupEmailVerifyComponent.swift deleted file mode 100644 index 2144fce..0000000 --- a/Projects/Feature/SignupFeature/Sources/EmailVerify/SignupEmailVerifyComponent.swift +++ /dev/null @@ -1,16 +0,0 @@ -import SwiftUI -import NeedleFoundation -import SignupFeatureInterface - -public protocol SignupEmailVerifyDependency: Dependency { - var signupEmailAuthCodeVerifyFactory: any SignupEmailAuthCodeVerifyFactory { get } -} - -public final class SignupEmailVerifyComponent: Component, SignupEmailVerifyFactory { - public func makeView() -> some View { - SignupEmailVerifyView( - viewModel: .init(), - signupEmailAuthCodeVerifyFactory: dependency.signupEmailAuthCodeVerifyFactory - ) - } -} diff --git a/Projects/Feature/SignupFeature/Sources/EmailVerify/SignupEmailVerifyViewModel.swift b/Projects/Feature/SignupFeature/Sources/EmailVerify/SignupEmailVerifyViewModel.swift deleted file mode 100644 index eda6279..0000000 --- a/Projects/Feature/SignupFeature/Sources/EmailVerify/SignupEmailVerifyViewModel.swift +++ /dev/null @@ -1,12 +0,0 @@ -import BaseFeature -import Combine - -final class SignupEmailVerifyViewModel: BaseViewModel { - @Published var email: String = "" - - @Published var isNavigatedToEmailAuthCodeVerify: Bool = false - - func nextButtonDidTap() { - self.isNavigatedToEmailAuthCodeVerify = true - } -} diff --git a/Projects/Feature/SignupFeature/Sources/Password/SignupPasswordComponent.swift b/Projects/Feature/SignupFeature/Sources/Password/SignupPasswordComponent.swift index f60ec80..eb59e0d 100644 --- a/Projects/Feature/SignupFeature/Sources/Password/SignupPasswordComponent.swift +++ b/Projects/Feature/SignupFeature/Sources/Password/SignupPasswordComponent.swift @@ -3,14 +3,14 @@ import NeedleFoundation import SignupFeatureInterface public protocol SignupPasswordDependency: Dependency { - var signupNameFactory: any SignupNameFactory { get } + var signupUserInfoFlowFactory: any SignupUserInfoFlowFactory { get } } public final class SignupPasswordComponent: Component, SignupPasswordFactory { public func makeView() -> some View { SignupPasswordView( viewModel: .init(), - signupNameFactory: dependency.signupNameFactory + signupUserInfoFlowFactory: dependency.signupUserInfoFlowFactory ) } } diff --git a/Projects/Feature/SignupFeature/Sources/Password/SignupPasswordView.swift b/Projects/Feature/SignupFeature/Sources/Password/SignupPasswordView.swift index a7f14b6..4663b23 100644 --- a/Projects/Feature/SignupFeature/Sources/Password/SignupPasswordView.swift +++ b/Projects/Feature/SignupFeature/Sources/Password/SignupPasswordView.swift @@ -12,14 +12,14 @@ struct SignupPasswordView: View { @FocusState private var focusField: FocusField? @StateObject var viewModel: SignupPasswordViewModel - private let signupNameFactory: any SignupNameFactory + private let signupUserInfoFlowFactory: any SignupUserInfoFlowFactory init( viewModel: SignupPasswordViewModel, - signupNameFactory: any SignupNameFactory + signupUserInfoFlowFactory: any SignupUserInfoFlowFactory ) { _viewModel = StateObject(wrappedValue: viewModel) - self.signupNameFactory = signupNameFactory + self.signupUserInfoFlowFactory = signupUserInfoFlowFactory } var body: some View { @@ -66,7 +66,7 @@ struct SignupPasswordView: View { .kgBackground() .hideKeyboardWhenTap() .navigate( - to: signupNameFactory.makeView().eraseToAnyView(), + to: signupUserInfoFlowFactory.makeView().eraseToAnyView(), when: $viewModel.isNavigatedToName ) } diff --git a/Projects/Feature/SignupFeature/Sources/UserInfo/CheckLevel/SignupCheckLevelComponent.swift b/Projects/Feature/SignupFeature/Sources/UserInfo/CheckLevel/SignupCheckLevelComponent.swift index f677aec..38ee5a8 100644 --- a/Projects/Feature/SignupFeature/Sources/UserInfo/CheckLevel/SignupCheckLevelComponent.swift +++ b/Projects/Feature/SignupFeature/Sources/UserInfo/CheckLevel/SignupCheckLevelComponent.swift @@ -6,9 +6,9 @@ public protocol SignupCheckLevelDependency: Dependency { } public final class SignupCheckLevelComponent: Component, SignupCheckLevelFactory { - public func makeView() -> some View { + public func makeView(nextViewNavigateAction: @escaping () -> Void) -> some View { SignupCheckLevelView( - viewModel: .init() + viewModel: .init(nextViewNavigateAction: nextViewNavigateAction) ) } } diff --git a/Projects/Feature/SignupFeature/Sources/UserInfo/CheckLevel/SignupCheckLevelView.swift b/Projects/Feature/SignupFeature/Sources/UserInfo/CheckLevel/SignupCheckLevelView.swift index 36308c6..bea9ec1 100644 --- a/Projects/Feature/SignupFeature/Sources/UserInfo/CheckLevel/SignupCheckLevelView.swift +++ b/Projects/Feature/SignupFeature/Sources/UserInfo/CheckLevel/SignupCheckLevelView.swift @@ -16,8 +16,6 @@ struct SignupCheckLevelView: View { var body: some View { VStack(spacing: 0) { - SignupProgressView(step: .checkLevel) - NavigationTitleView( title: "운동 실력을 알려주세요", description: "평소 운동을 좋아하고 잘하시는지 알려주세요" @@ -36,11 +34,9 @@ struct SignupCheckLevelView: View { Spacer() } .bottomButton( - text: "다음", -// isDisabled: viewModel.studentID.isEmpty, + text: "완료", action: viewModel.nextButtonDidTap ) - .navigationBackButton(navigationTitle: "정보 입력") .kgBackground() .hideKeyboardWhenTap() .onChange(of: viewModel.isSuccessToSignup) { _ in diff --git a/Projects/Feature/SignupFeature/Sources/UserInfo/CheckLevel/SignupCheckLevelViewModel.swift b/Projects/Feature/SignupFeature/Sources/UserInfo/CheckLevel/SignupCheckLevelViewModel.swift index 440ff20..b3913a6 100644 --- a/Projects/Feature/SignupFeature/Sources/UserInfo/CheckLevel/SignupCheckLevelViewModel.swift +++ b/Projects/Feature/SignupFeature/Sources/UserInfo/CheckLevel/SignupCheckLevelViewModel.swift @@ -6,7 +6,15 @@ final class SignupCheckLevelViewModel: BaseViewModel { @Published var selectedLevel: PhysicalLevel = .high @Published var isSuccessToSignup: Bool = false + private let nextViewNavigateAction: () -> Void + + init(nextViewNavigateAction: @escaping () -> Void) { + self.nextViewNavigateAction = nextViewNavigateAction + } + func nextButtonDidTap() { self.isSuccessToSignup = true + + nextViewNavigateAction() } } diff --git a/Projects/Feature/SignupFeature/Sources/UserInfo/Components/SignupProgressView.swift b/Projects/Feature/SignupFeature/Sources/UserInfo/Components/SignupProgressView.swift index a1a93dc..08d3e84 100644 --- a/Projects/Feature/SignupFeature/Sources/UserInfo/Components/SignupProgressView.swift +++ b/Projects/Feature/SignupFeature/Sources/UserInfo/Components/SignupProgressView.swift @@ -32,6 +32,7 @@ struct SignupProgressView: View { HStack(spacing: 0) { Color.Greens.secondary .frame(width: 31, height: 12) + ForEach(1...3, id: \.self) { index in (index < self.step.rawValue ? Color.Greens.secondary : Color.Grays.gray1000) .frame(height: 12) @@ -41,7 +42,9 @@ struct SignupProgressView: View { Color.Grays.gray1000 .frame(width: 31, height: 12) } + .background(Color.Grays.gray1000) } .padding(.vertical, 16) + .animation(.default, value: step) } } diff --git a/Projects/Feature/SignupFeature/Sources/UserInfo/Flow/SignupUserInfoFlowComponent.swift b/Projects/Feature/SignupFeature/Sources/UserInfo/Flow/SignupUserInfoFlowComponent.swift new file mode 100644 index 0000000..c983fb5 --- /dev/null +++ b/Projects/Feature/SignupFeature/Sources/UserInfo/Flow/SignupUserInfoFlowComponent.swift @@ -0,0 +1,22 @@ +import SwiftUI +import NeedleFoundation +import SignupFeatureInterface + +public protocol SignupUserInfoFlowDependency: Dependency { + var signupNameFactory: any SignupNameFactory { get } + var signupStudentIDFactory: any SignupStudentIDFactory { get } + var signupGenderFactory: any SignupGenderFactory { get } + var signupCheckLevelFactory: any SignupCheckLevelFactory { get } +} + +public final class SignupUserInfoFlowComponent: Component, SignupUserInfoFlowFactory { + public func makeView() -> some View { + SignupUserInfoFlowView( + viewModel: .init(), + nameFactory: dependency.signupNameFactory, + studentIDFactory: dependency.signupStudentIDFactory, + genderFactory: dependency.signupGenderFactory, + checkLevelFactory: dependency.signupCheckLevelFactory + ) + } +} diff --git a/Projects/Feature/SignupFeature/Sources/UserInfo/Flow/SignupUserInfoFlowView.swift b/Projects/Feature/SignupFeature/Sources/UserInfo/Flow/SignupUserInfoFlowView.swift new file mode 100644 index 0000000..864e95e --- /dev/null +++ b/Projects/Feature/SignupFeature/Sources/UserInfo/Flow/SignupUserInfoFlowView.swift @@ -0,0 +1,82 @@ +import SwiftUI +import DesignSystem +import ViewUtil +import SignupFeatureInterface + +struct SignupUserInfoFlowView: View { + @Environment(\.dismiss) var dismiss + @StateObject var viewModel: SignupUserInfoFlowViewModel + + private let nameFactory: any SignupNameFactory + private let studentIDFactory: any SignupStudentIDFactory + private let genderFactory: any SignupGenderFactory + private let checkLevelFactory: any SignupCheckLevelFactory + + init( + viewModel: SignupUserInfoFlowViewModel, + nameFactory: any SignupNameFactory, + studentIDFactory: any SignupStudentIDFactory, + genderFactory: any SignupGenderFactory, + checkLevelFactory: any SignupCheckLevelFactory + ) { + _viewModel = StateObject(wrappedValue: viewModel) + self.nameFactory = nameFactory + self.studentIDFactory = studentIDFactory + self.genderFactory = genderFactory + self.checkLevelFactory = checkLevelFactory + } + + var body: some View { + VStack(spacing: 0) { + SignupProgressView(step: viewModel.signupStep) + + Group { + switch viewModel.signupStep { + case .name: + nameFactory.makeView( + nextViewNavigateAction: viewModel.nextButtonDidTap + ) + .eraseToAnyView() + + case .studentID: + studentIDFactory.makeView( + nextViewNavigateAction: viewModel.nextButtonDidTap + ) + .eraseToAnyView() + + case .gender: + genderFactory.makeView( + nextViewNavigateAction: viewModel.nextButtonDidTap + ) + .eraseToAnyView() + + case .checkLevel: + checkLevelFactory.makeView( + nextViewNavigateAction: viewModel.nextButtonDidTap + ) + .eraseToAnyView() + } + } + .transition(viewModel.transition) + } + .animation(.default, value: viewModel.signupStep) + .navigationBackButton( + navigationTitle: "정보 입력", + backAction: { viewModel.backButtonDidTap(dismiss: dismiss) } + ) + .kgBackground() + .hideKeyboardWhenTap() + } +} + +extension AnyTransition { + static let backSlideTransition = AnyTransition.asymmetric( + insertion: .move(edge: .leading), + removal: .move(edge: .trailing) + ) + + static let nextSlideTransition = AnyTransition.asymmetric( + insertion: .move(edge: .trailing), + removal: .move(edge: .leading) + ) +} diff --git a/Projects/Feature/SignupFeature/Sources/UserInfo/Flow/SignupUserInfoFlowViewModel.swift b/Projects/Feature/SignupFeature/Sources/UserInfo/Flow/SignupUserInfoFlowViewModel.swift new file mode 100644 index 0000000..d99c071 --- /dev/null +++ b/Projects/Feature/SignupFeature/Sources/UserInfo/Flow/SignupUserInfoFlowViewModel.swift @@ -0,0 +1,38 @@ +import BaseFeature +import Combine +import SwiftUI + +final class SignupUserInfoFlowViewModel: BaseViewModel { + @Published var signupStep: SignupStep = .name + var transition: AnyTransition = .nextSlideTransition + + func nextButtonDidTap() { + self.transition = .nextSlideTransition + + switch signupStep { + case .name: + self.signupStep = .studentID + case .studentID: + self.signupStep = .gender + case .gender: + self.signupStep = .checkLevel + case .checkLevel: + break + } + } + + func backButtonDidTap(dismiss: DismissAction) { + self.transition = .backSlideTransition + + switch signupStep { + case .name: + dismiss() + case .studentID: + self.signupStep = .name + case .gender: + self.signupStep = .studentID + case .checkLevel: + self.signupStep = .gender + } + } +} diff --git a/Projects/Feature/SignupFeature/Sources/UserInfo/Gender/Components/SelectGenderView.swift b/Projects/Feature/SignupFeature/Sources/UserInfo/Gender/Components/SelectGenderView.swift new file mode 100644 index 0000000..f383473 --- /dev/null +++ b/Projects/Feature/SignupFeature/Sources/UserInfo/Gender/Components/SelectGenderView.swift @@ -0,0 +1,57 @@ +import SwiftUI +import DesignSystem + +enum Gender { + case man + case woman +} + +struct SelectGenderView: View { + let gender: Gender + @Binding var selectedGender: Gender + + init(_ gender: Gender, selectedGender: Binding) { + self.gender = gender + _selectedGender = selectedGender + } + + var body: some View { + Button { + self.selectedGender = gender + } label: { + VStack(spacing: 12) { + gender.image() + .frame(maxWidth: .infinity) + .background(Color.Grays.gray1100) + .clipCornerRadius(8) + .overlay { + RoundedRectangle(cornerRadius: 8) + .strokeBorder( + self.selectedGender == gender ? Color.Greens.main : .clear, + lineWidth: 1 + ) + } + } + } + } +} + +extension Gender { + @ViewBuilder + func image() -> some View { + switch self { + case .man: + DesignSystemAsset.Images.man.swiftUIImage + .resizable() + .frame(width: 130, height: 128) + .padding(.vertical, 16) + + case .woman: + DesignSystemAsset.Images.woman.swiftUIImage + .resizable() + .frame(width: 113, height: 144) + .padding(.top, 11) + .padding(.bottom, 5) + } + } +} diff --git a/Projects/Feature/SignupFeature/Sources/UserInfo/Gender/SignupGenderComponent.swift b/Projects/Feature/SignupFeature/Sources/UserInfo/Gender/SignupGenderComponent.swift index 7be04c9..97ab0d2 100644 --- a/Projects/Feature/SignupFeature/Sources/UserInfo/Gender/SignupGenderComponent.swift +++ b/Projects/Feature/SignupFeature/Sources/UserInfo/Gender/SignupGenderComponent.swift @@ -2,15 +2,12 @@ import SwiftUI import NeedleFoundation import SignupFeatureInterface -public protocol SignupGenderDependency: Dependency { - var signupCheckLevelFactory: any SignupCheckLevelFactory { get } -} +public protocol SignupGenderDependency: Dependency {} public final class SignupGenderComponent: Component, SignupGenderFactory { - public func makeView() -> some View { + public func makeView(nextViewNavigateAction: @escaping () -> Void) -> some View { SignupGenderView( - viewModel: .init(), - signupCheckLevelFactory: dependency.signupCheckLevelFactory + viewModel: .init(nextViewNavigateAction: nextViewNavigateAction) ) } } diff --git a/Projects/Feature/SignupFeature/Sources/UserInfo/Gender/SignupGenderView.swift b/Projects/Feature/SignupFeature/Sources/UserInfo/Gender/SignupGenderView.swift index 22e1a31..4c2f89f 100644 --- a/Projects/Feature/SignupFeature/Sources/UserInfo/Gender/SignupGenderView.swift +++ b/Projects/Feature/SignupFeature/Sources/UserInfo/Gender/SignupGenderView.swift @@ -7,38 +7,33 @@ import ViewUtil struct SignupGenderView: View { @StateObject var viewModel: SignupGenderViewModel - private let signupCheckLevelFactory: any SignupCheckLevelFactory - init( - viewModel: SignupGenderViewModel, - signupCheckLevelFactory: any SignupCheckLevelFactory + viewModel: SignupGenderViewModel ) { _viewModel = StateObject(wrappedValue: viewModel) - self.signupCheckLevelFactory = signupCheckLevelFactory } var body: some View { VStack(spacing: 0) { - SignupProgressView(step: .gender) - NavigationTitleView( title: "성별을 알려주세요", description: "원활한 서비스를 위해 성별을 알려주세요" ) + HStack(spacing: 22) { + SelectGenderView(.woman, selectedGender: $viewModel.selectedGender) + + SelectGenderView(.man, selectedGender: $viewModel.selectedGender) + } + .padding(.horizontal, 24) + Spacer() } .bottomButton( text: "다음", -// isDisabled: viewModel.studentID.isEmpty, action: viewModel.nextButtonDidTap ) - .navigationBackButton(navigationTitle: "정보 입력") .kgBackground() .hideKeyboardWhenTap() - .navigate( - to: signupCheckLevelFactory.makeView().eraseToAnyView(), - when: $viewModel.isNavigatedToLevelCheck - ) } } diff --git a/Projects/Feature/SignupFeature/Sources/UserInfo/Gender/SignupGenderViewModel.swift b/Projects/Feature/SignupFeature/Sources/UserInfo/Gender/SignupGenderViewModel.swift index 3203975..ae9d075 100644 --- a/Projects/Feature/SignupFeature/Sources/UserInfo/Gender/SignupGenderViewModel.swift +++ b/Projects/Feature/SignupFeature/Sources/UserInfo/Gender/SignupGenderViewModel.swift @@ -2,11 +2,15 @@ import BaseFeature import Combine final class SignupGenderViewModel: BaseViewModel { -// @Published var studentID: String = "" + @Published var selectedGender: Gender = .woman - @Published var isNavigatedToLevelCheck: Bool = false + private let nextViewNavigateAction: () -> Void + + init(nextViewNavigateAction: @escaping () -> Void) { + self.nextViewNavigateAction = nextViewNavigateAction + } func nextButtonDidTap() { - self.isNavigatedToLevelCheck = true + self.nextViewNavigateAction() } } diff --git a/Projects/Feature/SignupFeature/Sources/UserInfo/Name/SignupNameComponent.swift b/Projects/Feature/SignupFeature/Sources/UserInfo/Name/SignupNameComponent.swift index bb1f68e..19748b3 100644 --- a/Projects/Feature/SignupFeature/Sources/UserInfo/Name/SignupNameComponent.swift +++ b/Projects/Feature/SignupFeature/Sources/UserInfo/Name/SignupNameComponent.swift @@ -2,15 +2,12 @@ import SwiftUI import NeedleFoundation import SignupFeatureInterface -public protocol SignupNameDependency: Dependency { - var signupStudentIDFactory: any SignupStudentIDFactory { get } -} +public protocol SignupNameDependency: Dependency {} public final class SignupNameComponent: Component, SignupNameFactory { - public func makeView() -> some View { + public func makeView(nextViewNavigateAction: @escaping () -> Void) -> some View { SignupNameView( - viewModel: .init(), - signupStudentIDFactory: dependency.signupStudentIDFactory + viewModel: .init(nextViewNavigateAction: nextViewNavigateAction) ) } } diff --git a/Projects/Feature/SignupFeature/Sources/UserInfo/Name/SignupNameView.swift b/Projects/Feature/SignupFeature/Sources/UserInfo/Name/SignupNameView.swift index 4884ab0..e469159 100644 --- a/Projects/Feature/SignupFeature/Sources/UserInfo/Name/SignupNameView.swift +++ b/Projects/Feature/SignupFeature/Sources/UserInfo/Name/SignupNameView.swift @@ -11,19 +11,14 @@ struct SignupNameView: View { @FocusState private var focusField: FocusField? @StateObject var viewModel: SignupNameViewModel - private let signupStudentIDFactory: any SignupStudentIDFactory init( - viewModel: SignupNameViewModel, - signupStudentIDFactory: any SignupStudentIDFactory + viewModel: SignupNameViewModel ) { _viewModel = StateObject(wrappedValue: viewModel) - self.signupStudentIDFactory = signupStudentIDFactory } var body: some View { VStack(spacing: 0) { - SignupProgressView(step: .name) - NavigationTitleView( title: "이름을 입력해 주세요", description: "원활한 서비스를 위해 이름을 입력해주세요" @@ -48,12 +43,7 @@ struct SignupNameView: View { isDisabled: viewModel.name.isEmpty, action: viewModel.nextButtonDidTap ) - .navigationBackButton(navigationTitle: "정보 입력") .kgBackground() .hideKeyboardWhenTap() - .navigate( - to: signupStudentIDFactory.makeView().eraseToAnyView(), - when: $viewModel.isNavigatedToStudentID - ) } } diff --git a/Projects/Feature/SignupFeature/Sources/UserInfo/Name/SignupNameViewModel.swift b/Projects/Feature/SignupFeature/Sources/UserInfo/Name/SignupNameViewModel.swift index 978ddef..5921ff9 100644 --- a/Projects/Feature/SignupFeature/Sources/UserInfo/Name/SignupNameViewModel.swift +++ b/Projects/Feature/SignupFeature/Sources/UserInfo/Name/SignupNameViewModel.swift @@ -4,9 +4,13 @@ import Combine final class SignupNameViewModel: BaseViewModel { @Published var name: String = "" - @Published var isNavigatedToStudentID: Bool = false + private let nextViewNavigateAction: () -> Void + + init(nextViewNavigateAction: @escaping () -> Void) { + self.nextViewNavigateAction = nextViewNavigateAction + } func nextButtonDidTap() { - self.isNavigatedToStudentID = true + self.nextViewNavigateAction() } } diff --git a/Projects/Feature/SignupFeature/Sources/UserInfo/StudentID/SignupStudentIDComponent.swift b/Projects/Feature/SignupFeature/Sources/UserInfo/StudentID/SignupStudentIDComponent.swift index 004b8f0..615ee67 100644 --- a/Projects/Feature/SignupFeature/Sources/UserInfo/StudentID/SignupStudentIDComponent.swift +++ b/Projects/Feature/SignupFeature/Sources/UserInfo/StudentID/SignupStudentIDComponent.swift @@ -7,10 +7,9 @@ public protocol SignupStudentIDDependency: Dependency { } public final class SignupStudentIDComponent: Component, SignupStudentIDFactory { - public func makeView() -> some View { + public func makeView(nextViewNavigateAction: @escaping () -> Void) -> some View { SignupStudentIDView( - viewModel: .init(), - signupGenderFactory: dependency.signupGenderFactory + viewModel: .init(nextViewNavigateAction: nextViewNavigateAction) ) } } diff --git a/Projects/Feature/SignupFeature/Sources/UserInfo/StudentID/SignupStudentIDView.swift b/Projects/Feature/SignupFeature/Sources/UserInfo/StudentID/SignupStudentIDView.swift index 98cb253..a5a2dd7 100644 --- a/Projects/Feature/SignupFeature/Sources/UserInfo/StudentID/SignupStudentIDView.swift +++ b/Projects/Feature/SignupFeature/Sources/UserInfo/StudentID/SignupStudentIDView.swift @@ -11,20 +11,14 @@ struct SignupStudentIDView: View { @FocusState private var focusField: FocusField? @StateObject var viewModel: SignupStudentIDViewModel - private let signupGenderFactory: any SignupGenderFactory - init( - viewModel: SignupStudentIDViewModel, - signupGenderFactory: any SignupGenderFactory + viewModel: SignupStudentIDViewModel ) { _viewModel = StateObject(wrappedValue: viewModel) - self.signupGenderFactory = signupGenderFactory } var body: some View { VStack(spacing: 0) { - SignupProgressView(step: .studentID) - NavigationTitleView( title: "학번을 입력해주세요", description: "원활한 서비스를 위해 학번을 입력해주세요" @@ -51,12 +45,7 @@ struct SignupStudentIDView: View { isDisabled: viewModel.studentID.isEmpty, action: viewModel.nextButtonDidTap ) - .navigationBackButton(navigationTitle: "정보 입력") .kgBackground() .hideKeyboardWhenTap() - .navigate( - to: signupGenderFactory.makeView().eraseToAnyView(), - when: $viewModel.isNavigatedToGender - ) } } diff --git a/Projects/Feature/SignupFeature/Sources/UserInfo/StudentID/SignupStudentIDViewModel.swift b/Projects/Feature/SignupFeature/Sources/UserInfo/StudentID/SignupStudentIDViewModel.swift index b69ba66..be95a89 100644 --- a/Projects/Feature/SignupFeature/Sources/UserInfo/StudentID/SignupStudentIDViewModel.swift +++ b/Projects/Feature/SignupFeature/Sources/UserInfo/StudentID/SignupStudentIDViewModel.swift @@ -4,9 +4,13 @@ import Combine final class SignupStudentIDViewModel: BaseViewModel { @Published var studentID: String = "" - @Published var isNavigatedToGender: Bool = false + private let nextViewNavigateAction: () -> Void + + init(nextViewNavigateAction: @escaping () -> Void) { + self.nextViewNavigateAction = nextViewNavigateAction + } func nextButtonDidTap() { - self.isNavigatedToGender = true + self.nextViewNavigateAction() } } diff --git a/Projects/Feature/SignupFeature/Sources/EmailAuthCodeVerify/SignupEmailAuthCodeVerifyComponent.swift b/Projects/Feature/SignupFeature/Sources/VerifyAuthCode/SignupVerifyAuthCodeComponent.swift similarity index 50% rename from Projects/Feature/SignupFeature/Sources/EmailAuthCodeVerify/SignupEmailAuthCodeVerifyComponent.swift rename to Projects/Feature/SignupFeature/Sources/VerifyAuthCode/SignupVerifyAuthCodeComponent.swift index 4ac5e83..9b5a6c2 100644 --- a/Projects/Feature/SignupFeature/Sources/EmailAuthCodeVerify/SignupEmailAuthCodeVerifyComponent.swift +++ b/Projects/Feature/SignupFeature/Sources/VerifyAuthCode/SignupVerifyAuthCodeComponent.swift @@ -2,14 +2,14 @@ import SwiftUI import NeedleFoundation import SignupFeatureInterface -public protocol SignupEmailAuthCodeVerifyDependency: Dependency { +public protocol SignupVerifyAuthCodeDependency: Dependency { var signupPasswordFactory: any SignupPasswordFactory { get } } -public final class SignupEmailAuthCodeVerifyComponent: Component, - SignupEmailAuthCodeVerifyFactory { +public final class SignupVerifyAuthCodeComponent: Component, + SignupVerifyAuthCodeFactory { public func makeView() -> some View { - SignupEmailAuthCodeVerifyView( + SignupVerifyAuthCodeView( viewModel: .init(), signupPasswordFactory: dependency.signupPasswordFactory ) diff --git a/Projects/Feature/SignupFeature/Sources/EmailAuthCodeVerify/SignupEmailAuthCodeVerifyView.swift b/Projects/Feature/SignupFeature/Sources/VerifyAuthCode/SignupVerifyAuthCodeView.swift similarity index 90% rename from Projects/Feature/SignupFeature/Sources/EmailAuthCodeVerify/SignupEmailAuthCodeVerifyView.swift rename to Projects/Feature/SignupFeature/Sources/VerifyAuthCode/SignupVerifyAuthCodeView.swift index 33ee125..1f8c31b 100644 --- a/Projects/Feature/SignupFeature/Sources/EmailAuthCodeVerify/SignupEmailAuthCodeVerifyView.swift +++ b/Projects/Feature/SignupFeature/Sources/VerifyAuthCode/SignupVerifyAuthCodeView.swift @@ -4,17 +4,17 @@ import BaseFeature import ViewUtil import SignupFeatureInterface -struct SignupEmailAuthCodeVerifyView: View { +struct SignupVerifyAuthCodeView: View { private enum FocusField { case authCode } @FocusState private var focusField: FocusField? - @StateObject var viewModel: SignupEmailAuthCodeVerifyViewModel + @StateObject var viewModel: SignupVerifyAuthCodeViewModel private let signupPasswordFactory: any SignupPasswordFactory init( - viewModel: SignupEmailAuthCodeVerifyViewModel, + viewModel: SignupVerifyAuthCodeViewModel, signupPasswordFactory: any SignupPasswordFactory ) { _viewModel = StateObject(wrappedValue: viewModel) @@ -52,7 +52,7 @@ struct SignupEmailAuthCodeVerifyView: View { Spacer() } .bottomButton( - text: "다음", + text: "인증", isEditing: focusField != nil, isDisabled: viewModel.authCode.count < 4, action: viewModel.nextButtonDidTap diff --git a/Projects/Feature/SignupFeature/Sources/EmailAuthCodeVerify/SignupEmailAuthCodeVerifyViewModel.swift b/Projects/Feature/SignupFeature/Sources/VerifyAuthCode/SignupVerifyAuthCodeViewModel.swift similarity index 78% rename from Projects/Feature/SignupFeature/Sources/EmailAuthCodeVerify/SignupEmailAuthCodeVerifyViewModel.swift rename to Projects/Feature/SignupFeature/Sources/VerifyAuthCode/SignupVerifyAuthCodeViewModel.swift index 355656a..afc0e0c 100644 --- a/Projects/Feature/SignupFeature/Sources/EmailAuthCodeVerify/SignupEmailAuthCodeVerifyViewModel.swift +++ b/Projects/Feature/SignupFeature/Sources/VerifyAuthCode/SignupVerifyAuthCodeViewModel.swift @@ -2,7 +2,7 @@ import BaseFeature import Foundation import Combine -final class SignupEmailAuthCodeVerifyViewModel: BaseViewModel { +final class SignupVerifyAuthCodeViewModel: BaseViewModel { @Published var authCode: String = "" @Published var isNavigatedToPassword: Bool = false diff --git a/Projects/Shared/ViewUtil/Sources/View+onSuccess.swift b/Projects/Shared/ViewUtil/Sources/View+onSuccess.swift new file mode 100644 index 0000000..75f369f --- /dev/null +++ b/Projects/Shared/ViewUtil/Sources/View+onSuccess.swift @@ -0,0 +1,11 @@ +import SwiftUI + +public extension View { + func onSuccess(of value: Bool, _ action: @escaping () -> Void) -> some View { + self.onChange(of: value) { changedValue in + if changedValue { + action() + } + } + } +} diff --git a/Projects/UserInterface/DesignSystem/Resources/Images/Images.xcassets/Man.imageset/Contents.json b/Projects/UserInterface/DesignSystem/Resources/Images/Images.xcassets/Man.imageset/Contents.json new file mode 100644 index 0000000..5cfbadd --- /dev/null +++ b/Projects/UserInterface/DesignSystem/Resources/Images/Images.xcassets/Man.imageset/Contents.json @@ -0,0 +1,24 @@ +{ + "images" : [ + { + "filename" : "Frame 9576.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/Projects/UserInterface/DesignSystem/Resources/Images/Images.xcassets/Man.imageset/Frame 9576.png b/Projects/UserInterface/DesignSystem/Resources/Images/Images.xcassets/Man.imageset/Frame 9576.png new file mode 100644 index 0000000..8c7fb41 Binary files /dev/null and b/Projects/UserInterface/DesignSystem/Resources/Images/Images.xcassets/Man.imageset/Frame 9576.png differ diff --git a/Projects/UserInterface/DesignSystem/Resources/Images/Images.xcassets/Woman.imageset/Contents.json b/Projects/UserInterface/DesignSystem/Resources/Images/Images.xcassets/Woman.imageset/Contents.json new file mode 100644 index 0000000..5d8bf3f --- /dev/null +++ b/Projects/UserInterface/DesignSystem/Resources/Images/Images.xcassets/Woman.imageset/Contents.json @@ -0,0 +1,24 @@ +{ + "images" : [ + { + "filename" : "Frame 9593.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/Projects/UserInterface/DesignSystem/Resources/Images/Images.xcassets/Woman.imageset/Frame 9593.png b/Projects/UserInterface/DesignSystem/Resources/Images/Images.xcassets/Woman.imageset/Frame 9593.png new file mode 100644 index 0000000..8ee17d7 Binary files /dev/null and b/Projects/UserInterface/DesignSystem/Resources/Images/Images.xcassets/Woman.imageset/Frame 9593.png differ diff --git a/Projects/Feature/SignupFeature/Sources/Components/BottomButton.swift b/Projects/UserInterface/DesignSystem/Sources/Extension/View+bottomButton.swift similarity index 95% rename from Projects/Feature/SignupFeature/Sources/Components/BottomButton.swift rename to Projects/UserInterface/DesignSystem/Sources/Extension/View+bottomButton.swift index 6551884..80eb562 100644 --- a/Projects/Feature/SignupFeature/Sources/Components/BottomButton.swift +++ b/Projects/UserInterface/DesignSystem/Sources/Extension/View+bottomButton.swift @@ -1,10 +1,9 @@ import SwiftUI -import DesignSystem /// Vstack으로 할 경우 버튼이 잘리는 문제가 발생 /// ZStack을 사용해 SuperView의 상단으로 띄움 -struct BottomButton: ViewModifier { +private struct BottomButton: ViewModifier { let text: String let isEditing: Bool let isDisabled: Bool diff --git a/Projects/UserInterface/DesignSystem/Sources/Extension/View+navigationBar.swift b/Projects/UserInterface/DesignSystem/Sources/Extension/View+navigationBar.swift index 79e5075..a0ff6c5 100644 --- a/Projects/UserInterface/DesignSystem/Sources/Extension/View+navigationBar.swift +++ b/Projects/UserInterface/DesignSystem/Sources/Extension/View+navigationBar.swift @@ -7,15 +7,18 @@ public struct NavigationBarModifier: ViewModifier { private let navigationTitle: String private let hasBackButton: Bool + private let backAction: (() -> Void)? private let rightIcon: (icon: Icons, action: () -> Void)? init( navigationTitle: String, hasBackButton: Bool = false, + backAction: (() -> Void)?, rightIcon: (Icons, () -> Void)? = nil ) { self.navigationTitle = navigationTitle self.hasBackButton = hasBackButton + self.backAction = backAction self.rightIcon = rightIcon } @@ -25,7 +28,11 @@ public struct NavigationBarModifier: ViewModifier { HStack { if hasBackButton { Button { - dismiss() + if let backAction { + backAction() + } else { + dismiss() + } } label: { KGIcon(.chevronLeft) .frame(28) @@ -63,24 +70,28 @@ public extension View { func navigationBar( navigationTitle: String = "", hasBackButton: Bool = false, + backAction: (() -> Void)? = nil, rightIcon: (Icons, () -> Void)? = nil ) -> some View { modifier( NavigationBarModifier( navigationTitle: navigationTitle, hasBackButton: hasBackButton, + backAction: backAction, rightIcon: rightIcon ) ) } func navigationBackButton( - navigationTitle: String = "" + navigationTitle: String = "", + backAction: (() -> Void)? = nil ) -> some View { modifier( NavigationBarModifier( navigationTitle: navigationTitle, - hasBackButton: true + hasBackButton: true, + backAction: backAction ) ) } diff --git a/Projects/UserInterface/DesignSystem/Sources/TextField/KGTextField.swift b/Projects/UserInterface/DesignSystem/Sources/TextField/KGTextField.swift index cea365c..9e19534 100644 --- a/Projects/UserInterface/DesignSystem/Sources/TextField/KGTextField.swift +++ b/Projects/UserInterface/DesignSystem/Sources/TextField/KGTextField.swift @@ -69,6 +69,9 @@ public struct KGTextField: View { lineWidth: 1 ) } + .onTapGesture { + self.isFocused = true + } if !description.isEmpty || isErrorAndNotEmpty { Text(isErrorAndNotEmpty ? errorMessage : description) diff --git a/Projects/UserInterface/DesignSystem/Sources/View/CheckLevelCellView.swift b/Projects/UserInterface/DesignSystem/Sources/View/CheckLevelCellView.swift index a1c5029..bf6d255 100644 --- a/Projects/UserInterface/DesignSystem/Sources/View/CheckLevelCellView.swift +++ b/Projects/UserInterface/DesignSystem/Sources/View/CheckLevelCellView.swift @@ -24,7 +24,7 @@ public struct LevelCheckCellView: View { .kgFont(.m1, weight: .semiBold, color: .Grays.white) Text(level.description) - .kgFont(.m3, weight: .regular, color: .Grays.gray500) + .kgFont(.label, weight: .regular, color: .Grays.gray500) .lineLimit(1) Spacer() diff --git a/Projects/Feature/SignupFeature/Sources/Components/NavigationTitleView.swift b/Projects/UserInterface/DesignSystem/Sources/View/NavigationTitleView.swift similarity index 59% rename from Projects/Feature/SignupFeature/Sources/Components/NavigationTitleView.swift rename to Projects/UserInterface/DesignSystem/Sources/View/NavigationTitleView.swift index 6d6d621..1690ccc 100644 --- a/Projects/Feature/SignupFeature/Sources/Components/NavigationTitleView.swift +++ b/Projects/UserInterface/DesignSystem/Sources/View/NavigationTitleView.swift @@ -1,11 +1,15 @@ import SwiftUI -import DesignSystem -struct NavigationTitleView: View { - let title: String - let description: String +public struct NavigationTitleView: View { + private let title: String + private let description: String - var body: some View { + public init(title: String, description: String) { + self.title = title + self.description = description + } + + public var body: some View { VStack(alignment: .leading, spacing: 8) { Text(title) .kgFont(.h3, weight: .semiBold, color: .white) diff --git a/Tuist/ProjectDescriptionHelpers/GenerateEnvironment.swift b/Tuist/ProjectDescriptionHelpers/GenerateEnvironment.swift index e282684..8de9e77 100644 --- a/Tuist/ProjectDescriptionHelpers/GenerateEnvironment.swift +++ b/Tuist/ProjectDescriptionHelpers/GenerateEnvironment.swift @@ -18,7 +18,7 @@ public extension GenerateEnvironment { return [.needle] case .dev: - return [.swiftLint, .needle] + return [.needle] } } var scripts: [TargetScript] {