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

@ -218,12 +218,15 @@ public final class SearchResultsBar: UIView {
} }
func updateResults(results: [Int64]?) { func updateResults(results: [Int64]?) {
if let results: [Int64] = results, !results.isEmpty { currentIndex = {
currentIndex = min(currentIndex ?? 0, results.count - 1) guard let results: [Int64] = results, !results.isEmpty else { return nil }
}
else { if let currentIndex: Int = currentIndex {
currentIndex = nil return max(0, min(currentIndex, results.count - 1))
} }
return 0
}()
self.results = results 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 // If anything was inserted at the top then we need to maintain the current
// offset so always return a 'top' insert location // offset so always return a 'top' insert location
switch (insertedAtTop, insertedAtBot) { switch (insertedAtTop, insertedAtBot, isLoadingMore) {
case (true, _): return .top case (true, _, true), (true, false, false): return .top
case (false, true): return .bottom case (false, true, _): return .bottom
case (false, false): return .other case (false, false, _), (true, true, false): return .other
} }
}(), }(),
wasCloseToBottom: isCloseToBottom, 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 /// 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 /// 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 let cellSorting: (MessageCell, MessageCell) -> Bool = { lhs, rhs -> Bool in
if !lhs.isHidden && rhs.isHidden { return true } if !lhs.isHidden && rhs.isHidden { return true }
if lhs.isHidden && !rhs.isHidden { return false } 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 })? .first(where: { cell -> Bool in cell.viewModel?.id == itemChangeInfo.visibleInteractionId })?
.frame) .frame)
.defaulting(to: self.tableView.rectForRow(at: itemChangeInfo.oldVisibleIndexPath)) .defaulting(to: self.tableView.rectForRow(at: itemChangeInfo.oldVisibleIndexPath))
let oldContentSize: CGSize = self.tableView.contentSize
let oldOffsetFromTop: CGFloat = (self.tableView.contentOffset.y - oldRect.minY) // The the user triggered the 'scrollToTop' animation (by tapping in the nav bar) then we
let oldOffsetFromBottom: CGFloat = (oldContentSize.height - self.tableView.contentOffset.y) // 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 // Wait until the tableView has completed a layout and reported the correct number of
// sections/rows and then update the contentOffset // sections/rows and then update the contentOffset
@ -763,63 +766,26 @@ final class ConversationVC: BaseVC, OWSConversationSettingsViewDelegate, Convers
}, },
then: { [weak self] in then: { [weak self] in
UIView.performWithoutAnimation { UIView.performWithoutAnimation {
self?.tableView.scrollToRow( let calculatedRowHeights: CGFloat = (0..<itemChangeInfo.visibleIndexPath.row)
at: (itemChangeInfo.insertLocation == .top ? .reduce(into: 0) { result, next in
itemChangeInfo.visibleIndexPath : result += (self?.tableView
itemChangeInfo.lastVisibleIndexPath .rectForRow(
), at: IndexPath(
at: (itemChangeInfo.insertLocation == .top ? row: next,
.top : section: itemChangeInfo.visibleIndexPath.section
.bottom )
), )
animated: false .height)
) .defaulting(to: 0)
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))
} }
else { let newTargetRect: CGRect? = self?.tableView.rectForRow(at: itemChangeInfo.visibleIndexPath)
self?.tableView.contentOffset.y = ((newRect.minY + heightDiff) + oldOffsetFromTop) let heightDiff: CGFloat = (oldRect.height - (newTargetRect ?? oldRect).height)
}
} self?.tableView.contentOffset.y += (calculatedRowHeights - heightDiff)
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)
}
)
}
} }
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 // If we had a focusedInteractionId then scroll to it (and hide the search
// result bar loading indicator) // result bar loading indicator)
self?.searchController.resultsBar.stopLoading() self?.searchController.resultsBar.stopLoading()
@ -829,11 +795,6 @@ final class ConversationVC: BaseVC, OWSConversationSettingsViewDelegate, Convers
highlight: (self?.shouldHighlightNextScrollToInteraction == 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)
}
} }
// Complete page loading // 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) // Reload the table content (animate changes if we aren't inserting at the top)
self.tableView.reload( self.tableView.reload(
@ -857,6 +838,10 @@ final class ConversationVC: BaseVC, OWSConversationSettingsViewDelegate, Convers
self?.viewModel.updateInteractionData(updatedData) self?.viewModel.updateInteractionData(updatedData)
} }
if itemChangeInfo.insertLocation == .bottom || itemChangeInfo.insertLocation == .other {
CATransaction.commit()
}
// Mark received messages as read // Mark received messages as read
viewModel.markAllAsRead() viewModel.markAllAsRead()
viewModel.sentMessageBeforeUpdate = false viewModel.sentMessageBeforeUpdate = false

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

Loading…
Cancel
Save