import Foundation import Combine final class CreatorChannelDonationViewModel: ObservableObject { private let repository = CreatorChannelDonationRepository() private var subscription = Set() private let pageSize = 20 private var latestRequestId = 0 @Published var isLoading = false @Published var isLoadingNextPage = false @Published var response: CreatorChannelDonationTabResponse? @Published var rankings = [MemberDonationRankingResponse]() @Published var donations = [CreatorChannelDonationResponse]() @Published var page = 0 @Published var size = 20 @Published var hasNext = false @Published var hasLoaded = false @Published var errorMessage = "" @Published var isShowPopup = false var donationCount: Int { response?.donationCount ?? 0 } func fetchFirstPage(creatorId: Int) { fetchDonations(creatorId: creatorId, page: 0, isNextPage: false) } func fetchNextPageIfNeeded(creatorId: Int, currentItem: CreatorChannelDonationResponse) { guard let lastDonation = donations.last else { return } guard lastDonation.createdAtUtc == currentItem.createdAtUtc, lastDonation.nickname == currentItem.nickname, lastDonation.can == currentItem.can else { return } guard hasNext, isLoadingNextPage == false, isLoading == false else { return } fetchDonations(creatorId: creatorId, page: page + 1, isNextPage: true) } private func fetchDonations(creatorId: Int, page requestPage: Int, isNextPage: Bool) { latestRequestId += 1 let requestId = latestRequestId if isNextPage { isLoadingNextPage = true } else { isLoading = true } repository.getDonations(creatorId: creatorId, page: requestPage, size: pageSize) .sink { [weak self] result in guard let self else { return } guard requestId == self.latestRequestId else { return } switch result { case .finished: DEBUG_LOG("finish") self.isLoading = false self.isLoadingNextPage = false case .failure(let error): ERROR_LOG(error.localizedDescription) self.applyFailureState() } } receiveValue: { [weak self] response in guard let self else { return } guard requestId == self.latestRequestId else { return } do { let jsonDecoder = JSONDecoder() let decoded = try jsonDecoder.decode(ApiResponse.self, from: response.data) if let data = decoded.data, decoded.success { self.applySuccessState(data, isNextPage: isNextPage) self.hasLoaded = true } else { self.errorMessage = decoded.message ?? I18n.Common.commonError self.isShowPopup = true } } catch { ERROR_LOG(error.localizedDescription) self.errorMessage = I18n.Common.commonError self.isShowPopup = true } self.isLoading = false self.isLoadingNextPage = false } .store(in: &subscription) } private func applySuccessState(_ data: CreatorChannelDonationTabResponse, isNextPage: Bool) { response = data rankings = data.rankings page = data.page size = data.size hasNext = data.hasNext if isNextPage { donations.append(contentsOf: data.donations) } else { donations = data.donations } } private func applyFailureState() { errorMessage = I18n.Common.commonError isShowPopup = true isLoading = false isLoadingNextPage = false } }