import Foundation import Combine final class CreatorChannelSeriesViewModel: ObservableObject { private let repository = CreatorChannelSeriesRepository() private var subscription = Set() private let pageSize = 20 private var latestRequestId = 0 @Published var isLoading = false @Published var isLoadingNextPage = false @Published var response: CreatorChannelSeriesTabResponse? @Published var series = [CreatorChannelSeriesResponse]() @Published var selectedSort: ContentSort = .latest @Published var page = 0 @Published var size = 20 @Published var hasNext = false @Published var hasLoaded = false @Published var errorMessage = "" @Published var isShowPopup = false func fetchFirstPage(creatorId: Int) { fetchSeries(creatorId: creatorId, page: 0, isNextPage: false) } func fetchNextPageIfNeeded(creatorId: Int, currentItem: CreatorChannelSeriesResponse) { guard series.last?.seriesId == currentItem.seriesId else { return } guard hasNext, isLoadingNextPage == false, isLoading == false else { return } fetchSeries(creatorId: creatorId, page: page + 1, isNextPage: true) } func selectSort(_ sort: ContentSort, creatorId: Int) { guard selectedSort != sort else { return } selectedSort = sort fetchFirstPage(creatorId: creatorId) } private func fetchSeries(creatorId: Int, page requestPage: Int, isNextPage: Bool) { latestRequestId += 1 let requestId = latestRequestId if isNextPage { isLoadingNextPage = true } else { isLoading = true } repository.getSeries(creatorId: creatorId, page: requestPage, size: pageSize, sort: selectedSort) .sink { [weak self] result in guard let self else { return } guard requestId == self.latestRequestId else { return } switch result { case .finished: DEBUG_LOG("finish") 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) } 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.hasLoaded = true self.isLoading = false self.isLoadingNextPage = false } .store(in: &subscription) } private func applySuccessState(_ data: CreatorChannelSeriesTabResponse, isNextPage: Bool) { response = data selectedSort = data.sort page = data.page size = data.size hasNext = data.hasNext if isNextPage { series.append(contentsOf: data.series) } else { series = data.series } } private func applyFailureState() { errorMessage = I18n.Common.commonError isShowPopup = true hasLoaded = true isLoading = false isLoadingNextPage = false } }