import Foundation import Combine final class PushNotificationListViewModel: ObservableObject { private let repository = PushNotificationRepository() private var subscription = Set() @Published var errorMessage = "" @Published var isShowPopup = false @Published var isLoading = false @Published var categories: [String] = [] @Published var selectedCategory: String = "" { didSet { if oldValue != selectedCategory { let trimmed = selectedCategory.trimmingCharacters(in: .whitespacesAndNewlines) if !trimmed.isEmpty { getPushNotificationList(reset: true) } } } } @Published var items: [PushNotificationListItem] = [] @Published var totalCount: Int = 0 private var page: Int = 1 private let pageSize: Int = 20 private var isLast: Bool = false func initialize() { getPushNotificationCategories { [weak self] in guard let self = self else { return } let trimmed = self.selectedCategory.trimmingCharacters(in: .whitespacesAndNewlines) if trimmed.isEmpty { self.getPushNotificationList(reset: true) } } } func getPushNotificationList(reset: Bool = false) { if reset { page = 1 isLast = false totalCount = 0 items.removeAll() } if isLoading || isLast { return } isLoading = true repository.getPushNotificationList( page: page, size: pageSize, category: requestCategory ) .sink { [weak self] result in guard let self = self else { return } switch result { case .finished: DEBUG_LOG("finish") case .failure(let error): self.isLoading = false ERROR_LOG(error.localizedDescription) self.errorMessage = I18n.Common.commonError self.isShowPopup = true } } receiveValue: { [weak self] response in guard let self = self else { return } self.isLoading = false do { let decoded = try JSONDecoder().decode( ApiResponse.self, from: response.data ) if let data = decoded.data, decoded.success { self.totalCount = data.totalCount if data.items.isEmpty { self.isLast = true return } self.items.append(contentsOf: data.items) let totalPages = Int(ceil(Double(max(data.totalCount, 0)) / Double(self.pageSize))) let isReachedByPage = totalPages > 0 ? self.page >= totalPages : true let isReachedByCount = data.totalCount > 0 && self.items.count >= data.totalCount if isReachedByPage || isReachedByCount { self.isLast = true } else { self.page += 1 } } else { self.errorMessage = decoded.message ?? I18n.Common.commonError self.isShowPopup = true } } catch { self.errorMessage = I18n.Common.commonError self.isShowPopup = true } } .store(in: &subscription) } func onTapItem(_ item: PushNotificationListItem) { guard let deepLink = item.deepLink?.trimmingCharacters(in: .whitespacesAndNewlines), !deepLink.isEmpty else { return } _ = AppDeepLinkHandler.handle(urlString: deepLink, source: .notificationList) } private var requestCategory: String? { let trimmed = selectedCategory.trimmingCharacters(in: .whitespacesAndNewlines) return trimmed.isEmpty ? nil : trimmed } private func getPushNotificationCategories(completion: (() -> Void)? = nil) { repository.getPushNotificationCategories() .sink { result in switch result { case .finished: DEBUG_LOG("finish") case .failure(let error): ERROR_LOG(error.localizedDescription) } completion?() } receiveValue: { [weak self] response in guard let self = self else { return } do { let decoded = try JSONDecoder().decode( ApiResponse.self, from: response.data ) if let data = decoded.data, decoded.success { var filtered: [String] = [] for category in data.categories { let trimmed = category.trimmingCharacters(in: .whitespacesAndNewlines) if trimmed.isEmpty || filtered.contains(trimmed) { continue } filtered.append(trimmed) } self.categories = filtered self.selectedCategory = filtered.first ?? "" } else { self.errorMessage = decoded.message ?? I18n.Common.commonError self.isShowPopup = true } } catch { self.errorMessage = I18n.Common.commonError self.isShowPopup = true } } .store(in: &subscription) } }