Fixed a few more bugs

• Fixed an issue where the message status wouldn't update to the failed state if the MessageSendJob timed-out
• Fixed an issue where network issues could result in a backlog of TypingIndicator messages that failed to send (don't bother retrying them)
• Fixed an issue where concurrent jobs might not retry correctly if other jobs don't complete or the retry was triggered from the wrong queue
• Fixed an issue where the input view could disappear in some cases
pull/960/head
Morgan Pretty 3 months ago
parent 352f6d7337
commit 8d99f64f56

@ -472,6 +472,11 @@ final class ConversationVC: BaseVC, LibSessionRespondingViewController, Conversa
)?.becomeFirstResponder() )?.becomeFirstResponder()
} }
} }
else if !self.isFirstResponder && hasLoadedInitialThreadData {
// After we have loaded the initial data if the user starts and cancels the interactive pop
// gesture the input view will disappear
self.becomeFirstResponder()
}
recoverInputView { [weak self] in recoverInputView { [weak self] in
// Flag that the initial layout has been completed (the flag blocks and unblocks a number // Flag that the initial layout has been completed (the flag blocks and unblocks a number

@ -265,8 +265,7 @@ public class UserNotificationActionHandler: NSObject {
case .finished: break case .finished: break
case .failure(let error): case .failure(let error):
completionHandler() completionHandler()
owsFailDebug("error: \(error)") OWSLogger.error("Failed to handle notification response: \(error)")
Logger.error("error: \(error)")
} }
}, },
receiveValue: { _ in completionHandler() } receiveValue: { _ in completionHandler() }
@ -282,14 +281,14 @@ public class UserNotificationActionHandler: NSObject {
switch response.actionIdentifier { switch response.actionIdentifier {
case UNNotificationDefaultActionIdentifier: case UNNotificationDefaultActionIdentifier:
Logger.debug("default action") OWSLogger.debug("Notification response: default action")
return actionHandler.showThread(userInfo: userInfo) return actionHandler.showThread(userInfo: userInfo)
.setFailureType(to: Error.self) .setFailureType(to: Error.self)
.eraseToAnyPublisher() .eraseToAnyPublisher()
case UNNotificationDismissActionIdentifier: case UNNotificationDismissActionIdentifier:
// TODO - mark as read? // TODO - mark as read?
Logger.debug("dismissed notification") OWSLogger.debug("Notification response: dismissed notification")
return Just(()) return Just(())
.setFailureType(to: Error.self) .setFailureType(to: Error.self)
.eraseToAnyPublisher() .eraseToAnyPublisher()

@ -195,22 +195,41 @@ public enum MessageSendJob: JobExecutor {
case MessageSenderError.sendJobTimeout: case MessageSenderError.sendJobTimeout:
SNLog("[MessageSendJob] Couldn't send message due to error: \(error) (paths: \(OnionRequestAPI.paths.prettifiedDescription)).") SNLog("[MessageSendJob] Couldn't send message due to error: \(error) (paths: \(OnionRequestAPI.paths.prettifiedDescription)).")
// In this case the `MessageSender` process gets cancelled so we need to
// call `handleFailedMessageSend` to update the statuses correctly
dependencies.storage.write(using: dependencies) { db in
MessageSender.handleFailedMessageSend(
db,
message: details.message,
destination: details.destination,
with: .other(error),
interactionId: job.interactionId,
using: dependencies
)
}
default: default:
SNLog("[MessageSendJob] Couldn't send message due to error: \(error)") SNLog("[MessageSendJob] Couldn't send message due to error: \(error)")
} }
// Actual error handling // Actual error handling
switch error { switch (error, details.message) {
case let senderError as MessageSenderError where !senderError.isRetryable: case (let senderError as MessageSenderError, _) where !senderError.isRetryable:
failure(job, error, true, dependencies) failure(job, error, true, dependencies)
case SnodeAPIError.rateLimited: case (SnodeAPIError.rateLimited, _):
failure(job, error, true, dependencies) failure(job, error, true, dependencies)
case SnodeAPIError.clockOutOfSync: case (SnodeAPIError.clockOutOfSync, _):
SNLog("[MessageSendJob] \(originalSentTimestamp != nil ? "Permanently Failing" : "Failing") to send \(type(of: details.message)) due to clock out of sync issue.") SNLog("[MessageSendJob] \(originalSentTimestamp != nil ? "Permanently Failing" : "Failing") to send \(type(of: details.message)) due to clock out of sync issue.")
failure(job, error, (originalSentTimestamp != nil), dependencies) failure(job, error, (originalSentTimestamp != nil), dependencies)
// Don't bother retrying (it can just send a new one later but allowing retries
// can result in a large number of `MessageSendJobs` backing up)
case (_, is TypingIndicator):
SNLog("[MessageSendJob] Failed to send \(type(of: details.message)).")
failure(job, error, true, dependencies)
default: default:
SNLog("[MessageSendJob] Failed to send \(type(of: details.message)).") SNLog("[MessageSendJob] Failed to send \(type(of: details.message)).")

@ -1057,9 +1057,15 @@ public final class MessageSender {
interactionId: Int64?, interactionId: Int64?,
using dependencies: Dependencies using dependencies: Dependencies
) -> Error { ) -> Error {
// If the message was a reaction then we don't want to do anything to the original // Only 'VisibleMessage' messages can show a status so don't bother updating
// interaciton (which the 'interactionId' is pointing to // the other cases (if the VisibleMessage was a reaction then we also don't
guard (message as? VisibleMessage)?.reaction == nil else { return error } // want to do anything as the `interactionId` points to the original message
// which has it's own status)
switch message {
case let message as VisibleMessage where message.reaction != nil: return error
case is VisibleMessage: break
default: return error
}
// Check if we need to mark any "sending" recipients as "failed" // Check if we need to mark any "sending" recipients as "failed"
// //

@ -961,7 +961,7 @@ public final class JobQueue: Hashable {
repeats: false, repeats: false,
using: dependencies, using: dependencies,
block: { [weak queue] _ in block: { [weak queue] _ in
queue?.start(using: dependencies) queue?.start(forceWhenAlreadyRunning: (queue?.executionType == .concurrent), using: dependencies)
} }
) )
return trigger return trigger
@ -1306,7 +1306,7 @@ public final class JobQueue: Hashable {
// on the main thread (if it is on the main thread then swap to a different thread) // on the main thread (if it is on the main thread then swap to a different thread)
guard DispatchQueue.with(key: queueKey, matches: queueContext, using: dependencies) else { guard DispatchQueue.with(key: queueKey, matches: queueContext, using: dependencies) else {
internalQueue.async(using: dependencies) { [weak self] in internalQueue.async(using: dependencies) { [weak self] in
self?.start(using: dependencies) self?.start(forceWhenAlreadyRunning: forceWhenAlreadyRunning, using: dependencies)
} }
return return
} }
@ -1582,8 +1582,8 @@ public final class JobQueue: Hashable {
return return
} }
// Only schedule a trigger if this queue has actually completed // Only schedule a trigger if the queue is concurrent, or it has actually completed
guard executionType != .concurrent || currentlyRunningJobIds.wrappedValue.isEmpty else { return } guard executionType == .concurrent || currentlyRunningJobIds.wrappedValue.isEmpty else { return }
// Setup a trigger // Setup a trigger
SNLog("[JobRunner] Stopping \(queueContext) until next job in \(ceilSecondsUntilNextJob) second\(ceilSecondsUntilNextJob == 1 ? "" : "s")") SNLog("[JobRunner] Stopping \(queueContext) until next job in \(ceilSecondsUntilNextJob) second\(ceilSecondsUntilNextJob == 1 ? "" : "s")")

Loading…
Cancel
Save