add favor view

This commit is contained in:
anlicheng 2025-03-18 11:29:03 +08:00
parent cfcf59502d
commit d8dae2fa3c
3 changed files with 249 additions and 4 deletions

View File

@ -36,6 +36,7 @@ struct API {
// //
static let baseUrl = "https://dimensionhub.s5s8.com" static let baseUrl = "https://dimensionhub.s5s8.com"
//
static func getIndexData<T: Codable>(userId: String, as: T.Type) async -> APIResponse<T> { static func getIndexData<T: Codable>(userId: String, as: T.Type) async -> APIResponse<T> {
let request = URLRequest(url: URL(string: baseUrl + "/api/index?user_id=\(userId)")!) let request = URLRequest(url: URL(string: baseUrl + "/api/index?user_id=\(userId)")!)
@ -49,6 +50,13 @@ struct API {
return await doRequest(request: request, as: T.self) return await doRequest(request: request, as: T.self)
} }
//
static func getFavorData<T: Codable>(userId: String, id: Int, as: T.Type) async -> APIResponse<T> {
let request = URLRequest(url: URL(string: baseUrl + "/api/favor?user_id=\(userId)&id=\(id)")!)
return await doRequest(request: request, as: T.self)
}
// //
static func loadDateUpdateDramas<T: Codable>(userId: String, date: String, as: T.Type) async -> APIResponse<T> { static func loadDateUpdateDramas<T: Codable>(userId: String, date: String, as: T.Type) async -> APIResponse<T> {
let request = URLRequest(url: URL(string: baseUrl + "/api/load_date_dramas?user_id=\(userId)&date=\(date)")!) let request = URLRequest(url: URL(string: baseUrl + "/api/load_date_dramas?user_id=\(userId)&date=\(date)")!)

View File

@ -0,0 +1,235 @@
//
// FavorView.swift
// dimensionhub
//
// Created by on 2025/3/18.
//
import Foundation
import SwiftUI
import Observation
@Observable
final class FavorModel {
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: Int
let title: String
let episodes: [Episode]
}
struct FavorResponse: Codable {
let dramas: [DramaItem]
let has_more: Bool
}
var dramas: [DramaItem]
//
@ObservationIgnored
private var hasMore: Bool
init() {
self.dramas = []
self.hasMore = true
}
@MainActor
func loadData(userId: String) async {
let response = await API.getFavorData(userId: userId, id: 0, as: FavorResponse.self)
switch response {
case .error(let code, let message):
print("index load data get error_code: \(code), message: \(message)")
case .result(let result):
self.dramas = result.dramas
self.hasMore = result.has_more
}
}
@MainActor
func loadMoreFavorDramas(userId: String) async {
guard self.hasMore else {
return
}
let lastId = self.dramas.last?.id ?? 0
let response = await API.getFavorData(userId: userId, id: lastId, as: FavorResponse.self)
switch response {
case .result(let result):
self.dramas.append(contentsOf: result.dramas)
self.hasMore = result.has_more
case .error(let code, let string):
print("load more favor get error code: \(code), message: \(string)")
}
}
}
struct FavorView: View {
@AppStorage("userId") private var userId: String = Utils.defaultUserId()
@State var favorModel = FavorModel()
@State var isMoreLoading: Bool = false
var body: some View {
VStack(alignment: .center) {
HStack(alignment: .center) {
Color.clear
.overlay {
HStack(alignment: .center) {
Text("亚次元")
.font(.system(size: 16))
.padding([.top, .bottom], 5)
Spacer()
HStack {
Text("♡ 12")
.font(.system(size: 16))
.padding([.top, .bottom], 5)
}
}
.padding([.leading, .trailing], 15)
}
}
.frame(height: 50)
.background(Color(hex: "#F2F2F2"), ignoresSafeAreaEdges: .top)
VStack(alignment: .center) {
ScrollView(.vertical, showsIndicators: false) {
HStack(alignment: .center) {
Spacer()
Text("番剧补完计划")
.font(.system(size: 24))
.foregroundColor(Color(hex: "#999999"))
}
ForEach(favorModel.dramas, id: \.id) { drama in
DramaCellView(dramaItem: drama)
}
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
let contextFrame = geometry.frame(in: .named("indexScrollView"))
if screenBounds.height - frame.minY > 50 && contextFrame.minY > 0 && !isMoreLoading {
Task {
self.isMoreLoading = true
await self.favorModel.loadMoreFavorDramas(userId: userId)
self.isMoreLoading = false
}
}
}
})
if self.isMoreLoading {
ProgressView()
}
}
.coordinateSpace(name: "indexScrollView")
}
.frame(width: 370)
}
.ignoresSafeArea(edges: .bottom)
.task {
await self.favorModel.loadData(userId: self.userId)
}
}
}
extension FavorView {
//
struct DramaCellView: View {
let dramaItem: FavorModel.DramaItem
var body: some View {
VStack(alignment: .leading) {
NavigationLink(destination: DetailView(id: dramaItem.id)) {
Text(dramaItem.title)
.font(.system(size: 20))
.foregroundColor(Color(hex: "#333333"))
}
ScrollView(.horizontal, showsIndicators: false) {
LazyHStack(alignment: .center, spacing: 5) {
ForEach(dramaItem.episodes) { item in
VStack(alignment: .center) {
GeometryReader { geometry in
AsyncImage(url: URL(string: item.thumb)) { phase in
switch phase {
case .empty:
ProgressView()
case .success(let image):
image
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: geometry.frame(in: .local).width, height: 80)
.clipped()
default:
Image("ph_img_medium")
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: geometry.frame(in: .local).width, height: 80)
.clipped()
}
}
.frame(width: geometry.frame(in: .local).width, height: 80)
.overlay(alignment: .topLeading) {
if !item.num_name.isEmpty {
HStack(alignment: .center) {
Text(item.num_name)
.font(.system(size: 12))
.foregroundColor(.white)
.lineLimit(1)
}
.padding(3)
.background(
Color.black.opacity(0.6)
)
.cornerRadius(3)
.padding(3)
} else {
EmptyView()
}
}
}
Text(item.name)
.font(.system(size: 12))
.foregroundColor(Color(hex: "#333333"))
.lineLimit(1)
}
.frame(width: 120, height: 100)
.onTapGesture {
if let playUrl = URL(string: item.play) {
UIApplication.shared.open(playUrl)
}
}
}
}
}
}
}
}
}

View File

@ -296,10 +296,12 @@ extension IndexView {
.padding([.top, .bottom], 5) .padding([.top, .bottom], 5)
Spacer() Spacer()
HStack { NavigationLink(destination: FavorView()) {
Text("♡ 12") HStack {
.font(.system(size: 16)) Text("♡ 12")
.padding([.top, .bottom], 5) .font(.system(size: 16))
.padding([.top, .bottom], 5)
}
} }
} }
.padding([.leading, .trailing], 15) .padding([.leading, .trailing], 15)