From fd6f2993b07339aee8e6b8eea5546ab49ca321f5 Mon Sep 17 00:00:00 2001 From: Minseo Park <125115284+FpRaArNkK@users.noreply.github.com> Date: Wed, 12 Feb 2025 18:30:54 +0900 Subject: [PATCH 1/6] =?UTF-8?q?[Feat]=20User=20-=20=EB=A7=88=EC=9D=B4?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=20API=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Service/User/UserRepositoryImpl.swift | 5 +++ .../Network/Service/User/UserTargetType.swift | 13 ++++-- .../Sources/DTO/User/UserResponseDTO.swift | 44 +++++++++++++++++++ .../Sources/Repository/UserRepository.swift | 5 +++ .../Domain/Sources/UseCase/UserUseCase.swift | 8 +++- 5 files changed, 69 insertions(+), 6 deletions(-) diff --git a/TnT/Projects/Data/Sources/Network/Service/User/UserRepositoryImpl.swift b/TnT/Projects/Data/Sources/Network/Service/User/UserRepositoryImpl.swift index cfde03bf..749c2157 100644 --- a/TnT/Projects/Data/Sources/Network/Service/User/UserRepositoryImpl.swift +++ b/TnT/Projects/Data/Sources/Network/Service/User/UserRepositoryImpl.swift @@ -50,4 +50,9 @@ public struct UserRepositoryImpl: UserRepository { public func postWithdrawal() async throws -> PostWithdrawalResDTO { return try await networkService.request(UserTargetType.postWithdrawal, decodingType: PostWithdrawalResDTO.self) } + + /// 마이페이지 요청을 수행 + public func getMyPageInfo() async throws -> GetMyPageInfoResDTO { + return try await networkService.request(UserTargetType.getMyPageInfo, decodingType: GetMyPageInfoResDTO.self) + } } diff --git a/TnT/Projects/Data/Sources/Network/Service/User/UserTargetType.swift b/TnT/Projects/Data/Sources/Network/Service/User/UserTargetType.swift index 0d6e50be..3c365e37 100644 --- a/TnT/Projects/Data/Sources/Network/Service/User/UserTargetType.swift +++ b/TnT/Projects/Data/Sources/Network/Service/User/UserTargetType.swift @@ -22,6 +22,8 @@ public enum UserTargetType { case postLogout /// 회원 탈퇴 요청 case postWithdrawal + /// 마이페이지 정보 요청 + case getMyPageInfo } extension UserTargetType: TargetType { @@ -45,12 +47,15 @@ extension UserTargetType: TargetType { case .postWithdrawal: return "/members/withdraw" + + case .getMyPageInfo: + return "/members" } } var method: HTTPMethod { switch self { - case .getSessionCheck: + case .getSessionCheck, .getMyPageInfo: return .get case .postSocialLogin, .postSignUp, .postLogout, .postWithdrawal: @@ -60,7 +65,7 @@ extension UserTargetType: TargetType { var task: RequestTask { switch self { - case .getSessionCheck, .postLogout, .postWithdrawal: + case .getSessionCheck, .postLogout, .postWithdrawal, .getMyPageInfo: return .requestPlain case .postSocialLogin(let reqDto): @@ -80,7 +85,7 @@ extension UserTargetType: TargetType { var headers: [String: String]? { switch self { - case .getSessionCheck, .postLogout, .postWithdrawal: + case .getSessionCheck, .postLogout, .postWithdrawal, .getMyPageInfo: return nil case .postSocialLogin: @@ -96,7 +101,7 @@ extension UserTargetType: TargetType { var interceptors: [any Interceptor] { switch self { - case .getSessionCheck, .postLogout, .postWithdrawal: + case .getSessionCheck, .postLogout, .postWithdrawal, .getMyPageInfo: return [ LoggingInterceptor(), AuthTokenInterceptor(), diff --git a/TnT/Projects/Domain/Sources/DTO/User/UserResponseDTO.swift b/TnT/Projects/Domain/Sources/DTO/User/UserResponseDTO.swift index de90cb6c..c41ddb1e 100644 --- a/TnT/Projects/Domain/Sources/DTO/User/UserResponseDTO.swift +++ b/TnT/Projects/Domain/Sources/DTO/User/UserResponseDTO.swift @@ -104,6 +104,50 @@ public struct PostLogoutResDTO: Decodable { /// 회원탈퇴 응답 DTO public typealias PostWithdrawalResDTO = EmptyResponse +/// 마이페이지 정보 응답 DTO +public struct GetMyPageInfoResDTO: Decodable { + /// 회원 이름 + public let name: String + /// 이메일 + public let email: String + /// 프로필 사진 URL + public let profileImageUrl: String + /// 회원 타입 + public let memberType: MemberTypeResDTO + /// 소셜 타입 + public let socialType: String + /// 트레이너 DTO + public let trainer: TrainerInfoResDTO? + /// 트레이니 DTO + public let trainee: TraineeInfoResDTO? +} + +/// 트레이너 정보 표시에 사용되는 TrainerInfoDTO +public struct TrainerInfoResDTO: Decodable { + /// 관리 중인 회원 + public let activeTraineeCount: Int? + /// 함께했던 회원 + public let totalTraineeCount: Int? +} + +/// 트레이니 정보 표시에 사용되는 TraineeInfoDTO +public struct TraineeInfoResDTO: Decodable { + /// 트레이니 ID + public let id: Int + /// 트레이니 이름 + public let name: String + /// 프로필 사진 URL + public let profileImageUrl: String + /// 진행한 PT 횟수 + public let finishedPtCount: Int + /// 총 PT 횟수 + public let totalPtCount: Int + /// 메모 + public let memo: String? + /// PT 목표 + public let ptGoals: [String] +} + public extension PostSignUpResDTO { func toEntity() -> PostSignUpResEntity { return .init( diff --git a/TnT/Projects/Domain/Sources/Repository/UserRepository.swift b/TnT/Projects/Domain/Sources/Repository/UserRepository.swift index d30daf06..f4964f39 100644 --- a/TnT/Projects/Domain/Sources/Repository/UserRepository.swift +++ b/TnT/Projects/Domain/Sources/Repository/UserRepository.swift @@ -39,4 +39,9 @@ public protocol UserRepository { /// - Returns: 회원 탈퇴 완료 시 응답 DTO (`PostWithdrawalResDTO`) /// - Throws: 네트워크 오류 또는 서버에서 반환한 오류를 발생시킬 수 있음 func postWithdrawal() async throws -> PostWithdrawalResDTO + + /// 마이페이지 정보 요청 + /// - Returns: 마이페이지 표시에 필요한 응답 DTO (`GetMyPageInfoResDTO`) + /// - Throws: 네트워크 오류 또는 서버에서 반환한 오류를 발생시킬 수 있음 + func getMyPageInfo() async throws -> GetMyPageInfoResDTO } diff --git a/TnT/Projects/Domain/Sources/UseCase/UserUseCase.swift b/TnT/Projects/Domain/Sources/UseCase/UserUseCase.swift index 534c1224..1ab32a58 100644 --- a/TnT/Projects/Domain/Sources/UseCase/UserUseCase.swift +++ b/TnT/Projects/Domain/Sources/UseCase/UserUseCase.swift @@ -28,7 +28,7 @@ public protocol UserUseCase { } // MARK: - Default 구현체 -public struct DefaultUserUseCase: UserRepository, UserUseCase { +public struct DefaultUserUseCase: UserUseCase { public let userRepostiory: UserRepository @@ -67,7 +67,7 @@ public struct DefaultUserUseCase: UserRepository, UserUseCase { } // MARK: - Repository -extension DefaultUserUseCase { +extension DefaultUserUseCase: UserRepository { public func getSessionCheck() async throws -> GetSessionCheckResDTO { return try await userRepostiory.getSessionCheck() } @@ -87,4 +87,8 @@ extension DefaultUserUseCase { public func postWithdrawal() async throws -> PostWithdrawalResDTO { return try await userRepostiory.postWithdrawal() } + + public func getMyPageInfo() async throws -> GetMyPageInfoResDTO { + return try await userRepostiory.getMyPageInfo() + } } From d4cf228186c497ca1e70fdbc6e605d0adb0fb3cd Mon Sep 17 00:00:00 2001 From: Minseo Park <125115284+FpRaArNkK@users.noreply.github.com> Date: Wed, 12 Feb 2025 19:33:53 +0900 Subject: [PATCH 2/6] =?UTF-8?q?[Feat]=20ResDTO=20<->=20Trainer/Trainee=20M?= =?UTF-8?q?yPageEntity=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/DTO/User/UserResponseDTO.swift | 18 +++++------ .../Domain/Sources/Entity/MyPageEntity.swift | 31 +++++++++++++++++++ .../Domain/Sources/Mapper/UserMapper.swift | 29 +++++++++++++++++ 3 files changed, 68 insertions(+), 10 deletions(-) create mode 100644 TnT/Projects/Domain/Sources/Entity/MyPageEntity.swift create mode 100644 TnT/Projects/Domain/Sources/Mapper/UserMapper.swift diff --git a/TnT/Projects/Domain/Sources/DTO/User/UserResponseDTO.swift b/TnT/Projects/Domain/Sources/DTO/User/UserResponseDTO.swift index c41ddb1e..75f836a9 100644 --- a/TnT/Projects/Domain/Sources/DTO/User/UserResponseDTO.swift +++ b/TnT/Projects/Domain/Sources/DTO/User/UserResponseDTO.swift @@ -132,18 +132,16 @@ public struct TrainerInfoResDTO: Decodable { /// 트레이니 정보 표시에 사용되는 TraineeInfoDTO public struct TraineeInfoResDTO: Decodable { - /// 트레이니 ID - public let id: Int - /// 트레이니 이름 - public let name: String - /// 프로필 사진 URL - public let profileImageUrl: String + /// 생년월일 + public let birthday: String + /// 나이 + public let age: Int /// 진행한 PT 횟수 - public let finishedPtCount: Int + public let height: Double? /// 총 PT 횟수 - public let totalPtCount: Int - /// 메모 - public let memo: String? + public let weight: Double? + /// 주의사항 + public let cautionNote: String? /// PT 목표 public let ptGoals: [String] } diff --git a/TnT/Projects/Domain/Sources/Entity/MyPageEntity.swift b/TnT/Projects/Domain/Sources/Entity/MyPageEntity.swift new file mode 100644 index 00000000..ed37bbbb --- /dev/null +++ b/TnT/Projects/Domain/Sources/Entity/MyPageEntity.swift @@ -0,0 +1,31 @@ +// +// MyPageEntity.swift +// Domain +// +// Created by 박민서 on 2/12/25. +// Copyright © 2025 yapp25thTeamTnT. All rights reserved. +// + +import Foundation + +public struct TraineeMyPageEntity: Equatable, Sendable { + /// 트레이니 이름 + public let name: String + /// 트레이니 프로필 이미지 URL + public let profileImageUrl: String + /// 소셜 타입 + public let socialType: String +} + +public struct TrainerMyPageEntity: Equatable, Sendable { + /// 트레이너 이름 + public let name: String + /// 트레이너 프로필 이미지 URL + public let profileImageUrl: String + /// 소셜 타입 + public let socialType: String + /// 관리 중인 회원 수 + public let activeTraineeCount: Int? + /// 함께했던 회원 수 + public let totalTraineeCount: Int? +} diff --git a/TnT/Projects/Domain/Sources/Mapper/UserMapper.swift b/TnT/Projects/Domain/Sources/Mapper/UserMapper.swift new file mode 100644 index 00000000..4fabc31e --- /dev/null +++ b/TnT/Projects/Domain/Sources/Mapper/UserMapper.swift @@ -0,0 +1,29 @@ +// +// UserMapper.swift +// Domain +// +// Created by 박민서 on 2/12/25. +// Copyright © 2025 yapp25thTeamTnT. All rights reserved. +// + +import Foundation + +public extension GetMyPageInfoResDTO { + func toEntity() -> TraineeMyPageEntity { + return .init( + name: self.name, + profileImageUrl: self.profileImageUrl, + socialType: self.socialType + ) + } + + func toEntity() -> TrainerMyPageEntity { + return .init( + name: self.name, + profileImageUrl: self.profileImageUrl, + socialType: self.socialType, + activeTraineeCount: self.trainer?.activeTraineeCount, + totalTraineeCount: self.trainer?.totalTraineeCount + ) + } +} From bca687da74c68de67ca8a864ef88507c3c764483 Mon Sep 17 00:00:00 2001 From: Minseo Park <125115284+FpRaArNkK@users.noreply.github.com> Date: Wed, 12 Feb 2025 19:37:58 +0900 Subject: [PATCH 3/6] =?UTF-8?q?[Feat]=20Trainee/Trainer=20API,=20=ED=9D=90?= =?UTF-8?q?=EB=A6=84=20=EC=97=B0=EA=B2=B0=20=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../TraineeMainFlowFeature.swift | 5 ++++ .../TrainerMainFlowFeature.swift | 6 +++++ .../MyPage/Trainee/TraineeMyPageFeature.swift | 27 ++++++++++++++++--- .../MyPage/Trainer/TrainerMypageFeature.swift | 26 +++++++++++++++++- 4 files changed, 59 insertions(+), 5 deletions(-) diff --git a/TnT/Projects/Presentation/Sources/Coordinator/TraineeMainFlow/TraineeMainFlowFeature.swift b/TnT/Projects/Presentation/Sources/Coordinator/TraineeMainFlow/TraineeMainFlowFeature.swift index 8e8dbc05..3cf575d7 100644 --- a/TnT/Projects/Presentation/Sources/Coordinator/TraineeMainFlow/TraineeMainFlowFeature.swift +++ b/TnT/Projects/Presentation/Sources/Coordinator/TraineeMainFlow/TraineeMainFlowFeature.swift @@ -114,6 +114,11 @@ public struct TraineeMainFlowFeature { ) return .none + /// 연결 완료 화면 -> 홈으로 이동 + case .element(id: _, action: .traineeConnectionComplete(.setNavigating)): + state.path.removeLast(2) + return .none + default: return .none } diff --git a/TnT/Projects/Presentation/Sources/Coordinator/TrainerMainFlow/TrainerMainFlowFeature.swift b/TnT/Projects/Presentation/Sources/Coordinator/TrainerMainFlow/TrainerMainFlowFeature.swift index 7300085f..086970e3 100644 --- a/TnT/Projects/Presentation/Sources/Coordinator/TrainerMainFlow/TrainerMainFlowFeature.swift +++ b/TnT/Projects/Presentation/Sources/Coordinator/TrainerMainFlow/TrainerMainFlowFeature.swift @@ -71,10 +71,16 @@ public struct TrainerMainFlowFeature { state.path.removeLast() return .none + /// 연결 완료 -> 트레이니 정보 case .element(id: _, action: .connectionComplete(.setNavigating(let profile))): state.path.append(.connectedTraineeProfile(.init(traineeProfile: profile))) return .none + /// 트레이니 정보 -> 홈으로 + case .element(id: _, action: .connectedTraineeProfile(.setNavigating)): + state.path.removeLast(2) + return.none + default: return .none } diff --git a/TnT/Projects/Presentation/Sources/MyPage/Trainee/TraineeMyPageFeature.swift b/TnT/Projects/Presentation/Sources/MyPage/Trainee/TraineeMyPageFeature.swift index e170b45f..3da64f86 100644 --- a/TnT/Projects/Presentation/Sources/MyPage/Trainee/TraineeMyPageFeature.swift +++ b/TnT/Projects/Presentation/Sources/MyPage/Trainee/TraineeMyPageFeature.swift @@ -86,6 +86,8 @@ public struct TraineeMyPageFeature { case setAppPushNotificationAllowed(Bool) /// 푸시 알람 허용 시스템 화면 이동 case sendAppPushNotificationSetting + /// 마이페이지 정보 반영 + case setMyPageInfo(TraineeMyPageEntity) /// 팝업 세팅 처리 case setPopUpStatus(PopUp?) /// 네비게이션 여부 설정 @@ -125,6 +127,8 @@ public struct TraineeMyPageFeature { case logout /// 회원 탈퇴 API case withdraw + /// 마이페이지 정보 get API + case myPageInfo } } @@ -148,8 +152,7 @@ public struct TraineeMyPageFeature { return .none case .tapConnectTrainerButton: - print("tapConnectTrainerButton") - return .none + return .send(.setNavigating(.traineeInvitationCodeInput)) case .tapTOSButton: if let url = URL(string: AppLinks.termsOfService) { @@ -206,7 +209,10 @@ public struct TraineeMyPageFeature { } case .onAppear: - return self.getAppPushNotificationAllowed(state: &state, tryToggle: false) + return .merge( + self.getAppPushNotificationAllowed(state: &state, tryToggle: false), + .send(.api(.myPageInfo)) + ) } case .api(let action): @@ -217,14 +223,27 @@ public struct TraineeMyPageFeature { try keyChainManager.delete(.sessionId) await send(.setPopUpStatus(.logoutCompleted)) } + case .withdraw: return .run { send in let result = try await userUseRepoCase.postWithdrawal() try keyChainManager.delete(.sessionId) await send(.setPopUpStatus(.withdrawCompleted)) } + + case .myPageInfo: + return .run { send in + let result = try await userUseRepoCase.getMyPageInfo() + let info: TraineeMyPageEntity = result.toEntity() + await send(.setMyPageInfo(info)) + } } - + + case .setMyPageInfo(let myPageInfo): + state.userName = myPageInfo.name + state.userImageUrl = myPageInfo.profileImageUrl + return .none + case .setPopUpStatus(let popUp): state.view_popUp = popUp state.view_isPopUpPresented = popUp != nil diff --git a/TnT/Projects/Presentation/Sources/MyPage/Trainer/TrainerMypageFeature.swift b/TnT/Projects/Presentation/Sources/MyPage/Trainer/TrainerMypageFeature.swift index 3c977ec9..8dad7367 100644 --- a/TnT/Projects/Presentation/Sources/MyPage/Trainer/TrainerMypageFeature.swift +++ b/TnT/Projects/Presentation/Sources/MyPage/Trainer/TrainerMypageFeature.swift @@ -70,6 +70,8 @@ public struct TrainerMypageFeature { case setAppPushNotificationAllowed(Bool) /// 푸시 알람 허용 시스템 화면 이동 case sendAppPushNotificationSetting + /// 마이페이지 정보 반영 + case setMyPageInfo(TrainerMyPageEntity) /// 팝업 세팅 처리 case setPopUpStatus(PopUp?) /// 네비게이션 여부 설정 @@ -103,6 +105,8 @@ public struct TrainerMypageFeature { case logout /// 회원 탈퇴 API case withdraw + /// 마이페이지 정보 get API + case myPageInfo } } @@ -167,7 +171,10 @@ public struct TrainerMypageFeature { } case .onAppear: - return self.getAppPushNotificationAllowed(state: &state, tryToggle: false) + return .merge( + self.getAppPushNotificationAllowed(state: &state, tryToggle: false), + .send(.api(.myPageInfo)) + ) } case .api(let action): @@ -178,14 +185,31 @@ public struct TrainerMypageFeature { try keyChainManager.delete(.sessionId) await send(.setPopUpStatus(.logoutCompleted)) } + case .withdraw: return .run { send in let result = try await userUseRepoCase.postWithdrawal() try keyChainManager.delete(.sessionId) await send(.setPopUpStatus(.withdrawCompleted)) } + + case .myPageInfo: + return .run { send in + let result = try await userUseRepoCase.getMyPageInfo() + let info: TrainerMyPageEntity = result.toEntity() + await send(.setMyPageInfo(info)) + } } + case .setMyPageInfo(let myPageInfo): + state.userName = myPageInfo.name + state.userImageUrl = myPageInfo.profileImageUrl + if let activeCount = myPageInfo.activeTraineeCount, let totalCount = myPageInfo.totalTraineeCount { + state.studentCount = activeCount + state.oldStudentCount = totalCount + } + return .none + case .setPopUpStatus(let popUp): state.view_popUp = popUp state.view_isPopUpPresented = popUp != nil From a3192648fa15cd1004adb4b63c88eb791f380bb1 Mon Sep 17 00:00:00 2001 From: Minseo Park <125115284+FpRaArNkK@users.noreply.github.com> Date: Thu, 13 Feb 2025 05:18:47 +0900 Subject: [PATCH 4/6] =?UTF-8?q?[Feat]=20=ED=8A=B8=EB=A0=88=EC=9D=B4?= =?UTF-8?q?=EB=8B=88=20isConnected=20=EA=B4=80=EB=A0=A8=20API=20=EC=97=85?= =?UTF-8?q?=EB=8D=B0=EC=9D=B4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Domain/Sources/DTO/User/UserResponseDTO.swift | 9 +++++++-- TnT/Projects/Domain/Sources/Entity/MyPageEntity.swift | 2 ++ TnT/Projects/Domain/Sources/Mapper/UserMapper.swift | 1 + 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/TnT/Projects/Domain/Sources/DTO/User/UserResponseDTO.swift b/TnT/Projects/Domain/Sources/DTO/User/UserResponseDTO.swift index 75f836a9..c9bc49f1 100644 --- a/TnT/Projects/Domain/Sources/DTO/User/UserResponseDTO.swift +++ b/TnT/Projects/Domain/Sources/DTO/User/UserResponseDTO.swift @@ -10,6 +10,9 @@ import Foundation /// 로그인 세션 유효 확인 응답 DTO public struct GetSessionCheckResDTO: Decodable { + /// 트레이너/트레이니 연결 여부 + public let isConnected: Bool + /// 멤버 유형 public let memberType: MemberTypeResDTO } @@ -132,10 +135,12 @@ public struct TrainerInfoResDTO: Decodable { /// 트레이니 정보 표시에 사용되는 TraineeInfoDTO public struct TraineeInfoResDTO: Decodable { + /// 트레이너 연결 여부 + public let isConnected: Bool /// 생년월일 - public let birthday: String + public let birthday: String? /// 나이 - public let age: Int + public let age: Int? /// 진행한 PT 횟수 public let height: Double? /// 총 PT 횟수 diff --git a/TnT/Projects/Domain/Sources/Entity/MyPageEntity.swift b/TnT/Projects/Domain/Sources/Entity/MyPageEntity.swift index ed37bbbb..b46eb8e4 100644 --- a/TnT/Projects/Domain/Sources/Entity/MyPageEntity.swift +++ b/TnT/Projects/Domain/Sources/Entity/MyPageEntity.swift @@ -9,6 +9,8 @@ import Foundation public struct TraineeMyPageEntity: Equatable, Sendable { + /// 트레이니 연결 여부 + public let isConnected: Bool /// 트레이니 이름 public let name: String /// 트레이니 프로필 이미지 URL diff --git a/TnT/Projects/Domain/Sources/Mapper/UserMapper.swift b/TnT/Projects/Domain/Sources/Mapper/UserMapper.swift index 4fabc31e..80b8d61d 100644 --- a/TnT/Projects/Domain/Sources/Mapper/UserMapper.swift +++ b/TnT/Projects/Domain/Sources/Mapper/UserMapper.swift @@ -11,6 +11,7 @@ import Foundation public extension GetMyPageInfoResDTO { func toEntity() -> TraineeMyPageEntity { return .init( + isConnected: self.trainee?.isConnected ?? false, name: self.name, profileImageUrl: self.profileImageUrl, socialType: self.socialType From 55812a11b34be76a5c603d1c3f1f7237c22d5c34 Mon Sep 17 00:00:00 2001 From: Minseo Park <125115284+FpRaArNkK@users.noreply.github.com> Date: Thu, 13 Feb 2025 05:19:31 +0900 Subject: [PATCH 5/6] =?UTF-8?q?[Feat]=20isConnected=20=EA=B4=80=EB=A0=A8?= =?UTF-8?q?=20AppStorage=20=EC=B6=94=EA=B0=80,=20=EC=95=B1=20=EC=BD=94?= =?UTF-8?q?=EB=94=94=EB=84=A4=EC=9D=B4=ED=84=B0=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Domain/Sources/Policy/AppStorage.swift | 1 + .../AppFlow/AppFlowCoordinatorFeature.swift | 28 +++++++++++++------ 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/TnT/Projects/Domain/Sources/Policy/AppStorage.swift b/TnT/Projects/Domain/Sources/Policy/AppStorage.swift index 19971aaa..9c5a059f 100644 --- a/TnT/Projects/Domain/Sources/Policy/AppStorage.swift +++ b/TnT/Projects/Domain/Sources/Policy/AppStorage.swift @@ -10,4 +10,5 @@ import Foundation public enum AppStorage { public static let hideHomePopupUntil: String = "hideHomePopupUntil" + public static let isConnected: String = "isConnected" } diff --git a/TnT/Projects/Presentation/Sources/Coordinator/AppFlow/AppFlowCoordinatorFeature.swift b/TnT/Projects/Presentation/Sources/Coordinator/AppFlow/AppFlowCoordinatorFeature.swift index 81b9930e..707c5313 100644 --- a/TnT/Projects/Presentation/Sources/Coordinator/AppFlow/AppFlowCoordinatorFeature.swift +++ b/TnT/Projects/Presentation/Sources/Coordinator/AppFlow/AppFlowCoordinatorFeature.swift @@ -23,9 +23,14 @@ public struct AppFlowCoordinatorFeature { @ObservableState public struct State: Equatable { // MARK: Data related state + /// 트레이너/트레이니 연결 여부 + @Shared(.appStorage(AppStorage.isConnected)) var isConnected: Bool = false + /// 유저 멤버 유형 var userType: UserType? // MARK: UI related state + /// 스플래시 표시 여부 var view_isSplashActive: Bool + /// 팝업 표시 여부 var view_isPopUpPresented: Bool // MARK: SubFeature state @@ -59,7 +64,7 @@ public struct AppFlowCoordinatorFeature { /// 저장 세션 정보 확인 case checkSessionInfo /// 현재 유저 정보 업데이트 - case updateUserInfo(UserType?) + case updateUserInfo(type: UserType?, isConnected: Bool) /// 스플래시 표시 종료 시 case splashFinished /// 세션 만료 팝업 표시 @@ -139,15 +144,20 @@ public struct AppFlowCoordinatorFeature { switch action { case .checkSession: return .run { send in - let result = try? await userUseCaseRepo.getSessionCheck() - switch result?.memberType { + guard let result = try? await userUseCaseRepo.getSessionCheck() else { + try keyChainManager.delete(.sessionId) + await send(.updateUserInfo(type: nil, isConnected: false)) + return + } + + switch result.memberType { case .trainer: - await send(.updateUserInfo(.trainer)) + await send(.updateUserInfo(type: .trainer, isConnected: result.isConnected)) case .trainee: - await send(.updateUserInfo(.trainee)) + await send(.updateUserInfo(type: .trainee, isConnected: result.isConnected)) default: try keyChainManager.delete(.sessionId) - await send(.updateUserInfo(nil)) + await send(.updateUserInfo(type: nil, isConnected: false)) } } } @@ -156,9 +166,11 @@ public struct AppFlowCoordinatorFeature { let session: String? = try? keyChainManager.read(for: .sessionId) return session != nil ? .send(.api(.checkSession)) - : .send(.updateUserInfo(nil)) + : .send(.updateUserInfo(type: nil, isConnected: false)) + + case let .updateUserInfo(userType, isConnected): + state.$isConnected.withLock { $0 = isConnected } - case .updateUserInfo(let userType): switch userType { case .trainee: return self.setFlow(.traineeMainFlow, state: &state) From eb923097b9d420b75193e6a60af2e7c9da94b02f Mon Sep 17 00:00:00 2001 From: Minseo Park <125115284+FpRaArNkK@users.noreply.github.com> Date: Thu, 13 Feb 2025 05:20:05 +0900 Subject: [PATCH 6/6] =?UTF-8?q?[Feat]=20=ED=8A=B8=EB=A0=88=EC=9D=B4?= =?UTF-8?q?=EB=8B=88=20Home,=20Mypage=20-=20AppStroage-isConnected=20UI=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/Home/Trainee/TraineeHomeFeature.swift | 14 +++++--------- .../MyPage/Trainee/TraineeMyPageFeature.swift | 3 +++ .../Sources/MyPage/Trainee/TraineeMyPageView.swift | 2 +- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/TnT/Projects/Presentation/Sources/Home/Trainee/TraineeHomeFeature.swift b/TnT/Projects/Presentation/Sources/Home/Trainee/TraineeHomeFeature.swift index 9de4d833..19fb1e39 100644 --- a/TnT/Projects/Presentation/Sources/Home/Trainee/TraineeHomeFeature.swift +++ b/TnT/Projects/Presentation/Sources/Home/Trainee/TraineeHomeFeature.swift @@ -19,6 +19,8 @@ public struct TraineeHomeFeature { // MARK: Data related state /// 3일 동안 보지 않기 시작 날짜 @Shared(.appStorage(AppStorage.hideHomePopupUntil)) var hidePopupUntil: Date? + /// 트레이너 연결 여부 + @Shared(.appStorage(AppStorage.isConnected)) var isConnected: Bool = false /// 선택된 날짜 var selectedDate: Date /// 캘린더 이벤트 @@ -29,8 +31,6 @@ public struct TraineeHomeFeature { var records: [RecordListItemEntity] /// 3일 동안 보지 않기 선택되었는지 여부 var isHideUntilSelected: Bool - /// 트레이너 연결 여부 - var isConnected: Bool // MARK: UI related state /// 캘린더 표시 페이지 @@ -55,7 +55,6 @@ public struct TraineeHomeFeature { sessionInfo: WorkoutListItemEntity? = nil, records: [RecordListItemEntity] = [], isHideUntilSelected: Bool = false, - isConnected: Bool = false, view_currentPage: Date = .now, view_isBottomSheetPresented: Bool = false, view_isPopUpPresented: Bool = false @@ -65,7 +64,6 @@ public struct TraineeHomeFeature { self.sessionInfo = sessionInfo self.records = records self.isHideUntilSelected = isHideUntilSelected - self.isConnected = isConnected self.view_currentPage = view_currentPage self.view_isBottomSheetPresented = view_isBottomSheetPresented self.view_isPopUpPresented = view_isPopUpPresented @@ -172,11 +170,9 @@ public struct TraineeHomeFeature { return .send(.setNavigating(.traineeInvitationCodeInput)) case .onAppear: - if let hideUntil = state.hidePopupUntil, hideUntil > Date() { - state.view_isPopUpPresented = false - } else { - state.view_isPopUpPresented = true - } + let hideUntil = state.hidePopupUntil ?? Date() + let hidePopUp = state.isConnected || hideUntil > Date() + state.view_isPopUpPresented = !hidePopUp return .none } diff --git a/TnT/Projects/Presentation/Sources/MyPage/Trainee/TraineeMyPageFeature.swift b/TnT/Projects/Presentation/Sources/MyPage/Trainee/TraineeMyPageFeature.swift index 3da64f86..18a46dab 100644 --- a/TnT/Projects/Presentation/Sources/MyPage/Trainee/TraineeMyPageFeature.swift +++ b/TnT/Projects/Presentation/Sources/MyPage/Trainee/TraineeMyPageFeature.swift @@ -24,6 +24,8 @@ public struct TraineeMyPageFeature { // MARK: Data related state /// 3일 동안 보지 않기 시작 날짜 @Shared(.appStorage(AppStorage.hideHomePopupUntil)) var hidePopupUntil: Date? + /// 트레이너 연결 여부 + @Shared(.appStorage(AppStorage.isConnected)) var isConnected: Bool = false /// 사용자 이름 var userName: String /// 사용자 이미지 URL @@ -240,6 +242,7 @@ public struct TraineeMyPageFeature { } case .setMyPageInfo(let myPageInfo): + state.$isConnected.withLock { $0 = myPageInfo.isConnected } state.userName = myPageInfo.name state.userImageUrl = myPageInfo.profileImageUrl return .none diff --git a/TnT/Projects/Presentation/Sources/MyPage/Trainee/TraineeMyPageView.swift b/TnT/Projects/Presentation/Sources/MyPage/Trainee/TraineeMyPageView.swift index 5f1b318c..b7db4067 100644 --- a/TnT/Projects/Presentation/Sources/MyPage/Trainee/TraineeMyPageView.swift +++ b/TnT/Projects/Presentation/Sources/MyPage/Trainee/TraineeMyPageView.swift @@ -70,7 +70,7 @@ public struct TraineeMyPageView: View { @ViewBuilder private func TopItemSection() -> some View { VStack(spacing: 12) { - if !store.view_isTrainerConnected { + if !store.isConnected { ProfileItemView(title: "트레이너 연결하기", tapAction: { send(.tapConnectTrainerButton) }) .padding(.vertical, 4) .background(Color.common0)