Device manager fixes

* Avoid intermittent crash in device manager via YapDatabaseModified
* Properly align refresh text when expecting new device
* Avoid glitchy activityIndicator while polling
* Expose edit mode toggle

Much of the code changes here were in the corresponding SSK update.

// FREEBIE
pull/1/head
Michael Kirk 9 years ago
parent dee26e6e0a
commit 7c3a07960f

@ -42,7 +42,7 @@ PODS:
- CocoaLumberjack (~> 2.0) - CocoaLumberjack (~> 2.0)
- ProtocolBuffers (1.9.11) - ProtocolBuffers (1.9.11)
- Reachability (3.2) - Reachability (3.2)
- SAMKeychain (1.5.0) - SAMKeychain (1.5.1)
- SCWaveformView (1.0.0) - SCWaveformView (1.0.0)
- SignalServiceKit (0.2.0): - SignalServiceKit (0.2.0):
- '25519' - '25519'
@ -131,7 +131,7 @@ EXTERNAL SOURCES:
CHECKOUT OPTIONS: CHECKOUT OPTIONS:
SignalServiceKit: SignalServiceKit:
:commit: e61d89666e408d3a9d124f897ee2bf274b45712e :commit: 0eec84feb71955147cfebaebc5dd0aad5adf0cda
:git: https://github.com/WhisperSystems/SignalServiceKit.git :git: https://github.com/WhisperSystems/SignalServiceKit.git
SocketRocket: SocketRocket:
:commit: 8096fef47d582bff8ae3758c9ae7af1d55ea53d6 :commit: 8096fef47d582bff8ae3758c9ae7af1d55ea53d6
@ -153,7 +153,7 @@ SPEC CHECKSUMS:
PastelogKit: 7b475be4cf577713506a943dd940bcc0499c8bca PastelogKit: 7b475be4cf577713506a943dd940bcc0499c8bca
ProtocolBuffers: d509225eb2ea43d9582a59e94348fcf86e2abd65 ProtocolBuffers: d509225eb2ea43d9582a59e94348fcf86e2abd65
Reachability: 33e18b67625424e47b6cde6d202dce689ad7af96 Reachability: 33e18b67625424e47b6cde6d202dce689ad7af96
SAMKeychain: 1fc9ae02f576365395758b12888c84704eebc423 SAMKeychain: 6b04852a20684167aea97bdf8ba12c95d3616376
SCWaveformView: 52a96750255d817e300565a80c81fb643e233e07 SCWaveformView: 52a96750255d817e300565a80c81fb643e233e07
SignalServiceKit: 4e7a552635e10f4d94f0a047fc6554e932340b30 SignalServiceKit: 4e7a552635e10f4d94f0a047fc6554e932340b30
SocketRocket: 3f77ec2104cc113add553f817ad90a77114f5d43 SocketRocket: 3f77ec2104cc113add553f817ad90a77114f5d43

@ -21,7 +21,7 @@
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>APPL</string> <string>APPL</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>2.4.2</string> <string>2.5.0</string>
<key>CFBundleSignature</key> <key>CFBundleSignature</key>
<string>????</string> <string>????</string>
<key>CFBundleURLTypes</key> <key>CFBundleURLTypes</key>
@ -38,7 +38,7 @@
</dict> </dict>
</array> </array>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>2.4.2.0</string> <string>2.5.0.1</string>
<key>ITSAppUsesNonExemptEncryption</key> <key>ITSAppUsesNonExemptEncryption</key>
<false/> <false/>
<key>LOGS_EMAIL</key> <key>LOGS_EMAIL</key>

