Skip to content

Commit

Permalink
Add cancel buttons for import and export
Browse files Browse the repository at this point in the history
  • Loading branch information
tbodt committed Jun 19, 2020
1 parent f6ecb80 commit 2f28e6a
Show file tree
Hide file tree
Showing 7 changed files with 92 additions and 30 deletions.
15 changes: 15 additions & 0 deletions app/ProgressReportViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@ @interface ProgressReportViewController ()
@property (weak, nonatomic) IBOutlet UILabel *titleLabel;
@property (weak, nonatomic) IBOutlet UILabel *statusLabel;
@property (weak, nonatomic) IBOutlet UIProgressView *bar;
@property (weak, nonatomic) IBOutlet UIButton *cancelButton;

@property (nonatomic) double progress;
@property (nonatomic) NSString *message;
@property (nonatomic) BOOL cancelled;

@property CADisplayLink *timer;

Expand Down Expand Up @@ -52,11 +54,24 @@ - (void)updateProgress:(double)progressFraction message:(NSString *)progressMess
}
}

- (BOOL)shouldCancel {
@synchronized (self) {
return _cancelled;
}
}

- (void)update {
@synchronized (self) {
self.bar.progress = _progress;
self.statusLabel.text = _message;
}
}

- (IBAction)cancel:(id)sender {
@synchronized (self) {
self.cancelled = YES;
self.cancelButton.enabled = NO;
}
}

@end
1 change: 1 addition & 0 deletions app/Roots.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ NS_ASSUME_NONNULL_BEGIN
@protocol ProgressReporter

- (void)updateProgress:(double)progressFraction message:(NSString *)progressMessage;
- (BOOL)shouldCancel;

@end

Expand Down
18 changes: 16 additions & 2 deletions app/Roots.m
Original file line number Diff line number Diff line change
Expand Up @@ -109,16 +109,23 @@ - (BOOL)accessInstanceVariablesDirectly {
return YES;
}

