From 42b1bcb032da8f9020440d08271f02943259954d Mon Sep 17 00:00:00 2001 From: seungyooooong Date: Fri, 2 Jan 2026 15:20:13 +0900 Subject: [PATCH 01/18] =?UTF-8?q?feat=20#161:=20v2=20API=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit + 네이밍 수정 --- .../ChallengeGroupsDataSource.swift | 4 ++-- .../Response/GetMemberTodosResponse.swift | 19 ++++++++++++++----- .../Network/Response/GetMyTodosResponse.swift | 1 + .../Router/ChallengeGroupsRouter.swift | 10 +++++----- .../ChallengeGroupsRepository.swift | 10 +++++++--- .../ChallengeGroupsRepositoryTest.swift | 2 +- dogether/Domain/Entity/TodoEntity.swift | 9 +++++++++ .../Protocol/ChallengeGroupsProtocol.swift | 2 +- .../UseCase/ChallengeGroupUseCase.swift | 3 ++- 9 files changed, 42 insertions(+), 18 deletions(-) diff --git a/dogether/Data/DataSources/ChallengeGroupsDataSource.swift b/dogether/Data/DataSources/ChallengeGroupsDataSource.swift index ffbb43b0..31dbb4d3 100644 --- a/dogether/Data/DataSources/ChallengeGroupsDataSource.swift +++ b/dogether/Data/DataSources/ChallengeGroupsDataSource.swift @@ -28,7 +28,7 @@ final class ChallengeGroupsDataSource { try await NetworkManager.shared.request(ChallengeGroupsRouter.certifyTodo(todoId: todoId, certifyTodoRequest: certifyTodoRequest)) } - func readTodo(todoId: String) async throws { - try await NetworkManager.shared.request(ChallengeGroupsRouter.readTodo(todoId: todoId)) + func readTodo(todoHistoryId: String) async throws { + try await NetworkManager.shared.request(ChallengeGroupsRouter.readTodo(todoHistoryId: todoHistoryId)) } } diff --git a/dogether/Data/Network/Response/GetMemberTodosResponse.swift b/dogether/Data/Network/Response/GetMemberTodosResponse.swift index 7f5b9cab..4964c670 100644 --- a/dogether/Data/Network/Response/GetMemberTodosResponse.swift +++ b/dogether/Data/Network/Response/GetMemberTodosResponse.swift @@ -9,30 +9,39 @@ import Foundation struct GetMemberTodosResponse: Decodable { let currentTodoHistoryToReadIndex: Int - let todos: [MemberTodo] + let todos: [TodoEntityInGetMemberTodos] } -struct MemberTodo: Decodable { - let id: Int +struct TodoEntityInGetMemberTodos: Decodable { + let historyId: Int + let todoId: Int let content: String let status: String + let canRequestCertification: Bool + let canRequestCertificationReview: Bool let certificationContent: String? let certificationMediaUrl: String? let isRead: Bool let reviewFeedback: String? init( - id: Int, + historyId: Int, + todoId: Int, content: String, status: String, + canRequestCertification: Bool, + canRequestCertificationReview: Bool, certificationContent: String? = nil, certificationMediaUrl: String? = nil, isRead: Bool, reviewFeedback: String? = nil ) { - self.id = id + self.historyId = historyId + self.todoId = todoId self.content = content self.status = status + self.canRequestCertification = canRequestCertification + self.canRequestCertificationReview = canRequestCertificationReview self.certificationContent = certificationContent self.certificationMediaUrl = certificationMediaUrl self.isRead = isRead diff --git a/dogether/Data/Network/Response/GetMyTodosResponse.swift b/dogether/Data/Network/Response/GetMyTodosResponse.swift index d3dbed9e..439dc35b 100644 --- a/dogether/Data/Network/Response/GetMyTodosResponse.swift +++ b/dogether/Data/Network/Response/GetMyTodosResponse.swift @@ -15,6 +15,7 @@ struct TodoEntityInGetMyTodos: Decodable { let id: Int let content: String var status: String + var canRequestCertificationReview: Bool var certificationContent: String? var certificationMediaUrl: String? var reviewFeedback: String? diff --git a/dogether/Data/Network/Router/ChallengeGroupsRouter.swift b/dogether/Data/Network/Router/ChallengeGroupsRouter.swift index 6329310b..82680eed 100644 --- a/dogether/Data/Network/Router/ChallengeGroupsRouter.swift +++ b/dogether/Data/Network/Router/ChallengeGroupsRouter.swift @@ -13,7 +13,7 @@ enum ChallengeGroupsRouter: NetworkEndpoint { case getMyTodos(groupId: String, date: String) case getMyYesterdayTodos case getMemberTodos(groupId: String, memberId: String) - case readTodo(todoId: String) + case readTodo(todoHistoryId: String) var path: String { switch self { @@ -22,13 +22,13 @@ enum ChallengeGroupsRouter: NetworkEndpoint { case .certifyTodo(let todoId, _): // FIXME: 추후 TodosRouter 분리 return Path.api + Path.v1 + Path.todos + "/\(todoId)/certify" case .getMyTodos(let groupId, _): - return Path.api + Path.v1 + Path.challengeGroups + "/\(groupId)/my-todos" + return Path.api + Path.v2 + Path.challengeGroups + "/\(groupId)/my-todos" case .getMyYesterdayTodos: return Path.api + Path.v1 + Path.challengeGroups + "/my/yesterday" case .getMemberTodos(let groupId, let memberId): - return Path.api + Path.v1 + Path.challengeGroups + "/\(groupId)/challenge-group-members/\(memberId)/today-todo-history" - case .readTodo(let todoId): // FIXME: 추후 TodoHistoryRouter 분리 - return Path.api + Path.v1 + Path.todoHistory + "/\(todoId)" + return Path.api + Path.v2 + Path.challengeGroups + "/\(groupId)/challenge-group-members/\(memberId)/today-todo-history" + case .readTodo(let todoHistoryId): // FIXME: 추후 TodoHistoryRouter 분리 + return Path.api + Path.v1 + Path.todoHistory + "/\(todoHistoryId)" } } diff --git a/dogether/Data/Repository/ChallengeGroupsRepository.swift b/dogether/Data/Repository/ChallengeGroupsRepository.swift index 56ac1c9f..4e15ff58 100644 --- a/dogether/Data/Repository/ChallengeGroupsRepository.swift +++ b/dogether/Data/Repository/ChallengeGroupsRepository.swift @@ -25,6 +25,7 @@ final class ChallengeGroupsRepository: ChallengeGroupsProtocol { id: $0.id, content: $0.content, status: TodoStatus(rawValue: $0.status) ?? .waitCertification, + canRequestReview: $0.canRequestCertificationReview, certificationContent: $0.certificationContent, certificationMediaUrl: $0.certificationMediaUrl, reviewFeedback: $0.reviewFeedback @@ -40,10 +41,13 @@ final class ChallengeGroupsRepository: ChallengeGroupsProtocol { let currentIndex = response.currentTodoHistoryToReadIndex let memberTodos = response.todos.map { TodoEntity( - id: $0.id, + historyId: $0.historyId, + id: $0.todoId, content: $0.content, status: TodoStatus(rawValue: $0.status) ?? .waitCertification, thumbnailStatus: $0.isRead ? .done : .yet, + canRequestCertification: $0.canRequestCertification, + canRequestReview: $0.canRequestCertificationReview, certificationContent: $0.certificationContent, certificationMediaUrl: $0.certificationMediaUrl, reviewFeedback: $0.reviewFeedback @@ -52,8 +56,8 @@ final class ChallengeGroupsRepository: ChallengeGroupsProtocol { return (currentIndex, memberTodos) } - func readTodo(todoId: String) async throws { - try await challengeGroupsDataSource.readTodo(todoId: todoId) + func readTodo(todoHistoryId: String) async throws { + try await challengeGroupsDataSource.readTodo(todoHistoryId: todoHistoryId) } func certifyTodo(todoId: String, certifyTodoRequest: CertifyTodoRequest) async throws { diff --git a/dogether/Data/RepositoryTest/ChallengeGroupsRepositoryTest.swift b/dogether/Data/RepositoryTest/ChallengeGroupsRepositoryTest.swift index 52fe8dd2..e9460d56 100644 --- a/dogether/Data/RepositoryTest/ChallengeGroupsRepositoryTest.swift +++ b/dogether/Data/RepositoryTest/ChallengeGroupsRepositoryTest.swift @@ -50,7 +50,7 @@ final class ChallengeGroupsRepositoryTest: ChallengeGroupsProtocol { ]) } - func readTodo(todoId: String) async throws { } + func readTodo(todoHistoryId: String) async throws { } func certifyTodo(todoId: String, certifyTodoRequest: CertifyTodoRequest) async throws { } } diff --git a/dogether/Domain/Entity/TodoEntity.swift b/dogether/Domain/Entity/TodoEntity.swift index 0168f0bf..14a5af3a 100644 --- a/dogether/Domain/Entity/TodoEntity.swift +++ b/dogether/Domain/Entity/TodoEntity.swift @@ -8,29 +8,38 @@ import Foundation struct TodoEntity: BaseEntity { + let historyId: Int? // MARK: readTodo에서 사용 let id: Int let content: String var status: TodoStatus var thumbnailStatus: ThumbnailStatus + var canRequestCertification: Bool + var canRequestReview: Bool var certificationContent: String? var certificationMediaUrl: String? var reviewFeedback: String? var createdAt: String? init( + historyId: Int? = nil, id: Int, content: String, status: TodoStatus, thumbnailStatus: ThumbnailStatus = .yet, + canRequestCertification: Bool = false, + canRequestReview: Bool = false, certificationContent: String? = nil, certificationMediaUrl: String? = nil, reviewFeedback: String? = nil, createdAt: String? = nil ) { + self.historyId = historyId self.id = id self.content = content self.status = status self.thumbnailStatus = thumbnailStatus + self.canRequestCertification = canRequestCertification + self.canRequestReview = canRequestReview self.certificationContent = certificationContent self.certificationMediaUrl = certificationMediaUrl self.reviewFeedback = reviewFeedback diff --git a/dogether/Domain/Protocol/ChallengeGroupsProtocol.swift b/dogether/Domain/Protocol/ChallengeGroupsProtocol.swift index 6a5787b2..e4ca1585 100644 --- a/dogether/Domain/Protocol/ChallengeGroupsProtocol.swift +++ b/dogether/Domain/Protocol/ChallengeGroupsProtocol.swift @@ -11,6 +11,6 @@ protocol ChallengeGroupsProtocol { func createTodos(groupId: String, createTodosRequest: CreateTodosRequest) async throws func getMyTodos(groupId: String, date: String) async throws -> [TodoEntity] func getMemberTodos(groupId: Int, memberId: Int) async throws -> (index: Int, todos: [TodoEntity]) - func readTodo(todoId: String) async throws + func readTodo(todoHistoryId: String) async throws func certifyTodo(todoId: String, certifyTodoRequest: CertifyTodoRequest) async throws } diff --git a/dogether/Domain/UseCase/ChallengeGroupUseCase.swift b/dogether/Domain/UseCase/ChallengeGroupUseCase.swift index 9468f4c1..a5e732c8 100644 --- a/dogether/Domain/UseCase/ChallengeGroupUseCase.swift +++ b/dogether/Domain/UseCase/ChallengeGroupUseCase.swift @@ -28,9 +28,10 @@ final class ChallengeGroupUseCase { } func readTodo(todo: TodoEntity) async throws { + guard let todoHistoryId = todo.historyId else { return } // MARK: 이미 thumbnailStatus 가 done인 투두는 read API 호출할 필요 x if todo.thumbnailStatus == .done { return } - try await repository.readTodo(todoId: String(todo.id)) + try await repository.readTodo(todoHistoryId: String(todoHistoryId)) } func certifyTodo(todoId: Int, content: String, mediaUrl: String) async throws { From 3e1fce09b04f26a22d8b4f2c0b99b588c9ea6b89 Mon Sep 17 00:00:00 2001 From: seungyooooong Date: Sat, 3 Jan 2026 18:38:18 +0900 Subject: [PATCH 02/18] =?UTF-8?q?feat=20#161:=20Remind=20=EB=B2=84?= =?UTF-8?q?=ED=8A=BC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. TodosRouter 분리 2. review -> remind 용어 수정 --- .../ChallengeGroupsDataSource.swift | 12 ++-- .../Data/DataSources/TodosDataSource.swift | 26 +++++++++ .../Network/Request/RemindTodoRequest.swift | 12 ++++ .../Router/ChallengeGroupsRouter.swift | 7 +-- .../Data/Network/Router/TodosRouter.swift | 55 ++++++++++++++++++ .../ChallengeGroupsRepository.swift | 10 +--- .../Data/Repository/TodosRepository.swift | 24 ++++++++ .../ChallengeGroupsRepositoryTest.swift | 2 - .../RepositoryTest/TodosRepositoryTest.swift | 14 +++++ dogether/Domain/Entity/Enum/RemindTypes.swift | 13 +++++ dogether/Domain/Entity/TodoEntity.swift | 20 +++++-- .../Protocol/ChallengeGroupsProtocol.swift | 1 - dogether/Domain/Protocol/TodosProtocol.swift | 13 +++++ ...ase.swift => ChallengeGroupsUseCase.swift} | 11 +--- dogether/Domain/UseCase/TodosUseCase.swift | 26 +++++++++ .../Presentation/Common/DogetherButton.swift | 9 ++- .../Certificate/CertificateViewModel.swift | 8 +-- .../CertificationViewController.swift | 8 +++ .../CertificationViewModel.swift | 12 +++- .../Components/CertificationPage.swift | 56 ++++++++++++++----- .../Features/Main/MainViewModel.swift | 4 +- .../Features/Ranking/RankingViewModel.swift | 4 +- .../TodoWrite/TodoWriteViewModel.swift | 4 +- dogether/Utility/Manager/DIManager.swift | 9 +++ 24 files changed, 292 insertions(+), 68 deletions(-) create mode 100644 dogether/Data/DataSources/TodosDataSource.swift create mode 100644 dogether/Data/Network/Request/RemindTodoRequest.swift create mode 100644 dogether/Data/Network/Router/TodosRouter.swift create mode 100644 dogether/Data/Repository/TodosRepository.swift create mode 100644 dogether/Data/RepositoryTest/TodosRepositoryTest.swift create mode 100644 dogether/Domain/Entity/Enum/RemindTypes.swift create mode 100644 dogether/Domain/Protocol/TodosProtocol.swift rename dogether/Domain/UseCase/{ChallengeGroupUseCase.swift => ChallengeGroupsUseCase.swift} (76%) create mode 100644 dogether/Domain/UseCase/TodosUseCase.swift diff --git a/dogether/Data/DataSources/ChallengeGroupsDataSource.swift b/dogether/Data/DataSources/ChallengeGroupsDataSource.swift index 31dbb4d3..5facb687 100644 --- a/dogether/Data/DataSources/ChallengeGroupsDataSource.swift +++ b/dogether/Data/DataSources/ChallengeGroupsDataSource.swift @@ -17,15 +17,15 @@ final class ChallengeGroupsDataSource { } func getMemberTodos(groupId: String, memberId: String) async throws -> GetMemberTodosResponse { - try await NetworkManager.shared.request(ChallengeGroupsRouter.getMemberTodos(groupId: groupId, memberId: memberId)) + try await NetworkManager.shared.request( + ChallengeGroupsRouter.getMemberTodos(groupId: groupId, memberId: memberId) + ) } func createTodos(groupId: String, createTodosRequest: CreateTodosRequest) async throws { - try await NetworkManager.shared.request(ChallengeGroupsRouter.createTodos(groupId: groupId, createTodosRequest: createTodosRequest)) - } - - func certifyTodo(todoId: String, certifyTodoRequest: CertifyTodoRequest) async throws { - try await NetworkManager.shared.request(ChallengeGroupsRouter.certifyTodo(todoId: todoId, certifyTodoRequest: certifyTodoRequest)) + try await NetworkManager.shared.request( + ChallengeGroupsRouter.createTodos(groupId: groupId, createTodosRequest: createTodosRequest) + ) } func readTodo(todoHistoryId: String) async throws { diff --git a/dogether/Data/DataSources/TodosDataSource.swift b/dogether/Data/DataSources/TodosDataSource.swift new file mode 100644 index 00000000..25e2c247 --- /dev/null +++ b/dogether/Data/DataSources/TodosDataSource.swift @@ -0,0 +1,26 @@ +// +// TodosDataSource.swift +// dogether +// +// Created by seungyooooong on 1/3/26. +// + +import Foundation + +final class TodosDataSource { + static let shared = TodosDataSource() + + private init() { } + + func certifyTodo(todoId: String, certifyTodoRequest: CertifyTodoRequest) async throws { + try await NetworkManager.shared.request( + TodosRouter.certifyTodo(todoId: todoId, certifyTodoRequest: certifyTodoRequest) + ) + } + + func remindTodo(todoId: String, remindTodoRequest: RemindTodoRequest) async throws { + try await NetworkManager.shared.request( + TodosRouter.remindTodo(todoId: todoId, remindTodoRequest: remindTodoRequest) + ) + } +} diff --git a/dogether/Data/Network/Request/RemindTodoRequest.swift b/dogether/Data/Network/Request/RemindTodoRequest.swift new file mode 100644 index 00000000..9422385c --- /dev/null +++ b/dogether/Data/Network/Request/RemindTodoRequest.swift @@ -0,0 +1,12 @@ +// +// RemindTodoRequest.swift +// dogether +// +// Created by seungyooooong on 1/3/26. +// + +import Foundation + +struct RemindTodoRequest: Encodable { + let reminderType: String +} diff --git a/dogether/Data/Network/Router/ChallengeGroupsRouter.swift b/dogether/Data/Network/Router/ChallengeGroupsRouter.swift index 82680eed..c50a3438 100644 --- a/dogether/Data/Network/Router/ChallengeGroupsRouter.swift +++ b/dogether/Data/Network/Router/ChallengeGroupsRouter.swift @@ -9,7 +9,6 @@ import Foundation enum ChallengeGroupsRouter: NetworkEndpoint { case createTodos(groupId: String, createTodosRequest: CreateTodosRequest) - case certifyTodo(todoId: String, certifyTodoRequest: CertifyTodoRequest) case getMyTodos(groupId: String, date: String) case getMyYesterdayTodos case getMemberTodos(groupId: String, memberId: String) @@ -19,8 +18,6 @@ enum ChallengeGroupsRouter: NetworkEndpoint { switch self { case .createTodos(let groupId, _): return Path.api + Path.v1 + Path.challengeGroups + "/\(groupId)/todos" - case .certifyTodo(let todoId, _): // FIXME: 추후 TodosRouter 분리 - return Path.api + Path.v1 + Path.todos + "/\(todoId)/certify" case .getMyTodos(let groupId, _): return Path.api + Path.v2 + Path.challengeGroups + "/\(groupId)/my-todos" case .getMyYesterdayTodos: @@ -36,7 +33,7 @@ enum ChallengeGroupsRouter: NetworkEndpoint { switch self { case .getMyTodos, .getMyYesterdayTodos, .getMemberTodos: return .get - case .createTodos, .certifyTodo, .readTodo: + case .createTodos, .readTodo: return .post } } @@ -64,8 +61,6 @@ enum ChallengeGroupsRouter: NetworkEndpoint { switch self { case .createTodos(_, let createTodosRequest): return createTodosRequest - case .certifyTodo(_, let certifyTodoRequest): - return certifyTodoRequest default: return nil } diff --git a/dogether/Data/Network/Router/TodosRouter.swift b/dogether/Data/Network/Router/TodosRouter.swift new file mode 100644 index 00000000..4d56288f --- /dev/null +++ b/dogether/Data/Network/Router/TodosRouter.swift @@ -0,0 +1,55 @@ +// +// TodosRouter.swift +// dogether +// +// Created by seungyooooong on 1/2/26. +// + +import Foundation + +enum TodosRouter: NetworkEndpoint { + case certifyTodo(todoId: String, certifyTodoRequest: CertifyTodoRequest) + case remindTodo(todoId: String, remindTodoRequest: RemindTodoRequest) + + var path: String { + switch self { + case .certifyTodo(let todoId, _): + return Path.api + Path.v1 + Path.todos + "/\(todoId)/certify" + case .remindTodo(let todoId, _): + return Path.api + Path.v1 + Path.todos + "\(todoId)/reminders" + } + } + + var method: NetworkMethod { + switch self { + case .certifyTodo, .remindTodo: + return .post + } + } + + var parameters: [URLQueryItem]? { + switch self { + default: + return nil + } + } + + var header: [String : String]? { + switch self { + default: + return [ + Header.Key.contentType: Header.Value.applicationJson, + Header.Key.authorization: Header.Value.bearer + (UserDefaultsManager.shared.accessToken ?? "") + ] + } + } + + var body: (any Encodable)? { + switch self { + case .certifyTodo(_, let certifyTodoRequest): + return certifyTodoRequest + case .remindTodo(_, let remindTodoRequest): + return remindTodoRequest + } + } +} diff --git a/dogether/Data/Repository/ChallengeGroupsRepository.swift b/dogether/Data/Repository/ChallengeGroupsRepository.swift index 4e15ff58..bceff0ff 100644 --- a/dogether/Data/Repository/ChallengeGroupsRepository.swift +++ b/dogether/Data/Repository/ChallengeGroupsRepository.swift @@ -25,7 +25,7 @@ final class ChallengeGroupsRepository: ChallengeGroupsProtocol { id: $0.id, content: $0.content, status: TodoStatus(rawValue: $0.status) ?? .waitCertification, - canRequestReview: $0.canRequestCertificationReview, + canRemindReview: $0.canRequestCertificationReview, certificationContent: $0.certificationContent, certificationMediaUrl: $0.certificationMediaUrl, reviewFeedback: $0.reviewFeedback @@ -46,8 +46,8 @@ final class ChallengeGroupsRepository: ChallengeGroupsProtocol { content: $0.content, status: TodoStatus(rawValue: $0.status) ?? .waitCertification, thumbnailStatus: $0.isRead ? .done : .yet, - canRequestCertification: $0.canRequestCertification, - canRequestReview: $0.canRequestCertificationReview, + canRemindCertification: $0.canRequestCertification, + canRemindReview: $0.canRequestCertificationReview, certificationContent: $0.certificationContent, certificationMediaUrl: $0.certificationMediaUrl, reviewFeedback: $0.reviewFeedback @@ -59,8 +59,4 @@ final class ChallengeGroupsRepository: ChallengeGroupsProtocol { func readTodo(todoHistoryId: String) async throws { try await challengeGroupsDataSource.readTodo(todoHistoryId: todoHistoryId) } - - func certifyTodo(todoId: String, certifyTodoRequest: CertifyTodoRequest) async throws { - try await challengeGroupsDataSource.certifyTodo(todoId: todoId, certifyTodoRequest: certifyTodoRequest) - } } diff --git a/dogether/Data/Repository/TodosRepository.swift b/dogether/Data/Repository/TodosRepository.swift new file mode 100644 index 00000000..d08b53c5 --- /dev/null +++ b/dogether/Data/Repository/TodosRepository.swift @@ -0,0 +1,24 @@ +// +// TodosRepository.swift +// dogether +// +// Created by seungyooooong on 1/3/26. +// + +import Foundation + +final class TodosRepository: TodosProtocol { + private let todosDataSource: TodosDataSource + + init(todosDataSource: TodosDataSource = .shared) { + self.todosDataSource = todosDataSource + } + + func certifyTodo(todoId: String, certifyTodoRequest: CertifyTodoRequest) async throws { + try await todosDataSource.certifyTodo(todoId: todoId, certifyTodoRequest: certifyTodoRequest) + } + + func remindTodo(todoId: String, remindTodoRequest: RemindTodoRequest) async throws { + try await todosDataSource.remindTodo(todoId: todoId, remindTodoRequest: remindTodoRequest) + } +} diff --git a/dogether/Data/RepositoryTest/ChallengeGroupsRepositoryTest.swift b/dogether/Data/RepositoryTest/ChallengeGroupsRepositoryTest.swift index e9460d56..4e6f6f97 100644 --- a/dogether/Data/RepositoryTest/ChallengeGroupsRepositoryTest.swift +++ b/dogether/Data/RepositoryTest/ChallengeGroupsRepositoryTest.swift @@ -51,6 +51,4 @@ final class ChallengeGroupsRepositoryTest: ChallengeGroupsProtocol { } func readTodo(todoHistoryId: String) async throws { } - - func certifyTodo(todoId: String, certifyTodoRequest: CertifyTodoRequest) async throws { } } diff --git a/dogether/Data/RepositoryTest/TodosRepositoryTest.swift b/dogether/Data/RepositoryTest/TodosRepositoryTest.swift new file mode 100644 index 00000000..4e321ae3 --- /dev/null +++ b/dogether/Data/RepositoryTest/TodosRepositoryTest.swift @@ -0,0 +1,14 @@ +// +// TodosRepositoryTest.swift +// dogether +// +// Created by seungyooooong on 1/3/26. +// + +import Foundation + +final class TodosRepositoryTest: TodosProtocol { + func certifyTodo(todoId: String, certifyTodoRequest: CertifyTodoRequest) async throws { } + + func remindTodo(todoId: String, remindTodoRequest: RemindTodoRequest) async throws { } +} diff --git a/dogether/Domain/Entity/Enum/RemindTypes.swift b/dogether/Domain/Entity/Enum/RemindTypes.swift new file mode 100644 index 00000000..b77fd5ff --- /dev/null +++ b/dogether/Domain/Entity/Enum/RemindTypes.swift @@ -0,0 +1,13 @@ +// +// RemindTypes.swift +// dogether +// +// Created by seungyooooong on 1/2/26. +// + +import Foundation + +enum RemindTypes: String { + case certification = "TODO_CERTIFICATION" + case review = "TODO_CERTIFICATION_REVIEW" +} diff --git a/dogether/Domain/Entity/TodoEntity.swift b/dogether/Domain/Entity/TodoEntity.swift index 14a5af3a..ccf1cd23 100644 --- a/dogether/Domain/Entity/TodoEntity.swift +++ b/dogether/Domain/Entity/TodoEntity.swift @@ -13,8 +13,8 @@ struct TodoEntity: BaseEntity { let content: String var status: TodoStatus var thumbnailStatus: ThumbnailStatus - var canRequestCertification: Bool - var canRequestReview: Bool + var canRemindCertification: Bool + var canRemindReview: Bool var certificationContent: String? var certificationMediaUrl: String? var reviewFeedback: String? @@ -26,8 +26,8 @@ struct TodoEntity: BaseEntity { content: String, status: TodoStatus, thumbnailStatus: ThumbnailStatus = .yet, - canRequestCertification: Bool = false, - canRequestReview: Bool = false, + canRemindCertification: Bool = false, + canRemindReview: Bool = false, certificationContent: String? = nil, certificationMediaUrl: String? = nil, reviewFeedback: String? = nil, @@ -38,11 +38,19 @@ struct TodoEntity: BaseEntity { self.content = content self.status = status self.thumbnailStatus = thumbnailStatus - self.canRequestCertification = canRequestCertification - self.canRequestReview = canRequestReview + self.canRemindCertification = canRemindCertification + self.canRemindReview = canRemindReview self.certificationContent = certificationContent self.certificationMediaUrl = certificationMediaUrl self.reviewFeedback = reviewFeedback self.createdAt = createdAt } } + +extension TodoEntity { + func with(createdAt: String) -> Self { + var todo = self + todo.createdAt = createdAt + return todo + } +} diff --git a/dogether/Domain/Protocol/ChallengeGroupsProtocol.swift b/dogether/Domain/Protocol/ChallengeGroupsProtocol.swift index e4ca1585..916d466f 100644 --- a/dogether/Domain/Protocol/ChallengeGroupsProtocol.swift +++ b/dogether/Domain/Protocol/ChallengeGroupsProtocol.swift @@ -12,5 +12,4 @@ protocol ChallengeGroupsProtocol { func getMyTodos(groupId: String, date: String) async throws -> [TodoEntity] func getMemberTodos(groupId: Int, memberId: Int) async throws -> (index: Int, todos: [TodoEntity]) func readTodo(todoHistoryId: String) async throws - func certifyTodo(todoId: String, certifyTodoRequest: CertifyTodoRequest) async throws } diff --git a/dogether/Domain/Protocol/TodosProtocol.swift b/dogether/Domain/Protocol/TodosProtocol.swift new file mode 100644 index 00000000..f2c7767a --- /dev/null +++ b/dogether/Domain/Protocol/TodosProtocol.swift @@ -0,0 +1,13 @@ +// +// TodosProtocol.swift +// dogether +// +// Created by seungyooooong on 1/3/26. +// + +import Foundation + +protocol TodosProtocol { + func certifyTodo(todoId: String, certifyTodoRequest: CertifyTodoRequest) async throws + func remindTodo(todoId: String, remindTodoRequest: RemindTodoRequest) async throws +} diff --git a/dogether/Domain/UseCase/ChallengeGroupUseCase.swift b/dogether/Domain/UseCase/ChallengeGroupsUseCase.swift similarity index 76% rename from dogether/Domain/UseCase/ChallengeGroupUseCase.swift rename to dogether/Domain/UseCase/ChallengeGroupsUseCase.swift index a5e732c8..71e3b385 100644 --- a/dogether/Domain/UseCase/ChallengeGroupUseCase.swift +++ b/dogether/Domain/UseCase/ChallengeGroupsUseCase.swift @@ -1,5 +1,5 @@ // -// ChallengeGroupUseCase.swift +// ChallengeGroupsUseCase.swift // dogether // // Created by seungyooooong on 3/30/25. @@ -7,7 +7,7 @@ import Foundation -final class ChallengeGroupUseCase { +final class ChallengeGroupsUseCase { private let repository: ChallengeGroupsProtocol init(repository: ChallengeGroupsProtocol) { @@ -20,7 +20,7 @@ final class ChallengeGroupUseCase { } func getMyTodos(groupId: Int, date: String) async throws -> [TodoEntity] { - try await repository.getMyTodos(groupId: String(groupId), date: date) + try await repository.getMyTodos(groupId: String(groupId), date: date).map { $0.with(createdAt: date) } } func getMemberTodos(groupId: Int, memberId: Int) async throws -> (index: Int, todos: [TodoEntity]) { @@ -33,9 +33,4 @@ final class ChallengeGroupUseCase { if todo.thumbnailStatus == .done { return } try await repository.readTodo(todoHistoryId: String(todoHistoryId)) } - - func certifyTodo(todoId: Int, content: String, mediaUrl: String) async throws { - let certifyTodoRequest = CertifyTodoRequest(content: content, mediaUrl: mediaUrl) - try await repository.certifyTodo(todoId: String(todoId), certifyTodoRequest: certifyTodoRequest) - } } diff --git a/dogether/Domain/UseCase/TodosUseCase.swift b/dogether/Domain/UseCase/TodosUseCase.swift new file mode 100644 index 00000000..52411514 --- /dev/null +++ b/dogether/Domain/UseCase/TodosUseCase.swift @@ -0,0 +1,26 @@ +// +// TodosUseCase.swift +// dogether +// +// Created by seungyooooong on 1/3/26. +// + +import Foundation + +final class TodosUseCase { + private let repository: TodosProtocol + + init(repository: TodosProtocol) { + self.repository = repository + } + + func certifyTodo(todoId: Int, content: String, mediaUrl: String) async throws { + let certifyTodoRequest = CertifyTodoRequest(content: content, mediaUrl: mediaUrl) + try await repository.certifyTodo(todoId: String(todoId), certifyTodoRequest: certifyTodoRequest) + } + + func remindTodo(remindType: RemindTypes, todoId: Int) async throws { + let remindTodoRequest = RemindTodoRequest(reminderType: remindType.rawValue) + try await repository.remindTodo(todoId: String(todoId), remindTodoRequest: remindTodoRequest) + } +} diff --git a/dogether/Presentation/Common/DogetherButton.swift b/dogether/Presentation/Common/DogetherButton.swift index 579a32a7..594622d2 100644 --- a/dogether/Presentation/Common/DogetherButton.swift +++ b/dogether/Presentation/Common/DogetherButton.swift @@ -23,6 +23,9 @@ final class DogetherButton: BaseButton { setTitle(title, for: .normal) titleLabel?.font = Fonts.body1B layer.cornerRadius = 8 + + setTitleColor(ButtonStatus.enabled.textColor, for: .normal) + backgroundColor = ButtonStatus.enabled.backgroundColor } override func configureAction() { } @@ -45,8 +48,6 @@ final class DogetherButton: BaseButton { setTitleColor(datas.status.textColor, for: .normal) backgroundColor = datas.status.backgroundColor isEnabled = datas.status == .enabled - - isHidden = datas.isHidden } } } @@ -54,10 +55,8 @@ final class DogetherButton: BaseButton { // MARK: - ViewDatas struct DogetherButtonViewDatas: BaseEntity { var status: ButtonStatus - var isHidden: Bool - init(status: ButtonStatus = .enabled, isHidden: Bool = false) { + init(status: ButtonStatus = .enabled) { self.status = status - self.isHidden = isHidden } } diff --git a/dogether/Presentation/Features/Certificate/CertificateViewModel.swift b/dogether/Presentation/Features/Certificate/CertificateViewModel.swift index 65d2ac4e..217a373b 100644 --- a/dogether/Presentation/Features/Certificate/CertificateViewModel.swift +++ b/dogether/Presentation/Features/Certificate/CertificateViewModel.swift @@ -10,7 +10,7 @@ import UIKit import RxRelay final class CertificateViewModel { - private let challengeGroupUseCase: ChallengeGroupUseCase + private let todosUseCase: TodosUseCase private(set) var certificateViewDatas = BehaviorRelay(value: CertificateViewDatas()) private(set) var certificateTextViewDatas = BehaviorRelay(value: DogetherTextViewDatas()) @@ -19,8 +19,8 @@ final class CertificateViewModel { ) init() { - let repository = DIManager.shared.getChallengeGroupsRepository() - self.challengeGroupUseCase = ChallengeGroupUseCase(repository: repository) + let todosRepository = DIManager.shared.getTodosRepository() + self.todosUseCase = TodosUseCase(repository: todosRepository) } } @@ -58,6 +58,6 @@ extension CertificateViewModel { let todo = certificateViewDatas.value.todo guard let content = todo.certificationContent, let mediaUrl = todo.certificationMediaUrl else { return } - try await challengeGroupUseCase.certifyTodo(todoId: todo.id, content: content, mediaUrl: mediaUrl) + try await todosUseCase.certifyTodo(todoId: todo.id, content: content, mediaUrl: mediaUrl) } } diff --git a/dogether/Presentation/Features/Certification/CertificationViewController.swift b/dogether/Presentation/Features/Certification/CertificationViewController.swift index 64694b94..8fa014b4 100644 --- a/dogether/Presentation/Features/Certification/CertificationViewController.swift +++ b/dogether/Presentation/Features/Certification/CertificationViewController.swift @@ -45,6 +45,7 @@ protocol CertificationDelegate { func certificationTapAction(_ scrollView: UIScrollView, _ stackView: UIStackView, _ gesture: UITapGestureRecognizer) func certificationListScrollEndAction(index: Int) func goCertificateViewAction(todo: TodoEntity) + func remindTodoAction(remindType: RemindTypes, todoId: Int) } extension CertificationViewController: CertificationDelegate { @@ -98,4 +99,11 @@ extension CertificationViewController: CertificationDelegate { let certificateViewDatas = CertificateViewDatas(todo: todo) coordinator?.pushViewController(certificateImageViewController, datas: certificateViewDatas) } + + func remindTodoAction(remindType: RemindTypes, todoId: Int) { + Task { [weak self] in + guard let self else { return } + try await viewModel.remindTodo(remindType: remindType, todoId: todoId) + } + } } diff --git a/dogether/Presentation/Features/Certification/CertificationViewModel.swift b/dogether/Presentation/Features/Certification/CertificationViewModel.swift index 47f4d188..9478fa95 100644 --- a/dogether/Presentation/Features/Certification/CertificationViewModel.swift +++ b/dogether/Presentation/Features/Certification/CertificationViewModel.swift @@ -8,13 +8,17 @@ import RxRelay final class CertificationViewModel { - private let challengeGroupsUseCase: ChallengeGroupUseCase + private let challengeGroupsUseCase: ChallengeGroupsUseCase + private let todosUseCase: TodosUseCase private(set) var certificationViewDatas = BehaviorRelay(value: CertificationViewDatas()) init() { let challengeGroupsRepository = DIManager.shared.getChallengeGroupsRepository() - self.challengeGroupsUseCase = ChallengeGroupUseCase(repository: challengeGroupsRepository) + let todosRepository = DIManager.shared.getTodosRepository() + + self.challengeGroupsUseCase = ChallengeGroupsUseCase(repository: challengeGroupsRepository) + self.todosUseCase = TodosUseCase(repository: todosRepository) } } @@ -36,4 +40,8 @@ extension CertificationViewModel { todo: certificationViewDatas.value.todos[index ?? certificationViewDatas.value.index] ) } + + func remindTodo(remindType: RemindTypes, todoId: Int) async throws { + try await todosUseCase.remindTodo(remindType: remindType, todoId: todoId) + } } diff --git a/dogether/Presentation/Features/Certification/Components/CertificationPage.swift b/dogether/Presentation/Features/Certification/Components/CertificationPage.swift index 41af8f01..ea97bcf1 100644 --- a/dogether/Presentation/Features/Certification/Components/CertificationPage.swift +++ b/dogether/Presentation/Features/Certification/Components/CertificationPage.swift @@ -12,12 +12,24 @@ final class CertificationPage: BasePage { didSet { thumbnailListView.delegate = delegate certificationListView.delegate = delegate -// certificateButton.addAction( -// UIAction { [weak self] _ in -// guard let self, let currentTodo else { return } -// delegate?.goCertificateViewAction(todo: currentTodo) -// }, for: .touchUpInside -// ) + certificateButton.addAction( + UIAction { [weak self] _ in + guard let self, let currentTodo else { return } + delegate?.goCertificateViewAction(todo: currentTodo) + }, for: .touchUpInside + ) + remindCertificationButton.addAction( + UIAction { [weak self] _ in + guard let self, let currentTodo else { return } + delegate?.remindTodoAction(remindType: .certification, todoId: currentTodo.id) + }, for: .touchUpInside + ) + remindReviewButton.addAction( + UIAction { [weak self] _ in + guard let self, let currentTodo else { return } + delegate?.remindTodoAction(remindType: .review, todoId: currentTodo.id) + }, for: .touchUpInside + ) } } @@ -29,7 +41,9 @@ final class CertificationPage: BasePage { private let statusView = TodoStatusButton() private let contentLabel = UILabel() private let reviewFeedbackView = ReviewFeedbackView() -// private let certificateButton = DogetherButton("인증하기") + private let certificateButton = DogetherButton("인증하기") + private let remindCertificationButton = DogetherButton("인증 재촉하기") + private let remindReviewButton = DogetherButton("검사 재촉하기") private var currentTodo: TodoEntity? @@ -54,7 +68,9 @@ final class CertificationPage: BasePage { } override func configureHierarchy() { - [navigationHeader, thumbnailListView, certificationScrollView/*, certificateButton*/].forEach { addSubview($0) } + [ navigationHeader, thumbnailListView, certificationScrollView, + certificateButton, remindCertificationButton, remindReviewButton + ].forEach { addSubview($0) } certificationScrollView.addSubview(certificationStackView) } @@ -98,9 +114,17 @@ final class CertificationPage: BasePage { $0.horizontalEdges.equalToSuperview().inset(16) } -// certificateButton.snp.makeConstraints { -// $0.bottom.horizontalEdges.equalToSuperview().inset(16) -// } + certificateButton.snp.makeConstraints { + $0.bottom.horizontalEdges.equalToSuperview().inset(16) + } + + remindCertificationButton.snp.makeConstraints { + $0.bottom.horizontalEdges.equalToSuperview().inset(16) + } + + remindReviewButton.snp.makeConstraints { + $0.bottom.horizontalEdges.equalToSuperview().inset(16) + } } // MARK: - updateView @@ -122,10 +146,12 @@ final class CertificationPage: BasePage { reviewFeedbackView.updateView(datas.todos[datas.index].reviewFeedback ?? "") - // FIXME: 추후 수정 -// var dogetherButtonViewDatas = certificateButton.currentViewDatas ?? DogetherButtonViewDatas() -// dogetherButtonViewDatas.isHidden = datas.rankingEntity != nil || datas.todos[datas.index].status != .waitCertification -// certificateButton.updateView(dogetherButtonViewDatas) + let date = DateFormatterManager.formattedDate().split(separator: ".").joined(separator: "-") + let isWaitCertification = datas.todos[datas.index].status == .waitCertification + let isToday = datas.todos[datas.index].createdAt == date || datas.rankingEntity != nil + certificateButton.isHidden = !(isWaitCertification && isToday) + remindCertificationButton.isHidden = !datas.todos[datas.index].canRemindCertification + remindReviewButton.isHidden = !datas.todos[datas.index].canRemindReview } } } diff --git a/dogether/Presentation/Features/Main/MainViewModel.swift b/dogether/Presentation/Features/Main/MainViewModel.swift index 41b7af5b..a0e6909c 100644 --- a/dogether/Presentation/Features/Main/MainViewModel.swift +++ b/dogether/Presentation/Features/Main/MainViewModel.swift @@ -11,7 +11,7 @@ import RxRelay final class MainViewModel { private let groupUseCase: GroupUseCase - private let challengeGroupsUseCase: ChallengeGroupUseCase + private let challengeGroupsUseCase: ChallengeGroupsUseCase private let todoCertificationsUseCase: TodoCertificationsUseCase private(set) var bottomSheetViewDatas = BehaviorRelay(value: BottomSheetViewDatas()) @@ -30,7 +30,7 @@ final class MainViewModel { let todoCertificationsRepository = DIManager.shared.getTodoCertificationsRepository() self.groupUseCase = GroupUseCase(repository: groupRepository) - self.challengeGroupsUseCase = ChallengeGroupUseCase(repository: challengeGroupsRepository) + self.challengeGroupsUseCase = ChallengeGroupsUseCase(repository: challengeGroupsRepository) self.todoCertificationsUseCase = TodoCertificationsUseCase(repository: todoCertificationsRepository) } } diff --git a/dogether/Presentation/Features/Ranking/RankingViewModel.swift b/dogether/Presentation/Features/Ranking/RankingViewModel.swift index a6a3aee2..906049fe 100644 --- a/dogether/Presentation/Features/Ranking/RankingViewModel.swift +++ b/dogether/Presentation/Features/Ranking/RankingViewModel.swift @@ -11,7 +11,7 @@ import RxRelay final class RankingViewModel { private let groupUseCase: GroupUseCase - private let challengeGroupsUseCase: ChallengeGroupUseCase + private let challengeGroupsUseCase: ChallengeGroupsUseCase private(set) var rankingViewDatas = BehaviorRelay(value: RankingViewDatas()) @@ -20,7 +20,7 @@ final class RankingViewModel { let challengeGroupsRepository = DIManager.shared.getChallengeGroupsRepository() self.groupUseCase = GroupUseCase(repository: groupRepository) - self.challengeGroupsUseCase = ChallengeGroupUseCase(repository: challengeGroupsRepository) + self.challengeGroupsUseCase = ChallengeGroupsUseCase(repository: challengeGroupsRepository) } } diff --git a/dogether/Presentation/Features/TodoWrite/TodoWriteViewModel.swift b/dogether/Presentation/Features/TodoWrite/TodoWriteViewModel.swift index 4f085dda..e0322530 100644 --- a/dogether/Presentation/Features/TodoWrite/TodoWriteViewModel.swift +++ b/dogether/Presentation/Features/TodoWrite/TodoWriteViewModel.swift @@ -10,13 +10,13 @@ import Foundation import RxRelay final class TodoWriteViewModel { - private let challengeGroupsUseCase: ChallengeGroupUseCase + private let challengeGroupsUseCase: ChallengeGroupsUseCase private(set) var todoWriteViewDatas = BehaviorRelay(value: TodoWriteViewDatas()) init() { let challengeGroupsRepository = DIManager.shared.getChallengeGroupsRepository() - self.challengeGroupsUseCase = ChallengeGroupUseCase(repository: challengeGroupsRepository) + self.challengeGroupsUseCase = ChallengeGroupsUseCase(repository: challengeGroupsRepository) } } diff --git a/dogether/Utility/Manager/DIManager.swift b/dogether/Utility/Manager/DIManager.swift index a26f96e4..5223f0a5 100644 --- a/dogether/Utility/Manager/DIManager.swift +++ b/dogether/Utility/Manager/DIManager.swift @@ -66,6 +66,15 @@ extension DIManager { } } + func getTodosRepository(buildMode: BuildModes? = nil) -> TodosProtocol { + switch buildMode ?? defaultBuildMode { + case .debug: + return TodosRepositoryTest() + case .live: + return TodosRepository() + } + } + func getUserRepository(buildMode: BuildModes? = nil) -> UserProtocol { switch buildMode ?? defaultBuildMode { case .debug: From 094aab9f222f4c847dd72dfc5e3c378efb734ef4 Mon Sep 17 00:00:00 2001 From: seungyooooong Date: Fri, 9 Jan 2026 17:19:05 +0900 Subject: [PATCH 03/18] =?UTF-8?q?fix=20#161:=20path=20=EC=98=A4=ED=83=88?= =?UTF-8?q?=EC=9E=90=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dogether/Data/Network/Router/TodosRouter.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dogether/Data/Network/Router/TodosRouter.swift b/dogether/Data/Network/Router/TodosRouter.swift index 4d56288f..f027bbcf 100644 --- a/dogether/Data/Network/Router/TodosRouter.swift +++ b/dogether/Data/Network/Router/TodosRouter.swift @@ -16,7 +16,7 @@ enum TodosRouter: NetworkEndpoint { case .certifyTodo(let todoId, _): return Path.api + Path.v1 + Path.todos + "/\(todoId)/certify" case .remindTodo(let todoId, _): - return Path.api + Path.v1 + Path.todos + "\(todoId)/reminders" + return Path.api + Path.v1 + Path.todos + "/\(todoId)/reminders" } } From b302491dbcaeb49f62a33c10ca5ea31cf42bd7b4 Mon Sep 17 00:00:00 2001 From: seungyooooong Date: Sat, 10 Jan 2026 14:27:19 +0900 Subject: [PATCH 04/18] =?UTF-8?q?feat=20#161:=20=ED=9A=8C=EC=9D=98=20?= =?UTF-8?q?=EB=82=B4=EC=9A=A9=20=EA=B2=B0=EA=B3=BC=20=EC=A3=BC=EC=84=9D=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Features/Certification/CertificationViewController.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/dogether/Presentation/Features/Certification/CertificationViewController.swift b/dogether/Presentation/Features/Certification/CertificationViewController.swift index 8fa014b4..2fd2990a 100644 --- a/dogether/Presentation/Features/Certification/CertificationViewController.swift +++ b/dogether/Presentation/Features/Certification/CertificationViewController.swift @@ -104,6 +104,7 @@ extension CertificationViewController: CertificationDelegate { Task { [weak self] in guard let self else { return } try await viewModel.remindTodo(remindType: remindType, todoId: todoId) + // TODO: 재촉 메시지가 전송되었어요 토스트 메시지 노출 및 버튼 비활성화 } } } From 655be57146b591386a2db3e4e08d7b1d589fe13d Mon Sep 17 00:00:00 2001 From: seungyooooong Date: Mon, 12 Jan 2026 16:47:14 +0900 Subject: [PATCH 05/18] =?UTF-8?q?feat=20#165:=20=EC=8A=A4=EC=BC=88?= =?UTF-8?q?=EB=A0=88=ED=86=A4=20=EC=83=81=ED=83=9C=20=EB=8C=80=EC=9D=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CertificationViewController.swift | 7 ++++--- .../Certification/CertificationViewModel.swift | 6 +++--- .../Components/CertificationPage.swift | 18 +++++++++--------- 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/dogether/Presentation/Features/Certification/CertificationViewController.swift b/dogether/Presentation/Features/Certification/CertificationViewController.swift index 2fd2990a..1dfcdbaa 100644 --- a/dogether/Presentation/Features/Certification/CertificationViewController.swift +++ b/dogether/Presentation/Features/Certification/CertificationViewController.swift @@ -22,9 +22,9 @@ final class CertificationViewController: BaseViewController { } override func setViewDatas() { - if let datas = datas as? CertificationViewDatas { - viewModel.certificationViewDatas.accept(datas) - } +// if let datas = datas as? CertificationViewDatas { +// viewModel.certificationViewDatas.accept(datas) +// } bind(viewModel.certificationViewDatas) } @@ -34,6 +34,7 @@ extension CertificationViewController { private func onAppear() { Task { [weak self] in guard let self else { return } + // TODO: 상황에 따른 API 호출 및 데이터 바인딩 로직 추가 try await viewModel.readTodo() } } diff --git a/dogether/Presentation/Features/Certification/CertificationViewModel.swift b/dogether/Presentation/Features/Certification/CertificationViewModel.swift index 9478fa95..48dd09c7 100644 --- a/dogether/Presentation/Features/Certification/CertificationViewModel.swift +++ b/dogether/Presentation/Features/Certification/CertificationViewModel.swift @@ -36,9 +36,9 @@ extension CertificationViewModel { func readTodo(index: Int? = nil) async throws { if certificationViewDatas.value.rankingEntity == nil { return } - try await challengeGroupsUseCase.readTodo( - todo: certificationViewDatas.value.todos[index ?? certificationViewDatas.value.index] - ) + let index = index ?? certificationViewDatas.value.index + guard let todo = certificationViewDatas.value.todos[safe: index] else { return } + try await challengeGroupsUseCase.readTodo(todo: todo) } func remindTodo(remindType: RemindTypes, todoId: Int) async throws { diff --git a/dogether/Presentation/Features/Certification/Components/CertificationPage.swift b/dogether/Presentation/Features/Certification/Components/CertificationPage.swift index ea97bcf1..1e354eb4 100644 --- a/dogether/Presentation/Features/Certification/Components/CertificationPage.swift +++ b/dogether/Presentation/Features/Certification/Components/CertificationPage.swift @@ -134,24 +134,24 @@ final class CertificationPage: BasePage { thumbnailListView.updateView(datas) certificationListView.updateView(datas) - if currentTodo != datas.todos[datas.index] { - currentTodo = datas.todos[datas.index] + if let todo = datas.todos[safe: datas.index], currentTodo != todo { + currentTodo = todo - statusView.updateView(datas.todos[datas.index].status) + statusView.updateView(todo.status) contentLabel.attributedText = NSAttributedString( - string: datas.todos[datas.index].content, + string: todo.content, attributes: Fonts.getAttributes(for: Fonts.head1B, textAlignment: .center) ) - reviewFeedbackView.updateView(datas.todos[datas.index].reviewFeedback ?? "") + reviewFeedbackView.updateView(todo.reviewFeedback ?? "") let date = DateFormatterManager.formattedDate().split(separator: ".").joined(separator: "-") - let isWaitCertification = datas.todos[datas.index].status == .waitCertification - let isToday = datas.todos[datas.index].createdAt == date || datas.rankingEntity != nil + let isWaitCertification = todo.status == .waitCertification + let isToday = todo.createdAt == date || datas.rankingEntity != nil certificateButton.isHidden = !(isWaitCertification && isToday) - remindCertificationButton.isHidden = !datas.todos[datas.index].canRemindCertification - remindReviewButton.isHidden = !datas.todos[datas.index].canRemindReview + remindCertificationButton.isHidden = !todo.canRemindCertification + remindReviewButton.isHidden = !todo.canRemindReview } } } From 1549c10b5b62770c8816b82ef6f9f00bbfaf17b3 Mon Sep 17 00:00:00 2001 From: seungyooooong Date: Mon, 19 Jan 2026 14:55:10 +0900 Subject: [PATCH 06/18] =?UTF-8?q?fix=20#165:=20=EC=82=AC=EC=9A=A9=ED=95=98?= =?UTF-8?q?=EC=A7=80=20=EC=95=8A=EB=8A=94=20=EC=BD=94=EB=93=9C=20=EB=B0=8F?= =?UTF-8?q?=20=ED=8F=B4=EB=8D=94=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Entity/Enum/SortOptions.swift} | 4 ++-- .../Entity/ViewDatas/CertificationViewDatas.swift | 3 --- .../Components/CertificationListViewStatus.swift | 13 ------------- .../Features/Ranking/RankingViewController.swift | 1 - 4 files changed, 2 insertions(+), 19 deletions(-) rename dogether/{Presentation/Features/CertificationList/Components/CertificationSortOption.swift => Domain/Entity/Enum/SortOptions.swift} (87%) delete mode 100644 dogether/Presentation/Features/CertificationList/Components/CertificationListViewStatus.swift diff --git a/dogether/Presentation/Features/CertificationList/Components/CertificationSortOption.swift b/dogether/Domain/Entity/Enum/SortOptions.swift similarity index 87% rename from dogether/Presentation/Features/CertificationList/Components/CertificationSortOption.swift rename to dogether/Domain/Entity/Enum/SortOptions.swift index cf284543..e19371d9 100644 --- a/dogether/Presentation/Features/CertificationList/Components/CertificationSortOption.swift +++ b/dogether/Domain/Entity/Enum/SortOptions.swift @@ -1,8 +1,8 @@ // -// CertificationSortOption.swift +// SortOptions.swift // dogether // -// Created by yujaehong on 5/19/25. +// Created by seungyooooong on 1/19/26. // import Foundation diff --git a/dogether/Domain/Entity/ViewDatas/CertificationViewDatas.swift b/dogether/Domain/Entity/ViewDatas/CertificationViewDatas.swift index 99b801ba..69b6b93f 100644 --- a/dogether/Domain/Entity/ViewDatas/CertificationViewDatas.swift +++ b/dogether/Domain/Entity/ViewDatas/CertificationViewDatas.swift @@ -11,20 +11,17 @@ struct CertificationViewDatas: BaseEntity { var title: String var todos: [TodoEntity] var index: Int - var groupId: Int? var rankingEntity: RankingEntity? init( title: String = "", todos: [TodoEntity] = [], index: Int = 0, - groupId: Int? = nil, rankingEntity: RankingEntity? = nil ) { self.title = title self.todos = todos self.index = index - self.groupId = groupId self.rankingEntity = rankingEntity } } diff --git a/dogether/Presentation/Features/CertificationList/Components/CertificationListViewStatus.swift b/dogether/Presentation/Features/CertificationList/Components/CertificationListViewStatus.swift deleted file mode 100644 index e24fa49e..00000000 --- a/dogether/Presentation/Features/CertificationList/Components/CertificationListViewStatus.swift +++ /dev/null @@ -1,13 +0,0 @@ -// -// CertificationListViewStatus.swift -// dogether -// -// Created by yujaehong on 4/28/25. -// - -import UIKit - -enum CertificationListViewStatus { - case empty - case hasData -} diff --git a/dogether/Presentation/Features/Ranking/RankingViewController.swift b/dogether/Presentation/Features/Ranking/RankingViewController.swift index adaa414b..8bc15e4f 100644 --- a/dogether/Presentation/Features/Ranking/RankingViewController.swift +++ b/dogether/Presentation/Features/Ranking/RankingViewController.swift @@ -60,7 +60,6 @@ extension RankingViewController: RankingDelegate { title: "\(rankingEntity.name)님의 인증 정보", todos: todos, index: index, - groupId: viewModel.rankingViewDatas.value.groupId, rankingEntity: rankingEntity ) coordinator?.pushViewController(certificationViewController, datas: certificationViewDatas) From 52f578afd7ba332cd3cd54cd3a1b658a59e83e2c Mon Sep 17 00:00:00 2001 From: seungyooooong Date: Tue, 20 Jan 2026 18:12:45 +0900 Subject: [PATCH 07/18] =?UTF-8?q?feat=20#165:=20loadCertificationView=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ViewDatas/PreCertificationViewDatas.swift | 36 +++++++++++++++++ .../CertificationViewController.swift | 18 ++++++--- .../CertificationViewModel.swift | 40 +++++++++++++++++++ .../CertificationListViewController.swift | 13 ++++-- .../CertificationListContentView.swift | 2 +- .../Main/Components/TodoListItemButton.swift | 6 +-- .../Main/Components/TodoListView.swift | 4 +- .../Features/Main/MainViewController.swift | 19 +++++---- .../Ranking/RankingViewController.swift | 21 ++++------ .../Features/Ranking/RankingViewModel.swift | 8 ---- 10 files changed, 120 insertions(+), 47 deletions(-) create mode 100644 dogether/Domain/Entity/ViewDatas/PreCertificationViewDatas.swift diff --git a/dogether/Domain/Entity/ViewDatas/PreCertificationViewDatas.swift b/dogether/Domain/Entity/ViewDatas/PreCertificationViewDatas.swift new file mode 100644 index 00000000..876ba885 --- /dev/null +++ b/dogether/Domain/Entity/ViewDatas/PreCertificationViewDatas.swift @@ -0,0 +1,36 @@ +// +// PreCertificationViewDatas.swift +// dogether +// +// Created by seungyooooong on 1/19/26. +// + +import Foundation + +struct PreCertificationViewDatas: BaseEntity { + let title: String + let date: String? + let groupId: Int? + let memberId: Int? + let todoId: Int? + let sortOption: SortOptions? + let filter: FilterTypes? + + init( + title: String = "", + date: String? = nil, + groupId: Int? = nil, + memberId: Int? = nil, + todoId: Int? = nil, + sortOption: SortOptions? = nil, + filter: FilterTypes? = nil + ) { + self.title = title + self.date = date + self.groupId = groupId + self.memberId = memberId + self.todoId = todoId + self.sortOption = sortOption + self.filter = filter + } +} diff --git a/dogether/Presentation/Features/Certification/CertificationViewController.swift b/dogether/Presentation/Features/Certification/CertificationViewController.swift index 1dfcdbaa..0306f764 100644 --- a/dogether/Presentation/Features/Certification/CertificationViewController.swift +++ b/dogether/Presentation/Features/Certification/CertificationViewController.swift @@ -17,24 +17,30 @@ final class CertificationViewController: BaseViewController { pages = [certificationPage] super.viewDidLoad() + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) - onAppear() + loadCertificationView() + + coordinator?.updateViewController = loadCertificationView } override func setViewDatas() { -// if let datas = datas as? CertificationViewDatas { -// viewModel.certificationViewDatas.accept(datas) -// } + if let datas = datas as? PreCertificationViewDatas { + viewModel.preCertificationViewDatas.accept(datas) + } bind(viewModel.certificationViewDatas) } } extension CertificationViewController { - private func onAppear() { + private func loadCertificationView() { Task { [weak self] in guard let self else { return } - // TODO: 상황에 따른 API 호출 및 데이터 바인딩 로직 추가 + try await viewModel.loadCertificationView() try await viewModel.readTodo() } } diff --git a/dogether/Presentation/Features/Certification/CertificationViewModel.swift b/dogether/Presentation/Features/Certification/CertificationViewModel.swift index 48dd09c7..dca6ae16 100644 --- a/dogether/Presentation/Features/Certification/CertificationViewModel.swift +++ b/dogether/Presentation/Features/Certification/CertificationViewModel.swift @@ -11,6 +11,9 @@ final class CertificationViewModel { private let challengeGroupsUseCase: ChallengeGroupsUseCase private let todosUseCase: TodosUseCase + private(set) var preCertificationViewDatas = BehaviorRelay( + value: PreCertificationViewDatas() + ) private(set) var certificationViewDatas = BehaviorRelay(value: CertificationViewDatas()) init() { @@ -23,6 +26,43 @@ final class CertificationViewModel { } extension CertificationViewModel { + func loadCertificationView() async throws { + let datas = preCertificationViewDatas.value + // MARK: from Main + if let date = datas.date, let groupId = datas.groupId, let todoId = datas.todoId, let filter = datas.filter { + let todos = try await challengeGroupsUseCase.getMyTodos(groupId: groupId, date: date).filter { + filter == .all || filter == FilterTypes(status: $0.status.rawValue) + } + + certificationViewDatas.update { + $0.title = datas.title + $0.index = todos.firstIndex { $0.id == todoId } ?? 0 + $0.todos = todos + } + } + + // MARK: from Ranking + if let groupId = datas.groupId, let memberId = datas.memberId { + let (index, todos) = try await getMemberTodos(groupId: groupId, memberId: memberId) + + certificationViewDatas.update { + $0.title = datas.title + $0.index = index + $0.todos = todos + } + } + + // MARK: from CertificationList + if let todoId = datas.todoId, let sortOption = datas.sortOption, let filter = datas.filter { + // FIXME: 추가된 API 적용 + print("todo Id \(todoId), sortOption \(sortOption) filter \(filter)") + } + } + + func getMemberTodos(groupId: Int, memberId: Int) async throws -> (Int, [TodoEntity]) { + try await challengeGroupsUseCase.getMemberTodos(groupId: groupId, memberId: memberId) + } + func setIndex(index: Int) async throws { if certificationViewDatas.value.rankingEntity == nil { certificationViewDatas.update { $0.index = index } diff --git a/dogether/Presentation/Features/CertificationList/CertificationListViewController.swift b/dogether/Presentation/Features/CertificationList/CertificationListViewController.swift index 264389e2..d2293064 100644 --- a/dogether/Presentation/Features/CertificationList/CertificationListViewController.swift +++ b/dogether/Presentation/Features/CertificationList/CertificationListViewController.swift @@ -51,7 +51,7 @@ protocol CertificationListPageDelegate { func updateBottomSheetVisibleAction(isShowSheet: Bool) func selectSortAction(index: Int) func selectFilterAction(filterType: FilterTypes) - func selectCertificationAction(title: String, todos: [TodoEntity], index: Int) + func selectCertificationAction(title: String, todo: TodoEntity) func didScrollToBottom() } @@ -70,10 +70,15 @@ extension CertificationListViewController: CertificationListPageDelegate { viewModel.updateFilter(filter: filterType) } - func selectCertificationAction(title: String, todos: [TodoEntity], index: Int) { + func selectCertificationAction(title: String, todo: TodoEntity) { let certificationViewController = CertificationViewController() - let certificationViewDatas = CertificationViewDatas(title: title, todos: todos, index: index) - coordinator?.pushViewController(certificationViewController, datas: certificationViewDatas) + let preCertificationViewDatas = PreCertificationViewDatas( + title: title, + todoId: todo.id, + sortOption: viewModel.sortViewDatas.value.options[viewModel.sortViewDatas.value.index], + filter: viewModel.certificationListViewDatas.value.filter + ) + coordinator?.pushViewController(certificationViewController, datas: preCertificationViewDatas) } func didScrollToBottom() { diff --git a/dogether/Presentation/Features/CertificationList/Components/CertificationListContentView.swift b/dogether/Presentation/Features/CertificationList/Components/CertificationListContentView.swift index fb39d5b1..4d2c6534 100644 --- a/dogether/Presentation/Features/CertificationList/Components/CertificationListContentView.swift +++ b/dogether/Presentation/Features/CertificationList/Components/CertificationListContentView.swift @@ -211,7 +211,7 @@ extension CertificationListContentView: UICollectionViewDelegate { title = groupName } - delegate?.selectCertificationAction(title: title, todos: section.todos, index: indexPath.item) + delegate?.selectCertificationAction(title: title, todo: section.todos[indexPath.item]) } } diff --git a/dogether/Presentation/Features/Main/Components/TodoListItemButton.swift b/dogether/Presentation/Features/Main/Components/TodoListItemButton.swift index 5e966d2f..111929a7 100644 --- a/dogether/Presentation/Features/Main/Components/TodoListItemButton.swift +++ b/dogether/Presentation/Features/Main/Components/TodoListItemButton.swift @@ -19,20 +19,18 @@ final class TodoListItemButton: BaseButton { delegate?.goCertificateViewAction(todo: todo) } else { return } } else { - delegate?.goCertificationViewAction(index: index) + delegate?.goCertificationViewAction(todo: todo) } }, for: .touchUpInside ) } } - private(set) var index: Int private(set) var todo: TodoEntity private(set) var isToday: Bool private(set) var isUncertified: Bool - init(index: Int, todo: TodoEntity, isToday: Bool) { - self.index = index + init(todo: TodoEntity, isToday: Bool) { self.todo = todo self.isToday = isToday self.isUncertified = todo.status == .waitCertification diff --git a/dogether/Presentation/Features/Main/Components/TodoListView.swift b/dogether/Presentation/Features/Main/Components/TodoListView.swift index 423ca54a..c337f4ff 100644 --- a/dogether/Presentation/Features/Main/Components/TodoListView.swift +++ b/dogether/Presentation/Features/Main/Components/TodoListView.swift @@ -127,8 +127,8 @@ final class TodoListView: BaseView { todoListStackView.subviews.forEach { todoListStackView.removeArrangedSubview($0) } currentTodoList - .enumerated().map { - let todoListItemButton = TodoListItemButton(index: $0, todo: $1, isToday: isToday) + .map { + let todoListItemButton = TodoListItemButton(todo: $0, isToday: isToday) todoListItemButton.delegate = delegate return todoListItemButton } diff --git a/dogether/Presentation/Features/Main/MainViewController.swift b/dogether/Presentation/Features/Main/MainViewController.swift index 3d76d713..afbc7be8 100644 --- a/dogether/Presentation/Features/Main/MainViewController.swift +++ b/dogether/Presentation/Features/Main/MainViewController.swift @@ -115,7 +115,7 @@ protocol MainDelegate { func goWriteTodoViewAction(todos: [TodoEntity]) func selectFilterAction(filterType: FilterTypes) func goCertificateViewAction(todo: TodoEntity) - func goCertificationViewAction(index: Int) + func goCertificationViewAction(todo: TodoEntity) } extension MainViewController: MainDelegate { @@ -235,15 +235,18 @@ extension MainViewController: MainDelegate { coordinator?.pushViewController(certificateImageViewController, datas: certificateViewDatas) } - func goCertificationViewAction(index: Int) { + func goCertificationViewAction(todo: TodoEntity) { let certificationViewController = CertificationViewController() - let certificationViewDatas = CertificationViewDatas( + let date = DateFormatterManager.formattedDate( + viewModel.sheetViewDatas.value.dateOffset + ).split(separator: ".").joined(separator: "-") + let preCertificationViewDatas = PreCertificationViewDatas( title: "내 인증 정보", - todos: viewModel.sheetViewDatas.value.todoList.filter { - viewModel.sheetViewDatas.value.filter == .all || viewModel.sheetViewDatas.value.filter == FilterTypes(status: $0.status.rawValue) - }, - index: index + date: date, + groupId: viewModel.currentGroup.id, + todoId: todo.id, + filter: viewModel.sheetViewDatas.value.filter ) - coordinator?.pushViewController(certificationViewController, datas: certificationViewDatas) + coordinator?.pushViewController(certificationViewController, datas: preCertificationViewDatas) } } diff --git a/dogether/Presentation/Features/Ranking/RankingViewController.swift b/dogether/Presentation/Features/Ranking/RankingViewController.swift index 8bc15e4f..8210730e 100644 --- a/dogether/Presentation/Features/Ranking/RankingViewController.swift +++ b/dogether/Presentation/Features/Ranking/RankingViewController.swift @@ -51,19 +51,12 @@ protocol RankingDelegate { extension RankingViewController: RankingDelegate { func goCertificationViewAction(rankingEntity: RankingEntity) { - Task { - let (index, todos) = try await viewModel.getMemberTodos(memberId: rankingEntity.memberId) - - await MainActor.run { - let certificationViewController = CertificationViewController() - let certificationViewDatas = CertificationViewDatas( - title: "\(rankingEntity.name)님의 인증 정보", - todos: todos, - index: index, - rankingEntity: rankingEntity - ) - coordinator?.pushViewController(certificationViewController, datas: certificationViewDatas) - } - } + let certificationViewController = CertificationViewController() + let preCertificationViewDatas = PreCertificationViewDatas( + title: "\(rankingEntity.name)님의 인증 정보", + groupId: viewModel.rankingViewDatas.value.groupId, + memberId: rankingEntity.memberId + ) + coordinator?.pushViewController(certificationViewController, datas: preCertificationViewDatas) } } diff --git a/dogether/Presentation/Features/Ranking/RankingViewModel.swift b/dogether/Presentation/Features/Ranking/RankingViewModel.swift index 906049fe..3895e3fb 100644 --- a/dogether/Presentation/Features/Ranking/RankingViewModel.swift +++ b/dogether/Presentation/Features/Ranking/RankingViewModel.swift @@ -11,16 +11,12 @@ import RxRelay final class RankingViewModel { private let groupUseCase: GroupUseCase - private let challengeGroupsUseCase: ChallengeGroupsUseCase private(set) var rankingViewDatas = BehaviorRelay(value: RankingViewDatas()) init() { let groupRepository = DIManager.shared.getGroupRepository() - let challengeGroupsRepository = DIManager.shared.getChallengeGroupsRepository() - self.groupUseCase = GroupUseCase(repository: groupRepository) - self.challengeGroupsUseCase = ChallengeGroupsUseCase(repository: challengeGroupsRepository) } } @@ -35,8 +31,4 @@ extension RankingViewModel { let rankings = try await groupUseCase.getRankings(groupId: rankingViewDatas.value.groupId) rankingViewDatas.update { $0.rankings = rankings } } - - func getMemberTodos(memberId: Int) async throws -> (Int, [TodoEntity]) { - try await challengeGroupsUseCase.getMemberTodos(groupId: rankingViewDatas.value.groupId, memberId: memberId) - } } From 46b745a04efcb0d29db87abba8e92183a749f0a7 Mon Sep 17 00:00:00 2001 From: seungyooooong Date: Wed, 21 Jan 2026 14:41:26 +0900 Subject: [PATCH 08/18] =?UTF-8?q?feat=20#165:=20getMyCertifications=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Data/DataSources/UserDataSource.swift | 5 ++++ .../GetMyActivityFromTodoResponse.swift | 23 +++++++++++++++ dogether/Data/Network/Router/UserRouter.swift | 9 +++++- dogether/Data/Repository/UserRepository.swift | 15 ++++++++++ .../RepositoryTest/UserRepositoryTest.swift | 6 +++- dogether/Domain/Protocol/UserProtocol.swift | 2 ++ dogether/Domain/UseCase/UserUseCase.swift | 4 +++ .../CertificationViewModel.swift | 28 +++++++++++++++---- 8 files changed, 85 insertions(+), 7 deletions(-) create mode 100644 dogether/Data/Network/Response/GetMyActivityFromTodoResponse.swift diff --git a/dogether/Data/DataSources/UserDataSource.swift b/dogether/Data/DataSources/UserDataSource.swift index 138dec39..2a37f413 100644 --- a/dogether/Data/DataSources/UserDataSource.swift +++ b/dogether/Data/DataSources/UserDataSource.swift @@ -24,6 +24,11 @@ final class UserDataSource { try await NetworkManager.shared.request(UserRouter.getMyCertificationStats(groupId: groupId)) } + + func getMyActivityFromTodo(todoId: Int, sort: String) async throws -> GetMyActivityFromTodoResponse { + try await NetworkManager.shared.request(UserRouter.getMyActivityFromTodo(todoId: todoId, sort: sort)) + } + func getMyProfile() async throws -> GetMyProfileResponse { try await NetworkManager.shared.request(UserRouter.getMyProfile) } diff --git a/dogether/Data/Network/Response/GetMyActivityFromTodoResponse.swift b/dogether/Data/Network/Response/GetMyActivityFromTodoResponse.swift new file mode 100644 index 00000000..6e0c35d7 --- /dev/null +++ b/dogether/Data/Network/Response/GetMyActivityFromTodoResponse.swift @@ -0,0 +1,23 @@ +// +// GetMyActivityFromTodoResponse.swift +// dogether +// +// Created by seungyooooong on 1/20/26. +// + +import Foundation + +struct GetMyActivityFromTodoResponse: Decodable { + let certifications: [CertificationEntityInGetMyActivityFromTodoResponse] +} + + +struct CertificationEntityInGetMyActivityFromTodoResponse: Decodable { + let id: Int + let content: String + var status: String + var canRequestCertificationReview: Bool + var certificationContent: String + var certificationMediaUrl: String + var reviewFeedback: String? +} diff --git a/dogether/Data/Network/Router/UserRouter.swift b/dogether/Data/Network/Router/UserRouter.swift index 564ddab6..18cb9470 100644 --- a/dogether/Data/Network/Router/UserRouter.swift +++ b/dogether/Data/Network/Router/UserRouter.swift @@ -11,6 +11,7 @@ enum UserRouter: NetworkEndpoint { case getMyGroupActivity(groupId: Int) case getMyActivity(sort: String, page: String) case getMyCertificationStats(groupId: Int?) + case getMyActivityFromTodo(todoId: Int, sort: String) case getMyProfile var path: String { @@ -21,6 +22,8 @@ enum UserRouter: NetworkEndpoint { return Path.api + Path.v2 + Path.my + "/certifications" case .getMyCertificationStats: return Path.api + Path.v2 + Path.my + "/certification-stats" + case .getMyActivityFromTodo(let todoId, _): + return Path.api + Path.v1 + Path.my + "/activity" + "/todos/\(todoId)" + "/group-certifications" case .getMyProfile: return Path.api + Path.v1 + Path.my + "/profile" } @@ -28,7 +31,7 @@ enum UserRouter: NetworkEndpoint { var method: NetworkMethod { switch self { - case .getMyGroupActivity, .getMyActivity, .getMyCertificationStats, .getMyProfile: + case .getMyGroupActivity, .getMyActivity, .getMyCertificationStats, .getMyActivityFromTodo, .getMyProfile: return .get } } @@ -45,6 +48,10 @@ enum UserRouter: NetworkEndpoint { return [ .init(name: "groupId", value: String(groupId)) ] + case let.getMyActivityFromTodo(_, sort): + return [ + .init(name: "sortBy", value: sort) + ] default: return nil } diff --git a/dogether/Data/Repository/UserRepository.swift b/dogether/Data/Repository/UserRepository.swift index ef20cb2c..4dea59db 100644 --- a/dogether/Data/Repository/UserRepository.swift +++ b/dogether/Data/Repository/UserRepository.swift @@ -92,6 +92,21 @@ final class UserRepository: UserProtocol { return (statsViewDatas, certificationListViewDatas) } + func getMyCertifications(todoId: Int, sortOption: SortOptions) async throws -> [TodoEntity] { + let response = try await userDataSource.getMyActivityFromTodo(todoId: todoId, sort: sortOption.sortString) + return response.certifications.map { + TodoEntity( + id: $0.id, + content: $0.content, + status: TodoStatus(rawValue: $0.status) ?? .waitCertification, + canRemindReview: $0.canRequestCertificationReview, + certificationContent: $0.certificationContent, + certificationMediaUrl: $0.certificationMediaUrl, + reviewFeedback: $0.reviewFeedback + ) + } + } + func getProfileViewDatas() async throws -> ProfileViewDatas { let response = try await userDataSource.getMyProfile() return ProfileViewDatas( diff --git a/dogether/Data/RepositoryTest/UserRepositoryTest.swift b/dogether/Data/RepositoryTest/UserRepositoryTest.swift index 77dde783..838791fd 100644 --- a/dogether/Data/RepositoryTest/UserRepositoryTest.swift +++ b/dogether/Data/RepositoryTest/UserRepositoryTest.swift @@ -110,7 +110,11 @@ final class UserRepositoryTest: UserProtocol { return (statsViewDatas, certificationListViewDatas) } - + + func getMyCertifications(todoId: Int, sortOption: SortOptions) async throws -> [TodoEntity] { + return [] + } + func getProfileViewDatas() async throws -> ProfileViewDatas { return ProfileViewDatas(name: "두식", imageUrl: "") } diff --git a/dogether/Domain/Protocol/UserProtocol.swift b/dogether/Domain/Protocol/UserProtocol.swift index 5889438a..1109ce52 100644 --- a/dogether/Domain/Protocol/UserProtocol.swift +++ b/dogether/Domain/Protocol/UserProtocol.swift @@ -19,5 +19,7 @@ protocol UserProtocol { certificationListViewDatas: CertificationListViewDatas ) + func getMyCertifications(todoId: Int, sortOption: SortOptions) async throws -> [TodoEntity] + func getProfileViewDatas() async throws -> ProfileViewDatas } diff --git a/dogether/Domain/UseCase/UserUseCase.swift b/dogether/Domain/UseCase/UserUseCase.swift index df16cd54..3c8e8b70 100644 --- a/dogether/Domain/UseCase/UserUseCase.swift +++ b/dogether/Domain/UseCase/UserUseCase.swift @@ -29,6 +29,10 @@ final class UserUseCase { try await repository.getCertificationListViewDatas(option: option, page: page) } + func getMyCertifications(todoId: Int, sortOption: SortOptions) async throws -> [TodoEntity] { + try await repository.getMyCertifications(todoId: todoId, sortOption: sortOption) + } + func getProfileViewDatas() async throws -> ProfileViewDatas { try await repository.getProfileViewDatas() } diff --git a/dogether/Presentation/Features/Certification/CertificationViewModel.swift b/dogether/Presentation/Features/Certification/CertificationViewModel.swift index dca6ae16..d84f0151 100644 --- a/dogether/Presentation/Features/Certification/CertificationViewModel.swift +++ b/dogether/Presentation/Features/Certification/CertificationViewModel.swift @@ -10,6 +10,7 @@ import RxRelay final class CertificationViewModel { private let challengeGroupsUseCase: ChallengeGroupsUseCase private let todosUseCase: TodosUseCase + private let userUseCase: UserUseCase private(set) var preCertificationViewDatas = BehaviorRelay( value: PreCertificationViewDatas() @@ -19,9 +20,11 @@ final class CertificationViewModel { init() { let challengeGroupsRepository = DIManager.shared.getChallengeGroupsRepository() let todosRepository = DIManager.shared.getTodosRepository() + let userRepository = DIManager.shared.getUserRepository() self.challengeGroupsUseCase = ChallengeGroupsUseCase(repository: challengeGroupsRepository) self.todosUseCase = TodosUseCase(repository: todosRepository) + self.userUseCase = UserUseCase(repository: userRepository) } } @@ -30,9 +33,7 @@ extension CertificationViewModel { let datas = preCertificationViewDatas.value // MARK: from Main if let date = datas.date, let groupId = datas.groupId, let todoId = datas.todoId, let filter = datas.filter { - let todos = try await challengeGroupsUseCase.getMyTodos(groupId: groupId, date: date).filter { - filter == .all || filter == FilterTypes(status: $0.status.rawValue) - } + let todos = try await getMyTodos(groupId: groupId, date: date, filter: filter) certificationViewDatas.update { $0.title = datas.title @@ -54,8 +55,19 @@ extension CertificationViewModel { // MARK: from CertificationList if let todoId = datas.todoId, let sortOption = datas.sortOption, let filter = datas.filter { - // FIXME: 추가된 API 적용 - print("todo Id \(todoId), sortOption \(sortOption) filter \(filter)") + let todos = try await getMyCertifications(todoId: todoId, sortOption: sortOption, filter: filter) + + certificationViewDatas.update { + $0.title = datas.title + $0.index = todos.firstIndex { $0.id == todoId } ?? 0 + $0.todos = todos + } + } + } + + func getMyTodos(groupId: Int, date: String, filter: FilterTypes) async throws -> [TodoEntity] { + try await challengeGroupsUseCase.getMyTodos(groupId: groupId, date: date).filter { + filter == .all || filter == FilterTypes(status: $0.status.rawValue) } } @@ -63,6 +75,12 @@ extension CertificationViewModel { try await challengeGroupsUseCase.getMemberTodos(groupId: groupId, memberId: memberId) } + func getMyCertifications(todoId: Int, sortOption: SortOptions, filter: FilterTypes) async throws -> [TodoEntity] { + try await userUseCase.getMyCertifications(todoId: todoId, sortOption: sortOption).filter { + filter == .all || filter == FilterTypes(status: $0.status.rawValue) + } + } + func setIndex(index: Int) async throws { if certificationViewDatas.value.rankingEntity == nil { certificationViewDatas.update { $0.index = index } From d7b7ddfea5d2b22d42ae27e8b69fecc144b271d9 Mon Sep 17 00:00:00 2001 From: seungyooooong Date: Mon, 26 Jan 2026 22:52:08 +0900 Subject: [PATCH 09/18] =?UTF-8?q?feat=20#165:=20isFirst=20=EC=A1=B0?= =?UTF-8?q?=EA=B1=B4=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Certification/Components/CertificationListView.swift | 2 +- .../Features/Certification/Components/ThumbnailListView.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dogether/Presentation/Features/Certification/Components/CertificationListView.swift b/dogether/Presentation/Features/Certification/Components/CertificationListView.swift index 62512b44..1bab88e5 100644 --- a/dogether/Presentation/Features/Certification/Components/CertificationListView.swift +++ b/dogether/Presentation/Features/Certification/Components/CertificationListView.swift @@ -55,7 +55,7 @@ final class CertificationListView: BaseView { // MARK: - updateView override func updateView(_ data: (any BaseEntity)?) { if let datas = data as? CertificationViewDatas { - if isFirst { + if isFirst && datas.todos.count > 0 { isFirst = false layoutIfNeeded() diff --git a/dogether/Presentation/Features/Certification/Components/ThumbnailListView.swift b/dogether/Presentation/Features/Certification/Components/ThumbnailListView.swift index 2dd67e8e..7accd0f7 100644 --- a/dogether/Presentation/Features/Certification/Components/ThumbnailListView.swift +++ b/dogether/Presentation/Features/Certification/Components/ThumbnailListView.swift @@ -52,7 +52,7 @@ final class ThumbnailListView: BaseView { // MARK: - updateView override func updateView(_ data: (any BaseEntity)?) { if let datas = data as? CertificationViewDatas { - if isFirst { + if isFirst && datas.todos.count > 0 { isFirst = false datas.todos From 4fd5c7c5050daf9dac5d38e56cf3fe364991a7c9 Mon Sep 17 00:00:00 2001 From: seungyooooong Date: Tue, 27 Jan 2026 17:19:05 +0900 Subject: [PATCH 10/18] =?UTF-8?q?feat=20#165:=20skeletonView=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20(1)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Components/CertificationListView.swift | 16 ++++++++++++++-- .../Components/CertificationPage.swift | 2 ++ .../Components/ThumbnailListView.swift | 12 +++++++++++- 3 files changed, 27 insertions(+), 3 deletions(-) diff --git a/dogether/Presentation/Features/Certification/Components/CertificationListView.swift b/dogether/Presentation/Features/Certification/Components/CertificationListView.swift index 1bab88e5..85642186 100644 --- a/dogether/Presentation/Features/Certification/Components/CertificationListView.swift +++ b/dogether/Presentation/Features/Certification/Components/CertificationListView.swift @@ -19,6 +19,7 @@ final class CertificationListView: BaseView { private let scrollView = UIScrollView() private let stackView = UIStackView() + private let skeletonView = SkeletonView() private var isFirst: Bool = true private var currentIndex: Int? @@ -40,6 +41,7 @@ final class CertificationListView: BaseView { override func configureHierarchy() { [scrollView].forEach { addSubview($0) } [stackView].forEach { scrollView.addSubview($0) } + stackView.addArrangedSubview(skeletonView) } override func configureConstraints() { @@ -49,15 +51,25 @@ final class CertificationListView: BaseView { stackView.snp.makeConstraints { $0.horizontalEdges.equalToSuperview().inset(16) + $0.width.equalToSuperview().offset(-32) + $0.height.equalToSuperview() + } + + skeletonView.snp.makeConstraints { + $0.edges.equalToSuperview() } } // MARK: - updateView override func updateView(_ data: (any BaseEntity)?) { if let datas = data as? CertificationViewDatas { - if isFirst && datas.todos.count > 0 { + if datas.todos.isEmpty { return } + + if isFirst { isFirst = false + stackView.arrangedSubviews.forEach { $0.removeFromSuperview() } + layoutIfNeeded() datas.todos @@ -78,7 +90,7 @@ final class CertificationListView: BaseView { stackView.addArrangedSubview($0) } - stackView.snp.makeConstraints { + stackView.snp.remakeConstraints { $0.horizontalEdges.equalToSuperview().inset(16) $0.width.equalTo(frame.width * CGFloat(datas.todos.count) - 32) $0.height.equalToSuperview() diff --git a/dogether/Presentation/Features/Certification/Components/CertificationPage.swift b/dogether/Presentation/Features/Certification/Components/CertificationPage.swift index 1e354eb4..22b80a76 100644 --- a/dogether/Presentation/Features/Certification/Components/CertificationPage.swift +++ b/dogether/Presentation/Features/Certification/Components/CertificationPage.swift @@ -61,6 +61,8 @@ final class CertificationPage: BasePage { contentLabel.textColor = .grey0 contentLabel.numberOfLines = 0 + + [certificateButton, remindCertificationButton, remindReviewButton].forEach { $0.isHidden = true } } override func configureAction() { diff --git a/dogether/Presentation/Features/Certification/Components/ThumbnailListView.swift b/dogether/Presentation/Features/Certification/Components/ThumbnailListView.swift index 7accd0f7..097e2ea7 100644 --- a/dogether/Presentation/Features/Certification/Components/ThumbnailListView.swift +++ b/dogether/Presentation/Features/Certification/Components/ThumbnailListView.swift @@ -19,6 +19,7 @@ final class ThumbnailListView: BaseView { private let scrollView = UIScrollView() private let stackView = UIStackView() + private let skeletonViews = (0 ..< 6).map { _ in SkeletonView() } private var isFirst: Bool = true private var currentIndex: Int? @@ -37,6 +38,7 @@ final class ThumbnailListView: BaseView { override func configureHierarchy() { [scrollView].forEach { addSubview($0) } [stackView].forEach { scrollView.addSubview($0) } + skeletonViews.forEach { stackView.addArrangedSubview($0) } } override func configureConstraints() { @@ -47,14 +49,22 @@ final class ThumbnailListView: BaseView { stackView.snp.makeConstraints { $0.horizontalEdges.equalToSuperview().inset(16) } + + skeletonViews.forEach { $0.snp.makeConstraints { + $0.width.height.equalTo(54) + } } } // MARK: - updateView override func updateView(_ data: (any BaseEntity)?) { if let datas = data as? CertificationViewDatas { - if isFirst && datas.todos.count > 0 { + if datas.todos.isEmpty { return } + + if isFirst { isFirst = false + stackView.arrangedSubviews.forEach { $0.removeFromSuperview() } + datas.todos .enumerated().map { let thumbnailView = ThumbnailView() From a60f5e3a19a1d34ee794014bd175a634720b0fd7 Mon Sep 17 00:00:00 2001 From: seungyooooong Date: Wed, 28 Jan 2026 18:06:16 +0900 Subject: [PATCH 11/18] =?UTF-8?q?fix=20#165:=20readTodo=20=EC=A1=B0?= =?UTF-8?q?=EA=B1=B4=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Features/Certification/CertificationViewModel.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dogether/Presentation/Features/Certification/CertificationViewModel.swift b/dogether/Presentation/Features/Certification/CertificationViewModel.swift index d84f0151..4eecbb21 100644 --- a/dogether/Presentation/Features/Certification/CertificationViewModel.swift +++ b/dogether/Presentation/Features/Certification/CertificationViewModel.swift @@ -93,9 +93,9 @@ extension CertificationViewModel { } func readTodo(index: Int? = nil) async throws { - if certificationViewDatas.value.rankingEntity == nil { return } let index = index ?? certificationViewDatas.value.index - guard let todo = certificationViewDatas.value.todos[safe: index] else { return } + guard let todo = certificationViewDatas.value.todos[safe: index], + let _ = preCertificationViewDatas.value.memberId else { return } try await challengeGroupsUseCase.readTodo(todo: todo) } From 05d5c693bd7941d67f53e444679178a9a6320004 Mon Sep 17 00:00:00 2001 From: seungyooooong Date: Tue, 3 Feb 2026 13:17:48 +0900 Subject: [PATCH 12/18] =?UTF-8?q?feat=20#165:=20isMine=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Data/Network/Response/GetMemberTodosResponse.swift | 5 ++++- dogether/Data/Repository/ChallengeGroupsRepository.swift | 5 +++-- .../RepositoryTest/ChallengeGroupsRepositoryTest.swift | 4 ++-- .../Domain/Entity/ViewDatas/CertificationViewDatas.swift | 6 +++--- dogether/Domain/Protocol/ChallengeGroupsProtocol.swift | 2 +- dogether/Domain/UseCase/ChallengeGroupsUseCase.swift | 2 +- .../Features/Certification/CertificationViewModel.swift | 9 +++++---- .../Certification/Components/CertificationPage.swift | 9 +++++---- 8 files changed, 24 insertions(+), 18 deletions(-) diff --git a/dogether/Data/Network/Response/GetMemberTodosResponse.swift b/dogether/Data/Network/Response/GetMemberTodosResponse.swift index 4964c670..10df1f8b 100644 --- a/dogether/Data/Network/Response/GetMemberTodosResponse.swift +++ b/dogether/Data/Network/Response/GetMemberTodosResponse.swift @@ -23,6 +23,7 @@ struct TodoEntityInGetMemberTodos: Decodable { let certificationMediaUrl: String? let isRead: Bool let reviewFeedback: String? + let isMine: Bool init( historyId: Int, @@ -34,7 +35,8 @@ struct TodoEntityInGetMemberTodos: Decodable { certificationContent: String? = nil, certificationMediaUrl: String? = nil, isRead: Bool, - reviewFeedback: String? = nil + reviewFeedback: String? = nil, + isMine: Bool ) { self.historyId = historyId self.todoId = todoId @@ -46,5 +48,6 @@ struct TodoEntityInGetMemberTodos: Decodable { self.certificationMediaUrl = certificationMediaUrl self.isRead = isRead self.reviewFeedback = reviewFeedback + self.isMine = isMine } } diff --git a/dogether/Data/Repository/ChallengeGroupsRepository.swift b/dogether/Data/Repository/ChallengeGroupsRepository.swift index bceff0ff..b306478c 100644 --- a/dogether/Data/Repository/ChallengeGroupsRepository.swift +++ b/dogether/Data/Repository/ChallengeGroupsRepository.swift @@ -33,12 +33,13 @@ final class ChallengeGroupsRepository: ChallengeGroupsProtocol { } } - func getMemberTodos(groupId: Int, memberId: Int) async throws -> (index: Int, todos: [TodoEntity]) { + func getMemberTodos(groupId: Int, memberId: Int) async throws -> (index: Int, isMine: Bool, todos: [TodoEntity]) { let response = try await challengeGroupsDataSource.getMemberTodos( groupId: String(groupId), memberId: String(memberId) ) let currentIndex = response.currentTodoHistoryToReadIndex + let isMine = response.todos[0].isMine let memberTodos = response.todos.map { TodoEntity( historyId: $0.historyId, @@ -53,7 +54,7 @@ final class ChallengeGroupsRepository: ChallengeGroupsProtocol { reviewFeedback: $0.reviewFeedback ) } - return (currentIndex, memberTodos) + return (currentIndex, isMine, memberTodos) } func readTodo(todoHistoryId: String) async throws { diff --git a/dogether/Data/RepositoryTest/ChallengeGroupsRepositoryTest.swift b/dogether/Data/RepositoryTest/ChallengeGroupsRepositoryTest.swift index 4e6f6f97..6f61689e 100644 --- a/dogether/Data/RepositoryTest/ChallengeGroupsRepositoryTest.swift +++ b/dogether/Data/RepositoryTest/ChallengeGroupsRepositoryTest.swift @@ -29,8 +29,8 @@ final class ChallengeGroupsRepositoryTest: ChallengeGroupsProtocol { return certify_pendings + review_pendings + approves + rejects } - func getMemberTodos(groupId: Int, memberId: Int) async throws -> (index: Int, todos: [TodoEntity]) { - return (index: 3, todos: [ + func getMemberTodos(groupId: Int, memberId: Int) async throws -> (index: Int, isMine: Bool, todos: [TodoEntity]) { + return (index: 3, isMine: false, todos: [ TodoEntity(id: 1, content: "신규 기능 개발", status: .waitCertification, thumbnailStatus: .done), TodoEntity(id: 2, content: "치킨 먹기", status: .waitCertification, thumbnailStatus: .done, certificationContent: "치킨 냠냠", reviewFeedback: ""), TodoEntity(id: 1, content: "신규 기능 개발", status: .waitCertification, thumbnailStatus: .done, reviewFeedback: "test"), diff --git a/dogether/Domain/Entity/ViewDatas/CertificationViewDatas.swift b/dogether/Domain/Entity/ViewDatas/CertificationViewDatas.swift index 69b6b93f..9e4fc797 100644 --- a/dogether/Domain/Entity/ViewDatas/CertificationViewDatas.swift +++ b/dogether/Domain/Entity/ViewDatas/CertificationViewDatas.swift @@ -9,19 +9,19 @@ import Foundation struct CertificationViewDatas: BaseEntity { var title: String + var isMine: Bool? var todos: [TodoEntity] var index: Int - var rankingEntity: RankingEntity? init( title: String = "", + isMine: Bool? = nil, todos: [TodoEntity] = [], index: Int = 0, - rankingEntity: RankingEntity? = nil ) { self.title = title + self.isMine = isMine self.todos = todos self.index = index - self.rankingEntity = rankingEntity } } diff --git a/dogether/Domain/Protocol/ChallengeGroupsProtocol.swift b/dogether/Domain/Protocol/ChallengeGroupsProtocol.swift index 916d466f..b9f162dc 100644 --- a/dogether/Domain/Protocol/ChallengeGroupsProtocol.swift +++ b/dogether/Domain/Protocol/ChallengeGroupsProtocol.swift @@ -10,6 +10,6 @@ import Foundation protocol ChallengeGroupsProtocol { func createTodos(groupId: String, createTodosRequest: CreateTodosRequest) async throws func getMyTodos(groupId: String, date: String) async throws -> [TodoEntity] - func getMemberTodos(groupId: Int, memberId: Int) async throws -> (index: Int, todos: [TodoEntity]) + func getMemberTodos(groupId: Int, memberId: Int) async throws -> (index: Int, isMine: Bool, todos: [TodoEntity]) func readTodo(todoHistoryId: String) async throws } diff --git a/dogether/Domain/UseCase/ChallengeGroupsUseCase.swift b/dogether/Domain/UseCase/ChallengeGroupsUseCase.swift index 71e3b385..54eec0d8 100644 --- a/dogether/Domain/UseCase/ChallengeGroupsUseCase.swift +++ b/dogether/Domain/UseCase/ChallengeGroupsUseCase.swift @@ -23,7 +23,7 @@ final class ChallengeGroupsUseCase { try await repository.getMyTodos(groupId: String(groupId), date: date).map { $0.with(createdAt: date) } } - func getMemberTodos(groupId: Int, memberId: Int) async throws -> (index: Int, todos: [TodoEntity]) { + func getMemberTodos(groupId: Int, memberId: Int) async throws -> (index: Int, isMine: Bool, todos: [TodoEntity]) { try await repository.getMemberTodos(groupId: groupId, memberId: memberId) } diff --git a/dogether/Presentation/Features/Certification/CertificationViewModel.swift b/dogether/Presentation/Features/Certification/CertificationViewModel.swift index 4eecbb21..17f2069a 100644 --- a/dogether/Presentation/Features/Certification/CertificationViewModel.swift +++ b/dogether/Presentation/Features/Certification/CertificationViewModel.swift @@ -44,10 +44,11 @@ extension CertificationViewModel { // MARK: from Ranking if let groupId = datas.groupId, let memberId = datas.memberId { - let (index, todos) = try await getMemberTodos(groupId: groupId, memberId: memberId) + let (index, isMine, todos) = try await getMemberTodos(groupId: groupId, memberId: memberId) certificationViewDatas.update { $0.title = datas.title + $0.isMine = isMine $0.index = index $0.todos = todos } @@ -71,7 +72,7 @@ extension CertificationViewModel { } } - func getMemberTodos(groupId: Int, memberId: Int) async throws -> (Int, [TodoEntity]) { + func getMemberTodos(groupId: Int, memberId: Int) async throws -> (Int, Bool, [TodoEntity]) { try await challengeGroupsUseCase.getMemberTodos(groupId: groupId, memberId: memberId) } @@ -82,7 +83,7 @@ extension CertificationViewModel { } func setIndex(index: Int) async throws { - if certificationViewDatas.value.rankingEntity == nil { + if certificationViewDatas.value.isMine == nil { certificationViewDatas.update { $0.index = index } } else { // MARK: 이전에 보고 있던 thumbnailStatus를 수정하고 이동한 todo의 read API를 호출 @@ -95,7 +96,7 @@ extension CertificationViewModel { func readTodo(index: Int? = nil) async throws { let index = index ?? certificationViewDatas.value.index guard let todo = certificationViewDatas.value.todos[safe: index], - let _ = preCertificationViewDatas.value.memberId else { return } + let _ = preCertificationViewDatas.value.memberId else { return } try await challengeGroupsUseCase.readTodo(todo: todo) } diff --git a/dogether/Presentation/Features/Certification/Components/CertificationPage.swift b/dogether/Presentation/Features/Certification/Components/CertificationPage.swift index 22b80a76..c266e7e8 100644 --- a/dogether/Presentation/Features/Certification/Components/CertificationPage.swift +++ b/dogether/Presentation/Features/Certification/Components/CertificationPage.swift @@ -150,10 +150,11 @@ final class CertificationPage: BasePage { let date = DateFormatterManager.formattedDate().split(separator: ".").joined(separator: "-") let isWaitCertification = todo.status == .waitCertification - let isToday = todo.createdAt == date || datas.rankingEntity != nil - certificateButton.isHidden = !(isWaitCertification && isToday) - remindCertificationButton.isHidden = !todo.canRemindCertification - remindReviewButton.isHidden = !todo.canRemindReview + let isToday = todo.createdAt ?? date == date + let isMine = datas.isMine ?? true + certificateButton.isHidden = !(isWaitCertification && isToday && isMine) + remindCertificationButton.isHidden = !(todo.canRemindCertification && !isMine) + remindReviewButton.isHidden = !(todo.canRemindReview && !isMine) } } } From 164b5988e5c04bda52a1a06019b6497e3543f544 Mon Sep 17 00:00:00 2001 From: seungyooooong Date: Tue, 3 Feb 2026 15:22:55 +0900 Subject: [PATCH 13/18] =?UTF-8?q?feat=20#165:=20skeletonView=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20(2)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit + remindReviewButton hidden 조건 수정 --- .../Common/ReviewFeedbackView.swift | 1 + .../Components/CertificationPage.swift | 30 ++++++++++++++++++- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/dogether/Presentation/Common/ReviewFeedbackView.swift b/dogether/Presentation/Common/ReviewFeedbackView.swift index a2254a07..b5e2d792 100644 --- a/dogether/Presentation/Common/ReviewFeedbackView.swift +++ b/dogether/Presentation/Common/ReviewFeedbackView.swift @@ -11,6 +11,7 @@ final class ReviewFeedbackView: BaseView { private let reviewFeedbackLabel = UILabel() override func configureView() { + isHidden = true backgroundColor = .grey700 layer.cornerRadius = 8 diff --git a/dogether/Presentation/Features/Certification/Components/CertificationPage.swift b/dogether/Presentation/Features/Certification/Components/CertificationPage.swift index c266e7e8..89a1437e 100644 --- a/dogether/Presentation/Features/Certification/Components/CertificationPage.swift +++ b/dogether/Presentation/Features/Certification/Components/CertificationPage.swift @@ -39,13 +39,16 @@ final class CertificationPage: BasePage { private let certificationStackView = UIStackView() private let certificationListView = CertificationListView() private let statusView = TodoStatusButton() + private let statusSkeletonView = SkeletonView() private let contentLabel = UILabel() + private let contentLabelSkeletonView = SkeletonView() private let reviewFeedbackView = ReviewFeedbackView() private let certificateButton = DogetherButton("인증하기") private let remindCertificationButton = DogetherButton("인증 재촉하기") private let remindReviewButton = DogetherButton("검사 재촉하기") private var currentTodo: TodoEntity? + private var isFirst: Bool = true override func configureView() { certificationScrollView.showsVerticalScrollIndicator = false @@ -74,6 +77,9 @@ final class CertificationPage: BasePage { certificateButton, remindCertificationButton, remindReviewButton ].forEach { addSubview($0) } certificationScrollView.addSubview(certificationStackView) + + statusView.addSubview(statusSkeletonView) + contentLabel.addSubview(contentLabelSkeletonView) } override func configureConstraints() { @@ -106,10 +112,13 @@ final class CertificationPage: BasePage { statusView.snp.makeConstraints { $0.centerX.equalToSuperview() + $0.width.equalTo(100) + $0.height.equalTo(36) } contentLabel.snp.makeConstraints { $0.horizontalEdges.equalToSuperview().inset(16) + $0.height.equalTo(36) } reviewFeedbackView.snp.makeConstraints { @@ -127,16 +136,35 @@ final class CertificationPage: BasePage { remindReviewButton.snp.makeConstraints { $0.bottom.horizontalEdges.equalToSuperview().inset(16) } + + [statusSkeletonView, contentLabelSkeletonView].forEach { + $0.snp.makeConstraints { $0.edges.equalToSuperview() } + } } // MARK: - updateView override func updateView(_ data: (any BaseEntity)?) { guard let datas = data as? CertificationViewDatas else { return } + navigationHeader.updateView(datas) thumbnailListView.updateView(datas) certificationListView.updateView(datas) if let todo = datas.todos[safe: datas.index], currentTodo != todo { + if isFirst { + isFirst = false + + statusView.snp.remakeConstraints { + $0.centerX.equalToSuperview() + } + + contentLabel.snp.remakeConstraints { + $0.horizontalEdges.equalToSuperview().inset(16) + } + + [statusSkeletonView, contentLabelSkeletonView].forEach { $0.removeFromSuperview() } + } + currentTodo = todo statusView.updateView(todo.status) @@ -154,7 +182,7 @@ final class CertificationPage: BasePage { let isMine = datas.isMine ?? true certificateButton.isHidden = !(isWaitCertification && isToday && isMine) remindCertificationButton.isHidden = !(todo.canRemindCertification && !isMine) - remindReviewButton.isHidden = !(todo.canRemindReview && !isMine) + remindReviewButton.isHidden = !todo.canRemindReview } } } From 81fabb20ec6831a08d7673ef7c8dcbbf44a8b1e3 Mon Sep 17 00:00:00 2001 From: seungyooooong Date: Tue, 3 Feb 2026 15:53:19 +0900 Subject: [PATCH 14/18] =?UTF-8?q?feat=20#161:=20=EB=B2=84=ED=8A=BC=20?= =?UTF-8?q?=EB=B9=84=ED=99=9C=EC=84=B1=ED=99=94=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Certification/CertificationViewController.swift | 3 ++- .../Certification/CertificationViewModel.swift | 10 ++++++++++ .../Components/CertificationPage.swift | 13 +++++++++++-- 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/dogether/Presentation/Features/Certification/CertificationViewController.swift b/dogether/Presentation/Features/Certification/CertificationViewController.swift index 0306f764..8c1972c3 100644 --- a/dogether/Presentation/Features/Certification/CertificationViewController.swift +++ b/dogether/Presentation/Features/Certification/CertificationViewController.swift @@ -111,7 +111,8 @@ extension CertificationViewController: CertificationDelegate { Task { [weak self] in guard let self else { return } try await viewModel.remindTodo(remindType: remindType, todoId: todoId) - // TODO: 재촉 메시지가 전송되었어요 토스트 메시지 노출 및 버튼 비활성화 + // TODO: 재촉 메시지가 전송되었어요 토스트 메시지 노출 + viewModel.updateButtonStatus(remindType: remindType, todoId: todoId) } } } diff --git a/dogether/Presentation/Features/Certification/CertificationViewModel.swift b/dogether/Presentation/Features/Certification/CertificationViewModel.swift index 17f2069a..233df1fb 100644 --- a/dogether/Presentation/Features/Certification/CertificationViewModel.swift +++ b/dogether/Presentation/Features/Certification/CertificationViewModel.swift @@ -103,4 +103,14 @@ extension CertificationViewModel { func remindTodo(remindType: RemindTypes, todoId: Int) async throws { try await todosUseCase.remindTodo(remindType: remindType, todoId: todoId) } + + func updateButtonStatus(remindType: RemindTypes, todoId: Int) { + guard let index = certificationViewDatas.value.todos.firstIndex(where: { $0.id == todoId }) else { return } + switch remindType { + case .certification: + certificationViewDatas.update { $0.todos[index].canRemindCertification = false } + case .review: + certificationViewDatas.update { $0.todos[index].canRemindReview = false } + } + } } diff --git a/dogether/Presentation/Features/Certification/Components/CertificationPage.swift b/dogether/Presentation/Features/Certification/Components/CertificationPage.swift index 89a1437e..1aa4f45c 100644 --- a/dogether/Presentation/Features/Certification/Components/CertificationPage.swift +++ b/dogether/Presentation/Features/Certification/Components/CertificationPage.swift @@ -178,11 +178,20 @@ final class CertificationPage: BasePage { let date = DateFormatterManager.formattedDate().split(separator: ".").joined(separator: "-") let isWaitCertification = todo.status == .waitCertification + let isWaitExamination = todo.status == .waitExamination let isToday = todo.createdAt ?? date == date let isMine = datas.isMine ?? true + certificateButton.isHidden = !(isWaitCertification && isToday && isMine) - remindCertificationButton.isHidden = !(todo.canRemindCertification && !isMine) - remindReviewButton.isHidden = !todo.canRemindReview + remindCertificationButton.isHidden = !(isWaitCertification && !isMine) + remindReviewButton.isHidden = !isWaitExamination + + remindCertificationButton.updateView( + DogetherButtonViewDatas(status: todo.canRemindCertification ? .enabled : .disabled) + ) + remindReviewButton.updateView( + DogetherButtonViewDatas(status: todo.canRemindReview ? .enabled : .disabled) + ) } } } From 40ea7309bf4132d8441df12d3942fc89cd6972f4 Mon Sep 17 00:00:00 2001 From: seungyooooong Date: Wed, 4 Feb 2026 13:47:14 +0900 Subject: [PATCH 15/18] =?UTF-8?q?feat=20#161:=20noData=20case=20=EC=9E=84?= =?UTF-8?q?=EC=8B=9C=20=EC=98=88=EC=99=B8=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Data/Repository/ChallengeGroupsRepository.swift | 2 ++ .../Certification/CertificationViewController.swift | 10 ++++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/dogether/Data/Repository/ChallengeGroupsRepository.swift b/dogether/Data/Repository/ChallengeGroupsRepository.swift index b306478c..94e44ba2 100644 --- a/dogether/Data/Repository/ChallengeGroupsRepository.swift +++ b/dogether/Data/Repository/ChallengeGroupsRepository.swift @@ -38,6 +38,8 @@ final class ChallengeGroupsRepository: ChallengeGroupsProtocol { groupId: String(groupId), memberId: String(memberId) ) + if response.todos.isEmpty { throw NetworkError.noData } + let currentIndex = response.currentTodoHistoryToReadIndex let isMine = response.todos[0].isMine let memberTodos = response.todos.map { diff --git a/dogether/Presentation/Features/Certification/CertificationViewController.swift b/dogether/Presentation/Features/Certification/CertificationViewController.swift index 8c1972c3..d00f15cc 100644 --- a/dogether/Presentation/Features/Certification/CertificationViewController.swift +++ b/dogether/Presentation/Features/Certification/CertificationViewController.swift @@ -40,8 +40,14 @@ extension CertificationViewController { private func loadCertificationView() { Task { [weak self] in guard let self else { return } - try await viewModel.loadCertificationView() - try await viewModel.readTodo() + do { + try await viewModel.loadCertificationView() + try await viewModel.readTodo() + } catch let error as NetworkError { + if case .noData = error { + coordinator?.popViewController() + } + } } } } From 83afcf703fb63ed4c8b056684a46c0f07cfaffe3 Mon Sep 17 00:00:00 2001 From: seungyooooong Date: Wed, 4 Feb 2026 15:42:49 +0900 Subject: [PATCH 16/18] =?UTF-8?q?feat=20#161:=20isMine=20=EB=B0=B1?= =?UTF-8?q?=EC=97=94=EB=93=9C=20=EC=88=98=EC=A0=95=20=EB=82=B4=EC=9A=A9=20?= =?UTF-8?q?=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dogether/Data/Network/Response/GetMemberTodosResponse.swift | 6 ++---- dogether/Data/Repository/ChallengeGroupsRepository.swift | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/dogether/Data/Network/Response/GetMemberTodosResponse.swift b/dogether/Data/Network/Response/GetMemberTodosResponse.swift index 10df1f8b..25ca01af 100644 --- a/dogether/Data/Network/Response/GetMemberTodosResponse.swift +++ b/dogether/Data/Network/Response/GetMemberTodosResponse.swift @@ -8,6 +8,7 @@ import Foundation struct GetMemberTodosResponse: Decodable { + let isMine: Bool let currentTodoHistoryToReadIndex: Int let todos: [TodoEntityInGetMemberTodos] } @@ -23,7 +24,6 @@ struct TodoEntityInGetMemberTodos: Decodable { let certificationMediaUrl: String? let isRead: Bool let reviewFeedback: String? - let isMine: Bool init( historyId: Int, @@ -35,8 +35,7 @@ struct TodoEntityInGetMemberTodos: Decodable { certificationContent: String? = nil, certificationMediaUrl: String? = nil, isRead: Bool, - reviewFeedback: String? = nil, - isMine: Bool + reviewFeedback: String? = nil ) { self.historyId = historyId self.todoId = todoId @@ -48,6 +47,5 @@ struct TodoEntityInGetMemberTodos: Decodable { self.certificationMediaUrl = certificationMediaUrl self.isRead = isRead self.reviewFeedback = reviewFeedback - self.isMine = isMine } } diff --git a/dogether/Data/Repository/ChallengeGroupsRepository.swift b/dogether/Data/Repository/ChallengeGroupsRepository.swift index 94e44ba2..ee2911f5 100644 --- a/dogether/Data/Repository/ChallengeGroupsRepository.swift +++ b/dogether/Data/Repository/ChallengeGroupsRepository.swift @@ -41,7 +41,7 @@ final class ChallengeGroupsRepository: ChallengeGroupsProtocol { if response.todos.isEmpty { throw NetworkError.noData } let currentIndex = response.currentTodoHistoryToReadIndex - let isMine = response.todos[0].isMine + let isMine = response.isMine let memberTodos = response.todos.map { TodoEntity( historyId: $0.historyId, From 0343b35b915bd94d64f8905c64128b6679087c6f Mon Sep 17 00:00:00 2001 From: seungyooooong Date: Fri, 20 Feb 2026 20:15:07 +0900 Subject: [PATCH 17/18] =?UTF-8?q?feat=20#161:=20translateDateFormatForServ?= =?UTF-8?q?er=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Components/CertificationPage.swift | 2 +- .../Features/Main/MainViewController.swift | 2 +- .../Presentation/Features/Main/MainViewModel.swift | 2 +- dogether/Utility/Extension/StringExt.swift | 14 ++++++++++++++ 4 files changed, 17 insertions(+), 3 deletions(-) create mode 100644 dogether/Utility/Extension/StringExt.swift diff --git a/dogether/Presentation/Features/Certification/Components/CertificationPage.swift b/dogether/Presentation/Features/Certification/Components/CertificationPage.swift index 1aa4f45c..c0b8ad43 100644 --- a/dogether/Presentation/Features/Certification/Components/CertificationPage.swift +++ b/dogether/Presentation/Features/Certification/Components/CertificationPage.swift @@ -176,7 +176,7 @@ final class CertificationPage: BasePage { reviewFeedbackView.updateView(todo.reviewFeedback ?? "") - let date = DateFormatterManager.formattedDate().split(separator: ".").joined(separator: "-") + let date = DateFormatterManager.formattedDate().translateDateFormatForServer() let isWaitCertification = todo.status == .waitCertification let isWaitExamination = todo.status == .waitExamination let isToday = todo.createdAt ?? date == date diff --git a/dogether/Presentation/Features/Main/MainViewController.swift b/dogether/Presentation/Features/Main/MainViewController.swift index afbc7be8..e6d25c71 100644 --- a/dogether/Presentation/Features/Main/MainViewController.swift +++ b/dogether/Presentation/Features/Main/MainViewController.swift @@ -239,7 +239,7 @@ extension MainViewController: MainDelegate { let certificationViewController = CertificationViewController() let date = DateFormatterManager.formattedDate( viewModel.sheetViewDatas.value.dateOffset - ).split(separator: ".").joined(separator: "-") + ).translateDateFormatForServer() let preCertificationViewDatas = PreCertificationViewDatas( title: "내 인증 정보", date: date, diff --git a/dogether/Presentation/Features/Main/MainViewModel.swift b/dogether/Presentation/Features/Main/MainViewModel.swift index a0e6909c..13475efb 100644 --- a/dogether/Presentation/Features/Main/MainViewModel.swift +++ b/dogether/Presentation/Features/Main/MainViewModel.swift @@ -42,7 +42,7 @@ extension MainViewModel { } func getTodoList(dateOffset: Int, groupId: Int) async throws -> [TodoEntity] { - let date = DateFormatterManager.formattedDate(dateOffset).split(separator: ".").joined(separator: "-") + let date = DateFormatterManager.formattedDate(dateOffset).translateDateFormatForServer() return try await challengeGroupsUseCase.getMyTodos(groupId: groupId, date: date) } diff --git a/dogether/Utility/Extension/StringExt.swift b/dogether/Utility/Extension/StringExt.swift new file mode 100644 index 00000000..20e1cd5e --- /dev/null +++ b/dogether/Utility/Extension/StringExt.swift @@ -0,0 +1,14 @@ +// +// StringExt.swift +// dogether +// +// Created by seungyooooong on 2/20/26. +// + +import Foundation + +extension String { + func translateDateFormatForServer() -> String { + self.split(separator: ".").joined(separator: "-") + } +} From b77f8b2bebf3f7ba355412bba8e083f85bac08d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=8B=E1=85=B2=E1=84=8C=E1=85=A2=E1=84=92=E1=85=A9?= =?UTF-8?q?=E1=86=BC?= Date: Thu, 19 Feb 2026 17:07:57 +0900 Subject: [PATCH 18/18] =?UTF-8?q?refactor=20#161:=20PreCertificationViewDa?= =?UTF-8?q?tas=20enum=EC=9C=BC=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Conflicts: # dogether/Presentation/Features/Main/MainViewController.swift --- .../ViewDatas/PreCertificationViewDatas.swift | 30 +++------------- .../CertificationViewModel.swift | 34 +++++++------------ .../CertificationListViewController.swift | 2 +- .../Features/Main/MainViewController.swift | 2 +- .../Ranking/RankingViewController.swift | 2 +- 5 files changed, 20 insertions(+), 50 deletions(-) diff --git a/dogether/Domain/Entity/ViewDatas/PreCertificationViewDatas.swift b/dogether/Domain/Entity/ViewDatas/PreCertificationViewDatas.swift index 876ba885..0534b8c0 100644 --- a/dogether/Domain/Entity/ViewDatas/PreCertificationViewDatas.swift +++ b/dogether/Domain/Entity/ViewDatas/PreCertificationViewDatas.swift @@ -7,30 +7,8 @@ import Foundation -struct PreCertificationViewDatas: BaseEntity { - let title: String - let date: String? - let groupId: Int? - let memberId: Int? - let todoId: Int? - let sortOption: SortOptions? - let filter: FilterTypes? - - init( - title: String = "", - date: String? = nil, - groupId: Int? = nil, - memberId: Int? = nil, - todoId: Int? = nil, - sortOption: SortOptions? = nil, - filter: FilterTypes? = nil - ) { - self.title = title - self.date = date - self.groupId = groupId - self.memberId = memberId - self.todoId = todoId - self.sortOption = sortOption - self.filter = filter - } +enum PreCertificationViewDatas: BaseEntity { + case main(title: String, date: String, groupId: Int, todoId: Int, filter: FilterTypes) + case ranking(title: String, groupId: Int, memberId: Int) + case certificationList(title: String, todoId: Int, sortOption: SortOptions, filter: FilterTypes) } diff --git a/dogether/Presentation/Features/Certification/CertificationViewModel.swift b/dogether/Presentation/Features/Certification/CertificationViewModel.swift index 233df1fb..4c6c6a64 100644 --- a/dogether/Presentation/Features/Certification/CertificationViewModel.swift +++ b/dogether/Presentation/Features/Certification/CertificationViewModel.swift @@ -12,9 +12,7 @@ final class CertificationViewModel { private let todosUseCase: TodosUseCase private let userUseCase: UserUseCase - private(set) var preCertificationViewDatas = BehaviorRelay( - value: PreCertificationViewDatas() - ) + private(set) var preCertificationViewDatas = BehaviorRelay(value: nil) private(set) var certificationViewDatas = BehaviorRelay(value: CertificationViewDatas()) init() { @@ -30,36 +28,30 @@ final class CertificationViewModel { extension CertificationViewModel { func loadCertificationView() async throws { - let datas = preCertificationViewDatas.value - // MARK: from Main - if let date = datas.date, let groupId = datas.groupId, let todoId = datas.todoId, let filter = datas.filter { + guard let datas = preCertificationViewDatas.value else { return } + + switch datas { + case .main(let title, let date, let groupId, let todoId, let filter): let todos = try await getMyTodos(groupId: groupId, date: date, filter: filter) - certificationViewDatas.update { - $0.title = datas.title + $0.title = title $0.index = todos.firstIndex { $0.id == todoId } ?? 0 $0.todos = todos } - } - - // MARK: from Ranking - if let groupId = datas.groupId, let memberId = datas.memberId { + + case .ranking(let title, let groupId, let memberId): let (index, isMine, todos) = try await getMemberTodos(groupId: groupId, memberId: memberId) - certificationViewDatas.update { - $0.title = datas.title + $0.title = title $0.isMine = isMine $0.index = index $0.todos = todos } - } - - // MARK: from CertificationList - if let todoId = datas.todoId, let sortOption = datas.sortOption, let filter = datas.filter { + + case .certificationList(let title, let todoId, let sortOption, let filter): let todos = try await getMyCertifications(todoId: todoId, sortOption: sortOption, filter: filter) - certificationViewDatas.update { - $0.title = datas.title + $0.title = title $0.index = todos.firstIndex { $0.id == todoId } ?? 0 $0.todos = todos } @@ -96,7 +88,7 @@ extension CertificationViewModel { func readTodo(index: Int? = nil) async throws { let index = index ?? certificationViewDatas.value.index guard let todo = certificationViewDatas.value.todos[safe: index], - let _ = preCertificationViewDatas.value.memberId else { return } + case .ranking = preCertificationViewDatas.value else { return } try await challengeGroupsUseCase.readTodo(todo: todo) } diff --git a/dogether/Presentation/Features/CertificationList/CertificationListViewController.swift b/dogether/Presentation/Features/CertificationList/CertificationListViewController.swift index d2293064..e7f21250 100644 --- a/dogether/Presentation/Features/CertificationList/CertificationListViewController.swift +++ b/dogether/Presentation/Features/CertificationList/CertificationListViewController.swift @@ -72,7 +72,7 @@ extension CertificationListViewController: CertificationListPageDelegate { func selectCertificationAction(title: String, todo: TodoEntity) { let certificationViewController = CertificationViewController() - let preCertificationViewDatas = PreCertificationViewDatas( + let preCertificationViewDatas = PreCertificationViewDatas.certificationList( title: title, todoId: todo.id, sortOption: viewModel.sortViewDatas.value.options[viewModel.sortViewDatas.value.index], diff --git a/dogether/Presentation/Features/Main/MainViewController.swift b/dogether/Presentation/Features/Main/MainViewController.swift index e6d25c71..e768b94c 100644 --- a/dogether/Presentation/Features/Main/MainViewController.swift +++ b/dogether/Presentation/Features/Main/MainViewController.swift @@ -240,7 +240,7 @@ extension MainViewController: MainDelegate { let date = DateFormatterManager.formattedDate( viewModel.sheetViewDatas.value.dateOffset ).translateDateFormatForServer() - let preCertificationViewDatas = PreCertificationViewDatas( + let preCertificationViewDatas = PreCertificationViewDatas.main( title: "내 인증 정보", date: date, groupId: viewModel.currentGroup.id, diff --git a/dogether/Presentation/Features/Ranking/RankingViewController.swift b/dogether/Presentation/Features/Ranking/RankingViewController.swift index 8210730e..4022adf5 100644 --- a/dogether/Presentation/Features/Ranking/RankingViewController.swift +++ b/dogether/Presentation/Features/Ranking/RankingViewController.swift @@ -52,7 +52,7 @@ protocol RankingDelegate { extension RankingViewController: RankingDelegate { func goCertificationViewAction(rankingEntity: RankingEntity) { let certificationViewController = CertificationViewController() - let preCertificationViewDatas = PreCertificationViewDatas( + let preCertificationViewDatas = PreCertificationViewDatas.ranking( title: "\(rankingEntity.name)님의 인증 정보", groupId: viewModel.rankingViewDatas.value.groupId, memberId: rankingEntity.memberId