Skip to content

Commit 55d0cb9

Browse files
committed
Optimize timeline load more location logic
1 parent b49ffd5 commit 55d0cb9

File tree

9 files changed

+185
-116
lines changed

9 files changed

+185
-116
lines changed

iosApp/iosApp.xcodeproj/project.pbxproj

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -574,7 +574,7 @@
574574
CODE_SIGN_IDENTITY = "Apple Development";
575575
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
576576
CODE_SIGN_STYLE = Automatic;
577-
CURRENT_PROJECT_VERSION = 2025071901;
577+
CURRENT_PROJECT_VERSION = 2025072801;
578578
DEVELOPMENT_ASSET_PATHS = "\"iosApp/Preview Content\"";
579579
DEVELOPMENT_TEAM = 7LFDZ96332;
580580
ENABLE_PREVIEWS = YES;
@@ -592,7 +592,7 @@
592592
"@executable_path/Frameworks",
593593
);
594594
MACOSX_DEPLOYMENT_TARGET = 15.0;
595-
MARKETING_VERSION = 0.4.4;
595+
MARKETING_VERSION = 0.4.5;
596596
OTHER_LDFLAGS = (
597597
"$(inherited)",
598598
"-lsqlite3",
@@ -622,7 +622,7 @@
622622
CODE_SIGN_IDENTITY = "Apple Development";
623623
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
624624
CODE_SIGN_STYLE = Automatic;
625-
CURRENT_PROJECT_VERSION = 2025071901;
625+
CURRENT_PROJECT_VERSION = 2025072801;
626626
DEVELOPMENT_ASSET_PATHS = "\"iosApp/Preview Content\"";
627627
DEVELOPMENT_TEAM = 7LFDZ96332;
628628
ENABLE_PREVIEWS = YES;
@@ -640,7 +640,7 @@
640640
"@executable_path/Frameworks",
641641
);
642642
MACOSX_DEPLOYMENT_TARGET = 15.0;
643-
MARKETING_VERSION = 0.4.4;
643+
MARKETING_VERSION = 0.4.5;
644644
OTHER_LDFLAGS = (
645645
"$(inherited)",
646646
"-lsqlite3",

iosApp/iosApp/UI/DataLayer/FlareTimelineState.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,7 @@ struct TimelineItem: Identifiable, Equatable, Hashable {
274274
// FlareLog.debug("TimelineItem Action[\(index)] Other - type: \(type(of: item))")
275275
}
276276
} else if case let .group(group) = onEnum(of: action) {
277-
FlareLog.debug("TimelineItem Action[\(index)] Group - displayItem: \(type(of: group.displayItem))")
277+
// FlareLog.debug("TimelineItem Action[\(index)] Group - displayItem: \(type(of: group.displayItem))")
278278

279279
// 遍历Group中的所有SubActions来提取数据
280280
for (subIndex, subAction) in group.actions.enumerated() {
@@ -369,7 +369,7 @@ struct TimelineItem: Identifiable, Equatable, Hashable {
369369
// 🔥 新增:处理topMessage转换
370370
let topMessage = uiTimeline.topMessage?.toSwift()
371371
if let topMessage {
372-
FlareLog.debug("TimelineItem Found topMessage: \(topMessage.type)")
372+
// FlareLog.debug("TimelineItem Found topMessage: \(topMessage.type)")
373373
}
374374

375375
return TimelineItem(

iosApp/iosApp/UI/DataLayer/PagingStateConverter.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -328,7 +328,7 @@ class PagingStateConverter {
328328
FlareLog.debug("[PagingStateConverter] 开始转换范围: [\(startIndex), \(endIndex))")
329329

330330
for index in startIndex ..< endIndex {
331-
FlareLog.debug("[PagingStateConverter] 尝试获取index=\(index)的数据")
331+
//FlareLog.debug("[PagingStateConverter] 尝试获取index=\(index)的数据")
332332

333333
let uiTimeline: UiTimeline?
334334

@@ -348,7 +348,7 @@ class PagingStateConverter {
348348
if let timeline = uiTimeline {
349349
let timelineItem = TimelineItem.from(timeline)
350350
items.append(timelineItem)
351-
FlareLog.debug("[PagingStateConverter] 成功转换index=\(index)的数据,当前items数量: \(items.count)")
351+
//FlareLog.debug("[PagingStateConverter] 成功转换index=\(index)的数据,当前items数量: \(items.count)")
352352
} else {
353353
FlareLog.warning("PagingStateConverter Failed to get item at index \(index)")
354354
// 遇到nil时停止转换,避免空洞

iosApp/iosApp/UI/Page/Compose/TimelineV2/TimelineItemsViewV2.swift

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -28,21 +28,10 @@ struct TimelineItemsViewV2: View {
2828
}
2929
}
3030

31-
if hasMore {
32-
TimelineLoadMoreView(isRefreshing: false)
33-
.onAppear {
34-
if hasMore, !(viewModel?.isLoadingMore ?? false) {
35-
FlareLog.debug("TimelineItemsViewV2 Load more triggered")
36-
handleLoadMore()
37-
}
38-
}
39-
}
40-
}
41-
42-
private func handleLoadMore() {
43-
FlareLog.debug("[TimelineItemsViewV2 LoadMore] UI层触发load more,委托给ViewModel")
44-
Task {
45-
await viewModel?.handleLoadMore()
31+
if hasMore, let viewModel = viewModel {
32+
TimelineLoadMoreView {
33+
try await viewModel.handleLoadMore()
34+
}
4635
}
4736
}
4837
}

iosApp/iosApp/UI/Page/Home/Model/TimelineViewModel.swift

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,13 @@ class TimelineViewModel {
2121

2222
private(set) var isLoadingMore: Bool = false
2323

24+
25+
var scrollToId: String?
26+
27+
28+
@ObservationIgnored
29+
private var visibleItems: [TimelineItem] = []
30+
2431
var items: [TimelineItem] {
2532
if case let .loaded(items, _) = timelineState {
2633
return items
@@ -104,13 +111,19 @@ class TimelineViewModel {
104111
await MainActor.run {
105112
let oldItemsCount = self.items.count
106113
let oldState = type(of: self.timelineState)
114+
let oldHasMore = self.hasMore
107115

108116
self.timelineState = flareState
109117

110118
let newItemsCount = self.items.count
111119
let newState = type(of: self.timelineState)
120+
let newHasMore = self.hasMore
112121

113-
FlareLog.debug("🎨 [Timeline ViewModel] UI state updated - stateChange: \(oldState)\(newState), itemsChange: \(oldItemsCount)\(newItemsCount), hasMore: \(self.hasMore)")
122+
FlareLog.debug("🎨 [Timeline ViewModel] UI state updated - stateChange: \(oldState)\(newState), itemsChange: \(oldItemsCount)\(newItemsCount), hasMoreChange: \(oldHasMore)\(newHasMore)")
123+
124+
if newItemsCount != oldItemsCount {
125+
FlareLog.debug("📊 [Timeline ViewModel] Items数量变化详情 - 新增: \(newItemsCount - oldItemsCount)")
126+
}
114127
}
115128
}
116129

@@ -204,15 +217,20 @@ class TimelineViewModel {
204217
}
205218

206219
func handleError(_ error: FlareError) {
220+
FlareLog.error("[TimelineViewModel] 处理错误: \(error)")
207221
currentError = error
208222
showErrorAlert = true
223+
FlareLog.debug("[TimelineViewModel] 错误状态已设置 - showErrorAlert: true")
209224
}
210225

211226
func handleScrollOffsetChange(_ offsetY: CGFloat, showFloatingButton: Binding<Bool>) {
212227
let shouldShow = offsetY > 50
213228

229+
//FlareLog.debug("[TimelineViewModel] 滚动偏移变化: offsetY=\(offsetY), shouldShow=\(shouldShow), current=\(showFloatingButton.wrappedValue)")
230+
214231
if showFloatingButton.wrappedValue != shouldShow {
215232
showFloatingButton.wrappedValue = shouldShow
233+
FlareLog.debug("[TimelineViewModel] 浮动按钮状态更新: \(showFloatingButton.wrappedValue)")
216234
}
217235
}
218236

@@ -233,6 +251,10 @@ class TimelineViewModel {
233251
isLoadingMore = true
234252
FlareLog.debug("🔄 [Timeline ViewModel] isLoadingMore set to true, timestamp: \(timestamp)")
235253

254+
255+
let topVisibleItem = visibleItems.first
256+
FlareLog.debug("🎯 [Timeline ViewModel] 保存顶部可见item: \(topVisibleItem?.id ?? "nil")")
257+
236258
defer {
237259
isLoadingMore = false
238260
let deferTimestamp = Date().timeIntervalSince1970
@@ -245,11 +267,52 @@ class TimelineViewModel {
245267

246268
let completionTimestamp = Date().timeIntervalSince1970
247269
FlareLog.debug("✅ [Timeline ViewModel] handleLoadMore completed successfully, timestamp: \(completionTimestamp)")
270+
271+
272+
if let topItem = topVisibleItem,
273+
visibleItems.contains(where: { $0.id == topItem.id }) {
274+
FlareLog.debug("🎯 [Timeline ViewModel] 恢复滚动位置到: \(topItem.id)")
275+
276+
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
277+
self.scrollTo(itemId: topItem.id)
278+
}
279+
}
248280
} catch {
249281
let errorTimestamp = Date().timeIntervalSince1970
250282
FlareLog.error("💥 [Timeline ViewModel] handleLoadMore failed - error: \(error), timestamp: \(errorTimestamp)")
251283
}
252284
}
285+
286+
287+
func clearScrollTarget() {
288+
FlareLog.debug("[TimelineViewModel] 清除滚动目标")
289+
scrollToId = nil
290+
}
291+
292+
func scrollTo(itemId: String) {
293+
FlareLog.debug("[TimelineViewModel] 设置滚动目标: \(itemId)")
294+
scrollToId = itemId
295+
}
296+
297+
298+
func itemDidAppear(item: TimelineItem) {
299+
300+
if !visibleItems.contains(where: { $0.id == item.id }) {
301+
visibleItems.insert(item, at: 0)
302+
}
303+
304+
305+
if visibleItems.count > 50 {
306+
visibleItems = Array(visibleItems.prefix(50))
307+
}
308+
309+
FlareLog.debug("[TimelineViewModel] item出现: \(item.id), 当前可见items: \(visibleItems.count)")
310+
}
311+
312+
func itemDidDisappear(item: TimelineItem) {
313+
visibleItems.removeAll { $0.id == item.id }
314+
FlareLog.debug("[TimelineViewModel] item消失: \(item.id), 当前可见items: \(visibleItems.count)")
315+
}
253316
}
254317

255318
struct TimelineLoadingView: View {

iosApp/iosApp/UI/Page/Home/View/TimelineViewSwiftUI/TimelineItemsView.swift

Lines changed: 17 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,8 @@ struct TimelineItemsView: View {
77
let viewModel: TimelineViewModel
88

99
@State private var itemIndexMap: [String: Int] = [:]
10-
@State private var lastItemId: String?
1110
@Environment(\.appSettings) private var appSettings
1211

13-
private let loadMoreThreshold = 3
14-
1512
var body: some View {
1613
Group {
1714
ForEach(items) { item in
@@ -21,102 +18,43 @@ struct TimelineItemsView: View {
2118
)
2219
.padding(.vertical, 4)
2320
.onAppear {
24-
if let currentIndex = itemIndexMap[item.id] {
25-
let remainingItems = items.count - currentIndex - 1
26-
27-
// 🔥 添加日志:loadMore触发条件检查
28-
FlareLog.debug("[Timeline ItemsView] item出现 - ID: \(item.id), 当前索引: \(currentIndex), 剩余items: \(remainingItems)")
29-
FlareLog.debug("[Timeline ItemsView] loadMore条件检查 - remainingItems <= \(loadMoreThreshold): \(remainingItems <= loadMoreThreshold), hasMore: \(hasMore), isLoadingMore: \(!viewModel.isLoadingMore)")
30-
31-
if remainingItems <= loadMoreThreshold, hasMore, !viewModel.isLoadingMore {
32-
FlareLog.debug("[Timeline ItemsView] 触发loadMore - 当前索引: \(currentIndex), 剩余items: \(remainingItems)")
33-
FlareLog.debug("Timeline 提前触发load more,当前索引: \(currentIndex), 剩余items: \(remainingItems)")
34-
handleLoadMore()
35-
}
36-
}
21+
//FlareLog.debug("[TimelineItemsView] item出现: \(item.id), index: \(index)")
22+
viewModel.itemDidAppear(item: item)
3723
}
24+
.onDisappear {
25+
//FlareLog.debug("[TimelineItemsView] item消失: \(item.id), index: \(index)")
26+
viewModel.itemDidDisappear(item: item)
27+
}
28+
//.id(item.id)
3829
}
3930

40-
if viewModel.isLoadingMore {
41-
ForEach(0 ..< 2, id: \.self) { index in
42-
TimelineStatusViewV2(
43-
item: createSampleTimelineItem(),
44-
index: items.count + index
45-
)
46-
.redacted(reason: .placeholder)
47-
.padding(.vertical, 4)
31+
32+
if hasMore {
33+
TimelineLoadMoreView {
34+
FlareLog.debug("[TimelineItemsView] LoadMoreView触发handleLoadMore")
35+
try await viewModel.handleLoadMore()
36+
}
37+
.onAppear {
38+
FlareLog.debug("[TimelineItemsView] 创建TimelineLoadMoreView - hasMore: \(hasMore), items数量: \(items.count)")
4839
}
4940
}
5041
}
5142
.onChange(of: items) { oldItems, newItems in
52-
// 🔥 增强日志:详细的数据变化分析
53-
FlareLog.debug("[Timeline ItemsView] items数组变化详情:")
54-
FlareLog.debug(" - 数量变化: \(oldItems.count) -> \(newItems.count)")
5543
FlareLog.debug("[Timeline ItemsView] items数组变化: \(oldItems.count) -> \(newItems.count)")
56-
57-
if newItems.count > oldItems.count {
58-
let newItemIds = newItems.suffix(newItems.count - oldItems.count).map(\.id)
59-
FlareLog.debug(" - 新增items数量: \(newItems.count - oldItems.count)")
60-
FlareLog.debug(" - 新增items的ID: \(newItemIds)")
61-
FlareLog.debug("[Timeline ItemsView] 检测到新items!新增了 \(newItems.count - oldItems.count)")
62-
FlareLog.debug("[Timeline ItemsView] 新增items的ID: \(newItemIds)")
63-
64-
// 🔥 添加日志:检查新增items的位置
65-
if !oldItems.isEmpty, !newItems.isEmpty {
66-
let oldFirstId = oldItems.first?.id
67-
let newFirstId = newItems.first?.id
68-
FlareLog.debug(" - 第一个item变化: \(oldFirstId ?? "nil") -> \(newFirstId ?? "nil")")
69-
70-
if oldFirstId == newFirstId {
71-
FlareLog.debug(" - 新增位置:底部追加")
72-
} else {
73-
FlareLog.debug(" - 新增位置:顶部插入或全量替换")
74-
}
75-
}
76-
} else if newItems.count < oldItems.count {
77-
FlareLog.debug(" - 减少items数量: \(oldItems.count - newItems.count)")
78-
} else {
79-
FlareLog.debug(" - items数量未变,可能是内容更新")
80-
}
81-
8244
updateItemIndexMap(newItems)
83-
updateLastItemId(newItems)
8445
}
8546
.onAppear {
47+
FlareLog.debug("[Timeline ItemsView] TimelineItemsView appeared with \(items.count) items")
8648
updateItemIndexMap(items)
87-
updateLastItemId(items)
88-
}
89-
}
90-
91-
private func handleLoadMore() {
92-
Task {
93-
await viewModel.handleLoadMore()
9449
}
9550
}
9651

9752
private func updateItemIndexMap(_ items: [TimelineItem]) {
98-
// 🔥 添加日志:索引映射更新
99-
FlareLog.debug("[Timeline ItemsView] updateItemIndexMap开始 - items数量: \(items.count)")
100-
10153
var newIndexMap: [String: Int] = [:]
102-
var duplicateCount = 0
103-
10454
for (index, item) in items.enumerated() {
105-
if newIndexMap[item.id] != nil {
106-
duplicateCount += 1
107-
FlareLog.warning("[Timeline ItemsView] 发现重复item ID: \(item.id), 使用最新索引: \(index)")
108-
FlareLog.warning("Timeline Duplicate item ID found: \(item.id), using latest index: \(index)")
109-
}
11055
newIndexMap[item.id] = index
11156
}
112-
11357
itemIndexMap = newIndexMap
114-
FlareLog.debug("[Timeline ItemsView] 索引映射更新完成 - items: \(items.count), 重复处理: \(duplicateCount)")
115-
FlareLog.debug("Timeline Updated item index map with \(items.count) items, \(duplicateCount) duplicates handled")
116-
}
117-
118-
private func updateLastItemId(_ items: [TimelineItem]) {
119-
lastItemId = items.last?.id
120-
FlareLog.debug("Timeline Updated last item ID: \(lastItemId ?? "nil")")
58+
FlareLog.debug("[Timeline ItemsView] 更新索引映射 - items: \(items.count)")
12159
}
12260
}

0 commit comments

Comments
 (0)