mirror of https://github.com/oxen-io/session-ios
Device Manager
* List linked devices * Adding a new device * Removing a device TODO: design on the QRScanner // FREEBIEpull/1/head
parent
84156698c4
commit
ef6784ed95
@ -0,0 +1,9 @@
|
||||
// Copyright © 2016 Open Whisper Systems. All rights reserved.
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface OWSLinkedDevicesTableViewController : UITableViewController <UITableViewDataSource, UITableViewDelegate>
|
||||
|
||||
@property BOOL expectMoreDevices;
|
||||
|
||||
@end
|
@ -0,0 +1,255 @@
|
||||
// Copyright © 2016 Open Whisper Systems. All rights reserved.
|
||||
|
||||
#import "OWSLinkedDevicesTableViewController.h"
|
||||
#import "OWSDeviceTableViewCell.h"
|
||||
#import "OWSLinkDeviceViewController.h"
|
||||
#import <SignalServiceKit/OWSDevice.h>
|
||||
#import <SignalServiceKit/OWSDevicesService.h>
|
||||
|
||||
@interface OWSLinkedDevicesTableViewController ()
|
||||
|
||||
@property NSArray<OWSDevice *> *secondaryDevices;
|
||||
|
||||
@end
|
||||
|
||||
int const OWSLinkedDevicesTableViewControllerSectionExistingDevices = 0;
|
||||
int const OWSLinkedDevicesTableViewControllerSectionAddDevice = 1;
|
||||
|
||||
@implementation OWSLinkedDevicesTableViewController
|
||||
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
|
||||
self.expectMoreDevices = NO;
|
||||
self.tableView.rowHeight = UITableViewAutomaticDimension;
|
||||
self.tableView.estimatedRowHeight = 70;
|
||||
|
||||
self.refreshControl = [UIRefreshControl new];
|
||||
[self.refreshControl addTarget:self action:@selector(refreshDevices) forControlEvents:UIControlEventValueChanged];
|
||||
|
||||
// Since this table is primarily for deleting items...
|
||||
[self setEditing:YES animated:NO];
|
||||
// So we can still tap on "add new device"
|
||||
self.tableView.allowsSelectionDuringEditing = YES;
|
||||
}
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated
|
||||
{
|
||||
[super viewWillAppear:animated];
|
||||
self.secondaryDevices = [OWSDevice secondaryDevices];
|
||||
|
||||
// If we're returning from just adding a device, show that something's happening.
|
||||
if (self.expectMoreDevices) {
|
||||
[self.refreshControl beginRefreshing];
|
||||
[self.tableView setContentOffset:CGPointMake(0, -self.refreshControl.frame.size.height) animated:NO];
|
||||
}
|
||||
[self refreshDevices];
|
||||
}
|
||||
|
||||
- (void)refreshDevices
|
||||
{
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
[[OWSDevicesService new] getDevicesWithSuccess:^(NSArray<OWSDevice *> *devices) {
|
||||
if (devices.count > [OWSDevice numberOfKeysInCollection]) {
|
||||
// Got our new device, we can stop refreshing.
|
||||
self.expectMoreDevices = NO;
|
||||
}
|
||||
[OWSDevice replaceAll:devices];
|
||||
self.secondaryDevices = [OWSDevice secondaryDevices];
|
||||
|
||||
if (self.expectMoreDevices) {
|
||||
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(), ^{
|
||||
[self.refreshControl endRefreshing];
|
||||
[self.tableView reloadData];
|
||||
});
|
||||
}
|
||||
}
|
||||
failure:^(NSError *error) {
|
||||
DDLogError(@"Failed to fetch devices in linkedDevices controller with error: %@", error);
|
||||
|
||||
NSString *alertTitle = NSLocalizedString(
|
||||
@"Failed to update device list.", @"Alert title that can occur when viewing device manager.");
|
||||
|
||||
UIAlertController *alertController =
|
||||
[UIAlertController alertControllerWithTitle:alertTitle
|
||||
message:error.localizedDescription
|
||||
preferredStyle:UIAlertControllerStyleAlert];
|
||||
|
||||
NSString *retryTitle = NSLocalizedString(
|
||||
@"RETRY_BUTTON_TEXT", @"Generic text for button that retries whatever the last action was.");
|
||||
UIAlertAction *retryAction = [UIAlertAction actionWithTitle:retryTitle
|
||||
style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction *action) {
|
||||
[self refreshDevices];
|
||||
}];
|
||||
[alertController addAction:retryAction];
|
||||
|
||||
NSString *dismissTitle
|
||||
= NSLocalizedString(@"DISMISS_BUTTON_TEXT", @"Generic short text for button to dismiss a dialog");
|
||||
UIAlertAction *dismissAction =
|
||||
[UIAlertAction actionWithTitle:dismissTitle style:UIAlertActionStyleCancel handler:nil];
|
||||
[alertController addAction:dismissAction];
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self.refreshControl endRefreshing];
|
||||
[self presentViewController:alertController animated:YES completion:nil];
|
||||
});
|
||||
}];
|
||||
});
|
||||
}
|
||||
|
||||
#pragma mark - Table view data source
|
||||
|
||||
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
|
||||
{
|
||||
switch (section) {
|
||||
case OWSLinkedDevicesTableViewControllerSectionExistingDevices:
|
||||
return (NSInteger)self.secondaryDevices.count;
|
||||
|
||||
case OWSLinkedDevicesTableViewControllerSectionAddDevice:
|
||||
return 1;
|
||||
|
||||
default:
|
||||
DDLogError(@"Unknown section: %ld", (long)section);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
if (indexPath.section == OWSLinkedDevicesTableViewControllerSectionAddDevice) {
|
||||
return [tableView dequeueReusableCellWithIdentifier:@"AddNewDevice" forIndexPath:indexPath];
|
||||
} else if (indexPath.section == OWSLinkedDevicesTableViewControllerSectionExistingDevices) {
|
||||
OWSDeviceTableViewCell *cell =
|
||||
[tableView dequeueReusableCellWithIdentifier:@"ExistingDevice" forIndexPath:indexPath];
|
||||
OWSDevice *device = [self deviceForRowAtIndexPath:indexPath];
|
||||
[cell configureWithDevice:device];
|
||||
return cell;
|
||||
} else {
|
||||
DDLogError(@"Unknown section: %@", indexPath);
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (OWSDevice *)deviceForRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
if (indexPath.section == OWSLinkedDevicesTableViewControllerSectionExistingDevices) {
|
||||
return self.secondaryDevices[(NSUInteger)indexPath.row];
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
return indexPath.section == OWSLinkedDevicesTableViewControllerSectionExistingDevices;
|
||||
}
|
||||
|
||||
- (nullable NSString *)tableView:(UITableView *)tableView
|
||||
titleForDeleteConfirmationButtonForRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
return NSLocalizedString(@"Unlink", "Action title for unlinking a device");
|
||||
}
|
||||
|
||||
- (void)tableView:(UITableView *)tableView
|
||||
commitEditingStyle:(UITableViewCellEditingStyle)editingStyle
|
||||
forRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
if (editingStyle == UITableViewCellEditingStyleDelete) {
|
||||
OWSDevice *device = [self deviceForRowAtIndexPath:indexPath];
|
||||
[self touchedUnlinkControlForDevice:device
|
||||
success:^{
|
||||
DDLogInfo(@"Removing unlinked device with deviceId: %ld", device.deviceId);
|
||||
[device remove];
|
||||
self.secondaryDevices = [OWSDevice secondaryDevices];
|
||||
[tableView deleteRowsAtIndexPaths:@[ indexPath ]
|
||||
withRowAnimation:UITableViewRowAnimationFade];
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)touchedUnlinkControlForDevice:(OWSDevice *)device success:(void (^)())successCallback
|
||||
{
|
||||
NSString *confirmationTitleFormat
|
||||
= NSLocalizedString(@"Unlink \"%@\"?", @"Alert title for confirming device deletion");
|
||||
NSString *confirmationTitle = [NSString stringWithFormat:confirmationTitleFormat, device.name];
|
||||
NSString *confirmationMessage
|
||||
= NSLocalizedString(@"By unlinking this device, it will no longer be able to send or receive messages.",
|
||||
@"Alert description shown to confirm unlinking a device.");
|
||||
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:confirmationTitle
|
||||
message:confirmationMessage
|
||||
preferredStyle:UIAlertControllerStyleAlert];
|
||||
|
||||
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"TXT_CANCEL_TITLE", nil)
|
||||
style:UIAlertActionStyleCancel
|
||||
handler:nil];
|
||||
[alertController addAction:cancelAction];
|
||||
|
||||
UIAlertAction *unlinkAction =
|
||||
[UIAlertAction actionWithTitle:NSLocalizedString(@"Unlink", "Action title for unlinking a device")
|
||||
style:UIAlertActionStyleDestructive
|
||||
handler:^(UIAlertAction *action) {
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
[self unlinkDevice:device success:successCallback];
|
||||
});
|
||||
}];
|
||||
[alertController addAction:unlinkAction];
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self presentViewController:alertController animated:YES completion:nil];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)unlinkDevice:(OWSDevice *)device success:(void (^)())successCallback
|
||||
{
|
||||
[[OWSDevicesService new] unlinkDevice:device
|
||||
success:successCallback
|
||||
failure:^(NSError *error) {
|
||||
NSString *title = NSLocalizedString(@"Signal was unable to delete your device.",
|
||||
@"Alert title when unlinking device fails");
|
||||
UIAlertController *alertController =
|
||||
[UIAlertController alertControllerWithTitle:title
|
||||
message:error.localizedDescription
|
||||
preferredStyle:UIAlertControllerStyleAlert];
|
||||
|
||||
UIAlertAction *retryAction =
|
||||
[UIAlertAction actionWithTitle:NSLocalizedString(@"RETRY_BUTTON_TEXT", nil)
|
||||
style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction *aaction) {
|
||||
[self unlinkDevice:device success:successCallback];
|
||||
}];
|
||||
[alertController addAction:retryAction];
|
||||
|
||||
UIAlertAction *cancelRetryAction =
|
||||
[UIAlertAction actionWithTitle:NSLocalizedString(@"TXT_CANCEL_TITLE", nil)
|
||||
style:UIAlertActionStyleCancel
|
||||
handler:nil];
|
||||
[alertController addAction:cancelRetryAction];
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self presentViewController:alertController animated:YES completion:nil];
|
||||
});
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
|
||||
{
|
||||
if ([segue.destinationViewController isKindOfClass:[OWSLinkDeviceViewController class]]) {
|
||||
OWSLinkDeviceViewController *controller = (OWSLinkDeviceViewController *)segue.destinationViewController;
|
||||
controller.linkedDevicesTableViewController = self;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
@ -0,0 +1,18 @@
|
||||
// Copyright © 2016 Open Whisper Systems. All rights reserved.
|
||||
|
||||
#import <SignalServiceKit/OWSDevice.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface OWSDeviceTableViewCell : UITableViewCell
|
||||
|
||||
@property (strong, nonatomic) IBOutlet UILabel *nameLabel;
|
||||
@property (strong, nonatomic) IBOutlet UILabel *linkedLabel;
|
||||
@property (strong, nonatomic) IBOutlet UILabel *lastSeenLabel;
|
||||
|
||||
- (void)configureWithDevice:(OWSDevice *)device;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
@ -0,0 +1,26 @@
|
||||
// Copyright © 2016 Open Whisper Systems. All rights reserved.
|
||||
|
||||
#import "OWSDeviceTableViewCell.h"
|
||||
#import "DateUtil.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@implementation OWSDeviceTableViewCell
|
||||
|
||||
- (void)configureWithDevice:(OWSDevice *)device
|
||||
{
|
||||
self.nameLabel.text = device.name;
|
||||
|
||||
NSString *linkedFormatString = NSLocalizedString(@"Linked: %@", @"{{Short Date}} when device was linked.");
|
||||
self.linkedLabel.text =
|
||||
[NSString stringWithFormat:linkedFormatString, [DateUtil.dateFormatter stringFromDate:device.createdAt]];
|
||||
|
||||
NSString *lastSeenFormatString
|
||||
= NSLocalizedString(@"Last active: %@", @"{{Short Date}} when device last communicated with Signal Server.");
|
||||
self.lastSeenLabel.text =
|
||||
[NSString stringWithFormat:lastSeenFormatString, [DateUtil.dateFormatter stringFromDate:device.createdAt]];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
Binary file not shown.
Loading…
Reference in New Issue