void root_progress_callback(void *cookie, double progress, const char *message) {
void root_progress_callback(void *cookie, double progress, const char *message, bool *should_cancel) {
id <ProgressReporter> reporter = (__bridge id<ProgressReporter>) cookie;
[reporter updateProgress:progress message:[NSString stringWithUTF8String:message]];
if ([reporter shouldCancel])
*should_cancel = true;
}

- (BOOL)importRootFromArchive:(NSURL *)archive name:(NSString *)name error:(NSError **)error progressReporter:(id<ProgressReporter> _Nullable)progress {
NSAssert(![self.roots containsObject:name], @"root already exists: %@", name);
struct fakefsify_error fs_err;
NSURL *destination = [RootsDir() URLByAppendingPathComponent:name];
NSURL *tempDestination = [NSFileManager.defaultManager.temporaryDirectory
URLByAppendingPathComponent:[NSProcessInfo.processInfo globallyUniqueString]];
if (tempDestination == nil)
return NO;
if (!fakefs_import(archive.fileSystemRepresentation,
[RootsDir() URLByAppendingPathComponent:name].fileSystemRepresentation,
tempDestination.fileSystemRepresentation,
&fs_err, (struct progress) {(__bridge void *) progress, root_progress_callback})) {
NSString *domain = NSPOSIXErrorDomain;
if (fs_err.type == ERR_SQLITE)
Expand All @@ -127,9 +134,14 @@ - (BOOL)importRootFromArchive:(NSURL *)archive name:(NSString *)name error:(NSEr
code:fs_err.code
userInfo:@{NSLocalizedDescriptionKey:
[NSString stringWithFormat:@"%s, line %d", fs_err.message, fs_err.line]}];
if (fs_err.type == ERR_CANCELLED)
*error = nil;
free(fs_err.message);
[NSFileManager.defaultManager removeItemAtURL:tempDestination error:nil];
return NO;
}
if (![NSFileManager.defaultManager moveItemAtURL:tempDestination toURL:destination error:error])
return NO;
dispatch_async(dispatch_get_main_queue(), ^{
[[self mutableOrderedSetValueForKey:@"roots"] addObject:name];
});
Expand All @@ -149,6 +161,8 @@ - (BOOL)exportRootNamed:(NSString *)name toArchive:(NSURL *)archive error:(NSErr
*error = [NSError errorWithDomain:domain
code:fs_err.code
userInfo:@{NSLocalizedDescriptionKey: [NSString stringWithUTF8String:fs_err.message]}];
if (fs_err.type == ERR_CANCELLED)
*error = nil;
free(fs_err.message);
return NO;
}
Expand Down
25 changes: 21 additions & 4 deletions app/Roots.storyboard
Original file line number Diff line number Diff line change
Expand Up @@ -223,12 +223,12 @@
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="2X7-vG-Bav">
<rect key="frame" x="149" y="111" width="270" height="98.5"/>
<rect key="frame" x="149" y="96" width="270" height="128.5"/>
<subviews>
<visualEffectView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="fRw-kV-GMw">
<rect key="frame" x="0.0" y="0.0" width="270" height="98.5"/>
<rect key="frame" x="0.0" y="0.0" width="270" height="128.5"/>
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" id="X5o-tI-i04">
<rect key="frame" x="0.0" y="0.0" width="270" height="98.5"/>
<rect key="frame" x="0.0" y="0.0" width="270" height="128.5"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
</view>
<blurEffect style="systemMaterial"/>
Expand All @@ -253,18 +253,34 @@
</progressView>
</subviews>
</stackView>
<stackView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="dPf-45-cvF" userLabel="Buttons Stack View">
<rect key="frame" x="0.0" y="88.5" width="270" height="30"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Uvf-lb-Web">
<rect key="frame" x="0.0" y="0.0" width="270" height="30"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="15"/>
<state key="normal" title="Cancel"/>
<connections>
<action selector="cancel:" destination="KZI-b6-Ikz" eventType="touchUpInside" id="qYx-d0-Bt9"/>
</connections>
</button>
</subviews>
</stackView>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstItem="fRw-kV-GMw" firstAttribute="leading" secondItem="2X7-vG-Bav" secondAttribute="leading" id="0ku-0m-ybR"/>
<constraint firstAttribute="width" constant="270" id="1Wv-C6-zec">
<variation key="widthClass=regular" constant="500"/>
</constraint>
<constraint firstAttribute="bottom" secondItem="kaW-sc-dJA" secondAttribute="bottom" constant="20" id="EaF-lb-XpA"/>
<constraint firstAttribute="trailing" secondItem="fRw-kV-GMw" secondAttribute="trailing" id="Q3p-TZ-7yO"/>
<constraint firstAttribute="trailing" secondItem="dPf-45-cvF" secondAttribute="trailing" id="TAx-BY-2Ot"/>
<constraint firstItem="dPf-45-cvF" firstAttribute="leading" secondItem="2X7-vG-Bav" secondAttribute="leading" id="TXU-sz-q8H"/>
<constraint firstItem="kaW-sc-dJA" firstAttribute="top" secondItem="2X7-vG-Bav" secondAttribute="top" constant="20" id="V7X-IN-6rE"/>
<constraint firstItem="fRw-kV-GMw" firstAttribute="top" secondItem="2X7-vG-Bav" secondAttribute="top" id="inx-IG-lJ3"/>
<constraint firstItem="kaW-sc-dJA" firstAttribute="leading" secondItem="2X7-vG-Bav" secondAttribute="leading" constant="20" id="l5t-QP-ZpO"/>
<constraint firstItem="dPf-45-cvF" firstAttribute="top" secondItem="kaW-sc-dJA" secondAttribute="bottom" constant="10" id="mVP-ot-7GR"/>
<constraint firstAttribute="bottom" secondItem="dPf-45-cvF" secondAttribute="bottom" constant="10" id="nzx-Po-AXk"/>
<constraint firstAttribute="bottom" secondItem="fRw-kV-GMw" secondAttribute="bottom" id="vIb-Jh-Roo"/>
<constraint firstAttribute="trailing" secondItem="kaW-sc-dJA" secondAttribute="trailing" constant="20" id="zVd-X8-Plf"/>
</constraints>
Expand All @@ -280,6 +296,7 @@
<value key="contentSizeForViewInPopover" type="size" width="100" height="100"/>
<connections>
<outlet property="bar" destination="G6p-Tb-Jdw" id="8cY-YK-FAJ"/>
<outlet property="cancelButton" destination="Uvf-lb-Web" id="fJC-F3-qiT"/>
<outlet property="popupView" destination="2X7-vG-Bav" id="1DC-WM-4JH"/>
<outlet property="statusLabel" destination="QK2-Aw-TuX" id="1LM-hx-ZjX"/>
<outlet property="titleLabel" destination="uls-2R-fu1" id="m0x-lG-u1k"/>
Expand Down
47 changes: 28 additions & 19 deletions app/RootsTableViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,10 @@ - (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocum
NSError *error;
BOOL success = [Roots.instance importRootFromArchive:urls[0] name:name error:&error progressReporter:progressVC];
dispatch_async(dispatch_get_main_queue(), ^{
[progressVC dismissViewControllerAnimated:YES completion:nil];
if (!success)
[self presentError:error title:@"Import failed"];
[progressVC dismissViewControllerAnimated:YES completion:^{
if (!success && error != nil)
[self presentError:error title:@"Import failed"];
}];
});
});
}
Expand Down Expand Up @@ -153,7 +154,13 @@ - (void)browseFiles {
}

