Skip to content

Commit ab6ee56

Browse files
committed
Merge branch 'editing'
2 parents 8fcbe74 + 8a8fae1 commit ab6ee56

File tree

6 files changed

+242
-34
lines changed

6 files changed

+242
-34
lines changed

Authenticator.xcodeproj/project.pbxproj

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
C958CC51182A1FDD00DD47D0 /* OTPTokenType.m in Sources */ = {isa = PBXBuildFile; fileRef = C958CC50182A1FDD00DD47D0 /* OTPTokenType.m */; };
2424
C958CC54183043A600DD47D0 /* OTPTokenSerializationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = C958CC53183043A600DD47D0 /* OTPTokenSerializationTests.m */; };
2525
C9745D0B17DA5861008B6E23 /* UIAlertView+Blocks.m in Sources */ = {isa = PBXBuildFile; fileRef = C9745D0A17DA5861008B6E23 /* UIAlertView+Blocks.m */; };
26+
C9777A021908064500B4A7F5 /* OTPTokenEditViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C9777A011908064500B4A7F5 /* OTPTokenEditViewController.m */; };
2627
C97B68C717D9226D005D1FE0 /* Settings.bundle in Resources */ = {isa = PBXBuildFile; fileRef = C97B68C617D9226D005D1FE0 /* Settings.bundle */; };
2728
C99069D1180CBAC900BAEF53 /* OTPScannerViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C99069D0180CBAC900BAEF53 /* OTPScannerViewController.m */; };
2829
C99069D4180E07A100BAEF53 /* OTPTokenEntryViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C99069D3180E07A100BAEF53 /* OTPTokenEntryViewController.m */; };
@@ -83,6 +84,8 @@
8384
C9745D0917DA5861008B6E23 /* UIAlertView+Blocks.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIAlertView+Blocks.h"; sourceTree = "<group>"; };
8485
C9745D0A17DA5861008B6E23 /* UIAlertView+Blocks.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIAlertView+Blocks.m"; sourceTree = "<group>"; };
8586
C9776E3318518801003D53CB /* LICENSE.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = LICENSE.txt; sourceTree = "<group>"; };
87+
C9777A001908064500B4A7F5 /* OTPTokenEditViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OTPTokenEditViewController.h; sourceTree = "<group>"; };
88+
C9777A011908064500B4A7F5 /* OTPTokenEditViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OTPTokenEditViewController.m; sourceTree = "<group>"; };
8689
C97B68C617D9226D005D1FE0 /* Settings.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = Settings.bundle; sourceTree = "<group>"; };
8790
C99069CF180CBAC900BAEF53 /* OTPScannerViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OTPScannerViewController.h; sourceTree = "<group>"; };
8891
C99069D0180CBAC900BAEF53 /* OTPScannerViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OTPScannerViewController.m; sourceTree = "<group>"; };
@@ -227,6 +230,8 @@
227230
C99069D5180E09D700BAEF53 /* OTPTokenSourceDelegate.h */,
228231
C99069D2180E07A100BAEF53 /* OTPTokenEntryViewController.h */,
229232
C99069D3180E07A100BAEF53 /* OTPTokenEntryViewController.m */,
233+
C9777A001908064500B4A7F5 /* OTPTokenEditViewController.h */,
234+
C9777A011908064500B4A7F5 /* OTPTokenEditViewController.m */,
230235
C9D6C8401906BD6F004F0E08 /* Cells */,
231236
C99069CF180CBAC900BAEF53 /* OTPScannerViewController.h */,
232237
C99069D0180CBAC900BAEF53 /* OTPScannerViewController.m */,
@@ -475,6 +480,7 @@
475480
C99069D4180E07A100BAEF53 /* OTPTokenEntryViewController.m in Sources */,
476481
C9ABDACF17DD3CF500A86AB5 /* OTPTokenCell.m in Sources */,
477482
C9044FCF18224D32002C3C3F /* OTPAlgorithm.m in Sources */,
483+
C9777A021908064500B4A7F5 /* OTPTokenEditViewController.m in Sources */,
478484
C9044FDC1824E711002C3C3F /* NSDictionary+QueryString.m in Sources */,
479485
);
480486
runOnlyForDeploymentPostprocessing = 0;

Authenticator/Classes/OTPTokenCell.m

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
//
2424

2525
#import "OTPTokenCell.h"
26-
@import MobileCoreServices;
2726
#import "OTPToken+Persistence.h"
2827
#import "OTPToken+Generation.h"
2928

@@ -119,25 +118,6 @@ - (void)layoutSubviews
119118
}
120119

121120

122-
#pragma mark - Copying
123-
124-
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
125-
{
126-
[super setSelected:selected animated:animated];
127-
128-
if (selected) {
129-
[self copyPassword];
130-
}
131-
}
132-
133-
- (void)copyPassword
134-
{
135-
[[UIPasteboard generalPasteboard] setValue:self.token.password forPasteboardType:(__bridge NSString *)kUTTypeUTF8PlainText];
136-
137-
[SVProgressHUD showSuccessWithStatus:@"Copied"];
138-
}
139-
140-
141121
#pragma mark - KVO
142122

