Fixed a few message scrolling bugs and a couple minor UI tweaks

Fixed a bug where sending a quote message could result in the messages jumping incorrectly
Fixed a bug where the quote input UI wasn't styled correctly on dark themes
Fixed a minor padding issue
Fixed an issue where in-conversation search could be a bit jumpy (simplified logic greatly)
This commit is contained in:
Morgan Pretty 2022-10-10 11:48:48 +11:00
parent 0af00feed9
commit 2ad8fc381d
6 changed files with 47 additions and 70 deletions

View File

@ -1559,7 +1559,9 @@ extension ConversationVC:
body: cellViewModel.body,
timestampMs: cellViewModel.timestampMs,
attachments: cellViewModel.attachments,
linkPreviewAttachment: cellViewModel.linkPreviewAttachment
linkPreviewAttachment: cellViewModel.linkPreviewAttachment,
currentUserPublicKey: cellViewModel.currentUserPublicKey,
currentUserBlindedPublicKey: cellViewModel.currentUserBlindedPublicKey
)
guard let quoteDraft: QuotedReplyModel = maybeQuoteDraft else { return }

View File

@ -808,7 +808,8 @@ final class ConversationVC: BaseVC, ConversationSearchControllerDelegate, UITabl
/// 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 let itemChangeInfo: ItemChangeInfo = itemChangeInfo, itemChangeInfo.isInsertAtTop {
let oldCellHeight: CGFloat = self.tableView.rectForRow(at: itemChangeInfo.oldVisibleIndexPath).height
let oldCellRect: CGRect = self.tableView.rectForRow(at: itemChangeInfo.oldVisibleIndexPath)
let oldCellTopOffset: CGFloat = (self.tableView.frame.minY - self.tableView.convert(oldCellRect, to: self.tableView.superview).minY)
// 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)
@ -828,27 +829,13 @@ final class ConversationVC: BaseVC, ConversationSearchControllerDelegate, UITabl
// loaded was smaller than 2 pages (this will prevent calculating the frames of
// a large number of cells when getting search results which are very far away
// only to instantly start scrolling making the calculation redundant)
if (abs(itemChangeInfo.visibleIndexPath.row - itemChangeInfo.oldVisibleIndexPath.row) <= (ConversationViewModel.pageSize * 2)) {
UIView.performWithoutAnimation {
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)
}
let newTargetHeight: CGFloat? = self?.tableView
.rectForRow(at: itemChangeInfo.visibleIndexPath)
.height
let heightDiff: CGFloat = (oldCellHeight - (newTargetHeight ?? oldCellHeight))
self?.tableView.contentOffset.y += (calculatedRowHeights - heightDiff)
}
UIView.performWithoutAnimation {
self?.tableView.scrollToRow(
at: itemChangeInfo.visibleIndexPath,
at: .top,
animated: false
)
self?.tableView.contentOffset.y += oldCellTopOffset
}
if let focusedInteractionId: Int64 = self?.focusedInteractionId {
@ -1327,6 +1314,15 @@ final class ConversationVC: BaseVC, ConversationSearchControllerDelegate, UITabl
return
}
// Note: In this case we need to force a tableView layout to ensure updating the
// scroll position has the correct offset (otherwise there are some cases where
// the screen will jump up - eg. when sending a reply while the soft keyboard
// is visible)
UIView.performWithoutAnimation {
self.tableView.setNeedsLayout()
self.tableView.layoutIfNeeded()
}
let targetIndexPath: IndexPath = IndexPath(
row: (self.viewModel.interactionData[messagesSectionIndex].elements.count - 1),
section: messagesSectionIndex
@ -1337,7 +1333,6 @@ final class ConversationVC: BaseVC, ConversationSearchControllerDelegate, UITabl
animated: isAnimated
)
self.handleInitialOffsetBounceBug(targetIndexPath: targetIndexPath, at: .bottom)
self.viewModel.markAsRead(beforeInclusive: nil)
}
@ -1577,7 +1572,6 @@ final class ConversationVC: BaseVC, ConversationSearchControllerDelegate, UITabl
at: position,
animated: (self.didFinishInitialLayout && isAnimated)
)
self.handleInitialOffsetBounceBug(targetIndexPath: targetIndexPath, at: position)
// If we haven't finished the initial layout then we want to delay the highlight slightly
// so it doesn't look buggy with the push transition
@ -1604,7 +1598,6 @@ final class ConversationVC: BaseVC, ConversationSearchControllerDelegate, UITabl
}
self.tableView.scrollToRow(at: targetIndexPath, at: position, animated: true)
self.handleInitialOffsetBounceBug(targetIndexPath: targetIndexPath, at: position)
}
func highlightCellIfNeeded(interactionId: Int64) {
@ -1620,37 +1613,4 @@ final class ConversationVC: BaseVC, ConversationSearchControllerDelegate, UITabl
.highlight()
}
}
private func handleInitialOffsetBounceBug(targetIndexPath: IndexPath, at position: UITableView.ScrollPosition) {
/// Note: This code is a hack to prevent a weird 'bounce' behaviour that occurs when triggering the initial scroll due
/// to the UITableView properly calculating it's cell sizes (it seems to layout ~3 times each with slightly different sizes)
if !self.hasPerformedInitialScroll {
let initialUpdateTime: CFTimeInterval = CACurrentMediaTime()
var lastSize: CGSize = .zero
self.tableView.afterNextLayoutSubviews(
when: { [weak self] numSections, numRowInSections, updatedContentSize in
// If too much time has passed or the section/row count doesn't match then
// just stop the callback
guard
(CACurrentMediaTime() - initialUpdateTime) < 2 &&
lastSize != updatedContentSize &&
numSections > targetIndexPath.section &&
numRowInSections[targetIndexPath.section] > targetIndexPath.row
else { return true }
lastSize = updatedContentSize
self?.tableView.scrollToRow(
at: targetIndexPath,
at: position,
animated: false
)
return false
},
then: {}
)
}
}
}

