|
|
|
|
@ -2,6 +2,7 @@ import PromiseKit
|
|
|
|
|
|
|
|
|
|
@objc(LKAPI)
|
|
|
|
|
public final class LokiAPI : NSObject {
|
|
|
|
|
private static var lastDeviceLinkUpdate: [String:Date] = [:] // Hex encoded public key to date
|
|
|
|
|
internal static let storage = OWSPrimaryStorage.shared()
|
|
|
|
|
|
|
|
|
|
// MARK: Convenience
|
|
|
|
|
@ -14,6 +15,7 @@ public final class LokiAPI : NSObject {
|
|
|
|
|
private static let maxRetryCount: UInt = 8
|
|
|
|
|
private static let defaultTimeout: TimeInterval = 20
|
|
|
|
|
private static let longPollingTimeout: TimeInterval = 40
|
|
|
|
|
private static let deviceLinkUpdateInterval: TimeInterval = 8 * 60
|
|
|
|
|
public static let defaultMessageTTL: UInt64 = 24 * 60 * 60 * 1000
|
|
|
|
|
internal static var powDifficulty: UInt = 40
|
|
|
|
|
|
|
|
|
|
@ -33,6 +35,13 @@ public final class LokiAPI : NSObject {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal struct Destination {
|
|
|
|
|
internal let hexEncodedPublicKey: String
|
|
|
|
|
internal let kind: Kind
|
|
|
|
|
|
|
|
|
|
enum Kind { case master, slave }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public typealias MessageListPromise = Promise<[SSKProtoEnvelope]>
|
|
|
|
|
public typealias RawResponsePromise = Promise<RawResponse>
|
|
|
|
|
|
|
|
|
|
@ -109,6 +118,37 @@ public final class LokiAPI : NSObject {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal static func getDestinations(for hexEncodedPublicKey: String) -> Promise<[Destination]> {
|
|
|
|
|
let (promise, seal) = Promise<[Destination]>.pending()
|
|
|
|
|
func getDestinations() {
|
|
|
|
|
storage.dbReadConnection.read { transaction in
|
|
|
|
|
var destinations: [Destination] = []
|
|
|
|
|
let masterHexEncodedPublicKey = storage.getMasterHexEncodedPublicKey(for: hexEncodedPublicKey, in: transaction) ?? hexEncodedPublicKey
|
|
|
|
|
let masterDestination = Destination(hexEncodedPublicKey: masterHexEncodedPublicKey, kind: .master)
|
|
|
|
|
destinations.append(masterDestination)
|
|
|
|
|
let deviceLinks = storage.getDeviceLinks(for: masterHexEncodedPublicKey, in: transaction)
|
|
|
|
|
let slaveDestinations = deviceLinks.map { Destination(hexEncodedPublicKey: $0.slave.hexEncodedPublicKey, kind: .slave) }
|
|
|
|
|
destinations.append(contentsOf: slaveDestinations)
|
|
|
|
|
seal.fulfill(destinations)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
let timeSinceLastUpdate: TimeInterval
|
|
|
|
|
if let lastDeviceLinkUpdate = lastDeviceLinkUpdate[hexEncodedPublicKey] {
|
|
|
|
|
timeSinceLastUpdate = Date().timeIntervalSince(lastDeviceLinkUpdate)
|
|
|
|
|
} else {
|
|
|
|
|
timeSinceLastUpdate = .infinity
|
|
|
|
|
}
|
|
|
|
|
if timeSinceLastUpdate > deviceLinkUpdateInterval {
|
|
|
|
|
storage.dbReadConnection.read { transaction in
|
|
|
|
|
let masterHexEncodedPublicKey = storage.getMasterHexEncodedPublicKey(for: hexEncodedPublicKey, in: transaction) ?? hexEncodedPublicKey
|
|
|
|
|
LokiStorageAPI.getDeviceLinks(associatedWith: masterHexEncodedPublicKey).done { _ in getDestinations() }.catch { seal.reject($0) }
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
getDestinations()
|
|
|
|
|
}
|
|
|
|
|
return promise
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// MARK: Public API
|
|
|
|
|
public static func getMessages() -> Promise<Set<MessageListPromise>> {
|
|
|
|
|
return getTargetSnodes(for: userHexEncodedPublicKey).mapValues { targetSnode in
|
|
|
|
|
@ -117,17 +157,17 @@ public final class LokiAPI : NSObject {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static func sendSignalMessage(_ signalMessage: SignalMessage, onP2PSuccess: @escaping () -> Void) -> Promise<Set<RawResponsePromise>> {
|
|
|
|
|
let result = internalSendSignalMessage(signalMessage, onP2PSuccess: onP2PSuccess)
|
|
|
|
|
return getDestinations(for: signalMessage.recipientID).then { destinations -> Promise<Set<RawResponsePromise>> in
|
|
|
|
|
// Use a best attempt approach for multi device for now
|
|
|
|
|
// TODO: Caching & get master links if this is a slave
|
|
|
|
|
LokiStorageAPI.getDeviceLinks(associatedWith: signalMessage.recipientID).done { deviceLinks in
|
|
|
|
|
let associatedHexEncodedPublicKeys = Set(deviceLinks.flatMap { [ $0.master.hexEncodedPublicKey, $0.slave.hexEncodedPublicKey ] }).subtracting([ signalMessage.recipientID ])
|
|
|
|
|
associatedHexEncodedPublicKeys.forEach { hexEncodedPublicKey in
|
|
|
|
|
let signalMessageCopy = signalMessage.copy(with: hexEncodedPublicKey)
|
|
|
|
|
let slaveDestinations = destinations.filter { $0.kind == .slave }
|
|
|
|
|
slaveDestinations.forEach { destination in
|
|
|
|
|
let signalMessageCopy = signalMessage.copy(with: destination.hexEncodedPublicKey)
|
|
|
|
|
internalSendSignalMessage(signalMessageCopy) { }
|
|
|
|
|
}
|
|
|
|
|
let masterDestination = destinations.first { $0.kind == .master }!
|
|
|
|
|
let signalMessageCopy = signalMessage.copy(with: masterDestination.hexEncodedPublicKey)
|
|
|
|
|
return internalSendSignalMessage(signalMessage, onP2PSuccess: onP2PSuccess)
|
|
|
|
|
}
|
|
|
|
|
return result
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// MARK: Public API (Obj-C)
|
|
|
|
|
|