756 lines
No EOL
23 KiB
JavaScript
756 lines
No EOL
23 KiB
JavaScript
'use strict';
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
exports.getMainWindowId = getMainWindowId;
|
|
exports.webContentsSend = webContentsSend;
|
|
exports.init = init;
|
|
exports.handleSingleInstance = handleSingleInstance;
|
|
exports.setMainWindowVisible = setMainWindowVisible;
|
|
|
|
var _electron = require('electron');
|
|
|
|
var _path = require('path');
|
|
|
|
var _path2 = _interopRequireDefault(_path);
|
|
|
|
var _url = require('url');
|
|
|
|
var _url2 = _interopRequireDefault(_url);
|
|
|
|
var _Backoff = require('../common/Backoff');
|
|
|
|
var _Backoff2 = _interopRequireDefault(_Backoff);
|
|
|
|
var _appBadge = require('./appBadge');
|
|
|
|
var appBadge = _interopRequireWildcard(_appBadge);
|
|
|
|
var _appConfig = require('./appConfig');
|
|
|
|
var appConfig = _interopRequireWildcard(_appConfig);
|
|
|
|
var _appSettings = require('./appSettings');
|
|
|
|
var _buildInfo = require('./buildInfo');
|
|
|
|
var _buildInfo2 = _interopRequireDefault(_buildInfo);
|
|
|
|
var _ipcMain = require('./ipcMain');
|
|
|
|
var _ipcMain2 = _interopRequireDefault(_ipcMain);
|
|
|
|
var _moduleUpdater = require('./moduleUpdater');
|
|
|
|
var moduleUpdater = _interopRequireWildcard(_moduleUpdater);
|
|
|
|
var _notificationScreen = require('./notificationScreen');
|
|
|
|
var notificationScreen = _interopRequireWildcard(_notificationScreen);
|
|
|
|
var _popoutWindows = require('./popoutWindows');
|
|
|
|
var popoutWindows = _interopRequireWildcard(_popoutWindows);
|
|
|
|
var _splashScreen = require('./splashScreen');
|
|
|
|
var splashScreen = _interopRequireWildcard(_splashScreen);
|
|
|
|
var _systemTray = require('./systemTray');
|
|
|
|
var systemTray = _interopRequireWildcard(_systemTray);
|
|
|
|
var _Constants = require('./Constants');
|
|
|
|
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
|
|
|
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
|
|
const settings = (0, _appSettings.getSettings)();
|
|
const connectionBackoff = new _Backoff2.default(1000, 20000);
|
|
const DISCORD_NAMESPACE = 'DISCORD_';
|
|
|
|
const getWebappEndpoint = () => {
|
|
let endpoint = settings.get('WEBAPP_ENDPOINT');
|
|
if (!endpoint) {
|
|
if (_buildInfo2.default.releaseChannel === 'stable') {
|
|
endpoint = 'https://discordapp.com';
|
|
} else if (_buildInfo2.default.releaseChannel === 'development') {
|
|
endpoint = 'https://canary.discordapp.com';
|
|
} else {
|
|
endpoint = `https://${_buildInfo2.default.releaseChannel}.discordapp.com`;
|
|
}
|
|
}
|
|
return endpoint;
|
|
};
|
|
|
|
const WEBAPP_ENDPOINT = getWebappEndpoint();
|
|
|
|
function getSanitizedPath(path) {
|
|
// using the whatwg URL api, get a sanitized pathname from given path
|
|
// this is because url.parse's `path` may not always have a slash
|
|
// in front of it
|
|
return new _url2.default.URL(path, WEBAPP_ENDPOINT).pathname;
|
|
}
|
|
|
|
function extractPathFromArgs(args, fallbackPath) {
|
|
if (args.length === 3 && args[0] === '--url' && args[1] === '--') {
|
|
try {
|
|
const parsedURL = _url2.default.parse(args[2]);
|
|
if (parsedURL.protocol === 'discord:') {
|
|
return getSanitizedPath(parsedURL.path);
|
|
}
|
|
} catch (_) {} // protect against URIError: URI malformed
|
|
}
|
|
return fallbackPath;
|
|
}
|
|
|
|
// TODO: These should probably be thrown in constants.
|
|
const INITIAL_PATH = extractPathFromArgs(process.argv.slice(1), '/app');
|
|
const WEBAPP_PATH = settings.get('WEBAPP_PATH', `${INITIAL_PATH}?_=${Date.now()}`);
|
|
const URL_TO_LOAD = `${WEBAPP_ENDPOINT}${WEBAPP_PATH}`;
|
|
const MIN_WIDTH = settings.get('MIN_WIDTH', 940);
|
|
const MIN_HEIGHT = settings.get('MIN_HEIGHT', 500);
|
|
const DEFAULT_WIDTH = 1280;
|
|
const DEFAULT_HEIGHT = 720;
|
|
// TODO: document this var's purpose
|
|
const MIN_VISIBLE_ON_SCREEN = 32;
|
|
|
|
let mainWindow = null;
|
|
let mainWindowId = _Constants.DEFAULT_MAIN_WINDOW_ID;
|
|
|
|
// whether we are in an intermediate auth process outside of our normal login screen (for e.g. internal builds)
|
|
let insideAuthFlow = false;
|
|
|
|
// last time the main app renderer has crashed ('crashed' event)
|
|
let lastCrashed = 0;
|
|
|
|
// whether we failed to load a page outside of the intermediate auth flow
|
|
// used to reload the page after a delay
|
|
let lastPageLoadFailed = false;
|
|
|
|
function getMainWindowId() {
|
|
return mainWindowId;
|
|
}
|
|
|
|
function webContentsSend(...args) {
|
|
if (mainWindow != null && mainWindow.webContents != null) {
|
|
const [event, ...options] = args;
|
|
mainWindow.webContents.send(`${DISCORD_NAMESPACE}${event}`, ...options);
|
|
}
|
|
}
|
|
|
|
function saveWindowConfig(browserWindow) {
|
|
try {
|
|
if (!browserWindow) {
|
|
return;
|
|
}
|
|
|
|
settings.set('IS_MAXIMIZED', browserWindow.isMaximized());
|
|
settings.set('IS_MINIMIZED', browserWindow.isMinimized());
|
|
if (!settings.get('IS_MAXIMIZED') && !settings.get('IS_MINIMIZED')) {
|
|
settings.set('WINDOW_BOUNDS', browserWindow.getBounds());
|
|
}
|
|
|
|
settings.save();
|
|
} catch (e) {
|
|
console.error(e);
|
|
}
|
|
}
|
|
|
|
function setWindowVisible(isVisible, andUnminimize) {
|
|
if (mainWindow == null) {
|
|
return;
|
|
}
|
|
|
|
if (isVisible) {
|
|
if (andUnminimize || !mainWindow.isMinimized()) {
|
|
mainWindow.show();
|
|
webContentsSend('MAIN_WINDOW_FOCUS');
|
|
}
|
|
} else {
|
|
webContentsSend('MAIN_WINDOW_BLUR');
|
|
mainWindow.hide();
|
|
if (systemTray.hasInit) {
|
|
systemTray.displayHowToCloseHint();
|
|
}
|
|
}
|
|
|
|
mainWindow.setSkipTaskbar(!isVisible);
|
|
}
|
|
|
|
function doAABBsOverlap(a, b) {
|
|
const ax1 = a.x + a.width;
|
|
const bx1 = b.x + b.width;
|
|
const ay1 = a.y + a.height;
|
|
const by1 = b.y + b.height;
|
|
// clamp a to b, see if it is non-empty
|
|
const cx0 = a.x < b.x ? b.x : a.x;
|
|
const cx1 = ax1 < bx1 ? ax1 : bx1;
|
|
if (cx1 - cx0 > 0) {
|
|
const cy0 = a.y < b.y ? b.y : a.y;
|
|
const cy1 = ay1 < by1 ? ay1 : by1;
|
|
if (cy1 - cy0 > 0) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function applyWindowBoundsToConfig(mainWindowOptions) {
|
|
if (!settings.get('WINDOW_BOUNDS')) {
|
|
mainWindowOptions.center = true;
|
|
return;
|
|
}
|
|
|
|
const bounds = settings.get('WINDOW_BOUNDS');
|
|
bounds.width = Math.max(MIN_WIDTH, bounds.width);
|
|
bounds.height = Math.max(MIN_HEIGHT, bounds.height);
|
|
|
|
let isVisibleOnAnyScreen = false;
|
|
const displays = _electron.screen.getAllDisplays();
|
|
displays.forEach(display => {
|
|
if (isVisibleOnAnyScreen) {
|
|
return;
|
|
}
|
|
const displayBound = display.workArea;
|
|
displayBound.x += MIN_VISIBLE_ON_SCREEN;
|
|
displayBound.y += MIN_VISIBLE_ON_SCREEN;
|
|
displayBound.width -= 2 * MIN_VISIBLE_ON_SCREEN;
|
|
displayBound.height -= 2 * MIN_VISIBLE_ON_SCREEN;
|
|
isVisibleOnAnyScreen = doAABBsOverlap(bounds, displayBound);
|
|
});
|
|
|
|
if (isVisibleOnAnyScreen) {
|
|
mainWindowOptions.width = bounds.width;
|
|
mainWindowOptions.height = bounds.height;
|
|
mainWindowOptions.x = bounds.x;
|
|
mainWindowOptions.y = bounds.y;
|
|
} else {
|
|
mainWindowOptions.center = true;
|
|
}
|
|
}
|
|
|
|
// this can be called multiple times (due to recreating the main app window),
|
|
// so we only want to update existing if we already initialized it
|
|
function setupNotificationScreen(mainWindow) {
|
|
if (!notificationScreen.hasInit) {
|
|
notificationScreen.init({
|
|
mainWindow,
|
|
title: 'Discord Notifications',
|
|
maxVisible: 5,
|
|
screenPosition: 'bottom'
|
|
});
|
|
|
|
notificationScreen.events.on(notificationScreen.NOTIFICATION_CLICK, () => {
|
|
setWindowVisible(true, true);
|
|
});
|
|
} else {
|
|
notificationScreen.setMainWindow(mainWindow);
|
|
}
|
|
}
|
|
|
|
// this can be called multiple times (due to recreating the main app window),
|
|
// so we only want to update existing if we already initialized it
|
|
function setupSystemTray() {
|
|
if (!systemTray.hasInit) {
|
|
systemTray.init({
|
|
onCheckForUpdates: () => moduleUpdater.checkForUpdates(),
|
|
onTrayClicked: () => setWindowVisible(true, true),
|
|
onOpenVoiceSettings: openVoiceSettings,
|
|
onToggleMute: toggleMute,
|
|
onToggleDeafen: toggleDeafen,
|
|
onLaunchApplication: launchApplication
|
|
});
|
|
}
|
|
}
|
|
|
|
// this can be called multiple times (due to recreating the main app window),
|
|
// so we only want to update existing if we already initialized it
|
|
function setupAppBadge() {
|
|
if (!appBadge.hasInit) {
|
|
appBadge.init();
|
|
}
|
|
}
|
|
|
|
// this can be called multiple times (due to recreating the main app window),
|
|
// so we only want to update existing if we already initialized it
|
|
function setupAppConfig() {
|
|
if (!appConfig.hasInit) {
|
|
appConfig.init();
|
|
}
|
|
}
|
|
|
|
// this can be called multiple times (due to recreating the main app window),
|
|
// so we only want to update existing if we already initialized it
|
|
function setupPopouts() {
|
|
if (!popoutWindows.hasInit) {
|
|
popoutWindows.init();
|
|
}
|
|
}
|
|
|
|
function openVoiceSettings() {
|
|
setWindowVisible(true, true);
|
|
webContentsSend('SYSTEM_TRAY_OPEN_VOICE_SETTINGS');
|
|
}
|
|
|
|
function toggleMute() {
|
|
webContentsSend('SYSTEM_TRAY_TOGGLE_MUTE');
|
|
}
|
|
|
|
function toggleDeafen() {
|
|
webContentsSend('SYSTEM_TRAY_TOGGLE_DEAFEN');
|
|
}
|
|
|
|
function launchApplication(applicationId) {
|
|
webContentsSend('LAUNCH_APPLICATION', applicationId);
|
|
}
|
|
|
|
const loadMainPage = () => {
|
|
lastPageLoadFailed = false;
|
|
mainWindow.loadURL(URL_TO_LOAD);
|
|
};
|
|
|
|
const DEFAULT_BACKGROUND_COLOR = '#2f3136';
|
|
const BACKGROUND_COLOR_KEY = 'BACKGROUND_COLOR';
|
|
|
|
function getBackgroundColor() {
|
|
return settings.get(BACKGROUND_COLOR_KEY, DEFAULT_BACKGROUND_COLOR);
|
|
}
|
|
|
|
function setBackgroundColor(color) {
|
|
settings.set(BACKGROUND_COLOR_KEY, color);
|
|
mainWindow.setBackgroundColor(color);
|
|
settings.save();
|
|
}
|
|
|
|
// launch main app window; could be called multiple times for various reasons
|
|
function launchMainAppWindow(isVisible) {
|
|
if (mainWindow) {
|
|
// TODO: message here?
|
|
mainWindow.destroy();
|
|
}
|
|
|
|
const mainWindowOptions = {
|
|
title: 'Discord',
|
|
backgroundColor: getBackgroundColor(),
|
|
width: DEFAULT_WIDTH,
|
|
height: DEFAULT_HEIGHT,
|
|
minWidth: MIN_WIDTH,
|
|
minHeight: MIN_HEIGHT,
|
|
transparent: false,
|
|
frame: false,
|
|
resizable: true,
|
|
show: isVisible,
|
|
webPreferences: {
|
|
blinkFeatures: 'EnumerateDevices,AudioOutputDevices',
|
|
nodeIntegration: false,
|
|
preload: _path2.default.join(__dirname, 'mainScreenPreload.js'),
|
|
nativeWindowOpen: true,
|
|
enableRemoteModule: false,
|
|
spellcheck: true,
|
|
contextIsolation: true,
|
|
// NB: this is required in order to give popouts (or any child window opened via window.open w/ nativeWindowOpen)
|
|
// a chance at a node environment (i.e. they run the preload, have an isolated context, etc.) when
|
|
// `app.allowRendererProcessReuse === false` (default in Electron 7).
|
|
additionalArguments: ['--enable-node-leakage-in-renderers']
|
|
}
|
|
};
|
|
|
|
if (process.platform === 'linux') {
|
|
mainWindowOptions.icon = _path2.default.join(_path2.default.dirname(_electron.app.getPath('exe')), 'discord.png');
|
|
mainWindowOptions.frame = true;
|
|
}
|
|
|
|
applyWindowBoundsToConfig(mainWindowOptions);
|
|
|
|
mainWindow = new _electron.BrowserWindow(mainWindowOptions);
|
|
mainWindowId = mainWindow.id;
|
|
global.mainWindowId = mainWindowId;
|
|
|
|
mainWindow.setMenuBarVisibility(false);
|
|
|
|
if (settings.get('IS_MAXIMIZED')) {
|
|
mainWindow.maximize();
|
|
}
|
|
|
|
if (settings.get('IS_MINIMIZED')) {
|
|
mainWindow.minimize();
|
|
}
|
|
|
|
mainWindow.webContents.on('new-window', (e, windowURL, frameName, disposition, options) => {
|
|
e.preventDefault();
|
|
if (frameName.startsWith(DISCORD_NAMESPACE) && windowURL.startsWith(WEBAPP_ENDPOINT)) {
|
|
popoutWindows.openOrFocusWindow(e, windowURL, frameName, options);
|
|
} else {
|
|
_electron.shell.openExternal(windowURL);
|
|
}
|
|
});
|
|
|
|
mainWindow.webContents.on('did-fail-load', (e, errCode, errDesc, validatedUrl) => {
|
|
if (insideAuthFlow) {
|
|
return;
|
|
}
|
|
|
|
if (validatedUrl !== URL_TO_LOAD) {
|
|
return;
|
|
}
|
|
|
|
// -3 (ABORTED) means we are reloading the page before it has finished loading
|
|
// 0 (???) seems to also mean the same thing
|
|
if (errCode === -3 || errCode === 0) return;
|
|
|
|
lastPageLoadFailed = true;
|
|
console.error('[WebContents] did-fail-load', errCode, errDesc, `retry in ${connectionBackoff.current} ms`);
|
|
connectionBackoff.fail(() => {
|
|
console.log('[WebContents] retrying load', URL_TO_LOAD);
|
|
loadMainPage();
|
|
});
|
|
});
|
|
|
|
mainWindow.webContents.on('did-finish-load', () => {
|
|
if (insideAuthFlow && mainWindow.webContents && mainWindow.webContents.getURL().startsWith(WEBAPP_ENDPOINT)) {
|
|
insideAuthFlow = false;
|
|
}
|
|
|
|
webContentsSend(mainWindow != null && mainWindow.isFocused() ? 'MAIN_WINDOW_FOCUS' : 'MAIN_WINDOW_BLUR');
|
|
if (!lastPageLoadFailed) {
|
|
connectionBackoff.succeed();
|
|
splashScreen.pageReady();
|
|
}
|
|
});
|
|
|
|
mainWindow.webContents.on('crashed', (e, killed) => {
|
|
if (killed) {
|
|
_electron.app.quit();
|
|
return;
|
|
}
|
|
|
|
// if we just crashed under 5 seconds ago, we are probably in a loop, so just die.
|
|
const crashTime = Date.now();
|
|
if (crashTime - lastCrashed < 5 * 1000) {
|
|
console.error('[WebContents] double crashed... RIP =(');
|
|
_electron.app.quit();
|
|
return;
|
|
}
|
|
lastCrashed = crashTime;
|
|
console.error('[WebContents] crashed... reloading');
|
|
launchMainAppWindow(true);
|
|
});
|
|
|
|
// Prevent navigation when links or files are dropping into the app, turning it into a browser.
|
|
// https://github.com/discord/discord/pull/278
|
|
mainWindow.webContents.on('will-navigate', (evt, url) => {
|
|
if (!insideAuthFlow && !url.startsWith(WEBAPP_ENDPOINT)) {
|
|
evt.preventDefault();
|
|
}
|
|
});
|
|
|
|
// track intermediate auth flow
|
|
mainWindow.webContents.on('did-get-redirect-request', (event, oldUrl, newUrl) => {
|
|
if (oldUrl.startsWith(WEBAPP_ENDPOINT) && newUrl.startsWith('https://accounts.google.com/')) {
|
|
insideAuthFlow = true;
|
|
}
|
|
});
|
|
|
|
mainWindow.webContents.on('context-menu', (_, params) => {
|
|
webContentsSend('SPELLCHECK_RESULT', params.misspelledWord, params.dictionarySuggestions);
|
|
});
|
|
|
|
mainWindow.webContents.on('devtools-opened', () => {
|
|
webContentsSend('WINDOW_DEVTOOLS_OPENED');
|
|
});
|
|
|
|
mainWindow.webContents.on('devtools-closed', () => {
|
|
webContentsSend('WINDOW_DEVTOOLS_CLOSED');
|
|
});
|
|
|
|
mainWindow.on('focus', () => {
|
|
webContentsSend('MAIN_WINDOW_FOCUS');
|
|
});
|
|
|
|
mainWindow.on('blur', () => {
|
|
webContentsSend('MAIN_WINDOW_BLUR');
|
|
});
|
|
|
|
mainWindow.on('page-title-updated', (e, title) => {
|
|
if (mainWindow === null) {
|
|
return;
|
|
}
|
|
e.preventDefault();
|
|
if (!title.endsWith('Discord')) {
|
|
title += ' - Discord';
|
|
}
|
|
mainWindow.setTitle(title);
|
|
});
|
|
|
|
mainWindow.on('leave-html-full-screen', () => {
|
|
// fixes a bug wherein embedded videos returning from full screen cause our menu to be visible.
|
|
mainWindow.setMenuBarVisibility(false);
|
|
});
|
|
|
|
mainWindow.webContents.on('did-navigate-in-page', (_, eventUrl) => {
|
|
let parsedUrl;
|
|
try {
|
|
parsedUrl = _url2.default.parse(eventUrl);
|
|
} catch (_) {
|
|
return;
|
|
}
|
|
|
|
// Prevent back navigation from revisting the login page after logging in,
|
|
// or being able to navigate back after signing out.
|
|
if (parsedUrl && parsedUrl.pathname === '/login') {
|
|
mainWindow.webContents.clearHistory();
|
|
}
|
|
});
|
|
|
|
// 'swipe' only works if the classic 3 finger swipe style is enabled in
|
|
// 'System Preferences > Trackpad > More Gestures.' The more modern 2 finger
|
|
// gesture should be added when Electron adds support.
|
|
mainWindow.on('swipe', (_, direction) => {
|
|
switch (direction) {
|
|
case 'left':
|
|
webContentsSend('NAVIGATE_BACK', 'SWIPE');
|
|
break;
|
|
case 'right':
|
|
webContentsSend('NAVIGATE_FORWARD', 'SWIPE');
|
|
break;
|
|
}
|
|
});
|
|
|
|
// Windows/Linux media keys and 4th/5th mouse buttons.
|
|
mainWindow.on('app-command', (_, cmd) => {
|
|
switch (cmd) {
|
|
case 'browser-backward':
|
|
webContentsSend('NAVIGATE_BACK', 'BROWSER');
|
|
break;
|
|
case 'browser-forward':
|
|
webContentsSend('NAVIGATE_FORWARD', 'BROWSER');
|
|
break;
|
|
}
|
|
});
|
|
|
|
if (process.platform === 'win32') {
|
|
setupNotificationScreen(mainWindow);
|
|
}
|
|
|
|
setupSystemTray();
|
|
setupAppBadge();
|
|
setupAppConfig();
|
|
setupPopouts();
|
|
|
|
if (process.platform === 'linux' || process.platform === 'win32') {
|
|
systemTray.show();
|
|
|
|
mainWindow.on('close', e => {
|
|
if (mainWindow === null) {
|
|
// this means we're quitting
|
|
popoutWindows.closePopouts();
|
|
return;
|
|
}
|
|
webContentsSend('MAIN_WINDOW_BLUR');
|
|
|
|
// Save our app settings
|
|
saveWindowConfig(mainWindow);
|
|
|
|
// Quit app if that's the setting
|
|
if (!settings.get('MINIMIZE_TO_TRAY', true)) {
|
|
_electron.app.quit();
|
|
return;
|
|
}
|
|
|
|
// Else, minimize to tray
|
|
setWindowVisible(false);
|
|
e.preventDefault();
|
|
});
|
|
}
|
|
|
|
loadMainPage();
|
|
}
|
|
|
|
let updaterState = _Constants.UpdaterEvents.UPDATE_NOT_AVAILABLE;
|
|
|
|
function handleModuleUpdateCheckFinished(succeeded, updateCount, manualRequired) {
|
|
if (!succeeded) {
|
|
updaterState = _Constants.UpdaterEvents.UPDATE_NOT_AVAILABLE;
|
|
webContentsSend(_Constants.UpdaterEvents.UPDATE_ERROR);
|
|
return;
|
|
}
|
|
|
|
if (updateCount === 0) {
|
|
updaterState = _Constants.UpdaterEvents.UPDATE_NOT_AVAILABLE;
|
|
} else if (manualRequired) {
|
|
updaterState = _Constants.UpdaterEvents.UPDATE_MANUALLY;
|
|
} else {
|
|
updaterState = _Constants.UpdaterEvents.UPDATE_AVAILABLE;
|
|
}
|
|
webContentsSend(updaterState);
|
|
}
|
|
|
|
function handleModuleUpdateDownloadProgress(name, progress) {
|
|
if (mainWindow) {
|
|
mainWindow.setProgressBar(progress);
|
|
}
|
|
webContentsSend(_Constants.UpdaterEvents.MODULE_INSTALL_PROGRESS, name, progress);
|
|
}
|
|
|
|
function handleModuleUpdateDownloadsFinished(succeeded, failed) {
|
|
if (mainWindow) {
|
|
mainWindow.setProgressBar(-1);
|
|
}
|
|
|
|
if (updaterState === _Constants.UpdaterEvents.UPDATE_AVAILABLE) {
|
|
if (failed > 0) {
|
|
updaterState = _Constants.UpdaterEvents.UPDATE_NOT_AVAILABLE;
|
|
webContentsSend(_Constants.UpdaterEvents.UPDATE_ERROR);
|
|
} else {
|
|
updaterState = _Constants.UpdaterEvents.UPDATE_DOWNLOADED;
|
|
webContentsSend(updaterState);
|
|
}
|
|
}
|
|
}
|
|
|
|
function handleModuleUpdateInstalledModule(name, current, total, succeeded) {
|
|
if (mainWindow) {
|
|
mainWindow.setProgressBar(-1);
|
|
}
|
|
webContentsSend(_Constants.UpdaterEvents.MODULE_INSTALLED, name, succeeded);
|
|
}
|
|
|
|
// sets up event listeners between the browser window and the app to send
|
|
// and listen to update-related events
|
|
function setupUpdaterIPC() {
|
|
moduleUpdater.events.on(moduleUpdater.CHECKING_FOR_UPDATES, () => {
|
|
updaterState = _Constants.UpdaterEvents.CHECKING_FOR_UPDATES;
|
|
webContentsSend(updaterState);
|
|
});
|
|
|
|
// TODO(eiz): We currently still need to handle the old style non-object-based
|
|
// updater events to allow discord_desktop_core to be newer than the host asar,
|
|
// which contains the updater itself.
|
|
//
|
|
// Once all clients have updated to a sufficiently new host, we can delete this.
|
|
if (moduleUpdater.supportsEventObjects) {
|
|
moduleUpdater.events.on(moduleUpdater.UPDATE_CHECK_FINISHED, ({ succeeded, updateCount, manualRequired }) => {
|
|
handleModuleUpdateCheckFinished(succeeded, updateCount, manualRequired);
|
|
});
|
|
|
|
moduleUpdater.events.on(moduleUpdater.DOWNLOADING_MODULE_PROGRESS, ({ name, progress }) => {
|
|
handleModuleUpdateDownloadProgress(name, progress);
|
|
});
|
|
|
|
moduleUpdater.events.on(moduleUpdater.DOWNLOADING_MODULES_FINISHED, ({ succeeded, failed }) => {
|
|
handleModuleUpdateDownloadsFinished(succeeded, failed);
|
|
});
|
|
|
|
moduleUpdater.events.on(moduleUpdater.INSTALLED_MODULE, ({ name, current, total, succeeded }) => {
|
|
handleModuleUpdateInstalledModule(name, current, total, succeeded);
|
|
});
|
|
} else {
|
|
moduleUpdater.events.on(moduleUpdater.UPDATE_CHECK_FINISHED, (succeeded, updateCount, manualRequired) => {
|
|
handleModuleUpdateCheckFinished(succeeded, updateCount, manualRequired);
|
|
});
|
|
|
|
moduleUpdater.events.on(moduleUpdater.DOWNLOADING_MODULE_PROGRESS, (name, progress) => {
|
|
handleModuleUpdateDownloadProgress(name, progress);
|
|
});
|
|
|
|
moduleUpdater.events.on(moduleUpdater.DOWNLOADING_MODULES_FINISHED, (succeeded, failed) => {
|
|
handleModuleUpdateDownloadsFinished(succeeded, failed);
|
|
});
|
|
|
|
moduleUpdater.events.on(moduleUpdater.INSTALLED_MODULE, (name, current, total, succeeded) => {
|
|
handleModuleUpdateInstalledModule(name, current, total, succeeded);
|
|
});
|
|
}
|
|
|
|
_ipcMain2.default.on(_Constants.UpdaterEvents.CHECK_FOR_UPDATES, () => {
|
|
if (updaterState === _Constants.UpdaterEvents.UPDATE_NOT_AVAILABLE) {
|
|
moduleUpdater.checkForUpdates();
|
|
} else {
|
|
webContentsSend(updaterState);
|
|
}
|
|
});
|
|
|
|
_ipcMain2.default.on(_Constants.UpdaterEvents.QUIT_AND_INSTALL, () => {
|
|
saveWindowConfig(mainWindow);
|
|
mainWindow = null;
|
|
|
|
// TODO(eiz): This is a workaround for old Linux host versions whose host
|
|
// updater did not have a quitAndInstall() method, which causes the module
|
|
// updater to crash if a host update is available and we try to restart to
|
|
// install modules. Remove when all hosts are updated.
|
|
try {
|
|
moduleUpdater.quitAndInstallUpdates();
|
|
} catch (e) {
|
|
_electron.app.relaunch();
|
|
_electron.app.quit();
|
|
}
|
|
});
|
|
|
|
_ipcMain2.default.on(_Constants.UpdaterEvents.MODULE_INSTALL, (_event, name) => {
|
|
// NOTE: do NOT allow options to be passed in, as this enables a client to downgrade its modules to potentially
|
|
// insecure versions.
|
|
moduleUpdater.install(name, false);
|
|
});
|
|
|
|
_ipcMain2.default.on(_Constants.UpdaterEvents.MODULE_QUERY, (_event, name) => {
|
|
webContentsSend(_Constants.UpdaterEvents.MODULE_INSTALLED, name, moduleUpdater.isInstalled(name));
|
|
});
|
|
|
|
_ipcMain2.default.on(_Constants.UpdaterEvents.UPDATER_HISTORY_QUERY_AND_TRUNCATE, () => {
|
|
webContentsSend(_Constants.UpdaterEvents.UPDATER_HISTORY_RESPONSE, moduleUpdater.events.history);
|
|
moduleUpdater.events.history = [];
|
|
});
|
|
}
|
|
|
|
function init() {
|
|
// electron default behavior is to app.quit here, so long as there are no other listeners. we handle quitting
|
|
// or minimizing to system tray ourselves via mainWindow.on('closed') so this is simply to disable the electron
|
|
// default behavior.
|
|
_electron.app.on('window-all-closed', () => {});
|
|
|
|
_electron.app.on('before-quit', () => {
|
|
saveWindowConfig(mainWindow);
|
|
mainWindow = null;
|
|
notificationScreen.close();
|
|
});
|
|
|
|
// TODO: move this to main startup
|
|
_electron.app.on('gpu-process-crashed', (e, killed) => {
|
|
if (killed) {
|
|
_electron.app.quit();
|
|
}
|
|
});
|
|
|
|
_electron.app.on('accessibility-support-changed', (_event, accessibilitySupportEnabled) => webContentsSend('ACCESSIBILITY_SUPPORT_CHANGED', accessibilitySupportEnabled));
|
|
|
|
_electron.app.on(_Constants.MenuEvents.OPEN_HELP, () => webContentsSend('HELP_OPEN'));
|
|
_electron.app.on(_Constants.MenuEvents.OPEN_SETTINGS, () => webContentsSend('USER_SETTINGS_OPEN'));
|
|
_electron.app.on(_Constants.MenuEvents.CHECK_FOR_UPDATES, () => moduleUpdater.checkForUpdates());
|
|
|
|
_ipcMain2.default.on('SETTINGS_UPDATE_BACKGROUND_COLOR', (_event, backgroundColor) => {
|
|
if (getBackgroundColor() !== backgroundColor) {
|
|
setBackgroundColor(backgroundColor);
|
|
}
|
|
});
|
|
|
|
setupUpdaterIPC();
|
|
launchMainAppWindow(false);
|
|
}
|
|
|
|
function handleSingleInstance(args) {
|
|
if (mainWindow != null) {
|
|
const appPath = extractPathFromArgs(args);
|
|
if (appPath != null) {
|
|
webContentsSend('MAIN_WINDOW_PATH', appPath);
|
|
}
|
|
setWindowVisible(true, false);
|
|
mainWindow.focus();
|
|
}
|
|
}
|
|
|
|
function setMainWindowVisible(visible) {
|
|
setWindowVisible(visible, false);
|
|
} |