2
0

Ensure alerts are triggered on main thread.

This commit is contained in:
Maarten Billemont 2020-03-05 11:29:10 -05:00
parent 9c3e272849
commit ee16c4a66d
9 changed files with 148 additions and 135 deletions

View File

@ -161,13 +161,14 @@ PearlAssociatedObjectProperty( NSMutableArray*, ProductObservers, productObserve
MPError( error, @"StoreKit request (%@) failed.", request ); MPError( error, @"StoreKit request (%@) failed.", request );
#if TARGET_OS_IPHONE #if TARGET_OS_IPHONE
UIAlertController *controller = [UIAlertController alertControllerWithTitle:@"Purchase Failed" message: PearlMainQueue( ^{
strf( @"%@\n\n%@", error.localizedDescription, UIAlertController *controller = [UIAlertController alertControllerWithTitle:@"Purchase Failed" message:
@"Ensure you are online and try logging out and back into iTunes from your device's Settings." ) strf( @"%@\n\n%@", error.localizedDescription,
preferredStyle:UIAlertControllerStyleAlert]; @"Ensure you are online and try logging out and back into iTunes from your device's Settings." )
[controller addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleCancel handler:nil]]; preferredStyle:UIAlertControllerStyleAlert];
[self.navigationController presentViewController:controller animated:YES completion:nil]; [controller addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleCancel handler:nil]];
#else [self.navigationController presentViewController:controller animated:YES completion:nil];
} );
#endif #endif
} }

View File