143123
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
//
2+
// OTPTokenEditViewController.h
3+
// Authenticator
4+
//
5+
// Copyright (c) 2014 Matt Rubin
6+
//
7+
// Permission is hereby granted, free of charge, to any person obtaining a copy of
8+
// this software and associated documentation files (the "Software"), to deal in
9+
// the Software without restriction, including without limitation the rights to
10+
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
11+
// the Software, and to permit persons to whom the Software is furnished to do so,
12+
// subject to the following conditions:
13+
//
14+
// The above copyright notice and this permission notice shall be included in all
15+
// copies or substantial portions of the Software.
16+
//
17+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
19+
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
20+
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
21+
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22+
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23+
//
24+
25+
#import "OTPTokenEntryViewController.h"
26+
27+
28+
@protocol OTPTokenEditorDelegate <OTPTokenSourceDelegate>
29+
30+
- (void)tokenEditor:(id)tokenEditor didEditToken:(OTPToken *)token;
31+
32+
@end
33+
34+
35+
@interface OTPTokenEditViewController : OTPTokenEntryViewController
36+
37+
@property (nonatomic, weak) id <OTPTokenEditorDelegate> delegate;
38+
39+
@property (nonatomic, strong) OTPToken *token;
40+
41+
@end
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
//
2+
// OTPTokenEditViewController.m
3+
// Authenticator
4+
//
5+
// Copyright (c) 2014 Matt Rubin
6+
//
7+
// Permission is hereby granted, free of charge, to any person obtaining a copy of
8+
// this software and associated documentation files (the "Software"), to deal in
9+
// the Software without restriction, including without limitation the rights to
10+
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
11+
// the Software, and to permit persons to whom the Software is furnished to do so,
12+
// subject to the following conditions:
13+
//
14+
// The above copyright notice and this permission notice shall be included in all
15+
// copies or substantial portions of the Software.
16+
//
17+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
19+
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
20+
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
21+
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22+
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23+
//
24+
25+
#import "OTPTokenEditViewController.h"
26+
#import "OTPTextFieldCell.h"
27+
#import "OTPToken+Persistence.h"
28+
29+
30+
@interface OTPTokenEntryViewController () <UITextFieldDelegate>
31+
32+
@property (nonatomic, strong) UIBarButtonItem *doneButtonItem;
33+
34+
@property (nonatomic, strong) OTPTextFieldCell *issuerCell;
35+
@property (nonatomic, strong) OTPTextFieldCell *accountNameCell;
36+
37+
@end
38+
39+
40+
@implementation OTPTokenEditViewController
41+
42+
- (void)viewDidLoad
43+
{
44+
[super viewDidLoad];
45+
46+
self.title = @"Edit Token";
47+
48+
// Override the done button
49+
self.doneButtonItem.action = @selector(updateToken);
50+
51+
self.accountNameCell.textField.returnKeyType = UIReturnKeyDone;
52+
}
53+
54+
- (void)viewDidAppear:(BOOL)animated
55+
{
56+
[super viewDidAppear:animated];
57+
[self.issuerCell.textField becomeFirstResponder];
58+
}
59+
60+
61+
#pragma mark - Token
62+
63+
- (void)setToken:(OTPToken *)token
64+
{
65+
if (_token != token) {
66+
_token = token;
67+
}
68+
self.issuerCell.textField.text = token.issuer;
69+
self.accountNameCell.textField.text = token.name;
70+
}
71+
72+
- (void)updateToken
73+
{
74+
if (!self.formIsValid) return;
75+
76+
if (![self.token.name isEqualToString:self.accountNameCell.textField.text] ||
77+
![self.token.issuer isEqualToString:self.issuerCell.textField.text]) {
78+
self.token.name = self.accountNameCell.textField.text;
79+
self.token.issuer = self.issuerCell.textField.text;
80+
[self.token saveToKeychain];
81+
}
82+
83+
id <OTPTokenEditorDelegate> delegate = self.delegate;
84+
[delegate tokenEditor:self didEditToken:self.token];
85+
}
86+
87+
88+
#pragma mark - UITableViewDataSource
89+
90+
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
91+
{
92+
return 2;
93+
}
94+
95+
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
96+
{
97+
switch (indexPath.row) {
98+
case 0:
99+
return self.issuerCell;
100+
case 1:
101+
return self.accountNameCell;
102+
}
103+
return nil;
104+
}
105+
106+
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
107+
{
108+
return 74;
109+
}
110+
111+
112+
#pragma mark - UITextFieldDelegate
113+
114+
- (BOOL)textFieldShouldReturn:(UITextField *)textField
115+
{
116+
if (textField == self.issuerCell.textField) {
117+
[self.accountNameCell.textField becomeFirstResponder];
118+
} else if (textField == self.accountNameCell.textField) {
119+
[textField resignFirstResponder];
120+
[self updateToken];
121+
}
122+
return NO;
123+
}
124+
125+
126+
#pragma mark - Validation
127+
128+
- (BOOL)formIsValid
129+
{
130+
return (self.issuerCell.textField.text.length ||
131+
self.accountNameCell.textField.text.length);
132+
}
133+
134+
@end

