增加过渡页面
This commit is contained in:
parent
bda86f1c8a
commit
2175a8c74e
21
dimensionhub/Assets.xcassets/lost_network.imageset/Contents.json
vendored
Normal file
21
dimensionhub/Assets.xcassets/lost_network.imageset/Contents.json
vendored
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
dimensionhub/Assets.xcassets/lost_network.imageset/lost.jpg
vendored
Normal file
BIN
dimensionhub/Assets.xcassets/lost_network.imageset/lost.jpg
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.2 KiB |
@ -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)
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user