1#import "EXAppLoadingProgressWindowController.h" 2 3#import <React/React-Core-umbrella.h> // Keeps this import to fix the error from building React module: `error: definition of 'RCTBridge' must be imported from module 'React' before it is required` 4 5#import <ExpoModulesCore/EXDefines.h> 6 7#if !defined(EX_DETACHED) 8#import "Expo_Go-Swift.h" 9#endif // !defined(EX_DETACHED) 10 11#import "EXUtil.h" 12 13@interface EXAppLoadingProgressWindowController () 14 15@property (nonatomic, assign) BOOL enabled; 16@property (nonatomic, strong) UIWindow *window; 17@property (nonatomic, strong) UILabel *textLabel; 18 19@end 20 21@implementation EXAppLoadingProgressWindowController 22 23- (instancetype)initWithEnabled:(BOOL)enabled 24{ 25 if (self = [super init]) { 26 _enabled = enabled; 27 } 28 return self; 29} 30 31- (void)show 32{ 33#if !defined(EX_DETACHED) 34 if (!_enabled) { 35 return; 36 } 37 38 EX_WEAKIFY(self); 39 dispatch_async(dispatch_get_main_queue(), ^{ 40 EX_ENSURE_STRONGIFY(self); 41 if (!self.window) { 42 CGSize screenSize = [UIScreen mainScreen].bounds.size; 43 44 int bottomInsets = EXSharedApplication().keyWindow.safeAreaInsets.bottom; 45 self.window = [[UIWindow alloc] initWithFrame:CGRectMake(0, 46 screenSize.height - 36 - bottomInsets, 47 screenSize.width, 48 36 + bottomInsets)]; 49 self.window.windowLevel = UIWindowLevelStatusBar + 1; 50 self.window.rootViewController = [EXAppLoadingProgressWindowViewController new]; 51 self.window.backgroundColor = [EXUtil colorWithRGB:0xfafafa]; 52 53 UIView *containerView = [UIView new]; 54 [self.window addSubview:containerView]; 55 56 CALayer *topBorderLayer = [CALayer layer]; 57 topBorderLayer.frame = CGRectMake(0, 0, screenSize.width, 1); 58 topBorderLayer.backgroundColor = [EXUtil colorWithRGB:0xe3e3e3].CGColor; 59 [containerView.layer addSublayer:topBorderLayer]; 60 61 self.textLabel = [UILabel new]; 62 self.textLabel.frame = CGRectMake(10, 0, screenSize.width - 20, 36); 63 self.textLabel.font = [UIFont systemFontOfSize:12]; 64 self.textLabel.textAlignment = NSTextAlignmentLeft; 65 self.textLabel.textColor = [EXUtil colorWithRGB:0xa7a7a7]; 66 [containerView addSubview:self.textLabel]; 67 } 68 self.textLabel.text = @"Waiting for server ..."; 69 self.window.hidden = NO; 70 }); 71#endif // !defined(EX_DETACHED) 72} 73 74- (void)hide 75{ 76#if !defined(EX_DETACHED) 77 if (!_enabled) { 78 return; 79 } 80 81 EX_WEAKIFY(self); 82 dispatch_async(dispatch_get_main_queue(), ^{ 83 EX_ENSURE_STRONGIFY(self); 84 if (self.window) { 85 self.window.hidden = YES; 86 // remove this window altogther to hand over the command over StatusBar rotation 87 self.window = nil; 88 } 89 }); 90#endif // !defined(EX_DETACHED) 91} 92 93- (void)updateStatusWithProgress:(EXLoadingProgress *)progress 94{ 95#if !defined(EX_DETACHED) 96 if (!_enabled) { 97 return; 98 } 99 100 [self show]; 101 102 EX_WEAKIFY(self); 103 dispatch_async(dispatch_get_main_queue(), ^{ 104 EX_ENSURE_STRONGIFY(self); 105 float progressPercent = ([progress.done floatValue] / [progress.total floatValue]); 106 self.textLabel.text = [NSString stringWithFormat:@"%@ %.2f%%", progress.status, progressPercent * 100]; 107 [self.textLabel setNeedsDisplay]; 108 109 // TODO: (@bbarthec) maybe it's better to show/hide this based on other thing than progress status reported by the fetcher? 110 self.window.hidden = !(progress.total.floatValue > 0); 111 }); 112#endif // !defined(EX_DETACHED) 113} 114 115- (void)updateStatus:(EXAppLoaderRemoteUpdateStatus)status 116{ 117#if !defined(EX_DETACHED) 118 if (!_enabled) { 119 return; 120 } 121 122 NSString *statusText = [[self class] _loadingViewTextForStatus:status]; 123 if (!statusText) { 124 return; 125 } 126 127 [self show]; 128 129 EX_WEAKIFY(self); 130 dispatch_async(dispatch_get_main_queue(), ^{ 131 EX_ENSURE_STRONGIFY(self); 132 self.textLabel.text = statusText; 133 [self.textLabel setNeedsDisplay]; 134 }); 135#endif // !defined(EX_DETACHED) 136} 137 138+ (nullable NSString *)_loadingViewTextForStatus:(EXAppLoaderRemoteUpdateStatus)status 139{ 140 if (status == kEXAppLoaderRemoteUpdateStatusChecking) { 141 return @"Checking for new update..."; 142 } else if (status == kEXAppLoaderRemoteUpdateStatusDownloading) { 143 return @"New update available, downloading..."; 144 } else { 145 return nil; 146 } 147} 148 149@end 150