github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/updater/osx/Updater/Prompt.m (about) 1 // 2 // Prompt.m 3 // Updater 4 // 5 // Created by Gabriel on 4/13/16. 6 // Copyright © 2016 Keybase. All rights reserved. 7 // 8 9 #import "Prompt.h" 10 #import "TextView.h" 11 #import "Defines.h" 12 #import "NSDictionary+Extension.h" 13 14 @interface FView : NSView 15 @end 16 17 @implementation Prompt 18 19 + (NSDictionary *)parseInputString:(NSString *)inputString defaultValue:(NSDictionary *)defaultValue { 20 NSData *data = [inputString dataUsingEncoding:NSUTF8StringEncoding]; 21 if (!data) { 22 NSLog(@"No data for input"); 23 return defaultValue; 24 } 25 NSError *error = nil; 26 id input = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error]; 27 if (!!error) { 28 NSLog(@"Error parsing input: %@", error); 29 return defaultValue; 30 } 31 if (!input) { 32 NSLog(@"No input"); 33 return defaultValue; 34 } 35 if (![input isKindOfClass:[NSDictionary class]]) { 36 NSLog(@"Invalid input type"); 37 return defaultValue; 38 } 39 return input; 40 } 41 42 + (void)showPromptWithInputString:(NSString *)inputString presenter:(NSModalResponse (^)(NSAlert *alert))presenter completion:(void (^)(NSData *output))completion { 43 44 // Try to parse input, if there is any error use a default empty dictionary. 45 NSDictionary *input = [self parseInputString:inputString defaultValue:@{}]; 46 47 // If input defines buttons it's a generic prompt 48 if ([input[@"type"] isEqual:@"generic"]) { 49 [self showGenericPrompt:input presenter:presenter completion:completion]; 50 return; 51 } 52 53 [self showUpdatePrompt:input presenter:presenter completion:completion]; 54 } 55 56 + (void)showUpdatePrompt:(NSDictionary *)input presenter:(NSModalResponse (^)(NSAlert *alert))presenter completion:(void (^)(NSData *output))completion { 57 NSString *title = [input kb_stringForKey:@"title"]; 58 NSString *message = [input kb_stringForKey:@"message"]; 59 NSString *description = [input kb_stringForKey:@"description"]; 60 BOOL autoUpdate = [input kb_boolForKey:@"autoUpdate"]; 61 62 if (!title) title = @"Keybase Update"; 63 if (!message) message = @"There is an update available."; 64 if (!description) description = @"Please visit keybase.io for more information."; 65 66 if ([title length] > 700) title = [title substringToIndex:699]; 67 if ([message length] > 700) message = [message substringToIndex:699]; 68 69 NSAlert *alert = [[NSAlert alloc] init]; 70 alert.messageText = title; 71 alert.informativeText = message; 72 [alert addButtonWithTitle:@"Update"]; 73 [alert addButtonWithTitle:@"Ignore"]; 74 75 FView *accessoryView = [[FView alloc] init]; 76 TextView *textView = [[TextView alloc] init]; 77 textView.editable = NO; 78 textView.view.textContainerInset = CGSizeMake(5, 5); 79 80 NSFont *font = [NSFont fontWithName:@"Monaco" size:10]; 81 [textView setText:description font:font color:[NSColor blackColor] alignment:NSLeftTextAlignment lineBreakMode:NSLineBreakByWordWrapping]; 82 textView.borderType = NSBezelBorder; 83 textView.frame = CGRectMake(0, 0, 500, 160); 84 [accessoryView addSubview:textView]; 85 86 NSButton *autoCheckbox = [[NSButton alloc] init]; 87 autoCheckbox.title = @"Update automatically"; 88 autoCheckbox.state = autoUpdate ? NSOnState : NSOffState; 89 [autoCheckbox setButtonType:NSSwitchButton]; 90 autoCheckbox.frame = CGRectMake(0, 160, 500, 30); 91 [accessoryView addSubview:autoCheckbox]; 92 accessoryView.frame = CGRectMake(0, 0, 500, 190); 93 alert.accessoryView = accessoryView; 94 95 [alert setAlertStyle:NSInformationalAlertStyle]; 96 97 98 NSModalResponse response = presenter(alert); 99 100 BOOL autoUpdateResponse = NO; 101 102 NSString *action = @""; 103 if (response == NSAlertFirstButtonReturn) { 104 action = @"apply"; 105 autoUpdateResponse = autoCheckbox.state == NSOnState ? YES : NO; 106 } else if (response == NSAlertSecondButtonReturn) { 107 action = @"snooze"; 108 } 109 NSLog(@"Action: %@", action); 110 111 NSError *error = nil; 112 NSDictionary *result = @{ 113 @"action": action, 114 @"autoUpdate": @(autoUpdateResponse), 115 }; 116 117 NSData *data = [NSJSONSerialization dataWithJSONObject:result options:0 error:&error]; 118 if (!!error) { 119 NSLog(@"Error generating JSON response: %@", error); 120 } 121 completion(data); 122 } 123 124 + (void)showGenericPrompt:(NSDictionary *)input presenter:(NSModalResponse (^)(NSAlert *alert))presenter completion:(void (^)(NSData *output))completion { 125 NSString *title = [input kb_stringForKey:@"title"]; 126 NSString *message = [input kb_stringForKey:@"message"]; 127 NSArray *buttons = [input kb_stringArrayForKey:@"buttons"]; 128 129 if ([title length] > 700) title = [title substringToIndex:699]; 130 if ([message length] > 700) message = [message substringToIndex:699]; 131 132 NSAlert *alert = [[NSAlert alloc] init]; 133 alert.messageText = title; 134 alert.informativeText = message; 135 for (NSString *button in buttons) { 136 [alert addButtonWithTitle:button]; 137 } 138 [alert setAlertStyle:NSInformationalAlertStyle]; 139 140 NSModalResponse response = presenter(alert); 141 142 NSString *buttonSelected = buttons[response-NSAlertFirstButtonReturn]; 143 144 NSError *error = nil; 145 NSDictionary *result = @{@"button": buttonSelected}; 146 NSData *data = [NSJSONSerialization dataWithJSONObject:result options:0 error:&error]; 147 if (!!error) { 148 NSLog(@"Error generating JSON response: %@", error); 149 } 150 completion(data); 151 } 152 153 @end 154 155 @implementation FView 156 157 - (BOOL)isFlipped { 158 return YES; 159 } 160 161 @end