BTHLABS-66: Prepping for public release: Take five

AKA "Using Apple reviewers as QA for your project". Thanks, y'all! :)
This commit is contained in:
2025-11-27 17:51:19 +01:00
parent cca49f2292
commit 55126f4af6
26 changed files with 386 additions and 97 deletions

View File

@@ -21,19 +21,6 @@
}
-(void)application:(NSApplication *)application openURLs:(NSArray<NSURL *> *)urls {
HPAuthParams *receivedAuthParams = nil;
for (NSURL *url in urls) {
receivedAuthParams = [self.authFlow handlePostAuthenticateURL:url];
if (receivedAuthParams != nil) {
break;
}
}
if (receivedAuthParams != nil) {
[[NSApplication sharedApplication] activateIgnoringOtherApps:YES];
[self.authFlow handleAuthParams:receivedAuthParams];
}
}
@end

View File

@@ -6,13 +6,19 @@
//
#import <Cocoa/Cocoa.h>
#import <AuthenticationServices/AuthenticationServices.h>
NS_ASSUME_NONNULL_BEGIN
@interface AuthorizationProgressViewController : NSViewController
@interface AuthorizationProgressViewController : NSViewController<ASWebAuthenticationPresentationContextProviding>
@property IBOutlet NSProgressIndicator *progressIndicator;
@property NSString *progressLabelTitle;
@property (nullable, strong) NSURL *authorizationURL;
@property (nullable, strong) ASWebAuthenticationSession *webAuthenticationSession;
@property BOOL userCancelledSession;
-(IBAction)doCancel:(id)sender;
@end

View File

