mirror of
https://github.com/finos/SymphonyElectron.git
synced 2025-02-25 18:55:29 -06:00
ELECTRON-967 (Handle network state and implement retry logic) (#599)
* ELECTRON-967 - Display Error content when there is no active network connection * ELECTRON-967 - Fix css issue for Windows * ELECTRON-967 - Optimize code
This commit is contained in:
committed by
Vishwas Shashidhar
parent
565818cfce
commit
c1b09e4315
@@ -25,7 +25,9 @@ const cmds = keyMirror({
|
||||
setIsInMeeting: null,
|
||||
setLocale: null,
|
||||
keyPress: null,
|
||||
openScreenSharingIndicator: null
|
||||
openScreenSharingIndicator: null,
|
||||
cancelNetworkStatusCheck: null,
|
||||
quitWindow: null,
|
||||
});
|
||||
|
||||
module.exports = {
|
||||
|
@@ -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:
|
||||
}
|
||||
|
||||
|
126
js/networkError/contents.js
Normal file
126
js/networkError/contents.js
Normal file
@@ -0,0 +1,126 @@
|
||||
const errorContent = (data) => {
|
||||
return (`<div id="main-frame" class="content-wrapper">
|
||||
<div id="main-content">
|
||||
<div class="NetworkError-icon">
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="50px" viewBox="0 0 19.7 32" style="enable-background:new 0 0 19.7 32;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.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_);
|
||||
}
|
||||
</style>
|
||||
<title>Symphony_logo</title>
|
||||
<g id="Page-1">
|
||||
<g id="Symphony_logo">
|
||||
<g id="logo2">
|
||||
<linearGradient id="Shape_8_" gradientUnits="userSpaceOnUse" x1="39.8192" y1="23.9806"
|
||||
x2="39.2461" y2="23.7258"
|
||||
gradientTransform="matrix(7.6157 0 0 -3.458 -295.3247 101.0398)">
|
||||
<stop offset="0" style="stop-color:#197A68"/>
|
||||
<stop offset="1" style="stop-color:#329D87"/>
|
||||
</linearGradient>
|
||||
<path id="Shape" class="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.9162" y1="22.8111" x2="29.9162"
|
||||
y2="22.8111" gradientTransform="matrix(2.7978 0 0 -4.7596 -73.7035 128.374)">
|
||||
<stop offset="0" style="stop-color:#1D7E7B"/>
|
||||
<stop offset="1" style="stop-color:#35B0B7"/>
|
||||
</linearGradient>
|
||||
<path id="Shape_1_" class="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.9575" y1="21.1358" x2="38.1785"
|
||||
y2="21.8684" gradientTransform="matrix(6.1591 0 0 -11.4226 -223.952 256.8769)">
|
||||
<stop offset="0" style="stop-color:#175952"/>
|
||||
<stop offset="1" style="stop-color:#3A8F88"/>
|
||||
</linearGradient>
|
||||
<path id="Shape_2_" class="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.5688" y1="22.0975" x2="41.0294"
|
||||
y2="22.3767" gradientTransform="matrix(9.5186 0 0 -5.5951 -373.339 140.3243)">
|
||||
<stop offset="0" style="stop-color:#39A8BA"/>
|
||||
<stop offset="1" style="stop-color:#3992B4"/>
|
||||
</linearGradient>
|
||||
<path id="Shape_3_" class="st3" d="M10,17.4h9.5c0-2-0.6-4-1.8-5.6L10,17.4z"/>
|
||||
|
||||
<linearGradient id="Shape_12_" gradientUnits="userSpaceOnUse" x1="41.2142" y1="22.3254" x2="40.7055"
|
||||
y2="22.5477" gradientTransform="matrix(9.9955 0 0 -5.2227 -404.7955 132.8762)">
|
||||
<stop offset="0" style="stop-color:#021C3C"/>
|
||||
<stop offset="1" style="stop-color:#215180"/>
|
||||
</linearGradient>
|
||||
<path id="Shape_4_" class="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.5112" y1="22.1509" x2="34.5112"
|
||||
y2="22.1509" gradientTransform="matrix(3.9169 0 0 -6.6631 -125.1783 161.684)">
|
||||
<stop offset="0" style="stop-color:#23796C"/>
|
||||
<stop offset="1" style="stop-color:#41BEAF"/>
|
||||
</linearGradient>
|
||||
<path id="Shape_5_" class="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.1289" y1="21.9578" x2="36.4868"
|
||||
y2="21.4811" gradientTransform="matrix(5.0353 0 0 -8.5671 -171.5901 208.3326)">
|
||||
<stop offset="0" style="stop-color:#14466A"/>
|
||||
<stop offset="1" style="stop-color:#286395"/>
|
||||
</linearGradient>
|
||||
<path id="Shape_6_" class="st6" d="M10,26c1.8,0,3.6-0.6,5-1.6l-5-6.9V26z"/>
|
||||
|
||||
<linearGradient id="Shape_15_" gradientUnits="userSpaceOnUse" x1="38.5336" y1="23.6558" x2="39.0866"
|
||||
y2="23.5777" gradientTransform="matrix(6.663 0 0 -3.5931 -244.8399 102.8351)">
|
||||
<stop offset="0" style="stop-color:#261D49"/>
|
||||
<stop offset="1" style="stop-color:#483A6D"/>
|
||||
</linearGradient>
|
||||
<path id="Shape_7_" class="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 class="main-message">
|
||||
<p class="NetworkError-header">${data["Problem connecting to Symphony"] || "Problem connecting to Symphony"}</p>
|
||||
<p id="NetworkError-reason">
|
||||
${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."}
|
||||
</p>
|
||||
<div id="error-code" class="NetworkError-error-code">ERR_INTERNET_DISCONNECTED</div>
|
||||
<button id="cancel-retry-button" class="NetworkError-button">${data["Cancel Retry"] || "Cancel Retry"}</button>
|
||||
<button id="quit-button" class="NetworkError-button">${data["Quit Symphony"] || "Quit Symphony"}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>`);
|
||||
};
|
||||
|
||||
|
||||
module.exports = {
|
||||
errorContent,
|
||||
};
|
44
js/networkError/index.js
Normal file
44
js/networkError/index.js
Normal file
@@ -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,
|
||||
};
|
71
js/networkError/style.css
Normal file
71
js/networkError/style.css
Normal file
@@ -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;
|
||||
}
|
@@ -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
|
||||
|
102
js/windowMgr.js
102
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,
|
||||
};
|
||||
|
@@ -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",
|
||||
|
@@ -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",
|
||||
|
@@ -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",
|
||||
|
@@ -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",
|
||||
|
@@ -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": "許可されていませ。",
|
||||
|
@@ -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": "許可されていませ。",
|
||||
|
@@ -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.4",
|
||||
"ffi": "git+https://github.com/symphonyoss/node-ffi.git#v1.2.8",
|
||||
|
Reference in New Issue
Block a user