mirror of
https://github.com/kennethreitz-archive/gitx.git
synced 2026-06-05 23:40:18 +00:00
PBWebController: Add asynchronous functions
This allows us to keep the UI responsive while running expensive commands
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
Introduction
|
||||
============
|
||||
|
||||
A lot of the display code in GitX is based on the HTML views that are rendered
|
||||
by WebKit. As such, it's necessary to access a lot of information from WebKit
|
||||
that is only available in the Objective-C environment.
|
||||
|
||||
Because of this, some convenience methods have been added. Most notably, some
|
||||
objects can be accessed completely from WebKit, including:
|
||||
|
||||
* All webcontrollers
|
||||
* All PBGitRefs and PBGitCommits
|
||||
|
||||
All WebKit views should be controlled by a subclass of PBWebController.
|
||||
|
||||
|
||||
Subclassing PBWebController
|
||||
===========================
|
||||
|
||||
PBWebController does some magic to make using it easier. First of all, it
|
||||
automatically loads in the view specified by "startFile". This means that
|
||||
your views have to conform to a specific structure. For example, if you set
|
||||
startFile (in your awakeFromNib) to "commit", there should be a file named
|
||||
html/views/commit/index.html that will get loaded.
|
||||
|
||||
Furthermore, the controller keeps a finishedLoading member that indicates
|
||||
whether or not the webview is ready to be used. If it is ready, and your
|
||||
subclass has implemented the didLoad method, that method will be called.
|
||||
|
||||
After the document has succesfully loaded, the controller inserts itself into
|
||||
the Javascript environment under the "Controller" variable.
|
||||
|
||||
Useful PBWebController functions
|
||||
================================
|
||||
|
||||
There are some functions that might be useful in any context. These are put in
|
||||
the PBWebController class for easy access. To access these, replace the colons
|
||||
with underscores in the JS call. for example,
|
||||
|
||||
- (void) log:(NSString) logMessage
|
||||
|
||||
would be called in JS by:
|
||||
|
||||
Controller.log_("Hello from javascript!");
|
||||
|
||||
## - (void) log:(NSString) logMessage
|
||||
|
||||
This method simply relays a JS message to the program, which outputs it using
|
||||
NSLog. Handy for debugging.
|
||||
|
||||
## - (BOOL) isReachable:(NSString *)hostname
|
||||
|
||||
Will return true if the hostname is reachable from Javascript without creating
|
||||
an internet connection (for example, dialup). This is useful for optional
|
||||
features that depend on a working internet connection.
|
||||
|
||||
** NOTE ** The GitX JS environment is _NOT_ secure, which means that external
|
||||
HTTP calls should only be made to trusted sites. For example, DON'T open any
|
||||
link in a commit message, as this could potentially allow any site to run any
|
||||
command on your mac.
|
||||
|
||||
## - (void) runCommand:(WebScriptObject *)arguments
|
||||
inRepository:(PBGitRepository *)repository
|
||||
callBack:(WebScriptObject *)callBack
|
||||
|
||||
This somewhat specific function allows you to call any git command and have
|
||||
its output returned to a callback function. For example, the following:
|
||||
|
||||
Controller.runCommand_inRepository_callBack_(["--version"], obj.repository,
|
||||
function(v) { notify("Git Version: " + v, 1); });
|
||||
|
||||
will show a notification displaying the current git version. This method is
|
||||
async, which means that it doesn't matter if it takes a long time to run.
|
||||
|
||||
## - (void) callSelector:(NSString *)selectorString
|
||||
onObject:(id)object
|
||||
callBack:(WebScriptObject *)callBack
|
||||
|
||||
While you can have direct access to most objects, sometimes it is useful to
|
||||
have an async task, for example if the operation can take a long time. Use
|
||||
this function to keep the UI responsive.
|
||||
@@ -381,8 +381,8 @@
|
||||
911111F60E594F3F00BF76B4 /* PBRepositoryDocumentController.h */,
|
||||
911111F70E594F3F00BF76B4 /* PBRepositoryDocumentController.m */,
|
||||
F5E926040E8827D300056E75 /* PBViewController.h */,
|
||||
F5EF8C8C0E9D4A5D0050906B /* PBWebController.h */,
|
||||
F5E926050E8827D300056E75 /* PBViewController.m */,
|
||||
F5EF8C8C0E9D4A5D0050906B /* PBWebController.h */,
|
||||
F5EF8C8D0E9D4A5D0050906B /* PBWebController.m */,
|
||||
F5FE6C010EB13BC900F30D12 /* PBServicesController.h */,
|
||||
F5FE6C020EB13BC900F30D12 /* PBServicesController.m */,
|
||||
|
||||
@@ -13,6 +13,9 @@
|
||||
IBOutlet WebView* view;
|
||||
NSString *startFile;
|
||||
BOOL finishedLoading;
|
||||
|
||||
// For async git reading
|
||||
NSMapTable *callbacks;
|
||||
}
|
||||
|
||||
@property (retain) NSString *startFile;
|
||||
|
||||
+62
-3
@@ -7,7 +7,8 @@
|
||||
//
|
||||
|
||||
#import "PBWebController.h"
|
||||
|
||||
#import "PBGitRepository.h"
|
||||
#include <SystemConfiguration/SCNetworkReachability.h>
|
||||
|
||||
@implementation PBWebController
|
||||
|
||||
@@ -19,6 +20,7 @@
|
||||
NSString* file = [[NSBundle mainBundle] pathForResource:@"index" ofType:@"html" inDirectory:path];
|
||||
NSLog(@"path: %@, file: %@", path, file);
|
||||
NSURLRequest * request = [NSURLRequest requestWithURL:[NSURL fileURLWithPath:file]];
|
||||
callbacks = [NSMapTable mapTableWithKeyOptions:(NSPointerFunctionsObjectPointerPersonality|NSPointerFunctionsStrongMemory) valueOptions:(NSPointerFunctionsObjectPointerPersonality|NSPointerFunctionsStrongMemory)];
|
||||
|
||||
finishedLoading = NO;
|
||||
[view setUIDelegate:self];
|
||||
@@ -51,13 +53,12 @@
|
||||
}
|
||||
|
||||
#pragma mark Functions to be used from JavaScript
|
||||
|
||||
- (void) log: (NSString*) logMessage
|
||||
{
|
||||
NSLog(@"%@", logMessage);
|
||||
}
|
||||
|
||||
#include <SystemConfiguration/SCNetworkReachability.h>
|
||||
|
||||
- (BOOL) isReachable:(NSString *)hostname
|
||||
{
|
||||
SCNetworkConnectionFlags flags;
|
||||
@@ -71,4 +72,62 @@
|
||||
return flags > 0;
|
||||
}
|
||||
|
||||
#pragma mark Using async function from JS
|
||||
|
||||
- (void) runCommand:(WebScriptObject *)arguments inRepository:(PBGitRepository *)repository callBack:(WebScriptObject *)callBack
|
||||
{
|
||||
// The JS bridge does not handle JS Arrays, even though the docs say it does. So, we convert it ourselves.
|
||||
int length = [[arguments valueForKey:@"length"] intValue];
|
||||
NSMutableArray *realArguments = [NSMutableArray arrayWithCapacity:length];
|
||||
int i = 0;
|
||||
for (i = 0; i < length; i++)
|
||||
[realArguments addObject:[arguments webScriptValueAtIndex:i]];
|
||||
|
||||
NSFileHandle *handle = [repository handleInWorkDirForArguments:realArguments];
|
||||
[callbacks setObject:callBack forKey:handle];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(JSRunCommandDone:) name:NSFileHandleReadToEndOfFileCompletionNotification object:handle];
|
||||
[handle readToEndOfFileInBackgroundAndNotify];
|
||||
}
|
||||
|
||||
- (void) callSelector:(NSString *)selectorString onObject:(id)object callBack:(WebScriptObject *)callBack
|
||||
{
|
||||
NSArray *arguments = [NSArray arrayWithObjects:selectorString, object, nil];
|
||||
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(runInThread:) object:arguments];
|
||||
[callbacks setObject:callBack forKey:thread];
|
||||
[thread start];
|
||||
}
|
||||
|
||||
- (void) runInThread:(NSArray *)arguments
|
||||
{
|
||||
SEL selector = NSSelectorFromString([arguments objectAtIndex:0]);
|
||||
id object = [arguments objectAtIndex:1];
|
||||
id ret = [object performSelector:selector];
|
||||
NSArray *returnArray = [NSArray arrayWithObjects:[NSThread currentThread], ret, nil];
|
||||
[self performSelectorOnMainThread:@selector(threadFinished:) withObject:returnArray waitUntilDone:NO];
|
||||
}
|
||||
|
||||
|
||||
- (void) returnCallBackForObject:(id)object withData:(id)data
|
||||
{
|
||||
WebScriptObject *a = [callbacks objectForKey: object];
|
||||
if (!a) {
|
||||
NSLog(@"Could not find a callback for object: %@", object);
|
||||
return;
|
||||
}
|
||||
|
||||
[callbacks removeObjectForKey:object];
|
||||
[a callWebScriptMethod:@"call" withArguments:[NSArray arrayWithObjects:@"", data, nil]];
|
||||
}
|
||||
|
||||
- (void) threadFinished:(NSArray *)arguments
|
||||
{
|
||||
[self returnCallBackForObject:[arguments objectAtIndex:0] withData:[arguments objectAtIndex:1]];
|
||||
}
|
||||
|
||||
- (void) JSRunCommandDone:(NSNotification *)notification
|
||||
{
|
||||
NSString *data = [[NSString alloc] initWithData:[[notification userInfo] valueForKey:NSFileHandleNotificationDataItem] encoding:NSUTF8StringEncoding];
|
||||
[self returnCallBackForObject:[notification object] withData:data];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
Reference in New Issue
Block a user