1// Copyright 2015-present 650 Industries. All rights reserved. 2 3@import UIKit; 4 5#import "EXAppDelegate.h" 6#import "EXAppViewController.h" 7#import "EXHomeAppManager.h" 8#import "EXHomeDiagnosticsViewController.h" 9#import "EXKernel.h" 10#import "EXKernelAppLoader.h" 11#import "EXKernelAppRecord.h" 12#import "EXKernelAppRegistry.h" 13#import "EXKernelLinkingManager.h" 14#import "EXKernelServiceRegistry.h" 15#import "EXMenuViewController.h" 16#import "EXRootViewController.h" 17 18NSString * const kEXHomeDisableNuxDefaultsKey = @"EXKernelDisableNuxDefaultsKey"; 19NSString * const kEXHomeIsNuxFinishedDefaultsKey = @"EXHomeIsNuxFinishedDefaultsKey"; 20 21NS_ASSUME_NONNULL_BEGIN 22 23@interface EXRootViewController () <EXAppBrowserController> 24 25@property (nonatomic, strong) EXMenuViewController *menuViewController; 26@property (nonatomic, assign) BOOL isMenuVisible; 27 28@end 29 30@implementation EXRootViewController 31 32- (instancetype)init 33{ 34 if (self = [super init]) { 35 [EXKernel sharedInstance].browserController = self; 36 [self _maybeResetNuxState]; 37 } 38 return self; 39} 40 41#pragma mark - EXViewController 42 43- (void)createRootAppAndMakeVisible 44{ 45 EXHomeAppManager *homeAppManager = [[EXHomeAppManager alloc] init]; 46 EXKernelAppLoader *homeAppLoader = [[EXKernelAppLoader alloc] initWithLocalManifest:[EXHomeAppManager bundledHomeManifest]]; 47 EXKernelAppRecord *homeAppRecord = [[EXKernelAppRecord alloc] initWithAppLoader:homeAppLoader appManager:homeAppManager]; 48 [[EXKernel sharedInstance].appRegistry registerHomeAppRecord:homeAppRecord]; 49 [self moveAppToVisible:homeAppRecord]; 50} 51 52#pragma mark - EXAppBrowserController 53 54- (void)moveAppToVisible:(EXKernelAppRecord *)appRecord 55{ 56 [self _foregroundAppRecord:appRecord]; 57} 58 59- (void)toggleMenu 60{ 61 [self setIsMenuVisible:!_isMenuVisible]; 62} 63 64- (void)setIsMenuVisible:(BOOL)isMenuVisible 65{ 66 if (!_menuViewController) { 67 _menuViewController = [[EXMenuViewController alloc] init]; 68 } 69 70 // TODO: ben: can this be more robust? 71 // some third party libs (and core RN) often just look for the root VC and present random crap from it. 72 if (self.presentedViewController && self.presentedViewController != _menuViewController) { 73 [self.presentedViewController dismissViewControllerAnimated:NO completion:nil]; 74 } 75 76 if (isMenuVisible != _isMenuVisible) { 77 _isMenuVisible = isMenuVisible; 78 if (_isMenuVisible) { 79 [self presentViewController:_menuViewController animated:NO completion:nil]; 80 } else { 81 [_menuViewController dismissViewControllerAnimated:NO completion:nil]; 82 } 83 } 84} 85 86- (void)showDiagnostics 87{ 88 EXHomeDiagnosticsViewController *vcDiagnostics = [[EXHomeDiagnosticsViewController alloc] init]; 89 [self setIsMenuVisible:NO]; 90 [self presentViewController:vcDiagnostics animated:NO completion:nil]; 91} 92 93- (void)showQRReader 94{ 95 [self moveHomeToVisible]; 96 [[self _getHomeAppManager] showQRReader]; 97} 98 99- (void)moveHomeToVisible 100{ 101 [self setIsMenuVisible:NO]; 102 [[EXKernel sharedInstance] moveAppToVisible:[EXKernel sharedInstance].appRegistry.homeAppRecord]; 103} 104 105- (void)refreshVisibleApp 106{ 107 [self setIsMenuVisible:NO]; 108 NSURL *urlToRefresh = [EXKernel sharedInstance].visibleApp.appLoader.manifestUrl; 109 [[EXKernel sharedInstance] createNewAppWithUrl:urlToRefresh initialProps:nil]; 110} 111 112- (void)addHistoryItemWithUrl:(NSURL *)manifestUrl manifest:(NSDictionary *)manifest 113{ 114 [[self _getHomeAppManager] addHistoryItemWithUrl:manifestUrl manifest:manifest]; 115} 116 117- (void)getHistoryUrlForExperienceId:(NSString *)experienceId completion:(void (^)(NSString *))completion 118{ 119 return [[self _getHomeAppManager] getHistoryUrlForExperienceId:experienceId completion:completion]; 120} 121 122- (void)setIsNuxFinished:(BOOL)isFinished 123{ 124 [[NSUserDefaults standardUserDefaults] setBool:isFinished forKey:kEXHomeIsNuxFinishedDefaultsKey]; 125 [[NSUserDefaults standardUserDefaults] synchronize]; 126} 127 128- (BOOL)isNuxFinished 129{ 130 return [[NSUserDefaults standardUserDefaults] boolForKey:kEXHomeIsNuxFinishedDefaultsKey]; 131} 132 133- (void)appDidFinishLoadingSuccessfully:(EXKernelAppRecord *)appRecord 134{ 135 // show nux if needed 136 if (!self.isNuxFinished 137 && appRecord == [EXKernel sharedInstance].visibleApp 138 && appRecord != [EXKernel sharedInstance].appRegistry.homeAppRecord 139 && !self.isMenuVisible) { 140 [self setIsMenuVisible:YES]; 141 } 142} 143 144#pragma mark - internal 145 146- (void)_foregroundAppRecord:(EXKernelAppRecord *)appRecord 147{ 148 UIViewController *viewControllerToShow = appRecord.viewController; 149 if (viewControllerToShow != self.contentViewController) { 150 if (self.contentViewController) { 151 [self.contentViewController willMoveToParentViewController:nil]; 152 [self.contentViewController.view removeFromSuperview]; 153 [self.contentViewController didMoveToParentViewController:nil]; 154 } 155 156 if (viewControllerToShow) { 157 [viewControllerToShow willMoveToParentViewController:self]; 158 [self.view addSubview:viewControllerToShow.view]; 159 [viewControllerToShow didMoveToParentViewController:self]; 160 } 161 162 self.contentViewController = viewControllerToShow; 163 [self.view setNeedsLayout]; 164 [[EXKernel sharedInstance] appDidBecomeVisible:appRecord]; 165 } 166} 167 168- (EXHomeAppManager *)_getHomeAppManager 169{ 170 return (EXHomeAppManager *)[EXKernel sharedInstance].appRegistry.homeAppRecord.appManager; 171} 172 173- (void)_maybeResetNuxState 174{ 175 // used by appetize: optionally disable nux 176 BOOL disableNuxDefaultsValue = [[NSUserDefaults standardUserDefaults] boolForKey:kEXHomeDisableNuxDefaultsKey]; 177 if (disableNuxDefaultsValue) { 178 [self setIsNuxFinished:YES]; 179 [[NSUserDefaults standardUserDefaults] removeObjectForKey:kEXHomeDisableNuxDefaultsKey]; 180 } 181} 182 183@end 184 185NS_ASSUME_NONNULL_END 186