Finish integration
This commit is contained in:
parent
2459388a3a
commit
7031bde5a2
|
@ -19,5 +19,6 @@
|
|||
#import <SignalCoreKit/OWSAsserts.h>
|
||||
#import <SignalServiceKit/SSKAsserts.h>
|
||||
#import <SignalServiceKit/OWSAnalytics.h>
|
||||
#import <SignalServiceKit/NSArray+Functional.h>
|
||||
#import <SignalServiceKit/NSObject+Casting.h>
|
||||
#endif
|
||||
|
|
|
@ -5,11 +5,11 @@ final class SeedVC : OnboardingBaseViewController, DeviceLinkingModalDelegate {
|
|||
private var mode: Mode = .register { didSet { if mode != oldValue { handleModeChanged() } } }
|
||||
private var seed: Data! { didSet { updateMnemonic() } }
|
||||
private var mnemonic: String! { didSet { handleMnemonicChanged() } }
|
||||
private var timer: Timer?
|
||||
private var linkingRequestMessageSendingTimer: Timer?
|
||||
|
||||
// MARK: Components
|
||||
private lazy var registerStackView: UIStackView = {
|
||||
let result = UIStackView(arrangedSubviews: [ explanationLabel1, UIView.spacer(withHeight: 32), mnemonicLabel, UIView.spacer(withHeight: 24), copyButton, UIView.spacer(withHeight: 8), restoreButton1, linkButton1 ])
|
||||
let result = UIStackView(arrangedSubviews: [ explanationLabel1, UIView.spacer(withHeight: 32), mnemonicLabel, UIView.spacer(withHeight: 24), copyButton, restoreButton1, linkButton1 ])
|
||||
result.accessibilityIdentifier = "onboarding.keyPairStep.registerStackView"
|
||||
result.axis = .vertical
|
||||
return result
|
||||
|
@ -346,7 +346,7 @@ final class SeedVC : OnboardingBaseViewController, DeviceLinkingModalDelegate {
|
|||
present(deviceLinkingModal, animated: true, completion: nil)
|
||||
let masterHexEncodedPublicKey = masterHexEncodedPublicKeyTextField.text!.trimmingCharacters(in: CharacterSet.whitespaces)
|
||||
let linkingRequestMessage = DeviceLinkingUtilities.getLinkingRequestMessage(for: masterHexEncodedPublicKey)
|
||||
timer = Timer.scheduledTimer(withTimeInterval: 2, repeats: true) { [weak self] _ in
|
||||
linkingRequestMessageSendingTimer = Timer.scheduledTimer(withTimeInterval: 2, repeats: true) { [weak self] _ in
|
||||
self?.sendLinkingRequestMessage(linkingRequestMessage)
|
||||
}
|
||||
sendLinkingRequestMessage(linkingRequestMessage)
|
||||
|
@ -363,13 +363,13 @@ final class SeedVC : OnboardingBaseViewController, DeviceLinkingModalDelegate {
|
|||
}
|
||||
|
||||
func handleDeviceLinkAuthorized(_ deviceLink: DeviceLink) {
|
||||
timer?.invalidate()
|
||||
linkingRequestMessageSendingTimer?.invalidate()
|
||||
UserDefaults.standard.set(true, forKey: "didUpdateForMainnet")
|
||||
onboardingController.verificationDidComplete(fromView: self)
|
||||
}
|
||||
|
||||
func handleDeviceLinkingModalDismissed() {
|
||||
timer?.invalidate()
|
||||
linkingRequestMessageSendingTimer?.invalidate()
|
||||
let appDelegate = UIApplication.shared.delegate as! AppDelegate
|
||||
appDelegate.stopLongPollerIfNeeded()
|
||||
TSAccountManager.sharedInstance().resetForReregistration()
|
||||
|
|
|
@ -33,11 +33,25 @@ public final class LokiAPI : NSObject {
|
|||
}
|
||||
}
|
||||
|
||||
internal struct Destination {
|
||||
internal let hexEncodedPublicKey: String
|
||||
internal let kind: Kind
|
||||
@objc(LKDestination)
|
||||
public final class Destination : NSObject {
|
||||
@objc public let hexEncodedPublicKey: String
|
||||
@objc(kind)
|
||||
public let objc_kind: String
|
||||
|
||||
enum Kind { case master, slave }
|
||||
public var kind: Kind { return Kind(rawValue: objc_kind)! }
|
||||
|
||||
public enum Kind : String { case master, slave }
|
||||
|
||||
public init(hexEncodedPublicKey: String, kind: Kind) {
|
||||
self.hexEncodedPublicKey = hexEncodedPublicKey
|
||||
self.objc_kind = kind.rawValue
|
||||
}
|
||||
|
||||
@objc public init(hexEncodedPublicKey: String, kind: String) {
|
||||
self.hexEncodedPublicKey = hexEncodedPublicKey
|
||||
self.objc_kind = kind
|
||||
}
|
||||
}
|
||||
|
||||
public typealias MessageListPromise = Promise<[SSKProtoEnvelope]>
|
||||
|
@ -67,8 +81,47 @@ public final class LokiAPI : NSObject {
|
|||
let timeout: TimeInterval? = useLongPolling ? longPollingTimeout : nil
|
||||
return invoke(.getMessages, on: target, associatedWith: userHexEncodedPublicKey, parameters: parameters, headers: headers, timeout: timeout)
|
||||
}
|
||||
|
||||
internal static func internalSendSignalMessage(_ signalMessage: SignalMessage, onP2PSuccess: @escaping () -> Void) -> Promise<Set<RawResponsePromise>> {
|
||||
|
||||
// MARK: Public API
|
||||
public static func getMessages() -> Promise<Set<MessageListPromise>> {
|
||||
return getTargetSnodes(for: userHexEncodedPublicKey).mapValues { targetSnode in
|
||||
return getRawMessages(from: targetSnode, usingLongPolling: false).map { parseRawMessagesResponse($0, from: targetSnode) }
|
||||
}.map { Set($0) }.retryingIfNeeded(maxRetryCount: maxRetryCount)
|
||||
}
|
||||
|
||||
public 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)
|
||||
destinations = destinations.filter { $0.hexEncodedPublicKey != userHexEncodedPublicKey }
|
||||
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
|
||||
}
|
||||
|
||||
public static func sendSignalMessage(_ signalMessage: SignalMessage, onP2PSuccess: @escaping () -> Void) -> Promise<Set<RawResponsePromise>> {
|
||||
guard let lokiMessage = LokiMessage.from(signalMessage: signalMessage) else { return Promise(error: Error.messageConversionFailed) }
|
||||
let destination = lokiMessage.destination
|
||||
func sendLokiMessage(_ lokiMessage: LokiMessage, to target: LokiAPITarget) -> RawResponsePromise {
|
||||
|
@ -116,59 +169,13 @@ 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
|
||||
return getRawMessages(from: targetSnode, usingLongPolling: false).map { parseRawMessagesResponse($0, from: targetSnode) }
|
||||
}.map { Set($0) }.retryingIfNeeded(maxRetryCount: maxRetryCount)
|
||||
}
|
||||
|
||||
public static func sendSignalMessage(_ signalMessage: SignalMessage, onP2PSuccess: @escaping () -> Void) -> Promise<Set<RawResponsePromise>> {
|
||||
return getDestinations(for: signalMessage.recipientID).then { destinations -> Promise<Set<RawResponsePromise>> in
|
||||
// Use a best attempt approach for multi device for now
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Public API (Obj-C)
|
||||
@objc(getDestinationsFor:)
|
||||
public static func objc_getDestinations(for hexEncodedPublicKey: String) -> AnyPromise {
|
||||
let promise = getDestinations(for: hexEncodedPublicKey)
|
||||
return AnyPromise.from(promise)
|
||||
}
|
||||
|
||||
@objc(sendSignalMessage:onP2PSuccess:)
|
||||
public static func objc_sendSignalMessage(_ signalMessage: SignalMessage, onP2PSuccess: @escaping () -> Void) -> AnyPromise {
|
||||
let promise = sendSignalMessage(signalMessage, onP2PSuccess: onP2PSuccess).mapValues { AnyPromise.from($0) }.map { Set($0) }
|
||||
|
|
|
@ -25,10 +25,4 @@ public final class SignalMessage : NSObject {
|
|||
self.isPing = isPing
|
||||
super.init()
|
||||
}
|
||||
|
||||
public func copy(with recipientID: String) -> SignalMessage {
|
||||
return SignalMessage(type: type, timestamp: timestamp, senderID: senderID, senderDeviceID: senderDeviceID, content: content,
|
||||
recipientID: recipientID, ttl: objc_ttl, isPing: isPing)
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ extension OWSPrimaryStorage {
|
|||
|
||||
public func setDeviceLinks(_ deviceLinks: Set<DeviceLink>, in transaction: YapDatabaseReadWriteTransaction) {
|
||||
let masterHexEncodedPublicKeys = Set(deviceLinks.map { $0.master.hexEncodedPublicKey })
|
||||
guard !masterHexEncodedPublicKeys.isEmpty else { return }
|
||||
guard masterHexEncodedPublicKeys.count == 1 else {
|
||||
print("[Loki] Found inconsistent set of device links.")
|
||||
return
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
|
||||
public enum BuildConfiguration : CustomStringConvertible {
|
||||
public enum BuildConfiguration : String, CustomStringConvertible {
|
||||
case debug, production
|
||||
|
||||
public static let current: BuildConfiguration = {
|
||||
|
@ -10,12 +10,7 @@ public enum BuildConfiguration : CustomStringConvertible {
|
|||
#endif
|
||||
}()
|
||||
|
||||
public var description: String {
|
||||
switch self {
|
||||
case .debug: return "debug"
|
||||
case .production: return "production"
|
||||
}
|
||||
}
|
||||
public var description: String { return rawValue }
|
||||
}
|
||||
|
||||
@objc public final class LKBuildConfiguration : NSObject {
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
|
||||
@interface NSArray (Functional)
|
||||
|
||||
- (NSArray *)filtered:(BOOL (^)(NSObject *))isIncluded;
|
||||
|
||||
@end
|
|
@ -0,0 +1,15 @@
|
|||
#import "NSArray+Functional.h"
|
||||
|
||||
@implementation NSArray (Functional)
|
||||
|
||||
- (NSArray *)filtered:(BOOL (^)(NSObject *))isIncluded {
|
||||
NSMutableArray *result = [NSMutableArray new];
|
||||
for (NSObject *object in self) {
|
||||
if (isIncluded(object)) {
|
||||
[result addObject:object];
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@end
|
|
@ -80,7 +80,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
TSOutgoingMessageRecipientState *_Nullable recipientState =
|
||||
[self.message recipientStateForRecipientId:recipientId];
|
||||
if (!recipientState) {
|
||||
OWSFailDebug(@"missing recipient state for: %@", recipientId);
|
||||
// OWSFailDebug(@"missing recipient state for: %@", recipientId);
|
||||
continue;
|
||||
}
|
||||
if (recipientState.state != OWSOutgoingMessageRecipientStateSent) {
|
||||
|
|
|
@ -694,7 +694,7 @@ NSString *NSStringForOutgoingMessageRecipientState(OWSOutgoingMessageRecipientSt
|
|||
TSOutgoingMessageRecipientState *_Nullable recipientState
|
||||
= message.recipientStateMap[recipientId];
|
||||
if (!recipientState) {
|
||||
OWSFailDebug(@"Missing recipient state for recipient: %@", recipientId);
|
||||
// OWSFailDebug(@"Missing recipient state for recipient: %@", recipientId);
|
||||
return;
|
||||
}
|
||||
recipientState.state = OWSOutgoingMessageRecipientStateSent;
|
||||
|
@ -713,7 +713,7 @@ NSString *NSStringForOutgoingMessageRecipientState(OWSOutgoingMessageRecipientSt
|
|||
TSOutgoingMessageRecipientState *_Nullable recipientState
|
||||
= message.recipientStateMap[recipientId];
|
||||
if (!recipientState) {
|
||||
OWSFailDebug(@"Missing recipient state for recipient: %@", recipientId);
|
||||
// OWSFailDebug(@"Missing recipient state for recipient: %@", recipientId);
|
||||
return;
|
||||
}
|
||||
recipientState.state = OWSOutgoingMessageRecipientStateSkipped;
|
||||
|
|
|
@ -93,4 +93,15 @@ public class OWSMessageSend: NSObject {
|
|||
// We "fail over" to non-UD sends after auth errors sending via UD.
|
||||
disableUD()
|
||||
}
|
||||
|
||||
@objc(copyWithDestination:)
|
||||
public func copy(with destination: LokiAPI.Destination) -> OWSMessageSend {
|
||||
var recipient: SignalRecipient!
|
||||
OWSPrimaryStorage.shared().dbReadConnection.read { transaction in
|
||||
recipient = SignalRecipient.getOrBuildUnsavedRecipient(forRecipientId: destination.hexEncodedPublicKey, transaction: transaction)
|
||||
}
|
||||
let success = (destination.kind == .master) ? self.success : { }
|
||||
let failure = (destination.kind == .master) ? self.failure : { _ in }
|
||||
return OWSMessageSend(message: message, thread: thread, recipient: recipient, senderCertificate: senderCertificate, udAccess: udAccess, localNumber: localNumber, success: success, failure: failure)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -902,6 +902,37 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
|
|||
}
|
||||
|
||||
- (void)sendMessageToRecipient:(OWSMessageSend *)messageSend
|
||||
{
|
||||
if (messageSend.thread.isGroupThread) {
|
||||
[self sendMessage:messageSend];
|
||||
} else {
|
||||
[[LKAPI getDestinationsFor:messageSend.recipient.recipientId]
|
||||
.thenOn(OWSDispatch.sendingQueue, ^(NSArray<LKDestination *> *destinations) {
|
||||
// Use a best attempt approach for multi device for now
|
||||
NSArray *slaveDestinations = [destinations filtered:^BOOL(NSObject *object) {
|
||||
LKDestination *destination = [object as:LKDestination.class];
|
||||
return [destination.kind isEqual:@"slave"];
|
||||
}];
|
||||
for (LKDestination *slaveDestination in slaveDestinations) {
|
||||
OWSMessageSend *messageSendCopy = [messageSend copyWithDestination:slaveDestination];
|
||||
[self sendMessage:messageSendCopy];
|
||||
}
|
||||
LKDestination *masterDestination = [destinations filtered:^BOOL(NSObject *object) {
|
||||
LKDestination *destination = [object as:LKDestination.class];
|
||||
return [destination.kind isEqual:@"master"];
|
||||
}].firstObject;
|
||||
if (masterDestination != nil) {
|
||||
OWSMessageSend *messageSendCopy = [messageSend copyWithDestination:masterDestination];
|
||||
[self sendMessage:messageSendCopy];
|
||||
}
|
||||
})
|
||||
.catchOn(OWSDispatch.sendingQueue, ^(NSError *error) {
|
||||
[self messageSendDidFail:messageSend deviceMessages:@{} statusCode:0 error:error responseData:nil];
|
||||
}) retainUntilComplete];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)sendMessage:(OWSMessageSend *)messageSend
|
||||
{
|
||||
OWSAssertDebug(messageSend);
|
||||
OWSAssertDebug(messageSend.thread || [messageSend.message isKindOfClass:[OWSOutgoingSyncMessage class]]);
|
||||
|
@ -968,6 +999,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
|
|||
return messageSend.failure(deviceMessagesError);
|
||||
}
|
||||
|
||||
/*
|
||||
if (messageSend.isLocalNumber) {
|
||||
OWSAssertDebug([message isKindOfClass:[OWSOutgoingSyncMessage class]]);
|
||||
// Messages sent to the "local number" should be sync messages.
|
||||
|
@ -1041,6 +1073,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
|
|||
OWSLogWarn(@"Message send attempt with no device messages.");
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
for (NSDictionary *deviceMessage in deviceMessages) {
|
||||
NSNumber *_Nullable messageType = deviceMessage[@"type"];
|
||||
|
@ -1523,7 +1556,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
|
|||
|
||||
NSMutableArray *messagesArray = [NSMutableArray arrayWithCapacity:recipient.devices.count];
|
||||
|
||||
NSData *_Nullable plainText = [messageSend.message buildPlainTextData:messageSend.recipient];
|
||||
NSData *_Nullable plainText = [messageSend.message buildPlainTextData:recipient];
|
||||
if (!plainText) {
|
||||
OWSRaiseException(InvalidMessageException, @"Failed to build message proto");
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ static const NSUInteger ddLogLevel = DDLogLevelAll;
|
|||
static const NSUInteger ddLogLevel = DDLogLevelInfo;
|
||||
#endif
|
||||
#import "OWSAnalytics.h"
|
||||
#import "NSArray+Functional.h"
|
||||
#import "NSObject+Casting.h"
|
||||
#import "SSKAsserts.h"
|
||||
#import "TSConstants.h"
|
||||
|
|
Loading…
Reference in New Issue