import Foundation import Combine final class GiftBoxListViewModel: ObservableObject { private let repository = GiftRepository() private let pageSize = 20 private var subscription: AnyCancellable? private var page = 0 private var isRequestInFlight = false @Published private(set) var items = [GiftListItemResponse]() @Published private(set) var totalCount = 0 @Published private(set) var hasNext = false @Published private(set) var hasLoaded = false @Published private(set) var isLoading = false @Published private(set) var isLoadingMore = false @Published private(set) var isRefreshing = false @Published private(set) var errorMessage = "" @Published var isShowPopup = false func fetchInitialPage() { guard !hasLoaded else { return } fetch(page: 0) } func fetchNextPage(currentItem: GiftListItemResponse) { guard hasLoaded, hasNext, currentItem.applicationNo == items.last?.applicationNo else { return } fetch(page: page + 1) } @MainActor func refresh() async { await withCheckedContinuation { continuation in fetch(page: 0, isRefresh: true) { continuation.resume() } } } private func fetch(page requestedPage: Int, isRefresh: Bool = false, completion: (() -> Void)? = nil) { guard !isRequestInFlight else { completion?() return } isRequestInFlight = true isLoading = requestedPage == 0 && !isRefresh isLoadingMore = requestedPage > 0 isRefreshing = isRefresh errorMessage = "" isShowPopup = false subscription = repository.getGifts(page: requestedPage, size: pageSize) .receive(on: DispatchQueue.main) .sink { [weak self] result in defer { completion?() } guard let self else { return } if case .failure(let error) = result { self.showError(data: error.response?.data) } self.isLoading = false self.isLoadingMore = false self.isRefreshing = false self.isRequestInFlight = false } receiveValue: { [weak self] response in guard let self else { return } do { let decoded = try JSONDecoder().decode(ApiResponse.self, from: response.data) guard decoded.success, let data = decoded.data else { self.showError(data: response.data) return } if requestedPage == 0 { self.items = data.items } else { self.items.append(contentsOf: data.items) } self.page = data.page self.totalCount = data.totalCount self.hasNext = data.hasNext self.hasLoaded = true } catch { self.showError(data: response.data) } } } private func showError(data: Data?) { struct ErrorResponse: Decodable { let message: String? } let decoded = data.flatMap { try? JSONDecoder().decode(ErrorResponse.self, from: $0) } let message = decoded?.message?.trimmingCharacters(in: .whitespacesAndNewlines) ?? "" errorMessage = message.isEmpty ? I18n.Common.commonError : message isShowPopup = true } }