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
48
ts/OS.ts
48
ts/OS.ts
|
@ -1,48 +0,0 @@
|
|||
// Copyright 2018 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { release as osRelease } from 'os';
|
||||
import semver from 'semver';
|
||||
|
||||
const createIsPlatform = (
|
||||
platform: typeof process.platform
|
||||
): ((minVersion?: string) => boolean) => {
|
||||
return minVersion => {
|
||||
if (process.platform !== platform) {
|
||||
return false;
|
||||
}
|
||||
if (minVersion === undefined) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return semver.gte(osRelease(), minVersion);
|
||||
};
|
||||
};
|
||||
|
||||
export const isMacOS = createIsPlatform('darwin');
|
||||
export const isLinux = createIsPlatform('linux');
|
||||
export const isWindows = createIsPlatform('win32');
|
||||
|
||||
// Windows 10 and above
|
||||
export const hasCustomTitleBar = (): boolean =>
|
||||
isWindows('10.0.0') || Boolean(process.env.CUSTOM_TITLEBAR);
|
||||
|
||||
export const getName = (): string => {
|
||||
if (isMacOS()) {
|
||||
return 'macOS';
|
||||
}
|
||||
if (isWindows()) {
|
||||
return 'Windows';
|
||||
}
|
||||
return 'Linux';
|
||||
};
|
||||
|
||||
export const getClassName = (): string => {
|
||||
if (isMacOS()) {
|
||||
return 'os-macos';
|
||||
}
|
||||
if (isWindows()) {
|
||||
return 'os-windows';
|
||||
}
|
||||
return 'os-linux';
|
||||
};
|
|
@ -2,7 +2,7 @@
|
|||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { makeEnumParser } from '../util/enum';
|
||||
import * as OS from '../OS';
|
||||
import OS from '../util/os/osMain';
|
||||
|
||||
export enum AudioDeviceModule {
|
||||
Default = 'Default',
|
||||
|
|
|
@ -4,18 +4,18 @@
|
|||
import type { MouseEvent } from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import copyText from 'copy-text-to-clipboard';
|
||||
import type { ExecuteMenuRoleType } from './TitleBarContainer';
|
||||
import type { LocalizerType } from '../types/Util';
|
||||
import * as Errors from '../types/errors';
|
||||
import * as log from '../logging/log';
|
||||
import { Button, ButtonVariant } from './Button';
|
||||
import type { LocalizerType } from '../types/Util';
|
||||
import { Spinner } from './Spinner';
|
||||
import { TitleBarContainer } from './TitleBarContainer';
|
||||
import { ToastDebugLogError } from './ToastDebugLogError';
|
||||
import { ToastLinkCopied } from './ToastLinkCopied';
|
||||
import { TitleBarContainer } from './TitleBarContainer';
|
||||
import type { ExecuteMenuRoleType } from './TitleBarContainer';
|
||||
import { ToastLoadingFullLogs } from './ToastLoadingFullLogs';
|
||||
import { openLinkInWebBrowser } from '../util/openLinkInWebBrowser';
|
||||
import { createSupportUrl } from '../util/createSupportUrl';
|
||||
import * as Errors from '../types/errors';
|
||||
import { openLinkInWebBrowser } from '../util/openLinkInWebBrowser';
|
||||
import { useEscapeHandling } from '../hooks/useEscapeHandling';
|
||||
import { useTheme } from '../hooks/useTheme';
|
||||
|
||||
|
@ -137,7 +137,7 @@ export function DebugLogWindow({
|
|||
};
|
||||
|
||||
const supportURL = createSupportUrl({
|
||||
locale: i18n.getLocale(),
|
||||
locale: window.SignalContext.getI18nLocale(),
|
||||
query: {
|
||||
debugLog: publicLogURL,
|
||||
},
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
// Copyright 2021 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import type { AudioDevice } from '@signalapp/ringrtc';
|
||||
import type { ReactNode } from 'react';
|
||||
import React, { useEffect, useState, useCallback, useMemo } from 'react';
|
||||
import { noop } from 'lodash';
|
||||
import classNames from 'classnames';
|
||||
import type { AudioDevice } from '@signalapp/ringrtc';
|
||||
import uuid from 'uuid';
|
||||
|
||||
import type { MediaDeviceSettings } from '../types/Calling';
|
||||
|
@ -15,6 +15,19 @@ import type {
|
|||
ZoomFactorType,
|
||||
} from '../types/Storage.d';
|
||||
import type { ThemeSettingType } from '../types/StorageUIKeys';
|
||||
import type { ConversationType } from '../state/ducks/conversations';
|
||||
import type {
|
||||
ConversationColorType,
|
||||
CustomColorType,
|
||||
DefaultConversationColorType,
|
||||
} from '../types/Colors';
|
||||
import type {
|
||||
LocalizerType,
|
||||
SentMediaQualityType,
|
||||
ThemeType,
|
||||
} from '../types/Util';
|
||||
import type { ExecuteMenuRoleType } from './TitleBarContainer';
|
||||
|
||||
import { Button, ButtonVariant } from './Button';
|
||||
import { ChatColorPicker } from './ChatColorPicker';
|
||||
import { Checkbox } from './Checkbox';
|
||||
|
@ -23,24 +36,12 @@ import {
|
|||
Variant as CircleCheckboxVariant,
|
||||
} from './CircleCheckbox';
|
||||
import { ConfirmationDialog } from './ConfirmationDialog';
|
||||
import type { ConversationType } from '../state/ducks/conversations';
|
||||
import type {
|
||||
ConversationColorType,
|
||||
CustomColorType,
|
||||
DefaultConversationColorType,
|
||||
} from '../types/Colors';
|
||||
import { DisappearingTimeDialog } from './DisappearingTimeDialog';
|
||||
import type {
|
||||
LocalizerType,
|
||||
SentMediaQualityType,
|
||||
ThemeType,
|
||||
} from '../types/Util';
|
||||
import { PhoneNumberDiscoverability } from '../util/phoneNumberDiscoverability';
|
||||
import { PhoneNumberSharingMode } from '../util/phoneNumberSharingMode';
|
||||
import { Select } from './Select';
|
||||
import { Spinner } from './Spinner';
|
||||
import { TitleBarContainer } from './TitleBarContainer';
|
||||
import type { ExecuteMenuRoleType } from './TitleBarContainer';
|
||||
import { getCustomColorStyle } from '../util/getCustomColorStyle';
|
||||
import {
|
||||
DEFAULT_DURATIONS_IN_SECONDS,
|
||||
|
@ -179,6 +180,8 @@ type PropsFunctionType = {
|
|||
|
||||
export type PropsType = PropsDataType & PropsFunctionType;
|
||||
|
||||
export type PropsPreloadType = Omit<PropsType, 'i18n'>;
|
||||
|
||||
enum Page {
|
||||
// Accessible through left nav
|
||||
General = 'General',
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
// Copyright 2023 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { ipcRenderer } from 'electron';
|
||||
import { config } from './config';
|
||||
import { localeMessages } from './localeMessages';
|
||||
import { setupI18n } from '../util/setupI18n';
|
||||
import { strictAssert } from '../util/assert';
|
||||
|
||||
|
@ -16,7 +16,6 @@ strictAssert(
|
|||
'locale is not a string'
|
||||
);
|
||||
|
||||
const localeMessages = ipcRenderer.sendSync('locale-data');
|
||||
const i18n = setupI18n(resolvedTranslationsLocale, localeMessages);
|
||||
|
||||
export { i18n };
|
||||
|
|
6
ts/context/localeMessages.ts
Normal file
6
ts/context/localeMessages.ts
Normal file
|
@ -0,0 +1,6 @@
|
|||
// Copyright 2023 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { ipcRenderer } from 'electron';
|
||||
|
||||
export const localeMessages = ipcRenderer.sendSync('locale-data');
|
|
@ -1,8 +1,6 @@
|
|||
// Copyright 2021 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { z } from 'zod';
|
||||
|
||||
import { makeEnumParser } from './util/enum';
|
||||
|
||||
// Many places rely on this enum being a string.
|
||||
|
@ -13,8 +11,6 @@ export enum Environment {
|
|||
Test = 'test',
|
||||
}
|
||||
|
||||
export const environmentSchema = z.nativeEnum(Environment);
|
||||
|
||||
let environment: undefined | Environment;
|
||||
|
||||
export function getEnvironment(): Environment {
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { memoize, sortBy } from 'lodash';
|
||||
import os from 'os';
|
||||
import { ipcRenderer as ipc } from 'electron';
|
||||
import { reallyJsonStringify } from '../util/reallyJsonStringify';
|
||||
import type { FetchLogIpcData, LogEntryType } from './shared';
|
||||
import {
|
||||
|
@ -42,16 +40,18 @@ const getHeader = (
|
|||
user,
|
||||
}: Omit<FetchLogIpcData, 'logEntries'>,
|
||||
nodeVersion: string,
|
||||
appVersion: string
|
||||
appVersion: string,
|
||||
osVersion: string,
|
||||
userAgent: string
|
||||
): string =>
|
||||
[
|
||||
headerSection('System info', {
|
||||
Time: Date.now(),
|
||||
'User agent': window.navigator.userAgent,
|
||||
'User agent': userAgent,
|
||||
'Node version': nodeVersion,
|
||||
Environment: getEnvironment(),
|
||||
'App version': appVersion,
|
||||
'OS version': os.version(),
|
||||
'OS version': osVersion,
|
||||
}),
|
||||
headerSection('User info', user),
|
||||
headerSection('Capabilities', capabilities),
|
||||
|
@ -79,17 +79,18 @@ function formatLine(mightBeEntry: unknown): string {
|
|||
return `${getLevel(entry.level)} ${entry.time} ${entry.msg}`;
|
||||
}
|
||||
|
||||
export async function fetch(
|
||||
export function getLog(
|
||||
data: unknown,
|
||||
nodeVersion: string,
|
||||
appVersion: string
|
||||
): Promise<string> {
|
||||
const data: unknown = await ipc.invoke('fetch-log');
|
||||
|
||||
appVersion: string,
|
||||
osVersion: string,
|
||||
userAgent: string
|
||||
): string {
|
||||
let header: string;
|
||||
let body: string;
|
||||
if (isFetchLogIpcData(data)) {
|
||||
const { logEntries } = data;
|
||||
header = getHeader(data, nodeVersion, appVersion);
|
||||
header = getHeader(data, nodeVersion, appVersion, osVersion, userAgent);
|
||||
body = logEntries.map(formatLine).join('\n');
|
||||
} else {
|
||||
header = headerSectionTitle('Partial logs');
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright 2021 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { noop } from 'lodash';
|
||||
import noop from 'lodash/noop';
|
||||
import type { LogFunction } from '../types/Logging';
|
||||
import { LogLevel } from '../types/Logging';
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright 2020 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { throttle } from 'lodash';
|
||||
import { throttle } from '../util/throttle';
|
||||
|
||||
// Idle timer - you're active for ACTIVE_TIMEOUT after one of these events
|
||||
const ACTIVE_TIMEOUT = 15 * 1000;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
// Copyright 2015 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import os from 'os';
|
||||
import { debounce } from 'lodash';
|
||||
import EventEmitter from 'events';
|
||||
import { Sound } from '../util/Sound';
|
||||
|
@ -9,7 +10,7 @@ import {
|
|||
getAudioNotificationSupport,
|
||||
shouldHideExpiringMessageBody,
|
||||
} from '../types/Settings';
|
||||
import * as OS from '../OS';
|
||||
import OS from '../util/os/osMain';
|
||||
import * as log from '../logging/log';
|
||||
import { makeEnumParser } from '../util/enum';
|
||||
import { missingCaseError } from '../util/missingCaseError';
|
||||
|
@ -144,7 +145,7 @@ class NotificationService extends EventEmitter {
|
|||
|
||||
this.lastNotification?.close();
|
||||
|
||||
const audioNotificationSupport = getAudioNotificationSupport();
|
||||
const audioNotificationSupport = getAudioNotificationSupport(OS);
|
||||
|
||||
const notification = new window.Notification(title, {
|
||||
body: OS.isLinux() ? filterNotificationText(message) : message,
|
||||
|
@ -299,7 +300,10 @@ class NotificationService extends EventEmitter {
|
|||
notificationTitle = senderTitle;
|
||||
({ notificationIconUrl } = notificationData);
|
||||
|
||||
if (isExpiringMessage && shouldHideExpiringMessageBody()) {
|
||||
if (
|
||||
isExpiringMessage &&
|
||||
shouldHideExpiringMessageBody(OS, os.release())
|
||||
) {
|
||||
notificationMessage = i18n('icu:newMessage');
|
||||
} else if (userSetting === NotificationSetting.NameOnly) {
|
||||
if (reaction) {
|
||||
|
|
|
@ -8,7 +8,7 @@ import * as Curve from './Curve';
|
|||
import { start as conversationControllerStart } from './ConversationController';
|
||||
import Data from './sql/Client';
|
||||
import * as Groups from './groups';
|
||||
import * as OS from './OS';
|
||||
import OS from './util/os/osMain';
|
||||
import * as RemoteConfig from './RemoteConfig';
|
||||
|
||||
// Components
|
||||
|
|
|
@ -9,7 +9,7 @@ import type { LocalizerType } from '../../types/Util';
|
|||
import type { MenuOptionsType } from '../../types/menu';
|
||||
import type { NoopActionType } from './noop';
|
||||
import type { UUIDStringType } from '../../types/UUID';
|
||||
import * as OS from '../../OS';
|
||||
import OS from '../../util/os/osMain';
|
||||
import { ThemeType } from '../../types/Util';
|
||||
|
||||
// State
|
||||
|
@ -116,6 +116,7 @@ export function getEmptyState(): UserStateType {
|
|||
getLocale: intlNotSetup,
|
||||
getIntl: intlNotSetup,
|
||||
isLegacyFormat: intlNotSetup,
|
||||
getLocaleMessages: intlNotSetup,
|
||||
getLocaleDirection: intlNotSetup,
|
||||
}),
|
||||
interactionMode: 'mouse',
|
||||
|
|
|
@ -32,7 +32,7 @@ import type { MainWindowStatsType } from '../windows/context';
|
|||
import type { MenuOptionsType } from '../types/menu';
|
||||
import type { StoryDataType } from './ducks/stories';
|
||||
import type { StoryDistributionListDataType } from './ducks/storyDistributionLists';
|
||||
import * as OS from '../OS';
|
||||
import OS from '../util/os/osMain';
|
||||
import { UUIDKind } from '../types/UUID';
|
||||
import { getEmojiReducerState as emojis } from '../util/loadRecentEmojis';
|
||||
import { getInitialState as stickers } from '../types/Stickers';
|
||||
|
@ -132,7 +132,7 @@ export function getInitialState({
|
|||
interactionMode: getInteractionMode(),
|
||||
isMainWindowFullScreen: mainWindowStats.isFullScreen,
|
||||
isMainWindowMaximized: mainWindowStats.isMaximized,
|
||||
localeMessages: window.SignalContext.localeMessages,
|
||||
localeMessages: window.i18n.getLocaleMessages(),
|
||||
menuOptions,
|
||||
osName,
|
||||
ourACI,
|
||||
|
|
|
@ -7,7 +7,7 @@ import type { MenuItemConstructorOptions } from 'electron';
|
|||
|
||||
import type { MenuActionType } from '../../types/menu';
|
||||
import { App } from '../../components/App';
|
||||
import { getName as getOSName, getClassName as getOSClassName } from '../../OS';
|
||||
import OS from '../../util/os/osMain';
|
||||
import { SmartCallManager } from './CallManager';
|
||||
import { SmartGlobalModalContainer } from './GlobalModalContainer';
|
||||
import { SmartLightbox } from './Lightbox';
|
||||
|
@ -47,8 +47,8 @@ const mapStateToProps = (state: StateType) => {
|
|||
isFullScreen: getIsMainWindowFullScreen(state),
|
||||
menuOptions: getMenuOptions(state),
|
||||
hasCustomTitleBar: window.SignalContext.OS.hasCustomTitleBar(),
|
||||
OS: getOSName(),
|
||||
osClassName: getOSClassName(),
|
||||
OS: OS.getName(),
|
||||
osClassName: OS.getClassName(),
|
||||
hideMenuBar: getHideMenuBar(state),
|
||||
renderCallManager: () => (
|
||||
<ModalContainer className="module-calling__modal-container">
|
||||
|
|
|
@ -27,7 +27,7 @@ import { HTTPError } from '../../textsecure/Errors';
|
|||
import { isRecord } from '../../util/isRecord';
|
||||
import * as Errors from '../../types/errors';
|
||||
import { normalizeDeviceName } from '../../util/normalizeDeviceName';
|
||||
import { getName as getOSName } from '../../OS';
|
||||
import OS from '../../util/os/osMain';
|
||||
|
||||
type PropsType = ComponentProps<typeof InstallScreen>;
|
||||
|
||||
|
@ -258,7 +258,7 @@ export function SmartInstallScreen(): ReactElement {
|
|||
updates,
|
||||
currentVersion: window.getVersion(),
|
||||
startUpdate,
|
||||
OS: getOSName(),
|
||||
OS: OS.getName(),
|
||||
},
|
||||
};
|
||||
break;
|
||||
|
|
|
@ -8,7 +8,7 @@ import { UnsupportedOSDialog } from '../../components/UnsupportedOSDialog';
|
|||
import { getIntl } from '../selectors/user';
|
||||
import { getExpirationTimestamp } from '../selectors/expiration';
|
||||
import type { WidthBreakpoint } from '../../components/_util';
|
||||
import { getName as getOSName } from '../../OS';
|
||||
import OS from '../../util/os/osMain';
|
||||
|
||||
export type PropsType = Readonly<{
|
||||
type: 'warning' | 'error';
|
||||
|
@ -18,14 +18,14 @@ export type PropsType = Readonly<{
|
|||
export function SmartUnsupportedOSDialog(ownProps: PropsType): JSX.Element {
|
||||
const i18n = useSelector(getIntl);
|
||||
const expirationTimestamp = useSelector(getExpirationTimestamp);
|
||||
const OS = getOSName();
|
||||
const osName = OS.getName();
|
||||
|
||||
return (
|
||||
<UnsupportedOSDialog
|
||||
{...ownProps}
|
||||
i18n={i18n}
|
||||
expirationTimestamp={expirationTimestamp}
|
||||
OS={OS}
|
||||
OS={osName}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ import type { StateType } from '../reducer';
|
|||
import { getIntl } from '../selectors/user';
|
||||
import { getExpirationTimestamp } from '../selectors/expiration';
|
||||
import type { WidthBreakpoint } from '../../components/_util';
|
||||
import { getName as getOSName } from '../../OS';
|
||||
import OS from '../../util/os/osMain';
|
||||
|
||||
type PropsType = Readonly<{ containerWidthBreakpoint: WidthBreakpoint }>;
|
||||
|
||||
|
@ -18,7 +18,7 @@ const mapStateToProps = (state: StateType, ownProps: PropsType) => {
|
|||
i18n: getIntl(state),
|
||||
currentVersion: window.getVersion(),
|
||||
expirationTimestamp: getExpirationTimestamp(state),
|
||||
OS: getOSName(),
|
||||
OS: OS.getName(),
|
||||
...ownProps,
|
||||
};
|
||||
};
|
||||
|
|
|
@ -5,6 +5,7 @@ import os from 'os';
|
|||
import Sinon from 'sinon';
|
||||
import { assert } from 'chai';
|
||||
|
||||
import { getOSFunctions } from '../../util/os/shared';
|
||||
import * as Settings from '../../types/Settings';
|
||||
|
||||
describe('Settings', () => {
|
||||
|
@ -21,8 +22,9 @@ describe('Settings', () => {
|
|||
describe('getAudioNotificationSupport', () => {
|
||||
it('returns native support on macOS', () => {
|
||||
sandbox.stub(process, 'platform').value('darwin');
|
||||
const OS = getOSFunctions(os.release());
|
||||
assert.strictEqual(
|
||||
Settings.getAudioNotificationSupport(),
|
||||
Settings.getAudioNotificationSupport(OS),
|
||||
Settings.AudioNotificationSupport.Native
|
||||
);
|
||||
});
|
||||
|
@ -30,8 +32,9 @@ describe('Settings', () => {
|
|||
it('returns no support on Windows 7', () => {
|
||||
sandbox.stub(process, 'platform').value('win32');
|
||||
sandbox.stub(os, 'release').returns('7.0.0');
|
||||
const OS = getOSFunctions(os.release());
|
||||
assert.strictEqual(
|
||||
Settings.getAudioNotificationSupport(),
|
||||
Settings.getAudioNotificationSupport(OS),
|
||||
Settings.AudioNotificationSupport.None
|
||||
);
|
||||
});
|
||||
|
@ -39,16 +42,18 @@ describe('Settings', () => {
|
|||
it('returns native support on Windows 8', () => {
|
||||
sandbox.stub(process, 'platform').value('win32');
|
||||
sandbox.stub(os, 'release').returns('8.0.0');
|
||||
const OS = getOSFunctions(os.release());
|
||||
assert.strictEqual(
|
||||
Settings.getAudioNotificationSupport(),
|
||||
Settings.getAudioNotificationSupport(OS),
|
||||
Settings.AudioNotificationSupport.Native
|
||||
);
|
||||
});
|
||||
|
||||
it('returns custom support on Linux', () => {
|
||||
sandbox.stub(process, 'platform').value('linux');
|
||||
const OS = getOSFunctions(os.release());
|
||||
assert.strictEqual(
|
||||
Settings.getAudioNotificationSupport(),
|
||||
Settings.getAudioNotificationSupport(OS),
|
||||
Settings.AudioNotificationSupport.Custom
|
||||
);
|
||||
});
|
||||
|
@ -57,48 +62,56 @@ describe('Settings', () => {
|
|||
describe('isAudioNotificationSupported', () => {
|
||||
it('returns true on macOS', () => {
|
||||
sandbox.stub(process, 'platform').value('darwin');
|
||||
assert.isTrue(Settings.isAudioNotificationSupported());
|
||||
const OS = getOSFunctions(os.release());
|
||||
assert.isTrue(Settings.isAudioNotificationSupported(OS));
|
||||
});
|
||||
|
||||
it('returns false on Windows 7', () => {
|
||||
sandbox.stub(process, 'platform').value('win32');
|
||||
sandbox.stub(os, 'release').returns('7.0.0');
|
||||
assert.isFalse(Settings.isAudioNotificationSupported());
|
||||
const OS = getOSFunctions(os.release());
|
||||
assert.isFalse(Settings.isAudioNotificationSupported(OS));
|
||||
});
|
||||
|
||||
it('returns true on Windows 8', () => {
|
||||
sandbox.stub(process, 'platform').value('win32');
|
||||
sandbox.stub(os, 'release').returns('8.0.0');
|
||||
assert.isTrue(Settings.isAudioNotificationSupported());
|
||||
const OS = getOSFunctions(os.release());
|
||||
assert.isTrue(Settings.isAudioNotificationSupported(OS));
|
||||
});
|
||||
|
||||
it('returns true on Linux', () => {
|
||||
sandbox.stub(process, 'platform').value('linux');
|
||||
assert.isTrue(Settings.isAudioNotificationSupported());
|
||||
const OS = getOSFunctions(os.release());
|
||||
assert.isTrue(Settings.isAudioNotificationSupported(OS));
|
||||
});
|
||||
});
|
||||
|
||||
describe('isNotificationGroupingSupported', () => {
|
||||
it('returns true on macOS', () => {
|
||||
sandbox.stub(process, 'platform').value('darwin');
|
||||
assert.isTrue(Settings.isNotificationGroupingSupported());
|
||||
const OS = getOSFunctions(os.release());
|
||||
assert.isTrue(Settings.isNotificationGroupingSupported(OS));
|
||||
});
|
||||
|
||||
it('returns true on Windows 7', () => {
|
||||
sandbox.stub(process, 'platform').value('win32');
|
||||
sandbox.stub(os, 'release').returns('7.0.0');
|
||||
assert.isFalse(Settings.isNotificationGroupingSupported());
|
||||
const OS = getOSFunctions(os.release());
|
||||
assert.isFalse(Settings.isNotificationGroupingSupported(OS));
|
||||
});
|
||||
|
||||
it('returns true on Windows 8', () => {
|
||||
sandbox.stub(process, 'platform').value('win32');
|
||||
sandbox.stub(os, 'release').returns('8.0.0');
|
||||
assert.isTrue(Settings.isNotificationGroupingSupported());
|
||||
const OS = getOSFunctions(os.release());
|
||||
assert.isTrue(Settings.isNotificationGroupingSupported(OS));
|
||||
});
|
||||
|
||||
it('returns true on Linux', () => {
|
||||
sandbox.stub(process, 'platform').value('linux');
|
||||
assert.isTrue(Settings.isNotificationGroupingSupported());
|
||||
const OS = getOSFunctions(os.release());
|
||||
assert.isTrue(Settings.isNotificationGroupingSupported(OS));
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -106,88 +119,103 @@ describe('Settings', () => {
|
|||
it('returns true on Windows', () => {
|
||||
sandbox.stub(process, 'platform').value('win32');
|
||||
sandbox.stub(os, 'release').returns('8.0.0');
|
||||
assert.isTrue(Settings.isAutoLaunchSupported());
|
||||
const OS = getOSFunctions(os.release());
|
||||
assert.isTrue(Settings.isAutoLaunchSupported(OS));
|
||||
});
|
||||
|
||||
it('returns true on macOS', () => {
|
||||
sandbox.stub(process, 'platform').value('darwin');
|
||||
assert.isTrue(Settings.isAutoLaunchSupported());
|
||||
const OS = getOSFunctions(os.release());
|
||||
assert.isTrue(Settings.isAutoLaunchSupported(OS));
|
||||
});
|
||||
|
||||
it('returns false on Linux', () => {
|
||||
sandbox.stub(process, 'platform').value('linux');
|
||||
assert.isFalse(Settings.isAutoLaunchSupported());
|
||||
const OS = getOSFunctions(os.release());
|
||||
assert.isFalse(Settings.isAutoLaunchSupported(OS));
|
||||
});
|
||||
});
|
||||
|
||||
describe('isHideMenuBarSupported', () => {
|
||||
it('returns false on macOS', () => {
|
||||
sandbox.stub(process, 'platform').value('darwin');
|
||||
assert.isFalse(Settings.isHideMenuBarSupported());
|
||||
const OS = getOSFunctions(os.release());
|
||||
assert.isFalse(Settings.isHideMenuBarSupported(OS));
|
||||
});
|
||||
|
||||
it('returns true on Windows 7', () => {
|
||||
sandbox.stub(process, 'platform').value('win32');
|
||||
sandbox.stub(os, 'release').returns('7.0.0');
|
||||
assert.isTrue(Settings.isHideMenuBarSupported());
|
||||
const OS = getOSFunctions(os.release());
|
||||
assert.isTrue(Settings.isHideMenuBarSupported(OS));
|
||||
});
|
||||
|
||||
it('returns true on Windows 8', () => {
|
||||
sandbox.stub(process, 'platform').value('win32');
|
||||
sandbox.stub(os, 'release').returns('8.0.0');
|
||||
assert.isTrue(Settings.isHideMenuBarSupported());
|
||||
const OS = getOSFunctions(os.release());
|
||||
assert.isTrue(Settings.isHideMenuBarSupported(OS));
|
||||
});
|
||||
|
||||
it('returns true on Linux', () => {
|
||||
sandbox.stub(process, 'platform').value('linux');
|
||||
assert.isTrue(Settings.isHideMenuBarSupported());
|
||||
const OS = getOSFunctions(os.release());
|
||||
assert.isTrue(Settings.isHideMenuBarSupported(OS));
|
||||
});
|
||||
});
|
||||
|
||||
describe('isDrawAttentionSupported', () => {
|
||||
it('returns false on macOS', () => {
|
||||
sandbox.stub(process, 'platform').value('darwin');
|
||||
assert.isFalse(Settings.isDrawAttentionSupported());
|
||||
const OS = getOSFunctions(os.release());
|
||||
assert.isFalse(Settings.isDrawAttentionSupported(OS));
|
||||
});
|
||||
|
||||
it('returns true on Windows 7', () => {
|
||||
sandbox.stub(process, 'platform').value('win32');
|
||||
sandbox.stub(os, 'release').returns('7.0.0');
|
||||
assert.isTrue(Settings.isDrawAttentionSupported());
|
||||
const OS = getOSFunctions(os.release());
|
||||
assert.isTrue(Settings.isDrawAttentionSupported(OS));
|
||||
});
|
||||
|
||||
it('returns true on Windows 8', () => {
|
||||
sandbox.stub(process, 'platform').value('win32');
|
||||
sandbox.stub(os, 'release').returns('8.0.0');
|
||||
assert.isTrue(Settings.isDrawAttentionSupported());
|
||||
const OS = getOSFunctions(os.release());
|
||||
assert.isTrue(Settings.isDrawAttentionSupported(OS));
|
||||
});
|
||||
|
||||
it('returns true on Linux', () => {
|
||||
sandbox.stub(process, 'platform').value('linux');
|
||||
assert.isTrue(Settings.isDrawAttentionSupported());
|
||||
const OS = getOSFunctions(os.release());
|
||||
assert.isTrue(Settings.isDrawAttentionSupported(OS));
|
||||
});
|
||||
});
|
||||
|
||||
describe('isSystemTraySupported', () => {
|
||||
it('returns false on macOS', () => {
|
||||
sandbox.stub(process, 'platform').value('darwin');
|
||||
assert.isFalse(Settings.isSystemTraySupported('1.2.3'));
|
||||
const OS = getOSFunctions(os.release());
|
||||
assert.isFalse(Settings.isSystemTraySupported(OS, '1.2.3'));
|
||||
});
|
||||
|
||||
it('returns true on Windows 8', () => {
|
||||
sandbox.stub(process, 'platform').value('win32');
|
||||
sandbox.stub(os, 'release').returns('8.0.0');
|
||||
assert.isTrue(Settings.isSystemTraySupported('1.2.3'));
|
||||
const OS = getOSFunctions(os.release());
|
||||
assert.isTrue(Settings.isSystemTraySupported(OS, '1.2.3'));
|
||||
});
|
||||
|
||||
it('returns false on Linux production', () => {
|
||||
sandbox.stub(process, 'platform').value('linux');
|
||||
assert.isFalse(Settings.isSystemTraySupported('1.2.3'));
|
||||
const OS = getOSFunctions(os.release());
|
||||
assert.isFalse(Settings.isSystemTraySupported(OS, '1.2.3'));
|
||||
});
|
||||
|
||||
it('returns true on Linux beta', () => {
|
||||
sandbox.stub(process, 'platform').value('linux');
|
||||
assert.isTrue(Settings.isSystemTraySupported('1.2.3-beta.4'));
|
||||
const OS = getOSFunctions(os.release());
|
||||
assert.isTrue(Settings.isSystemTraySupported(OS, '1.2.3-beta.4'));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -715,7 +715,7 @@ export default class MessageSender {
|
|||
storyMessage.fileAttachment = fileAttachment;
|
||||
} catch (error) {
|
||||
if (error instanceof HTTPError) {
|
||||
throw new MessageError(message, error);
|
||||
throw new MessageError(storyMessage, error);
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
|
|
|
@ -3,8 +3,10 @@
|
|||
|
||||
import { z } from 'zod';
|
||||
|
||||
import { Environment } from '../environment';
|
||||
import { themeSettingSchema } from './StorageUIKeys';
|
||||
import { environmentSchema } from '../environment';
|
||||
|
||||
const environmentSchema = z.nativeEnum(Environment);
|
||||
|
||||
const configRequiredStringSchema = z.string().nonempty();
|
||||
export type ConfigRequiredStringType = z.infer<
|
||||
|
@ -39,6 +41,8 @@ export const rendererConfigSchema = z.object({
|
|||
environment: environmentSchema,
|
||||
homePath: configRequiredStringSchema,
|
||||
hostname: configRequiredStringSchema,
|
||||
osRelease: configRequiredStringSchema,
|
||||
osVersion: configRequiredStringSchema,
|
||||
resolvedTranslationsLocale: configRequiredStringSchema,
|
||||
resolvedTranslationsLocaleDirection: z.enum(['ltr', 'rtl']),
|
||||
preferredSystemLocales: z.array(configRequiredStringSchema),
|
||||
|
|
|
@ -2,9 +2,8 @@
|
|||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import semver from 'semver';
|
||||
import os from 'os';
|
||||
|
||||
import * as OS from '../OS';
|
||||
import type { OSType } from '../util/os/shared';
|
||||
import { isProduction } from '../util/version';
|
||||
|
||||
const MIN_WINDOWS_VERSION = '8.0.0';
|
||||
|
@ -15,7 +14,9 @@ export enum AudioNotificationSupport {
|
|||
Custom,
|
||||
}
|
||||
|
||||
export function getAudioNotificationSupport(): AudioNotificationSupport {
|
||||
export function getAudioNotificationSupport(
|
||||
OS: OSType
|
||||
): AudioNotificationSupport {
|
||||
if (OS.isWindows(MIN_WINDOWS_VERSION) || OS.isMacOS()) {
|
||||
return AudioNotificationSupport.Native;
|
||||
}
|
||||
|
@ -25,42 +26,48 @@ export function getAudioNotificationSupport(): AudioNotificationSupport {
|
|||
return AudioNotificationSupport.None;
|
||||
}
|
||||
|
||||
export const isAudioNotificationSupported = (): boolean =>
|
||||
getAudioNotificationSupport() !== AudioNotificationSupport.None;
|
||||
export const isAudioNotificationSupported = (OS: OSType): boolean =>
|
||||
getAudioNotificationSupport(OS) !== AudioNotificationSupport.None;
|
||||
|
||||
// Using `Notification::tag` has a bug on Windows 7:
|
||||
// https://github.com/electron/electron/issues/11189
|
||||
export const isNotificationGroupingSupported = (): boolean =>
|
||||
export const isNotificationGroupingSupported = (OS: OSType): boolean =>
|
||||
!OS.isWindows() || OS.isWindows(MIN_WINDOWS_VERSION);
|
||||
|
||||
// Login item settings are only supported on macOS and Windows, according to [Electron's
|
||||
// docs][0].
|
||||
// [0]: https://www.electronjs.org/docs/api/app#appsetloginitemsettingssettings-macos-windows
|
||||
export const isAutoLaunchSupported = (): boolean =>
|
||||
export const isAutoLaunchSupported = (OS: OSType): boolean =>
|
||||
OS.isWindows() || OS.isMacOS();
|
||||
|
||||
// the "hide menu bar" option is specific to Windows and Linux
|
||||
export const isHideMenuBarSupported = (): boolean => !OS.isMacOS();
|
||||
export const isHideMenuBarSupported = (OS: OSType): boolean => !OS.isMacOS();
|
||||
|
||||
// the "draw attention on notification" option is specific to Windows and Linux
|
||||
export const isDrawAttentionSupported = (): boolean => !OS.isMacOS();
|
||||
export const isDrawAttentionSupported = (OS: OSType): boolean => !OS.isMacOS();
|
||||
|
||||
/**
|
||||
* Returns `true` if you can minimize the app to the system tray. Users can override this
|
||||
* option with a command line flag, but that is not officially supported.
|
||||
*/
|
||||
export const isSystemTraySupported = (appVersion: string): boolean =>
|
||||
export const isSystemTraySupported = (
|
||||
OS: OSType,
|
||||
appVersion: string
|
||||
): boolean =>
|
||||
// We eventually want to support Linux in production.
|
||||
OS.isWindows() || (OS.isLinux() && !isProduction(appVersion));
|
||||
|
||||
// On Windows minimize and start in system tray is default when app is selected
|
||||
// to launch at login, because we can provide `['--start-in-tray']` args.
|
||||
export const isMinimizeToAndStartInSystemTraySupported = (
|
||||
OS: OSType,
|
||||
appVersion: string
|
||||
): boolean => !OS.isWindows() && isSystemTraySupported(appVersion);
|
||||
): boolean => !OS.isWindows() && isSystemTraySupported(OS, appVersion);
|
||||
|
||||
export const isAutoDownloadUpdatesSupported = (): boolean =>
|
||||
export const isAutoDownloadUpdatesSupported = (OS: OSType): boolean =>
|
||||
OS.isWindows() || OS.isMacOS();
|
||||
|
||||
export const shouldHideExpiringMessageBody = (): boolean =>
|
||||
OS.isWindows() || (OS.isMacOS() && semver.lt(os.release(), '21.1.0'));
|
||||
export const shouldHideExpiringMessageBody = (
|
||||
OS: OSType,
|
||||
release: string
|
||||
): boolean => OS.isWindows() || (OS.isMacOS() && semver.lt(release, '21.1.0'));
|
||||
|
|
|
@ -5,6 +5,8 @@ import type { IntlShape } from 'react-intl';
|
|||
import type { UUIDStringType } from './UUID';
|
||||
import type { LocaleDirection } from '../../app/locale';
|
||||
|
||||
import type { LocaleMessagesType } from './I18N';
|
||||
|
||||
export type StoryContextType = {
|
||||
authorUuid?: UUIDStringType;
|
||||
timestamp: number;
|
||||
|
@ -24,6 +26,7 @@ export type LocalizerType = {
|
|||
getIntl(): IntlShape;
|
||||
isLegacyFormat(key: string): boolean;
|
||||
getLocale(): string;
|
||||
getLocaleMessages(): LocaleMessagesType;
|
||||
getLocaleDirection(): LocaleDirection;
|
||||
};
|
||||
|
||||
|
|
|
@ -1,20 +1,18 @@
|
|||
// Copyright 2018 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { get, has } from 'lodash';
|
||||
|
||||
export function toLogFormat(error: unknown): string {
|
||||
let result = '';
|
||||
if (error instanceof Error && error.stack) {
|
||||
result = error.stack;
|
||||
} else if (has(error, 'message')) {
|
||||
result = get(error, 'message');
|
||||
} else if (error && typeof error === 'object' && 'message' in error) {
|
||||
result = String(error.message);
|
||||
} else {
|
||||
result = String(error);
|
||||
}
|
||||
|
||||
if (has(error, 'cause')) {
|
||||
result += `\nCaused by: ${String(get(error, 'cause'))}`;
|
||||
if (error && typeof error === 'object' && 'cause' in error) {
|
||||
result += `\nCaused by: ${String(error.cause)}`;
|
||||
}
|
||||
|
||||
return result;
|
||||
|
|
|
@ -2525,7 +2525,7 @@
|
|||
{
|
||||
"rule": "DOM-innerHTML",
|
||||
"path": "ts/windows/loading/start.ts",
|
||||
"line": " message.innerHTML = window.SignalContext.i18n('icu:optimizingApplication');",
|
||||
"line": " message.innerHTML = window.i18n('icu:optimizingApplication');",
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2021-09-17T21:02:59.414Z"
|
||||
},
|
||||
|
|
|
@ -24,8 +24,7 @@ const excludedFilesRegexp = RegExp(
|
|||
[
|
||||
'^release/',
|
||||
'^preload.bundle.js(LICENSE.txt|map)?',
|
||||
'^about.browser.bundle.js(LICENSE.txt|map)?',
|
||||
'^about.preload.bundle.js(LICENSE.txt|map)?',
|
||||
'^bundles/',
|
||||
'^storybook-static/',
|
||||
|
||||
// Non-distributed files
|
||||
|
|
9
ts/util/os/osMain.ts
Normal file
9
ts/util/os/osMain.ts
Normal file
|
@ -0,0 +1,9 @@
|
|||
// Copyright 2023 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import os from 'os';
|
||||
import { getOSFunctions } from './shared';
|
||||
|
||||
const OS = getOSFunctions(os.release());
|
||||
|
||||
export default OS;
|
9
ts/util/os/osPreload.ts
Normal file
9
ts/util/os/osPreload.ts
Normal file
|
@ -0,0 +1,9 @@
|
|||
// Copyright 2023 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { config } from '../../context/config';
|
||||
import { getOSFunctions } from './shared';
|
||||
|
||||
const OS = getOSFunctions(config.osRelease);
|
||||
|
||||
export default OS;
|
68
ts/util/os/shared.ts
Normal file
68
ts/util/os/shared.ts
Normal file
|
@ -0,0 +1,68 @@
|
|||
// Copyright 2018 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import semver from 'semver';
|
||||
|
||||
function createIsPlatform(
|
||||
platform: typeof process.platform,
|
||||
osRelease: string
|
||||
): (minVersion?: string) => boolean {
|
||||
return minVersion => {
|
||||
if (process.platform !== platform) {
|
||||
return false;
|
||||
}
|
||||
if (minVersion === undefined) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return semver.gte(osRelease, minVersion);
|
||||
};
|
||||
}
|
||||
|
||||
export type OSType = {
|
||||
getClassName: () => string;
|
||||
getName: () => string;
|
||||
hasCustomTitleBar: () => boolean;
|
||||
isLinux: (minVersion?: string) => boolean;
|
||||
isMacOS: (minVersion?: string) => boolean;
|
||||
isWindows: (minVersion?: string) => boolean;
|
||||
};
|
||||
|
||||
export function getOSFunctions(osRelease: string): OSType {
|
||||
const isMacOS = createIsPlatform('darwin', osRelease);
|
||||
const isLinux = createIsPlatform('linux', osRelease);
|
||||
const isWindows = createIsPlatform('win32', osRelease);
|
||||
|
||||
// Windows 10 and above
|
||||
const hasCustomTitleBar = (): boolean =>
|
||||
isWindows('10.0.0') || Boolean(process.env.CUSTOM_TITLEBAR);
|
||||
|
||||
const getName = (): string => {
|
||||
if (isMacOS()) {
|
||||
return 'macOS';
|
||||
}
|
||||
if (isWindows()) {
|
||||
return 'Windows';
|
||||
}
|
||||
return 'Linux';
|
||||
};
|
||||
|
||||
const getClassName = (): string => {
|
||||
if (isMacOS()) {
|
||||
return 'os-macos';
|
||||
}
|
||||
if (isWindows()) {
|
||||
return 'os-windows';
|
||||
}
|
||||
return 'os-linux';
|
||||
};
|
||||
|
||||
return {
|
||||
getClassName,
|
||||
getName,
|
||||
hasCustomTitleBar,
|
||||
isLinux,
|
||||
isMacOS,
|
||||
isWindows,
|
||||
};
|
||||
}
|
|
@ -172,6 +172,7 @@ export function setupI18n(
|
|||
return legacyMessages[key] != null;
|
||||
};
|
||||
getMessage.getLocale = () => locale;
|
||||
getMessage.getLocaleMessages = () => messages;
|
||||
getMessage.getLocaleDirection = () => {
|
||||
return window.getResolvedMessagesLocaleDirection();
|
||||
};
|
||||
|
|
70
ts/util/throttle.ts
Normal file
70
ts/util/throttle.ts
Normal file
|
@ -0,0 +1,70 @@
|
|||
// Copyright 2023 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
export function throttle(func: Function, wait: number): () => void {
|
||||
let lastCallTime: number;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
let lastArgs: Array<any> | undefined;
|
||||
let timerId: NodeJS.Timeout | undefined;
|
||||
|
||||
function call() {
|
||||
const args = lastArgs || [];
|
||||
lastArgs = undefined;
|
||||
func(...args);
|
||||
}
|
||||
|
||||
function leading() {
|
||||
timerId = setTimeout(timerExpired, wait);
|
||||
call();
|
||||
}
|
||||
|
||||
function remainingWait(time: number) {
|
||||
const timeSinceLastCall = time - lastCallTime;
|
||||
return wait - timeSinceLastCall;
|
||||
}
|
||||
|
||||
function shouldInvoke(time: number) {
|
||||
const timeSinceLastCall = time - lastCallTime;
|
||||
|
||||
return (
|
||||
lastCallTime === undefined ||
|
||||
timeSinceLastCall >= wait ||
|
||||
timeSinceLastCall < 0
|
||||
);
|
||||
}
|
||||
|
||||
function timerExpired() {
|
||||
const time = Date.now();
|
||||
if (shouldInvoke(time)) {
|
||||
return trailing();
|
||||
}
|
||||
timerId = setTimeout(timerExpired, remainingWait(time));
|
||||
}
|
||||
|
||||
function trailing() {
|
||||
timerId = undefined;
|
||||
|
||||
if (lastArgs) {
|
||||
return call();
|
||||
}
|
||||
lastArgs = undefined;
|
||||
}
|
||||
|
||||
return (...args) => {
|
||||
const time = Date.now();
|
||||
const isInvoking = shouldInvoke(time);
|
||||
|
||||
lastArgs = args;
|
||||
lastCallTime = time;
|
||||
|
||||
if (isInvoking) {
|
||||
if (timerId === undefined) {
|
||||
return leading();
|
||||
}
|
||||
}
|
||||
if (timerId === undefined) {
|
||||
timerId = setTimeout(timerExpired, wait);
|
||||
}
|
||||
};
|
||||
}
|
|
@ -2,7 +2,6 @@
|
|||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import * as semver from 'semver';
|
||||
import moment from 'moment';
|
||||
|
||||
export const isProduction = (version: string): boolean => {
|
||||
const parsed = semver.parse(version);
|
||||
|
@ -34,7 +33,22 @@ export const generateAlphaVersion = (options: {
|
|||
throw new Error(`generateAlphaVersion: Invalid version ${currentVersion}`);
|
||||
}
|
||||
|
||||
const formattedDate = moment().utc().format('YYYYMMDD.HH');
|
||||
const dateTimeParts = new Intl.DateTimeFormat('en', {
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
hourCycle: 'h23',
|
||||
month: '2-digit',
|
||||
timeZone: 'GMT',
|
||||
year: 'numeric',
|
||||
}).formatToParts(new Date());
|
||||
const dateTimeMap = new Map();
|
||||
dateTimeParts.forEach(({ type, value }) => {
|
||||
dateTimeMap.set(type, value);
|
||||
});
|
||||
const formattedDate = `${dateTimeMap.get('year')}${dateTimeMap.get(
|
||||
'month'
|
||||
)}${dateTimeMap.get('day')}.${dateTimeMap.get('hour')}`;
|
||||
|
||||
const formattedVersion = `${parsed.major}.${parsed.minor}.${parsed.patch}`;
|
||||
|
||||
return `${formattedVersion}-alpha.${formattedDate}-${shortSha}`;
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import * as fs from 'fs';
|
||||
import { isWindows } from '../OS';
|
||||
import OS from './os/osMain';
|
||||
|
||||
const ZONE_IDENTIFIER_CONTENTS = Buffer.from('[ZoneTransfer]\r\nZoneId=3');
|
||||
|
||||
|
@ -27,7 +27,7 @@ const ZONE_IDENTIFIER_CONTENTS = Buffer.from('[ZoneTransfer]\r\nZoneId=3');
|
|||
export async function writeWindowsZoneIdentifier(
|
||||
filePath: string
|
||||
): Promise<void> {
|
||||
if (!isWindows()) {
|
||||
if (!OS.isWindows()) {
|
||||
throw new Error('writeWindowsZoneIdentifier should only run on Windows');
|
||||
}
|
||||
|
||||
|
|
44
ts/window.d.ts
vendored
44
ts/window.d.ts
vendored
|
@ -3,7 +3,6 @@
|
|||
|
||||
// Captures the globals put in place by preload.js, background.js and others
|
||||
|
||||
import type { MenuItemConstructorOptions } from 'electron';
|
||||
import type { Store } from 'redux';
|
||||
import type * as Backbone from 'backbone';
|
||||
import type PQueue from 'p-queue/dist';
|
||||
|
@ -28,7 +27,7 @@ import type * as Groups from './groups';
|
|||
import type * as Crypto from './Crypto';
|
||||
import type * as Curve from './Curve';
|
||||
import type * as RemoteConfig from './RemoteConfig';
|
||||
import type * as OS from './OS';
|
||||
import type { OSType } from './util/os/shared';
|
||||
import type { getEnvironment } from './environment';
|
||||
import type { LocalizerType, ThemeType } from './types/Util';
|
||||
import type { Receipt } from './types/Receipt';
|
||||
|
@ -56,6 +55,7 @@ import type { SignalContextType } from './windows/context';
|
|||
import type * as Message2 from './types/Message2';
|
||||
import type { initializeMigrations } from './signal';
|
||||
import type { RetryPlaceholders } from './util/retryPlaceholders';
|
||||
import type { PropsPreloadType as PreferencesPropsType } from './components/Preferences';
|
||||
import type { LocaleDirection } from '../app/locale';
|
||||
|
||||
export { Long } from 'long';
|
||||
|
@ -103,21 +103,46 @@ export type FeatureFlagType = {
|
|||
GV2_MIGRATION_DISABLE_INVITE: boolean;
|
||||
};
|
||||
|
||||
type AboutWindowType = {
|
||||
type AboutWindowPropsType = {
|
||||
arch: string;
|
||||
environmentText: string;
|
||||
executeMenuRole: (role: MenuItemConstructorOptions['role']) => Promise<void>;
|
||||
hasCustomTitleBar: boolean;
|
||||
i18n: LocalizerType;
|
||||
version: string;
|
||||
platform: string;
|
||||
};
|
||||
|
||||
type DebugLogWindowPropsType = {
|
||||
downloadLog: (text: string) => unknown;
|
||||
fetchLogs: () => Promise<string>;
|
||||
uploadLogs: (text: string) => Promise<string>;
|
||||
};
|
||||
|
||||
type PermissionsWindowPropsType = {
|
||||
forCamera: boolean;
|
||||
forCalling: boolean;
|
||||
onAccept: () => void;
|
||||
onClose: () => void;
|
||||
};
|
||||
|
||||
type ScreenShareWindowPropsType = {
|
||||
onStopSharing: () => void;
|
||||
presentedSourceName: string;
|
||||
};
|
||||
|
||||
type SettingsOnRenderCallbackType = (props: PreferencesPropsType) => void;
|
||||
|
||||
type SettingsWindowPropsType = {
|
||||
onRender: (callback: SettingsOnRenderCallbackType) => void;
|
||||
};
|
||||
|
||||
export type SignalCoreType = {
|
||||
AboutWindow?: AboutWindowType;
|
||||
AboutWindowProps?: AboutWindowPropsType;
|
||||
Crypto: typeof Crypto;
|
||||
Curve: typeof Curve;
|
||||
Data: typeof Data;
|
||||
DebugLogWindowProps?: DebugLogWindowPropsType;
|
||||
Groups: typeof Groups;
|
||||
PermissionsWindowProps?: PermissionsWindowPropsType;
|
||||
RemoteConfig: typeof RemoteConfig;
|
||||
ScreenShareWindowProps?: ScreenShareWindowPropsType;
|
||||
Services: {
|
||||
calling: CallingClass;
|
||||
initializeGroupCredentialFetcher: () => Promise<void>;
|
||||
|
@ -127,6 +152,7 @@ export type SignalCoreType = {
|
|||
lightSessionResetQueue?: PQueue;
|
||||
storage: typeof StorageService;
|
||||
};
|
||||
SettingsWindowProps?: SettingsWindowPropsType;
|
||||
Migrations: ReturnType<typeof initializeMigrations>;
|
||||
Types: {
|
||||
Message: typeof Message2;
|
||||
|
@ -137,7 +163,7 @@ export type SignalCoreType = {
|
|||
Components: {
|
||||
ConfirmationDialog: typeof ConfirmationDialog;
|
||||
};
|
||||
OS: typeof OS;
|
||||
OS: OSType;
|
||||
State: {
|
||||
createStore: typeof createStore;
|
||||
Roots: {
|
||||
|
|
|
@ -5,20 +5,32 @@ import React from 'react';
|
|||
import ReactDOM from 'react-dom';
|
||||
|
||||
import { About } from '../../components/About';
|
||||
import { i18n } from '../sandboxedInit';
|
||||
import { strictAssert } from '../../util/assert';
|
||||
|
||||
const { AboutWindow } = window.Signal;
|
||||
const { AboutWindowProps } = window.Signal;
|
||||
|
||||
strictAssert(AboutWindow, 'window values not provided');
|
||||
strictAssert(AboutWindowProps, 'window values not provided');
|
||||
|
||||
let platform = '';
|
||||
if (AboutWindowProps.platform === 'darwin') {
|
||||
if (AboutWindowProps.arch === 'arm64') {
|
||||
platform = ` (${window.i18n('icu:appleSilicon')})`;
|
||||
} else {
|
||||
platform = ' (Intel)';
|
||||
}
|
||||
}
|
||||
|
||||
const environmentText = `${AboutWindowProps.environmentText}${platform}`;
|
||||
|
||||
ReactDOM.render(
|
||||
<About
|
||||
closeAbout={() => AboutWindow.executeMenuRole('close')}
|
||||
environment={AboutWindow.environmentText}
|
||||
executeMenuRole={AboutWindow.executeMenuRole}
|
||||
hasCustomTitleBar={AboutWindow.hasCustomTitleBar}
|
||||
i18n={AboutWindow.i18n}
|
||||
version={AboutWindow.version}
|
||||
closeAbout={() => window.SignalContext.executeMenuRole('close')}
|
||||
environment={environmentText}
|
||||
executeMenuRole={window.SignalContext.executeMenuRole}
|
||||
hasCustomTitleBar={window.SignalContext.OS.hasCustomTitleBar()}
|
||||
i18n={i18n}
|
||||
version={window.SignalContext.getVersion()}
|
||||
/>,
|
||||
document.getElementById('app')
|
||||
);
|
||||
|
|
|
@ -1,22 +1,10 @@
|
|||
// Copyright 2018 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import type { MenuItemConstructorOptions } from 'electron';
|
||||
import { contextBridge, ipcRenderer } from 'electron';
|
||||
import { activeWindowService } from '../../context/activeWindowService';
|
||||
import { contextBridge } from 'electron';
|
||||
import { MinimalSignalContext } from '../minimalContext';
|
||||
import { config } from '../../context/config';
|
||||
import { createNativeThemeListener } from '../../context/createNativeThemeListener';
|
||||
import { createSetting } from '../../util/preload';
|
||||
import { environment } from '../../context/environment';
|
||||
import { getClassName } from '../../OS';
|
||||
import { i18n } from '../../context/i18n';
|
||||
import { waitForSettingsChange } from '../../context/waitForSettingsChange';
|
||||
|
||||
async function executeMenuRole(
|
||||
role: MenuItemConstructorOptions['role']
|
||||
): Promise<void> {
|
||||
await ipcRenderer.invoke('executeMenuRole', role);
|
||||
}
|
||||
|
||||
const environments: Array<string> = [environment];
|
||||
|
||||
|
@ -24,40 +12,12 @@ if (config.appInstance) {
|
|||
environments.push(String(config.appInstance));
|
||||
}
|
||||
|
||||
let platform = '';
|
||||
if (process.platform === 'darwin') {
|
||||
if (process.arch === 'arm64') {
|
||||
platform = ` (${i18n('icu:appleSilicon')})`;
|
||||
} else {
|
||||
platform = ' (Intel)';
|
||||
}
|
||||
}
|
||||
|
||||
const environmentText = `${environments.join(' - ')}${platform}`;
|
||||
const hasCustomTitleBar = ipcRenderer.sendSync('getHasCustomTitleBar');
|
||||
|
||||
const Signal = {
|
||||
AboutWindow: {
|
||||
environmentText,
|
||||
executeMenuRole,
|
||||
hasCustomTitleBar,
|
||||
i18n,
|
||||
version: String(config.version),
|
||||
AboutWindowProps: {
|
||||
arch: process.arch,
|
||||
environmentText: environments.join(' - '),
|
||||
platform: process.platform,
|
||||
},
|
||||
};
|
||||
contextBridge.exposeInMainWorld('Signal', Signal);
|
||||
|
||||
// TODO DESKTOP-5054
|
||||
const SignalContext = {
|
||||
activeWindowService,
|
||||
OS: {
|
||||
getClassName,
|
||||
hasCustomTitleBar: () => hasCustomTitleBar,
|
||||
},
|
||||
Settings: {
|
||||
themeSetting: createSetting('themeSetting', { setter: false }),
|
||||
waitForChange: waitForSettingsChange,
|
||||
},
|
||||
nativeThemeListener: createNativeThemeListener(ipcRenderer, window),
|
||||
};
|
||||
contextBridge.exposeInMainWorld('SignalContext', SignalContext);
|
||||
contextBridge.exposeInMainWorld('SignalContext', MinimalSignalContext);
|
||||
|
|
|
@ -12,7 +12,7 @@ import * as Bytes from '../Bytes';
|
|||
|
||||
import { isPathInside } from '../util/isPathInside';
|
||||
import { writeWindowsZoneIdentifier } from '../util/windowsZoneIdentifier';
|
||||
import { isWindows } from '../OS';
|
||||
import OS from '../util/os/osMain';
|
||||
|
||||
export * from '../../app/attachments';
|
||||
|
||||
|
@ -229,7 +229,7 @@ async function writeWithAttributes(
|
|||
const attrValue = `${type};${timestamp};${appName};${guid}`;
|
||||
|
||||
await xattr.set(target, 'com.apple.quarantine', attrValue);
|
||||
} else if (isWindows()) {
|
||||
} else if (OS.isWindows()) {
|
||||
// This operation may fail (see the function's comments), which is not a show-stopper.
|
||||
try {
|
||||
await writeWindowsZoneIdentifier(target);
|
||||
|
|
|
@ -8,7 +8,6 @@ import type { MenuOptionsType, MenuActionType } from '../types/menu';
|
|||
import type { IPCEventsValuesType } from '../util/createIPCEvents';
|
||||
import type { LocalizerType } from '../types/Util';
|
||||
import type { LoggerType } from '../types/Logging';
|
||||
import type { LocaleMessagesType } from '../types/I18N';
|
||||
import type { NativeThemeType } from '../context/createNativeThemeListener';
|
||||
import type { SettingType } from '../util/preload';
|
||||
import type { RendererConfigType } from '../types/RendererConfig';
|
||||
|
@ -18,29 +17,10 @@ import { Crypto } from '../context/Crypto';
|
|||
import { Timers } from '../context/Timers';
|
||||
|
||||
import type { ActiveWindowServiceType } from '../services/ActiveWindowService';
|
||||
import { config } from '../context/config';
|
||||
import { i18n } from '../context/i18n';
|
||||
import { activeWindowService } from '../context/activeWindowService';
|
||||
import {
|
||||
getEnvironment,
|
||||
parseEnvironment,
|
||||
setEnvironment,
|
||||
} from '../environment';
|
||||
import { strictAssert } from '../util/assert';
|
||||
import { createSetting } from '../util/preload';
|
||||
import { initialize as initializeLogging } from '../logging/set_up_renderer_logging';
|
||||
import { waitForSettingsChange } from '../context/waitForSettingsChange';
|
||||
import { createNativeThemeListener } from '../context/createNativeThemeListener';
|
||||
import {
|
||||
isWindows,
|
||||
isLinux,
|
||||
isMacOS,
|
||||
hasCustomTitleBar,
|
||||
getClassName,
|
||||
} from '../OS';
|
||||
|
||||
const localeMessages = ipcRenderer.sendSync('locale-data');
|
||||
setEnvironment(parseEnvironment(config.environment));
|
||||
import { MinimalSignalContext } from './minimalContext';
|
||||
|
||||
strictAssert(Boolean(window.SignalContext), 'context must be defined');
|
||||
|
||||
|
@ -51,89 +31,53 @@ export type MainWindowStatsType = Readonly<{
|
|||
isFullScreen: boolean;
|
||||
}>;
|
||||
|
||||
export type SignalContextType = {
|
||||
bytes: Bytes;
|
||||
crypto: Crypto;
|
||||
timers: Timers;
|
||||
nativeThemeListener: NativeThemeType;
|
||||
setIsCallActive: (isCallActive: boolean) => unknown;
|
||||
|
||||
export type MinimalSignalContextType = {
|
||||
activeWindowService: ActiveWindowServiceType;
|
||||
config: RendererConfigType;
|
||||
executeMenuAction: (action: MenuActionType) => Promise<void>;
|
||||
executeMenuRole: (role: MenuItemConstructorOptions['role']) => Promise<void>;
|
||||
getAppInstance: () => string | undefined;
|
||||
getEnvironment: () => string;
|
||||
getI18nLocale: LocalizerType['getLocale'];
|
||||
getI18nLocaleMessages: LocalizerType['getLocaleMessages'];
|
||||
getMainWindowStats: () => Promise<MainWindowStatsType>;
|
||||
getMenuOptions: () => Promise<MenuOptionsType>;
|
||||
getNodeVersion: () => string;
|
||||
getPath: (name: 'userData' | 'home') => string;
|
||||
getVersion: () => string;
|
||||
nativeThemeListener: NativeThemeType;
|
||||
Settings: {
|
||||
themeSetting: SettingType<IPCEventsValuesType['themeSetting']>;
|
||||
waitForChange: () => Promise<void>;
|
||||
};
|
||||
OS: {
|
||||
hasCustomTitleBar: () => boolean;
|
||||
getClassName: () => string;
|
||||
platform: string;
|
||||
isWindows: typeof isWindows;
|
||||
isLinux: typeof isLinux;
|
||||
isMacOS: typeof isMacOS;
|
||||
hasCustomTitleBar: typeof hasCustomTitleBar;
|
||||
getClassName: typeof getClassName;
|
||||
release: string;
|
||||
};
|
||||
config: RendererConfigType;
|
||||
getAppInstance: () => string | undefined;
|
||||
getEnvironment: () => string;
|
||||
getNodeVersion: () => string;
|
||||
getVersion: () => string;
|
||||
getPath: (name: 'userData' | 'home') => string;
|
||||
i18n: LocalizerType;
|
||||
localeMessages: LocaleMessagesType;
|
||||
log: LoggerType;
|
||||
renderWindow?: () => void;
|
||||
executeMenuRole: (role: MenuItemConstructorOptions['role']) => Promise<void>;
|
||||
getMainWindowStats: () => Promise<MainWindowStatsType>;
|
||||
getMenuOptions: () => Promise<MenuOptionsType>;
|
||||
executeMenuAction: (action: MenuActionType) => Promise<void>;
|
||||
};
|
||||
|
||||
export type SignalContextType = {
|
||||
bytes: Bytes;
|
||||
crypto: Crypto;
|
||||
i18n: LocalizerType;
|
||||
log: LoggerType;
|
||||
renderWindow?: () => void;
|
||||
setIsCallActive: (isCallActive: boolean) => unknown;
|
||||
timers: Timers;
|
||||
} & MinimalSignalContextType;
|
||||
|
||||
export const SignalContext: SignalContextType = {
|
||||
activeWindowService,
|
||||
Settings: {
|
||||
themeSetting: createSetting('themeSetting', { setter: false }),
|
||||
waitForChange: waitForSettingsChange,
|
||||
},
|
||||
OS: {
|
||||
platform: process.platform,
|
||||
isWindows,
|
||||
isLinux,
|
||||
isMacOS,
|
||||
hasCustomTitleBar,
|
||||
getClassName,
|
||||
},
|
||||
...MinimalSignalContext,
|
||||
bytes: new Bytes(),
|
||||
config,
|
||||
crypto: new Crypto(),
|
||||
getAppInstance: (): string | undefined =>
|
||||
config.appInstance ? String(config.appInstance) : undefined,
|
||||
getEnvironment,
|
||||
getNodeVersion: (): string => String(config.nodeVersion),
|
||||
getVersion: (): string => String(config.version),
|
||||
getPath: (name: 'userData' | 'home'): string => {
|
||||
return String(config[`${name}Path`]);
|
||||
},
|
||||
i18n,
|
||||
localeMessages,
|
||||
log: window.SignalContext.log,
|
||||
nativeThemeListener: createNativeThemeListener(ipcRenderer, window),
|
||||
setIsCallActive(isCallActive: boolean): void {
|
||||
ipcRenderer.send('set-is-call-active', isCallActive);
|
||||
},
|
||||
timers: new Timers(),
|
||||
async executeMenuRole(
|
||||
role: MenuItemConstructorOptions['role']
|
||||
): Promise<void> {
|
||||
await ipcRenderer.invoke('executeMenuRole', role);
|
||||
},
|
||||
async getMainWindowStats(): Promise<MainWindowStatsType> {
|
||||
return ipcRenderer.invoke('getMainWindowStats');
|
||||
},
|
||||
async getMenuOptions(): Promise<MenuOptionsType> {
|
||||
return ipcRenderer.invoke('getMenuOptions');
|
||||
},
|
||||
async executeMenuAction(action: MenuActionType): Promise<void> {
|
||||
return ipcRenderer.invoke('executeMenuAction', action);
|
||||
},
|
||||
};
|
||||
|
||||
window.SignalContext = SignalContext;
|
||||
|
|
25
ts/windows/debuglog/app.tsx
Normal file
25
ts/windows/debuglog/app.tsx
Normal file
|
@ -0,0 +1,25 @@
|
|||
// Copyright 2023 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import React from 'react';
|
||||
import { render } from 'react-dom';
|
||||
import { DebugLogWindow } from '../../components/DebugLogWindow';
|
||||
import { i18n } from '../sandboxedInit';
|
||||
import { strictAssert } from '../../util/assert';
|
||||
|
||||
const { DebugLogWindowProps } = window.Signal;
|
||||
|
||||
strictAssert(DebugLogWindowProps, 'window values not provided');
|
||||
|
||||
render(
|
||||
<DebugLogWindow
|
||||
hasCustomTitleBar={window.SignalContext.OS.hasCustomTitleBar()}
|
||||
executeMenuRole={window.SignalContext.executeMenuRole}
|
||||
closeWindow={() => window.SignalContext.executeMenuRole('close')}
|
||||
downloadLog={DebugLogWindowProps.downloadLog}
|
||||
i18n={i18n}
|
||||
fetchLogs={DebugLogWindowProps.fetchLogs}
|
||||
uploadLogs={DebugLogWindowProps.uploadLogs}
|
||||
/>,
|
||||
document.getElementById('app')
|
||||
);
|
|
@ -1,49 +1,32 @@
|
|||
// Copyright 2021 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { contextBridge, ipcRenderer } from 'electron';
|
||||
import { MinimalSignalContext } from '../minimalContext';
|
||||
|
||||
import { SignalContext } from '../context';
|
||||
import { DebugLogWindow } from '../../components/DebugLogWindow';
|
||||
import * as debugLog from '../../logging/debuglogs';
|
||||
import { upload } from '../../logging/uploadDebugLog';
|
||||
import * as logger from '../../logging/log';
|
||||
function downloadLog(logText: string) {
|
||||
ipcRenderer.send('show-debug-log-save-dialog', logText);
|
||||
}
|
||||
|
||||
contextBridge.exposeInMainWorld('SignalContext', {
|
||||
...SignalContext,
|
||||
renderWindow: () => {
|
||||
const environmentText: Array<string> = [SignalContext.getEnvironment()];
|
||||
async function fetchLogs() {
|
||||
const data = await ipcRenderer.invoke('fetch-log');
|
||||
return ipcRenderer.invoke(
|
||||
'DebugLogs.getLogs',
|
||||
data,
|
||||
window.navigator.userAgent
|
||||
);
|
||||
}
|
||||
|
||||
const appInstance = SignalContext.getAppInstance();
|
||||
if (appInstance) {
|
||||
environmentText.push(appInstance);
|
||||
}
|
||||
function uploadLogs(logs: string) {
|
||||
return ipcRenderer.invoke('DebugLogs.upload', logs);
|
||||
}
|
||||
|
||||
ReactDOM.render(
|
||||
React.createElement(DebugLogWindow, {
|
||||
hasCustomTitleBar: SignalContext.OS.hasCustomTitleBar(),
|
||||
executeMenuRole: SignalContext.executeMenuRole,
|
||||
closeWindow: () => SignalContext.executeMenuRole('close'),
|
||||
downloadLog: (logText: string) =>
|
||||
ipcRenderer.send('show-debug-log-save-dialog', logText),
|
||||
i18n: SignalContext.i18n,
|
||||
fetchLogs() {
|
||||
return debugLog.fetch(
|
||||
SignalContext.getNodeVersion(),
|
||||
SignalContext.getVersion()
|
||||
);
|
||||
},
|
||||
uploadLogs(logs: string) {
|
||||
return upload({
|
||||
content: logs,
|
||||
appVersion: SignalContext.getVersion(),
|
||||
logger,
|
||||
});
|
||||
},
|
||||
}),
|
||||
document.getElementById('app')
|
||||
);
|
||||
const Signal = {
|
||||
DebugLogWindowProps: {
|
||||
downloadLog,
|
||||
fetchLogs,
|
||||
uploadLogs,
|
||||
},
|
||||
});
|
||||
};
|
||||
contextBridge.exposeInMainWorld('Signal', Signal);
|
||||
contextBridge.exposeInMainWorld('SignalContext', MinimalSignalContext);
|
||||
|
|
|
@ -2,7 +2,10 @@
|
|||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { contextBridge } from 'electron';
|
||||
import { config } from '../../context/config';
|
||||
import { localeMessages } from '../../context/localeMessages';
|
||||
|
||||
import { SignalContext } from '../context';
|
||||
|
||||
contextBridge.exposeInMainWorld('SignalContext', SignalContext);
|
||||
contextBridge.exposeInMainWorld('SignalContext', {
|
||||
getI18nLocale: () => config.resolvedTranslationsLocale,
|
||||
getI18nLocaleMessages: () => localeMessages,
|
||||
});
|
||||
|
|
|
@ -1,7 +1,14 @@
|
|||
// Copyright 2020 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { setupI18n } from '../../util/setupI18n';
|
||||
|
||||
window.i18n = setupI18n(
|
||||
window.SignalContext.getI18nLocale(),
|
||||
window.SignalContext.getI18nLocaleMessages()
|
||||
);
|
||||
|
||||
const message = document.getElementById('message');
|
||||
if (message) {
|
||||
message.innerHTML = window.SignalContext.i18n('icu:optimizingApplication');
|
||||
message.innerHTML = window.i18n('icu:optimizingApplication');
|
||||
}
|
||||
|
|
56
ts/windows/minimalContext.ts
Normal file
56
ts/windows/minimalContext.ts
Normal file
|
@ -0,0 +1,56 @@
|
|||
// Copyright 2023 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import type { MenuItemConstructorOptions } from 'electron';
|
||||
import { ipcRenderer } from 'electron';
|
||||
|
||||
import type { MenuOptionsType, MenuActionType } from '../types/menu';
|
||||
import type { MainWindowStatsType, MinimalSignalContextType } from './context';
|
||||
import { activeWindowService } from '../context/activeWindowService';
|
||||
import { config } from '../context/config';
|
||||
import { createNativeThemeListener } from '../context/createNativeThemeListener';
|
||||
import { createSetting } from '../util/preload';
|
||||
import { environment } from '../context/environment';
|
||||
import { localeMessages } from '../context/localeMessages';
|
||||
import { waitForSettingsChange } from '../context/waitForSettingsChange';
|
||||
|
||||
const hasCustomTitleBar = ipcRenderer.sendSync('OS.getHasCustomTitleBar');
|
||||
export const MinimalSignalContext: MinimalSignalContextType = {
|
||||
activeWindowService,
|
||||
config,
|
||||
async executeMenuAction(action: MenuActionType): Promise<void> {
|
||||
return ipcRenderer.invoke('executeMenuAction', action);
|
||||
},
|
||||
async executeMenuRole(
|
||||
role: MenuItemConstructorOptions['role']
|
||||
): Promise<void> {
|
||||
await ipcRenderer.invoke('executeMenuRole', role);
|
||||
},
|
||||
getAppInstance: (): string | undefined =>
|
||||
config.appInstance ? String(config.appInstance) : undefined,
|
||||
getEnvironment: () => environment,
|
||||
getNodeVersion: (): string => String(config.nodeVersion),
|
||||
getPath: (name: 'userData' | 'home'): string => {
|
||||
return String(config[`${name}Path`]);
|
||||
},
|
||||
getVersion: (): string => String(config.version),
|
||||
async getMainWindowStats(): Promise<MainWindowStatsType> {
|
||||
return ipcRenderer.invoke('getMainWindowStats');
|
||||
},
|
||||
async getMenuOptions(): Promise<MenuOptionsType> {
|
||||
return ipcRenderer.invoke('getMenuOptions');
|
||||
},
|
||||
getI18nLocale: () => config.resolvedTranslationsLocale,
|
||||
getI18nLocaleMessages: () => localeMessages,
|
||||
nativeThemeListener: createNativeThemeListener(ipcRenderer, window),
|
||||
OS: {
|
||||
getClassName: () => ipcRenderer.sendSync('OS.getClassName'),
|
||||
hasCustomTitleBar: () => hasCustomTitleBar,
|
||||
platform: process.platform,
|
||||
release: config.osRelease,
|
||||
},
|
||||
Settings: {
|
||||
themeSetting: createSetting('themeSetting', { setter: false }),
|
||||
waitForChange: waitForSettingsChange,
|
||||
},
|
||||
};
|
36
ts/windows/permissions/app.tsx
Normal file
36
ts/windows/permissions/app.tsx
Normal file
|
@ -0,0 +1,36 @@
|
|||
// Copyright 2023 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
|
||||
import { PermissionsPopup } from '../../components/PermissionsPopup';
|
||||
import { i18n } from '../sandboxedInit';
|
||||
import { strictAssert } from '../../util/assert';
|
||||
|
||||
const { PermissionsWindowProps } = window.Signal;
|
||||
|
||||
strictAssert(PermissionsWindowProps, 'window values not provided');
|
||||
|
||||
const { forCalling, forCamera } = PermissionsWindowProps;
|
||||
|
||||
let message;
|
||||
if (forCalling) {
|
||||
if (forCamera) {
|
||||
message = i18n('icu:videoCallingPermissionNeeded');
|
||||
} else {
|
||||
message = i18n('icu:audioCallingPermissionNeeded');
|
||||
}
|
||||
} else {
|
||||
message = i18n('icu:audioPermissionNeeded');
|
||||
}
|
||||
|
||||
ReactDOM.render(
|
||||
<PermissionsPopup
|
||||
i18n={i18n}
|
||||
message={message}
|
||||
onAccept={PermissionsWindowProps.onAccept}
|
||||
onClose={PermissionsWindowProps.onClose}
|
||||
/>,
|
||||
document.getElementById('app')
|
||||
);
|
|
@ -1,14 +1,10 @@
|
|||
// Copyright 2021 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { contextBridge } from 'electron';
|
||||
|
||||
import { SignalContext } from '../context';
|
||||
|
||||
import { MinimalSignalContext } from '../minimalContext';
|
||||
import { createSetting } from '../../util/preload';
|
||||
import { PermissionsPopup } from '../../components/PermissionsPopup';
|
||||
import { drop } from '../../util/drop';
|
||||
|
||||
const mediaCameraPermissions = createSetting('mediaCameraPermissions', {
|
||||
getter: false,
|
||||
|
@ -17,48 +13,28 @@ const mediaPermissions = createSetting('mediaPermissions', {
|
|||
getter: false,
|
||||
});
|
||||
|
||||
contextBridge.exposeInMainWorld(
|
||||
'nativeThemeListener',
|
||||
window.SignalContext.nativeThemeListener
|
||||
);
|
||||
const params = new URLSearchParams(document.location.search);
|
||||
const forCalling = params.get('forCalling') === 'true';
|
||||
const forCamera = params.get('forCamera') === 'true';
|
||||
|
||||
contextBridge.exposeInMainWorld('SignalContext', {
|
||||
...SignalContext,
|
||||
renderWindow: () => {
|
||||
const params = new URLSearchParams(document.location.search);
|
||||
const forCalling = params.get('forCalling') === 'true';
|
||||
const forCamera = params.get('forCamera') === 'true';
|
||||
function onClose() {
|
||||
drop(MinimalSignalContext.executeMenuRole('close'));
|
||||
}
|
||||
|
||||
let message;
|
||||
if (forCalling) {
|
||||
if (forCamera) {
|
||||
message = SignalContext.i18n('icu:videoCallingPermissionNeeded');
|
||||
const Signal = {
|
||||
PermissionsWindowProps: {
|
||||
forCalling,
|
||||
forCamera,
|
||||
onAccept: () => {
|
||||
if (!forCamera) {
|
||||
drop(mediaPermissions.setValue(true));
|
||||
} else {
|
||||
message = SignalContext.i18n('icu:audioCallingPermissionNeeded');
|
||||
drop(mediaCameraPermissions.setValue(true));
|
||||
}
|
||||
} else {
|
||||
message = SignalContext.i18n('icu:audioPermissionNeeded');
|
||||
}
|
||||
|
||||
function onClose() {
|
||||
void SignalContext.executeMenuRole('close');
|
||||
}
|
||||
|
||||
ReactDOM.render(
|
||||
React.createElement(PermissionsPopup, {
|
||||
i18n: SignalContext.i18n,
|
||||
message,
|
||||
onAccept: () => {
|
||||
if (!forCamera) {
|
||||
void mediaPermissions.setValue(true);
|
||||
} else {
|
||||
void mediaCameraPermissions.setValue(true);
|
||||
}
|
||||
onClose();
|
||||
},
|
||||
onClose,
|
||||
}),
|
||||
document.getElementById('app')
|
||||
);
|
||||
onClose();
|
||||
},
|
||||
onClose,
|
||||
},
|
||||
});
|
||||
};
|
||||
contextBridge.exposeInMainWorld('Signal', Signal);
|
||||
contextBridge.exposeInMainWorld('SignalContext', MinimalSignalContext);
|
||||
|
|
15
ts/windows/sandboxedInit.ts
Normal file
15
ts/windows/sandboxedInit.ts
Normal file
|
@ -0,0 +1,15 @@
|
|||
// Copyright 2023 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import './applyTheme';
|
||||
import { setupI18n } from '../util/setupI18n';
|
||||
|
||||
document.body.classList.add(window.SignalContext.OS.getClassName());
|
||||
if (window.SignalContext.OS.hasCustomTitleBar()) {
|
||||
document.body.classList.add('os-has-custom-titlebar');
|
||||
}
|
||||
|
||||
export const i18n = setupI18n(
|
||||
window.SignalContext.getI18nLocale(),
|
||||
window.SignalContext.getI18nLocaleMessages()
|
||||
);
|
24
ts/windows/screenShare/app.tsx
Normal file
24
ts/windows/screenShare/app.tsx
Normal file
|
@ -0,0 +1,24 @@
|
|||
// Copyright 2023 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
|
||||
import { CallingScreenSharingController } from '../../components/CallingScreenSharingController';
|
||||
import { i18n } from '../sandboxedInit';
|
||||
import { strictAssert } from '../../util/assert';
|
||||
|
||||
const { ScreenShareWindowProps } = window.Signal;
|
||||
|
||||
strictAssert(ScreenShareWindowProps, 'window values not provided');
|
||||
|
||||
ReactDOM.render(
|
||||
<CallingScreenSharingController
|
||||
i18n={i18n}
|
||||
onCloseController={() => window.SignalContext.executeMenuRole('close')}
|
||||
onStopSharing={ScreenShareWindowProps.onStopSharing}
|
||||
presentedSourceName={ScreenShareWindowProps.presentedSourceName}
|
||||
/>,
|
||||
|
||||
document.getElementById('app')
|
||||
);
|
|
@ -1,29 +1,18 @@
|
|||
// Copyright 2021 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { contextBridge, ipcRenderer } from 'electron';
|
||||
import { MinimalSignalContext } from '../minimalContext';
|
||||
|
||||
import { SignalContext } from '../context';
|
||||
import { CallingScreenSharingController } from '../../components/CallingScreenSharingController';
|
||||
const params = new URLSearchParams(document.location.search);
|
||||
|
||||
contextBridge.exposeInMainWorld('SignalContext', SignalContext);
|
||||
|
||||
function renderScreenSharingController(presentedSourceName: string): void {
|
||||
ReactDOM.render(
|
||||
React.createElement(CallingScreenSharingController, {
|
||||
platform: process.platform,
|
||||
executeMenuRole: SignalContext.executeMenuRole,
|
||||
i18n: SignalContext.i18n,
|
||||
onCloseController: () => SignalContext.executeMenuRole('close'),
|
||||
onStopSharing: () => ipcRenderer.send('stop-screen-share'),
|
||||
presentedSourceName,
|
||||
}),
|
||||
document.getElementById('app')
|
||||
);
|
||||
}
|
||||
|
||||
ipcRenderer.once('render-screen-sharing-controller', (_, name: string) => {
|
||||
renderScreenSharingController(name);
|
||||
});
|
||||
const Signal = {
|
||||
ScreenShareWindowProps: {
|
||||
onStopSharing: () => {
|
||||
ipcRenderer.send('stop-screen-share');
|
||||
},
|
||||
presentedSourceName: params.get('sourceName'),
|
||||
},
|
||||
};
|
||||
contextBridge.exposeInMainWorld('Signal', Signal);
|
||||
contextBridge.exposeInMainWorld('SignalContext', MinimalSignalContext);
|
||||
|
|
221
ts/windows/settings/app.tsx
Normal file
221
ts/windows/settings/app.tsx
Normal file
|
@ -0,0 +1,221 @@
|
|||
// Copyright 2023 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
|
||||
import type { PropsPreloadType } from '../../components/Preferences';
|
||||
import { i18n } from '../sandboxedInit';
|
||||
import { Preferences } from '../../components/Preferences';
|
||||
import { startInteractionMode } from '../../services/InteractionMode';
|
||||
import { strictAssert } from '../../util/assert';
|
||||
|
||||
const { SettingsWindowProps } = window.Signal;
|
||||
|
||||
strictAssert(SettingsWindowProps, 'window values not provided');
|
||||
|
||||
startInteractionMode();
|
||||
|
||||
SettingsWindowProps.onRender(
|
||||
({
|
||||
addCustomColor,
|
||||
availableCameras,
|
||||
availableMicrophones,
|
||||
availableSpeakers,
|
||||
blockedCount,
|
||||
closeSettings,
|
||||
customColors,
|
||||
defaultConversationColor,
|
||||
deviceName,
|
||||
doDeleteAllData,
|
||||
doneRendering,
|
||||
editCustomColor,
|
||||
executeMenuRole,
|
||||
getConversationsWithCustomColor,
|
||||
hasAudioNotifications,
|
||||
hasAutoDownloadUpdate,
|
||||
hasAutoLaunch,
|
||||
hasCallNotifications,
|
||||
hasCallRingtoneNotification,
|
||||
hasCountMutedConversations,
|
||||
hasCustomTitleBar,
|
||||
hasHideMenuBar,
|
||||
hasIncomingCallNotifications,
|
||||
hasLinkPreviews,
|
||||
hasMediaCameraPermissions,
|
||||
hasMediaPermissions,
|
||||
hasMinimizeToAndStartInSystemTray,
|
||||
hasMinimizeToSystemTray,
|
||||
hasNotificationAttention,
|
||||
hasNotifications,
|
||||
hasReadReceipts,
|
||||
hasRelayCalls,
|
||||
hasSpellCheck,
|
||||
hasStoriesDisabled,
|
||||
hasTextFormatting,
|
||||
hasTypingIndicators,
|
||||
initialSpellCheckSetting,
|
||||
isAudioNotificationsSupported,
|
||||
isAutoDownloadUpdatesSupported,
|
||||
isAutoLaunchSupported,
|
||||
isFormattingFlagEnabled,
|
||||
isHideMenuBarSupported,
|
||||
isMinimizeToAndStartInSystemTraySupported,
|
||||
isNotificationAttentionSupported,
|
||||
isPhoneNumberSharingSupported,
|
||||
isSyncSupported,
|
||||
isSystemTraySupported,
|
||||
lastSyncTime,
|
||||
makeSyncRequest,
|
||||
notificationContent,
|
||||
onAudioNotificationsChange,
|
||||
onAutoDownloadUpdateChange,
|
||||
onAutoLaunchChange,
|
||||
onCallNotificationsChange,
|
||||
onCallRingtoneNotificationChange,
|
||||
onCountMutedConversationsChange,
|
||||
onHasStoriesDisabledChanged,
|
||||
onHideMenuBarChange,
|
||||
onIncomingCallNotificationsChange,
|
||||
onLastSyncTimeChange,
|
||||
onMediaCameraPermissionsChange,
|
||||
onMediaPermissionsChange,
|
||||
onMinimizeToAndStartInSystemTrayChange,
|
||||
onMinimizeToSystemTrayChange,
|
||||
onNotificationAttentionChange,
|
||||
onNotificationContentChange,
|
||||
onNotificationsChange,
|
||||
onRelayCallsChange,
|
||||
onSelectedCameraChange,
|
||||
onSelectedMicrophoneChange,
|
||||
onSelectedSpeakerChange,
|
||||
onSentMediaQualityChange,
|
||||
onSpellCheckChange,
|
||||
onTextFormattingChange,
|
||||
onThemeChange,
|
||||
onUniversalExpireTimerChange,
|
||||
onWhoCanFindMeChange,
|
||||
onWhoCanSeeMeChange,
|
||||
onZoomFactorChange,
|
||||
removeCustomColor,
|
||||
removeCustomColorOnConversations,
|
||||
resetAllChatColors,
|
||||
resetDefaultChatColor,
|
||||
selectedCamera,
|
||||
selectedMicrophone,
|
||||
selectedSpeaker,
|
||||
sentMediaQualitySetting,
|
||||
setGlobalDefaultConversationColor,
|
||||
shouldShowStoriesSettings,
|
||||
themeSetting,
|
||||
universalExpireTimer,
|
||||
whoCanFindMe,
|
||||
whoCanSeeMe,
|
||||
zoomFactor,
|
||||
}: PropsPreloadType) => {
|
||||
ReactDOM.render(
|
||||
<Preferences
|
||||
addCustomColor={addCustomColor}
|
||||
availableCameras={availableCameras}
|
||||
availableMicrophones={availableMicrophones}
|
||||
availableSpeakers={availableSpeakers}
|
||||
blockedCount={blockedCount}
|
||||
closeSettings={closeSettings}
|
||||
customColors={customColors}
|
||||
defaultConversationColor={defaultConversationColor}
|
||||
deviceName={deviceName}
|
||||
doDeleteAllData={doDeleteAllData}
|
||||
doneRendering={doneRendering}
|
||||
editCustomColor={editCustomColor}
|
||||
executeMenuRole={executeMenuRole}
|
||||
getConversationsWithCustomColor={getConversationsWithCustomColor}
|
||||
hasAudioNotifications={hasAudioNotifications}
|
||||
hasAutoDownloadUpdate={hasAutoDownloadUpdate}
|
||||
hasAutoLaunch={hasAutoLaunch}
|
||||
hasCallNotifications={hasCallNotifications}
|
||||
hasCallRingtoneNotification={hasCallRingtoneNotification}
|
||||
hasCountMutedConversations={hasCountMutedConversations}
|
||||
hasCustomTitleBar={hasCustomTitleBar}
|
||||
hasHideMenuBar={hasHideMenuBar}
|
||||
hasIncomingCallNotifications={hasIncomingCallNotifications}
|
||||
hasLinkPreviews={hasLinkPreviews}
|
||||
hasMediaCameraPermissions={hasMediaCameraPermissions}
|
||||
hasMediaPermissions={hasMediaPermissions}
|
||||
hasMinimizeToAndStartInSystemTray={hasMinimizeToAndStartInSystemTray}
|
||||
hasMinimizeToSystemTray={hasMinimizeToSystemTray}
|
||||
hasNotificationAttention={hasNotificationAttention}
|
||||
hasNotifications={hasNotifications}
|
||||
hasReadReceipts={hasReadReceipts}
|
||||
hasRelayCalls={hasRelayCalls}
|
||||
hasSpellCheck={hasSpellCheck}
|
||||
hasStoriesDisabled={hasStoriesDisabled}
|
||||
hasTextFormatting={hasTextFormatting}
|
||||
hasTypingIndicators={hasTypingIndicators}
|
||||
i18n={i18n}
|
||||
initialSpellCheckSetting={initialSpellCheckSetting}
|
||||
isAudioNotificationsSupported={isAudioNotificationsSupported}
|
||||
isAutoDownloadUpdatesSupported={isAutoDownloadUpdatesSupported}
|
||||
isAutoLaunchSupported={isAutoLaunchSupported}
|
||||
isFormattingFlagEnabled={isFormattingFlagEnabled}
|
||||
isHideMenuBarSupported={isHideMenuBarSupported}
|
||||
isMinimizeToAndStartInSystemTraySupported={
|
||||
isMinimizeToAndStartInSystemTraySupported
|
||||
}
|
||||
isNotificationAttentionSupported={isNotificationAttentionSupported}
|
||||
isPhoneNumberSharingSupported={isPhoneNumberSharingSupported}
|
||||
isSyncSupported={isSyncSupported}
|
||||
isSystemTraySupported={isSystemTraySupported}
|
||||
lastSyncTime={lastSyncTime}
|
||||
makeSyncRequest={makeSyncRequest}
|
||||
notificationContent={notificationContent}
|
||||
onAudioNotificationsChange={onAudioNotificationsChange}
|
||||
onAutoDownloadUpdateChange={onAutoDownloadUpdateChange}
|
||||
onAutoLaunchChange={onAutoLaunchChange}
|
||||
onCallNotificationsChange={onCallNotificationsChange}
|
||||
onCallRingtoneNotificationChange={onCallRingtoneNotificationChange}
|
||||
onCountMutedConversationsChange={onCountMutedConversationsChange}
|
||||
onHasStoriesDisabledChanged={onHasStoriesDisabledChanged}
|
||||
onHideMenuBarChange={onHideMenuBarChange}
|
||||
onIncomingCallNotificationsChange={onIncomingCallNotificationsChange}
|
||||
onLastSyncTimeChange={onLastSyncTimeChange}
|
||||
onMediaCameraPermissionsChange={onMediaCameraPermissionsChange}
|
||||
onMediaPermissionsChange={onMediaPermissionsChange}
|
||||
onMinimizeToAndStartInSystemTrayChange={
|
||||
onMinimizeToAndStartInSystemTrayChange
|
||||
}
|
||||
onMinimizeToSystemTrayChange={onMinimizeToSystemTrayChange}
|
||||
onNotificationAttentionChange={onNotificationAttentionChange}
|
||||
onNotificationContentChange={onNotificationContentChange}
|
||||
onNotificationsChange={onNotificationsChange}
|
||||
onRelayCallsChange={onRelayCallsChange}
|
||||
onSelectedCameraChange={onSelectedCameraChange}
|
||||
onSelectedMicrophoneChange={onSelectedMicrophoneChange}
|
||||
onSelectedSpeakerChange={onSelectedSpeakerChange}
|
||||
onSentMediaQualityChange={onSentMediaQualityChange}
|
||||
onSpellCheckChange={onSpellCheckChange}
|
||||
onTextFormattingChange={onTextFormattingChange}
|
||||
onThemeChange={onThemeChange}
|
||||
onUniversalExpireTimerChange={onUniversalExpireTimerChange}
|
||||
onWhoCanFindMeChange={onWhoCanFindMeChange}
|
||||
onWhoCanSeeMeChange={onWhoCanSeeMeChange}
|
||||
onZoomFactorChange={onZoomFactorChange}
|
||||
removeCustomColorOnConversations={removeCustomColorOnConversations}
|
||||
removeCustomColor={removeCustomColor}
|
||||
resetAllChatColors={resetAllChatColors}
|
||||
resetDefaultChatColor={resetDefaultChatColor}
|
||||
selectedCamera={selectedCamera}
|
||||
selectedMicrophone={selectedMicrophone}
|
||||
selectedSpeaker={selectedSpeaker}
|
||||
sentMediaQualitySetting={sentMediaQualitySetting}
|
||||
setGlobalDefaultConversationColor={setGlobalDefaultConversationColor}
|
||||
shouldShowStoriesSettings={shouldShowStoriesSettings}
|
||||
themeSetting={themeSetting}
|
||||
universalExpireTimer={universalExpireTimer}
|
||||
whoCanFindMe={whoCanFindMe}
|
||||
whoCanSeeMe={whoCanSeeMe}
|
||||
zoomFactor={zoomFactor}
|
||||
/>,
|
||||
document.getElementById('app')
|
||||
);
|
||||
}
|
||||
);
|
|
@ -1,13 +1,12 @@
|
|||
// Copyright 2021 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { contextBridge, ipcRenderer } from 'electron';
|
||||
import { MinimalSignalContext } from '../minimalContext';
|
||||
|
||||
import { SignalContext } from '../context';
|
||||
import type { PropsPreloadType } from '../../components/Preferences';
|
||||
import OS from '../../util/os/osPreload';
|
||||
import * as Settings from '../../types/Settings';
|
||||
import { Preferences } from '../../components/Preferences';
|
||||
import {
|
||||
SystemTraySetting,
|
||||
parseSystemTraySetting,
|
||||
|
@ -16,7 +15,6 @@ import {
|
|||
import { awaitObject } from '../../util/awaitObject';
|
||||
import { DurationInSeconds } from '../../util/durations';
|
||||
import { createSetting, createCallback } from '../../util/preload';
|
||||
import { startInteractionMode } from '../../services/InteractionMode';
|
||||
|
||||
function doneRendering() {
|
||||
ipcRenderer.send('settings-done-rendering');
|
||||
|
@ -128,9 +126,18 @@ function getSystemTraySettingValues(systemTraySetting: SystemTraySetting): {
|
|||
};
|
||||
}
|
||||
|
||||
const renderPreferences = async () => {
|
||||
startInteractionMode();
|
||||
let renderInBrowser = (_props: PropsPreloadType): void => {
|
||||
throw new Error('render is not defined');
|
||||
};
|
||||
|
||||
function attachRenderCallback<Value>(f: (value: Value) => Promise<Value>) {
|
||||
return async (value: Value) => {
|
||||
await f(value);
|
||||
void renderPreferences();
|
||||
};
|
||||
}
|
||||
|
||||
async function renderPreferences() {
|
||||
const {
|
||||
blockedCount,
|
||||
deviceName,
|
||||
|
@ -222,7 +229,7 @@ const renderPreferences = async () => {
|
|||
const { hasMinimizeToAndStartInSystemTray, hasMinimizeToSystemTray } =
|
||||
getSystemTraySettingValues(systemTraySetting);
|
||||
|
||||
const onUniversalExpireTimerChange = reRender(
|
||||
const onUniversalExpireTimerChange = attachRenderCallback(
|
||||
settingUniversalExpireTimer.setValue
|
||||
);
|
||||
|
||||
|
@ -270,13 +277,13 @@ const renderPreferences = async () => {
|
|||
|
||||
// Actions and other props
|
||||
addCustomColor: ipcAddCustomColor,
|
||||
closeSettings: () => SignalContext.executeMenuRole('close'),
|
||||
closeSettings: () => MinimalSignalContext.executeMenuRole('close'),
|
||||
doDeleteAllData: () => ipcRenderer.send('delete-all-data'),
|
||||
doneRendering,
|
||||
editCustomColor: ipcEditCustomColor,
|
||||
getConversationsWithCustomColor: ipcGetConversationsWithCustomColor,
|
||||
initialSpellCheckSetting:
|
||||
SignalContext.config.appStartInitialSpellcheckSetting,
|
||||
MinimalSignalContext.config.appStartInitialSpellcheckSetting,
|
||||
makeSyncRequest: ipcMakeSyncRequest,
|
||||
removeCustomColor: ipcRemoveCustomColor,
|
||||
removeCustomColorOnConversations: ipcRemoveCustomColorOnConversations,
|
||||
|
@ -286,93 +293,121 @@ const renderPreferences = async () => {
|
|||
shouldShowStoriesSettings,
|
||||
|
||||
// Limited support features
|
||||
isAudioNotificationsSupported: Settings.isAudioNotificationSupported(),
|
||||
isAutoDownloadUpdatesSupported: Settings.isAutoDownloadUpdatesSupported(),
|
||||
isAutoLaunchSupported: Settings.isAutoLaunchSupported(),
|
||||
isHideMenuBarSupported: Settings.isHideMenuBarSupported(),
|
||||
isNotificationAttentionSupported: Settings.isDrawAttentionSupported(),
|
||||
isAudioNotificationsSupported: Settings.isAudioNotificationSupported(OS),
|
||||
isAutoDownloadUpdatesSupported: Settings.isAutoDownloadUpdatesSupported(OS),
|
||||
isAutoLaunchSupported: Settings.isAutoLaunchSupported(OS),
|
||||
isHideMenuBarSupported: Settings.isHideMenuBarSupported(OS),
|
||||
isNotificationAttentionSupported: Settings.isDrawAttentionSupported(OS),
|
||||
isPhoneNumberSharingSupported,
|
||||
isSyncSupported: !isSyncNotSupported,
|
||||
isSystemTraySupported: Settings.isSystemTraySupported(
|
||||
SignalContext.getVersion()
|
||||
OS,
|
||||
MinimalSignalContext.getVersion()
|
||||
),
|
||||
isMinimizeToAndStartInSystemTraySupported:
|
||||
Settings.isMinimizeToAndStartInSystemTraySupported(
|
||||
SignalContext.getVersion()
|
||||
OS,
|
||||
MinimalSignalContext.getVersion()
|
||||
),
|
||||
|
||||
// Feature flags
|
||||
isFormattingFlagEnabled,
|
||||
|
||||
// Change handlers
|
||||
onAudioNotificationsChange: reRender(settingAudioNotification.setValue),
|
||||
onAutoDownloadUpdateChange: reRender(settingAutoDownloadUpdate.setValue),
|
||||
onAutoLaunchChange: reRender(settingAutoLaunch.setValue),
|
||||
onCallNotificationsChange: reRender(settingCallSystemNotification.setValue),
|
||||
onCallRingtoneNotificationChange: reRender(
|
||||
onAudioNotificationsChange: attachRenderCallback(
|
||||
settingAudioNotification.setValue
|
||||
),
|
||||
onAutoDownloadUpdateChange: attachRenderCallback(
|
||||
settingAutoDownloadUpdate.setValue
|
||||
),
|
||||
onAutoLaunchChange: attachRenderCallback(settingAutoLaunch.setValue),
|
||||
onCallNotificationsChange: attachRenderCallback(
|
||||
settingCallSystemNotification.setValue
|
||||
),
|
||||
onCallRingtoneNotificationChange: attachRenderCallback(
|
||||
settingCallRingtoneNotification.setValue
|
||||
),
|
||||
onCountMutedConversationsChange: reRender(
|
||||
onCountMutedConversationsChange: attachRenderCallback(
|
||||
settingCountMutedConversations.setValue
|
||||
),
|
||||
onHasStoriesDisabledChanged: reRender(async (value: boolean) => {
|
||||
await settingHasStoriesDisabled.setValue(value);
|
||||
if (!value) {
|
||||
void ipcDeleteAllMyStories();
|
||||
onHasStoriesDisabledChanged: attachRenderCallback(
|
||||
async (value: boolean) => {
|
||||
await settingHasStoriesDisabled.setValue(value);
|
||||
if (!value) {
|
||||
void ipcDeleteAllMyStories();
|
||||
}
|
||||
return value;
|
||||
}
|
||||
return value;
|
||||
}),
|
||||
onHideMenuBarChange: reRender(settingHideMenuBar.setValue),
|
||||
onIncomingCallNotificationsChange: reRender(
|
||||
),
|
||||
onHideMenuBarChange: attachRenderCallback(settingHideMenuBar.setValue),
|
||||
onIncomingCallNotificationsChange: attachRenderCallback(
|
||||
settingIncomingCallNotification.setValue
|
||||
),
|
||||
onLastSyncTimeChange: reRender(settingLastSyncTime.setValue),
|
||||
onMediaCameraPermissionsChange: reRender(
|
||||
onLastSyncTimeChange: attachRenderCallback(settingLastSyncTime.setValue),
|
||||
onMediaCameraPermissionsChange: attachRenderCallback(
|
||||
settingMediaCameraPermissions.setValue
|
||||
),
|
||||
onMinimizeToAndStartInSystemTrayChange: reRender(async (value: boolean) => {
|
||||
await settingSystemTraySetting.setValue(
|
||||
value
|
||||
? SystemTraySetting.MinimizeToAndStartInSystemTray
|
||||
: SystemTraySetting.MinimizeToSystemTray
|
||||
);
|
||||
return value;
|
||||
}),
|
||||
onMinimizeToSystemTrayChange: reRender(async (value: boolean) => {
|
||||
await settingSystemTraySetting.setValue(
|
||||
value
|
||||
? SystemTraySetting.MinimizeToSystemTray
|
||||
: SystemTraySetting.DoNotUseSystemTray
|
||||
);
|
||||
return value;
|
||||
}),
|
||||
onMediaPermissionsChange: reRender(settingMediaPermissions.setValue),
|
||||
onNotificationAttentionChange: reRender(
|
||||
onMinimizeToAndStartInSystemTrayChange: attachRenderCallback(
|
||||
async (value: boolean) => {
|
||||
await settingSystemTraySetting.setValue(
|
||||
value
|
||||
? SystemTraySetting.MinimizeToAndStartInSystemTray
|
||||
: SystemTraySetting.MinimizeToSystemTray
|
||||
);
|
||||
return value;
|
||||
}
|
||||
),
|
||||
onMinimizeToSystemTrayChange: attachRenderCallback(
|
||||
async (value: boolean) => {
|
||||
await settingSystemTraySetting.setValue(
|
||||
value
|
||||
? SystemTraySetting.MinimizeToSystemTray
|
||||
: SystemTraySetting.DoNotUseSystemTray
|
||||
);
|
||||
return value;
|
||||
}
|
||||
),
|
||||
onMediaPermissionsChange: attachRenderCallback(
|
||||
settingMediaPermissions.setValue
|
||||
),
|
||||
onNotificationAttentionChange: attachRenderCallback(
|
||||
settingNotificationDrawAttention.setValue
|
||||
),
|
||||
onNotificationContentChange: reRender(settingNotificationSetting.setValue),
|
||||
onNotificationsChange: reRender(async (value: boolean) => {
|
||||
onNotificationContentChange: attachRenderCallback(
|
||||
settingNotificationSetting.setValue
|
||||
),
|
||||
onNotificationsChange: attachRenderCallback(async (value: boolean) => {
|
||||
await settingNotificationSetting.setValue(
|
||||
value ? DEFAULT_NOTIFICATION_SETTING : 'off'
|
||||
);
|
||||
return value;
|
||||
}),
|
||||
onRelayCallsChange: reRender(settingRelayCalls.setValue),
|
||||
onSelectedCameraChange: reRender(settingVideoInput.setValue),
|
||||
onSelectedMicrophoneChange: reRender(settingAudioInput.setValue),
|
||||
onSelectedSpeakerChange: reRender(settingAudioOutput.setValue),
|
||||
onSentMediaQualityChange: reRender(settingSentMediaQuality.setValue),
|
||||
onSpellCheckChange: reRender(settingSpellCheck.setValue),
|
||||
onTextFormattingChange: reRender(settingTextFormatting.setValue),
|
||||
onThemeChange: reRender(settingTheme.setValue),
|
||||
onRelayCallsChange: attachRenderCallback(settingRelayCalls.setValue),
|
||||
onSelectedCameraChange: attachRenderCallback(settingVideoInput.setValue),
|
||||
onSelectedMicrophoneChange: attachRenderCallback(
|
||||
settingAudioInput.setValue
|
||||
),
|
||||
onSelectedSpeakerChange: attachRenderCallback(settingAudioOutput.setValue),
|
||||
onSentMediaQualityChange: attachRenderCallback(
|
||||
settingSentMediaQuality.setValue
|
||||
),
|
||||
onSpellCheckChange: attachRenderCallback(settingSpellCheck.setValue),
|
||||
onTextFormattingChange: attachRenderCallback(
|
||||
settingTextFormatting.setValue
|
||||
),
|
||||
onThemeChange: attachRenderCallback(settingTheme.setValue),
|
||||
onUniversalExpireTimerChange: (newValue: number): Promise<void> => {
|
||||
return onUniversalExpireTimerChange(
|
||||
DurationInSeconds.fromSeconds(newValue)
|
||||
);
|
||||
},
|
||||
|
||||
onWhoCanFindMeChange: reRender(settingPhoneNumberDiscoverability.setValue),
|
||||
onWhoCanSeeMeChange: reRender(settingPhoneNumberSharing.setValue),
|
||||
onWhoCanFindMeChange: attachRenderCallback(
|
||||
settingPhoneNumberDiscoverability.setValue
|
||||
),
|
||||
onWhoCanSeeMeChange: attachRenderCallback(
|
||||
settingPhoneNumberSharing.setValue
|
||||
),
|
||||
|
||||
// Zoom factor change doesn't require immediate rerender since it will:
|
||||
// 1. Update the zoom factor in the main window
|
||||
|
@ -381,28 +416,22 @@ const renderPreferences = async () => {
|
|||
// rerender.
|
||||
onZoomFactorChange: settingZoomFactor.setValue,
|
||||
|
||||
i18n: SignalContext.i18n,
|
||||
|
||||
hasCustomTitleBar: SignalContext.OS.hasCustomTitleBar(),
|
||||
executeMenuRole: SignalContext.executeMenuRole,
|
||||
hasCustomTitleBar: MinimalSignalContext.OS.hasCustomTitleBar(),
|
||||
executeMenuRole: MinimalSignalContext.executeMenuRole,
|
||||
};
|
||||
|
||||
function reRender<Value>(f: (value: Value) => Promise<Value>) {
|
||||
return async (value: Value) => {
|
||||
await f(value);
|
||||
void renderPreferences();
|
||||
};
|
||||
}
|
||||
|
||||
ReactDOM.render(
|
||||
React.createElement(Preferences, props),
|
||||
document.getElementById('app')
|
||||
);
|
||||
};
|
||||
renderInBrowser(props);
|
||||
}
|
||||
|
||||
ipcRenderer.on('preferences-changed', () => renderPreferences());
|
||||
|
||||
contextBridge.exposeInMainWorld('SignalContext', {
|
||||
...SignalContext,
|
||||
renderWindow: renderPreferences,
|
||||
});
|
||||
const Signal = {
|
||||
SettingsWindowProps: {
|
||||
onRender: (renderer: (_props: PropsPreloadType) => void) => {
|
||||
renderInBrowser = renderer;
|
||||
void renderPreferences();
|
||||
},
|
||||
},
|
||||
};
|
||||
contextBridge.exposeInMainWorld('Signal', Signal);
|
||||
contextBridge.exposeInMainWorld('SignalContext', MinimalSignalContext);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue