diff --git a/Session/Conversations/ConversationVC.swift b/Session/Conversations/ConversationVC.swift index cbbad8e52..40e15ffcd 100644 --- a/Session/Conversations/ConversationVC.swift +++ b/Session/Conversations/ConversationVC.swift @@ -472,6 +472,11 @@ final class ConversationVC: BaseVC, LibSessionRespondingViewController, Conversa )?.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 // Flag that the initial layout has been completed (the flag blocks and unblocks a number diff --git a/Session/Notifications/UserNotificationsAdaptee.swift b/Session/Notifications/UserNotificationsAdaptee.swift index 76ebf2abd..e33173dc8 100644 --- a/Session/Notifications/UserNotificationsAdaptee.swift +++ b/Session/Notifications/UserNotificationsAdaptee.swift @@ -265,8 +265,7 @@ public class UserNotificationActionHandler: NSObject { case .finished: break case .failure(let error): completionHandler() - owsFailDebug("error: \(error)") - Logger.error("error: \(error)") + OWSLogger.error("Failed to handle notification response: \(error)") } }, receiveValue: { _ in completionHandler() } @@ -282,14 +281,14 @@ public class UserNotificationActionHandler: NSObject { switch response.actionIdentifier { case UNNotificationDefaultActionIdentifier: - Logger.debug("default action") + OWSLogger.debug("Notification response: default action") return actionHandler.showThread(userInfo: userInfo) .setFailureType(to: Error.self) .eraseToAnyPublisher() case UNNotificationDismissActionIdentifier: // TODO - mark as read? - Logger.debug("dismissed notification") + OWSLogger.debug("Notification response: dismissed notification") return Just(()) .setFailureType(to: Error.self) .eraseToAnyPublisher() diff --git a/SessionMessagingKit/Jobs/Types/MessageSendJob.swift b/SessionMessagingKit/Jobs/Types/MessageSendJob.swift index 4ce9778a6..6a37f9c6a 100644 --- a/SessionMessagingKit/Jobs/Types/MessageSendJob.swift +++ b/SessionMessagingKit/Jobs/Types/MessageSendJob.swift @@ -195,22 +195,41 @@ public enum MessageSendJob: JobExecutor { case MessageSenderError.sendJobTimeout: 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: SNLog("[MessageSendJob] Couldn't send message due to error: \(error)") } // Actual error handling - switch error { - case let senderError as MessageSenderError where !senderError.isRetryable: + switch (error, details.message) { + case (let senderError as MessageSenderError, _) where !senderError.isRetryable: failure(job, error, true, dependencies) - case SnodeAPIError.rateLimited: + case (SnodeAPIError.rateLimited, _): 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.") 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: SNLog("[MessageSendJob] Failed to send \(type(of: details.message)).") diff --git a/SessionMessagingKit/Sending & Receiving/MessageSender.swift b/SessionMessagingKit/Sending & Receiving/MessageSender.swift index 56bf211fe..7ce5dc49f 100644 --- a/SessionMessagingKit/Sending & Receiving/MessageSender.swift +++ b/SessionMessagingKit/Sending & Receiving/MessageSender.swift @@ -1057,9 +1057,15 @@ public final class MessageSender { interactionId: Int64?, using dependencies: Dependencies ) -> Error { - // If the message was a reaction then we don't want to do anything to the original - // interaciton (which the 'interactionId' is pointing to - guard (message as? VisibleMessage)?.reaction == nil else { return error } + // Only 'VisibleMessage' messages can show a status so don't bother updating + // the other cases (if the VisibleMessage was a reaction then we also don't + // 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" // diff --git a/SessionUtilitiesKit/JobRunner/JobRunner.swift b/SessionUtilitiesKit/JobRunner/JobRunner.swift index 36f7b159b..fcb491489 100644 --- a/SessionUtilitiesKit/JobRunner/JobRunner.swift +++ b/SessionUtilitiesKit/JobRunner/JobRunner.swift @@ -961,7 +961,7 @@ public final class JobQueue: Hashable { repeats: false, using: dependencies, block: { [weak queue] _ in - queue?.start(using: dependencies) + queue?.start(forceWhenAlreadyRunning: (queue?.executionType == .concurrent), using: dependencies) } ) 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) guard DispatchQueue.with(key: queueKey, matches: queueContext, using: dependencies) else { internalQueue.async(using: dependencies) { [weak self] in - self?.start(using: dependencies) + self?.start(forceWhenAlreadyRunning: forceWhenAlreadyRunning, using: dependencies) } return } @@ -1582,8 +1582,8 @@ public final class JobQueue: Hashable { return } - // Only schedule a trigger if this queue has actually completed - guard executionType != .concurrent || currentlyRunningJobIds.wrappedValue.isEmpty else { return } + // Only schedule a trigger if the queue is concurrent, or it has actually completed + guard executionType == .concurrent || currentlyRunningJobIds.wrappedValue.isEmpty else { return } // Setup a trigger SNLog("[JobRunner] Stopping \(queueContext) until next job in \(ceilSecondsUntilNextJob) second\(ceilSecondsUntilNextJob == 1 ? "" : "s")")