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