@ -248,8 +248,7 @@
MPKey *recoverKey = newKey; MPKey *recoverKey = newKey;
#ifdef PEARL_UIKIT #ifdef PEARL_UIKIT
PearlOverlay *activityOverlay = [PearlOverlay showProgressOverlayWithTitle:PearlString( @"Migrating %ld sites...", PearlOverlay *activityOverlay = [PearlOverlay showProgressOverlayWithTitle:strf( @"Migrating %ld sites...", (long)[user.sites count] )];
(long)[user.sites count] )];
#endif #endif
for (MPSiteEntity *site in user.sites) { for (MPSiteEntity *site in user.sites) {
@ -261,19 +260,21 @@
#ifdef PEARL_UIKIT #ifdef PEARL_UIKIT
masterPassword = PearlAwait( ^(void (^setResult)(id)) { masterPassword = PearlAwait( ^(void (^setResult)(id)) {
UIAlertController *controller = [UIAlertController alertControllerWithTitle:@"Enter Old Master Password" message: PearlMainQueue( ^{
PearlString( @"Your old master password is required to migrate the stored password for %@", site.name ) UIAlertController *controller = [UIAlertController alertControllerWithTitle:@"Enter Old Master Password" message:
preferredStyle:UIAlertControllerStyleAlert]; strf( @"Your old master password is required to migrate the stored password for %@", site.name )
[controller addTextFieldWithConfigurationHandler:nil]; preferredStyle:UIAlertControllerStyleAlert];
[controller addAction:[UIAlertAction actionWithTitle:@"Migrate" style:UIAlertActionStyleDefault handler: [controller addTextFieldWithConfigurationHandler:nil];
^(UIAlertAction *_Nonnull action) { [controller addAction:[UIAlertAction actionWithTitle:@"Migrate" style:UIAlertActionStyleDefault handler:
setResult( controller.textFields.firstObject.text ); ^(UIAlertAction *_Nonnull action) {
}]]; setResult( controller.textFields.firstObject.text );
[controller addAction:[UIAlertAction actionWithTitle:@"Don't Migrate" style:UIAlertActionStyleCancel handler: }]];
^(UIAlertAction *_Nonnull action) { [controller addAction:[UIAlertAction actionWithTitle:@"Don't Migrate" style:UIAlertActionStyleCancel handler:
setResult( nil ); ^(UIAlertAction *_Nonnull action) {
}]]; setResult( nil );
[self.navigationController presentViewController:controller animated:YES completion:nil]; }]];
[self.navigationController presentViewController:controller animated:YES completion:nil];
} );
} ); } );
#endif #endif
if (!masterPassword) if (!masterPassword)

View File

@ -548,11 +548,8 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
do { do {
if ([MPAppDelegate_Shared managedObjectContextPerformBlockAndWait:^(NSManagedObjectContext *context) { if ([MPAppDelegate_Shared managedObjectContextPerformBlockAndWait:^(NSManagedObjectContext *context) {
NSError *error = [self importSites:importData askImportPassword:importPassword askUserPassword:userPassword resultBlock( [self importSites:importData askImportPassword:importPassword askUserPassword:userPassword
saveInContext:context]; saveInContext:context] );
PearlMainQueue( ^{
resultBlock( error );
} );
}]) }])
break; break;
usleep( (useconds_t)(USEC_PER_SEC * 0.2) ); usleep( (useconds_t)(USEC_PER_SEC * 0.2) );

View File

@ -113,7 +113,7 @@
} }
if (cell == self.checkInconsistencies) if (cell == self.checkInconsistencies)
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) { [MPiOSAppDelegate managedObjectContextForMainThreadPerformBlock:^(NSManagedObjectContext *context) {
if ([[MPiOSAppDelegate get] findAndFixInconsistenciesSaveInContext:context] == MPFixableResultNoProblems) { if ([[MPiOSAppDelegate get] findAndFixInconsistenciesSaveInContext:context] == MPFixableResultNoProblems) {
UIAlertController *controller = [UIAlertController alertControllerWithTitle:@"No Inconsistencies" message: UIAlertController *controller = [UIAlertController alertControllerWithTitle:@"No Inconsistencies" message:
@"No inconsistencies were detected in your sites." @"No inconsistencies were detected in your sites."
@ -215,7 +215,7 @@
- (IBAction)securityButton:(id)sender { - (IBAction)securityButton:(id)sender {
[[self dismissPopup].navigationController performSegueWithIdentifier:@"web" sender: [[self dismissPopup].navigationController performSegueWithIdentifier:@"web" sender:
[NSURL URLWithString:@"https://masterpassword.app/security.html"]]; [NSURL URLWithString:@"https://masterpassword.app/masterpassword-algorithm.pdf"]];
} }
- (IBAction)sourceButton:(id)sender { - (IBAction)sourceButton:(id)sender {

View File

@ -40,7 +40,7 @@ PearlEnum( MPDevelopmentFuelConsumption,
NSMutableString *features = [NSMutableString string]; NSMutableString *features = [NSMutableString string];
NSArray *storeVersions = @[ NSArray *storeVersions = @[
@"Generated Usernames\nSecurity Question Answers", @"Generated Usernames\nSecurity Question Answers",
@"TouchID Support" @"Biometrics Support",
]; ];
NSInteger storeVersion = [[NSUserDefaults standardUserDefaults] integerForKey:@"storeVersion"]; NSInteger storeVersion = [[NSUserDefaults standardUserDefaults] integerForKey:@"storeVersion"];
for (; storeVersion < [storeVersions count]; ++storeVersion) for (; storeVersion < [storeVersions count]; ++storeVersion)

View File

@ -431,22 +431,22 @@ referenceSizeForFooterInSection:(NSInteger)section {
NSManagedObjectID *userID = user.permanentObjectID; NSManagedObjectID *userID = user.permanentObjectID;
UIAlertController *controller = [UIAlertController alertControllerWithTitle:user.name message:nil preferredStyle:UIAlertControllerStyleActionSheet]; UIAlertController *controller = [UIAlertController alertControllerWithTitle:user.name message:nil preferredStyle:UIAlertControllerStyleActionSheet];
[controller addAction:[UIAlertAction actionWithTitle:@"Delete User" style:UIAlertActionStyleDestructive handler:^(UIAlertAction * _Nonnull action) { [controller addAction:[UIAlertAction actionWithTitle:@"Delete User" style:UIAlertActionStyleDestructive handler:^(UIAlertAction * _Nonnull action) {
UIAlertController *controller = [UIAlertController alertControllerWithTitle:@"Deleting User" message: UIAlertController *controller_ = [UIAlertController alertControllerWithTitle:@"Deleting User" message:
@"The user and its sites will be deleted." preferredStyle:UIAlertControllerStyleAlert]; @"The user and its sites will be deleted." preferredStyle:UIAlertControllerStyleAlert];
[controller addAction:[UIAlertAction actionWithTitle:@"Delete User" style:UIAlertActionStyleDestructive handler:^(UIAlertAction * _Nonnull action) { [controller_ addAction:[UIAlertAction actionWithTitle:@"Delete User" style:UIAlertActionStyleDestructive handler:^(UIAlertAction * _Nonnull action) {
[self deleteUser:userID]; [self deleteUser:userID];
}]]; }]];
[controller addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]]; [controller_ addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]];
[self presentViewController:controller animated:YES completion:nil]; [self presentViewController:controller_ animated:YES completion:nil];
}]]; }]];
[controller addAction:[UIAlertAction actionWithTitle:@"Reset Password" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { [controller addAction:[UIAlertAction actionWithTitle:@"Reset Password" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
UIAlertController *controller = [UIAlertController alertControllerWithTitle:@"Resetting User" message: UIAlertController *controller_ = [UIAlertController alertControllerWithTitle:@"Resetting User" message:
@"The user's master password will be reset." preferredStyle:UIAlertControllerStyleAlert]; @"The user's master password will be reset." preferredStyle:UIAlertControllerStyleAlert];
[controller addAction:[UIAlertAction actionWithTitle:@"Reset User" style:UIAlertActionStyleDestructive handler:^(UIAlertAction * _Nonnull action) { [controller_ addAction:[UIAlertAction actionWithTitle:@"Reset User" style:UIAlertActionStyleDestructive handler:^(UIAlertAction * _Nonnull action) {
[self resetUser:userID avatar:avatarCell]; [self resetUser:userID avatar:avatarCell];
}]]; }]];
[controller addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]]; [controller_ addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]];
[self presentViewController:controller animated:YES completion:nil]; [self presentViewController:controller_ animated:YES completion:nil];
}]]; }]];
[controller addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]]; [controller addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]];
[self presentViewController:controller animated:YES completion:nil]; [self presentViewController:controller animated:YES completion:nil];

View File

@ -182,7 +182,8 @@ void mpw_log_sink_pearl(const MPLogEvent *record) {
@try { @try {
inf( @"Started up with device identifier: %@", [PearlKeyChain deviceIdentifier] ); inf( @"Started up with device identifier: %@", [PearlKeyChain deviceIdentifier] );
PearlAddNotificationObserver( MPFoundInconsistenciesNotification, nil, nil, ^(id self, NSNotification *note) { PearlAddNotificationObserver(
MPFoundInconsistenciesNotification, nil, [NSOperationQueue mainQueue], ^(id self, NSNotification *note) {
switch ((MPFixableResult)[note.userInfo[MPInconsistenciesFixResultUserKey] unsignedIntegerValue]) { switch ((MPFixableResult)[note.userInfo[MPInconsistenciesFixResultUserKey] unsignedIntegerValue]) {
case MPFixableResultNoProblems: case MPFixableResultNoProblems:
@ -244,22 +245,26 @@ void mpw_log_sink_pearl(const MPLogEvent *record) {
MPError( error, @"While reading imported sites from %@.", url ); MPError( error, @"While reading imported sites from %@.", url );
if (!importedSitesData) { if (!importedSitesData) {
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Error" message: PearlMainQueue( ^{
strf( @"Master Password couldn't read the import sites.\n\n%@", UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Error" message:
(id)[error localizedDescription]?: error ) strf( @"Master Password couldn't read the import sites.\n\n%@",
preferredStyle:UIAlertControllerStyleAlert]; (id)[error localizedDescription]?: error )
[alert addAction:[UIAlertAction actionWithTitle:@"Continue" style:UIAlertActionStyleCancel handler:nil]]; preferredStyle:UIAlertControllerStyleAlert];
[self.navigationController presentViewController:alert animated:YES completion:nil]; [alert addAction:[UIAlertAction actionWithTitle:@"Continue" style:UIAlertActionStyleCancel handler:nil]];
[self.navigationController presentViewController:alert animated:YES completion:nil];
} );
return; return;
} }
NSString *importedSitesString = [[NSString alloc] initWithData:importedSitesData encoding:NSUTF8StringEncoding]; NSString *importedSitesString = [[NSString alloc] initWithData:importedSitesData encoding:NSUTF8StringEncoding];
if (!importedSitesString) { if (!importedSitesString) {
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Error" message: PearlMainQueue( ^{
@"Master Password couldn't understand the import file." UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Error" message:
preferredStyle:UIAlertControllerStyleAlert]; @"Master Password couldn't understand the import file."
[alert addAction:[UIAlertAction actionWithTitle:@"Continue" style:UIAlertActionStyleCancel handler:nil]]; preferredStyle:UIAlertControllerStyleAlert];
[self.navigationController presentViewController:alert animated:YES completion:nil]; [alert addAction:[UIAlertAction actionWithTitle:@"Continue" style:UIAlertActionStyleCancel handler:nil]];
[self.navigationController presentViewController:alert animated:YES completion:nil];
} );
return; return;
} }
@ -281,44 +286,50 @@ void mpw_log_sink_pearl(const MPLogEvent *record) {
PearlOverlay *activityOverlay = [PearlOverlay showProgressOverlayWithTitle:@"Importing"]; PearlOverlay *activityOverlay = [PearlOverlay showProgressOverlayWithTitle:@"Importing"];
[self importSites:importData askImportPassword:^NSString *(NSString *userName) { [self importSites:importData askImportPassword:^NSString *(NSString *userName) {
return PearlAwait( ^(void (^setResult)(id)) { return PearlAwait( ^(void (^setResult)(id)) {
UIAlertController *alert = [UIAlertController alertControllerWithTitle:strf( @"Importing Sites For\n%@", userName ) message: PearlMainQueue( ^{
@"Enter the master password used to create this export file." UIAlertController *alert = [UIAlertController alertControllerWithTitle:strf( @"Importing Sites For\n%@", userName ) message:
preferredStyle:UIAlertControllerStyleAlert]; @"Enter the master password used to create this export file."
[alert addTextFieldWithConfigurationHandler:^(UITextField *textField) { preferredStyle:UIAlertControllerStyleAlert];
textField.secureTextEntry = YES; [alert addTextFieldWithConfigurationHandler:^(UITextField *textField) {
}]; textField.secureTextEntry = YES;
[alert addAction:[UIAlertAction actionWithTitle:@"Import" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) { }];
setResult( alert.textFields.firstObject.text ); [alert addAction:[UIAlertAction actionWithTitle:@"Import" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
}]]; setResult( alert.textFields.firstObject.text );
[alert addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) { }]];
setResult( nil ); [alert addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
}]]; setResult( nil );
[self.navigationController presentViewController:alert animated:YES completion:nil]; }]];
[self.navigationController presentViewController:alert animated:YES completion:nil];
} );
} ); } );
} askUserPassword:^NSString *(NSString *userName) { } askUserPassword:^NSString *(NSString *userName) {
return PearlAwait( (id)^(void (^setResult)(id)) { return PearlAwait( (id)^(void (^setResult)(id)) {
UIAlertController *alert = [UIAlertController alertControllerWithTitle:strf( @"Master Password For\n%@", userName ) message: PearlMainQueue( ^{
@"Enter the current master password for this user." UIAlertController *alert = [UIAlertController alertControllerWithTitle:strf( @"Master Password For\n%@", userName ) message:
preferredStyle:UIAlertControllerStyleAlert]; @"Enter the current master password for this user."
[alert addTextFieldWithConfigurationHandler:^(UITextField *textField) { preferredStyle:UIAlertControllerStyleAlert];
textField.secureTextEntry = YES; [alert addTextFieldWithConfigurationHandler:^(UITextField *textField) {
}]; textField.secureTextEntry = YES;
[alert addAction:[UIAlertAction actionWithTitle:@"Import" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) { }];
setResult( alert.textFields.firstObject.text ); [alert addAction:[UIAlertAction actionWithTitle:@"Import" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
}]]; setResult( alert.textFields.firstObject.text );
[alert addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) { }]];
setResult( nil ); [alert addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
}]]; setResult( nil );
[self.navigationController presentViewController:alert animated:YES completion:nil]; }]];
[self.navigationController presentViewController:alert animated:YES completion:nil];
} );
} ); } );
} result:^(NSError *error) { } result:^(NSError *error) {
[activityOverlay cancelOverlayAnimated:YES]; [activityOverlay cancelOverlayAnimated:YES];
if (error && !(error.domain == NSCocoaErrorDomain && error.code == NSUserCancelledError)) { if (error && !(error.domain == NSCocoaErrorDomain && error.code == NSUserCancelledError)) {
UIAlertController *controller = [UIAlertController alertControllerWithTitle:@"Error" message:[error localizedDescription] PearlMainQueue( ^{
preferredStyle:UIAlertControllerStyleAlert]; UIAlertController *controller = [UIAlertController alertControllerWithTitle:@"Error" message:[error localizedDescription]
[controller addAction:[UIAlertAction actionWithTitle:@"Continue" style:UIAlertActionStyleCancel handler:nil]]; preferredStyle:UIAlertControllerStyleAlert];
[self.navigationController presentViewController:controller animated:YES completion:nil]; [controller addAction:[UIAlertAction actionWithTitle:@"Continue" style:UIAlertActionStyleCancel handler:nil]];
[self.navigationController presentViewController:controller animated:YES completion:nil];
} );
} }
}]; }];
} }
@ -341,16 +352,18 @@ void mpw_log_sink_pearl(const MPLogEvent *record) {
NSString *importData = [UIPasteboard generalPasteboard].string; NSString *importData = [UIPasteboard generalPasteboard].string;
MPMarshalledFile *importFile = mpw_marshal_read( NULL, importData.UTF8String ); MPMarshalledFile *importFile = mpw_marshal_read( NULL, importData.UTF8String );
if (importFile && importFile->error.type == MPMarshalSuccess && importFile->info->format != MPMarshalFormatNone) { if (importFile && importFile->error.type == MPMarshalSuccess && importFile->info->format != MPMarshalFormatNone) {
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Import Sites?" message: PearlMainQueue( ^{
@"We've detected Master Password import sites on your pasteboard, would you like to import them?" UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Import Sites?" message:
preferredStyle:UIAlertControllerStyleAlert]; @"We've detected Master Password import sites on your pasteboard, would you like to import them?"
[alert addAction:[UIAlertAction actionWithTitle:@"Import Sites" style:UIAlertActionStyleDefault handler: preferredStyle:UIAlertControllerStyleAlert];
^(UIAlertAction *action) { [alert addAction:[UIAlertAction actionWithTitle:@"Import Sites" style:UIAlertActionStyleDefault handler:
[self importSites:importData]; ^(UIAlertAction *action) {
[UIPasteboard generalPasteboard].string = @""; [self importSites:importData];
}]]; [UIPasteboard generalPasteboard].string = @"";
[alert addAction:[UIAlertAction actionWithTitle:@"No" style:UIAlertActionStyleCancel handler:nil]]; }]];
[self.navigationController presentViewController:alert animated:YES completion:nil]; [alert addAction:[UIAlertAction actionWithTitle:@"No" style:UIAlertActionStyleCancel handler:nil]];
[self.navigationController presentViewController:alert animated:YES completion:nil];
} );
} }
mpw_marshal_file_free( &importFile ); mpw_marshal_file_free( &importFile );
} ); } );
@ -422,7 +435,7 @@ void mpw_log_sink_pearl(const MPLogEvent *record) {
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Feedback" message: UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Feedback" message:
@"Have a question, comment, issue or just saying thanks?\n\n" @"Have a question, comment, issue or just saying thanks?\n\n"
@"We'd love to hear what you think!\n" @"We'd love to hear what you think!\n"
@"masterpassword@lyndir.com" @"help@masterpassword.app"
preferredStyle:UIAlertControllerStyleAlert]; preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"Okay" style:UIAlertActionStyleCancel handler:nil]]; [alert addAction:[UIAlertAction actionWithTitle:@"Okay" style:UIAlertActionStyleCancel handler:nil]];
[self.navigationController presentViewController:alert animated:YES completion:nil]; [self.navigationController presentViewController:alert animated:YES completion:nil];
@ -452,7 +465,7 @@ void mpw_log_sink_pearl(const MPLogEvent *record) {
if (logs && ([[MPConfig get].sendInfo boolValue] || [[MPiOSConfig get].traceMode boolValue])) if (logs && ([[MPConfig get].sendInfo boolValue] || [[MPiOSConfig get].traceMode boolValue]))
logLevel = PearlLogLevelDebug; logLevel = PearlLogLevelDebug;
[[[PearlEMail alloc] initForEMailTo:@"Master Password Development <masterpassword@lyndir.com>" [[[PearlEMail alloc] initForEMailTo:@"Master Password Development <help@masterpassword.app"
subject:strf( @"Feedback for Master Password [%@]", subject:strf( @"Feedback for Master Password [%@]",
[[PearlKeyChain deviceIdentifier] stringByDeletingMatchesOf:@"-.*"] ) [[PearlKeyChain deviceIdentifier] stringByDeletingMatchesOf:@"-.*"] )
body:strf( @"\n\n\n" body:strf( @"\n\n\n"
@ -479,19 +492,22 @@ void mpw_log_sink_pearl(const MPLogEvent *record) {
static dispatch_once_t once = 0; static dispatch_once_t once = 0;
dispatch_once( &once, ^{ dispatch_once( &once, ^{
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Failed To Load Sites" message: PearlMainQueue( ^{
@"Master Password was unable to open your sites history.\n" UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Failed To Load Sites" message:
@"This may be due to corruption. You can either reset Master Password and " @"Master Password was unable to open your sites history.\n"
@"recreate your user, or E-Mail us your logs and leave your corrupt store as-is for now." @"This may be due to corruption. You can either reset Master Password and "
preferredStyle:UIAlertControllerStyleAlert]; @"recreate your user, or E-Mail us your logs and leave your corrupt store as-is for now."
[alert addAction:[UIAlertAction actionWithTitle:@"E-Mail Logs" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) { preferredStyle:UIAlertControllerStyleAlert];
[self openFeedbackWithLogs:YES forVC:nil]; [alert addAction:[UIAlertAction actionWithTitle:@"E-Mail Logs" style:UIAlertActionStyleDefault
}]]; handler:^(UIAlertAction *action) {
[alert addAction:[UIAlertAction actionWithTitle:@"Reset" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) { [self openFeedbackWithLogs:YES forVC:nil];
[self deleteAndResetStore]; }]];
}]]; [alert addAction:[UIAlertAction actionWithTitle:@"Reset" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
[alert addAction:[UIAlertAction actionWithTitle:@"Ignore" style:UIAlertActionStyleCancel handler:nil]]; [self deleteAndResetStore];
[self.navigationController presentViewController:alert animated:YES completion:nil]; }]];
[alert addAction:[UIAlertAction actionWithTitle:@"Ignore" style:UIAlertActionStyleCancel handler:nil]];
[self.navigationController presentViewController:alert animated:YES completion:nil];
} );
} ); } );
} }
@ -620,25 +636,27 @@ void mpw_log_sink_pearl(const MPLogEvent *record) {
- (void)changeMasterPasswordFor:(MPUserEntity *)user saveInContext:(NSManagedObjectContext *)moc didResetBlock:(void ( ^ )(void))didReset { - (void)changeMasterPasswordFor:(MPUserEntity *)user saveInContext:(NSManagedObjectContext *)moc didResetBlock:(void ( ^ )(void))didReset {
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Changing Master Password" message: PearlMainQueue( ^{
@"If you continue, you'll be able to set a new master password.\n\n" UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Changing Master Password" message:
@"Changing your master password will cause all your generated passwords to change!\n" @"If you continue, you'll be able to set a new master password.\n\n"
@"Changing the master password back to the old one will cause your passwords to revert as well." @"Changing your master password will cause all your generated passwords to change!\n"
preferredStyle:UIAlertControllerStyleAlert]; @"Changing the master password back to the old one will cause your passwords to revert as well."
[alert addAction:[UIAlertAction actionWithTitle:@"Abort" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) { preferredStyle:UIAlertControllerStyleAlert];
[moc performBlockAndWait:^{ [alert addAction:[UIAlertAction actionWithTitle:@"Abort" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
inf( @"Clearing keyID for user: %@.", user.userID ); [moc performBlockAndWait:^{
user.keyID = nil; inf( @"Clearing keyID for user: %@.", user.userID );
[self forgetSavedKeyFor:user]; user.keyID = nil;
[moc saveToStore]; [self forgetSavedKeyFor:user];
}]; [moc saveToStore];
}];
[self signOutAnimated:YES]; [self signOutAnimated:YES];
if (didReset) if (didReset)
didReset(); didReset();
}]]; }]];
[alert addAction:[UIAlertAction actionWithTitle:@"Abort" style:UIAlertActionStyleCancel handler:nil]]; [alert addAction:[UIAlertAction actionWithTitle:@"Abort" style:UIAlertActionStyleCancel handler:nil]];
[self.navigationController presentViewController:alert animated:YES completion:nil]; [self.navigationController presentViewController:alert animated:YES completion:nil];
} );
} }
#pragma mark - UIDocumentInteractionControllerDelegate #pragma mark - UIDocumentInteractionControllerDelegate