@ -303,6 +303,7 @@ typedef enum : NSUInteger {
[self initializeTitleLabelGestureRecognizer]; [self initializeTitleLabelGestureRecognizer];
// TODO prep this sync one time before view loads so we don't have to repaint.
[self updateBackButtonAsync]; [self updateBackButtonAsync];
[self.inputToolbar.contentView.textView endEditing:YES]; [self.inputToolbar.contentView.textView endEditing:YES];

@ -77,7 +77,7 @@ NS_ASSUME_NONNULL_BEGIN
[provisioner provisionWithSuccess:^{ [provisioner provisionWithSuccess:^{
DDLogInfo(@"Successfully provisioned device."); DDLogInfo(@"Successfully provisioned device.");
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async(dispatch_get_main_queue(), ^{
self.linkedDevicesTableViewController.expectMoreDevices = YES; [self.linkedDevicesTableViewController expectMoreDevices];
[self.navigationController popToViewController:self.linkedDevicesTableViewController animated:YES]; [self.navigationController popToViewController:self.linkedDevicesTableViewController animated:YES];
}); });
} }

@ -4,6 +4,9 @@
@interface OWSLinkedDevicesTableViewController : UITableViewController <UITableViewDataSource, UITableViewDelegate> @interface OWSLinkedDevicesTableViewController : UITableViewController <UITableViewDataSource, UITableViewDelegate>
@property BOOL expectMoreDevices; /**
* This is used to show the user there is a device provisioning in-progress.
*/
- (void)expectMoreDevices;
@end @end

@ -5,10 +5,20 @@
#import "OWSLinkDeviceViewController.h" #import "OWSLinkDeviceViewController.h"
#import <SignalServiceKit/OWSDevice.h> #import <SignalServiceKit/OWSDevice.h>
#import <SignalServiceKit/OWSDevicesService.h> #import <SignalServiceKit/OWSDevicesService.h>
#import <SignalServiceKit/TSDatabaseView.h>
#import <SignalServiceKit/TSStorageManager.h>
#import <YapDatabase/YapDatabaseTransaction.h>
#import <YapDatabase/YapDatabaseViewConnection.h>
#import <YapDatabase/YapDatabaseViewMappings.h>
NS_ASSUME_NONNULL_BEGIN
@interface OWSLinkedDevicesTableViewController () @interface OWSLinkedDevicesTableViewController ()
@property NSArray<OWSDevice *> *secondaryDevices; @property YapDatabaseConnection *dbConnection;
@property YapDatabaseViewMappings *deviceMappings;
@property NSTimer *pollingRefreshTimer;
@property BOOL isExpectingMoreDevices;
@end @end
@ -17,19 +27,36 @@ int const OWSLinkedDevicesTableViewControllerSectionAddDevice = 1;
@implementation OWSLinkedDevicesTableViewController @implementation OWSLinkedDevicesTableViewController
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)viewDidLoad - (void)viewDidLoad
{ {
[super viewDidLoad]; [super viewDidLoad];
self.isExpectingMoreDevices = NO;
self.expectMoreDevices = NO;
self.tableView.rowHeight = UITableViewAutomaticDimension; self.tableView.rowHeight = UITableViewAutomaticDimension;
self.tableView.estimatedRowHeight = 70; self.tableView.estimatedRowHeight = 70;
self.dbConnection = [[TSStorageManager sharedManager] newDatabaseConnection];
[self.dbConnection beginLongLivedReadTransaction];
self.deviceMappings = [[YapDatabaseViewMappings alloc] initWithGroups:@[ TSSecondaryDevicesGroup ]
view:TSSecondaryDevicesDatabaseViewExtensionName];
[self.dbConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
[self.deviceMappings updateWithTransaction:transaction];
}];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(yapDatabaseModified:)
name:YapDatabaseModifiedNotification
object:self.dbConnection.database];
self.refreshControl = [UIRefreshControl new]; self.refreshControl = [UIRefreshControl new];
[self.refreshControl addTarget:self action:@selector(refreshDevices) forControlEvents:UIControlEventValueChanged]; [self.refreshControl addTarget:self action:@selector(refreshDevices) forControlEvents:UIControlEventValueChanged];
// Since this table is primarily for deleting items... [self setupEditButton];
[self setEditing:YES animated:NO];
// So we can still tap on "add new device" // So we can still tap on "add new device"
self.tableView.allowsSelectionDuringEditing = YES; self.tableView.allowsSelectionDuringEditing = YES;
} }
@ -37,37 +64,78 @@ int const OWSLinkedDevicesTableViewControllerSectionAddDevice = 1;
- (void)viewWillAppear:(BOOL)animated - (void)viewWillAppear:(BOOL)animated
{ {
[super viewWillAppear:animated]; [super viewWillAppear:animated];
self.secondaryDevices = [OWSDevice secondaryDevices]; [self refreshDevices];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[self.pollingRefreshTimer invalidate];
}
// If we're returning from just adding a device, show that something's happening. // Don't show edit button for an empty table
if (self.expectMoreDevices) { - (void)setupEditButton
{
[self.dbConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
if ([OWSDevice hasSecondaryDevicesWithTransaction:transaction]) {
self.navigationItem.rightBarButtonItem = self.editButtonItem;
} else {
self.navigationItem.rightBarButtonItem = nil;
}
}];
}
- (void)expectMoreDevices
{
self.isExpectingMoreDevices = YES;
// When you delete and re-add a device, you will be returned to this view in editing mode, making your newly
// added device appear with a delete icon. Probably not what you want.
self.editing = NO;
__weak typeof(self) wself = self;
self.pollingRefreshTimer = [NSTimer scheduledTimerWithTimeInterval:(10.0)
target:wself
selector:@selector(refreshDevices)
userInfo:nil
repeats:YES];
NSString *progressText = NSLocalizedString(@"Complete setup on Signal Desktop.",
@"Activity indicator title, shown upon returning to the device manager, "
@"until you complete the provisioning process on desktop");
NSAttributedString *progressTitle = [[NSAttributedString alloc] initWithString:progressText];
// HACK to get refreshControl title to align properly.
self.refreshControl.attributedTitle = progressTitle;
[self.refreshControl endRefreshing];
dispatch_async(dispatch_get_main_queue(), ^{
self.refreshControl.attributedTitle = progressTitle;
[self.refreshControl beginRefreshing]; [self.refreshControl beginRefreshing];
// Needed to show refresh control programatically
[self.tableView setContentOffset:CGPointMake(0, -self.refreshControl.frame.size.height) animated:NO]; [self.tableView setContentOffset:CGPointMake(0, -self.refreshControl.frame.size.height) animated:NO];
} });
[self refreshDevices]; // END HACK to get refreshControl title to align properly.
} }
- (void)refreshDevices - (void)refreshDevices
{ {
__weak typeof(self) wself = self;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[[OWSDevicesService new] getDevicesWithSuccess:^(NSArray<OWSDevice *> *devices) { [[OWSDevicesService new] getDevicesWithSuccess:^(NSArray<OWSDevice *> *devices) {
if (devices.count > [OWSDevice numberOfKeysInCollection]) { if (devices.count > [OWSDevice numberOfKeysInCollection]) {
// Got our new device, we can stop refreshing. // Got our new device, we can stop refreshing.
self.expectMoreDevices = NO; wself.isExpectingMoreDevices = NO;
[wself.pollingRefreshTimer invalidate];
dispatch_async(dispatch_get_main_queue(), ^{
wself.refreshControl.attributedTitle = nil;
});
} }
[OWSDevice replaceAll:devices]; [OWSDevice replaceAll:devices];
self.secondaryDevices = [OWSDevice secondaryDevices];
if (self.expectMoreDevices) { if (!self.isExpectingMoreDevices) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 10 * NSEC_PER_SEC),
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
^{
[self refreshDevices];
});
} else {
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async(dispatch_get_main_queue(), ^{
[self.refreshControl endRefreshing]; [wself.refreshControl endRefreshing];
[self.tableView reloadData];
}); });
} }
} }
@ -87,7 +155,7 @@ int const OWSLinkedDevicesTableViewControllerSectionAddDevice = 1;
UIAlertAction *retryAction = [UIAlertAction actionWithTitle:retryTitle UIAlertAction *retryAction = [UIAlertAction actionWithTitle:retryTitle
style:UIAlertActionStyleDefault style:UIAlertActionStyleDefault
handler:^(UIAlertAction *action) { handler:^(UIAlertAction *action) {
[self refreshDevices]; [wself refreshDevices];
}]; }];
[alertController addAction:retryAction]; [alertController addAction:retryAction];
@ -98,8 +166,8 @@ int const OWSLinkedDevicesTableViewControllerSectionAddDevice = 1;
[alertController addAction:dismissAction]; [alertController addAction:dismissAction];
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async(dispatch_get_main_queue(), ^{
[self.refreshControl endRefreshing]; [wself.refreshControl endRefreshing];
[self presentViewController:alertController animated:YES completion:nil]; [wself presentViewController:alertController animated:YES completion:nil];
}); });
}]; }];
}); });
@ -107,6 +175,57 @@ int const OWSLinkedDevicesTableViewControllerSectionAddDevice = 1;
#pragma mark - Table view data source #pragma mark - Table view data source
- (void)yapDatabaseModified:(NSNotification *)notification
{
NSArray *notifications = [self.dbConnection beginLongLivedReadTransaction];
[self setupEditButton];
if ([notifications count] == 0) {
return; // already processed commit
}
NSArray *rowChanges;
[[self.dbConnection ext:TSSecondaryDevicesDatabaseViewExtensionName] getSectionChanges:nil
rowChanges:&rowChanges
forNotifications:notifications
withMappings:self.deviceMappings];
if (rowChanges.count == 0) {
// There aren't any changes that affect our tableView!
return;
}
[self.tableView beginUpdates];
for (YapDatabaseViewRowChange *rowChange in rowChanges) {
switch (rowChange.type) {
case YapDatabaseViewChangeDelete: {
[self.tableView deleteRowsAtIndexPaths:@[ rowChange.indexPath ]
withRowAnimation:UITableViewRowAnimationAutomatic];
break;
}
case YapDatabaseViewChangeInsert: {
[self.tableView insertRowsAtIndexPaths:@[ rowChange.newIndexPath ]
withRowAnimation:UITableViewRowAnimationAutomatic];
break;
}
case YapDatabaseViewChangeMove: {
[self.tableView deleteRowsAtIndexPaths:@[ rowChange.indexPath ]
withRowAnimation:UITableViewRowAnimationAutomatic];
[self.tableView insertRowsAtIndexPaths:@[ rowChange.newIndexPath ]
withRowAnimation:UITableViewRowAnimationAutomatic];
break;
}
case YapDatabaseViewChangeUpdate: {
[self.tableView reloadRowsAtIndexPaths:@[ rowChange.indexPath ]
withRowAnimation:UITableViewRowAnimationNone];
break;
}
}
}
[self.tableView endUpdates];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{ {
return 2; return 2;
@ -116,8 +235,7 @@ int const OWSLinkedDevicesTableViewControllerSectionAddDevice = 1;
{ {
switch (section) { switch (section) {
case OWSLinkedDevicesTableViewControllerSectionExistingDevices: case OWSLinkedDevicesTableViewControllerSectionExistingDevices:
return (NSInteger)self.secondaryDevices.count; return (NSInteger)[self.deviceMappings numberOfItemsInSection:(NSUInteger)section];
case OWSLinkedDevicesTableViewControllerSectionAddDevice: case OWSLinkedDevicesTableViewControllerSectionAddDevice:
return 1; return 1;
@ -143,10 +261,17 @@ int const OWSLinkedDevicesTableViewControllerSectionAddDevice = 1;
} }
} }
- (OWSDevice *)deviceForRowAtIndexPath:(NSIndexPath *)indexPath - (nullable OWSDevice *)deviceForRowAtIndexPath:(NSIndexPath *)indexPath
{ {
if (indexPath.section == OWSLinkedDevicesTableViewControllerSectionExistingDevices) { if (indexPath.section == OWSLinkedDevicesTableViewControllerSectionExistingDevices) {
return self.secondaryDevices[(NSUInteger)indexPath.row]; __block OWSDevice *device;
[self.dbConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
device = [[transaction extension:TSSecondaryDevicesDatabaseViewExtensionName]
objectAtIndexPath:indexPath
withMappings:self.deviceMappings];
}];
return device;
} }
return nil; return nil;
@ -173,9 +298,6 @@ int const OWSLinkedDevicesTableViewControllerSectionAddDevice = 1;
success:^{ success:^{
DDLogInfo(@"Removing unlinked device with deviceId: %ld", device.deviceId); DDLogInfo(@"Removing unlinked device with deviceId: %ld", device.deviceId);
[device remove]; [device remove];
self.secondaryDevices = [OWSDevice secondaryDevices];
[tableView deleteRowsAtIndexPaths:@[ indexPath ]
withRowAnimation:UITableViewRowAnimationFade];
}]; }];
} }
} }
@ -244,7 +366,7 @@ int const OWSLinkedDevicesTableViewControllerSectionAddDevice = 1;
}]; }];
} }
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(nullable id)sender
{ {
if ([segue.destinationViewController isKindOfClass:[OWSLinkDeviceViewController class]]) { if ([segue.destinationViewController isKindOfClass:[OWSLinkDeviceViewController class]]) {
OWSLinkDeviceViewController *controller = (OWSLinkDeviceViewController *)segue.destinationViewController; OWSLinkDeviceViewController *controller = (OWSLinkDeviceViewController *)segue.destinationViewController;
@ -253,3 +375,5 @@ int const OWSLinkedDevicesTableViewControllerSectionAddDevice = 1;
} }
@end @end
NS_ASSUME_NONNULL_END

@ -9,7 +9,7 @@ NS_ASSUME_NONNULL_BEGIN
- (void)configureWithDevice:(OWSDevice *)device - (void)configureWithDevice:(OWSDevice *)device
{ {
self.nameLabel.text = device.name; self.nameLabel.text = device.displayName;
NSString *linkedFormatString = NSLocalizedString(@"Linked: %@", @"{{Short Date}} when device was linked."); NSString *linkedFormatString = NSLocalizedString(@"Linked: %@", @"{{Short Date}} when device was linked.");
self.linkedLabel.text = self.linkedLabel.text =

Loading…
Cancel
Save