View File

@ -245,8 +245,8 @@ final class InputView: UIView, InputViewButtonDelegate, InputTextViewDelegate, M
authorId: quoteDraftInfo.model.authorId,
quotedText: quoteDraftInfo.model.body,
threadVariant: threadVariant,
currentUserPublicKey: nil,
currentUserBlindedPublicKey: nil,
currentUserPublicKey: quoteDraftInfo.model.currentUserPublicKey,
currentUserBlindedPublicKey: quoteDraftInfo.model.currentUserBlindedPublicKey,
direction: (quoteDraftInfo.isOutgoing ? .outgoing : .incoming),
attachment: quoteDraftInfo.model.attachment,
hInset: hInset,

View File

@ -112,7 +112,7 @@ final class LinkPreviewView: UIView {
// Title label
let titleLabelContainer = UIView()
titleLabelContainer.addSubview(titleLabel)
titleLabel.pin(to: titleLabelContainer, withInset: Values.smallSpacing)
titleLabel.pin(to: titleLabelContainer, withInset: Values.mediumSpacing)
// Horizontal stack view
hStackView.addArrangedSubview(imageViewContainer)

View File

@ -187,14 +187,19 @@ final class QuoteView: UIView {
}
// Body label
let bodyLabel = UILabel()
let bodyLabel = TappableLabel()
bodyLabel.numberOfLines = 0
bodyLabel.lineBreakMode = .byTruncatingTail
let targetThemeColor: ThemeValue = (direction == .outgoing ?
.messageBubble_outgoingText :
.messageBubble_incomingText
)
let targetThemeColor: ThemeValue = {
switch mode {
case .regular: return (direction == .outgoing ?
.messageBubble_outgoingText :
.messageBubble_incomingText
)
case .draft: return .textPrimary
}
}()
bodyLabel.font = .systemFont(ofSize: Values.smallFontSize)
ThemeManager.onThemeChange(observer: bodyLabel) { [weak bodyLabel] theme, primaryColor in

View File

@ -12,6 +12,8 @@ public struct QuotedReplyModel {
public let contentType: String?
public let sourceFileName: String?
public let thumbnailDownloadFailed: Bool
public let currentUserPublicKey: String?
public let currentUserBlindedPublicKey: String?
// MARK: - Initialization
@ -23,7 +25,9 @@ public struct QuotedReplyModel {
attachment: Attachment?,
contentType: String?,
sourceFileName: String?,
thumbnailDownloadFailed: Bool
thumbnailDownloadFailed: Bool,
currentUserPublicKey: String?,
currentUserBlindedPublicKey: String?
) {
self.attachment = attachment
self.threadId = threadId
@ -33,6 +37,8 @@ public struct QuotedReplyModel {
self.contentType = contentType
self.sourceFileName = sourceFileName
self.thumbnailDownloadFailed = thumbnailDownloadFailed
self.currentUserPublicKey = currentUserPublicKey
self.currentUserBlindedPublicKey = currentUserBlindedPublicKey
}
public static func quotedReplyForSending(
@ -42,7 +48,9 @@ public struct QuotedReplyModel {
body: String?,
timestampMs: Int64,
attachments: [Attachment]?,
linkPreviewAttachment: Attachment?
linkPreviewAttachment: Attachment?,
currentUserPublicKey: String?,
currentUserBlindedPublicKey: String?
) -> QuotedReplyModel? {
guard variant == .standardOutgoing || variant == .standardIncoming else { return nil }
guard (body != nil && body?.isEmpty == false) || attachments?.isEmpty == false else { return nil }
@ -57,7 +65,9 @@ public struct QuotedReplyModel {
attachment: targetAttachment,
contentType: targetAttachment?.contentType,
sourceFileName: targetAttachment?.sourceFilename,
thumbnailDownloadFailed: false
thumbnailDownloadFailed: false,
currentUserPublicKey: currentUserPublicKey,
currentUserBlindedPublicKey: currentUserBlindedPublicKey
)
}
}