#import #import #import #import #import #import #import #import #import #import #import #import #include "log.h" #include "util.js.h" #include #include void tf_run_thread_start(const char* zip_path); @interface ViewController : UINavigationController @property (strong, nonatomic) WKWebView* web_view; @property bool initial_load_complete; @property (retain) NSURL* download_url; @end static void _start_initial_load(WKWebView* web_view) { [web_view loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://localhost:12345/login/auto"]]]; } @implementation ViewController : UINavigationController - (void)viewDidLoad { [super viewDidLoad]; WKWebViewConfiguration* configuration = [[WKWebViewConfiguration alloc] init]; self.web_view = [[WKWebView alloc] initWithFrame:self.view.frame configuration:configuration]; self.web_view.UIDelegate = self; self.web_view.navigationDelegate = self; self.web_view.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; self.web_view.translatesAutoresizingMaskIntoConstraints = false; self.web_view.allowsBackForwardNavigationGestures = true; [self.view addSubview:self.web_view]; UIRefreshControl* refresh = [[UIRefreshControl alloc] init]; [refresh addTarget:self action:@selector(handleRefresh:) forControlEvents:UIControlEventValueChanged]; self.web_view.scrollView.refreshControl = refresh; [self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.web_view attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeTop multiplier:1.0 constant:0]]; [self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.web_view attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeBottom multiplier:1.0 constant:-75]]; [self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.web_view attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeLeft multiplier:1.0 constant:0]]; [self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.web_view attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeRight multiplier:1.0 constant:0]]; _start_initial_load(self.web_view); } - (void)handleRefresh:(id)sender { tf_printf("refresh\n"); [self.web_view reload]; } - (void)webView:(WKWebView*)webView didFinishNavigation:(WKNavigation*)navigation { if (!self.initial_load_complete) { tf_printf("initial load complete\n"); self.initial_load_complete = true; } self.navigationController.interactivePopGestureRecognizer.enabled = self.web_view.canGoBack; [self.web_view.scrollView.refreshControl endRefreshing]; } - (void)webView:(WKWebView*)webView didFailProvisionalNavigation:(WKNavigation*)navigation withError:(NSError*)error { if (!self.initial_load_complete) { _start_initial_load(self.web_view); } } - (void)webView:(WKWebView*)webView runJavaScriptConfirmPanelWithMessage:(NSString*)message initiatedByFrame:(WKFrameInfo*)frame completionHandler:(void (^)(BOOL result))completionHandler { UIAlertController* alertController = [UIAlertController alertControllerWithTitle:message message:nil preferredStyle:UIAlertControllerStyleAlert]; [alertController addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action) { completionHandler(true); }]]; [alertController addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction* action) { completionHandler(false); }]]; [self presentViewController:alertController animated:YES completion:^ {}]; } - (void)webView:(WKWebView*)webView runJavaScriptAlertPanelWithMessage:(NSString*)message initiatedByFrame:(WKFrameInfo*)frame completionHandler:(void (^)(void))completionHandler { UIAlertController* alertController = [UIAlertController alertControllerWithTitle:message message:nil preferredStyle:UIAlertControllerStyleAlert]; [alertController addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action) { completionHandler(); }]]; [self presentViewController:alertController animated:YES completion:^ {}]; } - (void)webView:(WKWebView*)webView runJavaScriptTextInputPanelWithPrompt:(NSString*)prompt defaultText:(NSString*)defaultText initiatedByFrame:(WKFrameInfo*)frame completionHandler:(void (^)(NSString*))completionHandler { NSString* sender = [NSString stringWithFormat:@"%@", self.web_view.URL.host]; UIAlertController* alertController = [UIAlertController alertControllerWithTitle:prompt message:sender preferredStyle:UIAlertControllerStyleAlert]; [alertController addTextFieldWithConfigurationHandler:^(UITextField* textField) { textField.placeholder = defaultText; textField.text = defaultText; }]; [alertController addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action) { NSString* input = ((UITextField*)alertController.textFields.firstObject).text; completionHandler(input); }]]; [alertController addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction* action) { completionHandler(nil); }]]; [self presentViewController:alertController animated:YES completion:^ {}]; } - (void)webView:(WKWebView*)webView decidePolicyForNavigationAction:(WKNavigationAction*)navigationAction decisionHandler:(void (^)(enum WKNavigationActionPolicy))decisionHandler { decisionHandler(navigationAction.shouldPerformDownload ? WKNavigationActionPolicyDownload : WKNavigationActionPolicyAllow); } - (void)webView:(WKWebView*)webView decidePolicyForNavigationResponse:(WKNavigationResponse*)navigationResponse decisionHandler:(void (^)(enum WKNavigationResponsePolicy))decisionHandler { decisionHandler(navigationResponse.canShowMIMEType ? WKNavigationResponsePolicyAllow : WKNavigationResponsePolicyDownload); } - (void)webView:(WKWebView*)webView navigationAction:(WKNavigationAction*)navigationAction didBecomeDownload:(WKDownload*)download { download.delegate = self; } - (void)webView:(WKWebView*)webView navigationResponse:(WKNavigationResponse*)navigationResponse didBecomeDownload:(WKDownload*)download { download.delegate = self; } - (void)download:(WKDownload*)download decideDestinationUsingResponse:(NSURLResponse*)response suggestedFilename:(NSString*)suggestedFilename completionHandler:(void (^)(NSURL*))completionHandler { self.download_url = [[NSURL fileURLWithPath:NSTemporaryDirectory()] URLByAppendingPathComponent:suggestedFilename]; completionHandler(self.download_url); } - (void)downloadDidFinish:(WKDownload*)download { UIDocumentPickerViewController* picker = [[UIDocumentPickerViewController alloc] initForExportingURLs:@[ self.download_url ]]; picker.delegate = self; [self presentViewController:picker animated:YES completion:nil]; } - (void)download:(WKDownload*)download didFailWithError:(NSError*)error resumeData:(NSData*)resumeData { tf_printf("download didFailWithError:%s\n", [error.localizedDescription UTF8String]); } - (void)documentPicker:(UIDocumentPickerViewController*)controller didPickDocumentAtURLs:(NSArray*)urls { [[NSFileManager defaultManager] removeItemAtURL:self.download_url error:nil]; } - (void)documentPickerWasCancelled:(UIDocumentPickerViewController*)controller { [[NSFileManager defaultManager] removeItemAtURL:self.download_url error:nil]; } @end @interface AppDelegate : UIResponder @property (strong, nonatomic) UIWindow* window; @end @implementation AppDelegate - (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions { self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; ViewController* view_controller = [[ViewController alloc] init]; self.window.rootViewController = view_controller; [self.window makeKeyAndVisible]; return YES; } - (BOOL)application:(UIApplication*)application continueUserActivity:(NSUserActivity*)activity restorationHandler:(void (^)(NSArray>*))restorationHandler { if ([activity.activityType isEqual:CSSearchableItemActionType]) { const char* identifier = [[activity.userInfo valueForKey:CSSearchableItemActivityIdentifier] UTF8String]; tf_printf("Jumping to search result: %s.\n", identifier); char url[1024]; snprintf(url, sizeof(url), "http://localhost:12345/~core/ssb/#%s", identifier); tf_printf("Navigating to %s.", url); ViewController* view_controller = (ViewController*)self.window.rootViewController; [view_controller.web_view loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithUTF8String:url]]]]; } else { tf_printf("no search\n"); } return NO; } @end void tf_notify_message_added_ios(const char* identifier, const char* title, const char* content) { tf_printf("indexing: identifier=%s title=%s content=%s\n", identifier, title, content); CSSearchableItemAttributeSet* attribute_set = [[CSSearchableItemAttributeSet alloc] initWithContentType:UTTypeText]; attribute_set.title = [NSString stringWithUTF8String:content]; attribute_set.contentDescription = [NSString stringWithUTF8String:title]; CSSearchableItem* item = [[CSSearchableItem alloc] initWithUniqueIdentifier:[NSString stringWithUTF8String:identifier] domainIdentifier:@"com.unprompted.tildefriends.messages" attributeSet:attribute_set]; [[CSSearchableIndex defaultSearchableIndex] indexSearchableItems:@[ item ] completionHandler:^(NSError* _Nullable error) { if (error) { tf_printf("indexing error: %s.\n", [error.localizedDescription UTF8String]); } else { tf_printf("indexed successfully.\n"); } }]; } int main(int argc, char* argv[]) { NSFileManager* file_manager = [NSFileManager defaultManager]; NSString* library_directory = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) objectAtIndex:0]; [file_manager changeCurrentDirectoryPath:library_directory]; size_t path_length = strlen(argv[0]) - strlen(basename(argv[0])); size_t length = path_length + strlen("data.zip"); char* zip_path = alloca(length + 1); snprintf(zip_path, length + 1, "%.*sdata.zip", (int)path_length, argv[0]); tf_run_thread_start(zip_path); return UIApplicationMain(argc, argv, nil, @"AppDelegate"); }