This commit is contained in:
Avently committed 2024-12-20 06:55:34 -08:00
1 parent 449f15ea0e
commit a4a268310b
4 files changed
+120 -11

No files matched your search

@@ -0,0 +1,27 @@
//
// ChatScrollHelpers.swift
// SimpleX (iOS)
//
// Created by me on 20.12.2024.
// Copyright © 2024 SimpleX Chat. All rights reserved.
//
import SwiftUI
import SimpleXChat
func loadLastItems(_ loadingMoreItems: Binding<Bool>, _ chatInfo: ChatInfo) {
//return ()
Task {
try? await Task.sleep(nanoseconds: 2500_000000)
if ItemsModel.shared.chatState.totalAfter == 0 || ChatModel.shared.chatId != chatInfo.id {
return
}
await MainActor.run {
loadingMoreItems.wrappedValue = true
}
await apiLoadMessages(chatInfo.chatType, chatInfo.apiId, ChatPagination.last(count: 50), ItemsModel.shared.chatState)
await MainActor.run {
loadingMoreItems.wrappedValue = false
}
}
}
@@ -465,12 +465,14 @@ struct ChatView: View {
mergedItems = MergedItems.create(ItemsModel.shared.reversedChatItems, chat.chatStats.unreadCount, revealedItems, ItemsModel.shared.chatState)
}
)
.environmentObject(theme) // crashes without this line when scrolling to the first unread in ReverseList
.id(ci.id) // Required to trigger `onAppear` on iOS15
} loadItems: { pagination, visibleItemIndexesNonReversed in
loadChatItems(cInfo, pagination, visibleItemIndexesNonReversed)
}
.onAppear {
mergedItems = MergedItems.create(im.reversedChatItems, chat.chatStats.unreadCount, revealedItems, ItemsModel.shared.chatState)
loadLastItems($loadingItems, chat.chatInfo)
}
.onChange(of: im.reversedChatItems) { items in
mergedItems = MergedItems.create(items, chat.chatStats.unreadCount, revealedItems, ItemsModel.shared.chatState)
@@ -481,6 +483,9 @@ struct ChatView: View {
.onChange(of: chat.chatStats.unreadCount) { unreadCount in
mergedItems = MergedItems.create(im.reversedChatItems, unreadCount, revealedItems, ItemsModel.shared.chatState)
}
.onChange(of: chat.id) { _ in
loadLastItems($loadingItems, chat.chatInfo)
}
.opacity(ItemsModel.shared.isLoading ? 0 : 1)
.padding(.vertical, -InvertedTableView.inset)
.onTapGesture { hideKeyboard() }
@@ -1032,6 +1037,8 @@ struct ChatView: View {
}
}
.onAppear {
// LALAL
return ()
if markedRead {
return
} else {
+82 -11
View File
@@ -60,6 +60,8 @@ struct ReverseList<Content: View>: UIViewControllerRepresentable {
private let updateFloatingButtons = PassthroughSubject<Void, Never>()
private var bag = Set<AnyCancellable>()
private var scrollToRowOnAppear = 0
init(representer: ReverseList) {
self.representer = representer
super.init(style: .plain)
@@ -156,6 +158,31 @@ struct ReverseList<Content: View>: UIViewControllerRepresentable {
NotificationCenter.default.post(name: .chatViewWillBeginScrolling, object: nil)
}
/// depending on tableView layout phase conditions, it can already have known size or not. Not possible to correctly scroll to required
/// item if the size is unknown
func scrollToRowWhenKnowSize(_ row: Int) {
// logger.debug("LALAL WILL SCROLL TO \(self.scrollToRowOnAppear)")
if row > 0 && tableView.visibleSize.height > 0 {
// logger.debug("LALAL OFFSET before \(self.tableView.contentOffset.y), visible \(self.tableView.visibleSize.height) \(self.tableView.frame.height) \(self.view.frame.height) \(self.tableView.indexPathsForVisibleRows!)")
//tableView.setContentOffset(CGPointMake(0, tableView.contentOffset.y - tableView.visibleSize.height), animated: false)
tableView.scrollToRow(at: IndexPath(item: min(tableView.numberOfRows(inSection: 0) - 1, row), section: 0), at: .bottom, animated: false)
// Without this small scroll position is not correct pixel-to-pixel.
// Only needed when viewDidAppear has not been called yet because in there clipsToBounds is applied
if tableView.clipsToBounds {
tableView.setContentOffset(CGPointMake(0, tableView.contentOffset.y - 5), animated: false)
}
// logger.debug("LALAL OFFSET after \(self.tableView.contentOffset.y) \(self.tableView.indexPathsForVisibleRows!)")
scrollToRowOnAppear = 0
} else {
scrollToRowOnAppear = row
}
}
override public func viewIsAppearing(_ animated: Bool) {
super.viewIsAppearing(animated)
scrollToRowWhenKnowSize(scrollToRowOnAppear)
}
override func viewDidAppear(_ animated: Bool) {
tableView.clipsToBounds = false
parent?.viewIfLoaded?.clipsToBounds = false
@@ -196,23 +223,67 @@ struct ReverseList<Content: View>: UIViewControllerRepresentable {
}
func update(items: [ChatItem]) {
let wasCount = dataSource.snapshot().numberOfItems
var prevSnapshot = dataSource.snapshot()
let wasCount = prevSnapshot.numberOfItems
let insertedOneNewestItem = wasCount != 0 && representer.mergedItems.items.count - wasCount == 1 && prevSnapshot.itemIdentifiers.first! == self.representer.mergedItems.items[1]
logger.debug("LALAL WAS \(wasCount) will be \(self.representer.mergedItems.items.count)")
//self.representer.mergedItems = MergedItems.create(items, representer.unreadCount, representer.revealedItems, ItemsModel.shared.chatState)
var snapshot = NSDiffableDataSourceSnapshot<Section, MergedItem>()
snapshot.appendSections([.main])
snapshot.appendItems(representer.mergedItems.items)
let snapshot: NSDiffableDataSourceSnapshot<Section, MergedItem>
if insertedOneNewestItem {
prevSnapshot.insertItems([representer.mergedItems.items.first!], beforeItem: prevSnapshot.itemIdentifiers.first!)
snapshot = prevSnapshot
} else {
var snap = NSDiffableDataSourceSnapshot<Section, MergedItem>()
snap.appendSections([.main])
snap.appendItems(representer.mergedItems.items)
if snap.itemIdentifiers == prevSnapshot.itemIdentifiers {
logger.debug("LALAL SAME ITEMS, not rebuilding the tableview")
return
}
snapshot = snap
}
dataSource.defaultRowAnimation = .none
dataSource.apply(
snapshot,
animatingDifferences: wasCount != 0 && abs(representer.mergedItems.items.count - wasCount) == 1
)
let wasContentHeight = tableView.contentSize.height
let wasOffset = tableView.contentOffset.y
let countDiff = max(0, snapshot.numberOfItems - prevSnapshot.numberOfItems)
// Sets content offset on initial load
if wasCount == 0 {
tableView.setContentOffset(
CGPoint(x: 0, y: -InvertedTableView.inset),
animated: false
dataSource.apply(
snapshot,
animatingDifferences: insertedOneNewestItem
)
if let firstUnreadItem = snapshot.itemIdentifiers.lastIndex(where: { merged in merged.hasUnread() }) {
scrollToRowWhenKnowSize(firstUnreadItem)
} else {
tableView.setContentOffset(
CGPoint(x: 0, y: -InvertedTableView.inset),
animated: false
)
}
} else if wasCount != snapshot.numberOfItems {
tableView.beginUpdates()
dataSource.apply(
snapshot,
animatingDifferences: false
)
// tableView.scrollToRow(
// at: IndexPath(row: countDiff, section: 0),
// at: .top,
// animated: false
// )
tableView.endUpdates()
// if snapshot.itemIdentifiers[0].newest().item.id == prevSnapshot.itemIdentifiers[0].newest().item.id {
// // added new items to top
// } else {
// // added new items to bottom
// logger.debug("LALAL WAS HEIGHT \(wasContentHeight) now \(self.tableView.contentSize.height), offset was \(wasOffset), now \(self.tableView.contentOffset.y), will be \(self.tableView.contentOffset.y + (self.tableView.contentSize.height - wasContentHeight))")
// DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
// self.tableView.setContentOffset(CGPointMake(0, self.tableView.contentOffset.y + (self.tableView.contentSize.height - wasContentHeight)), animated: false)
// }
// }
}
updateFloatingButtons.send()
}
@@ -198,6 +198,7 @@
8C8118722C220B5B00E6FC94 /* Yams in Frameworks */ = {isa = PBXBuildFile; productRef = 8C8118712C220B5B00E6FC94 /* Yams */; };
8C81482C2BD91CD4002CBEC3 /* AudioDevicePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C81482B2BD91CD4002CBEC3 /* AudioDevicePicker.swift */; };
8C9BC2652C240D5200875A27 /* ThemeModeEditor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C9BC2642C240D5100875A27 /* ThemeModeEditor.swift */; };
8CAD466F2D15A8100078D18F /* ChatScrollHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CAD466E2D15A8100078D18F /* ChatScrollHelpers.swift */; };
8CAEF1502D11A6A000240F00 /* ChatItemsLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CAEF14F2D11A6A000240F00 /* ChatItemsLoader.swift */; };
8CB15EA02CFDA30600C28209 /* ChatItemsMerger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CB15E9F2CFDA30600C28209 /* ChatItemsMerger.swift */; };
8CB3476C2CF5CFFA006787A5 /* Ink in Frameworks */ = {isa = PBXBuildFile; productRef = 8CB3476B2CF5CFFA006787A5 /* Ink */; };
@@ -549,6 +550,7 @@
8C852B072C1086D100BA61E8 /* Color.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Color.swift; sourceTree = "<group>"; };
8C86EBE42C0DAE4F00E12243 /* ThemeManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemeManager.swift; sourceTree = "<group>"; };
8C9BC2642C240D5100875A27 /* ThemeModeEditor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemeModeEditor.swift; sourceTree = "<group>"; };
8CAD466E2D15A8100078D18F /* ChatScrollHelpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatScrollHelpers.swift; sourceTree = "<group>"; };
8CAEF14F2D11A6A000240F00 /* ChatItemsLoader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatItemsLoader.swift; sourceTree = "<group>"; };
8CB15E9F2CFDA30600C28209 /* ChatItemsMerger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatItemsMerger.swift; sourceTree = "<group>"; };
8CB3476D2CF5F58B006787A5 /* ConditionsWebView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConditionsWebView.swift; sourceTree = "<group>"; };
@@ -750,6 +752,7 @@
8CE848A22C5A0FA000D5C7C8 /* SelectableChatItemToolbars.swift */,
8CB15E9F2CFDA30600C28209 /* ChatItemsMerger.swift */,
8CAEF14F2D11A6A000240F00 /* ChatItemsLoader.swift */,
8CAD466E2D15A8100078D18F /* ChatScrollHelpers.swift */,
);
path = Chat;
sourceTree = "<group>";
@@ -1507,6 +1510,7 @@
5C6BA667289BD954009B8ECC /* DismissSheets.swift in Sources */,
5C577F7D27C83AA10006112D /* MarkdownHelp.swift in Sources */,
6407BA83295DA85D0082BA18 /* CIInvalidJSONView.swift in Sources */,
8CAD466F2D15A8100078D18F /* ChatScrollHelpers.swift in Sources */,
644EFFDE292BCD9D00525D5B /* ComposeVoiceView.swift in Sources */,
5CA059EB279559F40002BEB4 /* SimpleXApp.swift in Sources */,
64D0C2C029F9688300B38D5F /* UserAddressView.swift in Sources */,