增加过渡页面

This commit is contained in:
anlicheng 2025-02-25 23:28:01 +08:00
parent bda86f1c8a
commit 2175a8c74e
4 changed files with 218 additions and 117 deletions

View File

@ -0,0 +1,21 @@
{
"images" : [
{
"filename" : "lost.jpg",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

View File

@ -48,7 +48,7 @@ final class DateNavModel {
} else { } else {
let response = await API.getDateIndex(userId: userId, as: [DateModel].self) let response = await API.getDateIndex(userId: userId, as: [DateModel].self)
switch response { switch response {
case .error(let code, let message): case .error(_, _):
return [] return []
case .result(let models): case .result(let models):
await DataCache.shared.setDateModelCache(models) await DataCache.shared.setDateModelCache(models)

View File

@ -8,6 +8,7 @@
import SwiftUI import SwiftUI
import SwiftData import SwiftData
import Observation import Observation
import Network
@Observable @Observable
final class IndexModel { final class IndexModel {
@ -194,134 +195,214 @@ final class IndexModel {
} }
struct IndexView: View { struct IndexView: View {
@Environment(\.modelContext) private var modelContext
@AppStorage("userId") private var userId: String = Utils.defaultUserId()
@State var indexModel = IndexModel() // , app
@State var isMoreLoading: Bool = false enum NetworkStatus {
// case satisfied
@State var isPrevLoading: Bool = false case unsatisfied
}
@State private var networkStatus: NetworkStatus = .satisfied
// private static let queue = DispatchQueue(label: "NetworkMonitorQueue")
@State var showPrompt: Bool = false
@State var promptMessage: String = ""
//
@State private var selectGroupId: String = ""
@State private var showDateNavPopover: Bool = false
var body: some View { var body: some View {
VStack(alignment: .center) { ZStack {
switch self.networkStatus {
HStack(alignment: .center) { case .unsatisfied:
Color.clear IndexExceptionView {
.overlay { self.checkNetworkStatus()
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.updateDramaGroups, id: \.group_id) { group in
DramaGroupView(group: group) {
selectGroupId = group.group_id
indexModel.selectedDate = group.group_id
showDateNavPopover = true
}
}
}
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("scrollView"))
if screenBounds.height - frame.minY > 50 && contextFrame.minY > 0 && !isMoreLoading {
Task {
self.isMoreLoading = true
let result = await self.indexModel.loadMoreUpdateDramas(userId: self.userId, mode: .next)
switch result {
case .success:
()
case .error(let message):
DispatchQueue.main.async {
self.showPrompt = true
self.promptMessage = message
}
}
self.isMoreLoading = false
}
}
}
})
if self.isMoreLoading {
ProgressView()
} }
case .satisfied:
IndexMainView()
} }
.coordinateSpace(name: "scrollView")
.popover(isPresented: $showDateNavPopover) {
DateNavView(selectGroupId: self.$selectGroupId, showDateNavPopover: $showDateNavPopover) { selectedDate in
Task {
await indexModel.loadDateUpdateDramas(userId: self.userId, date: selectedDate)
}
}
}
.refreshable {
guard !self.isPrevLoading else {
return
}
//
self.isPrevLoading = true
let result = await self.indexModel.loadMoreUpdateDramas(userId: self.userId, mode: .prev)
switch result {
case .success:
()
case .error(let message):
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
self.showPrompt = true
self.promptMessage = message
}
}
self.isPrevLoading = false
}
} }
.frame(width: 370) .onAppear {
.ignoresSafeArea(edges: .bottom) self.checkNetworkStatus()
.alert(isPresented: $showPrompt) {
Alert(title: Text("提示"), message: Text(self.promptMessage), dismissButton: .default(Text("OK")))
}
.task {
await self.indexModel.loadData(userId: self.userId)
} }
} }
private func checkNetworkStatus() {
let monitor = NWPathMonitor()
monitor.pathUpdateHandler = { path in
DispatchQueue.main.async {
switch path.status {
case .satisfied:
self.networkStatus = .satisfied
default:
self.networkStatus = .unsatisfied
}
}
}
monitor.start(queue: Self.queue)
}
} }
extension IndexView { extension IndexView {
struct IndexExceptionView: View {
let onRetry: () -> Void
var body: some View {
HStack {
Spacer()
VStack(alignment: .center, spacing: 20) {
Spacer()
Image("lost_network")
Text("网络状态待提升,点击重试")
.font(.system(size: 13))
.foregroundColor(Color(hex: "#333333"))
Rectangle()
.frame(width: 100, height: 25)
.foregroundColor(Color(hex: "#F2F2F2"))
.overlay {
Text("重新加载")
.font(.system(size: 13))
.foregroundColor(Color(hex: "#999999"))
.fontWeight(.regular)
}
.onTapGesture {
onRetry()
}
Spacer()
}
Spacer()
}
.background(Color(hex: "#F6F6F6"), ignoresSafeAreaEdges: .all)
}
}
//
struct IndexMainView: View {
@Environment(\.modelContext) private var modelContext
@AppStorage("userId") private var userId: String = Utils.defaultUserId()
@State var indexModel = IndexModel()
@State var isMoreLoading: Bool = false
//
@State var isPrevLoading: Bool = false
//
@State var showPrompt: Bool = false
@State var promptMessage: String = ""
//
@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.updateDramaGroups, id: \.group_id) { group in
DramaGroupView(group: group) {
selectGroupId = group.group_id
indexModel.selectedDate = group.group_id
showDateNavPopover = true
}
}
}
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("scrollView"))
if screenBounds.height - frame.minY > 50 && contextFrame.minY > 0 && !isMoreLoading {
Task {
self.isMoreLoading = true
let result = await self.indexModel.loadMoreUpdateDramas(userId: self.userId, mode: .next)
switch result {
case .success:
()
case .error(let message):
DispatchQueue.main.async {
self.showPrompt = true
self.promptMessage = message
}
}
self.isMoreLoading = false
}
}
}
})
if self.isMoreLoading {
ProgressView()
}
}
.coordinateSpace(name: "scrollView")
.popover(isPresented: $showDateNavPopover) {
DateNavView(selectGroupId: self.$selectGroupId, showDateNavPopover: $showDateNavPopover) { selectedDate in
Task {
await indexModel.loadDateUpdateDramas(userId: self.userId, date: selectedDate)
}
}
}
.refreshable {
guard !self.isPrevLoading else {
return
}
//
self.isPrevLoading = true
let result = await self.indexModel.loadMoreUpdateDramas(userId: self.userId, mode: .prev)
switch result {
case .success:
()
case .error(let message):
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
self.showPrompt = true
self.promptMessage = message
}
}
self.isPrevLoading = false
}
}
.frame(width: 370)
.ignoresSafeArea(edges: .bottom)
.alert(isPresented: $showPrompt) {
Alert(title: Text("提示"), message: Text(self.promptMessage), dismissButton: .default(Text("OK")))
}
.task {
await self.indexModel.loadData(userId: self.userId)
}
}
}
// //
struct DramaCellView: View { struct DramaCellView: View {
let dramaItem: IndexModel.DramaItem let dramaItem: IndexModel.DramaItem
@ -379,7 +460,6 @@ extension IndexView {
} }
} }
// //
struct DramaGroupView: View { struct DramaGroupView: View {
let group: IndexModel.UpdateDramaGroup let group: IndexModel.UpdateDramaGroup