diff --git a/Projects/Core/PPACData/Sources/DTO/MemeReactionRequestDTO.swift b/Projects/Core/PPACData/Sources/DTO/MemeReactionRequestDTO.swift new file mode 100644 index 0000000..28ceaef --- /dev/null +++ b/Projects/Core/PPACData/Sources/DTO/MemeReactionRequestDTO.swift @@ -0,0 +1,24 @@ +// +// MemeReactionRequestDTO.swift +// PPACData +// +// Created by kimchansoo on 10/1/24. +// + +import Foundation + +public struct MemeReactionRequestDTO: Codable { + public let count: Int + + public init(count: Int) { + self.count = count + } +} + +public struct MemeReactionResponseDTO: Codable { + public let count: Int + + public init(count: Int) { + self.count = count + } +} diff --git a/Projects/Core/PPACData/Sources/Endpoint/MemeEndpoint.swift b/Projects/Core/PPACData/Sources/Endpoint/MemeEndpoint.swift index a845369..a39dfe5 100644 --- a/Projects/Core/PPACData/Sources/Endpoint/MemeEndpoint.swift +++ b/Projects/Core/PPACData/Sources/Endpoint/MemeEndpoint.swift @@ -18,7 +18,7 @@ public enum MemeEndpoint: Requestable { case deleteBookmark(memeId: String) case share(memeId: String) case watch(memeId: String, type: String) - case reaction(memeId: String) + case reaction(memeId: String, count: Int) public var httpMethod: PPACNetwork.HTTPMethod { switch self { @@ -61,8 +61,8 @@ public enum MemeEndpoint: Requestable { return "/meme/\(memeId)/share" case .watch(let memeId, let type): return "/meme/\(memeId)/watch/\(type)" - case .reaction(let memeId): - return "/meme/\(memeId)/reaction" + case .reaction(let memeId, _): + return "meme/\(memeId)/reaction" } } @@ -85,8 +85,8 @@ public enum MemeEndpoint: Requestable { return nil case .watch: return nil - case .reaction: - return nil + case let .reaction(memeId, count): + return .body(MemeReactionRequestDTO(count: count)) case .meme(memeId: _): return nil } diff --git a/Projects/Core/PPACData/Sources/Repository/MemeRepositoryImpl.swift b/Projects/Core/PPACData/Sources/Repository/MemeRepositoryImpl.swift index c55da72..4fe58ae 100644 --- a/Projects/Core/PPACData/Sources/Repository/MemeRepositoryImpl.swift +++ b/Projects/Core/PPACData/Sources/Repository/MemeRepositoryImpl.swift @@ -114,12 +114,12 @@ public class MemeRepositoryImpl: MemeRepository { } } - public func reactToMeme(memeId: String) async throws { - let endpoint = MemeEndpoint.reaction(memeId: memeId) - let result = await networkservice.request(endpoint, dataType: BaseDTO.self) + public func reactToMeme(memeId: String, count: Int) async throws -> Int { + let endpoint = MemeEndpoint.reaction(memeId: memeId, count: count) + let result = await networkservice.request(endpoint, dataType: BaseDTO.self) switch result { - case .success: - return + case .success(let count): + return count.data?.count ?? 0 case .failure(let failure): throw failure } diff --git a/Projects/Core/PPACDomain/Sources/Repository/MemeRepository.swift b/Projects/Core/PPACDomain/Sources/Repository/MemeRepository.swift index a3a7eef..a8b4eed 100644 --- a/Projects/Core/PPACDomain/Sources/Repository/MemeRepository.swift +++ b/Projects/Core/PPACDomain/Sources/Repository/MemeRepository.swift @@ -19,7 +19,7 @@ public protocol MemeRepository { func deleteBookmarkMeme(memeId: String) async throws func shareMeme(memeId: String) async throws func watchMeme(memeId: String, type: String) async throws - func reactToMeme(memeId: String) async throws + func reactToMeme(memeId: String, count: Int) async throws -> Int func registerMeme(formData: FormData, title: String, source: String, keywordIds: [String]) async throws } diff --git a/Projects/Core/PPACDomain/Sources/UseCase/Meme/ReactToMemeUseCase.swift b/Projects/Core/PPACDomain/Sources/UseCase/Meme/ReactToMemeUseCase.swift index 79858bd..9208dff 100644 --- a/Projects/Core/PPACDomain/Sources/UseCase/Meme/ReactToMemeUseCase.swift +++ b/Projects/Core/PPACDomain/Sources/UseCase/Meme/ReactToMemeUseCase.swift @@ -10,7 +10,7 @@ import Foundation import PPACModels public protocol ReactToMemeUseCase { - func execute(memeId: String) async throws + func execute(memeId: String, count: Int) async throws -> Int } public class ReactToMemeUseCaseImpl: ReactToMemeUseCase { @@ -20,7 +20,7 @@ public class ReactToMemeUseCaseImpl: ReactToMemeUseCase { self.repository = repository } - public func execute(memeId: String) async throws { - try await repository.reactToMeme(memeId: memeId) + public func execute(memeId: String, count: Int) async throws -> Int { + try await repository.reactToMeme(memeId: memeId, count: count) } } diff --git a/Projects/Features/MemeDetail/Sources/MemeDetailViewModel.swift b/Projects/Features/MemeDetail/Sources/MemeDetailViewModel.swift index 6248497..8397504 100644 --- a/Projects/Features/MemeDetail/Sources/MemeDetailViewModel.swift +++ b/Projects/Features/MemeDetail/Sources/MemeDetailViewModel.swift @@ -47,6 +47,9 @@ public final class MemeDetailViewModel: ViewModelType, ObservableObject { private let watchMemeUseCase: WatchMemeUseCase private let reactToMemeUseCase: ReactToMemeUseCase + private var reactionCount = 0 + private var reactionTask: Task? + // MARK: - Initializers public init( @@ -67,7 +70,8 @@ public final class MemeDetailViewModel: ViewModelType, ObservableObject { } deinit { - print("memeviewmodel deinit") + print("memeviewmodel deinit") + reactionTask?.cancel() } // MARK: - Methods @@ -110,19 +114,50 @@ public final class MemeDetailViewModel: ViewModelType, ObservableObject { } private extension MemeDetailViewModel { - + @MainActor - func postReaction() async { - do { - try await reactToMemeUseCase.execute(memeId: state.meme.id) + func postReaction() { + reactionCount += 1 self.state.meme.reaction += 1 self.state.meme.isReaction = true self.logMemeDetail(event: .reaction) - print("reaction success") - } catch { - // TODO: - 에러처리 - print("Failed to post reaction: \(error)") - } + + reactionTask?.cancel() + + reactionTask = Task { [weak self] in + guard let self = self else { return } + do { + try await Task.sleep(nanoseconds: 3 * 1_000_000_000) + await self.sendReactions() + } catch { + if Task.isCancelled { + // 태스크가 취소되었으므로 아무 작업도 하지 않음 + return + } else { + print("Task error: \(error)") + } + } + } + } + + + @MainActor + func sendReactions() async { + let count = reactionCount + guard count > 0 else { + // 전송할 리액션이 없음 + return + } + reactionCount = 0 + do { + let count = try await reactToMemeUseCase.execute(memeId: state.meme.id, count: count) + print("currentMeme count: \(self.state.meme.reaction)") + print("new count: \(count)") + self.state.meme.reaction = count + print("Reactions sent successfully with count: \(count)") + } catch { + print("Failed to send reactions: \(error)") + } } @MainActor