mirror of
https://github.com/oxen-io/session-ios.git
synced 2023-12-13 21:30:14 +01:00
8c8453d922
Updated to the latest libSession version Updated the 'hidden' logic to be based on a negative 'priority' value Added an index on the Quote table to speed up conversation query Fixed an odd behaviour with GRDB and Combine (simplified the interface as well) Fixed an issue where migrations could fail
129 lines
5.7 KiB
Swift
129 lines
5.7 KiB
Swift
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
|
|
|
import Foundation
|
|
import Combine
|
|
import GRDB
|
|
import SignalCoreKit
|
|
import SessionUtilitiesKit
|
|
|
|
public enum AttachmentUploadJob: JobExecutor {
|
|
public static var maxFailureCount: Int = 10
|
|
public static var requiresThreadId: Bool = true
|
|
public static let requiresInteractionId: Bool = true
|
|
|
|
public static func run(
|
|
_ job: Job,
|
|
queue: DispatchQueue,
|
|
success: @escaping (Job, Bool) -> (),
|
|
failure: @escaping (Job, Error?, Bool) -> (),
|
|
deferred: @escaping (Job) -> ()
|
|
) {
|
|
guard
|
|
let threadId: String = job.threadId,
|
|
let interactionId: Int64 = job.interactionId,
|
|
let detailsData: Data = job.details,
|
|
let details: Details = try? JSONDecoder().decode(Details.self, from: detailsData),
|
|
let (attachment, openGroup): (Attachment, OpenGroup?) = Storage.shared.read({ db in
|
|
guard let attachment: Attachment = try Attachment.fetchOne(db, id: details.attachmentId) else {
|
|
return nil
|
|
}
|
|
|
|
return (attachment, try OpenGroup.fetchOne(db, id: threadId))
|
|
})
|
|
else {
|
|
failure(job, JobRunnerError.missingRequiredDetails, true)
|
|
return
|
|
}
|
|
|
|
// If the original interaction no longer exists then don't bother uploading the attachment (ie. the
|
|
// message was deleted before it even got sent)
|
|
guard Storage.shared.read({ db in try Interaction.exists(db, id: interactionId) }) == true else {
|
|
failure(job, StorageError.objectNotFound, true)
|
|
return
|
|
}
|
|
|
|
// If the attachment is still pending download the hold off on running this job
|
|
guard attachment.state != .pendingDownload && attachment.state != .downloading else {
|
|
deferred(job)
|
|
return
|
|
}
|
|
|
|
// If this upload is related to sending a message then trigger the 'handleMessageWillSend' logic
|
|
// as if this is a retry the logic wouldn't run until after the upload has completed resulting in
|
|
// a potentially incorrect delivery status
|
|
Storage.shared.write { db in
|
|
guard
|
|
let sendJob: Job = try Job.fetchOne(db, id: details.messageSendJobId),
|
|
let sendJobDetails: Data = sendJob.details,
|
|
let details: MessageSendJob.Details = try? JSONDecoder()
|
|
.decode(MessageSendJob.Details.self, from: sendJobDetails)
|
|
else { return }
|
|
|
|
MessageSender.handleMessageWillSend(
|
|
db,
|
|
message: details.message,
|
|
interactionId: interactionId,
|
|
isSyncMessage: details.isSyncMessage
|
|
)
|
|
}
|
|
|
|
// Note: In the AttachmentUploadJob we intentionally don't provide our own db instance to prevent
|
|
// reentrancy issues when the success/failure closures get called before the upload as the JobRunner
|
|
// will attempt to update the state of the job immediately
|
|
attachment
|
|
.upload(to: (openGroup.map { .openGroup($0) } ?? .fileServer))
|
|
.subscribe(on: queue)
|
|
.receive(on: queue)
|
|
.sinkUntilComplete(
|
|
receiveCompletion: { result in
|
|
switch result {
|
|
case .failure(let error):
|
|
// If this upload is related to sending a message then trigger the
|
|
// 'handleFailedMessageSend' logic as we want to ensure the message
|
|
// has the correct delivery status
|
|
Storage.shared.read { db in
|
|
guard
|
|
let sendJob: Job = try Job.fetchOne(db, id: details.messageSendJobId),
|
|
let sendJobDetails: Data = sendJob.details,
|
|
let details: MessageSendJob.Details = try? JSONDecoder()
|
|
.decode(MessageSendJob.Details.self, from: sendJobDetails)
|
|
else { return }
|
|
|
|
MessageSender.handleFailedMessageSend(
|
|
db,
|
|
message: details.message,
|
|
with: .other(error),
|
|
interactionId: interactionId,
|
|
isSyncMessage: details.isSyncMessage
|
|
)
|
|
}
|
|
|
|
failure(job, error, false)
|
|
|
|
case .finished: success(job, false)
|
|
}
|
|
}
|
|
)
|
|
}
|
|
}
|
|
|
|
// MARK: - AttachmentUploadJob.Details
|
|
|
|
extension AttachmentUploadJob {
|
|
public struct Details: Codable {
|
|
/// This is the id for the messageSend job this attachmentUpload job is associated to, the value isn't used for any of
|
|
/// the logic but we want to mandate that the attachmentUpload job can only be used alongside a messageSend job
|
|
///
|
|
/// **Note:** If we do decide to remove this the `_003_YDBToGRDBMigration` will need to be updated as it
|
|
/// fails if this connection can't be made
|
|
public let messageSendJobId: Int64
|
|
|
|
/// The id of the `Attachment` to upload
|
|
public let attachmentId: String
|
|
|
|
public init(messageSendJobId: Int64, attachmentId: String) {
|
|
self.messageSendJobId = messageSendJobId
|
|
self.attachmentId = attachmentId
|
|
}
|
|
}
|
|
}
|