mirror of https://github.com/oxen-io/session-ios
clean up SyncedExpiries
parent
0bfc337bcc
commit
4c366dedc2
@ -1,158 +0,0 @@
|
||||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import GRDB
|
||||
import PromiseKit
|
||||
import SignalCoreKit
|
||||
import SessionUtilitiesKit
|
||||
import SessionSnodeKit
|
||||
|
||||
public enum SyncExpiriesJob: JobExecutor {
|
||||
public static let maxFailureCount: Int = 10
|
||||
public static let requiresThreadId: Bool = false
|
||||
public static let requiresInteractionId: Bool = false
|
||||
|
||||
public static func run(
|
||||
_ job: Job,
|
||||
queue: DispatchQueue,
|
||||
success: @escaping (Job, Bool) -> (),
|
||||
failure: @escaping (Job, Error?, Bool) -> (),
|
||||
deferred: @escaping (Job) -> ()
|
||||
) {
|
||||
guard
|
||||
let detailsData: Data = job.details,
|
||||
let details: Details = try? JSONDecoder().decode(Details.self, from: detailsData)
|
||||
else {
|
||||
failure(job, JobRunnerError.missingRequiredDetails, false)
|
||||
return
|
||||
}
|
||||
|
||||
guard DisappearingMessagesConfiguration.isNewConfigurationEnabled else {
|
||||
success(job, false)
|
||||
return
|
||||
}
|
||||
|
||||
var interactionIdsWithNoServerHashByExpiresInSeconds: [TimeInterval: [Int64]] = [:]
|
||||
|
||||
details.interactionIdsByExpiresInSeconds.forEach { expiresInSeconds, interactionIds in
|
||||
guard let interactions = Storage.shared.read({ db in try? Interaction.fetchAll(db, ids: interactionIds) }) else { return }
|
||||
|
||||
let interactionIdsWithNoServerHash: [Int64] = interactions.compactMap { $0.serverHash == nil ? $0.id : nil }
|
||||
if !interactionIdsWithNoServerHash.isEmpty {
|
||||
interactionIdsWithNoServerHashByExpiresInSeconds[expiresInSeconds] = interactionIdsWithNoServerHash
|
||||
}
|
||||
|
||||
let serverHashes = interactions.compactMap { $0.serverHash }
|
||||
guard !serverHashes.isEmpty else { return }
|
||||
|
||||
let expirationTimestamp: Int64 = Int64(ceil(details.startedAtMs + expiresInSeconds * 1000))
|
||||
let userPublicKey: String = getUserHexEncodedPublicKey()
|
||||
|
||||
// Send SyncExpiriesMessage
|
||||
let syncTarget: String = interactions[0].authorId
|
||||
let syncExpiries: [SyncedExpiriesMessage.SyncedExpiry] = serverHashes.map { serverHash in
|
||||
return SyncedExpiriesMessage.SyncedExpiry(
|
||||
serverHash: serverHash,
|
||||
expirationTimestamp: expirationTimestamp)
|
||||
}
|
||||
|
||||
let syncExpiriesMessage = SyncedExpiriesMessage(
|
||||
conversationExpiries: [syncTarget: syncExpiries]
|
||||
)
|
||||
|
||||
Storage.shared.writeAsync { db in
|
||||
MessageSender
|
||||
.send(
|
||||
db,
|
||||
message: syncExpiriesMessage,
|
||||
threadId: details.threadId,
|
||||
interactionId: nil,
|
||||
to: .contact(publicKey: userPublicKey)
|
||||
)
|
||||
}
|
||||
|
||||
// Update the ttls
|
||||
SnodeAPI.updateExpiry(
|
||||
publicKey: userPublicKey,
|
||||
updatedExpiryMs: expirationTimestamp,
|
||||
serverHashes: serverHashes
|
||||
).retainUntilComplete()
|
||||
}
|
||||
|
||||
if !interactionIdsWithNoServerHashByExpiresInSeconds.isEmpty {
|
||||
Storage.shared.writeAsync { db in
|
||||
JobRunner.upsert(
|
||||
db,
|
||||
job: Job(
|
||||
variant: .syncExpires,
|
||||
details: SyncExpiriesJob.Details(
|
||||
interactionIdsByExpiresInSeconds: interactionIdsWithNoServerHashByExpiresInSeconds,
|
||||
startedAtMs: details.startedAtMs,
|
||||
threadId: details.threadId
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
success(job, false)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - SyncExpiriesJob.Details
|
||||
|
||||
extension SyncExpiriesJob {
|
||||
public struct Details: Codable {
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case interactionIdsByExpiresInSeconds
|
||||
case startedAtMs
|
||||
case threadId
|
||||
}
|
||||
|
||||
public let interactionIdsByExpiresInSeconds: [TimeInterval: [Int64]]
|
||||
public let startedAtMs: Double
|
||||
public let threadId: String
|
||||
|
||||
// MARK: - Initialization
|
||||
|
||||
public init(
|
||||
interactionIdsByExpiresInSeconds: [TimeInterval: [Int64]],
|
||||
startedAtMs: Double,
|
||||
threadId: String
|
||||
) {
|
||||
self.interactionIdsByExpiresInSeconds = interactionIdsByExpiresInSeconds
|
||||
self.startedAtMs = startedAtMs
|
||||
self.threadId = threadId
|
||||
}
|
||||
|
||||
// MARK: - Codable
|
||||
|
||||
public init(from decoder: Decoder) throws {
|
||||
let container: KeyedDecodingContainer<CodingKeys> = try decoder.container(keyedBy: CodingKeys.self)
|
||||
|
||||
self = Details(
|
||||
interactionIdsByExpiresInSeconds: try container.decode(
|
||||
[TimeInterval: [Int64]].self,
|
||||
forKey: .interactionIdsByExpiresInSeconds
|
||||
),
|
||||
startedAtMs: try container.decode(
|
||||
Double.self,
|
||||
forKey: .startedAtMs
|
||||
),
|
||||
threadId: try container.decode(
|
||||
String.self,
|
||||
forKey: .threadId
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container: KeyedEncodingContainer<CodingKeys> = encoder.container(keyedBy: CodingKeys.self)
|
||||
|
||||
try container.encode(interactionIdsByExpiresInSeconds, forKey: .interactionIdsByExpiresInSeconds)
|
||||
try container.encode(startedAtMs, forKey: .startedAtMs)
|
||||
try container.encode(threadId, forKey: .threadId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,113 +0,0 @@
|
||||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import GRDB
|
||||
import SessionUtilitiesKit
|
||||
|
||||
public final class SyncedExpiriesMessage: ControlMessage {
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case conversationExpiries
|
||||
}
|
||||
|
||||
public struct SyncedExpiry: Codable, Equatable {
|
||||
let serverHash: String
|
||||
let expirationTimestamp: Int64
|
||||
}
|
||||
|
||||
public var conversationExpiries: [String: [SyncedExpiry]] = [:]
|
||||
|
||||
// MARK: - Validation
|
||||
|
||||
public override var isValid: Bool {
|
||||
guard super.isValid else { return false }
|
||||
return conversationExpiries.count > 0
|
||||
}
|
||||
|
||||
override public var isSelfSendValid: Bool { true }
|
||||
|
||||
// MARK: - Codable
|
||||
|
||||
required init(from decoder: Decoder) throws {
|
||||
try super.init(from: decoder)
|
||||
|
||||
let container: KeyedDecodingContainer<CodingKeys> = try decoder.container(keyedBy: CodingKeys.self)
|
||||
|
||||
conversationExpiries = ((try? container.decode([String: [SyncedExpiry]].self, forKey: .conversationExpiries)) ?? [:])
|
||||
}
|
||||
|
||||
public override func encode(to encoder: Encoder) throws {
|
||||
try super.encode(to: encoder)
|
||||
|
||||
var container: KeyedEncodingContainer<CodingKeys> = encoder.container(keyedBy: CodingKeys.self)
|
||||
|
||||
try container.encode(conversationExpiries, forKey: .conversationExpiries)
|
||||
}
|
||||
|
||||
// MARK: - Initialization
|
||||
|
||||
init(conversationExpiries: [String : [SyncedExpiry]]) {
|
||||
super.init()
|
||||
self.conversationExpiries = conversationExpiries
|
||||
}
|
||||
|
||||
// MARK: - Proto Conversion
|
||||
|
||||
public override class func fromProto(_ proto: SNProtoContent, sender: String) -> SyncedExpiriesMessage? {
|
||||
guard let syncedExpiriesProto = proto.syncedExpiries else { return nil }
|
||||
let conversationExpiries = syncedExpiriesProto.conversationExpiries.reduce(into: [String: [SyncedExpiry]]()) {
|
||||
$0[$1.syncTarget] = $1.expiries.map { syncedExpiryProto in
|
||||
return SyncedExpiry(
|
||||
serverHash: syncedExpiryProto.serverHash,
|
||||
expirationTimestamp: Int64(syncedExpiryProto.expirationTimestamp)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return SyncedExpiriesMessage(conversationExpiries: conversationExpiries)
|
||||
}
|
||||
|
||||
public override func toProto(_ db: Database) -> SNProtoContent? {
|
||||
let syncedExpiriesProto = SNProtoSyncedExpiries.builder()
|
||||
|
||||
let conversationExpiriesProto = conversationExpiries.compactMap { (syncTarget, expires) in
|
||||
let syncedConversationExpiriesProto = SNProtoSyncedExpiriesSyncedConversationExpiries
|
||||
.builder(syncTarget: syncTarget)
|
||||
|
||||
let expiresProto = expires.compactMap { syncedExpiry in
|
||||
let syncedExpiryProto = SNProtoSyncedExpiriesSyncedConversationExpiriesSyncedExpiry
|
||||
.builder(
|
||||
serverHash: syncedExpiry.serverHash,
|
||||
expirationTimestamp: UInt64(syncedExpiry.expirationTimestamp)
|
||||
)
|
||||
|
||||
return try? syncedExpiryProto.build()
|
||||
}
|
||||
|
||||
syncedConversationExpiriesProto.setExpiries(expiresProto)
|
||||
|
||||
return try? syncedConversationExpiriesProto.build()
|
||||
}
|
||||
|
||||
syncedExpiriesProto.setConversationExpiries(conversationExpiriesProto)
|
||||
|
||||
let contentProto = SNProtoContent.builder()
|
||||
do {
|
||||
contentProto.setSyncedExpiries(try syncedExpiriesProto.build())
|
||||
return try contentProto.build()
|
||||
} catch {
|
||||
SNLog("Couldn't construct synced expiries proto from: \(self).")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Description
|
||||
|
||||
public var description: String {
|
||||
"""
|
||||
SyncedExpiriesMessage(
|
||||
conversationExpiries: \(conversationExpiries.prettifiedDescription)
|
||||
)
|
||||
"""
|
||||
}
|
||||
}
|
||||
|
@ -1,34 +0,0 @@
|
||||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import GRDB
|
||||
import SignalCoreKit
|
||||
import SessionUtilitiesKit
|
||||
|
||||
extension MessageReceiver {
|
||||
internal static func handleSyncedExpiriesMessage(
|
||||
_ db: Database,
|
||||
message: SyncedExpiriesMessage,
|
||||
dependencies: SMKDependencies
|
||||
) throws {
|
||||
let userPublicKey: String = getUserHexEncodedPublicKey(db, dependencies: dependencies)
|
||||
guard userPublicKey == message.sender else { return }
|
||||
|
||||
message.conversationExpiries.forEach { (syncTarget, expiries) in
|
||||
expiries.forEach { syncExpiry in
|
||||
guard
|
||||
let interaction = try? Interaction.filter(
|
||||
Interaction.Columns.threadId == syncTarget &&
|
||||
Interaction.Columns.serverHash == syncExpiry.serverHash
|
||||
).fetchOne(db),
|
||||
let durationSeconds = interaction.expiresInSeconds
|
||||
else { return }
|
||||
|
||||
try? interaction.with(
|
||||
wasRead: true,
|
||||
expiresStartedAtMs: Double(syncExpiry.expirationTimestamp) - durationSeconds * 1000
|
||||
).update(db)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue