Files
suixinkan_uikit/suixinkan/Features/MessageCenter/ViewModels/MessageCenterViewModels.swift

159 lines
4.6 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// 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?()
}
}