Enables sandbox on about window
This commit is contained in:
parent
58691b2f5e
commit
4591b56f7f
27 changed files with 262 additions and 93 deletions
|
@ -28,3 +28,5 @@ sticker-creator/**/*.js
|
||||||
.eslintrc.js
|
.eslintrc.js
|
||||||
webpack.config.ts
|
webpack.config.ts
|
||||||
preload.bundle.*
|
preload.bundle.*
|
||||||
|
about.browser.bundle.*
|
||||||
|
about.preload.bundle.*
|
||||||
|
|
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -26,6 +26,8 @@ libtextsecure/components.js
|
||||||
stylesheets/*.css
|
stylesheets/*.css
|
||||||
/storybook-static/
|
/storybook-static/
|
||||||
preload.bundle.*
|
preload.bundle.*
|
||||||
|
about.browser.bundle.*
|
||||||
|
about.preload.bundle.*
|
||||||
ts/sql/mainWorker.bundle.js.LICENSE.txt
|
ts/sql/mainWorker.bundle.js.LICENSE.txt
|
||||||
|
|
||||||
# React / TypeScript
|
# React / TypeScript
|
||||||
|
|
|
@ -43,3 +43,5 @@ js/WebAudioRecorderMp3.js
|
||||||
stylesheets/_intlTelInput.scss
|
stylesheets/_intlTelInput.scss
|
||||||
|
|
||||||
preload.bundle.*
|
preload.bundle.*
|
||||||
|
about.browser.bundle.*
|
||||||
|
about.preload.bundle.*
|
||||||
|
|
|
@ -30,5 +30,9 @@
|
||||||
<body>
|
<body>
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
<script type="application/javascript" src="ts/windows/init.js"></script>
|
<script type="application/javascript" src="ts/windows/init.js"></script>
|
||||||
|
<script
|
||||||
|
type="application/javascript"
|
||||||
|
src="about.browser.bundle.js"
|
||||||
|
></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -1253,7 +1253,7 @@ async function showAbout() {
|
||||||
nodeIntegrationInWorker: false,
|
nodeIntegrationInWorker: false,
|
||||||
sandbox: false,
|
sandbox: false,
|
||||||
contextIsolation: true,
|
contextIsolation: true,
|
||||||
preload: join(__dirname, '../ts/windows/about/preload.js'),
|
preload: join(__dirname, '../about.preload.bundle.js'),
|
||||||
nativeWindowOpen: true,
|
nativeWindowOpen: true,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -2357,6 +2357,11 @@ ipc.on('locale-data', event => {
|
||||||
event.returnValue = getResolvedMessagesLocale().messages;
|
event.returnValue = getResolvedMessagesLocale().messages;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ipc.on('getHasCustomTitleBar', event => {
|
||||||
|
// eslint-disable-next-line no-param-reassign
|
||||||
|
event.returnValue = OS.hasCustomTitleBar();
|
||||||
|
});
|
||||||
|
|
||||||
ipc.on('user-config-key', event => {
|
ipc.on('user-config-key', event => {
|
||||||
// eslint-disable-next-line no-param-reassign
|
// eslint-disable-next-line no-param-reassign
|
||||||
event.returnValue = userConfig.get('key');
|
event.returnValue = userConfig.get('key');
|
||||||
|
|
|
@ -462,6 +462,8 @@
|
||||||
"app/*",
|
"app/*",
|
||||||
"preload.bundle.js",
|
"preload.bundle.js",
|
||||||
"preload_utils.js",
|
"preload_utils.js",
|
||||||
|
"about.preload.bundle.js",
|
||||||
|
"about.browser.bundle.js",
|
||||||
"main.js",
|
"main.js",
|
||||||
"images/**",
|
"images/**",
|
||||||
"fonts/**",
|
"fonts/**",
|
||||||
|
|
|
@ -98,3 +98,18 @@ main().catch(error => {
|
||||||
console.error(error.stack);
|
console.error(error.stack);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// About bundle
|
||||||
|
esbuild.build({
|
||||||
|
...bundleDefaults,
|
||||||
|
mainFields: ['browser', 'main'],
|
||||||
|
entryPoints: [path.join(ROOT_DIR, 'ts', 'windows', 'about', 'app.tsx')],
|
||||||
|
outfile: path.join(ROOT_DIR, 'about.browser.bundle.js'),
|
||||||
|
});
|
||||||
|
|
||||||
|
esbuild.build({
|
||||||
|
...bundleDefaults,
|
||||||
|
mainFields: ['browser', 'main'],
|
||||||
|
entryPoints: [path.join(ROOT_DIR, 'ts', 'windows', 'about', 'preload.ts')],
|
||||||
|
outfile: path.join(ROOT_DIR, 'about.preload.bundle.js'),
|
||||||
|
});
|
||||||
|
|
|
@ -148,7 +148,7 @@ import { deleteAllLogs } from './util/deleteAllLogs';
|
||||||
import { ToastCaptchaFailed } from './components/ToastCaptchaFailed';
|
import { ToastCaptchaFailed } from './components/ToastCaptchaFailed';
|
||||||
import { ToastCaptchaSolved } from './components/ToastCaptchaSolved';
|
import { ToastCaptchaSolved } from './components/ToastCaptchaSolved';
|
||||||
import { showToast } from './util/showToast';
|
import { showToast } from './util/showToast';
|
||||||
import { startInteractionMode } from './windows/startInteractionMode';
|
import { startInteractionMode } from './services/InteractionMode';
|
||||||
import type { MainWindowStatsType } from './windows/context';
|
import type { MainWindowStatsType } from './windows/context';
|
||||||
import { ReactionSource } from './reactions/ReactionSource';
|
import { ReactionSource } from './reactions/ReactionSource';
|
||||||
import { singleProtoJobQueue } from './jobs/singleProtoJobQueue';
|
import { singleProtoJobQueue } from './jobs/singleProtoJobQueue';
|
||||||
|
|
|
@ -3,28 +3,28 @@
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
|
import type { ExecuteMenuRoleType } from './TitleBarContainer';
|
||||||
import type { LocalizerType } from '../types/Util';
|
import type { LocalizerType } from '../types/Util';
|
||||||
|
import { TitleBarContainer } from './TitleBarContainer';
|
||||||
import { useEscapeHandling } from '../hooks/useEscapeHandling';
|
import { useEscapeHandling } from '../hooks/useEscapeHandling';
|
||||||
import { useTheme } from '../hooks/useTheme';
|
import { useTheme } from '../hooks/useTheme';
|
||||||
import { TitleBarContainer } from './TitleBarContainer';
|
|
||||||
import type { ExecuteMenuRoleType } from './TitleBarContainer';
|
|
||||||
|
|
||||||
export type PropsType = {
|
export type PropsType = {
|
||||||
closeAbout: () => unknown;
|
closeAbout: () => unknown;
|
||||||
environment: string;
|
environment: string;
|
||||||
|
executeMenuRole: ExecuteMenuRoleType;
|
||||||
|
hasCustomTitleBar: boolean;
|
||||||
i18n: LocalizerType;
|
i18n: LocalizerType;
|
||||||
version: string;
|
version: string;
|
||||||
hasCustomTitleBar: boolean;
|
|
||||||
executeMenuRole: ExecuteMenuRoleType;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export function About({
|
export function About({
|
||||||
closeAbout,
|
closeAbout,
|
||||||
i18n,
|
|
||||||
environment,
|
environment,
|
||||||
version,
|
|
||||||
hasCustomTitleBar,
|
|
||||||
executeMenuRole,
|
executeMenuRole,
|
||||||
|
hasCustomTitleBar,
|
||||||
|
i18n,
|
||||||
|
version,
|
||||||
}: PropsType): JSX.Element {
|
}: PropsType): JSX.Element {
|
||||||
useEscapeHandling(closeAbout);
|
useEscapeHandling(closeAbout);
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ import type { Theme } from '../util/theme';
|
||||||
import { themeClassName } from '../util/theme';
|
import { themeClassName } from '../util/theme';
|
||||||
import { refMerger } from '../util/refMerger';
|
import { refMerger } from '../util/refMerger';
|
||||||
import { offsetDistanceModifier } from '../util/popperUtil';
|
import { offsetDistanceModifier } from '../util/popperUtil';
|
||||||
|
import { getInteractionMode } from '../services/InteractionMode';
|
||||||
|
|
||||||
type EventWrapperPropsType = {
|
type EventWrapperPropsType = {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
|
@ -35,7 +36,7 @@ const TooltipEventWrapper = React.forwardRef<
|
||||||
}, [onHoverChanged]);
|
}, [onHoverChanged]);
|
||||||
|
|
||||||
const onFocus = React.useCallback(() => {
|
const onFocus = React.useCallback(() => {
|
||||||
if (window.getInteractionMode() === 'keyboard') {
|
if (getInteractionMode() === 'keyboard') {
|
||||||
on();
|
on();
|
||||||
}
|
}
|
||||||
}, [on]);
|
}, [on]);
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { getInteractionMode } from '../../services/InteractionMode';
|
||||||
|
|
||||||
export type PropsType = {
|
export type PropsType = {
|
||||||
id: string;
|
id: string;
|
||||||
|
@ -22,7 +23,7 @@ export class InlineNotificationWrapper extends React.Component<PropsType> {
|
||||||
};
|
};
|
||||||
|
|
||||||
public handleFocus = (): void => {
|
public handleFocus = (): void => {
|
||||||
if (window.getInteractionMode() === 'keyboard') {
|
if (getInteractionMode() === 'keyboard') {
|
||||||
this.setSelected();
|
this.setSelected();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
12
ts/context/activeWindowService.ts
Normal file
12
ts/context/activeWindowService.ts
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
// Copyright 2023 Signal Messenger, LLC
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
import { ipcRenderer } from 'electron';
|
||||||
|
import { getActiveWindowService } from '../services/ActiveWindowService';
|
||||||
|
|
||||||
|
const activeWindowService = getActiveWindowService(
|
||||||
|
window.document,
|
||||||
|
ipcRenderer
|
||||||
|
);
|
||||||
|
|
||||||
|
export { activeWindowService };
|
12
ts/context/config.ts
Normal file
12
ts/context/config.ts
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
// Copyright 2023 Signal Messenger, LLC
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
import type { RendererConfigType } from '../types/RendererConfig';
|
||||||
|
import { strictAssert } from '../util/assert';
|
||||||
|
|
||||||
|
const params = new URLSearchParams(document.location.search);
|
||||||
|
const configParam = params.get('config');
|
||||||
|
strictAssert(typeof configParam === 'string', 'config is not a string');
|
||||||
|
const config: RendererConfigType = JSON.parse(configParam);
|
||||||
|
|
||||||
|
export { config };
|
15
ts/context/environment.ts
Normal file
15
ts/context/environment.ts
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
// Copyright 2023 Signal Messenger, LLC
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
import { config } from './config';
|
||||||
|
import {
|
||||||
|
getEnvironment,
|
||||||
|
parseEnvironment,
|
||||||
|
setEnvironment,
|
||||||
|
} from '../environment';
|
||||||
|
|
||||||
|
setEnvironment(parseEnvironment(config.environment));
|
||||||
|
|
||||||
|
const environment = getEnvironment();
|
||||||
|
|
||||||
|
export { environment };
|
22
ts/context/i18n.ts
Normal file
22
ts/context/i18n.ts
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
// Copyright 2023 Signal Messenger, LLC
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
import { ipcRenderer } from 'electron';
|
||||||
|
import { config } from './config';
|
||||||
|
import { setupI18n } from '../util/setupI18n';
|
||||||
|
import { strictAssert } from '../util/assert';
|
||||||
|
|
||||||
|
const { resolvedTranslationsLocale } = config;
|
||||||
|
strictAssert(
|
||||||
|
resolvedTranslationsLocale,
|
||||||
|
'locale could not be parsed from config'
|
||||||
|
);
|
||||||
|
strictAssert(
|
||||||
|
typeof resolvedTranslationsLocale === 'string',
|
||||||
|
'locale is not a string'
|
||||||
|
);
|
||||||
|
|
||||||
|
const localeMessages = ipcRenderer.sendSync('locale-data');
|
||||||
|
const i18n = setupI18n(resolvedTranslationsLocale, localeMessages);
|
||||||
|
|
||||||
|
export { i18n };
|
|
@ -16,7 +16,7 @@ const ACTIVE_EVENTS = [
|
||||||
'wheel',
|
'wheel',
|
||||||
];
|
];
|
||||||
|
|
||||||
export class ActiveWindowService {
|
class ActiveWindowService {
|
||||||
// This starting value might be wrong but we should get an update from the main process
|
// This starting value might be wrong but we should get an update from the main process
|
||||||
// soon. We'd rather report that the window is inactive so we can show notifications.
|
// soon. We'd rather report that the window is inactive so we can show notifications.
|
||||||
private isInitialized = false;
|
private isInitialized = false;
|
||||||
|
@ -113,3 +113,37 @@ export class ActiveWindowService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type ActiveWindowServiceType = {
|
||||||
|
isActive(): boolean;
|
||||||
|
registerForActive(callback: () => void): void;
|
||||||
|
unregisterForActive(callback: () => void): void;
|
||||||
|
registerForChange(callback: (isActive: boolean) => void): void;
|
||||||
|
unregisterForChange(callback: (isActive: boolean) => void): void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function getActiveWindowService(
|
||||||
|
document: EventTarget,
|
||||||
|
ipc: NodeJS.EventEmitter
|
||||||
|
): ActiveWindowServiceType {
|
||||||
|
const activeWindowService = new ActiveWindowService();
|
||||||
|
activeWindowService.initialize(document, ipc);
|
||||||
|
|
||||||
|
return {
|
||||||
|
isActive(): boolean {
|
||||||
|
return activeWindowService.isActive();
|
||||||
|
},
|
||||||
|
registerForActive(callback: () => void): void {
|
||||||
|
return activeWindowService.registerForActive(callback);
|
||||||
|
},
|
||||||
|
unregisterForActive(callback: () => void): void {
|
||||||
|
return activeWindowService.unregisterForActive(callback);
|
||||||
|
},
|
||||||
|
registerForChange(callback: (isActive: boolean) => void): void {
|
||||||
|
return activeWindowService.registerForChange(callback);
|
||||||
|
},
|
||||||
|
unregisterForChange(callback: (isActive: boolean) => void): void {
|
||||||
|
return activeWindowService.unregisterForChange(callback);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
// Copyright 2021 Signal Messenger, LLC
|
// Copyright 2021 Signal Messenger, LLC
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
type InteractionModeType = 'mouse' | 'keyboard';
|
||||||
|
|
||||||
let initialized = false;
|
let initialized = false;
|
||||||
let interactionMode: 'mouse' | 'keyboard' = 'mouse';
|
let interactionMode: InteractionModeType = 'mouse';
|
||||||
|
|
||||||
export function startInteractionMode(): void {
|
export function startInteractionMode(): void {
|
||||||
if (initialized) {
|
if (initialized) {
|
||||||
|
@ -66,6 +68,8 @@ export function startInteractionMode(): void {
|
||||||
);
|
);
|
||||||
document.addEventListener('wheel', window.enterMouseMode, true);
|
document.addEventListener('wheel', window.enterMouseMode, true);
|
||||||
document.addEventListener('mousedown', window.enterMouseMode, true);
|
document.addEventListener('mousedown', window.enterMouseMode, true);
|
||||||
|
}
|
||||||
window.getInteractionMode = () => interactionMode;
|
|
||||||
|
export function getInteractionMode(): InteractionModeType {
|
||||||
|
return interactionMode;
|
||||||
}
|
}
|
|
@ -36,6 +36,7 @@ import { UUIDKind } from '../types/UUID';
|
||||||
import { getEmojiReducerState as emojis } from '../util/loadRecentEmojis';
|
import { getEmojiReducerState as emojis } from '../util/loadRecentEmojis';
|
||||||
import { getInitialState as stickers } from '../types/Stickers';
|
import { getInitialState as stickers } from '../types/Stickers';
|
||||||
import { getThemeType } from '../util/getThemeType';
|
import { getThemeType } from '../util/getThemeType';
|
||||||
|
import { getInteractionMode } from '../services/InteractionMode';
|
||||||
|
|
||||||
export function getInitialState({
|
export function getInitialState({
|
||||||
badges,
|
badges,
|
||||||
|
@ -137,7 +138,7 @@ export function getInitialState({
|
||||||
...user(),
|
...user(),
|
||||||
attachmentsPath: window.BasePaths.attachments,
|
attachmentsPath: window.BasePaths.attachments,
|
||||||
i18n: window.i18n,
|
i18n: window.i18n,
|
||||||
interactionMode: window.getInteractionMode(),
|
interactionMode: getInteractionMode(),
|
||||||
isMainWindowFullScreen: mainWindowStats.isFullScreen,
|
isMainWindowFullScreen: mainWindowStats.isFullScreen,
|
||||||
isMainWindowMaximized: mainWindowStats.isMaximized,
|
isMainWindowMaximized: mainWindowStats.isMaximized,
|
||||||
localeMessages: window.SignalContext.localeMessages,
|
localeMessages: window.SignalContext.localeMessages,
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { assert } from 'chai';
|
||||||
import * as sinon from 'sinon';
|
import * as sinon from 'sinon';
|
||||||
import { EventEmitter } from 'events';
|
import { EventEmitter } from 'events';
|
||||||
|
|
||||||
import { ActiveWindowService } from '../../services/ActiveWindowService';
|
import { getActiveWindowService } from '../../services/ActiveWindowService';
|
||||||
|
|
||||||
describe('ActiveWindowService', () => {
|
describe('ActiveWindowService', () => {
|
||||||
const fakeIpcEvent = {};
|
const fakeIpcEvent = {};
|
||||||
|
@ -23,16 +23,17 @@ describe('ActiveWindowService', () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
it('is inactive at the start', () => {
|
it('is inactive at the start', () => {
|
||||||
const service = new ActiveWindowService();
|
const service = getActiveWindowService(
|
||||||
service.initialize(createFakeDocument(), new EventEmitter());
|
createFakeDocument(),
|
||||||
|
new EventEmitter()
|
||||||
|
);
|
||||||
|
|
||||||
assert.isFalse(service.isActive());
|
assert.isFalse(service.isActive());
|
||||||
});
|
});
|
||||||
|
|
||||||
it('becomes active after focusing', () => {
|
it('becomes active after focusing', () => {
|
||||||
const fakeIpc = new EventEmitter();
|
const fakeIpc = new EventEmitter();
|
||||||
const service = new ActiveWindowService();
|
const service = getActiveWindowService(createFakeDocument(), fakeIpc);
|
||||||
service.initialize(createFakeDocument(), fakeIpc);
|
|
||||||
|
|
||||||
fakeIpc.emit('set-window-focus', fakeIpcEvent, true);
|
fakeIpc.emit('set-window-focus', fakeIpcEvent, true);
|
||||||
|
|
||||||
|
@ -41,8 +42,7 @@ describe('ActiveWindowService', () => {
|
||||||
|
|
||||||
it('becomes inactive after 15 seconds without interaction', function test() {
|
it('becomes inactive after 15 seconds without interaction', function test() {
|
||||||
const fakeIpc = new EventEmitter();
|
const fakeIpc = new EventEmitter();
|
||||||
const service = new ActiveWindowService();
|
const service = getActiveWindowService(createFakeDocument(), fakeIpc);
|
||||||
service.initialize(createFakeDocument(), fakeIpc);
|
|
||||||
|
|
||||||
fakeIpc.emit('set-window-focus', fakeIpcEvent, true);
|
fakeIpc.emit('set-window-focus', fakeIpcEvent, true);
|
||||||
|
|
||||||
|
@ -61,8 +61,7 @@ describe('ActiveWindowService', () => {
|
||||||
it(`is inactive even in the face of ${eventName} events if unfocused`, function test() {
|
it(`is inactive even in the face of ${eventName} events if unfocused`, function test() {
|
||||||
const fakeDocument = createFakeDocument();
|
const fakeDocument = createFakeDocument();
|
||||||
const fakeIpc = new EventEmitter();
|
const fakeIpc = new EventEmitter();
|
||||||
const service = new ActiveWindowService();
|
const service = getActiveWindowService(fakeDocument, fakeIpc);
|
||||||
service.initialize(fakeDocument, fakeIpc);
|
|
||||||
|
|
||||||
fakeIpc.emit('set-window-focus', fakeIpcEvent, false);
|
fakeIpc.emit('set-window-focus', fakeIpcEvent, false);
|
||||||
|
|
||||||
|
@ -73,8 +72,7 @@ describe('ActiveWindowService', () => {
|
||||||
it(`stays active if focused and receiving ${eventName} events`, function test() {
|
it(`stays active if focused and receiving ${eventName} events`, function test() {
|
||||||
const fakeDocument = createFakeDocument();
|
const fakeDocument = createFakeDocument();
|
||||||
const fakeIpc = new EventEmitter();
|
const fakeIpc = new EventEmitter();
|
||||||
const service = new ActiveWindowService();
|
const service = getActiveWindowService(fakeDocument, fakeIpc);
|
||||||
service.initialize(fakeDocument, fakeIpc);
|
|
||||||
|
|
||||||
fakeIpc.emit('set-window-focus', fakeIpcEvent, true);
|
fakeIpc.emit('set-window-focus', fakeIpcEvent, true);
|
||||||
|
|
||||||
|
@ -94,8 +92,7 @@ describe('ActiveWindowService', () => {
|
||||||
|
|
||||||
it('calls callbacks when going from unfocused to focused', () => {
|
it('calls callbacks when going from unfocused to focused', () => {
|
||||||
const fakeIpc = new EventEmitter();
|
const fakeIpc = new EventEmitter();
|
||||||
const service = new ActiveWindowService();
|
const service = getActiveWindowService(createFakeDocument(), fakeIpc);
|
||||||
service.initialize(createFakeDocument(), fakeIpc);
|
|
||||||
|
|
||||||
const callback = sinon.stub();
|
const callback = sinon.stub();
|
||||||
service.registerForActive(callback);
|
service.registerForActive(callback);
|
||||||
|
@ -108,8 +105,7 @@ describe('ActiveWindowService', () => {
|
||||||
it('calls callbacks when receiving a click event after being focused', function test() {
|
it('calls callbacks when receiving a click event after being focused', function test() {
|
||||||
const fakeDocument = createFakeDocument();
|
const fakeDocument = createFakeDocument();
|
||||||
const fakeIpc = new EventEmitter();
|
const fakeIpc = new EventEmitter();
|
||||||
const service = new ActiveWindowService();
|
const service = getActiveWindowService(fakeDocument, fakeIpc);
|
||||||
service.initialize(fakeDocument, fakeIpc);
|
|
||||||
|
|
||||||
fakeIpc.emit('set-window-focus', fakeIpcEvent, true);
|
fakeIpc.emit('set-window-focus', fakeIpcEvent, true);
|
||||||
|
|
||||||
|
@ -125,8 +121,7 @@ describe('ActiveWindowService', () => {
|
||||||
|
|
||||||
it('only calls callbacks every 5 seconds; it is throttled', function test() {
|
it('only calls callbacks every 5 seconds; it is throttled', function test() {
|
||||||
const fakeIpc = new EventEmitter();
|
const fakeIpc = new EventEmitter();
|
||||||
const service = new ActiveWindowService();
|
const service = getActiveWindowService(createFakeDocument(), fakeIpc);
|
||||||
service.initialize(createFakeDocument(), fakeIpc);
|
|
||||||
|
|
||||||
const callback = sinon.stub();
|
const callback = sinon.stub();
|
||||||
service.registerForActive(callback);
|
service.registerForActive(callback);
|
||||||
|
@ -150,8 +145,7 @@ describe('ActiveWindowService', () => {
|
||||||
it('can remove callbacks', () => {
|
it('can remove callbacks', () => {
|
||||||
const fakeDocument = createFakeDocument();
|
const fakeDocument = createFakeDocument();
|
||||||
const fakeIpc = new EventEmitter();
|
const fakeIpc = new EventEmitter();
|
||||||
const service = new ActiveWindowService();
|
const service = getActiveWindowService(fakeDocument, fakeIpc);
|
||||||
service.initialize(fakeDocument, fakeIpc);
|
|
||||||
|
|
||||||
const callback = sinon.stub();
|
const callback = sinon.stub();
|
||||||
service.registerForActive(callback);
|
service.registerForActive(callback);
|
||||||
|
|
|
@ -24,6 +24,8 @@ const excludedFilesRegexp = RegExp(
|
||||||
[
|
[
|
||||||
'^release/',
|
'^release/',
|
||||||
'^preload.bundle.js(LICENSE.txt|map)?',
|
'^preload.bundle.js(LICENSE.txt|map)?',
|
||||||
|
'^about.browser.bundle.js(LICENSE.txt|map)?',
|
||||||
|
'^about.preload.bundle.js(LICENSE.txt|map)?',
|
||||||
'^storybook-static/',
|
'^storybook-static/',
|
||||||
|
|
||||||
// Non-distributed files
|
// Non-distributed files
|
||||||
|
|
10
ts/window.d.ts
vendored
10
ts/window.d.ts
vendored
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
// Captures the globals put in place by preload.js, background.js and others
|
// Captures the globals put in place by preload.js, background.js and others
|
||||||
|
|
||||||
|
import type { MenuItemConstructorOptions } from 'electron';
|
||||||
import type { Store } from 'redux';
|
import type { Store } from 'redux';
|
||||||
import type * as Backbone from 'backbone';
|
import type * as Backbone from 'backbone';
|
||||||
import type PQueue from 'p-queue/dist';
|
import type PQueue from 'p-queue/dist';
|
||||||
|
@ -101,7 +102,16 @@ export type FeatureFlagType = {
|
||||||
GV2_MIGRATION_DISABLE_INVITE: boolean;
|
GV2_MIGRATION_DISABLE_INVITE: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type AboutWindowType = {
|
||||||
|
environmentText: string;
|
||||||
|
executeMenuRole: (role: MenuItemConstructorOptions['role']) => Promise<void>;
|
||||||
|
hasCustomTitleBar: boolean;
|
||||||
|
i18n: LocalizerType;
|
||||||
|
version: string;
|
||||||
|
};
|
||||||
|
|
||||||
export type SignalCoreType = {
|
export type SignalCoreType = {
|
||||||
|
AboutWindow?: AboutWindowType;
|
||||||
Crypto: typeof Crypto;
|
Crypto: typeof Crypto;
|
||||||
Curve: typeof Curve;
|
Curve: typeof Curve;
|
||||||
Data: typeof Data;
|
Data: typeof Data;
|
||||||
|
|
24
ts/windows/about/app.tsx
Normal file
24
ts/windows/about/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 { About } from '../../components/About';
|
||||||
|
import { strictAssert } from '../../util/assert';
|
||||||
|
|
||||||
|
const { AboutWindow } = window.Signal;
|
||||||
|
|
||||||
|
strictAssert(AboutWindow, 'window values not provided');
|
||||||
|
|
||||||
|
ReactDOM.render(
|
||||||
|
<About
|
||||||
|
closeAbout={() => AboutWindow.executeMenuRole('close')}
|
||||||
|
environment={AboutWindow.environmentText}
|
||||||
|
executeMenuRole={AboutWindow.executeMenuRole}
|
||||||
|
hasCustomTitleBar={AboutWindow.hasCustomTitleBar}
|
||||||
|
i18n={AboutWindow.i18n}
|
||||||
|
version={AboutWindow.version}
|
||||||
|
/>,
|
||||||
|
document.getElementById('app')
|
||||||
|
);
|
|
@ -1,42 +1,63 @@
|
||||||
// Copyright 2018 Signal Messenger, LLC
|
// Copyright 2018 Signal Messenger, LLC
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
import React from 'react';
|
import type { MenuItemConstructorOptions } from 'electron';
|
||||||
import ReactDOM from 'react-dom';
|
import { contextBridge, ipcRenderer } from 'electron';
|
||||||
import { contextBridge } from 'electron';
|
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 { getClassName } from '../../OS';
|
||||||
|
import { i18n } from '../../context/i18n';
|
||||||
|
import { waitForSettingsChange } from '../../context/waitForSettingsChange';
|
||||||
|
|
||||||
import { SignalContext } from '../context';
|
async function executeMenuRole(
|
||||||
import { About } from '../../components/About';
|
role: MenuItemConstructorOptions['role']
|
||||||
|
): Promise<void> {
|
||||||
|
await ipcRenderer.invoke('executeMenuRole', role);
|
||||||
|
}
|
||||||
|
|
||||||
contextBridge.exposeInMainWorld('SignalContext', {
|
const environments: Array<string> = [environment];
|
||||||
...SignalContext,
|
|
||||||
renderWindow: () => {
|
|
||||||
const environmentText: Array<string> = [SignalContext.getEnvironment()];
|
|
||||||
|
|
||||||
const appInstance = SignalContext.getAppInstance();
|
if (config.appInstance) {
|
||||||
if (appInstance) {
|
environments.push(String(config.appInstance));
|
||||||
environmentText.push(appInstance);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
let platform = '';
|
let platform = '';
|
||||||
if (process.platform === 'darwin') {
|
if (process.platform === 'darwin') {
|
||||||
if (process.arch === 'arm64') {
|
if (process.arch === 'arm64') {
|
||||||
platform = ` (${SignalContext.i18n('appleSilicon')})`;
|
platform = ` (${i18n('appleSilicon')})`;
|
||||||
} else {
|
} else {
|
||||||
platform = ' (Intel)';
|
platform = ' (Intel)';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ReactDOM.render(
|
const environmentText = `${environments.join(' - ')}${platform}`;
|
||||||
React.createElement(About, {
|
const hasCustomTitleBar = ipcRenderer.sendSync('getHasCustomTitleBar');
|
||||||
closeAbout: () => SignalContext.executeMenuRole('close'),
|
|
||||||
environment: `${environmentText.join(' - ')}${platform}`,
|
const Signal = {
|
||||||
i18n: SignalContext.i18n,
|
AboutWindow: {
|
||||||
version: SignalContext.getVersion(),
|
environmentText,
|
||||||
hasCustomTitleBar: SignalContext.OS.hasCustomTitleBar(),
|
executeMenuRole,
|
||||||
executeMenuRole: SignalContext.executeMenuRole,
|
hasCustomTitleBar,
|
||||||
}),
|
i18n,
|
||||||
document.getElementById('app')
|
version: String(config.version),
|
||||||
);
|
|
||||||
},
|
},
|
||||||
});
|
};
|
||||||
|
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);
|
||||||
|
|
|
@ -12,13 +12,15 @@ import type { LocaleMessagesType } from '../types/I18N';
|
||||||
import type { NativeThemeType } from '../context/createNativeThemeListener';
|
import type { NativeThemeType } from '../context/createNativeThemeListener';
|
||||||
import type { SettingType } from '../util/preload';
|
import type { SettingType } from '../util/preload';
|
||||||
import type { RendererConfigType } from '../types/RendererConfig';
|
import type { RendererConfigType } from '../types/RendererConfig';
|
||||||
import { ActiveWindowService } from '../services/ActiveWindowService';
|
|
||||||
|
|
||||||
import { Bytes } from '../context/Bytes';
|
import { Bytes } from '../context/Bytes';
|
||||||
import { Crypto } from '../context/Crypto';
|
import { Crypto } from '../context/Crypto';
|
||||||
import { Timers } from '../context/Timers';
|
import { Timers } from '../context/Timers';
|
||||||
|
|
||||||
import { setupI18n } from '../util/setupI18n';
|
import type { ActiveWindowServiceType } from '../services/ActiveWindowService';
|
||||||
|
import { config } from '../context/config';
|
||||||
|
import { i18n } from '../context/i18n';
|
||||||
|
import { activeWindowService } from '../context/activeWindowService';
|
||||||
import {
|
import {
|
||||||
getEnvironment,
|
getEnvironment,
|
||||||
parseEnvironment,
|
parseEnvironment,
|
||||||
|
@ -27,7 +29,7 @@ import {
|
||||||
import { strictAssert } from '../util/assert';
|
import { strictAssert } from '../util/assert';
|
||||||
import { createSetting } from '../util/preload';
|
import { createSetting } from '../util/preload';
|
||||||
import { initialize as initializeLogging } from '../logging/set_up_renderer_logging';
|
import { initialize as initializeLogging } from '../logging/set_up_renderer_logging';
|
||||||
import { waitForSettingsChange } from './waitForSettingsChange';
|
import { waitForSettingsChange } from '../context/waitForSettingsChange';
|
||||||
import { createNativeThemeListener } from '../context/createNativeThemeListener';
|
import { createNativeThemeListener } from '../context/createNativeThemeListener';
|
||||||
import {
|
import {
|
||||||
isWindows,
|
isWindows,
|
||||||
|
@ -37,24 +39,6 @@ import {
|
||||||
getClassName,
|
getClassName,
|
||||||
} from '../OS';
|
} from '../OS';
|
||||||
|
|
||||||
const activeWindowService = new ActiveWindowService();
|
|
||||||
activeWindowService.initialize(window.document, ipcRenderer);
|
|
||||||
|
|
||||||
const params = new URLSearchParams(document.location.search);
|
|
||||||
const configParam = params.get('config');
|
|
||||||
strictAssert(typeof configParam === 'string', 'config is not a string');
|
|
||||||
const config: RendererConfigType = JSON.parse(configParam);
|
|
||||||
|
|
||||||
const { resolvedTranslationsLocale } = config;
|
|
||||||
strictAssert(
|
|
||||||
resolvedTranslationsLocale,
|
|
||||||
'locale could not be parsed from config'
|
|
||||||
);
|
|
||||||
strictAssert(
|
|
||||||
typeof resolvedTranslationsLocale === 'string',
|
|
||||||
'locale is not a string'
|
|
||||||
);
|
|
||||||
|
|
||||||
const localeMessages = ipcRenderer.sendSync('locale-data');
|
const localeMessages = ipcRenderer.sendSync('locale-data');
|
||||||
setEnvironment(parseEnvironment(config.environment));
|
setEnvironment(parseEnvironment(config.environment));
|
||||||
|
|
||||||
|
@ -74,7 +58,7 @@ export type SignalContextType = {
|
||||||
nativeThemeListener: NativeThemeType;
|
nativeThemeListener: NativeThemeType;
|
||||||
setIsCallActive: (isCallActive: boolean) => unknown;
|
setIsCallActive: (isCallActive: boolean) => unknown;
|
||||||
|
|
||||||
activeWindowService: typeof activeWindowService;
|
activeWindowService: ActiveWindowServiceType;
|
||||||
Settings: {
|
Settings: {
|
||||||
themeSetting: SettingType<IPCEventsValuesType['themeSetting']>;
|
themeSetting: SettingType<IPCEventsValuesType['themeSetting']>;
|
||||||
waitForChange: () => Promise<void>;
|
waitForChange: () => Promise<void>;
|
||||||
|
@ -128,7 +112,7 @@ export const SignalContext: SignalContextType = {
|
||||||
getPath: (name: 'userData' | 'home'): string => {
|
getPath: (name: 'userData' | 'home'): string => {
|
||||||
return String(config[`${name}Path`]);
|
return String(config[`${name}Path`]);
|
||||||
},
|
},
|
||||||
i18n: setupI18n(resolvedTranslationsLocale, localeMessages),
|
i18n,
|
||||||
localeMessages,
|
localeMessages,
|
||||||
log: window.SignalContext.log,
|
log: window.SignalContext.log,
|
||||||
nativeThemeListener: createNativeThemeListener(ipcRenderer, window),
|
nativeThemeListener: createNativeThemeListener(ipcRenderer, window),
|
||||||
|
|
|
@ -8,6 +8,6 @@ if (window.SignalContext.OS.hasCustomTitleBar()) {
|
||||||
|
|
||||||
if (window.SignalContext.renderWindow) {
|
if (window.SignalContext.renderWindow) {
|
||||||
window.SignalContext.renderWindow();
|
window.SignalContext.renderWindow();
|
||||||
} else {
|
} else if (window.SignalContext.log) {
|
||||||
window.SignalContext.log.error('renderWindow is undefined!');
|
window.SignalContext.log.error('renderWindow is undefined!');
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ import {
|
||||||
import { awaitObject } from '../../util/awaitObject';
|
import { awaitObject } from '../../util/awaitObject';
|
||||||
import { DurationInSeconds } from '../../util/durations';
|
import { DurationInSeconds } from '../../util/durations';
|
||||||
import { createSetting, createCallback } from '../../util/preload';
|
import { createSetting, createCallback } from '../../util/preload';
|
||||||
import { startInteractionMode } from '../startInteractionMode';
|
import { startInteractionMode } from '../../services/InteractionMode';
|
||||||
|
|
||||||
function doneRendering() {
|
function doneRendering() {
|
||||||
ipcRenderer.send('settings-done-rendering');
|
ipcRenderer.send('settings-done-rendering');
|
||||||
|
|
Loading…
Reference in a new issue