From 5301858c09cd1d23f05c9619407bb2b33481e777 Mon Sep 17 00:00:00 2001 From: Mikunj Date: Mon, 9 Dec 2019 15:12:50 +1100 Subject: [PATCH 01/10] Updated protobuf --- SignalServiceKit/protobuf/SignalService.proto | 1 + SignalServiceKit/src/Protos/Generated/SSKProto.swift | 3 +++ SignalServiceKit/src/Protos/Generated/SignalService.pb.swift | 4 ++++ 3 files changed, 8 insertions(+) diff --git a/SignalServiceKit/protobuf/SignalService.proto b/SignalServiceKit/protobuf/SignalService.proto index adc0a212b..b064605b2 100644 --- a/SignalServiceKit/protobuf/SignalService.proto +++ b/SignalServiceKit/protobuf/SignalService.proto @@ -148,6 +148,7 @@ message DataMessage { END_SESSION = 1; EXPIRATION_TIMER_UPDATE = 2; PROFILE_KEY_UPDATE = 4; + SESSION_RESTORE = 64; UNLINK_DEVICE = 128; } diff --git a/SignalServiceKit/src/Protos/Generated/SSKProto.swift b/SignalServiceKit/src/Protos/Generated/SSKProto.swift index 3270835f5..62e3b174c 100644 --- a/SignalServiceKit/src/Protos/Generated/SSKProto.swift +++ b/SignalServiceKit/src/Protos/Generated/SSKProto.swift @@ -3433,6 +3433,7 @@ extension SSKProtoDataMessageLokiProfile.SSKProtoDataMessageLokiProfileBuilder { case endSession = 1 case expirationTimerUpdate = 2 case profileKeyUpdate = 4 + case sessionRestore = 64 case unlinkDevice = 128 } @@ -3441,6 +3442,7 @@ extension SSKProtoDataMessageLokiProfile.SSKProtoDataMessageLokiProfileBuilder { case .endSession: return .endSession case .expirationTimerUpdate: return .expirationTimerUpdate case .profileKeyUpdate: return .profileKeyUpdate + case .sessionRestore: return .sessionRestore case .unlinkDevice: return .unlinkDevice } } @@ -3450,6 +3452,7 @@ extension SSKProtoDataMessageLokiProfile.SSKProtoDataMessageLokiProfileBuilder { case .endSession: return .endSession case .expirationTimerUpdate: return .expirationTimerUpdate case .profileKeyUpdate: return .profileKeyUpdate + case .sessionRestore: return .sessionRestore case .unlinkDevice: return .unlinkDevice } } diff --git a/SignalServiceKit/src/Protos/Generated/SignalService.pb.swift b/SignalServiceKit/src/Protos/Generated/SignalService.pb.swift index 5532d9b2a..4b3a5d5a8 100644 --- a/SignalServiceKit/src/Protos/Generated/SignalService.pb.swift +++ b/SignalServiceKit/src/Protos/Generated/SignalService.pb.swift @@ -881,6 +881,7 @@ struct SignalServiceProtos_DataMessage { case endSession // = 1 case expirationTimerUpdate // = 2 case profileKeyUpdate // = 4 + case sessionRestore // = 64 case unlinkDevice // = 128 init() { @@ -892,6 +893,7 @@ struct SignalServiceProtos_DataMessage { case 1: self = .endSession case 2: self = .expirationTimerUpdate case 4: self = .profileKeyUpdate + case 64: self = .sessionRestore case 128: self = .unlinkDevice default: return nil } @@ -902,6 +904,7 @@ struct SignalServiceProtos_DataMessage { case .endSession: return 1 case .expirationTimerUpdate: return 2 case .profileKeyUpdate: return 4 + case .sessionRestore: return 64 case .unlinkDevice: return 128 } } @@ -3478,6 +3481,7 @@ extension SignalServiceProtos_DataMessage.Flags: SwiftProtobuf._ProtoNameProvidi 1: .same(proto: "END_SESSION"), 2: .same(proto: "EXPIRATION_TIMER_UPDATE"), 4: .same(proto: "PROFILE_KEY_UPDATE"), + 64: .same(proto: "SESSION_RESTORE"), 128: .same(proto: "UNLINK_DEVICE"), ] } From 8a57632ced2b37ca457bb18fff7fe105fd8caf7f Mon Sep 17 00:00:00 2001 From: Mikunj Date: Mon, 9 Dec 2019 16:10:53 +1100 Subject: [PATCH 02/10] Ignore any friend requests that were made before we restored our device. --- Signal/src/Loki/Onboarding/SeedVC.swift | 3 +++ .../src/Loki/Database/OWSPrimaryStorage+Loki.h | 5 +++++ .../src/Loki/Database/OWSPrimaryStorage+Loki.m | 12 ++++++++++++ SignalServiceKit/src/Messages/OWSMessageReceiver.m | 12 +++++++++++- 4 files changed, 31 insertions(+), 1 deletion(-) diff --git a/Signal/src/Loki/Onboarding/SeedVC.swift b/Signal/src/Loki/Onboarding/SeedVC.swift index a66ac44fd..96cc34a1c 100644 --- a/Signal/src/Loki/Onboarding/SeedVC.swift +++ b/Signal/src/Loki/Onboarding/SeedVC.swift @@ -407,6 +407,9 @@ final class SeedVC : OnboardingBaseViewController, DeviceLinkingModalDelegate, O self.setUserInteractionEnabled(true) } } else { + if (mode == .restore) { + OWSPrimaryStorage.shared().setRestorationTime(Date().timeIntervalSince1970) + } onboardingController.pushDisplayNameVC(from: self) } } diff --git a/SignalServiceKit/src/Loki/Database/OWSPrimaryStorage+Loki.h b/SignalServiceKit/src/Loki/Database/OWSPrimaryStorage+Loki.h index 698af7894..90ffb8a4c 100644 --- a/SignalServiceKit/src/Loki/Database/OWSPrimaryStorage+Loki.h +++ b/SignalServiceKit/src/Loki/Database/OWSPrimaryStorage+Loki.h @@ -36,6 +36,11 @@ NS_ASSUME_NONNULL_BEGIN - (void)setIDForMessageWithServerID:(NSUInteger)serverID to:(NSString *)messageID in:(YapDatabaseReadWriteTransaction *)transaction; - (NSString *_Nullable)getIDForMessageWithServerID:(NSUInteger)serverID in:(YapDatabaseReadTransaction *)transaction; +# pragma mark - Restoration + +- (void)setRestorationTime:(NSTimeInterval)time; +- (NSTimeInterval)getRestorationTime; + @end NS_ASSUME_NONNULL_END diff --git a/SignalServiceKit/src/Loki/Database/OWSPrimaryStorage+Loki.m b/SignalServiceKit/src/Loki/Database/OWSPrimaryStorage+Loki.m index 0f6e2781f..9429af5c3 100644 --- a/SignalServiceKit/src/Loki/Database/OWSPrimaryStorage+Loki.m +++ b/SignalServiceKit/src/Loki/Database/OWSPrimaryStorage+Loki.m @@ -175,4 +175,16 @@ return [transaction objectForKey:key inCollection:LKMessageIDCollection]; } +# pragma mark - Restoration + +#define LKGeneralCollection @"Loki" + +- (void)setRestorationTime:(NSTimeInterval)time { + [self.dbReadWriteConnection setDouble:time forKey:@"restoration_time" inCollection:LKGeneralCollection]; +} + +- (NSTimeInterval)getRestorationTime { + return [self.dbReadConnection doubleForKey:@"restoration_time" inCollection:LKGeneralCollection defaultValue:0]; +} + @end diff --git a/SignalServiceKit/src/Messages/OWSMessageReceiver.m b/SignalServiceKit/src/Messages/OWSMessageReceiver.m index 56bd9da3e..2df3ccb24 100644 --- a/SignalServiceKit/src/Messages/OWSMessageReceiver.m +++ b/SignalServiceKit/src/Messages/OWSMessageReceiver.m @@ -10,7 +10,7 @@ #import "OWSBackgroundTask.h" #import "OWSBatchMessageProcessor.h" #import "OWSMessageDecrypter.h" -#import "OWSPrimaryStorage.h" +#import "OWSPrimaryStorage+Loki.h" #import "OWSQueues.h" #import "OWSStorage.h" #import "OWSIdentityManager.h" @@ -404,6 +404,16 @@ NSString *const OWSMessageDecryptJobFinderExtensionGroup = @"OWSMessageProcessin return; } + // Loki: Ignore any friend requests that we got before restoration + uint64_t restorationTime = [NSNumber numberWithDouble:[OWSPrimaryStorage.sharedManager getRestorationTime]].unsignedLongLongValue; + if (envelope.type == SSKProtoEnvelopeTypeFriendRequest && envelope.timestamp < restorationTime * 1000) { + OWSLogDebug(@"Ignoring friend request that was received before restoration."); + dispatch_async(self.serialQueue, ^{ + completion(YES); + }); + return; + } + // We use the original envelope for this check; // the decryption process might rewrite the envelope. BOOL wasReceivedByUD = [self wasReceivedByUD:envelope]; From 1a684aa4706bc7c95cfd1a4e8ffbd36c7a2c842f Mon Sep 17 00:00:00 2001 From: Mikunj Date: Mon, 9 Dec 2019 16:27:00 +1100 Subject: [PATCH 03/10] Reset session upon receiving a friend request from a contact we are friends with. --- SignalMessaging/contacts/OWSSyncManager.m | 9 +++++ .../src/Messages/OWSMessageManager.m | 40 +++++++++++-------- .../src/TestUtils/OWSMockSyncManager.swift | 6 +++ .../src/Util/OWSSyncManagerProtocol.h | 3 ++ 4 files changed, 41 insertions(+), 17 deletions(-) diff --git a/SignalMessaging/contacts/OWSSyncManager.m b/SignalMessaging/contacts/OWSSyncManager.m index 7fe2dac66..a7532e918 100644 --- a/SignalMessaging/contacts/OWSSyncManager.m +++ b/SignalMessaging/contacts/OWSSyncManager.m @@ -264,6 +264,15 @@ NSString *const kSyncManagerLastContactSyncKey = @"kTSStorageManagerOWSSyncManag return [self syncContactsForSignalAccounts:@[ signalAccount ]]; } +- (AnyPromise *)syncContact:(NSString *)hexEncodedPubKey transaction:(YapDatabaseReadTransaction *)transaction +{ + TSContactThread *thread = [TSContactThread getThreadWithContactId:hexEncodedPubKey transaction:transaction]; + if (thread != nil && thread.isContactFriend) { + return [self syncContactsForSignalAccounts:@[[[SignalAccount alloc] initWithRecipientId:hexEncodedPubKey]]]; + } + return [AnyPromise promiseWithValue:@1]; +} + - (AnyPromise *)syncAllContacts { NSMutableArray *friends = @[].mutableCopy; diff --git a/SignalServiceKit/src/Messages/OWSMessageManager.m b/SignalServiceKit/src/Messages/OWSMessageManager.m index f2a706e83..67849777b 100644 --- a/SignalServiceKit/src/Messages/OWSMessageManager.m +++ b/SignalServiceKit/src/Messages/OWSMessageManager.m @@ -447,6 +447,17 @@ NS_ASSUME_NONNULL_BEGIN OWSFailDebug(@"Failed to create a pre key bundle."); } [self.primaryStorage setPreKeyBundle:bundle forContact:envelope.source transaction:transaction]; + + // If we got a friend request and we were friends with this user then we need to reset our session + if (envelope.type == SSKProtoEnvelopeTypeFriendRequest) { + TSContactThread *thread = [TSContactThread getThreadWithContactId:envelope.source transaction:transaction]; + if (thread && thread.isContactFriend) { + [self resetSessionWithContact:envelope.source transaction:transaction]; + + // Let our other devices know that we have reset session + [SSKEnvironment.shared.syncManager syncContact:envelope.source transaction:transaction]; + } + } } // Loki: Handle address message if needed @@ -535,7 +546,10 @@ NS_ASSUME_NONNULL_BEGIN OWSFail(@"Missing transaction."); return; } - + + // Loki - Don't process session restore message + if ((dataMessage.flags & SSKProtoDataMessageFlagsSessionRestore) != 0) { return; } + if ([self isDataMessageBlocked:dataMessage envelope:envelope]) { NSString *logMessage = [NSString stringWithFormat:@"Ignoring blocked message from sender: %@", envelope.source]; if (dataMessage.group) { @@ -1081,23 +1095,21 @@ NS_ASSUME_NONNULL_BEGIN return; } - TSContactThread *thread = [TSContactThread getOrCreateThreadWithContactId:envelope.source transaction:transaction]; + [self resetSessionWithContact:envelope.source transaction:transaction]; +} + +- (void)resetSessionWithContact:(NSString *)hexEncodedPublicKey + transaction:(YapDatabaseReadWriteTransaction *)transaction { + TSContactThread *thread = [TSContactThread getOrCreateThreadWithContactId:hexEncodedPublicKey transaction:transaction]; // MJK TODO - safe to remove senderTimestamp [[[TSInfoMessage alloc] initWithTimestamp:NSDate.ows_millisecondTimeStamp inThread:thread messageType:TSInfoMessageTypeLokiSessionResetInProgress] saveWithTransaction:transaction]; - /* Loki: Original code - * ================ - [[[TSInfoMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] - inThread:thread - messageType:TSInfoMessageTypeSessionDidEnd] saveWithTransaction:transaction]; - * ================ - */ // Loki: Archive all our sessions // Ref: SignalServiceKit/Loki/Docs/SessionReset.md - [self.primaryStorage archiveAllSessionsForContact:envelope.source protocolContext:transaction]; + [self.primaryStorage archiveAllSessionsForContact:hexEncodedPublicKey protocolContext:transaction]; // Loki: Set our session reset state thread.sessionResetState = TSContactThreadSessionResetStateRequestReceived; @@ -1107,13 +1119,7 @@ NS_ASSUME_NONNULL_BEGIN LKEphemeralMessage *emptyMessage = [[LKEphemeralMessage alloc] initInThread:thread]; [self.messageSenderJobQueue addMessage:emptyMessage transaction:transaction]; - NSLog(@"[Loki] Session reset received from %@.", envelope.source); - - /* Loki: Original code - * ================ - [self.primaryStorage deleteAllSessionsForContact:envelope.source protocolContext:transaction]; - * ================ - */ + NSLog(@"[Loki] Session reset received from %@.", hexEncodedPublicKey); } - (void)handleExpirationTimerUpdateMessageWithEnvelope:(SSKProtoEnvelope *)envelope diff --git a/SignalServiceKit/src/TestUtils/OWSMockSyncManager.swift b/SignalServiceKit/src/TestUtils/OWSMockSyncManager.swift index 2655257a6..a3dcb9187 100644 --- a/SignalServiceKit/src/TestUtils/OWSMockSyncManager.swift +++ b/SignalServiceKit/src/TestUtils/OWSMockSyncManager.swift @@ -19,6 +19,12 @@ public class OWSMockSyncManager: NSObject, OWSSyncManagerProtocol { return AnyPromise() } + + @objc public func syncContact(_ hexEncodedPubKey: String, transaction: YapDatabaseReadTransaction) -> AnyPromise { + Logger.info("") + + return AnyPromise() + } @objc public func syncAllContacts() -> AnyPromise { Logger.info("") diff --git a/SignalServiceKit/src/Util/OWSSyncManagerProtocol.h b/SignalServiceKit/src/Util/OWSSyncManagerProtocol.h index da5e28684..eafb2e6fa 100644 --- a/SignalServiceKit/src/Util/OWSSyncManagerProtocol.h +++ b/SignalServiceKit/src/Util/OWSSyncManagerProtocol.h @@ -6,6 +6,7 @@ NS_ASSUME_NONNULL_BEGIN @class AnyPromise; @class SignalAccount; +@class YapDatabaseReadTransaction; @protocol OWSSyncManagerProtocol @@ -13,6 +14,8 @@ NS_ASSUME_NONNULL_BEGIN - (AnyPromise *)syncLocalContact __attribute__((warn_unused_result)); +- (AnyPromise *)syncContact:(NSString *)hexEncodedPubKey transaction:(YapDatabaseReadTransaction *)transaction; + - (AnyPromise *)syncAllContacts __attribute__((warn_unused_result)); - (AnyPromise *)syncContactsForSignalAccounts:(NSArray *)signalAccounts __attribute__((warn_unused_result)); From e38976b082ae2090cab1bc625e2be45d9e718f7a Mon Sep 17 00:00:00 2001 From: Mikunj Date: Tue, 10 Dec 2019 10:32:53 +1100 Subject: [PATCH 04/10] Store devices that we need to restore session with. --- .../ConversationViewController.m | 15 ++++++++++ .../src/Contacts/Threads/TSContactThread.h | 7 +++++ .../src/Contacts/Threads/TSContactThread.m | 30 +++++++++++++++++++ .../Loki/Messaging/Notification+Loki.swift | 2 ++ .../src/Messages/OWSMessageDecrypter.m | 19 +++++++++++- .../src/Messages/OWSMessageSender.m | 3 ++ 6 files changed, 75 insertions(+), 1 deletion(-) diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m index 32c82814b..c43e40d74 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m @@ -418,6 +418,10 @@ typedef enum : NSUInteger { selector:@selector(handleThreadFriendRequestStatusChangedNotification:) name:NSNotification.threadFriendRequestStatusChanged object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(handleThreadSessionRestoreDevicesChangedNotifiaction:) + name:NSNotification.threadSessionRestoreDevicesChanged + object:nil]; } - (BOOL)isGroupConversation @@ -494,6 +498,17 @@ typedef enum : NSUInteger { [self resetContentAndLayout]; } +- (void)handleThreadSessionRestoreDevicesChangedNotifiaction:(NSNotification *)notification +{ + // Check thread + NSString *threadID = (NSString *)notification.object; + if (![threadID isEqualToString:self.thread.uniqueId]) { return; } + // Ensure thread instance is up to date + [self.thread reload]; + // Update UI + // TODO: Show banner here +} + - (void)peekSetup { _peek = YES; diff --git a/SignalServiceKit/src/Contacts/Threads/TSContactThread.h b/SignalServiceKit/src/Contacts/Threads/TSContactThread.h index 44839a34e..9b62cd4d1 100644 --- a/SignalServiceKit/src/Contacts/Threads/TSContactThread.h +++ b/SignalServiceKit/src/Contacts/Threads/TSContactThread.h @@ -22,6 +22,7 @@ extern NSString *const TSContactThreadPrefix; // Loki: The current session reset state for this thread @property (atomic) TSContactThreadSessionResetState sessionResetState; +@property (atomic, readonly) NSArray *sessionRestoreDevices; @property (nonatomic) BOOL hasDismissedOffers; @@ -47,6 +48,12 @@ extern NSString *const TSContactThreadPrefix; + (NSString *)conversationColorNameForRecipientId:(NSString *)recipientId transaction:(YapDatabaseReadTransaction *)transaction; +#pragma mark - Loki Session Restore + +- (void)addSessionRestoreDevice:(NSString *)hexEncodedPublicKey transaction:(YapDatabaseReadWriteTransaction *_Nullable)transaction; + +- (void)removeAllRessionRestoreDevicesWithTransaction:(YapDatabaseReadWriteTransaction *_Nullable)transaction; + @end NS_ASSUME_NONNULL_END diff --git a/SignalServiceKit/src/Contacts/Threads/TSContactThread.m b/SignalServiceKit/src/Contacts/Threads/TSContactThread.m index 82a932a76..c0ba4c804 100644 --- a/SignalServiceKit/src/Contacts/Threads/TSContactThread.m +++ b/SignalServiceKit/src/Contacts/Threads/TSContactThread.m @@ -8,6 +8,7 @@ #import "NotificationsProtocol.h" #import "OWSIdentityManager.h" #import "SSKEnvironment.h" +#import #import #import @@ -26,6 +27,7 @@ NSString *const TSContactThreadPrefix = @"c"; // No session reset ongoing _sessionResetState = TSContactThreadSessionResetStateNone; + _sessionRestoreDevices = @[]; return self; } @@ -107,6 +109,34 @@ NSString *const TSContactThreadPrefix = @"c"; return [self stableColorNameForNewConversationWithString:recipientId]; } +#pragma mark - Loki Session Restore + +- (void)addSessionRestoreDevice:(NSString *)hexEncodedPublicKey transaction:(YapDatabaseReadWriteTransaction *_Nullable)transaction +{ + NSMutableSet *set = [[NSMutableSet alloc] initWithArray:_sessionRestoreDevices]; + [set addObject:hexEncodedPublicKey]; + [self setSessionRestoreDevices:[set allObjects] transaction:transaction]; +} + +- (void)removeAllRessionRestoreDevicesWithTransaction:(YapDatabaseReadWriteTransaction *_Nullable)transaction +{ + [self setSessionRestoreDevices:@[] transaction:transaction]; +} + +- (void)setSessionRestoreDevices:(NSArray * _Nonnull)sessionRestoreDevices transaction:(YapDatabaseReadWriteTransaction *_Nullable)transaction { + _sessionRestoreDevices = sessionRestoreDevices; + void (^postNotification)() = ^() { + [NSNotificationCenter.defaultCenter postNotificationName:NSNotification.threadSessionRestoreDevicesChanged object:self.uniqueId]; + }; + if (transaction == nil) { + [self save]; + [self.dbReadWriteConnection flushTransactionsWithCompletionQueue:dispatch_get_main_queue() completionBlock:^{ postNotification(); }]; + } else { + [self saveWithTransaction:transaction]; + [transaction.connection flushTransactionsWithCompletionQueue:dispatch_get_main_queue() completionBlock:^{ postNotification(); }]; + } +} + @end NS_ASSUME_NONNULL_END diff --git a/SignalServiceKit/src/Loki/Messaging/Notification+Loki.swift b/SignalServiceKit/src/Loki/Messaging/Notification+Loki.swift index 061c3144f..10f2ef82c 100644 --- a/SignalServiceKit/src/Loki/Messaging/Notification+Loki.swift +++ b/SignalServiceKit/src/Loki/Messaging/Notification+Loki.swift @@ -6,6 +6,7 @@ public extension Notification.Name { public static let messageFriendRequestStatusChanged = Notification.Name("messageFriendRequestStatusChanged") public static let threadDeleted = Notification.Name("threadDeleted") public static let dataNukeRequested = Notification.Name("dataNukeRequested") + public static let threadSessionRestoreDevicesChanged = Notification.Name("threadSessionRestoreDevicesChanged") } @objc public extension NSNotification { @@ -15,4 +16,5 @@ public extension Notification.Name { @objc public static let messageFriendRequestStatusChanged = Notification.Name.messageFriendRequestStatusChanged.rawValue as NSString @objc public static let threadDeleted = Notification.Name.threadDeleted.rawValue as NSString @objc public static let dataNukeRequested = Notification.Name.dataNukeRequested.rawValue as NSString + @objc public static let threadSessionRestoreDevicesChanged = Notification.Name.threadSessionRestoreDevicesChanged.rawValue as NSString } diff --git a/SignalServiceKit/src/Messages/OWSMessageDecrypter.m b/SignalServiceKit/src/Messages/OWSMessageDecrypter.m index 977978978..1cdc95153 100644 --- a/SignalServiceKit/src/Messages/OWSMessageDecrypter.m +++ b/SignalServiceKit/src/Messages/OWSMessageDecrypter.m @@ -678,16 +678,33 @@ NSError *EnsureDecryptError(NSError *_Nullable error, NSString *fallbackErrorDes OWSAssertDebug(errorMessage); if (errorMessage != nil) { [errorMessage saveWithTransaction:transaction]; + [self handleSessionRestoreForErrorMessage:errorMessage envelope:envelope transaction:transaction]; [self notifyUserForErrorMessage:errorMessage envelope:envelope transaction:transaction]; } }]; } +- (void)handleSessionRestoreForErrorMessage:(TSErrorMessage *)errorMessage + envelope:(SSKProtoEnvelope *)envelope + transaction:(YapDatabaseReadWriteTransaction *)transaction +{ + NSString *masterHexEncodedPublicKey = [LKDatabaseUtilities getMasterHexEncodedPublicKeyFor:envelope.source in:transaction]; + TSThread *contactThread = [TSContactThread getOrCreateThreadWithContactId:masterHexEncodedPublicKey transaction:transaction]; + + // Trigger a session restore prompt if we get specific errors + if (errorMessage.errorType == TSErrorMessageNoSession || + errorMessage.errorType == TSErrorMessageInvalidMessage || + errorMessage.errorType == TSErrorMessageInvalidKeyException) { + [((TSContactThread *) contactThread) addSessionRestoreDevice:masterHexEncodedPublicKey transaction:transaction]; + } +} + - (void)notifyUserForErrorMessage:(TSErrorMessage *)errorMessage envelope:(SSKProtoEnvelope *)envelope transaction:(YapDatabaseReadWriteTransaction *)transaction { - TSThread *contactThread = [TSContactThread getOrCreateThreadWithContactId:envelope.source transaction:transaction]; + NSString *masterHexEncodedPublicKey = [LKDatabaseUtilities getMasterHexEncodedPublicKeyFor:envelope.source in:transaction]; + TSThread *contactThread = [TSContactThread getOrCreateThreadWithContactId:masterHexEncodedPublicKey transaction:transaction]; [SSKEnvironment.shared.notificationsManager notifyUserForErrorMessage:errorMessage thread:contactThread transaction:transaction]; diff --git a/SignalServiceKit/src/Messages/OWSMessageSender.m b/SignalServiceKit/src/Messages/OWSMessageSender.m index f483239ef..307d7c8ea 100644 --- a/SignalServiceKit/src/Messages/OWSMessageSender.m +++ b/SignalServiceKit/src/Messages/OWSMessageSender.m @@ -1316,6 +1316,9 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; // Update the thread [message.thread saveFriendRequestStatus:LKThreadFriendRequestStatusRequestSent withTransaction:transaction]; [message.thread removeOldOutgoingFriendRequestMessagesIfNeededWithTransaction:transaction]; + if ([message.thread isKindOfClass:[TSContactThread class]]) { + [((TSContactThread *) message.thread) removeAllRessionRestoreDevicesWithTransaction:transaction]; + } // Update the message [message saveFriendRequestStatus:LKMessageFriendRequestStatusPending withTransaction:transaction]; NSTimeInterval expirationInterval = 72 * kHourInterval; From 58a260aac50e241419d81b9fa884cdec4944e3eb Mon Sep 17 00:00:00 2001 From: Mikunj Date: Tue, 10 Dec 2019 10:57:15 +1100 Subject: [PATCH 05/10] Show session restore banner --- .../ConversationViewController.m | 37 +++++++++++++++++-- .../src/Messages/OWSMessageDecrypter.m | 8 ++-- 2 files changed, 39 insertions(+), 6 deletions(-) diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m index c43e40d74..58554ad63 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m @@ -162,6 +162,7 @@ typedef enum : NSUInteger { @property (nonatomic) NSCache *cellMediaCache; @property (nonatomic) ConversationHeaderView *headerView; @property (nonatomic, nullable) UIView *bannerView; +@property (nonatomic, nullable) UIView *restoreSessionBannerView; @property (nonatomic, nullable) OWSDisappearingMessagesConfiguration *disappearingMessagesConfiguration; // Back Button Unread Count @@ -506,7 +507,7 @@ typedef enum : NSUInteger { // Ensure thread instance is up to date [self.thread reload]; // Update UI - // TODO: Show banner here + [self updateSessionRestoreBanner]; } - (void)peekSetup @@ -960,17 +961,37 @@ typedef enum : NSUInteger { return [result copy]; } +- (void)updateSessionRestoreBanner { + BOOL isContactThread = [self.thread isKindOfClass:[TSContactThread class]]; + BOOL shouldRemoveBanner = !isContactThread; + if (isContactThread) { + TSContactThread *thread = (TSContactThread *)self.thread; + if (thread.sessionRestoreDevices.count > 0) { + if (self.restoreSessionBannerView) { + // TODO: Create banner here + } + } else { + shouldRemoveBanner = true; + } + } + + if (shouldRemoveBanner && self.restoreSessionBannerView) { + [self.restoreSessionBannerView removeFromSuperview]; + self.restoreSessionBannerView = nil; + } +} + - (void)ensureBannerState { // This method should be called rarely, so it's simplest to discard and // rebuild the indicator view every time. [self.bannerView removeFromSuperview]; self.bannerView = nil; - + if (self.userHasScrolled) { return; } - + NSArray *noLongerVerifiedRecipientIds = [self noLongerVerifiedRecipientIds]; if (noLongerVerifiedRecipientIds.count > 0) { @@ -1131,6 +1152,16 @@ typedef enum : NSUInteger { }]; } +- (void)restoreSession { + if ([self.thread isKindOfClass:[TSContactThread class]]) { + TSContactThread *thread = (TSContactThread *)self.thread; + NSArray *devices = thread.sessionRestoreDevices; + // TODO: Send session restore to all devices + // TODO: Add message saying session restore was sent + [thread removeAllRessionRestoreDevicesWithTransaction:nil]; + } +} + - (void)noLongerVerifiedBannerViewWasTapped:(UIGestureRecognizer *)sender { if (sender.state == UIGestureRecognizerStateRecognized) { diff --git a/SignalServiceKit/src/Messages/OWSMessageDecrypter.m b/SignalServiceKit/src/Messages/OWSMessageDecrypter.m index 1cdc95153..78cadcd99 100644 --- a/SignalServiceKit/src/Messages/OWSMessageDecrypter.m +++ b/SignalServiceKit/src/Messages/OWSMessageDecrypter.m @@ -689,13 +689,14 @@ NSError *EnsureDecryptError(NSError *_Nullable error, NSString *fallbackErrorDes transaction:(YapDatabaseReadWriteTransaction *)transaction { NSString *masterHexEncodedPublicKey = [LKDatabaseUtilities getMasterHexEncodedPublicKeyFor:envelope.source in:transaction]; - TSThread *contactThread = [TSContactThread getOrCreateThreadWithContactId:masterHexEncodedPublicKey transaction:transaction]; + NSString *hexEncodedPublicKey = masterHexEncodedPublicKey ?: envelope.source; + TSThread *contactThread = [TSContactThread getOrCreateThreadWithContactId:hexEncodedPublicKey transaction:transaction]; // Trigger a session restore prompt if we get specific errors if (errorMessage.errorType == TSErrorMessageNoSession || errorMessage.errorType == TSErrorMessageInvalidMessage || errorMessage.errorType == TSErrorMessageInvalidKeyException) { - [((TSContactThread *) contactThread) addSessionRestoreDevice:masterHexEncodedPublicKey transaction:transaction]; + [((TSContactThread *) contactThread) addSessionRestoreDevice:hexEncodedPublicKey transaction:transaction]; } } @@ -704,7 +705,8 @@ NSError *EnsureDecryptError(NSError *_Nullable error, NSString *fallbackErrorDes transaction:(YapDatabaseReadWriteTransaction *)transaction { NSString *masterHexEncodedPublicKey = [LKDatabaseUtilities getMasterHexEncodedPublicKeyFor:envelope.source in:transaction]; - TSThread *contactThread = [TSContactThread getOrCreateThreadWithContactId:masterHexEncodedPublicKey transaction:transaction]; + NSString *hexEncodedPublicKey = masterHexEncodedPublicKey ?: envelope.source; + TSThread *contactThread = [TSContactThread getOrCreateThreadWithContactId:hexEncodedPublicKey transaction:transaction]; [SSKEnvironment.shared.notificationsManager notifyUserForErrorMessage:errorMessage thread:contactThread transaction:transaction]; From 770d8b2f3d523b215e371f9c2efa0db7d2eb2ea3 Mon Sep 17 00:00:00 2001 From: Mikunj Date: Tue, 10 Dec 2019 11:45:56 +1100 Subject: [PATCH 06/10] Added session restore message sending --- Pods | 2 +- Signal.xcodeproj/project.pbxproj | 4 + .../Messaging/SessionRestoreBannerView.swift | 83 +++++++++++++++++++ .../Cells/OWSSystemMessageCell.m | 6 +- .../ConversationViewController.m | 32 ++++++- .../translations/en.lproj/Localizable.strings | 2 + .../Loki/Messaging/LKSessionRestoreMessage.h | 8 ++ .../Loki/Messaging/LKSessionRestoreMessage.m | 23 +++++ .../Messages/Interactions/TSErrorMessage.m | 3 +- .../src/Messages/OWSMessageDecrypter.m | 9 +- .../src/Messages/OWSMessageManager.m | 4 - .../src/Messages/OWSMessageSender.h | 1 + .../src/Messages/OWSMessageSender.m | 28 +++++-- 13 files changed, 180 insertions(+), 25 deletions(-) create mode 100644 Signal/src/Loki/Messaging/SessionRestoreBannerView.swift create mode 100644 SignalServiceKit/src/Loki/Messaging/LKSessionRestoreMessage.h create mode 100644 SignalServiceKit/src/Loki/Messaging/LKSessionRestoreMessage.m diff --git a/Pods b/Pods index 79eaca77d..fd594cfe7 160000 --- a/Pods +++ b/Pods @@ -1 +1 @@ -Subproject commit 79eaca77d2f8b7aee0907f0610804682c41c8a4c +Subproject commit fd594cfe7694dd9b568254eaa9551ed18366ae0e diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index 96fb745af..aeb17a13a 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 2400888E239F30A600305217 /* SessionRestoreBannerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2400888D239F30A600305217 /* SessionRestoreBannerView.swift */; }; 241C6314231F64C000B4198E /* JazzIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = 241C630E231F5AAC00B4198E /* JazzIcon.swift */; }; 241C6315231F64CE00B4198E /* CGFloat+Rounding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 241C6312231F5F1D00B4198E /* CGFloat+Rounding.swift */; }; 241C6316231F64CE00B4198E /* UIColor+Helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 241C6310231F5C4400B4198E /* UIColor+Helper.swift */; }; @@ -681,6 +682,7 @@ 0F94C85CB0B235DA37F68ED0 /* Pods_SignalShareExtension.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SignalShareExtension.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 1C93CF3971B64E8B6C1F9AC1 /* Pods-SignalShareExtension.test.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SignalShareExtension.test.xcconfig"; path = "Pods/Target Support Files/Pods-SignalShareExtension/Pods-SignalShareExtension.test.xcconfig"; sourceTree = ""; }; 1CE3CD5C23334683BDD3D78C /* Pods-Signal.test.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Signal.test.xcconfig"; path = "Pods/Target Support Files/Pods-Signal/Pods-Signal.test.xcconfig"; sourceTree = ""; }; + 2400888D239F30A600305217 /* SessionRestoreBannerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionRestoreBannerView.swift; sourceTree = ""; }; 241C630E231F5AAC00B4198E /* JazzIcon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JazzIcon.swift; sourceTree = ""; }; 241C6310231F5C4400B4198E /* UIColor+Helper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor+Helper.swift"; sourceTree = ""; }; 241C6312231F5F1D00B4198E /* CGFloat+Rounding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CGFloat+Rounding.swift"; sourceTree = ""; }; @@ -2717,6 +2719,7 @@ B89841E222B7579F00B1BDC6 /* NewConversationVC.swift */, 24BD2608234DA2050008EB0A /* NewPublicChatVC.swift */, B893063E2383961A005EAA8E /* ScanQRCodeWrapperVC.swift */, + 2400888D239F30A600305217 /* SessionRestoreBannerView.swift */, ); path = Messaging; sourceTree = ""; @@ -3850,6 +3853,7 @@ B8162F0322891AD600D46544 /* FriendRequestView.swift in Sources */, 458E38371D668EBF0094BD24 /* OWSDeviceProvisioningURLParser.m in Sources */, 34B6A905218B4C91007C4606 /* TypingIndicatorInteraction.swift in Sources */, + 2400888E239F30A600305217 /* SessionRestoreBannerView.swift in Sources */, 4517642B1DE939FD00EDB8B9 /* ContactCell.swift in Sources */, 34EA69402194933900702471 /* MediaDownloadView.swift in Sources */, 340FC8AB204DAC8D007AEB0F /* DomainFrontingCountryViewController.m in Sources */, diff --git a/Signal/src/Loki/Messaging/SessionRestoreBannerView.swift b/Signal/src/Loki/Messaging/SessionRestoreBannerView.swift new file mode 100644 index 000000000..fb9d683a9 --- /dev/null +++ b/Signal/src/Loki/Messaging/SessionRestoreBannerView.swift @@ -0,0 +1,83 @@ +@objc(LKSessionRestoreBannerView) +final class SessionRestoreBannerView : UIView { + private let thread: TSThread + @objc public var onRestore: (() -> Void)? + @objc public var onDismiss: (() -> Void)? + + private lazy var bannerView: UIView = { + let bannerView = UIView.container() + bannerView.backgroundColor = UIColor.lokiGray() + bannerView.layer.cornerRadius = 2.5; + + // Use a shadow to "pop" the indicator above the other views. + bannerView.layer.shadowColor = UIColor.black.cgColor + bannerView.layer.shadowOffset = CGSize(width: 2, height: 3) + bannerView.layer.shadowRadius = 2 + bannerView.layer.shadowOpacity = 0.35 + return bannerView + }() + + private lazy var label: UILabel = { + let result = UILabel() + result.textColor = UIColor.white + result.font = UIFont.ows_dynamicTypeSubheadlineClamped + result.numberOfLines = 0 + result.textAlignment = .center + result.lineBreakMode = .byWordWrapping + return result + }() + + private lazy var buttonStackView: UIStackView = { + let result = UIStackView() + result.axis = .horizontal + result.distribution = .fillEqually + return result + }() + + private lazy var buttonFont = UIFont.ows_dynamicTypeBodyClamped.ows_mediumWeight() + private lazy var buttonHeight = buttonFont.pointSize * 48 / 17 + + // MARK: Lifecycle + @objc init(thread: TSThread) { + self.thread = thread; + super.init(frame: CGRect.zero) + initialize() + } + + required init?(coder: NSCoder) { fatalError("Using SessionRestoreBannerView.init(coder:) isn't allowed. Use SessionRestoreBannerView.init(thread:) instead.") } + override init(frame: CGRect) { fatalError("Using SessionRestoreBannerView.init(frame:) isn't allowed. Use SessionRestoreBannerView.init(thread:) instead.") } + + private func initialize() { + // Set up UI + let mainStackView = UIStackView() + mainStackView.axis = .vertical + mainStackView.distribution = .fill + mainStackView.addArrangedSubview(label) + mainStackView.addArrangedSubview(buttonStackView) + + let restoreButton = OWSFlatButton.button(title: NSLocalizedString("Restore session", comment: ""), font: buttonFont, titleColor: .ows_materialBlue, backgroundColor: .white, target: self, selector:#selector(restore)) + restoreButton.setBackgroundColors(upColor: .clear, downColor: .clear) + restoreButton.autoSetDimension(.height, toSize: buttonHeight) + buttonStackView.addArrangedSubview(restoreButton) + + let dismissButton = OWSFlatButton.button(title: NSLocalizedString("DISMISS_BUTTON_TEXT", comment: ""), font: buttonFont, titleColor: .ows_white, backgroundColor: .white, target: self, selector:#selector(dismiss)) + dismissButton.setBackgroundColors(upColor: .clear, downColor: .clear) + dismissButton.autoSetDimension(.height, toSize: buttonHeight) + buttonStackView.addArrangedSubview(dismissButton) + + bannerView.addSubview(mainStackView) + mainStackView.autoPinEdgesToSuperviewEdges(with: UIEdgeInsets(top: 16, left: 16, bottom: 8, right: 16)) + + addSubview(bannerView) + bannerView.autoPinEdgesToSuperviewEdges(with: UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)) + + if let contactID = thread.contactIdentifier() { + let displayName = Environment.shared.contactsManager.profileName(forRecipientId: contactID) ?? contactID + label.text = String(format: NSLocalizedString("Would you like to start a new session with %@?", comment: ""), displayName) + } + } + + @objc private func restore() { onRestore?() } + + @objc private func dismiss() { onDismiss?() } +} diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSSystemMessageCell.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSSystemMessageCell.m index b79a96acc..72aa6922c 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSSystemMessageCell.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSSystemMessageCell.m @@ -431,12 +431,8 @@ typedef void (^SystemMessageActionBlock)(void); }]; case TSErrorMessageMissingKeyId: case TSErrorMessageNoSession: - return nil; case TSErrorMessageInvalidMessage: - return [SystemMessageAction actionWithTitle:NSLocalizedString(@"FINGERPRINT_SHRED_KEYMATERIAL_BUTTON", @"") - block:^{ - [weakSelf.delegate tappedCorruptedMessage:message]; - }]; + return nil; case TSErrorMessageDuplicateMessage: case TSErrorMessageInvalidVersion: return nil; diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m index 58554ad63..cb33c845a 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m @@ -803,6 +803,7 @@ typedef enum : NSUInteger { OWSLogDebug(@"viewWillAppear"); [self ensureBannerState]; + [self updateSessionRestoreBanner]; [super viewWillAppear:animated]; @@ -967,8 +968,20 @@ typedef enum : NSUInteger { if (isContactThread) { TSContactThread *thread = (TSContactThread *)self.thread; if (thread.sessionRestoreDevices.count > 0) { - if (self.restoreSessionBannerView) { - // TODO: Create banner here + if (!self.restoreSessionBannerView) { + LKSessionRestoreBannerView *bannerView = [[LKSessionRestoreBannerView alloc] initWithThread:thread]; + [self.view addSubview:bannerView]; + [bannerView autoPinEdgeToSuperviewEdge:ALEdgeTop]; + [bannerView autoPinEdgeToSuperviewEdge:ALEdgeLeft]; + [bannerView autoPinEdgeToSuperviewEdge:ALEdgeRight]; + [self.view layoutSubviews]; + self.restoreSessionBannerView = bannerView; + [bannerView setOnRestore:^{ + [self restoreSession]; + }]; + [bannerView setOnDismiss:^{ + [thread removeAllRessionRestoreDevicesWithTransaction:nil]; + }]; } } else { shouldRemoveBanner = true; @@ -1154,10 +1167,21 @@ typedef enum : NSUInteger { - (void)restoreSession { if ([self.thread isKindOfClass:[TSContactThread class]]) { + OWSMessageSender *messageSender = SSKEnvironment.shared.messageSender; TSContactThread *thread = (TSContactThread *)self.thread; NSArray *devices = thread.sessionRestoreDevices; - // TODO: Send session restore to all devices - // TODO: Add message saying session restore was sent + for (NSString *device in devices) { + if (device.length == 0) { continue; } + OWSMessageSend *sessionRestoreMessage = [messageSender getSessionRestoreMessageForHexEncodedPublicKey:device]; + if (sessionRestoreMessage) { + dispatch_async(OWSDispatch.sendingQueue, ^{ + [messageSender sendMessage:sessionRestoreMessage]; + }); + } + } + [[[TSInfoMessage alloc] initWithTimestamp:NSDate.ows_millisecondTimeStamp inThread:thread messageType:TSInfoMessageTypeLokiSessionResetInProgress] save]; + thread.sessionResetState = TSContactThreadSessionResetStateRequestReceived; + [thread save]; [thread removeAllRessionRestoreDevicesWithTransaction:nil]; } } diff --git a/Signal/translations/en.lproj/Localizable.strings b/Signal/translations/en.lproj/Localizable.strings index f8d1e9a55..9c28757fa 100644 --- a/Signal/translations/en.lproj/Localizable.strings +++ b/Signal/translations/en.lproj/Localizable.strings @@ -2672,3 +2672,5 @@ "Your device was unlinked successfully" = "Your device was unlinked successfully"; "Unnamed Device" = "Unnamed Device"; "Linked device (%@)" = "Linked device (%@)"; +"Restore session" = "Restore session"; +"Would you like to start a new session with %@?" = "Would you like to start a new session with %@?"; diff --git a/SignalServiceKit/src/Loki/Messaging/LKSessionRestoreMessage.h b/SignalServiceKit/src/Loki/Messaging/LKSessionRestoreMessage.h new file mode 100644 index 000000000..876cde8d2 --- /dev/null +++ b/SignalServiceKit/src/Loki/Messaging/LKSessionRestoreMessage.h @@ -0,0 +1,8 @@ +#import "LKFriendRequestMessage.h" + +NS_SWIFT_NAME(FriendRequestMessage) +@interface LKSessionRestoreMessage : LKFriendRequestMessage + +- (instancetype)initWithThread:(TSThread *)thread; + +@end diff --git a/SignalServiceKit/src/Loki/Messaging/LKSessionRestoreMessage.m b/SignalServiceKit/src/Loki/Messaging/LKSessionRestoreMessage.m new file mode 100644 index 000000000..43ca9c3ea --- /dev/null +++ b/SignalServiceKit/src/Loki/Messaging/LKSessionRestoreMessage.m @@ -0,0 +1,23 @@ +#import "LKSessionRestoreMessage.h" +#import +#import + +@implementation LKSessionRestoreMessage + +#pragma mark Initialization +- (instancetype)initWithThread:(TSThread *)thread { + return [self initOutgoingMessageWithTimestamp:NSDate.ows_millisecondTimeStamp inThread:thread messageBody:@"" attachmentIds:[NSMutableArray new] + expiresInSeconds:0 expireStartedAt:0 isVoiceMessage:NO groupMetaMessage:TSGroupMetaMessageUnspecified quotedMessage:nil contactShare:nil linkPreview:nil]; +} + +#pragma mark Building +- (nullable SSKProtoDataMessageBuilder *)dataMessageBuilder +{ + SSKProtoDataMessageBuilder *builder = super.dataMessageBuilder; + if (builder == nil) { return nil; } + [builder setFlags:SSKProtoDataMessageFlagsSessionRestore]; + return builder; +} + + +@end diff --git a/SignalServiceKit/src/Messages/Interactions/TSErrorMessage.m b/SignalServiceKit/src/Messages/Interactions/TSErrorMessage.m index 63e37e645..72f0064a5 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSErrorMessage.m +++ b/SignalServiceKit/src/Messages/Interactions/TSErrorMessage.m @@ -89,8 +89,9 @@ NSUInteger TSErrorMessageSchemaVersion = 1; withTransaction:(YapDatabaseReadWriteTransaction *)transaction failedMessageType:(TSErrorMessageType)errorMessageType { + NSString *source = [LKDatabaseUtilities getMasterHexEncodedPublicKeyFor:envelope.source in:transaction] ?: envelope.source; TSContactThread *contactThread = - [TSContactThread getOrCreateThreadWithContactId:envelope.source transaction:transaction]; + [TSContactThread getOrCreateThreadWithContactId:source transaction:transaction]; // Legit usage of senderTimestamp. We don't actually currently surface it in the UI, but it serves as // a reference to the envelope which we failed to process. diff --git a/SignalServiceKit/src/Messages/OWSMessageDecrypter.m b/SignalServiceKit/src/Messages/OWSMessageDecrypter.m index 78cadcd99..cfcede47b 100644 --- a/SignalServiceKit/src/Messages/OWSMessageDecrypter.m +++ b/SignalServiceKit/src/Messages/OWSMessageDecrypter.m @@ -688,15 +688,15 @@ NSError *EnsureDecryptError(NSError *_Nullable error, NSString *fallbackErrorDes envelope:(SSKProtoEnvelope *)envelope transaction:(YapDatabaseReadWriteTransaction *)transaction { - NSString *masterHexEncodedPublicKey = [LKDatabaseUtilities getMasterHexEncodedPublicKeyFor:envelope.source in:transaction]; - NSString *hexEncodedPublicKey = masterHexEncodedPublicKey ?: envelope.source; + NSString *hexEncodedPublicKey = [LKDatabaseUtilities getMasterHexEncodedPublicKeyFor:envelope.source in:transaction] ?: envelope.source; TSThread *contactThread = [TSContactThread getOrCreateThreadWithContactId:hexEncodedPublicKey transaction:transaction]; // Trigger a session restore prompt if we get specific errors if (errorMessage.errorType == TSErrorMessageNoSession || errorMessage.errorType == TSErrorMessageInvalidMessage || errorMessage.errorType == TSErrorMessageInvalidKeyException) { - [((TSContactThread *) contactThread) addSessionRestoreDevice:hexEncodedPublicKey transaction:transaction]; + // We want to store the source device pubkey into the session restore incase it's a secondary device message + [((TSContactThread *) contactThread) addSessionRestoreDevice:envelope.source transaction:transaction]; } } @@ -704,8 +704,7 @@ NSError *EnsureDecryptError(NSError *_Nullable error, NSString *fallbackErrorDes envelope:(SSKProtoEnvelope *)envelope transaction:(YapDatabaseReadWriteTransaction *)transaction { - NSString *masterHexEncodedPublicKey = [LKDatabaseUtilities getMasterHexEncodedPublicKeyFor:envelope.source in:transaction]; - NSString *hexEncodedPublicKey = masterHexEncodedPublicKey ?: envelope.source; + NSString *hexEncodedPublicKey = [LKDatabaseUtilities getMasterHexEncodedPublicKeyFor:envelope.source in:transaction] ?: envelope.source; TSThread *contactThread = [TSContactThread getOrCreateThreadWithContactId:hexEncodedPublicKey transaction:transaction]; [SSKEnvironment.shared.notificationsManager notifyUserForErrorMessage:errorMessage thread:contactThread diff --git a/SignalServiceKit/src/Messages/OWSMessageManager.m b/SignalServiceKit/src/Messages/OWSMessageManager.m index 67849777b..ab283db93 100644 --- a/SignalServiceKit/src/Messages/OWSMessageManager.m +++ b/SignalServiceKit/src/Messages/OWSMessageManager.m @@ -1723,10 +1723,6 @@ NS_ASSUME_NONNULL_BEGIN }]; } message.friendRequestStatus = LKMessageFriendRequestStatusPending; // Don't save yet. This is done in finalizeIncomingMessage:thread:masterThread:envelope:transaction. - } else { - // This can happen if Alice and Bob have a session, Bob deletes his app, restores from seed, and then sends a friend request to Alice again. - // TODO: Re-enable when seed restoration is done -// [self handleEndSessionMessageWithEnvelope:envelope dataMessage:data transaction:transaction]; } } diff --git a/SignalServiceKit/src/Messages/OWSMessageSender.h b/SignalServiceKit/src/Messages/OWSMessageSender.h index f5f2e1364..f223aadcf 100644 --- a/SignalServiceKit/src/Messages/OWSMessageSender.h +++ b/SignalServiceKit/src/Messages/OWSMessageSender.h @@ -97,6 +97,7 @@ NS_SWIFT_NAME(MessageSender) success:(void (^)(void))successHandler failure:(void (^)(NSError *error))failureHandler; +- (OWSMessageSend *)getSessionRestoreMessageForHexEncodedPublicKey:(NSString *)hexEncodedPublicKey; - (OWSMessageSend *)getMultiDeviceFriendRequestMessageForHexEncodedPublicKey:(NSString *)hexEncodedPublicKey; - (void)sendMessage:(OWSMessageSend *)messageSend; diff --git a/SignalServiceKit/src/Messages/OWSMessageSender.m b/SignalServiceKit/src/Messages/OWSMessageSender.m index 307d7c8ea..4d11a2e62 100644 --- a/SignalServiceKit/src/Messages/OWSMessageSender.m +++ b/SignalServiceKit/src/Messages/OWSMessageSender.m @@ -45,6 +45,7 @@ #import "TSThread.h" #import "TSContactThread.h" #import "LKFriendRequestMessage.h" +#import "LKSessionRestoreMessage.h" #import "LKDeviceLinkMessage.h" #import "LKAddressMessage.h" #import @@ -915,15 +916,32 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; return deviceMessages; } +- (OWSMessageSend *)getSessionRestoreMessageForHexEncodedPublicKey:(NSString *)hexEncodedPublicKey +{ + __block TSContactThread *thread; + [OWSPrimaryStorage.sharedManager.dbReadWriteConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { + thread = [TSContactThread getOrCreateThreadWithContactId:hexEncodedPublicKey transaction:transaction]; + // Force hide secondary device thread + NSString *masterHexEncodedPublicKey = [LKDatabaseUtilities getMasterHexEncodedPublicKeyFor:hexEncodedPublicKey in:transaction]; + thread.isForceHidden = masterHexEncodedPublicKey != nil && ![masterHexEncodedPublicKey isEqualToString:hexEncodedPublicKey]; + [thread saveWithTransaction:transaction]; + }]; + if (!thread) { return nil; } + LKSessionRestoreMessage *message = [[LKSessionRestoreMessage alloc] initWithThread:thread]; + message.skipSave = YES; + SignalRecipient *recipient = [[SignalRecipient alloc] initWithUniqueId:hexEncodedPublicKey]; + NSString *userHexEncodedPublicKey = OWSIdentityManager.sharedManager.identityKeyPair.hexEncodedPublicKey; + return [[OWSMessageSend alloc] initWithMessage:message thread:thread recipient:recipient senderCertificate:nil udAccess:nil localNumber:userHexEncodedPublicKey success:^{ } failure:^(NSError *error) { }]; +} + - (OWSMessageSend *)getMultiDeviceFriendRequestMessageForHexEncodedPublicKey:(NSString *)hexEncodedPublicKey { __block TSContactThread *thread; [OWSPrimaryStorage.sharedManager.dbReadWriteConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { - thread = [TSContactThread fetchObjectWithUniqueID:[TSContactThread threadIdFromContactId:hexEncodedPublicKey] transaction:transaction]; - if (thread == nil) { - thread = [[TSContactThread alloc] initWithContactId:hexEncodedPublicKey]; - } - thread.isForceHidden = YES; + thread = [TSContactThread getOrCreateThreadWithContactId:hexEncodedPublicKey transaction:transaction]; + // Force hide secondary device thread + NSString *masterHexEncodedPublicKey = [LKDatabaseUtilities getMasterHexEncodedPublicKeyFor:hexEncodedPublicKey in:transaction]; + thread.isForceHidden = masterHexEncodedPublicKey != nil && ![masterHexEncodedPublicKey isEqualToString:hexEncodedPublicKey]; [thread saveWithTransaction:transaction]; }]; LKFriendRequestMessage *message = [[LKFriendRequestMessage alloc] initOutgoingMessageWithTimestamp:NSDate.ows_millisecondTimeStamp inThread:thread messageBody:@"Please accept to enable messages to be synced across devices" attachmentIds:[NSMutableArray new] From 57db0bd2112844f0e3ed58a84aab4a6b70c9b884 Mon Sep 17 00:00:00 2001 From: Mikunj Date: Tue, 10 Dec 2019 15:59:20 +1100 Subject: [PATCH 07/10] Clean --- .../ConversationView/ConversationViewController.m | 8 ++++---- SignalServiceKit/src/Contacts/Threads/TSContactThread.h | 2 +- SignalServiceKit/src/Contacts/Threads/TSContactThread.m | 2 +- SignalServiceKit/src/Messages/OWSMessageSender.m | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m index cb33c845a..e40f46059 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m @@ -980,7 +980,7 @@ typedef enum : NSUInteger { [self restoreSession]; }]; [bannerView setOnDismiss:^{ - [thread removeAllRessionRestoreDevicesWithTransaction:nil]; + [thread removeAllSessionRestoreDevicesWithTransaction:nil]; }]; } } else { @@ -1000,11 +1000,11 @@ typedef enum : NSUInteger { // rebuild the indicator view every time. [self.bannerView removeFromSuperview]; self.bannerView = nil; - + if (self.userHasScrolled) { return; } - + NSArray *noLongerVerifiedRecipientIds = [self noLongerVerifiedRecipientIds]; if (noLongerVerifiedRecipientIds.count > 0) { @@ -1182,7 +1182,7 @@ typedef enum : NSUInteger { [[[TSInfoMessage alloc] initWithTimestamp:NSDate.ows_millisecondTimeStamp inThread:thread messageType:TSInfoMessageTypeLokiSessionResetInProgress] save]; thread.sessionResetState = TSContactThreadSessionResetStateRequestReceived; [thread save]; - [thread removeAllRessionRestoreDevicesWithTransaction:nil]; + [thread removeAllSessionRestoreDevicesWithTransaction:nil]; } } diff --git a/SignalServiceKit/src/Contacts/Threads/TSContactThread.h b/SignalServiceKit/src/Contacts/Threads/TSContactThread.h index 9b62cd4d1..75ed6b416 100644 --- a/SignalServiceKit/src/Contacts/Threads/TSContactThread.h +++ b/SignalServiceKit/src/Contacts/Threads/TSContactThread.h @@ -52,7 +52,7 @@ extern NSString *const TSContactThreadPrefix; - (void)addSessionRestoreDevice:(NSString *)hexEncodedPublicKey transaction:(YapDatabaseReadWriteTransaction *_Nullable)transaction; -- (void)removeAllRessionRestoreDevicesWithTransaction:(YapDatabaseReadWriteTransaction *_Nullable)transaction; +- (void)removeAllSessionRestoreDevicesWithTransaction:(YapDatabaseReadWriteTransaction *_Nullable)transaction; @end diff --git a/SignalServiceKit/src/Contacts/Threads/TSContactThread.m b/SignalServiceKit/src/Contacts/Threads/TSContactThread.m index c0ba4c804..765943100 100644 --- a/SignalServiceKit/src/Contacts/Threads/TSContactThread.m +++ b/SignalServiceKit/src/Contacts/Threads/TSContactThread.m @@ -118,7 +118,7 @@ NSString *const TSContactThreadPrefix = @"c"; [self setSessionRestoreDevices:[set allObjects] transaction:transaction]; } -- (void)removeAllRessionRestoreDevicesWithTransaction:(YapDatabaseReadWriteTransaction *_Nullable)transaction +- (void)removeAllSessionRestoreDevicesWithTransaction:(YapDatabaseReadWriteTransaction *_Nullable)transaction { [self setSessionRestoreDevices:@[] transaction:transaction]; } diff --git a/SignalServiceKit/src/Messages/OWSMessageSender.m b/SignalServiceKit/src/Messages/OWSMessageSender.m index 4d11a2e62..845af8bf4 100644 --- a/SignalServiceKit/src/Messages/OWSMessageSender.m +++ b/SignalServiceKit/src/Messages/OWSMessageSender.m @@ -1335,7 +1335,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; [message.thread saveFriendRequestStatus:LKThreadFriendRequestStatusRequestSent withTransaction:transaction]; [message.thread removeOldOutgoingFriendRequestMessagesIfNeededWithTransaction:transaction]; if ([message.thread isKindOfClass:[TSContactThread class]]) { - [((TSContactThread *) message.thread) removeAllRessionRestoreDevicesWithTransaction:transaction]; + [((TSContactThread *) message.thread) removeAllSessionRestoreDevicesWithTransaction:transaction]; } // Update the message [message saveFriendRequestStatus:LKMessageFriendRequestStatusPending withTransaction:transaction]; From 477fbe494df7bb4d937bf8201667bc3b4017cd22 Mon Sep 17 00:00:00 2001 From: Niels Andriesse Date: Fri, 24 Jan 2020 11:03:45 +1100 Subject: [PATCH 08/10] Fix build --- Signal.xcodeproj/project.pbxproj | 2 +- .../{Messaging => Components}/SessionRestoreBannerView.swift | 0 Signal/src/Loki/View Controllers/RestoreVC.swift | 1 + SignalServiceKit/src/Loki/Database/OWSPrimaryStorage+Loki.m | 1 + 4 files changed, 3 insertions(+), 1 deletion(-) rename Signal/src/Loki/{Messaging => Components}/SessionRestoreBannerView.swift (100%) diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index 8786ae6d8..35c09f8df 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -2785,6 +2785,7 @@ B85357BE23A1AE0800AAF6CD /* SeedReminderView.swift */, B85357C023A1B81900AAF6CD /* SeedReminderViewDelegate.swift */, B8BB82B82394911B00BA5194 /* Separator.swift */, + 2400888D239F30A600305217 /* SessionRestoreBannerView.swift */, B8CCF638239721E20091D419 /* TabBar.swift */, B8BB82B423947F2D00BA5194 /* TextField.swift */, ); @@ -2824,7 +2825,6 @@ B82B408B239A068800A248E7 /* RegisterVC.swift */, B82B408F239DD75000A248E7 /* RestoreVC.swift */, B893063E2383961A005EAA8E /* ScanQRCodeWrapperVC.swift */, - 2400888D239F30A600305217 /* SessionRestoreBannerView.swift */, B86BD08523399CEF000F5AE3 /* SeedModal.swift */, B85357C223A1BD1200AAF6CD /* SeedVCV2.swift */, B8CCF6422397711F0091D419 /* SettingsVC.swift */, diff --git a/Signal/src/Loki/Messaging/SessionRestoreBannerView.swift b/Signal/src/Loki/Components/SessionRestoreBannerView.swift similarity index 100% rename from Signal/src/Loki/Messaging/SessionRestoreBannerView.swift rename to Signal/src/Loki/Components/SessionRestoreBannerView.swift diff --git a/Signal/src/Loki/View Controllers/RestoreVC.swift b/Signal/src/Loki/View Controllers/RestoreVC.swift index b6157326d..6b0b07cbc 100644 --- a/Signal/src/Loki/View Controllers/RestoreVC.swift +++ b/Signal/src/Loki/View Controllers/RestoreVC.swift @@ -176,6 +176,7 @@ final class RestoreVC : UIViewController { databaseConnection.setObject(seed.toHexString(), forKey: "LKLokiSeed", inCollection: OWSPrimaryStorageIdentityKeyStoreCollection) databaseConnection.setObject(keyPair, forKey: OWSPrimaryStorageIdentityKeyStoreIdentityKey, inCollection: OWSPrimaryStorageIdentityKeyStoreCollection) TSAccountManager.sharedInstance().phoneNumberAwaitingVerification = keyPair.hexEncodedPublicKey + OWSPrimaryStorage.shared().setRestorationTime(Date().timeIntervalSince1970) mnemonicTextField.resignFirstResponder() Timer.scheduledTimer(withTimeInterval: 0.25, repeats: false) { _ in let displayNameVC = DisplayNameVC() diff --git a/SignalServiceKit/src/Loki/Database/OWSPrimaryStorage+Loki.m b/SignalServiceKit/src/Loki/Database/OWSPrimaryStorage+Loki.m index 1a71d58d7..9b9e9e7cf 100644 --- a/SignalServiceKit/src/Loki/Database/OWSPrimaryStorage+Loki.m +++ b/SignalServiceKit/src/Loki/Database/OWSPrimaryStorage+Loki.m @@ -196,5 +196,6 @@ - (NSTimeInterval)getRestorationTime { return [self.dbReadConnection doubleForKey:@"restoration_time" inCollection:LKGeneralCollection defaultValue:0]; +} @end From 48804db7707e4ee9f20f3163942abe950b05af29 Mon Sep 17 00:00:00 2001 From: Niels Andriesse Date: Fri, 24 Jan 2020 11:04:18 +1100 Subject: [PATCH 09/10] Update Pods --- Pods | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Pods b/Pods index 80b9ef94d..3a470c921 160000 --- a/Pods +++ b/Pods @@ -1 +1 @@ -Subproject commit 80b9ef94d107a4c2794164537da30eb4482af49a +Subproject commit 3a470c921a599097807df0cdbff4b2f238c91f37 From a433d131770a7f8e46639f2e4d82a64aa178cc12 Mon Sep 17 00:00:00 2001 From: Niels Andriesse Date: Fri, 24 Jan 2020 13:14:49 +1100 Subject: [PATCH 10/10] Clean up session restoration view UI --- Signal.xcodeproj/project.pbxproj | 8 +- .../Loki/Components/FriendRequestView.swift | 14 ++-- .../Components/SessionRestorationView.swift | 84 +++++++++++++++++++ .../Components/SessionRestoreBannerView.swift | 83 ------------------ .../ConversationViewController.m | 12 +-- .../translations/en.lproj/Localizable.strings | 21 +++-- 6 files changed, 114 insertions(+), 108 deletions(-) create mode 100644 Signal/src/Loki/Components/SessionRestorationView.swift delete mode 100644 Signal/src/Loki/Components/SessionRestoreBannerView.swift diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index 35c09f8df..fd2e204a1 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -7,7 +7,7 @@ objects = { /* Begin PBXBuildFile section */ - 2400888E239F30A600305217 /* SessionRestoreBannerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2400888D239F30A600305217 /* SessionRestoreBannerView.swift */; }; + 2400888E239F30A600305217 /* SessionRestorationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2400888D239F30A600305217 /* SessionRestorationView.swift */; }; 241C6314231F64C000B4198E /* JazzIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = 241C630E231F5AAC00B4198E /* JazzIcon.swift */; }; 241C6315231F64CE00B4198E /* CGFloat+Rounding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 241C6312231F5F1D00B4198E /* CGFloat+Rounding.swift */; }; 241C6316231F64CE00B4198E /* UIColor+Helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 241C6310231F5C4400B4198E /* UIColor+Helper.swift */; }; @@ -710,7 +710,7 @@ 0F94C85CB0B235DA37F68ED0 /* Pods_SignalShareExtension.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SignalShareExtension.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 1C93CF3971B64E8B6C1F9AC1 /* Pods-SignalShareExtension.test.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SignalShareExtension.test.xcconfig"; path = "Pods/Target Support Files/Pods-SignalShareExtension/Pods-SignalShareExtension.test.xcconfig"; sourceTree = ""; }; 1CE3CD5C23334683BDD3D78C /* Pods-Signal.test.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Signal.test.xcconfig"; path = "Pods/Target Support Files/Pods-Signal/Pods-Signal.test.xcconfig"; sourceTree = ""; }; - 2400888D239F30A600305217 /* SessionRestoreBannerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionRestoreBannerView.swift; sourceTree = ""; }; + 2400888D239F30A600305217 /* SessionRestorationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionRestorationView.swift; sourceTree = ""; }; 241C630E231F5AAC00B4198E /* JazzIcon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JazzIcon.swift; sourceTree = ""; }; 241C6310231F5C4400B4198E /* UIColor+Helper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor+Helper.swift"; sourceTree = ""; }; 241C6312231F5F1D00B4198E /* CGFloat+Rounding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CGFloat+Rounding.swift"; sourceTree = ""; }; @@ -2785,7 +2785,7 @@ B85357BE23A1AE0800AAF6CD /* SeedReminderView.swift */, B85357C023A1B81900AAF6CD /* SeedReminderViewDelegate.swift */, B8BB82B82394911B00BA5194 /* Separator.swift */, - 2400888D239F30A600305217 /* SessionRestoreBannerView.swift */, + 2400888D239F30A600305217 /* SessionRestorationView.swift */, B8CCF638239721E20091D419 /* TabBar.swift */, B8BB82B423947F2D00BA5194 /* TextField.swift */, ); @@ -3983,7 +3983,7 @@ B8162F0322891AD600D46544 /* FriendRequestView.swift in Sources */, 458E38371D668EBF0094BD24 /* OWSDeviceProvisioningURLParser.m in Sources */, 34B6A905218B4C91007C4606 /* TypingIndicatorInteraction.swift in Sources */, - 2400888E239F30A600305217 /* SessionRestoreBannerView.swift in Sources */, + 2400888E239F30A600305217 /* SessionRestorationView.swift in Sources */, B886B4A72398B23E00211ABE /* QRCodeVC.swift in Sources */, 4517642B1DE939FD00EDB8B9 /* ContactCell.swift in Sources */, 34EA69402194933900702471 /* MediaDownloadView.swift in Sources */, diff --git a/Signal/src/Loki/Components/FriendRequestView.swift b/Signal/src/Loki/Components/FriendRequestView.swift index f4a79c8f5..905bd6a4c 100644 --- a/Signal/src/Loki/Components/FriendRequestView.swift +++ b/Signal/src/Loki/Components/FriendRequestView.swift @@ -114,10 +114,10 @@ final class FriendRequestView : UIView { let format: String = { switch (message.friendRequestStatus) { case .none, .sendingOrFailed: preconditionFailure() - case .pending: return NSLocalizedString("%@ sent you a message request", comment: "") - case .accepted: return NSLocalizedString("You've accepted %@'s message request", comment: "") - case .declined: return NSLocalizedString("You've declined %@'s message request", comment: "") - case .expired: return NSLocalizedString("%@'s message request has expired", comment: "") + case .pending: return NSLocalizedString("%@ sent you a session request", comment: "") + case .accepted: return NSLocalizedString("You've accepted %@'s session request", comment: "") + case .declined: return NSLocalizedString("You've declined %@'s session request", comment: "") + case .expired: return NSLocalizedString("%@'s session request has expired", comment: "") default: preconditionFailure() } }() @@ -130,10 +130,10 @@ final class FriendRequestView : UIView { switch (message.friendRequestStatus) { case .none: preconditionFailure() case .sendingOrFailed: return nil - case .pending: return NSLocalizedString("You've sent %@ a message request", comment: "") - case .accepted: return NSLocalizedString("%@ accepted your message request", comment: "") + case .pending: return NSLocalizedString("You've sent %@ a session request", comment: "") + case .accepted: return NSLocalizedString("%@ accepted your session request", comment: "") case .declined: preconditionFailure() - case .expired: return NSLocalizedString("Your message request to %@ has expired", comment: "") + case .expired: return NSLocalizedString("Your session request to %@ has expired", comment: "") default: preconditionFailure() } }() diff --git a/Signal/src/Loki/Components/SessionRestorationView.swift b/Signal/src/Loki/Components/SessionRestorationView.swift new file mode 100644 index 000000000..bac9d4737 --- /dev/null +++ b/Signal/src/Loki/Components/SessionRestorationView.swift @@ -0,0 +1,84 @@ + +@objc(LKSessionRestorationView) +final class SessionRestorationView : UIView { + private let thread: TSThread + @objc public var onRestore: (() -> Void)? + @objc public var onDismiss: (() -> Void)? + + // MARK: Lifecycle + @objc init(thread: TSThread) { + self.thread = thread; + super.init(frame: CGRect.zero) + initialize() + } + + required init?(coder: NSCoder) { fatalError("Using SessionRestorationView.init(coder:) isn't allowed. Use SessionRestorationView.init(thread:) instead.") } + override init(frame: CGRect) { fatalError("Using SessionRestorationView.init(frame:) isn't allowed. Use SessionRestorationView.init(thread:) instead.") } + + private func initialize() { + // Set up background + backgroundColor = Colors.modalBackground + layer.cornerRadius = Values.modalCornerRadius + layer.masksToBounds = false + layer.borderColor = Colors.modalBorder.cgColor + layer.borderWidth = Values.borderThickness + layer.shadowColor = UIColor.black.cgColor + layer.shadowRadius = 8 + layer.shadowOpacity = 0.64 + // Set up title label + let titleLabel = UILabel() + titleLabel.textColor = Colors.text + titleLabel.font = .boldSystemFont(ofSize: Values.mediumFontSize) + titleLabel.text = NSLocalizedString("Session Out of Sync", comment: "") + titleLabel.numberOfLines = 0 + titleLabel.lineBreakMode = .byWordWrapping + titleLabel.textAlignment = .center + // Set up explanation label + let explanationLabel = UILabel() + explanationLabel.textColor = Colors.text + explanationLabel.font = .systemFont(ofSize: Values.smallFontSize) + explanationLabel.text = NSLocalizedString("Would you like to restore your session?", comment: "") + explanationLabel.numberOfLines = 0 + explanationLabel.textAlignment = .center + explanationLabel.lineBreakMode = .byWordWrapping + // Set up restore button + let restoreButton = UIButton() + restoreButton.set(.height, to: Values.mediumButtonHeight) + restoreButton.layer.cornerRadius = Values.modalButtonCornerRadius + restoreButton.backgroundColor = Colors.accent + restoreButton.titleLabel!.font = .systemFont(ofSize: Values.smallFontSize) + restoreButton.setTitleColor(Colors.text, for: UIControl.State.normal) + restoreButton.setTitle(NSLocalizedString("Restore", comment: ""), for: UIControl.State.normal) + restoreButton.addTarget(self, action: #selector(restore), for: UIControl.Event.touchUpInside) + // Set up dismiss button + let dismissButton = UIButton() + dismissButton.set(.height, to: Values.mediumButtonHeight) + dismissButton.layer.cornerRadius = Values.modalButtonCornerRadius + dismissButton.backgroundColor = Colors.buttonBackground + dismissButton.titleLabel!.font = .systemFont(ofSize: Values.smallFontSize) + dismissButton.setTitleColor(Colors.text, for: UIControl.State.normal) + dismissButton.setTitle(NSLocalizedString("Dismiss", comment: ""), for: UIControl.State.normal) + dismissButton.addTarget(self, action: #selector(dismiss), for: UIControl.Event.touchUpInside) + // Set up button stack view + let buttonStackView = UIStackView(arrangedSubviews: [ dismissButton, restoreButton ]) + buttonStackView.axis = .horizontal + buttonStackView.spacing = Values.mediumSpacing + buttonStackView.distribution = .fillEqually + // Set up main stack view + let mainStackView = UIStackView(arrangedSubviews: [ titleLabel, explanationLabel, buttonStackView ]) + mainStackView.axis = .vertical + mainStackView.spacing = Values.smallSpacing + addSubview(mainStackView) + mainStackView.pin(to: self, withInset: Values.mediumSpacing) + // Update explanation label if possible + if let contactID = thread.contactIdentifier() { + let displayName = DisplayNameUtilities.getPrivateChatDisplayName(for: contactID) ?? contactID + explanationLabel.text = String(format: NSLocalizedString("Would you like to restore your session with %@?", comment: ""), displayName) + } + } + + // MARK: Interaction + @objc private func restore() { onRestore?() } + + @objc private func dismiss() { onDismiss?() } +} diff --git a/Signal/src/Loki/Components/SessionRestoreBannerView.swift b/Signal/src/Loki/Components/SessionRestoreBannerView.swift deleted file mode 100644 index fb9d683a9..000000000 --- a/Signal/src/Loki/Components/SessionRestoreBannerView.swift +++ /dev/null @@ -1,83 +0,0 @@ -@objc(LKSessionRestoreBannerView) -final class SessionRestoreBannerView : UIView { - private let thread: TSThread - @objc public var onRestore: (() -> Void)? - @objc public var onDismiss: (() -> Void)? - - private lazy var bannerView: UIView = { - let bannerView = UIView.container() - bannerView.backgroundColor = UIColor.lokiGray() - bannerView.layer.cornerRadius = 2.5; - - // Use a shadow to "pop" the indicator above the other views. - bannerView.layer.shadowColor = UIColor.black.cgColor - bannerView.layer.shadowOffset = CGSize(width: 2, height: 3) - bannerView.layer.shadowRadius = 2 - bannerView.layer.shadowOpacity = 0.35 - return bannerView - }() - - private lazy var label: UILabel = { - let result = UILabel() - result.textColor = UIColor.white - result.font = UIFont.ows_dynamicTypeSubheadlineClamped - result.numberOfLines = 0 - result.textAlignment = .center - result.lineBreakMode = .byWordWrapping - return result - }() - - private lazy var buttonStackView: UIStackView = { - let result = UIStackView() - result.axis = .horizontal - result.distribution = .fillEqually - return result - }() - - private lazy var buttonFont = UIFont.ows_dynamicTypeBodyClamped.ows_mediumWeight() - private lazy var buttonHeight = buttonFont.pointSize * 48 / 17 - - // MARK: Lifecycle - @objc init(thread: TSThread) { - self.thread = thread; - super.init(frame: CGRect.zero) - initialize() - } - - required init?(coder: NSCoder) { fatalError("Using SessionRestoreBannerView.init(coder:) isn't allowed. Use SessionRestoreBannerView.init(thread:) instead.") } - override init(frame: CGRect) { fatalError("Using SessionRestoreBannerView.init(frame:) isn't allowed. Use SessionRestoreBannerView.init(thread:) instead.") } - - private func initialize() { - // Set up UI - let mainStackView = UIStackView() - mainStackView.axis = .vertical - mainStackView.distribution = .fill - mainStackView.addArrangedSubview(label) - mainStackView.addArrangedSubview(buttonStackView) - - let restoreButton = OWSFlatButton.button(title: NSLocalizedString("Restore session", comment: ""), font: buttonFont, titleColor: .ows_materialBlue, backgroundColor: .white, target: self, selector:#selector(restore)) - restoreButton.setBackgroundColors(upColor: .clear, downColor: .clear) - restoreButton.autoSetDimension(.height, toSize: buttonHeight) - buttonStackView.addArrangedSubview(restoreButton) - - let dismissButton = OWSFlatButton.button(title: NSLocalizedString("DISMISS_BUTTON_TEXT", comment: ""), font: buttonFont, titleColor: .ows_white, backgroundColor: .white, target: self, selector:#selector(dismiss)) - dismissButton.setBackgroundColors(upColor: .clear, downColor: .clear) - dismissButton.autoSetDimension(.height, toSize: buttonHeight) - buttonStackView.addArrangedSubview(dismissButton) - - bannerView.addSubview(mainStackView) - mainStackView.autoPinEdgesToSuperviewEdges(with: UIEdgeInsets(top: 16, left: 16, bottom: 8, right: 16)) - - addSubview(bannerView) - bannerView.autoPinEdgesToSuperviewEdges(with: UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)) - - if let contactID = thread.contactIdentifier() { - let displayName = Environment.shared.contactsManager.profileName(forRecipientId: contactID) ?? contactID - label.text = String(format: NSLocalizedString("Would you like to start a new session with %@?", comment: ""), displayName) - } - } - - @objc private func restore() { onRestore?() } - - @objc private func dismiss() { onDismiss?() } -} diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m index f559c10e5..c8ee5809f 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m @@ -1015,12 +1015,12 @@ typedef enum : NSUInteger { if (isContactThread) { TSContactThread *thread = (TSContactThread *)self.thread; if (thread.sessionRestoreDevices.count > 0) { - if (!self.restoreSessionBannerView) { - LKSessionRestoreBannerView *bannerView = [[LKSessionRestoreBannerView alloc] initWithThread:thread]; + if (self.restoreSessionBannerView == nil) { + LKSessionRestorationView *bannerView = [[LKSessionRestorationView alloc] initWithThread:thread]; [self.view addSubview:bannerView]; - [bannerView autoPinEdgeToSuperviewEdge:ALEdgeTop]; - [bannerView autoPinEdgeToSuperviewEdge:ALEdgeLeft]; - [bannerView autoPinEdgeToSuperviewEdge:ALEdgeRight]; + [bannerView autoPinEdgeToSuperviewEdge:ALEdgeLeft withInset:LKValues.mediumSpacing]; + [bannerView autoPinEdgeToSuperviewEdge:ALEdgeTop withInset:LKValues.largeSpacing]; + [bannerView autoPinEdgeToSuperviewEdge:ALEdgeRight withInset:LKValues.mediumSpacing]; [self.view layoutSubviews]; self.restoreSessionBannerView = bannerView; [bannerView setOnRestore:^{ @@ -1674,7 +1674,7 @@ typedef enum : NSUInteger { isAttachmentButtonHidden = false; } [self.inputToolbar setUserInteractionEnabled:isEnabled]; - NSString *placeholderText = isEnabled ? NSLocalizedString(@"Message", "") : NSLocalizedString(@"Pending message request", ""); + NSString *placeholderText = isEnabled ? NSLocalizedString(@"Message", "") : NSLocalizedString(@"Pending session request", ""); [self.inputToolbar setPlaceholderText:placeholderText]; [self.inputToolbar setAttachmentButtonHidden:isAttachmentButtonHidden]; } diff --git a/Signal/translations/en.lproj/Localizable.strings b/Signal/translations/en.lproj/Localizable.strings index b874c7f0e..37dcdc3b0 100644 --- a/Signal/translations/en.lproj/Localizable.strings +++ b/Signal/translations/en.lproj/Localizable.strings @@ -2573,20 +2573,20 @@ "Calculating proof of work" = "Calculating proof of work"; "Failed to calculate proof of work." = "Failed to calculate proof of work."; "Share Public Key" = "Share Public Key"; -"%@ sent you a message request" = "%@ sent you a message request"; +"%@ sent you a session request" = "%@ sent you a session request"; "Accept" = "Accept"; "Decline" = "Decline"; -"Pending message request" = "Pending message request"; +"Pending session request" = "Pending session request"; "New Message" = "New Message"; "Secure session reset in progress" = "Secure session reset in progress"; "Secure session reset done" = "Secure session reset done"; "Session" = "Session"; -"You've sent %@ a message request" = "You've sent %@ a message request"; -"You've declined %@'s message request" = "You've declined %@'s message request"; -"You've accepted %@'s message request" = "You've accepted %@'s message request"; -"%@ accepted your message request" = "%@ accepted your message request"; -"%@'s message request has expired" = "%@'s message request has expired"; -"Your message request to %@ has expired" = "Your message request to %@ has expired"; +"You've sent %@ a session request" = "You've sent %@ a session request"; +"You've declined %@'s session request" = "You've declined %@'s session request"; +"You've accepted %@'s session request" = "You've accepted %@'s session request"; +"%@ accepted your session request" = "%@ accepted your session request"; +"%@'s session request has expired" = "%@'s session request has expired"; +"Your session request to %@ has expired" = "Your session request to %@ has expired"; "Show Seed" = "Show Seed"; "Your Seed" = "Your Seed"; "Require Touch ID, Face ID or your device passcode to unlock Session’s screen. You can still receive notifications when Screen Lock is enabled. Use Session’s notification settings to customise the information displayed in notifications." = "Require Touch ID, Face ID or your device passcode to unlock Session’s screen. You can still receive notifications when Screen Lock is enabled. Use Session’s notification settings to customise the information displayed in notifications."; @@ -2774,3 +2774,8 @@ "Are you sure? This cannot be undone." = "Are you sure? This cannot be undone."; "When enabled, messages between you and %@ will disappear after they have been seen." = "When enabled, messages between you and %@ will disappear after they have been seen."; "This will be your name when you use Session." = "This will be your name when you use Session."; +"Session Out of Sync" = "Session Out of Sync"; +"Would you like to restore your session?" = "Would you like to restore your session?"; +"Would you like to restore your session with %@?" = "Would you like to restore your session with %@?"; +"Restore" = "Restore"; +"Dismiss" = "Dismiss";