From 943356607b274e9854b9d9b383ab1be660ae9299 Mon Sep 17 00:00:00 2001 From: Yu Sung Date: Thu, 11 Sep 2025 14:39:11 +0900 Subject: [PATCH] =?UTF-8?q?feat(chat):=20Talk=20=ED=83=AD=EC=97=90=20?= =?UTF-8?q?=EC=8A=A4=ED=81=AC=EB=A1=A4=20=ED=8E=98=EC=9D=B4=EC=A7=80?= =?UTF-8?q?=EB=84=A4=EC=9D=B4=EC=85=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- SodaLive/Sources/Chat/Talk/TalkApi.swift | 9 ++- .../Sources/Chat/Talk/TalkRepository.swift | 4 +- SodaLive/Sources/Chat/Talk/TalkView.swift | 17 ++++- .../Sources/Chat/Talk/TalkViewModel.swift | 66 +++++++++++++++---- 4 files changed, 76 insertions(+), 20 deletions(-) diff --git a/SodaLive/Sources/Chat/Talk/TalkApi.swift b/SodaLive/Sources/Chat/Talk/TalkApi.swift index 4168bb5..42c44f9 100644 --- a/SodaLive/Sources/Chat/Talk/TalkApi.swift +++ b/SodaLive/Sources/Chat/Talk/TalkApi.swift @@ -9,7 +9,7 @@ import Foundation import Moya enum TalkApi { - case getTalkRooms + case getTalkRooms(page: Int) case createChatRoom(request: CreateChatRoomRequest) case enterChatRoom(roomId: Int, characterImageId: Int?) case sendMessage(roomId: Int, request: SendChatMessageRequest) @@ -89,8 +89,11 @@ extension TalkApi: TargetType { var task: Moya.Task { switch self { - case .getTalkRooms: - return .requestPlain + case .getTalkRooms(let page): + return .requestParameters( + parameters: ["page": page], + encoding: URLEncoding.queryString + ) case .createChatRoom(let request): return .requestJSONEncodable(request) diff --git a/SodaLive/Sources/Chat/Talk/TalkRepository.swift b/SodaLive/Sources/Chat/Talk/TalkRepository.swift index ab25a27..b02fd8d 100644 --- a/SodaLive/Sources/Chat/Talk/TalkRepository.swift +++ b/SodaLive/Sources/Chat/Talk/TalkRepository.swift @@ -13,7 +13,7 @@ import Moya class TalkRepository { private let api = MoyaProvider() - func getTalkRooms() -> AnyPublisher { - return api.requestPublisher(.getTalkRooms) + func getTalkRooms(page: Int) -> AnyPublisher { + return api.requestPublisher(.getTalkRooms(page: page)) } } diff --git a/SodaLive/Sources/Chat/Talk/TalkView.swift b/SodaLive/Sources/Chat/Talk/TalkView.swift index 8008458..eda9d85 100644 --- a/SodaLive/Sources/Chat/Talk/TalkView.swift +++ b/SodaLive/Sources/Chat/Talk/TalkView.swift @@ -19,9 +19,9 @@ struct TalkView: View { .foregroundColor(.white) } else { ScrollView(.vertical, showsIndicators: false) { - VStack(spacing: 24) { - ForEach(0..() + private var page: Int = 0 + private var isLastPage: Bool = false + private var isLoadingPage: Bool = false + // MARK: - API func getTalkRooms() { - isLoading = true + // 초기 로드: 페이지 리셋 후 0페이지 요청 + page = 0 + isLastPage = false + talkRooms.removeAll() + fetch(page: page, isInitial: true) + } + + func loadNextPage() { + guard !isLastPage, !isLoadingPage else { return } + page += 1 + fetch(page: page, isInitial: false) + } + + private func fetch(page: Int, isInitial: Bool) { + if isInitial { + isLoading = true + } else { + isLoadingMore = true + } + isLoadingPage = true - repository.getTalkRooms() - .sink { result in - switch result { - case .finished: - DEBUG_LOG("finish") - case .failure(let error): - ERROR_LOG(error.localizedDescription) + repository.getTalkRooms(page: page) + .sink { [weak self] completion in + guard let self = self else { return } + DispatchQueue.main.async { + self.isLoading = false + self.isLoadingMore = false + self.isLoadingPage = false } - } receiveValue: { response in + + if case let .failure(error) = completion { + ERROR_LOG(error.localizedDescription) + DispatchQueue.main.async { + self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다." + self.isShowPopup = true + } + } else { + DEBUG_LOG("fetch page=\(page) finished") + } + } receiveValue: { [weak self] response in + guard let self = self else { return } let responseData = response.data - self.isLoading = false do { let jsonDecoder = JSONDecoder() let decoded = try jsonDecoder.decode(ApiResponse<[TalkRoom]>.self, from: responseData) if let data = decoded.data, decoded.success { - self.talkRooms = data + if data.isEmpty { + // 빈 데이터면 마지막 페이지로 인식 + self.isLastPage = true + return + } + if isInitial || self.talkRooms.isEmpty && page == 0 { + self.talkRooms = data + } else { + self.talkRooms.append(contentsOf: data) + } } else { if let message = decoded.message { self.errorMessage = message } else { self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다." } - self.isShowPopup = true } } catch {