// // MessageCenterViewModels.swift // suixinkan // import Foundation /// 消息中心列表页 ViewModel,负责分页、刷新和未读点击处理。 final class MessageCenterViewModel { private(set) var items: [MessageItem] = [] private(set) var isLoading = false private(set) var isRefreshing = false private(set) var isLoadingMore = false private(set) var canLoadMore = false var onStateChange: (() -> Void)? var onShowMessage: ((String) -> Void)? var onOpenDetail: ((MessageItem) -> Void)? private let pageSize = 20 private var lastId = 0 /// 首次加载消息列表。 func loadInitial(api: any MessageCenterServing) async { await load(reset: true, showLoading: true, api: api) } /// 下拉刷新消息列表。 func refresh(api: any MessageCenterServing) async { guard !isLoading else { return } isRefreshing = true notifyStateChange() await load(reset: true, showLoading: false, api: api) } /// 滚动到底部附近时加载更多。 func loadMoreIfNeeded(lastVisibleIndex: Int, api: any MessageCenterServing) async { guard lastVisibleIndex >= items.count - 4 else { return } await load(reset: false, showLoading: false, api: api) } /// 点击消息:未读先标记已读,成功后进入详情;已读直接进入详情。 func selectMessage(id: Int, api: any MessageCenterServing) async { guard let item = items.first(where: { $0.id == id }) else { return } if item.isRead { onOpenDetail?(item) return } isLoading = true notifyStateChange() defer { isLoading = false notifyStateChange() } do { try await api.markAsRead(messageId: id) let readItem = item.markedRead() items = items.map { $0.id == id ? readItem : $0 } onOpenDetail?(readItem) } catch is CancellationError { return } catch { onShowMessage?("标记已读失败") } } /// 删除后从本地列表移除消息。 func removeMessage(id: Int) { items.removeAll { $0.id == id } notifyStateChange() } private func load(reset: Bool, showLoading: Bool, api: any MessageCenterServing) async { if reset { lastId = 0 canLoadMore = false } else { guard canLoadMore, !isLoading, !isLoadingMore else { return } isLoadingMore = true } isLoading = showLoading notifyStateChange() defer { isLoading = false isRefreshing = false isLoadingMore = false notifyStateChange() } do { let response = try await api.list(lastId: reset ? 0 : lastId, limit: pageSize, unread: 0) lastId = response.lastId items = reset ? response.items : items + response.items canLoadMore = response.hasMore && response.lastId > 0 } catch is CancellationError { return } catch { if reset { items = [] lastId = 0 canLoadMore = false } onShowMessage?(error.localizedDescription) } } private func notifyStateChange() { onStateChange?() } } /// 消息详情页 ViewModel,负责删除当前消息。 final class MessageDetailViewModel { private(set) var message: MessageItem private(set) var isDeleting = false var onStateChange: (() -> Void)? var onShowMessage: ((String) -> Void)? var onDeleted: ((Int) -> Void)? /// 初始化消息详情 ViewModel。 init(message: MessageItem) { self.message = message } /// 删除当前消息。 func delete(api: any MessageCenterServing) async { guard message.id > 0 else { onShowMessage?("消息ID无效") return } isDeleting = true notifyStateChange() defer { isDeleting = false notifyStateChange() } do { try await api.delete(messageId: message.id) onShowMessage?("删除成功") onDeleted?(message.id) } catch is CancellationError { return } catch { let detail = error.localizedDescription.trimmingCharacters(in: .whitespacesAndNewlines) onShowMessage?("删除失败: \(detail.isEmpty ? "请稍后重试" : detail)") } } private func notifyStateChange() { onStateChange?() } }