From 4c2f234d5ec6ffd0c2ff8e326897868a2b5ff99a Mon Sep 17 00:00:00 2001 From: Jisu Kim <108998071+jisu15-kim@users.noreply.github.com> Date: Tue, 1 Oct 2024 02:13:50 +0900 Subject: [PATCH] =?UTF-8?q?[WEAV-70]=20=EC=A0=84=ED=99=94=EB=B2=88?= =?UTF-8?q?=ED=98=B8=20=EC=9D=B8=EC=A6=9D=20=EB=B7=B0=20UI=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20(#22)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [WEAV-70] Project Navigation 구조 변경 * [WEAV-70] 전화번호 인증 뷰 구현 * [WEAV-70] 인증코드 입력 뷰 구현 * [WEAV-70] 인증 완료 뷰 구현 * [WEAV-70] AI 리뷰 반영 --- Projects/App/Project.swift | 1 + Projects/App/Sources/ContentView.swift | 4 - .../Sources/Navigation/NavigationStack.swift | 40 +++++ .../Sources/Splash/SplashAnimatedView.swift | 7 +- Projects/App/Sources/ThreeDaysApp.swift | 20 ++- .../CommonKit/Sources/AppCoordinator.swift | 38 +++++ .../CommonKit/Sources/Path/PathTypes.swift | 42 +++++ .../Core/CoreKit/Sources/AppCoordinator.swift | 23 --- .../Core/CoreKit/Sources/FeatureTypes.swift | 21 --- Projects/Core/Project.swift | 10 +- .../ComponentsKit/Sources/temp.swift | 10 -- .../left_arrow.imageset/Contents.json | 23 +++ .../left_arrow.imageset/left_arrow.png | Bin 0 -> 333 bytes .../left_arrow.imageset/left_arrow@2x.png | Bin 0 -> 464 bytes .../left_arrow.imageset/left_arrow@3x.png | Bin 0 -> 624 bytes .../Sources/CTAButton/CTABottomButton.swift | 70 +++++--- .../DesignCore/Sources/LeftAlignText.swift | 24 +++ .../DesignCore/Sources/NavigationBar.swift | 36 +++++ Projects/DesignSystem/Project.swift | 13 +- .../Sources/DesignButtonView.swift | 3 +- .../DesignPreview/Sources/DesignPreview.swift | 21 ++- Projects/Features/Project.swift | 16 +- .../AuthAgreement/AuthAgreementView.swift | 52 ++++++ .../AuthPhoneInput/AuthPhoneInputView.swift | 100 ++++++++++++ .../AuthPhoneVerify/AuthPhoneVerifyView.swift | 152 ++++++++++++++++++ .../TargetConfiguration.swift | 11 +- Workspace.swift | 2 +- 27 files changed, 613 insertions(+), 126 deletions(-) create mode 100644 Projects/App/Sources/Navigation/NavigationStack.swift create mode 100644 Projects/Core/CommonKit/Sources/AppCoordinator.swift create mode 100644 Projects/Core/CommonKit/Sources/Path/PathTypes.swift delete mode 100644 Projects/Core/CoreKit/Sources/AppCoordinator.swift delete mode 100644 Projects/Core/CoreKit/Sources/FeatureTypes.swift delete mode 100644 Projects/DesignSystem/ComponentsKit/Sources/temp.swift create mode 100644 Projects/DesignSystem/DesignCore/Resources/Images/Images.xcassets/left_arrow.imageset/Contents.json create mode 100644 Projects/DesignSystem/DesignCore/Resources/Images/Images.xcassets/left_arrow.imageset/left_arrow.png create mode 100644 Projects/DesignSystem/DesignCore/Resources/Images/Images.xcassets/left_arrow.imageset/left_arrow@2x.png create mode 100644 Projects/DesignSystem/DesignCore/Resources/Images/Images.xcassets/left_arrow.imageset/left_arrow@3x.png create mode 100644 Projects/DesignSystem/DesignCore/Sources/LeftAlignText.swift create mode 100644 Projects/DesignSystem/DesignCore/Sources/NavigationBar.swift create mode 100644 Projects/Features/SignUp/Sources/AuthAgreement/AuthAgreementView.swift create mode 100644 Projects/Features/SignUp/Sources/AuthPhoneInput/AuthPhoneInputView.swift create mode 100644 Projects/Features/SignUp/Sources/AuthPhoneVerify/AuthPhoneVerifyView.swift diff --git a/Projects/App/Project.swift b/Projects/App/Project.swift index 500525f..9c1e442 100644 --- a/Projects/App/Project.swift +++ b/Projects/App/Project.swift @@ -18,6 +18,7 @@ let infoPlist = InfoPlist.extendingDefault( let appDependencies: [TargetDependency] = [ .project(target: .main), + .project(target: .signUp), .project(target: .designPreview) ] diff --git a/Projects/App/Sources/ContentView.swift b/Projects/App/Sources/ContentView.swift index 6825998..bf43ce0 100644 --- a/Projects/App/Sources/ContentView.swift +++ b/Projects/App/Sources/ContentView.swift @@ -1,7 +1,6 @@ import SwiftUI import CoreKit import NetworkKit -import ComponentsKit import DesignCore import Main @@ -16,9 +15,6 @@ public struct ContentView: View { .robotoSlab(size: 12) MainView() - - SampleComponent() - .foregroundStyle(DesignCore.Colors.red300) } } } diff --git a/Projects/App/Sources/Navigation/NavigationStack.swift b/Projects/App/Sources/Navigation/NavigationStack.swift new file mode 100644 index 0000000..5dfb3b8 --- /dev/null +++ b/Projects/App/Sources/Navigation/NavigationStack.swift @@ -0,0 +1,40 @@ +// +// NavigationStack.swift +// three-days-dev +// +// Created by 김지수 on 9/30/24. +// Copyright © 2024 com.weave. All rights reserved. +// + +import SwiftUI +import CommonKit +import SignUp +import DesignPreview + +extension PathType { + @ViewBuilder + var view: some View { + switch self { + case .designPreview: + DesignPreviewView() + case .main: + SplashAnimatedView() + case .signUp(let subView): + switch subView { + case .authPhoneInput: + AuthPhoneInputView() + case .authPhoneVerify: + AuthPhoneVerifyView() + case .authAgreement: + AuthAgreementView() + } + } + } +} + +extension AppCoordinator { + @ViewBuilder + var rootView: some View { + navigationStack[0].view + } +} diff --git a/Projects/App/Sources/Splash/SplashAnimatedView.swift b/Projects/App/Sources/Splash/SplashAnimatedView.swift index f316a42..0ec826f 100644 --- a/Projects/App/Sources/Splash/SplashAnimatedView.swift +++ b/Projects/App/Sources/Splash/SplashAnimatedView.swift @@ -8,6 +8,8 @@ import SwiftUI import DesignCore +import CoreKit +import CommonKit enum SplashAnimationStep: CaseIterable { case first, second, third, fourth, fifth, sixth @@ -113,7 +115,9 @@ struct SplashAnimatedView: View { isActive: true, isShowLetter: $showLetterAnimation ) { - + AppCoordinator.shared.navigationStack.append( + .signUp(.authPhoneInput) + ) } .frame(height: 70) .padding(.horizontal, 50) @@ -121,6 +125,7 @@ struct SplashAnimatedView: View { Spacer() } + .toolbar(.hidden, for: .navigationBar) .textureBackground(animationStep.color) .task { await runSingleCycle() diff --git a/Projects/App/Sources/ThreeDaysApp.swift b/Projects/App/Sources/ThreeDaysApp.swift index a62da7d..80ad231 100644 --- a/Projects/App/Sources/ThreeDaysApp.swift +++ b/Projects/App/Sources/ThreeDaysApp.swift @@ -1,12 +1,12 @@ import SwiftUI import DesignCore import DesignPreview -import CoreKit +import CommonKit import Main @main struct ThreeDaysApp: App { - let coordinator = AppCoordinator.shared + @StateObject var coordinator = AppCoordinator.shared var body: some Scene { WindowGroup { @@ -23,11 +23,15 @@ struct ThreeDaysApp: App { @ViewBuilder var rootView: some View { - switch coordinator.rootView { - case .designPreview: - DesignPreviewView() - case .main: - SplashAnimatedView() + NavigationStack( + path: $coordinator.navigationStack + ) { + coordinator.rootView + .navigationDestination( + for: PathType.self + ) { feature in + feature.view + } } } @@ -35,7 +39,7 @@ struct ThreeDaysApp: App { @ViewBuilder var debugMenuPicker: some View { Menu("🚀 개발모드") { - ForEach(FeatureType.allCases, id: \.self) { feature in + ForEach(PathType.debugPreviewTypes, id: \.self) { feature in Button(feature.name) { coordinator.changeRootView(feature) } diff --git a/Projects/Core/CommonKit/Sources/AppCoordinator.swift b/Projects/Core/CommonKit/Sources/AppCoordinator.swift new file mode 100644 index 0000000..d804c3f --- /dev/null +++ b/Projects/Core/CommonKit/Sources/AppCoordinator.swift @@ -0,0 +1,38 @@ +// +// AppCoordinator.swift +// CommonKit +// +// Created by 김지수 on 9/18/24. +// Copyright © 2024 com.weave. All rights reserved. +// + +import SwiftUI + +public final class AppCoordinator: ObservableObject { + //MARK: - Lifecycle + public static var shared = AppCoordinator() + private init() {} + + //MARK: - Properties + @Published public var navigationStack: [PathType] = [.main] + + //MARK: - Methods + public func changeRootView(_ path: PathType) { + Task { + await MainActor.run { + navigationStack = [path] + } + } + } + + @MainActor + public func push(_ path: PathType) { + navigationStack.append(path) + } + + @MainActor + public func pop() { + guard navigationStack.count > 1 else { return } + navigationStack.removeLast() + } +} diff --git a/Projects/Core/CommonKit/Sources/Path/PathTypes.swift b/Projects/Core/CommonKit/Sources/Path/PathTypes.swift new file mode 100644 index 0000000..2da273f --- /dev/null +++ b/Projects/Core/CommonKit/Sources/Path/PathTypes.swift @@ -0,0 +1,42 @@ +// +// PathTypes.swift +// CommonKit +// +// Created by 김지수 on 9/18/24. +// Copyright © 2024 com.weave. All rights reserved. +// + +import Foundation + +public enum PathType: Hashable { + case designPreview + case main + case signUp(SignUpSubViewType) + + #if STAGING || DEBUG + public static var debugPreviewTypes: [PathType] = [ + .designPreview, + .main, + .signUp(.authPhoneInput) + ] + #endif + + public var name: String { + switch self { + case .designPreview: return "Design Preview" + case .main: return "메인" + case .signUp(let subType): + switch subType { + case .authPhoneInput: return "전화번호 입력" + case .authPhoneVerify: return "전화번호 인증" + case .authAgreement: return "이용 약관" + } + } + } +} + +public enum SignUpSubViewType: Hashable { + case authPhoneInput + case authPhoneVerify + case authAgreement +} diff --git a/Projects/Core/CoreKit/Sources/AppCoordinator.swift b/Projects/Core/CoreKit/Sources/AppCoordinator.swift deleted file mode 100644 index 48beccd..0000000 --- a/Projects/Core/CoreKit/Sources/AppCoordinator.swift +++ /dev/null @@ -1,23 +0,0 @@ -// -// AppCoordinator.swift -// CoreKit -// -// Created by 김지수 on 9/18/24. -// Copyright © 2024 com.weave. All rights reserved. -// - -import SwiftUI - -@Observable public final class AppCoordinator { - //MARK: - Lifecycle - public static var shared = AppCoordinator() - private init() {} - - //MARK: - Properties - public private(set) var rootView = FeatureType.main - - //MARK: - Methods - public func changeRootView(_ feature: FeatureType) { - rootView = feature - } -} diff --git a/Projects/Core/CoreKit/Sources/FeatureTypes.swift b/Projects/Core/CoreKit/Sources/FeatureTypes.swift deleted file mode 100644 index 14ad318..0000000 --- a/Projects/Core/CoreKit/Sources/FeatureTypes.swift +++ /dev/null @@ -1,21 +0,0 @@ -// -// FeatureTypes.swift -// CoreKit -// -// Created by 김지수 on 9/18/24. -// Copyright © 2024 com.weave. All rights reserved. -// - -import Foundation - -public enum FeatureType: CaseIterable { - case designPreview - case main - - public var name: String { - switch self { - case .designPreview: return "Design Preview" - case .main: return "메인" - } - } -} diff --git a/Projects/Core/Project.swift b/Projects/Core/Project.swift index e602a71..68f40ea 100644 --- a/Projects/Core/Project.swift +++ b/Projects/Core/Project.swift @@ -12,10 +12,16 @@ let project: Project = .make( .make(target: .coreKit), .make(target: .model), .make( - target: .networkKit, + target: .commonKit, dependencies: [ .target(name: .coreKit), - .target(name: .model), + .target(name: .model) + ] + ), + .make( + target: .networkKit, + dependencies: [ + .target(name: .commonKit), .external(.alamofire), .external(.openapiGenerated) ] diff --git a/Projects/DesignSystem/ComponentsKit/Sources/temp.swift b/Projects/DesignSystem/ComponentsKit/Sources/temp.swift deleted file mode 100644 index a5581a4..0000000 --- a/Projects/DesignSystem/ComponentsKit/Sources/temp.swift +++ /dev/null @@ -1,10 +0,0 @@ -import SwiftUI - -public struct SampleComponent: View { - - public init() {} - - public var body: some View { - Text("ThisIsComponentKit Sample View") - } -} diff --git a/Projects/DesignSystem/DesignCore/Resources/Images/Images.xcassets/left_arrow.imageset/Contents.json b/Projects/DesignSystem/DesignCore/Resources/Images/Images.xcassets/left_arrow.imageset/Contents.json new file mode 100644 index 0000000..4f06b0f --- /dev/null +++ b/Projects/DesignSystem/DesignCore/Resources/Images/Images.xcassets/left_arrow.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "left_arrow.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "left_arrow@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "left_arrow@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Projects/DesignSystem/DesignCore/Resources/Images/Images.xcassets/left_arrow.imageset/left_arrow.png b/Projects/DesignSystem/DesignCore/Resources/Images/Images.xcassets/left_arrow.imageset/left_arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..8b56187baef4aafe57c7d1eeb69620098b6a1096 GIT binary patch literal 333 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`GjoCO|{#S9GG!XV7ZFl!D-1!HlL zyA#8@b22Z19F}xPUq=Rpjs4tz5?O)#N1iT@Ar*{oryS&MG7xEtf6KJ)!9{KswE`CL zh6gNt40#9GLL-hWz89r-z;VGoHJOf(jE%=;e29&^Z1Ye4#cTczAF@~Y7RwxWKmU1K zXlUW(^sr^U8uOA)aw_uZ7#O`cV!U-)klX<#qpKgZ;-)>7)KR!EB5zut`0v_*sXMMI zbS*kk7F2b`H8AIwbhGB7DeDr|ek2{XjaB})h{yYuq*~8L?lUgclUoJC?|+~Alka?j zd|TpKOYsM%4lF&HA-20WbcEP)C8PsH2T2LJK7XO&2iV;dHB*tI-Q zS|M8_&wrAUJ^+Fs2!g;wp644nU?c<+S(a`4zW<_=rs)HiU=g73>QelE4uW6{CaeqW zhbNT44op}TI2K-=J1`Nz5+}mnR#nx22`d7w!s|U)h|BPL7Z%_;yxxbI_z+(2#0-21 zulK?bpTg_iCd0mk*ZZdq`WRkkm@;%I{0e+TQB;&=`EqJ#v`YNc;R5S(-F4m5*brNt zw!xGVI4Yq2-}EDoSQ!eho+U>2n%u;Ny6pG*T^NQ=Z{ld`VB5CuaU7HI&7ZHYhGDo* zlH{#vnol@jvH;44oyOKrR5{2^>2+u(Z5!1*eeACL zo5>{OX!9o|Gw-bc#u#IaF~%5U3^NHZF&>YnX`0^SW#@1>wWKbyDx|=g~%ulp60)Qj*(`-K-56z;+%AVfoOp$;?Nzm1_q)8x*$%0 z$0INh{4I82oVzT`3Sb~;AZc+(us{;ykRXAi#v$GVNsdFj29h3!cnhQ<4)GF5OB~`n zkfu1qX&`NJh_gT%;}9o-w8kN=18I&!S_RS`hcpgU6^EHUeo$GS=g&}6qcv$-U_Y%P zOL2|IZNJ}7w%hHx&L1{fFvH!42axFrYHGToxQBnDQ6T(!eSl0k)YRx1wApM5`aa|3 z5Cp&vl: View { private let title: String private let backgroundStyle: BackgroundStyle private let titleColor: Color = .white private let isActive: Bool - private let keyboardShown: Bool private var handler: () -> Void + @State private var cancellables: Set = [] + @State private var keyboardHeight: CGFloat = 0 + public init( title: String, backgroundStyle: BackgroundStyle = DesignCore.Colors.grey500, isActive: Bool = true, - keyboardShown: Bool = false, handler: @escaping () -> Void ) { self.title = title self.backgroundStyle = backgroundStyle self.isActive = isActive - self.keyboardShown = keyboardShown self.handler = handler } @@ -38,28 +39,49 @@ public struct CTABottomButton: View { } public var body: some View { - Button(action: { - handler() - }, label: { + GeometryReader { geometry in VStack(spacing: 0) { - ZStack { - Rectangle() - .foregroundStyle(buttonBackgroundColor) - .frame(height: 68) - .cornerRadius( - 20, - corners: [.topLeft, .topRight] - ) - Text(title) - .foregroundStyle(titleColor) - .typography(.semibold_18) - } - Rectangle() - .foregroundStyle(buttonBackgroundColor) - .frame(height: keyboardShown ? 0 : Device.bottomInset) + Spacer() + Button(action: { + handler() + }, label: { + VStack(spacing: 0) { + ZStack { + Rectangle() + .foregroundStyle(buttonBackgroundColor) + .frame(height: 68) + .cornerRadius( + 20, + corners: [.topLeft, .topRight] + ) + Text(title) + .foregroundStyle(titleColor) + .typography(.semibold_18) + } + Rectangle() + .foregroundStyle(buttonBackgroundColor) + .frame(height: max(Device.bottomInset, keyboardHeight)) + } + }) + .disabled(!isActive) } - }) - .disabled(!isActive) - .ignoresSafeArea() + .edgesIgnoringSafeArea(.bottom) + .offset(y: -min(keyboardHeight, geometry.safeAreaInsets.bottom)) + .animation(.snappy(duration: 0.35), value: keyboardHeight) + } + .onAppear { + Publishers.Merge( + NotificationCenter.default.publisher(for: UIResponder.keyboardWillShowNotification) + .compactMap { $0.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect } + .map { $0.height }, + NotificationCenter.default.publisher(for: UIResponder.keyboardWillHideNotification) + .map { _ in CGFloat(0) } + ) + .assign(to: \.keyboardHeight, on: self) + .store(in: &cancellables) + } + .onDisappear { + cancellables.removeAll() + } } } diff --git a/Projects/DesignSystem/DesignCore/Sources/LeftAlignText.swift b/Projects/DesignSystem/DesignCore/Sources/LeftAlignText.swift new file mode 100644 index 0000000..49f47ed --- /dev/null +++ b/Projects/DesignSystem/DesignCore/Sources/LeftAlignText.swift @@ -0,0 +1,24 @@ +// +// LeftAlignText.swift +// DesignCore +// +// Created by 김지수 on 10/1/24. +// Copyright © 2024 com.weave. All rights reserved. +// + +import SwiftUI + +public struct LeftAlignText: View { + private let text: String + + public init(_ text: String) { + self.text = text + } + + public var body: some View { + HStack { + Text(text) + Spacer() + } + } +} diff --git a/Projects/DesignSystem/DesignCore/Sources/NavigationBar.swift b/Projects/DesignSystem/DesignCore/Sources/NavigationBar.swift new file mode 100644 index 0000000..983932d --- /dev/null +++ b/Projects/DesignSystem/DesignCore/Sources/NavigationBar.swift @@ -0,0 +1,36 @@ +// +// NavigationBar.swift +// DesignCore +// +// Created by 김지수 on 10/1/24. +// Copyright © 2024 com.weave. All rights reserved. +// + +import SwiftUI + +private struct NavigationBarViewModifier: ViewModifier { + var handler: () -> Void + + func body(content: Content) -> some View { + content + .navigationBarBackButtonHidden() + .toolbar(.visible, for: .navigationBar) + .toolbarTitleDisplayMode(.inline) + .toolbar { + ToolbarItem(placement: .topBarLeading) { + Button { + handler() + } label: { + DesignCore.Images.leftArrow.image + } + + } + } + } +} + +public extension View { + func setNavigation(handler: @escaping () -> Void) -> some View { + return modifier(NavigationBarViewModifier(handler: handler)) + } +} diff --git a/Projects/DesignSystem/Project.swift b/Projects/DesignSystem/Project.swift index f4e4ace..293ad2e 100644 --- a/Projects/DesignSystem/Project.swift +++ b/Projects/DesignSystem/Project.swift @@ -10,7 +10,7 @@ let project: Project = .make( ]), targets: [ .make( - target: .DesignCore, + target: .designCore, product: .framework, useResource: true, dependencies: [ @@ -18,16 +18,9 @@ let project: Project = .make( ] ), .makeUnitTest( - target: .DesignCore, + target: .designCore, dependencies: [ - .target(name: .DesignCore) - ] - ), - .make( - target: .componentsKit, - product: .framework, - dependencies: [ - .target(name: .DesignCore) + .target(name: .designCore) ] ) ] diff --git a/Projects/Features/DesignPreview/Sources/DesignButtonView.swift b/Projects/Features/DesignPreview/Sources/DesignButtonView.swift index b7387b1..6cba3a1 100644 --- a/Projects/Features/DesignPreview/Sources/DesignButtonView.swift +++ b/Projects/Features/DesignPreview/Sources/DesignButtonView.swift @@ -36,8 +36,7 @@ struct DesignButtonView: View { CTABottomButton( title: "다음", backgroundStyle: LinearGradient.gradientA, - isActive: true, - keyboardShown: isTextFieldFocused + isActive: true ) { if isTextFieldFocused { isTextFieldFocused.toggle() diff --git a/Projects/Features/DesignPreview/Sources/DesignPreview.swift b/Projects/Features/DesignPreview/Sources/DesignPreview.swift index a3e6099..5e89c96 100644 --- a/Projects/Features/DesignPreview/Sources/DesignPreview.swift +++ b/Projects/Features/DesignPreview/Sources/DesignPreview.swift @@ -59,20 +59,19 @@ public struct DesignPreviewView: View { public init() {} public var body: some View { - NavigationStack { - List(PreviewTypes.allCases, id: \.self) { type in - NavigationLink { - type.nextView() - } label: { - HStack { - Text(type.name) - Spacer() - } + List(PreviewTypes.allCases, id: \.self) { type in + NavigationLink { + type.nextView() + } label: { + HStack { + Text(type.name) + Spacer() } } - .navigationTitle("3days Design System") - .toolbarTitleDisplayMode(.inline) } + .navigationBarBackButtonHidden() + .navigationTitle("3days Design System") + .toolbarTitleDisplayMode(.inline) } } diff --git a/Projects/Features/Project.swift b/Projects/Features/Project.swift index b9d19d6..86af520 100644 --- a/Projects/Features/Project.swift +++ b/Projects/Features/Project.swift @@ -8,15 +8,23 @@ let project: Project = .make( target: .designPreview, dependencies: [ .project(target: .coreKit), - .project(target: .componentsKit) + .project(target: .designCore) + ] + ), + .make( + target: .signUp, + dependencies: [ + .project(target: .commonKit), + .project(target: .designCore), + .project(target: .networkKit) ] ), .make( target: .main, dependencies: [ - .project(target: .coreKit), - .project(target: .networkKit), - .project(target: .componentsKit) + .project(target: .commonKit), + .project(target: .designCore), + .project(target: .networkKit) ] ) ] diff --git a/Projects/Features/SignUp/Sources/AuthAgreement/AuthAgreementView.swift b/Projects/Features/SignUp/Sources/AuthAgreement/AuthAgreementView.swift new file mode 100644 index 0000000..12100ab --- /dev/null +++ b/Projects/Features/SignUp/Sources/AuthAgreement/AuthAgreementView.swift @@ -0,0 +1,52 @@ +// +// AuthAgreementView.swift +// DesignPreview +// +// Created by 김지수 on 10/1/24. +// Copyright © 2024 com.weave. All rights reserved. +// + +import SwiftUI +import CoreKit +import DesignCore +import CommonKit + +public struct AuthAgreementView: View { + + @State var isShowAlert = false + + public init() {} + + public var body: some View { + VStack(spacing: 30) { + LeftAlignText("인증이 완료되었어요!\n이용약관에 동의하면 끝나요") + .typography(.semibold_24) + .padding(.horizontal, 26) + + Text("TBD") + .typography(.semibold_24) + .foregroundStyle(DesignCore.Colors.grey200) + + Spacer() + + CTABottomButton(title: "다음") { + isShowAlert = true + } + .alert("끝!!", isPresented: $isShowAlert) { + + } + } + .ignoresSafeArea(.all) + .padding(.top, 14) + .textureBackground() + .setNavigation { + AppCoordinator.shared.pop() + } + } +} + +#Preview { + NavigationView { + AuthAgreementView() + } +} diff --git a/Projects/Features/SignUp/Sources/AuthPhoneInput/AuthPhoneInputView.swift b/Projects/Features/SignUp/Sources/AuthPhoneInput/AuthPhoneInputView.swift new file mode 100644 index 0000000..daaccae --- /dev/null +++ b/Projects/Features/SignUp/Sources/AuthPhoneInput/AuthPhoneInputView.swift @@ -0,0 +1,100 @@ +// +// AuthPhoneInputView.swift +// SignUp +// +// Created by 김지수 on 9/30/24. +// Copyright © 2024 com.weave. All rights reserved. +// + +import SwiftUI +import CoreKit +import DesignCore +import CommonKit + +public struct AuthPhoneInputView: View { + + @State var phoneTextInput = String() + @State var isPhoneValidated = false + + public init() {} + + public var body: some View { + VStack(alignment: .leading, spacing: 30) { + LeftAlignText("전화번호를 입력해주세요") + .typography(.semibold_24) + .padding(.horizontal, 26) + + PhoneTextInputView( + phoneTextInput: $phoneTextInput, + isPhoneValidated: $isPhoneValidated + ) + .padding(.horizontal, 26) + + Spacer() + + CTABottomButton(title: "다음", isActive: isPhoneValidated) { + AppCoordinator.shared.push( + .signUp( + .authPhoneVerify + ) + ) + } + } + .ignoresSafeArea(.all) + .padding(.top, 14) + .textureBackground() + .setNavigation { + AppCoordinator.shared.pop() + } + } +} + +private struct PhoneTextInputView: View { + @Binding var phoneTextInput: String + @Binding var isPhoneValidated: Bool + + var phoneRightIcon: TextInputRightIconModel? { + if isPhoneValidated { + return .init( + icon: DesignCore.Images.checkBold.image, + backgroundColor: Color(hex: 0x2DE76B) + ) + } + return nil + } + + var body: some View { + TextInput( + placeholder: "전화번호를 입력하세요", + backgroundColor: .white, + text: $phoneTextInput, + keyboardType: .phonePad, + leftView: { + HStack(spacing: 6) { + DesignCore.Images.flagKorea.image + Text("+82") + .foregroundStyle(DesignCore.Colors.grey200) + .typography(.medium_16) + Rectangle() + .frame( + width: 1, + height: 11 + ) + .foregroundStyle(Color(hex: 0xE8E6E4)) + } + }, + rightIcon: phoneRightIcon + ) + .onChange(of: phoneTextInput) { + let editedPhone = phoneTextInput.formattedPhoneNumber() + phoneTextInput = editedPhone + isPhoneValidated = editedPhone.isValidPhoneNumber() + } + } +} + +#Preview { + NavigationView { + AuthPhoneInputView() + } +} diff --git a/Projects/Features/SignUp/Sources/AuthPhoneVerify/AuthPhoneVerifyView.swift b/Projects/Features/SignUp/Sources/AuthPhoneVerify/AuthPhoneVerifyView.swift new file mode 100644 index 0000000..210f3ef --- /dev/null +++ b/Projects/Features/SignUp/Sources/AuthPhoneVerify/AuthPhoneVerifyView.swift @@ -0,0 +1,152 @@ +// +// AuthPhoneVerifyView.swift +// DesignPreview +// +// Created by 김지수 on 10/1/24. +// Copyright © 2024 com.weave. All rights reserved. +// + +import SwiftUI +import CoreKit +import DesignCore +import CommonKit + +public struct AuthPhoneVerifyView: View { + + @State var verifyCode = "" + @State var errorMessage: String? = "에러에여" + @FocusState private var verifyTextFieldFocused: Bool + + public init() {} + + public var body: some View { + VStack(spacing: 30) { + LeftAlignText("인증코드를 입력해주세요") + .typography(.semibold_24) + + VerifyCodeInputView( + verifyCode: $verifyCode, + errorMessage: $errorMessage, + focused: _verifyTextFieldFocused + ) + + Button( + action: { + + }, + label: { + Text("010-1234-1234로 코드 재전송") + .typography(.regular_14) + .padding(.horizontal, 16) + .padding(.vertical, 8) + .background { + RoundedRectangle(cornerRadius: 12) + .foregroundStyle( + Color( + hex: 0x454545, + opacity: 0.06 + ) + ) + } + } + ) + .tint(DesignCore.Colors.grey300) + + Spacer() + } + .onChange(of: verifyCode) { + if verifyCode.count == 6 { + AppCoordinator.shared.push(.signUp(.authAgreement)) + } + } + .onAppear { + verifyTextFieldFocused = true + } + .ignoresSafeArea(.all) + .padding(.horizontal, 26) + .padding(.top, 14) + .textureBackground() + .setNavigation { + AppCoordinator.shared.pop() + } + } +} + +#Preview { + NavigationView { + AuthPhoneVerifyView() + } +} + +public struct VerifyCodeInputView: View { + + @Binding var verifyCode: String + @Binding var errorMessage: String? + var verifyCodeMaxCount: Int + @FocusState private var isTextFieldFocused: Bool + + public init( + verifyCode: Binding, + errorMessage: Binding, + verifyCodeMaxCount: Int = 6, + focused: FocusState + ) { + self._verifyCode = verifyCode + self._errorMessage = errorMessage + self.verifyCodeMaxCount = verifyCodeMaxCount + self._isTextFieldFocused = focused + } + + public var body: some View { + ZStack(alignment: .center) { + textBoxHStackView + clearTextFieldView + } + } + + private var textBoxHStackView: some View { + HStack(spacing: 8) { + ForEach(0 ..< verifyCodeMaxCount, id: \.self) { index in + let text = getCharFromString(index: index) + getSingleTextBox(text: text) + } + } + } + + @ViewBuilder + private func getSingleTextBox(text: String) -> some View { + ZStack { + RoundedRectangle(cornerRadius: 10) + .foregroundStyle(.white) + .clipShape(RoundedRectangle(cornerRadius: 10)) + Text(text) + .pretendard(weight: ._600, size: 32) + } + .frame(height: 72) + } + + private var clearTextFieldView: some View { + TextField("", text: $verifyCode) + .focused($isTextFieldFocused) + .accentColor(.clear) + .foregroundColor(.clear) + .onChange(of: verifyCode) { oldValue, newValue in + if newValue.count == 6 { + isTextFieldFocused = false + } + } + .keyboardType(.numberPad) + .onTapGesture { + verifyCode = "" + isTextFieldFocused = true + } + } + + private func getCharFromString(index: Int) -> String { + if index >= verifyCode.count { + return "" + } + let numArray = verifyCode.compactMap { Int(String($0)) } + return String(numArray[index]) + } +} diff --git a/Tuist/ProjectDescriptionHelpers/TargetConfiguration.swift b/Tuist/ProjectDescriptionHelpers/TargetConfiguration.swift index 1561bb4..78648d1 100644 --- a/Tuist/ProjectDescriptionHelpers/TargetConfiguration.swift +++ b/Tuist/ProjectDescriptionHelpers/TargetConfiguration.swift @@ -12,12 +12,13 @@ public enum TargetName: String { case prodApp = "ProdApp" case coreKit = "CoreKit" case model = "Model" + case commonKit = "CommonKit" case networkKit = "NetworkKit" - case DesignCore = "DesignCore" - case componentsKit = "ComponentsKit" + case designCore = "DesignCore" //MARK: - Features case designPreview = "DesignPreview" + case signUp = "SignUp" case main = "Main" } @@ -26,11 +27,11 @@ public extension TargetName { switch self { case .devApp, .prodApp: return .app - case .coreKit, .networkKit, .model: + case .coreKit, .networkKit, .model, .commonKit: return .core - case .DesignCore, .componentsKit: + case .designCore: return .designSystem - case .main, .designPreview: + case .main, .designPreview, .signUp: return .feature } } diff --git a/Workspace.swift b/Workspace.swift index a884388..acbc1ad 100644 --- a/Workspace.swift +++ b/Workspace.swift @@ -52,7 +52,7 @@ let workspace = Workspace( .testableTarget( target: .project( path: "./\(ProjectPath.designSystem.rawValue)", - target: TargetName.DesignCore.unitTestName + target: TargetName.designCore.unitTestName ) ) ]