diff --git a/Projects/Core/KeyChainStore/Interface/Sources/TokenStoreInterface.swift b/Projects/Core/KeyChainStore/Interface/Sources/TokenStoreInterface.swift index cb8e4ac..5a87dd1 100644 --- a/Projects/Core/KeyChainStore/Interface/Sources/TokenStoreInterface.swift +++ b/Projects/Core/KeyChainStore/Interface/Sources/TokenStoreInterface.swift @@ -1,6 +1,7 @@ public enum TokenProperties: String { case accessToken = "ACCESS-TOKEN" case expiresAt = "ACCESS-EXPIRES-AT" + case userId = "USERID" case loginType = "LOGIN-TYPE" case oauth2Id = "OAUTH2ID" diff --git a/Projects/Domain/Auth/Interface/Sources/DTO/Response/SignInWithAppleResponseDTO.swift b/Projects/Domain/Auth/Interface/Sources/DTO/Response/SignInWithAppleResponseDTO.swift index a75e6f2..3184701 100644 --- a/Projects/Domain/Auth/Interface/Sources/DTO/Response/SignInWithAppleResponseDTO.swift +++ b/Projects/Domain/Auth/Interface/Sources/DTO/Response/SignInWithAppleResponseDTO.swift @@ -33,6 +33,7 @@ public struct SignInWithAppleResponseDTO: Decodable, Equatable { public func toDomain() -> Token { return Token(accessToken: accessToken, expiresAt: expiresAt, + userId: nil, loginType: loginType, oauth2Id: oauth2Id) } diff --git a/Projects/Domain/Auth/Interface/Sources/DTO/Response/SignUpResponseDTO.swift b/Projects/Domain/Auth/Interface/Sources/DTO/Response/SignUpResponseDTO.swift index 86a6fe1..ed04333 100644 --- a/Projects/Domain/Auth/Interface/Sources/DTO/Response/SignUpResponseDTO.swift +++ b/Projects/Domain/Auth/Interface/Sources/DTO/Response/SignUpResponseDTO.swift @@ -10,25 +10,33 @@ import Foundation public struct SignUpResponseDTO: Codable { let accessToken: String? let expiresAt: String? + let userId: String? enum CodingKeys: String, CodingKey { case accessToken case expiresAt = "expiredTime" + case userId } - public init(accessToken: String?, expiresAt: String?) { + public init(accessToken: String?, + expiresAt: String?, + userId: String?) { self.accessToken = accessToken self.expiresAt = expiresAt + self.userId = userId } public func toDomain() -> Token { return Token(accessToken: accessToken, expiresAt: expiresAt, + userId: userId, loginType: nil, oauth2Id: nil) } } public extension SignUpResponseDTO { - static let mock = SignUpResponseDTO(accessToken: "MockAccessToken", expiresAt: "2023-06-29T10:59:14.131+00:00") + static let mock = SignUpResponseDTO(accessToken: "MockAccessToken", + expiresAt: "2023-06-29T10:59:14.131+00:00", + userId: "MockUserId") } diff --git a/Projects/Domain/Auth/Interface/Sources/Models/CharacterType.swift b/Projects/Domain/Auth/Interface/Sources/Models/CharacterType.swift index 744b475..626d199 100644 --- a/Projects/Domain/Auth/Interface/Sources/Models/CharacterType.swift +++ b/Projects/Domain/Auth/Interface/Sources/Models/CharacterType.swift @@ -6,6 +6,8 @@ // import Foundation +import Shared +import SwiftUI public enum CharacterType: String, CaseIterable { case a = "A" @@ -20,4 +22,70 @@ public enum CharacterType: String, CaseIterable { case j = "J" case k = "K" case l = "L" + + public func getCharacterImage(body: Bool = true, head: Bool = false) -> Image { + switch self { + case .a: + if body { + return PumpingImages.bodyA.swiftUIImage + } + return PumpingImages.headA.swiftUIImage + case .b: + if body { + return PumpingImages.bodyB.swiftUIImage + } + return PumpingImages.headB.swiftUIImage + case .c: + if body { + return PumpingImages.bodyC.swiftUIImage + } + return PumpingImages.headC.swiftUIImage + case .d: + if body { + return PumpingImages.bodyD.swiftUIImage + } + return PumpingImages.headD.swiftUIImage + case .e: + if body { + return PumpingImages.bodyE.swiftUIImage + } + return PumpingImages.headE.swiftUIImage + case .f: + if body { + return PumpingImages.bodyF.swiftUIImage + } + return PumpingImages.headF.swiftUIImage + case .g: + if body { + return PumpingImages.bodyG.swiftUIImage + } + return PumpingImages.headG.swiftUIImage + case .h: + if body { + return PumpingImages.bodyH.swiftUIImage + } + return PumpingImages.headH.swiftUIImage + case .i: + if body { + return PumpingImages.bodyI.swiftUIImage + } + return PumpingImages.headI.swiftUIImage + case .j: + if body { + return PumpingImages.bodyJ.swiftUIImage + } + return PumpingImages.headJ.swiftUIImage + case .k: + if body { + return PumpingImages.bodyK.swiftUIImage + } + return PumpingImages.headK.swiftUIImage + case .l: + if body { + return PumpingImages.bodyL.swiftUIImage + } + return PumpingImages.headL.swiftUIImage + } + } + } diff --git a/Projects/Domain/Auth/Interface/Sources/Models/Token.swift b/Projects/Domain/Auth/Interface/Sources/Models/Token.swift index 8f0b900..df7720e 100644 --- a/Projects/Domain/Auth/Interface/Sources/Models/Token.swift +++ b/Projects/Domain/Auth/Interface/Sources/Models/Token.swift @@ -10,20 +10,27 @@ import Foundation public struct Token: Equatable { public let accessToken: String? public let expiresAt: String? + public let userId: String? public let loginType: String? public let oauth2Id: String? public init(accessToken: String?, expiresAt: String?, + userId: String?, loginType: String?, oauth2Id: String?) { self.accessToken = accessToken self.expiresAt = expiresAt + self.userId = userId self.loginType = loginType self.oauth2Id = oauth2Id } } public extension Token { - static let mock = Self(accessToken: "mockAccessToken", expiresAt: "2023-10-23", loginType: "Apple", oauth2Id: "MockoAuth2Id") + static let mock = Self(accessToken: "mockAccessToken", + expiresAt: "2023-10-23", + userId: "djkfjldsf", + loginType: "Apple", + oauth2Id: "MockoAuth2Id") } diff --git a/Projects/Domain/Auth/Sources/LocalAuthStore.swift b/Projects/Domain/Auth/Sources/LocalAuthStore.swift index 07e18eb..7e92026 100644 --- a/Projects/Domain/Auth/Sources/LocalAuthStore.swift +++ b/Projects/Domain/Auth/Sources/LocalAuthStore.swift @@ -15,6 +15,7 @@ public final class LocalAuthStore : LocalAuthStoreInterface { return Token( accessToken: KeyChainStore.shared.load(property: .accessToken), expiresAt: KeyChainStore.shared.load(property: .expiresAt), + userId: KeyChainStore.shared.load(property: .userId), loginType: KeyChainStore.shared.load(property: .loginType), oauth2Id: KeyChainStore.shared.load(property: .oauth2Id) ) @@ -29,6 +30,10 @@ public final class LocalAuthStore : LocalAuthStoreInterface { KeyChainStore.shared.save(property: .expiresAt, value: expiresAt) } + if let userId = token.userId { + KeyChainStore.shared.save(property: .userId, value: userId) + } + if let loginType = token.loginType { KeyChainStore.shared.save(property: .loginType, value: loginType) } diff --git a/Projects/Domain/Crew/Interface/Sources/CrewEndpoint.swift b/Projects/Domain/Crew/Interface/Sources/CrewEndpoint.swift index f461584..6fc8a06 100644 --- a/Projects/Domain/Crew/Interface/Sources/CrewEndpoint.swift +++ b/Projects/Domain/Crew/Interface/Sources/CrewEndpoint.swift @@ -10,7 +10,7 @@ import CoreNetworkInterface import CoreKeyChainStore public struct CrewEndpoint { - public static func fetchCrew() -> Endpoint<[CrewInfoResponseDTO]> { + public static func fetchCrew() -> Endpoint { let accessToken = KeyChainStore.shared.load(property: .accessToken) return Endpoint(path: "crews", diff --git a/Projects/Domain/Crew/Interface/Sources/DTO/Response/CrewInfoResponseDTO.swift b/Projects/Domain/Crew/Interface/Sources/DTO/Response/CrewInfoResponseDTO.swift index 08b2af4..96a0548 100644 --- a/Projects/Domain/Crew/Interface/Sources/DTO/Response/CrewInfoResponseDTO.swift +++ b/Projects/Domain/Crew/Interface/Sources/DTO/Response/CrewInfoResponseDTO.swift @@ -9,23 +9,39 @@ import Foundation public struct CrewInfoResponseDTO: Codable, Equatable { + public let crews: [CrewInfoResponseItemDTO] + + public func toDomain() -> [CrewInfo] { + return crews.map{ + CrewInfo(crewName: $0.crewName, crewId: $0.crewId, code: $0.code, createDate: $0.createDate) + } + } +} + +public struct CrewInfoResponseItemDTO: Codable, Equatable { public let crewName: String public let crewId: String + public let code: String public let createDate: String public init(crewName: String, crewId: String, + code: String, createDate: String) { self.crewName = crewName self.crewId = crewId + self.code = code self.createDate = createDate } - - public func toDomain() -> CrewInfo { - return CrewInfo(crewName: crewName, crewId: crewId, createDate: createDate) - } +} + +public extension CrewInfoResponseItemDTO { + static let mock = CrewInfoResponseItemDTO(crewName: "너만 오면 고", + crewId: "fikemjwmn", + code: "dd", + createDate: "") } public extension CrewInfoResponseDTO { - static let mock = CrewInfoResponseDTO(crewName: "너만 오면 고", crewId: "fikemjwmn", createDate: "") + static let mock = CrewInfoResponseDTO(crews: [CrewInfoResponseItemDTO.mock]) } diff --git a/Projects/Domain/Crew/Interface/Sources/Models/CrewInfo.swift b/Projects/Domain/Crew/Interface/Sources/Models/CrewInfo.swift index e65c6f9..8265079 100644 --- a/Projects/Domain/Crew/Interface/Sources/Models/CrewInfo.swift +++ b/Projects/Domain/Crew/Interface/Sources/Models/CrewInfo.swift @@ -10,13 +10,16 @@ import Foundation public struct CrewInfo: Equatable { public let crewName: String public let crewId: String + public let code: String public let createDate: String public init(crewName: String, crewId: String, + code: String, createDate: String) { self.crewName = crewName self.crewId = crewId + self.code = code self.createDate = createDate } } diff --git a/Projects/Domain/Crew/Sources/CrewClient.swift b/Projects/Domain/Crew/Sources/CrewClient.swift index 979245c..287ae24 100644 --- a/Projects/Domain/Crew/Sources/CrewClient.swift +++ b/Projects/Domain/Crew/Sources/CrewClient.swift @@ -14,12 +14,9 @@ extension CrewClient: DependencyKey { public static let liveValue = CrewClient( fetchCrew: { let apiEndpoint = CrewEndpoint.fetchCrew() - let responseDTOList = try await NetworkProvider.shared.sendRequest(apiEndpoint) - let responseList = responseDTOList.map { - CrewInfo(crewName: $0.crewName, crewId: $0.crewId, createDate: $0.createDate) - } + let response = try await NetworkProvider.shared.sendRequest(apiEndpoint).toDomain() - return responseList + return response }, makeCrew: { crewName, goalCount in let makeCrewRequestDTO = MakeCrewRequestDTO(crewName: crewName, goalCount: goalCount) diff --git a/Projects/Feature/Crew/Interface/Sources/Home/CrewHomeStoreInterface.swift b/Projects/Feature/Crew/Interface/Sources/Home/CrewHomeStoreInterface.swift index 72e62d6..b2afb23 100644 --- a/Projects/Feature/Crew/Interface/Sources/Home/CrewHomeStoreInterface.swift +++ b/Projects/Feature/Crew/Interface/Sources/Home/CrewHomeStoreInterface.swift @@ -33,7 +33,8 @@ public struct CrewHomeStore: ReducerProtocol { public var crewJoin: CrewJoinStore.State? public var crewMake: CrewMakeStore.State? - + public var crewList: [CrewInfo] = [] + public var userRecordList: IdentifiedArrayOf = [ .init(id: .init(), avatarName: "몰라", ranking: "4", userName: "보민", numberOfExerciseGoals: "3 / 5회", workoutTime: "02:40"), .init(id: .init(), avatarName: "몰라", ranking: "1", userName: "희원", numberOfExerciseGoals: "3 / 5회", workoutTime: "02:40"), diff --git a/Projects/Feature/Crew/Interface/Sources/Home/CrewListView.swift b/Projects/Feature/Crew/Interface/Sources/Home/CrewListView.swift index 297a6ab..b986d68 100644 --- a/Projects/Feature/Crew/Interface/Sources/Home/CrewListView.swift +++ b/Projects/Feature/Crew/Interface/Sources/Home/CrewListView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import UIKit import ComposableArchitecture import SharedDesignSystem @@ -22,8 +23,9 @@ public struct CrewListView: View { Text("크루") .font(.pretendard(size: 21, type: .semiBold)) .foregroundColor(.colorGrey900) + .padding(.bottom, 50) - // 크루 리스트.. + crewListView(viewStore: viewStore) Spacer() @@ -45,6 +47,26 @@ public struct CrewListView: View { .padding(23) } } + + private func crewListView(viewStore : ViewStoreOf) -> some View { + VStack(alignment: .leading, spacing: 20) { + ForEach(viewStore.crewList, id: \.self.crewId) { crew in + HStack { + Text(crew.crewName) + .font(.pretendard(size: 16, type: .semiBold)) + .foregroundColor(.colorGrey600) + + Spacer() + + Button { + UIPasteboard.general.string = crew.code + } label: { + PumpingImages.crewcode.swiftUIImage + } + } + } + } + } private func buttonView(viewStore : ViewStoreOf) -> some View { HStack { diff --git a/Projects/Feature/Crew/Sources/Home/CrewHomeStore.swift b/Projects/Feature/Crew/Sources/Home/CrewHomeStore.swift index 3e7a430..1610ef4 100644 --- a/Projects/Feature/Crew/Sources/Home/CrewHomeStore.swift +++ b/Projects/Feature/Crew/Sources/Home/CrewHomeStore.swift @@ -34,15 +34,15 @@ extension CrewHomeStore { ) } - case let .fetchCrewResponse(.success(crewInfo)): - print(crewInfo) + case let .fetchCrewResponse(.success(crewList)): + state.crewList = crewList + print(crewList) return .none case let .fetchCrewResponse(.failure(error)): print(error) return .none - case .presentCrewListView: state.showCrewListView = true return .none diff --git a/Projects/Feature/Crew/Sources/Root/CrewRootStore.swift b/Projects/Feature/Crew/Sources/Root/CrewRootStore.swift index 8f33af9..a9aeb58 100644 --- a/Projects/Feature/Crew/Sources/Root/CrewRootStore.swift +++ b/Projects/Feature/Crew/Sources/Root/CrewRootStore.swift @@ -24,7 +24,7 @@ extension CrewRootStore { switch action { case .goToProfileView: state.path.append(.profile) - state.profileHome = .init(type: .other)//TODO: other이 아닌경우 체크 해야함 + state.profileHome = .init(userId: "", type: .other)//TODO: other이 아닌경우 체크 해야함 return .none //TODO: 머지 컨플릭트 해결후 버그 diff --git a/Projects/Feature/Onboarding/Interface/Sources/OnboardingAvatar/OnboardingAvatarInterfaceStore.swift b/Projects/Feature/Onboarding/Interface/Sources/OnboardingAvatar/OnboardingAvatarInterfaceStore.swift index 4eea0eb..cfa311e 100644 --- a/Projects/Feature/Onboarding/Interface/Sources/OnboardingAvatar/OnboardingAvatarInterfaceStore.swift +++ b/Projects/Feature/Onboarding/Interface/Sources/OnboardingAvatar/OnboardingAvatarInterfaceStore.swift @@ -23,7 +23,7 @@ public struct OnboardingAvatarStore : ReducerProtocol { public enum Action: BindableAction, Equatable { case binding(BindingAction) - case getRandomCharacter + case getRandomCharacter(selctedGender: GenderType?) case signUp case goToMain } diff --git a/Projects/Feature/Onboarding/Interface/Sources/OnboardingAvatar/OnboardingAvatarView.swift b/Projects/Feature/Onboarding/Interface/Sources/OnboardingAvatar/OnboardingAvatarView.swift index abd077f..02c5dc4 100644 --- a/Projects/Feature/Onboarding/Interface/Sources/OnboardingAvatar/OnboardingAvatarView.swift +++ b/Projects/Feature/Onboarding/Interface/Sources/OnboardingAvatar/OnboardingAvatarView.swift @@ -8,19 +8,21 @@ import Foundation import SwiftUI import ComposableArchitecture +import Domain import SharedDesignSystem struct OnboardingAvatarView: View { + public let selectedGender: GenderType? public let store: StoreOf public var body: some View { WithViewStore(self.store) { viewStore in ZStack { - if let character = viewStore.state.pickedCharacter { - PumpingLottieView(asset: AnimationAsset.confetti) + if viewStore.state.pickedCharacter != nil { + viewStore.pickedCharacter?.getCharacterImage() } else { - PumpingLottieView(asset: AnimationAsset.confetti) + PumpingLottieView(asset: AnimationAsset.characterLoop, contentMode: .scaleToFill) } VStack(alignment : .leading) { @@ -55,7 +57,7 @@ struct OnboardingAvatarView: View { if viewStore.isAvatarPicked { viewStore.send(.signUp) } else { - viewStore.send(.getRandomCharacter) + viewStore.send(.getRandomCharacter(selctedGender: selectedGender)) } } .padding([.horizontal, .bottom]) diff --git a/Projects/Feature/Onboarding/Interface/Sources/OnboardingRoot/OnboardingRootView.swift b/Projects/Feature/Onboarding/Interface/Sources/OnboardingRoot/OnboardingRootView.swift index 738b298..f2ba2aa 100644 --- a/Projects/Feature/Onboarding/Interface/Sources/OnboardingRoot/OnboardingRootView.swift +++ b/Projects/Feature/Onboarding/Interface/Sources/OnboardingRoot/OnboardingRootView.swift @@ -34,7 +34,7 @@ public struct OnboardingRootView : View { case .avatar: IfLetStore(self.store.scope(state: \.avatar, action: { .avatar($0) })) { - OnboardingAvatarView(store: $0) + OnboardingAvatarView(selectedGender: viewStore.state.profile?.gender, store: $0) } } diff --git a/Projects/Feature/Onboarding/Sources/OnboardingAvatar/OnboardingAvatarStore.swift b/Projects/Feature/Onboarding/Sources/OnboardingAvatar/OnboardingAvatarStore.swift index d108afb..4e509e5 100644 --- a/Projects/Feature/Onboarding/Sources/OnboardingAvatar/OnboardingAvatarStore.swift +++ b/Projects/Feature/Onboarding/Sources/OnboardingAvatar/OnboardingAvatarStore.swift @@ -14,8 +14,8 @@ extension OnboardingAvatarStore { public init() { let reducer : Reduce = Reduce { state, action in switch action { - case .getRandomCharacter: - state.pickedCharacter = getRandomCharacter() + case let .getRandomCharacter(genderType): + state.pickedCharacter = getRandomCharacter(genderType: genderType) state.isAvatarPicked = true return .none @@ -28,10 +28,22 @@ extension OnboardingAvatarStore { reducer: reducer ) - func getRandomCharacter() -> CharacterType { - let allCases = CharacterType.allCases - let randomIndex = Int.random(in: 0.. CharacterType? { + guard let genderType else { + return nil + } + + var characterTypes: [CharacterType] = [] + + switch genderType { + case .male: + characterTypes = [.a, .b, .c, .d, .e, .f] + case .female: + characterTypes = [.g, .h, .i, .j, .k, .l] + } + + let randomIndex = Int.random(in: 0..