- (void)exportFilesystem {
self.exportURL = [NSFileManager.defaultManager.temporaryDirectory URLByAppendingPathComponent:[NSString stringWithFormat:@"%@.tar.gz", self.rootName]];
self.exportURL = [[NSFileManager.defaultManager.temporaryDirectory
URLByAppendingPathComponent:[NSProcessInfo.processInfo globallyUniqueString]]
URLByAppendingPathComponent:[NSString stringWithFormat:@"%@.tar.gz", self.rootName]];
[NSFileManager.defaultManager createDirectoryAtURL:self.exportURL.URLByDeletingLastPathComponent
withIntermediateDirectories:YES
attributes:nil
error:nil];
ProgressReportViewController *progressVC = [self.storyboard instantiateViewControllerWithIdentifier:@"progress"];
progressVC.title = [NSString stringWithFormat:@"Exporting %@", self.rootName];
[self presentViewController:progressVC animated:YES completion:nil];
Expand All @@ -163,26 +170,28 @@ - (void)exportFilesystem {
NSError *err;
BOOL success = [Roots.instance exportRootNamed:self.rootName toArchive:self.exportURL error:&err progressReporter:progressVC];
dispatch_async(dispatch_get_main_queue(), ^{
[progressVC dismissViewControllerAnimated:YES completion:nil];
if (!success) {
[self presentError:err title:@"Export failed"];
return;
}

UIDocumentPickerViewController *picker = [[UIDocumentPickerViewController alloc]
initWithURL:self.exportURL
inMode:UIDocumentPickerModeExportToService];
picker.delegate = self;
if (@available(iOS 13, *)) {
picker.shouldShowFileExtensions = YES;
}
[self presentViewController:picker animated:YES completion:nil];
[progressVC dismissViewControllerAnimated:YES completion:^{
if (!success) {
if (err != nil)
[self presentError:err title:@"Export failed"];
return;
}

UIDocumentPickerViewController *picker = [[UIDocumentPickerViewController alloc]
initWithURL:self.exportURL
inMode:UIDocumentPickerModeExportToService];
picker.delegate = self;
if (@available(iOS 13, *)) {
picker.shouldShowFileExtensions = YES;
}
[self presentViewController:picker animated:YES completion:nil];
}];
});
});
}

- (void)setExportURL:(NSURL *)exportURL {
[NSFileManager.defaultManager removeItemAtURL:_exportURL error:nil];
[NSFileManager.defaultManager removeItemAtURL:_exportURL.URLByDeletingLastPathComponent error:nil];
_exportURL = exportURL;
}

Expand Down
13 changes: 9 additions & 4 deletions fs/fakefsify.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,13 @@
#define POSIX_ERR() FILL_ERR(ERR_POSIX, errno, strerror(errno))
#undef HANDLE_ERR // for sqlite
#define HANDLE_ERR(db) FILL_ERR(ERR_SQLITE, sqlite3_extended_errcode(db), sqlite3_errmsg(db))
#define CANCEL() FILL_ERR(ERR_CANCELLED, 0, "");

static void progress_update(struct progress *p, double progress, const char *message) {
static bool progress_update(struct progress *p, double progress, const char *message) {
bool cancelled = false;
if (p && p->callback)
p->callback(p->cookie, progress, message);
p->callback(p->cookie, progress, message, &cancelled);
return !cancelled;
}

// This isn't linked with ish which is why there's so much copy/pasted code
Expand Down Expand Up @@ -119,7 +122,8 @@ bool fakefs_import(const char *archive_path, const char *fs, struct fakefsify_er
fprintf(stderr, "warning: skipped possible path traversal %s\n", archive_entry_pathname(entry));
continue;
}
progress_update(&p, (double) archive_filter_bytes(archive, -1) / archive_bytes, entry_path);
if (!progress_update(&p, (double) archive_filter_bytes(archive, -1) / archive_bytes, entry_path))
CANCEL();

int fd = -1;
if (archive_entry_filetype(entry) != AE_IFDIR) {
Expand Down Expand Up @@ -229,7 +233,8 @@ bool fakefs_export(const char *fs, const char *archive_path, struct fakefsify_er
path[path_len + 1] = '\0';
archive_entry_set_pathname(entry, path);

progress_update(&p, (double) paths_done / paths_total, path);
if (!progress_update(&p, (double) paths_done / paths_total, path))
CANCEL();

struct ish_stat stat = *(struct ish_stat *) sqlite3_column_blob(query, 1);
archive_entry_set_mode(entry, stat.mode);
Expand Down
3 changes: 2 additions & 1 deletion fs/fakefsify.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,15 @@ struct fakefsify_error {
ERR_ARCHIVE,
ERR_SQLITE,
ERR_POSIX,
ERR_CANCELLED,
} type;
int code;
char *message;
};

struct progress {
void *cookie;
void (*callback)(void *cookie, double progress, const char *message);
void (*callback)(void *cookie, double progress, const char *message, bool *cancel_out);
};

bool fakefs_import(const char *archive_path, const char *fs, struct fakefsify_error *err_out, struct progress progress);
Expand Down

0 comments on commit 2f28e6a

Please sign in to comment.