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