Make reconnection work more realistically (#12420)

* Support reconnection even if an earlier attempt failed

* Add "reload" button to reconnect dialog on failure

* Update blazor.*.js
This commit is contained in:
Steve Sanderson 2019-07-22 07:52:20 -07:00 committed by GitHub
parent 330a7708c0
commit 178374d228
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 31 additions and 16 deletions

File diff suppressed because one or more lines are too long

View File

@ -105,7 +105,10 @@ async function initializeConnection(options: BlazorOptions, logger: Logger): Pro
}); });
connection.onclose(error => !renderingFailed && options.reconnectionHandler!.onConnectionDown(options.reconnectionOptions, error)); connection.onclose(error => !renderingFailed && options.reconnectionHandler!.onConnectionDown(options.reconnectionOptions, error));
connection.on('JS.Error', error => unhandledError(connection, error, logger)); connection.on('JS.Error', error => {
renderingFailed = true;
unhandledError(connection, error, logger);
});
window['Blazor']._internal.forceCloseConnection = () => connection.stop(); window['Blazor']._internal.forceCloseConnection = () => connection.stop();
@ -134,7 +137,6 @@ function unhandledError(connection: signalR.HubConnection, err: Error, logger: L
// //
// Trying to call methods on the connection after its been closed will throw. // Trying to call methods on the connection after its been closed will throw.
if (connection) { if (connection) {
renderingFailed = true;
connection.stop(); connection.stop();
} }
} }

View File

@ -9,6 +9,8 @@ export class DefaultReconnectDisplay implements ReconnectDisplay {
addedToDom: boolean = false; addedToDom: boolean = false;
reloadParagraph: HTMLParagraphElement;
constructor(dialogId: string, private document: Document) { constructor(dialogId: string, private document: Document) {
this.modal = this.document.createElement('div'); this.modal = this.document.createElement('div');
this.modal.id = dialogId; this.modal.id = dialogId;
@ -29,11 +31,13 @@ export class DefaultReconnectDisplay implements ReconnectDisplay {
]; ];
this.modal.style.cssText = modalStyles.join(';'); this.modal.style.cssText = modalStyles.join(';');
this.modal.innerHTML = '<h5 style="margin-top: 20px"></h5><button style="margin:5px auto 5px">Retry?</button>'; this.modal.innerHTML = '<h5 style="margin-top: 20px"></h5><button style="margin:5px auto 5px">Retry?</button><p>Alternatively, <a href>reload</a></p>';
this.message = this.modal.querySelector('h5')!; this.message = this.modal.querySelector('h5')!;
this.button = this.modal.querySelector('button')!; this.button = this.modal.querySelector('button')!;
this.reloadParagraph = this.modal.querySelector('p')!;
this.button.addEventListener('click', () => window['Blazor'].reconnect()); this.button.addEventListener('click', () => window['Blazor'].reconnect());
this.reloadParagraph.querySelector('a')!.addEventListener('click', () => location.reload());
} }
show(): void { show(): void {
@ -43,6 +47,7 @@ export class DefaultReconnectDisplay implements ReconnectDisplay {
} }
this.modal.style.display = 'block'; this.modal.style.display = 'block';
this.button.style.display = 'none'; this.button.style.display = 'none';
this.reloadParagraph.style.display = 'none';
this.message.textContent = 'Attempting to reconnect to the server...'; this.message.textContent = 'Attempting to reconnect to the server...';
} }
@ -52,6 +57,7 @@ export class DefaultReconnectDisplay implements ReconnectDisplay {
failed(): void { failed(): void {
this.button.style.display = 'block'; this.button.style.display = 'block';
this.reloadParagraph.style.display = 'block';
this.message.textContent = 'Failed to reconnect to the server.'; this.message.textContent = 'Failed to reconnect to the server.';
} }
} }

View File

@ -6,19 +6,26 @@ import { Logger, LogLevel } from '../Logging/Logger';
export class DefaultReconnectionHandler implements ReconnectionHandler { export class DefaultReconnectionHandler implements ReconnectionHandler {
private readonly _logger: Logger; private readonly _logger: Logger;
private readonly _overrideDisplay?: ReconnectDisplay;
private readonly _reconnectCallback: () => Promise<boolean>; private readonly _reconnectCallback: () => Promise<boolean>;
private _currentReconnectionProcess: ReconnectionProcess | null = null; private _currentReconnectionProcess: ReconnectionProcess | null = null;
private _reconnectionDisplay?: ReconnectDisplay;
constructor(logger: Logger, overrideDisplay?: ReconnectDisplay, reconnectCallback?: () => Promise<boolean>) { constructor(logger: Logger, overrideDisplay?: ReconnectDisplay, reconnectCallback?: () => Promise<boolean>) {
this._logger = logger; this._logger = logger;
this._overrideDisplay = overrideDisplay; this._reconnectionDisplay = overrideDisplay;
this._reconnectCallback = reconnectCallback || (() => window['Blazor'].reconnect()); this._reconnectCallback = reconnectCallback || (() => window['Blazor'].reconnect());
} }
onConnectionDown (options: ReconnectionOptions, error?: Error) { onConnectionDown (options: ReconnectionOptions, error?: Error) {
if (!this._reconnectionDisplay) {
const modal = document.getElementById(options.dialogId);
this._reconnectionDisplay = modal
? new UserSpecifiedDisplay(modal)
: new DefaultReconnectDisplay(options.dialogId, document);
}
if (!this._currentReconnectionProcess) { if (!this._currentReconnectionProcess) {
this._currentReconnectionProcess = new ReconnectionProcess(options, this._logger, this._reconnectCallback, this._overrideDisplay); this._currentReconnectionProcess = new ReconnectionProcess(options, this._logger, this._reconnectCallback, this._reconnectionDisplay!);
} }
} }
@ -34,12 +41,8 @@ class ReconnectionProcess {
readonly reconnectDisplay: ReconnectDisplay; readonly reconnectDisplay: ReconnectDisplay;
isDisposed = false; isDisposed = false;
constructor(options: ReconnectionOptions, private logger: Logger, private reconnectCallback: () => Promise<boolean>, display?: ReconnectDisplay) { constructor(options: ReconnectionOptions, private logger: Logger, private reconnectCallback: () => Promise<boolean>, display: ReconnectDisplay) {
const modal = document.getElementById(options.dialogId); this.reconnectDisplay = display;
this.reconnectDisplay = display || (modal
? new UserSpecifiedDisplay(modal)
: new DefaultReconnectDisplay(options.dialogId, document));
this.reconnectDisplay.show(); this.reconnectDisplay.show();
this.attemptPeriodicReconnection(options); this.attemptPeriodicReconnection(options);
} }

View File

@ -20,7 +20,11 @@
configureSignalR: function (builder) { configureSignalR: function (builder) {
builder.configureLogging(2); // LogLevel.Information builder.configureLogging(2); // LogLevel.Information
}, },
logLevel: 2 // LogLevel.Information logLevel: 2, // LogLevel.Information
reconnectionOptions: {
maxRetries: 3,
retryIntervalMilliseconds: 2000,
}
}).then(function () { }).then(function () {
window['__aspnetcore__testing__blazor__started__'] = true; window['__aspnetcore__testing__blazor__started__'] = true;
}); });