Typescript - Change network error logic and remove loading screen (#666)

This commit is contained in:
Kiran Niranjan 2019-05-31 17:52:18 +05:30 committed by Vishwas Shashidhar
parent 4da7cd6d17
commit cc451cb46f
18 changed files with 269 additions and 268 deletions

View File

@ -1,47 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`loading screen should render correctly 1`] = `
<div
className="LoadingScreen"
>
<img
className="LoadingScreen-logo"
src="../renderer/assets/symphony-logo.png"
/>
<span
className="LoadingScreen-name"
>
Symphony
</span>
<svg
height="100%"
preserveAspectRatio="xMidYMid"
viewBox="0 0 100 200"
width="100%"
xmlns="http://www.w3.org/2000/svg"
>
<circle
cx="50"
cy="50"
fill="none"
ng-attr-stroke="{{config.color}}"
r="35"
stroke="#ffffff"
strokeDasharray="164.93361431346415 56.97787143782138"
strokeWidth="10"
transform="rotate(59.6808 50 50)"
>
<animateTransform
attributeName="transform"
begin="0s"
calcMode="linear"
dur="1s"
keyTimes="0;1"
repeatCount="indefinite"
type="rotate"
values="0 50 50;360 50 50"
/>
</circle>
</svg>
</div>
`;

View File

@ -1,17 +0,0 @@
import { shallow } from 'enzyme';
import * as React from 'react';
import LoadingScreen from '../src/renderer/components/loading-screen';
describe('loading screen', () => {
it('should render correctly', () => {
const wrapper = shallow(React.createElement(LoadingScreen));
expect(wrapper).toMatchSnapshot();
});
it('should show app name correctly', () => {
const customSelector = '.LoadingScreen-name';
const wrapper = shallow(React.createElement(LoadingScreen));
const expectedValue = 'Symphony';
expect(wrapper.find(customSelector).text()).toBe(expectedValue);
});
});

View File

