diff --git a/OPeace/Projects/App/OpeaceTests/Sources/ProfileTest.swift b/OPeace/Projects/App/OpeaceTests/Sources/ProfileTest.swift index 1ab425b..2d0b053 100644 --- a/OPeace/Projects/App/OpeaceTests/Sources/ProfileTest.swift +++ b/OPeace/Projects/App/OpeaceTests/Sources/ProfileTest.swift @@ -18,51 +18,49 @@ import Utills @MainActor struct ProfileTest { - let store = TestStore(initialState: Profile.State()) { - Profile() - } withDependencies: { - let repository = AuthRepository() - $0.authUseCase = AuthUseCase(repository: repository) + let store = TestStore(initialState: Profile.State()) { + Profile() + } withDependencies: { + let repository = AuthRepository() + $0.authUseCase = AuthUseCase(repository: repository) + } + + @Test("유저 정보 조회") + func testUserInfo_유저정보_조회() async throws { + var mockUpdateUserInfo = UpdateUserInfoDTOModel.mockModel + + await store.send(.async(.fetchUserProfileResponse(.success(mockUpdateUserInfo)))) { state in + state.profileUserModel = mockUpdateUserInfo } - - @Test("유저 정보 조회") - func testUserInfo_유저정보_조회() async throws { - var mockUpdateUserInfo = UpdateUserInfoModel.mockModel - - await store.send(.async(.fetchUserProfileResponse(.success(mockUpdateUserInfo)))) { state in - state.profileUserModel = mockUpdateUserInfo - } - - await store.send(.async(.fetchUser)) - - store.assert { state in - state.profileUserModel = mockUpdateUserInfo - } - - let mockError = CustomError.unknownError("Failed to fetch user info") - - - await store.send(.async(.fetchUserProfileResponse(.failure(mockError)))) { - XCTAssertNil($0.profileUserModel) - } - - - - await store.finish() - store.exhaustivity = .off + + await store.send(.async(.fetchUser)) + + store.assert { state in + state.profileUserModel = mockUpdateUserInfo } - @Test("유저 정보 업데이트") - func testUserInfo_유저정보_업데이트() async throws { - let testEditStore = TestStore(initialState: EditProfile.State()) { - EditProfile() - } withDependencies: { - let repository = AuthRepository() - $0.authUseCase = AuthUseCase(repository: repository) - let signupRepository = SingUpRepository() - $0.signUpUseCase = SignUpUseCase(repository: signupRepository) - } - - await testEditStore.send(.async(.updateUserInfo(nickName: "로이", year: 1998, job: "개발", generation: "Z 세대"))) + let mockError = CustomError.unknownError("Failed to fetch user info") + + + await store.send(.async(.fetchUserProfileResponse(.failure(mockError)))) { + XCTAssertNil($0.profileUserModel) + } + + await store.finish() + store.exhaustivity = .off + } + + @Test("유저 정보 업데이트") + func testUserInfo_유저정보_업데이트() async throws { + let testEditStore = TestStore(initialState: EditProfile.State()) { + EditProfile() + } withDependencies: { + let repository = AuthRepository() + $0.authUseCase = AuthUseCase(repository: repository) + let signupRepository = SingUpRepository() + $0.signUpUseCase = SignUpUseCase(repository: signupRepository) } + + await testEditStore.send(.async(.updateUserInfo(nickName: "로이", year: 1998, job: "개발", generation: "Z 세대"))) + } } diff --git a/OPeace/Projects/Core/Networking/Model/Sources/Questions/DTO/FlagQuestionDTOModel.swift b/OPeace/Projects/Core/Networking/Model/Sources/Questions/DTO/FlagQuestionDTOModel.swift new file mode 100644 index 0000000..2ad8800 --- /dev/null +++ b/OPeace/Projects/Core/Networking/Model/Sources/Questions/DTO/FlagQuestionDTOModel.swift @@ -0,0 +1,41 @@ +// +// FlagQuestionDTOModel.swift +// Model +// +// Created by Wonji Suh on 11/11/24. +// + +import Foundation + +public struct FlagQuestionDTOModel: Codable, Equatable { + public let data: FlagQuestionResponseDTOModel + + public init(data: FlagQuestionResponseDTOModel) { + self.data = data + } +} + +public struct FlagQuestionResponseDTOModel: Codable, Equatable { + public let id, question: Int + public let userID, reason, createAt: String + public let status: Bool + public let message: String + + public init( + id: Int = .zero, + question: Int = .zero, + userID: String = "", + reason: String = "", + createAt: String = "", + status: Bool = false, + message: String = "" + ) { + self.id = id + self.question = question + self.userID = userID + self.reason = reason + self.createAt = createAt + self.status = status + self.message = message + } +} diff --git a/OPeace/Projects/Core/Networking/Model/Sources/Questions/DeleteQuestionModel.swift b/OPeace/Projects/Core/Networking/Model/Sources/Questions/DeleteQuestionModel.swift deleted file mode 100644 index 5b91144..0000000 --- a/OPeace/Projects/Core/Networking/Model/Sources/Questions/DeleteQuestionModel.swift +++ /dev/null @@ -1,32 +0,0 @@ -// -// DeleteQuestionModel.swift -// Model -// -// Created by 서원지 on 8/26/24. -// - -import Foundation - -public struct DeleteQuestionModel: Codable, Equatable { - public let data: DeleteQuestionResponseModel? - - public init( - data: DeleteQuestionResponseModel? - ) { - self.data = data - } -} - -// MARK: - DataClass -public struct DeleteQuestionResponseModel: Codable, Equatable { - public let status: Bool? - public let message: String? - - public init( - status: Bool?, - message: String? - ) { - self.status = status - self.message = message - } -} diff --git a/OPeace/Projects/Core/Networking/Model/Sources/Questions/Extension/Extension+DeleteQuestionModel.swift b/OPeace/Projects/Core/Networking/Model/Sources/Questions/Extension/Extension+DeleteQuestionModel.swift new file mode 100644 index 0000000..ed25096 --- /dev/null +++ b/OPeace/Projects/Core/Networking/Model/Sources/Questions/Extension/Extension+DeleteQuestionModel.swift @@ -0,0 +1,17 @@ +// +// Extension+DeleteQuestionModel.swift +// Model +// +// Created by Wonji Suh on 11/11/24. +// + +public extension DeleteQuestionModel { + func toFlagQuestionDTOToModel() -> FlagQuestionDTOModel { + let data: FlagQuestionResponseDTOModel = .init( + status: self.data?.status ?? false, + message: self.data?.message ?? "" + ) + + return FlagQuestionDTOModel(data: data) + } +} diff --git a/OPeace/Projects/Core/Networking/Model/Sources/Questions/Extension/Extension+ReportQuestionModel.swift b/OPeace/Projects/Core/Networking/Model/Sources/Questions/Extension/Extension+ReportQuestionModel.swift new file mode 100644 index 0000000..c0fef93 --- /dev/null +++ b/OPeace/Projects/Core/Networking/Model/Sources/Questions/Extension/Extension+ReportQuestionModel.swift @@ -0,0 +1,22 @@ +// +// Extension+ReportQuestionModel.swift +// Model +// +// Created by Wonji Suh on 11/11/24. +// + +import Foundation + +public extension ReportQuestionModel { + func toFlagQuestionDTOToModel() -> FlagQuestionDTOModel { + let data: FlagQuestionResponseDTOModel = .init( + id: self.data?.id ?? .zero, + question: self.data?.question ?? .zero, + userID: self.data?.userID ?? "", + reason: self.data?.reason ?? "", + createAt: self.data?.createAt ?? "" + ) + + return FlagQuestionDTOModel(data: data) + } +} diff --git a/OPeace/Projects/Core/Networking/Model/Sources/Questions/Extension/Extension+VoteQuestionLikeModel.swift b/OPeace/Projects/Core/Networking/Model/Sources/Questions/Extension/Extension+VoteQuestionLikeModel.swift new file mode 100644 index 0000000..6836b88 --- /dev/null +++ b/OPeace/Projects/Core/Networking/Model/Sources/Questions/Extension/Extension+VoteQuestionLikeModel.swift @@ -0,0 +1,20 @@ +// +// Extension+VoteQuestionLikeModel.swift +// Model +// +// Created by Wonji Suh on 11/11/24. +// + +import Foundation + +public extension VoteQuestionLikeModel { + func toFlagQuestionDTOToModel() -> FlagQuestionDTOModel { + let data: FlagQuestionResponseDTOModel = .init( + question: self.data?.question ?? .zero, + userID: self.data?.userID ?? "", + createAt: self.data?.createAt ?? "" + ) + + return FlagQuestionDTOModel(data: data) + } +} diff --git a/OPeace/Projects/Core/Networking/Model/Sources/Questions/Model/DeleteQuestionModel.swift b/OPeace/Projects/Core/Networking/Model/Sources/Questions/Model/DeleteQuestionModel.swift new file mode 100644 index 0000000..9ae4e85 --- /dev/null +++ b/OPeace/Projects/Core/Networking/Model/Sources/Questions/Model/DeleteQuestionModel.swift @@ -0,0 +1,20 @@ +// +// DeleteQuestionModel.swift +// Model +// +// Created by 서원지 on 8/26/24. +// + +import Foundation + +public struct DeleteQuestionModel: Decodable { + let data: DeleteQuestionResponseModel? + +} + +// MARK: - DataClass +struct DeleteQuestionResponseModel: Decodable { + let status: Bool? + let message: String? + +} diff --git a/OPeace/Projects/Core/Networking/Model/Sources/Questions/Model/ReportQuestion.swift b/OPeace/Projects/Core/Networking/Model/Sources/Questions/Model/ReportQuestion.swift new file mode 100644 index 0000000..695d467 --- /dev/null +++ b/OPeace/Projects/Core/Networking/Model/Sources/Questions/Model/ReportQuestion.swift @@ -0,0 +1,27 @@ +// +// ReportQuestion.swift +// Model +// +// Created by 서원지 on 8/26/24. +// + +import Foundation +// MARK: - Welcome +public struct ReportQuestionModel: Decodable { + let data: ReportQuestionResponseModel? + +} + +// MARK: - DataClass +struct ReportQuestionResponseModel: Decodable { + let id, question: Int? + let userID, reason, createAt: String? + + enum CodingKeys: String, CodingKey { + case id, question + case userID = "user_id" + case reason + case createAt = "create_at" + } +} + diff --git a/OPeace/Projects/Core/Networking/Model/Sources/Questions/VoteQuestionLikeModel.swift b/OPeace/Projects/Core/Networking/Model/Sources/Questions/Model/VoteQuestionLikeModel.swift similarity index 97% rename from OPeace/Projects/Core/Networking/Model/Sources/Questions/VoteQuestionLikeModel.swift rename to OPeace/Projects/Core/Networking/Model/Sources/Questions/Model/VoteQuestionLikeModel.swift index bb52927..57ca54c 100644 --- a/OPeace/Projects/Core/Networking/Model/Sources/Questions/VoteQuestionLikeModel.swift +++ b/OPeace/Projects/Core/Networking/Model/Sources/Questions/Model/VoteQuestionLikeModel.swift @@ -5,6 +5,8 @@ // Created by 서원지 on 8/25/24. // +import Foundation + public struct VoteQuestionLikeModel: Codable, Equatable { public let data: VoteQuestionLikeResponse? diff --git a/OPeace/Projects/Core/Networking/Model/Sources/Questions/ReportQuestion.swift b/OPeace/Projects/Core/Networking/Model/Sources/Questions/ReportQuestion.swift deleted file mode 100644 index 7e8b658..0000000 --- a/OPeace/Projects/Core/Networking/Model/Sources/Questions/ReportQuestion.swift +++ /dev/null @@ -1,46 +0,0 @@ -// -// ReportQuestion.swift -// Model -// -// Created by 서원지 on 8/26/24. -// - -import Foundation -// MARK: - Welcome -public struct ReportQuestionModel: Codable, Equatable { - public let data: ReportQuestionResponseModel? - - public init( - data: ReportQuestionResponseModel? - ) { - self.data = data - } -} - -// MARK: - DataClass -public struct ReportQuestionResponseModel: Codable, Equatable { - public let id, question: Int? - public let userID, reason, createAt: String? - - public enum CodingKeys: String, CodingKey { - case id, question - case userID = "user_id" - case reason - case createAt = "create_at" - } - - public init( - id: Int?, - question: Int?, - userID: String?, - reason: String?, - createAt: String? - ) { - self.id = id - self.question = question - self.userID = userID - self.reason = reason - self.createAt = createAt - } -} - diff --git a/OPeace/Projects/Core/Networking/Model/Sources/SignUp/Extension/Extension+UpdateUserInfoDTOModel.swift b/OPeace/Projects/Core/Networking/Model/Sources/SignUp/Extension/Extension+UpdateUserInfoDTOModel.swift new file mode 100644 index 0000000..5632530 --- /dev/null +++ b/OPeace/Projects/Core/Networking/Model/Sources/SignUp/Extension/Extension+UpdateUserInfoDTOModel.swift @@ -0,0 +1,40 @@ +// +// Extension+UpdateUserInfoDTOModel.swift +// Model +// +// Created by Wonji Suh on 11/11/24. +// + +public extension UpdateUserInfoModel { + func toUpdateUserInfoDTOToModel() -> UpdateUserInfoDTOModel { + let data: UpdateUserInfoResponseDTOModel = .init( + socialID: self.data?.socialID ?? "", + socialType: self.data?.socialType ?? "", + email: self.data?.email ?? "", + createdAt: self.data?.createdAt ?? "", + nickname: self.data?.nickname, + year: self.data?.year , + job: self.data?.job, + generation: self.data?.generation ?? "", + isFirstLogin: self.data?.isFirstLogin ?? false + ) + + return UpdateUserInfoDTOModel(data: data) + } +} + +public extension UpdateUserInfoDTOModel { + static var mockModel: UpdateUserInfoDTOModel = UpdateUserInfoDTOModel( + data: UpdateUserInfoResponseDTOModel( + socialID: "apple_001096.cf7680b2761f4de694a1d1c21ea507a6.1112", + socialType: "apple", + email: "shuwj81@daum.net", + createdAt: "2024-08-29 01:39:57", + nickname: "오피스", + year: 1998, + job: "개발", + generation: "Z 세대", + isFirstLogin: true + ) + ) +} diff --git a/OPeace/Projects/Core/Networking/Model/Sources/SignUp/Model/SignUpJobModelResponse.swift b/OPeace/Projects/Core/Networking/Model/Sources/SignUp/Model/SignUpJobModelResponse.swift index ffcd82c..d8f38eb 100644 --- a/OPeace/Projects/Core/Networking/Model/Sources/SignUp/Model/SignUpJobModelResponse.swift +++ b/OPeace/Projects/Core/Networking/Model/Sources/SignUp/Model/SignUpJobModelResponse.swift @@ -5,22 +5,15 @@ // Created by 서원지 on 7/27/24. // -public struct SignUpJobModel: Codable, Equatable { - public let data: SignUpJobModelResponse? +public struct SignUpJobModel: Decodable { + let data: SignUpJobModelResponse? - public init(data: SignUpJobModelResponse?) { - self.data = data - } } -public struct SignUpJobModelResponse: Codable, Equatable { - public let data: [String]? +struct SignUpJobModelResponse: Decodable { + let data: [String]? - public init(data: [String]?) { - self.data = data - - } - public enum Component: String, Codable { + enum Component: String, Codable { case data } } diff --git a/OPeace/Projects/Core/Networking/Model/Sources/SignUp/UpdateUserInfoModel.swift b/OPeace/Projects/Core/Networking/Model/Sources/SignUp/UpdateUserInfoModel.swift deleted file mode 100644 index 1d22871..0000000 --- a/OPeace/Projects/Core/Networking/Model/Sources/SignUp/UpdateUserInfoModel.swift +++ /dev/null @@ -1,56 +0,0 @@ -// -// UpdateUserInfoModel.swift -// Model -// -// Created by 서원지 on 7/30/24. -// - -import Foundation - -// MARK: - Welcome -public struct UpdateUserInfoModel: Codable, Equatable { - public var data: UpdateUserInfoResponse? - - public init(data: UpdateUserInfoResponse?) { - self.data = data - } - -} - -// MARK: - DataClass -public struct UpdateUserInfoResponse: Codable , Equatable { - public let socialID, socialType, email: String? - public let phone: String? - public let createdAt, lastLogin, nickname: String? - public let year: Int? - public let job, generation: String? - public let isFirstLogin: Bool? - - enum CodingKeys: String, CodingKey { - case socialID = "social_id" - case socialType = "social_type" - case email, phone - case createdAt = "created_at" - case lastLogin = "last_login" - case nickname, year, job, generation - case isFirstLogin = "is_first_login" - } -} - -public extension UpdateUserInfoModel { - static var mockModel: UpdateUserInfoModel = UpdateUserInfoModel( - data: UpdateUserInfoResponse( - socialID: "apple_001096.cf7680b2761f4de694a1d1c21ea507a6.1112", - socialType: "apple", - email: "shuwj81@daum.net", - phone: nil, - createdAt: "2024-08-29 01:39:57", - lastLogin: "2024-08-30 18:25:17", - nickname: "오피스", - year: 1998, - job: "개발", - generation: "Z 세대", - isFirstLogin: true - ) - ) -} diff --git a/OPeace/Projects/Core/Networking/UseCase/Sources/Auth/Repository/AuthRepository.swift b/OPeace/Projects/Core/Networking/UseCase/Sources/Auth/Repository/AuthRepository.swift index 5fcc3b1..c002d43 100644 --- a/OPeace/Projects/Core/Networking/UseCase/Sources/Auth/Repository/AuthRepository.swift +++ b/OPeace/Projects/Core/Networking/UseCase/Sources/Auth/Repository/AuthRepository.swift @@ -255,8 +255,9 @@ import SwiftJWT } //MARK: - 유저정보 조회 API - public func fetchUserInfo() async throws -> UpdateUserInfoModel? { - return try await authProvider.requestAsync(.fetchUserInfo, decodeTo: UpdateUserInfoModel.self) + public func fetchUserInfo() async throws -> UpdateUserInfoDTOModel? { + let updateUserInfoModel = try await authProvider.requestAsync(.fetchUserInfo, decodeTo: UpdateUserInfoModel.self) + return updateUserInfoModel.toUpdateUserInfoDTOToModel() } //MARK: - 유저 로그아웃 API diff --git a/OPeace/Projects/Core/Networking/UseCase/Sources/Auth/Repository/AuthRepositoryProtocol.swift b/OPeace/Projects/Core/Networking/UseCase/Sources/Auth/Repository/AuthRepositoryProtocol.swift index 70bcff9..470ea73 100644 --- a/OPeace/Projects/Core/Networking/UseCase/Sources/Auth/Repository/AuthRepositoryProtocol.swift +++ b/OPeace/Projects/Core/Networking/UseCase/Sources/Auth/Repository/AuthRepositoryProtocol.swift @@ -16,7 +16,7 @@ public protocol AuthRepositoryProtocol { func requestKakaoTokenAsync() async throws -> (String?, String?) func reauestKakaoLogin() async throws -> OAuthDTOModel? func requestRefreshToken(refreshToken : String) async throws -> RefreshDTOModel? - func fetchUserInfo() async throws -> UpdateUserInfoModel? + func fetchUserInfo() async throws -> UpdateUserInfoDTOModel? func logoutUser(refreshToken : String) async throws -> UserDTOModel? func autoLogin() async throws -> UserDTOModel? func deleteUser(reason: String) async throws -> DeletUserDTOModel? diff --git a/OPeace/Projects/Core/Networking/UseCase/Sources/Auth/Repository/DefaultAuthRepository.swift b/OPeace/Projects/Core/Networking/UseCase/Sources/Auth/Repository/DefaultAuthRepository.swift index 3c02aee..3b32b2e 100644 --- a/OPeace/Projects/Core/Networking/UseCase/Sources/Auth/Repository/DefaultAuthRepository.swift +++ b/OPeace/Projects/Core/Networking/UseCase/Sources/Auth/Repository/DefaultAuthRepository.swift @@ -46,7 +46,7 @@ public final class DefaultAuthRepository: AuthRepositoryProtocol { return nil } - public func fetchUserInfo() async throws -> UpdateUserInfoModel? { + public func fetchUserInfo() async throws -> UpdateUserInfoDTOModel? { return nil } diff --git a/OPeace/Projects/Core/Networking/UseCase/Sources/Auth/UseCase/ AuthUseCaseProtocol.swift b/OPeace/Projects/Core/Networking/UseCase/Sources/Auth/UseCase/ AuthUseCaseProtocol.swift index 30ca88a..6349299 100644 --- a/OPeace/Projects/Core/Networking/UseCase/Sources/Auth/UseCase/ AuthUseCaseProtocol.swift +++ b/OPeace/Projects/Core/Networking/UseCase/Sources/Auth/UseCase/ AuthUseCaseProtocol.swift @@ -16,7 +16,7 @@ public protocol AuthUseCaseProtocol { func appleLogin() async throws -> OAuthDTOModel? func reauestKakaoLogin() async throws -> OAuthDTOModel? func requestRefreshToken(refreshToken : String) async throws -> RefreshDTOModel? - func fetchUserInfo() async throws -> UpdateUserInfoModel? + func fetchUserInfo() async throws -> UpdateUserInfoDTOModel? func logoutUser(refreshToken : String) async throws -> UserDTOModel? func autoLogin() async throws -> UserDTOModel? func deleteUser(reason: String) async throws -> DeletUserDTOModel? diff --git a/OPeace/Projects/Core/Networking/UseCase/Sources/Auth/UseCase/AuthUseCase.swift b/OPeace/Projects/Core/Networking/UseCase/Sources/Auth/UseCase/AuthUseCase.swift index 2d63c55..934b206 100644 --- a/OPeace/Projects/Core/Networking/UseCase/Sources/Auth/UseCase/AuthUseCase.swift +++ b/OPeace/Projects/Core/Networking/UseCase/Sources/Auth/UseCase/AuthUseCase.swift @@ -71,7 +71,7 @@ public struct AuthUseCase: AuthUseCaseProtocol { } //MARK: - 유저 정보 조회 - public func fetchUserInfo() async throws -> UpdateUserInfoModel? { + public func fetchUserInfo() async throws -> UpdateUserInfoDTOModel? { try await repository.fetchUserInfo() } diff --git a/OPeace/Projects/Core/Networking/UseCase/Sources/Question/Repository/DefaultQuestionRepository.swift b/OPeace/Projects/Core/Networking/UseCase/Sources/Question/Repository/DefaultQuestionRepository.swift index d861742..6403a87 100644 --- a/OPeace/Projects/Core/Networking/UseCase/Sources/Question/Repository/DefaultQuestionRepository.swift +++ b/OPeace/Projects/Core/Networking/UseCase/Sources/Question/Repository/DefaultQuestionRepository.swift @@ -9,61 +9,61 @@ import Foundation import Model public final class DefaultQuestionRepository: QuestionRepositoryProtocol { + + public init() { - public init() { - - } - - public func fetchQuestionList( - page: Int, - pageSize: Int, - job: String, - generation: String, - sortBy: QuestionSort) async throws -> QuestionModel? { - return nil - } - - public func myQuestionList( - page: Int, - pageSize: Int - ) async throws -> QuestionModel? { - return nil - } - - public func createQuestion( - emoji: String, - title: String, - choiceA: String, - choiceB: String - ) async throws -> CreateQuestionModel? { - return nil - } - - public func isVoteQuestionLike(questionID: Int) async throws -> VoteQuestionLikeModel? { - return nil - } - - public func isVoteQuestionAnswer( - questionID: Int, - choicAnswer: String - ) async throws -> QuestionVoteModel? { - return nil - } - - public func deleteQuestion(questionID: Int) async throws -> DeleteQuestionModel? { - return nil - } - - public func reportQuestion( - questionID: Int, - reason: String - ) async throws -> ReportQuestionModel? { - return nil - } - - public func statusQuestion( - questionID: Int - ) async throws -> StatusQuestionModel? { - return nil + } + + public func fetchQuestionList( + page: Int, + pageSize: Int, + job: String, + generation: String, + sortBy: QuestionSort) async throws -> QuestionModel? { + return nil } + + public func myQuestionList( + page: Int, + pageSize: Int + ) async throws -> QuestionModel? { + return nil + } + + public func createQuestion( + emoji: String, + title: String, + choiceA: String, + choiceB: String + ) async throws -> CreateQuestionModel? { + return nil + } + + public func isVoteQuestionLike(questionID: Int) async throws -> FlagQuestionDTOModel? { + return nil + } + + public func isVoteQuestionAnswer( + questionID: Int, + choicAnswer: String + ) async throws -> QuestionVoteModel? { + return nil + } + + public func deleteQuestion(questionID: Int) async throws -> FlagQuestionDTOModel? { + return nil + } + + public func reportQuestion( + questionID: Int, + reason: String + ) async throws -> FlagQuestionDTOModel? { + return nil + } + + public func statusQuestion( + questionID: Int + ) async throws -> StatusQuestionModel? { + return nil + } } diff --git a/OPeace/Projects/Core/Networking/UseCase/Sources/Question/Repository/QuestionRepository.swift b/OPeace/Projects/Core/Networking/UseCase/Sources/Question/Repository/QuestionRepository.swift index a56f636..7e85a02 100644 --- a/OPeace/Projects/Core/Networking/UseCase/Sources/Question/Repository/QuestionRepository.swift +++ b/OPeace/Projects/Core/Networking/UseCase/Sources/Question/Repository/QuestionRepository.swift @@ -16,91 +16,94 @@ import Moya @Observable public class QuestionRepository: QuestionRepositoryProtocol { - private let provider = MoyaProvider(session: Session(interceptor: AuthInterceptor.shared), plugins: [MoyaLoggingPlugin()]) + private let provider = MoyaProvider(session: Session(interceptor: AuthInterceptor.shared), plugins: [MoyaLoggingPlugin()]) + + public init() { - public init() { - - } - - //MARK: - 피드 목록API - public func fetchQuestionList( - page: Int, - pageSize: Int, - job: String, - generation: String, - sortBy: QuestionSort - ) async throws -> QuestionModel? { - return try await provider.requestAsync(.fetchQuestionList( - page: page, - pageSize: pageSize, - job: job, - generation: generation, - sortBy: sortBy.questionSortDesc), decodeTo: QuestionModel.self) - } - - - //MARK: - 프로필 내가 쓴글 목록 조회 API - public func myQuestionList( - page: Int, - pageSize: Int - ) async throws -> QuestionModel? { - return try await provider.requestAsync(.myQuestionList( - page: page, - pageSize: pageSize - ), decodeTo: QuestionModel.self) - } - - //MARK: - 질문 생성API - public func createQuestion( - emoji: String, - title: String, - choiceA: String, - choiceB: String - ) async throws -> CreateQuestionModel? { - return try await provider.requestAsync(.createQuestion( - emoji: emoji, - title: title, - choiceA: choiceA, - choiceB: choiceB), decodeTo: CreateQuestionModel.self) - } - - //MARK: - 좋아요 API - public func isVoteQuestionLike(questionID: Int) async throws -> VoteQuestionLikeModel? { - return try await provider.requestAsync(.isVoteQustionLike( - id: questionID), decodeTo: VoteQuestionLikeModel.self) - } - - //MARK: - 유저 투표 API - public func isVoteQuestionAnswer( - questionID: Int, - choicAnswer: String - ) async throws -> QuestionVoteModel? { - return try await provider.requestAsync(.isVoteQuestionAnswer( - id: questionID, - userChoice: choicAnswer), decodeTo: QuestionVoteModel.self) - } - - //MARK: - 질문 삭제 API - public func deleteQuestion(questionID: Int) async throws -> DeleteQuestionModel? { - return try await provider.requestAsync(.deleteQuestion(id: questionID), decodeTo: DeleteQuestionModel.self) - } - - //MARK: - 유저 질문 신고 API - public func reportQuestion( - questionID: Int, - reason: String - ) async throws -> ReportQuestionModel? { - return try await provider.requestAsync(.reportQuestion( - id: questionID, - reason: reason - ), decodeTo: ReportQuestionModel.self) - } - - //MARK: - 유저 투표 결과 API - public func statusQuestion( - questionID: Int - ) async throws -> StatusQuestionModel? { - return try await provider.requestAsync(.statusQuestion( - id: questionID), decodeTo: StatusQuestionModel.self) - } + } + + //MARK: - 피드 목록API + public func fetchQuestionList( + page: Int, + pageSize: Int, + job: String, + generation: String, + sortBy: QuestionSort + ) async throws -> QuestionModel? { + return try await provider.requestAsync(.fetchQuestionList( + page: page, + pageSize: pageSize, + job: job, + generation: generation, + sortBy: sortBy.questionSortDesc), decodeTo: QuestionModel.self) + } + + + //MARK: - 프로필 내가 쓴글 목록 조회 API + public func myQuestionList( + page: Int, + pageSize: Int + ) async throws -> QuestionModel? { + return try await provider.requestAsync(.myQuestionList( + page: page, + pageSize: pageSize + ), decodeTo: QuestionModel.self) + } + + //MARK: - 질문 생성API + public func createQuestion( + emoji: String, + title: String, + choiceA: String, + choiceB: String + ) async throws -> CreateQuestionModel? { + return try await provider.requestAsync(.createQuestion( + emoji: emoji, + title: title, + choiceA: choiceA, + choiceB: choiceB), decodeTo: CreateQuestionModel.self) + } + + //MARK: - 좋아요 API + public func isVoteQuestionLike(questionID: Int) async throws -> FlagQuestionDTOModel? { + let voteQuestionLikeModel = try await provider.requestAsync(.isVoteQustionLike( + id: questionID), decodeTo: VoteQuestionLikeModel.self) + return voteQuestionLikeModel.toFlagQuestionDTOToModel() + } + + //MARK: - 유저 투표 API + public func isVoteQuestionAnswer( + questionID: Int, + choicAnswer: String + ) async throws -> QuestionVoteModel? { + return try await provider.requestAsync(.isVoteQuestionAnswer( + id: questionID, + userChoice: choicAnswer), decodeTo: QuestionVoteModel.self) + } + + //MARK: - 질문 삭제 API + public func deleteQuestion(questionID: Int) async throws -> FlagQuestionDTOModel? { + let deleteQuestionModel = try await provider.requestAsync(.deleteQuestion(id: questionID), decodeTo: DeleteQuestionModel.self) + return deleteQuestionModel.toFlagQuestionDTOToModel() + } + + //MARK: - 유저 질문 신고 API + public func reportQuestion( + questionID: Int, + reason: String + ) async throws -> FlagQuestionDTOModel? { + let reportQuestionModel = try await provider.requestAsync(.reportQuestion( + id: questionID, + reason: reason + ), decodeTo: ReportQuestionModel.self) + return reportQuestionModel.toFlagQuestionDTOToModel() + } + + //MARK: - 유저 투표 결과 API + public func statusQuestion( + questionID: Int + ) async throws -> StatusQuestionModel? { + return try await provider.requestAsync(.statusQuestion( + id: questionID), decodeTo: StatusQuestionModel.self) + } } diff --git a/OPeace/Projects/Core/Networking/UseCase/Sources/Question/Repository/QuestionRepositoryProtocol.swift b/OPeace/Projects/Core/Networking/UseCase/Sources/Question/Repository/QuestionRepositoryProtocol.swift index 8ae18de..3e9d886 100644 --- a/OPeace/Projects/Core/Networking/UseCase/Sources/Question/Repository/QuestionRepositoryProtocol.swift +++ b/OPeace/Projects/Core/Networking/UseCase/Sources/Question/Repository/QuestionRepositoryProtocol.swift @@ -9,24 +9,24 @@ import Foundation import Model public protocol QuestionRepositoryProtocol { - func fetchQuestionList( - page: Int, - pageSize: Int, - job: String, - generation: String, - sortBy: QuestionSort) async throws -> QuestionModel? - func myQuestionList(page: Int, pageSize: Int) async throws -> QuestionModel? - func createQuestion( - emoji: String, - title: String, - choiceA: String, - choiceB: String - ) async throws -> CreateQuestionModel? - func isVoteQuestionLike(questionID: Int) async throws -> VoteQuestionLikeModel? - func isVoteQuestionAnswer(questionID: Int, choicAnswer: String) async throws -> QuestionVoteModel? - func deleteQuestion(questionID: Int) async throws -> DeleteQuestionModel? - func reportQuestion(questionID: Int, reason: String) async throws -> ReportQuestionModel? - func statusQuestion(questionID: Int) async throws -> StatusQuestionModel? - + func fetchQuestionList( + page: Int, + pageSize: Int, + job: String, + generation: String, + sortBy: QuestionSort) async throws -> QuestionModel? + func myQuestionList(page: Int, pageSize: Int) async throws -> QuestionModel? + func createQuestion( + emoji: String, + title: String, + choiceA: String, + choiceB: String + ) async throws -> CreateQuestionModel? + func isVoteQuestionLike(questionID: Int) async throws -> FlagQuestionDTOModel? + func isVoteQuestionAnswer(questionID: Int, choicAnswer: String) async throws -> QuestionVoteModel? + func deleteQuestion(questionID: Int) async throws -> FlagQuestionDTOModel? + func reportQuestion(questionID: Int, reason: String) async throws -> FlagQuestionDTOModel? + func statusQuestion(questionID: Int) async throws -> StatusQuestionModel? + } diff --git a/OPeace/Projects/Core/Networking/UseCase/Sources/Question/UseCase/QuestionUseCase.swift b/OPeace/Projects/Core/Networking/UseCase/Sources/Question/UseCase/QuestionUseCase.swift index 778ba31..524d551 100644 --- a/OPeace/Projects/Core/Networking/UseCase/Sources/Question/UseCase/QuestionUseCase.swift +++ b/OPeace/Projects/Core/Networking/UseCase/Sources/Question/UseCase/QuestionUseCase.swift @@ -13,99 +13,99 @@ import DiContainer import ComposableArchitecture public struct QuestionUseCase: QuestionUseCaseProtocol { - private let repository: QuestionRepositoryProtocol - - public init( - repository: QuestionRepositoryProtocol - ) { - self.repository = repository - } - - //MARK: - 피드 목록 조회 - public func fetchQuestionList( - page: Int, - pageSize: Int, - job: String, - generation: String, - sortBy: QuestionSort - ) async throws -> QuestionModel? { - try await repository.fetchQuestionList( - page: page, - pageSize: pageSize, - job: job, - generation: generation, - sortBy: sortBy) - } - - - //MARK: - 프로필에서 내가 쓴글 조회 - public func myQuestionList( - page: Int, - pageSize: Int - ) async throws -> QuestionModel? { - try await repository.myQuestionList(page: page, pageSize: pageSize) - } - - //MARK: - 질문 생성 - public func createQuestion( - emoji: String, - title: String, - choiceA: String, - choiceB: String - ) async throws -> CreateQuestionModel? { - try await repository.createQuestion( - emoji: emoji, - title: title, - choiceA: choiceA, - choiceB: choiceB - ) - } - - //MARK: - 좋아요 누르기 - public func isVoteQuestionLike(questionID: Int) async throws -> VoteQuestionLikeModel? { - try await repository.isVoteQuestionLike(questionID: questionID) - } - - //MARK: - 응답 선택 - public func isVoteQuestionAnswer( - questionID: Int, - choicAnswer: String - ) async throws -> QuestionVoteModel? { - try await repository.isVoteQuestionAnswer(questionID: questionID, choicAnswer: choicAnswer) - } - - //MARK: - 내가 쓴글 삭제 - public func deleteQuestion(questionID: Int) async throws -> DeleteQuestionModel? { - try await repository.deleteQuestion(questionID: questionID) - } - - //MARK: - 유저글 신고 - public func reportQuestion( - questionID: Int, - reason: String - ) async throws -> ReportQuestionModel? { - try await repository.reportQuestion( - questionID: questionID, - reason: reason - ) - } - - //MARK: - 투표 결과 - public func statusQuestion(questionID: Int) async throws -> StatusQuestionModel? { - try await repository.statusQuestion(questionID: questionID) - } + private let repository: QuestionRepositoryProtocol + + public init( + repository: QuestionRepositoryProtocol + ) { + self.repository = repository + } + + //MARK: - 피드 목록 조회 + public func fetchQuestionList( + page: Int, + pageSize: Int, + job: String, + generation: String, + sortBy: QuestionSort + ) async throws -> QuestionModel? { + try await repository.fetchQuestionList( + page: page, + pageSize: pageSize, + job: job, + generation: generation, + sortBy: sortBy) + } + + + //MARK: - 프로필에서 내가 쓴글 조회 + public func myQuestionList( + page: Int, + pageSize: Int + ) async throws -> QuestionModel? { + try await repository.myQuestionList(page: page, pageSize: pageSize) + } + + //MARK: - 질문 생성 + public func createQuestion( + emoji: String, + title: String, + choiceA: String, + choiceB: String + ) async throws -> CreateQuestionModel? { + try await repository.createQuestion( + emoji: emoji, + title: title, + choiceA: choiceA, + choiceB: choiceB + ) + } + + //MARK: - 좋아요 누르기 + public func isVoteQuestionLike(questionID: Int) async throws -> FlagQuestionDTOModel? { + try await repository.isVoteQuestionLike(questionID: questionID) + } + + //MARK: - 응답 선택 + public func isVoteQuestionAnswer( + questionID: Int, + choicAnswer: String + ) async throws -> QuestionVoteModel? { + try await repository.isVoteQuestionAnswer(questionID: questionID, choicAnswer: choicAnswer) + } + + //MARK: - 내가 쓴글 삭제 + public func deleteQuestion(questionID: Int) async throws -> FlagQuestionDTOModel? { + try await repository.deleteQuestion(questionID: questionID) + } + + //MARK: - 유저글 신고 + public func reportQuestion( + questionID: Int, + reason: String + ) async throws -> FlagQuestionDTOModel? { + try await repository.reportQuestion( + questionID: questionID, + reason: reason + ) + } + + //MARK: - 투표 결과 + public func statusQuestion(questionID: Int) async throws -> StatusQuestionModel? { + try await repository.statusQuestion(questionID: questionID) + } } extension QuestionUseCase : DependencyKey { - public static let liveValue: QuestionUseCase = { - let questionRepository: QuestionRepositoryProtocol = DependencyContainer.live.resolve(QuestionRepositoryProtocol.self) ?? DefaultQuestionRepository() - return QuestionUseCase(repository: questionRepository) - }() + public static let liveValue: QuestionUseCase = { + let questionRepository: QuestionRepositoryProtocol = DependencyContainer.live.resolve(QuestionRepositoryProtocol.self) ?? DefaultQuestionRepository() + return QuestionUseCase(repository: questionRepository) + }() } public extension DependencyValues { - var questionUseCase: QuestionUseCaseProtocol { - get { self[QuestionUseCase.self] } - set { self[QuestionUseCase.self] = newValue as! QuestionUseCase } - } + var questionUseCase: QuestionUseCaseProtocol { + get { self[QuestionUseCase.self] } + set { self[QuestionUseCase.self] = newValue as! QuestionUseCase } + } } diff --git a/OPeace/Projects/Core/Networking/UseCase/Sources/Question/UseCase/QuestionUseCaseProtocol.swift b/OPeace/Projects/Core/Networking/UseCase/Sources/Question/UseCase/QuestionUseCaseProtocol.swift index 55da732..fe1b9f2 100644 --- a/OPeace/Projects/Core/Networking/UseCase/Sources/Question/UseCase/QuestionUseCaseProtocol.swift +++ b/OPeace/Projects/Core/Networking/UseCase/Sources/Question/UseCase/QuestionUseCaseProtocol.swift @@ -9,22 +9,22 @@ import Foundation import Model public protocol QuestionUseCaseProtocol { - func fetchQuestionList( - page: Int, - pageSize: Int, - job: String, - generation: String, - sortBy: QuestionSort) async throws -> QuestionModel? - func myQuestionList(page: Int, pageSize: Int) async throws -> QuestionModel? - func createQuestion( - emoji: String, - title: String, - choiceA: String, - choiceB: String - ) async throws -> CreateQuestionModel? - func isVoteQuestionLike(questionID: Int) async throws -> VoteQuestionLikeModel? - func isVoteQuestionAnswer(questionID: Int, choicAnswer: String) async throws -> QuestionVoteModel? - func deleteQuestion(questionID: Int) async throws -> DeleteQuestionModel? - func reportQuestion(questionID: Int, reason: String) async throws -> ReportQuestionModel? - func statusQuestion(questionID: Int) async throws -> StatusQuestionModel? + func fetchQuestionList( + page: Int, + pageSize: Int, + job: String, + generation: String, + sortBy: QuestionSort) async throws -> QuestionModel? + func myQuestionList(page: Int, pageSize: Int) async throws -> QuestionModel? + func createQuestion( + emoji: String, + title: String, + choiceA: String, + choiceB: String + ) async throws -> CreateQuestionModel? + func isVoteQuestionLike(questionID: Int) async throws -> FlagQuestionDTOModel? + func isVoteQuestionAnswer(questionID: Int, choicAnswer: String) async throws -> QuestionVoteModel? + func deleteQuestion(questionID: Int) async throws -> FlagQuestionDTOModel? + func reportQuestion(questionID: Int, reason: String) async throws -> FlagQuestionDTOModel? + func statusQuestion(questionID: Int) async throws -> StatusQuestionModel? } diff --git a/OPeace/Projects/Core/Networking/UseCase/Sources/SignUp/Repository/DefaultSingUpRepository.swift b/OPeace/Projects/Core/Networking/UseCase/Sources/SignUp/Repository/DefaultSingUpRepository.swift index 14c43e7..6b4b448 100644 --- a/OPeace/Projects/Core/Networking/UseCase/Sources/SignUp/Repository/DefaultSingUpRepository.swift +++ b/OPeace/Projects/Core/Networking/UseCase/Sources/SignUp/Repository/DefaultSingUpRepository.swift @@ -28,7 +28,7 @@ public final class DefaultSignUpRepository: SignUpRepositoryProtocol { year: Int, job: String, generation: String - ) async throws -> UpdateUserInfoModel? { + ) async throws -> UpdateUserInfoDTOModel? { return nil } @@ -36,7 +36,7 @@ public final class DefaultSignUpRepository: SignUpRepositoryProtocol { return nil } - public func fetchGenerationList() async throws -> GenerationListResponse? { + public func fetchGenerationList() async throws -> SignUpListDTOModel? { nil } } diff --git a/OPeace/Projects/Core/Networking/UseCase/Sources/SignUp/Repository/SignUpRepositoryProtocol.swift b/OPeace/Projects/Core/Networking/UseCase/Sources/SignUp/Repository/SignUpRepositoryProtocol.swift index ab3353e..32202b3 100644 --- a/OPeace/Projects/Core/Networking/UseCase/Sources/SignUp/Repository/SignUpRepositoryProtocol.swift +++ b/OPeace/Projects/Core/Networking/UseCase/Sources/SignUp/Repository/SignUpRepositoryProtocol.swift @@ -11,12 +11,12 @@ import Model public protocol SignUpRepositoryProtocol { func checkNickName(_ nickName: String) async throws -> SignUpCheckInfoDTOModel? func fetchJobList() async throws -> SignUpListDTOModel? - func fetchGenerationList() async throws -> GenerationListResponse? + func fetchGenerationList() async throws -> SignUpListDTOModel? func updateUserInfo( nickname: String, year: Int, job: String, generation: String - ) async throws -> UpdateUserInfoModel? + ) async throws -> UpdateUserInfoDTOModel? func checkGeneration(year: Int) async throws -> SignUpCheckInfoDTOModel? } diff --git a/OPeace/Projects/Core/Networking/UseCase/Sources/SignUp/Repository/SingUpRepository.swift b/OPeace/Projects/Core/Networking/UseCase/Sources/SignUp/Repository/SingUpRepository.swift index 0bd3d57..efc13eb 100644 --- a/OPeace/Projects/Core/Networking/UseCase/Sources/SignUp/Repository/SingUpRepository.swift +++ b/OPeace/Projects/Core/Networking/UseCase/Sources/SignUp/Repository/SingUpRepository.swift @@ -31,8 +31,9 @@ import AsyncMoya return jobListModel.toSignUPListDTOToModel() } - public func fetchGenerationList() async throws -> GenerationListResponse? { - return try await provider.requestAsync(.getGenerationList, decodeTo: GenerationListResponse.self) + public func fetchGenerationList() async throws -> SignUpListDTOModel? { + let generationListModel = try await provider.requestAsync(.getGenerationList, decodeTo: GenerationListResponse.self) + return generationListModel.toGenerationListDTOToModel() } //MARK: - 유저 정보 업데이트 @@ -41,13 +42,14 @@ import AsyncMoya year: Int, job: String, generation: String - ) async throws -> UpdateUserInfoModel? { - return try await provider.requestAsync(.updateUserInfo( + ) async throws -> UpdateUserInfoDTOModel? { + let updateUserInfoModel = try await provider.requestAsync(.updateUserInfo( nickname: nickname, year: year, job: job, generation: generation ), decodeTo: UpdateUserInfoModel.self) + return updateUserInfoModel.toUpdateUserInfoDTOToModel() } //MARK: - 년도 입력시 세대 확인 API diff --git a/OPeace/Projects/Core/Networking/UseCase/Sources/SignUp/UseCase/SingUpUseCase.swift b/OPeace/Projects/Core/Networking/UseCase/Sources/SignUp/UseCase/SingUpUseCase.swift index e5a633f..4a36024 100644 --- a/OPeace/Projects/Core/Networking/UseCase/Sources/SignUp/UseCase/SingUpUseCase.swift +++ b/OPeace/Projects/Core/Networking/UseCase/Sources/SignUp/UseCase/SingUpUseCase.swift @@ -39,7 +39,7 @@ public struct SignUpUseCase : SignUpUseCaseProtocol{ year: Int, job: String, generation: String - ) async throws -> UpdateUserInfoModel? { + ) async throws -> UpdateUserInfoDTOModel? { try await repository.updateUserInfo(nickname: nickname, year: year, job: job, generation: generation) } @@ -49,7 +49,7 @@ public struct SignUpUseCase : SignUpUseCaseProtocol{ } //MARK: - 세대 리스트 - public func fetchGenerationList() async throws -> GenerationListResponse? { + public func fetchGenerationList() async throws -> SignUpListDTOModel? { try await repository.fetchGenerationList() } } diff --git a/OPeace/Projects/Core/Networking/UseCase/Sources/SignUp/UseCase/SingUpUseCaseProtocol.swift b/OPeace/Projects/Core/Networking/UseCase/Sources/SignUp/UseCase/SingUpUseCaseProtocol.swift index 93e7a03..7cd1dd5 100644 --- a/OPeace/Projects/Core/Networking/UseCase/Sources/SignUp/UseCase/SingUpUseCaseProtocol.swift +++ b/OPeace/Projects/Core/Networking/UseCase/Sources/SignUp/UseCase/SingUpUseCaseProtocol.swift @@ -17,7 +17,7 @@ public protocol SignUpUseCaseProtocol { year: Int, job: String, generation: String - ) async throws -> UpdateUserInfoModel? + ) async throws -> UpdateUserInfoDTOModel? func checkGeneration(year: Int) async throws -> SignUpCheckInfoDTOModel? - func fetchGenerationList() async throws -> GenerationListResponse? + func fetchGenerationList() async throws -> SignUpListDTOModel? } diff --git a/OPeace/Projects/Presentation/Presentation/Sources/Auth/Login/Reducer/Login.swift b/OPeace/Projects/Presentation/Presentation/Sources/Auth/Login/Reducer/Login.swift index d520446..200dabb 100644 --- a/OPeace/Projects/Presentation/Presentation/Sources/Auth/Login/Reducer/Login.swift +++ b/OPeace/Projects/Presentation/Presentation/Sources/Auth/Login/Reducer/Login.swift @@ -34,7 +34,7 @@ public struct Login { var userLoginModel : OAuthDTOModel? = nil var socialType: SocialType? = nil - var profileUserModel: UpdateUserInfoModel? = nil + var profileUserModel: UpdateUserInfoDTOModel? = nil var refreshTokenModel: RefreshDTOModel? var appleRefreshTokenMode: OAuthDTOModel? = nil @@ -73,7 +73,7 @@ public struct Login { case kakaoLoginResponse(Result<(String?, String?), CustomError>) case loginWIthKakao case kakaoLoginApiResponse(Result) - case fetchUserProfileResponse(Result) + case fetchUserProfileResponse(Result) case fetchUser case refreshTokenResponse(Result) case refreshTokenRequest(refreshToken: String) @@ -344,7 +344,7 @@ public struct Login { await send(.async(.fetchUserProfileResponse(.success(fetchUserResult)))) UserDefaults.standard.set(true, forKey: "isFirstTimeUser") - if fetchUserResult.data?.nickname != nil && fetchUserResult.data?.year != nil && fetchUserResult.data?.job != nil { + if fetchUserResult.data.nickname != nil && fetchUserResult.data.year != nil && fetchUserResult.data.job != nil { await send(.navigation(.presentMain)) } else { await send(.navigation(.presnetAgreement)) diff --git a/OPeace/Projects/Presentation/Presentation/Sources/Auth/SignUpPaging/Reducer/SignUpPaging.swift b/OPeace/Projects/Presentation/Presentation/Sources/Auth/SignUpPaging/Reducer/SignUpPaging.swift index 5be00c9..3c34328 100644 --- a/OPeace/Projects/Presentation/Presentation/Sources/Auth/SignUpPaging/Reducer/SignUpPaging.swift +++ b/OPeace/Projects/Presentation/Presentation/Sources/Auth/SignUpPaging/Reducer/SignUpPaging.swift @@ -26,7 +26,7 @@ public struct SignUpPaging { var signUpJob = SignUpJob.State() var activeMenu: SignUpTab = .signUpName - var updateUserinfoModel: UpdateUserInfoModel? + var updateUserinfoModel: UpdateUserInfoDTOModel? = nil @Presents var destination: Destination.State? @@ -64,7 +64,7 @@ public struct SignUpPaging { //MARK: - AsyncAction 비동기 처리 액션 public enum AsyncAction: Equatable { - case updateUserInfoResponse(Result) + case updateUserInfoResponse(Result) case updateUserInfo( nickName: String, year: Int, @@ -186,7 +186,7 @@ public struct SignUpPaging { await send(.view(.closePopUp)) try await clock.sleep(for: .seconds(1)) - if updateUserInfoData.data?.isFirstLogin == false { + if updateUserInfoData.data.isFirstLogin == false { await send(.navigation(.presntOnboarding)) } else { await send(.navigation(.presntMainHome)) diff --git a/OPeace/Projects/Presentation/Presentation/Sources/Home/Coordinator/Reducer/HomeCoordinator.swift b/OPeace/Projects/Presentation/Presentation/Sources/Home/Coordinator/Reducer/HomeCoordinator.swift index 82d0bf8..9791e88 100644 --- a/OPeace/Projects/Presentation/Presentation/Sources/Home/Coordinator/Reducer/HomeCoordinator.swift +++ b/OPeace/Projects/Presentation/Presentation/Sources/Home/Coordinator/Reducer/HomeCoordinator.swift @@ -121,9 +121,8 @@ public struct HomeCoordinator { switch action { //MARK: - Home case .routeAction(id: _, action: .home(.navigation(.presntProfile))): - return .routeWithDelaysIfUnsupported(state.routes, action: \.router) { - $0.push(.profile(.init())) - } + state.routes.push(.profile(.init())) + return .none //MARK: - 로그인 case .routeAction(id: _, action: .home(.navigation(.presntLogin))): @@ -140,8 +139,7 @@ public struct HomeCoordinator { return .none case .routeAction(id: _, action: .report(.navigation(.presntMainHome))): - state.routes.goBackToRoot() - return .none + return .send(.inner(.removeToHome)) //MARK: - 프로필 화면 //MARK: - logout @@ -160,9 +158,7 @@ public struct HomeCoordinator { return .none case .routeAction(id: _, action: .withDraw(.navigation(.presntDeleteUser))): - return .routeWithDelaysIfUnsupported(state.routes, action: \.router) { - $0.goBackTo(\.home) - } + return .send(.inner(.removeToHome)) case .routeAction(id: _, action: .profile(.navigation(.presntUserBlock))): state.routes.push(.blockUser(.init())) @@ -170,8 +166,7 @@ public struct HomeCoordinator { //MARK: - 내가 작성한 글 삭제 case .routeAction(id: _, action: .profile(.navigation(.presntDeleteQuestion))): - state.routes.goBackTo(\.home) - return .none + return .send(.inner(.removeToHome)) //MARK: - 작성한 글이 없을때 case .routeAction(id: _, action: .profile(.navigation(.presnetCreateQuestionList))): @@ -179,8 +174,7 @@ public struct HomeCoordinator { return .none case .routeAction(id: _, action: .createQuestion(.navigation(.presntHome))): - state.routes.goBackTo(\.home) - return .none + return .send(.inner(.removeToHome)) default: return .none diff --git a/OPeace/Projects/Presentation/Presentation/Sources/Home/Filter/Reducer/HomeFilter.swift b/OPeace/Projects/Presentation/Presentation/Sources/Home/Filter/Reducer/HomeFilter.swift index 435feb5..cf56b2d 100644 --- a/OPeace/Projects/Presentation/Presentation/Sources/Home/Filter/Reducer/HomeFilter.swift +++ b/OPeace/Projects/Presentation/Presentation/Sources/Home/Filter/Reducer/HomeFilter.swift @@ -21,7 +21,7 @@ public struct HomeFilter { public var selectedItem: String? = nil var jsobListModel: SignUpListDTOModel? = nil - var generationListModel: GenerationListResponse? = nil + var generationListModel: SignUpListDTOModel? = nil public var homeFilterTypeState: HomeFilterEnum? = nil @@ -50,7 +50,7 @@ public struct HomeFilter { case fetchListByFilterEnum(HomeFilterEnum) case fetchJobList case fetchJobResponse(Result) - case fetchGenerationResponse(Result) + case fetchGenerationResponse(Result) } //MARK: - 앱내에서 사용하는 액션 diff --git a/OPeace/Projects/Presentation/Presentation/Sources/Home/Filter/View/HomeFilterView.swift b/OPeace/Projects/Presentation/Presentation/Sources/Home/Filter/View/HomeFilterView.swift index c52fda8..1ac292a 100644 --- a/OPeace/Projects/Presentation/Presentation/Sources/Home/Filter/View/HomeFilterView.swift +++ b/OPeace/Projects/Presentation/Presentation/Sources/Home/Filter/View/HomeFilterView.swift @@ -51,7 +51,7 @@ public struct HomeFilterView: View { } } case .generation: - if let generations = store.generationListModel?.data?.data { + if let generations = store.generationListModel?.data.content { ForEach(generations, id: \.self) { generation in filterListItem(title: generation, isSelected: selectItem == generation) { selectItem = generation diff --git a/OPeace/Projects/Presentation/Presentation/Sources/Home/Main/Reducer/Home.swift b/OPeace/Projects/Presentation/Presentation/Sources/Home/Main/Reducer/Home.swift index 12190b9..c03ea26 100644 --- a/OPeace/Projects/Presentation/Presentation/Sources/Home/Main/Reducer/Home.swift +++ b/OPeace/Projects/Presentation/Presentation/Sources/Home/Main/Reducer/Home.swift @@ -14,128 +14,128 @@ import Networkings @Reducer public struct Home { - public init() {} + public init() {} + + @ObservableState + public struct State: Equatable { - @ObservableState - public struct State: Equatable { - - var profileImage: String = "person.fill" - var loginTiltle: String = "로그인을 해야 다른 기능을 사용하실 수 있습니다. " - var floatingText: String = "" - var customPopUpText: String = "" - var floatingImage: ImageAsset = .succesLogout - var cancellable: AnyCancellable? - - var questionModel: QuestionModel? = nil - var isVoteLikeQuestionModel: VoteQuestionLikeModel? = nil - var userBlockModel: UserBlockDTOModel? = nil - var isVoteAnswerQuestionModel: QuestionVoteModel? = nil - var profileUserModel: UpdateUserInfoModel? = nil - var statusQuestionModel: StatusQuestionModel? = nil - var userBlockListModel: UserBlockListDTOModel? = nil - - var cardGenerationColor: Color = .basicBlack - var isLikeTap: Bool = false - var isRoatinCard: Bool = false - - var likedItemId: String? - var questionID: Int? - var userID: String? - var isSelectAnswerA: String = "a" - var isSelectAnswerB: String = "b" - var questionDetailId: Int? = nil - var isTapAVote: Bool = false - var isTapBVote: Bool = false - -// var profile = Profile.State() - var pageSize: Int = 20 - - var isTapBlockUser: Bool = false - var isShowSelectEditModal: Bool = false - var isBlockQuestionPopUp: Bool = false - var isReportQuestionPopUp: Bool = false - - var selectedJobButtonTitle: String = "계열" - var selectedJob: String = "" - var isActivateJobButton: Bool = false - - var selectedGenerationButtonTitle: String = "세대" - var selectedGeneration: String = "" - var isActivateGenerationButton: Bool = false - - var selectedSortedButtonTitle: QuestionSort = .recent - var selectedSorted: QuestionSort = .recent - var isActivateSortedButton: Bool = true - - var selectedSortDesc: String = "" - var isFilterQuestion: Bool = false - var selectedItem: String? = "" - - @Shared var userInfoModel: UserInfoModel? - - @Shared(.inMemory("questionID")) var reportQuestionID: Int = 0 - - @Presents var destination: Destination.State? - - - public init( - - userInfoModel: UserInfoModel? = .init() - ) { - self._userInfoModel = Shared(wrappedValue: userInfoModel, .inMemory("userInfoModel")) - } - - } + var profileImage: String = "person.fill" + var loginTiltle: String = "로그인을 해야 다른 기능을 사용하실 수 있습니다. " + var floatingText: String = "" + var customPopUpText: String = "" + var floatingImage: ImageAsset = .succesLogout + var cancellable: AnyCancellable? - public enum Action: ViewAction, BindableAction, FeatureAction { - case binding(BindingAction) - case destination(PresentationAction) - case view(View) - case async(AsyncAction) - case inner(InnerAction) - case navigation(NavigationAction) -// case profile(Profile.Action) - - - } + var questionModel: QuestionModel? = nil + var isVoteLikeQuestionModel: FlagQuestionDTOModel? = nil + var userBlockModel: UserBlockDTOModel? = nil + var isVoteAnswerQuestionModel: QuestionVoteModel? = nil + var profileUserModel: UpdateUserInfoDTOModel? = nil + var statusQuestionModel: StatusQuestionModel? = nil + var userBlockListModel: UserBlockListDTOModel? = nil - @Reducer(state: .equatable) - public enum Destination { - case homeFilter(HomeFilter) - case customPopUp(CustomPopUp) - case floatingPopUP(FloatingPopUp) - case editQuestion(EditQuestion) - - } + var cardGenerationColor: Color = .basicBlack + var isLikeTap: Bool = false + var isRoatinCard: Bool = false + + var likedItemId: String? + var questionID: Int? + var userID: String? + var isSelectAnswerA: String = "a" + var isSelectAnswerB: String = "b" + var questionDetailId: Int? = nil + var isTapAVote: Bool = false + var isTapBVote: Bool = false + + // var profile = Profile.State() + var pageSize: Int = 20 - //MARK: - ViewAction - public enum View { - case appaerProfiluserData - case prsentCustomPopUp - case presntFloatintPopUp - case presntEditQuestion - case closeEditQuestionModal - case closePopUp - case timeToCloseFloatingPopUp - case switchModalAction(EditQuestionType) - case filterViewTappd(HomeFilterEnum) - case closeFilterModal + var isTapBlockUser: Bool = false + var isShowSelectEditModal: Bool = false + var isBlockQuestionPopUp: Bool = false + var isReportQuestionPopUp: Bool = false + + var selectedJobButtonTitle: String = "계열" + var selectedJob: String = "" + var isActivateJobButton: Bool = false + + var selectedGenerationButtonTitle: String = "세대" + var selectedGeneration: String = "" + var isActivateGenerationButton: Bool = false + + var selectedSortedButtonTitle: QuestionSort = .recent + var selectedSorted: QuestionSort = .recent + var isActivateSortedButton: Bool = true + + var selectedSortDesc: String = "" + var isFilterQuestion: Bool = false + var selectedItem: String? = "" + + @Shared var userInfoModel: UserInfoModel? + + @Shared(.inMemory("questionID")) var reportQuestionID: Int = 0 + + @Presents var destination: Destination.State? + + + public init( + + userInfoModel: UserInfoModel? = .init() + ) { + self._userInfoModel = Shared(wrappedValue: userInfoModel, .inMemory("userInfoModel")) } + } + + public enum Action: ViewAction, BindableAction, FeatureAction { + case binding(BindingAction) + case destination(PresentationAction) + case view(View) + case async(AsyncAction) + case inner(InnerAction) + case navigation(NavigationAction) + // case profile(Profile.Action) + + + } + @Reducer(state: .equatable) + public enum Destination { + case homeFilter(HomeFilter) + case customPopUp(CustomPopUp) + case floatingPopUP(FloatingPopUp) + case editQuestion(EditQuestion) + } + + //MARK: - ViewAction + public enum View { + case appaerProfiluserData + case prsentCustomPopUp + case presntFloatintPopUp + case presntEditQuestion + case closeEditQuestionModal + case closePopUp + case timeToCloseFloatingPopUp + case switchModalAction(EditQuestionType) + case filterViewTappd(HomeFilterEnum) + case closeFilterModal + } + + + //MARK: - AsyncAction 비동기 처리 액션 public enum AsyncAction: Equatable { case fetchQuestionList case qusetsionListResponse(Result) case isVoteQuestionLike(questioniD: Int) - case isVoteQuestionLikeResponse(Result) + case isVoteQuestionLikeResponse(Result) case blockUser(qusetionID: Int, userID: String) case blockUserResponse(Result) case isVoteQuestionAnswer(questionID: Int, choiceAnswer: String) case isVoteQuestionAnsweResponse(Result) case fetchUserProfile - case userProfileResponse(Result) + case userProfileResponse(Result) case jobFilterSelected(job: String) case generationFilterSelected(generation: String) case sortedFilterSelected(sortedEnum:QuestionSort) @@ -148,71 +148,71 @@ public struct Home { case appearData } + + //MARK: - 앱내에서 사용하는 액션 + public enum InnerAction: Equatable { - //MARK: - 앱내에서 사용하는 액션 - public enum InnerAction: Equatable { - - } - - //MARK: - NavigationAction - public enum NavigationAction: Equatable { - case presntProfile - case presntLogin - case presntWriteQuestion - case presntReport + } + + //MARK: - NavigationAction + public enum NavigationAction: Equatable { + case presntProfile + case presntLogin + case presntWriteQuestion + case presntReport + } + + @Dependency(\.continuousClock) var clock + @Dependency(QuestionUseCase.self) var questionUseCase + @Dependency(AuthUseCase.self) var authUseCase + + public var body: some ReducerOf { + BindingReducer() + Reduce { state, action in + switch action { + case .binding(_): + return .none + + case .binding(\.questionID): + return .none + + case .view(let View): + return handleViewAction(state: &state, action: View) + + case .async(let AsyncAction): + return handleAsyncAction(state: &state, action: AsyncAction) + + case .inner(let InnerAction): + return handleInnerAction(state: &state, action: InnerAction) + + case .navigation(let NavigationAction): + return handleNavigationAction(state: &state, action: NavigationAction) + + default: + return .none + } } - - @Dependency(\.continuousClock) var clock - @Dependency(QuestionUseCase.self) var questionUseCase - @Dependency(AuthUseCase.self) var authUseCase - - public var body: some ReducerOf { - BindingReducer() - Reduce { state, action in - switch action { - case .binding(_): - return .none - - case .binding(\.questionID): - return .none - - case .view(let View): - return handleViewAction(state: &state, action: View) - - case .async(let AsyncAction): - return handleAsyncAction(state: &state, action: AsyncAction) - - case .inner(let InnerAction): - return handleInnerAction(state: &state, action: InnerAction) - - case .navigation(let NavigationAction): - return handleNavigationAction(state: &state, action: NavigationAction) - - default: - return .none - } - } - .ifLet(\.$destination, action: \.destination) - .onChange(of: \.questionModel) { oldValue, newValue in - Reduce { state, action in - state.questionModel = newValue - return .none - } - } - .onChange(of: \.statusQuestionModel) { oldValue, newValue in - Reduce { state, action in - state.statusQuestionModel = newValue - return .none - } - } - .onChange(of: \.userInfoModel) { oldValue, newValue in - Reduce { state, action in - state.userInfoModel = newValue - return .none - } - } + .ifLet(\.$destination, action: \.destination) + .onChange(of: \.questionModel) { oldValue, newValue in + Reduce { state, action in + state.questionModel = newValue + return .none + } } + .onChange(of: \.statusQuestionModel) { oldValue, newValue in + Reduce { state, action in + state.statusQuestionModel = newValue + return .none + } + } + .onChange(of: \.userInfoModel) { oldValue, newValue in + Reduce { state, action in + state.userInfoModel = newValue + return .none + } + } + } private func handleViewAction( state: inout State, @@ -220,75 +220,75 @@ public struct Home { ) -> Effect { switch action { case .appaerProfiluserData: - return .run { send in -// await send(.profile(.scopeFetchUser)) - } - + return .run { send in + // await send(.profile(.scopeFetchUser)) + } + case .prsentCustomPopUp: - state.destination = .customPopUp(.init()) - return .none - + state.destination = .customPopUp(.init()) + return .none + case .closePopUp: - state.destination = nil - return .none + state.destination = nil + return .none case .closeFilterModal: - state.destination = nil - return .none - + state.destination = nil + return .none + case .presntFloatintPopUp: - state.destination = .floatingPopUP(.init()) - return .none - + state.destination = .floatingPopUP(.init()) + return .none + case .timeToCloseFloatingPopUp: - return .run { send in - try await clock.sleep(for: .seconds(1.5)) - await send(.view(.closePopUp)) - } + return .run { send in + try await clock.sleep(for: .seconds(1.5)) + await send(.view(.closePopUp)) + } case .filterViewTappd(let filterEnum): - state.destination = .homeFilter(.init(homeFilterEnum: filterEnum)) - switch filterEnum { - case .job: - state.selectedItem = state.selectedJob - case .generation: - state.selectedItem = state.selectedGeneration - case .sorted(let sortedOrderEnum): - state.selectedItem = state.selectedSorted.sortedKoreanString - } - return .run { send in - await send(.destination(.presented(.homeFilter(.async(.fetchListByFilterEnum(filterEnum)))))) - } - + state.destination = .homeFilter(.init(homeFilterEnum: filterEnum)) + switch filterEnum { + case .job: + state.selectedItem = state.selectedJob + case .generation: + state.selectedItem = state.selectedGeneration + case .sorted(let sortedOrderEnum): + state.selectedItem = state.selectedSorted.sortedKoreanString + } + return .run { send in + await send(.destination(.presented(.homeFilter(.async(.fetchListByFilterEnum(filterEnum)))))) + } + case .presntEditQuestion: - state.destination = .editQuestion(.init()) - return .none - + state.destination = .editQuestion(.init()) + return .none + case .closeEditQuestionModal: - state.destination = nil - return .none - + state.destination = nil + return .none + case .switchModalAction(let editQuestion): - nonisolated(unsafe) let editQuestion = editQuestion + nonisolated(unsafe) let editQuestion = editQuestion + switch editQuestion { + case .reportUser: + Log.debug("신고하기") + state.customPopUpText = "정말 신고하시겠어요?" + state.isReportQuestionPopUp = true + case .blockUser: + Log.debug("차단하기") + state.customPopUpText = "정말 차단하시겠어요?" + state.isTapBlockUser = true + } + + return .run { send in switch editQuestion { case .reportUser: - Log.debug("신고하기") - state.customPopUpText = "정말 신고하시겠어요?" - state.isReportQuestionPopUp = true + Log.debug("신고하기") + await send(.view(.prsentCustomPopUp)) case .blockUser: - Log.debug("차단하기") - state.customPopUpText = "정말 차단하시겠어요?" - state.isTapBlockUser = true - } - - return .run { send in - switch editQuestion { - case .reportUser: - Log.debug("신고하기") - await send(.view(.prsentCustomPopUp)) - case .blockUser: - Log.debug("차단하기") - await send(.view(.prsentCustomPopUp)) - } + Log.debug("차단하기") + await send(.view(.prsentCustomPopUp)) } + } } } @@ -298,18 +298,18 @@ public struct Home { ) -> Effect { switch action { case .presntProfile: - return .run { send in -// await send(.profile(.scopeFetchUser)) - } - + return .run { send in + // await send(.profile(.scopeFetchUser)) + } + case .presntLogin: - return .none - + return .none + case .presntWriteQuestion: - return .none - + return .none + case .presntReport: - return .none + return .none } } @@ -319,311 +319,311 @@ public struct Home { ) -> Effect { switch action { case .fetchQuestionList: - let pageSize = state.pageSize - return .run { send in - let questionResult = await Result { - try await questionUseCase.fetchQuestionList( - page: 1, - pageSize: pageSize, - job: "", - generation: "", - sortBy: .empty) - } - - switch questionResult { - case .success(let questionModel): - if let questionModel = questionModel { - await send(.async(.qusetsionListResponse(.success(questionModel)))) - } - case .failure(let error): - await send(.async(.qusetsionListResponse(.failure(CustomError.createQuestionError(error.localizedDescription))))) - } - - } - - case .jobFilterSelected(let job): - let currentSelectedGeneration = state.selectedGeneration - nonisolated(unsafe) let currentSortBy = state.selectedSorted - if state.selectedJob == job { - state.selectedJobButtonTitle = "계열" - state.selectedJob = "" - state.selectedItem = "" - state.isActivateJobButton = false - - return .send(.async(.filterQuestionList(job: "", generation: currentSelectedGeneration, sortBy: currentSortBy))) - } else { - state.selectedJobButtonTitle = job - state.selectedJob = job - state.selectedItem = "" - state.isActivateJobButton = true - return .send(.async(.filterQuestionList(job: job, generation: currentSelectedGeneration, sortBy: currentSortBy))) + let pageSize = state.pageSize + return .run { send in + let questionResult = await Result { + try await questionUseCase.fetchQuestionList( + page: 1, + pageSize: pageSize, + job: "", + generation: "", + sortBy: .empty) } - case .generationFilterSelected(let generation): - let currentSelectedJob = state.selectedJob - nonisolated(unsafe) let currentSortBy = state.selectedSorted - if state.selectedGeneration == generation { - state.selectedGenerationButtonTitle = "세대" - state.selectedGeneration = "" - state.selectedItem = "" - state.isActivateGenerationButton = false - return .send(.async(.filterQuestionList(job: currentSelectedJob, generation: "", sortBy: currentSortBy))) - } else { - state.isActivateGenerationButton = true - state.selectedGenerationButtonTitle = generation - state.selectedItem = generation - state.selectedGeneration = generation - return .send(.async(.filterQuestionList(job: currentSelectedJob, generation: generation, sortBy: currentSortBy))) - } - case .sortedFilterSelected(let sorted): - let currentSelectedGeneration = state.selectedGeneration - let currentSelectedJob = state.selectedJob - - if state.selectedSorted == sorted { - state.selectedSorted = .empty - return .send(.async(.filterQuestionList(job: currentSelectedJob, generation: currentSelectedGeneration, sortBy: .empty))) - } else { - state.selectedSorted = sorted - state.selectedSortedButtonTitle = sorted - return .send(.async(.filterQuestionList(job: currentSelectedJob, generation: currentSelectedGeneration, sortBy: sorted))) - } - - case .filterQuestionList(let job, let generation, let sortBy): - let pageSize = state.pageSize - - return .run { send in - let questionResult = await Result { - try await questionUseCase.fetchQuestionList( - page: 1, - pageSize: pageSize, - job: job, - generation: generation, - sortBy: sortBy) - } - - switch questionResult { - case .success(let questionModel): - if let questionModel = questionModel { - await send(.async(.qusetsionListResponse(.success(questionModel)))) - } - case .failure(let error): - await send(.async(.qusetsionListResponse(.failure(CustomError.createQuestionError(error.localizedDescription))))) - } + switch questionResult { + case .success(let questionModel): + if let questionModel = questionModel { + await send(.async(.qusetsionListResponse(.success(questionModel)))) + } + case .failure(let error): + await send(.async(.qusetsionListResponse(.failure(CustomError.createQuestionError(error.localizedDescription))))) } - case .clearFilter: + } + + case .jobFilterSelected(let job): + let currentSelectedGeneration = state.selectedGeneration + nonisolated(unsafe) let currentSortBy = state.selectedSorted + if state.selectedJob == job { state.selectedJobButtonTitle = "계열" state.selectedJob = "" state.selectedItem = "" state.isActivateJobButton = false + + return .send(.async(.filterQuestionList(job: "", generation: currentSelectedGeneration, sortBy: currentSortBy))) + } else { + state.selectedJobButtonTitle = job + state.selectedJob = job + state.selectedItem = "" + state.isActivateJobButton = true + return .send(.async(.filterQuestionList(job: job, generation: currentSelectedGeneration, sortBy: currentSortBy))) + } + case .generationFilterSelected(let generation): + let currentSelectedJob = state.selectedJob + nonisolated(unsafe) let currentSortBy = state.selectedSorted + if state.selectedGeneration == generation { state.selectedGenerationButtonTitle = "세대" state.selectedGeneration = "" + state.selectedItem = "" state.isActivateGenerationButton = false - state.selectedSorted = .recent - return .none + return .send(.async(.filterQuestionList(job: currentSelectedJob, generation: "", sortBy: currentSortBy))) + } else { + state.isActivateGenerationButton = true + state.selectedGenerationButtonTitle = generation + state.selectedItem = generation + state.selectedGeneration = generation + return .send(.async(.filterQuestionList(job: currentSelectedJob, generation: generation, sortBy: currentSortBy))) + } + case .sortedFilterSelected(let sorted): + let currentSelectedGeneration = state.selectedGeneration + let currentSelectedJob = state.selectedJob + + + if state.selectedSorted == sorted { + state.selectedSorted = .empty + return .send(.async(.filterQuestionList(job: currentSelectedJob, generation: currentSelectedGeneration, sortBy: .empty))) + } else { + state.selectedSorted = sorted + state.selectedSortedButtonTitle = sorted + return .send(.async(.filterQuestionList(job: currentSelectedJob, generation: currentSelectedGeneration, sortBy: sorted))) + } + + case .filterQuestionList(let job, let generation, let sortBy): + let pageSize = state.pageSize + + return .run { send in + let questionResult = await Result { + try await questionUseCase.fetchQuestionList( + page: 1, + pageSize: pageSize, + job: job, + generation: generation, + sortBy: sortBy) + } - case .qusetsionListResponse(let result): - switch result { - case .success(let qusetsionListData): - // userInfoModel.isLogOut 확인 - if state.userInfoModel?.isLogOut == true { - // 로그아웃 상태에서는 필터링하지 않고 원래 데이터를 그대로 사용 - state.questionModel = qusetsionListData - } else { - // 차단된 닉네임 리스트 가져오기 - let blockedNicknames = state.userBlockListModel?.data.compactMap { $0.nickname } ?? [] - - // 차단된 닉네임 제외한 결과 필터링 - let filteredResults = qusetsionListData.data?.results?.filter { item in - guard let nickname = item.userInfo?.userNickname else { return true } - return !blockedNicknames.contains(nickname) - } - - // 새로운 데이터를 생성하여 필터링된 결과를 저장 - var updatedQuestionListData = qusetsionListData - updatedQuestionListData.data = updatedQuestionListData.data.map { data in - var modifiedData = data - modifiedData.results = filteredResults - return modifiedData - } - - state.questionModel = updatedQuestionListData - } - - // 페이지 크기 증가 - state.pageSize += 20 + switch questionResult { + case .success(let questionModel): + if let questionModel = questionModel { + await send(.async(.qusetsionListResponse(.success(questionModel)))) + } case .failure(let error): - Log.error("QuestionList 에러", error.localizedDescription) + await send(.async(.qusetsionListResponse(.failure(CustomError.createQuestionError(error.localizedDescription))))) + } + } + + case .clearFilter: + state.selectedJobButtonTitle = "계열" + state.selectedJob = "" + state.selectedItem = "" + state.isActivateJobButton = false + state.selectedGenerationButtonTitle = "세대" + state.selectedGeneration = "" + state.isActivateGenerationButton = false + state.selectedSorted = .recent + return .none + + case .qusetsionListResponse(let result): + switch result { + case .success(let qusetsionListData): + // userInfoModel.isLogOut 확인 + if state.userInfoModel?.isLogOut == true { + // 로그아웃 상태에서는 필터링하지 않고 원래 데이터를 그대로 사용 + state.questionModel = qusetsionListData + } else { + // 차단된 닉네임 리스트 가져오기 + let blockedNicknames = state.userBlockListModel?.data.compactMap { $0.nickname } ?? [] + + // 차단된 닉네임 제외한 결과 필터링 + let filteredResults = qusetsionListData.data?.results?.filter { item in + guard let nickname = item.userInfo?.userNickname else { return true } + return !blockedNicknames.contains(nickname) + } + + // 새로운 데이터를 생성하여 필터링된 결과를 저장 + var updatedQuestionListData = qusetsionListData + updatedQuestionListData.data = updatedQuestionListData.data.map { data in + var modifiedData = data + modifiedData.results = filteredResults + return modifiedData + } + + state.questionModel = updatedQuestionListData } - return .none + // 페이지 크기 증가 + state.pageSize += 20 + case .failure(let error): + Log.error("QuestionList 에러", error.localizedDescription) + } + return .none + case .isVoteQuestionLike(questioniD: let questioniD): - return .run { send in - let voteQuestionLikeResult = await Result { - try await questionUseCase.isVoteQuestionLike(questionID: questioniD) - } - - switch voteQuestionLikeResult { - case .success(let voteQuestionLikeResult): - if let voteQuestionLikeResult = voteQuestionLikeResult { - await send(.async(.isVoteQuestionLikeResponse( - .success(voteQuestionLikeResult)))) - - await send(.async(.fetchQuestionList)) - } - case .failure(let error): - await send(.async(.isVoteQuestionLikeResponse(.failure(CustomError.createQuestionError(error.localizedDescription))))) - } + return .run { send in + let voteQuestionLikeResult = await Result { + try await questionUseCase.isVoteQuestionLike(questionID: questioniD) } - case .isVoteQuestionLikeResponse(let voteQuestionResult): - switch voteQuestionResult { - case .success(let voteQuestionResult): - state.isVoteLikeQuestionModel = voteQuestionResult - state.isLikeTap.toggle() + switch voteQuestionLikeResult { + case .success(let voteQuestionLikeResult): + if let voteQuestionLikeResult = voteQuestionLikeResult { + await send(.async(.isVoteQuestionLikeResponse( + .success(voteQuestionLikeResult)))) + + await send(.async(.fetchQuestionList)) + } case .failure(let error): - Log.error("좋아요 누르기 에러", error.localizedDescription) + await send(.async(.isVoteQuestionLikeResponse(.failure(CustomError.createQuestionError(error.localizedDescription))))) } - return .none - + } + + case .isVoteQuestionLikeResponse(let voteQuestionResult): + switch voteQuestionResult { + case .success(let voteQuestionResult): + state.isVoteLikeQuestionModel = voteQuestionResult + state.isLikeTap.toggle() + case .failure(let error): + Log.error("좋아요 누르기 에러", error.localizedDescription) + } + return .none + case .blockUser(let qusetionID, let userID): - return .run { send in - let blockUserResult = await Result { - try await authUseCase.userBlock(questioniD: qusetionID, userID: userID) - } - switch blockUserResult { - case .success(let blockUserResult): - if let blockUserResult = blockUserResult { - await send(.async(.blockUserResponse(.success(blockUserResult)))) - - try await clock.sleep(for: .seconds(0.4)) - await send(.view(.presntFloatintPopUp)) - - await send(.view(.timeToCloseFloatingPopUp)) - } - case .failure(let error): - await send(.async(.blockUserResponse(.failure(CustomError.createQuestionError(error.localizedDescription))))) - } + return .run { send in + let blockUserResult = await Result { + try await authUseCase.userBlock(questioniD: qusetionID, userID: userID) } - - case .blockUserResponse(let result): - switch result { + switch blockUserResult { case .success(let blockUserResult): - state.userBlockModel = blockUserResult - state.floatingText = "차단이 완료 되었어요!" - state.floatingImage = .succesLogout + if let blockUserResult = blockUserResult { + await send(.async(.blockUserResponse(.success(blockUserResult)))) + + try await clock.sleep(for: .seconds(0.4)) + await send(.view(.presntFloatintPopUp)) + + await send(.view(.timeToCloseFloatingPopUp)) + } case .failure(let error): - Log.error("유저 차단 에러", error.localizedDescription) + await send(.async(.blockUserResponse(.failure(CustomError.createQuestionError(error.localizedDescription))))) } - return .none - + } + + case .blockUserResponse(let result): + switch result { + case .success(let blockUserResult): + state.userBlockModel = blockUserResult + state.floatingText = "차단이 완료 되었어요!" + state.floatingImage = .succesLogout + case .failure(let error): + Log.error("유저 차단 에러", error.localizedDescription) + } + return .none + case .isVoteQuestionAnswer(let questionID, let choiceAnswer): - return .run { send in - let voteQuestionAnswerResult = await Result { - try await questionUseCase.isVoteQuestionAnswer(questionID: questionID, choicAnswer: choiceAnswer) - } - - switch voteQuestionAnswerResult { - case .success(let voteQuestionResult): - if let voteQuestionResult = voteQuestionResult { - await send(.async(.isVoteQuestionAnsweResponse( - .success(voteQuestionResult)))) - - } - case .failure(let error): - await send(.async(.isVoteQuestionAnsweResponse(.failure( - CustomError.createQuestionError(error.localizedDescription))))) - } + return .run { send in + let voteQuestionAnswerResult = await Result { + try await questionUseCase.isVoteQuestionAnswer(questionID: questionID, choicAnswer: choiceAnswer) } - case .isVoteQuestionAnsweResponse(let reuslt): - switch reuslt { - case .success(let isVoteQuestionAnswer): - state.isVoteAnswerQuestionModel = isVoteQuestionAnswer - state.isRoatinCard = true + switch voteQuestionAnswerResult { + case .success(let voteQuestionResult): + if let voteQuestionResult = voteQuestionResult { + await send(.async(.isVoteQuestionAnsweResponse( + .success(voteQuestionResult)))) + + } case .failure(let error): - Log.error("유저 투표 에러", error.localizedDescription) + await send(.async(.isVoteQuestionAnsweResponse(.failure( + CustomError.createQuestionError(error.localizedDescription))))) } - return .none - - + } + + case .isVoteQuestionAnsweResponse(let reuslt): + switch reuslt { + case .success(let isVoteQuestionAnswer): + state.isVoteAnswerQuestionModel = isVoteQuestionAnswer + state.isRoatinCard = true + case .failure(let error): + Log.error("유저 투표 에러", error.localizedDescription) + } + return .none + + case .fetchUserProfile: - return .run { send in - let fetchUserData = await Result { - try await authUseCase.fetchUserInfo() - } - - switch fetchUserData { - case .success(let fetchUserResult): - if let fetchUserResult = fetchUserResult { - await send(.async(.userProfileResponse(.success(fetchUserResult)))) - } - case .failure(let error): - await send(.async(.userProfileResponse(.failure( - CustomError.userError(error.localizedDescription))))) - - } + return .run { send in + let fetchUserData = await Result { + try await authUseCase.fetchUserInfo() } - case .userProfileResponse(let result): - switch result { - case .success(let resultData): - state.profileUserModel = resultData - case let .failure(error): - Log.network("프로필 오류", error.localizedDescription) + switch fetchUserData { + case .success(let fetchUserResult): + if let fetchUserResult = fetchUserResult { + await send(.async(.userProfileResponse(.success(fetchUserResult)))) + } + case .failure(let error): + await send(.async(.userProfileResponse(.failure( + CustomError.userError(error.localizedDescription))))) + } - return .none - + } + + case .userProfileResponse(let result): + switch result { + case .success(let resultData): + state.profileUserModel = resultData + case let .failure(error): + Log.network("프로필 오류", error.localizedDescription) + } + return .none + case .statusQuestion(id: let id): - return .run { send in - let questionResult = await Result { - try await questionUseCase.statusQuestion(questionID: id) - } - - switch questionResult { - case .success(let questionStatusData): - if let questionStatusData = questionStatusData { - await send(.async(.statusQuestionResponse(.success(questionStatusData)))) - } - case .failure(let error): - await send(.async(.statusQuestionResponse(.failure(CustomError.encodingError(error.localizedDescription))))) - } + return .run { send in + let questionResult = await Result { + try await questionUseCase.statusQuestion(questionID: id) } - case .statusQuestionResponse(let result): - switch result { - case .success(let statusQuestionData): - state.statusQuestionModel = statusQuestionData + switch questionResult { + case .success(let questionStatusData): + if let questionStatusData = questionStatusData { + await send(.async(.statusQuestionResponse(.success(questionStatusData)))) + } case .failure(let error): - Log.error("질문 에대한 결과 보여주기 실패", error.localizedDescription) + await send(.async(.statusQuestionResponse(.failure(CustomError.encodingError(error.localizedDescription))))) } - return .none + } + + case .statusQuestionResponse(let result): + switch result { + case .success(let statusQuestionData): + state.statusQuestionModel = statusQuestionData + case .failure(let error): + Log.error("질문 에대한 결과 보여주기 실패", error.localizedDescription) + } + return .none case .fetchUserBlockList: - return .run { send in - let userBlockResult = await Result { - try await authUseCase.fetchUserBlockList() - } - - switch userBlockResult { - case .success(let userBlockLIstData): - if let userBlockListData = userBlockLIstData { - await send(.async(.fetchUserBlockResponse(.success(userBlockListData)))) - } - - case .failure(let error): - await send(.async(.fetchUserBlockResponse(.failure(CustomError.userError(error.localizedDescription))))) - } + return .run { send in + let userBlockResult = await Result { + try await authUseCase.fetchUserBlockList() } - case .fetchUserBlockResponse(let result): - switch result { - case .success(let userBlockListData): - state.userBlockListModel = userBlockListData + switch userBlockResult { + case .success(let userBlockLIstData): + if let userBlockListData = userBlockLIstData { + await send(.async(.fetchUserBlockResponse(.success(userBlockListData)))) + } + case .failure(let error): - Log.error("차단 유저 가져오기 실패", error.localizedDescription) + await send(.async(.fetchUserBlockResponse(.failure(CustomError.userError(error.localizedDescription))))) } - return .none + } + + case .fetchUserBlockResponse(let result): + switch result { + case .success(let userBlockListData): + state.userBlockListModel = userBlockListData + case .failure(let error): + Log.error("차단 유저 가져오기 실패", error.localizedDescription) + } + return .none case .appearData: return .concatenate( @@ -646,7 +646,7 @@ public struct Home { action: InnerAction ) -> Effect { switch action { - + } } diff --git a/OPeace/Projects/Presentation/Presentation/Sources/Home/Main/View/HomeView.swift b/OPeace/Projects/Presentation/Presentation/Sources/Home/Main/View/HomeView.swift index 9b72911..f4381ec 100644 --- a/OPeace/Projects/Presentation/Presentation/Sources/Home/Main/View/HomeView.swift +++ b/OPeace/Projects/Presentation/Presentation/Sources/Home/Main/View/HomeView.swift @@ -355,7 +355,7 @@ extension HomeView { resultData: item, statsData: store.statusQuestionModel?.data, isProfile: false, - userLoginID: store.profileUserModel?.data?.socialID ?? "", + userLoginID: store.profileUserModel?.data.socialID ?? "", generationColor: store.cardGenerationColor, isTapAVote: $store.isTapAVote, isTapBVote: $store.isTapBVote, @@ -401,7 +401,7 @@ extension HomeView { resultData: item, statsData: store.statusQuestionModel?.data, isProfile: false, - userLoginID: store.profileUserModel?.data?.socialID ?? "", + userLoginID: store.profileUserModel?.data.socialID ?? "", generationColor: store.cardGenerationColor, isTapAVote: $store.isTapAVote, isTapBVote: $store.isTapBVote, @@ -441,10 +441,10 @@ extension HomeView { if item.metadata?.voted == true { store.questionID = item.id ?? .zero store.send(.async(.statusQuestion(id: item.id ?? .zero))) - } else if store.profileUserModel?.data?.socialID == item.userInfo?.userID { + } else if store.profileUserModel?.data.socialID == item.userInfo?.userID { store.questionID = item.id ?? .zero store.isTapAVote = false - } else if store.profileUserModel?.data?.socialID != item.userInfo?.userID { + } else if store.profileUserModel?.data.socialID != item.userInfo?.userID { store.send(.async(.isVoteQuestionAnswer(questionID: item.id ?? .zero, choiceAnswer: store.isSelectAnswerA))) store.send(.async(.fetchQuestionList)) store.questionID = item.id ?? .zero @@ -454,10 +454,10 @@ extension HomeView { if item.metadata?.voted == true { store.questionID = item.id ?? .zero store.send(.async(.statusQuestion(id: store.questionID ?? .zero))) - } else if store.profileUserModel?.data?.socialID == item.userInfo?.userID { + } else if store.profileUserModel?.data.socialID == item.userInfo?.userID { store.questionID = item.id ?? .zero store.isTapBVote = false - } else if store.profileUserModel?.data?.socialID != item.userInfo?.userID{ + } else if store.profileUserModel?.data.socialID != item.userInfo?.userID{ store.send(.async(.isVoteQuestionAnswer(questionID: item.id ?? .zero, choiceAnswer: store.isSelectAnswerB))) store.send(.async(.fetchQuestionList)) store.questionID = item.id ?? .zero diff --git a/OPeace/Projects/Presentation/Presentation/Sources/Home/Report/Reducer/Report.swift b/OPeace/Projects/Presentation/Presentation/Sources/Home/Report/Reducer/Report.swift index 633d691..4adae2b 100644 --- a/OPeace/Projects/Presentation/Presentation/Sources/Home/Report/Reducer/Report.swift +++ b/OPeace/Projects/Presentation/Presentation/Sources/Home/Report/Reducer/Report.swift @@ -13,122 +13,148 @@ import Networkings @Reducer public struct Report { - public init() {} + public init() {} + + @ObservableState + public struct State: Equatable { - @ObservableState - public struct State: Equatable { - - var reportReasonText: String = "" - var reportButtonComplete: String = "완료" - var reportQuestionModel: ReportQuestionModel? = nil - - @Shared(.inMemory("userInfoModel")) var userInfoModel: UserInfoModel? = .init() - @Shared var questionID: Int - - public init( - questionID: Int = 0 - ) { - self._questionID = Shared(wrappedValue: questionID, .inMemory("questionID")) - } - } + var reportReasonText: String = "" + var reportButtonComplete: String = "완료" + var reportQuestionModel: FlagQuestionDTOModel? = nil - public enum Action: ViewAction, BindableAction, FeatureAction { - case binding(BindingAction) - case view(View) - case async(AsyncAction) - case inner(InnerAction) - case navigation(NavigationAction) - } + @Shared(.inMemory("userInfoModel")) var userInfoModel: UserInfoModel? = .init() + @Shared var questionID: Int - //MARK: - ViewAction - @CasePathable - public enum View { - + public init( + questionID: Int = 0 + ) { + self._questionID = Shared(wrappedValue: questionID, .inMemory("questionID")) } + } + + public enum Action: ViewAction, BindableAction, FeatureAction { + case binding(BindingAction) + case view(View) + case async(AsyncAction) + case inner(InnerAction) + case navigation(NavigationAction) + } + + //MARK: - ViewAction + @CasePathable + public enum View { - //MARK: - AsyncAction 비동기 처리 액션 - public enum AsyncAction: Equatable { - case reportQuestion(questionID: Int, reason: String) - case reportQuestionResponse(Result) - } + } + + //MARK: - AsyncAction 비동기 처리 액션 + public enum AsyncAction: Equatable { + case reportQuestion(questionID: Int, reason: String) + case reportQuestionResponse(Result) + } + + //MARK: - 앱내에서 사용하는 액션 + public enum InnerAction: Equatable { - //MARK: - 앱내에서 사용하는 액션 - public enum InnerAction: Equatable { - - } + } + + //MARK: - NavigationAction + public enum NavigationAction: Equatable { + case presntMainHome - //MARK: - NavigationAction - public enum NavigationAction: Equatable { - case presntMainHome + } + + @Dependency(QuestionUseCase.self) var questionUseCase + @Dependency(\.continuousClock) var clock + + public var body: some ReducerOf { + BindingReducer() + Reduce { state, action in + switch action { + case .binding(\.reportReasonText): + return .none + + case .binding(_): + return .none + + case .view(let viewAction): + return handleViewAction(state: &state, action: viewAction) + + case .async(let asyncAction): + return handleAsyncAction(state: &state, action: asyncAction) + + case .inner(let innerAction): + return handleInnerAction(state: &state, action: innerAction) + + case .navigation(let navigationAction): + return handleNavigationAction(state: &state, action: navigationAction) + } } - - @Dependency(QuestionUseCase.self) var questionUseCase - @Dependency(\.continuousClock) var clock - - public var body: some ReducerOf { - BindingReducer() - Reduce { state, action in - switch action { - case .binding(\.reportReasonText): - return .none - - case .binding(_): - return .none - - - case .view(let View): - switch View { - - } - - case .async(let AsyncAction): - switch AsyncAction { - - case .reportQuestion(questionID: let questionID, reason: let reason): - return .run { send in - let reportQuestionResult = await Result { - try await questionUseCase.reportQuestion(questionID: questionID, reason: reason) - } - - switch reportQuestionResult { - case .success(let reportQuestionResultData): - if let reportQuestionResultData = reportQuestionResultData { - await send(.async(.reportQuestionResponse(.success(reportQuestionResultData)))) - - try await clock.sleep(for: .seconds(1)) - - await send(.navigation(.presntMainHome)) - } - case .failure(let error): - await send(.async(.reportQuestionResponse(.failure(CustomError.encodingError(error.localizedDescription))))) - } - - } - - case .reportQuestionResponse(let result): - switch result { - case .success(let reportQuestionResult): - state.reportQuestionModel = reportQuestionResult - state.userInfoModel?.isReportQuestion = true - case .failure(let error): - Log.error("질문 신고 실패", error.localizedDescription) - } - return .none - } - - case .inner(let InnerAction): - switch InnerAction { - - } - - case .navigation(let NavigationAction): - switch NavigationAction { - - case .presntMainHome: - return .none - } - } + } + + private func handleViewAction( + state: inout State, + action: View + ) -> Effect { + switch action { + + } + } + + private func handleAsyncAction( + state: inout State, + action: AsyncAction + ) -> Effect { + switch action { + case .reportQuestion(questionID: let questionID, reason: let reason): + return .run { send in + let reportQuestionResult = await Result { + try await questionUseCase.reportQuestion(questionID: questionID, reason: reason) } + + switch reportQuestionResult { + case .success(let reportQuestionResultData): + if let reportQuestionResultData = reportQuestionResultData { + await send(.async(.reportQuestionResponse(.success(reportQuestionResultData)))) + + try await clock.sleep(for: .seconds(1)) + + await send(.navigation(.presntMainHome)) + } + case .failure(let error): + await send(.async(.reportQuestionResponse(.failure(CustomError.encodingError(error.localizedDescription))))) + } + + } + + case .reportQuestionResponse(let result): + switch result { + case .success(let reportQuestionResult): + state.reportQuestionModel = reportQuestionResult + state.userInfoModel?.isReportQuestion = true + case .failure(let error): + Log.error("질문 신고 실패", error.localizedDescription) + } + return .none + } + } + + private func handleInnerAction( + state: inout State, + action: InnerAction + ) -> Effect { + switch action { + + } + } + + private func handleNavigationAction( + state: inout State, + action: NavigationAction + ) -> Effect { + switch action { + case .presntMainHome: + return .none } + } } diff --git a/OPeace/Projects/Presentation/Presentation/Sources/Profile/BlockUser/View/BlockUserView.swift b/OPeace/Projects/Presentation/Presentation/Sources/Profile/BlockUser/View/BlockUserView.swift index 2df3c61..b0c640b 100644 --- a/OPeace/Projects/Presentation/Presentation/Sources/Profile/BlockUser/View/BlockUserView.swift +++ b/OPeace/Projects/Presentation/Presentation/Sources/Profile/BlockUser/View/BlockUserView.swift @@ -13,219 +13,219 @@ import PopupView import DesignSystem public struct BlockUserView: View { - @Bindable var store: StoreOf - var backAction: () -> Void = { } - - public init( - store: StoreOf, - backAction: @escaping () -> Void - ) { - self.store = store - self.backAction = backAction - } - - public var body: some View { - ZStack { - Color.gray600 - .edgesIgnoringSafeArea(.all) - - VStack { - Spacer() - .frame(height: 14) - - CustomTitleNaviagionBackButton(buttonAction: backAction, title: "차단 관리") - - blockUserHeader() - - ScrollView(showsIndicators: false) { - userBlockListView() - - } - - Spacer() - } - .onAppear { - store.send(.async(.fetchUserBlockList)) - } - .popup(item: $store.scope(state: \.destination?.floatingPopUP, action: \.destination.floatingPopUP)) { floatingPopUpStore in - FloatingPopUpView(store: floatingPopUpStore, title: "차단이 해제되었어요", image: .succesLogout) - .onAppear{ - store.send(.view(.timeToCloseFloatingPopUp)) - } - } customize: { popup in - popup - .type(.floater(verticalPadding: UIScreen.screenHeight * 0.02)) - .position(.bottom) - .animation(.spring) - .closeOnTap(true) - .closeOnTapOutside(true) - } + @Bindable var store: StoreOf + var backAction: () -> Void = { } + + public init( + store: StoreOf, + backAction: @escaping () -> Void + ) { + self.store = store + self.backAction = backAction + } + + public var body: some View { + ZStack { + Color.gray600 + .edgesIgnoringSafeArea(.all) + + VStack { + Spacer() + .frame(height: 14) + + CustomTitleNaviagionBackButton(buttonAction: backAction, title: "차단 관리") + + blockUserHeader() + + ScrollView(showsIndicators: false) { + userBlockListView() + } + + Spacer() + } + .onAppear { + store.send(.async(.fetchUserBlockList)) + } + .popup(item: $store.scope(state: \.destination?.floatingPopUP, action: \.destination.floatingPopUP)) { floatingPopUpStore in + FloatingPopUpView(store: floatingPopUpStore, title: "차단이 해제되었어요", image: .succesLogout) + .onAppear{ + store.send(.view(.timeToCloseFloatingPopUp)) + } + } customize: { popup in + popup + .type(.floater(verticalPadding: UIScreen.screenHeight * 0.02)) + .position(.bottom) + .animation(.spring) + .closeOnTap(true) + .closeOnTapOutside(true) + } } + } } extension BlockUserView { - - @ViewBuilder - private func blockUserHeader() -> some View { - VStack { - Spacer() - .frame(height: 16) - - HStack { - Text("차단 목록") - .pretendardFont(family: .Medium, size: 16) - .foregroundStyle(Color.gray200) - - Spacer() - } - } - .padding(.horizontal, 20) + + @ViewBuilder + private func blockUserHeader() -> some View { + VStack { + Spacer() + .frame(height: 16) + + HStack { + Text("차단 목록") + .pretendardFont(family: .Medium, size: 16) + .foregroundStyle(Color.gray200) + + Spacer() + } + } + .padding(.horizontal, 20) + } + + @ViewBuilder + private func userBlockListView() -> some View { + if store.userBlockListModel?.data == [] { + noBlockUserView() + } else { + blockUserListView() } - - @ViewBuilder - private func userBlockListView() -> some View { - if store.userBlockListModel?.data == [] { - noBlockUserView() - } else { - blockUserListView() + } + + @ViewBuilder + private func blockUserListView() -> some View { + VStack { + Spacer() + .frame(height: 16) + + if let userBlockData = store.userBlockListModel?.data { + ForEach(userBlockData, id: \.blockID) { item in + blockUserListItem( + nickName: item.nickname ?? "", + job: item.job ?? "", + generation: item.generation ?? "", + completion: { + store.send(.async(.realseUserBlock(blockUserID: item.blockedUserID ?? ""))) + }) } + } + } + } + + @ViewBuilder + private func noBlockUserView() -> some View { + VStack(alignment: .center) { + Spacer() + .frame(height: UIScreen.screenHeight * 0.3) + + Text("차단 목록이 비어있어요") + .pretendardFont(family: .SemiBold, size: 24) + .foregroundStyle(Color.gray200) + + Spacer() + .frame(height: 16) + + HStack { + Text("고민 카드 우측 상단에") + .pretendardFont(family: .Medium, size: 16) + .foregroundStyle(Color.gray200) + + Spacer() + .frame(width: 2) + + Image(asset: .questionEdit) + .resizable() + .scaledToFit() + .frame(width: 18, height: 18) + + Spacer() + .frame(width: 2) + + Text("를 누르면") + .pretendardFont(family: .Medium, size: 16) + .foregroundStyle(Color.gray200) + } + + Text("차단할 수 있어요") + .pretendardFont(family: .Medium, size: 16) + .foregroundStyle(Color.gray200) + + Spacer() } - - @ViewBuilder - private func blockUserListView() -> some View { + } + + @ViewBuilder + private func blockUserListItem( + nickName: String, + job: String, + generation: String, + completion: @escaping () -> Void + ) -> some View { + RoundedRectangle(cornerRadius: 16) + .fill(Color.gray500) + .padding(.horizontal, 20) + .frame(height: 72) + .overlay { VStack { + Spacer() + .frame(height: 28) + + HStack(spacing: .zero) { Spacer() - .frame(height: 16) + .frame(width: 16) + + Text(nickName) + .pretendardFont(family: .Bold, size: 16) + .foregroundStyle(Color.basicWhite) - if let userBlockData = store.userBlockListModel?.data { - ForEach(userBlockData, id: \.blockID) { item in - blockUserListItem( - nickName: item.nickname ?? "", - job: item.job ?? "", - generation: item.generation ?? "", - completion: { - store.send(.async(.realseUserBlock(blockUserID: item.blockedUserID ?? ""))) - }) - } - } - } - } - - @ViewBuilder - private func noBlockUserView() -> some View { - VStack(alignment: .center) { Spacer() - .frame(height: UIScreen.screenHeight * 0.3) + .frame(width: 8) - Text("차단 목록이 비어있어요") - .pretendardFont(family: .SemiBold, size: 24) - .foregroundStyle(Color.gray200) + Text(job) + .pretendardFont(family: .Medium, size: 14) + .foregroundStyle(Color.gray200) Spacer() - .frame(height: 16) + .frame(width: 8) + + Rectangle() + .fill(Color.gray400) + .frame(width: 1, height: 10) - HStack { - Text("고민 카드 우측 상단에") - .pretendardFont(family: .Medium, size: 16) - .foregroundStyle(Color.gray200) - - Spacer() - .frame(width: 2) - - Image(asset: .questionEdit) - .resizable() - .scaledToFit() - .frame(width: 18, height: 18) - - Spacer() - .frame(width: 2) - - Text("를 누르면") - .pretendardFont(family: .Medium, size: 16) - .foregroundStyle(Color.gray200) - } + Spacer() + .frame(width: 8) - Text("차단할 수 있어요") - .pretendardFont(family: .Medium, size: 16) - .foregroundStyle(Color.gray200) + Text(generation) + .pretendardFont(family: .Medium, size: 14) + .foregroundStyle(Color.gray200) Spacer() + + RoundedRectangle(cornerRadius: 40) + .stroke(Color.gray400, style: .init(lineWidth: 1)) + .frame(width: 72, height: 32) + .background(Color.gray500) + .clipShape(Capsule()) + .overlay { + Text("차단 해제") + .pretendardFont(family: .Medium, size: 14) + .foregroundStyle(Color.gray200) + } + .onTapGesture { + completion() + } + + Spacer() + .frame(width: 16) + + } + .padding(.horizontal, 16) + + + Spacer() + .frame(height: 28) } - } - - @ViewBuilder - private func blockUserListItem( - nickName: String, - job: String, - generation: String, - completion: @escaping () -> Void - ) -> some View { - RoundedRectangle(cornerRadius: 16) - .fill(Color.gray500) - .padding(.horizontal, 20) - .frame(height: 72) - .overlay { - VStack { - Spacer() - .frame(height: 28) - - HStack(spacing: .zero) { - Spacer() - .frame(width: 16) - - Text(nickName) - .pretendardFont(family: .Bold, size: 16) - .foregroundStyle(Color.basicWhite) - - Spacer() - .frame(width: 8) - - Text(job) - .pretendardFont(family: .Medium, size: 14) - .foregroundStyle(Color.gray200) - - Spacer() - .frame(width: 8) - - Rectangle() - .fill(Color.gray400) - .frame(width: 1, height: 10) - - Spacer() - .frame(width: 8) - - Text(generation) - .pretendardFont(family: .Medium, size: 14) - .foregroundStyle(Color.gray200) - - Spacer() - - RoundedRectangle(cornerRadius: 40) - .stroke(Color.gray400, style: .init(lineWidth: 1)) - .frame(width: 72, height: 32) - .background(Color.gray500) - .clipShape(Capsule()) - .overlay { - Text("차단 해제") - .pretendardFont(family: .Medium, size: 14) - .foregroundStyle(Color.gray200) - } - .onTapGesture { - completion() - } - - Spacer() - .frame(width: 16) - - } - .padding(.horizontal, 16) - - - Spacer() - .frame(height: 28) - } - } - } + } + } } diff --git a/OPeace/Projects/Presentation/Presentation/Sources/Profile/EditProfile/Reducer/EditProfile.swift b/OPeace/Projects/Presentation/Presentation/Sources/Profile/EditProfile/Reducer/EditProfile.swift index c9bffa3..0115197 100644 --- a/OPeace/Projects/Presentation/Presentation/Sources/Profile/EditProfile/Reducer/EditProfile.swift +++ b/OPeace/Projects/Presentation/Presentation/Sources/Profile/EditProfile/Reducer/EditProfile.swift @@ -32,10 +32,10 @@ public struct EditProfile { @Presents var destination: Destination.State? @Shared(.inMemory("userInfoModel")) var userInfoModel: UserInfoModel? = .init() - var profileUserModel: UpdateUserInfoModel? + var profileUserModel: UpdateUserInfoDTOModel? var nickNameModel: SignUpCheckInfoDTOModel? var editProfileJobModel: SignUpListDTOModel? - var updateUserinfoModel: UpdateUserInfoModel? + var updateUserinfoModel: UpdateUserInfoDTOModel? public init() {} } @@ -68,13 +68,13 @@ public struct EditProfile { //MARK: - AsyncAction 비동기 처리 액션 public enum AsyncAction: Equatable { - case fetchUserProfileResponse(Result) + case fetchUserProfileResponse(Result) case fetchUser case checkNickName(nickName: String) case checkNIckNameResponse(Result) case editProfileResponse(Result) case fetchEditProfileJobList - case updateUserInfoResponse(Result) + case updateUserInfoResponse(Result) case updateUserInfo( nickName: String, year: Int, @@ -188,9 +188,9 @@ public struct EditProfile { switch result { case .success(let resultData): state.profileUserModel = resultData - state.profileName = state.profileUserModel?.data?.nickname ?? "" - state.profileSelectedJob = state.profileUserModel?.data?.job ?? "" - state.profileYear = state.profileUserModel?.data?.year ?? .zero + state.profileName = state.profileUserModel?.data.nickname ?? "" + state.profileSelectedJob = state.profileUserModel?.data.job ?? "" + state.profileYear = state.profileUserModel?.data.year ?? .zero case let .failure(error): #logNetwork("프로필 오류", error.localizedDescription) } diff --git a/OPeace/Projects/Presentation/Presentation/Sources/Profile/Main/Reducer/Profile.swift b/OPeace/Projects/Presentation/Presentation/Sources/Profile/Main/Reducer/Profile.swift index 1fe019c..ab30c8c 100644 --- a/OPeace/Projects/Presentation/Presentation/Sources/Profile/Main/Reducer/Profile.swift +++ b/OPeace/Projects/Presentation/Presentation/Sources/Profile/Main/Reducer/Profile.swift @@ -26,10 +26,10 @@ public struct Profile { var profileGenerationTextColor: Color = Color.gray600 var profileGenerationText: String = "" - public var profileUserModel: UpdateUserInfoModel? = nil + public var profileUserModel: UpdateUserInfoDTOModel? = nil var userLogoutModel: UserDTOModel? = nil var myQuestionListModel: QuestionModel? = nil - var deleteQuestionModel: DeleteQuestionModel? = nil + var deleteQuestionModel: FlagQuestionDTOModel? = nil var statusQuestionModel: StatusQuestionModel? = nil var profileGenerationYear: Int? = .zero @@ -85,7 +85,7 @@ public struct Profile { //MARK: - AsyncAction 비동기 처리 액션 public enum AsyncAction: Equatable { - case fetchUserProfileResponse(Result) + case fetchUserProfileResponse(Result) case fetchUser case logoutUseResponse(Result) case logoutUser @@ -93,7 +93,7 @@ public struct Profile { case fetchQuestionResponse(Result) case fetchQuestion case deleteQuestion(questionID: Int) - case deleteQuestionResponse(Result) + case deleteQuestionResponse(Result) case statusQuestion(id: Int) case statusQuestionResponse(Result) @@ -283,7 +283,7 @@ public struct Profile { switch result { case .success(let resultData): state.profileUserModel = resultData - state.profileGenerationYear = state.profileUserModel?.data?.year + state.profileGenerationYear = state.profileUserModel?.data.year state.userInfoModel?.isChangeProfile = false case let .failure(error): diff --git a/OPeace/Projects/Presentation/Presentation/Sources/Profile/Main/View/ProfileView.swift b/OPeace/Projects/Presentation/Presentation/Sources/Profile/Main/View/ProfileView.swift index 28e83cf..d3da9d5 100644 --- a/OPeace/Projects/Presentation/Presentation/Sources/Profile/Main/View/ProfileView.swift +++ b/OPeace/Projects/Presentation/Presentation/Sources/Profile/Main/View/ProfileView.swift @@ -15,303 +15,303 @@ import SwiftUIIntrospect import Utill public struct ProfileView: View { - @Bindable var store: StoreOf - @Shared(.appStorage("lastViewedPage")) var lastViewedPage: Int = .zero - - var backAction: () -> Void = {} - - - public init( - store: StoreOf, - backAction: @escaping () -> Void - ) { - self.store = store - self.backAction = backAction - } - - public var body: some View { - ZStack { - Color.gray600 - .edgesIgnoringSafeArea(.all) + @Bindable var store: StoreOf + @Shared(.appStorage("lastViewedPage")) var lastViewedPage: Int = .zero + + var backAction: () -> Void = {} + + + public init( + store: StoreOf, + backAction: @escaping () -> Void + ) { + self.store = store + self.backAction = backAction + } + + public var body: some View { + ZStack { + Color.gray600 + .edgesIgnoringSafeArea(.all) + + VStack { + Spacer() + .frame(height: 14) + + CustomTitleNaviagionBackButton(buttonAction: backAction, title: "마이페이지") + + userInfoTitle( + nickName: store.profileUserModel?.data.nickname ?? "" , + job: store.profileUserModel?.data.job ?? "", + generation: store.profileUserModel?.data.generation ?? "" ) + + myPostWritingTitle() + + postingListView() + + Spacer() + } + .onAppear { + store.send(.async(.fetchUser)) + store.send(.async(.fetchQuestion)) + } + .onDisappear { + lastViewedPage = .zero + } + .introspect(.navigationStack, on: .iOS(.v17, .v18)) { navigationController in + navigationController.interactivePopGestureRecognizer?.isEnabled = true + } + .sheet(item: $store.scope(state: \.destination?.setting, action: \.destination.setting)) { settingStore in + SettingView(store: settingStore) { + guard let settingtitem = settingStore.settingtitem else {return} + store.send(.view(.switchModalAction(settingtitem ))) + } closeModalAction: { + store.send(.view(.closeModal)) + } + .presentationDetents([.height(UIScreen.screenHeight * 0.4)]) + .presentationCornerRadius(20) + .presentationDragIndicator(.hidden) + } + + .popup(item: $store.scope(state: \.destination?.popup, action: \.destination.popup)) { customPopUp in + if store.isLogOutPopUp { + CustomBasicPopUpView(store: customPopUp, title: store.popUpText) { + store.send(.async(.logoutUser)) + } cancelAction: { + store.send(.view(.closeModal)) + } + } else if store.isDeleteUserPopUp { + CustomBasicPopUpView(store: customPopUp, title: store.popUpText) { + store.send(.view(.closeModal)) - VStack { - Spacer() - .frame(height: 14) - - CustomTitleNaviagionBackButton(buttonAction: backAction, title: "마이페이지") - - userInfoTitle( - nickName: store.profileUserModel?.data?.nickname ?? "", - job: store.profileUserModel?.data?.job ?? "", - generation: store.profileUserModel?.data?.generation ?? "") - - myPostWritingTitle() - - postingListView() - - Spacer() - } - .onAppear { - store.send(.async(.fetchUser)) - store.send(.async(.fetchQuestion)) - } - .onDisappear { - lastViewedPage = .zero - } - .introspect(.navigationStack, on: .iOS(.v17, .v18)) { navigationController in - navigationController.interactivePopGestureRecognizer?.isEnabled = true - } - .sheet(item: $store.scope(state: \.destination?.setting, action: \.destination.setting)) { settingStore in - SettingView(store: settingStore) { - guard let settingtitem = settingStore.settingtitem else {return} - store.send(.view(.switchModalAction(settingtitem ))) - } closeModalAction: { - store.send(.view(.closeModal)) - } - .presentationDetents([.height(UIScreen.screenHeight * 0.4)]) - .presentationCornerRadius(20) - .presentationDragIndicator(.hidden) + DispatchQueue.main.asyncAfter(deadline: .now() + 0.8) { + store.send(.navigation(.presntWithDraw)) } + } cancelAction: { + store.send(.view(.closeModal)) + } + } else if store.isDeleteQuestionPopUp { + CustomBasicPopUpView(store: customPopUp, title: store.popUpText) { + store.send(.view(.closeModal)) - .popup(item: $store.scope(state: \.destination?.popup, action: \.destination.popup)) { customPopUp in - if store.isLogOutPopUp { - CustomBasicPopUpView(store: customPopUp, title: store.popUpText) { - store.send(.async(.logoutUser)) - } cancelAction: { - store.send(.view(.closeModal)) - } - } else if store.isDeleteUserPopUp { - CustomBasicPopUpView(store: customPopUp, title: store.popUpText) { - store.send(.view(.closeModal)) - - DispatchQueue.main.asyncAfter(deadline: .now() + 0.8) { - store.send(.navigation(.presntWithDraw)) - } - } cancelAction: { - store.send(.view(.closeModal)) - } - } else if store.isDeleteQuestionPopUp { - CustomBasicPopUpView(store: customPopUp, title: store.popUpText) { - store.send(.view(.closeModal)) - - DispatchQueue.main.asyncAfter(deadline: .now() + 0.4) { - store.send(.async(.deleteQuestion(questionID: store.deleteQuestionId))) - } - } cancelAction: { - store.send(.view(.closeModal)) - } - } - - } customize: { popup in - popup - .type(.floater(verticalPadding: UIScreen.screenHeight * 0.35)) - .position(.bottom) - .animation(.spring) - .closeOnTap(true) - .closeOnTapOutside(true) - .backgroundColor(Color.basicBlack.opacity(0.8)) + DispatchQueue.main.asyncAfter(deadline: .now() + 0.4) { + store.send(.async(.deleteQuestion(questionID: store.deleteQuestionId))) } + } cancelAction: { + store.send(.view(.closeModal)) + } } + + } customize: { popup in + popup + .type(.floater(verticalPadding: UIScreen.screenHeight * 0.35)) + .position(.bottom) + .animation(.spring) + .closeOnTap(true) + .closeOnTapOutside(true) + .backgroundColor(Color.basicBlack.opacity(0.8)) + } } + } } extension ProfileView { - - @ViewBuilder - private func userInfoTitle( - nickName: String, - job: String, - generation: String - ) -> some View { - VStack { - Spacer() - .frame(height: 20) - + + @ViewBuilder + private func userInfoTitle( + nickName: String, + job: String, + generation: String + ) -> some View { + VStack { + Spacer() + .frame(height: 20) + + HStack { + Text(nickName) + .pretendardFont(family: .SemiBold, size: 24) + .foregroundStyle(Color.basicWhite) + + Spacer() + + } + .padding(.horizontal, 20) + + Spacer() + .frame(height: 16) + + HStack { + RoundedRectangle(cornerRadius: 20) + .stroke(Color.gray400, style: .init(lineWidth: 1.13)) + .frame(width: job.calculateWidthProfile(for: job), height: 24) + .background(Color.gray600) + .overlay(alignment: .center) { HStack { - Text(nickName) - .pretendardFont(family: .SemiBold, size: 24) - .foregroundStyle(Color.basicWhite) - - Spacer() + Text(job) + .pretendardFont(family: .SemiBold, size: 12) + .foregroundStyle(Color.basicWhite) + } + .padding(.horizontal, 12) + } + + Spacer() + .frame(width: 4) + + RoundedRectangle(cornerRadius: 20) + .fill(store.profileGenerationColor) + .frame(width: generation.calculateWidthProfileGeneration(for: generation), height: 24) + .overlay(alignment: .center) { + Text(generation) + .pretendardFont(family: .SemiBold, size: 12) + .foregroundStyle(store.profileGenerationTextColor) + } + + + Spacer() + .frame(width: 4) + + Image(asset: .setting) + .resizable() + .scaledToFit() + .frame(width: 20, height: 20) + .onTapGesture { + store.send(.view(.tapPresntSettingModal)) + } + + Spacer() + } + .padding(.horizontal, 20) + + } + } + + @ViewBuilder + private func myPostWritingTitle() -> some View { + VStack { + Spacer() + .frame(height: 32) + + HStack { + Text("내가 올린 글") + .pretendardFont(family: .Regular, size: 16) + .foregroundStyle(Color.gray200) + + Spacer() + } + .padding(.horizontal, 20) + } + } + + @ViewBuilder + private func postingListView() -> some View { + if store.myQuestionListModel?.data?.results == [] { + noPostingListView() + } else { + myPostitngList() + } + } + + @ViewBuilder + private func myPostitngList() -> some View { + VStack { + Spacer() + .frame(height: 16) + + if let resultData = store.myQuestionListModel?.data?.results { + FlippableCardView( + data: resultData, + shouldSaveState: false, + onItemAppear: { item in + if let resultItem = item as? ResultData { + store.questionId = resultItem.id ?? .zero + } + } , content: { item in + CardItemView( + resultData: item, + statsData: store.statusQuestionModel?.data, + isProfile: true, + userLoginID: store.profileUserModel?.data.socialID ?? "", + generationColor: store.cardGenerationColor, + isTapAVote: .constant(false), + isTapBVote: .constant(false), + isLogOut: false, + isLookAround: false, + isDeleteUser: false, + answerRatio: (A: Int(item.answerRatio?.a ?? 0), B: Int(item.answerRatio?.b ?? 0)), + editTapAction: { + store.send(.view(.presntPopUp)) + store.isDeleteQuestionPopUp = true + store.deleteQuestionId = item.id ?? .zero + store.popUpText = "고민을 삭제하시겠어요?" + }, + likeTapAction: { _ in }, + appearStatusAction: { + }, + choiceTapAction: {}) + .onChange(of: store.questionId) { oldValue, newValue in + store.send(.async(.statusQuestion(id: newValue))) } - .padding(.horizontal, 20) - + }) + } + } + } + + @ViewBuilder + private func noPostingListView() -> some View { + VStack { + Spacer() + .frame(height: 16) + + RoundedRectangle(cornerRadius: 32) + .fill(Color.gray500) + .frame(height: UIScreen.screenHeight * 0.6) + .overlay(alignment: .center) { + VStack { Spacer() - .frame(height: 16) - HStack { - RoundedRectangle(cornerRadius: 20) - .stroke(Color.gray400, style: .init(lineWidth: 1.13)) - .frame(width: job.calculateWidthProfile(for: job), height: 24) - .background(Color.gray600) - .overlay(alignment: .center) { - HStack { - Text(job) - .pretendardFont(family: .SemiBold, size: 12) - .foregroundStyle(Color.basicWhite) - } - .padding(.horizontal, 12) - } - - Spacer() - .frame(width: 4) - - RoundedRectangle(cornerRadius: 20) - .fill(store.profileGenerationColor) - .frame(width: generation.calculateWidthProfileGeneration(for: generation), height: 24) - .overlay(alignment: .center) { - Text(generation) - .pretendardFont(family: .SemiBold, size: 12) - .foregroundStyle(store.profileGenerationTextColor) - } - - - Spacer() - .frame(width: 4) - - Image(asset: .setting) - .resizable() - .scaledToFit() - .frame(width: 20, height: 20) - .onTapGesture { - store.send(.view(.tapPresntSettingModal)) - } - - Spacer() - } - .padding(.horizontal, 20) + Image(asset: .questonSmail) + .resizable() + .scaledToFit() + .frame(width: 62, height: 62) - } - } - - @ViewBuilder - private func myPostWritingTitle() -> some View { - VStack { Spacer() - .frame(height: 32) + .frame(height: 16) + + Text("아직 작성한 고민이 없어요!") + .pretendardFont(family: .SemiBold, size: 24) + .foregroundStyle(Color.gray200) - HStack { - Text("내가 올린 글") - .pretendardFont(family: .Regular, size: 16) - .foregroundStyle(Color.gray200) - - Spacer() - } - .padding(.horizontal, 20) - } - } - - @ViewBuilder - private func postingListView() -> some View { - if store.myQuestionListModel?.data?.results == [] { - noPostingListView() - } else { - myPostitngList() - } - } - - @ViewBuilder - private func myPostitngList() -> some View { - VStack { Spacer() - .frame(height: 16) + .frame(height: 16) + + Text("지금 고민을 등록해 볼까요?") + .pretendardFont(family: .Regular, size: 16) + .foregroundStyle(Color.gray300) - if let resultData = store.myQuestionListModel?.data?.results { - FlippableCardView( - data: resultData, - shouldSaveState: false, - onItemAppear: { item in - if let resultItem = item as? ResultData { - store.questionId = resultItem.id ?? .zero - } - } , content: { item in - CardItemView( - resultData: item, - statsData: store.statusQuestionModel?.data, - isProfile: true, - userLoginID: store.profileUserModel?.data?.socialID ?? "", - generationColor: store.cardGenerationColor, - isTapAVote: .constant(false), - isTapBVote: .constant(false), - isLogOut: false, - isLookAround: false, - isDeleteUser: false, - answerRatio: (A: Int(item.answerRatio?.a ?? 0), B: Int(item.answerRatio?.b ?? 0)), - editTapAction: { - store.send(.view(.presntPopUp)) - store.isDeleteQuestionPopUp = true - store.deleteQuestionId = item.id ?? .zero - store.popUpText = "고민을 삭제하시겠어요?" - }, - likeTapAction: { _ in }, - appearStatusAction: { - - }, - choiceTapAction: {}) - .onChange(of: store.questionId) { oldValue, newValue in - store.send(.async(.statusQuestion(id: newValue))) - } - }) - } - } - } - - @ViewBuilder - private func noPostingListView() -> some View { - VStack { Spacer() - .frame(height: 16) + .frame(height: 32) - RoundedRectangle(cornerRadius: 32) - .fill(Color.gray500) - .frame(height: UIScreen.screenHeight * 0.6) - .overlay(alignment: .center) { - VStack { - Spacer() - - Image(asset: .questonSmail) - .resizable() - .scaledToFit() - .frame(width: 62, height: 62) - - Spacer() - .frame(height: 16) - - Text("아직 작성한 고민이 없어요!") - .pretendardFont(family: .SemiBold, size: 24) - .foregroundStyle(Color.gray200) - - Spacer() - .frame(height: 16) - - Text("지금 고민을 등록해 볼까요?") - .pretendardFont(family: .Regular, size: 16) - .foregroundStyle(Color.gray300) - - Spacer() - .frame(height: 32) - - RoundedRectangle(cornerRadius: 20) - .fill(Color.basicWhite) - .frame(width: 120, height: 56) - .clipShape(Capsule()) - .overlay { - Text("글쓰기") - .pretendardFont(family: .Medium, size: 20) - .foregroundStyle(Color.textColor100) - } - .onTapGesture { - store.send(.navigation(.presnetCreateQuestionList)) - } - - Spacer() - } - - } + RoundedRectangle(cornerRadius: 20) + .fill(Color.basicWhite) + .frame(width: 120, height: 56) + .clipShape(Capsule()) + .overlay { + Text("글쓰기") + .pretendardFont(family: .Medium, size: 20) + .foregroundStyle(Color.textColor100) + } + .onTapGesture { + store.send(.navigation(.presnetCreateQuestionList)) + } + + Spacer() + } + } - .padding(.horizontal, 16) } + .padding(.horizontal, 16) + } } diff --git a/OPeace/Projects/Presentation/Presentation/Sources/Profile/WithDraw/Reducer/WithDraw.swift b/OPeace/Projects/Presentation/Presentation/Sources/Profile/WithDraw/Reducer/WithDraw.swift index 28d02fd..9befb81 100644 --- a/OPeace/Projects/Presentation/Presentation/Sources/Profile/WithDraw/Reducer/WithDraw.swift +++ b/OPeace/Projects/Presentation/Presentation/Sources/Profile/WithDraw/Reducer/WithDraw.swift @@ -132,7 +132,7 @@ public struct WithDraw { if let deleteUserData = deleteUserData { await send(.async(.deleteUserResponse(.success(deleteUserData)))) - try await self.clock.sleep(for: .seconds(1)) + try await self.clock.sleep(for: .seconds(0.3)) if deleteUserData.data.status == true { await send(.navigation(.presntDeleteUser)) } diff --git a/Opeace/Projects/Core/Networking/Model/Sources/SignUp/DTO/UpdateUserInfoDTOModel.swift b/Opeace/Projects/Core/Networking/Model/Sources/SignUp/DTO/UpdateUserInfoDTOModel.swift new file mode 100644 index 0000000..6875f08 --- /dev/null +++ b/Opeace/Projects/Core/Networking/Model/Sources/SignUp/DTO/UpdateUserInfoDTOModel.swift @@ -0,0 +1,48 @@ +// +// UpdateUserInfoDTOModel.swift +// Model +// +// Created by Wonji Suh on 11/11/24. +// + +import Foundation + +public struct UpdateUserInfoDTOModel: Codable, Equatable { + public let data: UpdateUserInfoResponseDTOModel + + public init( + data: UpdateUserInfoResponseDTOModel + ) { + self.data = data + } +} + +public struct UpdateUserInfoResponseDTOModel: Codable, Equatable { + public let socialID, socialType, email: String + public let createdAt, generation: String + public let year: Int? + public let job, nickname: String? + public let isFirstLogin: Bool + + public init( + socialID: String, + socialType: String, + email: String, + createdAt: String, + nickname: String?, + year: Int?, + job: String?, + generation: String, + isFirstLogin: Bool + ) { + self.socialID = socialID + self.socialType = socialType + self.email = email + self.createdAt = createdAt + self.nickname = nickname + self.year = year + self.job = job + self.generation = generation + self.isFirstLogin = isFirstLogin + } +} diff --git a/Opeace/Projects/Core/Networking/Model/Sources/SignUp/Model/CheckGeneraion.swift b/Opeace/Projects/Core/Networking/Model/Sources/SignUp/Model/CheckGeneraion.swift index 1981604..f2e4de8 100644 --- a/Opeace/Projects/Core/Networking/Model/Sources/SignUp/Model/CheckGeneraion.swift +++ b/Opeace/Projects/Core/Networking/Model/Sources/SignUp/Model/CheckGeneraion.swift @@ -7,23 +7,13 @@ import Foundation // MARK: - Welcome -public struct CheckGeneraionModel: Codable, Equatable { - public let data: CheckGeneraionResponseModel? +public struct CheckGeneraionModel: Decodable { + let data: CheckGeneraionResponseModel? - public init( - data: CheckGeneraionResponseModel? - ) { - self.data = data - } } // MARK: - DataClass -public struct CheckGeneraionResponseModel: Codable, Equatable { - public let data: String? +struct CheckGeneraionResponseModel: Decodable { + let data: String? - public init( - data: String? - ) { - self.data = data - } } diff --git a/Opeace/Projects/Core/Networking/Model/Sources/SignUp/Model/CheckNickNameResponse.swift b/Opeace/Projects/Core/Networking/Model/Sources/SignUp/Model/CheckNickNameResponse.swift index ac089fa..f9c4377 100644 --- a/Opeace/Projects/Core/Networking/Model/Sources/SignUp/Model/CheckNickNameResponse.swift +++ b/Opeace/Projects/Core/Networking/Model/Sources/SignUp/Model/CheckNickNameResponse.swift @@ -7,30 +7,17 @@ import Foundation -public struct CheckNickNameModel: Codable, Equatable { - public var data: CheckNickNameResponse? +public struct CheckNickNameModel: Decodable { + var data: CheckNickNameResponse? - public init( - data: CheckNickNameResponse? - ) { - self.data = data - } } -public struct CheckNickNameResponse: Codable, Equatable { - public var exists: Bool? - public var message: String? +struct CheckNickNameResponse: Decodable { + var exists: Bool? + var message: String? - public enum CodingKeys: String, CodingKey { + enum CodingKeys: String, CodingKey { case exists, message } - - public init( - exists: Bool?, - message: String? - ) { - self.exists = exists - self.message = message - } } diff --git a/Opeace/Projects/Core/Networking/Model/Sources/SignUp/Model/GenerationListResponse.swift b/Opeace/Projects/Core/Networking/Model/Sources/SignUp/Model/GenerationListResponse.swift index 33a0e62..160c3b5 100644 --- a/Opeace/Projects/Core/Networking/Model/Sources/SignUp/Model/GenerationListResponse.swift +++ b/Opeace/Projects/Core/Networking/Model/Sources/SignUp/Model/GenerationListResponse.swift @@ -7,20 +7,14 @@ import Foundation -public struct GenerationListResponse: Codable, Equatable { - public let data: GenerationDataModel? +public struct GenerationListResponse: Decodable { + let data: GenerationDataModel? - public init(data: GenerationDataModel?) { - self.data = data - } } -public struct GenerationDataModel: Codable, Equatable { - public let data: [String]? +struct GenerationDataModel: Decodable { + let data: [String]? - public init(data: [String]?) { - self.data = data - } } diff --git a/Opeace/Projects/Core/Networking/Model/Sources/SignUp/Model/UpdateUserInfoModel.swift b/Opeace/Projects/Core/Networking/Model/Sources/SignUp/Model/UpdateUserInfoModel.swift new file mode 100644 index 0000000..82c53d3 --- /dev/null +++ b/Opeace/Projects/Core/Networking/Model/Sources/SignUp/Model/UpdateUserInfoModel.swift @@ -0,0 +1,34 @@ +// +// UpdateUserInfoModel.swift +// Model +// +// Created by 서원지 on 7/30/24. +// + +import Foundation + +// MARK: - Welcome +public struct UpdateUserInfoModel: Decodable { + let data: UpdateUserInfoResponse? + +} + +// MARK: - DataClass +struct UpdateUserInfoResponse: Decodable { + let socialID, socialType, email: String? + let phone: String? + let createdAt, lastLogin, nickname: String? + let year: Int? + let job, generation: String? + let isFirstLogin: Bool? + + enum CodingKeys: String, CodingKey { + case socialID = "social_id" + case socialType = "social_type" + case email, phone + case createdAt = "created_at" + case lastLogin = "last_login" + case nickname, year, job, generation + case isFirstLogin = "is_first_login" + } +}