diff --git a/js/enums/api.js b/js/enums/api.js index 1ecc121c..0376da2f 100644 --- a/js/enums/api.js +++ b/js/enums/api.js @@ -25,7 +25,9 @@ const cmds = keyMirror({ setIsInMeeting: null, setLocale: null, keyPress: null, - openScreenSharingIndicator: null + openScreenSharingIndicator: null, + cancelNetworkStatusCheck: null, + quitWindow: null, }); module.exports = { diff --git a/js/mainApiMgr.js b/js/mainApiMgr.js index 72544cd3..07f666fa 100644 --- a/js/mainApiMgr.js +++ b/js/mainApiMgr.js @@ -5,6 +5,7 @@ * from the renderer process. */ const electron = require('electron'); +const app = electron.app; const windowMgr = require('./windowMgr.js'); const log = require('./log.js'); @@ -190,6 +191,12 @@ electron.ipcMain.on(apiName, (event, arg) => { openScreenSharingIndicator(event.sender, arg.displayId, arg.id); } break; + case apiCmds.cancelNetworkStatusCheck: + windowMgr.cancelNetworkStatusCheck(); + break; + case apiCmds.quitWindow: + app.quit(); + break; default: } diff --git a/js/networkError/contents.js b/js/networkError/contents.js new file mode 100644 index 00000000..d285b28f --- /dev/null +++ b/js/networkError/contents.js @@ -0,0 +1,126 @@ +const errorContent = (data) => { + return (`
+
+
+ + + + Symphony_logo + + + + +
+
+

${data["Problem connecting to Symphony"] || "Problem connecting to Symphony"}

+

+ ${data["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."} +

+
ERR_INTERNET_DISCONNECTED
+ + +
+
+
`); +}; + + +module.exports = { + errorContent, +}; \ No newline at end of file diff --git a/js/networkError/index.js b/js/networkError/index.js new file mode 100644 index 00000000..e51b6163 --- /dev/null +++ b/js/networkError/index.js @@ -0,0 +1,44 @@ +const { ipcRenderer } = require('electron'); + +const apiEnums = require('../enums/api.js'); +const apiCmds = apiEnums.cmds; +const apiName = apiEnums.apiName; +const htmlContents = require('./contents'); + +class NetworkError { + + constructor() { + this.domParser = new DOMParser(); + } + + showError(data) { + if (!data) { + return; + } + const { message, error } = data; + const errorContent = this.domParser.parseFromString(htmlContents.errorContent(message), 'text/html'); + errorContent.getElementById('error-code').innerText = error || "UNKNOWN_ERROR"; + + // Add event listeners for buttons + const cancelRetryButton = errorContent.getElementById('cancel-retry-button'); + cancelRetryButton.addEventListener('click', () => { + ipcRenderer.send(apiName, { + cmd: apiCmds.cancelNetworkStatusCheck + }); + }); + + const quitButton = errorContent.getElementById('quit-button'); + quitButton.addEventListener('click', () => { + ipcRenderer.send(apiName, { + cmd: apiCmds.quitWindow + }) + }); + + const mainFrame = errorContent.getElementById('main-frame'); + document.body.appendChild(mainFrame); + } +} + +module.exports = { + NetworkError, +}; \ No newline at end of file diff --git a/js/networkError/style.css b/js/networkError/style.css new file mode 100644 index 00000000..e5a4dd40 --- /dev/null +++ b/js/networkError/style.css @@ -0,0 +1,71 @@ +body { + background-color: rgb(247, 247, 247); + color: rgb(100, 100, 100); + margin: 0; + display: flex; +} + +a { + color: rgb(17, 85, 204); + text-decoration: none; +} + +.NetworkError-header { + color: #333; + font-size: 1.6em; + font-weight: normal; + line-height: 1.25em; + margin-bottom: 16px; + margin-top: 0; + word-wrap: break-word; +} + +.NetworkError-icon { + background-repeat: no-repeat; + background-size: 100%; + height: 72px; + margin: 0 0 40px; + width: 72px; + -webkit-user-select: none; + display: inline-block; +} + +.NetworkError-error-code { + color: #646464; + text-transform: uppercase; + display: block; + font-size: .8em; + margin-top: 10px; +} + +.content-wrapper { + box-sizing: border-box; + line-height: 1.6em; + margin: 14vh auto 0; + max-width: 600px; + width: 100%; + -webkit-text-size-adjust: 100%; + font-size: 100%; + font-family: sans-serif, "Segoe UI", "Helvetica Neue", Arial; +} + +.NetworkError-reason { + display: inline; +} + +.NetworkError-button { + font-weight: 600; + margin: 20px 0; + text-transform: uppercase; + transform: translatez(0); + background: rgb(66, 133, 244); + border: 0; + border-radius: 2px; + box-sizing: border-box; + color: #fff; + cursor: pointer; + font-size: .875em; + padding: 10px 24px; + transition: box-shadow 200ms cubic-bezier(0.4, 0, 0.2, 1); + user-select: none; +} \ No newline at end of file diff --git a/js/preload/preloadMain.js b/js/preload/preloadMain.js index dcfbff6c..bf9f8be2 100644 --- a/js/preload/preloadMain.js +++ b/js/preload/preloadMain.js @@ -21,7 +21,9 @@ const getMediaSources = require('../desktopCapturer/getSources'); const getMediaSource = require('../desktopCapturer/getSource'); const showScreenSharingIndicator = require('../screenSharingIndicator/showScreenSharingIndicator'); const { TitleBar } = require('../windowsTitlebar'); +const { NetworkError } = require('../networkError'); const titleBar = new TitleBar(); +const networkError = new NetworkError(); const { buildNumber } = require('../../package.json'); const SnackBar = require('../snackBar').SnackBar; const KeyCodes = { @@ -493,6 +495,13 @@ function createAPI() { } }); + // display network error info + local.ipcRenderer.on('network-error', (event, data) => { + if (data && typeof data === 'object') { + networkError.showError(data); + } + }); + /** * an event triggered by the main process when * the locale is changed diff --git a/js/windowMgr.js b/js/windowMgr.js index 59d71970..48167037 100644 --- a/js/windowMgr.js +++ b/js/windowMgr.js @@ -10,9 +10,9 @@ const path = require('path'); const nodeURL = require('url'); const querystring = require('querystring'); const filesize = require('filesize'); +const fetch = require('electron-fetch').default; const { getTemplate, getMinimizeOnClose, getTitleBarStyle } = require('./menus/menuTemplate.js'); -const loadErrors = require('./dialogs/showLoadError.js'); const isInDisplayBounds = require('./utils/isInDisplayBounds.js'); const getGuid = require('./utils/getGuid.js'); const log = require('./log.js'); @@ -26,6 +26,7 @@ const { isWhitelisted, parseDomain } = require('./utils/whitelistHandler'); const { initCrashReporterMain, initCrashReporterRenderer } = require('./crashReporter.js'); const i18n = require('./translation/i18n'); const getCmdLineArg = require('./utils/getCmdLineArg'); +const config = readConfigFileSync(); const SpellChecker = require('./spellChecker').SpellCheckHelper; const spellchecker = new SpellChecker(); @@ -48,6 +49,8 @@ let isAutoReload = false; let devToolsEnabled = true; let isCustomTitleBarEnabled = true; let titleBarStyles; +let networkStatusCheckIntervalId; +const networkStatusCheckInterval = 5000; const KeyCodes = { Esc: 27, @@ -68,6 +71,12 @@ const MIN_HEIGHT = 300; const DEFAULT_WIDTH = 300; const DEFAULT_HEIGHT = 600; +const IGNORE_ERROR_CODES = [ + 0, + -3, + -111 +]; + // Certificate transparency whitelist let ctWhitelist = []; @@ -174,8 +183,6 @@ function doCreateMainWindow(initialUrl, initialBounds, isCustomTitleBar) { let url = initialUrl; let key = getGuid(); - const config = readConfigFileSync(); - // condition whether to enable custom Windows 10 title bar isCustomTitleBarEnabled = typeof isCustomTitleBar === 'boolean' && isCustomTitleBar @@ -268,17 +275,6 @@ function doCreateMainWindow(initialUrl, initialBounds, isCustomTitleBar) { } } - function retry() { - if (!isOnline) { - loadErrors.showNetworkConnectivityError(mainWindow, url, retry); - return; - } - - if (mainWindow.webContents) { - mainWindow.webContents.reload(); - } - } - // Event needed to hide native menu bar on Windows 10 as we use custom menu bar mainWindow.webContents.once('did-start-loading', () => { if ((isCustomTitleBarEnabled || isWindowsOS) && mainWindow && !mainWindow.isDestroyed()) { @@ -320,15 +316,13 @@ function doCreateMainWindow(initialUrl, initialBounds, isCustomTitleBar) { } if (!isOnline) { - loadErrors.showNetworkConnectivityError(mainWindow, url, retry); - } else { - // updates the notify config with user preference - notify.updateConfig({ position: position, display: display }); - // removes all existing notifications when main window reloads - notify.reset(); - log.send(logLevels.INFO, 'loaded main window url: ' + url); - + return; } + // updates the notify config with user preference + notify.updateConfig({ position: position, display: display }); + // removes all existing notifications when main window reloads + notify.reset(); + log.send(logLevels.INFO, 'loaded main window url: ' + url); // ELECTRON-540 - needed to automatically // select desktop capture source @@ -345,9 +339,28 @@ function doCreateMainWindow(initialUrl, initialBounds, isCustomTitleBar) { } }); - mainWindow.webContents.on('did-fail-load', function (event, errorCode, - errorDesc, validatedURL) { - loadErrors.showLoadFailure(mainWindow, validatedURL, errorDesc, errorCode, retry, false); + mainWindow.webContents.on('did-fail-load', (event, errorCode, errorDesc, validatedURL) => { + // Verify error code and display appropriate error content + if (!IGNORE_ERROR_CODES.includes(parseInt(errorCode, 10))) { + const message = i18n.getMessageFor('NetworkError'); + mainWindow.webContents.insertCSS(fs.readFileSync(path.join(__dirname, '/networkError/style.css'), 'utf8').toString()); + mainWindow.webContents.send('network-error', { message, error: errorDesc }); + isSymphonyReachable(mainWindow, validatedURL); + } + }); + + mainWindow.webContents.on('did-stop-loading', () => { + // Verify if SDA ended up in a blank page + mainWindow.webContents.executeJavaScript('document.location.href').then((href) => { + if (href === 'data:text/html,chromewebdata') { + const message = i18n.getMessageFor('NetworkError'); + mainWindow.webContents.insertCSS(fs.readFileSync(path.join(__dirname, '/networkError/style.css'), 'utf8').toString()); + mainWindow.webContents.send('network-error', { message, error: "stop loading"}); + isSymphonyReachable(mainWindow, config.url); + } + }).catch((error) => { + log.send(logLevels.ERROR, `Could not read document.location error: ${error}`); + }); }); // In case a renderer process crashes, provide an @@ -1304,6 +1317,44 @@ const logBrowserWindowEvents = (browserWindow, windowName) => { }; +/** + * Validates the network by fetching the pod url + * every 5sec, on active reloads the given window + * + * @param window + * @param url + */ +const isSymphonyReachable = (window, url) => { + if (networkStatusCheckIntervalId) { + return; + } + networkStatusCheckIntervalId = setInterval(() => { + fetch(config.url).then((rsp) => { + if (rsp.status === 200 && isOnline) { + window.loadURL(url); + if (networkStatusCheckIntervalId) { + clearInterval(networkStatusCheckIntervalId); + networkStatusCheckIntervalId = null; + } + } else { + log.send(logLevels.INFO, `Symphony down! statusCode: ${rsp.status} is online: ${isOnline}`); + } + }).catch((error) => { + log.send(logLevels.INFO, `Network status check: No active network connection ${error}`); + }); + }, networkStatusCheckInterval); +}; + +/** + * Clears the network status check interval + */ +const cancelNetworkStatusCheck = () => { + if (networkStatusCheckIntervalId) { + clearInterval(networkStatusCheckIntervalId); + networkStatusCheckIntervalId = null; + } +}; + module.exports = { createMainWindow: createMainWindow, getMainWindow: getMainWindow, @@ -1322,4 +1373,5 @@ module.exports = { getIsOnline: getIsOnline, getSpellchecker: getSpellchecker, isMisspelled: isMisspelled, + cancelNetworkStatusCheck: cancelNetworkStatusCheck, }; diff --git a/locale/en-US.json b/locale/en-US.json index 3c724296..a8a1dc97 100644 --- a/locale/en-US.json +++ b/locale/en-US.json @@ -68,6 +68,12 @@ "Minimize on Close": "Minimize on Close", "Native": "Native", "Network connectivity has been lost. Check your internet connection.": "Network connectivity has been lost. Check your internet connection.", + "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" + }, "No crashes available to share": "No crashes available to share", "No logs are available to share": "No logs are available to share", "Not Allowed": "Not Allowed", diff --git a/locale/en.json b/locale/en.json index 3c724296..a8a1dc97 100644 --- a/locale/en.json +++ b/locale/en.json @@ -68,6 +68,12 @@ "Minimize on Close": "Minimize on Close", "Native": "Native", "Network connectivity has been lost. Check your internet connection.": "Network connectivity has been lost. Check your internet connection.", + "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" + }, "No crashes available to share": "No crashes available to share", "No logs are available to share": "No logs are available to share", "Not Allowed": "Not Allowed", diff --git a/locale/fr-FR.json b/locale/fr-FR.json index 000e0854..a088e89b 100644 --- a/locale/fr-FR.json +++ b/locale/fr-FR.json @@ -68,6 +68,12 @@ "Minimiser on Close": "Minimiser à la fermeture", "Native": "Originaire", "Network connectivity has been lost. Check your internet connection.": "La connectivité a été perdue. Vérifiez votre connection à l'internet.", + "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 réessayer", + "Quit Symphony": "Quitter Symphony" + }, "No crashes available to share": "Pas de crash à partager", "No logs are available to share": "Pas de journal à partager", "Not Allowed": "Interdit", diff --git a/locale/fr.json b/locale/fr.json index 000e0854..a088e89b 100644 --- a/locale/fr.json +++ b/locale/fr.json @@ -68,6 +68,12 @@ "Minimiser on Close": "Minimiser à la fermeture", "Native": "Originaire", "Network connectivity has been lost. Check your internet connection.": "La connectivité a été perdue. Vérifiez votre connection à l'internet.", + "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 réessayer", + "Quit Symphony": "Quitter Symphony" + }, "No crashes available to share": "Pas de crash à partager", "No logs are available to share": "Pas de journal à partager", "Not Allowed": "Interdit", diff --git a/locale/ja-JP.json b/locale/ja-JP.json index 025d2447..3f94c2ad 100644 --- a/locale/ja-JP.json +++ b/locale/ja-JP.json @@ -68,6 +68,12 @@ "Minimize on Close": "閉じるで最小化", "Native": "Native", "Network connectivity has been lost. Check your internet connection.": "ネットワーク接続が失われました。インターネット接続を確認してください。", + "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を終了します" + }, "No crashes available to share": "共有できるクラッシュはありません", "No logs are available to share": "共有できるログはありません", "Not Allowed": "許可されていませ。", diff --git a/locale/ja.json b/locale/ja.json index 025d2447..3f94c2ad 100644 --- a/locale/ja.json +++ b/locale/ja.json @@ -68,6 +68,12 @@ "Minimize on Close": "閉じるで最小化", "Native": "Native", "Network connectivity has been lost. Check your internet connection.": "ネットワーク接続が失われました。インターネット接続を確認してください。", + "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を終了します" + }, "No crashes available to share": "共有できるクラッシュはありません", "No logs are available to share": "共有できるログはありません", "Not Allowed": "許可されていませ。", diff --git a/package.json b/package.json index 21d74d37..96e8c6dd 100644 --- a/package.json +++ b/package.json @@ -113,6 +113,7 @@ "async.mapseries": "0.5.2", "auto-launch": "5.0.5", "electron-dl": "1.12.0", + "electron-fetch": "1.3.0", "electron-log": "2.2.17", "electron-spellchecker": "git+https://github.com/symphonyoss/electron-spellchecker.git#v1.1.5", "ffi": "git+https://github.com/symphonyoss/node-ffi.git#v1.2.9",