From 0d51ec0aaba665be92b8c65259891adc9cd4aa50 Mon Sep 17 00:00:00 2001 From: Jisu Kim Date: Sat, 2 Nov 2024 18:10:11 +0900 Subject: [PATCH 1/2] =?UTF-8?q?[WEAV-118]=20HomeMainTabView=20=EA=B5=AC?= =?UTF-8?q?=EC=A1=B0=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/Navigation/NavigationStack.swift | 2 +- .../Sources/HomeMain/HomeMainIntent.swift | 55 +++++++++++ .../Home/Sources/HomeMain/HomeMainModel.swift | 84 ++++++++++++++++ .../Home/Sources/HomeMain/HomeMainTab.swift | 25 +++++ .../Home/Sources/HomeMain/HomeMainView.swift | 95 +++++++++++++++++++ Projects/Features/Home/Sources/HomeView.swift | 18 ---- 6 files changed, 260 insertions(+), 19 deletions(-) create mode 100644 Projects/Features/Home/Sources/HomeMain/HomeMainIntent.swift create mode 100644 Projects/Features/Home/Sources/HomeMain/HomeMainModel.swift create mode 100644 Projects/Features/Home/Sources/HomeMain/HomeMainTab.swift create mode 100644 Projects/Features/Home/Sources/HomeMain/HomeMainView.swift delete mode 100644 Projects/Features/Home/Sources/HomeView.swift diff --git a/Projects/App/Sources/Navigation/NavigationStack.swift b/Projects/App/Sources/Navigation/NavigationStack.swift index 6d07639..d652077 100644 --- a/Projects/App/Sources/Navigation/NavigationStack.swift +++ b/Projects/App/Sources/Navigation/NavigationStack.swift @@ -29,7 +29,7 @@ extension PathType { // features case .home: - HomeView() + HomeMainView() case .signUp(let subView): switch subView { diff --git a/Projects/Features/Home/Sources/HomeMain/HomeMainIntent.swift b/Projects/Features/Home/Sources/HomeMain/HomeMainIntent.swift new file mode 100644 index 0000000..e2d1f82 --- /dev/null +++ b/Projects/Features/Home/Sources/HomeMain/HomeMainIntent.swift @@ -0,0 +1,55 @@ +// +// HomeMainIntent.swift +// DesignPreview +// +// Created by 김지수 on 11/2/24. +// Copyright © 2024 com.weave. All rights reserved. +// + +import Foundation +import CommonKit +import CoreKit + +//MARK: - Intent +class HomeMainIntent { + private weak var model: HomeMainModelActionable? + private let input: DataModel + + // MARK: Life cycle + init( + model: HomeMainModelActionable, + input: DataModel + ) { + self.input = input + self.model = model + } +} + +//MARK: - Intentable +extension HomeMainIntent { + protocol Intentable { + // content + func onTapTab(_ tab: HomeMainTab) + func onTapNextButton() + + // default + func onAppear() + func task() async + } + + struct DataModel {} +} + +//MARK: - Intentable +extension HomeMainIntent: HomeMainIntent.Intentable { + // default + func onTapTab(_ tab: HomeMainTab) { + model?.setSelectedTab(tab: tab) + } + func onAppear() {} + + func task() async {} + + // content + func onTapNextButton() {} +} diff --git a/Projects/Features/Home/Sources/HomeMain/HomeMainModel.swift b/Projects/Features/Home/Sources/HomeMain/HomeMainModel.swift new file mode 100644 index 0000000..d4eb8d4 --- /dev/null +++ b/Projects/Features/Home/Sources/HomeMain/HomeMainModel.swift @@ -0,0 +1,84 @@ +// +// HomeMainModel.swift +// DesignPreview +// +// Created by 김지수 on 11/2/24. +// Copyright © 2024 com.weave. All rights reserved. +// + +import Foundation +import CommonKit +import CoreKit + +final class HomeMainModel: ObservableObject { + + //MARK: Stateful + protocol Stateful { + // content + var selectedTab: HomeMainTab { get set } + var isValidated: Bool { get } + + // default + var isLoading: Bool { get } + + // error + var showErrorView: ErrorModel? { get } + var showErrorAlert: ErrorModel? { get } + } + + //MARK: State Properties + // content + @Published var selectedTab: HomeMainTab = .home + @Published var isValidated: Bool = false + + // default + @Published var isLoading: Bool = false + + // error + @Published var showErrorView: ErrorModel? + @Published var showErrorAlert: ErrorModel? +} + +extension HomeMainModel: HomeMainModel.Stateful {} + +//MARK: - Actionable +protocol HomeMainModelActionable: AnyObject { + // content + func setSelectedTab(tab: HomeMainTab) + func setValidation(value: Bool) + + // default + func setLoading(status: Bool) + + // error + func showErrorView(error: ErrorModel) + func showErrorAlert(error: ErrorModel) + func resetError() +} + +extension HomeMainModel: HomeMainModelActionable { + // content + func setSelectedTab(tab: HomeMainTab) { + self.selectedTab = tab + } + func setValidation(value: Bool) { + isValidated = value + } + + // default + func setLoading(status: Bool) { + isLoading = status + } + + // error + func showErrorView(error: ErrorModel) { + showErrorView = error + } + func showErrorAlert(error: ErrorModel) { + showErrorAlert = error + } + func resetError() { + showErrorView = nil + showErrorAlert = nil + } +} diff --git a/Projects/Features/Home/Sources/HomeMain/HomeMainTab.swift b/Projects/Features/Home/Sources/HomeMain/HomeMainTab.swift new file mode 100644 index 0000000..6ae8f94 --- /dev/null +++ b/Projects/Features/Home/Sources/HomeMain/HomeMainTab.swift @@ -0,0 +1,25 @@ +// +// HomeMainTab.swift +// Home +// +// Created by 김지수 on 11/2/24. +// Copyright © 2024 com.weave. All rights reserved. +// + +import Foundation + +enum HomeMainTab: CaseIterable { + case home + case profile +} + +extension HomeMainTab { + var title: String { + switch self { + case .home: + return "Home" + case .profile: + return "Profile" + } + } +} diff --git a/Projects/Features/Home/Sources/HomeMain/HomeMainView.swift b/Projects/Features/Home/Sources/HomeMain/HomeMainView.swift new file mode 100644 index 0000000..6afbe6b --- /dev/null +++ b/Projects/Features/Home/Sources/HomeMain/HomeMainView.swift @@ -0,0 +1,95 @@ +// +// HomeMainView.swift +// DesignPreview +// +// Created by 김지수 on 11/2/24. +// Copyright © 2024 com.weave. All rights reserved. +// + +import SwiftUI +import CoreKit +import DesignCore +import CommonKit + +public struct HomeMainView: View { + + @StateObject var container: MVIContainer + + private var intent: HomeMainIntent.Intentable { container.intent } + private var state: HomeMainModel.Stateful { container.model } + + public init() { + let model = HomeMainModel() + let intent = HomeMainIntent( + model: model, + input: .init() + ) + let container = MVIContainer( + intent: intent as HomeMainIntent.Intentable, + model: model as HomeMainModel.Stateful, + modelChangePublisher: model.objectWillChange + ) + self._container = StateObject(wrappedValue: container) + } + + public var body: some View { + VStack(spacing: 0) { + // Tab + HStack(spacing: 30) { + ForEach(HomeMainTab.allCases, id: \.self) { tab in + Button(action: { + withAnimation { + intent.onTapTab(tab) + } + }) { + VStack { + let isSelected = tab == state.selectedTab + Circle() + .frame(width: 6, height: 6) + .foregroundStyle(isSelected ? DesignCore.Colors.grey500 : .clear) + Text(tab.title) + .typography(.en_medium_20) + .padding(.vertical, 12) + + .foregroundColor(isSelected ? DesignCore.Colors.grey500 : DesignCore.Colors.grey500.opacity(0.2)) + } + } + } + } + + // 탭 콘텐츠 + TabView(selection: $container.model.selectedTab) { + // 첫 번째 탭 내용 + VStack { + Text("첫 번째 탭") + } + .tag(HomeMainTab.home) + + // 두 번째 탭 내용 + VStack { + Text("두 번째 탭") + } + .tag(HomeMainTab.profile) + } + .tabViewStyle(PageTabViewStyle(indexDisplayMode: .never)) + } + .task { + await intent.task() + } + .onAppear { + intent.onAppear() + } + .ignoresSafeArea(.keyboard) + .textureBackground() + .setNavigation(showLeftBackButton: false) { + + } + .setLoading(state.isLoading) + } +} + +#Preview { + NavigationView { + HomeMainView() + } +} diff --git a/Projects/Features/Home/Sources/HomeView.swift b/Projects/Features/Home/Sources/HomeView.swift deleted file mode 100644 index 6cad18f..0000000 --- a/Projects/Features/Home/Sources/HomeView.swift +++ /dev/null @@ -1,18 +0,0 @@ -// -// HomeView.swift -// DesignPreview -// -// Created by 김지수 on 10/27/24. -// Copyright © 2024 com.weave. All rights reserved. -// - -import SwiftUI - -public struct HomeView: View { - - public init() {} - - public var body: some View { - Text("This Is Home View") - } -} From 018f5ab60717970bedac463c9cf5db3e60e12ed0 Mon Sep 17 00:00:00 2001 From: Jisu Kim <108998071+jisu15-kim@users.noreply.github.com> Date: Wed, 6 Nov 2024 01:07:48 +0900 Subject: [PATCH 2/2] =?UTF-8?q?[WEAV-121]=20=ED=99=88=20-=20=ED=94=84?= =?UTF-8?q?=EB=A1=9C=ED=95=84=20=ED=83=AD=20=EA=B5=AC=ED=98=84=20(#39)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [WEAV-121] Profile Pannel View 구현 * [WEAV-121] Home Main Tab View * [WEAV-121] 화면전화 방식 change root 로 변경 * [WEAV-121] 프로필 위젯 추가 뷰 구현 --- .../Sources/Navigation/NavigationStack.swift | 4 +- .../Sources/Splash/SplashAnimatedView.swift | 6 +- .../CommonKit/Sources/Path/PathTypes.swift | 10 +- Projects/Core/CoreKit/Sources/Int+Ext.swift | 15 ++ .../Images.xcassets/Profile/Contents.json | 6 + .../building_fill.imageset/Contents.json | 23 ++ .../building_fill.imageset/building_fill.png | Bin 0 -> 408 bytes .../building_fill@2x.png | Bin 0 -> 499 bytes .../building_fill@3x.png | Bin 0 -> 417 bytes .../business_fill.imageset/Contents.json | 23 ++ .../business_fill.imageset/business_fill.png | Bin 0 -> 460 bytes .../business_fill@2x.png | Bin 0 -> 644 bytes .../business_fill@3x.png | Bin 0 -> 871 bytes .../location_fill.imageset/Contents.json | 23 ++ .../location_fill.imageset/location_fill.png | Bin 0 -> 444 bytes .../location_fill@2x.png | Bin 0 -> 836 bytes .../location_fill@3x.png | Bin 0 -> 1136 bytes .../Sources/JobSelection/JobOccupation.swift | 59 ++--- .../TagListView/TagListCollectionView.swift | 33 +++ .../Sources/HomeMain/HomeMainIntent.swift | 23 +- .../Home/Sources/HomeMain/HomeMainModel.swift | 9 +- .../Home/Sources/HomeMain/HomeMainView.swift | 75 +++--- .../Home/Sources/Profile/ProfileIntent.swift | 56 +++++ .../Home/Sources/Profile/ProfileModel.swift | 85 +++++++ .../Home/Sources/Profile/ProfileView.swift | 127 ++++++++++ .../ProfilePannel/ProfilePannelIntent.swift | 58 +++++ .../ProfilePannel/ProfilePannelModel.swift | 91 ++++++++ .../ProfilePannel/ProfilePannelView.swift | 216 ++++++++++++++++++ .../AuthPhoneVerifyIntent.swift | 8 +- .../DreamPartnerDistanceIntent.swift | 6 +- .../SignUp/UnitTest/AuthPhoneVerifyTest.swift | 2 +- .../Model/Sources/Auth/Domain/UserInfo.swift | 30 +++ 32 files changed, 898 insertions(+), 90 deletions(-) create mode 100644 Projects/Core/CoreKit/Sources/Int+Ext.swift create mode 100644 Projects/DesignSystem/DesignCore/Resources/Images/Images.xcassets/Profile/Contents.json create mode 100644 Projects/DesignSystem/DesignCore/Resources/Images/Images.xcassets/Profile/building_fill.imageset/Contents.json create mode 100644 Projects/DesignSystem/DesignCore/Resources/Images/Images.xcassets/Profile/building_fill.imageset/building_fill.png create mode 100644 Projects/DesignSystem/DesignCore/Resources/Images/Images.xcassets/Profile/building_fill.imageset/building_fill@2x.png create mode 100644 Projects/DesignSystem/DesignCore/Resources/Images/Images.xcassets/Profile/building_fill.imageset/building_fill@3x.png create mode 100644 Projects/DesignSystem/DesignCore/Resources/Images/Images.xcassets/Profile/business_fill.imageset/Contents.json create mode 100644 Projects/DesignSystem/DesignCore/Resources/Images/Images.xcassets/Profile/business_fill.imageset/business_fill.png create mode 100644 Projects/DesignSystem/DesignCore/Resources/Images/Images.xcassets/Profile/business_fill.imageset/business_fill@2x.png create mode 100644 Projects/DesignSystem/DesignCore/Resources/Images/Images.xcassets/Profile/business_fill.imageset/business_fill@3x.png create mode 100644 Projects/DesignSystem/DesignCore/Resources/Images/Images.xcassets/Profile/location_fill.imageset/Contents.json create mode 100644 Projects/DesignSystem/DesignCore/Resources/Images/Images.xcassets/Profile/location_fill.imageset/location_fill.png create mode 100644 Projects/DesignSystem/DesignCore/Resources/Images/Images.xcassets/Profile/location_fill.imageset/location_fill@2x.png create mode 100644 Projects/DesignSystem/DesignCore/Resources/Images/Images.xcassets/Profile/location_fill.imageset/location_fill@3x.png create mode 100644 Projects/Features/Home/Sources/Profile/ProfileIntent.swift create mode 100644 Projects/Features/Home/Sources/Profile/ProfileModel.swift create mode 100644 Projects/Features/Home/Sources/Profile/ProfileView.swift create mode 100644 Projects/Features/Home/Sources/ProfilePannel/ProfilePannelIntent.swift create mode 100644 Projects/Features/Home/Sources/ProfilePannel/ProfilePannelModel.swift create mode 100644 Projects/Features/Home/Sources/ProfilePannel/ProfilePannelView.swift diff --git a/Projects/App/Sources/Navigation/NavigationStack.swift b/Projects/App/Sources/Navigation/NavigationStack.swift index d652077..8197b6f 100644 --- a/Projects/App/Sources/Navigation/NavigationStack.swift +++ b/Projects/App/Sources/Navigation/NavigationStack.swift @@ -28,8 +28,8 @@ extension PathType { SplashAnimatedView() // features - case .home: - HomeMainView() + case .home(let userInfo): + HomeMainView(userInfo: userInfo) case .signUp(let subView): switch subView { diff --git a/Projects/App/Sources/Splash/SplashAnimatedView.swift b/Projects/App/Sources/Splash/SplashAnimatedView.swift index 12cdc5f..0ea87bf 100644 --- a/Projects/App/Sources/Splash/SplashAnimatedView.swift +++ b/Projects/App/Sources/Splash/SplashAnimatedView.swift @@ -146,7 +146,7 @@ struct SplashAnimatedView: View { /// 로그아웃 -> 애니메이션 계속진행 if isAuthorized { try? await Task.sleep(for: .seconds(1)) - pushToHomeView() + await pushToHomeView() break } } @@ -177,7 +177,9 @@ struct SplashAnimatedView: View { @MainActor private func pushToHomeView() { - AppCoordinator.shared.changeRootView(.authDebug) + if let userInfo = AppCoordinator.shared.userInfo { + AppCoordinator.shared.changeRootView(.home(userInfo)) + } } private func updateIconStates(for step: SplashAnimationStep) { diff --git a/Projects/Core/CommonKit/Sources/Path/PathTypes.swift b/Projects/Core/CommonKit/Sources/Path/PathTypes.swift index 48761de..8666e05 100644 --- a/Projects/Core/CommonKit/Sources/Path/PathTypes.swift +++ b/Projects/Core/CommonKit/Sources/Path/PathTypes.swift @@ -10,13 +10,21 @@ import Foundation import Model public enum PathType: Hashable { + public static func == (lhs: PathType, rhs: PathType) -> Bool { + return lhs.hashValue == rhs.hashValue + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(self.name) + } + // App case designPreview case authDebug case intro // Features - case home + case home(UserInfo?) case signUp(SignUpSubViewType) #if STAGING || DEBUG diff --git a/Projects/Core/CoreKit/Sources/Int+Ext.swift b/Projects/Core/CoreKit/Sources/Int+Ext.swift new file mode 100644 index 0000000..b2df3f7 --- /dev/null +++ b/Projects/Core/CoreKit/Sources/Int+Ext.swift @@ -0,0 +1,15 @@ +// +// Int+Ext.swift +// CoreKit +// +// Created by 김지수 on 11/3/24. +// Copyright © 2024 com.weave. All rights reserved. +// + +import Foundation + +extension Int { + public func toString() -> String { + return String(self) + } +} diff --git a/Projects/DesignSystem/DesignCore/Resources/Images/Images.xcassets/Profile/Contents.json b/Projects/DesignSystem/DesignCore/Resources/Images/Images.xcassets/Profile/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/Projects/DesignSystem/DesignCore/Resources/Images/Images.xcassets/Profile/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Projects/DesignSystem/DesignCore/Resources/Images/Images.xcassets/Profile/building_fill.imageset/Contents.json b/Projects/DesignSystem/DesignCore/Resources/Images/Images.xcassets/Profile/building_fill.imageset/Contents.json new file mode 100644 index 0000000..d87bc05 --- /dev/null +++ b/Projects/DesignSystem/DesignCore/Resources/Images/Images.xcassets/Profile/building_fill.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "building_fill.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "building_fill@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "building_fill@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Projects/DesignSystem/DesignCore/Resources/Images/Images.xcassets/Profile/building_fill.imageset/building_fill.png b/Projects/DesignSystem/DesignCore/Resources/Images/Images.xcassets/Profile/building_fill.imageset/building_fill.png new file mode 100644 index 0000000000000000000000000000000000000000..29ba3e2541af924773602b57c6b38fa091234c55 GIT binary patch literal 408 zcmeAS@N?(olHy`uVBq!ia0vp^A|TAc1|)ksWqE-VXMsm#F#`j)FbFd;%$g$s6l5$8 za(7}_cTVOdki(Mh=1=ZVFLjf=^dY$4|yBdKVWCk zC@Exi4s@9VDUYf~m|pYM4s?O)2qpHG^+jY2M@OfS=t{MXZa6V{Tz1r8z%c0n@!_044N-~0|q36r>mdKI;Vst03ax! Ax&QzG literal 0 HcmV?d00001 diff --git a/Projects/DesignSystem/DesignCore/Resources/Images/Images.xcassets/Profile/building_fill.imageset/building_fill@2x.png b/Projects/DesignSystem/DesignCore/Resources/Images/Images.xcassets/Profile/building_fill.imageset/building_fill@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..fd649f6fb2074e754f3e73c35615130e2ae1ff25 GIT binary patch literal 499 zcmeAS@N?(olHy`uVBq!ia0vp^8X(NU1|)m_?Z^dEoCO|{#S9E$svykh8Km+7D9BhG zY2eIJZOR$aF5g0B;Yb#~z>8D*T`Q(m(!3JcA7L@$6uc z!aB(sr7t!yi>0se_pjdJo2cE~ZCT%XfaUI|ZMPq)b%oDfrX$&tG{xQ!jp#u!JtT$Kr=)BvxXaCu+GrmMd#^3uG@S~zlZQapr5?dnz zv^C|IZ+)ui8pNo)_mbbGV+IFKC|IT}^89BeyokB&X40$&oV*|VZ)s`Y^2pzKWzwb@ z**vpm9Od~?q>^^*XD*PGFex-l?*3#GF;8Ib{~!9o%loo7>Mm<|f757_VCSD(Lb(tu#vw`>J_-No}In_qBiD2j%Xci$`E~r`PcBBK zMaD@AArZIt|2r-$BRA(DgRTMF)cw(Ewr7sLPi18c{BV1*Y|ga^i|c+b77Dyu+&<|A z4^!+hwuf7zjlwS%EgI(t<1kVO0FFg;<@(j zQPcVh8=hBImdyNUuiRQ+wCn#2d7I5A_vrM+ezw}g$NOmFwZ4P5OzpNLwfjgU{hOs3 zbx3)wZ1dK~%a(%33#+`T;|f!PC{x JWt~$(69B>PuN43Q literal 0 HcmV?d00001 diff --git a/Projects/DesignSystem/DesignCore/Resources/Images/Images.xcassets/Profile/business_fill.imageset/Contents.json b/Projects/DesignSystem/DesignCore/Resources/Images/Images.xcassets/Profile/business_fill.imageset/Contents.json new file mode 100644 index 0000000..d76617a --- /dev/null +++ b/Projects/DesignSystem/DesignCore/Resources/Images/Images.xcassets/Profile/business_fill.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "business_fill.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "business_fill@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "business_fill@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Projects/DesignSystem/DesignCore/Resources/Images/Images.xcassets/Profile/business_fill.imageset/business_fill.png b/Projects/DesignSystem/DesignCore/Resources/Images/Images.xcassets/Profile/business_fill.imageset/business_fill.png new file mode 100644 index 0000000000000000000000000000000000000000..77b6fe74e806aa4ea578e2032345a0b0a5d4abd9 GIT binary patch literal 460 zcmV;-0Wgt0^s(= z3kF*Qb!8&^0K^25dTNabM{=;OMt0;6!s)L-_oijfW(RgYXPh@BXhZ<_f|*Zh%_M$C zL<2_HOARB+y_2QifQxGQo2Zkz@pF&lOy20NAp zl0iw8K{n;#*$YrTNc33gn=?!`jE6yV*>Crd;IxzxDJmughCp~&rqR1%6u_TSWd;Ig z-75?%&YR#kl?gseKn8Ihnu{-?DYtjG?!^qcovU`FV?izHDK!vbQf z*9o=N-IR>*X&#&VD@Zr@5_rfPJocS)9r z6=;eTXo_|%j*?Wy#^o@4vT`s$O*v{IBhqS}C)LA+eXgR?kPpsyKN9wJgHWx;OhSbFZ{CY6(ynU~ayGCL+ zv$g*xw<^k e`>00008L&$Nn0hcK3Yu-@K!n742i#^W8?Mkj46FOJ9D#6O^dq%aHp9fz0}=p?}gs+*vH*<)}4K%)FwG zUF|gVR{-}wGuj0cfU{T@fIW)4&$CKhNwEW(i5LWW;MwFuUI$43I7%1w=0z%a^>y@L6TOa0$FOQ0T@QF2|50klYUGpF+PQHU z1biSXkC>&`*}7NAHKZ0i+w!D-`8L{$~YDLD7S#~k=SP0%(O;`1U4 x{E8Li>ts>PGv9Bz!(j>^u^9{ogTat8e*vrY8_-|yT7v)p002ovPDHLkV1n3>mq`Es literal 0 HcmV?d00001 diff --git a/Projects/DesignSystem/DesignCore/Resources/Images/Images.xcassets/Profile/location_fill.imageset/Contents.json b/Projects/DesignSystem/DesignCore/Resources/Images/Images.xcassets/Profile/location_fill.imageset/Contents.json new file mode 100644 index 0000000..fe9fdaf --- /dev/null +++ b/Projects/DesignSystem/DesignCore/Resources/Images/Images.xcassets/Profile/location_fill.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "location_fill.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "location_fill@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "location_fill@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Projects/DesignSystem/DesignCore/Resources/Images/Images.xcassets/Profile/location_fill.imageset/location_fill.png b/Projects/DesignSystem/DesignCore/Resources/Images/Images.xcassets/Profile/location_fill.imageset/location_fill.png new file mode 100644 index 0000000000000000000000000000000000000000..9ad05ef4a6823a74bb3467ef310aad4ec12b91c3 GIT binary patch literal 444 zcmV;t0Ym>Ym`AM~3! znBBYm?Xq_V@ZaThO~y$UVv9?zn(Q4K1)r~KnW`TF3G%VS3jvdsxB@Pw>OL)cfk)Vy zFdyBrLPy53LuJ13md8<-&vrb?@R6u45T9Anj@YyFYS5kua1dWJrVYsILNt}ChjjI2 z>^H-~LX;G?0|BpLxyT_kf@;{>IcrXLt28_xBB_zDkxf*jabrZd(x>0+)>tfFap$s= zy+abuGmrjfRPET}ito^fsmVNt^K8vmfI)ILYMD&y9&B)QNf{7E`gcZolC1)Z+3mdGu!)Q_8@2k46Z}>j&C1Dq~PaOn2K@Wc) m5FV+=G{!Ze1gCYsQlaoS8*5T1eUwJycnY{ox=qph}~P9Ah?Sw%VpA-bSae+gbTpt zU!f#6cxP7#k`#RJ2?0CO{+ihx%`UKI%fCgTBtbW7VTv{?c#0Y7*`F0m`29PoNaB7t zMhR9DNKthPIE6-+KkE+j6iW9|QCAe3V$%YH&U*?|?>vMplJe-q=V4snnqmTj(v9r7s~keToZIaqe$58b9Y%y<5GOaZCm z+BW-WO9nNmksZ8%OiD5Pi}{Vejp&%eBVhz?#D`Ssh@XkA*%jhLvGoygcqlYw5Br#V z*@71PunG)bb8?8uSj9eXhRjA`D45jg=?01l#1qGxLhB)Xnb|{_)3P<#1x_84qb7xI z$yP2Hrgmr81?mC<+%&{iZJ+ISfip*nm^miUeWvyhx{$_T3JHy)tnBhZu{4tqhJaOI zp34&0X;7r|O^{VUVzO6B-q)kJ67?-_-nO>0^kS7$XFlvLrku>XQ(U@-g1&+%K_Kts z2mBcEZeQnp;u+?t!#xD5XiE9iUCo?CH$1hm5t!`HyJB7Fu4XiP*c2Yg-YGDz*AlE9 zde2b{8r&W#}io5%y7#!U7Ex znNZ{oMeccBpfRAhw^)Id1R5yvQ)~BmlZzgC(r64R?iE&~*d$q`rn>Z^y;?SHwQ@TW zr37YW(tOo6T5eS%(SOMFu=J-n~D98T6ZB5X?!s@Ahu*cC|oBNl8h`!zDmLM6GrmL)7VVf!?~1{ZnCp z5Gh@yNhj{30K5q%feR!sqRW@`GZs3Z9{n8CAoOSeQh30*U=ms*%WY$niRFl#MMA=N zT?=<6IJ4-8getUPC-gP}c9Qq;E*#-RaAwsJ&Pi+yen`pMD=w^Efh8oiL2LEWqgYK% zza!dgufY;4!Og9`4eH1_Tu$NHXzzZ~+}huO1;$f4dPUnzSeV$G3}}#7k|VQi%t&wn zYjz&SYuao|5ApUM#cK4L{saTWf}_{%NdLn9(;z3-fRHMr1etZs34)aFPse{K?g?yG zU$m8%WVZ;&t!pxs1|)KyhPOlOZgA_F+8_MI8m-A~qh+FKEg!}`ZgZ)H4Yq`!i%>Nu zg7`(zl(MAb$B?HE3iD~om3q>V+wu* zR~4#ut1M6yx1ZP$jdPldISyy8oEmg4t_zB_WkeXUo6T*=G#o6!r)73xZ=T}iIF| zFvR+Uc(L0Q<;o6PE3m*m0T5={$s(qU;Qne(to$?|bh;9|UnZ_@vA_PzdW zb&(dxbNGO^LE0HQaDdaYB`@n)7!A$CZ4ytf=RlNRokvPNT!%ya6sGB_9{bu4U6#1~ zU<=c3k{7&O!iFJWlh`luNsPW6l1KgsPk0lo z>3==DpzSIk(GAuOdi(hhzk^4-3f7RNdo0o8y> CGFloat { + var currentLineWidth: CGFloat = 0 + var totalHeight: CGFloat = verticalInsets + var currentLineCount: Int = 1 + + for tag in tags { + let label = UILabel() + label.font = UIFont.pretendard(._500, size: 14) + label.text = tag.name + label.sizeToFit() + + let cellWidth = label.frame.size.width + 24 + + if currentLineWidth + cellWidth + horizontalSpacing <= deviceWidth - horizontalInsets { + currentLineWidth += cellWidth + horizontalSpacing + } else { + currentLineCount += 1 + currentLineWidth = cellWidth + horizontalSpacing + } + } + + totalHeight += CGFloat(currentLineCount) * rowHeight - 8 + + return totalHeight + } } diff --git a/Projects/Features/Home/Sources/HomeMain/HomeMainIntent.swift b/Projects/Features/Home/Sources/HomeMain/HomeMainIntent.swift index e2d1f82..4a6fb33 100644 --- a/Projects/Features/Home/Sources/HomeMain/HomeMainIntent.swift +++ b/Projects/Features/Home/Sources/HomeMain/HomeMainIntent.swift @@ -9,19 +9,24 @@ import Foundation import CommonKit import CoreKit +import Model +import NetworkKit //MARK: - Intent class HomeMainIntent { private weak var model: HomeMainModelActionable? private let input: DataModel + private let authService: AuthServiceProtocol // MARK: Life cycle init( model: HomeMainModelActionable, - input: DataModel + input: DataModel, + service: AuthServiceProtocol = AuthService.shared ) { self.input = input self.model = model + self.authService = service } } @@ -37,7 +42,9 @@ extension HomeMainIntent { func task() async } - struct DataModel {} + struct DataModel { + let userInfo: UserInfo? + } } //MARK: - Intentable @@ -46,7 +53,17 @@ extension HomeMainIntent: HomeMainIntent.Intentable { func onTapTab(_ tab: HomeMainTab) { model?.setSelectedTab(tab: tab) } - func onAppear() {} + func onAppear() { + Task { + if let userInfo = input.userInfo { + model?.setUserInfo(userInfo: userInfo) + } else { + let userInfo = try await authService.requestMyUserInfo() + AppCoordinator.shared.userInfo = userInfo + model?.setUserInfo(userInfo: userInfo) + } + } + } func task() async {} diff --git a/Projects/Features/Home/Sources/HomeMain/HomeMainModel.swift b/Projects/Features/Home/Sources/HomeMain/HomeMainModel.swift index d4eb8d4..7bf3303 100644 --- a/Projects/Features/Home/Sources/HomeMain/HomeMainModel.swift +++ b/Projects/Features/Home/Sources/HomeMain/HomeMainModel.swift @@ -9,12 +9,14 @@ import Foundation import CommonKit import CoreKit +import Model final class HomeMainModel: ObservableObject { //MARK: Stateful protocol Stateful { // content + var userInfo: UserInfo? { get } var selectedTab: HomeMainTab { get set } var isValidated: Bool { get } @@ -28,7 +30,8 @@ final class HomeMainModel: ObservableObject { //MARK: State Properties // content - @Published var selectedTab: HomeMainTab = .home + @Published var userInfo: UserInfo? + @Published var selectedTab: HomeMainTab = .profile @Published var isValidated: Bool = false // default @@ -44,6 +47,7 @@ extension HomeMainModel: HomeMainModel.Stateful {} //MARK: - Actionable protocol HomeMainModelActionable: AnyObject { // content + func setUserInfo(userInfo: UserInfo) func setSelectedTab(tab: HomeMainTab) func setValidation(value: Bool) @@ -58,6 +62,9 @@ protocol HomeMainModelActionable: AnyObject { extension HomeMainModel: HomeMainModelActionable { // content + func setUserInfo(userInfo: UserInfo) { + self.userInfo = userInfo + } func setSelectedTab(tab: HomeMainTab) { self.selectedTab = tab } diff --git a/Projects/Features/Home/Sources/HomeMain/HomeMainView.swift b/Projects/Features/Home/Sources/HomeMain/HomeMainView.swift index 6afbe6b..f0643da 100644 --- a/Projects/Features/Home/Sources/HomeMain/HomeMainView.swift +++ b/Projects/Features/Home/Sources/HomeMain/HomeMainView.swift @@ -10,6 +10,7 @@ import SwiftUI import CoreKit import DesignCore import CommonKit +import Model public struct HomeMainView: View { @@ -18,11 +19,11 @@ public struct HomeMainView: View { private var intent: HomeMainIntent.Intentable { container.intent } private var state: HomeMainModel.Stateful { container.model } - public init() { + public init(userInfo: UserInfo?) { let model = HomeMainModel() let intent = HomeMainIntent( model: model, - input: .init() + input: .init(userInfo: userInfo) ) let container = MVIContainer( intent: intent as HomeMainIntent.Intentable, @@ -34,44 +35,44 @@ public struct HomeMainView: View { public var body: some View { VStack(spacing: 0) { - // Tab - HStack(spacing: 30) { - ForEach(HomeMainTab.allCases, id: \.self) { tab in - Button(action: { - withAnimation { - intent.onTapTab(tab) - } - }) { - VStack { - let isSelected = tab == state.selectedTab - Circle() - .frame(width: 6, height: 6) - .foregroundStyle(isSelected ? DesignCore.Colors.grey500 : .clear) - Text(tab.title) - .typography(.en_medium_20) - .padding(.vertical, 12) - - .foregroundColor(isSelected ? DesignCore.Colors.grey500 : DesignCore.Colors.grey500.opacity(0.2)) + if let userInfo = state.userInfo { + // Tab + HStack(spacing: 30) { + ForEach(HomeMainTab.allCases, id: \.self) { tab in + Button(action: { + withAnimation { + intent.onTapTab(tab) + } + }) { + VStack(spacing: 0) { + let isSelected = tab == state.selectedTab + Circle() + .frame(width: 6, height: 6) + .foregroundStyle(isSelected ? DesignCore.Colors.grey500 : .clear) + Text(tab.title) + .typography(.en_medium_20) + .padding(.vertical, 12) + + .foregroundColor(isSelected ? DesignCore.Colors.grey500 : DesignCore.Colors.grey500.opacity(0.2)) + } } } } - } - - // 탭 콘텐츠 - TabView(selection: $container.model.selectedTab) { - // 첫 번째 탭 내용 - VStack { - Text("첫 번째 탭") - } - .tag(HomeMainTab.home) - // 두 번째 탭 내용 - VStack { - Text("두 번째 탭") + // 탭 콘텐츠 + TabView(selection: $container.model.selectedTab) { + // 첫 번째 탭 내용 + VStack { + Text("첫 번째 탭") + } + .tag(HomeMainTab.home) + + // 두 번째 탭 내용 + ProfileView(userInfo: userInfo) + .tag(HomeMainTab.profile) } - .tag(HomeMainTab.profile) + .tabViewStyle(PageTabViewStyle(indexDisplayMode: .never)) } - .tabViewStyle(PageTabViewStyle(indexDisplayMode: .never)) } .task { await intent.task() @@ -81,15 +82,13 @@ public struct HomeMainView: View { } .ignoresSafeArea(.keyboard) .textureBackground() - .setNavigation(showLeftBackButton: false) { - - } + .setNavigation(showLeftBackButton: false) {} .setLoading(state.isLoading) } } #Preview { NavigationView { - HomeMainView() + HomeMainView(userInfo: .mock) } } diff --git a/Projects/Features/Home/Sources/Profile/ProfileIntent.swift b/Projects/Features/Home/Sources/Profile/ProfileIntent.swift new file mode 100644 index 0000000..dd0501a --- /dev/null +++ b/Projects/Features/Home/Sources/Profile/ProfileIntent.swift @@ -0,0 +1,56 @@ +// +// ProfileIntent.swift +// DesignPreview +// +// Created by 김지수 on 11/3/24. +// Copyright © 2024 com.weave. All rights reserved. +// + +import Foundation +import CommonKit +import CoreKit +import Model + +//MARK: - Intent +class ProfileIntent { + private weak var model: ProfileModelActionable? + private let input: DataModel + + // MARK: Life cycle + init( + model: ProfileModelActionable, + input: DataModel + ) { + self.input = input + self.model = model + } +} + +//MARK: - Intentable +extension ProfileIntent { + protocol Intentable { + // content + func onTapNextButton() + + // default + func onAppear() + func task() async + } + + struct DataModel { + let userInfo: UserInfo + } +} + +//MARK: - Intentable +extension ProfileIntent: ProfileIntent.Intentable { + // default + func onAppear() { + model?.setUserInfo(input.userInfo) + } + + func task() async {} + + // content + func onTapNextButton() {} +} diff --git a/Projects/Features/Home/Sources/Profile/ProfileModel.swift b/Projects/Features/Home/Sources/Profile/ProfileModel.swift new file mode 100644 index 0000000..51a88cf --- /dev/null +++ b/Projects/Features/Home/Sources/Profile/ProfileModel.swift @@ -0,0 +1,85 @@ +// +// ProfileModel.swift +// DesignPreview +// +// Created by 김지수 on 11/3/24. +// Copyright © 2024 com.weave. All rights reserved. +// + +import Foundation +import CommonKit +import CoreKit +import Model + +final class ProfileModel: ObservableObject { + + //MARK: Stateful + protocol Stateful { + // content + var userInfoModel: UserInfo? { get } + var isValidated: Bool { get } + + // default + var isLoading: Bool { get } + + // error + var showErrorView: ErrorModel? { get } + var showErrorAlert: ErrorModel? { get } + } + + //MARK: State Properties + // content + @Published var userInfoModel: UserInfo? + @Published var isValidated: Bool = false + + // default + @Published var isLoading: Bool = false + + // error + @Published var showErrorView: ErrorModel? + @Published var showErrorAlert: ErrorModel? +} + +extension ProfileModel: ProfileModel.Stateful {} + +//MARK: - Actionable +protocol ProfileModelActionable: AnyObject { + // content + func setUserInfo(_ userInfo: UserInfo) + func setValidation(value: Bool) + + // default + func setLoading(status: Bool) + + // error + func showErrorView(error: ErrorModel) + func showErrorAlert(error: ErrorModel) + func resetError() +} + +extension ProfileModel: ProfileModelActionable { + // content + func setUserInfo(_ userInfo: UserInfo) { + userInfoModel = userInfo + } + func setValidation(value: Bool) { + isValidated = value + } + + // default + func setLoading(status: Bool) { + isLoading = status + } + + // error + func showErrorView(error: ErrorModel) { + showErrorView = error + } + func showErrorAlert(error: ErrorModel) { + showErrorAlert = error + } + func resetError() { + showErrorView = nil + showErrorAlert = nil + } +} diff --git a/Projects/Features/Home/Sources/Profile/ProfileView.swift b/Projects/Features/Home/Sources/Profile/ProfileView.swift new file mode 100644 index 0000000..3a5bf94 --- /dev/null +++ b/Projects/Features/Home/Sources/Profile/ProfileView.swift @@ -0,0 +1,127 @@ +// +// ProfileView.swift +// DesignPreview +// +// Created by 김지수 on 11/3/24. +// Copyright © 2024 com.weave. All rights reserved. +// + +import SwiftUI +import CoreKit +import DesignCore +import CommonKit +import Model + +public struct ProfileView: View { + + @StateObject var container: MVIContainer + + private var intent: ProfileIntent.Intentable { container.intent } + private var state: ProfileModel.Stateful { container.model } + + private var widgetSize: CGFloat { + (Device.width - 36 - 12) / 2 + } + + public init(userInfo: UserInfo) { + let model = ProfileModel() + let intent = ProfileIntent( + model: model, + input: .init( + userInfo: userInfo + ) + ) + let container = MVIContainer( + intent: intent as ProfileIntent.Intentable, + model: model as ProfileModel.Stateful, + modelChangePublisher: model.objectWillChange + ) + self._container = StateObject(wrappedValue: container) + } + + public var body: some View { + ZStack { + if let userInfo = state.userInfoModel { + ScrollView { + VStack(spacing: 0) { + LeftAlignText("My Profile") + .typography(.en_medium_20) + + ProfilePannelView( + name: userInfo.name, + profile: userInfo.profile + ) + + LeftAlignText("Introductions") + .typography(.en_medium_16) + .padding(.bottom, 14) + + ZStack { + Capsule() + .inset(by: 1) + .stroke(DesignCore.Colors.blue300, lineWidth: 1) + .fill(Color(hex: 0xF2F9FF)) + LeftAlignText("프로필 위젯을 추가해 나를 더 소개해보세요!🙌") + .padding(.leading, 26) + .typography(.semibold_14) + .foregroundStyle(DesignCore.Colors.blue300) + } + .frame(height: 57) + .shadow(.default) + .padding(.bottom, 14) + + ZStack { + RoundedRectangle(cornerRadius: 24) + .fill(.white) + + RoundedRectangle(cornerRadius: 10) + .fill(DesignCore.Colors.grey50) + .strokeBorder( + style: StrokeStyle( + lineWidth: 3, + dash: [8, 8] + ) + ) + .foregroundStyle(Color(hex: 0xE0DEDD)) + .padding(.all, 8) + + VStack { + Image(systemName: "plus") + .resizable() + .frame(width: 24, height: 24) + Text("프로필 위젯 추가하기") + .typography(.semibold_14) + } + .foregroundStyle(DesignCore.Colors.grey200) + } + .frame(height: widgetSize) + .shadow(.default) + .padding(.bottom, 36) + } + .padding(.horizontal, 18) + .padding(.top, 36) + } + } else { + ProgressView() + } + } + .task { + await intent.task() + } + .onAppear { + intent.onAppear() + } + .ignoresSafeArea(.all) + .textureBackground() + .setPopNavigation { + AppCoordinator.shared.pop() + } + .setLoading(state.isLoading) + } +} + +#Preview { + NavigationView { + HomeMainView(userInfo: .mock) + } +} diff --git a/Projects/Features/Home/Sources/ProfilePannel/ProfilePannelIntent.swift b/Projects/Features/Home/Sources/ProfilePannel/ProfilePannelIntent.swift new file mode 100644 index 0000000..2515e00 --- /dev/null +++ b/Projects/Features/Home/Sources/ProfilePannel/ProfilePannelIntent.swift @@ -0,0 +1,58 @@ +// +// ProfilePannelIntent.swift +// Home +// +// Created by 김지수 on 11/3/24. +// Copyright © 2024 com.weave. All rights reserved. +// + +import Foundation +import CommonKit +import CoreKit +import Model + +//MARK: - Intent +class ProfilePannelIntent { + private weak var model: ProfilePannelModelActionable? + private let input: DataModel + + // MARK: Life cycle + init( + model: ProfilePannelModelActionable, + input: DataModel + ) { + self.input = input + self.model = model + } +} + +//MARK: - Intentable +extension ProfilePannelIntent { + protocol Intentable { + // content + func onTapNextButton() + + // default + func onAppear() + func task() async + } + + struct DataModel { + let name: String + let profile: UserInfoProfile + } +} + +//MARK: - Intentable +extension ProfilePannelIntent: ProfilePannelIntent.Intentable { + // default + func onAppear() { + model?.setProfile(profile: input.profile) + model?.setName(name: input.name) + } + + func task() async {} + + // content + func onTapNextButton() {} +} diff --git a/Projects/Features/Home/Sources/ProfilePannel/ProfilePannelModel.swift b/Projects/Features/Home/Sources/ProfilePannel/ProfilePannelModel.swift new file mode 100644 index 0000000..8da62b2 --- /dev/null +++ b/Projects/Features/Home/Sources/ProfilePannel/ProfilePannelModel.swift @@ -0,0 +1,91 @@ +// +// ProfilePannelModel.swift +// Home +// +// Created by 김지수 on 11/3/24. +// Copyright © 2024 com.weave. All rights reserved. +// + +import Foundation +import CommonKit +import CoreKit +import Model + +final class ProfilePannelModel: ObservableObject { + + //MARK: Stateful + protocol Stateful { + // content + var name: String? { get } + var profile: UserInfoProfile? { get } + var isValidated: Bool { get } + + // default + var isLoading: Bool { get } + + // error + var showErrorView: ErrorModel? { get } + var showErrorAlert: ErrorModel? { get } + } + + //MARK: State Properties + // content + @Published var name: String? = nil + @Published var profile: UserInfoProfile? = nil + @Published var isValidated: Bool = false + + // default + @Published var isLoading: Bool = false + + // error + @Published var showErrorView: ErrorModel? + @Published var showErrorAlert: ErrorModel? +} + +extension ProfilePannelModel: ProfilePannelModel.Stateful {} + +//MARK: - Actionable +protocol ProfilePannelModelActionable: AnyObject { + // content + func setName(name: String) + func setProfile(profile: UserInfoProfile) + func setValidation(value: Bool) + + // default + func setLoading(status: Bool) + + // error + func showErrorView(error: ErrorModel) + func showErrorAlert(error: ErrorModel) + func resetError() +} + +extension ProfilePannelModel: ProfilePannelModelActionable { + // content + func setName(name: String) { + self.name = name + } + func setProfile(profile: UserInfoProfile) { + self.profile = profile + } + func setValidation(value: Bool) { + isValidated = value + } + + // default + func setLoading(status: Bool) { + isLoading = status + } + + // error + func showErrorView(error: ErrorModel) { + showErrorView = error + } + func showErrorAlert(error: ErrorModel) { + showErrorAlert = error + } + func resetError() { + showErrorView = nil + showErrorAlert = nil + } +} diff --git a/Projects/Features/Home/Sources/ProfilePannel/ProfilePannelView.swift b/Projects/Features/Home/Sources/ProfilePannel/ProfilePannelView.swift new file mode 100644 index 0000000..3dfcea3 --- /dev/null +++ b/Projects/Features/Home/Sources/ProfilePannel/ProfilePannelView.swift @@ -0,0 +1,216 @@ +// +// ProfilePannelView.swift +// Home +// +// Created by 김지수 on 11/3/24. +// Copyright © 2024 com.weave. All rights reserved. +// + +import SwiftUI +import UIKit +import CoreKit +import DesignCore +import CommonKit +import Model + +public struct ProfilePannelView: View { + + @StateObject var container: MVIContainer + + private var intent: ProfilePannelIntent.Intentable { container.intent } + private var state: ProfilePannelModel.Stateful { container.model } + + public init(name: String, profile: UserInfoProfile) { + let model = ProfilePannelModel() + let intent = ProfilePannelIntent( + model: model, + input: .init( + name: name, + profile: profile + ) + ) + let container = MVIContainer( + intent: intent as ProfilePannelIntent.Intentable, + model: model as ProfilePannelModel.Stateful, + modelChangePublisher: model.objectWillChange + ) + self._container = StateObject(wrappedValue: container) + } + + @ViewBuilder + var circleDot: some View { + ZStack { + Circle() + .stroke(Color(hex: 0xD2D2D2), lineWidth: 1) + .fill(Color(hex: 0xEBEBEB)) + .frame(width: 12, height: 12) + } + } + + public var body: some View { + ZStack { + VStack(spacing: 6) { + HStack { + Spacer() + RoundedRectangle(cornerRadius: 24) + .frame(width: 102, height: 102) + Spacer() + } + .shadow(.default) + + VStack(spacing: 4) { + Text(state.name ?? "") + .pretendard(weight: ._600, size: 28) + .foregroundStyle(Color(hex: 0x1F1F1F)) + + Text("\(state.profile?.birthYear.toString() ?? "")년생") + .pretendard(weight: ._500, size: 14) + .foregroundStyle(Color(hex: 0xA0A0A0)) + } + .padding(.bottom, 16) + + innerRoundBoxView( + fillColor: DesignCore.Colors.green50, + strokeColor: Color(hex: 0xE6EFDF) + ) { + horizonIconKeyValueView( + icon: DesignCore.Images.businessFill.image, + key: "직군", + value: "IT 정보통신", + textColor: Color(hex: 0x5B6654) + ) + } + + innerRoundBoxView( + fillColor: DesignCore.Colors.pink50, + strokeColor: Color(hex: 0xEFDFE5) + ) { + horizonIconKeyValueView( + icon: DesignCore.Images.buildingFill.image, + key: "직장", + value: "현대자동차", + textColor: Color(hex: 0x846470) + ) + } + + innerRoundBoxView( + fillColor: DesignCore.Colors.blue50, + strokeColor: Color(hex: 0xDFE8EF) + ) { + VStack { + horizonIconKeyValueView( + icon: DesignCore.Images.locationFill.image, + key: "활동 지역", + value: nil, + textColor: Color(hex: 0x606D8F) + ) + let tagModels: [TagModel] = [ + .init(id: "UUID().uuidString", name: "용인"), + .init(id: UUID().uuidString, name: "성남"), + .init(id: UUID().uuidString, name: "강남구") + ] + + TagListView( + tagModels: tagModels, + selectedTagModels: [] + ) { _ in } + .frame( + height: TagListCollectionView.calculateHeight( + tags: tagModels, + deviceWidth: Device.width - (76 + 36) + ) + ) + } + } + } + .padding(.horizontal, 20) + .padding(.bottom, 80) + } + .background { + VStack(spacing: 0) { + Rectangle() + .foregroundStyle(.clear) + .frame(height: 51) + + ZStack { + RoundedRectangle(cornerRadius: 24) + .foregroundStyle(.white) + + VStack { + HStack { + circleDot + Spacer() + circleDot + } + Spacer() + HStack { + circleDot + Spacer() + circleDot + } + } + .padding(.all, 16) + } + } + .shadow(.default) + } + .padding(.vertical, 30) + .task { + await intent.task() + } + .onAppear { + intent.onAppear() + } + } + + @ViewBuilder + func horizonIconKeyValueView( + icon: Image, + key: String, + value: String?, + textColor: Color + ) -> some View { + HStack(spacing: 6) { + icon + .resizable() + .frame(width: 20, height: 20) + .aspectRatio(contentMode: .fit) + Text(key) + .typography(.medium_14) + Spacer() + if let value { + Text(value) + .typography(.medium_16) + .multilineTextAlignment(.trailing) + } + } + .foregroundStyle(textColor) + } + + @ViewBuilder + func innerRoundBoxView( + fillColor: Color, + strokeColor: Color, + contentView: @escaping () -> some View + ) -> some View { + VStack { + contentView() + } + .padding(.horizontal, 18) + .padding(.vertical, 10) + .background { + RoundedRectangle(cornerRadius: 14) + .stroke(strokeColor, lineWidth: 1) + .fill(fillColor) + } + } +} + +#Preview { + NavigationView { + ProfilePannelView( + name: "김삼일", + profile: .mock + ) + } +} diff --git a/Projects/Features/SignUp/Sources/AuthSignUp/AuthPhoneVerify/AuthPhoneVerifyIntent.swift b/Projects/Features/SignUp/Sources/AuthSignUp/AuthPhoneVerify/AuthPhoneVerifyIntent.swift index caa3c8c..1c3d032 100644 --- a/Projects/Features/SignUp/Sources/AuthSignUp/AuthPhoneVerify/AuthPhoneVerifyIntent.swift +++ b/Projects/Features/SignUp/Sources/AuthSignUp/AuthPhoneVerify/AuthPhoneVerifyIntent.swift @@ -131,13 +131,17 @@ extension AuthPhoneVerifyIntent: AuthPhoneVerifyIntent.Intentable { input: payload ) ) - case .EXISTING: return .authDebug + case .EXISTING: return .home(AppCoordinator.shared.userInfo) } } @MainActor func pushNextView(to targetPath: PathType) { - AppCoordinator.shared.push(targetPath) + if targetPath == .home(nil) { + AppCoordinator.shared.changeRootView(targetPath) + } else { + AppCoordinator.shared.push(targetPath) + } } func task() async {} diff --git a/Projects/Features/SignUp/Sources/DreamPartnerInput/DreamPartnerDistance/DreamPartnerDistanceIntent.swift b/Projects/Features/SignUp/Sources/DreamPartnerInput/DreamPartnerDistance/DreamPartnerDistanceIntent.swift index ce57895..f489875 100644 --- a/Projects/Features/SignUp/Sources/DreamPartnerInput/DreamPartnerDistance/DreamPartnerDistanceIntent.swift +++ b/Projects/Features/SignUp/Sources/DreamPartnerInput/DreamPartnerDistance/DreamPartnerDistanceIntent.swift @@ -76,6 +76,10 @@ extension DreamPartnerDistanceIntent: DreamPartnerDistanceIntent.Intentable { TokenManager.registerToken = "" TokenManager.accessToken = response.accessToken TokenManager.refreshToken = response.refreshToken + AppCoordinator.shared.validateToken( + accessToken: response.accessToken, + refreshToken: response.refreshToken + ) await pushNextView() } catch { print(error) @@ -84,6 +88,6 @@ extension DreamPartnerDistanceIntent: DreamPartnerDistanceIntent.Intentable { @MainActor func pushNextView() { - AppCoordinator.shared.push(.authDebug) + AppCoordinator.shared.changeRootView(.home(AppCoordinator.shared.userInfo)) } } diff --git a/Projects/Features/SignUp/UnitTest/AuthPhoneVerifyTest.swift b/Projects/Features/SignUp/UnitTest/AuthPhoneVerifyTest.swift index ec3b09e..8250bed 100644 --- a/Projects/Features/SignUp/UnitTest/AuthPhoneVerifyTest.swift +++ b/Projects/Features/SignUp/UnitTest/AuthPhoneVerifyTest.swift @@ -62,7 +62,7 @@ class AuthPhoneVerifyTest: XCTestCase { func testVerificationResult() { let homePath = intent.getNextPath(userType: .EXISTING) - XCTAssertEqual(homePath, PathType.authDebug) + XCTAssertEqual(homePath, PathType.home(nil)) let signUpProcessPath = intent.getNextPath(userType: .NEW) XCTAssertEqual(signUpProcessPath, PathType.signUp(.authAgreement(input: .mock))) diff --git a/Projects/Model/Model/Sources/Auth/Domain/UserInfo.swift b/Projects/Model/Model/Sources/Auth/Domain/UserInfo.swift index ba5c6fd..f173fae 100644 --- a/Projects/Model/Model/Sources/Auth/Domain/UserInfo.swift +++ b/Projects/Model/Model/Sources/Auth/Domain/UserInfo.swift @@ -37,6 +37,16 @@ public struct UserInfo { self.profile = .init(from: dto.profile) self.dreamPartner = .init(from: dto.desiredPartner) } + + public static var mock: UserInfo { + .init( + id: UUID().uuidString, + name: "김지수", + phone: "01012341234", + profile: .mock, + dreamPartner: .mock + ) + } } public struct UserInfoProfile { @@ -67,6 +77,16 @@ public struct UserInfoProfile { self.jobOccupation = dto.jobOccupation.rawValue self.locations = dto.locationIds } + + public static var mock: UserInfoProfile { + .init( + gender: .male, + birthYear: 1980, + companyId: nil, + jobOccupation: "IT_INFORMATION", + locations: ["용인", "성남", "강남구", "중구"] + ) + } } public struct DreamPartnerInfo { @@ -105,4 +125,14 @@ public struct DreamPartnerInfo { self.distanceType = .anywhere } } + + public static var mock: DreamPartnerInfo { + .init( + upperBirthYear: 4, + lowerBirthYear: 4, + jobOccupations: [], + distanceType: .myArea, + allowSameCompany: true + ) + } }