1// Copyright 2015-present 650 Industries. All rights reserved. 2 3#import "EXErrorView.h" 4#import "EXKernel.h" 5#import "EXKernelAppLoader.h" 6#import "EXKernelAppRecord.h" 7#import "EXUtil.h" 8 9@interface EXErrorView () 10 11@property (nonatomic, strong) UILabel *lblError; 12@property (nonatomic, strong) UIButton *btnRetry; 13@property (nonatomic, strong) UIButton *btnBack; 14@property (nonatomic, strong) UILabel *lblUrl; 15@property (nonatomic, strong) UILabel *lblErrorDetail; 16@property (nonatomic, strong) UIScrollView *vContainer; 17 18- (void)_onTapRetry; 19 20@end 21 22@implementation EXErrorView 23 24- (instancetype)initWithFrame:(CGRect)frame 25{ 26 if (self = [super initWithFrame:frame]) { 27 self.backgroundColor = [UIColor whiteColor]; 28 29 self.vContainer = [[UIScrollView alloc] init]; 30 [self addSubview:_vContainer]; 31 32 // error description label 33 self.lblError = [[UILabel alloc] init]; 34 _lblError.numberOfLines = 0; 35 _lblError.textAlignment = NSTextAlignmentCenter; 36 _lblError.font = [UIFont systemFontOfSize:14.0f]; 37 [_vContainer addSubview:_lblError]; 38 39 // retry button 40 self.btnRetry = [UIButton buttonWithType:UIButtonTypeRoundedRect]; 41 [_btnRetry setTitle:@"Try Again" forState:UIControlStateNormal]; 42 [_btnRetry addTarget:self action:@selector(_onTapRetry) forControlEvents:UIControlEventTouchUpInside]; 43 [_vContainer addSubview:_btnRetry]; 44 45 // back button 46 self.btnBack = [UIButton buttonWithType:UIButtonTypeRoundedRect]; 47 [_btnBack setTitle:@"Go back to Expo Home" forState:UIControlStateNormal]; 48 [_btnBack addTarget:self action:@selector(_onTapBack) forControlEvents:UIControlEventTouchUpInside]; 49 [_vContainer addSubview:_btnBack]; 50 51 for (UIButton *btnToStyle in @[ _btnRetry, _btnBack ]) { 52 [btnToStyle setTintColor:[EXUtil colorWithRGB:0x49a7e8]]; 53 [btnToStyle.titleLabel setFont:[UIFont boldSystemFontOfSize:14.0f]]; 54 } 55 56 // url label 57 self.lblUrl = [[UILabel alloc] init]; 58 _lblUrl.textAlignment = NSTextAlignmentCenter; 59 [_vContainer addSubview:_lblUrl]; 60 61 // error detail label 62 self.lblErrorDetail = [[UILabel alloc] init]; 63 _lblErrorDetail.numberOfLines = 0; 64 _lblErrorDetail.textAlignment = NSTextAlignmentLeft; 65 [_vContainer addSubview:_lblErrorDetail]; 66 67 for (UILabel *lblToStyle in @[ _lblUrl, _lblErrorDetail ]) { 68 lblToStyle.font = [UIFont systemFontOfSize:14.0f]; 69 lblToStyle.textColor = [UIColor lightGrayColor]; 70 } 71 } 72 return self; 73} 74 75- (void)setType:(EXFatalErrorType)type 76{ 77 _type = type; 78 NSString *appOwnerName = @"the requested app"; 79 if (_appRecord) { 80 if (_appRecord == [EXKernel sharedInstance].appRegistry.homeAppRecord) { 81 appOwnerName = @"Expo"; 82 } else if (_appRecord.appLoader.manifest && _appRecord.appLoader.manifest[@"name"]) { 83 appOwnerName = _appRecord.appLoader.manifest[@"name"]; 84 } 85 } 86 87 switch (type) { 88 case kEXFatalErrorTypeLoading: { 89 _lblError.text = [NSString stringWithFormat:@"There was a problem loading %@.", appOwnerName]; 90 if (_error.code == kCFURLErrorNotConnectedToInternet) { 91 _lblError.text = [NSString stringWithFormat:@"%@ Make sure you're connected to the internet.", _lblError.text]; 92 } else if (_appRecord.appLoader.manifestUrl) { 93 NSString *url = _appRecord.appLoader.manifestUrl.absoluteString; 94 if ([self _urlLooksLikeLAN:url]) { 95 _lblError.text = [NSString stringWithFormat: 96 @"%@ It looks like you may be using a LAN url." 97 "Make sure your device is on the same network as the server or try using a tunnel.", _lblError.text]; 98 } 99 } 100 break; 101 } 102 case kEXFatalErrorTypeException: { 103 _lblError.text = [NSString stringWithFormat:@"There was a problem running %@.", appOwnerName]; 104 break; 105 } 106 } 107 [self _resetUIState]; 108} 109 110- (void)setError:(NSError *)error 111{ 112 _error = error; 113 _lblErrorDetail.text = [error localizedDescription]; 114 [self _resetUIState]; 115} 116 117- (void)setAppRecord:(EXKernelAppRecord *)appRecord 118{ 119 _appRecord = appRecord; 120 [self _resetUIState]; 121} 122 123- (void)layoutSubviews 124{ 125 [super layoutSubviews]; 126 127 _vContainer.frame = self.bounds; 128 CGFloat maxLabelWidth = self.bounds.size.width - 32.0f; 129 130 _lblError.frame = CGRectMake(0, 0, maxLabelWidth, CGFLOAT_MAX); 131 [_lblError sizeToFit]; 132 _lblError.center = CGPointMake(self.bounds.size.width * 0.5f, self.bounds.size.height * 0.25f); 133 134 _btnRetry.frame = CGRectMake(0, 0, self.bounds.size.width, 24.0f); 135 _btnRetry.center = CGPointMake(_lblError.center.x, CGRectGetMaxY(_lblError.frame) + 32.0f); 136 137 _btnBack.frame = CGRectMake(0, 0, self.bounds.size.width, 24.0f); 138 _btnBack.center = CGPointMake(_lblError.center.x, CGRectGetMaxY(_btnRetry.frame) + 24); 139 140 _lblUrl.frame = CGRectMake(24.0f, CGRectGetMaxY(_btnBack.frame) + 12.0f, self.bounds.size.width - 48.0f, 24.0f); 141 142 _lblErrorDetail.frame = CGRectMake(0, 0, maxLabelWidth, CGFLOAT_MAX); 143 [_lblErrorDetail sizeToFit]; 144 145 _lblErrorDetail.center = CGPointMake(_lblError.center.x, CGRectGetMaxY(_lblUrl.frame) + 24.0f + CGRectGetMidY(_lblErrorDetail.bounds)); 146 147 _vContainer.contentSize = CGSizeMake(_vContainer.bounds.size.width, CGRectGetMaxY(_lblErrorDetail.frame) + 12.0f); 148} 149 150#pragma mark - Internal 151 152- (void)_resetUIState 153{ 154 EXKernelAppRecord *homeRecord = [EXKernel sharedInstance].appRegistry.homeAppRecord; 155 _btnBack.hidden = (!homeRecord || _appRecord == homeRecord); 156 _lblUrl.hidden = (!homeRecord); 157 _lblUrl.text = _appRecord.appLoader.manifestUrl.absoluteString; 158 // TODO: maybe hide retry (see BrowserErrorView) 159 [self setNeedsLayout]; 160} 161 162- (void)_onTapRetry 163{ 164 if (_delegate) { 165 [_delegate errorViewDidSelectRetry:self]; 166 } 167} 168 169- (void)_onTapBack 170{ 171 if ([EXKernel sharedInstance].browserController) { 172 [[EXKernel sharedInstance].browserController moveHomeToVisible]; 173 } 174} 175 176- (BOOL)_urlLooksLikeLAN:(NSString *)url 177{ 178 return ( 179 url && ( 180 [url rangeOfString:@".local"].length > 0 || 181 [url rangeOfString:@"192."].length > 0 || 182 [url rangeOfString:@"10."].length > 0 || 183 [url rangeOfString:@"172."].length > 0 184 ) 185 ); 186} 187 188@end 189