@ -34,9 +34,6 @@ ipcMain.on(apiName.symphonyApi, (event: Electron.Event, arg: IApiArgs) => {
}
switch (arg.cmd) {
case apiCmds.initMainWindow:
windowHandler.initMainWindow();
break;
case apiCmds.isOnline:
if (typeof arg.isOnline === 'boolean') {
windowHandler.isOnline = arg.isOnline;

View File

@ -32,7 +32,6 @@ const startApplication = async () => {
await app.whenReady();
logger.info(`main: app is ready, performing initial checks`);
createAppCacheFile();
windowHandler.showLoadingScreen();
windowHandler.createApplication();
logger.info(`main: created application`);

View File

@ -1,5 +1,6 @@
import * as electron from 'electron';
import { app, BrowserWindow, crashReporter, globalShortcut, ipcMain } from 'electron';
import * as fs from 'fs';
import * as path from 'path';
import { format, parse } from 'url';
@ -42,31 +43,6 @@ const DEFAULT_HEIGHT: number = 900;
export class WindowHandler {
/**
* Loading window opts
*/
private static getLoadingWindowOpts(): ICustomBrowserWindowConstructorOpts {
return {
alwaysOnTop: false,
center: true,
frame: false,
height: 250,
maximizable: false,
minimizable: false,
resizable: false,
show: false,
title: 'Symphony',
width: 400,
webPreferences: {
sandbox: true,
nodeIntegration: false,
devTools: false,
contextIsolation: false,
},
winKey: getGuid(),
};
}
/**
* Screen picker window opts
*/
@ -190,7 +166,6 @@ export class WindowHandler {
private loadFailError: string | undefined;
private mainWindow: ICustomBrowserWindow | null = null;
private loadingWindow: Electron.BrowserWindow | null = null;
private aboutAppWindow: Electron.BrowserWindow | null = null;
private moreInfoWindow: Electron.BrowserWindow | null = null;
private screenPickerWindow: Electron.BrowserWindow | null = null;
@ -285,25 +260,24 @@ export class WindowHandler {
this.loadFailError = errorDesc;
});
this.mainWindow.webContents.on('did-stop-loading', () => {
this.mainWindow.webContents.on('did-stop-loading', async () => {
if (this.mainWindow && windowExists(this.mainWindow)) {
this.mainWindow.webContents.executeJavaScript('document.location.href').then((href) => {
const href = await this.mainWindow.webContents.executeJavaScript('document.location.href');
try {
if (href === 'data:text/html,chromewebdata' || href === 'chrome-error://chromewebdata/') {
if (this.loadingWindow && windowExists(this.loadingWindow)) {
this.loadingWindow.webContents.send('loading-screen-data', { error: this.loadFailError });
return;
if (this.mainWindow && windowExists(this.mainWindow)) {
this.mainWindow.webContents.insertCSS(fs.readFileSync(path.join(__dirname, '..', '/renderer/styles/network-error.css'), 'utf8').toString());
this.mainWindow.webContents.send('network-error', { error: this.loadFailError });
isSymphonyReachable(this.mainWindow);
}
this.showLoadingScreen(this.loadFailError);
isSymphonyReachable(this.mainWindow);
}
}).catch((error) => {
logger.error(`Could not read document.location error: ${error}`);
});
} catch (error) {
logger.error(`window-handler: Could not read document.location`, error);
}
}
});
this.mainWindow.webContents.on('crashed', (_event: Event, killed: boolean) => {
this.mainWindow.webContents.on('crashed', (_event: Event, killed: boolean) => {
if (killed) {
logger.info(`window-handler: main window crashed (killed)!`);
return;
@ -347,6 +321,13 @@ export class WindowHandler {
this.destroyAllWindows();
});
// Reloads the Symphony
ipcMain.on('reload-symphony', () => {
if (this.mainWindow && windowExists(this.mainWindow)) {
this.mainWindow.loadURL(this.url || this.globalConfig.url);
}
});
// Certificate verification proxy
if (!isDevEnv) {
this.mainWindow.webContents.session.setCertificateVerifyProc(handleCertificateProxyVerification);
@ -372,34 +353,6 @@ export class WindowHandler {
return this.mainWindow;
}
/**
* Displays the main windows once
* all the HTML content have been injected
*/
public initMainWindow(): void {
logger.info(`window-handler: initializing main window!`);
if (this.mainWindow && windowExists(this.mainWindow)) {
if (!this.isOnline && this.loadingWindow && windowExists(this.loadingWindow)) {
logger.info(`window-handler: network is offline!`);
this.loadingWindow.webContents.send('loading-screen-data', { error: 'NETWORK_OFFLINE' });
return;
}
// close the loading window when
// the main windows finished loading
if (this.loadingWindow && windowExists(this.loadingWindow)) {
logger.info(`window-handler: closing loading window as the main window is now ready!`);
this.loadingWindow.close();
}
// Ready to show the window
// activate the window only if it is not visible to the user
if (!this.isAutoReload && !this.mainWindow.isVisible()) {
this.mainWindow.show();
}
}
}
/**
* Gets the main window
*/
@ -489,43 +442,6 @@ export class WindowHandler {
return browserWindow && window === browserWindow;
}
/**
* Displays a loading window until the main
* application is loaded
*/
public showLoadingScreen(error?: string | undefined): void {
const opts = WindowHandler.getLoadingWindowOpts();
this.loadingWindow = createComponentWindow('loading-screen', opts);
this.addWindow(opts.winKey, this.loadingWindow);
this.loadingWindow.webContents.once('did-finish-load', () => {
if (!this.loadingWindow || !windowExists(this.loadingWindow)) {
return;
}
if (error) {
logger.info(`window-handler: loading screen failed ${error}!`);
this.loadingWindow.webContents.send('loading-screen-data', { error });
}
logger.info(`window-handler: loading screen started!`);
});
ipcMain.once('reload-symphony', () => {
if (this.mainWindow && windowExists(this.mainWindow)) {
this.mainWindow.webContents.reload();
}
});
ipcMain.once('quit-symphony', () => {
if (this.mainWindow && windowExists(this.mainWindow)) {
app.quit();
}
});
this.loadingWindow.once('closed', () => {
this.removeWindow(opts.winKey);
this.loadingWindow = null;
});
}
/**
* Creates a about app window
*/
@ -867,7 +783,7 @@ export class WindowHandler {
frame: !this.isCustomTitleBar,
minHeight: 300,
minWidth: 300,
show: false,
show: true,
title: 'Symphony',
webPreferences: {
nodeIntegration: false,

View File

@ -26,7 +26,6 @@ export enum apiCmds {
getMediaSource = 'get-media-source',
notification = 'notification',
closeNotification = 'close-notification',
initMainWindow = 'init-main-window',
isMisspelled = 'is-misspelled',
memoryInfo = 'memory-info',
swiftSearch = 'swift-search',

View File

@ -77,8 +77,7 @@
"NetworkError": {
"Problem connecting to Symphony": "Problem connecting to Symphony",
"Looks like you are not connected to the Internet. We'll try to reconnect automatically.": "Looks like you are not connected to the Internet. We'll try to reconnect automatically.",
"Cancel Retry": "Cancel Retry",
"Quit Symphony": "Quit Symphony"
"Retry": "Retry"
},
"No crashes available to share": "No crashes available to share",
"No logs are available to share": "No logs are available to share",

View File

@ -77,8 +77,7 @@
"NetworkError": {
"Problem connecting to Symphony": "Problem connecting to Symphony",
"Looks like you are not connected to the Internet. We'll try to reconnect automatically.": "Looks like you are not connected to the Internet. We'll try to reconnect automatically.",
"Cancel Retry": "Cancel Retry",
"Quit Symphony": "Quit Symphony"
"Retry": "Retry"
},
"No crashes available to share": "No crashes available to share",
"No logs are available to share": "No logs are available to share",

View File

@ -77,8 +77,7 @@
"NetworkError": {
"Problem connecting to Symphony": "Problème de connexion à Symphony",
"Looks like you are not connected to the Internet. We'll try to reconnect automatically.": "On dirait que vous n'êtes pas connecté à Internet. Nous allons essayer de vous reconnecter automatiquement.",
"Cancel Retry": "Annuler nouvelle tentative",
"Quit Symphony": "Quitter Symphony"
"Retry": "Réessayez"
},
"No crashes available to share": "Pas de crash à partager",
"No logs are available to share": "Pas de journal à partager",

View File

@ -77,8 +77,7 @@
"NetworkError": {
"Problem connecting to Symphony": "Problème de connexion à Symphony",
"Looks like you are not connected to the Internet. We'll try to reconnect automatically.": "On dirait que vous n'êtes pas connecté à Internet. Nous allons essayer de vous reconnecter automatiquement.",
"Cancel Retry": "Annuler nouvelle tentative",
"Quit Symphony": "Quitter Symphony"
"Retry": "Réessayez"
},
"No crashes available to share": "Pas de crash à partager",
"No logs are available to share": "Pas de journal à partager",

View File

@ -77,8 +77,7 @@
"NetworkError": {
"Problem connecting to Symphony": "Symphonyへの接続に関する問題",
"Looks like you are not connected to the Internet. We'll try to reconnect automatically.": "インターネットに接続していないようです。 自動的に再接続します。",
"Cancel Retry": "再試行をキャンセル",
"Quit Symphony": "Symphonyを終了"
"Retry": "再試行を"
},
"No crashes available to share": "共有できるクラッシュはありません",
"No logs are available to share": "共有できるログはありません",

View File

@ -77,8 +77,7 @@
"NetworkError": {
"Problem connecting to Symphony": "Symphonyへの接続に関する問題",
"Looks like you are not connected to the Internet. We'll try to reconnect automatically.": "インターネットに接続していないようです。 自動的に再接続します。",
"Cancel Retry": "再試行をキャンセル",
"Quit Symphony": "Symphonyを終了"
"Retry": "再試行を"
},
"No crashes available to share": "共有できるクラッシュはありません",
"No logs are available to share": "共有できるログはありません",

View File

@ -102,7 +102,7 @@ export default class DownloadManager extends React.Component<{}, IManagerState>
</div>
</div>
<div className='downloaded-filename'>
<h1 className='text-cutoff'>
<h1 className='text-cutoff' title={fileDisplayName}>
{fileDisplayName}
</h1>
<span id='per'>

View File

@ -0,0 +1,127 @@
import { ipcRenderer } from 'electron';
import * as React from 'react';
import { i18n } from '../../common/i18n-preload';
interface IProps {
error: ERROR | string;
}
enum ERROR {
NETWORK_OFFLINE = 'NETWORK_OFFLINE',
}
const NETWORK_ERROR_NAMESPACE = 'NetworkError';
/**
* Window that display app version and copyright info
*/
export default class NetworkError extends React.Component<IProps, {}> {
private readonly eventHandlers = {
onRetry: () => this.retry(),
};
constructor(props) {
super(props);
}
/**
* main render function
*/
public render(): JSX.Element {
const { error } = this.props;
return (
<div id='main-content'>
<div className='NetworkError-icon'>
<svg id='Layer_1' xmlns='http://www.w3.org/2000/svg' width='50' viewBox='0 0 19.7 32'>
<g id='Page-1'>
<g id='Symphony_logo'>
<g id='logo2'>
<linearGradient id='Shape_8_' gradientUnits='userSpaceOnUse' x1='39.819'
y1='23.981' x2='39.246' y2='23.726' gradientTransform='matrix(7.6157 0 0 -3.458 -295.325 101.04)'>
<stop offset='0' stopColor='#197a68'/>
<stop offset='1' stopColor='#329d87'/>
</linearGradient>
<path id='Shape' className='st0' d='M2.4,17.4c0,1.2,0.3,2.4,0.8,3.5l6.8-3.5H2.4z'
/>
<linearGradient id='Shape_9_' gradientUnits='userSpaceOnUse' x1='28.916'
y1='22.811' x2='29.916' y2='22.811' gradientTransform='matrix(2.7978 0 0 -4.7596 -73.704 128.374)'>
<stop offset='0' stopColor='#1d7e7b'/>
<stop offset='1' stopColor='#35b0b7'/>
</linearGradient>
<path id='Shape_1_' className='st1' d='M7.2,21.3C8,21.9,9,22.2,10,22.2v-4.8L7.2,21.3z'
/>
<linearGradient id='Shape_10_' gradientUnits='userSpaceOnUse' x1='37.958'
y1='21.136' x2='38.178' y2='21.868' gradientTransform='matrix(6.1591 0 0 -11.4226 -223.952 256.877)'>
<stop offset='0' stopColor='#175952'/>
<stop offset='1' stopColor='#3a8f88'/>
</linearGradient>
<path id='Shape_2_' className='st2' d='M14.4,6.9C13,6.3,11.5,6,10,6C9.4,6,8.8,6,8.2,6.1L10,17.4L14.4,6.9z'
/>
<linearGradient id='Shape_11_' gradientUnits='userSpaceOnUse' x1='40.569'
y1='22.098' x2='41.029' y2='22.377' gradientTransform='matrix(9.5186 0 0 -5.5951 -373.339 140.324)'>
<stop offset='0' stopColor='#39a8ba'/>
<stop offset='1' stopColor='#3992b4'/>
</linearGradient>
<path id='Shape_3_' className='st3' d='M10,17.4h9.5c0-2-0.6-4-1.8-5.6L10,17.4z'
/>
<linearGradient id='Shape_12_' gradientUnits='userSpaceOnUse' x1='41.214'
y1='22.325' x2='40.706' y2='22.548' gradientTransform='matrix(9.9955 0 0 -5.2227 -404.796 132.876)'>
<stop offset='0' stopColor='#021c3c'/>
<stop offset='1' stopColor='#215180'/>
</linearGradient>
<path id='Shape_4_' className='st4' d='M1.5,12.2c-1,1.6-1.5,3.4-1.5,5.2h10L1.5,12.2z'
/>
<linearGradient id='Shape_13_' gradientUnits='userSpaceOnUse' x1='33.511'
y1='22.151' x2='34.511' y2='22.151' gradientTransform='matrix(3.9169 0 0 -6.6631 -125.178 161.684)'>
<stop offset='0' stopColor='#23796c'/>
<stop offset='1' stopColor='#41beaf'/>
</linearGradient>
<path id='Shape_5_' className='st5' d='M10,10.8c-1.4,0-2.8,0.4-3.9,1.3l3.9,5.4V10.8z'
/>
<linearGradient id='Shape_14_' gradientUnits='userSpaceOnUse' x1='36.129'
y1='21.958' x2='36.487' y2='21.481' gradientTransform='matrix(5.0353 0 0 -8.5671 -171.59 208.333)'>
<stop offset='0' stopColor='#14466a'/>
<stop offset='1' stopColor='#286395'/>
</linearGradient>
<path id='Shape_6_' className='st6' d='M10,26c1.8,0,3.6-0.6,5-1.6l-5-6.9V26z'
/>
<linearGradient id='Shape_15_' gradientUnits='userSpaceOnUse' x1='38.534'
y1='23.656' x2='39.087' y2='23.578' gradientTransform='matrix(6.663 0 0 -3.5931 -244.84 102.835)'>
<stop offset='0' stopColor='#261d49'/>
<stop offset='1' stopColor='#483a6d'/>
</linearGradient>
<path id='Shape_7_' className='st7' d='M16.6,16.4l-6.6,1l6.2,2.6c0.3-0.8,0.5-1.7,0.5-2.6C16.7,17.1,16.6,16.7,16.6,16.4z'
/>
</g>
</g>
</g>
</svg>
</div>
<div className='main-message'>
<p className='NetworkError-header'>
{i18n.t('Problem connecting to Symphony', NETWORK_ERROR_NAMESPACE)()}
</p>
<p id='NetworkError-reason'>
{i18n.t(`Looks like you are not connected to the Internet. We'll try to reconnect automatically.`, NETWORK_ERROR_NAMESPACE)()}
</p>
<div id='error-code' className='NetworkError-error-code'>
{error || ERROR.NETWORK_OFFLINE}
</div>
<button id='retry-button' onClick={this.eventHandlers.onRetry} className='NetworkError-button'>
{i18n.t('Retry', NETWORK_ERROR_NAMESPACE)()}
</button>
</div>
</div>
);
}
/**
* reloads the application
*/
private retry(): void {
ipcRenderer.send('reload-symphony');
}
}

View File

@ -5,7 +5,6 @@ import * as ReactDOM from 'react-dom';
import { i18n } from '../common/i18n-preload';
import AboutBox from './components/about-app';
import BasicAuth from './components/basic-auth';
import LoadingScreen from './components/loading-screen';
import MoreInfo from './components/more-info';
import NotificationComp from './components/notification-comp';
import NotificationSettings from './components/notification-settings';
@ -14,7 +13,6 @@ import ScreenSharingIndicator from './components/screen-sharing-indicator';
const enum components {
aboutApp = 'about-app',
loadingScreen = 'loading-screen',
moreInfo = 'more-info',
screenPicker = 'screen-picker',
screenSharingIndicator = 'screen-sharing-indicator',
@ -44,10 +42,6 @@ const load = () => {
loadStyle(components.aboutApp);
component = AboutBox;
break;
case components.loadingScreen:
loadStyle(components.loadingScreen);
component = LoadingScreen;
break;
case components.moreInfo:
loadStyle(components.moreInfo);
component = MoreInfo;

View File

@ -6,6 +6,7 @@ import { apiCmds, apiName } from '../common/api-interface';
import { i18n } from '../common/i18n-preload';
import './app-bridge';
import DownloadManager from './components/download-manager';
import NetworkError from './components/network-error';
import SnackBar from './components/snack-bar';
import WindowsTitleBar from './components/windows-title-bar';
import { SSFApi } from './ssf-api';
@ -77,10 +78,6 @@ ipcRenderer.on('page-load', (_event, { locale, resources, enableCustomTitleBar,
}
if (isMainWindow) {
ipcRenderer.send(apiName.symphonyApi, {
cmd: apiCmds.initMainWindow,
});
setInterval(async () => {
const memoryInfo = await process.getProcessMemoryInfo();
ipcRenderer.send(apiName.symphonyApi, {
@ -90,3 +87,13 @@ ipcRenderer.on('page-load', (_event, { locale, resources, enableCustomTitleBar,
}, memoryInfoFetchInterval);
}
});
// Injects network error content into the DOM
ipcRenderer.on('network-error', (_event, { error }) => {
const networkErrorContainer = document.createElement( 'div' );
networkErrorContainer.id = 'main-frame';
networkErrorContainer.classList.add('content-wrapper');
document.body.append(networkErrorContainer);
const networkError = React.createElement(NetworkError, { error });
ReactDOM.render(networkError, networkErrorContainer);
});

View File

@ -1,72 +0,0 @@
@import "theme";
@text-padding: 10px;
body {
overflow: hidden;
background-color: white;
margin: 0;
height: 100%;
}
.LoadingScreen {
margin: 0;
height: 250px;
font-family: @font-family;
text-align: center;
display: flex;
flex-direction: column;
padding-top: @text-padding;
background: url(../assets/symphony-background.jpg) no-repeat center center fixed;
background-size: cover;
&-name {
flex: 1;
font-size: 1.3em;
padding: @text-padding;
font-weight: bold;
color: @text-color-primary;
}
&-logo {
margin: auto;
}
&-error-code {
color: @text-color-primary;
text-transform: uppercase;
display: block;
font-size: .8em;
}
&-button {
min-height: 34px;
min-width: 125px;
color: @text-color-primary;
background-color: transparent;
-webkit-box-shadow: 0 0 0 1px @text-color-primary;
font-weight: 600;
font-style: normal;
cursor: pointer;
border: none;
border-radius: 20px;
font-size: 12px;
text-align: center;
padding: 8px 20px;
margin: 25px 5px;
outline: 0;
display: inline-block;
text-decoration: none;
line-height: 12px;
overflow: hidden;
position: relative;
text-transform: uppercase;
transition: 120ms ease-in-out;
}
&-button:hover {
background-color: @text-color-primary;
color: @text-color-primary-dark;
box-shadow: 0 0 0 1px transparent;
}
}

View File

@ -0,0 +1,104 @@
@import "theme";
body {
background-color: rgb(247, 247, 247);
color: rgb(100, 100, 100);
display: flex;
margin: 0;
}
a {
color: rgb(17, 85, 204);
text-decoration: none;
}
.st0 {
fill: url(#Shape_8_);
}
.st1 {
fill: url(#Shape_9_);
}
.st2 {
fill: url(#Shape_10_);
}
.st3 {
fill: url(#Shape_11_);
}
.st4 {
fill: url(#Shape_12_);
}
.st5 {
fill: url(#Shape_13_);
}
.st6 {
fill: url(#Shape_14_);
}
.st7 {
fill: url(#Shape_15_);
}
.NetworkError-header {
color: #333333;
font-size: 1.6em;
font-weight: normal;
line-height: 1.25em;
margin-bottom: 16px;
margin-top: 0;
word-wrap: break-word;
}
.NetworkError-icon {
-webkit-user-select: none;
background-repeat: no-repeat;
background-size: 100%;
display: inline-block;
height: 72px;
margin: 0 0 40px;
width: 72px;
}
.NetworkError-error-code {
color: #646464;
display: block;
font-size: .8em;
margin-top: 10px;
text-transform: uppercase;
}
.content-wrapper {
-webkit-text-size-adjust: 100%;
box-sizing: border-box;
font-family: @font-family;
font-size: 100%;
line-height: 1.6em;
margin: 14vh auto 0;
max-width: 600px;
width: 100%;
}
.NetworkError-reason {
display: inline;
}
.NetworkError-button {
background: rgb(66, 133, 244);
border-radius: 2px;
border: 0;
box-sizing: border-box;
color: #ffffff;
cursor: pointer;
font-size: .875em;
font-weight: 600;
margin: 20px 0;
padding: 10px 24px;
text-transform: uppercase;
transform: translatez(0);
transition: box-shadow 200ms cubic-bezier(0.4, 0, 0.2, 1);
user-select: none;
}
.disabled {
background: #cccccc;
color: #666666;
cursor: not-allowed;
}