diff --git a/apps/ios/Shared/Model/ChatModel.swift b/apps/ios/Shared/Model/ChatModel.swift index b302443d11..9ee821cd36 100644 --- a/apps/ios/Shared/Model/ChatModel.swift +++ b/apps/ios/Shared/Model/ChatModel.swift @@ -50,6 +50,10 @@ class ItemsModel: ObservableObject { var reversedChatItems: [ChatItem] = [] { willSet { publisher.send() } } + var itemAdded = false { + willSet { publisher.send() } + } + init() { publisher .throttle(for: 0.25, scheduler: DispatchQueue.main, latest: true) @@ -389,6 +393,7 @@ final class ChatModel: ObservableObject { ci.meta.itemStatus = status } im.reversedChatItems.insert(ci, at: hasLiveDummy ? 1 : 0) + im.itemAdded = true } return true } @@ -483,6 +488,7 @@ final class ChatModel: ObservableObject { let cItem = ChatItem.liveDummy(chatInfo.chatType) withAnimation { im.reversedChatItems.insert(cItem, at: 0) + im.itemAdded = true } return cItem } @@ -842,7 +848,13 @@ final class ChatModel: ObservableObject { } i += 1 } - return UnreadChatItemCounts(isNearBottom: totalBelow < 16, unreadBelow: unreadBelow) + 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, + isReallyNearBottom: totalBelow < 2, + unreadBelow: unreadBelow + ) } func topItemInView(itemsInView: Set) -> ChatItem? { @@ -881,6 +893,7 @@ struct NTFContactRequest { struct UnreadChatItemCounts: Equatable { var isNearBottom: Bool + var isReallyNearBottom: Bool var unreadBelow: Int } diff --git a/apps/ios/Shared/Views/Chat/ChatView.swift b/apps/ios/Shared/Views/Chat/ChatView.swift index 7951d1865f..04e084af15 100644 --- a/apps/ios/Shared/Views/Chat/ChatView.swift +++ b/apps/ios/Shared/Views/Chat/ChatView.swift @@ -413,11 +413,20 @@ struct ChatView: View { chat.copyFrom(c) showChatInfoSheet = false loadChat(chat: c) + scrollModel.scrollToBottom() } } .onChange(of: im.reversedChatItems) { _ in floatingButtonModel.chatItemsChanged() } + .onChange(of: im.itemAdded) { added in + if added { + im.itemAdded = false + if floatingButtonModel.unreadChatItemCounts.isReallyNearBottom { + scrollModel.scrollToBottom() + } + } + } } } @@ -450,19 +459,19 @@ struct ChatView: View { init() { unreadChatItemCounts = UnreadChatItemCounts( isNearBottom: true, + isReallyNearBottom: true, unreadBelow: 0 ) events .receive(on: DispatchQueue.global(qos: .background)) .scan(Set()) { itemsInView, event in - return switch event { - case let .appeared(viewId): - itemsInView.union([viewId]) - case let .disappeared(viewId): - itemsInView.subtracting([viewId]) - case .chatItemsChanged: - itemsInView + var updated = itemsInView + switch event { + case let .appeared(viewId): updated.insert(viewId) + case let .disappeared(viewId): updated.remove(viewId) + case .chatItemsChanged: () } + return updated } .map { ChatModel.shared.unreadChatItemCounts(itemsInView: $0) } .removeDuplicates()