dimensionhub/dimensionhub/Views/IndexView.swift
2025-02-24 21:21:03 +08:00

351 lines
12 KiB
Swift

//
// ContentView.swift
// dimensionhub
//
// Created by on 2025/2/18.
//
import SwiftUI
import SwiftData
import Observation
@Observable
final class IndexModel {
struct DramaItem: Codable {
struct Episode: Codable, Identifiable {
let id = UUID().uuidString
let name: String
let thumb: String
let num_name: String
let play: String
enum CodingKeys: String, CodingKey {
case name, thumb, num_name, play
}
}
let id: Int32
let title: String
let episodes: [Episode]
}
struct UpdateDramaGroup: Codable {
struct Item: Codable {
let id: Int
let name: String
let time: Int
let thumb: String
let status: String
}
let group_id: String
let group_name: String
let items: [Item]
}
struct IndexResponse: Codable {
let update_dramas: [UpdateDramaGroup]
let dramas: [DramaItem]
}
struct UpdateDramaShowItem {
enum Element {
case group(UpdateDramaGroup)
case item(UpdateDramaGroup.Item)
}
let id = UUID().uuidString
let element: Element
init(element: Element) {
self.element = element
}
}
var dramas: [DramaItem]
var showUpdateDramas: [UpdateDramaShowItem]
var selectedDate: String
init() {
self.dramas = []
self.showUpdateDramas = []
self.selectedDate = ""
}
@MainActor
func loadData() async {
let response = await API.getIndexData(userId: 1, as: IndexResponse.self)
switch response {
case .error(let code, let message):
print(code)
print(message)
case .result(let result):
self.dramas = result.dramas
self.showUpdateDramas = groupUpdateDramas(updateDramaGroups: result.update_dramas)
}
}
@MainActor
func loadMoreUpdateDramas(userId: Int, mode: API.LoadMode, id: Int) async {
let response = await API.loadMoreUpdateDramas(userId: userId, mode: mode, id: id, as: [UpdateDramaGroup].self)
if case let .result(groups) = response {
let showItems = groupUpdateDramas(updateDramaGroups: groups)
self.showUpdateDramas.append(contentsOf: showItems)
}
}
private func groupUpdateDramas(updateDramaGroups: [UpdateDramaGroup]) -> [UpdateDramaShowItem] {
var updateItems: [UpdateDramaShowItem] = []
updateDramaGroups.forEach { group in
updateItems.append(.init(element: .group(group)))
group.items.forEach { item in
updateItems.append(.init(element: .item(item)))
}
}
return updateItems
}
}
struct IndexView: View {
@Environment(\.modelContext) private var modelContext
//@Query private var items: [Item]
@Query private var userModel: [UserModel]
@State var indexModel = IndexModel()
@State var isLoading: Bool = false
//
@State private var selectGroupId: String = ""
@State private var showDateNavPopover: Bool = false
var body: some View {
VStack(alignment: .center) {
HStack(alignment: .center) {
Color.clear
.overlay {
Text("亚次元")
.font(.system(size: 16))
.padding([.top, .bottom], 5)
}
}
.frame(height: 50)
.background(Color(hex: "#F2F2F2"), ignoresSafeAreaEdges: .bottom)
HStack(alignment: .center) {
Spacer()
Text("番剧补完计划")
.font(.system(size: 24))
.foregroundColor(Color(hex: "#999999"))
}
ForEach(indexModel.dramas, id: \.id) { drama in
DramaCellView(dramaItem: drama)
}
//
ScrollView(.vertical, showsIndicators: false) {
VStack(alignment: .center, spacing: 10) {
ForEach(indexModel.showUpdateDramas, id: \.id) { drama in
switch drama.element {
case .group(let group):
HStack {
Spacer()
Text(group.group_name)
.font(.system(size: 18))
.fontWeight(.regular)
.onTapGesture {
selectGroupId = group.group_id
print("current group_id: \(self.selectGroupId)")
indexModel.selectedDate = group.group_id
showDateNavPopover = true
}
}
case .item(let item):
AsyncImage(url: URL(string: item.thumb)) { image in
image.resizable()
} placeholder: {
ProgressView()
}
.frame(width: 370, height: 180)
.overlay {
HStack {
VStack(alignment: .leading, spacing: 8) {
Text(item.name)
.font(.system(size: 16))
.foregroundColor(Color(hex: "#333333"))
.lineLimit(1)
Text(item.status)
.font(.system(size: 12))
.foregroundColor(Color(hex: "#333333"))
.lineLimit(1)
Spacer()
}
Spacer()
}
.padding([.top, .leading], 10)
}
}
}
}
.onChange(of: indexModel.selectedDate) { oldValue, newValue in
print("old date: \(oldValue), new value: \(newValue)")
}
Rectangle()
.frame(height: 0)
.background(GeometryReader {
geometry in
Color.clear.onChange(of: geometry.frame(in: .global).minY) {_, offset in
let frame = geometry.frame(in: .global)
let screenBounds = UIScreen.main.bounds
if screenBounds.height - frame.minY > 50 && !isLoading {
Task {
self.isLoading = true
await self.indexModel.loadMoreUpdateDramas(userId: 1, mode: .next, id: 1234)
self.isLoading = false
}
}
}
})
if self.isLoading {
ProgressView()
}
}
.popover(isPresented: $showDateNavPopover) {
DateNavView(selectGroupId: self.$selectGroupId, showDateNavPopover: $showDateNavPopover) { selectedDate in
print("new selected date: " + selectedDate)
}
}
}
.frame(width: 370)
.ignoresSafeArea(edges: .bottom)
.task {
//await self.indexModel.loadData()
print(userModel)
}
}
private func addItem() {
// withAnimation {
// let newItem = Item(timestamp: Date())
// modelContext.insert(newItem)
// }
}
private func deleteItems(offsets: IndexSet) {
// withAnimation {
// for index in offsets {
// modelContext.delete(items[index])
// }
// }
}
}
extension IndexView {
//
struct DramaCellView: View {
let dramaItem: IndexModel.DramaItem
var body: some View {
VStack(alignment: .leading) {
NavigationLink(destination: DetailView()) {
Text(dramaItem.title)
.font(.system(size: 20))
.foregroundColor(Color(hex: "#333333"))
}
ScrollView(.horizontal, showsIndicators: false) {
HStack(alignment: .center, spacing: 5) {
ForEach(dramaItem.episodes) { item in
VStack(alignment: .center) {
GeometryReader { geometry in
AsyncImage(url: URL(string: item.thumb)) { image in
image.resizable()
} placeholder: {
ProgressView()
}
.frame(width: geometry.frame(in: .local).width, height: 80)
.overlay {
VStack {
HStack(alignment: .center) {
Text(item.num_name)
.font(.system(size: 12))
.foregroundColor(Color(hex: "#333333"))
Spacer()
}
Spacer()
}
.padding([.top, .leading], 5)
}
}
Text(item.name)
.font(.system(size: 12))
.foregroundColor(Color(hex: "#333333"))
.lineLimit(1)
}
.frame(width: 120, height: 100)
}
}
}
}
}
}
}
extension IndexView {
struct UpdateDramaCellView: View {
let showItem: IndexModel.UpdateDramaShowItem
@Binding var showDateNavPopover: Bool
var body: some View {
switch showItem.element {
case .group(let group):
Text(group.group_name)
.onTapGesture {
showDateNavPopover = true
}
case .item(let item):
VStack(alignment: .center) {
AsyncImage(url: URL(string: item.thumb)) { image in
image.resizable()
} placeholder: {
ProgressView()
}
.frame(width: 80, height: 80)
.overlay {
VStack(alignment: .leading) {
Text(item.name)
.lineLimit(1)
Text(item.status)
.lineLimit(1)
}
}
}
.frame(width: 100, height: 120)
}
}
}
}
#Preview {
IndexView()
.modelContainer(for: Item.self, inMemory: true)
}