fix index view

This commit is contained in:
anlicheng 2025-04-14 17:00:54 +08:00
parent 2f56bc7e74
commit 98265c72d6
4 changed files with 124 additions and 116 deletions

View File

@ -118,8 +118,8 @@ struct API {
}
print("request url: \(request.url!.absoluteString)")
let x = String(data: data, encoding: .utf8)!
print("url: \(request.url!.path()), data is: \(x)")
// let x = String(data: data, encoding: .utf8)!
// print("url: \(request.url!.path()), data is: \(x)")
do {
let result = try JSONDecoder().decode(APISuccessResponse<T>.self, from: data)
return .result(result.result)

View File

@ -58,11 +58,18 @@ struct FlexImage: View {
}
}
case .local(let data):
Image(uiImage: UIImage(data: data)!)
if let uiImage = UIImage(data: data) {
Image(uiImage: uiImage)
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: width, height: height)
.clipped()
} else {
Image(placeholder)
.resizable()
.aspectRatio(contentMode: .fill)
.clipped()
}
}
}

View File

@ -14,6 +14,7 @@ struct IndexMainView: View {
@AppStorage("userId") private var userId: String = Utils.defaultUserId()
@State var indexModel = IndexModel()
@State private var scrollID: Int? = 0
//
@State var showPrompt: Bool = false
@ -62,30 +63,27 @@ struct IndexMainView: View {
//
LazyVStack(alignment: .center, spacing: 10) {
ForEach(indexModel.updateDramaGroups, id: \.group_id) { group in
DramaGroupView(group: group, model: indexModel) {
selectGroupId = group.group_id
indexModel.selectedDate = group.group_id
ForEach(Array(indexModel.dramaGroupElements.enumerated()), id: \.offset) { idx, item in
switch item {
case .label(groupId: let groupId, groupName: let groupName):
DramaGroupLabelView(group_name: groupName) {
selectGroupId = groupId
indexModel.selectedDate = groupId
showDateNavPopover = true
}
.contentShape(Rectangle())
case .item(groupId: let groupId, item: let item):
DramaGroupItemView(groupId: groupId, item: item)
.id(item.id)
}
}
}
.scrollTargetLayout()
ProgressView()
.background(GeometryReader { geometry in
let minY = geometry.frame(in: .global).minY
Color.clear.preference(key: FooterOffsetPreferenceKey.self, value: Int(minY))
})
}
.frame(width: 370)
.coordinateSpace(name: "indexScrollView")
.onPreferenceChange(FooterOffsetPreferenceKey.self) { offset in
// state
DispatchQueue.main.async {
indexModel.loadMorePublisher.send((userId, offset))
}
}
.scrollPosition(id: $scrollID)
.refreshable {
guard !self.showDateNavPopover && !self.headerRefreshing else {
return
@ -100,6 +98,11 @@ struct IndexMainView: View {
}
}
}
.onChange(of: scrollID) { _, newValue in
if let newValue {
indexModel.scrollIDPublisher.send((userId, newValue))
}
}
.overlay(alignment: .topTrailing) {
HStack(alignment: .center) {
Button(action: {
@ -139,9 +142,6 @@ struct IndexMainView: View {
.task {
await self.indexModel.loadData(userId: self.userId)
}
.onPreferenceChange(DramaGroupElementPreferenceKey.self) { frames in
indexModel.visiblePublisher.send(frames)
}
}
}
@ -149,30 +149,33 @@ struct IndexMainView: View {
extension IndexMainView {
//
struct DramaGroupView: View {
@EnvironmentObject var appNav: AppNavigation
let group: IndexModel.UpdateDramaGroup
let model: IndexModel
struct DramaGroupLabelView: View {
let group_name: String
var onTap: () -> Void
var body: some View {
VStack(alignment: .center, spacing: 10) {
HStack {
Spacer()
Text(group.group_name)
Text(group_name)
.font(.system(size: 18))
.fontWeight(.regular)
.onTapGesture {
onTap()
}
}
}
}
}
ForEach(group.items, id: \.id) { item in
Button(action: {
appNav.append(dest: .detail(id: item.id))
}) {
// item
struct DramaGroupItemView: View {
@EnvironmentObject var appNav: AppNavigation
let groupId: String
let item: IndexModel.UpdateDramaGroup.Item
var body: some View {
FlexImage(urlString: item.thumb, width: 370, height: 180, placeholder: "ph_img_big")
.frame(width: 370, height: 180)
.overlay(alignment: .topLeading) {
@ -194,47 +197,15 @@ extension IndexMainView {
.cornerRadius(5)
.padding(8)
}
.background(GeometryReader { geometry in
let height = geometry.size.height
let minY = geometry.frame(in: .named("indexScrollView")).minY
let y = minY >= 0 ? minY : minY + height
Color.clear
.preference(key: DramaGroupElementPreferenceKey.self, value: [ group.group_id : y])
})
}
}
.contentShape(Rectangle())
.onTapGesture {
appNav.append(dest: .detail(id: item.id))
}
}
}
}
// MARK: PreferenceKey
extension IndexMainView {
// , [groupId : minY]
struct DramaGroupElementPreferenceKey: PreferenceKey {
static var defaultValue: [String: CGFloat] = [:]
static func reduce(value: inout [String: CGFloat], nextValue: () -> [String: CGFloat]) {
value.merge(nextValue()) { $1 }
}
}
//
struct FooterOffsetPreferenceKey: PreferenceKey {
static var defaultValue: Int = 0
static func reduce(value: inout Int, nextValue: () -> Int) {
value += nextValue()
}
}
}
#Preview {
IndexMainView()
}

View File

@ -50,10 +50,18 @@ final class IndexModel {
let follow_num: Int
}
enum GroupElement {
case label(groupId: String, groupName: String)
case item(groupId: String, item: UpdateDramaGroup.Item)
}
var selectedDate: String
//
var updateDramaGroups: [UpdateDramaGroup] = []
var dramaGroupElements: [GroupElement] = []
var follow_num: String = "0"
// group_name
@ -74,12 +82,9 @@ final class IndexModel {
@ObservationIgnored
private var updateInterval = Date()
// footeroffset
// scrollID
@ObservationIgnored
var loadMorePublisher = PassthroughSubject<(String, Int), Never>()
@ObservationIgnored
var visiblePublisher = PassthroughSubject<[String: CGFloat], Never>()
var scrollIDPublisher = PassthroughSubject<(String, Int), Never>()
@ObservationIgnored
private var bag = Set<AnyCancellable>()
@ -87,16 +92,35 @@ final class IndexModel {
init() {
self.selectedDate = ""
self.loadMorePublisher
.debounce(for: 0.2, scheduler: RunLoop.main)
.dropFirst()
.sink { (userId, offset) in
self.scrollIDPublisher
.sink { userId, scrollID in
self.dramaGroupElements.forEach { element in
switch element {
case .label(let groupId, let groupName):
()
case .item(let groupId, let item):
if item.id == scrollID {
DispatchQueue.main.async {
self.setFixedDrameGroup(groupId: groupId)
}
}
}
}
let ids = self.dramaGroupElements.suffix(6).compactMap { element -> Int? in
switch element {
case .label(_, _):
return nil
case .item(groupId: _, item: let item):
return item.id
}
}
//
let timeDistance = self.updateInterval.distance(to: Date())
//
let height = Int(UIScreen.main.bounds.height)
let distance = height - offset
if timeDistance > 1.0 && (distance >= 0 && distance < 50) {
if timeDistance > 1.0 && ids.contains(scrollID) {
Task {
await self.loadMoreUpdateDramasTask(userId: userId, mode: .next)
}
@ -104,19 +128,6 @@ final class IndexModel {
}
}
.store(in: &bag)
self.visiblePublisher
.debounce(for: 0.05, scheduler: RunLoop.main)
.dropFirst()
.sink { frames in
let visibleFrames = frames.filter { $0.value >= 0}
if let minFrame = visibleFrames.min(by: { $0.value <= $1.value}) {
DispatchQueue.main.async {
self.setFixedDrameGroup(groupId: minFrame.key)
}
}
}
.store(in: &bag)
}
func loadData(userId: String) async {
@ -133,6 +144,8 @@ final class IndexModel {
await MainActor.run {
self.updateDramaGroups = result.update_dramas
self.dramaGroupElements = transformUpdateDramaGroups(groups: result.update_dramas)
self.fixedDramaGroup = result.update_dramas.first
self.follow_num = result.follow_num >= 100 ? "99+" : "\(result.follow_num)"
}
@ -169,6 +182,7 @@ final class IndexModel {
displayDramaGroups(self.updateDramaGroups, label: "before")
await MainActor.run {
self.updateDramaGroups = preappendMergeDramaGroups(groups: self.updateDramaGroups, mergeGroups: groups)
self.dramaGroupElements = transformUpdateDramaGroups(groups: self.updateDramaGroups)
}
displayDramaGroups(self.updateDramaGroups, label: "after")
}
@ -186,6 +200,7 @@ final class IndexModel {
displayDramaGroups(self.updateDramaGroups, label: "before")
await MainActor.run {
self.updateDramaGroups = appendMergeDramaGroups(groups: self.updateDramaGroups, mergeGroups: groups)
self.dramaGroupElements = transformUpdateDramaGroups(groups: self.updateDramaGroups)
}
displayDramaGroups(self.updateDramaGroups, label: "after")
@ -197,15 +212,30 @@ final class IndexModel {
}
}
//
private func transformUpdateDramaGroups(groups: [UpdateDramaGroup]) -> [GroupElement] {
var groupElements: [GroupElement] = []
for group in groups {
groupElements.append(.label(groupId: group.group_id, groupName: group.group_name))
for item in group.items {
groupElements.append(.item(groupId: group.group_id, item: item))
}
}
return groupElements
}
//
func loadDateUpdateDramas(userId: String, date: String) async {
self.updateDramaGroups.removeAll()
self.dramaGroupElements.removeAll()
let response = await API.loadDateUpdateDramas(userId: userId, date: date, as: [UpdateDramaGroup].self)
if case let .result(groups) = response {
preloadGroupImages(groups: groups)
await MainActor.run {
self.updateDramaGroups = groups
self.dramaGroupElements = transformUpdateDramaGroups(groups: self.updateDramaGroups)
self.fixedDramaGroup = groups.first
}
}