From 985f735f1afde65a3f2d8ba8137b1e99550e803a Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Fri, 15 Jun 2018 12:45:11 -0400 Subject: [PATCH 1/8] Track and persist "is de-registered" state. --- .../src/Account/TSAccountManager.h | 7 +++ .../src/Account/TSAccountManager.m | 47 +++++++++++++++++++ .../src/Network/API/TSNetworkManager.m | 6 +++ .../src/Network/WebSockets/TSSocketManager.m | 6 +++ 4 files changed, 66 insertions(+) diff --git a/SignalServiceKit/src/Account/TSAccountManager.h b/SignalServiceKit/src/Account/TSAccountManager.h index 50e0d75cf..18e1446a2 100644 --- a/SignalServiceKit/src/Account/TSAccountManager.h +++ b/SignalServiceKit/src/Account/TSAccountManager.h @@ -9,6 +9,7 @@ NS_ASSUME_NONNULL_BEGIN extern NSString *const TSRegistrationErrorDomain; extern NSString *const TSRegistrationErrorUserInfoHTTPStatus; extern NSString *const RegistrationStateDidChangeNotification; +extern NSString *const DeregistrationStateDidChangeNotification; extern NSString *const kNSNotificationName_LocalNumberDidChange; @class OWSPrimaryStorage; @@ -116,6 +117,12 @@ extern NSString *const kNSNotificationName_LocalNumberDidChange; + (void)unregisterTextSecureWithSuccess:(void (^)(void))success failure:(void (^)(NSError *error))failureBlock; +#pragma mark - Deregistration + +// De-registration reflects whether or not the service has received a 403 +- (BOOL)isDeregistered; +- (void)setIsDeregistered:(BOOL)isDeregistered; + @end NS_ASSUME_NONNULL_END diff --git a/SignalServiceKit/src/Account/TSAccountManager.m b/SignalServiceKit/src/Account/TSAccountManager.m index 1972c4b74..049459c69 100644 --- a/SignalServiceKit/src/Account/TSAccountManager.m +++ b/SignalServiceKit/src/Account/TSAccountManager.m @@ -23,9 +23,11 @@ NS_ASSUME_NONNULL_BEGIN NSString *const TSRegistrationErrorDomain = @"TSRegistrationErrorDomain"; NSString *const TSRegistrationErrorUserInfoHTTPStatus = @"TSHTTPStatus"; NSString *const RegistrationStateDidChangeNotification = @"RegistrationStateDidChangeNotification"; +NSString *const DeregistrationStateDidChangeNotification = @"DeregistrationStateDidChangeNotification"; NSString *const kNSNotificationName_LocalNumberDidChange = @"kNSNotificationName_LocalNumberDidChange"; NSString *const TSAccountManager_RegisteredNumberKey = @"TSStorageRegisteredNumberKey"; +NSString *const TSAccountManager_IsDeregisteredKey = @"TSAccountManager_IsDeregisteredKey"; NSString *const TSAccountManager_LocalRegistrationIdKey = @"TSStorageLocalRegistrationId"; NSString *const TSAccountManager_UserAccountCollection = @"TSStorageUserAccountCollection"; @@ -44,6 +46,8 @@ NSString *const TSAccountManager_ServerSignalingKey = @"TSStorageServerSignaling @property (nonatomic, nullable) NSString *cachedLocalNumber; @property (nonatomic, readonly) YapDatabaseConnection *dbConnection; +@property (nonatomic, nullable) NSNumber *cachedIsDeregistered; + @end #pragma mark - @@ -108,6 +112,7 @@ NSString *const TSAccountManager_ServerSignalingKey = @"TSStorageServerSignaling _isRegistered = NO; _cachedLocalNumber = nil; _phoneNumberAwaitingVerification = nil; + _cachedIsDeregistered = nil; [self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) { [transaction removeAllObjectsInCollection:TSAccountManager_UserAccountCollection]; @@ -152,6 +157,7 @@ NSString *const TSAccountManager_ServerSignalingKey = @"TSStorageServerSignaling // Warm these cached values. [self isRegistered]; [self localNumber]; + [self isDeregistered]; } + (nullable NSString *)localNumber @@ -524,6 +530,47 @@ NSString *const TSAccountManager_ServerSignalingKey = @"TSStorageServerSignaling } } +#pragma mark - Deregistration + +- (BOOL)isDeregistered +{ + // Cache this since we access this a lot, and once set it will not change. + @synchronized(self) { + if (self.cachedIsDeregistered == nil) { + self.cachedIsDeregistered = @([self.dbConnection boolForKey:TSAccountManager_IsDeregisteredKey + inCollection:TSAccountManager_UserAccountCollection + defaultValue:NO]); + } + } + + OWSAssert(self.cachedIsDeregistered); + return self.cachedIsDeregistered.boolValue; +} + +- (void)setIsDeregistered:(BOOL)isDeregistered +{ + @synchronized(self) { + if (self.cachedIsDeregistered && self.cachedIsDeregistered.boolValue == isDeregistered)) + { + return; + } + + DDLogWarn(@"%@ isDeregistered: %d", self.logTag, isDeregistered); + + self.cachedIsDeregistered == @(isDeregistered); + } + + [self.dbConnection asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { + [transaction setValue:@(isDeregistered) + forKey:TSAccountManager_IsDeregisteredKey + inCollection:TSAccountManager_UserAccountCollection]; + }]; + + [[NSNotificationCenter defaultCenter] postNotificationNameAsync:DeregistrationStateDidChangeNotification + object:nil + userInfo:nil]; +} + @end NS_ASSUME_NONNULL_END diff --git a/SignalServiceKit/src/Network/API/TSNetworkManager.m b/SignalServiceKit/src/Network/API/TSNetworkManager.m index 4d0d5adba..6e00e038f 100644 --- a/SignalServiceKit/src/Network/API/TSNetworkManager.m +++ b/SignalServiceKit/src/Network/API/TSNetworkManager.m @@ -87,6 +87,9 @@ typedef void (^failureBlock)(NSURLSessionDataTask *task, NSError *error); // TODO: Remove this logging when the call connection issues have been resolved. TSNetworkManagerSuccess success = ^(NSURLSessionDataTask *task, _Nullable id responseObject) { DDLogInfo(@"%@ request succeeded : %@", self.logTag, request); + + [TSAccountManager.sharedInstance setIsDeregistered:NO]; + successBlock(task, responseObject); }; TSNetworkManagerFailure failure = [TSNetworkManager errorPrettifyingForFailureBlock:failureBlock request:request]; @@ -166,6 +169,9 @@ typedef void (^failureBlock)(NSURLSessionDataTask *task, NSError *error); } case 400: { DDLogError(@"The request contains an invalid parameter : %@, %@", networkError.debugDescription, request); + + [TSAccountManager.sharedInstance setIsDeregistered:YES]; + failureBlock(task, error); break; } diff --git a/SignalServiceKit/src/Network/WebSockets/TSSocketManager.m b/SignalServiceKit/src/Network/WebSockets/TSSocketManager.m index de4c6c0ba..064bbdae4 100644 --- a/SignalServiceKit/src/Network/WebSockets/TSSocketManager.m +++ b/SignalServiceKit/src/Network/WebSockets/TSSocketManager.m @@ -612,8 +612,14 @@ NSString *const kNSNotification_SocketManagerStateDidChange = @"kNSNotification_ BOOL hasSuccessStatus = 200 <= responseStatus && responseStatus <= 299; BOOL didSucceed = hasSuccessStatus && hasValidResponse; if (didSucceed) { + [TSAccountManager.sharedInstance setIsDeregistered:NO]; + [socketMessage didSucceedWithResponseObject:responseObject]; } else { + if (responseStatus == 403) { + [TSAccountManager.sharedInstance setIsDeregistered:YES]; + } + NSError *error = OWSErrorWithCodeDescription(OWSErrorCodeMessageResponseFailed, NSLocalizedString( @"ERROR_DESCRIPTION_RESPONSE_FAILED", @"Error indicating that a socket response failed.")); From b0646e8bffe23f087ea81cd01db7f326b02f516a Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Fri, 15 Jun 2018 12:52:45 -0400 Subject: [PATCH 2/8] Track and persist 'is de-registered' state. --- .../HomeView/HomeViewController.m | 12 ++++++++++ .../src/Account/TSAccountManager.m | 22 +++++++++---------- 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/Signal/src/ViewControllers/HomeView/HomeViewController.m b/Signal/src/ViewControllers/HomeView/HomeViewController.m index 00738aa84..4d99f6e43 100644 --- a/Signal/src/ViewControllers/HomeView/HomeViewController.m +++ b/Signal/src/ViewControllers/HomeView/HomeViewController.m @@ -25,6 +25,7 @@ #import #import #import +#import #import #import #import @@ -159,6 +160,10 @@ NSString *const kArchivedConversationsReuseIdentifier = @"kArchivedConversations selector:@selector(yapDatabaseModifiedExternally:) name:YapDatabaseModifiedExternallyNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(deregistrationStateDidChange:) + name:DeregistrationStateDidChangeNotification + object:nil]; } - (void)dealloc @@ -184,6 +189,13 @@ NSString *const kArchivedConversationsReuseIdentifier = @"kArchivedConversations [self reloadTableViewData]; } +- (void)deregistrationStateDidChange:(id)notification +{ + OWSAssertIsOnMainThread(); + + [self reloadTableViewData]; +} + #pragma mark - View Life Cycle - (void)loadView diff --git a/SignalServiceKit/src/Account/TSAccountManager.m b/SignalServiceKit/src/Account/TSAccountManager.m index 049459c69..3e4d599a8 100644 --- a/SignalServiceKit/src/Account/TSAccountManager.m +++ b/SignalServiceKit/src/Account/TSAccountManager.m @@ -16,6 +16,7 @@ #import "TSPreKeyManager.h" #import "TSVerifyCodeRequest.h" #import "YapDatabaseConnection+OWS.h" +#import "YapDatabaseTransaction+OWS.h" #import NS_ASSUME_NONNULL_BEGIN @@ -541,29 +542,28 @@ NSString *const TSAccountManager_ServerSignalingKey = @"TSStorageServerSignaling inCollection:TSAccountManager_UserAccountCollection defaultValue:NO]); } - } - OWSAssert(self.cachedIsDeregistered); - return self.cachedIsDeregistered.boolValue; + OWSAssert(self.cachedIsDeregistered); + return self.cachedIsDeregistered.boolValue; + } } - (void)setIsDeregistered:(BOOL)isDeregistered { @synchronized(self) { - if (self.cachedIsDeregistered && self.cachedIsDeregistered.boolValue == isDeregistered)) - { - return; - } + if (self.cachedIsDeregistered && self.cachedIsDeregistered.boolValue == isDeregistered) { + return; + } DDLogWarn(@"%@ isDeregistered: %d", self.logTag, isDeregistered); - self.cachedIsDeregistered == @(isDeregistered); + self.cachedIsDeregistered = @(isDeregistered); } [self.dbConnection asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { - [transaction setValue:@(isDeregistered) - forKey:TSAccountManager_IsDeregisteredKey - inCollection:TSAccountManager_UserAccountCollection]; + [transaction setObject:@(isDeregistered) + forKey:TSAccountManager_IsDeregisteredKey + inCollection:TSAccountManager_UserAccountCollection]; }]; [[NSNotificationCenter defaultCenter] postNotificationNameAsync:DeregistrationStateDidChangeNotification From 6331fbb22a27d66e6d87937ddf892e22e81a5f34 Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Fri, 15 Jun 2018 13:39:35 -0400 Subject: [PATCH 3/8] Show de-registration nag view. --- .../HomeView/HomeViewController.m | 25 ++++++++++++++++--- Signal/src/views/ReminderView.swift | 2 +- .../translations/en.lproj/Localizable.strings | 5 +++- 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/Signal/src/ViewControllers/HomeView/HomeViewController.m b/Signal/src/ViewControllers/HomeView/HomeViewController.m index 4d99f6e43..9f62b3cdf 100644 --- a/Signal/src/ViewControllers/HomeView/HomeViewController.m +++ b/Signal/src/ViewControllers/HomeView/HomeViewController.m @@ -74,6 +74,7 @@ NSString *const kArchivedConversationsReuseIdentifier = @"kArchivedConversations // Views +@property (nonatomic) NSLayoutConstraint *hideDeregisteredViewConstraint; @property (nonatomic) NSLayoutConstraint *hideArchiveReminderViewConstraint; @property (nonatomic) NSLayoutConstraint *hideMissingContactsPermissionViewConstraint; @@ -193,7 +194,7 @@ NSString *const kArchivedConversationsReuseIdentifier = @"kArchivedConversations { OWSAssertIsOnMainThread(); - [self reloadTableViewData]; + [self updateReminderViews]; } #pragma mark - View Life Cycle @@ -209,12 +210,24 @@ NSString *const kArchivedConversationsReuseIdentifier = @"kArchivedConversations [SignalApp.sharedApp setHomeViewController:self]; } + ReminderView *deregisteredView = + [ReminderView nagWithText:NSLocalizedString(@"INBOX_VIEW_DEREGISTRATION_WARNING", + @"Label warning the user that they have been de-registered.") + tapAction:^{ + // TODO: + }]; + [self.view addSubview:deregisteredView]; + [deregisteredView autoPinWidthToSuperview]; + [deregisteredView autoPinToTopLayoutGuideOfViewController:self withInset:0]; + self.hideDeregisteredViewConstraint = [deregisteredView autoSetDimension:ALDimensionHeight toSize:0]; + self.hideDeregisteredViewConstraint.priority = UILayoutPriorityRequired; + ReminderView *archiveReminderView = [ReminderView explanationWithText:NSLocalizedString(@"INBOX_VIEW_ARCHIVE_MODE_REMINDER", @"Label reminding the user that they are in archive mode.")]; [self.view addSubview:archiveReminderView]; [archiveReminderView autoPinWidthToSuperview]; - [archiveReminderView autoPinToTopLayoutGuideOfViewController:self withInset:0]; + [archiveReminderView autoPinEdge:ALEdgeTop toEdge:ALEdgeBottom ofView:deregisteredView]; self.hideArchiveReminderViewConstraint = [archiveReminderView autoSetDimension:ALDimensionHeight toSize:0]; self.hideArchiveReminderViewConstraint.priority = UILayoutPriorityRequired; @@ -270,12 +283,18 @@ NSString *const kArchivedConversationsReuseIdentifier = @"kArchivedConversations { BOOL shouldHideArchiveReminderView = self.homeViewMode != HomeViewMode_Archive; BOOL shouldHideMissingContactsPermissionView = !self.shouldShowMissingContactsPermissionView; + BOOL shouldHideDeregisteredView = !TSAccountManager.sharedInstance.isDeregistered; + if (self.hideArchiveReminderViewConstraint.active == shouldHideArchiveReminderView - && self.hideMissingContactsPermissionViewConstraint.active == shouldHideMissingContactsPermissionView) { + && self.hideMissingContactsPermissionViewConstraint.active == shouldHideMissingContactsPermissionView + && self.hideDeregisteredViewConstraint.active == shouldHideDeregisteredView) { return; } + self.hideArchiveReminderViewConstraint.active = shouldHideArchiveReminderView; self.hideMissingContactsPermissionViewConstraint.active = shouldHideMissingContactsPermissionView; + self.hideDeregisteredViewConstraint.active = shouldHideDeregisteredView; + [self.view setNeedsLayout]; [self.view layoutSubviews]; } diff --git a/Signal/src/views/ReminderView.swift b/Signal/src/views/ReminderView.swift index a0c138a76..6b122d8a7 100644 --- a/Signal/src/views/ReminderView.swift +++ b/Signal/src/views/ReminderView.swift @@ -91,7 +91,7 @@ class ReminderView: UIView { // Label switch (mode) { case .nag: - label.font = UIFont.ows_regularFont(withSize: 14) + label.font = UIFont.ows_dynamicTypeFootnote case .explanation: label.font = UIFont.ows_dynamicTypeSubheadline } diff --git a/Signal/translations/en.lproj/Localizable.strings b/Signal/translations/en.lproj/Localizable.strings index f7346ece4..0c39bc411 100644 --- a/Signal/translations/en.lproj/Localizable.strings +++ b/Signal/translations/en.lproj/Localizable.strings @@ -659,7 +659,7 @@ /* table cell label in conversation settings */ "DISAPPEARING_MESSAGES" = "Disappearing Messages"; -/* Info Message when added to {{group name}} which has enabled message expiration after {{time amount}}, see the *_TIME_AMOUNT strings for context. */ +/* Info Message when added to a group which has enabled disappearing messages. Embeds {{time amount}} before messages disappear, see the *_TIME_AMOUNT strings for context. */ "DISAPPEARING_MESSAGES_CONFIGURATION_GROUP_EXISTING_FORMAT" = "Messages in this conversation will disappear after %@."; /* subheading in conversation settings */ @@ -1022,6 +1022,9 @@ /* Label reminding the user that they are in archive mode. */ "INBOX_VIEW_ARCHIVE_MODE_REMINDER" = "These conversations are archived. They will appear in the inbox if new messages are received."; +/* Label warning the user that they have been de-registered. */ +"INBOX_VIEW_DEREGISTRATION_WARNING" = "You are not logged in. Another device may have been registered with your phone number."; + /* Multi-line label explaining how to show names instead of phone numbers in your inbox */ "INBOX_VIEW_MISSING_CONTACTS_PERMISSION" = "To see the names of your contacts, update your system settings to allow contact access."; From fc4763673f52a4b54a0bb375d5860cb84e3aef96 Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Fri, 15 Jun 2018 14:00:29 -0400 Subject: [PATCH 4/8] Improve de-registration checks in socket manager. --- .../src/Network/WebSockets/TSSocketManager.m | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/SignalServiceKit/src/Network/WebSockets/TSSocketManager.m b/SignalServiceKit/src/Network/WebSockets/TSSocketManager.m index 064bbdae4..0c3d9b2f0 100644 --- a/SignalServiceKit/src/Network/WebSockets/TSSocketManager.m +++ b/SignalServiceKit/src/Network/WebSockets/TSSocketManager.m @@ -617,6 +617,8 @@ NSString *const kNSNotification_SocketManagerStateDidChange = @"kNSNotification_ [socketMessage didSucceedWithResponseObject:responseObject]; } else { if (responseStatus == 403) { + // This should be redundant with our check for the socket + // failing due to 403, but let's be thorough. [TSAccountManager.sharedInstance setIsDeregistered:YES]; } @@ -672,6 +674,9 @@ NSString *const kNSNotification_SocketManagerStateDidChange = @"kNSNotification_ } self.state = SocketManagerStateOpen; + + // If socket opens, we know we're not de-registered. + [TSAccountManager.sharedInstance setIsDeregistered:NO]; } - (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error { @@ -684,6 +689,13 @@ NSString *const kNSNotification_SocketManagerStateDidChange = @"kNSNotification_ DDLogError(@"Websocket did fail with error: %@", error); + if ([error.domain isEqualToString:SRWebSocketErrorDomain] && error.code == 2132) { + NSNumber *_Nullable statusCode = error.userInfo[SRHTTPResponseErrorKey]; + if (statusCode.unsignedIntegerValue == 403) { + [TSAccountManager.sharedInstance setIsDeregistered:YES]; + } + } + [self handleSocketFailure]; } @@ -696,6 +708,9 @@ NSString *const kNSNotification_SocketManagerStateDidChange = @"kNSNotification_ return; } + // If we receive a response, we know we're not de-registered. + [TSAccountManager.sharedInstance setIsDeregistered:NO]; + WebSocketResourcesWebSocketMessage *wsMessage; @try { wsMessage = [WebSocketResourcesWebSocketMessage parseFromData:data]; From bc6a4ea8d82ff1f957109f81f1860ff19635c81e Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Mon, 18 Jun 2018 10:54:06 -0400 Subject: [PATCH 5/8] Add re-registration UI. --- .../src/ViewControllers/DebugUI/DebugUIMisc.m | 15 ++-- .../HomeView/HomeViewController.m | 81 ++++++++++++++++++- .../CodeVerificationViewController.m | 29 ++++--- .../Registration/RegistrationViewController.m | 60 ++++++++++++++ .../translations/en.lproj/Localizable.strings | 9 ++- .../src/Account/TSAccountManager.h | 19 +++-- .../src/Account/TSAccountManager.m | 76 ++++++++++++----- 7 files changed, 239 insertions(+), 50 deletions(-) diff --git a/Signal/src/ViewControllers/DebugUI/DebugUIMisc.m b/Signal/src/ViewControllers/DebugUI/DebugUIMisc.m index 80609c4ba..6b3232ed8 100644 --- a/Signal/src/ViewControllers/DebugUI/DebugUIMisc.m +++ b/Signal/src/ViewControllers/DebugUI/DebugUIMisc.m @@ -24,14 +24,6 @@ NS_ASSUME_NONNULL_BEGIN -@interface TSAccountManager (DebugUI) - -- (void)resetForRegistration; - -@end - -#pragma mark - - @interface OWSStorage (DebugUI) - (NSData *)databasePassword; @@ -147,7 +139,12 @@ NS_ASSUME_NONNULL_BEGIN + (void)reregister { DDLogInfo(@"%@ re-registering.", self.logTag); - [[TSAccountManager sharedInstance] resetForRegistration]; + + if (![[TSAccountManager sharedInstance] resetForReregistration]) { + OWSFail(@"%@ could not reset for re-registration.", self.logTag); + return; + } + [[Environment current].preferences unsetRecordedAPNSTokens]; RegistrationViewController *viewController = [RegistrationViewController new]; diff --git a/Signal/src/ViewControllers/HomeView/HomeViewController.m b/Signal/src/ViewControllers/HomeView/HomeViewController.m index 9f62b3cdf..56406706b 100644 --- a/Signal/src/ViewControllers/HomeView/HomeViewController.m +++ b/Signal/src/ViewControllers/HomeView/HomeViewController.m @@ -5,12 +5,14 @@ #import "HomeViewController.h" #import "AppDelegate.h" #import "AppSettingsViewController.h" +#import "CodeVerificationViewController.h" #import "HomeViewCell.h" #import "NewContactThreadViewController.h" #import "OWSNavigationController.h" #import "OWSPrimaryStorage.h" #import "ProfileViewController.h" #import "PushManager.h" +#import "RegistrationViewController.h" #import "Signal-Swift.h" #import "SignalApp.h" #import "TSAccountManager.h" @@ -210,11 +212,12 @@ NSString *const kArchivedConversationsReuseIdentifier = @"kArchivedConversations [SignalApp.sharedApp setHomeViewController:self]; } + __weak HomeViewController *weakSelf = self; ReminderView *deregisteredView = - [ReminderView nagWithText:NSLocalizedString(@"INBOX_VIEW_DEREGISTRATION_WARNING", + [ReminderView nagWithText:NSLocalizedString(@"DEREGISTRATION_WARNING", @"Label warning the user that they have been de-registered.") tapAction:^{ - // TODO: + [weakSelf showReRegistrationUI]; }]; [self.view addSubview:deregisteredView]; [deregisteredView autoPinWidthToSuperview]; @@ -1407,6 +1410,80 @@ NSString *const kArchivedConversationsReuseIdentifier = @"kArchivedConversations _emptyBoxLabel.attributedText = fullLabelString; } +- (void)showReRegistrationUI +{ + UIAlertController *actionSheetController = + [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:UIAlertControllerStyleActionSheet]; + + __weak HomeViewController *weakSelf = self; + [actionSheetController + addAction:[UIAlertAction + actionWithTitle:NSLocalizedString(@"DEREGISTRATION_REREGISTER_WITH_SAME_PHONE_NUMBER", + @"Label for button that lets users re-register using the same phone number.") + style:UIAlertActionStyleDestructive + handler:^(UIAlertAction *action) { + [weakSelf reregisterWithSamePhoneNumber]; + }]]; + + [actionSheetController addAction:[OWSAlerts cancelAction]]; + + [self presentViewController:actionSheetController animated:YES completion:nil]; +} + +- (void)reregisterWithSamePhoneNumber +{ + DDLogInfo(@"%@ reregisterWithSamePhoneNumber.", self.logTag); + + if (![[TSAccountManager sharedInstance] resetForReregistration]) { + OWSFail(@"%@ could not reset for re-registration.", self.logTag); + return; + } + + [[Environment current].preferences unsetRecordedAPNSTokens]; + + [ModalActivityIndicatorViewController + presentFromViewController:self + canCancel:NO + backgroundBlock:^(ModalActivityIndicatorViewController *modalActivityIndicator) { + [TSAccountManager + registerWithPhoneNumber:[TSAccountManager sharedInstance].reregisterationPhoneNumber + success:^{ + DDLogInfo(@"%@ re-registering: send verification code succeeded.", self.logTag); + + dispatch_async(dispatch_get_main_queue(), ^{ + [modalActivityIndicator dismissWithCompletion:^{ + CodeVerificationViewController *viewController = + [CodeVerificationViewController new]; + + OWSNavigationController *navigationController = + [[OWSNavigationController alloc] initWithRootViewController:viewController]; + navigationController.navigationBarHidden = YES; + + [UIApplication sharedApplication].delegate.window.rootViewController + = navigationController; + }]; + }); + } + failure:^(NSError *error) { + DDLogError(@"%@ re-registering: send verification code failed.", self.logTag); + + dispatch_async(dispatch_get_main_queue(), ^{ + [modalActivityIndicator dismissWithCompletion:^{ + if (error.code == 400) { + [OWSAlerts showAlertWithTitle:NSLocalizedString(@"REGISTRATION_ERROR", nil) + message:NSLocalizedString( + @"REGISTRATION_NON_VALID_NUMBER", nil)]; + } else { + [OWSAlerts showAlertWithTitle:error.localizedDescription + message:error.localizedRecoverySuggestion]; + } + }]; + }); + } + smsVerification:YES]; + }]; +} + @end NS_ASSUME_NONNULL_END diff --git a/Signal/src/ViewControllers/Registration/CodeVerificationViewController.m b/Signal/src/ViewControllers/Registration/CodeVerificationViewController.m index 9124b9024..0cc01b497 100644 --- a/Signal/src/ViewControllers/Registration/CodeVerificationViewController.m +++ b/Signal/src/ViewControllers/Registration/CodeVerificationViewController.m @@ -112,16 +112,25 @@ NS_ASSUME_NONNULL_BEGIN [titleLabel autoSetDimension:ALDimensionHeight toSize:40]; [titleLabel autoHCenterInSuperview]; - UIButton *backButton = [UIButton buttonWithType:UIButtonTypeCustom]; - [backButton - setTitle:NSLocalizedString(@"VERIFICATION_BACK_BUTTON", @"button text for back button on verification view") - forState:UIControlStateNormal]; - [backButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; - backButton.titleLabel.font = [UIFont ows_mediumFontWithSize:14.f]; - [header addSubview:backButton]; - [backButton autoPinLeadingToSuperviewMarginWithInset:10.f]; - [backButton autoAlignAxis:ALAxisHorizontal toSameAxisOfView:titleLabel]; - [backButton addTarget:self action:@selector(backButtonPressed:) forControlEvents:UIControlEventTouchUpInside]; + // This view is used in more than one context. + // + // * Usually, it is pushed atop RegistrationViewController in which + // case we want a "back" button. + // * It can also be used to re-register from the app's "de-registration" + // views, in which case RegistrationViewController is not used and we + // do _not_ want a "back" button. + if (self.navigationController.viewControllers.count > 1) { + UIButton *backButton = [UIButton buttonWithType:UIButtonTypeCustom]; + [backButton + setTitle:NSLocalizedString(@"VERIFICATION_BACK_BUTTON", @"button text for back button on verification view") + forState:UIControlStateNormal]; + [backButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; + backButton.titleLabel.font = [UIFont ows_mediumFontWithSize:14.f]; + [header addSubview:backButton]; + [backButton autoPinLeadingToSuperviewMarginWithInset:10.f]; + [backButton autoAlignAxis:ALAxisHorizontal toSameAxisOfView:titleLabel]; + [backButton addTarget:self action:@selector(backButtonPressed:) forControlEvents:UIControlEventTouchUpInside]; + } _phoneNumberLabel = [UILabel new]; _phoneNumberLabel.textColor = [UIColor ows_darkGrayColor]; diff --git a/Signal/src/ViewControllers/Registration/RegistrationViewController.m b/Signal/src/ViewControllers/Registration/RegistrationViewController.m index ecc463ed3..48bb9d6b9 100644 --- a/Signal/src/ViewControllers/Registration/RegistrationViewController.m +++ b/Signal/src/ViewControllers/Registration/RegistrationViewController.m @@ -278,6 +278,61 @@ NSString *const kKeychainKey_LastRegisteredPhoneNumber = @"kKeychainKey_LastRegi [self.activateButton setEnabled:YES]; [self.spinnerView stopAnimating]; [self.phoneNumberTextField becomeFirstResponder]; + + if ([TSAccountManager sharedInstance].isReregistering) { + // If re-registering, pre-populate the country (country code, calling code, country name) + // and phone number state. + NSString *_Nullable phoneNumberE164 = [TSAccountManager sharedInstance].reregisterationPhoneNumber; + if (!phoneNumberE164) { + OWSFail(@"%@ Could not resume re-registration; missing phone number.", self.logTag); + } else if ([self tryToApplyPhoneNumberE164:phoneNumberE164]) { + // Don't let user edit their phone number while re-registering. + self.phoneNumberTextField.enabled = NO; + } + } +} + +- (BOOL)tryToApplyPhoneNumberE164:(NSString *)phoneNumberE164 +{ + OWSAssert(phoneNumberE164); + + if (phoneNumberE164.length < 1) { + OWSFail(@"%@ Could not resume re-registration; invalid phoneNumberE164.", self.logTag); + return NO; + } + PhoneNumber *_Nullable parsedPhoneNumber = [PhoneNumber phoneNumberFromE164:phoneNumberE164]; + if (!parsedPhoneNumber) { + OWSFail(@"%@ Could not resume re-registration; couldn't parse phoneNumberE164.", self.logTag); + return NO; + } + NSNumber *_Nullable callingCode = parsedPhoneNumber.getCountryCode; + if (!callingCode) { + OWSFail(@"%@ Could not resume re-registration; missing callingCode.", self.logTag); + return NO; + } + NSString *callingCodeText = [NSString stringWithFormat:@"+%d", callingCode.intValue]; + NSArray *_Nullable countryCodes = + [PhoneNumberUtil.sharedThreadLocal countryCodesFromCallingCode:callingCodeText]; + if (countryCodes.count < 1) { + OWSFail(@"%@ Could not resume re-registration; unknown countryCode.", self.logTag); + return NO; + } + NSString *countryCode = countryCodes.firstObject; + NSString *_Nullable countryName = [PhoneNumberUtil countryNameFromCountryCode:countryCode]; + if (!countryName) { + OWSFail(@"%@ Could not resume re-registration; unknown countryName.", self.logTag); + return NO; + } + if (![phoneNumberE164 hasPrefix:callingCodeText]) { + OWSFail(@"%@ Could not resume re-registration; non-matching calling code.", self.logTag); + return NO; + } + NSString *phoneNumberWithoutCallingCode = [phoneNumberE164 substringFromIndex:callingCodeText.length]; + + [self updateCountryWithName:countryName callingCode:callingCodeText countryCode:countryCode]; + self.phoneNumberTextField.text = phoneNumberWithoutCallingCode; + + return YES; } #pragma mark - Country @@ -385,6 +440,11 @@ NSString *const kKeychainKey_LastRegisteredPhoneNumber = @"kKeychainKey_LastRegi - (void)countryCodeRowWasTapped:(UIGestureRecognizer *)sender { + if (TSAccountManager.sharedInstance.isReregistering) { + // Don't let user edit their phone number while re-registering. + return; + } + if (sender.state == UIGestureRecognizerStateRecognized) { [self changeCountryCodeTapped]; } diff --git a/Signal/translations/en.lproj/Localizable.strings b/Signal/translations/en.lproj/Localizable.strings index 0c39bc411..f63544a0d 100644 --- a/Signal/translations/en.lproj/Localizable.strings +++ b/Signal/translations/en.lproj/Localizable.strings @@ -647,6 +647,12 @@ /* Title of the alert before redirecting to GitHub Issues. */ "DEBUG_LOG_GITHUB_ISSUE_ALERT_TITLE" = "GitHub Redirection"; +/* Label for button that lets users re-register using the same phone number. */ +"DEREGISTRATION_REREGISTER_WITH_SAME_PHONE_NUMBER" = "Re-register this phone number"; + +/* Label warning the user that they have been de-registered. */ +"DEREGISTRATION_WARNING" = "Device no longer registered! This is likely because you registered your phone number with Signal on a different device. Tap to re-register."; + /* {{Short Date}} when device last communicated with Signal Server. */ "DEVICE_LAST_ACTIVE_AT_LABEL" = "Last active: %@"; @@ -1022,9 +1028,6 @@ /* Label reminding the user that they are in archive mode. */ "INBOX_VIEW_ARCHIVE_MODE_REMINDER" = "These conversations are archived. They will appear in the inbox if new messages are received."; -/* Label warning the user that they have been de-registered. */ -"INBOX_VIEW_DEREGISTRATION_WARNING" = "You are not logged in. Another device may have been registered with your phone number."; - /* Multi-line label explaining how to show names instead of phone numbers in your inbox */ "INBOX_VIEW_MISSING_CONTACTS_PERMISSION" = "To see the names of your contacts, update your system settings to allow contact access."; diff --git a/SignalServiceKit/src/Account/TSAccountManager.h b/SignalServiceKit/src/Account/TSAccountManager.h index 18e1446a2..a60dc410f 100644 --- a/SignalServiceKit/src/Account/TSAccountManager.h +++ b/SignalServiceKit/src/Account/TSAccountManager.h @@ -18,10 +18,7 @@ extern NSString *const kNSNotificationName_LocalNumberDidChange; @interface TSAccountManager : NSObject -// This property is exposed for testing purposes only. -#ifdef DEBUG -@property (nonatomic, nullable) NSString *phoneNumberAwaitingVerification; -#endif +@property (nonatomic, nullable, readonly) NSString *phoneNumberAwaitingVerification; #pragma mark - Initializers @@ -117,12 +114,22 @@ extern NSString *const kNSNotificationName_LocalNumberDidChange; + (void)unregisterTextSecureWithSuccess:(void (^)(void))success failure:(void (^)(NSError *error))failureBlock; -#pragma mark - Deregistration +#pragma mark - De-Registration -// De-registration reflects whether or not the service has received a 403 +// De-registration reflects whether or not the client has received +// a 403 from the service. - (BOOL)isDeregistered; - (void)setIsDeregistered:(BOOL)isDeregistered; +#pragma mark - Re-registration + +// Re-registration is the process of re-registering _with the same phone number_. + +// Returns YES on success. +- (BOOL)resetForReregistration; +- (NSString *)reregisterationPhoneNumber; +- (BOOL)isReregistering; + @end NS_ASSUME_NONNULL_END diff --git a/SignalServiceKit/src/Account/TSAccountManager.m b/SignalServiceKit/src/Account/TSAccountManager.m index 3e4d599a8..09c0e19f7 100644 --- a/SignalServiceKit/src/Account/TSAccountManager.m +++ b/SignalServiceKit/src/Account/TSAccountManager.m @@ -29,6 +29,7 @@ NSString *const kNSNotificationName_LocalNumberDidChange = @"kNSNotificationName NSString *const TSAccountManager_RegisteredNumberKey = @"TSStorageRegisteredNumberKey"; NSString *const TSAccountManager_IsDeregisteredKey = @"TSAccountManager_IsDeregisteredKey"; +NSString *const TSAccountManager_ReregisteringPhoneNumberKey = @"TSAccountManager_ReregisteringPhoneNumberKey"; NSString *const TSAccountManager_LocalRegistrationIdKey = @"TSStorageLocalRegistrationId"; NSString *const TSAccountManager_UserAccountCollection = @"TSStorageUserAccountCollection"; @@ -39,10 +40,7 @@ NSString *const TSAccountManager_ServerSignalingKey = @"TSStorageServerSignaling @property (nonatomic, readonly) BOOL isRegistered; -// This property is exposed publicly for testing purposes only. -#ifndef DEBUG @property (nonatomic, nullable) NSString *phoneNumberAwaitingVerification; -#endif @property (nonatomic, nullable) NSString *cachedLocalNumber; @property (nonatomic, readonly) YapDatabaseConnection *dbConnection; @@ -75,6 +73,10 @@ NSString *const TSAccountManager_ServerSignalingKey = @"TSStorageServerSignaling selector:@selector(yapDatabaseModifiedExternally:) name:YapDatabaseModifiedExternallyNotification object:nil]; + + self.phoneNumberAwaitingVerification = + [self.dbConnection stringForKey:TSAccountManager_ReregisteringPhoneNumberKey + inCollection:TSAccountManager_UserAccountCollection]; } return self; @@ -106,22 +108,6 @@ NSString *const TSAccountManager_ServerSignalingKey = @"TSStorageServerSignaling userInfo:nil]; } -- (void)resetForRegistration -{ - @synchronized(self) - { - _isRegistered = NO; - _cachedLocalNumber = nil; - _phoneNumberAwaitingVerification = nil; - _cachedIsDeregistered = nil; - [self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) { - [transaction removeAllObjectsInCollection:TSAccountManager_UserAccountCollection]; - - [[OWSPrimaryStorage sharedManager] resetSessionStore:transaction]; - }]; - } -} - + (BOOL)isRegistered { return [[self sharedInstance] isRegistered]; @@ -198,6 +184,11 @@ NSString *const TSAccountManager_ServerSignalingKey = @"TSStorageServerSignaling [self.dbConnection setObject:localNumber forKey:TSAccountManager_RegisteredNumberKey inCollection:TSAccountManager_UserAccountCollection]; + + [self.dbConnection removeObjectForKey:TSAccountManager_ReregisteringPhoneNumberKey + inCollection:TSAccountManager_UserAccountCollection]; + + self.phoneNumberAwaitingVerification = nil; } } @@ -531,7 +522,7 @@ NSString *const TSAccountManager_ServerSignalingKey = @"TSStorageServerSignaling } } -#pragma mark - Deregistration +#pragma mark - De-Registration - (BOOL)isDeregistered { @@ -571,6 +562,51 @@ NSString *const TSAccountManager_ServerSignalingKey = @"TSStorageServerSignaling userInfo:nil]; } +#pragma mark - Re-registration + +- (BOOL)resetForReregistration +{ + @synchronized(self) { + NSString *_Nullable localNumber = self.localNumber; + if (!localNumber) { + OWSFail(@"%@ can't re-register without valid local number.", self.logTag); + return NO; + } + + _isRegistered = NO; + _cachedLocalNumber = nil; + _phoneNumberAwaitingVerification = nil; + _cachedIsDeregistered = nil; + [self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { + [transaction removeAllObjectsInCollection:TSAccountManager_UserAccountCollection]; + + [[OWSPrimaryStorage sharedManager] resetSessionStore:transaction]; + + [transaction setObject:localNumber + forKey:TSAccountManager_ReregisteringPhoneNumberKey + inCollection:TSAccountManager_UserAccountCollection]; + }]; + return YES; + } +} + +- (NSString *)reregisterationPhoneNumber +{ + OWSAssert([self isReregistering]); + + NSString *_Nullable result = [self.dbConnection stringForKey:TSAccountManager_ReregisteringPhoneNumberKey + inCollection:TSAccountManager_UserAccountCollection]; + OWSAssert(result); + return result; +} + +- (BOOL)isReregistering +{ + return nil != + [self.dbConnection stringForKey:TSAccountManager_ReregisteringPhoneNumberKey + inCollection:TSAccountManager_UserAccountCollection]; +} + @end NS_ASSUME_NONNULL_END From 7f346326f6284c78dbf27e0b978bcf26257b0a7a Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Mon, 18 Jun 2018 11:10:09 -0400 Subject: [PATCH 6/8] Add re-registration UI. --- SignalServiceKit/src/Account/TSAccountManager.h | 13 ++++++++++--- SignalServiceKit/src/Account/TSAccountManager.m | 9 +++++---- SignalServiceKit/src/Network/API/TSNetworkManager.m | 4 +++- 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/SignalServiceKit/src/Account/TSAccountManager.h b/SignalServiceKit/src/Account/TSAccountManager.h index a60dc410f..a9e4ba626 100644 --- a/SignalServiceKit/src/Account/TSAccountManager.h +++ b/SignalServiceKit/src/Account/TSAccountManager.h @@ -18,7 +18,10 @@ extern NSString *const kNSNotificationName_LocalNumberDidChange; @interface TSAccountManager : NSObject -@property (nonatomic, nullable, readonly) NSString *phoneNumberAwaitingVerification; +// This property is exposed for testing purposes only. +#ifdef DEBUG +@property (nonatomic, nullable) NSString *phoneNumberAwaitingVerification; +#endif #pragma mark - Initializers @@ -116,8 +119,12 @@ extern NSString *const kNSNotificationName_LocalNumberDidChange; #pragma mark - De-Registration -// De-registration reflects whether or not the client has received -// a 403 from the service. +// De-registration reflects whether or not the "last known contact" +// with the service was: +// +// * A 403 from the service, indicating de-registration. +// * A successful auth'd request _or_ websocket connection indicating +// valid registration. - (BOOL)isDeregistered; - (void)setIsDeregistered:(BOOL)isDeregistered; diff --git a/SignalServiceKit/src/Account/TSAccountManager.m b/SignalServiceKit/src/Account/TSAccountManager.m index 09c0e19f7..0a5533296 100644 --- a/SignalServiceKit/src/Account/TSAccountManager.m +++ b/SignalServiceKit/src/Account/TSAccountManager.m @@ -40,7 +40,10 @@ NSString *const TSAccountManager_ServerSignalingKey = @"TSStorageServerSignaling @property (nonatomic, readonly) BOOL isRegistered; +// This property is exposed publicly for testing purposes only. +#ifndef DEBUG @property (nonatomic, nullable) NSString *phoneNumberAwaitingVerification; +#endif @property (nonatomic, nullable) NSString *cachedLocalNumber; @property (nonatomic, readonly) YapDatabaseConnection *dbConnection; @@ -73,10 +76,6 @@ NSString *const TSAccountManager_ServerSignalingKey = @"TSStorageServerSignaling selector:@selector(yapDatabaseModifiedExternally:) name:YapDatabaseModifiedExternallyNotification object:nil]; - - self.phoneNumberAwaitingVerification = - [self.dbConnection stringForKey:TSAccountManager_ReregisteringPhoneNumberKey - inCollection:TSAccountManager_UserAccountCollection]; } return self; @@ -189,6 +188,8 @@ NSString *const TSAccountManager_ServerSignalingKey = @"TSStorageServerSignaling inCollection:TSAccountManager_UserAccountCollection]; self.phoneNumberAwaitingVerification = nil; + + self.cachedLocalNumber = localNumber; } } diff --git a/SignalServiceKit/src/Network/API/TSNetworkManager.m b/SignalServiceKit/src/Network/API/TSNetworkManager.m index 6e00e038f..938f57fa7 100644 --- a/SignalServiceKit/src/Network/API/TSNetworkManager.m +++ b/SignalServiceKit/src/Network/API/TSNetworkManager.m @@ -88,7 +88,9 @@ typedef void (^failureBlock)(NSURLSessionDataTask *task, NSError *error); TSNetworkManagerSuccess success = ^(NSURLSessionDataTask *task, _Nullable id responseObject) { DDLogInfo(@"%@ request succeeded : %@", self.logTag, request); - [TSAccountManager.sharedInstance setIsDeregistered:NO]; + if (request.shouldHaveAuthorizationHeaders) { + [TSAccountManager.sharedInstance setIsDeregistered:NO]; + } successBlock(task, responseObject); }; From 010c10cb0cd5d147187c96302300a34d24197b52 Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Mon, 18 Jun 2018 11:28:21 -0400 Subject: [PATCH 7/8] Show re-registration in app settings. --- Signal.xcodeproj/project.pbxproj | 6 ++ .../AppSettings/AppSettingsViewController.m | 63 +++++++++---- .../HomeView/HomeViewController.m | 83 ++--------------- Signal/src/util/RegistrationUtils.h | 15 +++ Signal/src/util/RegistrationUtils.m | 91 +++++++++++++++++++ .../translations/en.lproj/Localizable.strings | 6 ++ 6 files changed, 167 insertions(+), 97 deletions(-) create mode 100644 Signal/src/util/RegistrationUtils.h create mode 100644 Signal/src/util/RegistrationUtils.m diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index 046338fe7..d81bb1ece 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -231,6 +231,7 @@ 34E3E5681EC4B19400495BAC /* AudioProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34E3E5671EC4B19400495BAC /* AudioProgressView.swift */; }; 34E3EF0D1EFC235B007F6822 /* DebugUIDiskUsage.m in Sources */ = {isa = PBXBuildFile; fileRef = 34E3EF0C1EFC235B007F6822 /* DebugUIDiskUsage.m */; }; 34E3EF101EFC2684007F6822 /* DebugUIPage.m in Sources */ = {isa = PBXBuildFile; fileRef = 34E3EF0F1EFC2684007F6822 /* DebugUIPage.m */; }; + 34E5DC8220D8050D00C08145 /* RegistrationUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 34E5DC8120D8050D00C08145 /* RegistrationUtils.m */; }; 34E88D262098C5AE00A608F4 /* ContactViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34E88D252098C5AE00A608F4 /* ContactViewController.swift */; }; 34E8A8D12085238A00B272B1 /* ProtoParsingTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 34E8A8D02085238900B272B1 /* ProtoParsingTest.m */; }; 34F308A21ECB469700BB7697 /* OWSBezierPathView.m in Sources */ = {isa = PBXBuildFile; fileRef = 34F308A11ECB469700BB7697 /* OWSBezierPathView.m */; }; @@ -894,6 +895,8 @@ 34E3EF0C1EFC235B007F6822 /* DebugUIDiskUsage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DebugUIDiskUsage.m; sourceTree = ""; }; 34E3EF0E1EFC2684007F6822 /* DebugUIPage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DebugUIPage.h; sourceTree = ""; }; 34E3EF0F1EFC2684007F6822 /* DebugUIPage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DebugUIPage.m; sourceTree = ""; }; + 34E5DC8020D8050D00C08145 /* RegistrationUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RegistrationUtils.h; sourceTree = ""; }; + 34E5DC8120D8050D00C08145 /* RegistrationUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RegistrationUtils.m; sourceTree = ""; }; 34E88D252098C5AE00A608F4 /* ContactViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContactViewController.swift; sourceTree = ""; }; 34E8A8D02085238900B272B1 /* ProtoParsingTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ProtoParsingTest.m; sourceTree = ""; }; 34F308A01ECB469700BB7697 /* OWSBezierPathView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSBezierPathView.h; sourceTree = ""; }; @@ -2115,6 +2118,8 @@ 4579431C1E7C8CE9008ED0C0 /* Pastelog.h */, 4579431D1E7C8CE9008ED0C0 /* Pastelog.m */, 450DF2041E0D74AC003D14BE /* Platform.swift */, + 34E5DC8020D8050D00C08145 /* RegistrationUtils.h */, + 34E5DC8120D8050D00C08145 /* RegistrationUtils.m */, 4521C3BF1F59F3BA00B4C582 /* TextFieldHelper.swift */, FCFA64B11A24F29E0007FB87 /* UI Categories */, ); @@ -3235,6 +3240,7 @@ 452037D11EE84975004E4CDF /* DebugUISessionState.m in Sources */, D221A09A169C9E5E00537ABF /* main.m in Sources */, 34B3F87B1E8DF1700035BE1A /* ExperienceUpgradesPageViewController.swift in Sources */, + 34E5DC8220D8050D00C08145 /* RegistrationUtils.m in Sources */, 452EA09E1EA7ABE00078744B /* AttachmentPointerView.swift in Sources */, 45638BDC1F3DD0D400128435 /* DebugUICalling.swift in Sources */, 45464DBC1DFA041F001D3FD6 /* DataChannelMessage.swift in Sources */, diff --git a/Signal/src/ViewControllers/AppSettings/AppSettingsViewController.m b/Signal/src/ViewControllers/AppSettings/AppSettingsViewController.m index 51de91328..bf2b0fadf 100644 --- a/Signal/src/ViewControllers/AppSettings/AppSettingsViewController.m +++ b/Signal/src/ViewControllers/AppSettings/AppSettingsViewController.m @@ -14,6 +14,7 @@ #import "PrivacySettingsTableViewController.h" #import "ProfileViewController.h" #import "PushManager.h" +#import "RegistrationUtils.h" #import "Signal-Swift.h" #import #import @@ -144,19 +145,25 @@ cell.selectionStyle = UITableViewCellSelectionStyleNone; UILabel *accessoryLabel = [UILabel new]; accessoryLabel.font = [UIFont ows_regularFontWithSize:18.f]; - switch ([TSSocketManager sharedManager].state) { - case SocketManagerStateClosed: - accessoryLabel.text = NSLocalizedString(@"NETWORK_STATUS_OFFLINE", @""); - accessoryLabel.textColor = [UIColor ows_redColor]; - break; - case SocketManagerStateConnecting: - accessoryLabel.text = NSLocalizedString(@"NETWORK_STATUS_CONNECTING", @""); - accessoryLabel.textColor = [UIColor ows_yellowColor]; - break; - case SocketManagerStateOpen: - accessoryLabel.text = NSLocalizedString(@"NETWORK_STATUS_CONNECTED", @""); - accessoryLabel.textColor = [UIColor ows_greenColor]; - break; + if (TSAccountManager.sharedInstance.isDeregistered) { + accessoryLabel.text = NSLocalizedString( + @"NETWORK_STATUS_DEREGISTERED", @"Error indicating that this device is no longer registered."); + accessoryLabel.textColor = [UIColor ows_redColor]; + } else { + switch ([TSSocketManager sharedManager].state) { + case SocketManagerStateClosed: + accessoryLabel.text = NSLocalizedString(@"NETWORK_STATUS_OFFLINE", @""); + accessoryLabel.textColor = [UIColor ows_redColor]; + break; + case SocketManagerStateConnecting: + accessoryLabel.text = NSLocalizedString(@"NETWORK_STATUS_CONNECTING", @""); + accessoryLabel.textColor = [UIColor ows_yellowColor]; + break; + case SocketManagerStateOpen: + accessoryLabel.text = NSLocalizedString(@"NETWORK_STATUS_CONNECTED", @""); + accessoryLabel.textColor = [UIColor ows_greenColor]; + break; + } } [accessoryLabel sizeToFit]; cell.accessoryView = accessoryLabel; @@ -227,12 +234,23 @@ cell.selectionStyle = UITableViewCellSelectionStyleNone; const CGFloat kButtonHeight = 40.f; - OWSFlatButton *button = [OWSFlatButton buttonWithTitle:NSLocalizedString(@"SETTINGS_DELETE_ACCOUNT_BUTTON", @"") - font:[OWSFlatButton fontForHeight:kButtonHeight] - titleColor:[UIColor whiteColor] - backgroundColor:[UIColor ows_destructiveRedColor] - target:self - selector:@selector(unregisterUser)]; + OWSFlatButton *button; + if (TSAccountManager.sharedInstance.isDeregistered) { + button = [OWSFlatButton + buttonWithTitle:NSLocalizedString(@"SETTINGS_REREGISTER_BUTTON", @"Label for re-registration button.") + font:[OWSFlatButton fontForHeight:kButtonHeight] + titleColor:[UIColor whiteColor] + backgroundColor:[UIColor ows_destructiveRedColor] + target:self + selector:@selector(reregisterUser)]; + } else { + button = [OWSFlatButton buttonWithTitle:NSLocalizedString(@"SETTINGS_DELETE_ACCOUNT_BUTTON", @"") + font:[OWSFlatButton fontForHeight:kButtonHeight] + titleColor:[UIColor whiteColor] + backgroundColor:[UIColor ows_destructiveRedColor] + target:self + selector:@selector(unregisterUser)]; + } [cell.contentView addSubview:button]; [button autoSetDimension:ALDimensionHeight toSize:kButtonHeight]; [button autoVCenterInSuperview]; @@ -390,7 +408,7 @@ [self dismissViewControllerAnimated:YES completion:nil]; } -#pragma mark - Table view data source +#pragma mark - Unregister & Re-register - (void)unregisterUser { @@ -427,6 +445,11 @@ }]; } +- (void)reregisterUser +{ + [RegistrationUtils showReregistrationUIFromViewController:self]; +} + #pragma mark - Socket Status Notifications - (void)observeNotifications diff --git a/Signal/src/ViewControllers/HomeView/HomeViewController.m b/Signal/src/ViewControllers/HomeView/HomeViewController.m index 56406706b..0f674a8d2 100644 --- a/Signal/src/ViewControllers/HomeView/HomeViewController.m +++ b/Signal/src/ViewControllers/HomeView/HomeViewController.m @@ -5,14 +5,13 @@ #import "HomeViewController.h" #import "AppDelegate.h" #import "AppSettingsViewController.h" -#import "CodeVerificationViewController.h" #import "HomeViewCell.h" #import "NewContactThreadViewController.h" #import "OWSNavigationController.h" #import "OWSPrimaryStorage.h" #import "ProfileViewController.h" #import "PushManager.h" -#import "RegistrationViewController.h" +#import "RegistrationUtils.h" #import "Signal-Swift.h" #import "SignalApp.h" #import "TSAccountManager.h" @@ -217,7 +216,11 @@ NSString *const kArchivedConversationsReuseIdentifier = @"kArchivedConversations [ReminderView nagWithText:NSLocalizedString(@"DEREGISTRATION_WARNING", @"Label warning the user that they have been de-registered.") tapAction:^{ - [weakSelf showReRegistrationUI]; + HomeViewController *strongSelf = weakSelf; + if (!strongSelf) { + return; + } + [RegistrationUtils showReregistrationUIFromViewController:strongSelf]; }]; [self.view addSubview:deregisteredView]; [deregisteredView autoPinWidthToSuperview]; @@ -1410,80 +1413,6 @@ NSString *const kArchivedConversationsReuseIdentifier = @"kArchivedConversations _emptyBoxLabel.attributedText = fullLabelString; } -- (void)showReRegistrationUI -{ - UIAlertController *actionSheetController = - [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:UIAlertControllerStyleActionSheet]; - - __weak HomeViewController *weakSelf = self; - [actionSheetController - addAction:[UIAlertAction - actionWithTitle:NSLocalizedString(@"DEREGISTRATION_REREGISTER_WITH_SAME_PHONE_NUMBER", - @"Label for button that lets users re-register using the same phone number.") - style:UIAlertActionStyleDestructive - handler:^(UIAlertAction *action) { - [weakSelf reregisterWithSamePhoneNumber]; - }]]; - - [actionSheetController addAction:[OWSAlerts cancelAction]]; - - [self presentViewController:actionSheetController animated:YES completion:nil]; -} - -- (void)reregisterWithSamePhoneNumber -{ - DDLogInfo(@"%@ reregisterWithSamePhoneNumber.", self.logTag); - - if (![[TSAccountManager sharedInstance] resetForReregistration]) { - OWSFail(@"%@ could not reset for re-registration.", self.logTag); - return; - } - - [[Environment current].preferences unsetRecordedAPNSTokens]; - - [ModalActivityIndicatorViewController - presentFromViewController:self - canCancel:NO - backgroundBlock:^(ModalActivityIndicatorViewController *modalActivityIndicator) { - [TSAccountManager - registerWithPhoneNumber:[TSAccountManager sharedInstance].reregisterationPhoneNumber - success:^{ - DDLogInfo(@"%@ re-registering: send verification code succeeded.", self.logTag); - - dispatch_async(dispatch_get_main_queue(), ^{ - [modalActivityIndicator dismissWithCompletion:^{ - CodeVerificationViewController *viewController = - [CodeVerificationViewController new]; - - OWSNavigationController *navigationController = - [[OWSNavigationController alloc] initWithRootViewController:viewController]; - navigationController.navigationBarHidden = YES; - - [UIApplication sharedApplication].delegate.window.rootViewController - = navigationController; - }]; - }); - } - failure:^(NSError *error) { - DDLogError(@"%@ re-registering: send verification code failed.", self.logTag); - - dispatch_async(dispatch_get_main_queue(), ^{ - [modalActivityIndicator dismissWithCompletion:^{ - if (error.code == 400) { - [OWSAlerts showAlertWithTitle:NSLocalizedString(@"REGISTRATION_ERROR", nil) - message:NSLocalizedString( - @"REGISTRATION_NON_VALID_NUMBER", nil)]; - } else { - [OWSAlerts showAlertWithTitle:error.localizedDescription - message:error.localizedRecoverySuggestion]; - } - }]; - }); - } - smsVerification:YES]; - }]; -} - @end NS_ASSUME_NONNULL_END diff --git a/Signal/src/util/RegistrationUtils.h b/Signal/src/util/RegistrationUtils.h new file mode 100644 index 000000000..5777c22a8 --- /dev/null +++ b/Signal/src/util/RegistrationUtils.h @@ -0,0 +1,15 @@ +// +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. +// + +NS_ASSUME_NONNULL_BEGIN + +@interface RegistrationUtils : NSObject + +- (instancetype)init NS_UNAVAILABLE; + ++ (void)showReregistrationUIFromViewController:(UIViewController *)fromViewController; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Signal/src/util/RegistrationUtils.m b/Signal/src/util/RegistrationUtils.m new file mode 100644 index 000000000..b1be2f46b --- /dev/null +++ b/Signal/src/util/RegistrationUtils.m @@ -0,0 +1,91 @@ +// +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. +// + +#import "RegistrationUtils.h" +#import "CodeVerificationViewController.h" +#import "OWSNavigationController.h" +#import +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +@implementation RegistrationUtils + ++ (void)showReregistrationUIFromViewController:(UIViewController *)fromViewController +{ + UIAlertController *actionSheetController = + [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:UIAlertControllerStyleActionSheet]; + + [actionSheetController + addAction:[UIAlertAction + actionWithTitle:NSLocalizedString(@"DEREGISTRATION_REREGISTER_WITH_SAME_PHONE_NUMBER", + @"Label for button that lets users re-register using the same phone number.") + style:UIAlertActionStyleDestructive + handler:^(UIAlertAction *action) { + [RegistrationUtils reregisterWithFromViewController:fromViewController]; + }]]; + + [actionSheetController addAction:[OWSAlerts cancelAction]]; + + [fromViewController presentViewController:actionSheetController animated:YES completion:nil]; +} + ++ (void)reregisterWithFromViewController:(UIViewController *)fromViewController +{ + DDLogInfo(@"%@ reregisterWithSamePhoneNumber.", self.logTag); + + if (![[TSAccountManager sharedInstance] resetForReregistration]) { + OWSFail(@"%@ could not reset for re-registration.", self.logTag); + return; + } + + [[Environment current].preferences unsetRecordedAPNSTokens]; + + [ModalActivityIndicatorViewController + presentFromViewController:fromViewController + canCancel:NO + backgroundBlock:^(ModalActivityIndicatorViewController *modalActivityIndicator) { + [TSAccountManager + registerWithPhoneNumber:[TSAccountManager sharedInstance].reregisterationPhoneNumber + success:^{ + DDLogInfo(@"%@ re-registering: send verification code succeeded.", self.logTag); + + dispatch_async(dispatch_get_main_queue(), ^{ + [modalActivityIndicator dismissWithCompletion:^{ + CodeVerificationViewController *viewController = + [CodeVerificationViewController new]; + + OWSNavigationController *navigationController = + [[OWSNavigationController alloc] initWithRootViewController:viewController]; + navigationController.navigationBarHidden = YES; + + [UIApplication sharedApplication].delegate.window.rootViewController + = navigationController; + }]; + }); + } + failure:^(NSError *error) { + DDLogError(@"%@ re-registering: send verification code failed.", self.logTag); + + dispatch_async(dispatch_get_main_queue(), ^{ + [modalActivityIndicator dismissWithCompletion:^{ + if (error.code == 400) { + [OWSAlerts showAlertWithTitle:NSLocalizedString(@"REGISTRATION_ERROR", nil) + message:NSLocalizedString( + @"REGISTRATION_NON_VALID_NUMBER", nil)]; + } else { + [OWSAlerts showAlertWithTitle:error.localizedDescription + message:error.localizedRecoverySuggestion]; + } + }]; + }); + } + smsVerification:YES]; + }]; +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/Signal/translations/en.lproj/Localizable.strings b/Signal/translations/en.lproj/Localizable.strings index f63544a0d..89aafcf4c 100644 --- a/Signal/translations/en.lproj/Localizable.strings +++ b/Signal/translations/en.lproj/Localizable.strings @@ -1315,6 +1315,9 @@ /* No comment provided by engineer. */ "NETWORK_STATUS_CONNECTING" = "Connecting"; +/* Error indicating that this device is no longer registered. */ +"NETWORK_STATUS_DEREGISTERED" = "No Longer Registered"; + /* No comment provided by engineer. */ "NETWORK_STATUS_HEADER" = "Network Status"; @@ -1978,6 +1981,9 @@ /* An explanation of the 'read receipts' setting. */ "SETTINGS_READ_RECEIPTS_SECTION_FOOTER" = "See and share when messages have been read. This setting is optional and applies to all conversations."; +/* Label for re-registration button. */ +"SETTINGS_REREGISTER_BUTTON" = "Re-register"; + /* Label for the 'screen lock activity timeout' setting of the privacy settings. */ "SETTINGS_SCREEN_LOCK_ACTIVITY_TIMEOUT" = "Screen Lock Timeout"; From 4ac8100973d1dfc505eca6c050d326af301bd38f Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Wed, 20 Jun 2018 15:15:33 -0400 Subject: [PATCH 8/8] Respond to CR. --- .../HomeView/HomeViewController.m | 28 +++++++++---------- Signal/src/views/ReminderView.swift | 7 +---- 2 files changed, 14 insertions(+), 21 deletions(-) diff --git a/Signal/src/ViewControllers/HomeView/HomeViewController.m b/Signal/src/ViewControllers/HomeView/HomeViewController.m index 0f674a8d2..edb93f5cd 100644 --- a/Signal/src/ViewControllers/HomeView/HomeViewController.m +++ b/Signal/src/ViewControllers/HomeView/HomeViewController.m @@ -211,6 +211,13 @@ NSString *const kArchivedConversationsReuseIdentifier = @"kArchivedConversations [SignalApp.sharedApp setHomeViewController:self]; } + UIStackView *reminderStackView = [UIStackView new]; + reminderStackView.axis = UILayoutConstraintAxisVertical; + reminderStackView.spacing = 0; + [self.view addSubview:reminderStackView]; + [reminderStackView autoPinWidthToSuperview]; + [reminderStackView autoPinToTopLayoutGuideOfViewController:self withInset:0]; + __weak HomeViewController *weakSelf = self; ReminderView *deregisteredView = [ReminderView nagWithText:NSLocalizedString(@"DEREGISTRATION_WARNING", @@ -222,18 +229,14 @@ NSString *const kArchivedConversationsReuseIdentifier = @"kArchivedConversations } [RegistrationUtils showReregistrationUIFromViewController:strongSelf]; }]; - [self.view addSubview:deregisteredView]; - [deregisteredView autoPinWidthToSuperview]; - [deregisteredView autoPinToTopLayoutGuideOfViewController:self withInset:0]; + [reminderStackView addArrangedSubview:deregisteredView]; self.hideDeregisteredViewConstraint = [deregisteredView autoSetDimension:ALDimensionHeight toSize:0]; self.hideDeregisteredViewConstraint.priority = UILayoutPriorityRequired; ReminderView *archiveReminderView = [ReminderView explanationWithText:NSLocalizedString(@"INBOX_VIEW_ARCHIVE_MODE_REMINDER", @"Label reminding the user that they are in archive mode.")]; - [self.view addSubview:archiveReminderView]; - [archiveReminderView autoPinWidthToSuperview]; - [archiveReminderView autoPinEdge:ALEdgeTop toEdge:ALEdgeBottom ofView:deregisteredView]; + [reminderStackView addArrangedSubview:archiveReminderView]; self.hideArchiveReminderViewConstraint = [archiveReminderView autoSetDimension:ALDimensionHeight toSize:0]; self.hideArchiveReminderViewConstraint.priority = UILayoutPriorityRequired; @@ -243,9 +246,7 @@ NSString *const kArchivedConversationsReuseIdentifier = @"kArchivedConversations tapAction:^{ [[UIApplication sharedApplication] openSystemSettings]; }]; - [self.view addSubview:missingContactsPermissionView]; - [missingContactsPermissionView autoPinWidthToSuperview]; - [missingContactsPermissionView autoPinEdge:ALEdgeTop toEdge:ALEdgeBottom ofView:archiveReminderView]; + [reminderStackView addArrangedSubview:missingContactsPermissionView]; self.hideMissingContactsPermissionViewConstraint = [missingContactsPermissionView autoSetDimension:ALDimensionHeight toSize:0]; self.hideMissingContactsPermissionViewConstraint.priority = UILayoutPriorityRequired; @@ -259,7 +260,7 @@ NSString *const kArchivedConversationsReuseIdentifier = @"kArchivedConversations [self.view addSubview:self.tableView]; [self.tableView autoPinWidthToSuperview]; [self.tableView autoPinEdgeToSuperviewEdge:ALEdgeBottom]; - [self.tableView autoPinEdge:ALEdgeTop toEdge:ALEdgeBottom ofView:missingContactsPermissionView]; + [self.tableView autoPinEdge:ALEdgeTop toEdge:ALEdgeBottom ofView:reminderStackView]; self.tableView.rowHeight = UITableViewAutomaticDimension; self.tableView.estimatedRowHeight = 60; @@ -825,11 +826,8 @@ NSString *const kArchivedConversationsReuseIdentifier = @"kArchivedConversations // Constrain to cell margins. [stackView autoPinEdgeToSuperviewMargin:ALEdgeLeading relation:NSLayoutRelationGreaterThanOrEqual]; [stackView autoPinEdgeToSuperviewMargin:ALEdgeTrailing relation:NSLayoutRelationGreaterThanOrEqual]; - // Ensure that the cell's contents never overflow the cell bounds. - // We pin to the superview _edge_ and not _margin_ for the purposes - // of overflow, so that changes to the margins do not trip these safe guards. - [stackView autoPinEdgeToSuperviewEdge:ALEdgeTop withInset:0 relation:NSLayoutRelationGreaterThanOrEqual]; - [stackView autoPinEdgeToSuperviewEdge:ALEdgeBottom withInset:0 relation:NSLayoutRelationGreaterThanOrEqual]; + [stackView autoPinEdgeToSuperviewMargin:ALEdgeTop]; + [stackView autoPinEdgeToSuperviewMargin:ALEdgeBottom]; return cell; } diff --git a/Signal/src/views/ReminderView.swift b/Signal/src/views/ReminderView.swift index 6b122d8a7..1f6944e03 100644 --- a/Signal/src/views/ReminderView.swift +++ b/Signal/src/views/ReminderView.swift @@ -89,12 +89,7 @@ class ReminderView: UIView { // Margin: top and bottom 12 left and right 16. // Label - switch (mode) { - case .nag: - label.font = UIFont.ows_dynamicTypeFootnote - case .explanation: - label.font = UIFont.ows_dynamicTypeSubheadline - } + label.font = UIFont.ows_dynamicTypeSubheadline container.addSubview(label) label.textColor = UIColor.black.withAlphaComponent(0.9) label.numberOfLines = 0