Authenticator/Classes/OTPTokenEntryViewController.m

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,13 @@ - (void)viewDidLoad
5555
self.title = @"Add Token";
5656
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self action:@selector(cancel)];
5757
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(createToken)];
58-
5958
self.doneButtonItem = self.navigationItem.rightBarButtonItem;
60-
self.doneButtonItem.enabled = NO;
59+
}
60+
61+
- (void)viewWillAppear:(BOOL)animated
62+
{
63+
[super viewWillAppear:animated];
64+
[self validateForm];
6165
}
6266

6367

@@ -70,9 +74,7 @@ - (void)cancel
7074

7175
- (void)createToken
7276
{
73-
if (!self.accountNameCell.textField.text.length || !self.secretKeyCell.textField.text.length) {
74-
return;
75-
}
77+
if (!self.formIsValid) return;
7678

7779
NSData *secret = [NSData dataWithBase32String:self.secretKeyCell.textField.text];
7880

@@ -219,15 +221,27 @@ - (BOOL)textFieldShouldReturn:(UITextField *)textField
219221

220222
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
221223
{
222-
// Ensure both fields (will) have text in them
223-
NSString *newText = [[textField.text mutableCopy] stringByReplacingCharactersInRange:range withString:string];
224-
if (textField == self.accountNameCell.textField) {
225-
self.doneButtonItem.enabled = newText.length && self.secretKeyCell.textField.text.length;
226-
} else if (textField == self.secretKeyCell.textField) {
227-
self.doneButtonItem.enabled = newText.length && self.accountNameCell.textField.text.length;
228-
}
224+
// Delay validation slightly to allow the text field time to commit the new value
225+
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.01 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
226+
[self validateForm];
227+
});
229228

230229
return YES;
231230
}
232231

232+
233+
#pragma mark - Validation
234+
235+
- (void)validateForm
236+
{
237+
self.doneButtonItem.enabled = self.formIsValid;
238+
}
239+
240+
- (BOOL)formIsValid
241+
{
242+
return ((self.issuerCell.textField.text.length ||
243+
self.accountNameCell.textField.text.length) &&
244+
self.secretKeyCell.textField.text.length);
245+
}
246+
233247
@end

Authenticator/Classes/OTPTokenListViewController.m

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,14 @@
2626
#import "OTPTokenManager.h"
2727
#import "OTPTokenCell.h"
2828
#import "OTPProgressRing.h"
29-
#import "OTPToken.h"
29+
#import "OTPToken+Generation.h"
3030
#import "OTPTokenEntryViewController.h"
3131
#import "OTPScannerViewController.h"
32+
@import MobileCoreServices;
33+
#import "OTPTokenEditViewController.h"
3234

3335

34-
@interface OTPTokenListViewController ()
36+
@interface OTPTokenListViewController () <OTPTokenEditorDelegate>
3537

3638
@property (nonatomic, strong) OTPTokenManager *tokenManager;
3739
@property (nonatomic, strong) OTPProgressRing *ring;
@@ -73,6 +75,8 @@ - (void)viewDidLoad
7375
self.navigationController.toolbarHidden = NO;
7476

7577
self.tableView.contentInset = UIEdgeInsetsMake(10, 0, 0, 0);
78+
self.tableView.allowsSelectionDuringEditing = YES;
79+
7680
[self update];
7781
}
7882

@@ -137,6 +141,26 @@ - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPa
137141
return 85;
138142
}
139143

144+
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
145+
{
146+
if (self.isEditing) {
147+
self.editing = NO;
148+
149+
OTPTokenEditViewController *editController = [OTPTokenEditViewController new];
150+
editController.token = self.tokenManager.tokens[(NSUInteger)indexPath.row];
151+
editController.delegate = self;
152+
153+
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:editController];
154+
navController.navigationBar.translucent = NO;
155+
156+
[self presentViewController:navController animated:YES completion:nil];
157+
} else {
158+
OTPToken *token = self.tokenManager.tokens[(NSUInteger)indexPath.row];
159+
[[UIPasteboard generalPasteboard] setValue:token.password forPasteboardType:(__bridge NSString *)kUTTypeUTF8PlainText];
160+
[SVProgressHUD showSuccessWithStatus:@"Copied"];
161+
}
162+
}
163+
140164

141165
#pragma mark - Target actions
142166

@@ -169,4 +193,13 @@ - (void)tokenSource:(id)tokenSource didCreateToken:(OTPToken *)token
169193
}
170194
}
171195

196+
197+
#pragma mark - OTPTokenEditorDelegate
198+
199+
- (void)tokenEditor:(id)tokenEditor didEditToken:(OTPToken *)token
200+
{
201+
[self dismissViewControllerAnimated:YES completion:nil];
202+
[self.tableView reloadData];
203+
}
204+
172205
@end

0 commit comments

Comments
 (0)