@@ -9,27 +9,42 @@
#import "AppDelegate.h"
#import "AuthorizationViewController.h"
#import "HPAuthFlow.h"
#import "HPCredentialsHelper.h"
#import "MainViewController.h"
#import "NSBundle+HotPocketExtensions.h"
#import "ReplaceAnimator.h"
@interface AuthorizationProgressViewController (AuthorizationProgressViewControllerPrivate)
#pragma mark - Private interface
-(void)presentAuthorizationError;
@end
@implementation AuthorizationProgressViewController
#pragma mark - View lifecycle
-(instancetype)initWithCoder:(NSCoder *)coder {
if (self = [super initWithCoder:coder]) {
self.authorizationURL = nil;
self.webAuthenticationSession = nil;
self.userCancelledSession = NO;
}
return self;
}
-(void)viewDidLoad {
[super viewDidLoad];
self.progressLabelTitle = NSLocalizedString(@"Continue to sign out in your browser...", @"Continue to sign out in your browser...");
self.progressLabelTitle = NSLocalizedString(@"Continue to sign in in your browser...", @"Continue to sign in in your browser...");
}
-(void)viewWillAppear {
AppDelegate *appDelegate = [[NSApplication sharedApplication] delegate];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(onAuthFlowDidFinish:)
name:@"AuthFlowDidFinish"
@@ -43,12 +58,84 @@
[self.progressIndicator startAnimation:self];
}
-(void)viewDidAppear {
[super viewDidAppear];
AppDelegate *appDelegate = [[NSApplication sharedApplication] delegate];
ASWebAuthenticationSessionCompletionHandler completionHandler = ^(NSURL *url, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
if (error != nil) {
#ifdef DEBUG
NSLog(@"[AuthorizationViewController.session completionHandler] error=`%@`", error);
#endif
if (error.code == ASWebAuthenticationSessionErrorCodeCanceledLogin) {
self.userCancelledSession = YES;
}
[self presentAuthorizationError];
} else {
HPAuthParams *receivedAuthParams = [appDelegate.authFlow handlePostAuthenticateURL:url];
if (receivedAuthParams != nil) {
[[NSApplication sharedApplication] activateIgnoringOtherApps:YES];
[appDelegate.authFlow handleAuthParams:receivedAuthParams];
} else {
[self presentAuthorizationError];
}
}
self.webAuthenticationSession = nil;
});
};
ASWebAuthenticationSessionCallback *callback = [ASWebAuthenticationSessionCallback callbackWithCustomScheme:[NSBundle postAuthenticateURLScheme]];
self.webAuthenticationSession = [[ASWebAuthenticationSession alloc] initWithURL:self.authorizationURL
callback:callback
completionHandler:completionHandler];
self.webAuthenticationSession.presentationContextProvider = self;
#ifdef DEBUG
self.webAuthenticationSession.prefersEphemeralWebBrowserSession = YES;
#endif
if (self.webAuthenticationSession.canStart == NO) {
[self presentAuthorizationError];
return;
}
[self.webAuthenticationSession start];
}
-(void)viewDidDisappear {
[super viewDidDisappear];
self.webAuthenticationSession = nil;
[self.progressIndicator stopAnimation:self];
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
#pragma mark - Actions
-(IBAction)doCancel:(id)sender {
[self.webAuthenticationSession cancel];
}
#pragma mark - Private interface
-(void)presentAuthorizationError {
if (self.userCancelledSession == NO) {
NSAlert *alert = [[NSAlert alloc] init];
alert.alertStyle = NSAlertStyleCritical;
alert.messageText = NSLocalizedString(@"Oops!", @"Oops!");
alert.informativeText = NSLocalizedString(@"HotPocket couldn't complete this operation.", @"HotPocket couldn't complete this operation.");
[alert beginSheetModalForWindow:self.view.window completionHandler:^(NSModalResponse response) {
AuthorizationViewController *authorizationViewController = [self.storyboard instantiateControllerWithIdentifier:@"AuthorizationViewController"];
[self presentViewController:authorizationViewController animator:[[ReplaceAnimator alloc] init]];
}];
} else {
AuthorizationViewController *authorizationViewController = [self.storyboard instantiateControllerWithIdentifier:@"AuthorizationViewController"];
[self presentViewController:authorizationViewController animator:[[ReplaceAnimator alloc] init]];
}
}
#pragma mark - Notification handlers
-(void)onAuthFlowDidFinish:(NSNotification *)notification {
@@ -59,14 +146,7 @@
[[NSApplication sharedApplication] activateIgnoringOtherApps:YES];
if (credentials.usable == NO) {
NSAlert *alert = [[NSAlert alloc] init];
alert.alertStyle = NSAlertStyleCritical;
alert.messageText = NSLocalizedString(@"Oops!", @"Oops!");
alert.informativeText = NSLocalizedString(@"HotPocket couldn't complete this operation.", @"HotPocket couldn't complete this operation.");
[alert beginSheetModalForWindow:self.view.window completionHandler:^(NSModalResponse response) {
AuthorizationViewController *authorizationViewController = [self.storyboard instantiateControllerWithIdentifier:@"AuthorizationViewController"];
[self presentViewController:authorizationViewController animator:[[ReplaceAnimator alloc] init]];
}];
[self presentAuthorizationError];
} else {
MainViewController *mainViewController = [self.storyboard instantiateControllerWithIdentifier:@"MainViewController"];
[self presentViewController:mainViewController animator:[[ReplaceAnimator alloc] init]];
@@ -78,4 +158,10 @@
self.progressLabelTitle = NSLocalizedString(@"Processing authorization...", @"Processing authorization...");
}
# pragma mark - ASWebAuthenticationPresentationContextProviding implementation
-(ASPresentationAnchor)presentationAnchorForWebAuthenticationSession:(ASWebAuthenticationSession *)session {
return self.view.window;
}
@end

View File

@@ -31,31 +31,24 @@
#pragma mark - Actions
-(IBAction)doStartAuthorizationFlow:(id)sender {
NSAlert *alert = [[NSAlert alloc] init];
alert.alertStyle = NSAlertStyleInformational;
alert.messageText = NSLocalizedString(@"Continue in the browser", @"Continue in the browser");
alert.informativeText = NSLocalizedString(@"HotPocket will now open the instance in your browser and you can continue to sign in.", @"HotPocket will now open the instance in your browser and you can continue to sign in.");
AppDelegate *appDelegate = [[NSApplication sharedApplication] delegate];
appDelegate.authFlow.baseURL = [NSURL URLWithString:self.baseURL];
[alert addButtonWithTitle:NSLocalizedString(@"Let's go!", @"Let's go!")];
[alert addButtonWithTitle:NSLocalizedString(@"Cancel", @"Cancel")];
NSURL *authURL = [appDelegate.authFlow start];
if (authURL == nil) {
NSBeep();
return;
}
[alert beginSheetModalForWindow:self.view.window completionHandler:^(NSModalResponse response) {
if (response == NSAlertFirstButtonReturn) {
AppDelegate *appDeleate = [[NSApplication sharedApplication] delegate];
appDeleate.authFlow.baseURL = [NSURL URLWithString:self.baseURL];
NSURL *authURL = [appDeleate.authFlow start];
if (authURL == nil) {
NSBeep();
return;
}
AuthorizationProgressViewController *authProgressViewController = [self.storyboard instantiateControllerWithIdentifier:@"AuthorizationProgressViewController"];
authProgressViewController.authorizationURL = authURL;
[self presentViewController:authProgressViewController animator:[[ReplaceAnimator alloc] init]];
}
AuthorizationProgressViewController *authProgressViewController = [self.storyboard instantiateControllerWithIdentifier:@"AuthorizationProgressViewController"];
[self presentViewController:authProgressViewController animator:[[ReplaceAnimator alloc] init]];
[[NSWorkspace sharedWorkspace] openURL:authURL];
}
}];
# pragma mark - ASWebAuthenticationPresentationContextProviding implementation
-(ASPresentationAnchor)presentationAnchorForWebAuthenticationSession:(ASWebAuthenticationSession *)session {
return self.view.window;
}
@end

View File

@@ -207,6 +207,20 @@
<binding destination="OX4-Oj-1cw" name="value" keyPath="self.progressLabelTitle" id="ydU-jy-p3F"/>
</connections>
</textField>
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Sip-bU-V5i">
<rect key="frame" x="175" y="14" width="76" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="push" title="Cancel" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="nYJ-LO-V7O">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
<string key="keyEquivalent" base64-UTF8="YES">
Gw
</string>
</buttonCell>
<connections>
<action selector="doCancel:" target="OX4-Oj-1cw" id="nAC-If-aib"/>
</connections>
</button>
</subviews>
</view>
<connections>