forked from cory/tildefriends
257 lines
11 KiB
Objective-C
257 lines
11 KiB
Objective-C
#import <CoreSpotlight/CSSearchableIndex.h>
|
|
#import <CoreSpotlight/CSSearchableItem.h>
|
|
#import <CoreSpotlight/CSSearchableItemAttributeSet.h>
|
|
#import <UIKit/UIKit.h>
|
|
#import <WebKit/WKDownload.h>
|
|
#import <WebKit/WKDownloadDelegate.h>
|
|
#import <WebKit/WKNavigationAction.h>
|
|
#import <WebKit/WKNavigationDelegate.h>
|
|
#import <WebKit/WKNavigationResponse.h>
|
|
#import <WebKit/WKUIDelegate.h>
|
|
#import <WebKit/WKWebView.h>
|
|
#import <WebKit/WKWebViewConfiguration.h>
|
|
|
|
#include "log.h"
|
|
#include "util.js.h"
|
|
|
|
#include <libgen.h>
|
|
#include <string.h>
|
|
|
|
void tf_run_thread_start(const char* zip_path);
|
|
|
|
@interface ViewController : UINavigationController <WKUIDelegate, WKNavigationDelegate, WKDownloadDelegate, UIDocumentPickerDelegate>
|
|
@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<NSURL*>*)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 <UIApplicationDelegate>
|
|
@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<id<UIUserActivityRestoring>>*))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");
|
|
}
|