refactor main_node.js to TS

pull/2239/head
Audric Ackermann 3 years ago
parent 70ee8cefdc
commit 0dfa3e35cc

@ -128,6 +128,7 @@
"@types/config": "0.0.34",
"@types/dompurify": "^2.0.0",
"@types/electron-is-dev": "^1.1.1",
"@types/electron-localshortcut": "^3.1.0",
"@types/emoji-mart": "^2.11.3",
"@types/filesize": "3.6.0",
"@types/firstline": "^2.0.2",

@ -6,9 +6,8 @@ import {
ipcMain as ipc,
Menu,
protocol as electronProtocol,
session,
shell,
screen,
shell,
systemPreferences,
} from 'electron';
@ -20,11 +19,11 @@ import crypto from 'crypto';
import _ from 'lodash';
import pify from 'pify';
import { setup as setupSpellChecker } from './app/spell_check';
import packageJson from './package.json';
import GlobalErrors from './app/global_errors';
import { setup as setupSpellChecker } from '../node/spell_check';
import packageJson from '../../package.json';
import { setupGlobalErrorHandler } from '../node/global_errors';
GlobalErrors.addHandler();
setupGlobalErrorHandler();
import electronLocalshortcut from 'electron-localshortcut';
// tslint:disable: no-console
@ -47,42 +46,42 @@ function getMainWindow() {
return mainWindow;
}
let readyForShutdown: boolean = false;
// Tray icon and related objects
let tray = null;
let tray: any = null;
import config from './app/config';
import { config } from '../node/config';
// Very important to put before the single instance check, since it is based on the
// userData directory.
import userConfig from './app/user_config';
import passwordUtil from './ts/util/passwordUtils';
import { userConfig } from '../node/config/user_config';
import * as passwordUtil from '../util/passwordUtils';
const development = config.environment === 'development';
const development = (config as any).environment === 'development';
const appInstance = config.util.getEnv('NODE_APP_INSTANCE') || 0;
// We generally want to pull in our own modules after this point, after the user
// data directory has been set.
import attachments from './ts/attachments/attachments';
import attachmentChannel from './app/attachment_channel';
import { initAttachmentsChannel } from '../node/attachment_channel';
import updater from './ts/updater/index';
import * as updater from '../updater/index';
import createTrayIcon from './app/tray_icon';
import ephemeralConfig from './app/ephemeral_config';
import logging from './app/logging';
import sql from './app/sql';
import sqlChannels from './app/sql_channel';
import windowState from './app/window_state';
import { createTemplate } from './app/menu';
import { installFileHandler, installWebHandler } from './app/protocol_filter';
import { installPermissionsHandler } from './app/permissions';
import { createTrayIcon } from '../node/tray_icon';
import { ephemeralConfig } from '../node/config/ephemeral_config';
import { getLogger, initializeLogger } from '../node/logging';
import { sqlNode } from '../node/sql';
import * as sqlChannels from '../node/sql_channel';
import { windowMarkShouldQuit, windowShouldQuit } from '../node/window_state';
import { createTemplate } from '../node/menu';
import { installFileHandler, installWebHandler } from '../node/protocol_filter';
import { installPermissionsHandler } from '../node/permissions';
import Logger from 'bunyan';
let appStartInitialSpellcheckSetting = true;
let latestDesktopRelease: string | undefined;
async function getSpellCheckSetting() {
const json = await sql.getItemById('spell-check');
const json = sqlNode.getItemById('spell-check');
// Default to `true` if setting doesn't exist yet
if (!json) {
return true;
@ -145,14 +144,22 @@ if (windowFromUserConfig) {
}
// import {load as loadLocale} from '../..'
const loadLocale = './app/locale'.load;
import { load as loadLocale, LocaleMessagesWithNameType } from '../node/locale';
import { setLastestRelease } from '../node/latest_desktop_release';
// Both of these will be set after app fires the 'ready' event
let logger;
let locale;
let logger: Logger | null = null;
let locale: LocaleMessagesWithNameType;
function assertLogger(): Logger {
if (!logger) {
throw new Error('assertLogger: logger is not set');
}
return logger;
}
function prepareURL(pathSegments, moreKeys) {
return url.format({
function prepareURL(pathSegments: Array<string>, moreKeys?: { theme: any }) {
const urlObject: url.UrlObject = {
pathname: path.join.apply(null, pathSegments),
protocol: 'file:',
slashes: true,
@ -161,7 +168,7 @@ function prepareURL(pathSegments, moreKeys) {
locale: locale.name,
version: app.getVersion(),
commitHash: config.get('commitHash'),
environment: config.environment,
environment: (config as any).environment,
node_version: process.versions.node,
hostname: os.hostname(),
appInstance: process.env.NODE_APP_INSTANCE,
@ -169,18 +176,20 @@ function prepareURL(pathSegments, moreKeys) {
appStartInitialSpellcheckSetting,
...moreKeys,
},
});
};
return url.format(urlObject);
}
function handleUrl(event, target) {
function handleUrl(event: any, target: string) {
event.preventDefault();
const { protocol } = url.parse(target);
// tslint:disable-next-line: no-http-string
if (protocol === 'http:' || protocol === 'https:') {
shell.openExternal(target);
void shell.openExternal(target);
}
}
function captureClicks(window) {
function captureClicks(window: BrowserWindow) {
window.webContents.on('will-navigate', handleUrl);
window.webContents.on('new-window', handleUrl);
}
@ -193,7 +202,6 @@ const WINDOW_SIZE = Object.freeze({
});
function getWindowSize() {
const { screen } = electron;
const screenSize = screen.getPrimaryDisplay().workAreaSize;
const { minWidth, minHeight, defaultWidth, defaultHeight } = WINDOW_SIZE;
// Ensure that the screen can fit within the default size
@ -203,7 +211,7 @@ function getWindowSize() {
return { width, height, minWidth, minHeight };
}
function isVisible(window, bounds) {
function isVisible(window: { x: number; y: number; width: number }, bounds: any) {
const boundsX = _.get(bounds, 'x') || 0;
const boundsY = _.get(bounds, 'y') || 0;
const boundsWidth = _.get(bounds, 'width') || WINDOW_SIZE.defaultWidth;
@ -211,6 +219,7 @@ function isVisible(window, bounds) {
const BOUNDS_BUFFER = 100;
// requiring BOUNDS_BUFFER pixels on the left or right side
// tslint:disable: restrict-plus-operands
const rightSideClearOfLeftBound = window.x + window.width >= boundsX + BOUNDS_BUFFER;
const leftSideClearOfRightBound = window.x <= boundsX + boundsWidth - BOUNDS_BUFFER;
@ -235,29 +244,32 @@ function getStartInTray() {
// tslint:disable-next-line: max-func-body-length
async function createWindow() {
const { minWidth, minHeight, width, height } = getWindowSize();
const picked = {
maximized: (windowConfig as any).maximized || false,
autoHideMenuBar: (windowConfig as any).autoHideMenuBar || false,
width: (windowConfig as any).width || width,
height: (windowConfig as any).height || height,
x: (windowConfig as any).x,
y: (windowConfig as any).y,
};
const windowOptions = Object.assign(
{
show: true,
width,
height,
minWidth,
minHeight,
autoHideMenuBar: false,
backgroundColor: '#000',
webPreferences: {
nodeIntegration: false,
enableRemoteModule: true,
nodeIntegrationInWorker: true,
contextIsolation: false,
preload: path.join(__dirname, 'preload.js'),
nativeWindowOpen: true,
spellcheck: await getSpellCheckSetting(),
},
// don't setup icon, the executable one will be used by default
const windowOptions = {
show: true,
minWidth,
minHeight,
fullscreen: false as boolean | undefined,
backgroundColor: '#000',
webPreferences: {
nodeIntegration: false,
enableRemoteModule: true,
nodeIntegrationInWorker: true,
contextIsolation: false,
preload: path.join(__dirname, 'preload.js'),
nativeWindowOpen: true,
spellcheck: await getSpellCheckSetting(),
},
_.pick(windowConfig, ['maximized', 'autoHideMenuBar', 'width', 'height', 'x', 'y'])
);
...picked,
};
if (!_.isNumber(windowOptions.width) || windowOptions.width < minWidth) {
windowOptions.width = Math.max(minWidth, width);
@ -289,16 +301,22 @@ async function createWindow() {
delete windowOptions.fullscreen;
}
logger.info('Initializing BrowserWindow config: %s', JSON.stringify(windowOptions));
assertLogger().info('Initializing BrowserWindow config: %s', JSON.stringify(windowOptions));
// Create the browser window.
mainWindow = new BrowserWindow(windowOptions);
setupSpellChecker(mainWindow, locale.messages);
electronLocalshortcut.register(mainWindow, 'F5', () => {
if (!mainWindow) {
return;
}
mainWindow.reload();
});
electronLocalshortcut.register(mainWindow, 'CommandOrControl+R', () => {
if (!mainWindow) {
return;
}
mainWindow.reload();
});
@ -318,15 +336,16 @@ async function createWindow() {
height: size[1],
x: position[0],
y: position[1],
fullscreen: false as boolean | undefined,
};
if (mainWindow.isFullScreen()) {
// Only include this property if true, because when explicitly set to
// false the fullscreen button will be disabled on osx
windowConfig.fullscreen = true;
(windowConfig as any).fullscreen = true;
}
logger.info('Updating BrowserWindow config: %s', JSON.stringify(windowConfig));
assertLogger().info('Updating BrowserWindow config: %s', JSON.stringify(windowConfig));
ephemeralConfig.set('window', windowConfig);
}
@ -335,6 +354,9 @@ async function createWindow() {
mainWindow.on('move', debouncedCaptureStats);
mainWindow.on('focus', () => {
if (!mainWindow) {
return;
}
mainWindow.flashFrame(false);
if (passwordWindow) {
passwordWindow.close();
@ -342,7 +364,7 @@ async function createWindow() {
}
});
mainWindow.loadURL(prepareURL([__dirname, 'background.html']));
await mainWindow.loadURL(prepareURL([__dirname, 'background.html']));
if ((process.env.NODE_APP_INSTANCE || '').startsWith('devprod')) {
// Open the DevTools.
@ -358,24 +380,21 @@ async function createWindow() {
// Electron before the app quits.
mainWindow.on('close', async e => {
console.log('close event', {
readyForShutdown: mainWindow ? mainWindow.readyForShutdown : null,
shouldQuit: windowState.shouldQuit(),
readyForShutdown: mainWindow ? readyForShutdown : null,
shouldQuit: windowShouldQuit(),
});
// If the application is terminating, just do the default
if (mainWindow.readyForShutdown && windowState.shouldQuit()) {
if (mainWindow && readyForShutdown && windowShouldQuit()) {
return;
}
// Prevent the shutdown
e.preventDefault();
mainWindow.hide();
mainWindow?.hide();
// On Mac, or on other platforms when the tray icon is in use, the window
// should be only hidden, not closed, when the user clicks the close button
if (
!windowState.shouldQuit() &&
(getStartInTray().usingTrayIcon || process.platform === 'darwin')
) {
if (!windowShouldQuit() && (getStartInTray().usingTrayIcon || process.platform === 'darwin')) {
// toggle the visibility of the show/hide tray icon menu entries
if (tray) {
tray.updateContextMenu();
@ -386,7 +405,7 @@ async function createWindow() {
await requestShutdown();
if (mainWindow) {
mainWindow.readyForShutdown = true;
readyForShutdown = true;
}
app.quit();
});
@ -398,8 +417,6 @@ async function createWindow() {
// when you should delete the corresponding element.
mainWindow = null;
});
mainWindow.getLatestDesktopRelease = () => latestDesktopRelease;
}
ipc.on('show-window', () => {
@ -407,7 +424,7 @@ ipc.on('show-window', () => {
});
ipc.on('set-release-from-file-server', (_event, releaseGotFromFileServer) => {
latestDesktopRelease = releaseGotFromFileServer;
setLastestRelease(releaseGotFromFileServer);
});
let isReadyForUpdates = false;
@ -436,17 +453,17 @@ const TEN_MINUTES = 10 * 60 * 1000;
setTimeout(readyForUpdates, TEN_MINUTES);
function openReleaseNotes() {
shell.openExternal(
void shell.openExternal(
`https://github.com/oxen-io/session-desktop/releases/tag/v${app.getVersion()}`
);
}
function openSupportPage() {
shell.openExternal('https://docs.oxen.io/products-built-on-oxen/session');
void shell.openExternal('https://docs.oxen.io/products-built-on-oxen/session');
}
let passwordWindow;
function showPasswordWindow() {
let passwordWindow: BrowserWindow | null = null;
async function showPasswordWindow() {
if (passwordWindow) {
passwordWindow.show();
return;
@ -474,26 +491,23 @@ function showPasswordWindow() {
passwordWindow = new BrowserWindow(windowOptions);
passwordWindow.loadURL(prepareURL([__dirname, 'password.html']));
await passwordWindow.loadURL(prepareURL([__dirname, 'password.html']));
captureClicks(passwordWindow);
passwordWindow.on('close', e => {
// If the application is terminating, just do the default
if (windowState.shouldQuit()) {
if (windowShouldQuit()) {
return;
}
// Prevent the shutdown
e.preventDefault();
passwordWindow.hide();
passwordWindow?.hide();
// On Mac, or on other platforms when the tray icon is in use, the window
// should be only hidden, not closed, when the user clicks the close button
if (
!windowState.shouldQuit() &&
(getStartInTray().usingTrayIcon || process.platform === 'darwin')
) {
if (!windowShouldQuit() && (getStartInTray().usingTrayIcon || process.platform === 'darwin')) {
// toggle the visibility of the show/hide tray icon menu entries
if (tray) {
tray.updateContextMenu();
@ -502,8 +516,9 @@ function showPasswordWindow() {
return;
}
passwordWindow.readyForShutdown = true;
if (passwordWindow) {
(passwordWindow as any).readyForShutdown = true;
}
// Quit the app if we don't have a main window
if (!mainWindow) {
app.quit();
@ -515,13 +530,18 @@ function showPasswordWindow() {
});
}
let aboutWindow;
function showAbout() {
let aboutWindow: BrowserWindow | null;
async function showAbout() {
if (aboutWindow) {
aboutWindow.show();
return;
}
if (!mainWindow) {
console.info('about window needs mainwindow as parent');
return;
}
const options = {
width: 500,
height: 400,
@ -544,24 +564,29 @@ function showAbout() {
captureClicks(aboutWindow);
aboutWindow.loadURL(prepareURL([__dirname, 'about.html']));
await aboutWindow.loadURL(prepareURL([__dirname, 'about.html']));
aboutWindow.on('closed', () => {
aboutWindow = null;
});
aboutWindow.once('ready-to-show', () => {
aboutWindow.show();
aboutWindow?.show();
});
}
let debugLogWindow;
let debugLogWindow: BrowserWindow | null = null;
async function showDebugLogWindow() {
if (debugLogWindow) {
debugLogWindow.show();
return;
}
if (!mainWindow) {
console.info('debug log neeeds mainwindow size to open');
return;
}
const theme = await getThemeFromMainWindow();
const size = mainWindow.getSize();
const options = {
@ -587,14 +612,14 @@ async function showDebugLogWindow() {
captureClicks(debugLogWindow);
debugLogWindow.loadURL(prepareURL([__dirname, 'debug_log.html'], { theme }));
await debugLogWindow.loadURL(prepareURL([__dirname, 'debug_log.html'], { theme }));
debugLogWindow.on('closed', () => {
debugLogWindow = null;
});
debugLogWindow.once('ready-to-show', () => {
debugLogWindow.show();
debugLogWindow?.show();
});
}
@ -617,12 +642,12 @@ app.on('ready', async () => {
protocol: electronProtocol,
});
installPermissionsHandler({ session, userConfig });
installPermissionsHandler({ userConfig });
await logging.initialize();
logger = logging.getLogger();
logger.info('app ready');
logger.info(`starting version ${packageJson.version}`);
await initializeLogger();
logger = getLogger();
assertLogger().info('app ready');
assertLogger().info(`starting version ${packageJson.version}`);
if (!locale) {
const appLocale = app.getLocale() || 'en';
locale = loadLocale({ appLocale, logger });
@ -634,7 +659,7 @@ app.on('ready', async () => {
// If that fails then show the password window
const dbHasPassword = userConfig.get('dbHasPassword');
if (dbHasPassword) {
showPasswordWindow();
await showPasswordWindow();
} else {
await showMainWindow(key);
}
@ -649,13 +674,13 @@ function getDefaultSQLKey() {
userConfig.set('key', key);
}
return key;
return key as string;
}
async function removeDB() {
// this don't remove attachments and stuff like that...
const userDir = await getRealPath(app.getPath('userData'));
await sql.removeDB(userDir);
sqlNode.removeDB(userDir);
try {
console.error('Remove DB: removing.', userDir);
@ -670,16 +695,16 @@ async function removeDB() {
async function showMainWindow(sqlKey: string, passwordAttempt = false) {
const userDataPath = await getRealPath(app.getPath('userData'));
sql.initialize({
await sqlNode.initializeSql({
configDir: userDataPath,
key: sqlKey,
messages: locale.messages,
passwordAttempt,
});
appStartInitialSpellcheckSetting = await getSpellCheckSetting();
await sqlChannels.initialize();
sqlChannels.initialize();
await attachmentChannel.initialize({
await initAttachmentsChannel({
userDataPath,
});
@ -694,9 +719,9 @@ async function showMainWindow(sqlKey: string, passwordAttempt = false) {
setupMenu();
}
function setupMenu(options) {
function setupMenu() {
const { platform } = process;
const menuOptions = Object.assign({}, options, {
const menuOptions = {
development,
showDebugLog: showDebugLogWindow,
showWindow,
@ -704,7 +729,7 @@ function setupMenu(options) {
openReleaseNotes,
openSupportPage,
platform,
});
};
const template = createTemplate(menuOptions, locale.messages);
const menu = Menu.buildFromTemplate(template);
Menu.setApplicationMenu(menu);
@ -721,12 +746,14 @@ async function requestShutdown() {
console.log('requestShutdown: Response received');
if (error) {
return reject(error);
reject(error);
return;
}
return resolve();
resolve(undefined);
return;
});
mainWindow.webContents.send('get-ready-for-shutdown');
mainWindow?.webContents.send('get-ready-for-shutdown');
// We'll wait two minutes, then force the app to go down. This can happen if someone
// exits the app before we've set everything up in preload() (so the browser isn't
@ -734,7 +761,7 @@ async function requestShutdown() {
// Note: two minutes is also our timeout for SQL tasks in data.ts in the browser.
setTimeout(() => {
console.log('requestShutdown: Response never received; forcing shutdown.');
resolve();
resolve(undefined);
}, 2 * 60 * 1000);
});
@ -747,15 +774,15 @@ async function requestShutdown() {
app.on('before-quit', () => {
console.log('before-quit event', {
readyForShutdown: mainWindow ? mainWindow.readyForShutdown : null,
shouldQuit: windowState.shouldQuit(),
readyForShutdown: mainWindow ? readyForShutdown : null,
shouldQuit: windowShouldQuit(),
});
if (tray) {
tray.destroy();
}
windowState.markShouldQuit();
windowMarkShouldQuit();
});
// Quit when all windows are closed.
@ -767,7 +794,7 @@ app.on('window-all-closed', () => {
}
});
app.on('activate', () => {
app.on('activate', async () => {
if (!ready) {
return;
}
@ -777,12 +804,12 @@ app.on('activate', () => {
if (mainWindow) {
mainWindow.show();
} else {
createWindow();
await createWindow();
}
});
// Defense in depth. We never intend to open webviews or windows. Prevent it completely.
app.on('web-contents-created', (createEvent, contents) => {
app.on('web-contents-created', (_createEvent, contents) => {
contents.on('will-attach-webview', attachEvent => {
attachEvent.preventDefault();
});
@ -797,12 +824,6 @@ ipc.on('locale-data', event => {
event.returnValue = locale.messages;
});
ipc.on('add-setup-menu-items', () => {
setupMenu({
includeSetup: false,
});
});
ipc.on('draw-attention', () => {
if (!mainWindow) {
return;
@ -823,13 +844,13 @@ ipc.on('resetDatabase', async () => {
app.quit();
});
ipc.on('set-auto-hide-menu-bar', (event, autoHide) => {
ipc.on('set-auto-hide-menu-bar', (_event, autoHide) => {
if (mainWindow) {
mainWindow.setAutoHideMenuBar(autoHide);
}
});
ipc.on('set-menu-bar-visibility', (event, visibility) => {
ipc.on('set-menu-bar-visibility', (_event, visibility) => {
if (mainWindow) {
mainWindow.setMenuBarVisibility(visibility);
}
@ -843,18 +864,20 @@ ipc.on('close-about', () => {
// Password screen related IPC calls
ipc.on('password-window-login', async (event, passPhrase) => {
const sendResponse = e => event.sender.send('password-window-login-response', e);
const sendResponse = (e: string | undefined) => {
event.sender.send('password-window-login-response', e);
};
try {
const passwordAttempt = true;
await showMainWindow(passPhrase, passwordAttempt);
sendResponse();
sendResponse(undefined);
} catch (e) {
const localisedError = locale.messages.invalidPassword;
sendResponse(localisedError || 'Invalid password');
}
});
ipc.on('start-in-tray-on-start', async (event, newValue) => {
ipc.on('start-in-tray-on-start', (event, newValue) => {
try {
userConfig.set('startInTray', newValue);
if (newValue) {
@ -873,9 +896,9 @@ ipc.on('start-in-tray-on-start', async (event, newValue) => {
}
});
ipc.on('get-start-in-tray', async (event, newValue) => {
ipc.on('get-start-in-tray', event => {
try {
const val = userConfig.get('startInTray', newValue);
const val = userConfig.get('startInTray');
event.sender.send('get-start-in-tray-response', val);
} catch (e) {
event.sender.send('get-start-in-tray-response', false);
@ -883,11 +906,13 @@ ipc.on('get-start-in-tray', async (event, newValue) => {
});
ipc.on('set-password', async (event, passPhrase, oldPhrase) => {
const sendResponse = e => event.sender.send('set-password-response', e);
const sendResponse = (response: string | undefined) => {
event.sender.send('set-password-response', response);
};
try {
// Check if the hash we have stored matches the hash of the old passphrase.
const hash = sql.getPasswordHash();
const hash = sqlNode.getPasswordHash();
const hashMatches = oldPhrase && passwordUtil.matchesHash(oldPhrase, hash);
if (hash && !hashMatches) {
@ -900,17 +925,17 @@ ipc.on('set-password', async (event, passPhrase, oldPhrase) => {
if (_.isEmpty(passPhrase)) {
const defaultKey = getDefaultSQLKey();
sql.setSQLPassword(defaultKey);
sql.removePasswordHash();
sqlNode.setSQLPassword(defaultKey);
sqlNode.removePasswordHash();
userConfig.set('dbHasPassword', false);
} else {
sql.setSQLPassword(passPhrase);
sqlNode.setSQLPassword(passPhrase);
const newHash = passwordUtil.generateHash(passPhrase);
sql.savePasswordHash(newHash);
sqlNode.savePasswordHash(newHash);
userConfig.set('dbHasPassword', true);
}
sendResponse();
sendResponse(undefined);
} catch (e) {
const localisedError = locale.messages.setPasswordFail;
sendResponse(localisedError || 'Failed to set password');
@ -930,6 +955,7 @@ ipc.on('save-debug-log', (_event, logText) => {
console.info(`Trying to save logs to log Desktop ${osSpecificDesktopFolder}`);
const outputPath = path.join(osSpecificDesktopFolder, `session_debug_${Date.now()}.log`);
// tslint:disable: non-literal-fs-path
fs.writeFile(outputPath, logText, err => {
if (err) {
console.error(`Error saving debug log to ${outputPath}`);
@ -948,7 +974,7 @@ ipc.on('set-media-permissions', (event, value) => {
userConfig.set('mediaPermissions', value);
// We reinstall permissions handler to ensure that a revoked permission takes effect
installPermissionsHandler({ session, userConfig });
installPermissionsHandler({ userConfig });
event.sender.send('set-success-media-permissions', null);
});
@ -962,7 +988,7 @@ ipc.on('set-call-media-permissions', (event, value) => {
userConfig.set('callMediaPermissions', value);
// We reinstall permissions handler to ensure that a revoked permission takes effect
installPermissionsHandler({ session, userConfig });
installPermissionsHandler({ userConfig });
event.sender.send('set-success-call-media-permissions', null);
});
@ -974,21 +1000,23 @@ ipc.on('get-auto-update-setting', event => {
event.returnValue = typeof configValue !== 'boolean' ? true : configValue;
});
ipc.on('set-auto-update-setting', (event, enabled) => {
ipc.on('set-auto-update-setting', async (_event, enabled) => {
userConfig.set('autoUpdate', !!enabled);
if (enabled) {
readyForUpdates();
await readyForUpdates();
} else {
updater.stop();
isReadyForUpdates = false;
}
});
function getThemeFromMainWindow() {
async function getThemeFromMainWindow() {
return new Promise(resolve => {
ipc.once('get-success-theme-setting', (_event, value) => resolve(value));
mainWindow.webContents.send('get-theme-setting');
ipc.once('get-success-theme-setting', (_event, value) => {
resolve(value);
});
mainWindow?.webContents.send('get-theme-setting');
});
}
@ -1006,5 +1034,5 @@ async function askForMediaAccess() {
}
ipc.on('media-access', async () => {
askForMediaAccess();
await askForMediaAccess();
});

@ -2,6 +2,7 @@ import { ipcMain } from 'electron';
import rimraf from 'rimraf';
import * as Attachments from '../attachments/attachments';
import { removeKnownAttachments } from './sql';
// tslint:disable: no-console
let initialized = false;
@ -11,14 +12,14 @@ const CLEANUP_ORPHANED_ATTACHMENTS_KEY = 'cleanup-orphaned-attachments';
export async function cleanupOrphanedAttachments(userDataPath: string) {
const allAttachments = await Attachments.getAllAttachments(userDataPath);
const orphanedAttachments = await sql.removeKnownAttachments(allAttachments); //sql.js
const orphanedAttachments = await removeKnownAttachments(allAttachments); //sql.js
await Attachments.deleteAll({
userDataPath,
attachments: orphanedAttachments,
});
}
export async function initialize({ userDataPath }: { userDataPath: string }) {
export async function initAttachmentsChannel({ userDataPath }: { userDataPath: string }) {
if (initialized) {
throw new Error('initialze: Already initialized!');
}

@ -1,7 +1,10 @@
import { readFileSync, unlinkSync, writeFileSync } from 'fs';
// tslint:disable: no-console
const ENCODING = 'utf8';
type ValueType = number | string | boolean | null | object;
export function start(
name: string,
targetPath: string,
@ -10,7 +13,7 @@ export function start(
} = {}
) {
const { allowMalformedOnStartup } = options;
let cachedValue: Record<string, number | string | boolean> = {};
let cachedValue: Record<string, ValueType> = {};
try {
const text = readFileSync(targetPath, ENCODING);
@ -34,7 +37,7 @@ export function start(
return cachedValue[keyPath];
}
function set(keyPath: string, value: number | string | boolean) {
function set(keyPath: string, value: ValueType) {
cachedValue[keyPath] = value;
console.log(`config/set: Saving ${name} config to disk`);
const text = JSON.stringify(cachedValue, null, ' ');

@ -40,7 +40,7 @@ export const updateLocale = (messages: LocaleMessagesType) => {
copyErrorAndQuitText = messages.copyErrorAndQuit;
};
export const addHandler = () => {
export const setupGlobalErrorHandler = () => {
process.on('uncaughtException', async error => {
await handleError('Unhandled Error', error);
});

@ -0,0 +1,9 @@
let latestRelease: string | undefined;
export function setLastestRelease(release: string) {
latestRelease = release;
}
export function getLastestRelease() {
return latestRelease;
}

@ -24,7 +24,7 @@ export type ConsoleCustom = typeof console & {
_error: (...args: any) => void;
};
export async function initialize() {
export async function initializeLogger() {
if (logger) {
throw new Error('Already called initialize!');
}

@ -7,7 +7,7 @@ export const createTemplate = (
options: {
openReleaseNotes: () => void;
openSupportPage: () => void;
platform: () => void;
platform: string;
showAbout: () => void;
showDebugLog: () => void;
showWindow: () => void;

@ -3,6 +3,7 @@
// tslint:disable: no-console
import { UserConfig } from './config/user_config';
import { session } from 'electron/main';
const PERMISSIONS: Record<string, boolean> = {
// Allowed
@ -40,7 +41,7 @@ export function installPermissionsHandler({ userConfig }: { userConfig: UserConf
// Setting the permission request handler to null first forces any permissions to be
// requested again. Without this, revoked permissions might still be available if
// they've already been used successfully.
Electron.Session.defaultSession.setPermissionRequestHandler(null);
session.defaultSession.setPermissionRequestHandler(null);
Electron.Session.defaultSession.setPermissionRequestHandler(createPermissionHandler(userConfig));
session.defaultSession.setPermissionRequestHandler(createPermissionHandler(userConfig));
}

@ -1269,8 +1269,8 @@ function updateToLokiSchemaVersion20(currentVersion: number, db: BetterSqlite3.D
obj.name = obj.profile.displayName;
updateConversation(obj, db);
}
writeLokiSchemaVersion(targetVersion, db);
})();
});
writeLokiSchemaVersion(targetVersion, db);
});
console.log(`updateToLokiSchemaVersion${targetVersion}: success!`);
}
@ -1407,7 +1407,7 @@ function showFailedToStart() {
notification.show();
}
async function initialize({
async function initializeSql({
configDir,
key,
messages,
@ -3024,7 +3024,7 @@ function getExternalFilesForConversation(conversation: any) {
return files;
}
function removeKnownAttachments(allAttachments: any) {
export function removeKnownAttachments(allAttachments: any) {
const lookup = fromPairs(map(allAttachments, file => [file, true]));
const chunkSize = 50;
@ -3492,7 +3492,7 @@ function fillWithTestData(numConvosToAdd: number, numMsgsToAdd: number) {
}
const exportedFunctions = {
initialize,
initializeSql,
close,
removeDB,
setSQLPassword,
@ -3599,4 +3599,4 @@ const exportedFunctions = {
};
// tslint:disable-next-line: no-default-export
export default exportedFunctions;
export const sqlNode = exportedFunctions;

@ -8,7 +8,10 @@ let trayContextMenu = null;
let tray: Tray | null = null;
let trayAny: any;
function createTrayIcon(getMainWindow: () => BrowserWindow, messages: LocaleMessagesType) {
export function createTrayIcon(
getMainWindow: () => BrowserWindow | null,
messages: LocaleMessagesType
) {
// keep the duplicated part to allow for search and find
const iconFile = process.platform === 'darwin' ? 'session_icon_16.png' : 'session_icon_32.png';
const iconNoNewMessages = path.join(__dirname, '..', 'images', 'session', iconFile);
@ -61,7 +64,7 @@ function createTrayIcon(getMainWindow: () => BrowserWindow, messages: LocaleMess
trayContextMenu = Menu.buildFromTemplate([
{
id: 'toggleWindowVisibility',
label: messages[mainWindow.isVisible() ? 'appMenuHide' : 'show'],
label: messages[mainWindow?.isVisible() ? 'appMenuHide' : 'show'],
click: trayAny.toggleWindowVisibility,
},
{
@ -81,5 +84,3 @@ function createTrayIcon(getMainWindow: () => BrowserWindow, messages: LocaleMess
return tray;
}
module.exports = createTrayIcon;

@ -1,9 +1,9 @@
let shouldQuitFlag = false;
export function markShouldQuit() {
export function windowMarkShouldQuit() {
shouldQuitFlag = true;
}
export function shouldQuit() {
export function windowShouldQuit() {
return shouldQuitFlag;
}

@ -7,10 +7,10 @@ let initialized = false;
let localUserConfig: UserConfig;
export async function start(
getMainWindow: () => BrowserWindow,
getMainWindow: () => BrowserWindow | null,
userConfig: UserConfig,
messages: MessagesType,
logger: LoggerType
logger?: LoggerType | null
) {
if (initialized) {
throw new Error('updater/start: Updates have already been initialized!');

@ -2,7 +2,7 @@ import * as path from 'path';
import * as fs from 'fs-extra';
import { autoUpdater, UpdateInfo } from 'electron-updater';
import { app, BrowserWindow } from 'electron';
import { markShouldQuit } from '../node/window_state';
import { windowMarkShouldQuit } from '../node/window_state';
import {
getPrintableError,
@ -13,6 +13,7 @@ import {
showUpdateDialog,
} from './common';
import { gt as isVersionGreaterThan, parse as parseVersion } from 'semver';
import { getLastestRelease } from '../node/latest_desktop_release';
let isUpdating = false;
let downloadIgnored = false;
@ -20,7 +21,7 @@ let interval: NodeJS.Timeout | undefined;
let stopped = false;
export async function start(
getMainWindow: () => BrowserWindow,
getMainWindow: () => BrowserWindow | null,
messages: MessagesType,
logger: LoggerType
) {
@ -56,7 +57,7 @@ export function stop() {
}
async function checkForUpdates(
getMainWindow: () => BrowserWindow,
getMainWindow: () => BrowserWindow | null,
messages: MessagesType,
logger: LoggerType
) {
@ -77,9 +78,7 @@ async function checkForUpdates(
isUpdating = true;
try {
const latestVersionFromFsFromRenderer = getMainWindow()
? ((getMainWindow() as any).getLatestDesktopRelease() as string | undefined)
: undefined;
const latestVersionFromFsFromRenderer = getLastestRelease();
logger.info('[updater] latestVersionFromFsFromRenderer', latestVersionFromFsFromRenderer);
if (!latestVersionFromFsFromRenderer || !latestVersionFromFsFromRenderer?.length) {
@ -120,8 +119,13 @@ async function checkForUpdates(
return;
}
const mainWindow = getMainWindow();
if (!mainWindow) {
console.warn('cannot showDownloadUpdateDialog, mainWindow is unset');
return;
}
logger.info('[updater] showing download dialog...');
const shouldDownload = await showDownloadUpdateDialog(getMainWindow(), messages);
const shouldDownload = await showDownloadUpdateDialog(mainWindow, messages);
logger.info('[updater] shouldDownload:', shouldDownload);
if (!shouldDownload) {
@ -132,19 +136,28 @@ async function checkForUpdates(
await autoUpdater.downloadUpdate();
} catch (error) {
await showCannotUpdateDialog(getMainWindow(), messages);
const mainWindow = getMainWindow();
if (!mainWindow) {
console.warn('cannot showDownloadUpdateDialog, mainWindow is unset');
return;
}
await showCannotUpdateDialog(mainWindow, messages);
throw error;
}
const window = getMainWindow();
if (!window) {
console.warn('cannot showDownloadUpdateDialog, mainWindow is unset');
return;
}
// Update downloaded successfully, we should ask the user to update
logger.info('[updater] showing update dialog...');
const shouldUpdate = await showUpdateDialog(getMainWindow(), messages);
const shouldUpdate = await showUpdateDialog(window, messages);
if (!shouldUpdate) {
return;
}
logger.info('[updater] calling quitAndInstall...');
markShouldQuit();
windowMarkShouldQuit();
autoUpdater.quitAndInstall();
} finally {
isUpdating = false;

@ -621,6 +621,22 @@
global-agent "^2.0.2"
global-tunnel-ng "^2.7.1"
"@electron/get@^1.13.0":
version "1.14.1"
resolved "https://registry.yarnpkg.com/@electron/get/-/get-1.14.1.tgz#16ba75f02dffb74c23965e72d617adc721d27f40"
integrity sha512-BrZYyL/6m0ZXz/lDxy/nlVhQz+WF+iPS6qXolEU8atw7h6v1aYkjwJZ63m+bJMBTxDE66X+r2tPS4a/8C82sZw==
dependencies:
debug "^4.1.1"
env-paths "^2.2.0"
fs-extra "^8.1.0"
got "^9.6.0"
progress "^2.0.3"
semver "^6.2.0"
sumchecker "^3.0.1"
optionalDependencies:
global-agent "^3.0.0"
global-tunnel-ng "^2.7.1"
"@emotion/is-prop-valid@^0.8.8":
version "0.8.8"
resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz#db28b1c4368a259b60a97311d6a952d4fd01ac1a"
@ -928,6 +944,13 @@
dependencies:
electron-is-dev "*"
"@types/electron-localshortcut@^3.1.0":
version "3.1.0"
resolved "https://registry.yarnpkg.com/@types/electron-localshortcut/-/electron-localshortcut-3.1.0.tgz#eb3c270bb47f1e0b583749c7e988f5c5c1e7e4a1"
integrity sha512-upKSXMxBPRdz5kmcXfdfn+hWH9PCAvwhyVozDXTIwwHQ1lUJcdSgGUfxOC1QBlnAPKPqcW/r4icWfMosKz8ibg==
dependencies:
electron "*"
"@types/emoji-mart@^2.11.3":
version "2.11.3"
resolved "https://registry.yarnpkg.com/@types/emoji-mart/-/emoji-mart-2.11.3.tgz#9949f6a8a231aea47aac1b2d4212597b41140b07"
@ -1941,6 +1964,11 @@ boolean@^3.0.0:
resolved "https://registry.yarnpkg.com/boolean/-/boolean-3.0.1.tgz#35ecf2b4a2ee191b0b44986f14eb5f052a5cbb4f"
integrity sha512-HRZPIjPcbwAVQvOTxR4YE3o8Xs98NqbbL1iEZDCz7CL8ql0Lt5iOyJFxfnAB0oFs8Oh02F/lLlg30Mexv46LjA==
boolean@^3.0.1:
version "3.2.0"
resolved "https://registry.yarnpkg.com/boolean/-/boolean-3.2.0.tgz#9e5294af4e98314494cbb17979fa54ca159f116b"
integrity sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==
boxen@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/boxen/-/boxen-4.2.0.tgz#e411b62357d6d6d36587c8ac3d5d974daa070e64"
@ -3071,6 +3099,15 @@ electron-updater@^4.2.2:
pako "^1.0.11"
semver "^7.1.3"
electron@*:
version "17.3.0"
resolved "https://registry.yarnpkg.com/electron/-/electron-17.3.0.tgz#cdcc46a7a3cd0b6f2a1757fbeb807f6b2fce847e"
integrity sha512-KuYHCOw1a+CE9thZlWRqTScf6M81KLd6n5qpdBGb0rl62+50RUuau9CnYpBb3EJxrjsXLaiQCBBSdPsozf/XUg==
dependencies:
"@electron/get" "^1.13.0"
"@types/node" "^14.6.2"
extract-zip "^1.0.3"
electron@^13.6.2:
version "13.6.3"
resolved "https://registry.yarnpkg.com/electron/-/electron-13.6.3.tgz#c0217178807d3e0b2175c49dbe33ea8dac447e73"
@ -3183,6 +3220,11 @@ escape-string-regexp@^2.0.0:
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344"
integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==
escape-string-regexp@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34"
integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==
eslint-config-airbnb-base@12.1.0:
version "12.1.0"
resolved "https://registry.yarnpkg.com/eslint-config-airbnb-base/-/eslint-config-airbnb-base-12.1.0.tgz#386441e54a12ccd957b0a92564a4bafebd747944"
@ -3900,6 +3942,18 @@ global-agent@^2.0.2:
semver "^7.1.2"
serialize-error "^5.0.0"
global-agent@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/global-agent/-/global-agent-3.0.0.tgz#ae7cd31bd3583b93c5a16437a1afe27cc33a1ab6"
integrity sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==
dependencies:
boolean "^3.0.1"
es6-error "^4.1.1"
matcher "^3.0.0"
roarr "^2.15.3"
semver "^7.3.2"
serialize-error "^7.0.1"
global-dirs@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-2.0.1.tgz#acdf3bb6685bcd55cb35e8a052266569e9469201"
@ -5093,6 +5147,13 @@ matcher@^2.1.0:
dependencies:
escape-string-regexp "^2.0.0"
matcher@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/matcher/-/matcher-3.0.0.tgz#bd9060f4c5b70aa8041ccc6f80368760994f30ca"
integrity sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==
dependencies:
escape-string-regexp "^4.0.0"
mdn-data@2.0.14:
version "2.0.14"
resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50"
@ -6790,6 +6851,18 @@ roarr@^2.15.2:
semver-compare "^1.0.0"
sprintf-js "^1.1.2"
roarr@^2.15.3:
version "2.15.4"
resolved "https://registry.yarnpkg.com/roarr/-/roarr-2.15.4.tgz#f5fe795b7b838ccfe35dc608e0282b9eba2e7afd"
integrity sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==
dependencies:
boolean "^3.0.1"
detect-node "^2.0.4"
globalthis "^1.0.1"
json-stringify-safe "^5.0.1"
semver-compare "^1.0.0"
sprintf-js "^1.1.2"
rtl-css-js@^1.14.0:
version "1.14.0"
resolved "https://registry.yarnpkg.com/rtl-css-js/-/rtl-css-js-1.14.0.tgz#daa4f192a92509e292a0519f4b255e6e3c076b7d"
@ -6954,6 +7027,13 @@ serialize-error@^5.0.0:
dependencies:
type-fest "^0.8.0"
serialize-error@^7.0.1:
version "7.0.1"
resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-7.0.1.tgz#f1360b0447f61ffb483ec4157c737fab7d778e18"
integrity sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==
dependencies:
type-fest "^0.13.1"
serialize-javascript@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.0.tgz#efae5d88f45d7924141da8b5c3a7a7e663fefeb8"
@ -7727,6 +7807,11 @@ type-detect@4.0.8, type-detect@^4.0.0, type-detect@^4.0.5, type-detect@^4.0.8:
resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c"
integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==
type-fest@^0.13.1:
version "0.13.1"
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.13.1.tgz#0172cb5bce80b0bd542ea348db50c7e21834d934"
integrity sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==
type-fest@^0.18.0:
version "0.18.1"
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.18.1.tgz#db4bc151a4a2cf4eebf9add5db75508db6cc841f"

Loading…
Cancel
Save