import Foundation import Combine final class MainChatViewModel: ObservableObject { private let repository = MainChatRepository() private var subscription = Set() private var latestRequestId = 0 @Published var rooms = [MainChatRoomItem]() @Published var selectedFilter: MainChatFilter = .all @Published var isLoading = false @Published var isLoadingNextPage = false @Published var hasMore = false @Published var nextCursor: String? @Published var hasLoaded = false @Published var errorMessage = "" @Published var isShowPopup = false var shouldShowEmptyState: Bool { hasLoaded && isLoading == false && rooms.isEmpty } func fetchFirstPageIfNeeded() { guard hasLoaded == false else { return } fetchFirstPage(filter: selectedFilter) } func fetchFirstPage(filter: MainChatFilter) { selectedFilter = filter clearRooms() fetchChatRooms(filter: filter, cursor: nil, isNextPage: false) } func applyFilter(_ filter: MainChatFilter) { guard selectedFilter != filter else { return } fetchFirstPage(filter: filter) } func fetchNextPageIfNeeded(currentItem: MainChatRoomItem) { guard rooms.last?.roomId == currentItem.roomId else { return } guard hasMore, let nextCursor, isLoading == false, isLoadingNextPage == false else { return } fetchChatRooms(filter: selectedFilter, cursor: nextCursor, isNextPage: true) } private func fetchChatRooms(filter: MainChatFilter, cursor: String?, isNextPage: Bool) { latestRequestId += 1 let requestId = latestRequestId if isNextPage { isLoadingNextPage = true } else { isLoading = true } repository.getChatRooms(filter: filter.queryValue, cursor: cursor) .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.MainChat.loadFailedMessage self.isShowPopup = true } } catch { ERROR_LOG(error.localizedDescription) self.errorMessage = I18n.MainChat.loadFailedMessage self.isShowPopup = true } self.hasLoaded = true self.isLoading = false self.isLoadingNextPage = false } .store(in: &subscription) } private func applySuccessState(_ data: MainChatRoomsResponse, isNextPage: Bool) { hasMore = data.hasMore nextCursor = data.nextCursor if isNextPage { rooms.append(contentsOf: data.rooms) } else { rooms = data.rooms } } private func applyFailureState() { errorMessage = I18n.MainChat.loadFailedMessage isShowPopup = true hasLoaded = true isLoading = false isLoadingNextPage = false } private func clearRooms() { rooms = [] hasMore = false nextCursor = nil hasLoaded = false } }