99 lines
2.8 KiB
Swift
99 lines
2.8 KiB
Swift
import Foundation
|
||
|
||
@Observable
|
||
final class BrowseViewModel {
|
||
var items: [ContentItem] = []
|
||
var category: ContentCategory = .movie
|
||
var filter = FilterState()
|
||
var currentPage = 1
|
||
var totalPages = 1
|
||
var isLoading = false
|
||
var isLoadingMore = false
|
||
var error: String?
|
||
|
||
private var cacheKey: String {
|
||
"\(category.rawValue)_\(filter.sort)_\(filter.genre)_\(filter.region)_\(filter.year)"
|
||
}
|
||
|
||
var hasData: Bool {
|
||
!items.isEmpty
|
||
}
|
||
|
||
@MainActor
|
||
func loadContentIfNeeded() async {
|
||
if hasData && !ContentCache.shared.isExpired(key: cacheKey) {
|
||
return
|
||
}
|
||
await loadContent()
|
||
}
|
||
|
||
@MainActor
|
||
func loadContent() async {
|
||
isLoading = true
|
||
error = nil
|
||
currentPage = 1
|
||
totalPages = 1
|
||
items = []
|
||
|
||
do {
|
||
let html = try await APIClient.shared.fetchCategoryPage(
|
||
category: category,
|
||
page: 1,
|
||
filter: filter
|
||
)
|
||
let newItems = try HTMLParser.parseContentList(html: html, defaultCategory: category)
|
||
let pagination = try HTMLParser.parsePagination(html: html)
|
||
|
||
self.items = newItems
|
||
self.currentPage = pagination.current
|
||
self.totalPages = pagination.total
|
||
|
||
ContentCache.shared.markFresh(key: cacheKey)
|
||
} catch is CancellationError {
|
||
} catch let error as URLError where error.code == .cancelled {
|
||
} catch {
|
||
self.error = error.localizedDescription
|
||
}
|
||
|
||
isLoading = false
|
||
}
|
||
|
||
@MainActor
|
||
func loadMore() async {
|
||
guard !isLoadingMore, !isLoading, currentPage < totalPages else { return }
|
||
isLoadingMore = true
|
||
|
||
let nextPage = currentPage + 1
|
||
do {
|
||
let html = try await APIClient.shared.fetchCategoryPage(
|
||
category: category,
|
||
page: nextPage,
|
||
filter: filter
|
||
)
|
||
let newItems = try HTMLParser.parseContentList(html: html, defaultCategory: category)
|
||
let pagination = try HTMLParser.parsePagination(html: html)
|
||
|
||
self.items.append(contentsOf: newItems)
|
||
self.currentPage = pagination.current
|
||
self.totalPages = pagination.total
|
||
} catch is CancellationError {
|
||
} catch let error as URLError where error.code == .cancelled {
|
||
} catch {
|
||
// 加载更多失败不显示全局错误,允许重试
|
||
}
|
||
|
||
isLoadingMore = false
|
||
}
|
||
|
||
func changeCategory(_ newCategory: ContentCategory) async {
|
||
category = newCategory
|
||
filter = FilterState()
|
||
await loadContentIfNeeded()
|
||
}
|
||
|
||
func applyFilter(_ newFilter: FilterState) async {
|
||
filter = newFilter
|
||
await loadContent()
|
||
}
|
||
}
|