Fix edge cases around UD v. linked devices.
This commit is contained in:
parent
24e3dbbe46
commit
d656ae1017
@ -76,6 +76,69 @@ static NSTimeInterval launchStartedAt;
|
||||
|
||||
@synthesize window = _window;
|
||||
|
||||
#pragma mark - Dependencies
|
||||
|
||||
- (OWSProfileManager *)profileManager
|
||||
{
|
||||
return [OWSProfileManager sharedManager];
|
||||
}
|
||||
|
||||
- (OWSReadReceiptManager *)readReceiptManager
|
||||
{
|
||||
return [OWSReadReceiptManager sharedManager];
|
||||
}
|
||||
|
||||
- (id<OWSUDManager>)udManager
|
||||
{
|
||||
OWSAssertDebug(SSKEnvironment.shared.udManager);
|
||||
|
||||
return SSKEnvironment.shared.udManager;
|
||||
}
|
||||
|
||||
- (OWSPrimaryStorage *)primaryStorage
|
||||
{
|
||||
OWSAssertDebug(SSKEnvironment.shared.primaryStorage);
|
||||
|
||||
return SSKEnvironment.shared.primaryStorage;
|
||||
}
|
||||
|
||||
- (PushRegistrationManager *)pushRegistrationManager
|
||||
{
|
||||
OWSAssertDebug(AppEnvironment.shared.pushRegistrationManager);
|
||||
|
||||
return AppEnvironment.shared.pushRegistrationManager;
|
||||
}
|
||||
|
||||
- (TSAccountManager *)tsAccountManager
|
||||
{
|
||||
OWSAssertDebug(SSKEnvironment.shared.tsAccountManager);
|
||||
|
||||
return SSKEnvironment.shared.tsAccountManager;
|
||||
}
|
||||
|
||||
- (OWSDisappearingMessagesJob *)disappearingMessagesJob
|
||||
{
|
||||
OWSAssertDebug(SSKEnvironment.shared.disappearingMessagesJob);
|
||||
|
||||
return SSKEnvironment.shared.disappearingMessagesJob;
|
||||
}
|
||||
|
||||
- (TSSocketManager *)socketManager
|
||||
{
|
||||
OWSAssertDebug(SSKEnvironment.shared.socketManager);
|
||||
|
||||
return SSKEnvironment.shared.socketManager;
|
||||
}
|
||||
|
||||
- (OWSMessageManager *)messageManager
|
||||
{
|
||||
OWSAssertDebug(SSKEnvironment.shared.messageManager);
|
||||
|
||||
return SSKEnvironment.shared.messageManager;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
- (void)applicationDidEnterBackground:(UIApplication *)application {
|
||||
OWSLogWarn(@"applicationDidEnterBackground.");
|
||||
|
||||
@ -425,7 +488,7 @@ static NSTimeInterval launchStartedAt;
|
||||
}
|
||||
|
||||
OWSLogInfo(@"registered vanilla push token: %@", deviceToken);
|
||||
[PushRegistrationManager.shared didReceiveVanillaPushToken:deviceToken];
|
||||
[self.pushRegistrationManager didReceiveVanillaPushToken:deviceToken];
|
||||
}
|
||||
|
||||
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
|
||||
@ -440,10 +503,10 @@ static NSTimeInterval launchStartedAt;
|
||||
OWSLogError(@"failed to register vanilla push token with error: %@", error);
|
||||
#ifdef DEBUG
|
||||
OWSLogWarn(@"We're in debug mode. Faking success for remote registration with a fake push identifier");
|
||||
[PushRegistrationManager.shared didReceiveVanillaPushToken:[[NSMutableData dataWithLength:32] copy]];
|
||||
[self.pushRegistrationManager didReceiveVanillaPushToken:[[NSMutableData dataWithLength:32] copy]];
|
||||
#else
|
||||
OWSProdError([OWSAnalyticsEvents appDelegateErrorFailedToRegisterForRemoteNotifications]);
|
||||
[PushRegistrationManager.shared didFailToReceiveVanillaPushTokenWithError:error];
|
||||
[self.pushRegistrationManager didFailToReceiveVanillaPushTokenWithError:error];
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -458,7 +521,7 @@ static NSTimeInterval launchStartedAt;
|
||||
}
|
||||
|
||||
OWSLogInfo(@"registered user notification settings");
|
||||
[PushRegistrationManager.shared didRegisterUserNotificationSettings];
|
||||
[self.pushRegistrationManager didRegisterUserNotificationSettings];
|
||||
}
|
||||
|
||||
- (BOOL)application:(UIApplication *)application
|
||||
@ -482,7 +545,7 @@ static NSTimeInterval launchStartedAt;
|
||||
}
|
||||
|
||||
if ([url.scheme isEqualToString:kURLSchemeSGNLKey]) {
|
||||
if ([url.host hasPrefix:kURLHostVerifyPrefix] && ![TSAccountManager isRegistered]) {
|
||||
if ([url.host hasPrefix:kURLHostVerifyPrefix] && ![self.tsAccountManager isRegistered]) {
|
||||
id signupController = SignalApp.sharedApp.signUpFlowNavigationController;
|
||||
if ([signupController isKindOfClass:[OWSNavigationController class]]) {
|
||||
OWSNavigationController *navController = (OWSNavigationController *)signupController;
|
||||
@ -542,7 +605,7 @@ static NSTimeInterval launchStartedAt;
|
||||
- (void)enableBackgroundRefreshIfNecessary
|
||||
{
|
||||
[AppReadiness runNowOrWhenAppIsReady:^{
|
||||
if (OWS2FAManager.sharedManager.is2FAEnabled && [TSAccountManager isRegistered]) {
|
||||
if (OWS2FAManager.sharedManager.is2FAEnabled && [self.tsAccountManager isRegistered]) {
|
||||
// Ping server once a day to keep-alive 2FA clients.
|
||||
const NSTimeInterval kBackgroundRefreshInterval = 24 * 60 * 60;
|
||||
[[UIApplication sharedApplication] setMinimumBackgroundFetchInterval:kBackgroundRefreshInterval];
|
||||
@ -566,26 +629,25 @@ static NSTimeInterval launchStartedAt;
|
||||
dispatch_once(&onceToken, ^{
|
||||
RTCInitializeSSL();
|
||||
|
||||
if ([TSAccountManager isRegistered]) {
|
||||
if ([self.tsAccountManager isRegistered]) {
|
||||
// At this point, potentially lengthy DB locking migrations could be running.
|
||||
// Avoid blocking app launch by putting all further possible DB access in async block
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
OWSLogInfo(@"running post launch block for registered user: %@", [TSAccountManager localNumber]);
|
||||
OWSLogInfo(@"running post launch block for registered user: %@", [self.tsAccountManager localNumber]);
|
||||
|
||||
// Clean up any messages that expired since last launch immediately
|
||||
// and continue cleaning in the background.
|
||||
[[OWSDisappearingMessagesJob sharedJob] startIfNecessary];
|
||||
[self.disappearingMessagesJob startIfNecessary];
|
||||
|
||||
[self enableBackgroundRefreshIfNecessary];
|
||||
|
||||
// Mark all "attempting out" messages as "unsent", i.e. any messages that were not successfully
|
||||
// sent before the app exited should be marked as failures.
|
||||
[[[OWSFailedMessagesJob alloc] initWithPrimaryStorage:[OWSPrimaryStorage sharedManager]] run];
|
||||
[[[OWSFailedMessagesJob alloc] initWithPrimaryStorage:self.primaryStorage] run];
|
||||
// Mark all "incomplete" calls as missed, e.g. any incoming or outgoing calls that were not
|
||||
// connected, failed or hung up before the app existed should be marked as missed.
|
||||
[[[OWSIncompleteCallsJob alloc] initWithPrimaryStorage:[OWSPrimaryStorage sharedManager]] run];
|
||||
[[[OWSFailedAttachmentDownloadsJob alloc] initWithPrimaryStorage:[OWSPrimaryStorage sharedManager]]
|
||||
run];
|
||||
[[[OWSIncompleteCallsJob alloc] initWithPrimaryStorage:self.primaryStorage] run];
|
||||
[[[OWSFailedAttachmentDownloadsJob alloc] initWithPrimaryStorage:self.primaryStorage] run];
|
||||
});
|
||||
} else {
|
||||
OWSLogInfo(@"running post launch block for unregistered user.");
|
||||
@ -593,7 +655,7 @@ static NSTimeInterval launchStartedAt;
|
||||
// Unregistered user should have no unread messages. e.g. if you delete your account.
|
||||
[SignalApp clearAllNotifications];
|
||||
|
||||
[TSSocketManager.shared requestSocketOpen];
|
||||
[self.socketManager requestSocketOpen];
|
||||
|
||||
UITapGestureRecognizer *gesture =
|
||||
[[UITapGestureRecognizer alloc] initWithTarget:[Pastelog class] action:@selector(submitLogs)];
|
||||
@ -603,11 +665,11 @@ static NSTimeInterval launchStartedAt;
|
||||
}); // end dispatchOnce for first time we become active
|
||||
|
||||
// Every time we become active...
|
||||
if ([TSAccountManager isRegistered]) {
|
||||
if ([self.tsAccountManager isRegistered]) {
|
||||
// At this point, potentially lengthy DB locking migrations could be running.
|
||||
// Avoid blocking app launch by putting all further possible DB access in async block
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[TSSocketManager.shared requestSocketOpen];
|
||||
[self.socketManager requestSocketOpen];
|
||||
[Environment.shared.contactsManager fetchSystemContactsOnceIfAlreadyAuthorized];
|
||||
// This will fetch new messages, if we're using domain fronting.
|
||||
[[PushManager sharedManager] applicationDidBecomeActive];
|
||||
@ -674,7 +736,7 @@ static NSTimeInterval launchStartedAt;
|
||||
}
|
||||
|
||||
[AppReadiness runNowOrWhenAppIsReady:^{
|
||||
if (![TSAccountManager isRegistered]) {
|
||||
if (![self.tsAccountManager isRegistered]) {
|
||||
UIAlertController *controller =
|
||||
[UIAlertController alertControllerWithTitle:NSLocalizedString(@"REGISTER_CONTACTS_WELCOME", nil)
|
||||
message:NSLocalizedString(@"REGISTRATION_RESTRICTED_MESSAGE", nil)
|
||||
@ -747,7 +809,7 @@ static NSTimeInterval launchStartedAt;
|
||||
[AppReadiness runNowOrWhenAppIsReady:^{
|
||||
NSString *_Nullable phoneNumber = handle;
|
||||
if ([handle hasPrefix:CallKitCallManager.kAnonymousCallHandlePrefix]) {
|
||||
phoneNumber = [[OWSPrimaryStorage sharedManager] phoneNumberForCallKitId:handle];
|
||||
phoneNumber = [self.primaryStorage phoneNumberForCallKitId:handle];
|
||||
if (phoneNumber.length < 1) {
|
||||
OWSLogWarn(@"ignoring attempt to initiate video call to unknown anonymous signal user.");
|
||||
return;
|
||||
@ -804,7 +866,7 @@ static NSTimeInterval launchStartedAt;
|
||||
[AppReadiness runNowOrWhenAppIsReady:^{
|
||||
NSString *_Nullable phoneNumber = handle;
|
||||
if ([handle hasPrefix:CallKitCallManager.kAnonymousCallHandlePrefix]) {
|
||||
phoneNumber = [[OWSPrimaryStorage sharedManager] phoneNumberForCallKitId:handle];
|
||||
phoneNumber = [self.primaryStorage phoneNumberForCallKitId:handle];
|
||||
if (phoneNumber.length < 1) {
|
||||
OWSLogWarn(@"ignoring attempt to initiate audio call to unknown anonymous signal user.");
|
||||
return;
|
||||
@ -1010,8 +1072,8 @@ static NSTimeInterval launchStartedAt;
|
||||
OWSLogInfo(@"checkIfAppIsReady");
|
||||
|
||||
// TODO: Once "app ready" logic is moved into AppSetup, move this line there.
|
||||
[[OWSProfileManager sharedManager] ensureLocalProfileCached];
|
||||
|
||||
[self.profileManager ensureLocalProfileCached];
|
||||
|
||||
// Note that this does much more than set a flag;
|
||||
// it will also run all deferred blocks.
|
||||
[AppReadiness setAppIsReady];
|
||||
@ -1021,8 +1083,8 @@ static NSTimeInterval launchStartedAt;
|
||||
return;
|
||||
}
|
||||
|
||||
if ([TSAccountManager isRegistered]) {
|
||||
OWSLogInfo(@"localNumber: %@", [TSAccountManager localNumber]);
|
||||
if ([self.tsAccountManager isRegistered]) {
|
||||
OWSLogVerbose(@"localNumber: %@", [self.tsAccountManager localNumber]);
|
||||
|
||||
// Fetch messages as soon as possible after launching. In particular, when
|
||||
// launching from the background, without this, we end up waiting some extra
|
||||
@ -1047,7 +1109,7 @@ static NSTimeInterval launchStartedAt;
|
||||
[SSKEnvironment.shared.batchMessageProcessor handleAnyUnprocessedEnvelopesAsync];
|
||||
|
||||
if (!Environment.shared.preferences.hasGeneratedThumbnails) {
|
||||
[OWSPrimaryStorage.sharedManager.newDatabaseConnection
|
||||
[self.primaryStorage.newDatabaseConnection
|
||||
asyncReadWithBlock:^(YapDatabaseReadTransaction *_Nonnull transaction) {
|
||||
[TSAttachmentStream enumerateCollectionObjectsUsingBlock:^(id _Nonnull obj, BOOL *_Nonnull stop){
|
||||
// no-op. It's sufficient to initWithCoder: each object.
|
||||
@ -1069,8 +1131,8 @@ static NSTimeInterval launchStartedAt;
|
||||
[OWSOrphanDataCleaner auditOnLaunchIfNecessary];
|
||||
#endif
|
||||
|
||||
[OWSProfileManager.sharedManager fetchLocalUsersProfile];
|
||||
[[OWSReadReceiptManager sharedManager] prepareCachedValues];
|
||||
[self.profileManager fetchLocalUsersProfile];
|
||||
[self.readReceiptManager prepareCachedValues];
|
||||
|
||||
// Disable the SAE until the main app has successfully completed launch process
|
||||
// at least once in the post-SAE world.
|
||||
@ -1080,14 +1142,14 @@ static NSTimeInterval launchStartedAt;
|
||||
|
||||
[OWSBackup.sharedManager setup];
|
||||
|
||||
[SSKEnvironment.shared.messageManager startObserving];
|
||||
[self.messageManager startObserving];
|
||||
|
||||
#ifdef DEBUG
|
||||
// Resume lazy restore.
|
||||
[OWSBackupLazyRestoreJob runAsync];
|
||||
#endif
|
||||
|
||||
[SSKEnvironment.shared.udManager setup];
|
||||
[self.udManager setup];
|
||||
}
|
||||
|
||||
- (void)registrationStateDidChange
|
||||
@ -1098,20 +1160,20 @@ static NSTimeInterval launchStartedAt;
|
||||
|
||||
[self enableBackgroundRefreshIfNecessary];
|
||||
|
||||
if ([TSAccountManager isRegistered]) {
|
||||
OWSLogInfo(@"localNumber: %@", [TSAccountManager localNumber]);
|
||||
if ([self.tsAccountManager isRegistered]) {
|
||||
OWSLogInfo(@"localNumber: %@", [self.tsAccountManager localNumber]);
|
||||
|
||||
[[OWSPrimaryStorage sharedManager].newDatabaseConnection
|
||||
[self.primaryStorage.newDatabaseConnection
|
||||
readWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) {
|
||||
[ExperienceUpgradeFinder.sharedManager markAllAsSeenWithTransaction:transaction];
|
||||
}];
|
||||
// Start running the disappearing messages job in case the newly registered user
|
||||
// enables this feature
|
||||
[[OWSDisappearingMessagesJob sharedJob] startIfNecessary];
|
||||
[[OWSProfileManager sharedManager] ensureLocalProfileCached];
|
||||
[self.disappearingMessagesJob startIfNecessary];
|
||||
[self.profileManager ensureLocalProfileCached];
|
||||
|
||||
// For non-legacy users, read receipts are on by default.
|
||||
[OWSReadReceiptManager.sharedManager setAreReadReceiptsEnabled:YES];
|
||||
[self.readReceiptManager setAreReadReceiptsEnabled:YES];
|
||||
}
|
||||
}
|
||||
|
||||
@ -1134,7 +1196,7 @@ static NSTimeInterval launchStartedAt;
|
||||
NSTimeInterval startupDuration = CACurrentMediaTime() - launchStartedAt;
|
||||
OWSLogInfo(@"Presenting app %.2f seconds after launch started.", startupDuration);
|
||||
|
||||
if ([TSAccountManager isRegistered]) {
|
||||
if ([self.tsAccountManager isRegistered]) {
|
||||
HomeViewController *homeView = [HomeViewController new];
|
||||
SignalsNavigationController *navigationController =
|
||||
[[SignalsNavigationController alloc] initWithRootViewController:homeView];
|
||||
|
||||
@ -68,6 +68,18 @@ int const OWSLinkedDevicesTableViewControllerSectionAddDevice = 1;
|
||||
selector:@selector(yapDatabaseModifiedExternally:)
|
||||
name:YapDatabaseModifiedExternallyNotification
|
||||
object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(deviceListUpdateSucceeded:)
|
||||
name:NSNotificationName_DeviceListUpdateSucceeded
|
||||
object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(deviceListUpdateFailed:)
|
||||
name:NSNotificationName_DeviceListUpdateFailed
|
||||
object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(deviceListUpdateModifiedDeviceList:)
|
||||
name:NSNotificationName_DeviceListUpdateModifiedDeviceList
|
||||
object:nil];
|
||||
|
||||
self.refreshControl = [UIRefreshControl new];
|
||||
[self.refreshControl addTarget:self action:@selector(refreshDevices) forControlEvents:UIControlEventValueChanged];
|
||||
@ -141,61 +153,54 @@ int const OWSLinkedDevicesTableViewControllerSectionAddDevice = 1;
|
||||
|
||||
- (void)refreshDevices
|
||||
{
|
||||
__weak typeof(self) wself = self;
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
[[OWSDevicesService new]
|
||||
getDevicesWithSuccess:^(NSArray<OWSDevice *> *devices) {
|
||||
// If we have more than one device; we may have a linked device.
|
||||
if (devices.count > 1) {
|
||||
// Setting this flag here shouldn't be necessary, but we do so
|
||||
// because the "cost" is low and it will improve robustness.
|
||||
[OWSDeviceManager.sharedManager setMayHaveLinkedDevices];
|
||||
}
|
||||
[OWSDevicesService refreshDevices];
|
||||
}
|
||||
|
||||
if (devices.count > [OWSDevice numberOfKeysInCollection]) {
|
||||
// Got our new device, we can stop refreshing.
|
||||
wself.isExpectingMoreDevices = NO;
|
||||
[wself.pollingRefreshTimer invalidate];
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
wself.refreshControl.attributedTitle = nil;
|
||||
});
|
||||
}
|
||||
[OWSDevice replaceAll:devices];
|
||||
- (void)deviceListUpdateSucceeded:(NSNotification *)notification
|
||||
{
|
||||
OWSAssertIsOnMainThread();
|
||||
|
||||
if (!self.isExpectingMoreDevices) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[wself.refreshControl endRefreshing];
|
||||
});
|
||||
}
|
||||
}
|
||||
failure:^(NSError *error) {
|
||||
OWSLogError(@"Failed to fetch devices in linkedDevices controller with error: %@", error);
|
||||
[self.refreshControl endRefreshing];
|
||||
}
|
||||
|
||||
NSString *alertTitle = NSLocalizedString(
|
||||
@"DEVICE_LIST_UPDATE_FAILED_TITLE", @"Alert title that can occur when viewing device manager.");
|
||||
- (void)deviceListUpdateFailed:(NSNotification *)notification
|
||||
{
|
||||
OWSAssertIsOnMainThread();
|
||||
|
||||
UIAlertController *alertController =
|
||||
[UIAlertController alertControllerWithTitle:alertTitle
|
||||
message:error.localizedDescription
|
||||
preferredStyle:UIAlertControllerStyleAlert];
|
||||
NSError *error = notification.object;
|
||||
OWSAssertDebug(error);
|
||||
|
||||
UIAlertAction *retryAction = [UIAlertAction actionWithTitle:[CommonStrings retryButton]
|
||||
style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction *action) {
|
||||
[wself refreshDevices];
|
||||
}];
|
||||
[alertController addAction:retryAction];
|
||||
NSString *alertTitle = NSLocalizedString(
|
||||
@"DEVICE_LIST_UPDATE_FAILED_TITLE", @"Alert title that can occur when viewing device manager.");
|
||||
|
||||
UIAlertAction *dismissAction = [UIAlertAction actionWithTitle:CommonStrings.dismissButton
|
||||
style:UIAlertActionStyleCancel
|
||||
handler:nil];
|
||||
[alertController addAction:dismissAction];
|
||||
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:alertTitle
|
||||
message:error.localizedDescription
|
||||
preferredStyle:UIAlertControllerStyleAlert];
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[wself.refreshControl endRefreshing];
|
||||
[wself presentViewController:alertController animated:YES completion:nil];
|
||||
});
|
||||
}];
|
||||
UIAlertAction *retryAction = [UIAlertAction actionWithTitle:[CommonStrings retryButton]
|
||||
style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction *action) {
|
||||
[self refreshDevices];
|
||||
}];
|
||||
[alertController addAction:retryAction];
|
||||
|
||||
UIAlertAction *dismissAction =
|
||||
[UIAlertAction actionWithTitle:CommonStrings.dismissButton style:UIAlertActionStyleCancel handler:nil];
|
||||
[alertController addAction:dismissAction];
|
||||
|
||||
[self.refreshControl endRefreshing];
|
||||
[self presentViewController:alertController animated:YES completion:nil];
|
||||
}
|
||||
|
||||
- (void)deviceListUpdateModifiedDeviceList:(NSNotification *)notification
|
||||
{
|
||||
OWSAssertIsOnMainThread();
|
||||
|
||||
// Got our new device, we can stop refreshing.
|
||||
self.isExpectingMoreDevices = NO;
|
||||
[self.pollingRefreshTimer invalidate];
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
self.refreshControl.attributedTitle = nil;
|
||||
});
|
||||
}
|
||||
|
||||
@ -397,29 +402,29 @@ int const OWSLinkedDevicesTableViewControllerSectionAddDevice = 1;
|
||||
|
||||
- (void)unlinkDevice:(OWSDevice *)device success:(void (^)(void))successCallback
|
||||
{
|
||||
[[OWSDevicesService new] unlinkDevice:device
|
||||
success:successCallback
|
||||
failure:^(NSError *error) {
|
||||
NSString *title = NSLocalizedString(
|
||||
@"UNLINKING_FAILED_ALERT_TITLE", @"Alert title when unlinking device fails");
|
||||
UIAlertController *alertController =
|
||||
[UIAlertController alertControllerWithTitle:title
|
||||
message:error.localizedDescription
|
||||
preferredStyle:UIAlertControllerStyleAlert];
|
||||
[OWSDevicesService unlinkDevice:device
|
||||
success:successCallback
|
||||
failure:^(NSError *error) {
|
||||
NSString *title = NSLocalizedString(
|
||||
@"UNLINKING_FAILED_ALERT_TITLE", @"Alert title when unlinking device fails");
|
||||
UIAlertController *alertController =
|
||||
[UIAlertController alertControllerWithTitle:title
|
||||
message:error.localizedDescription
|
||||
preferredStyle:UIAlertControllerStyleAlert];
|
||||
|
||||
UIAlertAction *retryAction =
|
||||
[UIAlertAction actionWithTitle:[CommonStrings retryButton]
|
||||
style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction *aaction) {
|
||||
[self unlinkDevice:device success:successCallback];
|
||||
}];
|
||||
[alertController addAction:retryAction];
|
||||
[alertController addAction:[OWSAlerts cancelAction]];
|
||||
UIAlertAction *retryAction =
|
||||
[UIAlertAction actionWithTitle:[CommonStrings retryButton]
|
||||
style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction *aaction) {
|
||||
[self unlinkDevice:device success:successCallback];
|
||||
}];
|
||||
[alertController addAction:retryAction];
|
||||
[alertController addAction:[OWSAlerts cancelAction]];
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self presentViewController:alertController animated:YES completion:nil];
|
||||
});
|
||||
}];
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self presentViewController:alertController animated:YES completion:nil];
|
||||
});
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(nullable id)sender
|
||||
|
||||
@ -142,7 +142,8 @@ public class ProfileFetcherJob: NSObject {
|
||||
}, websocketFailureBlock: {
|
||||
// Do nothing
|
||||
}, recipientId: recipientId,
|
||||
unidentifiedAccess: unidentifiedAccess)
|
||||
unidentifiedAccess: unidentifiedAccess,
|
||||
canFailoverUDAuth: true)
|
||||
return requestMaker.makeRequest()
|
||||
.map { (result: RequestMakerResult) -> SignalServiceProfile in
|
||||
try SignalServiceProfile(recipientId: recipientId, responseObject: result.responseObject)
|
||||
|
||||
@ -35,13 +35,17 @@ extern uint32_t const OWSDevicePrimaryDeviceId;
|
||||
|
||||
+ (nullable instancetype)deviceFromJSONDictionary:(NSDictionary *)deviceAttributes error:(NSError **)error;
|
||||
|
||||
+ (NSArray<OWSDevice *> *)currentDevicesWithTransaction:(YapDatabaseReadTransaction *)transaction;
|
||||
|
||||
/**
|
||||
* Set local database of devices to `devices`.
|
||||
*
|
||||
* This will create missing devices, update existing devices, and delete stale devices.
|
||||
* @param devices Removes any existing devices, replacing them with `devices`
|
||||
*
|
||||
* Returns YET if any devices were added or removed.
|
||||
*/
|
||||
+ (void)replaceAll:(NSArray<OWSDevice *> *)devices;
|
||||
+ (BOOL)replaceAll:(NSArray<OWSDevice *> *)devices;
|
||||
|
||||
/**
|
||||
* The id of the device currently running this application
|
||||
|
||||
@ -171,21 +171,37 @@ NSString *const kOWSPrimaryStorage_MayHaveLinkedDevices = @"kTSStorageManager_Ma
|
||||
return self.millisecondTimestampToDateTransformer;
|
||||
}
|
||||
|
||||
+ (void)replaceAll:(NSArray<OWSDevice *> *)currentDevices
|
||||
+ (NSArray<OWSDevice *> *)currentDevicesWithTransaction:(YapDatabaseReadTransaction *)transaction
|
||||
{
|
||||
BOOL didChange = NO;
|
||||
OWSAssertDebug(transaction);
|
||||
|
||||
NSMutableArray<OWSDevice *> *result = [NSMutableArray new];
|
||||
[transaction enumerateKeysAndObjectsInCollection:OWSDevice.collection
|
||||
usingBlock:^(NSString *key, OWSDevice *object, BOOL *stop) {
|
||||
if (![object isKindOfClass:[OWSDevice class]]) {
|
||||
OWSFailDebug(@"Unexpected object in collection: %@", object.class);
|
||||
return;
|
||||
}
|
||||
[result addObject:object];
|
||||
}];
|
||||
return result;
|
||||
}
|
||||
|
||||
+ (BOOL)replaceAll:(NSArray<OWSDevice *> *)currentDevices
|
||||
{
|
||||
BOOL didAddOrRemove = NO;
|
||||
NSMutableArray<OWSDevice *> *existingDevices = [[self allObjectsInCollection] mutableCopy];
|
||||
for (OWSDevice *currentDevice in currentDevices) {
|
||||
NSUInteger existingDeviceIndex = [existingDevices indexOfObject:currentDevice];
|
||||
if (existingDeviceIndex == NSNotFound) {
|
||||
// New Device
|
||||
OWSLogInfo(@"Adding device: %@", currentDevice);
|
||||
[currentDevice save];
|
||||
didChange = YES;
|
||||
didAddOrRemove = YES;
|
||||
} else {
|
||||
OWSDevice *existingDevice = existingDevices[existingDeviceIndex];
|
||||
if ([existingDevice updateAttributesWithDevice:currentDevice]) {
|
||||
[existingDevice save];
|
||||
didChange = YES;
|
||||
}
|
||||
[existingDevices removeObjectAtIndex:existingDeviceIndex];
|
||||
}
|
||||
@ -193,11 +209,12 @@ NSString *const kOWSPrimaryStorage_MayHaveLinkedDevices = @"kTSStorageManager_Ma
|
||||
|
||||
// Since we removed existing devices as we went, only stale devices remain
|
||||
for (OWSDevice *staleDevice in existingDevices) {
|
||||
OWSLogVerbose(@"Removing device: %@", staleDevice);
|
||||
[staleDevice remove];
|
||||
didChange = YES;
|
||||
didAddOrRemove = YES;
|
||||
}
|
||||
|
||||
if (didChange) {
|
||||
if (didAddOrRemove) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
// Device changes can affect the UD access mode for a recipient,
|
||||
// so we need to:
|
||||
@ -208,6 +225,9 @@ NSString *const kOWSPrimaryStorage_MayHaveLinkedDevices = @"kTSStorageManager_Ma
|
||||
recipientId:self.tsAccountManager.localNumber];
|
||||
[self.profileManager fetchLocalUsersProfile];
|
||||
});
|
||||
return YES;
|
||||
} else {
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -14,6 +14,7 @@
|
||||
#import "OWSCallMessageHandler.h"
|
||||
#import "OWSContact.h"
|
||||
#import "OWSDevice.h"
|
||||
#import "OWSDevicesService.h"
|
||||
#import "OWSDisappearingConfigurationUpdateInfoMessage.h"
|
||||
#import "OWSDisappearingMessagesConfiguration.h"
|
||||
#import "OWSDisappearingMessagesJob.h"
|
||||
@ -47,6 +48,7 @@
|
||||
#import <SignalCoreKit/Cryptography.h>
|
||||
#import <SignalCoreKit/NSDate+OWS.h>
|
||||
#import <SignalCoreKit/NSString+SSK.h>
|
||||
#import <SignalServiceKit/SignalRecipient.h>
|
||||
#import <SignalServiceKit/SignalServiceKit-Swift.h>
|
||||
#import <YapDatabase/YapDatabase.h>
|
||||
|
||||
@ -89,6 +91,8 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark - Dependencies
|
||||
|
||||
- (id<OWSCallMessageHandler>)callMessageHandler
|
||||
{
|
||||
OWSAssertDebug(SSKEnvironment.shared.callMessageHandler);
|
||||
@ -145,6 +149,13 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
return SSKEnvironment.shared.syncManager;
|
||||
}
|
||||
|
||||
- (TSAccountManager *)tsAccountManager
|
||||
{
|
||||
OWSAssertDebug(SSKEnvironment.shared.tsAccountManager);
|
||||
|
||||
return SSKEnvironment.shared.tsAccountManager;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
- (void)startObserving
|
||||
@ -235,6 +246,8 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
OWSAssertDebug(![self isEnvelopeSenderBlocked:envelope]);
|
||||
|
||||
[self checkForUnknownLinkedDevice:envelope transaction:transaction];
|
||||
|
||||
switch (envelope.type) {
|
||||
case SSKProtoEnvelopeTypeCiphertext:
|
||||
case SSKProtoEnvelopeTypePrekeyBundle:
|
||||
@ -471,7 +484,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
if (groupThread) {
|
||||
if (dataMessage.group.type != SSKProtoGroupContextTypeUpdate) {
|
||||
if (![groupThread.groupModel.groupMemberIds containsObject:[TSAccountManager localNumber]]) {
|
||||
if (![groupThread.groupModel.groupMemberIds containsObject:self.tsAccountManager.localNumber]) {
|
||||
OWSLogInfo(@"Ignoring messages for left group.");
|
||||
return;
|
||||
}
|
||||
@ -749,7 +762,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
return;
|
||||
}
|
||||
|
||||
NSString *localNumber = [TSAccountManager localNumber];
|
||||
NSString *localNumber = self.tsAccountManager.localNumber;
|
||||
if (![localNumber isEqualToString:envelope.source]) {
|
||||
// Sync messages should only come from linked devices.
|
||||
OWSProdErrorWEnvelope([OWSAnalyticsEvents messageManagerErrorSyncMessageFromUnknownSource], envelope);
|
||||
@ -1062,7 +1075,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
}
|
||||
|
||||
// Ensure we are in the group.
|
||||
NSString *localNumber = [TSAccountManager localNumber];
|
||||
NSString *localNumber = self.tsAccountManager.localNumber;
|
||||
if (![gThread.groupModel.groupMemberIds containsObject:localNumber]) {
|
||||
OWSLogWarn(@"Ignoring 'Request Group Info' message for group we no longer belong to.");
|
||||
return;
|
||||
@ -1298,7 +1311,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
[incomingMessage saveWithTransaction:transaction];
|
||||
|
||||
// Any messages sent from the current user - from this device or another - should be automatically marked as read.
|
||||
if ([envelope.source isEqualToString:TSAccountManager.localNumber]) {
|
||||
if ([envelope.source isEqualToString:self.tsAccountManager.localNumber]) {
|
||||
// Don't send a read receipt for messages sent by ourselves.
|
||||
[incomingMessage markAsReadAtTimestamp:envelope.timestamp sendReadReceipt:NO transaction:transaction];
|
||||
}
|
||||
@ -1424,6 +1437,48 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
- (void)checkForUnknownLinkedDevice:(SSKProtoEnvelope *)envelope
|
||||
transaction:(YapDatabaseReadWriteTransaction *)transaction
|
||||
{
|
||||
OWSAssertDebug(envelope);
|
||||
OWSAssertDebug(transaction);
|
||||
|
||||
NSString *localNumber = self.tsAccountManager.localNumber;
|
||||
if (![localNumber isEqualToString:envelope.source]) {
|
||||
return;
|
||||
}
|
||||
|
||||
NSMutableSet<NSNumber *> *deviceIdSet = [NSMutableSet new];
|
||||
for (OWSDevice *device in [OWSDevice currentDevicesWithTransaction:transaction]) {
|
||||
[deviceIdSet addObject:@(device.deviceId)];
|
||||
}
|
||||
SignalRecipient *_Nullable recipient =
|
||||
[SignalRecipient registeredRecipientForRecipientId:localNumber transaction:transaction];
|
||||
if (!recipient) {
|
||||
OWSFailDebug(@"No local SignalRecipient.");
|
||||
} else {
|
||||
BOOL isRecipientDevice = [recipient.devices containsObject:@(envelope.sourceDevice)];
|
||||
if (!isRecipientDevice) {
|
||||
OWSLogInfo(@"Message received from unknown linked device; adding to local SignalRecipient: %lu.",
|
||||
(unsigned long) envelope.sourceDevice);
|
||||
|
||||
[recipient updateRegisteredRecipientWithDevicesToAdd:@[ @(envelope.sourceDevice) ]
|
||||
devicesToRemove:nil
|
||||
transaction:transaction];
|
||||
}
|
||||
}
|
||||
|
||||
BOOL isInDeviceList = [deviceIdSet containsObject:@(envelope.sourceDevice)];
|
||||
if (!isInDeviceList) {
|
||||
OWSLogInfo(@"Message received from unknown linked device; refreshing device list: %lu.",
|
||||
(unsigned long) envelope.sourceDevice);
|
||||
|
||||
[OWSDevicesService refreshDevices];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@ -933,6 +933,11 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
|
||||
// Consume an attempt.
|
||||
messageSend.remainingAttempts = messageSend.remainingAttempts - 1;
|
||||
|
||||
if ([message isKindOfClass:[OWSOutgoingSyncMessage class]]
|
||||
&& ![message isKindOfClass:[OWSOutgoingSentMessageTranscript class]]) {
|
||||
[messageSend disableUD];
|
||||
}
|
||||
|
||||
NSError *deviceMessagesError;
|
||||
NSArray<NSDictionary *> *_Nullable deviceMessages =
|
||||
[self deviceMessagesForMessageSendSafe:messageSend error:&deviceMessagesError];
|
||||
@ -1028,11 +1033,8 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
|
||||
OWSLogWarn(@"Sending a message with no device messages.");
|
||||
}
|
||||
|
||||
if ([message isKindOfClass:[OWSOutgoingSyncMessage class]]
|
||||
&& ![message isKindOfClass:[OWSOutgoingSentMessageTranscript class]]) {
|
||||
[messageSend disableUD];
|
||||
}
|
||||
|
||||
// NOTE: canFailoverUDAuth is NO because UD-auth and Non-UD-auth requests
|
||||
// use different device lists.
|
||||
OWSRequestMaker *requestMaker = [[OWSRequestMaker alloc]
|
||||
initWithRequestFactoryBlock:^(SSKUnidentifiedAccess *_Nullable unidentifiedAccess) {
|
||||
return [OWSRequestFactory submitMessageRequestWithRecipient:recipient.recipientId
|
||||
@ -1047,7 +1049,8 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
|
||||
messageSend.hasWebsocketSendFailed = YES;
|
||||
}
|
||||
recipientId:recipient.recipientId
|
||||
unidentifiedAccess:messageSend.unidentifiedAccess];
|
||||
unidentifiedAccess:messageSend.unidentifiedAccess
|
||||
canFailoverUDAuth:NO];
|
||||
[[requestMaker makeRequestObjc]
|
||||
.then(^(OWSRequestMakerResult *result) {
|
||||
dispatch_async([OWSDispatch sendingQueue], ^{
|
||||
@ -1059,7 +1062,11 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
|
||||
dispatch_async([OWSDispatch sendingQueue], ^{
|
||||
NSUInteger statusCode = 0;
|
||||
NSData *_Nullable responseData = nil;
|
||||
if ([error.domain isEqualToString:TSNetworkManagerErrorDomain]) {
|
||||
if ([error.domain isEqualToString:@"SignalServiceKit.RequestMakerUDAuthError"]) {
|
||||
// Try again.
|
||||
OWSLogInfo(@"UD request auth failed; failing over to non-UD request.");
|
||||
[error setIsRetryable:YES];
|
||||
} else if ([error.domain isEqualToString:TSNetworkManagerErrorDomain]) {
|
||||
statusCode = error.code;
|
||||
|
||||
NSError *_Nullable underlyingError = error.userInfo[NSUnderlyingErrorKey];
|
||||
@ -1508,7 +1515,8 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
|
||||
messageSend.hasWebsocketSendFailed = YES;
|
||||
}
|
||||
recipientId:recipientId
|
||||
unidentifiedAccess:messageSend.unidentifiedAccess];
|
||||
unidentifiedAccess:messageSend.unidentifiedAccess
|
||||
canFailoverUDAuth:YES];
|
||||
[[requestMaker makeRequestObjc]
|
||||
.then(^(OWSRequestMakerResult *result) {
|
||||
// We _do not_ want to dispatch to the sendingQueue here; we're
|
||||
|
||||
@ -5,6 +5,11 @@
|
||||
import Foundation
|
||||
import PromiseKit
|
||||
|
||||
@objc
|
||||
public enum RequestMakerUDAuthError: Int, Error {
|
||||
case udAuthFailure
|
||||
}
|
||||
|
||||
public enum RequestMakerError: Error {
|
||||
case websocketRequestError(statusCode : Int, responseData : Data?, underlyingError : Error)
|
||||
}
|
||||
@ -40,18 +45,21 @@ public class RequestMaker: NSObject {
|
||||
private let websocketFailureBlock: WebsocketFailureBlock
|
||||
private let recipientId: String
|
||||
private let unidentifiedAccess: SSKUnidentifiedAccess?
|
||||
private let canFailoverUDAuth: Bool
|
||||
|
||||
@objc
|
||||
public init(requestFactoryBlock : @escaping RequestFactoryBlock,
|
||||
udAuthFailureBlock : @escaping UDAuthFailureBlock,
|
||||
websocketFailureBlock : @escaping WebsocketFailureBlock,
|
||||
recipientId: String,
|
||||
unidentifiedAccess: SSKUnidentifiedAccess?) {
|
||||
unidentifiedAccess: SSKUnidentifiedAccess?,
|
||||
canFailoverUDAuth: Bool) {
|
||||
self.requestFactoryBlock = requestFactoryBlock
|
||||
self.udAuthFailureBlock = udAuthFailureBlock
|
||||
self.websocketFailureBlock = websocketFailureBlock
|
||||
self.recipientId = recipientId
|
||||
self.unidentifiedAccess = unidentifiedAccess
|
||||
self.canFailoverUDAuth = canFailoverUDAuth
|
||||
}
|
||||
|
||||
// MARK: - Dependencies
|
||||
@ -115,8 +123,13 @@ public class RequestMaker: NSObject {
|
||||
// failure), mark recipient as _not_ in UD mode, then retry.
|
||||
self.udManager.setUnidentifiedAccessMode(.disabled, recipientId: self.recipientId)
|
||||
self.udAuthFailureBlock()
|
||||
Logger.info("UD websocket request failed; failing over to non-UD websocket request.")
|
||||
return self.makeRequestInternal(skipUD: true, skipWebsocket: skipWebsocket)
|
||||
if self.canFailoverUDAuth {
|
||||
Logger.info("UD websocket request auth failed; failing over to non-UD websocket request.")
|
||||
return self.makeRequestInternal(skipUD: true, skipWebsocket: skipWebsocket)
|
||||
} else {
|
||||
Logger.info("UD websocket request auth failed; aborting.")
|
||||
throw RequestMakerUDAuthError.udAuthFailure
|
||||
}
|
||||
}
|
||||
break
|
||||
default:
|
||||
@ -141,8 +154,13 @@ public class RequestMaker: NSObject {
|
||||
// failure), mark recipient as _not_ in UD mode, then retry.
|
||||
self.udManager.setUnidentifiedAccessMode(.disabled, recipientId: self.recipientId)
|
||||
self.udAuthFailureBlock()
|
||||
Logger.info("UD REST request failed; failing over to non-UD REST request.")
|
||||
return self.makeRequestInternal(skipUD: true, skipWebsocket: skipWebsocket)
|
||||
if self.canFailoverUDAuth {
|
||||
Logger.info("UD REST request auth failed; failing over to non-UD REST request.")
|
||||
return self.makeRequestInternal(skipUD: true, skipWebsocket: skipWebsocket)
|
||||
} else {
|
||||
Logger.info("UD REST request auth failed; aborting.")
|
||||
throw RequestMakerUDAuthError.udAuthFailure
|
||||
}
|
||||
}
|
||||
break
|
||||
default:
|
||||
|
||||
@ -1,17 +1,20 @@
|
||||
//
|
||||
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
extern NSString *const NSNotificationName_DeviceListUpdateSucceeded;
|
||||
extern NSString *const NSNotificationName_DeviceListUpdateFailed;
|
||||
extern NSString *const NSNotificationName_DeviceListUpdateModifiedDeviceList;
|
||||
|
||||
@class OWSDevice;
|
||||
|
||||
@interface OWSDevicesService : NSObject
|
||||
|
||||
- (void)getDevicesWithSuccess:(void (^)(NSArray<OWSDevice *> *))successCallback
|
||||
failure:(void (^)(NSError *))failureCallback;
|
||||
+ (void)refreshDevices;
|
||||
|
||||
- (void)unlinkDevice:(OWSDevice *)device
|
||||
+ (void)unlinkDevice:(OWSDevice *)device
|
||||
success:(void (^)(void))successCallback
|
||||
failure:(void (^)(NSError *))failureCallback;
|
||||
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
//
|
||||
|
||||
#import "OWSDevicesService.h"
|
||||
#import "NSNotificationCenter+OWS.h"
|
||||
#import "OWSDevice.h"
|
||||
#import "OWSError.h"
|
||||
#import "OWSRequestFactory.h"
|
||||
@ -11,9 +12,47 @@
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
NSString *const NSNotificationName_DeviceListUpdateSucceeded = @"NSNotificationName_DeviceListUpdateSucceeded";
|
||||
NSString *const NSNotificationName_DeviceListUpdateFailed = @"NSNotificationName_DeviceListUpdateFailed";
|
||||
NSString *const NSNotificationName_DeviceListUpdateModifiedDeviceList
|
||||
= @"NSNotificationName_DeviceListUpdateModifiedDeviceList";
|
||||
|
||||
@implementation OWSDevicesService
|
||||
|
||||
- (void)getDevicesWithSuccess:(void (^)(NSArray<OWSDevice *> *))successCallback
|
||||
+ (void)refreshDevices
|
||||
{
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
[self
|
||||
getDevicesWithSuccess:^(NSArray<OWSDevice *> *devices) {
|
||||
// If we have more than one device; we may have a linked device.
|
||||
if (devices.count > 1) {
|
||||
// Setting this flag here shouldn't be necessary, but we do so
|
||||
// because the "cost" is low and it will improve robustness.
|
||||
[OWSDeviceManager.sharedManager setMayHaveLinkedDevices];
|
||||
}
|
||||
|
||||
BOOL didAddOrRemove = [OWSDevice replaceAll:devices];
|
||||
|
||||
[NSNotificationCenter.defaultCenter
|
||||
postNotificationNameAsync:NSNotificationName_DeviceListUpdateSucceeded
|
||||
object:nil];
|
||||
|
||||
if (didAddOrRemove) {
|
||||
[NSNotificationCenter.defaultCenter
|
||||
postNotificationNameAsync:NSNotificationName_DeviceListUpdateModifiedDeviceList
|
||||
object:nil];
|
||||
}
|
||||
}
|
||||
failure:^(NSError *error) {
|
||||
OWSLogError(@"Request device list failed with error: %@", error);
|
||||
|
||||
[NSNotificationCenter.defaultCenter postNotificationNameAsync:NSNotificationName_DeviceListUpdateFailed
|
||||
object:error];
|
||||
}];
|
||||
});
|
||||
}
|
||||
|
||||
+ (void)getDevicesWithSuccess:(void (^)(NSArray<OWSDevice *> *))successCallback
|
||||
failure:(void (^)(NSError *))failureCallback
|
||||
{
|
||||
TSRequest *request = [OWSRequestFactory getDevicesRequest];
|
||||
@ -39,7 +78,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)unlinkDevice:(OWSDevice *)device
|
||||
+ (void)unlinkDevice:(OWSDevice *)device
|
||||
success:(void (^)(void))successCallback
|
||||
failure:(void (^)(NSError *))failureCallback
|
||||
{
|
||||
@ -59,7 +98,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
}];
|
||||
}
|
||||
|
||||
- (NSArray<OWSDevice *> *)parseResponse:(id)responseObject
|
||||
+ (NSArray<OWSDevice *> *)parseResponse:(id)responseObject
|
||||
{
|
||||
if (![responseObject isKindOfClass:[NSDictionary class]]) {
|
||||
OWSLogError(@"Device response was not a dictionary.");
|
||||
|
||||
Loading…
Reference in New Issue
Block a user