Changes of osx canary v0.0.248 74
55
app/Constants.js
Normal file
|
@ -0,0 +1,55 @@
|
|||
'use strict';
|
||||
|
||||
// before we can set up (and export) our constants, we first need to grab bootstrap's constants
|
||||
// so we can merge them in with our constants
|
||||
function init(bootstrapConstants) {
|
||||
const APP_NAME = bootstrapConstants.APP_NAME;
|
||||
const API_ENDPOINT = bootstrapConstants.API_ENDPOINT;
|
||||
const UPDATE_ENDPOINT = bootstrapConstants.UPDATE_ENDPOINT;
|
||||
const APP_ID = bootstrapConstants.APP_ID;
|
||||
|
||||
const DEFAULT_MAIN_WINDOW_ID = 0;
|
||||
const MAIN_APP_DIRNAME = __dirname;
|
||||
|
||||
const UpdaterEvents = {
|
||||
UPDATE_NOT_AVAILABLE: 'UPDATE_NOT_AVAILABLE',
|
||||
CHECKING_FOR_UPDATES: 'CHECKING_FOR_UPDATES',
|
||||
UPDATE_ERROR: 'UPDATE_ERROR',
|
||||
UPDATE_MANUALLY: 'UPDATE_MANUALLY',
|
||||
UPDATE_AVAILABLE: 'UPDATE_AVAILABLE',
|
||||
MODULE_INSTALL_PROGRESS: 'MODULE_INSTALL_PROGRESS',
|
||||
UPDATE_DOWNLOADED: 'UPDATE_DOWNLOADED',
|
||||
MODULE_INSTALLED: 'MODULE_INSTALLED',
|
||||
CHECK_FOR_UPDATES: 'CHECK_FOR_UPDATES',
|
||||
QUIT_AND_INSTALL: 'QUIT_AND_INSTALL',
|
||||
MODULE_INSTALL: 'MODULE_INSTALL',
|
||||
MODULE_QUERY: 'MODULE_QUERY',
|
||||
UPDATER_HISTORY_QUERY_AND_TRUNCATE: 'UPDATER_HISTORY_QUERY_AND_TRUNCATE',
|
||||
UPDATER_HISTORY_RESPONSE: 'UPDATER_HISTORY_RESPONSE'
|
||||
};
|
||||
|
||||
const MenuEvents = {
|
||||
OPEN_HELP: 'menu:open-help',
|
||||
OPEN_SETTINGS: 'menu:open-settings',
|
||||
CHECK_FOR_UPDATES: 'menu:check-for-updates'
|
||||
};
|
||||
|
||||
const exported = {
|
||||
APP_NAME,
|
||||
DEFAULT_MAIN_WINDOW_ID,
|
||||
MAIN_APP_DIRNAME,
|
||||
APP_ID,
|
||||
API_ENDPOINT,
|
||||
UPDATE_ENDPOINT,
|
||||
UpdaterEvents,
|
||||
MenuEvents
|
||||
};
|
||||
|
||||
for (const key of Object.keys(exported)) {
|
||||
module.exports[key] = exported[key];
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
init
|
||||
};
|
26
app/GPUSettings.js
Normal file
|
@ -0,0 +1,26 @@
|
|||
'use strict';
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.getEnableHardwareAcceleration = getEnableHardwareAcceleration;
|
||||
exports.setEnableHardwareAcceleration = setEnableHardwareAcceleration;
|
||||
|
||||
var _electron = require('electron');
|
||||
|
||||
var _appSettings = require('./appSettings');
|
||||
|
||||
const settings = (0, _appSettings.getSettings)();
|
||||
|
||||
function getEnableHardwareAcceleration() {
|
||||
// TODO: This should probably a constant
|
||||
return settings.get('enableHardwareAcceleration', true);
|
||||
}
|
||||
|
||||
function setEnableHardwareAcceleration(enableHardwareAcceleration) {
|
||||
settings.set('enableHardwareAcceleration', enableHardwareAcceleration);
|
||||
settings.save();
|
||||
|
||||
_electron.app.relaunch();
|
||||
_electron.app.exit(0);
|
||||
}
|
104
app/appBadge.js
Normal file
|
@ -0,0 +1,104 @@
|
|||
'use strict';
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.hasInit = undefined;
|
||||
exports.init = init;
|
||||
|
||||
var _electron = require('electron');
|
||||
|
||||
var _utils = require('./utils');
|
||||
|
||||
var _mainScreen = require('./mainScreen');
|
||||
|
||||
var _appFeatures = require('./appFeatures');
|
||||
|
||||
var _ipcMain = require('./ipcMain');
|
||||
|
||||
var _ipcMain2 = _interopRequireDefault(_ipcMain);
|
||||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
|
||||
const features = (0, _appFeatures.getFeatures)();
|
||||
|
||||
let hasInit = exports.hasInit = false;
|
||||
|
||||
let lastIndex;
|
||||
let appIcons;
|
||||
|
||||
/**
|
||||
* Used on Windows to set the taskbar icon
|
||||
*/
|
||||
function init() {
|
||||
// Only init on win32 platforms
|
||||
if (process.platform !== 'win32') return;
|
||||
|
||||
if (hasInit) {
|
||||
console.warn('appBadge: Has already init! Cancelling init.');
|
||||
return;
|
||||
}
|
||||
exports.hasInit = hasInit = true;
|
||||
|
||||
lastIndex = null;
|
||||
appIcons = [];
|
||||
|
||||
const resourcePath = `app/images/badges`;
|
||||
|
||||
for (let i = 1; i <= 11; i++) {
|
||||
appIcons.push((0, _utils.exposeModuleResource)(resourcePath, `badge-${i}.ico`));
|
||||
}
|
||||
|
||||
// TODO: remove on or after April 2018
|
||||
features.declareSupported('new_app_badge');
|
||||
|
||||
_ipcMain2.default.on('APP_BADGE_SET', (_event, count) => setAppBadge(count));
|
||||
}
|
||||
|
||||
function setAppBadge(count) {
|
||||
const win = _electron.BrowserWindow.fromId((0, _mainScreen.getMainWindowId)());
|
||||
|
||||
const { index, description } = getOverlayIconData(count);
|
||||
|
||||
// Prevent setting a new icon when the icon is the same
|
||||
if (lastIndex !== index) {
|
||||
if (index == null) {
|
||||
win.setOverlayIcon(null, description);
|
||||
} else {
|
||||
win.setOverlayIcon(appIcons[index], description);
|
||||
}
|
||||
|
||||
lastIndex = index;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* -1 is bullet
|
||||
* 0 is nothing
|
||||
* 1-9 is a number badge
|
||||
* 10+ is `9+`
|
||||
*/
|
||||
function getOverlayIconData(count) {
|
||||
// Unread message badge
|
||||
if (count === -1) {
|
||||
return {
|
||||
index: 10, // this.appIcons.length - 1
|
||||
description: `Unread messages`
|
||||
};
|
||||
}
|
||||
|
||||
// Clear overlay icon
|
||||
if (count === 0) {
|
||||
return {
|
||||
index: null, // null is used to clear the overlay icon
|
||||
description: 'No Notifications'
|
||||
};
|
||||
}
|
||||
|
||||
// Notification badge
|
||||
const index = Math.max(1, Math.min(count, 10)) - 1; // arrays are 0 based
|
||||
return {
|
||||
index,
|
||||
description: `${index} notifications`
|
||||
};
|
||||
}
|
68
app/appConfig.js
Normal file
|
@ -0,0 +1,68 @@
|
|||
'use strict';
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.hasInit = undefined;
|
||||
exports.init = init;
|
||||
|
||||
var _autoStart = require('./autoStart');
|
||||
|
||||
var autoStart = _interopRequireWildcard(_autoStart);
|
||||
|
||||
var _appSettings = require('./appSettings');
|
||||
|
||||
var _appFeatures = require('./appFeatures');
|
||||
|
||||
var _ipcMain = require('./ipcMain');
|
||||
|
||||
var _ipcMain2 = _interopRequireDefault(_ipcMain);
|
||||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
|
||||
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; } }
|
||||
|
||||
const settings = (0, _appSettings.getSettings)();
|
||||
const features = (0, _appFeatures.getFeatures)();
|
||||
const NOOP = () => {};
|
||||
|
||||
let hasInit = exports.hasInit = false;
|
||||
|
||||
function init() {
|
||||
if (hasInit) {
|
||||
console.warn('appConfig: Has already init! Cancelling init.');
|
||||
return;
|
||||
}
|
||||
exports.hasInit = hasInit = true;
|
||||
|
||||
// TODO remove on or after March 2018
|
||||
features.declareSupported('app_configs');
|
||||
|
||||
_ipcMain2.default.on('TOGGLE_MINIMIZE_TO_TRAY', (_event, value) => setMinimizeOnClose(value));
|
||||
_ipcMain2.default.on('TOGGLE_OPEN_ON_STARTUP', (_event, value) => toggleRunOnStartup(value));
|
||||
_ipcMain2.default.on('TOGGLE_START_MINIMIZED', (_event, value) => toggleStartMinimized(value));
|
||||
}
|
||||
|
||||
function setMinimizeOnClose(minimizeToTray) {
|
||||
settings.set('MINIMIZE_TO_TRAY', minimizeToTray);
|
||||
}
|
||||
|
||||
function toggleRunOnStartup(openOnStartup) {
|
||||
settings.set('OPEN_ON_STARTUP', openOnStartup);
|
||||
|
||||
if (openOnStartup) {
|
||||
autoStart.install(NOOP);
|
||||
} else {
|
||||
autoStart.uninstall(NOOP);
|
||||
}
|
||||
}
|
||||
|
||||
function toggleStartMinimized(startMinimized) {
|
||||
settings.set('START_MINIMIZED', startMinimized);
|
||||
autoStart.isInstalled(installed => {
|
||||
// Only update the registry for this toggle if the app was already set to autorun
|
||||
if (installed) {
|
||||
autoStart.install(NOOP);
|
||||
}
|
||||
});
|
||||
}
|
23
app/appFeatures.js
Normal file
|
@ -0,0 +1,23 @@
|
|||
'use strict';
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.init = init;
|
||||
exports.getFeatures = getFeatures;
|
||||
|
||||
var _FeatureFlags = require('../common/FeatureFlags');
|
||||
|
||||
var _FeatureFlags2 = _interopRequireDefault(_FeatureFlags);
|
||||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
|
||||
let features;
|
||||
|
||||
function init() {
|
||||
features = new _FeatureFlags2.default();
|
||||
}
|
||||
|
||||
function getFeatures() {
|
||||
return features;
|
||||
}
|
3
app/appSettings.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
'use strict';
|
||||
|
||||
module.exports = require('./bootstrapModules').appSettings;
|
143
app/applicationMenu/darwin.js
Normal file
|
@ -0,0 +1,143 @@
|
|||
'use strict';
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
|
||||
var _electron = require('electron');
|
||||
|
||||
var _Constants = require('../Constants');
|
||||
|
||||
var Constants = _interopRequireWildcard(_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; } }
|
||||
|
||||
const { MenuEvents } = Constants;
|
||||
const SEPARATOR = { type: 'separator' };
|
||||
|
||||
function getWindow() {
|
||||
let window = _electron.BrowserWindow.getFocusedWindow();
|
||||
if (!window) {
|
||||
const windowList = _electron.BrowserWindow.getAllWindows();
|
||||
if (windowList && windowList[0]) {
|
||||
window = windowList[0];
|
||||
window.show();
|
||||
window.focus();
|
||||
}
|
||||
}
|
||||
return window;
|
||||
}
|
||||
|
||||
exports.default = [{
|
||||
label: 'Discord',
|
||||
submenu: [{
|
||||
label: 'About Discord',
|
||||
selector: 'orderFrontStandardAboutPanel:'
|
||||
}, {
|
||||
label: 'Check for Updates...',
|
||||
click: () => _electron.app.emit(MenuEvents.CHECK_FOR_UPDATES)
|
||||
}, {
|
||||
label: 'Acknowledgements',
|
||||
click: () => _electron.shell.openExternal('https://discordapp.com/acknowledgements')
|
||||
}, SEPARATOR, {
|
||||
label: 'Preferences',
|
||||
click: () => _electron.app.emit(MenuEvents.OPEN_SETTINGS),
|
||||
accelerator: 'Command+,'
|
||||
}, SEPARATOR, {
|
||||
label: 'Services',
|
||||
submenu: []
|
||||
}, SEPARATOR, {
|
||||
label: 'Hide Discord',
|
||||
selector: 'hide:',
|
||||
accelerator: 'Command+H'
|
||||
}, {
|
||||
label: 'Hide Others',
|
||||
selector: 'hideOtherApplications:',
|
||||
accelerator: 'Command+Alt+H'
|
||||
}, {
|
||||
label: 'Show All',
|
||||
selector: 'unhideAllApplications:'
|
||||
}, SEPARATOR, {
|
||||
label: 'Quit',
|
||||
click: () => _electron.app.quit(),
|
||||
accelerator: 'Command+Q'
|
||||
}]
|
||||
}, {
|
||||
label: 'Edit',
|
||||
submenu: [{
|
||||
role: 'undo',
|
||||
accelerator: 'Command+Z'
|
||||
}, {
|
||||
role: 'redo',
|
||||
accelerator: 'Shift+Command+Z'
|
||||
}, SEPARATOR, {
|
||||
role: 'cut',
|
||||
accelerator: 'Command+X'
|
||||
}, {
|
||||
role: 'copy',
|
||||
accelerator: 'Command+C'
|
||||
}, {
|
||||
role: 'paste',
|
||||
accelerator: 'Command+V'
|
||||
}, {
|
||||
role: 'selectAll',
|
||||
accelerator: 'Command+A'
|
||||
}]
|
||||
}, {
|
||||
label: 'View',
|
||||
submenu: [{
|
||||
label: 'Reload',
|
||||
click: () => {
|
||||
const window = getWindow();
|
||||
if (window) {
|
||||
window.webContents.reloadIgnoringCache();
|
||||
}
|
||||
},
|
||||
accelerator: 'Command+R'
|
||||
}, {
|
||||
label: 'Toggle Full Screen',
|
||||
click: () => {
|
||||
const window = getWindow();
|
||||
if (window) {
|
||||
window.setFullScreen(!window.isFullScreen());
|
||||
}
|
||||
},
|
||||
accelerator: 'Command+Control+F'
|
||||
}, SEPARATOR, {
|
||||
label: 'Developer',
|
||||
submenu: [{
|
||||
label: 'Toggle Developer Tools',
|
||||
click: () => {
|
||||
const window = getWindow();
|
||||
if (window) {
|
||||
window.toggleDevTools();
|
||||
}
|
||||
},
|
||||
accelerator: 'Alt+Command+I'
|
||||
}]
|
||||
}]
|
||||
}, {
|
||||
label: 'Window',
|
||||
submenu: [{
|
||||
label: 'Minimize',
|
||||
selector: 'performMiniaturize:',
|
||||
accelerator: 'Command+M'
|
||||
}, {
|
||||
label: 'Zoom',
|
||||
selector: 'performZoom:'
|
||||
}, {
|
||||
label: 'Close',
|
||||
accelerator: 'Command+W',
|
||||
selector: 'hide:'
|
||||
}, SEPARATOR, {
|
||||
label: 'Bring All to Front',
|
||||
selector: 'arrangeInFront:'
|
||||
}]
|
||||
}, {
|
||||
label: 'Help',
|
||||
submenu: [{
|
||||
label: 'Discord Help',
|
||||
click: () => _electron.app.emit(MenuEvents.OPEN_HELP)
|
||||
}]
|
||||
}];
|
||||
module.exports = exports.default;
|
12
app/applicationMenu/index.js
Normal file
|
@ -0,0 +1,12 @@
|
|||
'use strict';
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
|
||||
var _electron = require('electron');
|
||||
|
||||
const menu = require('./' + process.platform);
|
||||
|
||||
exports.default = _electron.Menu.buildFromTemplate(menu);
|
||||
module.exports = exports.default;
|
78
app/applicationMenu/linux.js
Normal file
|
@ -0,0 +1,78 @@
|
|||
'use strict';
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
|
||||
var _electron = require('electron');
|
||||
|
||||
var _Constants = require('../Constants');
|
||||
|
||||
var Constants = _interopRequireWildcard(_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; } }
|
||||
|
||||
const { MenuEvents } = Constants;
|
||||
const SEPARATOR = { type: 'separator' };
|
||||
|
||||
exports.default = [{
|
||||
label: '&File',
|
||||
submenu: [{
|
||||
label: '&Options',
|
||||
click: () => _electron.app.emit(MenuEvents.OPEN_SETTINGS),
|
||||
accelerator: 'Control+,'
|
||||
}, SEPARATOR, {
|
||||
label: 'E&xit',
|
||||
click: () => _electron.app.quit(),
|
||||
accelerator: 'Control+Q'
|
||||
}]
|
||||
}, {
|
||||
label: '&Edit',
|
||||
submenu: [{
|
||||
role: 'undo',
|
||||
accelerator: 'Control+Z'
|
||||
}, {
|
||||
role: 'redo',
|
||||
accelerator: 'Shift+Control+Z'
|
||||
}, SEPARATOR, {
|
||||
role: 'cut',
|
||||
accelerator: 'Control+X'
|
||||
}, {
|
||||
role: 'copy',
|
||||
accelerator: 'Control+C'
|
||||
}, {
|
||||
role: 'paste',
|
||||
accelerator: 'Control+V'
|
||||
}, {
|
||||
role: 'selectAll',
|
||||
accelerator: 'Control+A'
|
||||
}]
|
||||
}, {
|
||||
label: '&View',
|
||||
submenu: [{
|
||||
label: '&Reload',
|
||||
click: () => _electron.BrowserWindow.getFocusedWindow().webContents.reloadIgnoringCache(),
|
||||
accelerator: 'Control+R'
|
||||
}, {
|
||||
label: 'Toggle &Full Screen',
|
||||
click: () => _electron.BrowserWindow.getFocusedWindow().setFullScreen(!_electron.BrowserWindow.getFocusedWindow().isFullScreen()),
|
||||
accelerator: 'Control+Shift+F'
|
||||
}, SEPARATOR, {
|
||||
label: '&Developer',
|
||||
submenu: [{
|
||||
label: 'Toggle Developer &Tools',
|
||||
click: () => _electron.BrowserWindow.getFocusedWindow().toggleDevTools(),
|
||||
accelerator: 'Control+Shift+I'
|
||||
}]
|
||||
}]
|
||||
}, {
|
||||
label: '&Help',
|
||||
submenu: [{
|
||||
label: 'Check for Updates',
|
||||
click: () => _electron.app.emit(MenuEvents.CHECK_FOR_UPDATES)
|
||||
}, SEPARATOR, {
|
||||
label: 'Discord Help',
|
||||
click: () => _electron.app.emit(MenuEvents.OPEN_HELP)
|
||||
}]
|
||||
}];
|
||||
module.exports = exports.default;
|
57
app/applicationMenu/win32.js
Normal file
|
@ -0,0 +1,57 @@
|
|||
'use strict';
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
|
||||
var _electron = require('electron');
|
||||
|
||||
var _Constants = require('../Constants');
|
||||
|
||||
var Constants = _interopRequireWildcard(_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; } }
|
||||
|
||||
const { MenuEvents } = Constants;
|
||||
const SEPARATOR = { type: 'separator' };
|
||||
|
||||
exports.default = [{
|
||||
label: '&File',
|
||||
submenu: [{
|
||||
label: '&Options',
|
||||
click: () => _electron.app.emit(MenuEvents.OPEN_SETTINGS),
|
||||
accelerator: 'Ctrl+,'
|
||||
}, SEPARATOR, {
|
||||
label: '&Exit',
|
||||
click: () => _electron.app.quit(),
|
||||
accelerator: 'Alt+F4'
|
||||
}]
|
||||
}, {
|
||||
label: '&View',
|
||||
submenu: [{
|
||||
label: '&Reload',
|
||||
click: () => _electron.BrowserWindow.getFocusedWindow().webContents.reloadIgnoringCache(),
|
||||
accelerator: 'Control+R'
|
||||
}, {
|
||||
label: 'Toggle &Full Screen',
|
||||
click: () => _electron.BrowserWindow.getFocusedWindow().setFullScreen(!_electron.BrowserWindow.getFocusedWindow().isFullScreen()),
|
||||
accelerator: 'Control+Shift+F'
|
||||
}, SEPARATOR, {
|
||||
label: '&Developer',
|
||||
submenu: [{
|
||||
label: 'Toggle Developer &Tools',
|
||||
click: () => _electron.BrowserWindow.getFocusedWindow().toggleDevTools(),
|
||||
accelerator: 'Control+Shift+I'
|
||||
}]
|
||||
}]
|
||||
}, {
|
||||
label: '&Help',
|
||||
submenu: [{
|
||||
label: 'Check for Updates',
|
||||
click: () => _electron.app.emit(MenuEvents.CHECK_FOR_UPDATES)
|
||||
}, SEPARATOR, {
|
||||
label: 'Discord Help',
|
||||
click: () => _electron.app.emit(MenuEvents.OPEN_HELP)
|
||||
}]
|
||||
}];
|
||||
module.exports = exports.default;
|
3
app/autoStart.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
'use strict';
|
||||
|
||||
module.exports = require('./bootstrapModules').autoStart;
|
13
app/bootstrapModules.js
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
"use strict";
|
||||
|
||||
let hasInit = false;
|
||||
|
||||
exports.init = function (bootstrapModules) {
|
||||
if (hasInit) {
|
||||
throw new Error(`bootstrapModules has already init`);
|
||||
}
|
||||
for (const mod of Object.keys(bootstrapModules)) {
|
||||
exports[mod] = bootstrapModules[mod];
|
||||
}
|
||||
hasInit = true;
|
||||
};
|
3
app/buildInfo.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
'use strict';
|
||||
|
||||
module.exports = require('./bootstrapModules').buildInfo;
|
3865
app/data/cacert.pem
Normal file
29
app/discord_native/_common.js
Normal file
|
@ -0,0 +1,29 @@
|
|||
'use strict';
|
||||
|
||||
// Private utilities for discordNativeAPI.
|
||||
// Don't expose to the public DiscordNative.
|
||||
|
||||
const electron = require('electron');
|
||||
const remoteProcess = electron.remote.process;
|
||||
const BrowserWindow = electron.remote.BrowserWindow;
|
||||
|
||||
function getWindow(key) {
|
||||
if (!key) return getCurrentWindow();
|
||||
return BrowserWindow.getAllWindows().find(window => window.windowKey === key);
|
||||
}
|
||||
|
||||
function getCurrentWindow() {
|
||||
return electron.remote.getCurrentWindow();
|
||||
}
|
||||
|
||||
function getElectronMajorVersion() {
|
||||
if (remoteProcess.versions.electron) {
|
||||
return parseInt(remoteProcess.versions.electron.split('.')[0]);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getCurrentWindow,
|
||||
getWindow,
|
||||
getElectronMajorVersion
|
||||
};
|
14
app/discord_native/accessibility.js
Normal file
|
@ -0,0 +1,14 @@
|
|||
'use strict';
|
||||
|
||||
var _electron = require('electron');
|
||||
|
||||
function isAccessibilitySupportEnabled() {
|
||||
// NOTE(faulty): `isX` methods are deprecated and being replaced with
|
||||
// property accessors in Electron. However, the property accessor for this is
|
||||
// seemingly not available yet, so this check tries to accomodate both.
|
||||
return _electron.remote.app.accessibilitySupportEnabled != null ? _electron.remote.app.accessibilitySupportEnabled : _electron.remote.app.isAccessibilitySupportEnabled();
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
isAccessibilitySupportEnabled
|
||||
};
|
41
app/discord_native/clipboard.js
Normal file
|
@ -0,0 +1,41 @@
|
|||
'use strict';
|
||||
|
||||
const electron = require('electron');
|
||||
const invariant = require('invariant');
|
||||
const remote = electron.remote;
|
||||
const nativeImage = electron.nativeImage;
|
||||
|
||||
function copy(text) {
|
||||
if (text) {
|
||||
remote.clipboard.writeText(text);
|
||||
} else {
|
||||
remote.getCurrentWebContents().copy();
|
||||
}
|
||||
}
|
||||
|
||||
function copyImage(imageArrayBuffer, imageSrc) {
|
||||
invariant(imageArrayBuffer != null, 'Image data is empty');
|
||||
|
||||
const nativeImg = nativeImage.createFromBuffer(imageArrayBuffer);
|
||||
electron.clipboard.write({ html: `<img src="${imageSrc}">`, image: nativeImg });
|
||||
}
|
||||
|
||||
function cut() {
|
||||
remote.getCurrentWebContents().cut();
|
||||
}
|
||||
|
||||
function paste() {
|
||||
remote.getCurrentWebContents().paste();
|
||||
}
|
||||
|
||||
function read() {
|
||||
return electron.clipboard.readText();
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
copy,
|
||||
copyImage,
|
||||
cut,
|
||||
paste,
|
||||
read
|
||||
};
|
26
app/discord_native/crashReporter.js
Normal file
|
@ -0,0 +1,26 @@
|
|||
'use strict';
|
||||
|
||||
var _lodash = require('lodash');
|
||||
|
||||
var _lodash2 = _interopRequireDefault(_lodash);
|
||||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
|
||||
const electron = require('electron');
|
||||
const ipcRenderer = require('./ipc');
|
||||
const crashReporter = electron.crashReporter;
|
||||
|
||||
const UPDATE_CRASH_REPORT_METADATA = 'UPDATE_CRASH_REPORT_METADATA';
|
||||
const START_CRASH_REPORTER = 'START_CRASH_REPORTER';
|
||||
|
||||
ipcRenderer.on(START_CRASH_REPORTER, (event, args) => {
|
||||
crashReporter.start(args);
|
||||
});
|
||||
|
||||
function updateCrashReporter(additional_metadata) {
|
||||
ipcRenderer.send(UPDATE_CRASH_REPORT_METADATA, additional_metadata);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
updateCrashReporter
|
||||
};
|
42
app/discord_native/desktopCapture.js
Normal file
|
@ -0,0 +1,42 @@
|
|||
'use strict';
|
||||
|
||||
const electron = require('electron');
|
||||
const { getElectronMajorVersion } = require('./_common');
|
||||
const desktopCapturer = electron.desktopCapturer;
|
||||
|
||||
function hasPromisifiedGetSources() {
|
||||
return getElectronMajorVersion() >= 6;
|
||||
}
|
||||
|
||||
function getDesktopCaptureSources(options) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let getSourcesPromise;
|
||||
if (hasPromisifiedGetSources()) {
|
||||
getSourcesPromise = desktopCapturer.getSources(options);
|
||||
} else {
|
||||
getSourcesPromise = new Promise((resolve, reject) => {
|
||||
desktopCapturer.getSources(options, (err, sources) => {
|
||||
if (err != null) {
|
||||
return reject(err);
|
||||
}
|
||||
|
||||
return resolve(sources);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
getSourcesPromise.then(sources => {
|
||||
return resolve(sources.map(source => {
|
||||
return {
|
||||
id: source.id,
|
||||
name: source.name,
|
||||
url: source.thumbnail.toDataURL()
|
||||
};
|
||||
}));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getDesktopCaptureSources
|
||||
};
|
180
app/discord_native/fileManager.js
Normal file
|
@ -0,0 +1,180 @@
|
|||
'use strict';
|
||||
|
||||
let orderedFiles = (() => {
|
||||
var _ref = _asyncToGenerator(function* (folder) {
|
||||
try {
|
||||
const filenames = yield readdir(folder);
|
||||
const times = yield getTimes(filenames.map(function (filename) {
|
||||
return path.join(folder, filename);
|
||||
}));
|
||||
return times.filter(function (result) {
|
||||
return result.status === 'fulfilled';
|
||||
}).map(function (result) {
|
||||
return result.value;
|
||||
}).sort(function (a, b) {
|
||||
return b.mtime.getTime() - a.mtime.getTime();
|
||||
}).map(function (a) {
|
||||
return a.filename;
|
||||
});
|
||||
} catch (err) {
|
||||
return [];
|
||||
}
|
||||
});
|
||||
|
||||
return function orderedFiles(_x) {
|
||||
return _ref.apply(this, arguments);
|
||||
};
|
||||
})();
|
||||
|
||||
let readLogFiles = (() => {
|
||||
var _ref2 = _asyncToGenerator(function* (maxSize, makeFile) {
|
||||
const modulePath = electron.remote.getGlobal('modulePath');
|
||||
const webrtcLog0 = path.join(modulePath, 'discord_voice', 'discord-webrtc_0');
|
||||
const webrtcLog1 = path.join(modulePath, 'discord_voice', 'discord-webrtc_1');
|
||||
const webrtcLog2 = path.join(modulePath, 'discord_voice', 'discord-last-webrtc_0');
|
||||
const webrtcLog3 = path.join(modulePath, 'discord_voice', 'discord-last-webrtc_1');
|
||||
const hookLog = path.join(modulePath, 'discord_hook', 'hook.log');
|
||||
const audioState = path.join(modulePath, 'discord_voice', 'audio_state.json');
|
||||
const filesToUpload = [webrtcLog0, webrtcLog1, webrtcLog2, webrtcLog3, hookLog, audioState];
|
||||
|
||||
const crashFolder = process.platform === 'win32' ? path.join(os.tmpdir(), 'Discord Crashes', 'reports') : path.join(os.tmpdir(), 'Discord Crashes', 'completed');
|
||||
const crashFiles = yield orderedFiles(crashFolder);
|
||||
if (crashFiles.length > 0) {
|
||||
filesToUpload.push(crashFiles[0]);
|
||||
}
|
||||
|
||||
const files = yield readFiles(filesToUpload, maxSize, function (data, filename) {
|
||||
return makeFile(data, filename, 'application/octet-stream');
|
||||
});
|
||||
return files.filter(function (result) {
|
||||
return result.status === 'fulfilled';
|
||||
}).map(function (result) {
|
||||
return result.value;
|
||||
});
|
||||
});
|
||||
|
||||
return function readLogFiles(_x2, _x3) {
|
||||
return _ref2.apply(this, arguments);
|
||||
};
|
||||
})();
|
||||
|
||||
let openFiles = (() => {
|
||||
var _ref3 = _asyncToGenerator(function* (dialogOptions, maxSize, makeFile) {
|
||||
const filenames = yield showOpenDialog(dialogOptions);
|
||||
if (filenames == null) {
|
||||
return;
|
||||
}
|
||||
const files = yield readFiles(filenames, maxSize, makeFile);
|
||||
files.forEach(function (result) {
|
||||
if (result.status === 'rejected') {
|
||||
throw result.reason;
|
||||
}
|
||||
});
|
||||
return files.map(function (result) {
|
||||
return result.value;
|
||||
});
|
||||
});
|
||||
|
||||
return function openFiles(_x4, _x5, _x6) {
|
||||
return _ref3.apply(this, arguments);
|
||||
};
|
||||
})();
|
||||
|
||||
function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }
|
||||
|
||||
const electron = require('electron');
|
||||
const fs = require('fs');
|
||||
const os = require('os');
|
||||
const path = require('path');
|
||||
const originalFs = require('original-fs');
|
||||
const remoteDialog = electron.remote.dialog;
|
||||
const remoteShell = electron.remote.shell;
|
||||
const { getElectronMajorVersion } = require('./_common');
|
||||
const util = require('util');
|
||||
// TODO: Remove when moving to Electron 6
|
||||
const allSettled = require('promise.allsettled');
|
||||
|
||||
const INVALID_FILENAME_CHAR_REGEX = /[^a-zA-Z0-9-_.]/g;
|
||||
|
||||
allSettled.shim();
|
||||
const readdir = util.promisify(originalFs.readdir);
|
||||
|
||||
function remoteDialogReturnsPromises() {
|
||||
return getElectronMajorVersion() >= 6;
|
||||
}
|
||||
|
||||
function saveWithDialog(fileContents, fileName, filePath) {
|
||||
if (INVALID_FILENAME_CHAR_REGEX.test(fileName)) {
|
||||
throw new Error(`fileName has invalid characters`);
|
||||
}
|
||||
const defaultPath = filePath != null ? path.join(os.homedir(), filePath, fileName) : path.join(os.homedir(), fileName);
|
||||
|
||||
const writeFileToDisk = selectedFileName => {
|
||||
selectedFileName && fs.writeFileSync(selectedFileName, fileContents);
|
||||
};
|
||||
|
||||
if (remoteDialogReturnsPromises()) {
|
||||
remoteDialog.showSaveDialog({ defaultPath }).then(results => writeFileToDisk(results.filePath));
|
||||
} else {
|
||||
remoteDialog.showSaveDialog({ defaultPath }, writeFileToDisk);
|
||||
}
|
||||
}
|
||||
|
||||
function showOpenDialog(dialogOptions) {
|
||||
if (remoteDialogReturnsPromises()) {
|
||||
return remoteDialog.showOpenDialog(dialogOptions).then(results => results.filePaths);
|
||||
} else {
|
||||
return new Promise(resolve => remoteDialog.showOpenDialog(dialogOptions, resolve));
|
||||
}
|
||||
}
|
||||
|
||||
function getTimes(filenames) {
|
||||
return Promise.allSettled(filenames.map(filename => new Promise((resolve, reject) => {
|
||||
originalFs.stat(filename, (err, stats) => {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
if (!stats.isFile()) {
|
||||
return reject(new Error('Not a file'));
|
||||
}
|
||||
return resolve({ filename, mtime: stats.mtime });
|
||||
});
|
||||
})));
|
||||
}
|
||||
|
||||
function showItemInFolder(path) {
|
||||
return remoteShell.showItemInFolder(path);
|
||||
}
|
||||
|
||||
function readFiles(filenames, maxSize, makeFile) {
|
||||
return Promise.allSettled(filenames.map(filename => new Promise((resolve, reject) => {
|
||||
originalFs.stat(filename, (err, stats) => {
|
||||
if (err) return reject(err);
|
||||
|
||||
if (stats.size > maxSize) {
|
||||
const err = new Error('upload too large');
|
||||
// used to help determine why openFiles failed
|
||||
err.code = 'ETOOLARGE';
|
||||
return reject(err);
|
||||
}
|
||||
|
||||
originalFs.readFile(filename, (err, data) => {
|
||||
if (err) return reject(err);
|
||||
return resolve(makeFile(data.buffer, path.basename(filename)));
|
||||
});
|
||||
});
|
||||
})));
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
readLogFiles,
|
||||
saveWithDialog,
|
||||
openFiles,
|
||||
showOpenDialog,
|
||||
showItemInFolder,
|
||||
extname: path.extname,
|
||||
basename: path.basename,
|
||||
dirname: path.dirname,
|
||||
join: path.join,
|
||||
modulePath: electron.remote.getGlobal('modulePath')
|
||||
};
|
19
app/discord_native/globals.js
Normal file
|
@ -0,0 +1,19 @@
|
|||
'use strict';
|
||||
|
||||
const electron = require('electron');
|
||||
const getGlobal = electron.remote.getGlobal;
|
||||
|
||||
module.exports = {
|
||||
get releaseChannel() {
|
||||
return getGlobal('releaseChannel');
|
||||
},
|
||||
get crashReporterMetadata() {
|
||||
return getGlobal('crashReporterMetadata');
|
||||
},
|
||||
get features() {
|
||||
return getGlobal('features');
|
||||
},
|
||||
get appSettings() {
|
||||
return getGlobal('appSettings');
|
||||
}
|
||||
};
|
17
app/discord_native/gpuSettings.js
Normal file
|
@ -0,0 +1,17 @@
|
|||
'use strict';
|
||||
|
||||
const electron = require('electron');
|
||||
const remoteGPUSettings = electron.remote.require('./GPUSettings');
|
||||
|
||||
function getEnableHardwareAcceleration() {
|
||||
return remoteGPUSettings.getEnableHardwareAcceleration();
|
||||
}
|
||||
|
||||
function setEnableHardwareAcceleration(enable) {
|
||||
remoteGPUSettings.setEnableHardwareAcceleration(enable);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getEnableHardwareAcceleration,
|
||||
setEnableHardwareAcceleration
|
||||
};
|
80
app/discord_native/http.js
Normal file
|
@ -0,0 +1,80 @@
|
|||
'use strict';
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.makeChunkedRequest = makeChunkedRequest;
|
||||
|
||||
function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }
|
||||
|
||||
const http = require('http');
|
||||
const https = require('https');
|
||||
|
||||
function makeChunkedRequest(route, chunks, options, callback) {
|
||||
/**
|
||||
* Given an array of chunks, make a slow request, only writing chunks
|
||||
* after a specified amount of time
|
||||
*
|
||||
* route: string
|
||||
* options: object
|
||||
* method: the method of the request
|
||||
* contentType: the content type of the request
|
||||
* chunkInterval: how long to wait to upload a chunk after the last chunk was flushed
|
||||
* token: the token to make an authorized request from
|
||||
* chunks: chunked body of the request to upload
|
||||
*/
|
||||
|
||||
const { method, chunkInterval, token, contentType } = options;
|
||||
|
||||
let httpModule = http;
|
||||
if (route.startsWith('https')) {
|
||||
httpModule = https;
|
||||
}
|
||||
|
||||
const requestPromise = new Promise((() => {
|
||||
var _ref = _asyncToGenerator(function* (resolve, reject) {
|
||||
let writeTimeout;
|
||||
const req = httpModule.request(route, {
|
||||
method,
|
||||
headers: {
|
||||
authorization: token,
|
||||
'Content-Type': contentType,
|
||||
'Content-Length': Buffer.byteLength(chunks.join(''))
|
||||
}
|
||||
}, function (res) {
|
||||
let responseData = '';
|
||||
res.setEncoding('utf8');
|
||||
res.on('data', function (chunk) {
|
||||
responseData += chunk;
|
||||
});
|
||||
|
||||
res.on('end', function () {
|
||||
resolve({ status: res.statusCode, body: responseData });
|
||||
});
|
||||
});
|
||||
|
||||
req.on('error', function (e) {
|
||||
if (writeTimeout != null) {
|
||||
clearTimeout(writeTimeout);
|
||||
}
|
||||
reject(e);
|
||||
});
|
||||
|
||||
for (let i = 0; i < chunks.length; i++) {
|
||||
yield new Promise(function (resolve) {
|
||||
req.write(chunks[i], function () {
|
||||
writeTimeout = setTimeout(resolve, chunkInterval);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
req.end();
|
||||
});
|
||||
|
||||
return function (_x, _x2) {
|
||||
return _ref.apply(this, arguments);
|
||||
};
|
||||
})());
|
||||
|
||||
requestPromise.then(body => callback(null, body)).catch(callback);
|
||||
}
|
23
app/discord_native/ipc.js
Normal file
|
@ -0,0 +1,23 @@
|
|||
'use strict';
|
||||
|
||||
const electron = require('electron');
|
||||
const ipcRenderer = electron.ipcRenderer;
|
||||
|
||||
// We prefix events with the `DISCORD_` string to prevent malicious use
|
||||
function send(event, ...args) {
|
||||
ipcRenderer.send(`DISCORD_${event}`, ...args);
|
||||
}
|
||||
|
||||
function on(event, callback) {
|
||||
ipcRenderer.on(`DISCORD_${event}`, callback);
|
||||
}
|
||||
|
||||
function removeListener(event, callback) {
|
||||
ipcRenderer.removeListener(`DISCORD_${event}`, callback);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
send,
|
||||
on,
|
||||
removeListener
|
||||
};
|
95
app/discord_native/nativeModules.js
Normal file
|
@ -0,0 +1,95 @@
|
|||
'use strict';
|
||||
|
||||
const electron = require('electron');
|
||||
const vm = require('vm');
|
||||
const ipcRenderer = require('./ipc');
|
||||
|
||||
// Set up global module paths in renderer's require
|
||||
const NodeModule = require('module');
|
||||
const mainAppDirname = electron.remote.getGlobal('mainAppDirname');
|
||||
|
||||
let modulePaths = [];
|
||||
|
||||
// add native module paths
|
||||
const remotePaths = electron.remote.require('module').globalPaths;
|
||||
remotePaths.forEach(path => {
|
||||
if (!path.includes('electron.asar')) {
|
||||
modulePaths.push(path);
|
||||
}
|
||||
});
|
||||
|
||||
// add main app module paths (limited to the discord_desktop_core .asar)
|
||||
const mainAppModulePaths = NodeModule._nodeModulePaths(mainAppDirname);
|
||||
modulePaths = modulePaths.concat(mainAppModulePaths.slice(0, 2));
|
||||
|
||||
// apply the module paths
|
||||
module.paths = modulePaths;
|
||||
|
||||
const MODULE_INSTALL = 'MODULE_INSTALL';
|
||||
const MODULE_QUERY = 'MODULE_QUERY';
|
||||
const MODULE_INSTALLED = 'MODULE_INSTALLED';
|
||||
|
||||
const modulePromises = {};
|
||||
|
||||
// Handle successfully installed modules, used in ensureModule
|
||||
ipcRenderer.on(MODULE_INSTALLED, (e, name, success) => {
|
||||
const promise = modulePromises[name];
|
||||
|
||||
if (promise == null || promise.callback == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
promise.callback(success);
|
||||
});
|
||||
|
||||
function ensureModule(name) {
|
||||
let modulePromise = modulePromises[name];
|
||||
if (modulePromise == null) {
|
||||
modulePromise = modulePromises[name] = {};
|
||||
modulePromise.promise = new Promise((resolve, reject) => {
|
||||
modulePromise.callback = success => {
|
||||
modulePromise.callback = null;
|
||||
success ? resolve() : reject(new Error('failed to ensure module'));
|
||||
};
|
||||
|
||||
installModule(name);
|
||||
});
|
||||
}
|
||||
|
||||
return modulePromise.promise;
|
||||
}
|
||||
|
||||
function installModule(name) {
|
||||
ipcRenderer.send(MODULE_INSTALL, name);
|
||||
}
|
||||
|
||||
function queryModule(name) {
|
||||
ipcRenderer.send(MODULE_QUERY, name);
|
||||
}
|
||||
|
||||
// sandbox this function in a new context, else it's susceptible to prototype attacks
|
||||
// - RegExp.prototype.test = () => true
|
||||
const context = vm.createContext(Object.create(null));
|
||||
const _requireModule = vm.runInContext(`
|
||||
function requireModule(remoteRequire, localRequire, name, remote) {
|
||||
if (!/^discord_[a-z0-9_-]+$/.test(name) && name !== 'erlpack') {
|
||||
throw new Error('"' + String(name) + '" is not a whitelisted native module');
|
||||
}
|
||||
return remote ? remoteRequire(name) : localRequire(name);
|
||||
}
|
||||
requireModule
|
||||
`, context);
|
||||
|
||||
const remoteRequire = electron.remote.require;
|
||||
const localRequire = require;
|
||||
|
||||
function requireModule(name, remote) {
|
||||
return _requireModule(remoteRequire, localRequire, name, remote);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
installModule,
|
||||
queryModule,
|
||||
ensureModule,
|
||||
requireModule
|
||||
};
|
14
app/discord_native/os.js
Normal file
|
@ -0,0 +1,14 @@
|
|||
'use strict';
|
||||
|
||||
const os = require('os');
|
||||
const process = require('process');
|
||||
|
||||
let arch = os.arch();
|
||||
if (process.platform === 'win32' && process.env['PROCESSOR_ARCHITEW6432'] != null) {
|
||||
arch = 'x64';
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
release: os.release(),
|
||||
arch
|
||||
};
|
31
app/discord_native/powerSaveBlocker.js
Normal file
|
@ -0,0 +1,31 @@
|
|||
'use strict';
|
||||
|
||||
const electron = require('electron');
|
||||
const powerSaveBlocker = electron.remote.powerSaveBlocker;
|
||||
|
||||
const powerSaveBlockerIds = new Set();
|
||||
|
||||
function blockDisplaySleep() {
|
||||
const newId = powerSaveBlocker.start('prevent-display-sleep');
|
||||
powerSaveBlockerIds.add(newId);
|
||||
return newId;
|
||||
}
|
||||
|
||||
function unblockDisplaySleep(id) {
|
||||
powerSaveBlocker.stop(id);
|
||||
powerSaveBlockerIds.delete(id);
|
||||
}
|
||||
|
||||
function cleanupDisplaySleep() {
|
||||
// cleanup all previous sleeps
|
||||
for (const id of powerSaveBlockerIds) {
|
||||
unblockDisplaySleep(id);
|
||||
}
|
||||
powerSaveBlockerIds.clear();
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
blockDisplaySleep,
|
||||
unblockDisplaySleep,
|
||||
cleanupDisplaySleep
|
||||
};
|
21
app/discord_native/process.js
Normal file
|
@ -0,0 +1,21 @@
|
|||
'use strict';
|
||||
|
||||
const electron = require('electron');
|
||||
const process = require('process');
|
||||
const remoteProcess = electron.remote.require('process');
|
||||
const env = process.env;
|
||||
|
||||
module.exports = {
|
||||
platform: process.platform,
|
||||
arch: process.arch,
|
||||
env: {
|
||||
LOCALAPPDATA: env['LOCALAPPDATA'],
|
||||
'PROGRAMFILES(X86)': env['PROGRAMFILES(X86)'],
|
||||
PROGRAMFILES: env['PROGRAMFILES'],
|
||||
PROGRAMW6432: env['PROGRAMW6432'],
|
||||
PROGRAMDATA: env['PROGRAMDATA']
|
||||
},
|
||||
remote: {
|
||||
resourcesPath: remoteProcess.resourcesPath
|
||||
}
|
||||
};
|
76
app/discord_native/processUtils.js
Normal file
|
@ -0,0 +1,76 @@
|
|||
'use strict';
|
||||
|
||||
const electron = require('electron');
|
||||
const webFrame = electron.webFrame;
|
||||
const remoteSession = electron.remote.session;
|
||||
const remoteApp = electron.remote.app;
|
||||
const process = require('process');
|
||||
const remoteProcess = electron.remote.require('process');
|
||||
const { getElectronMajorVersion } = require('./_common');
|
||||
|
||||
const MEMORY_USAGE_GATHER_INTERVAL = 5000;
|
||||
|
||||
let totalMemoryUsageKB = 0;
|
||||
|
||||
setInterval(() => {
|
||||
Promise.all([process.getProcessMemoryInfo(), remoteProcess.getProcessMemoryInfo()].map(x => x.catch(() => 0))).then(usages => {
|
||||
totalMemoryUsageKB = usages.reduce((total, usage) => total + usage.private, 0);
|
||||
});
|
||||
}, MEMORY_USAGE_GATHER_INTERVAL);
|
||||
|
||||
// [adill] "warm" the cache, as the first call to query cpu % will always be zero
|
||||
let _ = getCurrentCPUUsagePercent();
|
||||
|
||||
function flushDNSCache() {
|
||||
if (!remoteSession) return;
|
||||
const defaultSession = remoteSession.defaultSession;
|
||||
if (!defaultSession || !defaultSession.clearHostResolverCache) return;
|
||||
defaultSession.clearHostResolverCache();
|
||||
}
|
||||
|
||||
function flushCookies(callback) {
|
||||
try {
|
||||
if (getElectronMajorVersion() >= 7) {
|
||||
remoteSession.defaultSession.cookies.flushStore().then(() => callback());
|
||||
} else {
|
||||
remoteSession.defaultSession.cookies.flushStore(callback);
|
||||
}
|
||||
} catch (err) {
|
||||
callback(err);
|
||||
}
|
||||
}
|
||||
|
||||
function flushStorageData(callback) {
|
||||
try {
|
||||
remoteSession.defaultSession.flushStorageData();
|
||||
} catch (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
callback();
|
||||
}
|
||||
|
||||
function purgeMemory() {
|
||||
webFrame.clearCache();
|
||||
}
|
||||
|
||||
function getCurrentCPUUsagePercent() {
|
||||
let totalProcessorUsagePercent = 0.0;
|
||||
for (const processMetric of remoteApp.getAppMetrics()) {
|
||||
totalProcessorUsagePercent += processMetric.cpu.percentCPUUsage;
|
||||
}
|
||||
return totalProcessorUsagePercent;
|
||||
}
|
||||
|
||||
function getCurrentMemoryUsageKB() {
|
||||
return totalMemoryUsageKB;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
flushDNSCache,
|
||||
flushCookies,
|
||||
flushStorageData,
|
||||
purgeMemory,
|
||||
getCurrentCPUUsagePercent,
|
||||
getCurrentMemoryUsageKB
|
||||
};
|
73
app/discord_native/remoteApp.js
Normal file
|
@ -0,0 +1,73 @@
|
|||
'use strict';
|
||||
|
||||
const electron = require('electron');
|
||||
const moduleUpdater = electron.remote.getGlobal('moduleUpdater');
|
||||
const remoteApp = electron.remote.app;
|
||||
|
||||
function getVersion() {
|
||||
return remoteApp.getVersion();
|
||||
}
|
||||
|
||||
const allowedAppPaths = new Set(['home', 'appData', 'desktop', 'documents', 'downloads']);
|
||||
|
||||
function getPath(path) {
|
||||
if (!allowedAppPaths.has(path)) {
|
||||
throw new Error(`${path} is not an allowed app path`);
|
||||
}
|
||||
return remoteApp.getPath(path);
|
||||
}
|
||||
|
||||
function setBadgeCount(count) {
|
||||
remoteApp.setBadgeCount(count);
|
||||
}
|
||||
|
||||
function dockSetBadge(badge) {
|
||||
remoteApp.dock.setBadge(badge);
|
||||
}
|
||||
|
||||
function dockBounce(type) {
|
||||
return remoteApp.dock.bounce(type);
|
||||
}
|
||||
|
||||
function dockCancelBounce(id) {
|
||||
remoteApp.dock.cancelBounce(id);
|
||||
}
|
||||
|
||||
function getModuleVersions() {
|
||||
const versions = {};
|
||||
if (!__OVERLAY__) {
|
||||
const installed = moduleUpdater.getInstalled();
|
||||
for (const name of Object.keys(installed)) {
|
||||
versions[name] = installed[name].installedVersion;
|
||||
}
|
||||
}
|
||||
return versions;
|
||||
}
|
||||
|
||||
let dockAPI;
|
||||
if (remoteApp.dock) {
|
||||
dockAPI = {
|
||||
setBadge: dockSetBadge,
|
||||
bounce: dockBounce,
|
||||
cancelBounce: dockCancelBounce
|
||||
};
|
||||
}
|
||||
|
||||
function relaunch() {
|
||||
remoteApp.relaunch();
|
||||
remoteApp.exit(0);
|
||||
}
|
||||
|
||||
function getDefaultDoubleClickAction() {
|
||||
return electron.remote.systemPreferences.getUserDefault('AppleActionOnDoubleClick', 'string');
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getVersion,
|
||||
getModuleVersions,
|
||||
getPath,
|
||||
setBadgeCount,
|
||||
dock: dockAPI,
|
||||
relaunch,
|
||||
getDefaultDoubleClickAction
|
||||
};
|
42
app/discord_native/remotePowerMonitor.js
Normal file
|
@ -0,0 +1,42 @@
|
|||
'use strict';
|
||||
|
||||
const electron = require('electron');
|
||||
const EventEmitter = require('events');
|
||||
const remotePowerMonitor = electron.remote.powerMonitor;
|
||||
|
||||
class RemotePowerMonitor extends EventEmitter {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
remotePowerMonitor.on('resume', () => {
|
||||
this.emit('resume');
|
||||
});
|
||||
|
||||
remotePowerMonitor.on('suspend', () => {
|
||||
this.emit('suspend');
|
||||
});
|
||||
|
||||
remotePowerMonitor.on('lock-screen', () => {
|
||||
this.emit('lock-screen');
|
||||
});
|
||||
|
||||
remotePowerMonitor.on('unlock-screen', () => {
|
||||
this.emit('unlock-screen');
|
||||
});
|
||||
}
|
||||
|
||||
removeAllListeners() {
|
||||
remotePowerMonitor.removeAllListeners();
|
||||
super.removeAllListeners();
|
||||
}
|
||||
|
||||
getSystemIdleTimeMs() {
|
||||
if (remotePowerMonitor.getSystemIdleTime != null) {
|
||||
return remotePowerMonitor.getSystemIdleTime() * 1000;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new RemotePowerMonitor();
|
98
app/discord_native/spellCheck.js
Normal file
|
@ -0,0 +1,98 @@
|
|||
'use strict';
|
||||
|
||||
const electron = require('electron');
|
||||
const EventEmitter = require('events');
|
||||
const { getElectronMajorVersion } = require('./_common');
|
||||
const ipcRenderer = require('./ipc');
|
||||
|
||||
if (getElectronMajorVersion() < 8) {
|
||||
function setSpellCheckProvider(locale, autoCorrectWord, provider) {
|
||||
const asyncProvider = {
|
||||
spellCheck: (words, callback) => callback(words.filter(word => !provider.spellCheck(word)))
|
||||
};
|
||||
electron.webFrame.setSpellCheckProvider(locale, asyncProvider);
|
||||
}
|
||||
|
||||
function replaceMisspelling(word) {
|
||||
electron.remote.getCurrentWebContents().replaceMisspelling(word);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
setSpellCheckProvider,
|
||||
replaceMisspelling
|
||||
};
|
||||
} else {
|
||||
class Spellchecker extends EventEmitter {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this._learnedWords = new Set();
|
||||
|
||||
this._handleSpellcheckData = (_, misspelledWord, dictionarySuggestions) => {
|
||||
this.emit('spellcheck-result', misspelledWord, dictionarySuggestions);
|
||||
};
|
||||
|
||||
ipcRenderer.on('SPELLCHECK_RESULT', this._handleSpellcheckData);
|
||||
}
|
||||
|
||||
removeAllListeners() {
|
||||
ipcRenderer.removeListener('SPELLCHECK_RESULT', this._handleSpellcheckData);
|
||||
super.removeAllListeners();
|
||||
}
|
||||
|
||||
_loadLearnedWords() {
|
||||
if (!this._loadInitialLearnedWordsPromise) {
|
||||
this._loadInitialLearnedWordsPromise = electron.remote.session.defaultSession.listWordsInSpellCheckerDictionary().then(dictionaryContents => {
|
||||
this._learnedWords = new Set(dictionaryContents);
|
||||
return Promise.resolve();
|
||||
});
|
||||
}
|
||||
|
||||
return this._loadInitialLearnedWordsPromise;
|
||||
}
|
||||
|
||||
getAvailableDictionaries() {
|
||||
const session = electron.remote.session.defaultSession;
|
||||
return session.availableSpellCheckerLanguages;
|
||||
}
|
||||
|
||||
setLocale(locale) {
|
||||
let succeeded = true;
|
||||
try {
|
||||
electron.remote.session.defaultSession.setSpellCheckerLanguages([locale]);
|
||||
} catch (_) {
|
||||
succeeded = false;
|
||||
}
|
||||
|
||||
return succeeded;
|
||||
}
|
||||
|
||||
setLearnedWords(learnedWords) {
|
||||
const session = electron.remote.session.defaultSession;
|
||||
|
||||
this._loadLearnedWords().then(() => {
|
||||
this._learnedWords.forEach(word => {
|
||||
if (!learnedWords.has(word)) {
|
||||
session.removeWordFromSpellCheckerDictionary(word);
|
||||
}
|
||||
});
|
||||
|
||||
learnedWords.forEach(word => {
|
||||
if (!this._learnedWords.has(word)) {
|
||||
session.addWordToSpellCheckerDictionary(word);
|
||||
}
|
||||
});
|
||||
|
||||
this._learnedWords = new Set(learnedWords);
|
||||
});
|
||||
}
|
||||
|
||||
replaceMisspelling(correction) {
|
||||
electron.remote.getCurrentWebContents().replaceMisspelling(correction);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
Spellchecker: new Spellchecker()
|
||||
};
|
||||
}
|
121
app/discord_native/window.js
Normal file
|
@ -0,0 +1,121 @@
|
|||
'use strict';
|
||||
|
||||
const electron = require('electron');
|
||||
const EventEmitter = require('events');
|
||||
const process = require('process');
|
||||
const common = require('./_common');
|
||||
const remoteMenu = electron.remote.Menu;
|
||||
const webFrame = electron.webFrame;
|
||||
|
||||
function flashFrame(flag) {
|
||||
const currentWindow = common.getWindow();
|
||||
if (currentWindow == null || currentWindow.flashFrame == null) return;
|
||||
currentWindow.flashFrame(!currentWindow.isFocused() && flag);
|
||||
}
|
||||
|
||||
function minimize(key) {
|
||||
const win = common.getWindow(key);
|
||||
if (win == null) return;
|
||||
win.minimize();
|
||||
}
|
||||
|
||||
function restore(key) {
|
||||
const win = common.getWindow(key);
|
||||
if (win == null) return;
|
||||
win.restore();
|
||||
}
|
||||
|
||||
function maximize(key) {
|
||||
const win = common.getWindow(key);
|
||||
if (win == null) return;
|
||||
if (win.isMaximized()) {
|
||||
win.unmaximize();
|
||||
} else {
|
||||
win.maximize();
|
||||
}
|
||||
}
|
||||
|
||||
function focus(hack, key) {
|
||||
const win = common.getWindow(key);
|
||||
if (win == null) return;
|
||||
win.show();
|
||||
}
|
||||
|
||||
function setAlwaysOnTop(key, enabled) {
|
||||
const win = common.getWindow(key);
|
||||
if (win == null) return;
|
||||
win.setAlwaysOnTop(enabled);
|
||||
}
|
||||
|
||||
function isAlwaysOnTop(key) {
|
||||
const win = common.getWindow(key);
|
||||
if (win == null) return false;
|
||||
return win.isAlwaysOnTop();
|
||||
}
|
||||
|
||||
function blur(key) {
|
||||
const win = common.getWindow(key);
|
||||
if (win != null && !win.isDestroyed()) {
|
||||
win.blur();
|
||||
}
|
||||
}
|
||||
|
||||
function setProgressBar(progress, key) {
|
||||
const win = common.getWindow(key);
|
||||
if (win == null) return;
|
||||
win.setProgressBar(progress);
|
||||
}
|
||||
|
||||
function fullscreen(key) {
|
||||
const currentWindow = common.getWindow(key);
|
||||
currentWindow.setFullScreen(!currentWindow.isFullScreen());
|
||||
}
|
||||
|
||||
function close(key) {
|
||||
if (key == null && process.platform === 'darwin') {
|
||||
remoteMenu.sendActionToFirstResponder('hide:');
|
||||
} else {
|
||||
const win = common.getWindow(key);
|
||||
if (win == null) return;
|
||||
win.close();
|
||||
}
|
||||
}
|
||||
|
||||
function setZoomFactor(factor) {
|
||||
if (!webFrame.setZoomFactor) return;
|
||||
webFrame.setZoomFactor(factor / 100);
|
||||
}
|
||||
|
||||
const webContents = common.getCurrentWindow().webContents;
|
||||
class WebContents extends EventEmitter {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
webContents.removeAllListeners('devtools-opened');
|
||||
webContents.on('devtools-opened', () => {
|
||||
this.emit('devtools-opened');
|
||||
});
|
||||
}
|
||||
|
||||
setBackgroundThrottling(enabled) {
|
||||
if (webContents.setBackgroundThrottling != null) {
|
||||
webContents.setBackgroundThrottling(enabled);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
flashFrame,
|
||||
minimize,
|
||||
restore,
|
||||
maximize,
|
||||
focus,
|
||||
blur,
|
||||
fullscreen,
|
||||
close,
|
||||
setAlwaysOnTop,
|
||||
isAlwaysOnTop,
|
||||
setZoomFactor,
|
||||
webContents: new WebContents(),
|
||||
setProgressBar
|
||||
};
|
75
app/errorReporting.js
Normal file
|
@ -0,0 +1,75 @@
|
|||
'use strict';
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.metadata = undefined;
|
||||
exports.init = init;
|
||||
|
||||
var _child_process = require('child_process');
|
||||
|
||||
var _child_process2 = _interopRequireDefault(_child_process);
|
||||
|
||||
var _electron = require('electron');
|
||||
|
||||
var _lodash = require('lodash');
|
||||
|
||||
var _lodash2 = _interopRequireDefault(_lodash);
|
||||
|
||||
var _buildInfo = require('./buildInfo');
|
||||
|
||||
var _buildInfo2 = _interopRequireDefault(_buildInfo);
|
||||
|
||||
var _ipcMain = require('./ipcMain');
|
||||
|
||||
var _ipcMain2 = _interopRequireDefault(_ipcMain);
|
||||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
|
||||
let metadata = exports.metadata = {};
|
||||
|
||||
function init() {
|
||||
exports.metadata = metadata = {
|
||||
channel: _buildInfo2.default.releaseChannel,
|
||||
sentry: {
|
||||
environment: _buildInfo2.default.releaseChannel,
|
||||
release: _buildInfo2.default.version
|
||||
}
|
||||
};
|
||||
|
||||
if (process.platform === 'linux') {
|
||||
const XDG_CURRENT_DESKTOP = process.env.XDG_CURRENT_DESKTOP || 'unknown';
|
||||
const GDMSESSION = process.env.GDMSESSION || 'unknown';
|
||||
metadata['wm'] = `${XDG_CURRENT_DESKTOP},${GDMSESSION}`;
|
||||
try {
|
||||
metadata['distro'] = _child_process2.default.execFileSync('lsb_release', ['-ds'], { timeout: 100, maxBuffer: 512, encoding: 'utf-8' }).trim();
|
||||
} catch (e) {} // just in case lsb_release doesn't exist
|
||||
}
|
||||
|
||||
_electron.crashReporter.start(getCrashReporterArgs());
|
||||
_ipcMain2.default.on('UPDATE_CRASH_REPORT_METADATA', (event, additional_metadata) => {
|
||||
const args = getCrashReporterArgs(additional_metadata);
|
||||
_ipcMain2.default.reply(event, 'START_CRASH_REPORTER', args);
|
||||
_electron.crashReporter.start(args);
|
||||
});
|
||||
}
|
||||
|
||||
function getCrashReporterArgs(additional_metadata) {
|
||||
additional_metadata = additional_metadata || {};
|
||||
const final_metadata = _lodash2.default.defaultsDeep({}, metadata, additional_metadata);
|
||||
|
||||
for (const key in final_metadata) {
|
||||
if (typeof final_metadata[key] === 'object') {
|
||||
final_metadata[key] = JSON.stringify(final_metadata[key]);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
productName: 'Discord',
|
||||
companyName: 'Discord Inc.',
|
||||
submitURL: 'https://sentry.io/api/146342/minidump/?sentry_key=384ce4413de74fe0be270abe03b2b35a',
|
||||
uploadToServer: true,
|
||||
ignoreSystemCrashHandler: false,
|
||||
extra: final_metadata
|
||||
};
|
||||
}
|
BIN
app/images/badges/badge-1.ico
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
app/images/badges/badge-10.ico
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
app/images/badges/badge-11.ico
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
app/images/badges/badge-2.ico
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
app/images/badges/badge-3.ico
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
app/images/badges/badge-4.ico
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
app/images/badges/badge-5.ico
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
app/images/badges/badge-6.ico
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
app/images/badges/badge-7.ico
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
app/images/badges/badge-8.ico
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
app/images/badges/badge-9.ico
Normal file
After Width: | Height: | Size: 15 KiB |
15
app/images/close.svg
Normal file
|
@ -0,0 +1,15 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 17.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns"
|
||||
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="20px" height="20px"
|
||||
viewBox="40 40 20 20" enable-background="new 40 40 20 20" xml:space="preserve">
|
||||
<title>Slice 1</title>
|
||||
<description>Created with Sketch (http://www.bohemiancoding.com/sketch)</description>
|
||||
<g id="Page_1">
|
||||
<path id="Close" fill="#99aab5" d="M40.445,42.853l2.408-2.408c0.593-0.593,1.555-0.593,2.147,0l5,5l5-5
|
||||
c0.593-0.593,1.554-0.593,2.147,0l2.408,2.408c0.593,0.593,0.593,1.554,0,2.147l-5,5l5,5c0.593,0.593,0.592,1.554,0,2.147
|
||||
l-2.408,2.408c-0.593,0.593-1.555,0.593-2.147,0l-5-5l-5,5c-0.593,0.593-1.554,0.593-2.147,0l-2.408-2.408
|
||||
c-0.593-0.593-0.593-1.554,0-2.147l5-5l-5-5C39.852,44.407,39.852,43.445,40.445,42.853z"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.1 KiB |
34
app/images/discord.svg
Normal file
|
@ -0,0 +1,34 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 17.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<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"
|
||||
viewBox="0 0 168 190" enable-background="new 0 0 168 190" xml:space="preserve">
|
||||
<g>
|
||||
<rect x="14.4" y="46.1" fill="#FFFFFF" width="139.2" height="97.7"/>
|
||||
<g>
|
||||
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="66.6616" y1="79.7119" x2="66.6616" y2="100.7434">
|
||||
<stop offset="0" style="stop-color:#7491D5"/>
|
||||
<stop offset="1" style="stop-color:#4E68A0"/>
|
||||
</linearGradient>
|
||||
<path fill="url(#SVGID_1_)" d="M66.7,79.7c-5.4,0-9.8,4.7-9.8,10.5c0,5.8,4.4,10.5,9.8,10.5c5.4,0,9.8-4.7,9.8-10.5
|
||||
C76.5,84.4,72.1,79.7,66.7,79.7z"/>
|
||||
<linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="101.661" y1="79.7119" x2="101.661" y2="100.7434">
|
||||
<stop offset="0" style="stop-color:#7491D5"/>
|
||||
<stop offset="1" style="stop-color:#4E68A0"/>
|
||||
</linearGradient>
|
||||
<path fill="url(#SVGID_2_)" d="M101.7,79.7c-5.4,0-9.8,4.7-9.8,10.5c0,5.8,4.4,10.5,9.8,10.5c5.4,0,9.8-4.7,9.8-10.5
|
||||
C111.5,84.4,107.1,79.7,101.7,79.7z"/>
|
||||
<linearGradient id="SVGID_3_" gradientUnits="userSpaceOnUse" x1="84" y1="-4.545736e-07" x2="84" y2="190">
|
||||
<stop offset="0" style="stop-color:#7491D5"/>
|
||||
<stop offset="1" style="stop-color:#4E68A0"/>
|
||||
</linearGradient>
|
||||
<path fill="url(#SVGID_3_)" d="M148.4,0H19.6C8.8,0,0,8.8,0,19.6v128.4c0,10.8,8.8,19.6,19.6,19.6h108.9l-5.1-17.5l12.3,11.3
|
||||
l11.6,10.7L168,190v-41.9v-9.5V19.6C168,8.8,159.2,0,148.4,0z M111.3,124.1c0,0-3.4-4.1-6.3-7.7c12.6-3.5,17.4-11.3,17.4-11.3
|
||||
c-4,2.6-7.7,4.4-11.1,5.6c-4.8,2-9.5,3.3-14,4.1c-9.2,1.7-17.6,1.3-24.9-0.1c-5.5-1-10.2-2.5-14.1-4.1c-2.2-0.8-4.6-1.9-7.1-3.3
|
||||
c-0.3-0.2-0.6-0.3-0.9-0.5c-0.1-0.1-0.3-0.2-0.4-0.2c-1.7-1-2.6-1.6-2.6-1.6s4.6,7.6,16.8,11.2c-2.9,3.6-6.4,7.9-6.4,7.9
|
||||
c-21.2-0.6-29.3-14.5-29.3-14.5c0-30.6,13.8-55.4,13.8-55.4c13.8-10.3,26.9-10,26.9-10l1,1.1C52.8,50.3,45,57.9,45,57.9
|
||||
s2.1-1.2,5.7-2.7c10.3-4.5,18.4-5.7,21.8-6c0.5-0.1,1.1-0.2,1.6-0.2c5.9-0.7,12.5-0.9,19.4-0.2c9.1,1,18.9,3.7,28.9,9.1
|
||||
c0,0-7.5-7.2-23.9-12.1l1.3-1.5c0,0,13.1-0.3,26.9,10c0,0,13.8,24.8,13.8,55.4C140.6,109.6,132.5,123.5,111.3,124.1z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.3 KiB |
BIN
app/images/systemtray/darwin/tray-connectedTemplate.png
Normal file
After Width: | Height: | Size: 153 B |
BIN
app/images/systemtray/darwin/tray-connectedTemplate@1.25x.png
Normal file
After Width: | Height: | Size: 189 B |
BIN
app/images/systemtray/darwin/tray-connectedTemplate@1.33x.png
Normal file
After Width: | Height: | Size: 232 B |
BIN
app/images/systemtray/darwin/tray-connectedTemplate@1.4x.png
Normal file
After Width: | Height: | Size: 238 B |
BIN
app/images/systemtray/darwin/tray-connectedTemplate@1.5x.png
Normal file
After Width: | Height: | Size: 205 B |
BIN
app/images/systemtray/darwin/tray-connectedTemplate@1.8x.png
Normal file
After Width: | Height: | Size: 281 B |
BIN
app/images/systemtray/darwin/tray-connectedTemplate@2.5x.png
Normal file
After Width: | Height: | Size: 294 B |
BIN
app/images/systemtray/darwin/tray-connectedTemplate@2x.png
Normal file
After Width: | Height: | Size: 229 B |
BIN
app/images/systemtray/darwin/tray-connectedTemplate@3x.png
Normal file
After Width: | Height: | Size: 301 B |
BIN
app/images/systemtray/darwin/tray-connectedTemplate@4x.png
Normal file
After Width: | Height: | Size: 421 B |
BIN
app/images/systemtray/darwin/tray-connectedTemplate@5x.png
Normal file
After Width: | Height: | Size: 540 B |
BIN
app/images/systemtray/darwin/tray-deafenedTemplate.png
Normal file
After Width: | Height: | Size: 233 B |
BIN
app/images/systemtray/darwin/tray-deafenedTemplate@1.25x.png
Normal file
After Width: | Height: | Size: 291 B |
BIN
app/images/systemtray/darwin/tray-deafenedTemplate@1.33x.png
Normal file
After Width: | Height: | Size: 308 B |
BIN
app/images/systemtray/darwin/tray-deafenedTemplate@1.4x.png
Normal file
After Width: | Height: | Size: 313 B |
BIN
app/images/systemtray/darwin/tray-deafenedTemplate@1.5x.png
Normal file
After Width: | Height: | Size: 305 B |
BIN
app/images/systemtray/darwin/tray-deafenedTemplate@1.8x.png
Normal file
After Width: | Height: | Size: 383 B |
BIN
app/images/systemtray/darwin/tray-deafenedTemplate@2.5x.png
Normal file
After Width: | Height: | Size: 470 B |
BIN
app/images/systemtray/darwin/tray-deafenedTemplate@2x.png
Normal file
After Width: | Height: | Size: 385 B |
BIN
app/images/systemtray/darwin/tray-deafenedTemplate@3x.png
Normal file
After Width: | Height: | Size: 491 B |
BIN
app/images/systemtray/darwin/tray-deafenedTemplate@4x.png
Normal file
After Width: | Height: | Size: 668 B |
BIN
app/images/systemtray/darwin/tray-deafenedTemplate@5x.png
Normal file
After Width: | Height: | Size: 834 B |
BIN
app/images/systemtray/darwin/tray-mutedTemplate.png
Normal file
After Width: | Height: | Size: 216 B |
BIN
app/images/systemtray/darwin/tray-mutedTemplate@1.25x.png
Normal file
After Width: | Height: | Size: 271 B |
BIN
app/images/systemtray/darwin/tray-mutedTemplate@1.33x.png
Normal file
After Width: | Height: | Size: 284 B |
BIN
app/images/systemtray/darwin/tray-mutedTemplate@1.4x.png
Normal file
After Width: | Height: | Size: 298 B |
BIN
app/images/systemtray/darwin/tray-mutedTemplate@1.5x.png
Normal file
After Width: | Height: | Size: 293 B |
BIN
app/images/systemtray/darwin/tray-mutedTemplate@1.8x.png
Normal file
After Width: | Height: | Size: 368 B |
BIN
app/images/systemtray/darwin/tray-mutedTemplate@2.5x.png
Normal file
After Width: | Height: | Size: 454 B |
BIN
app/images/systemtray/darwin/tray-mutedTemplate@2x.png
Normal file
After Width: | Height: | Size: 354 B |
BIN
app/images/systemtray/darwin/tray-mutedTemplate@3x.png
Normal file
After Width: | Height: | Size: 496 B |
BIN
app/images/systemtray/darwin/tray-mutedTemplate@4x.png
Normal file
After Width: | Height: | Size: 609 B |
BIN
app/images/systemtray/darwin/tray-mutedTemplate@5x.png
Normal file
After Width: | Height: | Size: 753 B |
BIN
app/images/systemtray/darwin/tray-speakingTemplate.png
Normal file
After Width: | Height: | Size: 199 B |
BIN
app/images/systemtray/darwin/tray-speakingTemplate@1.25x.png
Normal file
After Width: | Height: | Size: 261 B |
BIN
app/images/systemtray/darwin/tray-speakingTemplate@1.33x.png
Normal file
After Width: | Height: | Size: 297 B |
BIN
app/images/systemtray/darwin/tray-speakingTemplate@1.4x.png
Normal file
After Width: | Height: | Size: 325 B |
BIN
app/images/systemtray/darwin/tray-speakingTemplate@1.5x.png
Normal file
After Width: | Height: | Size: 303 B |
BIN
app/images/systemtray/darwin/tray-speakingTemplate@1.8x.png
Normal file
After Width: | Height: | Size: 387 B |
BIN
app/images/systemtray/darwin/tray-speakingTemplate@2.5x.png
Normal file
After Width: | Height: | Size: 461 B |
BIN
app/images/systemtray/darwin/tray-speakingTemplate@2x.png
Normal file
After Width: | Height: | Size: 337 B |
BIN
app/images/systemtray/darwin/tray-speakingTemplate@3x.png
Normal file
After Width: | Height: | Size: 520 B |
BIN
app/images/systemtray/darwin/tray-speakingTemplate@4x.png
Normal file
After Width: | Height: | Size: 695 B |
BIN
app/images/systemtray/darwin/tray-speakingTemplate@5x.png
Normal file
After Width: | Height: | Size: 846 B |
BIN
app/images/systemtray/darwin/tray-unreadTemplate.png
Normal file
After Width: | Height: | Size: 272 B |
BIN
app/images/systemtray/darwin/trayTemplate.png
Normal file
After Width: | Height: | Size: 232 B |
BIN
app/images/systemtray/linux/tray-connected.png
Normal file
After Width: | Height: | Size: 289 B |
BIN
app/images/systemtray/linux/tray-deafened.png
Normal file
After Width: | Height: | Size: 365 B |
BIN
app/images/systemtray/linux/tray-muted.png
Normal file
After Width: | Height: | Size: 381 B |
BIN
app/images/systemtray/linux/tray-speaking.png
Normal file
After Width: | Height: | Size: 420 B |
BIN
app/images/systemtray/linux/tray-unread.png
Normal file
After Width: | Height: | Size: 762 B |
BIN
app/images/systemtray/linux/tray.png
Normal file
After Width: | Height: | Size: 718 B |
BIN
app/images/systemtray/win32/tray-connected.png
Normal file
After Width: | Height: | Size: 276 B |