diff --git a/apps/ios/Shared/Model/ChatModel.swift b/apps/ios/Shared/Model/ChatModel.swift index 62d0051aa1..59ffba203d 100644 --- a/apps/ios/Shared/Model/ChatModel.swift +++ b/apps/ios/Shared/Model/ChatModel.swift @@ -897,21 +897,14 @@ final class ChatModel: ObservableObject { func unreadChatItemCounts(itemsInView: Set) -> UnreadChatItemCounts { var i = 0 - var totalBelow = 0 var unreadBelow = 0 while i < im.reversedChatItems.count - 1 && !itemsInView.contains(im.reversedChatItems[i].viewId) { - totalBelow += 1 if im.reversedChatItems[i].isRcvNew { unreadBelow += 1 } i += 1 } - return UnreadChatItemCounts( - // TODO these thresholds account for the fact that items are still "visible" while - // covered by compose area, they should be replaced with the actual height in pixels below the screen. - isNearBottom: totalBelow < 15, - unreadBelow: unreadBelow - ) + return UnreadChatItemCounts(unreadBelow: unreadBelow) } func topItemInView(itemsInView: Set) -> ChatItem? { @@ -935,7 +928,6 @@ struct NTFContactRequest { } struct UnreadChatItemCounts: Equatable { - var isNearBottom: Bool var unreadBelow: Int } diff --git a/apps/ios/Shared/Views/Chat/ChatView.swift b/apps/ios/Shared/Views/Chat/ChatView.swift index a22a56722a..a6fbf435ef 100644 --- a/apps/ios/Shared/Views/Chat/ChatView.swift +++ b/apps/ios/Shared/Views/Chat/ChatView.swift @@ -395,7 +395,11 @@ struct ChatView: View { let cInfo = chat.chatInfo let mergedItems = filtered(im.reversedChatItems) return GeometryReader { g in - ReverseList(items: mergedItems, scrollState: $scrollModel.state) { ci in + ReverseList( + items: mergedItems, + scrollState: $scrollModel.state, + isFarFromBottom: $scrollModel.isFarFromBottom + ) { ci in let voiceNoFrame = voiceWithoutFrame(ci) let maxWidth = cInfo.chatType == .group ? voiceNoFrame @@ -470,10 +474,7 @@ struct ChatView: View { private var bag = Set() init() { - unreadChatItemCounts = UnreadChatItemCounts( - isNearBottom: true, - unreadBelow: 0 - ) + unreadChatItemCounts = UnreadChatItemCounts(unreadBelow: 0) events .receive(on: DispatchQueue.global(qos: .background)) .scan(Set()) { itemsInView, event in @@ -537,7 +538,7 @@ struct ChatView: View { .onTapGesture { scrollModel.scrollToBottom() } - } else if !counts.isNearBottom { + } else if scrollModel.isFarFromBottom { circleButton { Image(systemName: "chevron.down") .foregroundColor(theme.colors.primary) diff --git a/apps/ios/Shared/Views/Chat/ReverseList.swift b/apps/ios/Shared/Views/Chat/ReverseList.swift index 33b1265e4e..33d3b9b693 100644 --- a/apps/ios/Shared/Views/Chat/ReverseList.swift +++ b/apps/ios/Shared/Views/Chat/ReverseList.swift @@ -14,6 +14,7 @@ struct ReverseList: UIV let items: Array @Binding var scrollState: ReverseListScrollModel.State + @Binding var isFarFromBottom: Bool /// Closure, that returns user interface for a given item let content: (Item) -> Content @@ -175,16 +176,22 @@ struct ReverseList: UIV ) } + override func scrollViewDidScroll(_ scrollView: UIScrollView) { + let adjustedOffset = tableView.contentOffset.y + InvertedTableView.inset + if representer.isFarFromBottom { + if adjustedOffset < 800 { + DispatchQueue.main.async { self.representer.isFarFromBottom = false } + } + } else if adjustedOffset > 1000 { + DispatchQueue.main.async { self.representer.isFarFromBottom = true } + } + } + private var isNearBottom: Bool { let adjustedOffset = tableView.contentOffset.y + InvertedTableView.inset return adjustedOffset > 0 && adjustedOffset < 500 } - private var isFarFromBottom: Bool { - let adjustedOffset = tableView.contentOffset.y + InvertedTableView.inset - return adjustedOffset > 1000 - } - private var itemId: Item.ID? private var retainedItems: [Item]? private var updateInProgress = false @@ -217,7 +224,7 @@ struct ReverseList: UIV itemCount = items.count } - func _update(items: Array, animated: Bool, completion: (() -> Void)? = nil) { + private func _update(items: [Item], animated: Bool, completion: (() -> Void)? = nil) { var snapshot = NSDiffableDataSourceSnapshot() snapshot.appendSections([.main]) snapshot.appendItems(items) @@ -281,6 +288,7 @@ class ReverseListScrollModel: ObservableObject { } @Published var state: State = .atDestination + @Published var isFarFromBottom: Bool = false func scrollToNextPage() { state = .scrollingTo(.nextPage)