View File

@ -37,10 +37,6 @@
<string>[auto]</string> <string>[auto]</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>[auto]</string> <string>[auto]</string>
<key>LSApplicationQueriesSchemes</key>
<array>
<string>volto</string>
</array>
<key>LSRequiresIPhoneOS</key> <key>LSRequiresIPhoneOS</key>
<true/> <true/>
<key>NSHumanReadableCopyright</key> <key>NSHumanReadableCopyright</key>

View File

@ -69,7 +69,7 @@
</collectionViewFlowLayout> </collectionViewFlowLayout>
<cells> <cells>
<collectionViewCell opaque="NO" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="MPAvatarCell" id="Zab-uQ-uk9" customClass="MPAvatarCell"> <collectionViewCell opaque="NO" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="MPAvatarCell" id="Zab-uQ-uk9" customClass="MPAvatarCell">
<rect key="frame" x="80" y="114.5" width="215" height="667"/> <rect key="frame" x="80" y="115" width="215" height="667"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center"> <view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
<rect key="frame" x="0.0" y="0.0" width="215" height="667"/> <rect key="frame" x="0.0" y="0.0" width="215" height="667"/>
@ -898,15 +898,15 @@
<action selector="valueChanged:" destination="JFc-sj-awD" eventType="valueChanged" id="KmT-CO-GZh"/> <action selector="valueChanged:" destination="JFc-sj-awD" eventType="valueChanged" id="KmT-CO-GZh"/>
</connections> </connections>
</switch> </switch>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" verticalCompressionResistancePriority="751" text="TouchID" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="6xf-vt-aXd"> <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" verticalCompressionResistancePriority="751" text="Biometrics" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="6xf-vt-aXd">
<rect key="frame" x="20" y="20" width="374" height="20"/> <rect key="frame" x="20" y="20" width="374" height="21"/>
<fontDescription key="fontDescription" name="Exo2.0-Bold" family="Exo 2.0" pointSize="17"/> <fontDescription key="fontDescription" name="Exo2.0-Bold" family="Exo 2.0" pointSize="17"/>
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" alpha="0.69999999999999996" contentMode="left" horizontalHuggingPriority="251" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="URR-yZ-QuC"> <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" alpha="0.69999999999999996" contentMode="left" horizontalHuggingPriority="251" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="URR-yZ-QuC">
<rect key="frame" x="20" y="48" width="374" height="143"/> <rect key="frame" x="20" y="49" width="374" height="142"/>
<string key="text">When enabled on a TouchID-enabled device, your fingerprint will be required to load your saved password. Note that this feature requires that the Save Password option is enabled and TouchID support has been purchased from the in-app store.</string> <string key="text">When enabled on a biometrics-enabled device, your fingerprint or face scan will be required to load your saved password. Note that this feature requires that the Save Password option is enabled and Biometrics support has been purchased from the in-app store.</string>
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="12"/> <fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="12"/>
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
@ -1030,7 +1030,7 @@
<rect key="frame" x="0.0" y="0.0" width="414" height="250"/> <rect key="frame" x="0.0" y="0.0" width="414" height="250"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<subviews> <subviews>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" alpha="0.69999999999999996" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="999" verticalCompressionResistancePriority="1000" text="© 2011-2017, Maarten Billemont (lhunath)" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="sPw-mV-mFF"> <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" alpha="0.69999999999999996" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="999" verticalCompressionResistancePriority="1000" text="© 2011-2020, Maarten Billemont (lhunath)" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="sPw-mV-mFF">
<rect key="frame" x="20" y="4" width="374" height="106"/> <rect key="frame" x="20" y="4" width="374" height="106"/>
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="11"/> <fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="11"/>
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>