Enables sandbox for all windows except main
This commit is contained in:
parent
abb839c24b
commit
e211837bcd
67 changed files with 1190 additions and 615 deletions
|
@ -26,8 +26,7 @@ ts/**/*.js
|
|||
.eslintrc.js
|
||||
webpack.config.ts
|
||||
preload.bundle.*
|
||||
about.browser.bundle.*
|
||||
about.preload.bundle.*
|
||||
bundles/**
|
||||
|
||||
# Sticker Creator has its own eslint config
|
||||
sticker-creator/**
|
||||
|
|
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -27,8 +27,7 @@ libtextsecure/components.js
|
|||
stylesheets/*.css
|
||||
/storybook-static/
|
||||
preload.bundle.*
|
||||
about.browser.bundle.*
|
||||
about.preload.bundle.*
|
||||
bundles/
|
||||
ts/sql/mainWorker.bundle.js.LICENSE.txt
|
||||
|
||||
# React / TypeScript
|
||||
|
|
|
@ -41,8 +41,7 @@ js/WebAudioRecorderMp3.js
|
|||
stylesheets/_intlTelInput.scss
|
||||
|
||||
preload.bundle.*
|
||||
about.browser.bundle.*
|
||||
about.preload.bundle.*
|
||||
bundles/**
|
||||
|
||||
# Sticker Creator has its own prettier config
|
||||
sticker-creator/**
|
||||
|
|
|
@ -391,6 +391,30 @@ Signal Desktop makes use of the following open source projects.
|
|||
|
||||
License: MIT
|
||||
|
||||
## buffer
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) Feross Aboukhadijeh, and other contributors.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
## cirbuf
|
||||
|
||||
MIT License
|
||||
|
@ -2398,6 +2422,10 @@ Signal Desktop makes use of the following open source projects.
|
|||
|
||||
License: MIT
|
||||
|
||||
## uuid-browser
|
||||
|
||||
License: MIT
|
||||
|
||||
## websocket
|
||||
|
||||
Apache License
|
||||
|
|
|
@ -29,10 +29,6 @@
|
|||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="application/javascript" src="ts/windows/init.js"></script>
|
||||
<script
|
||||
type="application/javascript"
|
||||
src="about.browser.bundle.js"
|
||||
></script>
|
||||
<script type="module" src="bundles/about/app.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import * as log from '../ts/logging/log';
|
||||
import OS from '../ts/util/os/osMain';
|
||||
import {
|
||||
parseSystemTraySetting,
|
||||
SystemTraySetting,
|
||||
|
@ -54,7 +55,7 @@ export class SystemTraySettingCache {
|
|||
log.info(
|
||||
`getSystemTraySetting saw --use-tray-icon flag. Returning ${result}`
|
||||
);
|
||||
} else if (isSystemTraySupported(this.appVersion)) {
|
||||
} else if (isSystemTraySupported(OS, this.appVersion)) {
|
||||
const fastValue = this.ephemeralConfig.get('system-tray-setting');
|
||||
if (fastValue !== undefined) {
|
||||
log.info('getSystemTraySetting got fast value', fastValue);
|
||||
|
|
|
@ -10,7 +10,7 @@ import * as Errors from '../ts/types/errors';
|
|||
import { isProduction } from '../ts/util/version';
|
||||
import { upload as uploadDebugLog } from '../ts/logging/uploadDebugLog';
|
||||
import { SignalService as Proto } from '../ts/protobuf';
|
||||
import * as OS from '../ts/OS';
|
||||
import OS from '../ts/util/os/osMain';
|
||||
|
||||
async function getPendingDumps(): Promise<ReadonlyArray<string>> {
|
||||
const crashDumpsPath = await realpath(app.getPath('crashDumps'));
|
||||
|
|
80
app/main.ts
80
app/main.ts
|
@ -48,6 +48,8 @@ import type { ThemeSettingType } from '../ts/types/StorageUIKeys';
|
|||
import { ThemeType } from '../ts/types/Util';
|
||||
import * as Errors from '../ts/types/errors';
|
||||
import { resolveCanonicalLocales } from '../ts/util/resolveCanonicalLocales';
|
||||
import * as debugLog from '../ts/logging/debuglogs';
|
||||
import * as uploadDebugLog from '../ts/logging/uploadDebugLog';
|
||||
import { explodePromise } from '../ts/util/explodePromise';
|
||||
|
||||
import './startup_config';
|
||||
|
@ -94,7 +96,7 @@ import type { CreateTemplateOptionsType } from './menu';
|
|||
import type { MenuActionType } from '../ts/types/menu';
|
||||
import { createTemplate } from './menu';
|
||||
import { installFileHandler, installWebHandler } from './protocol_filter';
|
||||
import * as OS from '../ts/OS';
|
||||
import OS from '../ts/util/os/osMain';
|
||||
import { isProduction } from '../ts/util/version';
|
||||
import {
|
||||
isSgnlHref,
|
||||
|
@ -390,7 +392,11 @@ function getResolvedMessagesLocale(): LocaleType {
|
|||
return resolvedTranslationsLocale;
|
||||
}
|
||||
|
||||
type PrepareUrlOptions = { forCalling?: boolean; forCamera?: boolean };
|
||||
type PrepareUrlOptions = {
|
||||
forCalling?: boolean;
|
||||
forCamera?: boolean;
|
||||
sourceName?: string;
|
||||
};
|
||||
|
||||
async function prepareFileUrl(
|
||||
pathSegments: ReadonlyArray<string>,
|
||||
|
@ -403,9 +409,9 @@ async function prepareFileUrl(
|
|||
|
||||
async function prepareUrl(
|
||||
url: URL,
|
||||
{ forCalling, forCamera }: PrepareUrlOptions = {}
|
||||
{ forCalling, forCamera, sourceName }: PrepareUrlOptions = {}
|
||||
): Promise<string> {
|
||||
return setUrlSearchParams(url, { forCalling, forCamera }).href;
|
||||
return setUrlSearchParams(url, { forCalling, forCamera, sourceName }).href;
|
||||
}
|
||||
|
||||
async function handleUrl(rawTarget: string) {
|
||||
|
@ -1155,9 +1161,9 @@ async function showScreenShareWindow(sourceName: string) {
|
|||
...defaultWebPrefs,
|
||||
nodeIntegration: false,
|
||||
nodeIntegrationInWorker: false,
|
||||
sandbox: false,
|
||||
sandbox: true,
|
||||
contextIsolation: true,
|
||||
preload: join(__dirname, '../ts/windows/screenShare/preload.js'),
|
||||
preload: join(__dirname, '../bundles/screenShare/preload.js'),
|
||||
},
|
||||
x: Math.floor(display.size.width / 2) - width / 2,
|
||||
y: 24,
|
||||
|
@ -1173,17 +1179,13 @@ async function showScreenShareWindow(sourceName: string) {
|
|||
|
||||
screenShareWindow.once('ready-to-show', () => {
|
||||
if (screenShareWindow) {
|
||||
screenShareWindow.showInactive();
|
||||
screenShareWindow.webContents.send(
|
||||
'render-screen-sharing-controller',
|
||||
sourceName
|
||||
);
|
||||
screenShareWindow.show();
|
||||
}
|
||||
});
|
||||
|
||||
await safeLoadURL(
|
||||
screenShareWindow,
|
||||
await prepareFileUrl([__dirname, '../screenShare.html'])
|
||||
await prepareFileUrl([__dirname, '../screenShare.html'], { sourceName })
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1210,9 +1212,9 @@ async function showAbout() {
|
|||
...defaultWebPrefs,
|
||||
nodeIntegration: false,
|
||||
nodeIntegrationInWorker: false,
|
||||
sandbox: false,
|
||||
sandbox: true,
|
||||
contextIsolation: true,
|
||||
preload: join(__dirname, '../about.preload.bundle.js'),
|
||||
preload: join(__dirname, '../bundles/about/preload.js'),
|
||||
nativeWindowOpen: true,
|
||||
},
|
||||
};
|
||||
|
@ -1261,9 +1263,9 @@ async function showSettingsWindow() {
|
|||
...defaultWebPrefs,
|
||||
nodeIntegration: false,
|
||||
nodeIntegrationInWorker: false,
|
||||
sandbox: false,
|
||||
sandbox: true,
|
||||
contextIsolation: true,
|
||||
preload: join(__dirname, '../ts/windows/settings/preload.js'),
|
||||
preload: join(__dirname, '../bundles/settings/preload.js'),
|
||||
nativeWindowOpen: true,
|
||||
},
|
||||
};
|
||||
|
@ -1341,9 +1343,9 @@ async function showDebugLogWindow() {
|
|||
...defaultWebPrefs,
|
||||
nodeIntegration: false,
|
||||
nodeIntegrationInWorker: false,
|
||||
sandbox: false,
|
||||
sandbox: true,
|
||||
contextIsolation: true,
|
||||
preload: join(__dirname, '../ts/windows/debuglog/preload.js'),
|
||||
preload: join(__dirname, '../bundles/debuglog/preload.js'),
|
||||
nativeWindowOpen: true,
|
||||
},
|
||||
parent: mainWindow,
|
||||
|
@ -1406,9 +1408,9 @@ function showPermissionsPopupWindow(forCalling: boolean, forCamera: boolean) {
|
|||
...defaultWebPrefs,
|
||||
nodeIntegration: false,
|
||||
nodeIntegrationInWorker: false,
|
||||
sandbox: false,
|
||||
sandbox: true,
|
||||
contextIsolation: true,
|
||||
preload: join(__dirname, '../ts/windows/permissions/preload.js'),
|
||||
preload: join(__dirname, '../bundles/permissions/preload.js'),
|
||||
nativeWindowOpen: true,
|
||||
},
|
||||
parent: mainWindow,
|
||||
|
@ -1676,7 +1678,7 @@ app.on('ready', async () => {
|
|||
// would still show the window.
|
||||
// (User can change these settings later)
|
||||
if (
|
||||
isSystemTraySupported(app.getVersion()) &&
|
||||
isSystemTraySupported(OS, app.getVersion()) &&
|
||||
(await systemTraySettingCache.get()) === SystemTraySetting.Uninitialized
|
||||
) {
|
||||
const newValue = SystemTraySetting.MinimizeToSystemTray;
|
||||
|
@ -1799,9 +1801,9 @@ app.on('ready', async () => {
|
|||
webPreferences: {
|
||||
...defaultWebPrefs,
|
||||
nodeIntegration: false,
|
||||
sandbox: false,
|
||||
sandbox: true,
|
||||
contextIsolation: true,
|
||||
preload: join(__dirname, '../ts/windows/loading/preload.js'),
|
||||
preload: join(__dirname, '../bundles/loading/preload.js'),
|
||||
},
|
||||
icon: windowIcon,
|
||||
});
|
||||
|
@ -2278,6 +2280,8 @@ ipc.on('get-config', async event => {
|
|||
enableCI,
|
||||
nodeVersion: process.versions.node,
|
||||
hostname: os.hostname(),
|
||||
osRelease: os.release(),
|
||||
osVersion: os.version(),
|
||||
appInstance: process.env.NODE_APP_INSTANCE || undefined,
|
||||
proxyUrl: process.env.HTTPS_PROXY || process.env.https_proxy || undefined,
|
||||
contentProxyUrl: config.get<string>('contentProxyUrl'),
|
||||
|
@ -2320,11 +2324,39 @@ ipc.on('locale-data', event => {
|
|||
event.returnValue = getResolvedMessagesLocale().messages;
|
||||
});
|
||||
|
||||
ipc.on('getHasCustomTitleBar', event => {
|
||||
// TODO DESKTOP-5241
|
||||
ipc.on('OS.getHasCustomTitleBar', event => {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
event.returnValue = OS.hasCustomTitleBar();
|
||||
});
|
||||
|
||||
// TODO DESKTOP-5241
|
||||
ipc.on('OS.getClassName', event => {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
event.returnValue = OS.getClassName();
|
||||
});
|
||||
|
||||
ipc.handle(
|
||||
'DebugLogs.getLogs',
|
||||
async (_event, data: unknown, userAgent: string) => {
|
||||
return debugLog.getLog(
|
||||
data,
|
||||
process.versions.node,
|
||||
app.getVersion(),
|
||||
os.version(),
|
||||
userAgent
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
ipc.handle('DebugLogs.upload', async (_event, content: string) => {
|
||||
return uploadDebugLog.upload({
|
||||
content,
|
||||
appVersion: app.getVersion(),
|
||||
logger: getLogger(),
|
||||
});
|
||||
});
|
||||
|
||||
ipc.on('user-config-key', event => {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
event.returnValue = userConfig.get('key');
|
||||
|
|
|
@ -29,10 +29,6 @@
|
|||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script
|
||||
type="application/javascript"
|
||||
src="ts/windows/applyTheme.js"
|
||||
></script>
|
||||
<script type="application/javascript" src="ts/windows/init.js"></script>
|
||||
<script type="module" src="bundles/debuglog/app.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -33,6 +33,6 @@
|
|||
</div>
|
||||
<div id="message"></div>
|
||||
</div>
|
||||
<script type="text/javascript" src="ts/windows/loading/start.js"></script>
|
||||
<script type="module" src="bundles/loading/start.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
10
package.json
10
package.json
|
@ -11,6 +11,9 @@
|
|||
"email": "support@signal.org"
|
||||
},
|
||||
"browserslist": "last 1 chrome versions",
|
||||
"browser": {
|
||||
"uuid": "uuid-browser"
|
||||
},
|
||||
"main": "app/main.js",
|
||||
"scripts": {
|
||||
"postinstall": "yarn build:acknowledgments && patch-package && yarn electron:install-app-deps",
|
||||
|
@ -56,7 +59,7 @@
|
|||
"svgo": "svgo --multipass images/**/*.svg",
|
||||
"transpile": "run-p check:types build:esbuild",
|
||||
"check:types": "tsc --noEmit",
|
||||
"clean-transpile-once": "rimraf sticker-creator/dist app/**/*.js app/*.js ts/**/*.js ts/*.js tsconfig.tsbuildinfo",
|
||||
"clean-transpile-once": "rimraf sticker-creator/dist app/**/*.js app/*.js ts/**/*.js ts/*.js bundles tsconfig.tsbuildinfo",
|
||||
"clean-transpile": "yarn run clean-transpile-once && yarn run clean-transpile-once",
|
||||
"open-coverage": "open coverage/lcov-report/index.html",
|
||||
"ready": "npm-run-all --print-label clean-transpile generate --parallel lint lint-deps lint-intl test-node test-electron",
|
||||
|
@ -94,6 +97,7 @@
|
|||
"blob-util": "2.0.2",
|
||||
"blueimp-load-image": "5.14.0",
|
||||
"blurhash": "1.1.3",
|
||||
"buffer": "6.0.3",
|
||||
"cirbuf": "1.0.1",
|
||||
"classnames": "2.2.5",
|
||||
"config": "1.28.1",
|
||||
|
@ -169,6 +173,7 @@
|
|||
"split2": "4.0.0",
|
||||
"type-fest": "3.5.0",
|
||||
"uuid": "3.3.2",
|
||||
"uuid-browser": "3.1.0",
|
||||
"websocket": "1.0.34",
|
||||
"zod": "3.5.1"
|
||||
},
|
||||
|
@ -424,6 +429,7 @@
|
|||
"config/default.json",
|
||||
"config/${env.SIGNAL_ENV}.json",
|
||||
"config/local-${env.SIGNAL_ENV}.json",
|
||||
"bundles/**",
|
||||
"background.html",
|
||||
"about.html",
|
||||
"screenShare.html",
|
||||
|
@ -456,8 +462,6 @@
|
|||
"app/*",
|
||||
"preload.bundle.js",
|
||||
"preload_utils.js",
|
||||
"about.preload.bundle.js",
|
||||
"about.browser.bundle.js",
|
||||
"main.js",
|
||||
"images/**",
|
||||
"fonts/**",
|
||||
|
|
|
@ -1,19 +1,28 @@
|
|||
diff --git a/node_modules/@types/backbone/index.d.ts b/node_modules/@types/backbone/index.d.ts
|
||||
index a172230..6f6de9f 100644
|
||||
index a172230..2c62e92 100644
|
||||
--- a/node_modules/@types/backbone/index.d.ts
|
||||
+++ b/node_modules/@types/backbone/index.d.ts
|
||||
@@ -81,7 +81,7 @@ declare namespace Backbone {
|
||||
collection?: Backbone.Collection<TModel>;
|
||||
}
|
||||
|
||||
- type CombinedModelConstructorOptions<E, M extends Model<any, any, E> = Model> = ModelConstructorOptions<M> & E;
|
||||
+ type CombinedModelConstructorOptions<E, M extends Model<any, any, E> = Model<any, any, E>> = ModelConstructorOptions<M> & E;
|
||||
|
||||
interface ModelSetOptions extends Silenceable, Validable {
|
||||
}
|
||||
@@ -218,14 +218,14 @@ declare namespace Backbone {
|
||||
* E - Extensions to the model constructor options. You can accept additional constructor options
|
||||
* by listing them in the E parameter.
|
||||
*/
|
||||
- class Model<T = any, S = Backbone.ModelSetOptions, E = {}> extends ModelBase implements Events {
|
||||
+ class Model<T extends Record<string, any> = any, S = Backbone.ModelSetOptions, E = {}> extends ModelBase implements Events {
|
||||
|
||||
|
||||
/**
|
||||
* Do not use, prefer TypeScript's extend functionality.
|
||||
**/
|
||||
public static extend(properties: any, classProperties?: any): any;
|
||||
|
||||
|
||||
- attributes: any;
|
||||
+ attributes: T;
|
||||
changed: any[];
|
||||
|
|
|
@ -24,10 +24,6 @@
|
|||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script
|
||||
type="application/javascript"
|
||||
src="ts/windows/applyTheme.js"
|
||||
></script>
|
||||
<script type="application/javascript" src="ts/windows/init.js"></script>
|
||||
<script type="module" src="bundles/permissions/app.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -24,5 +24,6 @@
|
|||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="bundles/screenShare/app.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -6,6 +6,7 @@ const path = require('path');
|
|||
const glob = require('glob');
|
||||
|
||||
const ROOT_DIR = path.join(__dirname, '..');
|
||||
const BUNDLES_DIR = 'bundles';
|
||||
|
||||
const watch = process.argv.some(argv => argv === '-w' || argv === '--watch');
|
||||
const isProd = process.argv.some(argv => argv === '-prod' || argv === '--prod');
|
||||
|
@ -26,6 +27,7 @@ const bundleDefaults = {
|
|||
'process.env.NODE_ENV': isProd ? '"production"' : '"development"',
|
||||
},
|
||||
bundle: true,
|
||||
minify: isProd,
|
||||
external: [
|
||||
// Native libraries
|
||||
'@signalapp/libsignal-client',
|
||||
|
@ -61,55 +63,94 @@ const bundleDefaults = {
|
|||
],
|
||||
};
|
||||
|
||||
async function main() {
|
||||
// App, tests, and scripts
|
||||
const app = await esbuild.context({
|
||||
...nodeDefaults,
|
||||
format: 'cjs',
|
||||
mainFields: ['browser', 'main'],
|
||||
entryPoints: glob
|
||||
.sync('{app,ts}/**/*.{ts,tsx}', {
|
||||
nodir: true,
|
||||
root: ROOT_DIR,
|
||||
})
|
||||
.filter(file => !file.endsWith('.d.ts')),
|
||||
outdir: path.join(ROOT_DIR),
|
||||
});
|
||||
const sandboxedPreloadDefaults = {
|
||||
...nodeDefaults,
|
||||
define: {
|
||||
'process.env.NODE_ENV': isProd ? '"production"' : '"development"',
|
||||
},
|
||||
external: ['electron'],
|
||||
bundle: true,
|
||||
minify: isProd,
|
||||
};
|
||||
|
||||
// Preload bundle
|
||||
const bundle = await esbuild.context({
|
||||
...bundleDefaults,
|
||||
mainFields: ['browser', 'main'],
|
||||
entryPoints: [path.join(ROOT_DIR, 'ts', 'windows', 'main', 'preload.ts')],
|
||||
outfile: path.join(ROOT_DIR, 'preload.bundle.js'),
|
||||
});
|
||||
const sandboxedBrowserDefaults = {
|
||||
...sandboxedPreloadDefaults,
|
||||
chunkNames: 'chunks/[name]-[hash]',
|
||||
format: 'esm',
|
||||
outdir: path.join(ROOT_DIR, BUNDLES_DIR),
|
||||
platform: 'browser',
|
||||
splitting: true,
|
||||
};
|
||||
|
||||
async function build({ appConfig, preloadConfig }) {
|
||||
const app = await esbuild.context(appConfig);
|
||||
const preload = await esbuild.context(preloadConfig);
|
||||
|
||||
if (watch) {
|
||||
await Promise.all([app.watch(), bundle.watch()]);
|
||||
await Promise.all([app.watch(), preload.watch()]);
|
||||
} else {
|
||||
await Promise.all([app.rebuild(), bundle.rebuild()]);
|
||||
await Promise.all([app.rebuild(), preload.rebuild()]);
|
||||
|
||||
await app.dispose();
|
||||
await bundle.dispose();
|
||||
await preload.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
main().catch(error => {
|
||||
async function main() {
|
||||
await build({
|
||||
appConfig: {
|
||||
...nodeDefaults,
|
||||
format: 'cjs',
|
||||
mainFields: ['browser', 'main'],
|
||||
entryPoints: glob
|
||||
.sync('{app,ts}/**/*.{ts,tsx}', {
|
||||
nodir: true,
|
||||
root: ROOT_DIR,
|
||||
})
|
||||
.filter(file => !file.endsWith('.d.ts')),
|
||||
outdir: path.join(ROOT_DIR),
|
||||
},
|
||||
preloadConfig: {
|
||||
...bundleDefaults,
|
||||
mainFields: ['browser', 'main'],
|
||||
entryPoints: [path.join(ROOT_DIR, 'ts', 'windows', 'main', 'preload.ts')],
|
||||
outfile: path.join(ROOT_DIR, 'preload.bundle.js'),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async function sandboxedEnv() {
|
||||
await build({
|
||||
appConfig: {
|
||||
...sandboxedBrowserDefaults,
|
||||
mainFields: ['browser', 'main'],
|
||||
entryPoints: [
|
||||
path.join(ROOT_DIR, 'ts', 'windows', 'about', 'app.tsx'),
|
||||
path.join(ROOT_DIR, 'ts', 'windows', 'debuglog', 'app.tsx'),
|
||||
path.join(ROOT_DIR, 'ts', 'windows', 'loading', 'start.ts'),
|
||||
path.join(ROOT_DIR, 'ts', 'windows', 'permissions', 'app.tsx'),
|
||||
path.join(ROOT_DIR, 'ts', 'windows', 'screenShare', 'app.tsx'),
|
||||
path.join(ROOT_DIR, 'ts', 'windows', 'settings', 'app.tsx'),
|
||||
],
|
||||
},
|
||||
preloadConfig: {
|
||||
...sandboxedPreloadDefaults,
|
||||
mainFields: ['main'],
|
||||
entryPoints: [
|
||||
path.join(ROOT_DIR, 'ts', 'windows', 'about', 'preload.ts'),
|
||||
path.join(ROOT_DIR, 'ts', 'windows', 'debuglog', 'preload.ts'),
|
||||
path.join(ROOT_DIR, 'ts', 'windows', 'loading', 'preload.ts'),
|
||||
path.join(ROOT_DIR, 'ts', 'windows', 'permissions', 'preload.ts'),
|
||||
path.join(ROOT_DIR, 'ts', 'windows', 'screenShare', 'preload.ts'),
|
||||
path.join(ROOT_DIR, 'ts', 'windows', 'settings', 'preload.ts'),
|
||||
],
|
||||
format: 'cjs',
|
||||
outdir: 'bundles',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
Promise.all([main(), sandboxedEnv()]).catch(error => {
|
||||
console.error(error.stack);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
// About bundle
|
||||
esbuild.build({
|
||||
...bundleDefaults,
|
||||
mainFields: ['browser', 'main'],
|
||||
entryPoints: [path.join(ROOT_DIR, 'ts', 'windows', 'about', 'app.tsx')],
|
||||
outfile: path.join(ROOT_DIR, 'about.browser.bundle.js'),
|
||||
});
|
||||
|
||||
esbuild.build({
|
||||
...bundleDefaults,
|
||||
mainFields: ['browser', 'main'],
|
||||
entryPoints: [path.join(ROOT_DIR, 'ts', 'windows', 'about', 'preload.ts')],
|
||||
outfile: path.join(ROOT_DIR, 'about.preload.bundle.js'),
|
||||
});
|
||||
|
|
|
@ -29,10 +29,6 @@
|
|||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script
|
||||
type="application/javascript"
|
||||
src="ts/windows/applyTheme.js"
|
||||
></script>
|
||||
<script type="application/javascript" src="ts/windows/init.js"></script>
|
||||
<script type="module" src="bundles/settings/app.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
48
ts/OS.ts
48
ts/OS.ts
|
@ -1,48 +0,0 @@
|
|||
// Copyright 2018 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { release as osRelease } from 'os';
|
||||
import semver from 'semver';
|
||||
|
||||
const createIsPlatform = (
|
||||
platform: typeof process.platform
|
||||
): ((minVersion?: string) => boolean) => {
|
||||
return minVersion => {
|
||||
if (process.platform !== platform) {
|
||||
return false;
|
||||
}
|
||||
if (minVersion === undefined) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return semver.gte(osRelease(), minVersion);
|
||||
};
|
||||
};
|
||||
|
||||
export const isMacOS = createIsPlatform('darwin');
|
||||
export const isLinux = createIsPlatform('linux');
|
||||
export const isWindows = createIsPlatform('win32');
|
||||
|
||||
// Windows 10 and above
|
||||
export const hasCustomTitleBar = (): boolean =>
|
||||
isWindows('10.0.0') || Boolean(process.env.CUSTOM_TITLEBAR);
|
||||
|
||||
export const getName = (): string => {
|
||||
if (isMacOS()) {
|
||||
return 'macOS';
|
||||
}
|
||||
if (isWindows()) {
|
||||
return 'Windows';
|
||||
}
|
||||
return 'Linux';
|
||||
};
|
||||
|
||||
export const getClassName = (): string => {
|
||||
if (isMacOS()) {
|
||||
return 'os-macos';
|
||||
}
|
||||
if (isWindows()) {
|
||||
return 'os-windows';
|
||||
}
|
||||
return 'os-linux';
|
||||
};
|
|
@ -2,7 +2,7 @@
|
|||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { makeEnumParser } from '../util/enum';
|
||||
import * as OS from '../OS';
|
||||
import OS from '../util/os/osMain';
|
||||
|
||||
export enum AudioDeviceModule {
|
||||
Default = 'Default',
|
||||
|
|
|
@ -4,18 +4,18 @@
|
|||
import type { MouseEvent } from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import copyText from 'copy-text-to-clipboard';
|
||||
import type { ExecuteMenuRoleType } from './TitleBarContainer';
|
||||
import type { LocalizerType } from '../types/Util';
|
||||
import * as Errors from '../types/errors';
|
||||
import * as log from '../logging/log';
|
||||
import { Button, ButtonVariant } from './Button';
|
||||
import type { LocalizerType } from '../types/Util';
|
||||
import { Spinner } from './Spinner';
|
||||
import { TitleBarContainer } from './TitleBarContainer';
|
||||
import { ToastDebugLogError } from './ToastDebugLogError';
|
||||
import { ToastLinkCopied } from './ToastLinkCopied';
|
||||
import { TitleBarContainer } from './TitleBarContainer';
|
||||
import type { ExecuteMenuRoleType } from './TitleBarContainer';
|
||||
import { ToastLoadingFullLogs } from './ToastLoadingFullLogs';
|
||||
import { openLinkInWebBrowser } from '../util/openLinkInWebBrowser';
|
||||
import { createSupportUrl } from '../util/createSupportUrl';
|
||||
import * as Errors from '../types/errors';
|
||||
import { openLinkInWebBrowser } from '../util/openLinkInWebBrowser';
|
||||
import { useEscapeHandling } from '../hooks/useEscapeHandling';
|
||||
import { useTheme } from '../hooks/useTheme';
|
||||
|
||||
|
@ -137,7 +137,7 @@ export function DebugLogWindow({
|
|||
};
|
||||
|
||||
const supportURL = createSupportUrl({
|
||||
locale: i18n.getLocale(),
|
||||
locale: window.SignalContext.getI18nLocale(),
|
||||
query: {
|
||||
debugLog: publicLogURL,
|
||||
},
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
// Copyright 2021 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import type { AudioDevice } from '@signalapp/ringrtc';
|
||||
import type { ReactNode } from 'react';
|
||||
import React, { useEffect, useState, useCallback, useMemo } from 'react';
|
||||
import { noop } from 'lodash';
|
||||
import classNames from 'classnames';
|
||||
import type { AudioDevice } from '@signalapp/ringrtc';
|
||||
import uuid from 'uuid';
|
||||
|
||||
import type { MediaDeviceSettings } from '../types/Calling';
|
||||
|
@ -15,6 +15,19 @@ import type {
|
|||
ZoomFactorType,
|
||||
} from '../types/Storage.d';
|
||||
import type { ThemeSettingType } from '../types/StorageUIKeys';
|
||||
import type { ConversationType } from '../state/ducks/conversations';
|
||||
import type {
|
||||
ConversationColorType,
|
||||
CustomColorType,
|
||||
DefaultConversationColorType,
|
||||
} from '../types/Colors';
|
||||
import type {
|
||||
LocalizerType,
|
||||
SentMediaQualityType,
|
||||
ThemeType,
|
||||
} from '../types/Util';
|
||||
import type { ExecuteMenuRoleType } from './TitleBarContainer';
|
||||
|
||||
import { Button, ButtonVariant } from './Button';
|
||||
import { ChatColorPicker } from './ChatColorPicker';
|
||||
import { Checkbox } from './Checkbox';
|
||||
|
@ -23,24 +36,12 @@ import {
|
|||
Variant as CircleCheckboxVariant,
|
||||
} from './CircleCheckbox';
|
||||
import { ConfirmationDialog } from './ConfirmationDialog';
|
||||
import type { ConversationType } from '../state/ducks/conversations';
|
||||
import type {
|
||||
ConversationColorType,
|
||||
CustomColorType,
|
||||
DefaultConversationColorType,
|
||||
} from '../types/Colors';
|
||||
import { DisappearingTimeDialog } from './DisappearingTimeDialog';
|
||||
import type {
|
||||
LocalizerType,
|
||||
SentMediaQualityType,
|
||||
ThemeType,
|
||||
} from '../types/Util';
|
||||
import { PhoneNumberDiscoverability } from '../util/phoneNumberDiscoverability';
|
||||
import { PhoneNumberSharingMode } from '../util/phoneNumberSharingMode';
|
||||
import { Select } from './Select';
|
||||
import { Spinner } from './Spinner';
|
||||
import { TitleBarContainer } from './TitleBarContainer';
|
||||
import type { ExecuteMenuRoleType } from './TitleBarContainer';
|
||||
import { getCustomColorStyle } from '../util/getCustomColorStyle';
|
||||
import {
|
||||
DEFAULT_DURATIONS_IN_SECONDS,
|
||||
|
@ -179,6 +180,8 @@ type PropsFunctionType = {
|
|||
|
||||
export type PropsType = PropsDataType & PropsFunctionType;
|
||||
|
||||
export type PropsPreloadType = Omit<PropsType, 'i18n'>;
|
||||
|
||||
enum Page {
|
||||
// Accessible through left nav
|
||||
General = 'General',
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
// Copyright 2023 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { ipcRenderer } from 'electron';
|
||||
import { config } from './config';
|
||||
import { localeMessages } from './localeMessages';
|
||||
import { setupI18n } from '../util/setupI18n';
|
||||
import { strictAssert } from '../util/assert';
|
||||
|
||||
|
@ -16,7 +16,6 @@ strictAssert(
|
|||
'locale is not a string'
|
||||
);
|
||||
|
||||
const localeMessages = ipcRenderer.sendSync('locale-data');
|
||||
const i18n = setupI18n(resolvedTranslationsLocale, localeMessages);
|
||||
|
||||
export { i18n };
|
||||
|
|
6
ts/context/localeMessages.ts
Normal file
6
ts/context/localeMessages.ts
Normal file
|
@ -0,0 +1,6 @@
|
|||
// Copyright 2023 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { ipcRenderer } from 'electron';
|
||||
|
||||
export const localeMessages = ipcRenderer.sendSync('locale-data');
|
|
@ -1,8 +1,6 @@
|
|||
// Copyright 2021 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { z } from 'zod';
|
||||
|
||||
import { makeEnumParser } from './util/enum';
|
||||
|
||||
// Many places rely on this enum being a string.
|
||||
|
@ -13,8 +11,6 @@ export enum Environment {
|
|||
Test = 'test',
|
||||
}
|
||||
|
||||
export const environmentSchema = z.nativeEnum(Environment);
|
||||
|
||||
let environment: undefined | Environment;
|
||||
|
||||
export function getEnvironment(): Environment {
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { memoize, sortBy } from 'lodash';
|
||||
import os from 'os';
|
||||
import { ipcRenderer as ipc } from 'electron';
|
||||
import { reallyJsonStringify } from '../util/reallyJsonStringify';
|
||||
import type { FetchLogIpcData, LogEntryType } from './shared';
|
||||
import {
|
||||
|
@ -42,16 +40,18 @@ const getHeader = (
|
|||
user,
|
||||
}: Omit<FetchLogIpcData, 'logEntries'>,
|
||||
nodeVersion: string,
|
||||
appVersion: string
|
||||
appVersion: string,
|
||||
osVersion: string,
|
||||
userAgent: string
|
||||
): string =>
|
||||
[
|
||||
headerSection('System info', {
|
||||
Time: Date.now(),
|
||||
'User agent': window.navigator.userAgent,
|
||||
'User agent': userAgent,
|
||||
'Node version': nodeVersion,
|
||||
Environment: getEnvironment(),
|
||||
'App version': appVersion,
|
||||
'OS version': os.version(),
|
||||
'OS version': osVersion,
|
||||
}),
|
||||
headerSection('User info', user),
|
||||
headerSection('Capabilities', capabilities),
|
||||
|
@ -79,17 +79,18 @@ function formatLine(mightBeEntry: unknown): string {
|
|||
return `${getLevel(entry.level)} ${entry.time} ${entry.msg}`;
|
||||
}
|
||||
|
||||
export async function fetch(
|
||||
export function getLog(
|
||||
data: unknown,
|
||||
nodeVersion: string,
|
||||
appVersion: string
|
||||
): Promise<string> {
|
||||
const data: unknown = await ipc.invoke('fetch-log');
|
||||
|
||||
appVersion: string,
|
||||
osVersion: string,
|
||||
userAgent: string
|
||||
): string {
|
||||
let header: string;
|
||||
let body: string;
|
||||
if (isFetchLogIpcData(data)) {
|
||||
const { logEntries } = data;
|
||||
header = getHeader(data, nodeVersion, appVersion);
|
||||
header = getHeader(data, nodeVersion, appVersion, osVersion, userAgent);
|
||||
body = logEntries.map(formatLine).join('\n');
|
||||
} else {
|
||||
header = headerSectionTitle('Partial logs');
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright 2021 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { noop } from 'lodash';
|
||||
import noop from 'lodash/noop';
|
||||
import type { LogFunction } from '../types/Logging';
|
||||
import { LogLevel } from '../types/Logging';
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright 2020 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { throttle } from 'lodash';
|
||||
import { throttle } from '../util/throttle';
|
||||
|
||||
// Idle timer - you're active for ACTIVE_TIMEOUT after one of these events
|
||||
const ACTIVE_TIMEOUT = 15 * 1000;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
// Copyright 2015 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import os from 'os';
|
||||
import { debounce } from 'lodash';
|
||||
import EventEmitter from 'events';
|
||||
import { Sound } from '../util/Sound';
|
||||
|
@ -9,7 +10,7 @@ import {
|
|||
getAudioNotificationSupport,
|
||||
shouldHideExpiringMessageBody,
|
||||
} from '../types/Settings';
|
||||
import * as OS from '../OS';
|
||||
import OS from '../util/os/osMain';
|
||||
import * as log from '../logging/log';
|
||||
import { makeEnumParser } from '../util/enum';
|
||||
import { missingCaseError } from '../util/missingCaseError';
|
||||
|
@ -144,7 +145,7 @@ class NotificationService extends EventEmitter {
|
|||
|
||||
this.lastNotification?.close();
|
||||
|
||||
const audioNotificationSupport = getAudioNotificationSupport();
|
||||
const audioNotificationSupport = getAudioNotificationSupport(OS);
|
||||
|
||||
const notification = new window.Notification(title, {
|
||||
body: OS.isLinux() ? filterNotificationText(message) : message,
|
||||
|
@ -299,7 +300,10 @@ class NotificationService extends EventEmitter {
|
|||
notificationTitle = senderTitle;
|
||||
({ notificationIconUrl } = notificationData);
|
||||
|
||||
if (isExpiringMessage && shouldHideExpiringMessageBody()) {
|
||||
if (
|
||||
isExpiringMessage &&
|
||||
shouldHideExpiringMessageBody(OS, os.release())
|
||||
) {
|
||||
notificationMessage = i18n('icu:newMessage');
|
||||
} else if (userSetting === NotificationSetting.NameOnly) {
|
||||
if (reaction) {
|
||||
|
|
|
@ -8,7 +8,7 @@ import * as Curve from './Curve';
|
|||
import { start as conversationControllerStart } from './ConversationController';
|
||||
import Data from './sql/Client';
|
||||
import * as Groups from './groups';
|
||||
import * as OS from './OS';
|
||||
import OS from './util/os/osMain';
|
||||
import * as RemoteConfig from './RemoteConfig';
|
||||
|
||||
// Components
|
||||
|
|
|
@ -9,7 +9,7 @@ import type { LocalizerType } from '../../types/Util';
|
|||
import type { MenuOptionsType } from '../../types/menu';
|
||||
import type { NoopActionType } from './noop';
|
||||
import type { UUIDStringType } from '../../types/UUID';
|
||||
import * as OS from '../../OS';
|
||||
import OS from '../../util/os/osMain';
|
||||
import { ThemeType } from '../../types/Util';
|
||||
|
||||
// State
|
||||
|
@ -116,6 +116,7 @@ export function getEmptyState(): UserStateType {
|
|||
getLocale: intlNotSetup,
|
||||
getIntl: intlNotSetup,
|
||||
isLegacyFormat: intlNotSetup,
|
||||
getLocaleMessages: intlNotSetup,
|
||||
getLocaleDirection: intlNotSetup,
|
||||
}),
|
||||
interactionMode: 'mouse',
|
||||
|
|
|
@ -32,7 +32,7 @@ import type { MainWindowStatsType } from '../windows/context';
|
|||
import type { MenuOptionsType } from '../types/menu';
|
||||
import type { StoryDataType } from './ducks/stories';
|
||||
import type { StoryDistributionListDataType } from './ducks/storyDistributionLists';
|
||||
import * as OS from '../OS';
|
||||
import OS from '../util/os/osMain';
|
||||
import { UUIDKind } from '../types/UUID';
|
||||
import { getEmojiReducerState as emojis } from '../util/loadRecentEmojis';
|
||||
import { getInitialState as stickers } from '../types/Stickers';
|
||||
|
@ -132,7 +132,7 @@ export function getInitialState({
|
|||
interactionMode: getInteractionMode(),
|
||||
isMainWindowFullScreen: mainWindowStats.isFullScreen,
|
||||
isMainWindowMaximized: mainWindowStats.isMaximized,
|
||||
localeMessages: window.SignalContext.localeMessages,
|
||||
localeMessages: window.i18n.getLocaleMessages(),
|
||||
menuOptions,
|
||||
osName,
|
||||
ourACI,
|
||||
|
|
|
@ -7,7 +7,7 @@ import type { MenuItemConstructorOptions } from 'electron';
|
|||
|
||||
import type { MenuActionType } from '../../types/menu';
|
||||
import { App } from '../../components/App';
|
||||
import { getName as getOSName, getClassName as getOSClassName } from '../../OS';
|
||||
import OS from '../../util/os/osMain';
|
||||
import { SmartCallManager } from './CallManager';
|
||||
import { SmartGlobalModalContainer } from './GlobalModalContainer';
|
||||
import { SmartLightbox } from './Lightbox';
|
||||
|
@ -47,8 +47,8 @@ const mapStateToProps = (state: StateType) => {
|
|||
isFullScreen: getIsMainWindowFullScreen(state),
|
||||
menuOptions: getMenuOptions(state),
|
||||
hasCustomTitleBar: window.SignalContext.OS.hasCustomTitleBar(),
|
||||
OS: getOSName(),
|
||||
osClassName: getOSClassName(),
|
||||
OS: OS.getName(),
|
||||
osClassName: OS.getClassName(),
|
||||
hideMenuBar: getHideMenuBar(state),
|
||||
renderCallManager: () => (
|
||||
<ModalContainer className="module-calling__modal-container">
|
||||
|
|
|
@ -27,7 +27,7 @@ import { HTTPError } from '../../textsecure/Errors';
|
|||
import { isRecord } from '../../util/isRecord';
|
||||
import * as Errors from '../../types/errors';
|
||||
import { normalizeDeviceName } from '../../util/normalizeDeviceName';
|
||||
import { getName as getOSName } from '../../OS';
|
||||
import OS from '../../util/os/osMain';
|
||||
|
||||
type PropsType = ComponentProps<typeof InstallScreen>;
|
||||
|
||||
|
@ -258,7 +258,7 @@ export function SmartInstallScreen(): ReactElement {
|
|||
updates,
|
||||
currentVersion: window.getVersion(),
|
||||
startUpdate,
|
||||
OS: getOSName(),
|
||||
OS: OS.getName(),
|
||||
},
|
||||
};
|
||||
break;
|
||||
|
|
|
@ -8,7 +8,7 @@ import { UnsupportedOSDialog } from '../../components/UnsupportedOSDialog';
|
|||
import { getIntl } from '../selectors/user';
|
||||
import { getExpirationTimestamp } from '../selectors/expiration';
|
||||
import type { WidthBreakpoint } from '../../components/_util';
|
||||
import { getName as getOSName } from '../../OS';
|
||||
import OS from '../../util/os/osMain';
|
||||
|
||||
export type PropsType = Readonly<{
|
||||
type: 'warning' | 'error';
|
||||
|
@ -18,14 +18,14 @@ export type PropsType = Readonly<{
|
|||
export function SmartUnsupportedOSDialog(ownProps: PropsType): JSX.Element {
|
||||
const i18n = useSelector(getIntl);
|
||||
const expirationTimestamp = useSelector(getExpirationTimestamp);
|
||||
const OS = getOSName();
|
||||
const osName = OS.getName();
|
||||
|
||||
return (
|
||||
<UnsupportedOSDialog
|
||||
{...ownProps}
|
||||
i18n={i18n}
|
||||
expirationTimestamp={expirationTimestamp}
|
||||
OS={OS}
|
||||
OS={osName}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ import type { StateType } from '../reducer';
|
|||
import { getIntl } from '../selectors/user';
|
||||
import { getExpirationTimestamp } from '../selectors/expiration';
|
||||
import type { WidthBreakpoint } from '../../components/_util';
|
||||
import { getName as getOSName } from '../../OS';
|
||||
import OS from '../../util/os/osMain';
|
||||
|
||||
type PropsType = Readonly<{ containerWidthBreakpoint: WidthBreakpoint }>;
|
||||
|
||||
|
@ -18,7 +18,7 @@ const mapStateToProps = (state: StateType, ownProps: PropsType) => {
|
|||
i18n: getIntl(state),
|
||||
currentVersion: window.getVersion(),
|
||||
expirationTimestamp: getExpirationTimestamp(state),
|
||||
OS: getOSName(),
|
||||
OS: OS.getName(),
|
||||
...ownProps,
|
||||
};
|
||||
};
|
||||
|
|
|
@ -5,6 +5,7 @@ import os from 'os';
|
|||
import Sinon from 'sinon';
|
||||
import { assert } from 'chai';
|
||||
|
||||
import { getOSFunctions } from '../../util/os/shared';
|
||||
import * as Settings from '../../types/Settings';
|
||||
|
||||
describe('Settings', () => {
|
||||
|
@ -21,8 +22,9 @@ describe('Settings', () => {
|
|||
describe('getAudioNotificationSupport', () => {
|
||||
it('returns native support on macOS', () => {
|
||||
sandbox.stub(process, 'platform').value('darwin');
|
||||
const OS = getOSFunctions(os.release());
|
||||
assert.strictEqual(
|
||||
Settings.getAudioNotificationSupport(),
|
||||
Settings.getAudioNotificationSupport(OS),
|
||||
Settings.AudioNotificationSupport.Native
|
||||
);
|
||||
});
|
||||
|
@ -30,8 +32,9 @@ describe('Settings', () => {
|
|||
it('returns no support on Windows 7', () => {
|
||||
sandbox.stub(process, 'platform').value('win32');
|
||||
sandbox.stub(os, 'release').returns('7.0.0');
|
||||
const OS = getOSFunctions(os.release());
|
||||
assert.strictEqual(
|
||||
Settings.getAudioNotificationSupport(),
|
||||
Settings.getAudioNotificationSupport(OS),
|
||||
Settings.AudioNotificationSupport.None
|
||||
);
|
||||
});
|
||||
|
@ -39,16 +42,18 @@ describe('Settings', () => {
|
|||
it('returns native support on Windows 8', () => {
|
||||
sandbox.stub(process, 'platform').value('win32');
|
||||
sandbox.stub(os, 'release').returns('8.0.0');
|
||||
const OS = getOSFunctions(os.release());
|
||||
assert.strictEqual(
|
||||
Settings.getAudioNotificationSupport(),
|
||||
Settings.getAudioNotificationSupport(OS),
|
||||
Settings.AudioNotificationSupport.Native
|
||||
);
|
||||
});
|
||||
|
||||
it('returns custom support on Linux', () => {
|
||||
sandbox.stub(process, 'platform').value('linux');
|
||||
const OS = getOSFunctions(os.release());
|
||||
assert.strictEqual(
|
||||
Settings.getAudioNotificationSupport(),
|
||||
Settings.getAudioNotificationSupport(OS),
|
||||
Settings.AudioNotificationSupport.Custom
|
||||
);
|
||||
});
|
||||
|
@ -57,48 +62,56 @@ describe('Settings', () => {
|
|||
describe('isAudioNotificationSupported', () => {
|
||||
it('returns true on macOS', () => {
|
||||
sandbox.stub(process, 'platform').value('darwin');
|
||||
assert.isTrue(Settings.isAudioNotificationSupported());
|
||||
const OS = getOSFunctions(os.release());
|
||||
assert.isTrue(Settings.isAudioNotificationSupported(OS));
|
||||
});
|
||||
|
||||
it('returns false on Windows 7', () => {
|
||||
sandbox.stub(process, 'platform').value('win32');
|
||||
sandbox.stub(os, 'release').returns('7.0.0');
|
||||
assert.isFalse(Settings.isAudioNotificationSupported());
|
||||
const OS = getOSFunctions(os.release());
|
||||
assert.isFalse(Settings.isAudioNotificationSupported(OS));
|
||||
});
|
||||
|
||||
it('returns true on Windows 8', () => {
|
||||
sandbox.stub(process, 'platform').value('win32');
|
||||
sandbox.stub(os, 'release').returns('8.0.0');
|
||||
assert.isTrue(Settings.isAudioNotificationSupported());
|
||||
const OS = getOSFunctions(os.release());
|
||||
assert.isTrue(Settings.isAudioNotificationSupported(OS));
|
||||
});
|
||||
|
||||
it('returns true on Linux', () => {
|
||||
sandbox.stub(process, 'platform').value('linux');
|
||||
assert.isTrue(Settings.isAudioNotificationSupported());
|
||||
const OS = getOSFunctions(os.release());
|
||||
assert.isTrue(Settings.isAudioNotificationSupported(OS));
|
||||
});
|
||||
});
|
||||
|
||||
describe('isNotificationGroupingSupported', () => {
|
||||
it('returns true on macOS', () => {
|
||||
sandbox.stub(process, 'platform').value('darwin');
|
||||
assert.isTrue(Settings.isNotificationGroupingSupported());
|
||||
const OS = getOSFunctions(os.release());
|
||||
assert.isTrue(Settings.isNotificationGroupingSupported(OS));
|
||||
});
|
||||
|
||||
it('returns true on Windows 7', () => {
|
||||
sandbox.stub(process, 'platform').value('win32');
|
||||
sandbox.stub(os, 'release').returns('7.0.0');
|
||||
assert.isFalse(Settings.isNotificationGroupingSupported());
|
||||
const OS = getOSFunctions(os.release());
|
||||
assert.isFalse(Settings.isNotificationGroupingSupported(OS));
|
||||
});
|
||||
|
||||
it('returns true on Windows 8', () => {
|
||||
sandbox.stub(process, 'platform').value('win32');
|
||||
sandbox.stub(os, 'release').returns('8.0.0');
|
||||
assert.isTrue(Settings.isNotificationGroupingSupported());
|
||||
const OS = getOSFunctions(os.release());
|
||||
assert.isTrue(Settings.isNotificationGroupingSupported(OS));
|
||||
});
|
||||
|
||||
it('returns true on Linux', () => {
|
||||
sandbox.stub(process, 'platform').value('linux');
|
||||
assert.isTrue(Settings.isNotificationGroupingSupported());
|
||||
const OS = getOSFunctions(os.release());
|
||||
assert.isTrue(Settings.isNotificationGroupingSupported(OS));
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -106,88 +119,103 @@ describe('Settings', () => {
|
|||
it('returns true on Windows', () => {
|
||||
sandbox.stub(process, 'platform').value('win32');
|
||||
sandbox.stub(os, 'release').returns('8.0.0');
|
||||
assert.isTrue(Settings.isAutoLaunchSupported());
|
||||
const OS = getOSFunctions(os.release());
|
||||
assert.isTrue(Settings.isAutoLaunchSupported(OS));
|
||||
});
|
||||
|
||||
it('returns true on macOS', () => {
|
||||
sandbox.stub(process, 'platform').value('darwin');
|
||||
assert.isTrue(Settings.isAutoLaunchSupported());
|
||||
const OS = getOSFunctions(os.release());
|
||||
assert.isTrue(Settings.isAutoLaunchSupported(OS));
|
||||
});
|
||||
|
||||
it('returns false on Linux', () => {
|
||||
sandbox.stub(process, 'platform').value('linux');
|
||||
assert.isFalse(Settings.isAutoLaunchSupported());
|
||||
const OS = getOSFunctions(os.release());
|
||||
assert.isFalse(Settings.isAutoLaunchSupported(OS));
|
||||
});
|
||||
});
|
||||
|
||||
describe('isHideMenuBarSupported', () => {
|
||||
it('returns false on macOS', () => {
|
||||
sandbox.stub(process, 'platform').value('darwin');
|
||||
assert.isFalse(Settings.isHideMenuBarSupported());
|
||||
const OS = getOSFunctions(os.release());
|
||||
assert.isFalse(Settings.isHideMenuBarSupported(OS));
|
||||
});
|
||||
|
||||
it('returns true on Windows 7', () => {
|
||||
sandbox.stub(process, 'platform').value('win32');
|
||||
sandbox.stub(os, 'release').returns('7.0.0');
|
||||
assert.isTrue(Settings.isHideMenuBarSupported());
|
||||
const OS = getOSFunctions(os.release());
|
||||
assert.isTrue(Settings.isHideMenuBarSupported(OS));
|
||||
});
|
||||
|
||||
it('returns true on Windows 8', () => {
|
||||
sandbox.stub(process, 'platform').value('win32');
|
||||
sandbox.stub(os, 'release').returns('8.0.0');
|
||||
assert.isTrue(Settings.isHideMenuBarSupported());
|
||||
const OS = getOSFunctions(os.release());
|
||||
assert.isTrue(Settings.isHideMenuBarSupported(OS));
|
||||
});
|
||||
|
||||
it('returns true on Linux', () => {
|
||||
sandbox.stub(process, 'platform').value('linux');
|
||||
assert.isTrue(Settings.isHideMenuBarSupported());
|
||||
const OS = getOSFunctions(os.release());
|
||||
assert.isTrue(Settings.isHideMenuBarSupported(OS));
|
||||
});
|
||||
});
|
||||
|
||||
describe('isDrawAttentionSupported', () => {
|
||||
it('returns false on macOS', () => {
|
||||
sandbox.stub(process, 'platform').value('darwin');
|
||||
assert.isFalse(Settings.isDrawAttentionSupported());
|
||||
const OS = getOSFunctions(os.release());
|
||||
assert.isFalse(Settings.isDrawAttentionSupported(OS));
|
||||
});
|
||||
|
||||
it('returns true on Windows 7', () => {
|
||||
sandbox.stub(process, 'platform').value('win32');
|
||||
sandbox.stub(os, 'release').returns('7.0.0');
|
||||
assert.isTrue(Settings.isDrawAttentionSupported());
|
||||
const OS = getOSFunctions(os.release());
|
||||
assert.isTrue(Settings.isDrawAttentionSupported(OS));
|
||||
});
|
||||
|
||||
it('returns true on Windows 8', () => {
|
||||
sandbox.stub(process, 'platform').value('win32');
|
||||
sandbox.stub(os, 'release').returns('8.0.0');
|
||||
assert.isTrue(Settings.isDrawAttentionSupported());
|
||||
const OS = getOSFunctions(os.release());
|
||||
assert.isTrue(Settings.isDrawAttentionSupported(OS));
|
||||
});
|
||||
|
||||
it('returns true on Linux', () => {
|
||||
sandbox.stub(process, 'platform').value('linux');
|
||||
assert.isTrue(Settings.isDrawAttentionSupported());
|
||||
const OS = getOSFunctions(os.release());
|
||||
assert.isTrue(Settings.isDrawAttentionSupported(OS));
|
||||
});
|
||||
});
|
||||
|
||||
describe('isSystemTraySupported', () => {
|
||||
it('returns false on macOS', () => {
|
||||
sandbox.stub(process, 'platform').value('darwin');
|
||||
assert.isFalse(Settings.isSystemTraySupported('1.2.3'));
|
||||
const OS = getOSFunctions(os.release());
|
||||
assert.isFalse(Settings.isSystemTraySupported(OS, '1.2.3'));
|
||||
});
|
||||
|
||||
it('returns true on Windows 8', () => {
|
||||
sandbox.stub(process, 'platform').value('win32');
|
||||
sandbox.stub(os, 'release').returns('8.0.0');
|
||||
assert.isTrue(Settings.isSystemTraySupported('1.2.3'));
|
||||
const OS = getOSFunctions(os.release());
|
||||
assert.isTrue(Settings.isSystemTraySupported(OS, '1.2.3'));
|
||||
});
|
||||
|
||||
it('returns false on Linux production', () => {
|
||||
sandbox.stub(process, 'platform').value('linux');
|
||||
assert.isFalse(Settings.isSystemTraySupported('1.2.3'));
|
||||
const OS = getOSFunctions(os.release());
|
||||
assert.isFalse(Settings.isSystemTraySupported(OS, '1.2.3'));
|
||||
});
|
||||
|
||||
it('returns true on Linux beta', () => {
|
||||
sandbox.stub(process, 'platform').value('linux');
|
||||
assert.isTrue(Settings.isSystemTraySupported('1.2.3-beta.4'));
|
||||
const OS = getOSFunctions(os.release());
|
||||
assert.isTrue(Settings.isSystemTraySupported(OS, '1.2.3-beta.4'));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -715,7 +715,7 @@ export default class MessageSender {
|
|||
storyMessage.fileAttachment = fileAttachment;
|
||||
} catch (error) {
|
||||
if (error instanceof HTTPError) {
|
||||
throw new MessageError(message, error);
|
||||
throw new MessageError(storyMessage, error);
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
|
|
|
@ -3,8 +3,10 @@
|
|||
|
||||
import { z } from 'zod';
|
||||
|
||||
import { Environment } from '../environment';
|
||||
import { themeSettingSchema } from './StorageUIKeys';
|
||||
import { environmentSchema } from '../environment';
|
||||
|
||||
const environmentSchema = z.nativeEnum(Environment);
|
||||
|
||||
const configRequiredStringSchema = z.string().nonempty();
|
||||
export type ConfigRequiredStringType = z.infer<
|
||||
|
@ -39,6 +41,8 @@ export const rendererConfigSchema = z.object({
|
|||
environment: environmentSchema,
|
||||
homePath: configRequiredStringSchema,
|
||||
hostname: configRequiredStringSchema,
|
||||
osRelease: configRequiredStringSchema,
|
||||
osVersion: configRequiredStringSchema,
|
||||
resolvedTranslationsLocale: configRequiredStringSchema,
|
||||
resolvedTranslationsLocaleDirection: z.enum(['ltr', 'rtl']),
|
||||
preferredSystemLocales: z.array(configRequiredStringSchema),
|
||||
|
|
|
@ -2,9 +2,8 @@
|
|||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import semver from 'semver';
|
||||
import os from 'os';
|
||||
|
||||
import * as OS from '../OS';
|
||||
import type { OSType } from '../util/os/shared';
|
||||
import { isProduction } from '../util/version';
|
||||
|
||||
const MIN_WINDOWS_VERSION = '8.0.0';
|
||||
|
@ -15,7 +14,9 @@ export enum AudioNotificationSupport {
|
|||
Custom,
|
||||
}
|
||||
|
||||
export function getAudioNotificationSupport(): AudioNotificationSupport {
|
||||
export function getAudioNotificationSupport(
|
||||
OS: OSType
|
||||
): AudioNotificationSupport {
|
||||
if (OS.isWindows(MIN_WINDOWS_VERSION) || OS.isMacOS()) {
|
||||
return AudioNotificationSupport.Native;
|
||||
}
|
||||
|
@ -25,42 +26,48 @@ export function getAudioNotificationSupport(): AudioNotificationSupport {
|
|||
return AudioNotificationSupport.None;
|
||||
}
|
||||
|
||||
export const isAudioNotificationSupported = (): boolean =>
|
||||
getAudioNotificationSupport() !== AudioNotificationSupport.None;
|
||||
export const isAudioNotificationSupported = (OS: OSType): boolean =>
|
||||
getAudioNotificationSupport(OS) !== AudioNotificationSupport.None;
|
||||
|
||||
// Using `Notification::tag` has a bug on Windows 7:
|
||||
// https://github.com/electron/electron/issues/11189
|
||||
export const isNotificationGroupingSupported = (): boolean =>
|
||||
export const isNotificationGroupingSupported = (OS: OSType): boolean =>
|
||||
!OS.isWindows() || OS.isWindows(MIN_WINDOWS_VERSION);
|
||||
|
||||
// Login item settings are only supported on macOS and Windows, according to [Electron's
|
||||
// docs][0].
|
||||
// [0]: https://www.electronjs.org/docs/api/app#appsetloginitemsettingssettings-macos-windows
|
||||
export const isAutoLaunchSupported = (): boolean =>
|
||||
export const isAutoLaunchSupported = (OS: OSType): boolean =>
|
||||
OS.isWindows() || OS.isMacOS();
|
||||
|
||||
// the "hide menu bar" option is specific to Windows and Linux
|
||||
export const isHideMenuBarSupported = (): boolean => !OS.isMacOS();
|
||||
export const isHideMenuBarSupported = (OS: OSType): boolean => !OS.isMacOS();
|
||||
|
||||
// the "draw attention on notification" option is specific to Windows and Linux
|
||||
export const isDrawAttentionSupported = (): boolean => !OS.isMacOS();
|
||||
export const isDrawAttentionSupported = (OS: OSType): boolean => !OS.isMacOS();
|
||||
|
||||
/**
|
||||
* Returns `true` if you can minimize the app to the system tray. Users can override this
|
||||
* option with a command line flag, but that is not officially supported.
|
||||
*/
|
||||
export const isSystemTraySupported = (appVersion: string): boolean =>
|
||||
export const isSystemTraySupported = (
|
||||
OS: OSType,
|
||||
appVersion: string
|
||||
): boolean =>
|
||||
// We eventually want to support Linux in production.
|
||||
OS.isWindows() || (OS.isLinux() && !isProduction(appVersion));
|
||||
|
||||
// On Windows minimize and start in system tray is default when app is selected
|
||||
// to launch at login, because we can provide `['--start-in-tray']` args.
|
||||
export const isMinimizeToAndStartInSystemTraySupported = (
|
||||
OS: OSType,
|
||||
appVersion: string
|
||||
): boolean => !OS.isWindows() && isSystemTraySupported(appVersion);
|
||||
): boolean => !OS.isWindows() && isSystemTraySupported(OS, appVersion);
|
||||
|
||||
export const isAutoDownloadUpdatesSupported = (): boolean =>
|
||||
export const isAutoDownloadUpdatesSupported = (OS: OSType): boolean =>
|
||||
OS.isWindows() || OS.isMacOS();
|
||||
|
||||
export const shouldHideExpiringMessageBody = (): boolean =>
|
||||
OS.isWindows() || (OS.isMacOS() && semver.lt(os.release(), '21.1.0'));
|
||||
export const shouldHideExpiringMessageBody = (
|
||||
OS: OSType,
|
||||
release: string
|
||||
): boolean => OS.isWindows() || (OS.isMacOS() && semver.lt(release, '21.1.0'));
|
||||
|
|
|
@ -5,6 +5,8 @@ import type { IntlShape } from 'react-intl';
|
|||
import type { UUIDStringType } from './UUID';
|
||||
import type { LocaleDirection } from '../../app/locale';
|
||||
|
||||
import type { LocaleMessagesType } from './I18N';
|
||||
|
||||
export type StoryContextType = {
|
||||
authorUuid?: UUIDStringType;
|
||||
timestamp: number;
|
||||
|
@ -24,6 +26,7 @@ export type LocalizerType = {
|
|||
getIntl(): IntlShape;
|
||||
isLegacyFormat(key: string): boolean;
|
||||
getLocale(): string;
|
||||
getLocaleMessages(): LocaleMessagesType;
|
||||
getLocaleDirection(): LocaleDirection;
|
||||
};
|
||||
|
||||
|
|
|
@ -1,20 +1,18 @@
|
|||
// Copyright 2018 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { get, has } from 'lodash';
|
||||
|
||||
export function toLogFormat(error: unknown): string {
|
||||
let result = '';
|
||||
if (error instanceof Error && error.stack) {
|
||||
result = error.stack;
|
||||
} else if (has(error, 'message')) {
|
||||
result = get(error, 'message');
|
||||
} else if (error && typeof error === 'object' && 'message' in error) {
|
||||
result = String(error.message);
|
||||
} else {
|
||||
result = String(error);
|
||||
}
|
||||
|
||||
if (has(error, 'cause')) {
|
||||
result += `\nCaused by: ${String(get(error, 'cause'))}`;
|
||||
if (error && typeof error === 'object' && 'cause' in error) {
|
||||
result += `\nCaused by: ${String(error.cause)}`;
|
||||
}
|
||||
|
||||
return result;
|
||||
|
|
|
@ -2525,7 +2525,7 @@
|
|||
{
|
||||
"rule": "DOM-innerHTML",
|
||||
"path": "ts/windows/loading/start.ts",
|
||||
"line": " message.innerHTML = window.SignalContext.i18n('icu:optimizingApplication');",
|
||||
"line": " message.innerHTML = window.i18n('icu:optimizingApplication');",
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2021-09-17T21:02:59.414Z"
|
||||
},
|
||||
|
|
|
@ -24,8 +24,7 @@ const excludedFilesRegexp = RegExp(
|
|||
[
|
||||
'^release/',
|
||||
'^preload.bundle.js(LICENSE.txt|map)?',
|
||||
'^about.browser.bundle.js(LICENSE.txt|map)?',
|
||||
'^about.preload.bundle.js(LICENSE.txt|map)?',
|
||||
'^bundles/',
|
||||
'^storybook-static/',
|
||||
|
||||
// Non-distributed files
|
||||
|
|
9
ts/util/os/osMain.ts
Normal file
9
ts/util/os/osMain.ts
Normal file
|
@ -0,0 +1,9 @@
|
|||
// Copyright 2023 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import os from 'os';
|
||||
import { getOSFunctions } from './shared';
|
||||
|
||||
const OS = getOSFunctions(os.release());
|
||||
|
||||
export default OS;
|
9
ts/util/os/osPreload.ts
Normal file
9
ts/util/os/osPreload.ts
Normal file
|
@ -0,0 +1,9 @@
|
|||
// Copyright 2023 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { config } from '../../context/config';
|
||||
import { getOSFunctions } from './shared';
|
||||
|
||||
const OS = getOSFunctions(config.osRelease);
|
||||
|
||||
export default OS;
|
68
ts/util/os/shared.ts
Normal file
68
ts/util/os/shared.ts
Normal file
|
@ -0,0 +1,68 @@
|
|||
// Copyright 2018 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import semver from 'semver';
|
||||
|
||||
function createIsPlatform(
|
||||
platform: typeof process.platform,
|
||||
osRelease: string
|
||||
): (minVersion?: string) => boolean {
|
||||
return minVersion => {
|
||||
if (process.platform !== platform) {
|
||||
return false;
|
||||
}
|
||||
if (minVersion === undefined) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return semver.gte(osRelease, minVersion);
|
||||
};
|
||||
}
|
||||
|
||||
export type OSType = {
|
||||
getClassName: () => string;
|
||||
getName: () => string;
|
||||
hasCustomTitleBar: () => boolean;
|
||||
isLinux: (minVersion?: string) => boolean;
|
||||
isMacOS: (minVersion?: string) => boolean;
|
||||
isWindows: (minVersion?: string) => boolean;
|
||||
};
|
||||
|
||||
export function getOSFunctions(osRelease: string): OSType {
|
||||
const isMacOS = createIsPlatform('darwin', osRelease);
|
||||
const isLinux = createIsPlatform('linux', osRelease);
|
||||
const isWindows = createIsPlatform('win32', osRelease);
|
||||
|
||||
// Windows 10 and above
|
||||
const hasCustomTitleBar = (): boolean =>
|
||||
isWindows('10.0.0') || Boolean(process.env.CUSTOM_TITLEBAR);
|
||||
|
||||
const getName = (): string => {
|
||||
if (isMacOS()) {
|
||||
return 'macOS';
|
||||
}
|
||||
if (isWindows()) {
|
||||
return 'Windows';
|
||||
}
|
||||
return 'Linux';
|
||||
};
|
||||
|
||||
const getClassName = (): string => {
|
||||
if (isMacOS()) {
|
||||
return 'os-macos';
|
||||
}
|
||||
if (isWindows()) {
|
||||
return 'os-windows';
|
||||
}
|
||||
return 'os-linux';
|
||||
};
|
||||
|
||||
return {
|
||||
getClassName,
|
||||
getName,
|
||||
hasCustomTitleBar,
|
||||
isLinux,
|
||||
isMacOS,
|
||||
isWindows,
|
||||
};
|
||||
}
|
|
@ -172,6 +172,7 @@ export function setupI18n(
|
|||
return legacyMessages[key] != null;
|
||||
};
|
||||
getMessage.getLocale = () => locale;
|
||||
getMessage.getLocaleMessages = () => messages;
|
||||
getMessage.getLocaleDirection = () => {
|
||||
return window.getResolvedMessagesLocaleDirection();
|
||||
};
|
||||
|
|
70
ts/util/throttle.ts
Normal file
70
ts/util/throttle.ts
Normal file
|
@ -0,0 +1,70 @@
|
|||
// Copyright 2023 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
export function throttle(func: Function, wait: number): () => void {
|
||||
let lastCallTime: number;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
let lastArgs: Array<any> | undefined;
|
||||
let timerId: NodeJS.Timeout | undefined;
|
||||
|
||||
function call() {
|
||||
const args = lastArgs || [];
|
||||
lastArgs = undefined;
|
||||
func(...args);
|
||||
}
|
||||
|
||||
function leading() {
|
||||
timerId = setTimeout(timerExpired, wait);
|
||||
call();
|
||||
}
|
||||
|
||||
function remainingWait(time: number) {
|
||||
const timeSinceLastCall = time - lastCallTime;
|
||||
return wait - timeSinceLastCall;
|
||||
}
|
||||
|
||||
function shouldInvoke(time: number) {
|
||||
const timeSinceLastCall = time - lastCallTime;
|
||||
|
||||
return (
|
||||
lastCallTime === undefined ||
|
||||
timeSinceLastCall >= wait ||
|
||||
timeSinceLastCall < 0
|
||||
);
|
||||
}
|
||||
|
||||
function timerExpired() {
|
||||
const time = Date.now();
|
||||
if (shouldInvoke(time)) {
|
||||
return trailing();
|
||||
}
|
||||
timerId = setTimeout(timerExpired, remainingWait(time));
|
||||
}
|
||||
|
||||
function trailing() {
|
||||
timerId = undefined;
|
||||
|
||||
if (lastArgs) {
|
||||
return call();
|
||||
}
|
||||
lastArgs = undefined;
|
||||
}
|
||||
|
||||
return (...args) => {
|
||||
const time = Date.now();
|
||||
const isInvoking = shouldInvoke(time);
|
||||
|
||||
lastArgs = args;
|
||||
lastCallTime = time;
|
||||
|
||||
if (isInvoking) {
|
||||
if (timerId === undefined) {
|
||||
return leading();
|
||||
}
|
||||
}
|
||||
if (timerId === undefined) {
|
||||
timerId = setTimeout(timerExpired, wait);
|
||||
}
|
||||
};
|
||||
}
|
|
@ -2,7 +2,6 @@
|
|||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import * as semver from 'semver';
|
||||
import moment from 'moment';
|
||||
|
||||
export const isProduction = (version: string): boolean => {
|
||||
const parsed = semver.parse(version);
|
||||
|
@ -34,7 +33,22 @@ export const generateAlphaVersion = (options: {
|
|||
throw new Error(`generateAlphaVersion: Invalid version ${currentVersion}`);
|
||||
}
|
||||
|
||||
const formattedDate = moment().utc().format('YYYYMMDD.HH');
|
||||
const dateTimeParts = new Intl.DateTimeFormat('en', {
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
hourCycle: 'h23',
|
||||
month: '2-digit',
|
||||
timeZone: 'GMT',
|
||||
year: 'numeric',
|
||||
}).formatToParts(new Date());
|
||||
const dateTimeMap = new Map();
|
||||
dateTimeParts.forEach(({ type, value }) => {
|
||||
dateTimeMap.set(type, value);
|
||||
});
|
||||
const formattedDate = `${dateTimeMap.get('year')}${dateTimeMap.get(
|
||||
'month'
|
||||
)}${dateTimeMap.get('day')}.${dateTimeMap.get('hour')}`;
|
||||
|
||||
const formattedVersion = `${parsed.major}.${parsed.minor}.${parsed.patch}`;
|
||||
|
||||
return `${formattedVersion}-alpha.${formattedDate}-${shortSha}`;
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import * as fs from 'fs';
|
||||
import { isWindows } from '../OS';
|
||||
import OS from './os/osMain';
|
||||
|
||||
const ZONE_IDENTIFIER_CONTENTS = Buffer.from('[ZoneTransfer]\r\nZoneId=3');
|
||||
|
||||
|
@ -27,7 +27,7 @@ const ZONE_IDENTIFIER_CONTENTS = Buffer.from('[ZoneTransfer]\r\nZoneId=3');
|
|||
export async function writeWindowsZoneIdentifier(
|
||||
filePath: string
|
||||
): Promise<void> {
|
||||
if (!isWindows()) {
|
||||
if (!OS.isWindows()) {
|
||||
throw new Error('writeWindowsZoneIdentifier should only run on Windows');
|
||||
}
|
||||
|
||||
|
|
44
ts/window.d.ts
vendored
44
ts/window.d.ts
vendored
|
@ -3,7 +3,6 @@
|
|||
|
||||
// Captures the globals put in place by preload.js, background.js and others
|
||||
|
||||
import type { MenuItemConstructorOptions } from 'electron';
|
||||
import type { Store } from 'redux';
|
||||
import type * as Backbone from 'backbone';
|
||||
import type PQueue from 'p-queue/dist';
|
||||
|
@ -28,7 +27,7 @@ import type * as Groups from './groups';
|
|||
import type * as Crypto from './Crypto';
|
||||
import type * as Curve from './Curve';
|
||||
import type * as RemoteConfig from './RemoteConfig';
|
||||
import type * as OS from './OS';
|
||||
import type { OSType } from './util/os/shared';
|
||||
import type { getEnvironment } from './environment';
|
||||
import type { LocalizerType, ThemeType } from './types/Util';
|
||||
import type { Receipt } from './types/Receipt';
|
||||
|
@ -56,6 +55,7 @@ import type { SignalContextType } from './windows/context';
|
|||
import type * as Message2 from './types/Message2';
|
||||
import type { initializeMigrations } from './signal';
|
||||
import type { RetryPlaceholders } from './util/retryPlaceholders';
|
||||
import type { PropsPreloadType as PreferencesPropsType } from './components/Preferences';
|
||||
import type { LocaleDirection } from '../app/locale';
|
||||
|
||||
export { Long } from 'long';
|
||||
|
@ -103,21 +103,46 @@ export type FeatureFlagType = {
|
|||
GV2_MIGRATION_DISABLE_INVITE: boolean;
|
||||
};
|
||||
|
||||
type AboutWindowType = {
|
||||
type AboutWindowPropsType = {
|
||||
arch: string;
|
||||
environmentText: string;
|
||||
executeMenuRole: (role: MenuItemConstructorOptions['role']) => Promise<void>;
|
||||
hasCustomTitleBar: boolean;
|
||||
i18n: LocalizerType;
|
||||
version: string;
|
||||
platform: string;
|
||||
};
|
||||
|
||||
type DebugLogWindowPropsType = {
|
||||
downloadLog: (text: string) => unknown;
|
||||
fetchLogs: () => Promise<string>;
|
||||
uploadLogs: (text: string) => Promise<string>;
|
||||
};
|
||||
|
||||
type PermissionsWindowPropsType = {
|
||||
forCamera: boolean;
|
||||
forCalling: boolean;
|
||||
onAccept: () => void;
|
||||
onClose: () => void;
|
||||
};
|
||||
|
||||
type ScreenShareWindowPropsType = {
|
||||
onStopSharing: () => void;
|
||||
presentedSourceName: string;
|
||||
};
|
||||
|
||||
type SettingsOnRenderCallbackType = (props: PreferencesPropsType) => void;
|
||||
|
||||
type SettingsWindowPropsType = {
|
||||
onRender: (callback: SettingsOnRenderCallbackType) => void;
|
||||
};
|
||||
|
||||
export type SignalCoreType = {
|
||||
AboutWindow?: AboutWindowType;
|
||||
AboutWindowProps?: AboutWindowPropsType;
|
||||
Crypto: typeof Crypto;
|
||||
Curve: typeof Curve;
|
||||
Data: typeof Data;
|
||||
DebugLogWindowProps?: DebugLogWindowPropsType;
|
||||
Groups: typeof Groups;
|
||||
PermissionsWindowProps?: PermissionsWindowPropsType;
|
||||
RemoteConfig: typeof RemoteConfig;
|
||||
ScreenShareWindowProps?: ScreenShareWindowPropsType;
|
||||
Services: {
|
||||
calling: CallingClass;
|
||||
initializeGroupCredentialFetcher: () => Promise<void>;
|
||||
|
@ -127,6 +152,7 @@ export type SignalCoreType = {
|
|||
lightSessionResetQueue?: PQueue;
|
||||
storage: typeof StorageService;
|
||||
};
|
||||
SettingsWindowProps?: SettingsWindowPropsType;
|
||||
Migrations: ReturnType<typeof initializeMigrations>;
|
||||
Types: {
|
||||
Message: typeof Message2;
|
||||
|
@ -137,7 +163,7 @@ export type SignalCoreType = {
|
|||
Components: {
|
||||
ConfirmationDialog: typeof ConfirmationDialog;
|
||||
};
|
||||
OS: typeof OS;
|
||||
OS: OSType;
|
||||
State: {
|
||||
createStore: typeof createStore;
|
||||
Roots: {
|
||||
|
|
|
@ -5,20 +5,32 @@ import React from 'react';
|
|||
import ReactDOM from 'react-dom';
|
||||
|
||||
import { About } from '../../components/About';
|
||||
import { i18n } from '../sandboxedInit';
|
||||
import { strictAssert } from '../../util/assert';
|
||||
|
||||
const { AboutWindow } = window.Signal;
|
||||
const { AboutWindowProps } = window.Signal;
|
||||
|
||||
strictAssert(AboutWindow, 'window values not provided');
|
||||
strictAssert(AboutWindowProps, 'window values not provided');
|
||||
|
||||
let platform = '';
|
||||
if (AboutWindowProps.platform === 'darwin') {
|
||||
if (AboutWindowProps.arch === 'arm64') {
|
||||
platform = ` (${window.i18n('icu:appleSilicon')})`;
|
||||
} else {
|
||||
platform = ' (Intel)';
|
||||
}
|
||||
}
|
||||
|
||||
const environmentText = `${AboutWindowProps.environmentText}${platform}`;
|
||||
|
||||
ReactDOM.render(
|
||||
<About
|
||||
closeAbout={() => AboutWindow.executeMenuRole('close')}
|
||||
environment={AboutWindow.environmentText}
|
||||
executeMenuRole={AboutWindow.executeMenuRole}
|
||||
hasCustomTitleBar={AboutWindow.hasCustomTitleBar}
|
||||
i18n={AboutWindow.i18n}
|
||||
version={AboutWindow.version}
|
||||
closeAbout={() => window.SignalContext.executeMenuRole('close')}
|
||||
environment={environmentText}
|
||||
executeMenuRole={window.SignalContext.executeMenuRole}
|
||||
hasCustomTitleBar={window.SignalContext.OS.hasCustomTitleBar()}
|
||||
i18n={i18n}
|
||||
version={window.SignalContext.getVersion()}
|
||||
/>,
|
||||
document.getElementById('app')
|
||||
);
|
||||
|
|
|
@ -1,22 +1,10 @@
|
|||
// Copyright 2018 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import type { MenuItemConstructorOptions } from 'electron';
|
||||
import { contextBridge, ipcRenderer } from 'electron';
|
||||
import { activeWindowService } from '../../context/activeWindowService';
|
||||
import { contextBridge } from 'electron';
|
||||
import { MinimalSignalContext } from '../minimalContext';
|
||||
import { config } from '../../context/config';
|
||||
import { createNativeThemeListener } from '../../context/createNativeThemeListener';
|
||||
import { createSetting } from '../../util/preload';
|
||||
import { environment } from '../../context/environment';
|
||||
import { getClassName } from '../../OS';
|
||||
import { i18n } from '../../context/i18n';
|
||||
import { waitForSettingsChange } from '../../context/waitForSettingsChange';
|
||||
|
||||
async function executeMenuRole(
|
||||
role: MenuItemConstructorOptions['role']
|
||||
): Promise<void> {
|
||||
await ipcRenderer.invoke('executeMenuRole', role);
|
||||
}
|
||||
|
||||
const environments: Array<string> = [environment];
|
||||
|
||||
|
@ -24,40 +12,12 @@ if (config.appInstance) {
|
|||
environments.push(String(config.appInstance));
|
||||
}
|
||||
|
||||
let platform = '';
|
||||
if (process.platform === 'darwin') {
|
||||
if (process.arch === 'arm64') {
|
||||
platform = ` (${i18n('icu:appleSilicon')})`;
|
||||
} else {
|
||||
platform = ' (Intel)';
|
||||
}
|
||||
}
|
||||
|
||||
const environmentText = `${environments.join(' - ')}${platform}`;
|
||||
const hasCustomTitleBar = ipcRenderer.sendSync('getHasCustomTitleBar');
|
||||
|
||||
const Signal = {
|
||||
AboutWindow: {
|
||||
environmentText,
|
||||
executeMenuRole,
|
||||
hasCustomTitleBar,
|
||||
i18n,
|
||||
version: String(config.version),
|
||||
AboutWindowProps: {
|
||||
arch: process.arch,
|
||||
environmentText: environments.join(' - '),
|
||||
platform: process.platform,
|
||||
},
|
||||
};
|
||||
contextBridge.exposeInMainWorld('Signal', Signal);
|
||||
|
||||
// TODO DESKTOP-5054
|
||||
const SignalContext = {
|
||||
activeWindowService,
|
||||
OS: {
|
||||
getClassName,
|
||||
hasCustomTitleBar: () => hasCustomTitleBar,
|
||||
},
|
||||
Settings: {
|
||||
themeSetting: createSetting('themeSetting', { setter: false }),
|
||||
waitForChange: waitForSettingsChange,
|
||||
},
|
||||
nativeThemeListener: createNativeThemeListener(ipcRenderer, window),
|
||||
};
|
||||
contextBridge.exposeInMainWorld('SignalContext', SignalContext);
|
||||
contextBridge.exposeInMainWorld('SignalContext', MinimalSignalContext);
|
||||
|
|
|
@ -12,7 +12,7 @@ import * as Bytes from '../Bytes';
|
|||
|
||||
import { isPathInside } from '../util/isPathInside';
|
||||
import { writeWindowsZoneIdentifier } from '../util/windowsZoneIdentifier';
|
||||
import { isWindows } from '../OS';
|
||||
import OS from '../util/os/osMain';
|
||||
|
||||
export * from '../../app/attachments';
|
||||
|
||||
|
@ -229,7 +229,7 @@ async function writeWithAttributes(
|
|||
const attrValue = `${type};${timestamp};${appName};${guid}`;
|
||||
|
||||
await xattr.set(target, 'com.apple.quarantine', attrValue);
|
||||
} else if (isWindows()) {
|
||||
} else if (OS.isWindows()) {
|
||||
// This operation may fail (see the function's comments), which is not a show-stopper.
|
||||
try {
|
||||
await writeWindowsZoneIdentifier(target);
|
||||
|
|
|
@ -8,7 +8,6 @@ import type { MenuOptionsType, MenuActionType } from '../types/menu';
|
|||
import type { IPCEventsValuesType } from '../util/createIPCEvents';
|
||||
import type { LocalizerType } from '../types/Util';
|
||||
import type { LoggerType } from '../types/Logging';
|
||||
import type { LocaleMessagesType } from '../types/I18N';
|
||||
import type { NativeThemeType } from '../context/createNativeThemeListener';
|
||||
import type { SettingType } from '../util/preload';
|
||||
import type { RendererConfigType } from '../types/RendererConfig';
|
||||
|
@ -18,29 +17,10 @@ import { Crypto } from '../context/Crypto';
|
|||
import { Timers } from '../context/Timers';
|
||||
|
||||
import type { ActiveWindowServiceType } from '../services/ActiveWindowService';
|
||||
import { config } from '../context/config';
|
||||
import { i18n } from '../context/i18n';
|
||||
import { activeWindowService } from '../context/activeWindowService';
|
||||
import {
|
||||
getEnvironment,
|
||||
parseEnvironment,
|
||||
setEnvironment,
|
||||
} from '../environment';
|
||||
import { strictAssert } from '../util/assert';
|
||||
import { createSetting } from '../util/preload';
|
||||
import { initialize as initializeLogging } from '../logging/set_up_renderer_logging';
|
||||
import { waitForSettingsChange } from '../context/waitForSettingsChange';
|
||||
import { createNativeThemeListener } from '../context/createNativeThemeListener';
|
||||
import {
|
||||
isWindows,
|
||||
isLinux,
|
||||
isMacOS,
|
||||
hasCustomTitleBar,
|
||||
getClassName,
|
||||
} from '../OS';
|
||||
|
||||
const localeMessages = ipcRenderer.sendSync('locale-data');
|
||||
setEnvironment(parseEnvironment(config.environment));
|
||||
import { MinimalSignalContext } from './minimalContext';
|
||||
|
||||
strictAssert(Boolean(window.SignalContext), 'context must be defined');
|
||||
|
||||
|
@ -51,89 +31,53 @@ export type MainWindowStatsType = Readonly<{
|
|||
isFullScreen: boolean;
|
||||
}>;
|
||||
|
||||
export type SignalContextType = {
|
||||
bytes: Bytes;
|
||||
crypto: Crypto;
|
||||
timers: Timers;
|
||||
nativeThemeListener: NativeThemeType;
|
||||
setIsCallActive: (isCallActive: boolean) => unknown;
|
||||
|
||||
export type MinimalSignalContextType = {
|
||||
activeWindowService: ActiveWindowServiceType;
|
||||
config: RendererConfigType;
|
||||
executeMenuAction: (action: MenuActionType) => Promise<void>;
|
||||
executeMenuRole: (role: MenuItemConstructorOptions['role']) => Promise<void>;
|
||||
getAppInstance: () => string | undefined;
|
||||
getEnvironment: () => string;
|
||||
getI18nLocale: LocalizerType['getLocale'];
|
||||
getI18nLocaleMessages: LocalizerType['getLocaleMessages'];
|
||||
getMainWindowStats: () => Promise<MainWindowStatsType>;
|
||||
getMenuOptions: () => Promise<MenuOptionsType>;
|
||||
getNodeVersion: () => string;
|
||||
getPath: (name: 'userData' | 'home') => string;
|
||||
getVersion: () => string;
|
||||
nativeThemeListener: NativeThemeType;
|
||||
Settings: {
|
||||
themeSetting: SettingType<IPCEventsValuesType['themeSetting']>;
|
||||
waitForChange: () => Promise<void>;
|
||||
};
|
||||
OS: {
|
||||
hasCustomTitleBar: () => boolean;
|
||||
getClassName: () => string;
|
||||
platform: string;
|
||||
isWindows: typeof isWindows;
|
||||
isLinux: typeof isLinux;
|
||||
isMacOS: typeof isMacOS;
|
||||
hasCustomTitleBar: typeof hasCustomTitleBar;
|
||||
getClassName: typeof getClassName;
|
||||
release: string;
|
||||
};
|
||||
config: RendererConfigType;
|
||||
getAppInstance: () => string | undefined;
|
||||
getEnvironment: () => string;
|
||||
getNodeVersion: () => string;
|
||||
getVersion: () => string;
|
||||
getPath: (name: 'userData' | 'home') => string;
|
||||
i18n: LocalizerType;
|
||||
localeMessages: LocaleMessagesType;
|
||||
log: LoggerType;
|
||||
renderWindow?: () => void;
|
||||
executeMenuRole: (role: MenuItemConstructorOptions['role']) => Promise<void>;
|
||||
getMainWindowStats: () => Promise<MainWindowStatsType>;
|
||||
getMenuOptions: () => Promise<MenuOptionsType>;
|
||||
executeMenuAction: (action: MenuActionType) => Promise<void>;
|
||||
};
|
||||
|
||||
export type SignalContextType = {
|
||||
bytes: Bytes;
|
||||
crypto: Crypto;
|
||||
i18n: LocalizerType;
|
||||
log: LoggerType;
|
||||
renderWindow?: () => void;
|
||||
setIsCallActive: (isCallActive: boolean) => unknown;
|
||||
timers: Timers;
|
||||
} & MinimalSignalContextType;
|
||||
|
||||
export const SignalContext: SignalContextType = {
|
||||
activeWindowService,
|
||||
Settings: {
|
||||
themeSetting: createSetting('themeSetting', { setter: false }),
|
||||
waitForChange: waitForSettingsChange,
|
||||
},
|
||||
OS: {
|
||||
platform: process.platform,
|
||||
isWindows,
|
||||
isLinux,
|
||||
isMacOS,
|
||||
hasCustomTitleBar,
|
||||
getClassName,
|
||||
},
|
||||
...MinimalSignalContext,
|
||||
bytes: new Bytes(),
|
||||
config,
|
||||
crypto: new Crypto(),
|
||||
getAppInstance: (): string | undefined =>
|
||||
config.appInstance ? String(config.appInstance) : undefined,
|
||||
getEnvironment,
|
||||
getNodeVersion: (): string => String(config.nodeVersion),
|
||||
getVersion: (): string => String(config.version),
|
||||
getPath: (name: 'userData' | 'home'): string => {
|
||||
return String(config[`${name}Path`]);
|
||||
},
|
||||
i18n,
|
||||
localeMessages,
|
||||
log: window.SignalContext.log,
|
||||
nativeThemeListener: createNativeThemeListener(ipcRenderer, window),
|
||||
setIsCallActive(isCallActive: boolean): void {
|
||||
ipcRenderer.send('set-is-call-active', isCallActive);
|
||||
},
|
||||
timers: new Timers(),
|
||||
async executeMenuRole(
|
||||
role: MenuItemConstructorOptions['role']
|
||||
): Promise<void> {
|
||||
await ipcRenderer.invoke('executeMenuRole', role);
|
||||
},
|
||||
async getMainWindowStats(): Promise<MainWindowStatsType> {
|
||||
return ipcRenderer.invoke('getMainWindowStats');
|
||||
},
|
||||
async getMenuOptions(): Promise<MenuOptionsType> {
|
||||
return ipcRenderer.invoke('getMenuOptions');
|
||||
},
|
||||
async executeMenuAction(action: MenuActionType): Promise<void> {
|
||||
return ipcRenderer.invoke('executeMenuAction', action);
|
||||
},
|
||||
};
|
||||
|
||||
window.SignalContext = SignalContext;
|
||||
|
|
25
ts/windows/debuglog/app.tsx
Normal file
25
ts/windows/debuglog/app.tsx
Normal file
|
@ -0,0 +1,25 @@
|
|||
// Copyright 2023 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import React from 'react';
|
||||
import { render } from 'react-dom';
|
||||
import { DebugLogWindow } from '../../components/DebugLogWindow';
|
||||
import { i18n } from '../sandboxedInit';
|
||||
import { strictAssert } from '../../util/assert';
|
||||
|
||||
const { DebugLogWindowProps } = window.Signal;
|
||||
|
||||
strictAssert(DebugLogWindowProps, 'window values not provided');
|
||||
|
||||
render(
|
||||
<DebugLogWindow
|
||||
hasCustomTitleBar={window.SignalContext.OS.hasCustomTitleBar()}
|
||||
executeMenuRole={window.SignalContext.executeMenuRole}
|
||||
closeWindow={() => window.SignalContext.executeMenuRole('close')}
|
||||
downloadLog={DebugLogWindowProps.downloadLog}
|
||||
i18n={i18n}
|
||||
fetchLogs={DebugLogWindowProps.fetchLogs}
|
||||
uploadLogs={DebugLogWindowProps.uploadLogs}
|
||||
/>,
|
||||
document.getElementById('app')
|
||||
);
|
|
@ -1,49 +1,32 @@
|
|||
// Copyright 2021 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { contextBridge, ipcRenderer } from 'electron';
|
||||
import { MinimalSignalContext } from '../minimalContext';
|
||||
|
||||
import { SignalContext } from '../context';
|
||||
import { DebugLogWindow } from '../../components/DebugLogWindow';
|
||||
import * as debugLog from '../../logging/debuglogs';
|
||||
import { upload } from '../../logging/uploadDebugLog';
|
||||
import * as logger from '../../logging/log';
|
||||
function downloadLog(logText: string) {
|
||||
ipcRenderer.send('show-debug-log-save-dialog', logText);
|
||||
}
|
||||
|
||||
contextBridge.exposeInMainWorld('SignalContext', {
|
||||
...SignalContext,
|
||||
renderWindow: () => {
|
||||
const environmentText: Array<string> = [SignalContext.getEnvironment()];
|
||||
async function fetchLogs() {
|
||||
const data = await ipcRenderer.invoke('fetch-log');
|
||||
return ipcRenderer.invoke(
|
||||
'DebugLogs.getLogs',
|
||||
data,
|
||||
window.navigator.userAgent
|
||||
);
|
||||
}
|
||||
|
||||
const appInstance = SignalContext.getAppInstance();
|
||||
if (appInstance) {
|
||||
environmentText.push(appInstance);
|
||||
}
|
||||
function uploadLogs(logs: string) {
|
||||
return ipcRenderer.invoke('DebugLogs.upload', logs);
|
||||
}
|
||||
|
||||
ReactDOM.render(
|
||||
React.createElement(DebugLogWindow, {
|
||||
hasCustomTitleBar: SignalContext.OS.hasCustomTitleBar(),
|
||||
executeMenuRole: SignalContext.executeMenuRole,
|
||||
closeWindow: () => SignalContext.executeMenuRole('close'),
|
||||
downloadLog: (logText: string) =>
|
||||
ipcRenderer.send('show-debug-log-save-dialog', logText),
|
||||
i18n: SignalContext.i18n,
|
||||
fetchLogs() {
|
||||
return debugLog.fetch(
|
||||
SignalContext.getNodeVersion(),
|
||||
SignalContext.getVersion()
|
||||
);
|
||||
},
|
||||
uploadLogs(logs: string) {
|
||||
return upload({
|
||||
content: logs,
|
||||
appVersion: SignalContext.getVersion(),
|
||||
logger,
|
||||
});
|
||||
},
|
||||
}),
|
||||
document.getElementById('app')
|
||||
);
|
||||
const Signal = {
|
||||
DebugLogWindowProps: {
|
||||
downloadLog,
|
||||
fetchLogs,
|
||||
uploadLogs,
|
||||
},
|
||||
});
|
||||
};
|
||||
contextBridge.exposeInMainWorld('Signal', Signal);
|
||||
contextBridge.exposeInMainWorld('SignalContext', MinimalSignalContext);
|
||||
|
|
|
@ -2,7 +2,10 @@
|
|||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { contextBridge } from 'electron';
|
||||
import { config } from '../../context/config';
|
||||
import { localeMessages } from '../../context/localeMessages';
|
||||
|
||||
import { SignalContext } from '../context';
|
||||
|
||||
contextBridge.exposeInMainWorld('SignalContext', SignalContext);
|
||||
contextBridge.exposeInMainWorld('SignalContext', {
|
||||
getI18nLocale: () => config.resolvedTranslationsLocale,
|
||||
getI18nLocaleMessages: () => localeMessages,
|
||||
});
|
||||
|
|
|
@ -1,7 +1,14 @@
|
|||
// Copyright 2020 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { setupI18n } from '../../util/setupI18n';
|
||||
|
||||
window.i18n = setupI18n(
|
||||
window.SignalContext.getI18nLocale(),
|
||||
window.SignalContext.getI18nLocaleMessages()
|
||||
);
|
||||
|
||||
const message = document.getElementById('message');
|
||||
if (message) {
|
||||
message.innerHTML = window.SignalContext.i18n('icu:optimizingApplication');
|
||||
message.innerHTML = window.i18n('icu:optimizingApplication');
|
||||
}
|
||||
|
|
56
ts/windows/minimalContext.ts
Normal file
56
ts/windows/minimalContext.ts
Normal file
|
@ -0,0 +1,56 @@
|
|||
// Copyright 2023 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import type { MenuItemConstructorOptions } from 'electron';
|
||||
import { ipcRenderer } from 'electron';
|
||||
|
||||
import type { MenuOptionsType, MenuActionType } from '../types/menu';
|
||||
import type { MainWindowStatsType, MinimalSignalContextType } from './context';
|
||||
import { activeWindowService } from '../context/activeWindowService';
|
||||
import { config } from '../context/config';
|
||||
import { createNativeThemeListener } from '../context/createNativeThemeListener';
|
||||
import { createSetting } from '../util/preload';
|
||||
import { environment } from '../context/environment';
|
||||
import { localeMessages } from '../context/localeMessages';
|
||||
import { waitForSettingsChange } from '../context/waitForSettingsChange';
|
||||
|
||||
const hasCustomTitleBar = ipcRenderer.sendSync('OS.getHasCustomTitleBar');
|
||||
export const MinimalSignalContext: MinimalSignalContextType = {
|
||||
activeWindowService,
|
||||
config,
|
||||
async executeMenuAction(action: MenuActionType): Promise<void> {
|
||||
return ipcRenderer.invoke('executeMenuAction', action);
|
||||
},
|
||||
async executeMenuRole(
|
||||
role: MenuItemConstructorOptions['role']
|
||||
): Promise<void> {
|
||||
await ipcRenderer.invoke('executeMenuRole', role);
|
||||
},
|
||||
getAppInstance: (): string | undefined =>
|
||||
config.appInstance ? String(config.appInstance) : undefined,
|
||||
getEnvironment: () => environment,
|
||||
getNodeVersion: (): string => String(config.nodeVersion),
|
||||
getPath: (name: 'userData' | 'home'): string => {
|
||||
return String(config[`${name}Path`]);
|
||||
},
|
||||
getVersion: (): string => String(config.version),
|
||||
async getMainWindowStats(): Promise<MainWindowStatsType> {
|
||||
return ipcRenderer.invoke('getMainWindowStats');
|
||||
},
|
||||
async getMenuOptions(): Promise<MenuOptionsType> {
|
||||
return ipcRenderer.invoke('getMenuOptions');
|
||||
},
|
||||
getI18nLocale: () => config.resolvedTranslationsLocale,
|
||||
getI18nLocaleMessages: () => localeMessages,
|
||||
nativeThemeListener: createNativeThemeListener(ipcRenderer, window),
|
||||
OS: {
|
||||
getClassName: () => ipcRenderer.sendSync('OS.getClassName'),
|
||||
hasCustomTitleBar: () => hasCustomTitleBar,
|
||||
platform: process.platform,
|
||||
release: config.osRelease,
|
||||
},
|
||||
Settings: {
|
||||
themeSetting: createSetting('themeSetting', { setter: false }),
|
||||
waitForChange: waitForSettingsChange,
|
||||
},
|
||||
};
|
36
ts/windows/permissions/app.tsx
Normal file
36
ts/windows/permissions/app.tsx
Normal file
|
@ -0,0 +1,36 @@
|
|||
// Copyright 2023 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
|
||||
import { PermissionsPopup } from '../../components/PermissionsPopup';
|
||||
import { i18n } from '../sandboxedInit';
|
||||
import { strictAssert } from '../../util/assert';
|
||||
|
||||
const { PermissionsWindowProps } = window.Signal;
|
||||
|
||||
strictAssert(PermissionsWindowProps, 'window values not provided');
|
||||
|
||||
const { forCalling, forCamera } = PermissionsWindowProps;
|
||||
|
||||
let message;
|
||||
if (forCalling) {
|
||||
if (forCamera) {
|
||||
message = i18n('icu:videoCallingPermissionNeeded');
|
||||
} else {
|
||||
message = i18n('icu:audioCallingPermissionNeeded');
|
||||
}
|
||||
} else {
|
||||
message = i18n('icu:audioPermissionNeeded');
|
||||
}
|
||||
|
||||
ReactDOM.render(
|
||||
<PermissionsPopup
|
||||
i18n={i18n}
|
||||
message={message}
|
||||
onAccept={PermissionsWindowProps.onAccept}
|
||||
onClose={PermissionsWindowProps.onClose}
|
||||
/>,
|
||||
document.getElementById('app')
|
||||
);
|
|
@ -1,14 +1,10 @@
|
|||
// Copyright 2021 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { contextBridge } from 'electron';
|
||||
|
||||
import { SignalContext } from '../context';
|
||||
|
||||
import { MinimalSignalContext } from '../minimalContext';
|
||||
import { createSetting } from '../../util/preload';
|
||||
import { PermissionsPopup } from '../../components/PermissionsPopup';
|
||||
import { drop } from '../../util/drop';
|
||||
|
||||
const mediaCameraPermissions = createSetting('mediaCameraPermissions', {
|
||||
getter: false,
|
||||
|
@ -17,48 +13,28 @@ const mediaPermissions = createSetting('mediaPermissions', {
|
|||
getter: false,
|
||||
});
|
||||
|
||||
contextBridge.exposeInMainWorld(
|
||||
'nativeThemeListener',
|
||||
window.SignalContext.nativeThemeListener
|
||||
);
|
||||
const params = new URLSearchParams(document.location.search);
|
||||
const forCalling = params.get('forCalling') === 'true';
|
||||
const forCamera = params.get('forCamera') === 'true';
|
||||
|
||||
contextBridge.exposeInMainWorld('SignalContext', {
|
||||
...SignalContext,
|
||||
renderWindow: () => {
|
||||
const params = new URLSearchParams(document.location.search);
|
||||
const forCalling = params.get('forCalling') === 'true';
|
||||
const forCamera = params.get('forCamera') === 'true';
|
||||
function onClose() {
|
||||
drop(MinimalSignalContext.executeMenuRole('close'));
|
||||
}
|
||||
|
||||
let message;
|
||||
if (forCalling) {
|
||||
if (forCamera) {
|
||||
message = SignalContext.i18n('icu:videoCallingPermissionNeeded');
|
||||
const Signal = {
|
||||
PermissionsWindowProps: {
|
||||
forCalling,
|
||||
forCamera,
|
||||
onAccept: () => {
|
||||
if (!forCamera) {
|
||||
drop(mediaPermissions.setValue(true));
|
||||
} else {
|
||||
message = SignalContext.i18n('icu:audioCallingPermissionNeeded');
|
||||
drop(mediaCameraPermissions.setValue(true));
|
||||
}
|
||||
} else {
|
||||
message = SignalContext.i18n('icu:audioPermissionNeeded');
|
||||
}
|
||||
|
||||
function onClose() {
|
||||
void SignalContext.executeMenuRole('close');
|
||||
}
|
||||
|
||||
ReactDOM.render(
|
||||
React.createElement(PermissionsPopup, {
|
||||
i18n: SignalContext.i18n,
|
||||
message,
|
||||
onAccept: () => {
|
||||
if (!forCamera) {
|
||||
void mediaPermissions.setValue(true);
|
||||
} else {
|
||||
void mediaCameraPermissions.setValue(true);
|
||||
}
|
||||
onClose();
|
||||
},
|
||||
onClose,
|
||||
}),
|
||||
document.getElementById('app')
|
||||
);
|
||||
onClose();
|
||||
},
|
||||
onClose,
|
||||
},
|
||||
});
|
||||
};
|
||||
contextBridge.exposeInMainWorld('Signal', Signal);
|
||||
contextBridge.exposeInMainWorld('SignalContext', MinimalSignalContext);
|
||||
|
|
15
ts/windows/sandboxedInit.ts
Normal file
15
ts/windows/sandboxedInit.ts
Normal file
|
@ -0,0 +1,15 @@
|
|||
// Copyright 2023 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import './applyTheme';
|
||||
import { setupI18n } from '../util/setupI18n';
|
||||
|
||||
document.body.classList.add(window.SignalContext.OS.getClassName());
|
||||
if (window.SignalContext.OS.hasCustomTitleBar()) {
|
||||
document.body.classList.add('os-has-custom-titlebar');
|
||||
}
|
||||
|
||||
export const i18n = setupI18n(
|
||||
window.SignalContext.getI18nLocale(),
|
||||
window.SignalContext.getI18nLocaleMessages()
|
||||
);
|
24
ts/windows/screenShare/app.tsx
Normal file
24
ts/windows/screenShare/app.tsx
Normal file
|
@ -0,0 +1,24 @@
|
|||
// Copyright 2023 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
|
||||
import { CallingScreenSharingController } from '../../components/CallingScreenSharingController';
|
||||
import { i18n } from '../sandboxedInit';
|
||||
import { strictAssert } from '../../util/assert';
|
||||
|
||||
const { ScreenShareWindowProps } = window.Signal;
|
||||
|
||||
strictAssert(ScreenShareWindowProps, 'window values not provided');
|
||||
|
||||
ReactDOM.render(
|
||||
<CallingScreenSharingController
|
||||
i18n={i18n}
|
||||
onCloseController={() => window.SignalContext.executeMenuRole('close')}
|
||||
onStopSharing={ScreenShareWindowProps.onStopSharing}
|
||||
presentedSourceName={ScreenShareWindowProps.presentedSourceName}
|
||||
/>,
|
||||
|
||||
document.getElementById('app')
|
||||
);
|
|
@ -1,29 +1,18 @@
|
|||
// Copyright 2021 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { contextBridge, ipcRenderer } from 'electron';
|
||||
import { MinimalSignalContext } from '../minimalContext';
|
||||
|
||||
import { SignalContext } from '../context';
|
||||
import { CallingScreenSharingController } from '../../components/CallingScreenSharingController';
|
||||
const params = new URLSearchParams(document.location.search);
|
||||
|
||||
contextBridge.exposeInMainWorld('SignalContext', SignalContext);
|
||||
|
||||
function renderScreenSharingController(presentedSourceName: string): void {
|
||||
ReactDOM.render(
|
||||
React.createElement(CallingScreenSharingController, {
|
||||
platform: process.platform,
|
||||
executeMenuRole: SignalContext.executeMenuRole,
|
||||
i18n: SignalContext.i18n,
|
||||
onCloseController: () => SignalContext.executeMenuRole('close'),
|
||||
onStopSharing: () => ipcRenderer.send('stop-screen-share'),
|
||||
presentedSourceName,
|
||||
}),
|
||||
document.getElementById('app')
|
||||
);
|
||||
}
|
||||
|
||||
ipcRenderer.once('render-screen-sharing-controller', (_, name: string) => {
|
||||
renderScreenSharingController(name);
|
||||
});
|
||||
const Signal = {
|
||||
ScreenShareWindowProps: {
|
||||
onStopSharing: () => {
|
||||
ipcRenderer.send('stop-screen-share');
|
||||
},
|
||||
presentedSourceName: params.get('sourceName'),
|
||||
},
|
||||
};
|
||||
contextBridge.exposeInMainWorld('Signal', Signal);
|
||||
contextBridge.exposeInMainWorld('SignalContext', MinimalSignalContext);
|
||||
|
|
221
ts/windows/settings/app.tsx
Normal file
221
ts/windows/settings/app.tsx
Normal file
|
@ -0,0 +1,221 @@
|
|||
// Copyright 2023 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
|
||||
import type { PropsPreloadType } from '../../components/Preferences';
|
||||
import { i18n } from '../sandboxedInit';
|
||||
import { Preferences } from '../../components/Preferences';
|
||||
import { startInteractionMode } from '../../services/InteractionMode';
|
||||
import { strictAssert } from '../../util/assert';
|
||||
|
||||
const { SettingsWindowProps } = window.Signal;
|
||||
|
||||
strictAssert(SettingsWindowProps, 'window values not provided');
|
||||
|
||||
startInteractionMode();
|
||||
|
||||
SettingsWindowProps.onRender(
|
||||
({
|
||||
addCustomColor,
|
||||
availableCameras,
|
||||
availableMicrophones,
|
||||
availableSpeakers,
|
||||
blockedCount,
|
||||
closeSettings,
|
||||
customColors,
|
||||
defaultConversationColor,
|
||||
deviceName,
|
||||
doDeleteAllData,
|
||||
doneRendering,
|
||||
editCustomColor,
|
||||
executeMenuRole,
|
||||
getConversationsWithCustomColor,
|
||||
hasAudioNotifications,
|
||||
hasAutoDownloadUpdate,
|
||||
hasAutoLaunch,
|
||||
hasCallNotifications,
|
||||
hasCallRingtoneNotification,
|
||||
hasCountMutedConversations,
|
||||
hasCustomTitleBar,
|
||||
hasHideMenuBar,
|
||||
hasIncomingCallNotifications,
|
||||
hasLinkPreviews,
|
||||
hasMediaCameraPermissions,
|
||||
hasMediaPermissions,
|
||||
hasMinimizeToAndStartInSystemTray,
|
||||
hasMinimizeToSystemTray,
|
||||
hasNotificationAttention,
|
||||
hasNotifications,
|
||||
hasReadReceipts,
|
||||
hasRelayCalls,
|
||||
hasSpellCheck,
|
||||
hasStoriesDisabled,
|
||||
hasTextFormatting,
|
||||
hasTypingIndicators,
|
||||
initialSpellCheckSetting,
|
||||
isAudioNotificationsSupported,
|
||||
isAutoDownloadUpdatesSupported,
|
||||
isAutoLaunchSupported,
|
||||
isFormattingFlagEnabled,
|
||||
isHideMenuBarSupported,
|
||||
isMinimizeToAndStartInSystemTraySupported,
|
||||
isNotificationAttentionSupported,
|
||||
isPhoneNumberSharingSupported,
|
||||
isSyncSupported,
|
||||
isSystemTraySupported,
|
||||
lastSyncTime,
|
||||
makeSyncRequest,
|
||||
notificationContent,
|
||||
onAudioNotificationsChange,
|
||||
onAutoDownloadUpdateChange,
|
||||
onAutoLaunchChange,
|
||||
onCallNotificationsChange,
|
||||
onCallRingtoneNotificationChange,
|
||||
onCountMutedConversationsChange,
|
||||
onHasStoriesDisabledChanged,
|
||||
onHideMenuBarChange,
|
||||
onIncomingCallNotificationsChange,
|
||||
onLastSyncTimeChange,
|
||||
onMediaCameraPermissionsChange,
|
||||
onMediaPermissionsChange,
|
||||
onMinimizeToAndStartInSystemTrayChange,
|
||||
onMinimizeToSystemTrayChange,
|
||||
onNotificationAttentionChange,
|
||||
onNotificationContentChange,
|
||||
onNotificationsChange,
|
||||
onRelayCallsChange,
|
||||
onSelectedCameraChange,
|
||||
onSelectedMicrophoneChange,
|
||||
onSelectedSpeakerChange,
|
||||
onSentMediaQualityChange,
|
||||
onSpellCheckChange,
|
||||
onTextFormattingChange,
|
||||
onThemeChange,
|
||||
onUniversalExpireTimerChange,
|
||||
onWhoCanFindMeChange,
|
||||
onWhoCanSeeMeChange,
|
||||
onZoomFactorChange,
|
||||
removeCustomColor,
|
||||
removeCustomColorOnConversations,
|
||||
resetAllChatColors,
|
||||
resetDefaultChatColor,
|
||||
selectedCamera,
|
||||
selectedMicrophone,
|
||||
selectedSpeaker,
|
||||
sentMediaQualitySetting,
|
||||
setGlobalDefaultConversationColor,
|
||||
shouldShowStoriesSettings,
|
||||
themeSetting,
|
||||
universalExpireTimer,
|
||||
whoCanFindMe,
|
||||
whoCanSeeMe,
|
||||
zoomFactor,
|
||||
}: PropsPreloadType) => {
|
||||
ReactDOM.render(
|
||||
<Preferences
|
||||
addCustomColor={addCustomColor}
|
||||
availableCameras={availableCameras}
|
||||
availableMicrophones={availableMicrophones}
|
||||
availableSpeakers={availableSpeakers}
|
||||
blockedCount={blockedCount}
|
||||
closeSettings={closeSettings}
|
||||
customColors={customColors}
|
||||
defaultConversationColor={defaultConversationColor}
|
||||
deviceName={deviceName}
|
||||
doDeleteAllData={doDeleteAllData}
|
||||
doneRendering={doneRendering}
|
||||
editCustomColor={editCustomColor}
|
||||
executeMenuRole={executeMenuRole}
|
||||
getConversationsWithCustomColor={getConversationsWithCustomColor}
|
||||
hasAudioNotifications={hasAudioNotifications}
|
||||
hasAutoDownloadUpdate={hasAutoDownloadUpdate}
|
||||
hasAutoLaunch={hasAutoLaunch}
|
||||
hasCallNotifications={hasCallNotifications}
|
||||
hasCallRingtoneNotification={hasCallRingtoneNotification}
|
||||
hasCountMutedConversations={hasCountMutedConversations}
|
||||
hasCustomTitleBar={hasCustomTitleBar}
|
||||
hasHideMenuBar={hasHideMenuBar}
|
||||
hasIncomingCallNotifications={hasIncomingCallNotifications}
|
||||
hasLinkPreviews={hasLinkPreviews}
|
||||
hasMediaCameraPermissions={hasMediaCameraPermissions}
|
||||
hasMediaPermissions={hasMediaPermissions}
|
||||
hasMinimizeToAndStartInSystemTray={hasMinimizeToAndStartInSystemTray}
|
||||
hasMinimizeToSystemTray={hasMinimizeToSystemTray}
|
||||
hasNotificationAttention={hasNotificationAttention}
|
||||
hasNotifications={hasNotifications}
|
||||
hasReadReceipts={hasReadReceipts}
|
||||
hasRelayCalls={hasRelayCalls}
|
||||
hasSpellCheck={hasSpellCheck}
|
||||
hasStoriesDisabled={hasStoriesDisabled}
|
||||
hasTextFormatting={hasTextFormatting}
|
||||
hasTypingIndicators={hasTypingIndicators}
|
||||
i18n={i18n}
|
||||
initialSpellCheckSetting={initialSpellCheckSetting}
|
||||
isAudioNotificationsSupported={isAudioNotificationsSupported}
|
||||
isAutoDownloadUpdatesSupported={isAutoDownloadUpdatesSupported}
|
||||
isAutoLaunchSupported={isAutoLaunchSupported}
|
||||
isFormattingFlagEnabled={isFormattingFlagEnabled}
|
||||
isHideMenuBarSupported={isHideMenuBarSupported}
|
||||
isMinimizeToAndStartInSystemTraySupported={
|
||||
isMinimizeToAndStartInSystemTraySupported
|
||||
}
|
||||
isNotificationAttentionSupported={isNotificationAttentionSupported}
|
||||
isPhoneNumberSharingSupported={isPhoneNumberSharingSupported}
|
||||
isSyncSupported={isSyncSupported}
|
||||
isSystemTraySupported={isSystemTraySupported}
|
||||
lastSyncTime={lastSyncTime}
|
||||
makeSyncRequest={makeSyncRequest}
|
||||
notificationContent={notificationContent}
|
||||
onAudioNotificationsChange={onAudioNotificationsChange}
|
||||
onAutoDownloadUpdateChange={onAutoDownloadUpdateChange}
|
||||
onAutoLaunchChange={onAutoLaunchChange}
|
||||
onCallNotificationsChange={onCallNotificationsChange}
|
||||
onCallRingtoneNotificationChange={onCallRingtoneNotificationChange}
|
||||
onCountMutedConversationsChange={onCountMutedConversationsChange}
|
||||
onHasStoriesDisabledChanged={onHasStoriesDisabledChanged}
|
||||
onHideMenuBarChange={onHideMenuBarChange}
|
||||
onIncomingCallNotificationsChange={onIncomingCallNotificationsChange}
|
||||
onLastSyncTimeChange={onLastSyncTimeChange}
|
||||
onMediaCameraPermissionsChange={onMediaCameraPermissionsChange}
|
||||
onMediaPermissionsChange={onMediaPermissionsChange}
|
||||
onMinimizeToAndStartInSystemTrayChange={
|
||||
onMinimizeToAndStartInSystemTrayChange
|
||||
}
|
||||
onMinimizeToSystemTrayChange={onMinimizeToSystemTrayChange}
|
||||
onNotificationAttentionChange={onNotificationAttentionChange}
|
||||
onNotificationContentChange={onNotificationContentChange}
|
||||
onNotificationsChange={onNotificationsChange}
|
||||
onRelayCallsChange={onRelayCallsChange}
|
||||
onSelectedCameraChange={onSelectedCameraChange}
|
||||
onSelectedMicrophoneChange={onSelectedMicrophoneChange}
|
||||
onSelectedSpeakerChange={onSelectedSpeakerChange}
|
||||
onSentMediaQualityChange={onSentMediaQualityChange}
|
||||
onSpellCheckChange={onSpellCheckChange}
|
||||
onTextFormattingChange={onTextFormattingChange}
|
||||
onThemeChange={onThemeChange}
|
||||
onUniversalExpireTimerChange={onUniversalExpireTimerChange}
|
||||
onWhoCanFindMeChange={onWhoCanFindMeChange}
|
||||
onWhoCanSeeMeChange={onWhoCanSeeMeChange}
|
||||
onZoomFactorChange={onZoomFactorChange}
|
||||
removeCustomColorOnConversations={removeCustomColorOnConversations}
|
||||
removeCustomColor={removeCustomColor}
|
||||
resetAllChatColors={resetAllChatColors}
|
||||
resetDefaultChatColor={resetDefaultChatColor}
|
||||
selectedCamera={selectedCamera}
|
||||
selectedMicrophone={selectedMicrophone}
|
||||
selectedSpeaker={selectedSpeaker}
|
||||
sentMediaQualitySetting={sentMediaQualitySetting}
|
||||
setGlobalDefaultConversationColor={setGlobalDefaultConversationColor}
|
||||
shouldShowStoriesSettings={shouldShowStoriesSettings}
|
||||
themeSetting={themeSetting}
|
||||
universalExpireTimer={universalExpireTimer}
|
||||
whoCanFindMe={whoCanFindMe}
|
||||
whoCanSeeMe={whoCanSeeMe}
|
||||
zoomFactor={zoomFactor}
|
||||
/>,
|
||||
document.getElementById('app')
|
||||
);
|
||||
}
|
||||
);
|
|
@ -1,13 +1,12 @@
|
|||
// Copyright 2021 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { contextBridge, ipcRenderer } from 'electron';
|
||||
import { MinimalSignalContext } from '../minimalContext';
|
||||
|
||||
import { SignalContext } from '../context';
|
||||
import type { PropsPreloadType } from '../../components/Preferences';
|
||||
import OS from '../../util/os/osPreload';
|
||||
import * as Settings from '../../types/Settings';
|
||||
import { Preferences } from '../../components/Preferences';
|
||||
import {
|
||||
SystemTraySetting,
|
||||
parseSystemTraySetting,
|
||||
|
@ -16,7 +15,6 @@ import {
|
|||
import { awaitObject } from '../../util/awaitObject';
|
||||
import { DurationInSeconds } from '../../util/durations';
|
||||
import { createSetting, createCallback } from '../../util/preload';
|
||||
import { startInteractionMode } from '../../services/InteractionMode';
|
||||
|
||||
function doneRendering() {
|
||||
ipcRenderer.send('settings-done-rendering');
|
||||
|
@ -128,9 +126,18 @@ function getSystemTraySettingValues(systemTraySetting: SystemTraySetting): {
|
|||
};
|
||||
}
|
||||
|
||||
const renderPreferences = async () => {
|
||||
startInteractionMode();
|
||||
let renderInBrowser = (_props: PropsPreloadType): void => {
|
||||
throw new Error('render is not defined');
|
||||
};
|
||||
|
||||
function attachRenderCallback<Value>(f: (value: Value) => Promise<Value>) {
|
||||
return async (value: Value) => {
|
||||
await f(value);
|
||||
void renderPreferences();
|
||||
};
|
||||
}
|
||||
|
||||
async function renderPreferences() {
|
||||
const {
|
||||
blockedCount,
|
||||
deviceName,
|
||||
|
@ -222,7 +229,7 @@ const renderPreferences = async () => {
|
|||
const { hasMinimizeToAndStartInSystemTray, hasMinimizeToSystemTray } =
|
||||
getSystemTraySettingValues(systemTraySetting);
|
||||
|
||||
const onUniversalExpireTimerChange = reRender(
|
||||
const onUniversalExpireTimerChange = attachRenderCallback(
|
||||
settingUniversalExpireTimer.setValue
|
||||
);
|
||||
|
||||
|
@ -270,13 +277,13 @@ const renderPreferences = async () => {
|
|||
|
||||
// Actions and other props
|
||||
addCustomColor: ipcAddCustomColor,
|
||||
closeSettings: () => SignalContext.executeMenuRole('close'),
|
||||
closeSettings: () => MinimalSignalContext.executeMenuRole('close'),
|
||||
doDeleteAllData: () => ipcRenderer.send('delete-all-data'),
|
||||
doneRendering,
|
||||
editCustomColor: ipcEditCustomColor,
|
||||
getConversationsWithCustomColor: ipcGetConversationsWithCustomColor,
|
||||
initialSpellCheckSetting:
|
||||
SignalContext.config.appStartInitialSpellcheckSetting,
|
||||
MinimalSignalContext.config.appStartInitialSpellcheckSetting,
|
||||
makeSyncRequest: ipcMakeSyncRequest,
|
||||
removeCustomColor: ipcRemoveCustomColor,
|
||||
removeCustomColorOnConversations: ipcRemoveCustomColorOnConversations,
|
||||
|
@ -286,93 +293,121 @@ const renderPreferences = async () => {
|
|||
shouldShowStoriesSettings,
|
||||
|
||||
// Limited support features
|
||||
isAudioNotificationsSupported: Settings.isAudioNotificationSupported(),
|
||||
isAutoDownloadUpdatesSupported: Settings.isAutoDownloadUpdatesSupported(),
|
||||
isAutoLaunchSupported: Settings.isAutoLaunchSupported(),
|
||||
isHideMenuBarSupported: Settings.isHideMenuBarSupported(),
|
||||
isNotificationAttentionSupported: Settings.isDrawAttentionSupported(),
|
||||
isAudioNotificationsSupported: Settings.isAudioNotificationSupported(OS),
|
||||
isAutoDownloadUpdatesSupported: Settings.isAutoDownloadUpdatesSupported(OS),
|
||||
isAutoLaunchSupported: Settings.isAutoLaunchSupported(OS),
|
||||
isHideMenuBarSupported: Settings.isHideMenuBarSupported(OS),
|
||||
isNotificationAttentionSupported: Settings.isDrawAttentionSupported(OS),
|
||||
isPhoneNumberSharingSupported,
|
||||
isSyncSupported: !isSyncNotSupported,
|
||||
isSystemTraySupported: Settings.isSystemTraySupported(
|
||||
SignalContext.getVersion()
|
||||
OS,
|
||||
MinimalSignalContext.getVersion()
|
||||
),
|
||||
isMinimizeToAndStartInSystemTraySupported:
|
||||
Settings.isMinimizeToAndStartInSystemTraySupported(
|
||||
SignalContext.getVersion()
|
||||
OS,
|
||||
MinimalSignalContext.getVersion()
|
||||
),
|
||||
|
||||
// Feature flags
|
||||
isFormattingFlagEnabled,
|
||||
|
||||
// Change handlers
|
||||
onAudioNotificationsChange: reRender(settingAudioNotification.setValue),
|
||||
onAutoDownloadUpdateChange: reRender(settingAutoDownloadUpdate.setValue),
|
||||
onAutoLaunchChange: reRender(settingAutoLaunch.setValue),
|
||||
onCallNotificationsChange: reRender(settingCallSystemNotification.setValue),
|
||||
onCallRingtoneNotificationChange: reRender(
|
||||
onAudioNotificationsChange: attachRenderCallback(
|
||||
settingAudioNotification.setValue
|
||||
),
|
||||
onAutoDownloadUpdateChange: attachRenderCallback(
|
||||
settingAutoDownloadUpdate.setValue
|
||||
),
|
||||
onAutoLaunchChange: attachRenderCallback(settingAutoLaunch.setValue),
|
||||
onCallNotificationsChange: attachRenderCallback(
|
||||
settingCallSystemNotification.setValue
|
||||
),
|
||||
onCallRingtoneNotificationChange: attachRenderCallback(
|
||||
settingCallRingtoneNotification.setValue
|
||||
),
|
||||
onCountMutedConversationsChange: reRender(
|
||||
onCountMutedConversationsChange: attachRenderCallback(
|
||||
settingCountMutedConversations.setValue
|
||||
),
|
||||
onHasStoriesDisabledChanged: reRender(async (value: boolean) => {
|
||||
await settingHasStoriesDisabled.setValue(value);
|
||||
if (!value) {
|
||||
void ipcDeleteAllMyStories();
|
||||
onHasStoriesDisabledChanged: attachRenderCallback(
|
||||
async (value: boolean) => {
|
||||
await settingHasStoriesDisabled.setValue(value);
|
||||
if (!value) {
|
||||
void ipcDeleteAllMyStories();
|
||||
}
|
||||
return value;
|
||||
}
|
||||
return value;
|
||||
}),
|
||||
onHideMenuBarChange: reRender(settingHideMenuBar.setValue),
|
||||
onIncomingCallNotificationsChange: reRender(
|
||||
),
|
||||
onHideMenuBarChange: attachRenderCallback(settingHideMenuBar.setValue),
|
||||
onIncomingCallNotificationsChange: attachRenderCallback(
|
||||
settingIncomingCallNotification.setValue
|
||||
),
|
||||
onLastSyncTimeChange: reRender(settingLastSyncTime.setValue),
|
||||
onMediaCameraPermissionsChange: reRender(
|
||||
onLastSyncTimeChange: attachRenderCallback(settingLastSyncTime.setValue),
|
||||
onMediaCameraPermissionsChange: attachRenderCallback(
|
||||
settingMediaCameraPermissions.setValue
|
||||
),
|
||||
onMinimizeToAndStartInSystemTrayChange: reRender(async (value: boolean) => {
|
||||
await settingSystemTraySetting.setValue(
|
||||
value
|
||||
? SystemTraySetting.MinimizeToAndStartInSystemTray
|
||||
: SystemTraySetting.MinimizeToSystemTray
|
||||
);
|
||||
return value;
|
||||
}),
|
||||
onMinimizeToSystemTrayChange: reRender(async (value: boolean) => {
|
||||
await settingSystemTraySetting.setValue(
|
||||
value
|
||||
? SystemTraySetting.MinimizeToSystemTray
|
||||
: SystemTraySetting.DoNotUseSystemTray
|
||||
);
|
||||
return value;
|
||||
}),
|
||||
onMediaPermissionsChange: reRender(settingMediaPermissions.setValue),
|
||||
onNotificationAttentionChange: reRender(
|
||||
onMinimizeToAndStartInSystemTrayChange: attachRenderCallback(
|
||||
async (value: boolean) => {
|
||||
await settingSystemTraySetting.setValue(
|
||||
value
|
||||
? SystemTraySetting.MinimizeToAndStartInSystemTray
|
||||
: SystemTraySetting.MinimizeToSystemTray
|
||||
);
|
||||
return value;
|
||||
}
|
||||
),
|
||||
onMinimizeToSystemTrayChange: attachRenderCallback(
|
||||
async (value: boolean) => {
|
||||
await settingSystemTraySetting.setValue(
|
||||
value
|
||||
? SystemTraySetting.MinimizeToSystemTray
|
||||
: SystemTraySetting.DoNotUseSystemTray
|
||||
);
|
||||
return value;
|
||||
}
|
||||
),
|
||||
onMediaPermissionsChange: attachRenderCallback(
|
||||
settingMediaPermissions.setValue
|
||||
),
|
||||
onNotificationAttentionChange: attachRenderCallback(
|
||||
settingNotificationDrawAttention.setValue
|
||||
),
|
||||
onNotificationContentChange: reRender(settingNotificationSetting.setValue),
|
||||
onNotificationsChange: reRender(async (value: boolean) => {
|
||||
onNotificationContentChange: attachRenderCallback(
|
||||
settingNotificationSetting.setValue
|
||||
),
|
||||
onNotificationsChange: attachRenderCallback(async (value: boolean) => {
|
||||
await settingNotificationSetting.setValue(
|
||||
value ? DEFAULT_NOTIFICATION_SETTING : 'off'
|
||||
);
|
||||
return value;
|
||||
}),
|
||||
onRelayCallsChange: reRender(settingRelayCalls.setValue),
|
||||
onSelectedCameraChange: reRender(settingVideoInput.setValue),
|
||||
onSelectedMicrophoneChange: reRender(settingAudioInput.setValue),
|
||||
onSelectedSpeakerChange: reRender(settingAudioOutput.setValue),
|
||||
onSentMediaQualityChange: reRender(settingSentMediaQuality.setValue),
|
||||
onSpellCheckChange: reRender(settingSpellCheck.setValue),
|
||||
onTextFormattingChange: reRender(settingTextFormatting.setValue),
|
||||
onThemeChange: reRender(settingTheme.setValue),
|
||||
onRelayCallsChange: attachRenderCallback(settingRelayCalls.setValue),
|
||||
onSelectedCameraChange: attachRenderCallback(settingVideoInput.setValue),
|
||||
onSelectedMicrophoneChange: attachRenderCallback(
|
||||
settingAudioInput.setValue
|
||||
),
|
||||
onSelectedSpeakerChange: attachRenderCallback(settingAudioOutput.setValue),
|
||||
onSentMediaQualityChange: attachRenderCallback(
|
||||
settingSentMediaQuality.setValue
|
||||
),
|
||||
onSpellCheckChange: attachRenderCallback(settingSpellCheck.setValue),
|
||||
onTextFormattingChange: attachRenderCallback(
|
||||
settingTextFormatting.setValue
|
||||
),
|
||||
onThemeChange: attachRenderCallback(settingTheme.setValue),
|
||||
onUniversalExpireTimerChange: (newValue: number): Promise<void> => {
|
||||
return onUniversalExpireTimerChange(
|
||||
DurationInSeconds.fromSeconds(newValue)
|
||||
);
|
||||
},
|
||||
|
||||
onWhoCanFindMeChange: reRender(settingPhoneNumberDiscoverability.setValue),
|
||||
onWhoCanSeeMeChange: reRender(settingPhoneNumberSharing.setValue),
|
||||
onWhoCanFindMeChange: attachRenderCallback(
|
||||
settingPhoneNumberDiscoverability.setValue
|
||||
),
|
||||
onWhoCanSeeMeChange: attachRenderCallback(
|
||||
settingPhoneNumberSharing.setValue
|
||||
),
|
||||
|
||||
// Zoom factor change doesn't require immediate rerender since it will:
|
||||
// 1. Update the zoom factor in the main window
|
||||
|
@ -381,28 +416,22 @@ const renderPreferences = async () => {
|
|||
// rerender.
|
||||
onZoomFactorChange: settingZoomFactor.setValue,
|
||||
|
||||
i18n: SignalContext.i18n,
|
||||
|
||||
hasCustomTitleBar: SignalContext.OS.hasCustomTitleBar(),
|
||||
executeMenuRole: SignalContext.executeMenuRole,
|
||||
hasCustomTitleBar: MinimalSignalContext.OS.hasCustomTitleBar(),
|
||||
executeMenuRole: MinimalSignalContext.executeMenuRole,
|
||||
};
|
||||
|
||||
function reRender<Value>(f: (value: Value) => Promise<Value>) {
|
||||
return async (value: Value) => {
|
||||
await f(value);
|
||||
void renderPreferences();
|
||||
};
|
||||
}
|
||||
|
||||
ReactDOM.render(
|
||||
React.createElement(Preferences, props),
|
||||
document.getElementById('app')
|
||||
);
|
||||
};
|
||||
renderInBrowser(props);
|
||||
}
|
||||
|
||||
ipcRenderer.on('preferences-changed', () => renderPreferences());
|
||||
|
||||
contextBridge.exposeInMainWorld('SignalContext', {
|
||||
...SignalContext,
|
||||
renderWindow: renderPreferences,
|
||||
});
|
||||
const Signal = {
|
||||
SettingsWindowProps: {
|
||||
onRender: (renderer: (_props: PropsPreloadType) => void) => {
|
||||
renderInBrowser = renderer;
|
||||
void renderPreferences();
|
||||
},
|
||||
},
|
||||
};
|
||||
contextBridge.exposeInMainWorld('Signal', Signal);
|
||||
contextBridge.exposeInMainWorld('SignalContext', MinimalSignalContext);
|
||||
|
|
18
yarn.lock
18
yarn.lock
|
@ -6182,6 +6182,14 @@ buffer-xor@^1.0.3:
|
|||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9"
|
||||
|
||||
buffer@6.0.3, buffer@^6.0.3:
|
||||
version "6.0.3"
|
||||
resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6"
|
||||
integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==
|
||||
dependencies:
|
||||
base64-js "^1.3.1"
|
||||
ieee754 "^1.2.1"
|
||||
|
||||
buffer@^4.3.0:
|
||||
version "4.9.1"
|
||||
resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.1.tgz#6d1bb601b07a4efced97094132093027c95bc298"
|
||||
|
@ -6198,14 +6206,6 @@ buffer@^5.5.0:
|
|||
base64-js "^1.3.1"
|
||||
ieee754 "^1.1.13"
|
||||
|
||||
buffer@^6.0.3:
|
||||
version "6.0.3"
|
||||
resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6"
|
||||
integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==
|
||||
dependencies:
|
||||
base64-js "^1.3.1"
|
||||
ieee754 "^1.2.1"
|
||||
|
||||
bufferutil@^4.0.1:
|
||||
version "4.0.7"
|
||||
resolved "https://registry.yarnpkg.com/bufferutil/-/bufferutil-4.0.7.tgz#60c0d19ba2c992dd8273d3f73772ffc894c153ad"
|
||||
|
@ -18385,7 +18385,7 @@ utils-merge@1.0.1:
|
|||
resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
|
||||
integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==
|
||||
|
||||
uuid-browser@^3.1.0:
|
||||
uuid-browser@3.1.0, uuid-browser@^3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/uuid-browser/-/uuid-browser-3.1.0.tgz#0f05a40aef74f9e5951e20efbf44b11871e56410"
|
||||
integrity sha1-DwWkCu90+eWVHiDvv0SxGHHlZBA=
|
||||
|
|
Loading…
Reference in a new issue