Typescriptify main.js

This commit is contained in:
Scott Nonnenberg 2021-10-01 11:49:59 -07:00 committed by GitHub
parent e033fd2cf3
commit 9a1430a460
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 721 additions and 524 deletions

View File

@ -193,7 +193,7 @@ module.exports = grunt => {
process.platform === 'win32' ? 'electron.cmd' : 'electron'; process.platform === 'win32' ? 'electron.cmd' : 'electron';
const path = join(__dirname, 'node_modules', '.bin', electronBinary); const path = join(__dirname, 'node_modules', '.bin', electronBinary);
const args = [join(__dirname, 'main.js')]; const args = [join(__dirname, 'app', 'main.js')];
grunt.log.writeln('Starting path', path, 'with args', args); grunt.log.writeln('Starting path', path, 'with args', args);
const app = new Application({ const app = new Application({
path, path,

View File

@ -7,18 +7,20 @@ import { get, set } from 'lodash';
const ENCODING = 'utf8'; const ENCODING = 'utf8';
type ConfigType = Record<string, unknown>; type InternalConfigType = Record<string, unknown>;
export type ConfigType = {
set: (keyPath: string, value: unknown) => void;
get: (keyPath: string) => unknown;
remove: () => void;
};
export function start( export function start(
name: string, name: string,
targetPath: string, targetPath: string,
options?: { allowMalformedOnStartup?: boolean } options?: { allowMalformedOnStartup?: boolean }
): { ): ConfigType {
set: (keyPath: string, value: unknown) => void; let cachedValue: InternalConfigType | undefined;
get: (keyPath: string) => unknown;
remove: () => void;
} {
let cachedValue: ConfigType | undefined;
try { try {
const text = readFileSync(targetPath, ENCODING); const text = readFileSync(targetPath, ENCODING);

View File

@ -4,6 +4,8 @@
import { join } from 'path'; import { join } from 'path';
import { app } from 'electron'; import { app } from 'electron';
import type { IConfig } from 'config';
import { import {
Environment, Environment,
getEnvironment, getEnvironment,
@ -34,21 +36,9 @@ if (getEnvironment() === Environment.Production) {
process.env.SIGNAL_ENABLE_HTTP = ''; process.env.SIGNAL_ENABLE_HTTP = '';
} }
export type ConfigType = {
get: (key: string) => unknown;
has: (key: string) => unknown;
[key: string]: unknown;
util: {
getEnv: (keY: string) => string | undefined;
};
};
// We load config after we've made our modifications to NODE_ENV // We load config after we've made our modifications to NODE_ENV
// eslint-disable-next-line @typescript-eslint/no-var-requires // eslint-disable-next-line import/order, import/first
const config: ConfigType = require('config'); import config from 'config';
config.environment = getEnvironment();
config.enableHttp = process.env.SIGNAL_ENABLE_HTTP;
// Log resulting env vars in use by config // Log resulting env vars in use by config
[ [
@ -65,3 +55,4 @@ config.enableHttp = process.env.SIGNAL_ENABLE_HTTP;
}); });
export default config; export default config;
export type { IConfig as ConfigType };

View File

@ -32,17 +32,19 @@ function getLocaleMessages(locale: string): LocaleMessagesType {
return JSON.parse(readFileSync(targetFile, 'utf-8')); return JSON.parse(readFileSync(targetFile, 'utf-8'));
} }
export type LocaleType = {
i18n: LocalizerType;
name: string;
messages: LocaleMessagesType;
};
export function load({ export function load({
appLocale, appLocale,
logger, logger,
}: { }: {
appLocale: string; appLocale: string;
logger: LoggerType; logger: LoggerType;
}): { }): LocaleType {
i18n: LocalizerType;
name: string;
messages: LocaleMessagesType;
} {
if (!appLocale) { if (!appLocale) {
throw new TypeError('`appLocale` is required'); throw new TypeError('`appLocale` is required');
} }

File diff suppressed because it is too large Load Diff

View File

@ -8,7 +8,7 @@ import { LocaleMessagesType } from '../ts/types/I18N';
export type MenuListType = Array<MenuItemConstructorOptions>; export type MenuListType = Array<MenuItemConstructorOptions>;
type OptionsType = { export type MenuOptionsType = {
// options // options
development: boolean; development: boolean;
devTools: boolean; devTools: boolean;
@ -17,6 +17,7 @@ type OptionsType = {
platform: string; platform: string;
// actions // actions
forceUpdate: () => unknown;
openContactUs: () => unknown; openContactUs: () => unknown;
openForums: () => unknown; openForums: () => unknown;
openJoinTheBeta: () => unknown; openJoinTheBeta: () => unknown;
@ -24,7 +25,6 @@ type OptionsType = {
openSupportPage: () => unknown; openSupportPage: () => unknown;
setupAsNewDevice: () => unknown; setupAsNewDevice: () => unknown;
setupAsStandalone: () => unknown; setupAsStandalone: () => unknown;
forceUpdate: () => unknown;
showAbout: () => unknown; showAbout: () => unknown;
showDebugLog: () => unknown; showDebugLog: () => unknown;
showKeyboardShortcuts: () => unknown; showKeyboardShortcuts: () => unknown;
@ -34,7 +34,7 @@ type OptionsType = {
}; };
export const createTemplate = ( export const createTemplate = (
options: OptionsType, options: MenuOptionsType,
messages: LocaleMessagesType messages: LocaleMessagesType
): MenuListType => { ): MenuListType => {
if (!isString(options.platform)) { if (!isString(options.platform)) {
@ -265,7 +265,7 @@ export const createTemplate = (
function updateForMac( function updateForMac(
template: MenuListType, template: MenuListType,
messages: LocaleMessagesType, messages: LocaleMessagesType,
options: OptionsType options: MenuOptionsType
): MenuListType { ): MenuListType {
const { showAbout, showSettings, showWindow } = options; const { showAbout, showSettings, showWindow } = options;

View File

@ -6,7 +6,7 @@
import { session as ElectronSession } from 'electron'; import { session as ElectronSession } from 'electron';
import { ConfigType } from './config'; import type { ConfigType } from './base_config';
const PERMISSIONS: Record<string, boolean> = { const PERMISSIONS: Record<string, boolean> = {
// Allowed // Allowed

View File

@ -140,7 +140,7 @@ export function installWebHandler({
enableHttp, enableHttp,
}: { }: {
protocol: typeof ElectronProtocol; protocol: typeof ElectronProtocol;
enableHttp: string; enableHttp: boolean;
}): void { }): void {
protocol.interceptFileProtocol('about', _disabledHandler); protocol.interceptFileProtocol('about', _disabledHandler);
protocol.interceptFileProtocol('content', _disabledHandler); protocol.interceptFileProtocol('content', _disabledHandler);

24
app/startup_config.ts Normal file
View File

@ -0,0 +1,24 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import { app } from 'electron';
import packageJson from '../package.json';
import * as GlobalErrors from './global_errors';
GlobalErrors.addHandler();
// Set umask early on in the process lifecycle to ensure file permissions are
// set such that only we have read access to our files
process.umask(0o077);
const appUserModelId = `org.whispersystems.${packageJson.name}`;
console.log('Set Windows Application User Model ID (AUMID)', {
appUserModelId,
});
app.setAppUserModelId(appUserModelId);
// We don't navigate, but this is the way of the future
// https://github.com/electron/electron/issues/18397
// TODO: Make ringrtc-node context-aware and change this to true.
app.allowRendererProcessReuse = false;

View File

@ -10,3 +10,13 @@ export function markShouldQuit(): void {
export function shouldQuit(): boolean { export function shouldQuit(): boolean {
return shouldQuitFlag; return shouldQuitFlag;
} }
let isReadyForShutdown = false;
export function markReadyForShutdown(): void {
isReadyForShutdown = true;
}
export function readyForShutdown(): boolean {
return isReadyForShutdown;
}

View File

@ -18,7 +18,6 @@
"buildCreation": 0, "buildCreation": 0,
"buildExpiration": 0, "buildExpiration": 0,
"certificateAuthority": "-----BEGIN CERTIFICATE-----\nMIID7zCCAtegAwIBAgIJAIm6LatK5PNiMA0GCSqGSIb3DQEBBQUAMIGNMQswCQYD\nVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5j\naXNjbzEdMBsGA1UECgwUT3BlbiBXaGlzcGVyIFN5c3RlbXMxHTAbBgNVBAsMFE9w\nZW4gV2hpc3BlciBTeXN0ZW1zMRMwEQYDVQQDDApUZXh0U2VjdXJlMB4XDTEzMDMy\nNTIyMTgzNVoXDTIzMDMyMzIyMTgzNVowgY0xCzAJBgNVBAYTAlVTMRMwEQYDVQQI\nDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4gRnJhbmNpc2NvMR0wGwYDVQQKDBRP\ncGVuIFdoaXNwZXIgU3lzdGVtczEdMBsGA1UECwwUT3BlbiBXaGlzcGVyIFN5c3Rl\nbXMxEzARBgNVBAMMClRleHRTZWN1cmUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw\nggEKAoIBAQDBSWBpOCBDF0i4q2d4jAXkSXUGpbeWugVPQCjaL6qD9QDOxeW1afvf\nPo863i6Crq1KDxHpB36EwzVcjwLkFTIMeo7t9s1FQolAt3mErV2U0vie6Ves+yj6\ngrSfxwIDAcdsKmI0a1SQCZlr3Q1tcHAkAKFRxYNawADyps5B+Zmqcgf653TXS5/0\nIPPQLocLn8GWLwOYNnYfBvILKDMItmZTtEbucdigxEA9mfIvvHADEbteLtVgwBm9\nR5vVvtwrD6CCxI3pgH7EH7kMP0Od93wLisvn1yhHY7FuYlrkYqdkMvWUrKoASVw4\njb69vaeJCUdU+HCoXOSP1PQcL6WenNCHAgMBAAGjUDBOMB0GA1UdDgQWBBQBixjx\nP/s5GURuhYa+lGUypzI8kDAfBgNVHSMEGDAWgBQBixjxP/s5GURuhYa+lGUypzI8\nkDAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQB+Hr4hC56m0LvJAu1R\nK6NuPDbTMEN7/jMojFHxH4P3XPFfupjR+bkDq0pPOU6JjIxnrD1XD/EVmTTaTVY5\niOheyv7UzJOefb2pLOc9qsuvI4fnaESh9bhzln+LXxtCrRPGhkxA1IMIo3J/s2WF\n/KVYZyciu6b4ubJ91XPAuBNZwImug7/srWvbpk0hq6A6z140WTVSKtJG7EP41kJe\n/oF4usY5J7LPkxK3LWzMJnb5EIJDmRvyH8pyRwWg6Qm6qiGFaI4nL8QU4La1x2en\n4DGXRaLMPRwjELNgQPodR38zoCMuA8gHZfZYYoZ7D7Q1wNUiVHcxuFrEeBaYJbLE\nrwLV\n-----END CERTIFICATE-----\n", "certificateAuthority": "-----BEGIN CERTIFICATE-----\nMIID7zCCAtegAwIBAgIJAIm6LatK5PNiMA0GCSqGSIb3DQEBBQUAMIGNMQswCQYD\nVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5j\naXNjbzEdMBsGA1UECgwUT3BlbiBXaGlzcGVyIFN5c3RlbXMxHTAbBgNVBAsMFE9w\nZW4gV2hpc3BlciBTeXN0ZW1zMRMwEQYDVQQDDApUZXh0U2VjdXJlMB4XDTEzMDMy\nNTIyMTgzNVoXDTIzMDMyMzIyMTgzNVowgY0xCzAJBgNVBAYTAlVTMRMwEQYDVQQI\nDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4gRnJhbmNpc2NvMR0wGwYDVQQKDBRP\ncGVuIFdoaXNwZXIgU3lzdGVtczEdMBsGA1UECwwUT3BlbiBXaGlzcGVyIFN5c3Rl\nbXMxEzARBgNVBAMMClRleHRTZWN1cmUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw\nggEKAoIBAQDBSWBpOCBDF0i4q2d4jAXkSXUGpbeWugVPQCjaL6qD9QDOxeW1afvf\nPo863i6Crq1KDxHpB36EwzVcjwLkFTIMeo7t9s1FQolAt3mErV2U0vie6Ves+yj6\ngrSfxwIDAcdsKmI0a1SQCZlr3Q1tcHAkAKFRxYNawADyps5B+Zmqcgf653TXS5/0\nIPPQLocLn8GWLwOYNnYfBvILKDMItmZTtEbucdigxEA9mfIvvHADEbteLtVgwBm9\nR5vVvtwrD6CCxI3pgH7EH7kMP0Od93wLisvn1yhHY7FuYlrkYqdkMvWUrKoASVw4\njb69vaeJCUdU+HCoXOSP1PQcL6WenNCHAgMBAAGjUDBOMB0GA1UdDgQWBBQBixjx\nP/s5GURuhYa+lGUypzI8kDAfBgNVHSMEGDAWgBQBixjxP/s5GURuhYa+lGUypzI8\nkDAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQB+Hr4hC56m0LvJAu1R\nK6NuPDbTMEN7/jMojFHxH4P3XPFfupjR+bkDq0pPOU6JjIxnrD1XD/EVmTTaTVY5\niOheyv7UzJOefb2pLOc9qsuvI4fnaESh9bhzln+LXxtCrRPGhkxA1IMIo3J/s2WF\n/KVYZyciu6b4ubJ91XPAuBNZwImug7/srWvbpk0hq6A6z140WTVSKtJG7EP41kJe\n/oF4usY5J7LPkxK3LWzMJnb5EIJDmRvyH8pyRwWg6Qm6qiGFaI4nL8QU4La1x2en\n4DGXRaLMPRwjELNgQPodR38zoCMuA8gHZfZYYoZ7D7Q1wNUiVHcxuFrEeBaYJbLE\nrwLV\n-----END CERTIFICATE-----\n",
"import": false,
"serverPublicParams": "ABSY21VckQcbSXVNCGRYJcfWHiAMZmpTtTELcDmxgdFbtp/bWsSxZdMKzfCp8rvIs8ocCU3B37fT3r4Mi5qAemeGeR2X+/YmOGR5ofui7tD5mDQfstAI9i+4WpMtIe8KC3wU5w3Inq3uNWVmoGtpKndsNfwJrCg0Hd9zmObhypUnSkfYn2ooMOOnBpfdanRtrvetZUayDMSC5iSRcXKpdlukrpzzsCIvEwjwQlJYVPOQPj4V0F4UXXBdHSLK05uoPBCQG8G9rYIGedYsClJXnbrgGYG3eMTG5hnx4X4ntARB", "serverPublicParams": "ABSY21VckQcbSXVNCGRYJcfWHiAMZmpTtTELcDmxgdFbtp/bWsSxZdMKzfCp8rvIs8ocCU3B37fT3r4Mi5qAemeGeR2X+/YmOGR5ofui7tD5mDQfstAI9i+4WpMtIe8KC3wU5w3Inq3uNWVmoGtpKndsNfwJrCg0Hd9zmObhypUnSkfYn2ooMOOnBpfdanRtrvetZUayDMSC5iSRcXKpdlukrpzzsCIvEwjwQlJYVPOQPj4V0F4UXXBdHSLK05uoPBCQG8G9rYIGedYsClJXnbrgGYG3eMTG5hnx4X4ntARB",
"serverTrustRoot": "BbqY1DzohE4NUZoVF+L18oUPrK3kILllLEJh2UnPSsEx" "serverTrustRoot": "BbqY1DzohE4NUZoVF+L18oUPrK3kILllLEJh2UnPSsEx"
} }

View File

@ -10,7 +10,7 @@
"name": "Open Whisper Systems", "name": "Open Whisper Systems",
"email": "support@signal.org" "email": "support@signal.org"
}, },
"main": "main.js", "main": "app/main.js",
"scripts": { "scripts": {
"postinstall": "yarn build:acknowledgments && yarn build:fuses && snyk protect && patch-package && electron-builder install-app-deps && rimraf node_modules/dtrace-provider", "postinstall": "yarn build:acknowledgments && yarn build:fuses && snyk protect && patch-package && electron-builder install-app-deps && rimraf node_modules/dtrace-provider",
"postuninstall": "yarn build:acknowledgments", "postuninstall": "yarn build:acknowledgments",
@ -188,7 +188,7 @@
"@types/chai": "4.2.18", "@types/chai": "4.2.18",
"@types/chai-as-promised": "7.1.4", "@types/chai-as-promised": "7.1.4",
"@types/classnames": "2.2.3", "@types/classnames": "2.2.3",
"@types/config": "0.0.34", "@types/config": "0.0.39",
"@types/dashdash": "1.14.0", "@types/dashdash": "1.14.0",
"@types/filesize": "3.6.0", "@types/filesize": "3.6.0",
"@types/fs-extra": "5.0.5", "@types/fs-extra": "5.0.5",

View File

@ -5,19 +5,21 @@
/* eslint-disable more/no-then */ /* eslint-disable more/no-then */
/* eslint-disable no-console */ /* eslint-disable no-console */
import * as path from 'path'; import { join } from 'path';
import * as fs from 'fs'; import { readdirSync, readFile, unlinkSync, writeFileSync } from 'fs';
import { BrowserWindow, app, ipcMain as ipc } from 'electron'; import { BrowserWindow, app, ipcMain as ipc } from 'electron';
import pinoms from 'pino-multi-stream'; import pinoms from 'pino-multi-stream';
import pino from 'pino'; import pino from 'pino';
import * as mkdirp from 'mkdirp'; import * as mkdirp from 'mkdirp';
import * as _ from 'lodash'; import { compact, filter, flatten, map, pick, sortBy } from 'lodash';
import readFirstLine from 'firstline'; import readFirstLine from 'firstline';
import { read as readLastLines } from 'read-last-lines'; import { read as readLastLines } from 'read-last-lines';
import rimraf from 'rimraf'; import rimraf from 'rimraf';
import { createStream } from 'rotating-file-stream'; import { createStream } from 'rotating-file-stream';
import { setLogAtLevel } from './log'; import type { LoggerType } from '../types/Logging';
import * as log from './log';
import { Environment, getEnvironment } from '../environment'; import { Environment, getEnvironment } from '../environment';
import { import {
@ -39,7 +41,7 @@ declare global {
} }
} }
let globalLogger: undefined | pinoms.Logger; let globalLogger: undefined | pino.Logger;
let shouldRestart = false; let shouldRestart = false;
const isRunningFromConsole = const isRunningFromConsole =
@ -49,13 +51,13 @@ const isRunningFromConsole =
export async function initialize( export async function initialize(
getMainWindow: () => undefined | BrowserWindow getMainWindow: () => undefined | BrowserWindow
): Promise<pinoms.Logger> { ): Promise<LoggerType> {
if (globalLogger) { if (globalLogger) {
throw new Error('Already called initialize!'); throw new Error('Already called initialize!');
} }
const basePath = app.getPath('userData'); const basePath = app.getPath('userData');
const logPath = path.join(basePath, 'logs'); const logPath = join(basePath, 'logs');
mkdirp.sync(logPath); mkdirp.sync(logPath);
try { try {
@ -73,7 +75,7 @@ export async function initialize(
}, 500); }, 500);
} }
const logFile = path.join(logPath, 'main.log'); const logFile = join(logPath, 'main.log');
const stream = createStream(logFile, { const stream = createStream(logFile, {
interval: '1d', interval: '1d',
rotate: 3, rotate: 3,
@ -160,7 +162,7 @@ export async function initialize(
globalLogger = logger; globalLogger = logger;
return logger; return log;
} }
async function deleteAllLogs(logPath: string): Promise<void> { async function deleteAllLogs(logPath: string): Promise<void> {
@ -189,7 +191,7 @@ async function cleanupLogs(logPath: string) {
try { try {
const remaining = await eliminateOutOfDateFiles(logPath, earliestDate); const remaining = await eliminateOutOfDateFiles(logPath, earliestDate);
const files = _.filter(remaining, file => !file.start && file.end); const files = filter(remaining, file => !file.start && file.end);
if (!files.length) { if (!files.length) {
return; return;
@ -234,11 +236,11 @@ export function eliminateOutOfDateFiles(
end: boolean; end: boolean;
}> }>
> { > {
const files = fs.readdirSync(logPath); const files = readdirSync(logPath);
const paths = files.map(file => path.join(logPath, file)); const paths = files.map(file => join(logPath, file));
return Promise.all( return Promise.all(
_.map(paths, target => map(paths, target =>
Promise.all([readFirstLine(target), readLastLines(target, 2)]).then( Promise.all([readFirstLine(target), readLastLines(target, 2)]).then(
results => { results => {
const start = results[0]; const start = results[0];
@ -253,7 +255,7 @@ export function eliminateOutOfDateFiles(
}; };
if (!file.start && !file.end) { if (!file.start && !file.end) {
fs.unlinkSync(file.path); unlinkSync(file.path);
} }
return file; return file;
@ -269,12 +271,12 @@ export async function eliminateOldEntries(
date: Readonly<Date> date: Readonly<Date>
): Promise<void> { ): Promise<void> {
await Promise.all( await Promise.all(
_.map(files, file => map(files, file =>
fetchLog(file.path).then(lines => { fetchLog(file.path).then(lines => {
const recent = _.filter(lines, line => new Date(line.time) >= date); const recent = filter(lines, line => new Date(line.time) >= date);
const text = _.map(recent, line => JSON.stringify(line)).join('\n'); const text = map(recent, line => JSON.stringify(line)).join('\n');
return fs.writeFileSync(file.path, `${text}\n`); return writeFileSync(file.path, `${text}\n`);
}) })
) )
); );
@ -283,16 +285,16 @@ export async function eliminateOldEntries(
// Exported for testing only. // Exported for testing only.
export function fetchLog(logFile: string): Promise<Array<LogEntryType>> { export function fetchLog(logFile: string): Promise<Array<LogEntryType>> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
fs.readFile(logFile, { encoding: 'utf8' }, (err, text) => { readFile(logFile, { encoding: 'utf8' }, (err, text) => {
if (err) { if (err) {
return reject(err); return reject(err);
} }
const lines = _.compact(text.split('\n')); const lines = compact(text.split('\n'));
const data = _.compact( const data = compact(
lines.map(line => { lines.map(line => {
try { try {
const result = _.pick(JSON.parse(line), ['level', 'time', 'msg']); const result = pick(JSON.parse(line), ['level', 'time', 'msg']);
return isLogEntry(result) ? result : null; return isLogEntry(result) ? result : null;
} catch (e) { } catch (e) {
return null; return null;
@ -307,8 +309,8 @@ export function fetchLog(logFile: string): Promise<Array<LogEntryType>> {
// Exported for testing only. // Exported for testing only.
export function fetchLogs(logPath: string): Promise<Array<LogEntryType>> { export function fetchLogs(logPath: string): Promise<Array<LogEntryType>> {
const files = fs.readdirSync(logPath); const files = readdirSync(logPath);
const paths = files.map(file => path.join(logPath, file)); const paths = files.map(file => join(logPath, file));
// creating a manual log entry for the final log result // creating a manual log entry for the final log result
const fileListEntry: LogEntryType = { const fileListEntry: LogEntryType = {
@ -318,11 +320,11 @@ export function fetchLogs(logPath: string): Promise<Array<LogEntryType>> {
}; };
return Promise.all(paths.map(fetchLog)).then(results => { return Promise.all(paths.map(fetchLog)).then(results => {
const data = _.flatten(results); const data = flatten(results);
data.push(fileListEntry); data.push(fileListEntry);
return _.sortBy(data, logEntry => logEntry.time); return sortBy(data, logEntry => logEntry.time);
}); });
} }
@ -351,12 +353,12 @@ function isProbablyObjectHasBeenDestroyedError(err: unknown): boolean {
// This blows up using mocha --watch, so we ensure it is run just once // This blows up using mocha --watch, so we ensure it is run just once
if (!console._log) { if (!console._log) {
setLogAtLevel(logAtLevel); log.setLogAtLevel(logAtLevel);
console._log = console.log; console._log = console.log;
console.log = _.partial(logAtLevel, LogLevel.Info); console.log = log.info;
console._error = console.error; console._error = console.error;
console.error = _.partial(logAtLevel, LogLevel.Error); console.error = log.error;
console._warn = console.warn; console._warn = console.warn;
console.warn = _.partial(logAtLevel, LogLevel.Warn); console.warn = log.warn;
} }

View File

@ -15,7 +15,7 @@ import {
export class SettingsChannel { export class SettingsChannel {
private mainWindow?: BrowserWindow; private mainWindow?: BrowserWindow;
public setMainWindow(mainWindow: BrowserWindow): void { public setMainWindow(mainWindow: BrowserWindow | undefined): void {
this.mainWindow = mainWindow; this.mainWindow = mainWindow;
} }

View File

@ -170,6 +170,7 @@ describe('sgnlHref', () => {
assert.deepEqual(parseSgnlHref(href, explodingLogger), { assert.deepEqual(parseSgnlHref(href, explodingLogger), {
command: null, command: null,
args: new Map<never, never>(), args: new Map<never, never>(),
hash: undefined,
}); });
}); });
}); });
@ -329,6 +330,7 @@ describe('sgnlHref', () => {
assert.deepEqual(parseSignalHttpsLink(href, explodingLogger), { assert.deepEqual(parseSignalHttpsLink(href, explodingLogger), {
command: null, command: null,
args: new Map<never, never>(), args: new Map<never, never>(),
hash: undefined,
}); });
}); });
}); });

View File

@ -354,9 +354,17 @@ export function setUpdateListener(performUpdateCallback: () => void): void {
ipcMain.once('start-update', performUpdateCallback); ipcMain.once('start-update', performUpdateCallback);
} }
export function getAutoDownloadUpdateSetting( export async function getAutoDownloadUpdateSetting(
mainWindow: BrowserWindow mainWindow: BrowserWindow | undefined,
logger: LoggerType
): Promise<boolean> { ): Promise<boolean> {
if (!mainWindow) {
logger.warn(
'getAutoDownloadUpdateSetting: No main window, returning false'
);
return false;
}
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
ipcMain.once( ipcMain.once(
'settings:get-success:autoDownloadUpdate', 'settings:get-success:autoDownloadUpdate',

View File

@ -14,7 +14,7 @@ let initialized = false;
let updater: UpdaterInterface | undefined; let updater: UpdaterInterface | undefined;
export async function start( export async function start(
getMainWindow: () => BrowserWindow, getMainWindow: () => BrowserWindow | undefined,
logger?: LoggerType logger?: LoggerType
): Promise<void> { ): Promise<void> {
const { platform } = process; const { platform } = process;

View File

@ -30,7 +30,7 @@ import { DialogType } from '../types/Dialogs';
const INTERVAL = 30 * durations.MINUTE; const INTERVAL = 30 * durations.MINUTE;
export async function start( export async function start(
getMainWindow: () => BrowserWindow, getMainWindow: () => BrowserWindow | undefined,
logger: LoggerType logger: LoggerType
): Promise<UpdaterInterface> { ): Promise<UpdaterInterface> {
logger.info('macos/start: starting checks...'); logger.info('macos/start: starting checks...');
@ -61,7 +61,7 @@ let updateFilePath: string;
let loggerForQuitHandler: LoggerType; let loggerForQuitHandler: LoggerType;
async function checkForUpdatesMaybeInstall( async function checkForUpdatesMaybeInstall(
getMainWindow: () => BrowserWindow, getMainWindow: () => BrowserWindow | undefined,
logger: LoggerType, logger: LoggerType,
force = false force = false
) { ) {
@ -75,12 +75,13 @@ async function checkForUpdatesMaybeInstall(
if (fileName !== newFileName || !version || gt(newVersion, version)) { if (fileName !== newFileName || !version || gt(newVersion, version)) {
const autoDownloadUpdates = await getAutoDownloadUpdateSetting( const autoDownloadUpdates = await getAutoDownloadUpdateSetting(
getMainWindow() getMainWindow(),
logger
); );
if (!autoDownloadUpdates) { if (!autoDownloadUpdates) {
setUpdateListener(async () => { setUpdateListener(async () => {
logger.info( logger.info(
'performUpdate: have not downloaded update, going to download' 'checkForUpdatesMaybeInstall: have not downloaded update, going to download'
); );
await downloadAndInstall( await downloadAndInstall(
newFileName, newFileName,
@ -90,14 +91,22 @@ async function checkForUpdatesMaybeInstall(
true true
); );
}); });
getMainWindow().webContents.send( const mainWindow = getMainWindow();
'show-update-dialog',
DialogType.DownloadReady, if (mainWindow) {
{ mainWindow.webContents.send(
downloadSize: result.size, 'show-update-dialog',
version: result.version, DialogType.DownloadReady,
} {
); downloadSize: result.size,
version: result.version,
}
);
} else {
logger.warn(
'checkForUpdatesMaybeInstall: no mainWindow, cannot show update dialog'
);
}
return; return;
} }
await downloadAndInstall(newFileName, newVersion, getMainWindow, logger); await downloadAndInstall(newFileName, newVersion, getMainWindow, logger);
@ -107,7 +116,7 @@ async function checkForUpdatesMaybeInstall(
async function downloadAndInstall( async function downloadAndInstall(
newFileName: string, newFileName: string,
newVersion: string, newVersion: string,
getMainWindow: () => BrowserWindow, getMainWindow: () => BrowserWindow | undefined,
logger: LoggerType, logger: LoggerType,
updateOnProgress?: boolean updateOnProgress?: boolean
) { ) {
@ -151,20 +160,25 @@ async function downloadAndInstall(
} catch (error) { } catch (error) {
const readOnly = 'Cannot update while running on a read-only volume'; const readOnly = 'Cannot update while running on a read-only volume';
const message: string = error.message || ''; const message: string = error.message || '';
if (message.includes(readOnly)) { const mainWindow = getMainWindow();
if (mainWindow && message.includes(readOnly)) {
logger.info('downloadAndInstall: showing read-only dialog...'); logger.info('downloadAndInstall: showing read-only dialog...');
getMainWindow().webContents.send( mainWindow.webContents.send(
'show-update-dialog', 'show-update-dialog',
DialogType.MacOS_Read_Only DialogType.MacOS_Read_Only
); );
} else { } else if (mainWindow) {
logger.info( logger.info(
'downloadAndInstall: showing general update failure dialog...' 'downloadAndInstall: showing general update failure dialog...'
); );
getMainWindow().webContents.send( mainWindow.webContents.send(
'show-update-dialog', 'show-update-dialog',
DialogType.Cannot_Update DialogType.Cannot_Update
); );
} else {
logger.warn(
'downloadAndInstall: no mainWindow, cannot show update dialog'
);
} }
throw error; throw error;
@ -179,9 +193,17 @@ async function downloadAndInstall(
markShouldQuit(); markShouldQuit();
autoUpdater.quitAndInstall(); autoUpdater.quitAndInstall();
}); });
getMainWindow().webContents.send('show-update-dialog', DialogType.Update, { const mainWindow = getMainWindow();
version,
}); if (mainWindow) {
mainWindow.webContents.send('show-update-dialog', DialogType.Update, {
version,
});
} else {
logger.warn(
'checkForUpdatesMaybeInstall: no mainWindow, cannot show update dialog'
);
}
} catch (error) { } catch (error) {
logger.error(`downloadAndInstall: ${getPrintableError(error)}`); logger.error(`downloadAndInstall: ${getPrintableError(error)}`);
} }

View File

@ -37,7 +37,7 @@ let installing: boolean;
let loggerForQuitHandler: LoggerType; let loggerForQuitHandler: LoggerType;
export async function start( export async function start(
getMainWindow: () => BrowserWindow, getMainWindow: () => BrowserWindow | undefined,
logger: LoggerType logger: LoggerType
): Promise<UpdaterInterface> { ): Promise<UpdaterInterface> {
logger.info('windows/start: starting checks...'); logger.info('windows/start: starting checks...');
@ -64,7 +64,7 @@ export async function start(
} }
async function checkForUpdatesMaybeInstall( async function checkForUpdatesMaybeInstall(
getMainWindow: () => BrowserWindow, getMainWindow: () => BrowserWindow | undefined,
logger: LoggerType, logger: LoggerType,
force = false force = false
) { ) {
@ -78,12 +78,13 @@ async function checkForUpdatesMaybeInstall(
if (fileName !== newFileName || !version || gt(newVersion, version)) { if (fileName !== newFileName || !version || gt(newVersion, version)) {
const autoDownloadUpdates = await getAutoDownloadUpdateSetting( const autoDownloadUpdates = await getAutoDownloadUpdateSetting(
getMainWindow() getMainWindow(),
logger
); );
if (!autoDownloadUpdates) { if (!autoDownloadUpdates) {
setUpdateListener(async () => { setUpdateListener(async () => {
logger.info( logger.info(
'performUpdate: have not downloaded update, going to download' 'checkForUpdatesMaybeInstall: have not downloaded update, going to download'
); );
await downloadAndInstall( await downloadAndInstall(
newFileName, newFileName,
@ -93,14 +94,21 @@ async function checkForUpdatesMaybeInstall(
true true
); );
}); });
getMainWindow().webContents.send( const mainWindow = getMainWindow();
'show-update-dialog', if (mainWindow) {
DialogType.DownloadReady, mainWindow.webContents.send(
{ 'show-update-dialog',
downloadSize: result.size, DialogType.DownloadReady,
version: result.version, {
} downloadSize: result.size,
); version: result.version,
}
);
} else {
logger.warn(
'checkForUpdatesMaybeInstall: No mainWindow, not showing update dialog'
);
}
return; return;
} }
await downloadAndInstall(newFileName, newVersion, getMainWindow, logger); await downloadAndInstall(newFileName, newVersion, getMainWindow, logger);
@ -110,7 +118,7 @@ async function checkForUpdatesMaybeInstall(
async function downloadAndInstall( async function downloadAndInstall(
newFileName: string, newFileName: string,
newVersion: string, newVersion: string,
getMainWindow: () => BrowserWindow, getMainWindow: () => BrowserWindow | undefined,
logger: LoggerType, logger: LoggerType,
updateOnProgress?: boolean updateOnProgress?: boolean
) { ) {
@ -151,11 +159,18 @@ async function downloadAndInstall(
await verifyAndInstall(updateFilePath, newVersion, logger); await verifyAndInstall(updateFilePath, newVersion, logger);
installing = true; installing = true;
} catch (error) { } catch (error) {
logger.info('createUpdater: showing general update failure dialog...'); const mainWindow = getMainWindow();
getMainWindow().webContents.send( if (mainWindow) {
'show-update-dialog', logger.info(
DialogType.Cannot_Update 'createUpdater: showing general update failure dialog...'
); );
mainWindow.webContents.send(
'show-update-dialog',
DialogType.Cannot_Update
);
} else {
logger.warn('createUpdater: no mainWindow, just failing over...');
}
throw error; throw error;
} }
@ -163,9 +178,17 @@ async function downloadAndInstall(
markShouldQuit(); markShouldQuit();
app.quit(); app.quit();
}); });
getMainWindow().webContents.send('show-update-dialog', DialogType.Update, {
version, const mainWindow = getMainWindow();
}); if (mainWindow) {
mainWindow.webContents.send('show-update-dialog', DialogType.Update, {
version,
});
} else {
logger.warn(
'downloadAndInstall: no mainWindow, cannot show update dialog'
);
}
} catch (error) { } catch (error) {
logger.error(`downloadAndInstall: ${getPrintableError(error)}`); logger.error(`downloadAndInstall: ${getPrintableError(error)}`);
} }

View File

@ -3,6 +3,7 @@
import { LocaleMessagesType } from '../types/I18N'; import { LocaleMessagesType } from '../types/I18N';
import { LocalizerType } from '../types/Util'; import { LocalizerType } from '../types/Util';
import * as log from '../logging/log';
export function setupI18n( export function setupI18n(
locale: string, locale: string,
@ -16,9 +17,6 @@ export function setupI18n(
} }
const getMessage: LocalizerType = (key, substitutions) => { const getMessage: LocalizerType = (key, substitutions) => {
// eslint-disable-next-line no-console
const log = window?.SignalWindow?.log || console;
const entry = messages[key]; const entry = messages[key];
if (!entry) { if (!entry) {
log.error( log.error(

View File

@ -51,7 +51,7 @@ export function isSignalHttpsLink(
} }
type ParsedSgnlHref = type ParsedSgnlHref =
| { command: null; args: Map<never, never> } | { command: null; args: Map<never, never>; hash: undefined }
| { command: string; args: Map<string, string>; hash: string | undefined }; | { command: string; args: Map<string, string>; hash: string | undefined };
export function parseSgnlHref( export function parseSgnlHref(
href: string, href: string,
@ -59,7 +59,7 @@ export function parseSgnlHref(
): ParsedSgnlHref { ): ParsedSgnlHref {
const url = parseUrl(href, logger); const url = parseUrl(href, logger);
if (!url || !isSgnlHref(url, logger)) { if (!url || !isSgnlHref(url, logger)) {
return { command: null, args: new Map<never, never>() }; return { command: null, args: new Map<never, never>(), hash: undefined };
} }
const args = new Map<string, string>(); const args = new Map<string, string>();
@ -99,7 +99,7 @@ export function parseSignalHttpsLink(
): ParsedSgnlHref { ): ParsedSgnlHref {
const url = parseUrl(href, logger); const url = parseUrl(href, logger);
if (!url || !isSignalHttpsLink(url, logger)) { if (!url || !isSignalHttpsLink(url, logger)) {
return { command: null, args: new Map<never, never>() }; return { command: null, args: new Map<never, never>(), hash: undefined };
} }
if (url.host === 'signal.art') { if (url.host === 'signal.art') {
@ -114,7 +114,7 @@ export function parseSignalHttpsLink(
}); });
if (!args.get('pack_id') || !args.get('pack_key')) { if (!args.get('pack_id') || !args.get('pack_key')) {
return { command: null, args: new Map<never, never>() }; return { command: null, args: new Map<never, never>(), hash: undefined };
} }
return { return {
@ -132,7 +132,7 @@ export function parseSignalHttpsLink(
}; };
} }
return { command: null, args: new Map<never, never>() }; return { command: null, args: new Map<never, never>(), hash: undefined };
} }
export function parseE164FromSignalDotMeHash(hash: string): undefined | string { export function parseE164FromSignalDotMeHash(hash: string): undefined | string {

View File

@ -2476,10 +2476,10 @@
resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0" resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0"
integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ== integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==
"@types/config@0.0.34": "@types/config@0.0.39":
version "0.0.34" version "0.0.39"
resolved "https://registry.yarnpkg.com/@types/config/-/config-0.0.34.tgz#123f91bdb5afdd702294b9de9ca04d9ea11137b0" resolved "https://registry.yarnpkg.com/@types/config/-/config-0.0.39.tgz#aad18ceb9439329adc3d4c6b91a908a72c715612"
integrity sha512-jWi9DXx77hnzN4kHCNEvP/kab+nchRLTg9yjXYxjTcMBkuc5iBb3QuwJ4sPrb+nzy1GQjrfyfMqZOdR4i7opRQ== integrity sha512-EBHj9lSIyw62vwqCwkeJXjiV6C2m2o+RJZlRWLkHduGYiNBoMXcY6AhSLqjQQ+uPdrPYrOMYvVa41zjo00LbFQ==
"@types/connect-history-api-fallback@*": "@types/connect-history-api-fallback@*":
version "1.3.3" version "1.3.3"