Fixed a few bugs with scrolling behaviour on the conversation screen

Fixed a couple of bugs with in-conversation search
pull/612/head
Morgan Pretty 2 years ago
parent 12f1e95534
commit 4a29ad1f4f

@ -6818,7 +6818,7 @@
CODE_SIGN_ENTITLEMENTS = Session/Meta/Signal.entitlements;
CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CURRENT_PROJECT_VERSION = 346;
CURRENT_PROJECT_VERSION = 350;
DEVELOPMENT_TEAM = SUQ8J2PCT7;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
@ -6857,7 +6857,7 @@
"$(SRCROOT)",
);
LLVM_LTO = NO;
MARKETING_VERSION = 1.12.9;
MARKETING_VERSION = 2.0.0;
OTHER_LDFLAGS = "$(inherited)";
OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\"";
PRODUCT_BUNDLE_IDENTIFIER = "com.loki-project.loki-messenger";
@ -6890,7 +6890,7 @@
CODE_SIGN_ENTITLEMENTS = Session/Meta/Signal.entitlements;
CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CURRENT_PROJECT_VERSION = 346;
CURRENT_PROJECT_VERSION = 350;
DEVELOPMENT_TEAM = SUQ8J2PCT7;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
@ -6929,7 +6929,7 @@
"$(SRCROOT)",
);
LLVM_LTO = NO;
MARKETING_VERSION = 1.12.9;
MARKETING_VERSION = 2.0.0;
OTHER_LDFLAGS = "$(inherited)";
PRODUCT_BUNDLE_IDENTIFIER = "com.loki-project.loki-messenger";
PRODUCT_NAME = Session;

