Skip to content

Commit

Permalink
Add progress bars for import/export
Browse files Browse the repository at this point in the history
  • Loading branch information
tbodt committed Jun 19, 2020
1 parent b4c5ba8 commit f6ecb80
Show file tree
Hide file tree
Showing 10 changed files with 318 additions and 102 deletions.
19 changes: 19 additions & 0 deletions app/ProgressReportViewController.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//
// ProgressReportViewController.h
// iSH
//
// Created by Theodore Dubois on 6/18/20.
//

#import <UIKit/UIKit.h>
#import "Roots.h"

NS_ASSUME_NONNULL_BEGIN

@interface ProgressReportViewController : UIViewController <ProgressReporter>

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

@end

NS_ASSUME_NONNULL_END
62 changes: 62 additions & 0 deletions app/ProgressReportViewController.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
//
// ProgressReportViewController.m
// iSH
//
// Created by Theodore Dubois on 6/18/20.
//

#import "ProgressReportViewController.h"

@interface ProgressReportViewController ()

@property (weak, nonatomic) IBOutlet UIView *popupView;
@property (weak, nonatomic) IBOutlet UILabel *titleLabel;
@property (weak, nonatomic) IBOutlet UILabel *statusLabel;
@property (weak, nonatomic) IBOutlet UIProgressView *bar;

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

@property CADisplayLink *timer;

@end

@implementation ProgressReportViewController

- (void)viewDidLoad {
self.titleLabel.text = self.title;
}

- (void)viewDidLayoutSubviews {
CAShapeLayer *mask = [CAShapeLayer new];
mask.path = [UIBezierPath bezierPathWithRoundedRect:self.popupView.bounds cornerRadius:13].CGPath;
self.popupView.layer.mask = mask;
self.popupView.layer.masksToBounds = YES;
}

- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
self.timer = [CADisplayLink displayLinkWithTarget:self selector:@selector(update)];
[self.timer addToRunLoop:NSRunLoop.mainRunLoop forMode:NSRunLoopCommonModes];
}

- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
[self.timer invalidate];
}

- (void)updateProgress:(double)progressFraction message:(NSString *)progressMessage {
@synchronized (self) {
_progress = progressFraction;
_message = progressMessage;
}
}

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

@end
10 changes: 7 additions & 3 deletions app/Roots.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,20 @@

NS_ASSUME_NONNULL_BEGIN

@class Root;
@protocol ProgressReporter

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

@end

@interface Roots : NSObject

+ (instancetype)instance;

@property (readonly) NSOrderedSet<NSString *> *roots;
@property NSString *defaultRoot;
- (BOOL)importRootFromArchive:(NSURL *)archive name:(NSString *)name error:(NSError **)error;
- (BOOL)exportRootNamed:(NSString *)name toArchive:(NSURL *)archive error:(NSError **)error;
- (BOOL)importRootFromArchive:(NSURL *)archive name:(NSString *)name error:(NSError **)error progressReporter:(id<ProgressReporter> _Nullable)progress;
- (BOOL)exportRootNamed:(NSString *)name toArchive:(NSURL *)archive error:(NSError **)error progressReporter:(id<ProgressReporter> _Nullable)progress;
- (BOOL)destroyRootNamed:(NSString *)name error:(NSError **)error;

@end
Expand Down
27 changes: 20 additions & 7 deletions app/Roots.m
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ - (instancetype)init {
NSError *error;
if (![self importRootFromArchive:[NSBundle.mainBundle URLForResource:@"alpine" withExtension:@"tar.gz"]
name:@"alpine"
error:&error]) {
error:&error
progressReporter:nil]) {
NSAssert(NO, @"failed to import alpine, error %@", error);
}
}
Expand Down Expand Up @@ -108,27 +109,39 @@ - (BOOL)accessInstanceVariablesDirectly {
return YES;
}

- (BOOL)importRootFromArchive:(NSURL *)archive name:(NSString *)name error:(NSError **)error {
void root_progress_callback(void *cookie, double progress, const char *message) {
id <ProgressReporter> reporter = (__bridge id<ProgressReporter>) cookie;
[reporter updateProgress:progress message:[NSString stringWithUTF8String:message]];
}

- (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;
if (!fakefs_import(archive.fileSystemRepresentation, [RootsDir() URLByAppendingPathComponent:name].fileSystemRepresentation, &fs_err)) {
if (!fakefs_import(archive.fileSystemRepresentation,
[RootsDir() URLByAppendingPathComponent:name].fileSystemRepresentation,
&fs_err, (struct progress) {(__bridge void *) progress, root_progress_callback})) {
NSString *domain = NSPOSIXErrorDomain;
if (fs_err.type == ERR_SQLITE)
domain = @"SQLite";
*error = [NSError errorWithDomain:domain
code:fs_err.code
userInfo:@{NSLocalizedDescriptionKey: [NSString stringWithUTF8String:fs_err.message]}];
userInfo:@{NSLocalizedDescriptionKey:
[NSString stringWithFormat:@"%s, line %d", fs_err.message, fs_err.line]}];
free(fs_err.message);
return NO;
}
[[self mutableOrderedSetValueForKey:@"roots"] addObject:name];
dispatch_async(dispatch_get_main_queue(), ^{
[[self mutableOrderedSetValueForKey:@"roots"] addObject:name];
});
return YES;
}

- (BOOL)exportRootNamed:(NSString *)name toArchive:(NSURL *)archive error:(NSError **)error {
- (BOOL)exportRootNamed:(NSString *)name toArchive:(NSURL *)archive error:(NSError **)error progressReporter:(id<ProgressReporter> _Nullable)progress {
NSAssert([self.roots containsObject:name], @"trying to export a root that doesn't exist: %@", name);
struct fakefsify_error fs_err;
if (!fakefs_export([RootsDir() URLByAppendingPathComponent:name].fileSystemRepresentation, archive.fileSystemRepresentation, &fs_err)) {
if (!fakefs_export([RootsDir() URLByAppendingPathComponent:name].fileSystemRepresentation,
archive.fileSystemRepresentation,
&fs_err, (struct progress) {(__bridge void *) progress, root_progress_callback})) {
// TODO: dedup with above method
NSString *domain = NSPOSIXErrorDomain;
if (fs_err.type == ERR_SQLITE)
Expand Down
Loading

0 comments on commit f6ecb80

Please sign in to comment.