1//
2//  KBApp.m
3//  Keybase
4//
5//  Created by Gabriel on 6/10/15.
6//  Copyright (c) 2015 Keybase. All rights reserved.
7//
8
9#import "KBApp.h"
10
11#import "KBAppView.h"
12#import "KBPreferences.h"
13#import "KBControlPanel.h"
14#import "KBConsoleView.h"
15#import "KBWorkspace.h"
16#import "KBLogFormatter.h"
17#import "KBEnvSelectView.h"
18#import "KBUninstaller.h"
19
20#import "KBStyleGuideView.h"
21
22#import "KBPGPEncryptView.h"
23#import "KBPGPEncryptFilesView.h"
24#import "KBPGPDecryptView.h"
25#import "KBPGPDecryptFileView.h"
26#import "KBPGPSignView.h"
27#import "KBPGPSignFileView.h"
28#import "KBPGPSignFilesView.h"
29#import "KBPGPVerifyView.h"
30#import "KBPGPVerifyFileView.h"
31#import "KBWorkspace.h"
32#import "KBPGPEncryptActionView.h"
33#import "KBPGPEncrypt.h"
34
35#import <AFNetworking/AFNetworking.h>
36
37@interface KBApp ()
38@property KBAppView *appView;
39@property KBPreferences *preferences;
40@property BOOL alerting;
41
42// Debug
43@property KBControlPanel *controlPanel;
44@property KBConsoleView *consoleView;
45@end
46
47@implementation KBApp
48
49+ (instancetype)app {
50  id<KBAppDelegate> delegate = [NSApp delegate];
51  return [delegate app];
52}
53
54+ (KBEnvironment *)environment {
55  return [self.app appView].environment;
56}
57
58- (void)open {
59  [self setup];
60}
61
62- (void)setup {
63  _preferences = [[KBPreferences alloc] init];
64
65  _consoleView = [[KBConsoleView alloc] init];
66
67  _controlPanel = [[KBControlPanel alloc] init];
68  [_controlPanel addComponents:@[_consoleView]];
69
70  DDLogLevel logLevel = [[_preferences valueForIdentifier:@"Preferences.Log.Level"] unsignedIntegerValue];
71  [DDLog addLogger:DDASLLogger.sharedInstance withLevel:logLevel];
72  [DDLog addLogger:_consoleView withLevel:DDLogLevelVerbose];
73
74  [KBAppearance setCurrentAppearance:KBAppearance.lightAppearance];
75
76  // Network reachability is a diagnostic tool that can be used to understand why a request might have failed.
77  // It should not be used to determine whether or not to make a request.
78  [AFNetworkReachabilityManager.sharedManager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
79    DDLogInfo(@"Reachability: %@", AFStringFromNetworkReachabilityStatus(status));
80  }];
81  [AFNetworkReachabilityManager.sharedManager startMonitoring];
82
83  // Cleanup old stuff
84  //[KBUninstaller uninstall:@"keybase" completion:^(NSError *error) {}];
85
86  // Save installed version in case a later upgrade needs this info
87  NSString *version = NSBundle.mainBundle.infoDictionary[@"CFBundleShortVersionString"];
88  NSUserDefaults *userDefaults = [KBWorkspace userDefaults];
89  [userDefaults setObject:version forKey:@"InstallVersion"];
90  [userDefaults synchronize];
91}
92
93- (void)openWithEnvironment:(KBEnvironment *)environment {
94  _appView = [[KBAppView alloc] init];
95  [_appView openWindow];
96
97  NSMutableArray *componentsForControlPanel = [environment.componentsForControlPanel mutableCopy];
98//  [componentsForControlPanel addObject:_appView];
99//  [componentsForControlPanel addObject:[[KBStyleGuideView alloc] init]];
100  [_controlPanel addComponents:componentsForControlPanel];
101
102  [_appView openWithEnvironment:environment completion:^(NSError *error) {}];
103}
104
105- (void)openControlPanel {
106  [_controlPanel open:_appView];
107}
108
109- (void)_test {
110  KBRTestRequest *request = [[KBRTestRequest alloc] initWithClient:self.service.client];
111  [self.service.client registerMethod:@"keybase.1.test.testCallback" sessionId:request.sessionId requestHandler:^(NSNumber *messageId, NSString *method, NSArray *params, MPRequestCompletion completion) {
112    completion(nil, @"the reply");
113  }];
114  [request testWithName:@"testing" completion:^(NSError *error, KBRTest *test) {
115    // Testing
116  }];
117}
118
119- (NSWindow *)mainWindow {
120  return _appView.window;
121}
122
123- (KBService *)service {
124  return _appView.environment.service;
125}
126
127- (NSString *)currentUsername {
128  return self.appView.userStatus.user.username;
129}
130
131- (NSString *)APIURLString:(NSString *)path {
132  return [[self appView] APIURLString:path];
133}
134
135- (void)quitWithPrompt:(BOOL)prompt sender:(id)sender {
136  if (prompt) {
137    [KBAlert yesNoWithTitle:@"Quit" description:@"Are you sure you want to quit?" yes:@"Quit" view:_appView completion:^(BOOL yes) {
138      if (yes) [NSApplication.sharedApplication terminate:sender];
139    }];
140  } else {
141    [NSApplication.sharedApplication terminate:sender];
142  }
143}
144
145- (void)closeAllWindows {
146  [_appView.window close];
147  [_preferences close];
148}
149
150#pragma mark Error Handling
151
152- (BOOL)setError:(NSError *)error sender:(id)sender completion:(void (^)(NSModalResponse response))completion {
153  if (!error || KBIsErrorName(error, @"CANCELED")) {
154    // Canceled, ok to ignore
155    if (completion) completion(KBErrorResponseIgnored);
156    return NO;
157  }
158
159  if (KBIsErrorName(error, @"LOGIN_REQUIRED")) {
160    [self.appView showInProgress:@"Loading"];
161    [self.appView checkStatus];
162    return YES;
163  }
164
165  DDLogError(@"%@", error);
166
167  if (_alerting) {
168    DDLogDebug(@"Already showing error (%@)", error);
169    if (completion) completion(KBErrorResponseIgnored);
170    return NO;
171  }
172
173  NSWindow *window = nil;
174  if ([sender isKindOfClass:NSWindow.class]) window = sender;
175  if (!window && [sender respondsToSelector:@selector(window)]) window = [sender window];
176
177  if (!window) window = [NSApp mainWindow];
178  if (!window) window = [NSApp keyWindow];
179  if (!window) window = [[NSApp windows] firstObject];
180
181  NSAssert(window, @"No window to show alert");
182
183  _alerting = YES;
184  GHWeakSelf gself = self;
185  [[NSAlert alertWithError:error] beginSheetModalForWindow:window completionHandler:^(NSModalResponse returnCode) {
186    gself.alerting = NO;
187    if (completion) completion(returnCode);
188  }];
189  return YES;
190}
191
192#pragma mark Menu Actions
193
194- (IBAction)encrypt:(id)sender {
195  KBPGPEncryptView *view = [[KBPGPEncryptView alloc] init];
196  view.client = self.service.client;
197  [self.mainWindow kb_addChildWindowForView:view rect:CGRectMake(0, 0, 510, 400) position:KBWindowPositionCenter title:@"Encrypt" fixed:NO makeKey:YES];
198}
199
200- (IBAction)encryptFile:(id)sender {
201  KBPGPEncryptFilesView *view = [[KBPGPEncryptFilesView alloc] init];
202  view.client = self.service.client;
203  [self.mainWindow kb_addChildWindowForView:view rect:CGRectMake(0, 0, 510, 400) position:KBWindowPositionCenter title:@"Encrypt Files" fixed:NO makeKey:YES];
204}
205
206- (IBAction)decrypt:(id)sender {
207  KBPGPDecryptView *view = [[KBPGPDecryptView alloc] init];
208  view.client = self.service.client;
209  [self.mainWindow kb_addChildWindowForView:view rect:CGRectMake(0, 0, 510, 400) position:KBWindowPositionCenter title:@"Decrypt" fixed:NO makeKey:YES];
210}
211
212- (IBAction)decryptFile:(id)sender {
213  KBPGPDecryptFileView *view = [[KBPGPDecryptFileView alloc] init];
214  view.client = self.service.client;
215  [self.mainWindow kb_addChildWindowForView:view rect:CGRectMake(0, 0, 510, 400) position:KBWindowPositionCenter title:@"Decrypt Files" fixed:NO makeKey:YES];
216}
217
218- (IBAction)sign:(id)sender {
219  KBPGPSignView *view = [[KBPGPSignView alloc] init];
220  view.client = self.service.client;
221  [self.mainWindow kb_addChildWindowForView:view rect:CGRectMake(0, 0, 510, 400) position:KBWindowPositionCenter title:@"Sign" fixed:NO makeKey:YES];
222}
223
224- (IBAction)signFile:(id)sender {
225  KBPGPSignFileView *view = [[KBPGPSignFileView alloc] init];
226  view.client = self.service.client;
227  [self.mainWindow kb_addChildWindowForView:view rect:CGRectMake(0, 0, 510, 400) position:KBWindowPositionCenter title:@"Sign File" fixed:NO makeKey:YES];
228}
229
230- (IBAction)signFiles:(id)sender {
231  KBPGPSignFilesView *view = [[KBPGPSignFilesView alloc] init];
232  view.client = self.service.client;
233  [self.mainWindow kb_addChildWindowForView:view rect:CGRectMake(0, 0, 510, 400) position:KBWindowPositionCenter title:@"Sign Files" fixed:NO makeKey:YES];
234}
235
236- (IBAction)verify:(id)sender {
237  KBPGPVerifyView *view = [[KBPGPVerifyView alloc] init];
238  view.client = self.service.client;
239  [self.mainWindow kb_addChildWindowForView:view rect:CGRectMake(0, 0, 510, 400) position:KBWindowPositionCenter title:@"Verify" fixed:NO makeKey:YES];
240}
241
242- (IBAction)verifyFile:(id)sender {
243  KBPGPVerifyFileView *view = [[KBPGPVerifyFileView alloc] init];
244  view.client = self.service.client;
245  [self.mainWindow kb_addChildWindowForView:view rect:CGRectMake(0, 0, 510, 400) position:KBWindowPositionCenter title:@"Verify File" fixed:NO makeKey:YES];
246}
247
248@end
249