@ -218,12 +218,15 @@ public final class SearchResultsBar: UIView {
}
func updateResults(results: [Int64]?) {
if let results: [Int64] = results, !results.isEmpty {
currentIndex = min(currentIndex ?? 0, results.count - 1)
}
else {
currentIndex = nil
}
currentIndex = {
guard let results: [Int64] = results, !results.isEmpty else { return nil }
if let currentIndex: Int = currentIndex {
return max(0, min(currentIndex, results.count - 1))
}
return 0
}()
self.results = results

@ -715,10 +715,10 @@ final class ConversationVC: BaseVC, OWSConversationSettingsViewDelegate, Convers
// If anything was inserted at the top then we need to maintain the current
// offset so always return a 'top' insert location
switch (insertedAtTop, insertedAtBot) {
case (true, _): return .top
case (false, true): return .bottom
case (false, false): return .other
switch (insertedAtTop, insertedAtBot, isLoadingMore) {
case (true, _, true), (true, false, false): return .top
case (false, true, _): return .bottom
case (false, false, _), (true, true, false): return .other
}
}(),
wasCloseToBottom: isCloseToBottom,
@ -737,7 +737,7 @@ final class ConversationVC: BaseVC, OWSConversationSettingsViewDelegate, Convers
///
/// Unfortunately the UITableView also does some weird things when updating (where it won't have updated it's internal data until
/// after it performs the next layout); the below code checks a condition on layout and if it passes it calls a closure
if itemChangeInfo.insertLocation != .none {
if itemChangeInfo.insertLocation == .top {
let cellSorting: (MessageCell, MessageCell) -> Bool = { lhs, rhs -> Bool in
if !lhs.isHidden && rhs.isHidden { return true }
if lhs.isHidden && !rhs.isHidden { return false }
@ -750,9 +750,12 @@ final class ConversationVC: BaseVC, OWSConversationSettingsViewDelegate, Convers
.first(where: { cell -> Bool in cell.viewModel?.id == itemChangeInfo.visibleInteractionId })?
.frame)
.defaulting(to: self.tableView.rectForRow(at: itemChangeInfo.oldVisibleIndexPath))
let oldContentSize: CGSize = self.tableView.contentSize
let oldOffsetFromTop: CGFloat = (self.tableView.contentOffset.y - oldRect.minY)
let oldOffsetFromBottom: CGFloat = (oldContentSize.height - self.tableView.contentOffset.y)
// The the user triggered the 'scrollToTop' animation (by tapping in the nav bar) then we
// need to stop the animation before attempting to lock the offset (otherwise things break)
if itemChangeInfo.firstIndexIsVisible {
self.tableView.setContentOffset(self.tableView.contentOffset, animated: false)
}
// Wait until the tableView has completed a layout and reported the correct number of
// sections/rows and then update the contentOffset
@ -763,63 +766,26 @@ final class ConversationVC: BaseVC, OWSConversationSettingsViewDelegate, Convers
},
then: { [weak self] in
UIView.performWithoutAnimation {
self?.tableView.scrollToRow(
at: (itemChangeInfo.insertLocation == .top ?
itemChangeInfo.visibleIndexPath :
itemChangeInfo.lastVisibleIndexPath
),
at: (itemChangeInfo.insertLocation == .top ?
.top :
.bottom
),
animated: false
)
self?.tableView.layoutIfNeeded()
let newContentSize: CGSize = (self?.tableView.contentSize)
.defaulting(to: oldContentSize)
/// **Note:** I wasn't able to get a prober equation to handle both "insert" and "insert at top off screen", it
/// seems that the 'contentOffset' value won't expose negative values (eg. when you over-scroll and trigger
/// the bounce effect) and this results in requiring the conditional logic below
if itemChangeInfo.insertLocation == .top {
let newRect: CGRect = (self?.tableView.subviews
.compactMap { $0 as? MessageCell }
.sorted(by: cellSorting)
.first(where: { $0.viewModel?.id == itemChangeInfo.visibleInteractionId })?
.frame)
.defaulting(to: oldRect)
let heightDiff: CGFloat = (oldRect.height - newRect.height)
if itemChangeInfo.firstIndexIsVisible {
self?.tableView.contentOffset.y = (newRect.minY - (oldRect.minY + heightDiff))
let calculatedRowHeights: CGFloat = (0..<itemChangeInfo.visibleIndexPath.row)
.reduce(into: 0) { result, next in
result += (self?.tableView
.rectForRow(
at: IndexPath(
row: next,
section: itemChangeInfo.visibleIndexPath.section
)
)
.height)
.defaulting(to: 0)
}
else {
self?.tableView.contentOffset.y = ((newRect.minY + heightDiff) + oldOffsetFromTop)
}
}
else {
self?.tableView.contentOffset.y = (newContentSize.height - oldOffsetFromBottom)
}
/// **Note:** There is yet another weird issue where the tableView will layout again shortly after the initial
/// layout with a slightly different contentSize (usually about 8pt off), this catches that case and prevents it
/// from affecting the UI
if !itemChangeInfo.firstIndexIsVisible {
self?.tableView.afterNextLayoutSubviews(
when: { _, _, contentSize in (contentSize.height != newContentSize.height) },
then: { [weak self] in
let finalContentSize: CGSize = (self?.tableView.contentSize)
.defaulting(to: newContentSize)
self?.tableView.contentOffset.y += (finalContentSize.height - newContentSize.height)
}
)
}
let newTargetRect: CGRect? = self?.tableView.rectForRow(at: itemChangeInfo.visibleIndexPath)
let heightDiff: CGFloat = (oldRect.height - (newTargetRect ?? oldRect).height)
self?.tableView.contentOffset.y += (calculatedRowHeights - heightDiff)
}
DispatchQueue.main.async { [weak self] in
if let focusedInteractionId: Int64 = self?.focusedInteractionId {
if let focusedInteractionId: Int64 = self?.focusedInteractionId {
DispatchQueue.main.async { [weak self] in
// If we had a focusedInteractionId then scroll to it (and hide the search
// result bar loading indicator)
self?.searchController.resultsBar.stopLoading()
@ -829,11 +795,6 @@ final class ConversationVC: BaseVC, OWSConversationSettingsViewDelegate, Convers
highlight: (self?.shouldHighlightNextScrollToInteraction == true)
)
}
else if itemChangeInfo.sentMessageBeforeUpdate || itemChangeInfo.wasCloseToBottom {
// Scroll to the bottom if an interaction was just inserted and we either
// just sent a message or are close enough to the bottom
self?.scrollToBottom(isAnimated: true)
}
}
// Complete page loading
@ -842,6 +803,26 @@ final class ConversationVC: BaseVC, OWSConversationSettingsViewDelegate, Convers
}
)
}
else if itemChangeInfo.insertLocation == .bottom || itemChangeInfo.insertLocation == .other {
CATransaction.begin()
CATransaction.setCompletionBlock { [weak self] in
if let focusedInteractionId: Int64 = self?.focusedInteractionId {
// If we had a focusedInteractionId then scroll to it (and hide the search
// result bar loading indicator)
self?.searchController.resultsBar.stopLoading()
self?.scrollToInteractionIfNeeded(
with: focusedInteractionId,
isAnimated: true,
highlight: (self?.shouldHighlightNextScrollToInteraction == true)
)
}
else if itemChangeInfo.sentMessageBeforeUpdate || itemChangeInfo.wasCloseToBottom {
// Scroll to the bottom if an interaction was just inserted and we either
// just sent a message or are close enough to the bottom
self?.scrollToBottom(isAnimated: true)
}
}
}
// Reload the table content (animate changes if we aren't inserting at the top)
self.tableView.reload(
@ -857,6 +838,10 @@ final class ConversationVC: BaseVC, OWSConversationSettingsViewDelegate, Convers
self?.viewModel.updateInteractionData(updatedData)
}
if itemChangeInfo.insertLocation == .bottom || itemChangeInfo.insertLocation == .other {
CATransaction.commit()
}
// Mark received messages as read
viewModel.markAllAsRead()
viewModel.sentMessageBeforeUpdate = false

@ -378,7 +378,7 @@ public class PagedDatabaseObserver<ObservedTable, T>: TransactionObserver where
let targetIndex: Int = maybeIndex.map({ max(0, min(totalCount, $0)) }),
(
targetIndex < currentPageInfo.pageOffset ||
targetIndex > cacheCurrentEndIndex
targetIndex >= cacheCurrentEndIndex
)
else { return nil }

Loading…
Cancel
Save