Fixed a few bugs uncovered by QA

Fixed a bug where the ConfigurationMessage was getting generated before the contact state was persisted to the database in the message request flow causing odd behaviours (now generating the ConfigurationMessage within the same transaction)
Fixed a bug where sending a message to an existing message request thread once the message requests item has been hidden would show the message requests notification and trigger the section to re-appear on the home screen
Fixed a bug where blocked contacts weren't getting excluded from the contacts list in the configuration message
pull/559/head
Morgan Pretty 3 years ago
parent 2d6dad67eb
commit 4c89c165d8

@ -1138,7 +1138,7 @@ extension ConversationVC {
// Send a sync message with the details of the contact // Send a sync message with the details of the contact
if let appDelegate = UIApplication.shared.delegate as? AppDelegate { if let appDelegate = UIApplication.shared.delegate as? AppDelegate {
appDelegate.forceSyncConfigurationNowIfNeeded().retainUntilComplete() appDelegate.forceSyncConfigurationNowIfNeeded(with: transaction).retainUntilComplete()
} }
} }
} }

@ -273,24 +273,28 @@ final class HomeVC : BaseVC, UITableViewDataSource, UITableViewDelegate, NewConv
// Separate out the changes for new message requests and the inbox (so we can avoid updating for // Separate out the changes for new message requests and the inbox (so we can avoid updating for
// new messages within an existing message request) // new messages within an existing message request)
let messageRequestInserts = rowChanges let messageRequestChanges = rowChanges
.compactMap { $0 as? YapDatabaseViewRowChange } .compactMap { $0 as? YapDatabaseViewRowChange }
.filter { $0.finalGroup == TSMessageRequestGroup && $0.type == .insert } .filter { $0.originalGroup == TSMessageRequestGroup || $0.finalGroup == TSMessageRequestGroup }
let inboxRowChanges = rowChanges let inboxRowChanges = rowChanges
.filter { ($0 as? YapDatabaseViewRowChange)?.finalGroup != TSMessageRequestGroup } .compactMap { $0 as? YapDatabaseViewRowChange }
.filter { $0.originalGroup == TSInboxGroup || $0.finalGroup == TSInboxGroup }
guard sectionChanges.count > 0 || inboxRowChanges.count > 0 || messageRequestInserts.count > 0 else { return } guard sectionChanges.count > 0 || inboxRowChanges.count > 0 || messageRequestChanges.count > 0 else { return }
tableView.beginUpdates() tableView.beginUpdates()
// If we need to unhide the message request row and then re-insert it // If we need to unhide the message request row and then re-insert it
if !messageRequestInserts.isEmpty && (CurrentAppContext().appUserDefaults()[.hasHiddenMessageRequests] || tableView.numberOfRows(inSection: 0) == 0) { if !messageRequestChanges.isEmpty {
CurrentAppContext().appUserDefaults()[.hasHiddenMessageRequests] = false if tableView.numberOfRows(inSection: 0) == 1 && Int(messageRequestCount) <= 0 {
tableView.insertRows(at: [IndexPath(row: 0, section: 0)], with: .automatic) tableView.deleteRows(at: [IndexPath(row: 0, section: 0)], with: .automatic)
}
else if tableView.numberOfRows(inSection: 0) == 0 && Int(messageRequestCount) > 0 && !CurrentAppContext().appUserDefaults()[.hasHiddenMessageRequests] {
tableView.insertRows(at: [IndexPath(row: 0, section: 0)], with: .automatic)
}
} }
inboxRowChanges.forEach { rowChange in inboxRowChanges.forEach { rowChange in
let rowChange = rowChange as! YapDatabaseViewRowChange
let key = rowChange.collectionKey.key let key = rowChange.collectionKey.key
threadViewModelCache[key] = nil threadViewModelCache[key] = nil
@ -310,12 +314,9 @@ final class HomeVC : BaseVC, UITableViewDataSource, UITableViewDelegate, NewConv
// an insert as the change won't be defined correctly) // an insert as the change won't be defined correctly)
if rowChange.originalGroup == TSMessageRequestGroup && rowChange.finalGroup == TSInboxGroup { if rowChange.originalGroup == TSMessageRequestGroup && rowChange.finalGroup == TSInboxGroup {
tableView.insertRows(at: [ rowChange.newIndexPath! ], with: .automatic) tableView.insertRows(at: [ rowChange.newIndexPath! ], with: .automatic)
}
// If that was the last message request then we need to also remove the message request else if rowChange.originalGroup == TSInboxGroup && rowChange.finalGroup == TSMessageRequestGroup {
// row to prevent a crash tableView.deleteRows(at: [ rowChange.indexPath! ], with: .automatic)
if messageRequestCount == 0 {
tableView.deleteRows(at: [ IndexPath(row: 0, section: 0) ], with: .automatic)
}
} }
default: break default: break
@ -336,7 +337,7 @@ final class HomeVC : BaseVC, UITableViewDataSource, UITableViewDelegate, NewConv
case .move: case .move:
// Since we are custom handling this specific movement in the above 'updates' call we need // Since we are custom handling this specific movement in the above 'updates' call we need
// to avoid trying to handle it here // to avoid trying to handle it here
if rowChange.originalGroup == TSMessageRequestGroup && rowChange.finalGroup == TSInboxGroup { if rowChange.originalGroup == TSMessageRequestGroup || rowChange.finalGroup == TSMessageRequestGroup {
return return
} }

@ -22,9 +22,11 @@ extension AppDelegate {
} }
} }
func forceSyncConfigurationNowIfNeeded() -> Promise<Void> { func forceSyncConfigurationNowIfNeeded(with transaction: YapDatabaseReadWriteTransaction? = nil) -> Promise<Void> {
guard Storage.shared.getUser()?.name != nil, guard Storage.shared.getUser()?.name != nil, let configurationMessage = ConfigurationMessage.getCurrent(with: transaction) else {
let configurationMessage = ConfigurationMessage.getCurrent() else { return Promise.value(()) } return Promise.value(())
}
let destination = Message.Destination.contact(publicKey: getUserHexEncodedPublicKey()) let destination = Message.Destination.contact(publicKey: getUserHexEncodedPublicKey())
let (promise, seal) = Promise<Void>.pending() let (promise, seal) = Promise<Void>.pending()
Storage.writeSync { transaction in Storage.writeSync { transaction in

@ -182,6 +182,9 @@ public class NotificationPresenter: NSObject, NotificationsProtocol {
guard numMessageRequests == 0 else { return } guard numMessageRequests == 0 else { return }
} }
else if thread.isMessageRequest() && CurrentAppContext().appUserDefaults()[.hasHiddenMessageRequests] { else if thread.isMessageRequest() && CurrentAppContext().appUserDefaults()[.hasHiddenMessageRequests] {
// If there are other interactions on this thread already then don't show the notification
if thread.numberOfInteractions() > 1 { return }
CurrentAppContext().appUserDefaults()[.hasHiddenMessageRequests] = false CurrentAppContext().appUserDefaults()[.hasHiddenMessageRequests] = false
} }

@ -55,10 +55,16 @@ extension Storage {
@objc public func getAllContacts() -> Set<Contact> { @objc public func getAllContacts() -> Set<Contact> {
var result: Set<Contact> = [] var result: Set<Contact> = []
Storage.read { transaction in Storage.read { transaction in
transaction.enumerateRows(inCollection: Storage.contactCollection) { _, object, _, _ in result = self.getAllContacts(with: transaction)
guard let contact = object as? Contact else { return } }
result.insert(contact) return result
} }
@objc public func getAllContacts(with transaction: YapDatabaseReadTransaction) -> Set<Contact> {
var result: Set<Contact> = []
transaction.enumerateRows(inCollection: Storage.contactCollection) { _, object, _, _ in
guard let contact = object as? Contact else { return }
result.insert(contact)
} }
return result return result
} }

@ -1,7 +1,7 @@
extension ConfigurationMessage { extension ConfigurationMessage {
public static func getCurrent() -> ConfigurationMessage? { public static func getCurrent(with transaction: YapDatabaseReadWriteTransaction? = nil) -> ConfigurationMessage? {
let storage = Storage.shared let storage = Storage.shared
guard let user = storage.getUser() else { return nil } guard let user = storage.getUser() else { return nil }
@ -13,7 +13,7 @@ extension ConfigurationMessage {
var contacts: Set<Contact> = [] var contacts: Set<Contact> = []
var contactCount = 0 var contactCount = 0
Storage.read { transaction in let populateDataClosure: (YapDatabaseReadTransaction) -> () = { transaction in
TSGroupThread.enumerateCollectionObjects(with: transaction) { object, _ in TSGroupThread.enumerateCollectionObjects(with: transaction) { object, _ in
guard let thread = object as? TSGroupThread else { return } guard let thread = object as? TSGroupThread else { return }
@ -47,7 +47,8 @@ extension ConfigurationMessage {
} }
} }
var truncatedContacts = storage.getAllContacts() let currentUserPublicKey: String = getUserHexEncodedPublicKey()
var truncatedContacts = storage.getAllContacts(with: transaction)
if truncatedContacts.count > 200 { if truncatedContacts.count > 200 {
truncatedContacts = Set(Array(truncatedContacts)[0..<200]) truncatedContacts = Set(Array(truncatedContacts)[0..<200])
@ -57,10 +58,12 @@ extension ConfigurationMessage {
let publicKey = contact.sessionID let publicKey = contact.sessionID
let threadID = TSContactThread.threadID(fromContactSessionID: publicKey) let threadID = TSContactThread.threadID(fromContactSessionID: publicKey)
// Want to sync contacts for visible threads and blocked contacts between devices
guard guard
let thread = TSContactThread.fetch(uniqueId: threadID, transaction: transaction), publicKey != currentUserPublicKey && (
thread.shouldBeVisible && TSContactThread.fetch(uniqueId: threadID, transaction: transaction)?.shouldBeVisible == true ||
!SSKEnvironment.shared.blockingManager.isRecipientIdBlocked(publicKey) SSKEnvironment.shared.blockingManager.isRecipientIdBlocked(publicKey)
)
else { else {
return return
} }
@ -87,6 +90,15 @@ extension ConfigurationMessage {
} }
} }
// If we are provided with a transaction then read the data based on the state of the database
// from within the transaction rather than the state in disk
if let transaction: YapDatabaseReadWriteTransaction = transaction {
populateDataClosure(transaction)
}
else {
Storage.read { transaction in populateDataClosure(transaction) }
}
return ConfigurationMessage( return ConfigurationMessage(
displayName: displayName, displayName: displayName,
profilePictureURL: profilePictureURL, profilePictureURL: profilePictureURL,

@ -223,24 +223,40 @@ extension MessageReceiver {
if contactInfo.hasDidApproveMe { contact.didApproveMe = contactInfo.didApproveMe } if contactInfo.hasDidApproveMe { contact.didApproveMe = contactInfo.didApproveMe }
Storage.shared.setContact(contact, using: transaction) Storage.shared.setContact(contact, using: transaction)
let thread = TSContactThread.getOrCreateThread(withContactSessionID: sessionID, transaction: transaction)
thread.shouldBeVisible = true // If the contact is blocked
thread.save(with: transaction) if contactInfo.hasIsBlocked && contactInfo.isBlocked {
// If this message changed them to the blocked state and there is an existing thread
// associated with them that is a message request thread then delete it (assume
// that the current user had deleted that message request)
if
contactInfo.isBlocked != OWSBlockingManager.shared().isRecipientIdBlocked(sessionID),
let thread: TSContactThread = TSContactThread.getWithContactSessionID(sessionID, transaction: transaction),
thread.isMessageRequest(using: transaction)
{
thread.removeAllThreadInteractions(with: transaction)
thread.remove(with: transaction)
}
}
else {
// Otherwise create and save the thread
let thread = TSContactThread.getOrCreateThread(withContactSessionID: sessionID, transaction: transaction)
thread.shouldBeVisible = true
thread.save(with: transaction)
}
} }
// Contacts blocked state
// FIXME: 'OWSBlockingManager' manages it's own dbConnection and transactions so we have to dispatch this to prevent deadlocks // FIXME: 'OWSBlockingManager' manages it's own dbConnection and transactions so we have to dispatch this to prevent deadlocks
DispatchQueue.global(qos: .background).async { DispatchQueue.global().async {
for contactInfo in message.contacts { for contactInfo in message.contacts {
let sessionID = contactInfo.publicKey! let sessionID = contactInfo.publicKey!
let contact = Contact(sessionID: sessionID)
if contact.isBlocked != OWSBlockingManager.shared().isRecipientIdBlocked(contact.sessionID) { if contactInfo.hasIsBlocked && contactInfo.isBlocked != OWSBlockingManager.shared().isRecipientIdBlocked(sessionID) {
if contact.isBlocked { if contactInfo.isBlocked {
OWSBlockingManager.shared().addBlockedPhoneNumber(contact.sessionID) OWSBlockingManager.shared().addBlockedPhoneNumber(sessionID)
} }
else { else {
OWSBlockingManager.shared().removeBlockedPhoneNumber(contact.sessionID) OWSBlockingManager.shared().removeBlockedPhoneNumber(sessionID)
} }
} }
} }

@ -18,6 +18,7 @@ public protocol SessionMessagingKitStorageProtocol {
func getUserED25519KeyPair() -> Box.KeyPair? func getUserED25519KeyPair() -> Box.KeyPair?
func getUser() -> Contact? func getUser() -> Contact?
func getAllContacts() -> Set<Contact> func getAllContacts() -> Set<Contact>
func getAllContacts(with transaction: YapDatabaseReadTransaction) -> Set<Contact>
// MARK: - Closed Groups // MARK: - Closed Groups

@ -88,6 +88,9 @@ public final class NotificationServiceExtension : UNNotificationServiceExtension
guard numMessageRequests == 0 else { return self.completeSilenty() } guard numMessageRequests == 0 else { return self.completeSilenty() }
} }
else if thread.isMessageRequest() && CurrentAppContext().appUserDefaults()[.hasHiddenMessageRequests] { else if thread.isMessageRequest() && CurrentAppContext().appUserDefaults()[.hasHiddenMessageRequests] {
// If there are other interactions on this thread already then don't show the notification
if thread.numberOfInteractions() > 1 { return }
CurrentAppContext().appUserDefaults()[.hasHiddenMessageRequests] = false CurrentAppContext().appUserDefaults()[.hasHiddenMessageRequests] = false
} }

Loading…
Cancel
Save