From 35f2354327325896aa81efeddf0714ef469a3bdb Mon Sep 17 00:00:00 2001 From: Niels Andriesse Date: Mon, 27 May 2019 15:06:54 +1000 Subject: [PATCH] Show when a message was sent using P2P --- .../Cells/OWSMessageFooterView.m | 13 +++++- SignalServiceKit/src/Loki/API/LokiAPI.swift | 17 +++---- .../src/Loki/API/LokiP2PManager.swift | 44 +++++++++---------- .../src/Loki/API/SignalMessage.swift | 1 - .../src/Messages/Interactions/TSMessage.h | 1 + .../src/Messages/OWSMessageManager.m | 4 ++ .../src/Messages/OWSMessageSender.m | 3 +- 7 files changed, 50 insertions(+), 33 deletions(-) diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageFooterView.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageFooterView.m index 2060d052b..38e9c2e53 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageFooterView.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageFooterView.m @@ -218,7 +218,18 @@ NS_ASSUME_NONNULL_BEGIN timestampLabelText = [DateUtil formatMessageTimestamp:viewItem.interaction.timestamp]; } - self.timestampLabel.text = timestampLabelText.localizedUppercaseString; + TSMessage *message = (TSMessage *)[viewItem.interaction as:TSMessage.class]; + if (message != nil && message.isP2P) { + NSString *string = [timestampLabelText.localizedUppercaseString stringByAppendingString:@" ยท P2P"]; + NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:string]; + NSRange range = [string rangeOfString:@"P2P"]; + [attributedString beginEditing]; + [attributedString addAttribute:NSFontAttributeName value:[UIFont.ows_dynamicTypeCaption1Font ows_bold] range:range]; + [attributedString endEditing]; + [self.timestampLabel setAttributedText:attributedString]; + } else { + [self.timestampLabel setText:timestampLabelText.localizedUppercaseString]; + } } - (CGSize)measureWithConversationViewItem:(id)viewItem diff --git a/SignalServiceKit/src/Loki/API/LokiAPI.swift b/SignalServiceKit/src/Loki/API/LokiAPI.swift index 9bffa439d..ebf79c7ad 100644 --- a/SignalServiceKit/src/Loki/API/LokiAPI.swift +++ b/SignalServiceKit/src/Loki/API/LokiAPI.swift @@ -53,7 +53,7 @@ import PromiseKit }.map { Set($0) }.retryingIfNeeded(maxRetryCount: maxRetryCount) } - public static func sendSignalMessage(_ signalMessage: SignalMessage, to destination: String, with timestamp: UInt64) -> Promise> { + public static func sendSignalMessage(_ signalMessage: SignalMessage, to destination: String, with timestamp: UInt64, onP2PSuccess: @escaping () -> Void) -> Promise> { guard let lokiMessage = Message.from(signalMessage: signalMessage, with: timestamp) else { return Promise(error: Error.messageConversionFailed) } let destination = lokiMessage.destination func sendLokiMessage(_ lokiMessage: Message, to target: Target) -> RawResponsePromise { @@ -65,12 +65,13 @@ import PromiseKit let swarmPromise = getTargetSnodes(for: destination) return when(fulfilled: powPromise, swarmPromise).map { lokiMessageWithPoW, swarm in return Set(swarm.map { sendLokiMessage(lokiMessageWithPoW, to: $0) }) - } + }.retryingIfNeeded(maxRetryCount: maxRetryCount) } - if let p2pState = LokiP2PManager.getState(for: destination), (lokiMessage.isPing || p2pState.isOnline) { - let target = Target(address: p2pState.address, port: p2pState.port) + if let peer = LokiP2PManager.getInfo(for: destination), (lokiMessage.isPing || peer.isOnline) { + let target = Target(address: peer.address, port: peer.port) return Promise.value([ target ]).mapValues { sendLokiMessage(lokiMessage, to: $0) }.map { Set($0) }.retryingIfNeeded(maxRetryCount: maxRetryCount).get { _ in LokiP2PManager.markOnline(destination) + onP2PSuccess() }.recover { error -> Promise> in LokiP2PManager.markOffline(destination) if lokiMessage.isPing { @@ -82,16 +83,16 @@ import PromiseKit throw error } } - return sendLokiMessageUsingSwarmAPI().retryingIfNeeded(maxRetryCount: maxRetryCount) + return sendLokiMessageUsingSwarmAPI() } } else { - return sendLokiMessageUsingSwarmAPI().retryingIfNeeded(maxRetryCount: maxRetryCount) + return sendLokiMessageUsingSwarmAPI() } } // MARK: Public API (Obj-C) - @objc public static func objc_sendSignalMessage(_ signalMessage: SignalMessage, to destination: String, with timestamp: UInt64) -> AnyPromise { - let promise = sendSignalMessage(signalMessage, to: destination, with: timestamp).mapValues { AnyPromise.from($0) }.map { Set($0) } + @objc public static func objc_sendSignalMessage(_ signalMessage: SignalMessage, to destination: String, with timestamp: UInt64, onP2PSuccess: @escaping () -> Void) -> AnyPromise { + let promise = sendSignalMessage(signalMessage, to: destination, with: timestamp, onP2PSuccess: onP2PSuccess).mapValues { AnyPromise.from($0) }.map { Set($0) } return AnyPromise.from(promise) } diff --git a/SignalServiceKit/src/Loki/API/LokiP2PManager.swift b/SignalServiceKit/src/Loki/API/LokiP2PManager.swift index 880f7c87a..979eb7cff 100644 --- a/SignalServiceKit/src/Loki/API/LokiP2PManager.swift +++ b/SignalServiceKit/src/Loki/API/LokiP2PManager.swift @@ -8,7 +8,7 @@ private static let offlinePingTime = 2 * kMinuteInterval /// A p2p state struct - internal struct P2PState { + internal struct PeerInfo { var address: String var port: UInt16 var isOnline: Bool @@ -20,7 +20,7 @@ private static var ourP2PAddress: LokiAPI.Target? = nil /// This is where we store the p2p details of our contacts - private static var contactP2PStates = [String:P2PState]() + private static var peerInfo = [String:PeerInfo]() // MARK: - Public functions @@ -46,12 +46,12 @@ } guard let thread = contactThread else { - Logger.warn("[Loki][Ping] Failed to fetch thread for \(pubKey).") + Logger.warn("[Loki] Failed to fetch thread when attempting to ping: \(pubKey).") return } - guard let message = lokiAddressMessage(for: thread, isPing: true) else { - Logger.warn("[Loki][Ping] Failed to build ping message for \(pubKey).") + guard let message = createLokiAddressMessage(for: thread, isPing: true) else { + Logger.warn("[Loki] Failed to build ping message for \(pubKey).") return } @@ -77,8 +77,8 @@ /// /// - Parameter pubKey: The contact hex pubkey /// - Returns: The P2P Details or nil if they don't exist - internal static func getState(for hexEncodedPublicKey: String) -> P2PState? { - return contactP2PStates[hexEncodedPublicKey] + internal static func getInfo(for hexEncodedPublicKey: String) -> PeerInfo? { + return peerInfo[hexEncodedPublicKey] } /// Get the `LokiAddressMessage` for the given thread. @@ -86,7 +86,7 @@ /// - Parameter thread: The contact thread. /// - Returns: The `LokiAddressMessage` for that thread. @objc public static func onlineBroadcastMessage(forThread thread: TSThread) -> LokiAddressMessage? { - return lokiAddressMessage(for: thread, isPing: false) + return createLokiAddressMessage(for: thread, isPing: false) } /// Handle P2P logic when we receive a `LokiAddressMessage` @@ -101,17 +101,17 @@ let timerDuration = pubKey < ourHexEncodedPubKey ? 1 * kMinuteInterval : 2 * kMinuteInterval // Get out current contact details - let oldContactDetails = contactP2PStates[pubKey] + let oldContactInfo = peerInfo[pubKey] // Set the new contact details // A contact is always assumed to be offline unless the specific conditions below are met - let details = P2PState(address: address, port: port, isOnline: false, timerDuration: timerDuration, pingTimer: nil) - contactP2PStates[pubKey] = details + let info = PeerInfo(address: address, port: port, isOnline: false, timerDuration: timerDuration, pingTimer: nil) + peerInfo[pubKey] = info // Set up our checks - let oldContactExists = oldContactDetails != nil - let wasOnline = oldContactDetails?.isOnline ?? false - let p2pDetailsMatch = oldContactDetails?.address == address && oldContactDetails?.port == port + let oldContactExists = oldContactInfo != nil + let wasOnline = oldContactInfo?.isOnline ?? false + let isPeerInfoMatching = oldContactInfo?.address == address && oldContactInfo?.port == port /* We need to check if we should ping the user. @@ -121,7 +121,7 @@ - The old contact was set as `Online` - The new p2p details match the old one */ - if oldContactExists && receivedThroughP2P && wasOnline && p2pDetailsMatch { + if oldContactExists && receivedThroughP2P && wasOnline && isPeerInfoMatching { setOnline(true, forContact: pubKey) return } @@ -153,16 +153,16 @@ @objc internal static func setOnline(_ isOnline: Bool, forContact pubKey: String) { // Make sure we are on the main thread DispatchQueue.main.async { - guard var details = contactP2PStates[pubKey] else { return } + guard var info = peerInfo[pubKey] else { return } - let interval = isOnline ? details.timerDuration : offlinePingTime + let interval = isOnline ? info.timerDuration : offlinePingTime // Setup a new timer - details.pingTimer?.invalidate() - details.pingTimer = WeakTimer.scheduledTimer(timeInterval: interval, target: self, userInfo: nil, repeats: true) { _ in ping(contact: pubKey) } - details.isOnline = isOnline + info.pingTimer?.invalidate() + info.pingTimer = WeakTimer.scheduledTimer(timeInterval: interval, target: self, userInfo: nil, repeats: true) { _ in ping(contact: pubKey) } + info.isOnline = isOnline - contactP2PStates[pubKey] = details + peerInfo[pubKey] = info } } @@ -194,7 +194,7 @@ return friendThreadIds.compactMap { TSContactThread.fetch(uniqueId: $0) } } - private static func lokiAddressMessage(for thread: TSThread, isPing: Bool) -> LokiAddressMessage? { + private static func createLokiAddressMessage(for thread: TSThread, isPing: Bool) -> LokiAddressMessage? { guard let ourAddress = ourP2PAddress else { Logger.error("P2P Address not set") return nil diff --git a/SignalServiceKit/src/Loki/API/SignalMessage.swift b/SignalServiceKit/src/Loki/API/SignalMessage.swift index f543fe1df..11d7eec95 100644 --- a/SignalServiceKit/src/Loki/API/SignalMessage.swift +++ b/SignalServiceKit/src/Loki/API/SignalMessage.swift @@ -1,3 +1,2 @@ -// This is basically OWSMessageServiceParams public typealias SignalMessage = [String:Any] diff --git a/SignalServiceKit/src/Messages/Interactions/TSMessage.h b/SignalServiceKit/src/Messages/Interactions/TSMessage.h index b6d98f64b..14c38e934 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSMessage.h +++ b/SignalServiceKit/src/Messages/Interactions/TSMessage.h @@ -45,6 +45,7 @@ typedef NS_ENUM(NSInteger, LKMessageFriendRequestStatus) { @property (nonatomic) uint64_t friendRequestExpiresAt; @property (nonatomic, readonly) BOOL isFriendRequest; @property (nonatomic, readonly) BOOL hasFriendRequestStatusMessage; +@property (nonatomic) BOOL isP2P; // ======== - (instancetype)initInteractionWithTimestamp:(uint64_t)timestamp inThread:(TSThread *)thread NS_UNAVAILABLE; diff --git a/SignalServiceKit/src/Messages/OWSMessageManager.m b/SignalServiceKit/src/Messages/OWSMessageManager.m index dc5671d51..527f9988b 100644 --- a/SignalServiceKit/src/Messages/OWSMessageManager.m +++ b/SignalServiceKit/src/Messages/OWSMessageManager.m @@ -1371,6 +1371,8 @@ NS_ASSUME_NONNULL_BEGIN linkPreview:linkPreview serverTimestamp:serverTimestamp wasReceivedByUD:wasReceivedByUD]; + + if (envelope.isPtpMessage) { incomingMessage.isP2P = YES; } NSArray *attachmentPointers = [TSAttachmentPointer attachmentPointersFromProtos:dataMessage.attachments @@ -1443,6 +1445,8 @@ NS_ASSUME_NONNULL_BEGIN serverTimestamp:serverTimestamp wasReceivedByUD:wasReceivedByUD]; + if (envelope.isPtpMessage) { incomingMessage.isP2P = YES; } + NSArray *attachmentPointers = [TSAttachmentPointer attachmentPointersFromProtos:dataMessage.attachments albumMessage:incomingMessage]; for (TSAttachmentPointer *pointer in attachmentPointers) { diff --git a/SignalServiceKit/src/Messages/OWSMessageSender.m b/SignalServiceKit/src/Messages/OWSMessageSender.m index b355b19ef..928f14a0e 100644 --- a/SignalServiceKit/src/Messages/OWSMessageSender.m +++ b/SignalServiceKit/src/Messages/OWSMessageSender.m @@ -1117,6 +1117,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; } }]; // Convenience + void (^onP2PSuccess)() = ^() { message.isP2P = YES; }; void (^handleError)(NSError *error) = ^(NSError *error) { [self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { // Update the thread if needed @@ -1144,7 +1145,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; [self messageSendDidFail:messageSend deviceMessages:deviceMessages statusCode:statusCode error:error responseData:responseData]; }; // Convert the message to a Loki message and send it using the Loki API - [[LokiAPI objc_sendSignalMessage:signalMessage to:recipient.recipientId with:message.timestamp] + [[LokiAPI objc_sendSignalMessage:signalMessage to:recipient.recipientId with:message.timestamp onP2PSuccess:onP2PSuccess] .thenOn(OWSDispatch.sendingQueue, ^(id result) { NSSet *promises = (NSSet *)result; __block BOOL isSuccess = NO;