diff --git a/Session/Conversations/ConversationVC.swift b/Session/Conversations/ConversationVC.swift index 73038c611..92ce1c2f1 100644 --- a/Session/Conversations/ConversationVC.swift +++ b/Session/Conversations/ConversationVC.swift @@ -1194,14 +1194,17 @@ final class ConversationVC: BaseVC, OWSConversationSettingsViewDelegate, Convers return } + let targetIndexPath: IndexPath = IndexPath( + row: (self.viewModel.interactionData[messagesSectionIndex].elements.count - 1), + section: messagesSectionIndex + ) self.tableView.scrollToRow( - at: IndexPath( - row: (self.viewModel.interactionData[messagesSectionIndex].elements.count - 1), - section: messagesSectionIndex - ), + at: targetIndexPath, at: .bottom, animated: isAnimated ) + + self.handleInitialOffsetBounceBug(targetIndexPath: targetIndexPath, at: .bottom) } func scrollViewWillBeginDragging(_ scrollView: UIScrollView) { @@ -1398,6 +1401,7 @@ final class ConversationVC: BaseVC, OWSConversationSettingsViewDelegate, Convers 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 @@ -1424,6 +1428,7 @@ final class ConversationVC: BaseVC, OWSConversationSettingsViewDelegate, Convers } self.tableView.scrollToRow(at: targetIndexPath, at: position, animated: true) + self.handleInitialOffsetBounceBug(targetIndexPath: targetIndexPath, at: position) } func highlightCellIfNeeded(interactionId: Int64) { @@ -1439,4 +1444,32 @@ final class ConversationVC: BaseVC, OWSConversationSettingsViewDelegate, Convers .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] a, b, updatedContentSize in + guard (CACurrentMediaTime() - initialUpdateTime) < 2 && lastSize != updatedContentSize else { + return true + } + + lastSize = updatedContentSize + + self?.tableView.scrollToRow( + at: targetIndexPath, + at: position, + animated: false + ) + + return false + }, + then: {} + ) + } + } } diff --git a/Session/Conversations/Views & Modals/InsetLockableTableView.swift b/Session/Conversations/Views & Modals/InsetLockableTableView.swift index cb0abdc1f..1f0fb7980 100644 --- a/Session/Conversations/Views & Modals/InsetLockableTableView.swift +++ b/Session/Conversations/Views & Modals/InsetLockableTableView.swift @@ -29,7 +29,7 @@ public class InsetLockableTableView: UITableView { // Store the callback locally to prevent infinite loops var callback: (() -> ())? - if self.testCallbackCondition() { + if self.checkCallbackCondition() { callback = self.afterLayoutSubviewsCallback self.afterLayoutSubviewsCallback = nil } @@ -61,7 +61,7 @@ public class InsetLockableTableView: UITableView { self.afterLayoutSubviewsCallback = callback } - private func testCallbackCondition() -> Bool { + private func checkCallbackCondition() -> Bool { guard self.callbackCondition != nil else { return false } let numSections: Int = self.numberOfSections diff --git a/SessionMessagingKit/Sending & Receiving/MessageReceiver.swift b/SessionMessagingKit/Sending & Receiving/MessageReceiver.swift index b1ba41403..5b4dca427 100644 --- a/SessionMessagingKit/Sending & Receiving/MessageReceiver.swift +++ b/SessionMessagingKit/Sending & Receiving/MessageReceiver.swift @@ -345,7 +345,7 @@ public enum MessageReceiver { // Download the profile picture if needed db.afterNextTransactionCommit { _ in - ProfileManager.downloadAvatar(for: profile) + ProfileManager.downloadAvatar(for: updatedProfile) } } } diff --git a/SessionMessagingKit/Utilities/ProfileManager.swift b/SessionMessagingKit/Utilities/ProfileManager.swift index 885fdf0d3..992fdaaa6 100644 --- a/SessionMessagingKit/Utilities/ProfileManager.swift +++ b/SessionMessagingKit/Utilities/ProfileManager.swift @@ -129,11 +129,12 @@ public struct ProfileManager { return } + let queue: DispatchQueue = DispatchQueue.global(qos: .default) let fileName: String = UUID().uuidString.appendingFileExtension("jpg") let filePath: String = ProfileManager.profileAvatarFilepath(filename: fileName) var backgroundTask: OWSBackgroundTask? = OWSBackgroundTask(label: funcName) - DispatchQueue.global(qos: .default).async { + queue.async { OWSLogger.verbose("downloading profile avatar: \(profile.id)") currentAvatarDownloads.mutate { $0.insert(profile.id) } @@ -141,7 +142,7 @@ public struct ProfileManager { FileServerAPI .download(fileId, useOldServer: useOldServer) - .done { data in + .done(on: queue) { data in currentAvatarDownloads.mutate { $0.remove(profile.id) } GRDBStorage.shared.write { db in @@ -189,6 +190,13 @@ public struct ProfileManager { // isn't used if backgroundTask != nil { backgroundTask = nil } } + .catch(on: queue) { _ in + currentAvatarDownloads.mutate { $0.remove(profile.id) } + + // Redundant but without reading 'backgroundTask' it will warn that the variable + // isn't used + if backgroundTask != nil { backgroundTask = nil } + } .retainUntilComplete() } }