Fixed an issue where the MessageRequestResponse could fail to send but would be flagged locally as approved

pull/568/head
Morgan Pretty 3 years ago
parent b648d27ba1
commit f0f4128db2

@ -2,6 +2,7 @@ import UIKit
import CoreServices import CoreServices
import Photos import Photos
import PhotosUI import PhotosUI
import PromiseKit
import SessionUtilitiesKit import SessionUtilitiesKit
import SignalUtilitiesKit import SignalUtilitiesKit
@ -262,41 +263,48 @@ extension ConversationVC : InputViewDelegate, MessageCellDelegate, ContextMenuAc
let linkPreviewDraft = snInputView.linkPreviewInfo?.draft let linkPreviewDraft = snInputView.linkPreviewInfo?.draft
let tsMessage = TSOutgoingMessage.from(message, associatedWith: thread) let tsMessage = TSOutgoingMessage.from(message, associatedWith: thread)
viewModel.appendUnsavedOutgoingTextMessage(tsMessage)
Storage.write(with: { transaction in Storage.write(with: { transaction in
message.linkPreview = VisibleMessage.LinkPreview.from(linkPreviewDraft, using: transaction) let promise: Promise<Void> = self.approveMessageRequestIfNeeded(
}, completion: { [weak self] in for: self.thread,
tsMessage.linkPreview = OWSLinkPreview.from(message.linkPreview) with: transaction,
isNewThread: !oldThreadShouldBeVisible,
Storage.shared.write( timestamp: (sentTimestamp - 1) // Set 1ms earlier as this is used for sorting
with: { transaction in
tsMessage.save(with: transaction as! YapDatabaseReadWriteTransaction)
},
completion: { [weak self] in
// At this point the TSOutgoingMessage should have its link preview set, so we can scroll to the bottom knowing
// the height of the new message cell
self?.scrollToBottom(isAnimated: false)
}
) )
.map { [weak self] _ in
Storage.shared.write( self?.viewModel.appendUnsavedOutgoingTextMessage(tsMessage)
with: { transaction in
self?.approveMessageRequestIfNeeded( Storage.write(with: { transaction in
for: self?.thread, message.linkPreview = VisibleMessage.LinkPreview.from(linkPreviewDraft, using: transaction)
with: (transaction as! YapDatabaseReadWriteTransaction), }, completion: { [weak self] in
isNewThread: !oldThreadShouldBeVisible, tsMessage.linkPreview = OWSLinkPreview.from(message.linkPreview)
timestamp: (sentTimestamp - 1) // Set 1ms earlier as this is used for sorting
Storage.shared.write(
with: { transaction in
tsMessage.save(with: transaction as! YapDatabaseReadWriteTransaction)
},
completion: { [weak self] in
// At this point the TSOutgoingMessage should have its link preview set, so we can scroll to the bottom knowing
// the height of the new message cell
self?.scrollToBottom(isAnimated: false)
}
) )
},
completion: { [weak self] in
Storage.shared.write { transaction in Storage.shared.write { transaction in
MessageSender.send(message, with: [], in: thread, using: transaction as! YapDatabaseReadWriteTransaction) MessageSender.send(message, with: [], in: thread, using: transaction as! YapDatabaseReadWriteTransaction)
} }
self?.handleMessageSent() self?.handleMessageSent()
} })
) }
// Show an error indicating that approving the thread failed
promise.catch(on: DispatchQueue.main) { [weak self] _ in
let alert = UIAlertController(title: "Session", message: "An error occurred when trying to accept this message request", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
self?.present(alert, animated: true, completion: nil)
}
promise.retainUntilComplete()
}) })
} }
@ -321,20 +329,18 @@ extension ConversationVC : InputViewDelegate, MessageCellDelegate, ContextMenuAc
let oldThreadShouldBeVisible: Bool = thread.shouldBeVisible let oldThreadShouldBeVisible: Bool = thread.shouldBeVisible
let tsMessage = TSOutgoingMessage.from(message, associatedWith: thread) let tsMessage = TSOutgoingMessage.from(message, associatedWith: thread)
Storage.write( Storage.write(with: { transaction in
with: { transaction in let promise: Promise<Void> = self.approveMessageRequestIfNeeded(
tsMessage.save(with: transaction) for: self.thread,
// The new message cell is inserted at this point, but the TSOutgoingMessage doesn't have its attachment yet with: transaction,
}, isNewThread: !oldThreadShouldBeVisible,
completion: { [weak self] in timestamp: (sentTimestamp - 1) // Set 1ms earlier as this is used for sorting
Storage.shared.write( )
.map { [weak self] _ in
Storage.write(
with: { transaction in with: { transaction in
self?.approveMessageRequestIfNeeded( tsMessage.save(with: transaction)
for: self?.thread, // The new message cell is inserted at this point, but the TSOutgoingMessage doesn't have its attachment yet
with: (transaction as! YapDatabaseReadWriteTransaction),
isNewThread: !oldThreadShouldBeVisible,
timestamp: (sentTimestamp - 1) // Set 1ms earlier as this is used for sorting
)
}, },
completion: { [weak self] in completion: { [weak self] in
Storage.write(with: { transaction in Storage.write(with: { transaction in
@ -351,7 +357,16 @@ extension ConversationVC : InputViewDelegate, MessageCellDelegate, ContextMenuAc
} }
) )
} }
)
// Show an error indicating that approving the thread failed
promise.catch(on: DispatchQueue.main) { [weak self] _ in
let alert = UIAlertController(title: "Session", message: "An error occurred when trying to accept this message request", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
self?.present(alert, animated: true, completion: nil)
}
promise.retainUntilComplete()
})
} }
func handleMessageSent() { func handleMessageSent() {
@ -1119,8 +1134,8 @@ extension ConversationVC {
navigationController?.popToViewController(viewControllers[messageRequestsIndex - 1], animated: true) navigationController?.popToViewController(viewControllers[messageRequestsIndex - 1], animated: true)
} }
fileprivate func approveMessageRequestIfNeeded(for thread: TSThread?, with transaction: YapDatabaseReadWriteTransaction, isNewThread: Bool, timestamp: UInt64) { fileprivate func approveMessageRequestIfNeeded(for thread: TSThread?, with transaction: YapDatabaseReadWriteTransaction, isNewThread: Bool, timestamp: UInt64) -> Promise<Void> {
guard let contactThread: TSContactThread = thread as? TSContactThread else { return } guard let contactThread: TSContactThread = thread as? TSContactThread else { return Promise.value(()) }
// If the contact doesn't exist then we should create it so we can store the 'isApproved' state // If the contact doesn't exist then we should create it so we can store the 'isApproved' state
// (it'll be updated with correct profile info if they accept the message request so this // (it'll be updated with correct profile info if they accept the message request so this
@ -1128,64 +1143,77 @@ extension ConversationVC {
let sessionId: String = contactThread.contactSessionID() let sessionId: String = contactThread.contactSessionID()
let contact: Contact = (Storage.shared.getContact(with: sessionId) ?? Contact(sessionID: sessionId)) let contact: Contact = (Storage.shared.getContact(with: sessionId) ?? Contact(sessionID: sessionId))
if !contact.isApproved { guard !contact.isApproved else { return Promise.value(()) }
// Default 'didApproveMe' to true for the person approving the message request
contact.isApproved = true return Promise.value(())
contact.didApproveMe = (contact.didApproveMe || !isNewThread) .then { _ -> Promise<Void> in
Storage.shared.setContact(contact, using: transaction) guard !isNewThread else { return Promise.value(()) }
// If we aren't creating a new thread (ie. sending a message request) then send a // If we aren't creating a new thread (ie. sending a message request) then send a
// messageRequestResponse back to the sender (this allows the sender to know that // messageRequestResponse back to the sender (this allows the sender to know that
// they have been approved and can now use this contact in closed groups) // they have been approved and can now use this contact in closed groups)
if !isNewThread {
let messageRequestResponse: MessageRequestResponse = MessageRequestResponse( let messageRequestResponse: MessageRequestResponse = MessageRequestResponse(
isApproved: true isApproved: true
) )
messageRequestResponse.sentTimestamp = timestamp messageRequestResponse.sentTimestamp = timestamp
MessageSender.send(messageRequestResponse, in: contactThread, using: transaction) return MessageSender.sendNonDurably(messageRequestResponse, in: contactThread, using: transaction)
} }
.map { _ in
// Hide the 'messageRequestView' since the request has been approved and force a config // Default 'didApproveMe' to true for the person approving the message request
// sync to propagate the contact approval state (both must run on the main thread) contact.isApproved = true
DispatchQueue.main.async { [weak self] in contact.didApproveMe = (contact.didApproveMe || !isNewThread)
let messageRequestViewWasVisible: Bool = (self?.messageRequestView.isHidden == false) Storage.shared.setContact(contact, using: transaction)
UIView.animate(withDuration: 0.3) { // Hide the 'messageRequestView' since the request has been approved and force a config
self?.messageRequestView.isHidden = true // sync to propagate the contact approval state (both must run on the main thread)
self?.scrollButtonMessageRequestsBottomConstraint?.isActive = false DispatchQueue.main.async { [weak self] in
self?.scrollButtonBottomConstraint?.isActive = true let messageRequestViewWasVisible: Bool = (self?.messageRequestView.isHidden == false)
// Update the table content inset and offset to account for the dissapearance of UIView.animate(withDuration: 0.3) {
// the messageRequestsView self?.messageRequestView.isHidden = true
if messageRequestViewWasVisible { self?.scrollButtonMessageRequestsBottomConstraint?.isActive = false
let messageRequestsOffset: CGFloat = ((self?.messageRequestView.bounds.height ?? 0) + 16) self?.scrollButtonBottomConstraint?.isActive = true
let oldContentInset: UIEdgeInsets = (self?.messagesTableView.contentInset ?? UIEdgeInsets.zero)
self?.messagesTableView.contentInset = UIEdgeInsets( // Update the table content inset and offset to account for the dissapearance of
top: 0, // the messageRequestsView
leading: 0, if messageRequestViewWasVisible {
bottom: max(oldContentInset.bottom - messageRequestsOffset, 0), let messageRequestsOffset: CGFloat = ((self?.messageRequestView.bounds.height ?? 0) + 16)
trailing: 0 let oldContentInset: UIEdgeInsets = (self?.messagesTableView.contentInset ?? UIEdgeInsets.zero)
) self?.messagesTableView.contentInset = UIEdgeInsets(
top: 0,
leading: 0,
bottom: max(oldContentInset.bottom - messageRequestsOffset, 0),
trailing: 0
)
}
} }
}
// 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(with: transaction).retainUntilComplete() appDelegate.forceSyncConfigurationNowIfNeeded(with: transaction).retainUntilComplete()
}
} }
} }
}
} }
@objc func acceptMessageRequest() { @objc func acceptMessageRequest() {
Storage.write { [weak self] transaction in Storage.write { transaction in
self?.approveMessageRequestIfNeeded( let promise: Promise<Void> = self.approveMessageRequestIfNeeded(
for: self?.thread, for: self.thread,
with: transaction, with: transaction,
isNewThread: false, isNewThread: false,
timestamp: NSDate.millisecondTimestamp() timestamp: NSDate.millisecondTimestamp()
) )
// Show an error indicating that approving the thread failed
promise.catch(on: DispatchQueue.main) { [weak self] _ in
let alert = UIAlertController(title: "Session", message: "An error occurred when trying to accept this message request", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
self?.present(alert, animated: true, completion: nil)
}
promise.retainUntilComplete()
} }
} }

Loading…
Cancel
Save