Merge pull request #926 from mpretty-cyro/fix/rare-multi-threading-crash

Fixed a crash which could occur when scrolling conversation messages
pull/939/head
Morgan Pretty 9 months ago committed by GitHub
commit e427e59544
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -49,11 +49,13 @@ public class ConversationViewModel: OWSAudioPlayerDelegate {
private var markAsReadPublisher: AnyPublisher<Void, Never>? private var markAsReadPublisher: AnyPublisher<Void, Never>?
public lazy var blockedBannerMessage: String = { public lazy var blockedBannerMessage: String = {
switch self.threadData.threadVariant { let threadData: SessionThreadViewModel = self._threadData.wrappedValue
switch threadData.threadVariant {
case .contact: case .contact:
let name: String = Profile.displayName( let name: String = Profile.displayName(
id: self.threadData.threadId, id: threadData.threadId,
threadVariant: self.threadData.threadVariant threadVariant: threadData.threadVariant
) )
return "\(name) is blocked. Unblock them?" return "\(name) is blocked. Unblock them?"
@ -140,16 +142,18 @@ public class ConversationViewModel: OWSAudioPlayerDelegate {
self.focusedInteractionInfo = (focusedInteractionInfo ?? initialData?.initialUnreadInteractionInfo) self.focusedInteractionInfo = (focusedInteractionInfo ?? initialData?.initialUnreadInteractionInfo)
self.focusBehaviour = (focusedInteractionInfo == nil ? .none : .highlight) self.focusBehaviour = (focusedInteractionInfo == nil ? .none : .highlight)
self.initialUnreadInteractionId = initialData?.initialUnreadInteractionInfo?.id self.initialUnreadInteractionId = initialData?.initialUnreadInteractionInfo?.id
self.threadData = SessionThreadViewModel( self._threadData = Atomic(
threadId: threadId, SessionThreadViewModel(
threadVariant: threadVariant, threadId: threadId,
threadIsNoteToSelf: (initialData?.currentUserPublicKey == threadId), threadVariant: threadVariant,
threadIsBlocked: initialData?.threadIsBlocked, threadIsNoteToSelf: (initialData?.currentUserPublicKey == threadId),
currentUserIsClosedGroupMember: initialData?.currentUserIsClosedGroupMember, threadIsBlocked: initialData?.threadIsBlocked,
openGroupPermissions: initialData?.openGroupPermissions currentUserIsClosedGroupMember: initialData?.currentUserIsClosedGroupMember,
).populatingCurrentUserBlindedKeys( openGroupPermissions: initialData?.openGroupPermissions
currentUserBlinded15PublicKeyForThisThread: initialData?.blinded15Key, ).populatingCurrentUserBlindedKeys(
currentUserBlinded25PublicKeyForThisThread: initialData?.blinded25Key currentUserBlinded15PublicKeyForThisThread: initialData?.blinded15Key,
currentUserBlinded25PublicKeyForThisThread: initialData?.blinded25Key
)
) )
self.pagedDataObserver = nil self.pagedDataObserver = nil
@ -179,8 +183,10 @@ public class ConversationViewModel: OWSAudioPlayerDelegate {
// MARK: - Thread Data // MARK: - Thread Data
private var _threadData: Atomic<SessionThreadViewModel>
/// This value is the current state of the view /// This value is the current state of the view
public private(set) var threadData: SessionThreadViewModel public var threadData: SessionThreadViewModel { _threadData.wrappedValue }
/// This is all the data the screen needs to populate itself, please see the following link for tips to help optimise /// This is all the data the screen needs to populate itself, please see the following link for tips to help optimise
/// performance https://github.com/groue/GRDB.swift#valueobservation-performance /// performance https://github.com/groue/GRDB.swift#valueobservation-performance
@ -200,6 +206,7 @@ public class ConversationViewModel: OWSAudioPlayerDelegate {
.trackingConstantRegion { [weak self] db -> SessionThreadViewModel? in .trackingConstantRegion { [weak self] db -> SessionThreadViewModel? in
let userPublicKey: String = getUserHexEncodedPublicKey(db) let userPublicKey: String = getUserHexEncodedPublicKey(db)
let recentReactionEmoji: [String] = try Emoji.getRecent(db, withDefaultEmoji: true) let recentReactionEmoji: [String] = try Emoji.getRecent(db, withDefaultEmoji: true)
let oldThreadData: SessionThreadViewModel? = self?._threadData.wrappedValue
let threadViewModel: SessionThreadViewModel? = try SessionThreadViewModel let threadViewModel: SessionThreadViewModel? = try SessionThreadViewModel
.conversationQuery(threadId: threadId, userPublicKey: userPublicKey) .conversationQuery(threadId: threadId, userPublicKey: userPublicKey)
.fetchOne(db) .fetchOne(db)
@ -209,8 +216,8 @@ public class ConversationViewModel: OWSAudioPlayerDelegate {
.map { viewModel -> SessionThreadViewModel in .map { viewModel -> SessionThreadViewModel in
viewModel.populatingCurrentUserBlindedKeys( viewModel.populatingCurrentUserBlindedKeys(
db, db,
currentUserBlinded15PublicKeyForThisThread: self?.threadData.currentUserBlinded15PublicKey, currentUserBlinded15PublicKeyForThisThread: oldThreadData?.currentUserBlinded15PublicKey,
currentUserBlinded25PublicKeyForThisThread: self?.threadData.currentUserBlinded25PublicKey currentUserBlinded25PublicKeyForThisThread: oldThreadData?.currentUserBlinded25PublicKey
) )
} }
} }
@ -219,7 +226,7 @@ public class ConversationViewModel: OWSAudioPlayerDelegate {
} }
public func updateThreadData(_ updatedData: SessionThreadViewModel) { public func updateThreadData(_ updatedData: SessionThreadViewModel) {
self.threadData = updatedData self._threadData.mutate { $0 = updatedData }
} }
// MARK: - Interaction Data // MARK: - Interaction Data
@ -393,6 +400,7 @@ public class ConversationViewModel: OWSAudioPlayerDelegate {
optimisticMessages: [MessageViewModel]?, optimisticMessages: [MessageViewModel]?,
initialUnreadInteractionId: Int64? initialUnreadInteractionId: Int64?
) -> [SectionModel] { ) -> [SectionModel] {
let threadData: SessionThreadViewModel = self._threadData.wrappedValue
let typingIndicator: MessageViewModel? = data.first(where: { $0.isTypingIndicator == true }) let typingIndicator: MessageViewModel? = data.first(where: { $0.isTypingIndicator == true })
let sortedData: [MessageViewModel] = data let sortedData: [MessageViewModel] = data
.filter { $0.id != MessageViewModel.optimisticUpdateId } // Remove old optimistic updates .filter { $0.id != MessageViewModel.optimisticUpdateId } // Remove old optimistic updates
@ -498,6 +506,7 @@ public class ConversationViewModel: OWSAudioPlayerDelegate {
) -> OptimisticMessageData { ) -> OptimisticMessageData {
// Generate the optimistic data // Generate the optimistic data
let optimisticMessageId: UUID = UUID() let optimisticMessageId: UUID = UUID()
let threadData: SessionThreadViewModel = self._threadData.wrappedValue
let currentUserProfile: Profile = Profile.fetchOrCreateCurrentUser() let currentUserProfile: Profile = Profile.fetchOrCreateCurrentUser()
let interaction: Interaction = Interaction( let interaction: Interaction = Interaction(
threadId: threadData.threadId, threadId: threadData.threadId,
@ -658,7 +667,7 @@ public class ConversationViewModel: OWSAudioPlayerDelegate {
// MARK: - Mentions // MARK: - Mentions
public func mentions(for query: String = "") -> [MentionInfo] { public func mentions(for query: String = "") -> [MentionInfo] {
let threadData: SessionThreadViewModel = self.threadData let threadData: SessionThreadViewModel = self._threadData.wrappedValue
return Storage.shared return Storage.shared
.read { db -> [MentionInfo] in .read { db -> [MentionInfo] in
@ -733,15 +742,17 @@ public class ConversationViewModel: OWSAudioPlayerDelegate {
.throttle(for: .milliseconds(100), scheduler: DispatchQueue.global(qos: .userInitiated), latest: true) .throttle(for: .milliseconds(100), scheduler: DispatchQueue.global(qos: .userInitiated), latest: true)
.handleEvents( .handleEvents(
receiveOutput: { [weak self] target, timestampMs in receiveOutput: { [weak self] target, timestampMs in
let threadData: SessionThreadViewModel? = self?._threadData.wrappedValue
switch target { switch target {
case .thread: self?.threadData.markAsRead(target: target) case .thread: threadData?.markAsRead(target: target)
case .threadAndInteractions(let interactionId): case .threadAndInteractions(let interactionId):
guard guard
timestampMs == nil || timestampMs == nil ||
(self?.lastInteractionTimestampMsMarkedAsRead ?? 0) < (timestampMs ?? 0) || (self?.lastInteractionTimestampMsMarkedAsRead ?? 0) < (timestampMs ?? 0) ||
(self?.lastInteractionIdMarkedAsRead ?? 0) < (interactionId ?? 0) (self?.lastInteractionIdMarkedAsRead ?? 0) < (interactionId ?? 0)
else { else {
self?.threadData.markAsRead(target: .thread) threadData?.markAsRead(target: .thread)
return return
} }
@ -751,8 +762,8 @@ public class ConversationViewModel: OWSAudioPlayerDelegate {
self?.lastInteractionTimestampMsMarkedAsRead = timestampMs self?.lastInteractionTimestampMsMarkedAsRead = timestampMs
} }
self?.lastInteractionIdMarkedAsRead = (interactionId ?? self?.threadData.interactionId) self?.lastInteractionIdMarkedAsRead = (interactionId ?? threadData?.interactionId)
self?.threadData.markAsRead(target: target) threadData?.markAsRead(target: target)
} }
} }
) )
@ -791,7 +802,7 @@ public class ConversationViewModel: OWSAudioPlayerDelegate {
} }
public func trustContact() { public func trustContact() {
guard self.threadData.threadVariant == .contact else { return } guard self._threadData.wrappedValue.threadVariant == .contact else { return }
let threadId: String = self.threadId let threadId: String = self.threadId
@ -822,7 +833,7 @@ public class ConversationViewModel: OWSAudioPlayerDelegate {
} }
public func unblockContact() { public func unblockContact() {
guard self.threadData.threadVariant == .contact else { return } guard self._threadData.wrappedValue.threadVariant == .contact else { return }
let threadId: String = self.threadId let threadId: String = self.threadId

Loading…
Cancel
Save