[MM-41891] Improve error and reload states (#2019)
This commit is contained in:
parent
f79c445920
commit
3eb904cd67
|
@ -118,3 +118,5 @@ export const UPDATE_URL_VIEW_WIDTH = 'update-url-view-width';
|
||||||
export const DISPATCH_GET_DESKTOP_SOURCES = 'dispatch-get-desktop-sources';
|
export const DISPATCH_GET_DESKTOP_SOURCES = 'dispatch-get-desktop-sources';
|
||||||
export const DESKTOP_SOURCES_RESULT = 'desktop-sources-result';
|
export const DESKTOP_SOURCES_RESULT = 'desktop-sources-result';
|
||||||
|
|
||||||
|
export const RELOAD_CURRENT_VIEW = 'reload-current-view';
|
||||||
|
|
||||||
|
|
|
@ -105,12 +105,15 @@ describe('main/views/MattermostView', () => {
|
||||||
describe('retry', () => {
|
describe('retry', () => {
|
||||||
const window = {on: jest.fn()};
|
const window = {on: jest.fn()};
|
||||||
const mattermostView = new MattermostView(tabView, {}, window, {});
|
const mattermostView = new MattermostView(tabView, {}, window, {});
|
||||||
|
const retryInBackgroundFn = jest.fn();
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
jest.useFakeTimers();
|
||||||
mattermostView.view.webContents.loadURL.mockImplementation(() => Promise.resolve());
|
mattermostView.view.webContents.loadURL.mockImplementation(() => Promise.resolve());
|
||||||
mattermostView.loadSuccess = jest.fn();
|
mattermostView.loadSuccess = jest.fn();
|
||||||
mattermostView.loadRetry = jest.fn();
|
mattermostView.loadRetry = jest.fn();
|
||||||
mattermostView.emit = jest.fn();
|
mattermostView.emit = jest.fn();
|
||||||
|
mattermostView.retryInBackground = () => retryInBackgroundFn;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should do nothing when webcontents are destroyed', () => {
|
it('should do nothing when webcontents are destroyed', () => {
|
||||||
|
@ -140,7 +143,7 @@ describe('main/views/MattermostView', () => {
|
||||||
expect(mattermostView.loadRetry).toBeCalledWith('http://server-1.com', error);
|
expect(mattermostView.loadRetry).toBeCalledWith('http://server-1.com', error);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set to error status when max retries are reached', async () => {
|
it('should set to error status and retry in the background when max retries are reached', async () => {
|
||||||
mattermostView.maxRetries = 0;
|
mattermostView.maxRetries = 0;
|
||||||
const error = new Error('test');
|
const error = new Error('test');
|
||||||
const promise = Promise.reject(error);
|
const promise = Promise.reject(error);
|
||||||
|
@ -151,6 +154,8 @@ describe('main/views/MattermostView', () => {
|
||||||
expect(mattermostView.loadRetry).not.toBeCalled();
|
expect(mattermostView.loadRetry).not.toBeCalled();
|
||||||
expect(WindowManager.sendToRenderer).toBeCalledWith(LOAD_FAILED, mattermostView.tab.name, expect.any(String), expect.any(String));
|
expect(WindowManager.sendToRenderer).toBeCalledWith(LOAD_FAILED, mattermostView.tab.name, expect.any(String), expect.any(String));
|
||||||
expect(mattermostView.status).toBe(-1);
|
expect(mattermostView.status).toBe(-1);
|
||||||
|
jest.runAllTimers();
|
||||||
|
expect(retryInBackgroundFn).toBeCalled();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -175,13 +175,27 @@ export class MattermostView extends EventEmitter {
|
||||||
} else {
|
} else {
|
||||||
WindowManager.sendToRenderer(LOAD_FAILED, this.tab.name, err.toString(), loadURL.toString());
|
WindowManager.sendToRenderer(LOAD_FAILED, this.tab.name, err.toString(), loadURL.toString());
|
||||||
this.emit(LOAD_FAILED, this.tab.name, err.toString(), loadURL.toString());
|
this.emit(LOAD_FAILED, this.tab.name, err.toString(), loadURL.toString());
|
||||||
log.info(`[${Util.shorten(this.tab.name)}] Couldn't stablish a connection with ${loadURL}: ${err}.`);
|
log.info(`[${Util.shorten(this.tab.name)}] Couldn't stablish a connection with ${loadURL}: ${err}. Will continue to retry in the background.`);
|
||||||
this.status = Status.ERROR;
|
this.status = Status.ERROR;
|
||||||
|
this.retryLoad = setTimeout(this.retryInBackground(loadURL), RELOAD_INTERVAL);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
retryInBackground = (loadURL: string) => {
|
||||||
|
return () => {
|
||||||
|
// window was closed while retrying
|
||||||
|
if (!this.view || !this.view.webContents) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const loading = this.view.webContents.loadURL(loadURL, {userAgent: composeUserAgent()});
|
||||||
|
loading.then(this.loadSuccess(loadURL)).catch(() => {
|
||||||
|
this.retryLoad = setTimeout(this.retryInBackground(loadURL), RELOAD_INTERVAL);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
loadRetry = (loadURL: string, err: Error) => {
|
loadRetry = (loadURL: string, err: Error) => {
|
||||||
this.retryLoad = setTimeout(this.retry(loadURL), RELOAD_INTERVAL);
|
this.retryLoad = setTimeout(this.retry(loadURL), RELOAD_INTERVAL);
|
||||||
WindowManager.sendToRenderer(LOAD_RETRY, this.tab.name, Date.now() + RELOAD_INTERVAL, err.toString(), loadURL.toString());
|
WindowManager.sendToRenderer(LOAD_RETRY, this.tab.name, Date.now() + RELOAD_INTERVAL, err.toString(), loadURL.toString());
|
||||||
|
|
|
@ -98,7 +98,7 @@ export class ViewManager {
|
||||||
view.load(url);
|
view.load(url);
|
||||||
view.on(UPDATE_TARGET_URL, this.showURLView);
|
view.on(UPDATE_TARGET_URL, this.showURLView);
|
||||||
view.on(LOADSCREEN_END, this.finishLoading);
|
view.on(LOADSCREEN_END, this.finishLoading);
|
||||||
view.once(LOAD_FAILED, this.failLoading);
|
view.on(LOAD_FAILED, this.failLoading);
|
||||||
}
|
}
|
||||||
|
|
||||||
reloadViewIfNeeded = (viewName: string) => {
|
reloadViewIfNeeded = (viewName: string) => {
|
||||||
|
|
|
@ -24,6 +24,7 @@ import {
|
||||||
BROWSER_HISTORY_BUTTON,
|
BROWSER_HISTORY_BUTTON,
|
||||||
DISPATCH_GET_DESKTOP_SOURCES,
|
DISPATCH_GET_DESKTOP_SOURCES,
|
||||||
DESKTOP_SOURCES_RESULT,
|
DESKTOP_SOURCES_RESULT,
|
||||||
|
RELOAD_CURRENT_VIEW,
|
||||||
} from 'common/communication';
|
} from 'common/communication';
|
||||||
import urlUtils from 'common/utils/url';
|
import urlUtils from 'common/utils/url';
|
||||||
import {SECOND} from 'common/utils/constants';
|
import {SECOND} from 'common/utils/constants';
|
||||||
|
@ -68,6 +69,7 @@ export class WindowManager {
|
||||||
ipcMain.handle(GET_VIEW_NAME, this.handleGetViewName);
|
ipcMain.handle(GET_VIEW_NAME, this.handleGetViewName);
|
||||||
ipcMain.handle(GET_VIEW_WEBCONTENTS_ID, this.handleGetWebContentsId);
|
ipcMain.handle(GET_VIEW_WEBCONTENTS_ID, this.handleGetWebContentsId);
|
||||||
ipcMain.on(DISPATCH_GET_DESKTOP_SOURCES, this.handleGetDesktopSources);
|
ipcMain.on(DISPATCH_GET_DESKTOP_SOURCES, this.handleGetDesktopSources);
|
||||||
|
ipcMain.on(RELOAD_CURRENT_VIEW, this.handleReloadCurrentView);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleUpdateConfig = () => {
|
handleUpdateConfig = () => {
|
||||||
|
@ -650,6 +652,15 @@ export class WindowManager {
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleReloadCurrentView = () => {
|
||||||
|
const view = this.viewManager?.getCurrentView();
|
||||||
|
if (!view) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
view?.reload();
|
||||||
|
this.viewManager?.showByName(view?.name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const windowManager = new WindowManager();
|
const windowManager = new WindowManager();
|
||||||
|
|
|
@ -13,6 +13,7 @@ type Props = {
|
||||||
id?: string;
|
id?: string;
|
||||||
active?: boolean;
|
active?: boolean;
|
||||||
appName?: string;
|
appName?: string;
|
||||||
|
handleLink: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function ErrorView(props: Props) {
|
export default function ErrorView(props: Props) {
|
||||||
|
@ -42,27 +43,26 @@ export default function ErrorView(props: Props) {
|
||||||
>
|
>
|
||||||
<h2>{`Cannot connect to ${props.appName}`}</h2>
|
<h2>{`Cannot connect to ${props.appName}`}</h2>
|
||||||
<hr/>
|
<hr/>
|
||||||
<p>{`We're having trouble connecting to ${props.appName}. If refreshing this page (Ctrl+R or Command+R) does not work please verify that:`}</p>
|
<p>
|
||||||
|
{`We're having trouble connecting to ${props.appName}. We'll continue to try and establish a connection.`}
|
||||||
<br/>
|
<br/>
|
||||||
|
{'If refreshing this page (Ctrl+R or Command+R) does not work please verify that:'}
|
||||||
|
</p>
|
||||||
<ul className='ErrorView-bullets' >
|
<ul className='ErrorView-bullets' >
|
||||||
<li>{'Your computer is connected to the internet.'}</li>
|
<li>{'Your computer is connected to the internet.'}</li>
|
||||||
<li>{`The ${props.appName} URL `}
|
<li>{`The ${props.appName} URL `}
|
||||||
<a
|
<a
|
||||||
|
|
||||||
//onClick={handleClick}
|
onClick={props.handleLink}
|
||||||
href={props.url}
|
href='#'
|
||||||
target='_blank'
|
|
||||||
rel='noreferrer'
|
|
||||||
>
|
>
|
||||||
{props.url}
|
{props.url}
|
||||||
</a>{' is correct.'}</li>
|
</a>{' is correct.'}</li>
|
||||||
<li>{'You can reach '}
|
<li>{'You can reach '}
|
||||||
<a
|
<a
|
||||||
|
|
||||||
// onClick={handleClick}
|
onClick={props.handleLink}
|
||||||
href={props.url}
|
href='#'
|
||||||
target='_blank'
|
|
||||||
rel='noreferrer'
|
|
||||||
>
|
>
|
||||||
{props.url}
|
{props.url}
|
||||||
</a>{' from a browser window.'}</li>
|
</a>{' from a browser window.'}</li>
|
||||||
|
|
|
@ -44,6 +44,7 @@ import {
|
||||||
START_UPGRADE,
|
START_UPGRADE,
|
||||||
START_DOWNLOAD,
|
START_DOWNLOAD,
|
||||||
CLOSE_TAB,
|
CLOSE_TAB,
|
||||||
|
RELOAD_CURRENT_VIEW,
|
||||||
} from 'common/communication';
|
} from 'common/communication';
|
||||||
|
|
||||||
import restoreButton from '../../assets/titlebar/chrome-restore.svg';
|
import restoreButton from '../../assets/titlebar/chrome-restore.svg';
|
||||||
|
@ -358,6 +359,10 @@ export default class MainPage extends React.PureComponent<Props, State> {
|
||||||
this.handleCloseTeamsDropdown();
|
this.handleCloseTeamsDropdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
reloadCurrentView = () => {
|
||||||
|
window.ipcRenderer.send(RELOAD_CURRENT_VIEW);
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const currentTabs = this.props.teams.find((team) => team.name === this.state.activeServerName)?.tabs || [];
|
const currentTabs = this.props.teams.find((team) => team.name === this.state.activeServerName)?.tabs || [];
|
||||||
|
|
||||||
|
@ -528,16 +533,6 @@ export default class MainPage extends React.PureComponent<Props, State> {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
switch (tabStatus.status) {
|
switch (tabStatus.status) {
|
||||||
case Status.NOSERVERS: // TODO: substitute with https://mattermost.atlassian.net/browse/MM-25003
|
|
||||||
component = (
|
|
||||||
<ErrorView
|
|
||||||
id={'NoServers'}
|
|
||||||
errorInfo={'No Servers configured'}
|
|
||||||
url={tabStatus.extra ? tabStatus.extra.url : ''}
|
|
||||||
active={true}
|
|
||||||
appName={this.props.appName}
|
|
||||||
/>);
|
|
||||||
break;
|
|
||||||
case Status.FAILED:
|
case Status.FAILED:
|
||||||
component = (
|
component = (
|
||||||
<ErrorView
|
<ErrorView
|
||||||
|
@ -546,6 +541,7 @@ export default class MainPage extends React.PureComponent<Props, State> {
|
||||||
url={tabStatus.extra ? tabStatus.extra.url : ''}
|
url={tabStatus.extra ? tabStatus.extra.url : ''}
|
||||||
active={true}
|
active={true}
|
||||||
appName={this.props.appName}
|
appName={this.props.appName}
|
||||||
|
handleLink={this.reloadCurrentView}
|
||||||
/>);
|
/>);
|
||||||
break;
|
break;
|
||||||
case Status.LOADING:
|
case Status.LOADING:
|
||||||
|
|
Loading…
Reference in a new issue