Hot Loading Indicators

Summary:
public

Introduce a header bar similar to the one shown when loading the bundle to indicate that the packager server is processing an HMR update. Hook into HMR events to show this bar when appropriate.

Reviewed By: javache

Differential Revision: D2873521

fb-gh-sync-id: a77cbb2368b75b045aa8c6ababce2f731baf514b
This commit is contained in:
Martín Bigio 2016-02-01 12:41:04 -08:00 committed by facebook-github-bot-7
parent 180ead05e9
commit 36efbc341d
3 changed files with 77 additions and 44 deletions

View File

@ -11,6 +11,7 @@
'use strict';
const invariant = require('invariant');
const processColor = require('processColor');
/**
* HMR Client that receives from the server HMR updates and propagates them
@ -49,39 +50,60 @@ Error: ${e.message}`
);
};
activeWS.onmessage = ({data}) => {
const DevLoadingView = require('NativeModules').DevLoadingView;
data = JSON.parse(data);
if (data.type === 'update') {
const modules = data.body.modules;
const sourceMappingURLs = data.body.sourceMappingURLs;
const sourceURLs = data.body.sourceURLs;
const RCTRedBox = require('NativeModules').RedBox;
RCTRedBox && RCTRedBox.dismiss && RCTRedBox.dismiss();
switch(data.type) {
case 'update-start': {
DevLoadingView.showMessage(
'Hot Loading...',
processColor('#000000'),
processColor('#aaaaaa'),
);
break;
}
case 'update': {
const modules = data.body.modules;
const sourceMappingURLs = data.body.sourceMappingURLs;
const sourceURLs = data.body.sourceURLs;
modules.forEach((code, i) => {
code = code + '\n\n' + sourceMappingURLs[i];
const RCTRedBox = require('NativeModules').RedBox;
RCTRedBox && RCTRedBox.dismiss && RCTRedBox.dismiss();
require('SourceMapsCache').fetch({
text: code,
url: sourceURLs[i],
sourceMappingURL: sourceMappingURLs[i],
modules.forEach((code, i) => {
code = code + '\n\n' + sourceMappingURLs[i];
require('SourceMapsCache').fetch({
text: code,
url: sourceURLs[i],
sourceMappingURL: sourceMappingURLs[i],
});
// on JSC we need to inject from native for sourcemaps to work
// (Safari doesn't support `sourceMappingURL` nor any variant when
// evaluating code) but on Chrome we can simply use eval
const injectFunction = typeof __injectHMRUpdate === 'function'
? __injectHMRUpdate
: eval;
injectFunction(code, sourceURLs[i]);
});
// on JSC we need to inject from native for sourcemaps to work
// (Safari doesn't support `sourceMappingURL` nor any variant when
// evaluating code) but on Chrome we can simply use eval
const injectFunction = typeof __injectHMRUpdate === 'function'
? __injectHMRUpdate
: eval;
injectFunction(code, sourceURLs[i]);
})
return;
DevLoadingView.hide();
break;
}
case 'update-done': {
DevLoadingView.hide();
break;
}
case 'error': {
DevLoadingView.hide();
throw new Error(data.body.type + ' ' + data.body.description);
}
default: {
throw new Error(`Unexpected message: ${data}`);
}
}
// TODO: add support for opening filename by clicking on the stacktrace
const error = data.body;
throw new Error(error.type + ' ' + error.description);
};
},
};

View File

@ -55,14 +55,13 @@ RCT_EXPORT_MODULE()
[self showWithURL:bridge.bundleURL];
}
- (void)showWithURL:(NSURL *)URL
RCT_EXPORT_METHOD(showMessage:(NSString *)message color:(UIColor *)color backgroundColor:(UIColor *)backgroundColor)
{
if (!isEnabled) {
return;
}
dispatch_async(dispatch_get_main_queue(), ^{
_showDate = [NSDate date];
if (!_window && !RCTRunningInTestEnvironment()) {
CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width;
@ -77,34 +76,23 @@ RCT_EXPORT_MODULE()
[_window makeKeyAndVisible];
}
NSString *source;
if (URL.fileURL) {
_window.backgroundColor = [UIColor blackColor];
_label.textColor = [UIColor grayColor];
source = @"pre-bundled file";
} else {
_window.backgroundColor = [UIColor colorWithHue:1./3 saturation:1 brightness:.35 alpha:1];
_label.textColor = [UIColor whiteColor];
source = [NSString stringWithFormat:@"%@:%@", URL.host, URL.port];
}
_label.text = [NSString stringWithFormat:@"Loading from %@...", source];
_label.text = message;
_label.textColor = color;
_window.backgroundColor = backgroundColor;
_window.hidden = NO;
});
}
- (void)hide
RCT_EXPORT_METHOD(hide)
{
if (!isEnabled) {
return;
}
dispatch_async(dispatch_get_main_queue(), ^{
const NSTimeInterval MIN_PRESENTED_TIME = 0.6;
NSTimeInterval presentedTime = [[NSDate date] timeIntervalSinceDate:_showDate];
NSTimeInterval delay = MAX(0, MIN_PRESENTED_TIME - presentedTime);
CGRect windowFrame = _window.frame;
[UIView animateWithDuration:0.25
delay:delay
@ -119,6 +107,26 @@ RCT_EXPORT_MODULE()
});
}
- (void)showWithURL:(NSURL *)URL
{
UIColor *color;
UIColor *backgroundColor;
NSString *source;
if (URL.fileURL) {
color = [UIColor grayColor];
backgroundColor = [UIColor blackColor];
source = @"pre-bundled file";
} else {
color = [UIColor whiteColor];
backgroundColor = [UIColor colorWithHue:1./3 saturation:1 brightness:.35 alpha:1];
source = [NSString stringWithFormat:@"%@:%@", URL.host, URL.port];
}
[self showMessage:[NSString stringWithFormat:@"Loading from %@...", source]
color:color
backgroundColor:backgroundColor];
}
@end
#else

View File

@ -112,6 +112,7 @@ function attachHMRServer({httpServer, path, packagerServer}) {
return;
}
client.ws.send(JSON.stringify({type: 'update-start'}));
stat.then(() => {
return packagerServer.getShallowDependencies(filename)
.then(deps => {
@ -240,7 +241,9 @@ function attachHMRServer({httpServer, path, packagerServer}) {
() => {
// do nothing, file was removed
},
);
).finally(() => {
client.ws.send(JSON.stringify({type: 'update-done'}));
});
});
client.ws.on('error', e => {