Convert signal.js and preload.js to Typescript
This commit is contained in:
parent
e18510e41c
commit
2464e0a9c1
94 changed files with 2113 additions and 1848 deletions
28
.eslintrc.js
28
.eslintrc.js
|
@ -167,7 +167,13 @@ module.exports = {
|
|||
|
||||
overrides: [
|
||||
{
|
||||
files: ['ts/**/*.ts', 'ts/**/*.tsx', 'app/**/*.ts'],
|
||||
files: [
|
||||
'ts/**/*.ts',
|
||||
'ts/**/*.tsx',
|
||||
'app/**/*.ts',
|
||||
'sticker-creator/**/*.ts',
|
||||
'sticker-creator/**/*.tsx',
|
||||
],
|
||||
parser: '@typescript-eslint/parser',
|
||||
parserOptions: {
|
||||
project: 'tsconfig.json',
|
||||
|
@ -186,26 +192,6 @@ module.exports = {
|
|||
],
|
||||
rules: typescriptRules,
|
||||
},
|
||||
{
|
||||
files: ['sticker-creator/**/*.ts', 'sticker-creator/**/*.tsx'],
|
||||
parser: '@typescript-eslint/parser',
|
||||
parserOptions: {
|
||||
project: './sticker-creator/tsconfig.json',
|
||||
ecmaFeatures: {
|
||||
jsx: true,
|
||||
},
|
||||
ecmaVersion: 2018,
|
||||
sourceType: 'module',
|
||||
},
|
||||
plugins: ['@typescript-eslint'],
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'plugin:react/recommended',
|
||||
'airbnb-typescript-prettier',
|
||||
],
|
||||
rules: typescriptRules,
|
||||
},
|
||||
{
|
||||
files: ['**/*.stories.tsx', 'ts/build/**', 'ts/test-*/**'],
|
||||
rules: {
|
||||
|
|
|
@ -27,7 +27,7 @@ process.env.NODE_CONFIG_DIR = join(__dirname, '..', 'config');
|
|||
if (getEnvironment() === Environment.Production) {
|
||||
// harden production config against the local env
|
||||
process.env.NODE_CONFIG = '';
|
||||
process.env.NODE_CONFIG_STRICT_MODE = 'true';
|
||||
process.env.NODE_CONFIG_STRICT_MODE = '';
|
||||
process.env.HOSTNAME = '';
|
||||
process.env.NODE_APP_INSTANCE = '';
|
||||
process.env.ALLOW_CONFIG_MUTATIONS = '';
|
||||
|
|
118
app/main.ts
118
app/main.ts
|
@ -42,12 +42,14 @@ import { createSupportUrl } from '../ts/util/createSupportUrl';
|
|||
import { missingCaseError } from '../ts/util/missingCaseError';
|
||||
import { strictAssert } from '../ts/util/assert';
|
||||
import { consoleLogger } from '../ts/util/consoleLogger';
|
||||
import type { ThemeSettingType } from '../ts/types/Storage.d';
|
||||
import type { ThemeSettingType } from '../ts/types/StorageUIKeys';
|
||||
import { ThemeType } from '../ts/types/Util';
|
||||
|
||||
import './startup_config';
|
||||
|
||||
import type { ConfigType } from './config';
|
||||
import type { RendererConfigType } from '../ts/types/RendererConfig';
|
||||
import { rendererConfigSchema } from '../ts/types/RendererConfig';
|
||||
import config from './config';
|
||||
import {
|
||||
Environment,
|
||||
|
@ -349,25 +351,29 @@ function getLocale(): LocaleType {
|
|||
return locale;
|
||||
}
|
||||
|
||||
function prepareFileUrl(
|
||||
type PrepareUrlOptions = { forCalling?: boolean; forCamera?: boolean };
|
||||
|
||||
async function prepareFileUrl(
|
||||
pathSegments: ReadonlyArray<string>,
|
||||
moreKeys?: undefined | Record<string, unknown>
|
||||
): string {
|
||||
options: PrepareUrlOptions = {}
|
||||
): Promise<string> {
|
||||
const filePath = join(...pathSegments);
|
||||
const fileUrl = pathToFileURL(filePath);
|
||||
return prepareUrl(fileUrl, moreKeys);
|
||||
return prepareUrl(fileUrl, options);
|
||||
}
|
||||
|
||||
function prepareUrl(
|
||||
async function prepareUrl(
|
||||
url: URL,
|
||||
moreKeys?: undefined | Record<string, unknown>
|
||||
): string {
|
||||
return setUrlSearchParams(url, {
|
||||
{ forCalling, forCamera }: PrepareUrlOptions = {}
|
||||
): Promise<string> {
|
||||
const theme = await getResolvedThemeSetting();
|
||||
|
||||
const urlParams: RendererConfigType = {
|
||||
name: packageJson.productName,
|
||||
locale: locale ? locale.name : undefined,
|
||||
locale: getLocale().name,
|
||||
version: app.getVersion(),
|
||||
buildCreation: config.get<number | undefined>('buildCreation'),
|
||||
buildExpiration: config.get<number | undefined>('buildExpiration'),
|
||||
buildCreation: config.get<number>('buildCreation'),
|
||||
buildExpiration: config.get<number>('buildExpiration'),
|
||||
serverUrl: config.get<string>('serverUrl'),
|
||||
storageUrl: config.get<string>('storageUrl'),
|
||||
updatesUrl: config.get<string>('updatesUrl'),
|
||||
|
@ -377,29 +383,52 @@ function prepareUrl(
|
|||
config.get<string | null>('directoryEnclaveId') || undefined,
|
||||
directoryTrustAnchor:
|
||||
config.get<string | null>('directoryTrustAnchor') || undefined,
|
||||
directoryV2Url: config.get<string>('directoryV2Url'),
|
||||
directoryV2PublicKey: config.get<string>('directoryV2PublicKey'),
|
||||
directoryV2CodeHashes: config.get<string>('directoryV2CodeHashes'),
|
||||
directoryV2Url: config.get<string | null>('directoryV2Url') || undefined,
|
||||
directoryV2PublicKey:
|
||||
config.get<string | null>('directoryV2PublicKey') || undefined,
|
||||
directoryV2CodeHashes:
|
||||
config.get<Array<string> | null>('directoryV2CodeHashes') || undefined,
|
||||
cdnUrl0: config.get<ConfigType>('cdn').get<string>('0'),
|
||||
cdnUrl2: config.get<ConfigType>('cdn').get<string>('2'),
|
||||
certificateAuthority: config.get<string>('certificateAuthority'),
|
||||
environment: enableCI ? 'production' : getEnvironment(),
|
||||
enableCI: enableCI ? 'true' : '',
|
||||
node_version: process.versions.node,
|
||||
environment: enableCI ? Environment.Production : getEnvironment(),
|
||||
enableCI,
|
||||
nodeVersion: process.versions.node,
|
||||
hostname: os.hostname(),
|
||||
appInstance: process.env.NODE_APP_INSTANCE,
|
||||
proxyUrl: process.env.HTTPS_PROXY || process.env.https_proxy,
|
||||
appInstance: process.env.NODE_APP_INSTANCE || undefined,
|
||||
proxyUrl: process.env.HTTPS_PROXY || process.env.https_proxy || undefined,
|
||||
contentProxyUrl: config.get<string>('contentProxyUrl'),
|
||||
sfuUrl: config.get('sfuUrl'),
|
||||
reducedMotionSetting: animationSettings.prefersReducedMotion ? 'true' : '',
|
||||
reducedMotionSetting: animationSettings.prefersReducedMotion,
|
||||
serverPublicParams: config.get<string>('serverPublicParams'),
|
||||
serverTrustRoot: config.get<string>('serverTrustRoot'),
|
||||
theme,
|
||||
appStartInitialSpellcheckSetting,
|
||||
userDataPath: app.getPath('userData'),
|
||||
homePath: app.getPath('home'),
|
||||
crashDumpsPath: app.getPath('crashDumps'),
|
||||
...moreKeys,
|
||||
}).href;
|
||||
|
||||
// Only used by the main window
|
||||
isMainWindowFullScreen: Boolean(mainWindow?.isFullScreen()),
|
||||
|
||||
// Only for tests
|
||||
argv: JSON.stringify(process.argv),
|
||||
|
||||
// Only for permission popup window
|
||||
forCalling: Boolean(forCalling),
|
||||
forCamera: Boolean(forCamera),
|
||||
};
|
||||
|
||||
const parsed = rendererConfigSchema.safeParse(urlParams);
|
||||
if (!parsed.success) {
|
||||
throw new Error(
|
||||
`prepareUrl: Failed to parse renderer config ${JSON.stringify(
|
||||
parsed.error.flatten()
|
||||
)}`
|
||||
);
|
||||
}
|
||||
|
||||
return setUrlSearchParams(url, { config: JSON.stringify(parsed.data) }).href;
|
||||
}
|
||||
|
||||
async function handleUrl(event: Electron.Event, rawTarget: string) {
|
||||
|
@ -607,7 +636,9 @@ async function createWindow() {
|
|||
contextIsolation: false,
|
||||
preload: join(
|
||||
__dirname,
|
||||
usePreloadBundle ? '../preload.bundle.js' : '../preload.js'
|
||||
usePreloadBundle
|
||||
? '../preload.bundle.js'
|
||||
: '../ts/windows/main/preload.js'
|
||||
),
|
||||
spellcheck: await getSpellCheckSetting(),
|
||||
backgroundThrottling: isThrottlingEnabled,
|
||||
|
@ -738,22 +769,10 @@ async function createWindow() {
|
|||
// This is a fallback in case we drop an event for some reason.
|
||||
setInterval(setWindowFocus, 10000);
|
||||
|
||||
const moreKeys = {
|
||||
isFullScreen: String(Boolean(mainWindow.isFullScreen())),
|
||||
resolvedTheme: await getResolvedThemeSetting(),
|
||||
};
|
||||
|
||||
if (getEnvironment() === Environment.Test) {
|
||||
mainWindow.loadURL(
|
||||
prepareFileUrl([__dirname, '../test/index.html'], {
|
||||
...moreKeys,
|
||||
argv: JSON.stringify(process.argv),
|
||||
})
|
||||
);
|
||||
mainWindow.loadURL(await prepareFileUrl([__dirname, '../test/index.html']));
|
||||
} else {
|
||||
mainWindow.loadURL(
|
||||
prepareFileUrl([__dirname, '../background.html'], moreKeys)
|
||||
);
|
||||
mainWindow.loadURL(await prepareFileUrl([__dirname, '../background.html']));
|
||||
}
|
||||
|
||||
if (!enableCI && config.get<boolean>('openDevTools')) {
|
||||
|
@ -1039,7 +1058,7 @@ function setupAsStandalone() {
|
|||
}
|
||||
|
||||
let screenShareWindow: BrowserWindow | undefined;
|
||||
function showScreenShareWindow(sourceName: string) {
|
||||
async function showScreenShareWindow(sourceName: string) {
|
||||
if (screenShareWindow) {
|
||||
screenShareWindow.showInactive();
|
||||
return;
|
||||
|
@ -1078,7 +1097,9 @@ function showScreenShareWindow(sourceName: string) {
|
|||
|
||||
handleCommonWindowEvents(screenShareWindow);
|
||||
|
||||
screenShareWindow.loadURL(prepareFileUrl([__dirname, '../screenShare.html']));
|
||||
screenShareWindow.loadURL(
|
||||
await prepareFileUrl([__dirname, '../screenShare.html'])
|
||||
);
|
||||
|
||||
screenShareWindow.on('closed', () => {
|
||||
screenShareWindow = undefined;
|
||||
|
@ -1128,7 +1149,7 @@ async function showAbout() {
|
|||
|
||||
handleCommonWindowEvents(aboutWindow, titleBarOverlay);
|
||||
|
||||
aboutWindow.loadURL(prepareFileUrl([__dirname, '../about.html']));
|
||||
aboutWindow.loadURL(await prepareFileUrl([__dirname, '../about.html']));
|
||||
|
||||
aboutWindow.on('closed', () => {
|
||||
aboutWindow = undefined;
|
||||
|
@ -1175,7 +1196,7 @@ async function showSettingsWindow() {
|
|||
|
||||
handleCommonWindowEvents(settingsWindow, titleBarOverlay);
|
||||
|
||||
settingsWindow.loadURL(prepareFileUrl([__dirname, '../settings.html']));
|
||||
settingsWindow.loadURL(await prepareFileUrl([__dirname, '../settings.html']));
|
||||
|
||||
settingsWindow.on('closed', () => {
|
||||
settingsWindow = undefined;
|
||||
|
@ -1254,7 +1275,7 @@ async function showStickerCreator() {
|
|||
)
|
||||
: prepareFileUrl([__dirname, '../sticker-creator/dist/index.html']);
|
||||
|
||||
stickerCreatorWindow.loadURL(appUrl);
|
||||
stickerCreatorWindow.loadURL(await appUrl);
|
||||
|
||||
stickerCreatorWindow.on('closed', () => {
|
||||
stickerCreatorWindow = undefined;
|
||||
|
@ -1283,7 +1304,6 @@ async function showDebugLogWindow() {
|
|||
|
||||
const titleBarOverlay = await getTitleBarOverlay();
|
||||
|
||||
const theme = await getThemeSetting();
|
||||
const options = {
|
||||
width: 700,
|
||||
height: 500,
|
||||
|
@ -1315,7 +1335,7 @@ async function showDebugLogWindow() {
|
|||
handleCommonWindowEvents(debugLogWindow, titleBarOverlay);
|
||||
|
||||
debugLogWindow.loadURL(
|
||||
prepareFileUrl([__dirname, '../debug_log.html'], { theme })
|
||||
await prepareFileUrl([__dirname, '../debug_log.html'])
|
||||
);
|
||||
|
||||
debugLogWindow.on('closed', () => {
|
||||
|
@ -1346,7 +1366,6 @@ function showPermissionsPopupWindow(forCalling: boolean, forCamera: boolean) {
|
|||
return;
|
||||
}
|
||||
|
||||
const theme = await getThemeSetting();
|
||||
const size = mainWindow.getSize();
|
||||
const options = {
|
||||
width: Math.min(400, size[0]),
|
||||
|
@ -1374,8 +1393,7 @@ function showPermissionsPopupWindow(forCalling: boolean, forCamera: boolean) {
|
|||
handleCommonWindowEvents(permissionsPopupWindow);
|
||||
|
||||
permissionsPopupWindow.loadURL(
|
||||
prepareFileUrl([__dirname, '../permissions_popup.html'], {
|
||||
theme,
|
||||
await prepareFileUrl([__dirname, '../permissions_popup.html'], {
|
||||
forCalling,
|
||||
forCamera,
|
||||
})
|
||||
|
@ -1629,7 +1647,7 @@ app.on('ready', async () => {
|
|||
const backgroundColor = await getBackgroundColor({ ephemeralOnly: true });
|
||||
|
||||
// eslint-disable-next-line more/no-then
|
||||
Promise.race([sqlInitPromise, timeout]).then(maybeTimeout => {
|
||||
Promise.race([sqlInitPromise, timeout]).then(async maybeTimeout => {
|
||||
if (maybeTimeout !== 'timeout') {
|
||||
return;
|
||||
}
|
||||
|
@ -1665,7 +1683,7 @@ app.on('ready', async () => {
|
|||
loadingWindow = undefined;
|
||||
});
|
||||
|
||||
loadingWindow.loadURL(prepareFileUrl([__dirname, '../loading.html']));
|
||||
loadingWindow.loadURL(await prepareFileUrl([__dirname, '../loading.html']));
|
||||
});
|
||||
|
||||
try {
|
||||
|
|
|
@ -175,10 +175,6 @@
|
|||
src="ts/backbone/reliable_trigger.js"
|
||||
></script>
|
||||
|
||||
<script
|
||||
type="text/javascript"
|
||||
src="js/views/react_wrapper_view.js"
|
||||
></script>
|
||||
<script
|
||||
type="text/javascript"
|
||||
src="ts/shims/showConfirmationDialog.js"
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
// Copyright 2020 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
module.exports = {
|
||||
env: {
|
||||
browser: false,
|
||||
commonjs: true,
|
||||
node: false,
|
||||
},
|
||||
globals: {
|
||||
console: true,
|
||||
},
|
||||
parserOptions: {
|
||||
sourceType: 'module',
|
||||
},
|
||||
};
|
|
@ -1,409 +0,0 @@
|
|||
// Copyright 2018-2022 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
// The idea with this file is to make it webpackable for the style guide
|
||||
|
||||
const Backbone = require('../../ts/backbone');
|
||||
const Crypto = require('../../ts/Crypto');
|
||||
const Curve = require('../../ts/Curve');
|
||||
const {
|
||||
start: conversationControllerStart,
|
||||
} = require('../../ts/ConversationController');
|
||||
const Data = require('../../ts/sql/Client').default;
|
||||
const EmojiLib = require('../../ts/components/emoji/lib');
|
||||
const Groups = require('../../ts/groups');
|
||||
const GroupChange = require('../../ts/groupChange');
|
||||
const OS = require('../../ts/OS');
|
||||
const Stickers = require('../../ts/types/Stickers');
|
||||
const RemoteConfig = require('../../ts/RemoteConfig');
|
||||
const Util = require('../../ts/util');
|
||||
|
||||
// Components
|
||||
const {
|
||||
AttachmentList,
|
||||
} = require('../../ts/components/conversation/AttachmentList');
|
||||
const { ChatColorPicker } = require('../../ts/components/ChatColorPicker');
|
||||
const {
|
||||
ConfirmationDialog,
|
||||
} = require('../../ts/components/ConfirmationDialog');
|
||||
const {
|
||||
ContactModal,
|
||||
} = require('../../ts/components/conversation/ContactModal');
|
||||
const { Emojify } = require('../../ts/components/conversation/Emojify');
|
||||
const {
|
||||
MessageDetail,
|
||||
} = require('../../ts/components/conversation/MessageDetail');
|
||||
const { Quote } = require('../../ts/components/conversation/Quote');
|
||||
const {
|
||||
StagedLinkPreview,
|
||||
} = require('../../ts/components/conversation/StagedLinkPreview');
|
||||
const {
|
||||
DisappearingTimeDialog,
|
||||
} = require('../../ts/components/DisappearingTimeDialog');
|
||||
const {
|
||||
SystemTraySettingsCheckboxes,
|
||||
} = require('../../ts/components/conversation/SystemTraySettingsCheckboxes');
|
||||
|
||||
// State
|
||||
const {
|
||||
createChatColorPicker,
|
||||
} = require('../../ts/state/roots/createChatColorPicker');
|
||||
const {
|
||||
createConversationDetails,
|
||||
} = require('../../ts/state/roots/createConversationDetails');
|
||||
const { createApp } = require('../../ts/state/roots/createApp');
|
||||
const {
|
||||
createForwardMessageModal,
|
||||
} = require('../../ts/state/roots/createForwardMessageModal');
|
||||
const {
|
||||
createGroupLinkManagement,
|
||||
} = require('../../ts/state/roots/createGroupLinkManagement');
|
||||
const {
|
||||
createGroupV1MigrationModal,
|
||||
} = require('../../ts/state/roots/createGroupV1MigrationModal');
|
||||
const {
|
||||
createGroupV2JoinModal,
|
||||
} = require('../../ts/state/roots/createGroupV2JoinModal');
|
||||
const { createLeftPane } = require('../../ts/state/roots/createLeftPane');
|
||||
const {
|
||||
createMessageDetail,
|
||||
} = require('../../ts/state/roots/createMessageDetail');
|
||||
const {
|
||||
createConversationNotificationsSettings,
|
||||
} = require('../../ts/state/roots/createConversationNotificationsSettings');
|
||||
const {
|
||||
createGroupV2Permissions,
|
||||
} = require('../../ts/state/roots/createGroupV2Permissions');
|
||||
const {
|
||||
createPendingInvites,
|
||||
} = require('../../ts/state/roots/createPendingInvites');
|
||||
const {
|
||||
createSafetyNumberViewer,
|
||||
} = require('../../ts/state/roots/createSafetyNumberViewer');
|
||||
const {
|
||||
createStickerManager,
|
||||
} = require('../../ts/state/roots/createStickerManager');
|
||||
const {
|
||||
createStickerPreviewModal,
|
||||
} = require('../../ts/state/roots/createStickerPreviewModal');
|
||||
const {
|
||||
createShortcutGuideModal,
|
||||
} = require('../../ts/state/roots/createShortcutGuideModal');
|
||||
|
||||
const { createStore } = require('../../ts/state/createStore');
|
||||
const appDuck = require('../../ts/state/ducks/app');
|
||||
const callingDuck = require('../../ts/state/ducks/calling');
|
||||
const conversationsDuck = require('../../ts/state/ducks/conversations');
|
||||
const emojisDuck = require('../../ts/state/ducks/emojis');
|
||||
const expirationDuck = require('../../ts/state/ducks/expiration');
|
||||
const itemsDuck = require('../../ts/state/ducks/items');
|
||||
const linkPreviewsDuck = require('../../ts/state/ducks/linkPreviews');
|
||||
const networkDuck = require('../../ts/state/ducks/network');
|
||||
const searchDuck = require('../../ts/state/ducks/search');
|
||||
const stickersDuck = require('../../ts/state/ducks/stickers');
|
||||
const updatesDuck = require('../../ts/state/ducks/updates');
|
||||
const userDuck = require('../../ts/state/ducks/user');
|
||||
|
||||
const conversationsSelectors = require('../../ts/state/selectors/conversations');
|
||||
const searchSelectors = require('../../ts/state/selectors/search');
|
||||
|
||||
// Types
|
||||
const AttachmentType = require('../../ts/types/Attachment');
|
||||
const VisualAttachment = require('../../ts/types/VisualAttachment');
|
||||
const MessageType = require('../../ts/types/Message2');
|
||||
const { UUID } = require('../../ts/types/UUID');
|
||||
const { Address } = require('../../ts/types/Address');
|
||||
const { QualifiedAddress } = require('../../ts/types/QualifiedAddress');
|
||||
|
||||
// Processes / Services
|
||||
const {
|
||||
initializeGroupCredentialFetcher,
|
||||
} = require('../../ts/services/groupCredentialFetcher');
|
||||
const {
|
||||
initializeNetworkObserver,
|
||||
} = require('../../ts/services/networkObserver');
|
||||
const {
|
||||
initializeUpdateListener,
|
||||
} = require('../../ts/services/updateListener');
|
||||
const { calling } = require('../../ts/services/calling');
|
||||
const {
|
||||
enableStorageService,
|
||||
eraseAllStorageServiceState,
|
||||
runStorageServiceSyncJob,
|
||||
storageServiceUploadJob,
|
||||
} = require('../../ts/services/storage');
|
||||
|
||||
function initializeMigrations({
|
||||
userDataPath,
|
||||
getRegionCode,
|
||||
Attachments,
|
||||
Type,
|
||||
VisualType,
|
||||
logger,
|
||||
}) {
|
||||
if (!Attachments) {
|
||||
return null;
|
||||
}
|
||||
const {
|
||||
createAbsolutePathGetter,
|
||||
createReader,
|
||||
createWriterForExisting,
|
||||
createWriterForNew,
|
||||
createDoesExist,
|
||||
getAvatarsPath,
|
||||
getDraftPath,
|
||||
getPath,
|
||||
getStickersPath,
|
||||
getBadgesPath,
|
||||
getTempPath,
|
||||
openFileInFolder,
|
||||
saveAttachmentToDisk,
|
||||
} = Attachments;
|
||||
const {
|
||||
getImageDimensions,
|
||||
makeImageThumbnail,
|
||||
makeObjectUrl,
|
||||
makeVideoScreenshot,
|
||||
revokeObjectUrl,
|
||||
} = VisualType;
|
||||
|
||||
const attachmentsPath = getPath(userDataPath);
|
||||
const readAttachmentData = createReader(attachmentsPath);
|
||||
const loadAttachmentData = Type.loadData(readAttachmentData);
|
||||
const loadContactData = MessageType.loadContactData(loadAttachmentData);
|
||||
const loadPreviewData = MessageType.loadPreviewData(loadAttachmentData);
|
||||
const loadQuoteData = MessageType.loadQuoteData(loadAttachmentData);
|
||||
const loadStickerData = MessageType.loadStickerData(loadAttachmentData);
|
||||
const getAbsoluteAttachmentPath = createAbsolutePathGetter(attachmentsPath);
|
||||
const deleteOnDisk = Attachments.createDeleter(attachmentsPath);
|
||||
const writeNewAttachmentData = createWriterForNew(attachmentsPath);
|
||||
const copyIntoAttachmentsDirectory =
|
||||
Attachments.copyIntoAttachmentsDirectory(attachmentsPath);
|
||||
const doesAttachmentExist = createDoesExist(attachmentsPath);
|
||||
|
||||
const stickersPath = getStickersPath(userDataPath);
|
||||
const writeNewStickerData = createWriterForNew(stickersPath);
|
||||
const getAbsoluteStickerPath = createAbsolutePathGetter(stickersPath);
|
||||
const deleteSticker = Attachments.createDeleter(stickersPath);
|
||||
const readStickerData = createReader(stickersPath);
|
||||
|
||||
const badgesPath = getBadgesPath(userDataPath);
|
||||
const getAbsoluteBadgeImageFilePath = createAbsolutePathGetter(badgesPath);
|
||||
const writeNewBadgeImageFileData = createWriterForNew(badgesPath, '.svg');
|
||||
|
||||
const tempPath = getTempPath(userDataPath);
|
||||
const getAbsoluteTempPath = createAbsolutePathGetter(tempPath);
|
||||
const writeNewTempData = createWriterForNew(tempPath);
|
||||
const deleteTempFile = Attachments.createDeleter(tempPath);
|
||||
const readTempData = createReader(tempPath);
|
||||
const copyIntoTempDirectory =
|
||||
Attachments.copyIntoAttachmentsDirectory(tempPath);
|
||||
|
||||
const draftPath = getDraftPath(userDataPath);
|
||||
const getAbsoluteDraftPath = createAbsolutePathGetter(draftPath);
|
||||
const writeNewDraftData = createWriterForNew(draftPath);
|
||||
const deleteDraftFile = Attachments.createDeleter(draftPath);
|
||||
const readDraftData = createReader(draftPath);
|
||||
|
||||
const avatarsPath = getAvatarsPath(userDataPath);
|
||||
const getAbsoluteAvatarPath = createAbsolutePathGetter(avatarsPath);
|
||||
const writeNewAvatarData = createWriterForNew(avatarsPath);
|
||||
const deleteAvatar = Attachments.createDeleter(avatarsPath);
|
||||
|
||||
return {
|
||||
attachmentsPath,
|
||||
copyIntoAttachmentsDirectory,
|
||||
copyIntoTempDirectory,
|
||||
deleteAttachmentData: deleteOnDisk,
|
||||
deleteAvatar,
|
||||
deleteDraftFile,
|
||||
deleteExternalMessageFiles: MessageType.deleteAllExternalFiles({
|
||||
deleteAttachmentData: Type.deleteData(deleteOnDisk),
|
||||
deleteOnDisk,
|
||||
}),
|
||||
deleteSticker,
|
||||
deleteTempFile,
|
||||
doesAttachmentExist,
|
||||
getAbsoluteAttachmentPath,
|
||||
getAbsoluteAvatarPath,
|
||||
getAbsoluteBadgeImageFilePath,
|
||||
getAbsoluteDraftPath,
|
||||
getAbsoluteStickerPath,
|
||||
getAbsoluteTempPath,
|
||||
loadAttachmentData,
|
||||
loadContactData,
|
||||
loadMessage: MessageType.createAttachmentLoader(loadAttachmentData),
|
||||
loadPreviewData,
|
||||
loadQuoteData,
|
||||
loadStickerData,
|
||||
openFileInFolder,
|
||||
readAttachmentData,
|
||||
readDraftData,
|
||||
readStickerData,
|
||||
readTempData,
|
||||
saveAttachmentToDisk,
|
||||
processNewAttachment: attachment =>
|
||||
MessageType.processNewAttachment(attachment, {
|
||||
writeNewAttachmentData,
|
||||
getAbsoluteAttachmentPath,
|
||||
makeObjectUrl,
|
||||
revokeObjectUrl,
|
||||
getImageDimensions,
|
||||
makeImageThumbnail,
|
||||
makeVideoScreenshot,
|
||||
logger,
|
||||
}),
|
||||
processNewSticker: stickerData =>
|
||||
MessageType.processNewSticker(stickerData, {
|
||||
writeNewStickerData,
|
||||
getAbsoluteStickerPath,
|
||||
getImageDimensions,
|
||||
logger,
|
||||
}),
|
||||
processNewEphemeralSticker: stickerData =>
|
||||
MessageType.processNewSticker(stickerData, {
|
||||
writeNewStickerData: writeNewTempData,
|
||||
getAbsoluteStickerPath: getAbsoluteTempPath,
|
||||
getImageDimensions,
|
||||
logger,
|
||||
}),
|
||||
upgradeMessageSchema: (message, options = {}) => {
|
||||
const { maxVersion } = options;
|
||||
|
||||
return MessageType.upgradeSchema(message, {
|
||||
writeNewAttachmentData,
|
||||
getRegionCode,
|
||||
getAbsoluteAttachmentPath,
|
||||
makeObjectUrl,
|
||||
revokeObjectUrl,
|
||||
getImageDimensions,
|
||||
makeImageThumbnail,
|
||||
makeVideoScreenshot,
|
||||
logger,
|
||||
maxVersion,
|
||||
getAbsoluteStickerPath,
|
||||
writeNewStickerData,
|
||||
});
|
||||
},
|
||||
writeMessageAttachments: MessageType.createAttachmentDataWriter({
|
||||
writeExistingAttachmentData: createWriterForExisting(attachmentsPath),
|
||||
logger,
|
||||
}),
|
||||
writeNewAttachmentData: createWriterForNew(attachmentsPath),
|
||||
writeNewAvatarData,
|
||||
writeNewDraftData,
|
||||
writeNewBadgeImageFileData,
|
||||
};
|
||||
}
|
||||
|
||||
exports.setup = (options = {}) => {
|
||||
const { Attachments, userDataPath, getRegionCode, logger } = options;
|
||||
|
||||
const Migrations = initializeMigrations({
|
||||
userDataPath,
|
||||
getRegionCode,
|
||||
Attachments,
|
||||
Type: AttachmentType,
|
||||
VisualType: VisualAttachment,
|
||||
logger,
|
||||
});
|
||||
|
||||
const Components = {
|
||||
AttachmentList,
|
||||
ChatColorPicker,
|
||||
ConfirmationDialog,
|
||||
ContactModal,
|
||||
Emojify,
|
||||
MessageDetail,
|
||||
Quote,
|
||||
StagedLinkPreview,
|
||||
DisappearingTimeDialog,
|
||||
SystemTraySettingsCheckboxes,
|
||||
};
|
||||
|
||||
const Roots = {
|
||||
createApp,
|
||||
createChatColorPicker,
|
||||
createConversationDetails,
|
||||
createForwardMessageModal,
|
||||
createGroupLinkManagement,
|
||||
createGroupV1MigrationModal,
|
||||
createGroupV2JoinModal,
|
||||
createGroupV2Permissions,
|
||||
createLeftPane,
|
||||
createMessageDetail,
|
||||
createConversationNotificationsSettings,
|
||||
createPendingInvites,
|
||||
createSafetyNumberViewer,
|
||||
createShortcutGuideModal,
|
||||
createStickerManager,
|
||||
createStickerPreviewModal,
|
||||
};
|
||||
|
||||
const Ducks = {
|
||||
app: appDuck,
|
||||
calling: callingDuck,
|
||||
conversations: conversationsDuck,
|
||||
emojis: emojisDuck,
|
||||
expiration: expirationDuck,
|
||||
items: itemsDuck,
|
||||
linkPreviews: linkPreviewsDuck,
|
||||
network: networkDuck,
|
||||
updates: updatesDuck,
|
||||
user: userDuck,
|
||||
search: searchDuck,
|
||||
stickers: stickersDuck,
|
||||
};
|
||||
|
||||
const Selectors = {
|
||||
conversations: conversationsSelectors,
|
||||
search: searchSelectors,
|
||||
};
|
||||
|
||||
const Services = {
|
||||
calling,
|
||||
enableStorageService,
|
||||
eraseAllStorageServiceState,
|
||||
initializeGroupCredentialFetcher,
|
||||
initializeNetworkObserver,
|
||||
initializeUpdateListener,
|
||||
runStorageServiceSyncJob,
|
||||
storageServiceUploadJob,
|
||||
};
|
||||
|
||||
const State = {
|
||||
createStore,
|
||||
Roots,
|
||||
Ducks,
|
||||
Selectors,
|
||||
};
|
||||
|
||||
const Types = {
|
||||
Message: MessageType,
|
||||
|
||||
// Mostly for debugging
|
||||
UUID,
|
||||
Address,
|
||||
QualifiedAddress,
|
||||
};
|
||||
|
||||
return {
|
||||
Backbone,
|
||||
Components,
|
||||
Crypto,
|
||||
Curve,
|
||||
conversationControllerStart,
|
||||
Data,
|
||||
EmojiLib,
|
||||
Groups,
|
||||
GroupChange,
|
||||
Migrations,
|
||||
OS,
|
||||
RemoteConfig,
|
||||
Services,
|
||||
State,
|
||||
Stickers,
|
||||
Types,
|
||||
Util,
|
||||
};
|
||||
};
|
|
@ -147,7 +147,7 @@
|
|||
"react-blurhash": "0.1.2",
|
||||
"react-contextmenu": "2.11.0",
|
||||
"react-dom": "17.0.2",
|
||||
"react-dropzone": "10.1.7",
|
||||
"react-dropzone": "10.2.2",
|
||||
"react-hot-loader": "4.13.0",
|
||||
"react-measure": "2.3.0",
|
||||
"react-popper": "2.3.0",
|
||||
|
|
522
preload.js
522
preload.js
|
@ -1,522 +0,0 @@
|
|||
// Copyright 2017-2022 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
/* global Whisper, window */
|
||||
|
||||
/* eslint-disable global-require */
|
||||
|
||||
const preloadStartTime = Date.now();
|
||||
let preloadEndTime = 0;
|
||||
|
||||
try {
|
||||
const electron = require('electron');
|
||||
const semver = require('semver');
|
||||
const _ = require('lodash');
|
||||
const { strictAssert } = require('./ts/util/assert');
|
||||
const { parseIntWithFallback } = require('./ts/util/parseIntWithFallback');
|
||||
const { UUIDKind } = require('./ts/types/UUID');
|
||||
const { ThemeType } = require('./ts/types/Util');
|
||||
|
||||
// It is important to call this as early as possible
|
||||
const { SignalContext } = require('./ts/windows/context');
|
||||
window.i18n = SignalContext.i18n;
|
||||
|
||||
const { getEnvironment, Environment } = require('./ts/environment');
|
||||
const ipc = electron.ipcRenderer;
|
||||
|
||||
const config = require('url').parse(window.location.toString(), true).query;
|
||||
|
||||
const log = require('./ts/logging/log');
|
||||
|
||||
let title = config.name;
|
||||
if (getEnvironment() !== Environment.Production) {
|
||||
title += ` - ${getEnvironment()}`;
|
||||
}
|
||||
if (config.appInstance) {
|
||||
title += ` - ${config.appInstance}`;
|
||||
}
|
||||
|
||||
// Flags for testing
|
||||
window.GV2_ENABLE_SINGLE_CHANGE_PROCESSING = true;
|
||||
window.GV2_ENABLE_CHANGE_PROCESSING = true;
|
||||
window.GV2_ENABLE_STATE_PROCESSING = true;
|
||||
window.GV2_ENABLE_PRE_JOIN_FETCH = true;
|
||||
|
||||
window.GV2_MIGRATION_DISABLE_ADD = false;
|
||||
window.GV2_MIGRATION_DISABLE_INVITE = false;
|
||||
|
||||
window.RETRY_DELAY = false;
|
||||
|
||||
window.platform = process.platform;
|
||||
window.getTitle = () => title;
|
||||
window.getLocale = () => config.locale;
|
||||
window.getEnvironment = getEnvironment;
|
||||
window.getAppInstance = () => config.appInstance;
|
||||
window.getVersion = () => config.version;
|
||||
window.getBuildCreation = () => parseIntWithFallback(config.buildCreation, 0);
|
||||
window.getExpiration = () => {
|
||||
const sixtyDays = 60 * 86400 * 1000;
|
||||
const remoteBuildExpiration = window.storage.get('remoteBuildExpiration');
|
||||
const localBuildExpiration = window.Events.getAutoDownloadUpdate()
|
||||
? config.buildExpiration
|
||||
: config.buildExpiration - sixtyDays;
|
||||
|
||||
if (remoteBuildExpiration) {
|
||||
return remoteBuildExpiration < config.buildExpiration
|
||||
? remoteBuildExpiration
|
||||
: localBuildExpiration;
|
||||
}
|
||||
return localBuildExpiration;
|
||||
};
|
||||
window.getHostName = () => config.hostname;
|
||||
window.getServerTrustRoot = () => config.serverTrustRoot;
|
||||
window.getServerPublicParams = () => config.serverPublicParams;
|
||||
window.getSfuUrl = () => config.sfuUrl;
|
||||
window.isBehindProxy = () => Boolean(config.proxyUrl);
|
||||
window.getAutoLaunch = () => {
|
||||
return ipc.invoke('get-auto-launch');
|
||||
};
|
||||
window.setAutoLaunch = value => {
|
||||
return ipc.invoke('set-auto-launch', value);
|
||||
};
|
||||
|
||||
window.isBeforeVersion = (toCheck, baseVersion) => {
|
||||
try {
|
||||
return semver.lt(toCheck, baseVersion);
|
||||
} catch (error) {
|
||||
log.error(
|
||||
`isBeforeVersion error: toCheck: ${toCheck}, baseVersion: ${baseVersion}`,
|
||||
error && error.stack ? error.stack : error
|
||||
);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
window.isAfterVersion = (toCheck, baseVersion) => {
|
||||
try {
|
||||
return semver.gt(toCheck, baseVersion);
|
||||
} catch (error) {
|
||||
log.error(
|
||||
`isBeforeVersion error: toCheck: ${toCheck}, baseVersion: ${baseVersion}`,
|
||||
error && error.stack ? error.stack : error
|
||||
);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
window.setBadgeCount = count => ipc.send('set-badge-count', count);
|
||||
|
||||
let connectStartTime = 0;
|
||||
|
||||
window.logAuthenticatedConnect = () => {
|
||||
if (connectStartTime === 0) {
|
||||
connectStartTime = Date.now();
|
||||
}
|
||||
};
|
||||
|
||||
window.logAppLoadedEvent = ({ processedCount }) =>
|
||||
ipc.send('signal-app-loaded', {
|
||||
preloadTime: preloadEndTime - preloadStartTime,
|
||||
connectTime: connectStartTime - preloadEndTime,
|
||||
processedCount,
|
||||
});
|
||||
|
||||
// We never do these in our code, so we'll prevent it everywhere
|
||||
window.open = () => null;
|
||||
|
||||
// Playwright uses `eval` for `.evaluate()` API
|
||||
if (!config.enableCI && config.environment !== 'test') {
|
||||
// eslint-disable-next-line no-eval, no-multi-assign
|
||||
window.eval = global.eval = () => null;
|
||||
}
|
||||
|
||||
window.drawAttention = () => {
|
||||
log.info('draw attention');
|
||||
ipc.send('draw-attention');
|
||||
};
|
||||
window.showWindow = () => {
|
||||
log.info('show window');
|
||||
ipc.send('show-window');
|
||||
};
|
||||
|
||||
window.titleBarDoubleClick = () => {
|
||||
ipc.send('title-bar-double-click');
|
||||
};
|
||||
|
||||
window.setAutoHideMenuBar = autoHide =>
|
||||
ipc.send('set-auto-hide-menu-bar', autoHide);
|
||||
|
||||
window.setMenuBarVisibility = visibility =>
|
||||
ipc.send('set-menu-bar-visibility', visibility);
|
||||
|
||||
window.updateSystemTraySetting = (
|
||||
systemTraySetting /* : Readonly<SystemTraySetting> */
|
||||
) => {
|
||||
ipc.send('update-system-tray-setting', systemTraySetting);
|
||||
};
|
||||
|
||||
window.restart = () => {
|
||||
log.info('restart');
|
||||
ipc.send('restart');
|
||||
};
|
||||
window.shutdown = () => {
|
||||
log.info('shutdown');
|
||||
ipc.send('shutdown');
|
||||
};
|
||||
window.showDebugLog = () => {
|
||||
log.info('showDebugLog');
|
||||
ipc.send('show-debug-log');
|
||||
};
|
||||
|
||||
window.closeAbout = () => ipc.send('close-about');
|
||||
window.readyForUpdates = () => ipc.send('ready-for-updates');
|
||||
|
||||
window.updateTrayIcon = unreadCount =>
|
||||
ipc.send('update-tray-icon', unreadCount);
|
||||
|
||||
ipc.on('additional-log-data-request', async event => {
|
||||
const ourConversation = window.ConversationController.getOurConversation();
|
||||
const ourCapabilities = ourConversation
|
||||
? ourConversation.get('capabilities')
|
||||
: undefined;
|
||||
|
||||
const remoteConfig = window.storage.get('remoteConfig') || {};
|
||||
|
||||
let statistics;
|
||||
try {
|
||||
statistics = await window.Signal.Data.getStatisticsForLogging();
|
||||
} catch (error) {
|
||||
statistics = {};
|
||||
}
|
||||
|
||||
const ourUuid = window.textsecure.storage.user.getUuid();
|
||||
const ourPni = window.textsecure.storage.user.getUuid(UUIDKind.PNI);
|
||||
|
||||
event.sender.send('additional-log-data-response', {
|
||||
capabilities: ourCapabilities || {},
|
||||
remoteConfig: _.mapValues(remoteConfig, ({ value, enabled }) => {
|
||||
const enableString = enabled ? 'enabled' : 'disabled';
|
||||
const valueString = value && value !== 'TRUE' ? ` ${value}` : '';
|
||||
return `${enableString}${valueString}`;
|
||||
}),
|
||||
statistics,
|
||||
user: {
|
||||
deviceId: window.textsecure.storage.user.getDeviceId(),
|
||||
e164: window.textsecure.storage.user.getNumber(),
|
||||
uuid: ourUuid && ourUuid.toString(),
|
||||
pni: ourPni && ourPni.toString(),
|
||||
conversationId: ourConversation && ourConversation.id,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
ipc.on('set-up-as-new-device', () => {
|
||||
Whisper.events.trigger('setupAsNewDevice');
|
||||
});
|
||||
|
||||
ipc.on('set-up-as-standalone', () => {
|
||||
Whisper.events.trigger('setupAsStandalone');
|
||||
});
|
||||
|
||||
ipc.on('challenge:response', (_event, response) => {
|
||||
Whisper.events.trigger('challengeResponse', response);
|
||||
});
|
||||
|
||||
ipc.on('power-channel:suspend', () => {
|
||||
Whisper.events.trigger('powerMonitorSuspend');
|
||||
});
|
||||
|
||||
ipc.on('power-channel:resume', () => {
|
||||
Whisper.events.trigger('powerMonitorResume');
|
||||
});
|
||||
|
||||
ipc.on('power-channel:lock-screen', () => {
|
||||
Whisper.events.trigger('powerMonitorLockScreen');
|
||||
});
|
||||
|
||||
ipc.on('window:set-window-stats', (_event, stats) => {
|
||||
if (!Whisper.events) {
|
||||
return;
|
||||
}
|
||||
Whisper.events.trigger('setWindowStats', stats);
|
||||
});
|
||||
|
||||
ipc.on('window:set-menu-options', (_event, options) => {
|
||||
if (!Whisper.events) {
|
||||
return;
|
||||
}
|
||||
Whisper.events.trigger('setMenuOptions', options);
|
||||
});
|
||||
|
||||
window.sendChallengeRequest = request =>
|
||||
ipc.send('challenge:request', request);
|
||||
|
||||
{
|
||||
let isFullScreen = config.isFullScreen === 'true';
|
||||
|
||||
window.isFullScreen = () => isFullScreen;
|
||||
// This is later overwritten.
|
||||
window.onFullScreenChange = _.noop;
|
||||
|
||||
ipc.on('full-screen-change', (_event, isFull) => {
|
||||
isFullScreen = Boolean(isFull);
|
||||
window.onFullScreenChange(isFullScreen);
|
||||
});
|
||||
}
|
||||
|
||||
if (config.resolvedTheme === 'light') {
|
||||
window.initialTheme = ThemeType.light;
|
||||
} else if (config.resolvedTheme === 'dark') {
|
||||
window.initialTheme = ThemeType.dark;
|
||||
}
|
||||
|
||||
// Settings-related events
|
||||
|
||||
window.showSettings = () => ipc.send('show-settings');
|
||||
window.showPermissionsPopup = (forCalling, forCamera) =>
|
||||
ipc.invoke('show-permissions-popup', forCalling, forCamera);
|
||||
|
||||
ipc.on('show-keyboard-shortcuts', () => {
|
||||
window.Events.showKeyboardShortcuts();
|
||||
});
|
||||
ipc.on('add-dark-overlay', () => {
|
||||
window.Events.addDarkOverlay();
|
||||
});
|
||||
ipc.on('remove-dark-overlay', () => {
|
||||
window.Events.removeDarkOverlay();
|
||||
});
|
||||
|
||||
require('./ts/windows/preload');
|
||||
|
||||
window.getBuiltInImages = () =>
|
||||
new Promise((resolve, reject) => {
|
||||
ipc.once('get-success-built-in-images', (_event, error, value) => {
|
||||
if (error) {
|
||||
return reject(new Error(error));
|
||||
}
|
||||
|
||||
return resolve(value);
|
||||
});
|
||||
ipc.send('get-built-in-images');
|
||||
});
|
||||
|
||||
ipc.on('delete-all-data', async () => {
|
||||
const { deleteAllData } = window.Events;
|
||||
if (!deleteAllData) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await deleteAllData();
|
||||
} catch (error) {
|
||||
log.error('delete-all-data: error', error && error.stack);
|
||||
}
|
||||
});
|
||||
|
||||
ipc.on('show-sticker-pack', (_event, info) => {
|
||||
const { packId, packKey } = info;
|
||||
const { showStickerPack } = window.Events;
|
||||
if (showStickerPack) {
|
||||
showStickerPack(packId, packKey);
|
||||
}
|
||||
});
|
||||
|
||||
ipc.on('show-group-via-link', (_event, info) => {
|
||||
const { hash } = info;
|
||||
const { showGroupViaLink } = window.Events;
|
||||
if (showGroupViaLink) {
|
||||
showGroupViaLink(hash);
|
||||
}
|
||||
});
|
||||
|
||||
ipc.on('show-conversation-via-signal.me', (_event, info) => {
|
||||
const { hash } = info;
|
||||
strictAssert(typeof hash === 'string', 'Got an invalid hash over IPC');
|
||||
|
||||
const { showConversationViaSignalDotMe } = window.Events;
|
||||
if (showConversationViaSignalDotMe) {
|
||||
showConversationViaSignalDotMe(hash);
|
||||
}
|
||||
});
|
||||
|
||||
ipc.on('unknown-sgnl-link', () => {
|
||||
const { unknownSignalLink } = window.Events;
|
||||
if (unknownSignalLink) {
|
||||
unknownSignalLink();
|
||||
}
|
||||
});
|
||||
|
||||
ipc.on('install-sticker-pack', (_event, info) => {
|
||||
const { packId, packKey } = info;
|
||||
const { installStickerPack } = window.Events;
|
||||
if (installStickerPack) {
|
||||
installStickerPack(packId, packKey);
|
||||
}
|
||||
});
|
||||
|
||||
ipc.on('get-ready-for-shutdown', async () => {
|
||||
const { shutdown } = window.Events || {};
|
||||
if (!shutdown) {
|
||||
log.error('preload shutdown handler: shutdown method not found');
|
||||
ipc.send('now-ready-for-shutdown');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await shutdown();
|
||||
ipc.send('now-ready-for-shutdown');
|
||||
} catch (error) {
|
||||
ipc.send(
|
||||
'now-ready-for-shutdown',
|
||||
error && error.stack ? error.stack : error
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
ipc.on('show-release-notes', () => {
|
||||
const { showReleaseNotes } = window.Events;
|
||||
if (showReleaseNotes) {
|
||||
showReleaseNotes();
|
||||
}
|
||||
});
|
||||
|
||||
window.addSetupMenuItems = () => ipc.send('add-setup-menu-items');
|
||||
window.removeSetupMenuItems = () => ipc.send('remove-setup-menu-items');
|
||||
|
||||
// We pull these dependencies in now, from here, because they have Node.js dependencies
|
||||
|
||||
if (config.proxyUrl) {
|
||||
log.info('Using provided proxy url');
|
||||
}
|
||||
|
||||
window.nodeSetImmediate = setImmediate;
|
||||
|
||||
window.Backbone = require('backbone');
|
||||
window.textsecure = require('./ts/textsecure').default;
|
||||
|
||||
window.WebAPI = window.textsecure.WebAPI.initialize({
|
||||
url: config.serverUrl,
|
||||
storageUrl: config.storageUrl,
|
||||
updatesUrl: config.updatesUrl,
|
||||
directoryVersion: parseInt(config.directoryVersion, 10),
|
||||
directoryUrl: config.directoryUrl,
|
||||
directoryEnclaveId: config.directoryEnclaveId,
|
||||
directoryTrustAnchor: config.directoryTrustAnchor,
|
||||
directoryV2Url: config.directoryV2Url,
|
||||
directoryV2PublicKey: config.directoryV2PublicKey,
|
||||
directoryV2CodeHashes: (config.directoryV2CodeHashes || '').split(','),
|
||||
cdnUrlObject: {
|
||||
0: config.cdnUrl0,
|
||||
2: config.cdnUrl2,
|
||||
},
|
||||
certificateAuthority: config.certificateAuthority,
|
||||
contentProxyUrl: config.contentProxyUrl,
|
||||
proxyUrl: config.proxyUrl,
|
||||
version: config.version,
|
||||
});
|
||||
|
||||
const { imageToBlurHash } = require('./ts/util/imageToBlurHash');
|
||||
const { ActiveWindowService } = require('./ts/services/ActiveWindowService');
|
||||
|
||||
window.imageToBlurHash = imageToBlurHash;
|
||||
window.libphonenumber =
|
||||
require('google-libphonenumber').PhoneNumberUtil.getInstance();
|
||||
window.libphonenumber.PhoneNumberFormat =
|
||||
require('google-libphonenumber').PhoneNumberFormat;
|
||||
|
||||
const activeWindowService = new ActiveWindowService();
|
||||
activeWindowService.initialize(window.document, ipc);
|
||||
window.isActive = activeWindowService.isActive.bind(activeWindowService);
|
||||
window.registerForActive =
|
||||
activeWindowService.registerForActive.bind(activeWindowService);
|
||||
window.unregisterForActive =
|
||||
activeWindowService.unregisterForActive.bind(activeWindowService);
|
||||
|
||||
window.Accessibility = {
|
||||
reducedMotionSetting: Boolean(config.reducedMotionSetting),
|
||||
};
|
||||
|
||||
window.React = require('react');
|
||||
window.ReactDOM = require('react-dom');
|
||||
|
||||
window.moment = require('moment');
|
||||
require('moment/min/locales.min');
|
||||
|
||||
window.PQueue = require('p-queue').default;
|
||||
|
||||
const Signal = require('./js/modules/signal');
|
||||
const Attachments = require('./ts/windows/attachments');
|
||||
|
||||
const { locale } = config;
|
||||
window.moment.updateLocale(locale, {
|
||||
relativeTime: {
|
||||
s: window.i18n('timestamp_s'),
|
||||
m: window.i18n('timestamp_m'),
|
||||
h: window.i18n('timestamp_h'),
|
||||
},
|
||||
});
|
||||
window.moment.locale(locale);
|
||||
|
||||
const userDataPath = SignalContext.getPath('userData');
|
||||
window.baseAttachmentsPath = Attachments.getPath(userDataPath);
|
||||
window.baseStickersPath = Attachments.getStickersPath(userDataPath);
|
||||
window.baseTempPath = Attachments.getTempPath(userDataPath);
|
||||
window.baseDraftPath = Attachments.getDraftPath(userDataPath);
|
||||
|
||||
const { addSensitivePath } = require('./ts/util/privacy');
|
||||
|
||||
addSensitivePath(window.baseAttachmentsPath);
|
||||
if (config.crashDumpsPath) {
|
||||
addSensitivePath(config.crashDumpsPath);
|
||||
}
|
||||
|
||||
window.Signal = Signal.setup({
|
||||
Attachments,
|
||||
userDataPath,
|
||||
getRegionCode: () => window.storage.get('regionCode'),
|
||||
logger: log,
|
||||
});
|
||||
|
||||
if (config.enableCI) {
|
||||
const { CI } = require('./ts/CI');
|
||||
window.CI = new CI(title);
|
||||
}
|
||||
|
||||
// these need access to window.Signal:
|
||||
require('./ts/models/messages');
|
||||
require('./ts/models/conversations');
|
||||
|
||||
require('./ts/backbone/views/whisper_view');
|
||||
require('./ts/views/conversation_view');
|
||||
require('./ts/views/inbox_view');
|
||||
require('./ts/SignalProtocolStore');
|
||||
require('./ts/background');
|
||||
|
||||
window.addEventListener('contextmenu', e => {
|
||||
const editable = e.target.closest(
|
||||
'textarea, input, [contenteditable="true"]'
|
||||
);
|
||||
const link = e.target.closest('a');
|
||||
const selection = Boolean(window.getSelection().toString());
|
||||
const image = e.target.closest('.Lightbox img');
|
||||
if (!editable && !selection && !link && !image) {
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
|
||||
if (config.environment === 'test') {
|
||||
require('./preload_test');
|
||||
}
|
||||
log.info('preload complete');
|
||||
} catch (error) {
|
||||
/* eslint-disable no-console */
|
||||
if (console._log) {
|
||||
console._log('preload error!', error.stack);
|
||||
} else {
|
||||
console.log('preload error!', error.stack);
|
||||
}
|
||||
/* eslint-enable no-console */
|
||||
|
||||
throw error;
|
||||
}
|
||||
|
||||
preloadEndTime = Date.now();
|
|
@ -1,26 +0,0 @@
|
|||
// Copyright 2021-2022 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
/* global window */
|
||||
|
||||
const { ipcRenderer } = require('electron');
|
||||
const fastGlob = require('fast-glob');
|
||||
|
||||
window.assert = require('chai').assert;
|
||||
|
||||
// This is a hack to let us run TypeScript tests in the renderer process. See the
|
||||
// code in `test/index.html`.
|
||||
|
||||
window.test = {
|
||||
onComplete(info) {
|
||||
return ipcRenderer.invoke('ci:test-electron:done', info);
|
||||
},
|
||||
prepareTests() {
|
||||
fastGlob
|
||||
.sync('./ts/test-{both,electron}/**/*_test.js', {
|
||||
absolute: true,
|
||||
cwd: __dirname,
|
||||
})
|
||||
.forEach(require);
|
||||
},
|
||||
};
|
|
@ -80,6 +80,6 @@ esbuild.build({
|
|||
esbuild.build({
|
||||
...bundleDefaults,
|
||||
mainFields: ['browser', 'main'],
|
||||
entryPoints: [path.join(ROOT_DIR, 'preload.js')],
|
||||
entryPoints: [path.join(ROOT_DIR, 'ts/windows/main/preload.ts')],
|
||||
outfile: path.join(ROOT_DIR, 'preload.bundle.js'),
|
||||
});
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright 2019-2020 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import * as React from 'react';
|
||||
import React from 'react';
|
||||
import * as styles from './AppStage.scss';
|
||||
import { history } from '../../util/history';
|
||||
import { Button } from '../../elements/Button';
|
||||
|
@ -49,11 +49,15 @@ export const AppStage: React.ComponentType<Props> = props => {
|
|||
const i18n = useI18n();
|
||||
|
||||
const handleNext = React.useCallback(() => {
|
||||
history.push(next);
|
||||
if (next) {
|
||||
history.push(next);
|
||||
}
|
||||
}, [next]);
|
||||
|
||||
const handlePrev = React.useCallback(() => {
|
||||
history.push(prev);
|
||||
if (prev) {
|
||||
history.push(prev);
|
||||
}
|
||||
}, [prev]);
|
||||
|
||||
const addMoreCount = stickersDuck.useAddMoreCount();
|
||||
|
@ -71,7 +75,9 @@ export const AppStage: React.ComponentType<Props> = props => {
|
|||
) : null}
|
||||
{addMoreCount > 0 ? (
|
||||
<Text secondary>
|
||||
{i18n('StickerCreator--DropStage--addMore', [addMoreCount])}
|
||||
{i18n('StickerCreator--DropStage--addMore', [
|
||||
addMoreCount.toString(),
|
||||
])}
|
||||
</Text>
|
||||
) : null}
|
||||
{next || onNext ? (
|
||||
|
|
|
@ -85,7 +85,7 @@ export const MetaStage: React.ComponentType = () => {
|
|||
<Text>{i18n('StickerCreator--MetaStage--Field--cover--help')}</Text>
|
||||
<div className={styles.coverContainer}>
|
||||
<div {...getRootProps()} className={coverFrameClass}>
|
||||
{cover.src ? (
|
||||
{cover?.src ? (
|
||||
<img
|
||||
className={styles.coverImage}
|
||||
src={cover.src}
|
||||
|
|
|
@ -8,6 +8,8 @@ import { action } from '@storybook/addon-actions';
|
|||
import { StoryRow } from '../elements/StoryRow';
|
||||
import { StickerFrame } from './StickerFrame';
|
||||
|
||||
import type { EmojiPickDataType } from '../../ts/components/emoji/EmojiPicker';
|
||||
|
||||
export default {
|
||||
title: 'Sticker Creator/components',
|
||||
};
|
||||
|
@ -15,11 +17,13 @@ export default {
|
|||
export const _StickerFrame = (): JSX.Element => {
|
||||
const image = text('image url', '/fixtures/512x515-thumbs-up-lincoln.webp');
|
||||
const showGuide = boolean('show guide', true);
|
||||
const mode = select('mode', [null, 'removable', 'pick-emoji', 'add'], null);
|
||||
const mode = select('mode', ['removable', 'pick-emoji', 'add'], 'add');
|
||||
const onRemove = action('onRemove');
|
||||
const onDrop = action('onDrop');
|
||||
const [skinTone, setSkinTone] = React.useState(0);
|
||||
const [emoji, setEmoji] = React.useState(undefined);
|
||||
const [emoji, setEmoji] = React.useState<EmojiPickDataType | undefined>(
|
||||
undefined
|
||||
);
|
||||
|
||||
return (
|
||||
<StoryRow top>
|
||||
|
|
|
@ -94,6 +94,14 @@ export const StickerFrame = React.memo(
|
|||
|
||||
const handlePickEmoji = React.useCallback(
|
||||
(emoji: EmojiPickDataType) => {
|
||||
if (!id) {
|
||||
return;
|
||||
}
|
||||
if (!onPickEmoji) {
|
||||
throw new Error(
|
||||
'StickerFrame/handlePickEmoji: onPickEmoji was not provided!'
|
||||
);
|
||||
}
|
||||
onPickEmoji({ id, emoji });
|
||||
setEmojiPickerOpen(false);
|
||||
},
|
||||
|
@ -101,6 +109,14 @@ export const StickerFrame = React.memo(
|
|||
);
|
||||
|
||||
const handleRemove = React.useCallback(() => {
|
||||
if (!id) {
|
||||
return;
|
||||
}
|
||||
if (!onRemove) {
|
||||
throw new Error(
|
||||
'StickerFrame/handleRemove: onRemove was not provided!'
|
||||
);
|
||||
}
|
||||
onRemove(id);
|
||||
}, [onRemove, id]);
|
||||
|
||||
|
@ -238,7 +254,7 @@ export const StickerFrame = React.memo(
|
|||
</button>
|
||||
)}
|
||||
</PopperReference>
|
||||
{emojiPickerOpen && emojiPopperRoot
|
||||
{emojiPickerOpen && onSetSkinTone && emojiPopperRoot
|
||||
? createPortal(
|
||||
<Popper placement="bottom-start">
|
||||
{({ ref, style }) => (
|
||||
|
|
|
@ -16,8 +16,10 @@ import { useI18n } from '../util/i18n';
|
|||
|
||||
const queue = new PQueue({ concurrency: 3, timeout: 1000 * 60 * 2 });
|
||||
|
||||
type SmartStickerFrameProps = Omit<StickerFrameProps, 'id'> & { id: string };
|
||||
|
||||
const SmartStickerFrame = SortableElement(
|
||||
({ id, showGuide, mode }: StickerFrameProps) => {
|
||||
({ id, showGuide, mode }: SmartStickerFrameProps) => {
|
||||
const data = stickersDuck.useStickerData(id);
|
||||
const actions = stickersDuck.useStickerActions();
|
||||
const image = data.imageData ? data.imageData.src : undefined;
|
||||
|
|
|
@ -5,7 +5,7 @@ import * as React from 'react';
|
|||
import { last, noop } from 'lodash';
|
||||
import { Toast } from '../elements/Toast';
|
||||
|
||||
export type Props = React.HTMLProps<HTMLDivElement> & {
|
||||
export type Props = React.HTMLAttributes<HTMLDivElement> & {
|
||||
loaf: Array<{ id: number; text: string }>;
|
||||
onDismiss: () => unknown;
|
||||
};
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
// Copyright 2019-2020 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import * as React from 'react';
|
||||
import * as classnames from 'classnames';
|
||||
import React from 'react';
|
||||
import classnames from 'classnames';
|
||||
|
||||
import * as styles from './Button.scss';
|
||||
|
||||
export type Props = React.HTMLProps<HTMLButtonElement> & {
|
||||
export type Props = React.ButtonHTMLAttributes<HTMLButtonElement> & {
|
||||
pill?: boolean;
|
||||
primary?: boolean;
|
||||
};
|
||||
|
|
|
@ -3,9 +3,11 @@
|
|||
|
||||
import * as React from 'react';
|
||||
import type { FileWithPath } from 'react-dropzone';
|
||||
|
||||
import * as styles from './DropZone.scss';
|
||||
import { useI18n } from '../util/i18n';
|
||||
import { useStickerDropzone } from '../util/useStickerDropzone';
|
||||
import { isNotNil } from '../../ts/util/isNotNil';
|
||||
|
||||
export type Props = {
|
||||
readonly inner?: boolean;
|
||||
|
@ -32,7 +34,7 @@ export const DropZone: React.ComponentType<Props> = props => {
|
|||
|
||||
const handleDrop = React.useCallback(
|
||||
(files: ReadonlyArray<FileWithPath>) => {
|
||||
onDrop(files.map(({ path }) => path));
|
||||
onDrop(files.map(({ path }) => path).filter(isNotNil));
|
||||
},
|
||||
[onDrop]
|
||||
);
|
||||
|
|
|
@ -34,7 +34,9 @@ export const MessageMeta = React.memo((props: Props) => {
|
|||
<path d="M6.003 1H6a5.06 5.06 0 00-.5.025V.02A6.08 6.08 0 016 0h.003A6 6 0 0112 6h-1a5 5 0 00-4.997-5zM3.443.572l.502.87a5.06 5.06 0 00-.866.5l-.502-.87a6.08 6.08 0 01.866-.5z" />
|
||||
</g>
|
||||
</svg>
|
||||
<div className={itemClass}>{i18n('minutesAgo', [props.minutesAgo])}</div>
|
||||
<div className={itemClass}>
|
||||
{i18n('minutesAgo', [props.minutesAgo.toString()])}
|
||||
</div>
|
||||
<svg width={18} height={12} className={itemClass}>
|
||||
<defs>
|
||||
<path
|
||||
|
|
|
@ -2,10 +2,11 @@
|
|||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import * as React from 'react';
|
||||
import * as classnames from 'classnames';
|
||||
import classnames from 'classnames';
|
||||
|
||||
import * as styles from './ProgressBar.scss';
|
||||
|
||||
export type Props = Pick<React.HTMLProps<HTMLDivElement>, 'className'> & {
|
||||
export type Props = Pick<React.HTMLAttributes<HTMLDivElement>, 'className'> & {
|
||||
readonly count: number;
|
||||
readonly total: number;
|
||||
};
|
||||
|
|
|
@ -5,7 +5,7 @@ import * as React from 'react';
|
|||
import classNames from 'classnames';
|
||||
import * as styles from './Toast.scss';
|
||||
|
||||
export type Props = React.HTMLProps<HTMLButtonElement> & {
|
||||
export type Props = React.HTMLAttributes<HTMLButtonElement> & {
|
||||
children: React.ReactNode;
|
||||
};
|
||||
|
||||
|
|
|
@ -2,20 +2,21 @@
|
|||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import * as React from 'react';
|
||||
import * as classnames from 'classnames';
|
||||
import classnames from 'classnames';
|
||||
|
||||
import * as styles from './Typography.scss';
|
||||
|
||||
export type Props = {
|
||||
children: React.ReactNode;
|
||||
};
|
||||
|
||||
export type HeadingProps = React.HTMLProps<HTMLHeadingElement>;
|
||||
export type ParagraphProps = React.HTMLProps<HTMLParagraphElement> & {
|
||||
export type HeadingProps = React.HTMLAttributes<HTMLHeadingElement>;
|
||||
export type ParagraphProps = React.HTMLAttributes<HTMLParagraphElement> & {
|
||||
center?: boolean;
|
||||
wide?: boolean;
|
||||
secondary?: boolean;
|
||||
};
|
||||
export type SpanProps = React.HTMLProps<HTMLSpanElement>;
|
||||
export type SpanProps = React.HTMLAttributes<HTMLSpanElement>;
|
||||
|
||||
export const H1 = React.memo(
|
||||
({ children, className, ...rest }: Props & HeadingProps) => (
|
||||
|
|
|
@ -1,29 +1,37 @@
|
|||
// Copyright 2019-2022 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
/* global window */
|
||||
const { ipcRenderer: ipc } = require('electron');
|
||||
const sharp = require('sharp');
|
||||
const pify = require('pify');
|
||||
const { readFile } = require('fs');
|
||||
const config = require('url').parse(window.location.toString(), true).query;
|
||||
const { noop, uniqBy } = require('lodash');
|
||||
const pMap = require('p-map');
|
||||
import PQueue from 'p-queue';
|
||||
import Backbone from 'backbone';
|
||||
|
||||
import { ipcRenderer as ipc } from 'electron';
|
||||
import sharp from 'sharp';
|
||||
import pify from 'pify';
|
||||
import { readFile } from 'fs';
|
||||
import { noop, uniqBy } from 'lodash';
|
||||
|
||||
// It is important to call this as early as possible
|
||||
const { SignalContext } = require('../ts/windows/context');
|
||||
import { SignalContext } from '../ts/windows/context';
|
||||
|
||||
window.i18n = SignalContext.i18n;
|
||||
|
||||
const {
|
||||
import {
|
||||
deriveStickerPackKey,
|
||||
encryptAttachment,
|
||||
getRandomBytes,
|
||||
} = require('../ts/Crypto');
|
||||
const Bytes = require('../ts/Bytes');
|
||||
const { SignalService: Proto } = require('../ts/protobuf');
|
||||
const { getEnvironment } = require('../ts/environment');
|
||||
const { createSetting } = require('../ts/util/preload');
|
||||
} from '../ts/Crypto';
|
||||
import * as Bytes from '../ts/Bytes';
|
||||
import { SignalService as Proto } from '../ts/protobuf';
|
||||
import { getEnvironment } from '../ts/environment';
|
||||
import { createSetting } from '../ts/util/preload';
|
||||
import * as Attachments from '../ts/windows/attachments';
|
||||
|
||||
import * as Signal from '../ts/signal';
|
||||
import { textsecure } from '../ts/textsecure';
|
||||
|
||||
import { initialize as initializeWebAPI } from '../ts/textsecure/WebAPI';
|
||||
import { getAnimatedPngDataIfExists } from '../ts/util/getAnimatedPngDataIfExists';
|
||||
|
||||
const { config } = SignalContext;
|
||||
window.i18n = SignalContext.i18n;
|
||||
|
||||
const STICKER_SIZE = 512;
|
||||
const MIN_STICKER_DIMENSION = 10;
|
||||
|
@ -32,9 +40,10 @@ const MAX_STICKER_BYTE_LENGTH = 300 * 1024;
|
|||
|
||||
window.ROOT_PATH = window.location.href.startsWith('file') ? '../../' : '/';
|
||||
window.getEnvironment = getEnvironment;
|
||||
window.getVersion = () => config.version;
|
||||
window.PQueue = require('p-queue').default;
|
||||
window.Backbone = require('backbone');
|
||||
window.getVersion = () => window.SignalContext.config.version;
|
||||
|
||||
window.PQueue = PQueue;
|
||||
window.Backbone = Backbone;
|
||||
|
||||
window.localeMessages = ipc.sendSync('locale-data');
|
||||
|
||||
|
@ -42,27 +51,27 @@ require('../ts/SignalProtocolStore');
|
|||
|
||||
SignalContext.log.info('sticker-creator starting up...');
|
||||
|
||||
const Signal = require('../js/modules/signal');
|
||||
|
||||
window.Signal = Signal.setup({});
|
||||
window.textsecure = require('../ts/textsecure').default;
|
||||
|
||||
const { initialize: initializeWebAPI } = require('../ts/textsecure/WebAPI');
|
||||
const {
|
||||
getAnimatedPngDataIfExists,
|
||||
} = require('../ts/util/getAnimatedPngDataIfExists');
|
||||
window.Signal = Signal.setup({
|
||||
Attachments,
|
||||
getRegionCode: () => {
|
||||
throw new Error('Sticker Creator preload: Not implemented!');
|
||||
},
|
||||
logger: SignalContext.log,
|
||||
userDataPath: SignalContext.config.userDataPath,
|
||||
});
|
||||
window.textsecure = textsecure;
|
||||
|
||||
const WebAPI = initializeWebAPI({
|
||||
url: config.serverUrl,
|
||||
storageUrl: config.storageUrl,
|
||||
updatesUrl: config.updatesUrl,
|
||||
directoryVersion: parseInt(config.directoryVersion, 10),
|
||||
directoryVersion: config.directoryVersion,
|
||||
directoryUrl: config.directoryUrl,
|
||||
directoryEnclaveId: config.directoryEnclaveId,
|
||||
directoryTrustAnchor: config.directoryTrustAnchor,
|
||||
directoryV2Url: config.directoryV2Url,
|
||||
directoryV2PublicKey: config.directoryV2PublicKey,
|
||||
directoryV2CodeHashes: (config.directoryV2CodeHashes || '').split(','),
|
||||
directoryV2CodeHashes: config.directoryV2CodeHashes,
|
||||
cdnUrlObject: {
|
||||
0: config.cdnUrl0,
|
||||
2: config.cdnUrl2,
|
||||
|
@ -73,13 +82,17 @@ const WebAPI = initializeWebAPI({
|
|||
version: config.version,
|
||||
});
|
||||
|
||||
function processStickerError(message, i18nKey) {
|
||||
function processStickerError(message: string, i18nKey: string): Error {
|
||||
const result = new Error(message);
|
||||
result.errorMessageI18nKey = i18nKey;
|
||||
return result;
|
||||
}
|
||||
|
||||
window.processStickerImage = async path => {
|
||||
window.processStickerImage = async (path: string | undefined) => {
|
||||
if (!path) {
|
||||
throw new Error(`Path ${path} is not valid!`);
|
||||
}
|
||||
|
||||
const imgBuffer = await pify(readFile)(path);
|
||||
const sharpImg = sharp(imgBuffer);
|
||||
const meta = await sharpImg.metadata();
|
||||
|
@ -170,7 +183,8 @@ window.encryptAndUpload = async (
|
|||
const oldUsernameItem = await window.Signal.Data.getItemById('number_id');
|
||||
const passwordItem = await window.Signal.Data.getItemById('password');
|
||||
|
||||
if (!oldUsernameItem || !passwordItem) {
|
||||
const username = usernameItem?.value || oldUsernameItem?.value;
|
||||
if (!username || !passwordItem?.value) {
|
||||
const { message } =
|
||||
window.localeMessages['StickerCreator--Authentication--error'];
|
||||
|
||||
|
@ -182,8 +196,6 @@ window.encryptAndUpload = async (
|
|||
throw new Error(message);
|
||||
}
|
||||
|
||||
const { value: username } = usernameItem;
|
||||
const { value: oldUsername } = oldUsernameItem;
|
||||
const { value: password } = passwordItem;
|
||||
|
||||
const packKey = getRandomBytes(32);
|
||||
|
@ -191,7 +203,7 @@ window.encryptAndUpload = async (
|
|||
const iv = getRandomBytes(16);
|
||||
|
||||
const server = WebAPI.connect({
|
||||
username: username || oldUsername,
|
||||
username,
|
||||
password,
|
||||
useWebSocket: false,
|
||||
});
|
||||
|
@ -207,7 +219,9 @@ window.encryptAndUpload = async (
|
|||
manifestProto.stickers = stickers.map(({ emoji }, id) => {
|
||||
const s = new Proto.StickerPack.Sticker();
|
||||
s.id = id;
|
||||
s.emoji = emoji;
|
||||
if (emoji) {
|
||||
s.emoji = emoji;
|
||||
}
|
||||
|
||||
return s;
|
||||
});
|
||||
|
@ -222,14 +236,13 @@ window.encryptAndUpload = async (
|
|||
encryptionKey,
|
||||
iv
|
||||
);
|
||||
const encryptedStickers = await pMap(
|
||||
uniqueStickers,
|
||||
({ imageData }) => encrypt(imageData.buffer, encryptionKey, iv),
|
||||
{
|
||||
concurrency: 3,
|
||||
timeout: 1000 * 60 * 2,
|
||||
const encryptedStickers = uniqueStickers.map(({ imageData }) => {
|
||||
if (!imageData?.buffer) {
|
||||
throw new Error('encryptStickers: Missing image data on sticker');
|
||||
}
|
||||
);
|
||||
|
||||
return encrypt(imageData.buffer, encryptionKey, iv);
|
||||
});
|
||||
|
||||
const packId = await server.putStickers(
|
||||
encryptedManifest,
|
||||
|
@ -244,8 +257,12 @@ window.encryptAndUpload = async (
|
|||
return { packId, key: hexKey };
|
||||
};
|
||||
|
||||
async function encrypt(data, key, iv) {
|
||||
const { ciphertext } = await encryptAttachment(data, key, iv);
|
||||
function encrypt(
|
||||
data: Uint8Array,
|
||||
key: Uint8Array,
|
||||
iv: Uint8Array
|
||||
): Uint8Array {
|
||||
const { ciphertext } = encryptAttachment(data, key, iv);
|
||||
|
||||
return ciphertext;
|
||||
}
|
|
@ -10,20 +10,12 @@ import { history } from './util/history';
|
|||
import { store } from './store';
|
||||
import { I18n } from './util/i18n';
|
||||
|
||||
declare global {
|
||||
// We want to extend `window` here.
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
interface Window {
|
||||
localeMessages: { [key: string]: { message: string } };
|
||||
}
|
||||
}
|
||||
|
||||
const { localeMessages } = window;
|
||||
const { localeMessages, SignalContext } = window;
|
||||
|
||||
const ColdRoot = () => (
|
||||
<ReduxProvider store={store}>
|
||||
<Router history={history}>
|
||||
<I18n messages={localeMessages}>
|
||||
<I18n messages={localeMessages} locale={SignalContext.config.locale}>
|
||||
<App />
|
||||
</I18n>
|
||||
</Router>
|
||||
|
|
|
@ -8,7 +8,16 @@ import type { Draft } from 'redux-ts-utils';
|
|||
import { createAction, handleAction, reduceReducers } from 'redux-ts-utils';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import { clamp, find, isNumber, pull, remove, take, uniq } from 'lodash';
|
||||
import {
|
||||
clamp,
|
||||
find,
|
||||
isNumber,
|
||||
isString,
|
||||
pull,
|
||||
remove,
|
||||
take,
|
||||
uniq,
|
||||
} from 'lodash';
|
||||
import type { SortEnd } from 'react-sortable-hoc';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import arrayMove from 'array-move';
|
||||
|
@ -20,6 +29,7 @@ import type {
|
|||
} from '../../util/preload';
|
||||
import type { EmojiPickDataType } from '../../../ts/components/emoji/EmojiPicker';
|
||||
import { convertShortName } from '../../../ts/components/emoji/lib';
|
||||
import { isNotNil } from '../../../ts/util/isNotNil';
|
||||
|
||||
export const initializeStickers = createAction<Array<string>>(
|
||||
'stickers/initializeStickers'
|
||||
|
@ -39,7 +49,7 @@ export const setPackMeta = createAction<PackMetaData>('stickers/setPackMeta');
|
|||
|
||||
export const addToast = createAction<{
|
||||
key: string;
|
||||
subs?: Array<number | string>;
|
||||
subs?: Array<string>;
|
||||
}>('stickers/addToast');
|
||||
export const dismissToast = createAction<void>('stickers/dismissToast');
|
||||
|
||||
|
@ -57,7 +67,7 @@ type StateStickerData = {
|
|||
|
||||
type StateToastData = {
|
||||
key: string;
|
||||
subs?: Array<number | string>;
|
||||
subs?: Array<string>;
|
||||
};
|
||||
|
||||
export type State = {
|
||||
|
@ -149,14 +159,21 @@ export const reducer = reduceReducers<State>(
|
|||
return oldToast;
|
||||
}
|
||||
|
||||
const newToast = { key, subs: [0] };
|
||||
const newToast = { key, subs: ['0'] };
|
||||
state.toasts.push(newToast);
|
||||
|
||||
return newToast;
|
||||
})();
|
||||
|
||||
if (toast.subs && isNumber(toast.subs[0])) {
|
||||
toast.subs[0] = (toast.subs[0] || 0) + 1;
|
||||
const previousSub = toast?.subs?.[0];
|
||||
if (toast && isString(previousSub)) {
|
||||
const previousCount = parseInt(previousSub, 10);
|
||||
const newCount = Number.isFinite(previousCount)
|
||||
? previousCount + 1
|
||||
: 1;
|
||||
|
||||
toast.subs = toast.subs || [];
|
||||
toast.subs[0] = newCount.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -276,7 +293,7 @@ export const useAddMoreCount = (): number =>
|
|||
|
||||
const selectOrderedData = createSelector(
|
||||
({ stickers }: AppState) => stickers.order,
|
||||
({ stickers }) => stickers.data,
|
||||
({ stickers }: AppState) => stickers.data,
|
||||
(order, data) =>
|
||||
order.map(id => ({
|
||||
...data[id],
|
||||
|
@ -290,8 +307,10 @@ const selectOrderedData = createSelector(
|
|||
export const useSelectOrderedData = (): Array<StickerData> =>
|
||||
useSelector(selectOrderedData);
|
||||
|
||||
const selectOrderedImagePaths = createSelector(selectOrderedData, data =>
|
||||
data.map(({ imageData }) => imageData.src)
|
||||
const selectOrderedImagePaths = createSelector(
|
||||
selectOrderedData,
|
||||
(data: Array<StickerData>) =>
|
||||
data.map(({ imageData }) => imageData?.src).filter(isNotNil)
|
||||
);
|
||||
|
||||
export const useOrderedImagePaths = (): Array<string> =>
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "es2017",
|
||||
"module": "commonjs",
|
||||
"lib": ["dom", "es2017"],
|
||||
"jsx": "react",
|
||||
"rootDir": "."
|
||||
}
|
||||
}
|
|
@ -2,81 +2,89 @@
|
|||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import * as React from 'react';
|
||||
import type { LocalizerType, ReplacementValuesType } from '../../ts/types/Util';
|
||||
|
||||
export type I18nFn = (
|
||||
key: string,
|
||||
substitutions?: Array<string | number> | ReplacementValuesType
|
||||
) => string;
|
||||
const placeholder = () => 'NO LOCALE LOADED';
|
||||
placeholder.getLocale = () => 'none';
|
||||
|
||||
export type ReplacementValuesType = {
|
||||
[key: string]: string | number;
|
||||
};
|
||||
|
||||
const I18nContext = React.createContext<I18nFn>(() => 'NO LOCALE LOADED');
|
||||
const I18nContext = React.createContext<LocalizerType>(placeholder);
|
||||
|
||||
export type I18nProps = {
|
||||
children: React.ReactNode;
|
||||
locale: string;
|
||||
messages: { [key: string]: { message: string } };
|
||||
};
|
||||
|
||||
export const I18n = ({ messages, children }: I18nProps): JSX.Element => {
|
||||
const getMessage = React.useCallback<I18nFn>(
|
||||
(key, substitutions) => {
|
||||
if (Array.isArray(substitutions) && substitutions.length > 1) {
|
||||
throw new Error(
|
||||
'Array syntax is not supported with more than one placeholder'
|
||||
export const I18n = ({
|
||||
messages,
|
||||
locale,
|
||||
children,
|
||||
}: I18nProps): JSX.Element => {
|
||||
const callback = (key: string, substitutions?: ReplacementValuesType) => {
|
||||
if (Array.isArray(substitutions) && substitutions.length > 1) {
|
||||
throw new Error(
|
||||
'Array syntax is not supported with more than one placeholder'
|
||||
);
|
||||
}
|
||||
|
||||
const stringInfo = messages[key];
|
||||
if (!stringInfo) {
|
||||
window.SignalContext.log.warn(
|
||||
`getMessage: No string found for key ${key}`
|
||||
);
|
||||
return '';
|
||||
}
|
||||
|
||||
const { message } = stringInfo;
|
||||
if (!substitutions) {
|
||||
return message;
|
||||
}
|
||||
if (Array.isArray(substitutions)) {
|
||||
return substitutions.reduce(
|
||||
(result, substitution) =>
|
||||
result.toString().replace(/\$.+?\$/, substitution.toString()),
|
||||
message
|
||||
) as string;
|
||||
}
|
||||
|
||||
const FIND_REPLACEMENTS = /\$([^$]+)\$/g;
|
||||
|
||||
let match = FIND_REPLACEMENTS.exec(message);
|
||||
let builder = '';
|
||||
let lastTextIndex = 0;
|
||||
|
||||
while (match) {
|
||||
if (lastTextIndex < match.index) {
|
||||
builder += message.slice(lastTextIndex, match.index);
|
||||
}
|
||||
|
||||
const placeholderName = match[1];
|
||||
const value = substitutions[placeholderName];
|
||||
if (!value) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(
|
||||
`i18n: Value not provided for placeholder ${placeholderName} in key '${key}'`
|
||||
);
|
||||
}
|
||||
builder += value || '';
|
||||
|
||||
const { message } = messages[key];
|
||||
if (!substitutions) {
|
||||
return message;
|
||||
}
|
||||
if (Array.isArray(substitutions)) {
|
||||
return substitutions.reduce(
|
||||
(result, substitution) =>
|
||||
result.toString().replace(/\$.+?\$/, substitution.toString()),
|
||||
message
|
||||
) as string;
|
||||
}
|
||||
lastTextIndex = FIND_REPLACEMENTS.lastIndex;
|
||||
match = FIND_REPLACEMENTS.exec(message);
|
||||
}
|
||||
|
||||
const FIND_REPLACEMENTS = /\$([^$]+)\$/g;
|
||||
if (lastTextIndex < message.length) {
|
||||
builder += message.slice(lastTextIndex);
|
||||
}
|
||||
|
||||
let match = FIND_REPLACEMENTS.exec(message);
|
||||
let builder = '';
|
||||
let lastTextIndex = 0;
|
||||
return builder;
|
||||
};
|
||||
callback.getLocale = () => locale;
|
||||
|
||||
while (match) {
|
||||
if (lastTextIndex < match.index) {
|
||||
builder += message.slice(lastTextIndex, match.index);
|
||||
}
|
||||
|
||||
const placeholderName = match[1];
|
||||
const value = substitutions[placeholderName];
|
||||
if (!value) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(
|
||||
`i18n: Value not provided for placeholder ${placeholderName} in key '${key}'`
|
||||
);
|
||||
}
|
||||
builder += value || '';
|
||||
|
||||
lastTextIndex = FIND_REPLACEMENTS.lastIndex;
|
||||
match = FIND_REPLACEMENTS.exec(message);
|
||||
}
|
||||
|
||||
if (lastTextIndex < message.length) {
|
||||
builder += message.slice(lastTextIndex);
|
||||
}
|
||||
|
||||
return builder;
|
||||
},
|
||||
[messages]
|
||||
);
|
||||
const getMessage = React.useCallback<LocalizerType>(callback, [messages]);
|
||||
|
||||
return (
|
||||
<I18nContext.Provider value={getMessage}>{children}</I18nContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const useI18n = (): I18nFn => React.useContext(I18nContext);
|
||||
export const useI18n = (): LocalizerType => React.useContext(I18nContext);
|
||||
|
|
|
@ -19,7 +19,9 @@ export type StickerImageData = {
|
|||
meta: Metadata;
|
||||
};
|
||||
|
||||
type ProcessStickerImageFn = (path: string) => Promise<StickerImageData>;
|
||||
type ProcessStickerImageFn = (
|
||||
path: string | undefined
|
||||
) => Promise<StickerImageData>;
|
||||
|
||||
export type StickerData = { imageData?: StickerImageData; emoji?: string };
|
||||
export type PackMetaData = { packId: string; key: string };
|
||||
|
@ -27,7 +29,7 @@ export type PackMetaData = { packId: string; key: string };
|
|||
export type EncryptAndUploadFn = (
|
||||
manifest: { title: string; author: string },
|
||||
stickers: Array<StickerData>,
|
||||
cover: StickerImageData,
|
||||
cover: StickerImageData | undefined,
|
||||
onProgress?: () => unknown
|
||||
) => Promise<PackMetaData>;
|
||||
|
||||
|
|
|
@ -83,32 +83,11 @@
|
|||
</script>
|
||||
<script type="text/javascript" src="test.js"></script>
|
||||
|
||||
<script type="text/javascript" src="../js/database.js" data-cover></script>
|
||||
|
||||
<script type="text/javascript" src="../js/libphonenumber-util.js"></script>
|
||||
<script
|
||||
type="text/javascript"
|
||||
src="../js/expiring_messages.js"
|
||||
data-cover
|
||||
></script>
|
||||
<script
|
||||
type="text/javascript"
|
||||
src="../js/expiring_tap_to_view_messages.js"
|
||||
data-cover
|
||||
></script>
|
||||
|
||||
<script
|
||||
type="text/javascript"
|
||||
src="../js/views/react_wrapper_view.js"
|
||||
></script>
|
||||
|
||||
<script type="text/javascript" src="views/whisper_view_test.js"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
window.Signal.conversationControllerStart();
|
||||
|
||||
window.test.prepareTests();
|
||||
delete window.test.prepareTests;
|
||||
window.testUtilities.prepareTests();
|
||||
delete window.testUtilities.prepareTests;
|
||||
|
||||
!(function () {
|
||||
const passed = [];
|
||||
|
@ -126,7 +105,9 @@
|
|||
});
|
||||
});
|
||||
|
||||
runner.on('end', () => window.test.onComplete({ passed, failed }));
|
||||
runner.on('end', () =>
|
||||
window.testUtilities.onComplete({ passed, failed })
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -52,6 +52,7 @@ import { QualifiedAddress } from './types/QualifiedAddress';
|
|||
import * as log from './logging/log';
|
||||
import { singleProtoJobQueue } from './jobs/singleProtoJobQueue';
|
||||
import * as Errors from './types/errors';
|
||||
import MessageSender from './textsecure/SendMessage';
|
||||
|
||||
const TIMESTAMP_THRESHOLD = 5 * 1000; // 5 seconds
|
||||
|
||||
|
@ -1386,7 +1387,7 @@ export class SignalProtocolStore extends EventsMixin {
|
|||
|
||||
// Enqueue a null message with newly-created session
|
||||
await singleProtoJobQueue.add(
|
||||
window.textsecure.messaging.getNullMessage({
|
||||
MessageSender.getNullMessage({
|
||||
uuid: uuid.toString(),
|
||||
})
|
||||
);
|
||||
|
|
|
@ -149,6 +149,8 @@ import { singleProtoJobQueue } from './jobs/singleProtoJobQueue';
|
|||
import { getInitialState } from './state/getInitialState';
|
||||
import { conversationJobQueue } from './jobs/conversationJobQueue';
|
||||
import { SeenStatus } from './MessageSeenStatus';
|
||||
import MessageSender from './textsecure/SendMessage';
|
||||
import type AccountManager from './textsecure/AccountManager';
|
||||
|
||||
const MAX_ATTACHMENT_DOWNLOAD_AGE = 3600 * 72 * 1000;
|
||||
|
||||
|
@ -237,7 +239,11 @@ export async function startApp(): Promise<void> {
|
|||
},
|
||||
|
||||
async sendChallengeResponse(data) {
|
||||
await window.textsecure.messaging.sendChallengeResponse(data);
|
||||
const { messaging } = window.textsecure;
|
||||
if (!messaging) {
|
||||
throw new Error('sendChallengeResponse: messaging is not available!');
|
||||
}
|
||||
await messaging.sendChallengeResponse(data);
|
||||
},
|
||||
|
||||
onChallengeFailed() {
|
||||
|
@ -532,11 +538,14 @@ export async function startApp(): Promise<void> {
|
|||
}
|
||||
return server.getSocketStatus();
|
||||
};
|
||||
let accountManager: typeof window.textsecure.AccountManager;
|
||||
let accountManager: AccountManager;
|
||||
window.getAccountManager = () => {
|
||||
if (accountManager) {
|
||||
return accountManager;
|
||||
}
|
||||
if (!server) {
|
||||
throw new Error('getAccountManager: server is not available!');
|
||||
}
|
||||
|
||||
accountManager = new window.textsecure.AccountManager(server);
|
||||
accountManager.addEventListener('registration', () => {
|
||||
|
@ -1675,9 +1684,7 @@ export async function startApp(): Promise<void> {
|
|||
}
|
||||
|
||||
try {
|
||||
await singleProtoJobQueue.add(
|
||||
window.textsecure.messaging.getRequestKeySyncMessage()
|
||||
);
|
||||
await singleProtoJobQueue.add(MessageSender.getRequestKeySyncMessage());
|
||||
} catch (error) {
|
||||
log.error(
|
||||
'runStorageService: Failed to queue sync message',
|
||||
|
@ -1875,7 +1882,6 @@ export async function startApp(): Promise<void> {
|
|||
strictAssert(messageReceiver, 'MessageReceiver not initialized');
|
||||
|
||||
const syncRequest = new window.textsecure.SyncRequest(
|
||||
window.textsecure.messaging,
|
||||
messageReceiver,
|
||||
timeoutMillis
|
||||
);
|
||||
|
@ -2173,7 +2179,6 @@ export async function startApp(): Promise<void> {
|
|||
}
|
||||
|
||||
if (firstRun === true && deviceId !== 1) {
|
||||
const { messaging } = window.textsecure;
|
||||
const hasThemeSetting = Boolean(window.storage.get('theme-setting'));
|
||||
if (
|
||||
!hasThemeSetting &&
|
||||
|
@ -2214,11 +2219,13 @@ export async function startApp(): Promise<void> {
|
|||
try {
|
||||
await Promise.all([
|
||||
singleProtoJobQueue.add(
|
||||
messaging.getRequestConfigurationSyncMessage()
|
||||
MessageSender.getRequestConfigurationSyncMessage()
|
||||
),
|
||||
singleProtoJobQueue.add(MessageSender.getRequestBlockSyncMessage()),
|
||||
singleProtoJobQueue.add(MessageSender.getRequestGroupSyncMessage()),
|
||||
singleProtoJobQueue.add(
|
||||
MessageSender.getRequestContactSyncMessage()
|
||||
),
|
||||
singleProtoJobQueue.add(messaging.getRequestBlockSyncMessage()),
|
||||
singleProtoJobQueue.add(messaging.getRequestGroupSyncMessage()),
|
||||
singleProtoJobQueue.add(messaging.getRequestContactSyncMessage()),
|
||||
runStorageService(),
|
||||
]);
|
||||
} catch (error) {
|
||||
|
@ -2270,7 +2277,7 @@ export async function startApp(): Promise<void> {
|
|||
log.info('firstRun: requesting stickers', operations.length);
|
||||
try {
|
||||
await singleProtoJobQueue.add(
|
||||
messaging.getStickerPackSync(operations)
|
||||
MessageSender.getStickerPackSync(operations)
|
||||
);
|
||||
} catch (error) {
|
||||
log.error(
|
||||
|
@ -2345,7 +2352,7 @@ export async function startApp(): Promise<void> {
|
|||
window.waitForEmptyEventQueue = waitForEmptyEventQueue;
|
||||
|
||||
async function onEmpty() {
|
||||
const { storage, messaging } = window.textsecure;
|
||||
const { storage } = window.textsecure;
|
||||
|
||||
await Promise.all([
|
||||
window.waitForAllBatchers(),
|
||||
|
@ -2454,7 +2461,7 @@ export async function startApp(): Promise<void> {
|
|||
if (!pniIdentity) {
|
||||
log.info('Requesting PNI identity sync');
|
||||
await singleProtoJobQueue.add(
|
||||
messaging.getRequestPniIdentitySyncMessage()
|
||||
MessageSender.getRequestPniIdentitySyncMessage()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -88,7 +88,14 @@ function getUrlsToDownload(): Array<string> {
|
|||
async function downloadBadgeImageFile(url: string): Promise<string> {
|
||||
await waitForOnline(navigator, window, { timeout: 1 * MINUTE });
|
||||
|
||||
const imageFileData = await window.textsecure.server.getBadgeImageFile(url);
|
||||
const { server } = window.textsecure;
|
||||
if (!server) {
|
||||
throw new Error(
|
||||
'downloadBadgeImageFile: window.textsecure.server is not available!'
|
||||
);
|
||||
}
|
||||
|
||||
const imageFileData = await server.getBadgeImageFile(url);
|
||||
const localPath = await window.Signal.Migrations.writeNewBadgeImageFileData(
|
||||
imageFileData
|
||||
);
|
||||
|
|
|
@ -10,9 +10,9 @@ import type { AudioDevice } from 'ringrtc';
|
|||
import type { MediaDeviceSettings } from '../types/Calling';
|
||||
import type {
|
||||
ZoomFactorType,
|
||||
ThemeSettingType,
|
||||
NotificationSettingType,
|
||||
} from '../types/Storage.d';
|
||||
import type { ThemeSettingType } from '../types/StorageUIKeys';
|
||||
import { Button, ButtonVariant } from './Button';
|
||||
import { ChatColorPicker } from './ChatColorPicker';
|
||||
import { Checkbox } from './Checkbox';
|
||||
|
|
|
@ -129,6 +129,10 @@ export const StandaloneRegistration = ({
|
|||
}
|
||||
|
||||
document.location.href = getChallengeURL();
|
||||
if (!window.Signal.challengeHandler) {
|
||||
setError('Captcha handler is not ready!');
|
||||
return;
|
||||
}
|
||||
const token = await window.Signal.challengeHandler.requestCaptcha();
|
||||
|
||||
try {
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
// 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.
|
||||
|
@ -11,6 +13,8 @@ export enum Environment {
|
|||
Test = 'test',
|
||||
}
|
||||
|
||||
export const environmentSchema = z.nativeEnum(Environment);
|
||||
|
||||
let environment: undefined | Environment;
|
||||
|
||||
export function getEnvironment(): Environment {
|
||||
|
|
|
@ -33,6 +33,7 @@ import { missingCaseError } from '../util/missingCaseError';
|
|||
import { explodePromise } from '../util/explodePromise';
|
||||
import type { Job } from './Job';
|
||||
import type { ParsedJob } from './types';
|
||||
import type SendMessage from '../textsecure/SendMessage';
|
||||
|
||||
// Note: generally, we only want to add to this list. If you do need to change one of
|
||||
// these values, you'll likely need to write a database migration.
|
||||
|
@ -118,10 +119,11 @@ export type ConversationQueueJobData = z.infer<
|
|||
|
||||
export type ConversationQueueJobBundle = {
|
||||
isFinalAttempt: boolean;
|
||||
log: LoggerType;
|
||||
messaging: SendMessage;
|
||||
shouldContinue: boolean;
|
||||
timeRemaining: number;
|
||||
timestamp: number;
|
||||
log: LoggerType;
|
||||
};
|
||||
|
||||
const MAX_RETRY_TIME = durations.DAY;
|
||||
|
@ -143,6 +145,10 @@ export class ConversationJobQueue extends JobQueue<ConversationQueueJobData> {
|
|||
insert?: (job: ParsedJob<ConversationQueueJobData>) => Promise<void>
|
||||
): Promise<Job<ConversationQueueJobData>> {
|
||||
const { conversationId } = data;
|
||||
strictAssert(
|
||||
window.Signal.challengeHandler,
|
||||
'conversationJobQueue.add: Missing challengeHandler!'
|
||||
);
|
||||
window.Signal.challengeHandler.maybeSolve(conversationId);
|
||||
|
||||
return super.add(data, insert);
|
||||
|
@ -232,7 +238,7 @@ export class ConversationJobQueue extends JobQueue<ConversationQueueJobData> {
|
|||
break;
|
||||
}
|
||||
|
||||
if (window.Signal.challengeHandler.isRegistered(conversationId)) {
|
||||
if (window.Signal.challengeHandler?.isRegistered(conversationId)) {
|
||||
log.info(
|
||||
'captcha challenge is pending for this conversation; waiting at most 5m...'
|
||||
);
|
||||
|
@ -290,7 +296,13 @@ export class ConversationJobQueue extends JobQueue<ConversationQueueJobData> {
|
|||
throw missingCaseError(verificationData);
|
||||
}
|
||||
|
||||
const jobBundle = {
|
||||
const { messaging } = window.textsecure;
|
||||
if (!messaging) {
|
||||
throw new Error('messaging interface is not available!');
|
||||
}
|
||||
|
||||
const jobBundle: ConversationQueueJobBundle = {
|
||||
messaging,
|
||||
isFinalAttempt,
|
||||
shouldContinue,
|
||||
timeRemaining,
|
||||
|
@ -348,7 +360,7 @@ export class ConversationJobQueue extends JobQueue<ConversationQueueJobData> {
|
|||
}
|
||||
untrustedUuids.push(uuid);
|
||||
} else if (toProcess instanceof SendMessageChallengeError) {
|
||||
window.Signal.challengeHandler.register(
|
||||
window.Signal.challengeHandler?.register(
|
||||
{
|
||||
conversationId,
|
||||
createdAt: Date.now(),
|
||||
|
|
|
@ -39,6 +39,7 @@ export async function sendDeleteForEveryone(
|
|||
conversation: ConversationModel,
|
||||
{
|
||||
isFinalAttempt,
|
||||
messaging,
|
||||
shouldContinue,
|
||||
timestamp,
|
||||
timeRemaining,
|
||||
|
@ -108,7 +109,7 @@ export async function sendDeleteForEveryone(
|
|||
|
||||
try {
|
||||
if (isMe(conversation.attributes)) {
|
||||
const proto = await window.textsecure.messaging.getContentMessage({
|
||||
const proto = await messaging.getContentMessage({
|
||||
deletedForEveryoneTimestamp: targetTimestamp,
|
||||
profileKey,
|
||||
recipients: conversation.getRecipients(),
|
||||
|
@ -120,7 +121,7 @@ export async function sendDeleteForEveryone(
|
|||
);
|
||||
|
||||
await handleMessageSend(
|
||||
window.textsecure.messaging.sendSyncMessage({
|
||||
messaging.sendSyncMessage({
|
||||
encodedDataMessage: Proto.DataMessage.encode(
|
||||
proto.dataMessage
|
||||
).finish(),
|
||||
|
|
|
@ -24,6 +24,7 @@ export async function sendDirectExpirationTimerUpdate(
|
|||
conversation: ConversationModel,
|
||||
{
|
||||
isFinalAttempt,
|
||||
messaging,
|
||||
shouldContinue,
|
||||
timeRemaining,
|
||||
timestamp,
|
||||
|
@ -75,7 +76,7 @@ export async function sendDirectExpirationTimerUpdate(
|
|||
|
||||
const sendType = 'expirationTimerUpdate';
|
||||
const flags = Proto.DataMessage.Flags.EXPIRATION_TIMER_UPDATE;
|
||||
const proto = await window.textsecure.messaging.getContentMessage({
|
||||
const proto = await messaging.getContentMessage({
|
||||
expireTimer,
|
||||
flags,
|
||||
profileKey,
|
||||
|
@ -95,7 +96,7 @@ export async function sendDirectExpirationTimerUpdate(
|
|||
try {
|
||||
if (isMe(conversation.attributes)) {
|
||||
await handleMessageSend(
|
||||
window.textsecure.messaging.sendSyncMessage({
|
||||
messaging.sendSyncMessage({
|
||||
encodedDataMessage: Proto.DataMessage.encode(
|
||||
proto.dataMessage
|
||||
).finish(),
|
||||
|
|
|
@ -36,6 +36,7 @@ export async function sendNormalMessage(
|
|||
conversation: ConversationModel,
|
||||
{
|
||||
isFinalAttempt,
|
||||
messaging,
|
||||
shouldContinue,
|
||||
timeRemaining,
|
||||
log,
|
||||
|
@ -166,7 +167,7 @@ export async function sendNormalMessage(
|
|||
|
||||
// We're sending to Note to Self or a 'lonely group' with just us in it
|
||||
log.info('sending sync message only');
|
||||
const dataMessage = await window.textsecure.messaging.getDataMessage({
|
||||
const dataMessage = await messaging.getDataMessage({
|
||||
attachments,
|
||||
body,
|
||||
contact,
|
||||
|
@ -261,7 +262,7 @@ export async function sendNormalMessage(
|
|||
}
|
||||
|
||||
log.info('sending direct message');
|
||||
innerPromise = window.textsecure.messaging.sendMessageToIdentifier({
|
||||
innerPromise = messaging.sendMessageToIdentifier({
|
||||
attachments,
|
||||
contact,
|
||||
contentHint: ContentHint.RESENDABLE,
|
||||
|
|
|
@ -64,6 +64,7 @@ export async function sendProfileKey(
|
|||
conversation: ConversationModel,
|
||||
{
|
||||
isFinalAttempt,
|
||||
messaging,
|
||||
shouldContinue,
|
||||
timestamp,
|
||||
timeRemaining,
|
||||
|
@ -123,13 +124,13 @@ export async function sendProfileKey(
|
|||
return;
|
||||
}
|
||||
|
||||
const proto = await window.textsecure.messaging.getContentMessage({
|
||||
const proto = await messaging.getContentMessage({
|
||||
flags: Proto.DataMessage.Flags.PROFILE_KEY_UPDATE,
|
||||
profileKey,
|
||||
recipients: conversation.getRecipients(),
|
||||
timestamp,
|
||||
});
|
||||
sendPromise = window.textsecure.messaging.sendIndividualProto({
|
||||
sendPromise = messaging.sendIndividualProto({
|
||||
contentHint,
|
||||
identifier: conversation.getSendTarget(),
|
||||
options: sendOptions,
|
||||
|
|
|
@ -40,6 +40,7 @@ export async function sendReaction(
|
|||
conversation: ConversationModel,
|
||||
{
|
||||
isFinalAttempt,
|
||||
messaging,
|
||||
shouldContinue,
|
||||
timeRemaining,
|
||||
log,
|
||||
|
@ -169,7 +170,7 @@ export async function sendReaction(
|
|||
|
||||
if (recipientIdentifiersWithoutMe.length === 0) {
|
||||
log.info('sending sync reaction message only');
|
||||
const dataMessage = await window.textsecure.messaging.getDataMessage({
|
||||
const dataMessage = await messaging.getDataMessage({
|
||||
attachments: [],
|
||||
expireTimer,
|
||||
groupV2: conversation.getGroupV2Info({
|
||||
|
@ -217,7 +218,7 @@ export async function sendReaction(
|
|||
}
|
||||
|
||||
log.info('sending direct reaction message');
|
||||
promise = window.textsecure.messaging.sendMessageToIdentifier({
|
||||
promise = messaging.sendMessageToIdentifier({
|
||||
identifier: recipientIdentifiersWithoutMe[0],
|
||||
messageText: undefined,
|
||||
attachments: [],
|
||||
|
|
|
@ -13,6 +13,7 @@ import { isRecord } from '../../util/isRecord';
|
|||
import { commonShouldJobContinue } from './commonShouldJobContinue';
|
||||
import { handleCommonJobRequestError } from './handleCommonJobRequestError';
|
||||
import { missingCaseError } from '../../util/missingCaseError';
|
||||
import type SendMessage from '../../textsecure/SendMessage';
|
||||
|
||||
const CHUNK_SIZE = 100;
|
||||
|
||||
|
@ -122,25 +123,24 @@ export async function runSyncJob({
|
|||
syncMessage: true,
|
||||
});
|
||||
|
||||
const { messaging } = window.textsecure;
|
||||
if (!messaging) {
|
||||
throw new Error('messaging is not available!');
|
||||
}
|
||||
|
||||
let doSync:
|
||||
| typeof window.textsecure.messaging.syncReadMessages
|
||||
| typeof window.textsecure.messaging.syncView
|
||||
| typeof window.textsecure.messaging.syncViewOnceOpen;
|
||||
| SendMessage['syncReadMessages']
|
||||
| SendMessage['syncView']
|
||||
| SendMessage['syncViewOnceOpen'];
|
||||
switch (type) {
|
||||
case SyncTypeList.View:
|
||||
doSync = window.textsecure.messaging.syncView.bind(
|
||||
window.textsecure.messaging
|
||||
);
|
||||
doSync = messaging.syncView.bind(messaging);
|
||||
break;
|
||||
case SyncTypeList.Read:
|
||||
doSync = window.textsecure.messaging.syncReadMessages.bind(
|
||||
window.textsecure.messaging
|
||||
);
|
||||
doSync = messaging.syncReadMessages.bind(messaging);
|
||||
break;
|
||||
case SyncTypeList.ViewOnceOpen:
|
||||
doSync = window.textsecure.messaging.syncViewOnceOpen.bind(
|
||||
window.textsecure.messaging
|
||||
);
|
||||
doSync = messaging.syncViewOnceOpen.bind(messaging);
|
||||
break;
|
||||
default: {
|
||||
throw missingCaseError(type);
|
||||
|
|
|
@ -103,9 +103,14 @@ export class SingleProtoJobQueue extends JobQueue<SingleProtoJobData> {
|
|||
syncMessage: isSyncMessage,
|
||||
});
|
||||
|
||||
const { messaging } = window.textsecure;
|
||||
if (!messaging) {
|
||||
throw new Error('messaging is not available!');
|
||||
}
|
||||
|
||||
try {
|
||||
await handleMessageSend(
|
||||
window.textsecure.messaging.sendIndividualProto({
|
||||
messaging.sendIndividualProto({
|
||||
contentHint,
|
||||
identifier,
|
||||
options,
|
||||
|
|
32
ts/model-types.d.ts
vendored
32
ts/model-types.d.ts
vendored
|
@ -28,6 +28,10 @@ import AccessRequiredEnum = Proto.AccessControl.AccessRequired;
|
|||
import MemberRoleEnum = Proto.Member.Role;
|
||||
import { SeenStatus } from './MessageSeenStatus';
|
||||
import { GiftBadgeStates } from './components/conversation/Message';
|
||||
import { LinkPreviewType } from './types/message/LinkPreviews';
|
||||
|
||||
import type { StickerType } from './types/Stickers';
|
||||
import { MIMEType } from './types/MIME';
|
||||
|
||||
export type WhatIsThis = any;
|
||||
|
||||
|
@ -61,17 +65,15 @@ export type GroupMigrationType = {
|
|||
droppedMemberIds: Array<string>;
|
||||
invitedMembers: Array<GroupV2PendingMemberType>;
|
||||
};
|
||||
export type PreviewType = {
|
||||
domain: string;
|
||||
image: AttachmentType;
|
||||
title: string;
|
||||
url: string;
|
||||
|
||||
export type QuotedAttachment = {
|
||||
contentType: MIMEType;
|
||||
fileName?: string;
|
||||
thumbnail?: AttachmentType;
|
||||
};
|
||||
|
||||
export type PreviewMessageType = Array<PreviewType>;
|
||||
|
||||
export type QuotedMessageType = {
|
||||
attachments: Array<typeof window.WhatIsThis>;
|
||||
attachments: Array<WhatIsThis /* QuotedAttachment */>;
|
||||
// `author` is an old attribute that holds the author's E164. We shouldn't use it for
|
||||
// new messages, but old messages might have this attribute.
|
||||
author?: string;
|
||||
|
@ -91,16 +93,6 @@ type StoryReplyContextType = {
|
|||
messageId: string;
|
||||
};
|
||||
|
||||
export type StickerMessageType = {
|
||||
packId: string;
|
||||
stickerId: number;
|
||||
packKey: string;
|
||||
data?: AttachmentType;
|
||||
path?: string;
|
||||
width?: number;
|
||||
height?: number;
|
||||
};
|
||||
|
||||
export type RetryOptions = Readonly<{
|
||||
type: 'session-reset';
|
||||
uuid: string;
|
||||
|
@ -182,8 +174,8 @@ export type MessageAttributesType = {
|
|||
| 'verified-change';
|
||||
body?: string;
|
||||
attachments?: Array<AttachmentType>;
|
||||
preview?: PreviewMessageType;
|
||||
sticker?: StickerMessageType;
|
||||
preview?: Array<LinkPreviewType>;
|
||||
sticker?: StickerType;
|
||||
sent_at: number;
|
||||
unidentifiedDeliveries?: Array<string>;
|
||||
contact?: Array<EmbeddedContactType>;
|
||||
|
|
|
@ -38,6 +38,7 @@ import type {
|
|||
StickerType,
|
||||
} from '../textsecure/SendMessage';
|
||||
import createTaskWithTimeout from '../textsecure/TaskWithTimeout';
|
||||
import MessageSender from '../textsecure/SendMessage';
|
||||
import type { CallbackResultType } from '../textsecure/Types.d';
|
||||
import type { ConversationType } from '../state/ducks/conversations';
|
||||
import type {
|
||||
|
@ -1249,7 +1250,9 @@ export class ConversationModel extends window.Backbone
|
|||
}
|
||||
|
||||
async sendTypingMessage(isTyping: boolean): Promise<void> {
|
||||
if (!window.textsecure.messaging) {
|
||||
const { messaging } = window.textsecure;
|
||||
|
||||
if (!messaging) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1298,8 +1301,7 @@ export class ConversationModel extends window.Backbone
|
|||
`sendTypingMessage(${this.idForLogging()}): sending ${content.isTyping}`
|
||||
);
|
||||
|
||||
const contentMessage =
|
||||
window.textsecure.messaging.getTypingContentMessage(content);
|
||||
const contentMessage = messaging.getTypingContentMessage(content);
|
||||
|
||||
const { ContentHint } = Proto.UnidentifiedSenderMessage.Message;
|
||||
|
||||
|
@ -1309,7 +1311,7 @@ export class ConversationModel extends window.Backbone
|
|||
};
|
||||
if (isDirectConversation(this.attributes)) {
|
||||
await handleMessageSend(
|
||||
window.textsecure.messaging.sendMessageProtoAndWait({
|
||||
messaging.sendMessageProtoAndWait({
|
||||
timestamp,
|
||||
recipients: groupMembers,
|
||||
proto: contentMessage,
|
||||
|
@ -2504,7 +2506,7 @@ export class ConversationModel extends window.Backbone
|
|||
|
||||
try {
|
||||
await singleProtoJobQueue.add(
|
||||
window.textsecure.messaging.getMessageRequestResponseSync({
|
||||
MessageSender.getMessageRequestResponseSync({
|
||||
threadE164: this.get('e164'),
|
||||
threadUuid: this.get('uuid'),
|
||||
groupId,
|
||||
|
@ -2696,12 +2698,7 @@ export class ConversationModel extends window.Backbone
|
|||
|
||||
try {
|
||||
await singleProtoJobQueue.add(
|
||||
window.textsecure.messaging.getVerificationSync(
|
||||
e164,
|
||||
uuid.toString(),
|
||||
state,
|
||||
key
|
||||
)
|
||||
MessageSender.getVerificationSync(e164, uuid.toString(), state, key)
|
||||
);
|
||||
} catch (error) {
|
||||
log.error(
|
||||
|
@ -3987,15 +3984,11 @@ export class ConversationModel extends window.Backbone
|
|||
'desktop.mandatoryProfileSharing'
|
||||
);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
const destination = this.getSendTarget()!;
|
||||
const recipients = this.getRecipients();
|
||||
|
||||
await this.maybeApplyUniversalTimer();
|
||||
|
||||
const expireTimer = this.get('expireTimer');
|
||||
|
||||
const recipientMaybeConversations = map(recipients, identifier =>
|
||||
const recipientMaybeConversations = map(this.getRecipients(), identifier =>
|
||||
window.ConversationController.get(identifier)
|
||||
);
|
||||
const recipientConversations = filter(
|
||||
|
@ -4020,7 +4013,8 @@ export class ConversationModel extends window.Backbone
|
|||
}
|
||||
|
||||
// Here we move attachments to disk
|
||||
const messageWithSchema = await upgradeMessageSchema({
|
||||
const attributes = await upgradeMessageSchema({
|
||||
id: UUID.generate().toString(),
|
||||
timestamp: now,
|
||||
type: 'outgoing',
|
||||
body,
|
||||
|
@ -4033,7 +4027,6 @@ export class ConversationModel extends window.Backbone
|
|||
received_at: window.Signal.Util.incrementMessageCounter(),
|
||||
received_at_ms: now,
|
||||
expireTimer,
|
||||
recipients,
|
||||
readStatus: ReadStatus.Read,
|
||||
seenStatus: SeenStatus.NotApplicable,
|
||||
sticker,
|
||||
|
@ -4049,14 +4042,6 @@ export class ConversationModel extends window.Backbone
|
|||
storyId,
|
||||
});
|
||||
|
||||
if (isDirectConversation(this.attributes)) {
|
||||
messageWithSchema.destination = destination;
|
||||
}
|
||||
const attributes: MessageAttributesType = {
|
||||
...messageWithSchema,
|
||||
id: UUID.generate().toString(),
|
||||
};
|
||||
|
||||
const model = new window.Whisper.Message(attributes);
|
||||
const message = window.MessageController.register(model.id, model);
|
||||
message.cachedOutgoingContactData = contact;
|
||||
|
@ -4588,6 +4573,11 @@ export class ConversationModel extends window.Backbone
|
|||
|
||||
// Deprecated: only applies to GroupV1
|
||||
async leaveGroup(): Promise<void> {
|
||||
const { messaging } = window.textsecure;
|
||||
if (!messaging) {
|
||||
throw new Error('leaveGroup: Cannot leave v1 group when offline!');
|
||||
}
|
||||
|
||||
if (!isGroupV1(this.attributes)) {
|
||||
throw new Error(
|
||||
`leaveGroup: Group ${this.idForLogging()} is not GroupV1!`
|
||||
|
@ -4628,11 +4618,7 @@ export class ConversationModel extends window.Backbone
|
|||
const options = await getSendOptions(this.attributes);
|
||||
message.send(
|
||||
handleMessageSend(
|
||||
window.textsecure.messaging.leaveGroup(
|
||||
groupId,
|
||||
groupIdentifiers,
|
||||
options
|
||||
),
|
||||
messaging.leaveGroup(groupId, groupIdentifiers, options),
|
||||
{ messageIds: [], sendType: 'legacyGroupChange' }
|
||||
)
|
||||
);
|
||||
|
@ -4797,7 +4783,11 @@ export class ConversationModel extends window.Backbone
|
|||
return;
|
||||
}
|
||||
|
||||
const avatar = await window.textsecure.messaging.getAvatar(avatarPath);
|
||||
const { messaging } = window.textsecure;
|
||||
if (!messaging) {
|
||||
throw new Error('setProfileAvatar: Cannot fetch avatar when offline!');
|
||||
}
|
||||
const avatar = await messaging.getAvatar(avatarPath);
|
||||
|
||||
// decrypt
|
||||
const decrypted = decryptProfile(avatar, decryptionKey);
|
||||
|
@ -5017,17 +5007,17 @@ export class ConversationModel extends window.Backbone
|
|||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
const number = this.get('e164')!;
|
||||
try {
|
||||
const parsedNumber = window.libphonenumber.parse(number);
|
||||
const regionCode = getRegionCodeForNumber(parsedNumber);
|
||||
const parsedNumber = window.libphonenumberInstance.parse(number);
|
||||
const regionCode = getRegionCodeForNumber(number);
|
||||
if (regionCode === window.storage.get('regionCode')) {
|
||||
return window.libphonenumber.format(
|
||||
return window.libphonenumberInstance.format(
|
||||
parsedNumber,
|
||||
window.libphonenumber.PhoneNumberFormat.NATIONAL
|
||||
window.libphonenumberFormat.NATIONAL
|
||||
);
|
||||
}
|
||||
return window.libphonenumber.format(
|
||||
return window.libphonenumberInstance.format(
|
||||
parsedNumber,
|
||||
window.libphonenumber.PhoneNumberFormat.INTERNATIONAL
|
||||
window.libphonenumberFormat.INTERNATIONAL
|
||||
);
|
||||
} catch (e) {
|
||||
return number;
|
||||
|
|
|
@ -205,7 +205,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
|||
if (_.isObject(attributes)) {
|
||||
this.set(
|
||||
TypedMessage.initializeSchemaVersion({
|
||||
message: attributes,
|
||||
message: attributes as MessageAttributesType,
|
||||
logger: log,
|
||||
})
|
||||
);
|
||||
|
@ -1638,6 +1638,11 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
|||
return;
|
||||
}
|
||||
|
||||
const { messaging } = window.textsecure;
|
||||
if (!messaging) {
|
||||
throw new Error('sendSyncMessage: messaging not available!');
|
||||
}
|
||||
|
||||
this.syncPromise = this.syncPromise || Promise.resolve();
|
||||
const next = async () => {
|
||||
const dataMessage = this.get('dataMessage');
|
||||
|
@ -1677,7 +1682,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
|||
);
|
||||
|
||||
return handleMessageSend(
|
||||
window.textsecure.messaging.sendSyncMessage({
|
||||
messaging.sendSyncMessage({
|
||||
encodedDataMessage: dataMessage,
|
||||
timestamp: this.get('sent_at'),
|
||||
destination: conv.get('e164'),
|
||||
|
@ -2347,6 +2352,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
|||
]);
|
||||
|
||||
const withQuoteReference = {
|
||||
...message.attributes,
|
||||
...initialMessage,
|
||||
quote,
|
||||
storyId: storyQuote?.id,
|
||||
|
@ -2410,12 +2416,12 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
|||
};
|
||||
|
||||
// GroupV1
|
||||
if (!hasGroupV2Prop && dataMessage.group) {
|
||||
if (!hasGroupV2Prop && initialMessage.group) {
|
||||
const pendingGroupUpdate: GroupV1Update = {};
|
||||
|
||||
const memberConversations: Array<ConversationModel> =
|
||||
await Promise.all(
|
||||
dataMessage.group.membersE164.map((e164: string) =>
|
||||
initialMessage.group.membersE164.map((e164: string) =>
|
||||
window.ConversationController.getOrCreateAndWait(
|
||||
e164,
|
||||
'private'
|
||||
|
@ -2426,20 +2432,20 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
|||
attributes = {
|
||||
...attributes,
|
||||
type: 'group',
|
||||
groupId: dataMessage.group.id,
|
||||
groupId: initialMessage.group.id,
|
||||
};
|
||||
if (dataMessage.group.type === GROUP_TYPES.UPDATE) {
|
||||
if (initialMessage.group.type === GROUP_TYPES.UPDATE) {
|
||||
attributes = {
|
||||
...attributes,
|
||||
name: dataMessage.group.name,
|
||||
name: initialMessage.group.name,
|
||||
members: _.union(members, conversation.get('members')),
|
||||
};
|
||||
|
||||
if (dataMessage.group.name !== conversation.get('name')) {
|
||||
pendingGroupUpdate.name = dataMessage.group.name;
|
||||
if (initialMessage.group.name !== conversation.get('name')) {
|
||||
pendingGroupUpdate.name = initialMessage.group.name;
|
||||
}
|
||||
|
||||
const avatarAttachment = dataMessage.group.avatar;
|
||||
const avatarAttachment = initialMessage.group.avatar;
|
||||
|
||||
let downloadedAvatar;
|
||||
let hash;
|
||||
|
@ -2520,7 +2526,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
|||
attributes.left = false;
|
||||
conversation.set({ addedBy: getContactId(message.attributes) });
|
||||
}
|
||||
} else if (dataMessage.group.type === GROUP_TYPES.QUIT) {
|
||||
} else if (initialMessage.group.type === GROUP_TYPES.QUIT) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
const sender = window.ConversationController.get(senderId)!;
|
||||
const inGroup = Boolean(
|
||||
|
@ -2584,7 +2590,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
|||
expirationTimerUpdate: {
|
||||
source,
|
||||
sourceUuid,
|
||||
expireTimer: dataMessage.expireTimer,
|
||||
expireTimer: initialMessage.expireTimer,
|
||||
},
|
||||
});
|
||||
conversation.set({ expireTimer: dataMessage.expireTimer });
|
||||
|
@ -2626,8 +2632,8 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
|||
}
|
||||
}
|
||||
|
||||
if (dataMessage.profileKey) {
|
||||
const { profileKey } = dataMessage;
|
||||
if (initialMessage.profileKey) {
|
||||
const { profileKey } = initialMessage;
|
||||
if (
|
||||
source === window.textsecure.storage.user.getNumber() ||
|
||||
sourceUuid ===
|
||||
|
@ -2700,10 +2706,13 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
|||
navigator.languages,
|
||||
window.getLocale()
|
||||
);
|
||||
const response =
|
||||
await window.textsecure.messaging.server.getBoostBadgesFromServer(
|
||||
userLanguages
|
||||
);
|
||||
const { messaging } = window.textsecure;
|
||||
if (!messaging) {
|
||||
throw new Error('handleDataMessage: messaging is not available');
|
||||
}
|
||||
const response = await messaging.server.getBoostBadgesFromServer(
|
||||
userLanguages
|
||||
);
|
||||
const boostBadgesByLevel = parseBoostBadgeListFromServer(
|
||||
response,
|
||||
updatesUrl
|
||||
|
|
|
@ -49,6 +49,7 @@ import type {
|
|||
RemoteRecord,
|
||||
UnknownRecord,
|
||||
} from '../types/StorageService.d';
|
||||
import MessageSender from '../textsecure/SendMessage';
|
||||
|
||||
type IManifestRecordIdentifier = Proto.ManifestRecord.IIdentifier;
|
||||
|
||||
|
@ -604,9 +605,7 @@ async function uploadManifest(
|
|||
backOff.reset();
|
||||
|
||||
try {
|
||||
await singleProtoJobQueue.add(
|
||||
window.textsecure.messaging.getFetchManifestSyncMessage()
|
||||
);
|
||||
await singleProtoJobQueue.add(MessageSender.getFetchManifestSyncMessage());
|
||||
} catch (error) {
|
||||
log.error(
|
||||
`storageService.upload(${version}): Failed to queue sync message`,
|
||||
|
@ -630,10 +629,6 @@ async function stopStorageServiceSync(reason: Error) {
|
|||
await sleep(backOff.getAndIncrement());
|
||||
log.info('storageService.stopStorageServiceSync: requesting new keys');
|
||||
setTimeout(async () => {
|
||||
if (!window.textsecure.messaging) {
|
||||
throw new Error('storageService.stopStorageServiceSync: We are offline!');
|
||||
}
|
||||
|
||||
if (window.ConversationController.areWePrimaryDevice()) {
|
||||
log.warn(
|
||||
'stopStorageServiceSync: We are primary device; not sending key sync request'
|
||||
|
@ -641,9 +636,7 @@ async function stopStorageServiceSync(reason: Error) {
|
|||
return;
|
||||
}
|
||||
try {
|
||||
await singleProtoJobQueue.add(
|
||||
window.textsecure.messaging.getRequestKeySyncMessage()
|
||||
);
|
||||
await singleProtoJobQueue.add(MessageSender.getRequestKeySyncMessage());
|
||||
} catch (error) {
|
||||
log.error(
|
||||
'storageService.stopStorageServiceSync: Failed to queue sync message',
|
||||
|
@ -974,6 +967,11 @@ async function processRemoteRecords(
|
|||
if (!storageKeyBase64) {
|
||||
throw new Error('No storage key');
|
||||
}
|
||||
const { messaging } = window.textsecure;
|
||||
if (!messaging) {
|
||||
throw new Error('messaging is not available');
|
||||
}
|
||||
|
||||
const storageKey = Bytes.fromBase64(storageKeyBase64);
|
||||
|
||||
log.info(
|
||||
|
@ -993,13 +991,12 @@ async function processRemoteRecords(
|
|||
const readOperation = new Proto.ReadOperation();
|
||||
readOperation.readKey = batch.map(Bytes.fromBase64);
|
||||
|
||||
const storageItemsBuffer =
|
||||
await window.textsecure.messaging.getStorageRecords(
|
||||
Proto.ReadOperation.encode(readOperation).finish(),
|
||||
{
|
||||
credentials,
|
||||
}
|
||||
);
|
||||
const storageItemsBuffer = await messaging.getStorageRecords(
|
||||
Proto.ReadOperation.encode(readOperation).finish(),
|
||||
{
|
||||
credentials,
|
||||
}
|
||||
);
|
||||
|
||||
return Proto.StorageItems.decode(storageItemsBuffer).items ?? [];
|
||||
},
|
||||
|
@ -1403,9 +1400,7 @@ async function upload(fromSync = false): Promise<void> {
|
|||
}
|
||||
|
||||
try {
|
||||
await singleProtoJobQueue.add(
|
||||
window.textsecure.messaging.getRequestKeySyncMessage()
|
||||
);
|
||||
await singleProtoJobQueue.add(MessageSender.getRequestKeySyncMessage());
|
||||
} catch (error) {
|
||||
log.error(
|
||||
'storageService.upload: Failed to queue sync message',
|
||||
|
|
|
@ -12,11 +12,17 @@ import { singleProtoJobQueue } from '../jobs/singleProtoJobQueue';
|
|||
import { strictAssert } from '../util/assert';
|
||||
import { isWhitespace } from '../util/whitespaceStringUtil';
|
||||
import type { AvatarUpdateType } from '../types/Avatar';
|
||||
import MessageSender from '../textsecure/SendMessage';
|
||||
|
||||
export async function writeProfile(
|
||||
conversation: ConversationType,
|
||||
avatar: AvatarUpdateType
|
||||
): Promise<void> {
|
||||
const { messaging } = window.textsecure;
|
||||
if (!messaging) {
|
||||
throw new Error('messaging is not available!');
|
||||
}
|
||||
|
||||
// Before we write anything we request the user's profile so that we can
|
||||
// have an up-to-date paymentAddress to be able to include it when we write
|
||||
const model = window.ConversationController.get(conversation.id);
|
||||
|
@ -44,9 +50,7 @@ export async function writeProfile(
|
|||
conversation,
|
||||
avatar
|
||||
);
|
||||
const avatarRequestHeaders = await window.textsecure.messaging.putProfile(
|
||||
profileData
|
||||
);
|
||||
const avatarRequestHeaders = await messaging.putProfile(profileData);
|
||||
|
||||
// Upload the avatar if provided
|
||||
// delete existing files on disk if avatar has been removed
|
||||
|
@ -64,7 +68,7 @@ export async function writeProfile(
|
|||
log.info('writeProfile: not updating avatar');
|
||||
} else if (avatarRequestHeaders && encryptedAvatarData && newAvatar) {
|
||||
log.info('writeProfile: uploading new avatar');
|
||||
const avatarUrl = await window.textsecure.messaging.uploadAvatar(
|
||||
const avatarUrl = await messaging.uploadAvatar(
|
||||
avatarRequestHeaders,
|
||||
encryptedAvatarData
|
||||
);
|
||||
|
@ -109,7 +113,7 @@ export async function writeProfile(
|
|||
|
||||
try {
|
||||
await singleProtoJobQueue.add(
|
||||
window.textsecure.messaging.getFetchLocalProfileSyncMessage()
|
||||
MessageSender.getFetchLocalProfileSyncMessage()
|
||||
);
|
||||
} catch (error) {
|
||||
log.error(
|
||||
|
|
|
@ -6,6 +6,7 @@ import dataInterface from '../sql/Client';
|
|||
import { updateOurUsername } from '../util/updateOurUsername';
|
||||
import * as Errors from '../types/errors';
|
||||
import * as log from '../logging/log';
|
||||
import MessageSender from '../textsecure/SendMessage';
|
||||
|
||||
export async function writeUsername({
|
||||
username,
|
||||
|
@ -14,6 +15,11 @@ export async function writeUsername({
|
|||
username: string | undefined;
|
||||
previousUsername: string | undefined;
|
||||
}): Promise<void> {
|
||||
const { messaging } = window.textsecure;
|
||||
if (!messaging) {
|
||||
throw new Error('messaging interface is not available!');
|
||||
}
|
||||
|
||||
const me = window.ConversationController.getOurConversationOrThrow();
|
||||
await updateOurUsername();
|
||||
|
||||
|
@ -22,9 +28,9 @@ export async function writeUsername({
|
|||
}
|
||||
|
||||
if (username) {
|
||||
await window.textsecure.messaging.putUsername(username);
|
||||
await messaging.putUsername(username);
|
||||
} else {
|
||||
await window.textsecure.messaging.deleteUsername();
|
||||
await messaging.deleteUsername();
|
||||
}
|
||||
|
||||
// Update backbone, update DB, then tell linked devices about profile update
|
||||
|
@ -36,7 +42,7 @@ export async function writeUsername({
|
|||
|
||||
try {
|
||||
await singleProtoJobQueue.add(
|
||||
window.textsecure.messaging.getFetchLocalProfileSyncMessage()
|
||||
MessageSender.getFetchLocalProfileSyncMessage()
|
||||
);
|
||||
} catch (error) {
|
||||
log.error(
|
||||
|
|
|
@ -4,22 +4,13 @@
|
|||
import * as log from '../logging/log';
|
||||
import { singleProtoJobQueue } from '../jobs/singleProtoJobQueue';
|
||||
import * as Errors from '../types/errors';
|
||||
import MessageSender from '../textsecure/SendMessage';
|
||||
|
||||
export async function sendStickerPackSync(
|
||||
packId: string,
|
||||
packKey: string,
|
||||
installed: boolean
|
||||
): Promise<void> {
|
||||
const { textsecure } = window;
|
||||
|
||||
if (!textsecure.messaging) {
|
||||
log.error(
|
||||
'shim: Cannot call sendStickerPackSync, textsecure.messaging is falsey'
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (window.ConversationController.areWePrimaryDevice()) {
|
||||
log.warn(
|
||||
'shims/sendStickerPackSync: We are primary device; not sending sync'
|
||||
|
@ -29,7 +20,7 @@ export async function sendStickerPackSync(
|
|||
|
||||
try {
|
||||
await singleProtoJobQueue.add(
|
||||
textsecure.messaging.getStickerPackSync([
|
||||
MessageSender.getStickerPackSync([
|
||||
{
|
||||
packId,
|
||||
packKey,
|
||||
|
|
501
ts/signal.ts
Normal file
501
ts/signal.ts
Normal file
|
@ -0,0 +1,501 @@
|
|||
// Copyright 2018-2022 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
// The idea with this file is to make it webpackable for the style guide
|
||||
|
||||
import * as Backbone from './backbone';
|
||||
import * as Crypto from './Crypto';
|
||||
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 * as RemoteConfig from './RemoteConfig';
|
||||
import * as Util from './util';
|
||||
|
||||
// Components
|
||||
import { AttachmentList } from './components/conversation/AttachmentList';
|
||||
import { ChatColorPicker } from './components/ChatColorPicker';
|
||||
import { ConfirmationDialog } from './components/ConfirmationDialog';
|
||||
import { ContactModal } from './components/conversation/ContactModal';
|
||||
import { Emojify } from './components/conversation/Emojify';
|
||||
import { MessageDetail } from './components/conversation/MessageDetail';
|
||||
import { Quote } from './components/conversation/Quote';
|
||||
import { StagedLinkPreview } from './components/conversation/StagedLinkPreview';
|
||||
import { DisappearingTimeDialog } from './components/DisappearingTimeDialog';
|
||||
import { SystemTraySettingsCheckboxes } from './components/conversation/SystemTraySettingsCheckboxes';
|
||||
|
||||
// State
|
||||
import { createChatColorPicker } from './state/roots/createChatColorPicker';
|
||||
import { createConversationDetails } from './state/roots/createConversationDetails';
|
||||
import { createApp } from './state/roots/createApp';
|
||||
import { createForwardMessageModal } from './state/roots/createForwardMessageModal';
|
||||
import { createGroupLinkManagement } from './state/roots/createGroupLinkManagement';
|
||||
import { createGroupV1MigrationModal } from './state/roots/createGroupV1MigrationModal';
|
||||
import { createGroupV2JoinModal } from './state/roots/createGroupV2JoinModal';
|
||||
import { createLeftPane } from './state/roots/createLeftPane';
|
||||
import { createMessageDetail } from './state/roots/createMessageDetail';
|
||||
import { createConversationNotificationsSettings } from './state/roots/createConversationNotificationsSettings';
|
||||
import { createGroupV2Permissions } from './state/roots/createGroupV2Permissions';
|
||||
import { createPendingInvites } from './state/roots/createPendingInvites';
|
||||
import { createSafetyNumberViewer } from './state/roots/createSafetyNumberViewer';
|
||||
import { createStickerManager } from './state/roots/createStickerManager';
|
||||
import { createStickerPreviewModal } from './state/roots/createStickerPreviewModal';
|
||||
import { createShortcutGuideModal } from './state/roots/createShortcutGuideModal';
|
||||
|
||||
import { createStore } from './state/createStore';
|
||||
import * as appDuck from './state/ducks/app';
|
||||
import * as callingDuck from './state/ducks/calling';
|
||||
import * as conversationsDuck from './state/ducks/conversations';
|
||||
import * as emojisDuck from './state/ducks/emojis';
|
||||
import * as expirationDuck from './state/ducks/expiration';
|
||||
import * as itemsDuck from './state/ducks/items';
|
||||
import * as linkPreviewsDuck from './state/ducks/linkPreviews';
|
||||
import * as networkDuck from './state/ducks/network';
|
||||
import * as searchDuck from './state/ducks/search';
|
||||
import * as stickersDuck from './state/ducks/stickers';
|
||||
import * as updatesDuck from './state/ducks/updates';
|
||||
import * as userDuck from './state/ducks/user';
|
||||
|
||||
import * as conversationsSelectors from './state/selectors/conversations';
|
||||
import * as searchSelectors from './state/selectors/search';
|
||||
|
||||
// Types
|
||||
import * as TypesAttachment from './types/Attachment';
|
||||
import * as VisualAttachment from './types/VisualAttachment';
|
||||
import * as MessageType from './types/Message2';
|
||||
import { UUID } from './types/UUID';
|
||||
import { Address } from './types/Address';
|
||||
import { QualifiedAddress } from './types/QualifiedAddress';
|
||||
|
||||
// Processes / Services
|
||||
import { initializeGroupCredentialFetcher } from './services/groupCredentialFetcher';
|
||||
import { initializeNetworkObserver } from './services/networkObserver';
|
||||
import { initializeUpdateListener } from './services/updateListener';
|
||||
import { calling } from './services/calling';
|
||||
import {
|
||||
enableStorageService,
|
||||
eraseAllStorageServiceState,
|
||||
runStorageServiceSyncJob,
|
||||
storageServiceUploadJob,
|
||||
} from './services/storage';
|
||||
|
||||
import type { LoggerType } from './types/Logging';
|
||||
import type {
|
||||
AttachmentType,
|
||||
AttachmentWithHydratedData,
|
||||
} from './types/Attachment';
|
||||
import type { MessageAttributesType, QuotedMessageType } from './model-types.d';
|
||||
import type { SignalCoreType } from './window.d';
|
||||
import type { EmbeddedContactType } from './types/EmbeddedContact';
|
||||
import type { ContactWithHydratedAvatar } from './textsecure/SendMessage';
|
||||
import type { LinkPreviewType } from './types/message/LinkPreviews';
|
||||
import type { StickerType, StickerWithHydratedData } from './types/Stickers';
|
||||
|
||||
type MigrationsModuleType = {
|
||||
attachmentsPath: string;
|
||||
copyIntoAttachmentsDirectory: (
|
||||
path: string
|
||||
) => Promise<{ path: string; size: number }>;
|
||||
copyIntoTempDirectory: (
|
||||
path: string
|
||||
) => Promise<{ path: string; size: number }>;
|
||||
deleteAttachmentData: (path: string) => Promise<void>;
|
||||
deleteAvatar: (path: string) => Promise<void>;
|
||||
deleteDraftFile: (path: string) => Promise<void>;
|
||||
deleteExternalMessageFiles: (
|
||||
attributes: MessageAttributesType
|
||||
) => Promise<void>;
|
||||
deleteSticker: (path: string) => Promise<void>;
|
||||
deleteTempFile: (path: string) => Promise<void>;
|
||||
doesAttachmentExist: (path: string) => Promise<boolean>;
|
||||
getAbsoluteAttachmentPath: (path: string) => string;
|
||||
getAbsoluteAvatarPath: (src: string) => string;
|
||||
getAbsoluteBadgeImageFilePath: (path: string) => string;
|
||||
getAbsoluteDraftPath: (path: string) => string;
|
||||
getAbsoluteStickerPath: (path: string) => string;
|
||||
getAbsoluteTempPath: (path: string) => string;
|
||||
loadAttachmentData: (
|
||||
attachment: AttachmentType
|
||||
) => Promise<AttachmentWithHydratedData>;
|
||||
loadContactData: (
|
||||
contact: Array<EmbeddedContactType> | undefined
|
||||
) => Promise<Array<ContactWithHydratedAvatar> | undefined>;
|
||||
loadMessage: (
|
||||
message: MessageAttributesType
|
||||
) => Promise<MessageAttributesType>;
|
||||
loadPreviewData: (
|
||||
preview: Array<LinkPreviewType> | undefined
|
||||
) => Promise<Array<LinkPreviewType>>;
|
||||
loadQuoteData: (
|
||||
quote: QuotedMessageType | null | undefined
|
||||
) => Promise<QuotedMessageType | null>;
|
||||
loadStickerData: (
|
||||
sticker: StickerType | undefined
|
||||
) => Promise<StickerWithHydratedData | undefined>;
|
||||
openFileInFolder: (target: string) => Promise<void>;
|
||||
readAttachmentData: (path: string) => Promise<Uint8Array>;
|
||||
readDraftData: (path: string) => Promise<Uint8Array>;
|
||||
readStickerData: (path: string) => Promise<Uint8Array>;
|
||||
readTempData: (path: string) => Promise<Uint8Array>;
|
||||
saveAttachmentToDisk: (options: {
|
||||
data: Uint8Array;
|
||||
name: string;
|
||||
}) => Promise<null | { fullPath: string; name: string }>;
|
||||
processNewAttachment: (attachment: AttachmentType) => Promise<AttachmentType>;
|
||||
processNewSticker: (stickerData: Uint8Array) => Promise<{
|
||||
path: string;
|
||||
width: number;
|
||||
height: number;
|
||||
}>;
|
||||
processNewEphemeralSticker: (stickerData: Uint8Array) => Promise<{
|
||||
path: string;
|
||||
width: number;
|
||||
height: number;
|
||||
}>;
|
||||
upgradeMessageSchema: (
|
||||
attributes: MessageAttributesType,
|
||||
options?: { maxVersion?: number }
|
||||
) => Promise<MessageAttributesType>;
|
||||
writeMessageAttachments: (
|
||||
message: MessageAttributesType
|
||||
) => Promise<MessageAttributesType>;
|
||||
writeNewAttachmentData: (data: Uint8Array) => Promise<string>;
|
||||
writeNewDraftData: (data: Uint8Array) => Promise<string>;
|
||||
writeNewAvatarData: (data: Uint8Array) => Promise<string>;
|
||||
writeNewBadgeImageFileData: (data: Uint8Array) => Promise<string>;
|
||||
};
|
||||
|
||||
export function initializeMigrations({
|
||||
getRegionCode,
|
||||
Attachments,
|
||||
Type,
|
||||
VisualType,
|
||||
logger,
|
||||
userDataPath,
|
||||
}: {
|
||||
getRegionCode: () => string | undefined;
|
||||
Attachments: AttachmentsModuleType;
|
||||
Type: typeof TypesAttachment;
|
||||
VisualType: typeof VisualAttachment;
|
||||
logger: LoggerType;
|
||||
userDataPath: string;
|
||||
}): MigrationsModuleType {
|
||||
if (!Attachments) {
|
||||
throw new Error('initializeMigrations: Missing provided attachments!');
|
||||
}
|
||||
const {
|
||||
createAbsolutePathGetter,
|
||||
createReader,
|
||||
createWriterForExisting,
|
||||
createWriterForNew,
|
||||
createDoesExist,
|
||||
getAvatarsPath,
|
||||
getDraftPath,
|
||||
getPath,
|
||||
getStickersPath,
|
||||
getBadgesPath,
|
||||
getTempPath,
|
||||
openFileInFolder,
|
||||
saveAttachmentToDisk,
|
||||
} = Attachments;
|
||||
const {
|
||||
getImageDimensions,
|
||||
makeImageThumbnail,
|
||||
makeObjectUrl,
|
||||
makeVideoScreenshot,
|
||||
revokeObjectUrl,
|
||||
} = VisualType;
|
||||
|
||||
const attachmentsPath = getPath(userDataPath);
|
||||
const readAttachmentData = createReader(attachmentsPath);
|
||||
const loadAttachmentData = Type.loadData(readAttachmentData);
|
||||
const loadContactData = MessageType.loadContactData(loadAttachmentData);
|
||||
const loadPreviewData = MessageType.loadPreviewData(loadAttachmentData);
|
||||
const loadQuoteData = MessageType.loadQuoteData(loadAttachmentData);
|
||||
const loadStickerData = MessageType.loadStickerData(loadAttachmentData);
|
||||
const getAbsoluteAttachmentPath = createAbsolutePathGetter(attachmentsPath);
|
||||
const deleteOnDisk = Attachments.createDeleter(attachmentsPath);
|
||||
const writeExistingAttachmentData = createWriterForExisting(attachmentsPath);
|
||||
const writeNewAttachmentData = createWriterForNew(attachmentsPath);
|
||||
const copyIntoAttachmentsDirectory =
|
||||
Attachments.copyIntoAttachmentsDirectory(attachmentsPath);
|
||||
const doesAttachmentExist = createDoesExist(attachmentsPath);
|
||||
|
||||
const stickersPath = getStickersPath(userDataPath);
|
||||
const writeNewStickerData = createWriterForNew(stickersPath);
|
||||
const getAbsoluteStickerPath = createAbsolutePathGetter(stickersPath);
|
||||
const deleteSticker = Attachments.createDeleter(stickersPath);
|
||||
const readStickerData = createReader(stickersPath);
|
||||
|
||||
const badgesPath = getBadgesPath(userDataPath);
|
||||
const getAbsoluteBadgeImageFilePath = createAbsolutePathGetter(badgesPath);
|
||||
const writeNewBadgeImageFileData = createWriterForNew(badgesPath, '.svg');
|
||||
|
||||
const tempPath = getTempPath(userDataPath);
|
||||
const getAbsoluteTempPath = createAbsolutePathGetter(tempPath);
|
||||
const writeNewTempData = createWriterForNew(tempPath);
|
||||
const deleteTempFile = Attachments.createDeleter(tempPath);
|
||||
const readTempData = createReader(tempPath);
|
||||
const copyIntoTempDirectory =
|
||||
Attachments.copyIntoAttachmentsDirectory(tempPath);
|
||||
|
||||
const draftPath = getDraftPath(userDataPath);
|
||||
const getAbsoluteDraftPath = createAbsolutePathGetter(draftPath);
|
||||
const writeNewDraftData = createWriterForNew(draftPath);
|
||||
const deleteDraftFile = Attachments.createDeleter(draftPath);
|
||||
const readDraftData = createReader(draftPath);
|
||||
|
||||
const avatarsPath = getAvatarsPath(userDataPath);
|
||||
const getAbsoluteAvatarPath = createAbsolutePathGetter(avatarsPath);
|
||||
const writeNewAvatarData = createWriterForNew(avatarsPath);
|
||||
const deleteAvatar = Attachments.createDeleter(avatarsPath);
|
||||
|
||||
return {
|
||||
attachmentsPath,
|
||||
copyIntoAttachmentsDirectory,
|
||||
copyIntoTempDirectory,
|
||||
deleteAttachmentData: deleteOnDisk,
|
||||
deleteAvatar,
|
||||
deleteDraftFile,
|
||||
deleteExternalMessageFiles: MessageType.deleteAllExternalFiles({
|
||||
deleteAttachmentData: Type.deleteData(deleteOnDisk),
|
||||
deleteOnDisk,
|
||||
}),
|
||||
deleteSticker,
|
||||
deleteTempFile,
|
||||
doesAttachmentExist,
|
||||
getAbsoluteAttachmentPath,
|
||||
getAbsoluteAvatarPath,
|
||||
getAbsoluteBadgeImageFilePath,
|
||||
getAbsoluteDraftPath,
|
||||
getAbsoluteStickerPath,
|
||||
getAbsoluteTempPath,
|
||||
loadAttachmentData,
|
||||
loadContactData,
|
||||
loadMessage: MessageType.createAttachmentLoader(loadAttachmentData),
|
||||
loadPreviewData,
|
||||
loadQuoteData,
|
||||
loadStickerData,
|
||||
openFileInFolder,
|
||||
readAttachmentData,
|
||||
readDraftData,
|
||||
readStickerData,
|
||||
readTempData,
|
||||
saveAttachmentToDisk,
|
||||
processNewAttachment: (attachment: AttachmentType) =>
|
||||
MessageType.processNewAttachment(attachment, {
|
||||
writeNewAttachmentData,
|
||||
getAbsoluteAttachmentPath,
|
||||
makeObjectUrl,
|
||||
revokeObjectUrl,
|
||||
getImageDimensions,
|
||||
makeImageThumbnail,
|
||||
makeVideoScreenshot,
|
||||
logger,
|
||||
}),
|
||||
processNewSticker: (stickerData: Uint8Array) =>
|
||||
MessageType.processNewSticker(stickerData, {
|
||||
writeNewStickerData,
|
||||
getAbsoluteStickerPath,
|
||||
getImageDimensions,
|
||||
logger,
|
||||
}),
|
||||
processNewEphemeralSticker: (stickerData: Uint8Array) =>
|
||||
MessageType.processNewSticker(stickerData, {
|
||||
writeNewStickerData: writeNewTempData,
|
||||
getAbsoluteStickerPath: getAbsoluteTempPath,
|
||||
getImageDimensions,
|
||||
logger,
|
||||
}),
|
||||
upgradeMessageSchema: (
|
||||
message: MessageAttributesType,
|
||||
options: { maxVersion?: number } = {}
|
||||
) => {
|
||||
const { maxVersion } = options;
|
||||
|
||||
return MessageType.upgradeSchema(message, {
|
||||
writeNewAttachmentData,
|
||||
getRegionCode,
|
||||
getAbsoluteAttachmentPath,
|
||||
makeObjectUrl,
|
||||
revokeObjectUrl,
|
||||
getImageDimensions,
|
||||
makeImageThumbnail,
|
||||
makeVideoScreenshot,
|
||||
logger,
|
||||
maxVersion,
|
||||
getAbsoluteStickerPath,
|
||||
writeNewStickerData,
|
||||
});
|
||||
},
|
||||
writeMessageAttachments: MessageType.createAttachmentDataWriter({
|
||||
writeExistingAttachmentData,
|
||||
logger,
|
||||
}),
|
||||
writeNewAttachmentData: createWriterForNew(attachmentsPath),
|
||||
writeNewAvatarData,
|
||||
writeNewDraftData,
|
||||
writeNewBadgeImageFileData,
|
||||
};
|
||||
}
|
||||
|
||||
type StringGetterType = (basePath: string) => string;
|
||||
|
||||
type AttachmentsModuleType = {
|
||||
getAvatarsPath: StringGetterType;
|
||||
getBadgesPath: StringGetterType;
|
||||
getDraftPath: StringGetterType;
|
||||
getPath: StringGetterType;
|
||||
getStickersPath: StringGetterType;
|
||||
getTempPath: StringGetterType;
|
||||
getUpdateCachePath: StringGetterType;
|
||||
|
||||
createDeleter: (root: string) => (relativePath: string) => Promise<void>;
|
||||
|
||||
createReader: (root: string) => (relativePath: string) => Promise<Uint8Array>;
|
||||
getRelativePath: (name: string) => string;
|
||||
createName: (suffix?: string) => string;
|
||||
|
||||
copyIntoAttachmentsDirectory: (
|
||||
root: string
|
||||
) => (sourcePath: string) => Promise<{ path: string; size: number }>;
|
||||
|
||||
createWriterForNew: (
|
||||
root: string,
|
||||
suffix?: string
|
||||
) => (bytes: Uint8Array) => Promise<string>;
|
||||
|
||||
createWriterForExisting: (
|
||||
root: string
|
||||
) => (options: { data?: Uint8Array; path?: string }) => Promise<string>;
|
||||
|
||||
createAbsolutePathGetter: (
|
||||
rootPath: string
|
||||
) => (relativePath: string) => string;
|
||||
|
||||
createDoesExist: (root: string) => (relativePath: string) => Promise<boolean>;
|
||||
openFileInFolder: (target: string) => Promise<void>;
|
||||
saveAttachmentToDisk: ({
|
||||
data,
|
||||
name,
|
||||
}: {
|
||||
data: Uint8Array;
|
||||
name: string;
|
||||
}) => Promise<null | { fullPath: string; name: string }>;
|
||||
};
|
||||
|
||||
export const setup = (options: {
|
||||
Attachments: AttachmentsModuleType;
|
||||
getRegionCode: () => string | undefined;
|
||||
logger: LoggerType;
|
||||
userDataPath: string;
|
||||
}): SignalCoreType => {
|
||||
const { Attachments, getRegionCode, logger, userDataPath } = options;
|
||||
|
||||
const Migrations = initializeMigrations({
|
||||
getRegionCode,
|
||||
Attachments,
|
||||
Type: TypesAttachment,
|
||||
VisualType: VisualAttachment,
|
||||
logger,
|
||||
userDataPath,
|
||||
});
|
||||
|
||||
const Components = {
|
||||
AttachmentList,
|
||||
ChatColorPicker,
|
||||
ConfirmationDialog,
|
||||
ContactModal,
|
||||
Emojify,
|
||||
MessageDetail,
|
||||
Quote,
|
||||
StagedLinkPreview,
|
||||
DisappearingTimeDialog,
|
||||
SystemTraySettingsCheckboxes,
|
||||
};
|
||||
|
||||
const Roots = {
|
||||
createApp,
|
||||
createChatColorPicker,
|
||||
createConversationDetails,
|
||||
createForwardMessageModal,
|
||||
createGroupLinkManagement,
|
||||
createGroupV1MigrationModal,
|
||||
createGroupV2JoinModal,
|
||||
createGroupV2Permissions,
|
||||
createLeftPane,
|
||||
createMessageDetail,
|
||||
createConversationNotificationsSettings,
|
||||
createPendingInvites,
|
||||
createSafetyNumberViewer,
|
||||
createShortcutGuideModal,
|
||||
createStickerManager,
|
||||
createStickerPreviewModal,
|
||||
};
|
||||
|
||||
const Ducks = {
|
||||
app: appDuck,
|
||||
calling: callingDuck,
|
||||
conversations: conversationsDuck,
|
||||
emojis: emojisDuck,
|
||||
expiration: expirationDuck,
|
||||
items: itemsDuck,
|
||||
linkPreviews: linkPreviewsDuck,
|
||||
network: networkDuck,
|
||||
updates: updatesDuck,
|
||||
user: userDuck,
|
||||
search: searchDuck,
|
||||
stickers: stickersDuck,
|
||||
};
|
||||
|
||||
const Selectors = {
|
||||
conversations: conversationsSelectors,
|
||||
search: searchSelectors,
|
||||
};
|
||||
|
||||
const Services = {
|
||||
calling,
|
||||
enableStorageService,
|
||||
eraseAllStorageServiceState,
|
||||
initializeGroupCredentialFetcher,
|
||||
initializeNetworkObserver,
|
||||
initializeUpdateListener,
|
||||
runStorageServiceSyncJob,
|
||||
storageServiceUploadJob,
|
||||
};
|
||||
|
||||
const State = {
|
||||
createStore,
|
||||
Roots,
|
||||
Ducks,
|
||||
Selectors,
|
||||
};
|
||||
|
||||
const Types = {
|
||||
Message: MessageType,
|
||||
|
||||
// Mostly for debugging
|
||||
UUID,
|
||||
Address,
|
||||
QualifiedAddress,
|
||||
};
|
||||
|
||||
return {
|
||||
Backbone,
|
||||
Components,
|
||||
Crypto,
|
||||
Curve,
|
||||
// Note: used in test/index.html, and not type-checked!
|
||||
conversationControllerStart,
|
||||
Data,
|
||||
Groups,
|
||||
Migrations,
|
||||
OS,
|
||||
RemoteConfig,
|
||||
Services,
|
||||
State,
|
||||
Types,
|
||||
Util,
|
||||
};
|
||||
};
|
|
@ -199,6 +199,7 @@ export type UnprocessedType = {
|
|||
attempts: number;
|
||||
envelope?: string;
|
||||
|
||||
messageAgeSec?: number;
|
||||
source?: string;
|
||||
sourceUuid?: string;
|
||||
sourceDevice?: number;
|
||||
|
|
|
@ -10,6 +10,7 @@ import {
|
|||
} from '../textsecure/processDataMessage';
|
||||
import type { ProcessedAttachment } from '../textsecure/Types.d';
|
||||
import { SignalService as Proto } from '../protobuf';
|
||||
import { IMAGE_GIF } from '../types/MIME';
|
||||
|
||||
const FLAGS = Proto.DataMessage.Flags;
|
||||
|
||||
|
@ -19,12 +20,16 @@ const UNPROCESSED_ATTACHMENT: Proto.IAttachmentPointer = {
|
|||
cdnId: Long.fromNumber(123),
|
||||
key: new Uint8Array([1, 2, 3]),
|
||||
digest: new Uint8Array([4, 5, 6]),
|
||||
contentType: IMAGE_GIF,
|
||||
size: 34,
|
||||
};
|
||||
|
||||
const PROCESSED_ATTACHMENT: ProcessedAttachment = {
|
||||
cdnId: '123',
|
||||
key: 'AQID',
|
||||
digest: 'BAUG',
|
||||
contentType: IMAGE_GIF,
|
||||
size: 34,
|
||||
};
|
||||
|
||||
const GROUP_ID = new Uint8Array([0x68, 0x65, 0x79]);
|
||||
|
@ -118,7 +123,6 @@ describe('processDataMessage', () => {
|
|||
id: GROUP_ID,
|
||||
name: 'should be deleted',
|
||||
membersE164: ['should be deleted'],
|
||||
avatar: {},
|
||||
type: Proto.GroupContext.Type.DELIVER,
|
||||
},
|
||||
});
|
||||
|
|
|
@ -28,6 +28,10 @@ describe('license comments', () => {
|
|||
[firstLine, secondLine] = await readFirstLines(file, 2);
|
||||
}
|
||||
|
||||
if (!firstLine || !secondLine) {
|
||||
throw new Error(`file ${file}: was missing a first or second line!`);
|
||||
}
|
||||
|
||||
const { groups = {} } =
|
||||
firstLine.match(
|
||||
/Copyright (?<startYearWithDash>\d{4}-)?(?<endYearString>\d{4}) Signal Messenger, LLC/
|
||||
|
|
|
@ -10,10 +10,7 @@ import * as Bytes from '../../Bytes';
|
|||
import * as MIME from '../../types/MIME';
|
||||
|
||||
import type { EmbeddedContactType } from '../../types/EmbeddedContact';
|
||||
import type {
|
||||
MessageAttributesType,
|
||||
StickerMessageType,
|
||||
} from '../../model-types.d';
|
||||
import type { MessageAttributesType } from '../../model-types.d';
|
||||
import type { AttachmentType } from '../../types/Attachment';
|
||||
import type { LoggerType } from '../../types/Logging';
|
||||
|
||||
|
@ -75,12 +72,11 @@ describe('Message', () => {
|
|||
revokeObjectUrl: (_objectUrl: string) => undefined,
|
||||
writeNewAttachmentData: async (_data: Uint8Array) =>
|
||||
'fake-attachment-path',
|
||||
writeNewStickerData: async (_sticker: StickerMessageType) =>
|
||||
'fake-sticker-path',
|
||||
writeNewStickerData: async (_data: Uint8Array) => 'fake-sticker-path',
|
||||
...props,
|
||||
};
|
||||
}
|
||||
const writeExistingAttachmentData = () => Promise.resolve();
|
||||
const writeExistingAttachmentData = () => Promise.resolve('path');
|
||||
|
||||
describe('createAttachmentDataWriter', () => {
|
||||
it('should ignore messages that didn’t go through attachment migration', async () => {
|
||||
|
@ -155,6 +151,7 @@ describe('Message', () => {
|
|||
Bytes.toString(attachment.data || new Uint8Array()),
|
||||
'It’s easy if you try'
|
||||
);
|
||||
return 'path';
|
||||
};
|
||||
|
||||
const actual = await Message.createAttachmentDataWriter({
|
||||
|
@ -214,6 +211,7 @@ describe('Message', () => {
|
|||
Bytes.toString(attachment.data || new Uint8Array()),
|
||||
'It’s easy if you try'
|
||||
);
|
||||
return 'path';
|
||||
};
|
||||
|
||||
const actual = await Message.createAttachmentDataWriter({
|
||||
|
@ -272,6 +270,7 @@ describe('Message', () => {
|
|||
Bytes.toString(attachment.data || new Uint8Array()),
|
||||
'It’s easy if you try'
|
||||
);
|
||||
return 'path';
|
||||
};
|
||||
|
||||
const actual = await Message.createAttachmentDataWriter({
|
||||
|
@ -279,6 +278,7 @@ describe('Message', () => {
|
|||
logger,
|
||||
})(input);
|
||||
assert.deepEqual(actual, expected);
|
||||
return 'path';
|
||||
});
|
||||
});
|
||||
|
||||
|
|
51
ts/textsecure.d.ts
vendored
51
ts/textsecure.d.ts
vendored
|
@ -1,51 +0,0 @@
|
|||
// Copyright 2020-2021 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { UnidentifiedSenderMessageContent } from '@signalapp/libsignal-client';
|
||||
|
||||
import MessageSender from './textsecure/SendMessage';
|
||||
import SyncRequest from './textsecure/SyncRequest';
|
||||
import EventTarget from './textsecure/EventTarget';
|
||||
import SendMessage, { SendOptionsType } from './textsecure/SendMessage';
|
||||
import { WebAPIType } from './textsecure/WebAPI';
|
||||
import utils from './textsecure/Helpers';
|
||||
import { CallingMessage as CallingMessageClass } from 'ringrtc';
|
||||
import { WhatIsThis } from './window.d';
|
||||
import { Storage } from './textsecure/Storage';
|
||||
import {
|
||||
StorageServiceCallOptionsType,
|
||||
StorageServiceCredentials,
|
||||
ProcessedAttachment,
|
||||
} from './textsecure/Types.d';
|
||||
|
||||
export type UnprocessedType = {
|
||||
attempts: number;
|
||||
decrypted?: string;
|
||||
envelope?: string;
|
||||
id: string;
|
||||
receivedAtCounter: number | null;
|
||||
timestamp: number;
|
||||
serverGuid?: string;
|
||||
serverTimestamp?: number;
|
||||
source?: string;
|
||||
sourceDevice?: number;
|
||||
sourceUuid?: string;
|
||||
destinationUuid?: string;
|
||||
messageAgeSec?: number;
|
||||
version: number;
|
||||
};
|
||||
|
||||
export { StorageServiceCallOptionsType, StorageServiceCredentials };
|
||||
|
||||
export type TextSecureType = {
|
||||
storage: Storage;
|
||||
server: WebAPIType;
|
||||
messageSender: MessageSender;
|
||||
messaging: SendMessage;
|
||||
utils: typeof utils;
|
||||
|
||||
EventTarget: typeof EventTarget;
|
||||
AccountManager: WhatIsThis;
|
||||
MessageSender: typeof MessageSender;
|
||||
SyncRequest: typeof SyncRequest;
|
||||
};
|
|
@ -55,7 +55,6 @@ import * as Errors from '../types/errors';
|
|||
import { isEnabled } from '../RemoteConfig';
|
||||
|
||||
import { SignalService as Proto } from '../protobuf';
|
||||
import type { UnprocessedType } from '../textsecure.d';
|
||||
import { deriveGroupFields, MASTER_KEY_LENGTH } from '../groups';
|
||||
|
||||
import createTaskWithTimeout from './TaskWithTimeout';
|
||||
|
@ -81,6 +80,7 @@ import type {
|
|||
ProcessedSent,
|
||||
ProcessedEnvelope,
|
||||
IRequestHandler,
|
||||
UnprocessedType,
|
||||
} from './Types.d';
|
||||
import {
|
||||
EmptyEvent,
|
||||
|
@ -114,6 +114,7 @@ import * as log from '../logging/log';
|
|||
import * as durations from '../util/durations';
|
||||
import { areArraysMatchingSets } from '../util/areArraysMatchingSets';
|
||||
import { generateBlurHash } from '../util/generateBlurHash';
|
||||
import { APPLICATION_OCTET_STREAM } from '../types/MIME';
|
||||
|
||||
const GROUPV1_ID_LENGTH = 16;
|
||||
const GROUPV2_ID_LENGTH = 32;
|
||||
|
@ -1800,8 +1801,14 @@ export default class MessageReceiver
|
|||
}
|
||||
|
||||
if (msg.textAttachment) {
|
||||
const { text } = msg.textAttachment;
|
||||
if (!text) {
|
||||
throw new Error('Text attachments must have text!');
|
||||
}
|
||||
|
||||
attachments.push({
|
||||
size: msg.textAttachment.text?.length,
|
||||
size: text.length,
|
||||
contentType: APPLICATION_OCTET_STREAM,
|
||||
textAttachment: msg.textAttachment,
|
||||
blurHash: generateBlurHash(
|
||||
(msg.textAttachment.color ||
|
||||
|
|
|
@ -38,7 +38,11 @@ import type {
|
|||
WebAPIType,
|
||||
} from './WebAPI';
|
||||
import createTaskWithTimeout from './TaskWithTimeout';
|
||||
import type { CallbackResultType } from './Types.d';
|
||||
import type {
|
||||
CallbackResultType,
|
||||
StorageServiceCallOptionsType,
|
||||
StorageServiceCredentials,
|
||||
} from './Types.d';
|
||||
import type {
|
||||
SerializedCertificateType,
|
||||
SendLogCallbackType,
|
||||
|
@ -47,10 +51,6 @@ import OutgoingMessage from './OutgoingMessage';
|
|||
import type { CDSResponseType } from './CDSSocketManager';
|
||||
import * as Bytes from '../Bytes';
|
||||
import { getRandomBytes, getZeroes, encryptAttachment } from '../Crypto';
|
||||
import type {
|
||||
StorageServiceCallOptionsType,
|
||||
StorageServiceCredentials,
|
||||
} from '../textsecure.d';
|
||||
import {
|
||||
MessageError,
|
||||
SignedPreKeyRotationError,
|
||||
|
@ -73,6 +73,7 @@ import {
|
|||
numberToEmailType,
|
||||
numberToAddressType,
|
||||
} from '../types/EmbeddedContact';
|
||||
import type { StickerWithHydratedData } from '../types/Stickers';
|
||||
|
||||
export type SendMetadataType = {
|
||||
[identifier: string]: {
|
||||
|
@ -106,13 +107,7 @@ type GroupCallUpdateType = {
|
|||
eraId: string;
|
||||
};
|
||||
|
||||
export type StickerType = {
|
||||
packId: string;
|
||||
stickerId: number;
|
||||
packKey: string;
|
||||
data: Readonly<AttachmentType>;
|
||||
emoji?: string;
|
||||
|
||||
export type StickerType = StickerWithHydratedData & {
|
||||
attachmentPointer?: Proto.IAttachmentPointer;
|
||||
};
|
||||
|
||||
|
@ -631,7 +626,7 @@ export default class MessageSender {
|
|||
);
|
||||
}
|
||||
|
||||
getRandomPadding(): Uint8Array {
|
||||
static getRandomPadding(): Uint8Array {
|
||||
// Generate a random int from 1 and 512
|
||||
const buffer = getRandomBytes(2);
|
||||
const paddingLength = (new Uint16Array(buffer)[0] & 0x1ff) + 1;
|
||||
|
@ -996,7 +991,7 @@ export default class MessageSender {
|
|||
};
|
||||
}
|
||||
|
||||
createSyncMessage(): Proto.SyncMessage {
|
||||
static createSyncMessage(): Proto.SyncMessage {
|
||||
const syncMessage = new Proto.SyncMessage();
|
||||
|
||||
syncMessage.padding = this.getRandomPadding();
|
||||
|
@ -1287,7 +1282,7 @@ export default class MessageSender {
|
|||
];
|
||||
}
|
||||
|
||||
const syncMessage = this.createSyncMessage();
|
||||
const syncMessage = MessageSender.createSyncMessage();
|
||||
syncMessage.sent = sentMessage;
|
||||
const contentMessage = new Proto.Content();
|
||||
contentMessage.syncMessage = syncMessage;
|
||||
|
@ -1303,12 +1298,12 @@ export default class MessageSender {
|
|||
});
|
||||
}
|
||||
|
||||
getRequestBlockSyncMessage(): SingleProtoJobData {
|
||||
static getRequestBlockSyncMessage(): SingleProtoJobData {
|
||||
const myUuid = window.textsecure.storage.user.getCheckedUuid();
|
||||
|
||||
const request = new Proto.SyncMessage.Request();
|
||||
request.type = Proto.SyncMessage.Request.Type.BLOCKED;
|
||||
const syncMessage = this.createSyncMessage();
|
||||
const syncMessage = MessageSender.createSyncMessage();
|
||||
syncMessage.request = request;
|
||||
const contentMessage = new Proto.Content();
|
||||
contentMessage.syncMessage = syncMessage;
|
||||
|
@ -1326,12 +1321,12 @@ export default class MessageSender {
|
|||
};
|
||||
}
|
||||
|
||||
getRequestConfigurationSyncMessage(): SingleProtoJobData {
|
||||
static getRequestConfigurationSyncMessage(): SingleProtoJobData {
|
||||
const myUuid = window.textsecure.storage.user.getCheckedUuid();
|
||||
|
||||
const request = new Proto.SyncMessage.Request();
|
||||
request.type = Proto.SyncMessage.Request.Type.CONFIGURATION;
|
||||
const syncMessage = this.createSyncMessage();
|
||||
const syncMessage = MessageSender.createSyncMessage();
|
||||
syncMessage.request = request;
|
||||
const contentMessage = new Proto.Content();
|
||||
contentMessage.syncMessage = syncMessage;
|
||||
|
@ -1349,7 +1344,7 @@ export default class MessageSender {
|
|||
};
|
||||
}
|
||||
|
||||
getRequestGroupSyncMessage(): SingleProtoJobData {
|
||||
static getRequestGroupSyncMessage(): SingleProtoJobData {
|
||||
const myUuid = window.textsecure.storage.user.getCheckedUuid();
|
||||
|
||||
const request = new Proto.SyncMessage.Request();
|
||||
|
@ -1372,7 +1367,7 @@ export default class MessageSender {
|
|||
};
|
||||
}
|
||||
|
||||
getRequestContactSyncMessage(): SingleProtoJobData {
|
||||
static getRequestContactSyncMessage(): SingleProtoJobData {
|
||||
const myUuid = window.textsecure.storage.user.getCheckedUuid();
|
||||
|
||||
const request = new Proto.SyncMessage.Request();
|
||||
|
@ -1395,7 +1390,7 @@ export default class MessageSender {
|
|||
};
|
||||
}
|
||||
|
||||
getRequestPniIdentitySyncMessage(): SingleProtoJobData {
|
||||
static getRequestPniIdentitySyncMessage(): SingleProtoJobData {
|
||||
const myUuid = window.textsecure.storage.user.getCheckedUuid();
|
||||
|
||||
const request = new Proto.SyncMessage.Request();
|
||||
|
@ -1418,7 +1413,7 @@ export default class MessageSender {
|
|||
};
|
||||
}
|
||||
|
||||
getFetchManifestSyncMessage(): SingleProtoJobData {
|
||||
static getFetchManifestSyncMessage(): SingleProtoJobData {
|
||||
const myUuid = window.textsecure.storage.user.getCheckedUuid();
|
||||
|
||||
const fetchLatest = new Proto.SyncMessage.FetchLatest();
|
||||
|
@ -1442,7 +1437,7 @@ export default class MessageSender {
|
|||
};
|
||||
}
|
||||
|
||||
getFetchLocalProfileSyncMessage(): SingleProtoJobData {
|
||||
static getFetchLocalProfileSyncMessage(): SingleProtoJobData {
|
||||
const myUuid = window.textsecure.storage.user.getCheckedUuid();
|
||||
|
||||
const fetchLatest = new Proto.SyncMessage.FetchLatest();
|
||||
|
@ -1466,7 +1461,7 @@ export default class MessageSender {
|
|||
};
|
||||
}
|
||||
|
||||
getRequestKeySyncMessage(): SingleProtoJobData {
|
||||
static getRequestKeySyncMessage(): SingleProtoJobData {
|
||||
const myUuid = window.textsecure.storage.user.getCheckedUuid();
|
||||
|
||||
const request = new Proto.SyncMessage.Request();
|
||||
|
@ -1500,7 +1495,7 @@ export default class MessageSender {
|
|||
): Promise<CallbackResultType> {
|
||||
const myUuid = window.textsecure.storage.user.getCheckedUuid();
|
||||
|
||||
const syncMessage = this.createSyncMessage();
|
||||
const syncMessage = MessageSender.createSyncMessage();
|
||||
syncMessage.read = [];
|
||||
for (let i = 0; i < reads.length; i += 1) {
|
||||
const proto = new Proto.SyncMessage.Read({
|
||||
|
@ -1534,7 +1529,7 @@ export default class MessageSender {
|
|||
): Promise<CallbackResultType> {
|
||||
const myUuid = window.textsecure.storage.user.getCheckedUuid();
|
||||
|
||||
const syncMessage = this.createSyncMessage();
|
||||
const syncMessage = MessageSender.createSyncMessage();
|
||||
syncMessage.viewed = views.map(
|
||||
view =>
|
||||
new Proto.SyncMessage.Viewed({
|
||||
|
@ -1577,7 +1572,7 @@ export default class MessageSender {
|
|||
|
||||
const myUuid = window.textsecure.storage.user.getCheckedUuid();
|
||||
|
||||
const syncMessage = this.createSyncMessage();
|
||||
const syncMessage = MessageSender.createSyncMessage();
|
||||
|
||||
const viewOnceOpen = new Proto.SyncMessage.ViewOnceOpen();
|
||||
if (senderE164 !== undefined) {
|
||||
|
@ -1601,7 +1596,7 @@ export default class MessageSender {
|
|||
});
|
||||
}
|
||||
|
||||
getMessageRequestResponseSync(
|
||||
static getMessageRequestResponseSync(
|
||||
options: Readonly<{
|
||||
threadE164?: string;
|
||||
threadUuid?: string;
|
||||
|
@ -1611,7 +1606,7 @@ export default class MessageSender {
|
|||
): SingleProtoJobData {
|
||||
const myUuid = window.textsecure.storage.user.getCheckedUuid();
|
||||
|
||||
const syncMessage = this.createSyncMessage();
|
||||
const syncMessage = MessageSender.createSyncMessage();
|
||||
|
||||
const response = new Proto.SyncMessage.MessageRequestResponse();
|
||||
if (options.threadE164 !== undefined) {
|
||||
|
@ -1642,7 +1637,7 @@ export default class MessageSender {
|
|||
};
|
||||
}
|
||||
|
||||
getStickerPackSync(
|
||||
static getStickerPackSync(
|
||||
operations: ReadonlyArray<{
|
||||
packId: string;
|
||||
packKey: string;
|
||||
|
@ -1663,7 +1658,7 @@ export default class MessageSender {
|
|||
return operation;
|
||||
});
|
||||
|
||||
const syncMessage = this.createSyncMessage();
|
||||
const syncMessage = MessageSender.createSyncMessage();
|
||||
syncMessage.stickerPackOperation = packOperations;
|
||||
|
||||
const contentMessage = new Proto.Content();
|
||||
|
@ -1682,7 +1677,7 @@ export default class MessageSender {
|
|||
};
|
||||
}
|
||||
|
||||
getVerificationSync(
|
||||
static getVerificationSync(
|
||||
destinationE164: string | undefined,
|
||||
destinationUuid: string | undefined,
|
||||
state: number,
|
||||
|
@ -1694,7 +1689,7 @@ export default class MessageSender {
|
|||
throw new Error('syncVerification: Neither e164 nor UUID were provided');
|
||||
}
|
||||
|
||||
const padding = this.getRandomPadding();
|
||||
const padding = MessageSender.getRandomPadding();
|
||||
|
||||
const verified = new Proto.Verified();
|
||||
verified.state = state;
|
||||
|
@ -1707,7 +1702,7 @@ export default class MessageSender {
|
|||
verified.identityKey = identityKey;
|
||||
verified.nullMessage = padding;
|
||||
|
||||
const syncMessage = this.createSyncMessage();
|
||||
const syncMessage = MessageSender.createSyncMessage();
|
||||
syncMessage.verified = verified;
|
||||
|
||||
const contentMessage = new Proto.Content();
|
||||
|
@ -1832,7 +1827,7 @@ export default class MessageSender {
|
|||
});
|
||||
}
|
||||
|
||||
getNullMessage({
|
||||
static getNullMessage({
|
||||
uuid,
|
||||
e164,
|
||||
padding,
|
||||
|
@ -1848,7 +1843,7 @@ export default class MessageSender {
|
|||
throw new Error('sendNullMessage: Got neither uuid nor e164!');
|
||||
}
|
||||
|
||||
nullMessage.padding = padding || this.getRandomPadding();
|
||||
nullMessage.padding = padding || MessageSender.getRandomPadding();
|
||||
|
||||
const contentMessage = new Proto.Content();
|
||||
contentMessage.nullMessage = nullMessage;
|
||||
|
|
|
@ -29,19 +29,12 @@ class SyncRequestInner extends EventTarget {
|
|||
|
||||
timeoutMillis: number;
|
||||
|
||||
constructor(
|
||||
private sender: MessageSender,
|
||||
private receiver: MessageReceiver,
|
||||
timeoutMillis?: number
|
||||
) {
|
||||
constructor(private receiver: MessageReceiver, timeoutMillis?: number) {
|
||||
super();
|
||||
|
||||
if (
|
||||
!(sender instanceof MessageSender) ||
|
||||
!(receiver instanceof MessageReceiver)
|
||||
) {
|
||||
if (!(receiver instanceof MessageReceiver)) {
|
||||
throw new Error(
|
||||
'Tried to construct a SyncRequest without MessageSender and MessageReceiver'
|
||||
'Tried to construct a SyncRequest without MessageReceiver'
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -61,8 +54,6 @@ class SyncRequestInner extends EventTarget {
|
|||
}
|
||||
this.started = true;
|
||||
|
||||
const { sender } = this;
|
||||
|
||||
if (window.ConversationController.areWePrimaryDevice()) {
|
||||
log.warn('SyncRequest.start: We are primary device; returning early');
|
||||
return;
|
||||
|
@ -73,10 +64,12 @@ class SyncRequestInner extends EventTarget {
|
|||
);
|
||||
try {
|
||||
await Promise.all([
|
||||
singleProtoJobQueue.add(sender.getRequestConfigurationSyncMessage()),
|
||||
singleProtoJobQueue.add(sender.getRequestBlockSyncMessage()),
|
||||
singleProtoJobQueue.add(sender.getRequestContactSyncMessage()),
|
||||
singleProtoJobQueue.add(sender.getRequestGroupSyncMessage()),
|
||||
singleProtoJobQueue.add(
|
||||
MessageSender.getRequestConfigurationSyncMessage()
|
||||
),
|
||||
singleProtoJobQueue.add(MessageSender.getRequestBlockSyncMessage()),
|
||||
singleProtoJobQueue.add(MessageSender.getRequestContactSyncMessage()),
|
||||
singleProtoJobQueue.add(MessageSender.getRequestGroupSyncMessage()),
|
||||
]);
|
||||
} catch (error: unknown) {
|
||||
log.error(
|
||||
|
@ -135,12 +128,8 @@ export default class SyncRequest {
|
|||
handler: EventHandler
|
||||
) => void;
|
||||
|
||||
constructor(
|
||||
sender: MessageSender,
|
||||
receiver: MessageReceiver,
|
||||
timeoutMillis?: number
|
||||
) {
|
||||
const inner = new SyncRequestInner(sender, receiver, timeoutMillis);
|
||||
constructor(receiver: MessageReceiver, timeoutMillis?: number) {
|
||||
const inner = new SyncRequestInner(receiver, timeoutMillis);
|
||||
this.inner = inner;
|
||||
this.addEventListener = inner.addEventListener.bind(inner);
|
||||
this.removeEventListener = inner.removeEventListener.bind(inner);
|
||||
|
|
5
ts/textsecure/Types.d.ts
vendored
5
ts/textsecure/Types.d.ts
vendored
|
@ -6,6 +6,7 @@ import type { IncomingWebSocketRequest } from './WebsocketResources';
|
|||
import type { UUID } from '../types/UUID';
|
||||
import type { TextAttachmentType } from '../types/Attachment';
|
||||
import { GiftBadgeStates } from '../components/conversation/Message';
|
||||
import { MIMEType } from '../types/MIME';
|
||||
|
||||
export {
|
||||
IdentityKeyType,
|
||||
|
@ -97,9 +98,9 @@ export type ProcessedAttachment = {
|
|||
cdnId?: string;
|
||||
cdnKey?: string;
|
||||
digest?: string;
|
||||
contentType?: string;
|
||||
contentType: MIMEType;
|
||||
key?: string;
|
||||
size?: number;
|
||||
size: number;
|
||||
fileName?: string;
|
||||
flags?: number;
|
||||
width?: number;
|
||||
|
|
|
@ -52,10 +52,6 @@ import { calculateAgreement, generateKeyPair } from '../Curve';
|
|||
import * as linkPreviewFetch from '../linkPreviews/linkPreviewFetch';
|
||||
import { isBadgeImageFileUrlValid } from '../badges/isBadgeImageFileUrlValid';
|
||||
|
||||
import type {
|
||||
StorageServiceCallOptionsType,
|
||||
StorageServiceCredentials,
|
||||
} from '../textsecure.d';
|
||||
import { SocketManager } from './SocketManager';
|
||||
import type { CDSResponseType } from './CDSSocketManager';
|
||||
import { CDSSocketManager } from './CDSSocketManager';
|
||||
|
@ -64,7 +60,12 @@ import { SignalService as Proto } from '../protobuf';
|
|||
|
||||
import { HTTPError } from './Errors';
|
||||
import type MessageSender from './SendMessage';
|
||||
import type { WebAPICredentials, IRequestHandler } from './Types.d';
|
||||
import type {
|
||||
WebAPICredentials,
|
||||
IRequestHandler,
|
||||
StorageServiceCallOptionsType,
|
||||
StorageServiceCredentials,
|
||||
} from './Types.d';
|
||||
import { handleStatusCode, translateError } from './Utils';
|
||||
import * as log from '../logging/log';
|
||||
import { maybeParseUrl } from '../util/url';
|
||||
|
@ -601,16 +602,16 @@ type InitializeOptionsType = {
|
|||
directoryUrl?: string;
|
||||
directoryEnclaveId?: string;
|
||||
directoryTrustAnchor?: string;
|
||||
directoryV2Url: string;
|
||||
directoryV2PublicKey: string;
|
||||
directoryV2CodeHashes: ReadonlyArray<string>;
|
||||
directoryV2Url?: string;
|
||||
directoryV2PublicKey?: string;
|
||||
directoryV2CodeHashes?: ReadonlyArray<string>;
|
||||
cdnUrlObject: {
|
||||
readonly '0': string;
|
||||
readonly [propName: string]: string;
|
||||
};
|
||||
certificateAuthority: string;
|
||||
contentProxyUrl: string;
|
||||
proxyUrl: string;
|
||||
proxyUrl: string | undefined;
|
||||
version: string;
|
||||
};
|
||||
|
||||
|
@ -1018,6 +1019,13 @@ export type ProxiedRequestOptionsType = {
|
|||
end?: number;
|
||||
};
|
||||
|
||||
export type TopLevelType = {
|
||||
multiRecipient200ResponseSchema: typeof multiRecipient200ResponseSchema;
|
||||
multiRecipient409ResponseSchema: typeof multiRecipient409ResponseSchema;
|
||||
multiRecipient410ResponseSchema: typeof multiRecipient410ResponseSchema;
|
||||
initialize: (options: InitializeOptionsType) => WebAPIConnectType;
|
||||
};
|
||||
|
||||
// We first set up the data that won't change during this session of the app
|
||||
export function initialize({
|
||||
url,
|
||||
|
@ -1138,8 +1146,15 @@ export function initialize({
|
|||
socketManager.authenticate({ username, password });
|
||||
}
|
||||
|
||||
const cdsUrl = directoryV2Url || directoryUrl;
|
||||
if (!cdsUrl) {
|
||||
throw new Error('No CDS url available!');
|
||||
}
|
||||
if (!directoryV2PublicKey || !directoryV2CodeHashes?.length) {
|
||||
throw new Error('No CDS public key or code hashes available');
|
||||
}
|
||||
const cdsSocketManager = new CDSSocketManager({
|
||||
url: directoryV2Url,
|
||||
url: cdsUrl,
|
||||
publicKey: directoryV2PublicKey,
|
||||
codeHashes: directoryV2CodeHashes,
|
||||
certificateAuthority,
|
||||
|
|
|
@ -12,7 +12,25 @@ import { Storage } from './Storage';
|
|||
import * as WebAPI from './WebAPI';
|
||||
import WebSocketResource from './WebsocketResources';
|
||||
|
||||
export const textsecure = {
|
||||
export type TextSecureType = {
|
||||
utils: typeof utils;
|
||||
storage: Storage;
|
||||
|
||||
AccountManager: typeof AccountManager;
|
||||
ContactBuffer: typeof ContactBuffer;
|
||||
EventTarget: typeof EventTarget;
|
||||
GroupBuffer: typeof GroupBuffer;
|
||||
MessageReceiver: typeof MessageReceiver;
|
||||
MessageSender: typeof MessageSender;
|
||||
SyncRequest: typeof SyncRequest;
|
||||
WebAPI: typeof WebAPI;
|
||||
WebSocketResource: typeof WebSocketResource;
|
||||
|
||||
server?: WebAPI.WebAPIType;
|
||||
messaging?: MessageSender;
|
||||
};
|
||||
|
||||
export const textsecure: TextSecureType = {
|
||||
utils,
|
||||
storage: new Storage(),
|
||||
|
||||
|
@ -26,5 +44,3 @@ export const textsecure = {
|
|||
WebAPI,
|
||||
WebSocketResource,
|
||||
};
|
||||
|
||||
export default textsecure;
|
||||
|
|
|
@ -26,6 +26,7 @@ import type {
|
|||
} from './Types.d';
|
||||
import { WarnOnlyError } from './Errors';
|
||||
import { GiftBadgeStates } from '../components/conversation/Message';
|
||||
import { APPLICATION_OCTET_STREAM, stringToMIMEType } from '../types/MIME';
|
||||
|
||||
const FLAGS = Proto.DataMessage.Flags;
|
||||
export const ATTACHMENT_MAX = 32;
|
||||
|
@ -47,12 +48,21 @@ export function processAttachment(
|
|||
const { cdnId } = attachment;
|
||||
const hasCdnId = Long.isLong(cdnId) ? !cdnId.isZero() : Boolean(cdnId);
|
||||
|
||||
const { contentType, digest, key, size } = attachment;
|
||||
if (!size) {
|
||||
throw new Error('Missing size on incoming attachment!');
|
||||
}
|
||||
|
||||
return {
|
||||
...shallowDropNull(attachment),
|
||||
|
||||
cdnId: hasCdnId ? String(cdnId) : undefined,
|
||||
key: attachment.key ? Bytes.toBase64(attachment.key) : undefined,
|
||||
digest: attachment.digest ? Bytes.toBase64(attachment.digest) : undefined,
|
||||
contentType: contentType
|
||||
? stringToMIMEType(contentType)
|
||||
: APPLICATION_OCTET_STREAM,
|
||||
digest: digest ? Bytes.toBase64(digest) : undefined,
|
||||
key: key ? Bytes.toBase64(key) : undefined,
|
||||
size,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -83,6 +83,10 @@ export type AttachmentType = {
|
|||
key?: string;
|
||||
};
|
||||
|
||||
export type AttachmentWithHydratedData = AttachmentType & {
|
||||
data: Uint8Array;
|
||||
};
|
||||
|
||||
export enum TextAttachmentStyleType {
|
||||
DEFAULT = 0,
|
||||
REGULAR = 1,
|
||||
|
@ -379,19 +383,21 @@ export function hasData(attachment: AttachmentType): boolean {
|
|||
|
||||
export function loadData(
|
||||
readAttachmentData: (path: string) => Promise<Uint8Array>
|
||||
): (attachment?: AttachmentType) => Promise<AttachmentType> {
|
||||
): (attachment: AttachmentType) => Promise<AttachmentWithHydratedData> {
|
||||
if (!is.function_(readAttachmentData)) {
|
||||
throw new TypeError("'readAttachmentData' must be a function");
|
||||
}
|
||||
|
||||
return async (attachment?: AttachmentType): Promise<AttachmentType> => {
|
||||
return async (
|
||||
attachment: AttachmentType
|
||||
): Promise<AttachmentWithHydratedData> => {
|
||||
if (!isValid(attachment)) {
|
||||
throw new TypeError("'attachment' is not valid");
|
||||
}
|
||||
|
||||
const isAlreadyLoaded = Boolean(attachment.data);
|
||||
if (isAlreadyLoaded) {
|
||||
return attachment;
|
||||
return attachment as AttachmentWithHydratedData;
|
||||
}
|
||||
|
||||
if (!is.string(attachment.path)) {
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
import { isFunction, isObject, isString, omit } from 'lodash';
|
||||
|
||||
import * as Contact from './EmbeddedContact';
|
||||
import type { AttachmentType } from './Attachment';
|
||||
import type { AttachmentType, AttachmentWithHydratedData } from './Attachment';
|
||||
import {
|
||||
autoOrientJPEG,
|
||||
captureDimensionsAndScreenshot,
|
||||
|
@ -24,11 +24,10 @@ import type { EmbeddedContactType } from './EmbeddedContact';
|
|||
|
||||
import type {
|
||||
MessageAttributesType,
|
||||
PreviewMessageType,
|
||||
PreviewType,
|
||||
QuotedMessageType,
|
||||
StickerMessageType,
|
||||
} from '../model-types.d';
|
||||
import type { LinkPreviewType } from './message/LinkPreviews';
|
||||
import type { StickerType, StickerWithHydratedData } from './Stickers';
|
||||
|
||||
export { hasExpiration } from './Message';
|
||||
|
||||
|
@ -45,7 +44,7 @@ export type ContextType = {
|
|||
width: number;
|
||||
height: number;
|
||||
}>;
|
||||
getRegionCode: () => string;
|
||||
getRegionCode: () => string | undefined;
|
||||
logger: LoggerType;
|
||||
makeImageThumbnail: (params: {
|
||||
size: number;
|
||||
|
@ -65,12 +64,12 @@ export type ContextType = {
|
|||
maxVersion?: number;
|
||||
revokeObjectUrl: (objectUrl: string) => void;
|
||||
writeNewAttachmentData: (data: Uint8Array) => Promise<string>;
|
||||
writeNewStickerData: (sticker: StickerMessageType) => Promise<string>;
|
||||
writeNewStickerData: (data: Uint8Array) => Promise<string>;
|
||||
};
|
||||
|
||||
type WriteExistingAttachmentDataType = (
|
||||
attachment: Pick<AttachmentType, 'data' | 'path'>
|
||||
) => Promise<void>;
|
||||
) => Promise<string>;
|
||||
|
||||
export type ContextWithMessageType = ContextType & {
|
||||
message: MessageAttributesType;
|
||||
|
@ -343,7 +342,7 @@ export const _mapPreviewAttachments =
|
|||
);
|
||||
}
|
||||
|
||||
const upgradeWithContext = async (preview: PreviewType) => {
|
||||
const upgradeWithContext = async (preview: LinkPreviewType) => {
|
||||
const { image } = preview;
|
||||
if (!image) {
|
||||
return preview;
|
||||
|
@ -544,7 +543,17 @@ export const processNewAttachment = async (
|
|||
makeImageThumbnail,
|
||||
makeVideoScreenshot,
|
||||
logger,
|
||||
}: ContextType
|
||||
}: Pick<
|
||||
ContextType,
|
||||
| 'writeNewAttachmentData'
|
||||
| 'getAbsoluteAttachmentPath'
|
||||
| 'makeObjectUrl'
|
||||
| 'revokeObjectUrl'
|
||||
| 'getImageDimensions'
|
||||
| 'makeImageThumbnail'
|
||||
| 'makeVideoScreenshot'
|
||||
| 'logger'
|
||||
>
|
||||
): Promise<AttachmentType> => {
|
||||
if (!isFunction(writeNewAttachmentData)) {
|
||||
throw new TypeError('context.writeNewAttachmentData is required');
|
||||
|
@ -595,13 +604,19 @@ export const processNewAttachment = async (
|
|||
};
|
||||
|
||||
export const processNewSticker = async (
|
||||
stickerData: StickerMessageType,
|
||||
stickerData: Uint8Array,
|
||||
{
|
||||
writeNewStickerData,
|
||||
getAbsoluteStickerPath,
|
||||
getImageDimensions,
|
||||
logger,
|
||||
}: ContextType
|
||||
}: Pick<
|
||||
ContextType,
|
||||
| 'writeNewStickerData'
|
||||
| 'getAbsoluteStickerPath'
|
||||
| 'getImageDimensions'
|
||||
| 'logger'
|
||||
>
|
||||
): Promise<{ path: string; width: number; height: number }> => {
|
||||
if (!isFunction(writeNewStickerData)) {
|
||||
throw new TypeError('context.writeNewStickerData is required');
|
||||
|
@ -633,7 +648,7 @@ export const processNewSticker = async (
|
|||
|
||||
type LoadAttachmentType = (
|
||||
attachment: AttachmentType
|
||||
) => Promise<AttachmentType>;
|
||||
) => Promise<AttachmentWithHydratedData>;
|
||||
|
||||
export const createAttachmentLoader = (
|
||||
loadAttachmentData: LoadAttachmentType
|
||||
|
@ -694,16 +709,16 @@ export const loadContactData = (
|
|||
loadAttachmentData: LoadAttachmentType
|
||||
): ((
|
||||
contact: Array<EmbeddedContactType> | undefined
|
||||
) => Promise<Array<EmbeddedContactType> | null>) => {
|
||||
) => Promise<Array<EmbeddedContactType> | undefined>) => {
|
||||
if (!isFunction(loadAttachmentData)) {
|
||||
throw new TypeError('loadContactData: loadAttachmentData is required');
|
||||
}
|
||||
|
||||
return async (
|
||||
contact: Array<EmbeddedContactType> | undefined
|
||||
): Promise<Array<EmbeddedContactType> | null> => {
|
||||
): Promise<Array<EmbeddedContactType> | undefined> => {
|
||||
if (!contact) {
|
||||
return null;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return Promise.all(
|
||||
|
@ -736,12 +751,14 @@ export const loadContactData = (
|
|||
|
||||
export const loadPreviewData = (
|
||||
loadAttachmentData: LoadAttachmentType
|
||||
): ((preview: PreviewMessageType) => Promise<PreviewMessageType>) => {
|
||||
): ((
|
||||
preview: Array<LinkPreviewType> | undefined
|
||||
) => Promise<Array<LinkPreviewType>>) => {
|
||||
if (!isFunction(loadAttachmentData)) {
|
||||
throw new TypeError('loadPreviewData: loadAttachmentData is required');
|
||||
}
|
||||
|
||||
return async (preview: PreviewMessageType) => {
|
||||
return async (preview: Array<LinkPreviewType> | undefined) => {
|
||||
if (!preview || !preview.length) {
|
||||
return [];
|
||||
}
|
||||
|
@ -763,14 +780,16 @@ export const loadPreviewData = (
|
|||
|
||||
export const loadStickerData = (
|
||||
loadAttachmentData: LoadAttachmentType
|
||||
): ((sticker: StickerMessageType) => Promise<StickerMessageType | null>) => {
|
||||
): ((
|
||||
sticker: StickerType | undefined
|
||||
) => Promise<StickerWithHydratedData | undefined>) => {
|
||||
if (!isFunction(loadAttachmentData)) {
|
||||
throw new TypeError('loadStickerData: loadAttachmentData is required');
|
||||
}
|
||||
|
||||
return async (sticker: StickerMessageType) => {
|
||||
return async (sticker: StickerType | undefined) => {
|
||||
if (!sticker || !sticker.data) {
|
||||
return null;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return {
|
||||
|
@ -966,8 +985,8 @@ export const createAttachmentDataWriter = ({
|
|||
};
|
||||
|
||||
const writePreviewImage = async (
|
||||
item: PreviewType
|
||||
): Promise<PreviewType> => {
|
||||
item: LinkPreviewType
|
||||
): Promise<LinkPreviewType> => {
|
||||
const { image } = item;
|
||||
if (!image) {
|
||||
return omit(item, ['image']);
|
||||
|
|
66
ts/types/RendererConfig.ts
Normal file
66
ts/types/RendererConfig.ts
Normal file
|
@ -0,0 +1,66 @@
|
|||
// Copyright 2022 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { z } from 'zod';
|
||||
|
||||
import { themeSettingSchema } from './StorageUIKeys';
|
||||
import { environmentSchema } from '../environment';
|
||||
|
||||
const configRequiredStringSchema = z.string().nonempty();
|
||||
export type ConfigRequiredStringType = z.infer<
|
||||
typeof configRequiredStringSchema
|
||||
>;
|
||||
|
||||
const configOptionalStringSchema = configRequiredStringSchema.or(z.undefined());
|
||||
export type configOptionalStringType = z.infer<
|
||||
typeof configOptionalStringSchema
|
||||
>;
|
||||
|
||||
export const rendererConfigSchema = z.object({
|
||||
appInstance: configOptionalStringSchema,
|
||||
appStartInitialSpellcheckSetting: z.boolean(),
|
||||
buildCreation: z.number(),
|
||||
buildExpiration: z.number(),
|
||||
cdnUrl0: configRequiredStringSchema,
|
||||
cdnUrl2: configRequiredStringSchema,
|
||||
certificateAuthority: configRequiredStringSchema,
|
||||
contentProxyUrl: configRequiredStringSchema,
|
||||
crashDumpsPath: configRequiredStringSchema,
|
||||
directoryEnclaveId: configOptionalStringSchema,
|
||||
directoryTrustAnchor: configOptionalStringSchema,
|
||||
directoryUrl: configOptionalStringSchema,
|
||||
directoryV2CodeHashes: z.array(z.string().nonempty()).or(z.undefined()),
|
||||
directoryV2PublicKey: configOptionalStringSchema,
|
||||
directoryV2Url: configOptionalStringSchema,
|
||||
directoryVersion: z.number(),
|
||||
enableCI: z.boolean(),
|
||||
environment: environmentSchema,
|
||||
homePath: configRequiredStringSchema,
|
||||
hostname: configRequiredStringSchema,
|
||||
locale: configRequiredStringSchema,
|
||||
name: configRequiredStringSchema,
|
||||
nodeVersion: configRequiredStringSchema,
|
||||
proxyUrl: configOptionalStringSchema,
|
||||
reducedMotionSetting: z.boolean(),
|
||||
serverPublicParams: configRequiredStringSchema,
|
||||
serverTrustRoot: configRequiredStringSchema,
|
||||
serverUrl: configRequiredStringSchema,
|
||||
sfuUrl: configRequiredStringSchema,
|
||||
storageUrl: configRequiredStringSchema,
|
||||
theme: themeSettingSchema,
|
||||
updatesUrl: configRequiredStringSchema,
|
||||
userDataPath: configRequiredStringSchema,
|
||||
version: configRequiredStringSchema,
|
||||
|
||||
// Only used by main window
|
||||
isMainWindowFullScreen: z.boolean(),
|
||||
|
||||
// Only for tests
|
||||
argv: configOptionalStringSchema,
|
||||
|
||||
// Only for permission popup window
|
||||
forCalling: z.boolean(),
|
||||
forCamera: z.boolean(),
|
||||
});
|
||||
|
||||
export type RendererConfigType = z.infer<typeof rendererConfigSchema>;
|
|
@ -12,12 +12,12 @@ import { maybeParseUrl } from '../util/url';
|
|||
import * as Bytes from '../Bytes';
|
||||
import * as Errors from './errors';
|
||||
import { deriveStickerPackKey, decryptAttachment } from '../Crypto';
|
||||
import type { MIMEType } from './MIME';
|
||||
import { IMAGE_WEBP } from './MIME';
|
||||
import type { MIMEType } from './MIME';
|
||||
import { sniffImageMimeType } from '../util/sniffImageMimeType';
|
||||
import type { AttachmentType } from './Attachment';
|
||||
import type { AttachmentType, AttachmentWithHydratedData } from './Attachment';
|
||||
import type {
|
||||
StickerType,
|
||||
StickerType as StickerFromDBType,
|
||||
StickerPackType,
|
||||
StickerPackStatusType,
|
||||
} from '../sql/Interface';
|
||||
|
@ -26,6 +26,20 @@ import { SignalService as Proto } from '../protobuf';
|
|||
import * as log from '../logging/log';
|
||||
import type { StickersStateType } from '../state/ducks/stickers';
|
||||
|
||||
export type StickerType = {
|
||||
packId: string;
|
||||
stickerId: number;
|
||||
packKey: string;
|
||||
emoji?: string;
|
||||
data?: AttachmentType;
|
||||
path?: string;
|
||||
width?: number;
|
||||
height?: number;
|
||||
};
|
||||
export type StickerWithHydratedData = StickerType & {
|
||||
data: AttachmentWithHydratedData;
|
||||
};
|
||||
|
||||
export type RecentStickerType = Readonly<{
|
||||
stickerId: number;
|
||||
packId: string;
|
||||
|
@ -300,11 +314,16 @@ async function downloadSticker(
|
|||
packKey: string,
|
||||
proto: Proto.StickerPack.ISticker,
|
||||
{ ephemeral }: { ephemeral?: boolean } = {}
|
||||
): Promise<Omit<StickerType, 'isCoverOnly'>> {
|
||||
): Promise<Omit<StickerFromDBType, 'isCoverOnly'>> {
|
||||
const { id, emoji } = proto;
|
||||
strictAssert(id !== undefined && id !== null, "Sticker id can't be null");
|
||||
|
||||
const ciphertext = await window.textsecure.messaging.getSticker(packId, id);
|
||||
const { messaging } = window.textsecure;
|
||||
if (!messaging) {
|
||||
throw new Error('messaging is not available!');
|
||||
}
|
||||
|
||||
const ciphertext = await messaging.getSticker(packId, id);
|
||||
const plaintext = decryptSticker(packKey, ciphertext);
|
||||
|
||||
const sticker = ephemeral
|
||||
|
@ -401,9 +420,12 @@ export async function downloadEphemeralPack(
|
|||
};
|
||||
stickerPackAdded(placeholder);
|
||||
|
||||
const ciphertext = await window.textsecure.messaging.getStickerPackManifest(
|
||||
packId
|
||||
);
|
||||
const { messaging } = window.textsecure;
|
||||
if (!messaging) {
|
||||
throw new Error('messaging is not available!');
|
||||
}
|
||||
|
||||
const ciphertext = await messaging.getStickerPackManifest(packId);
|
||||
const plaintext = decryptSticker(packKey, ciphertext);
|
||||
const proto = Proto.StickerPack.decode(plaintext);
|
||||
const firstStickerProto = proto.stickers ? proto.stickers[0] : null;
|
||||
|
@ -599,9 +621,12 @@ async function doDownloadStickerPack(
|
|||
};
|
||||
stickerPackAdded(placeholder);
|
||||
|
||||
const ciphertext = await window.textsecure.messaging.getStickerPackManifest(
|
||||
packId
|
||||
);
|
||||
const { messaging } = window.textsecure;
|
||||
if (!messaging) {
|
||||
throw new Error('messaging is not available!');
|
||||
}
|
||||
|
||||
const ciphertext = await messaging.getStickerPackManifest(packId);
|
||||
const plaintext = decryptSticker(packKey, ciphertext);
|
||||
const proto = Proto.StickerPack.decode(plaintext);
|
||||
const firstStickerProto = proto.stickers ? proto.stickers[0] : undefined;
|
||||
|
@ -776,7 +801,7 @@ export function getStickerPackStatus(
|
|||
export function getSticker(
|
||||
packId: string,
|
||||
stickerId: number
|
||||
): StickerType | undefined {
|
||||
): StickerFromDBType | undefined {
|
||||
const pack = getStickerPack(packId);
|
||||
|
||||
if (!pack || !pack.stickers) {
|
||||
|
@ -803,9 +828,7 @@ export async function copyStickerToAttachments(
|
|||
const { path, size } =
|
||||
await window.Signal.Migrations.copyIntoAttachmentsDirectory(absolutePath);
|
||||
|
||||
const { data } = await window.Signal.Migrations.loadAttachmentData({
|
||||
path,
|
||||
});
|
||||
const data = await window.Signal.Migrations.readAttachmentData(path);
|
||||
|
||||
let contentType: MIMEType;
|
||||
const sniffedMimeType = sniffImageMimeType(data);
|
||||
|
|
12
ts/types/Storage.d.ts
vendored
12
ts/types/Storage.d.ts
vendored
|
@ -12,19 +12,15 @@ import type { PhoneNumberSharingMode } from '../util/phoneNumberSharingMode';
|
|||
import type { RetryItemType } from '../util/retryPlaceholders';
|
||||
import type { ConfigMapType as RemoteConfigType } from '../RemoteConfig';
|
||||
import type { SystemTraySetting } from './SystemTraySetting';
|
||||
import type {
|
||||
ExtendedStorageID,
|
||||
RemoteRecord,
|
||||
UnknownRecord,
|
||||
} from './StorageService';
|
||||
import type { ExtendedStorageID, UnknownRecord } from './StorageService';
|
||||
|
||||
import type { GroupCredentialType } from '../textsecure/WebAPI';
|
||||
import type {
|
||||
KeyPairType,
|
||||
SessionResetsType,
|
||||
StorageServiceCredentials,
|
||||
} from '../textsecure/Types.d';
|
||||
import { UUIDStringType } from './UUID';
|
||||
import type { ThemeSettingType } from './StorageUIKeys';
|
||||
|
||||
import { RegisteredChallengeType } from '../challenge';
|
||||
|
||||
export type SerializedCertificateType = {
|
||||
|
@ -34,8 +30,6 @@ export type SerializedCertificateType = {
|
|||
|
||||
export type ZoomFactorType = 0.75 | 1 | 1.25 | 1.5 | 2 | number;
|
||||
|
||||
export type ThemeSettingType = 'system' | 'light' | 'dark';
|
||||
|
||||
export type NotificationSettingType = 'message' | 'name' | 'count' | 'off';
|
||||
|
||||
export type IdentityKeyMap = Record<
|
||||
|
|
|
@ -1,8 +1,13 @@
|
|||
// Copyright 2021 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { z } from 'zod';
|
||||
|
||||
import type { StorageAccessType } from './Storage.d';
|
||||
|
||||
export const themeSettingSchema = z.enum(['system', 'light', 'dark']);
|
||||
export type ThemeSettingType = z.infer<typeof themeSettingSchema>;
|
||||
|
||||
// Configuration keys that only affect UI
|
||||
export const STORAGE_UI_KEYS: ReadonlyArray<keyof StorageAccessType> = [
|
||||
'always-relay-calls',
|
||||
|
|
|
@ -23,12 +23,14 @@ export type RenderTextCallbackType = (options: {
|
|||
key: number;
|
||||
}) => JSX.Element | string;
|
||||
|
||||
export type ReplacementValuesType = {
|
||||
[key: string]: string | number | undefined;
|
||||
};
|
||||
export type ReplacementValuesType =
|
||||
| Array<string>
|
||||
| {
|
||||
[key: string]: string | number | undefined;
|
||||
};
|
||||
|
||||
export type LocalizerType = {
|
||||
(key: string, values?: Array<string> | ReplacementValuesType): string;
|
||||
(key: string, values?: ReplacementValuesType): string;
|
||||
getLocale(): string;
|
||||
};
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ export type LinkPreviewType = {
|
|||
description?: string;
|
||||
domain: string;
|
||||
url: string;
|
||||
isStickerPack: boolean;
|
||||
isStickerPack?: boolean;
|
||||
image?: Readonly<AttachmentType>;
|
||||
date?: number;
|
||||
};
|
||||
|
|
|
@ -12,6 +12,11 @@ export async function downloadAttachment(
|
|||
): Promise<DownloadedAttachmentType | null> {
|
||||
let migratedAttachment: AttachmentType;
|
||||
|
||||
const { server } = window.textsecure;
|
||||
if (!server) {
|
||||
throw new Error('window.textsecure.server is not available!');
|
||||
}
|
||||
|
||||
const { id: legacyId } = attachmentData;
|
||||
if (legacyId === undefined) {
|
||||
migratedAttachment = attachmentData;
|
||||
|
@ -24,10 +29,7 @@ export async function downloadAttachment(
|
|||
|
||||
let downloaded;
|
||||
try {
|
||||
downloaded = await doDownloadAttachment(
|
||||
window.textsecure.server,
|
||||
migratedAttachment
|
||||
);
|
||||
downloaded = await doDownloadAttachment(server, migratedAttachment);
|
||||
} catch (error) {
|
||||
// Attachments on the server expire after 30 days, then start returning 404
|
||||
if (error && error.code === 404) {
|
||||
|
|
|
@ -32,6 +32,7 @@ import type {
|
|||
|
||||
import { SignalService as Proto } from '../protobuf';
|
||||
import * as log from '../logging/log';
|
||||
import MessageSender from '../textsecure/SendMessage';
|
||||
|
||||
const RETRY_LIMIT = 5;
|
||||
|
||||
|
@ -125,6 +126,11 @@ export async function onRetryRequest(event: RetryRequestEvent): Promise<void> {
|
|||
|
||||
log.info(`onRetryRequest/${logId}: Resending message`);
|
||||
|
||||
const { messaging } = window.textsecure;
|
||||
if (!messaging) {
|
||||
throw new Error(`onRetryRequest/${logId}: messaging is not available!`);
|
||||
}
|
||||
|
||||
const { contentHint, messageIds, proto, timestamp } = sentProto;
|
||||
|
||||
const { contentProto, groupId } = await maybeAddSenderKeyDistributionMessage({
|
||||
|
@ -141,7 +147,7 @@ export async function onRetryRequest(event: RetryRequestEvent): Promise<void> {
|
|||
'private'
|
||||
);
|
||||
const sendOptions = await getSendOptions(recipientConversation.attributes);
|
||||
const promise = window.textsecure.messaging.sendMessageProtoAndWait({
|
||||
const promise = messaging.sendMessageProtoAndWait({
|
||||
timestamp,
|
||||
recipients: [requesterUuid],
|
||||
proto: new Proto.Content(contentProto),
|
||||
|
@ -263,6 +269,13 @@ async function sendDistributionMessageOrNullMessage(
|
|||
let sentDistributionMessage = false;
|
||||
log.info(`sendDistributionMessageOrNullMessage/${logId}: Starting...`);
|
||||
|
||||
const { messaging } = window.textsecure;
|
||||
if (!messaging) {
|
||||
throw new Error(
|
||||
`sendDistributionMessageOrNullMessage/${logId}: messaging is not available!`
|
||||
);
|
||||
}
|
||||
|
||||
const conversation = window.ConversationController.getOrCreate(
|
||||
requesterUuid,
|
||||
'private'
|
||||
|
@ -286,7 +299,7 @@ async function sendDistributionMessageOrNullMessage(
|
|||
|
||||
try {
|
||||
await handleMessageSend(
|
||||
window.textsecure.messaging.sendSenderKeyDistributionMessage(
|
||||
messaging.sendSenderKeyDistributionMessage(
|
||||
{
|
||||
contentHint: ContentHint.RESENDABLE,
|
||||
distributionId,
|
||||
|
@ -322,11 +335,11 @@ async function sendDistributionMessageOrNullMessage(
|
|||
|
||||
// Enqueue a null message using the newly-created session
|
||||
try {
|
||||
const nullMessage = window.textsecure.messaging.getNullMessage({
|
||||
const nullMessage = MessageSender.getNullMessage({
|
||||
uuid: requesterUuid,
|
||||
});
|
||||
await handleMessageSend(
|
||||
window.textsecure.messaging.sendIndividualProto({
|
||||
messaging.sendIndividualProto({
|
||||
...nullMessage,
|
||||
options: sendOptions,
|
||||
proto: Proto.Content.decode(
|
||||
|
@ -397,6 +410,13 @@ async function maybeAddSenderKeyDistributionMessage({
|
|||
requestGroupId,
|
||||
});
|
||||
|
||||
const { messaging } = window.textsecure;
|
||||
if (!messaging) {
|
||||
throw new Error(
|
||||
`maybeAddSenderKeyDistributionMessage/${logId}: messaging is not available!`
|
||||
);
|
||||
}
|
||||
|
||||
if (!conversation) {
|
||||
log.warn(
|
||||
`maybeAddSenderKeyDistributionMessage/${logId}: Unable to find conversation`
|
||||
|
@ -421,7 +441,7 @@ async function maybeAddSenderKeyDistributionMessage({
|
|||
const senderKeyInfo = conversation.get('senderKeyInfo');
|
||||
if (senderKeyInfo && senderKeyInfo.distributionId) {
|
||||
const protoWithDistributionMessage =
|
||||
await window.textsecure.messaging.getSenderKeyDistributionMessage(
|
||||
await messaging.getSenderKeyDistributionMessage(
|
||||
senderKeyInfo.distributionId,
|
||||
{ throwIfNotInDatabase: true, timestamp }
|
||||
);
|
||||
|
@ -463,6 +483,11 @@ async function requestResend(decryptionError: DecryptionErrorEventData) {
|
|||
groupId: groupId ? `groupv2(${groupId})` : undefined,
|
||||
});
|
||||
|
||||
const { messaging } = window.textsecure;
|
||||
if (!messaging) {
|
||||
throw new Error(`requestResend/${logId}: messaging is not available!`);
|
||||
}
|
||||
|
||||
// 1. Find the target conversation
|
||||
|
||||
const group = groupId
|
||||
|
@ -495,7 +520,7 @@ async function requestResend(decryptionError: DecryptionErrorEventData) {
|
|||
const plaintext = PlaintextContent.from(message);
|
||||
const options = await getSendOptions(conversation.attributes);
|
||||
const result = await handleMessageSend(
|
||||
window.textsecure.messaging.sendRetryRequest({
|
||||
messaging.sendRetryRequest({
|
||||
plaintext,
|
||||
options,
|
||||
groupId,
|
||||
|
|
|
@ -10,12 +10,10 @@ const NINETY_ONE_DAYS = 91 * ONE_DAY_MS;
|
|||
const THIRTY_ONE_DAYS = 31 * ONE_DAY_MS;
|
||||
|
||||
export function hasExpired(): boolean {
|
||||
const { getExpiration } = window;
|
||||
|
||||
let buildExpiration = 0;
|
||||
|
||||
try {
|
||||
buildExpiration = parseInt(getExpiration(), 10);
|
||||
buildExpiration = window.getExpiration();
|
||||
if (buildExpiration) {
|
||||
log.info('Build expires: ', new Date(buildExpiration).toISOString());
|
||||
}
|
||||
|
|
|
@ -8283,7 +8283,7 @@
|
|||
{
|
||||
"rule": "jQuery-$(",
|
||||
"path": "sticker-creator/util/i18n.tsx",
|
||||
"line": " const FIND_REPLACEMENTS = /\\$([^$]+)\\$/g;",
|
||||
"line": " const FIND_REPLACEMENTS = /\\$([^$]+)\\$/g;",
|
||||
"reasonCategory": "falseMatch",
|
||||
"updated": "2020-07-21T18:34:59.251Z"
|
||||
},
|
||||
|
|
|
@ -62,12 +62,15 @@ export async function lookupConversationWithoutUuid(
|
|||
const { showUserNotFoundModal, setIsFetchingUUID } = options;
|
||||
setIsFetchingUUID(identifier, true);
|
||||
|
||||
const { messaging } = window.textsecure;
|
||||
if (!messaging) {
|
||||
throw new Error('messaging is not available!');
|
||||
}
|
||||
|
||||
try {
|
||||
let conversationId: string | undefined;
|
||||
if (options.type === 'e164') {
|
||||
const serverLookup = await window.textsecure.messaging.getUuidsForE164s([
|
||||
options.e164,
|
||||
]);
|
||||
const serverLookup = await messaging.getUuidsForE164s([options.e164]);
|
||||
|
||||
if (serverLookup[options.e164]) {
|
||||
conversationId = window.ConversationController.ensureContactIds({
|
||||
|
@ -131,10 +134,13 @@ async function checkForUsername(
|
|||
return undefined;
|
||||
}
|
||||
|
||||
const { messaging } = window.textsecure;
|
||||
if (!messaging) {
|
||||
throw new Error('messaging is not available!');
|
||||
}
|
||||
|
||||
try {
|
||||
const profile = await window.textsecure.messaging.getProfileForUsername(
|
||||
username
|
||||
);
|
||||
const profile = await messaging.getProfileForUsername(username);
|
||||
|
||||
if (!profile.uuid) {
|
||||
log.error("checkForUsername: Returned profile didn't include a uuid");
|
||||
|
|
|
@ -74,9 +74,7 @@ export type ElectronLocaleType =
|
|||
| 'zh_CN'
|
||||
| 'zh_TW';
|
||||
|
||||
export function mapToSupportLocale(
|
||||
ourLocale: ElectronLocaleType
|
||||
): SupportLocaleType {
|
||||
export function mapToSupportLocale(ourLocale: string): SupportLocaleType {
|
||||
if (ourLocale === 'ar') {
|
||||
return ourLocale;
|
||||
}
|
||||
|
|
|
@ -2,14 +2,6 @@
|
|||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { partition } from 'lodash';
|
||||
import type { AttachmentType } from '../types/Attachment';
|
||||
import type { EmbeddedContactType } from '../types/EmbeddedContact';
|
||||
import type {
|
||||
MessageAttributesType,
|
||||
PreviewMessageType,
|
||||
QuotedMessageType,
|
||||
StickerMessageType,
|
||||
} from '../model-types.d';
|
||||
import * as AttachmentDownloads from '../messageModifiers/AttachmentDownloads';
|
||||
import * as log from '../logging/log';
|
||||
import { isLongMessage } from '../types/MIME';
|
||||
|
@ -21,13 +13,22 @@ import {
|
|||
} from '../types/Stickers';
|
||||
import dataInterface from '../sql/Client';
|
||||
|
||||
import type { AttachmentType } from '../types/Attachment';
|
||||
import type { EmbeddedContactType } from '../types/EmbeddedContact';
|
||||
import type {
|
||||
MessageAttributesType,
|
||||
QuotedMessageType,
|
||||
} from '../model-types.d';
|
||||
import type { StickerType } from '../types/Stickers';
|
||||
import type { LinkPreviewType } from '../types/message/LinkPreviews';
|
||||
|
||||
type ReturnType = {
|
||||
bodyAttachment?: AttachmentType;
|
||||
attachments: Array<AttachmentType>;
|
||||
preview: PreviewMessageType;
|
||||
preview: Array<LinkPreviewType>;
|
||||
contact: Array<EmbeddedContactType>;
|
||||
quote?: QuotedMessageType;
|
||||
sticker?: StickerMessageType;
|
||||
sticker?: StickerType;
|
||||
};
|
||||
|
||||
// Receive logic
|
||||
|
|
|
@ -45,6 +45,11 @@ export async function sendReceipts({
|
|||
throw missingCaseError(type);
|
||||
}
|
||||
|
||||
const { messaging } = window.textsecure;
|
||||
if (!messaging) {
|
||||
throw new Error('messaging is not available!');
|
||||
}
|
||||
|
||||
if (requiresUserSetting && !window.storage.get('read-receipt-setting')) {
|
||||
log.info('requires user setting. Not sending these receipts');
|
||||
return;
|
||||
|
@ -119,7 +124,7 @@ export async function sendReceipts({
|
|||
const messageIds = batch.map(receipt => receipt.messageId);
|
||||
|
||||
await handleMessageSend(
|
||||
window.textsecure.messaging[methodName]({
|
||||
messaging[methodName]({
|
||||
senderE164: sender.get('e164'),
|
||||
senderUuid: sender.get('uuid'),
|
||||
timestamps,
|
||||
|
|
|
@ -3288,16 +3288,20 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
|
|||
return this.getGroupPreview(url, abortSignal);
|
||||
}
|
||||
|
||||
const { messaging } = window.textsecure;
|
||||
if (!messaging) {
|
||||
throw new Error('messaging is not available!');
|
||||
}
|
||||
|
||||
// This is already checked elsewhere, but we want to be extra-careful.
|
||||
if (!LinkPreview.shouldPreviewHref(url)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const linkPreviewMetadata =
|
||||
await window.textsecure.messaging.fetchLinkPreviewMetadata(
|
||||
url,
|
||||
abortSignal
|
||||
);
|
||||
const linkPreviewMetadata = await messaging.fetchLinkPreviewMetadata(
|
||||
url,
|
||||
abortSignal
|
||||
);
|
||||
if (!linkPreviewMetadata || abortSignal.aborted) {
|
||||
return null;
|
||||
}
|
||||
|
@ -3307,11 +3311,10 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
|
|||
if (imageHref && LinkPreview.shouldPreviewHref(imageHref)) {
|
||||
let objectUrl: void | string;
|
||||
try {
|
||||
const fullSizeImage =
|
||||
await window.textsecure.messaging.fetchLinkPreviewImage(
|
||||
imageHref,
|
||||
abortSignal
|
||||
);
|
||||
const fullSizeImage = await messaging.fetchLinkPreviewImage(
|
||||
imageHref,
|
||||
abortSignal
|
||||
);
|
||||
if (abortSignal.aborted) {
|
||||
return null;
|
||||
}
|
||||
|
|
315
ts/window.d.ts
vendored
315
ts/window.d.ts
vendored
|
@ -3,19 +3,21 @@
|
|||
|
||||
// Captures the globals put in place by preload.js, background.js and others
|
||||
|
||||
import type { Cancelable } from 'lodash';
|
||||
import { Store } from 'redux';
|
||||
import * as Backbone from 'backbone';
|
||||
import * as Underscore from 'underscore';
|
||||
import moment from 'moment';
|
||||
import PQueue from 'p-queue/dist';
|
||||
import { Ref } from 'react';
|
||||
import { assert } from 'chai';
|
||||
|
||||
import { imageToBlurHash } from './util/imageToBlurHash';
|
||||
import * as Util from './util';
|
||||
import {
|
||||
ConversationModelCollectionType,
|
||||
MessageModelCollectionType,
|
||||
} from './model-types.d';
|
||||
import { TextSecureType } from './textsecure.d';
|
||||
import { textsecure } from './textsecure';
|
||||
import { Storage } from './textsecure/Storage';
|
||||
import {
|
||||
ChallengeHandler,
|
||||
|
@ -28,7 +30,7 @@ import * as Crypto from './Crypto';
|
|||
import * as Curve from './Curve';
|
||||
import * as RemoteConfig from './RemoteConfig';
|
||||
import * as OS from './OS';
|
||||
import { getEnvironment } from './environment';
|
||||
import { Environment, getEnvironment } from './environment';
|
||||
import { LocalizerType, ThemeType } from './types/Util';
|
||||
import type { Receipt } from './types/Receipt';
|
||||
import { ConversationController } from './ConversationController';
|
||||
|
@ -65,9 +67,8 @@ import * as stickersDuck from './state/ducks/stickers';
|
|||
import * as conversationsSelectors from './state/selectors/conversations';
|
||||
import * as searchSelectors from './state/selectors/search';
|
||||
import AccountManager from './textsecure/AccountManager';
|
||||
import { ContactWithHydratedAvatar } from './textsecure/SendMessage';
|
||||
import Data from './sql/Client';
|
||||
import { PhoneNumberFormat } from 'google-libphonenumber';
|
||||
import { PhoneNumber, PhoneNumberFormat } from 'google-libphonenumber';
|
||||
import { MessageModel } from './models/messages';
|
||||
import { ConversationModel } from './models/conversations';
|
||||
import { BatcherType } from './util/batcher';
|
||||
|
@ -79,8 +80,6 @@ import { MessageDetail } from './components/conversation/MessageDetail';
|
|||
import { Quote } from './components/conversation/Quote';
|
||||
import { StagedLinkPreview } from './components/conversation/StagedLinkPreview';
|
||||
import { DisappearingTimeDialog } from './components/DisappearingTimeDialog';
|
||||
import { DownloadedAttachmentType } from './types/Attachment';
|
||||
import { ElectronLocaleType } from './util/mapToSupportLocale';
|
||||
import { SignalProtocolStore } from './SignalProtocolStore';
|
||||
import { StartupQueue } from './util/StartupQueue';
|
||||
import { SocketStatus } from './types/SocketStatus';
|
||||
|
@ -95,7 +94,9 @@ import { CI } from './CI';
|
|||
import { IPCEventsType } from './util/createIPCEvents';
|
||||
import { ConversationView } from './views/conversation_view';
|
||||
import type { SignalContextType } from './windows/context';
|
||||
import type { EmbeddedContactType } from './types/EmbeddedContact';
|
||||
import type * as Message2 from './types/Message2';
|
||||
import type { initializeMigrations } from './signal';
|
||||
import { RendererConfigType } from './types/RendererConfig';
|
||||
|
||||
export { Long } from 'long';
|
||||
|
||||
|
@ -135,12 +136,105 @@ export declare class WebAudioRecorderClass {
|
|||
worker: Worker;
|
||||
}
|
||||
|
||||
export type SignalCoreType = {
|
||||
Backbone: any;
|
||||
Crypto: typeof Crypto;
|
||||
Curve: typeof Curve;
|
||||
Data: typeof Data;
|
||||
Groups: typeof Groups;
|
||||
RemoteConfig: typeof RemoteConfig;
|
||||
Services: {
|
||||
calling: CallingClass;
|
||||
enableStorageService: () => void;
|
||||
eraseAllStorageServiceState: (options?: {
|
||||
keepUnknownFields?: boolean | undefined;
|
||||
}) => Promise<void>;
|
||||
initializeGroupCredentialFetcher: () => Promise<void>;
|
||||
initializeNetworkObserver: (network: ReduxActions['network']) => void;
|
||||
initializeUpdateListener: (updates: ReduxActions['updates']) => void;
|
||||
retryPlaceholders?: Util.RetryPlaceholders;
|
||||
lightSessionResetQueue?: PQueue;
|
||||
runStorageServiceSyncJob: (() => void) & Cancelable;
|
||||
storageServiceUploadJob: (() => void) & Cancelable;
|
||||
};
|
||||
Migrations: ReturnType<typeof initializeMigrations>;
|
||||
Types: {
|
||||
Message: typeof Message2;
|
||||
UUID: typeof UUID;
|
||||
Address: typeof Address;
|
||||
QualifiedAddress: typeof QualifiedAddress;
|
||||
};
|
||||
Util: typeof Util;
|
||||
Components: {
|
||||
AttachmentList: typeof AttachmentList;
|
||||
ChatColorPicker: typeof ChatColorPicker;
|
||||
ConfirmationDialog: typeof ConfirmationDialog;
|
||||
ContactModal: typeof ContactModal;
|
||||
DisappearingTimeDialog: typeof DisappearingTimeDialog;
|
||||
MessageDetail: typeof MessageDetail;
|
||||
Quote: typeof Quote;
|
||||
StagedLinkPreview: typeof StagedLinkPreview;
|
||||
};
|
||||
OS: typeof OS;
|
||||
State: {
|
||||
createStore: typeof createStore;
|
||||
Roots: {
|
||||
createApp: typeof createApp;
|
||||
createChatColorPicker: typeof createChatColorPicker;
|
||||
createConversationDetails: typeof createConversationDetails;
|
||||
createForwardMessageModal: typeof createForwardMessageModal;
|
||||
createGroupLinkManagement: typeof createGroupLinkManagement;
|
||||
createGroupV1MigrationModal: typeof createGroupV1MigrationModal;
|
||||
createGroupV2JoinModal: typeof createGroupV2JoinModal;
|
||||
createGroupV2Permissions: typeof createGroupV2Permissions;
|
||||
createLeftPane: typeof createLeftPane;
|
||||
createMessageDetail: typeof createMessageDetail;
|
||||
createConversationNotificationsSettings: typeof createConversationNotificationsSettings;
|
||||
createPendingInvites: typeof createPendingInvites;
|
||||
createSafetyNumberViewer: typeof createSafetyNumberViewer;
|
||||
createShortcutGuideModal: typeof createShortcutGuideModal;
|
||||
createStickerManager: typeof createStickerManager;
|
||||
createStickerPreviewModal: typeof createStickerPreviewModal;
|
||||
};
|
||||
Ducks: {
|
||||
app: typeof appDuck;
|
||||
calling: typeof callingDuck;
|
||||
conversations: typeof conversationsDuck;
|
||||
emojis: typeof emojisDuck;
|
||||
expiration: typeof expirationDuck;
|
||||
items: typeof itemsDuck;
|
||||
linkPreviews: typeof linkPreviewsDuck;
|
||||
network: typeof networkDuck;
|
||||
updates: typeof updatesDuck;
|
||||
user: typeof userDuck;
|
||||
search: typeof searchDuck;
|
||||
stickers: typeof stickersDuck;
|
||||
};
|
||||
Selectors: {
|
||||
conversations: typeof conversationsSelectors;
|
||||
search: typeof searchSelectors;
|
||||
};
|
||||
};
|
||||
conversationControllerStart: () => void;
|
||||
challengeHandler?: ChallengeHandler;
|
||||
};
|
||||
|
||||
declare global {
|
||||
// We want to extend `window`'s properties, so we need an interface.
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
interface Window {
|
||||
// Used in Sticker Creator to create proper paths to emoji images
|
||||
ROOT_PATH?: string;
|
||||
// Used for sticker creator localization
|
||||
localeMessages: { [key: string]: { message: string } };
|
||||
|
||||
// Note: used in background.html, and not type-checked
|
||||
startApp: () => void;
|
||||
|
||||
preloadStartTime: number;
|
||||
preloadEndTime: number;
|
||||
preloadConnectTime: number;
|
||||
|
||||
removeSetupMenuItems: () => unknown;
|
||||
showPermissionsPopup: (
|
||||
forCalling: boolean,
|
||||
|
@ -151,7 +245,6 @@ declare global {
|
|||
_: typeof Underscore;
|
||||
$: typeof jQuery;
|
||||
|
||||
moment: typeof moment;
|
||||
imageToBlurHash: typeof imageToBlurHash;
|
||||
loadImage: any;
|
||||
isBehindProxy: () => boolean;
|
||||
|
@ -174,6 +267,8 @@ declare global {
|
|||
baseAttachmentsPath: string;
|
||||
baseStickersPath: string;
|
||||
baseTempPath: string;
|
||||
baseDraftPath: string;
|
||||
closeAbout: () => void;
|
||||
crashReports: {
|
||||
getCount: () => Promise<number>;
|
||||
upload: () => Promise<void>;
|
||||
|
@ -183,14 +278,15 @@ declare global {
|
|||
enterKeyboardMode: () => void;
|
||||
enterMouseMode: () => void;
|
||||
getAccountManager: () => AccountManager;
|
||||
getAppInstance: () => string | undefined;
|
||||
getBuiltInImages: () => Promise<Array<string>>;
|
||||
getConversations: () => ConversationModelCollectionType;
|
||||
getBuildCreation: () => number;
|
||||
getEnvironment: typeof getEnvironment;
|
||||
getExpiration: () => string;
|
||||
getExpiration: () => number;
|
||||
getHostName: () => string;
|
||||
getInteractionMode: () => 'mouse' | 'keyboard';
|
||||
getLocale: () => ElectronLocaleType;
|
||||
getLocale: () => string;
|
||||
getMediaCameraPermissions: () => Promise<boolean>;
|
||||
getMediaPermissions: () => Promise<boolean>;
|
||||
getServerPublicParams: () => string;
|
||||
|
@ -206,11 +302,12 @@ declare global {
|
|||
isBeforeVersion: (version: string, anotherVersion: string) => boolean;
|
||||
isFullScreen: () => boolean;
|
||||
initialTheme?: ThemeType;
|
||||
libphonenumber: {
|
||||
parse: (number: string) => string;
|
||||
getRegionCodeForNumber: (number: string) => string;
|
||||
format: (number: string, format: PhoneNumberFormat) => string;
|
||||
libphonenumberInstance: {
|
||||
parse: (number: string) => PhoneNumber;
|
||||
getRegionCodeForNumber: (number: PhoneNumber) => string | undefined;
|
||||
format: (number: PhoneNumber, format: PhoneNumberFormat) => string;
|
||||
};
|
||||
libphonenumberFormat: typeof PhoneNumberFormat;
|
||||
nodeSetImmediate: typeof setImmediate;
|
||||
onFullScreenChange: (fullScreen: boolean) => void;
|
||||
platform: string;
|
||||
|
@ -233,7 +330,7 @@ declare global {
|
|||
showKeyboardShortcuts: () => void;
|
||||
storage: Storage;
|
||||
systemTheme: WhatIsThis;
|
||||
textsecure: TextSecureType;
|
||||
textsecure: typeof textsecure;
|
||||
titleBarDoubleClick: () => void;
|
||||
unregisterForActive: (handler: () => void) => void;
|
||||
updateTrayIcon: (count: number) => void;
|
||||
|
@ -243,180 +340,7 @@ declare global {
|
|||
Accessibility: {
|
||||
reducedMotionSetting: boolean;
|
||||
};
|
||||
Signal: {
|
||||
Backbone: any;
|
||||
Crypto: typeof Crypto;
|
||||
Curve: typeof Curve;
|
||||
Data: typeof Data;
|
||||
Groups: typeof Groups;
|
||||
RemoteConfig: typeof RemoteConfig;
|
||||
Services: {
|
||||
calling: CallingClass;
|
||||
enableStorageService: () => boolean;
|
||||
eraseAllStorageServiceState: (options?: {
|
||||
keepUnknownFields?: boolean;
|
||||
}) => Promise<void>;
|
||||
initializeGroupCredentialFetcher: () => void;
|
||||
initializeNetworkObserver: (network: ReduxActions['network']) => void;
|
||||
initializeUpdateListener: (updates: ReduxActions['updates']) => void;
|
||||
retryPlaceholders?: Util.RetryPlaceholders;
|
||||
lightSessionResetQueue?: PQueue;
|
||||
runStorageServiceSyncJob: () => Promise<void>;
|
||||
storageServiceUploadJob: () => void;
|
||||
};
|
||||
Migrations: {
|
||||
readTempData: (path: string) => Promise<Uint8Array>;
|
||||
deleteAttachmentData: (path: string) => Promise<void>;
|
||||
doesAttachmentExist: (path: string) => Promise<boolean>;
|
||||
writeNewAttachmentData: (data: Uint8Array) => Promise<string>;
|
||||
deleteExternalMessageFiles: (attributes: unknown) => Promise<void>;
|
||||
getAbsoluteAttachmentPath: (path: string) => string;
|
||||
loadAttachmentData: <T extends { path?: string }>(
|
||||
attachment: T
|
||||
) => Promise<
|
||||
T & {
|
||||
data: Uint8Array;
|
||||
size: number;
|
||||
}
|
||||
>;
|
||||
loadQuoteData: (quote: unknown) => WhatIsThis;
|
||||
loadContactData: (
|
||||
contact?: Array<EmbeddedContactType>
|
||||
) => Promise<Array<ContactWithHydratedAvatar> | undefined>;
|
||||
loadPreviewData: (preview: unknown) => WhatIsThis;
|
||||
loadStickerData: (sticker: unknown) => WhatIsThis;
|
||||
readStickerData: (path: string) => Promise<Uint8Array>;
|
||||
deleteSticker: (path: string) => Promise<void>;
|
||||
getAbsoluteStickerPath: (path: string) => string;
|
||||
processNewEphemeralSticker: (stickerData: Uint8Array) => {
|
||||
path: string;
|
||||
width: number;
|
||||
height: number;
|
||||
};
|
||||
processNewSticker: (stickerData: Uint8Array) => {
|
||||
path: string;
|
||||
width: number;
|
||||
height: number;
|
||||
};
|
||||
copyIntoAttachmentsDirectory: (
|
||||
path: string
|
||||
) => Promise<{ path: string; size: number }>;
|
||||
upgradeMessageSchema: (attributes: unknown) => WhatIsThis;
|
||||
processNewAttachment: (
|
||||
attachment: DownloadedAttachmentType
|
||||
) => Promise<DownloadedAttachmentType>;
|
||||
|
||||
copyIntoTempDirectory: (
|
||||
path: string
|
||||
) => Promise<{ path: string; size: number }>;
|
||||
deleteDraftFile: (path: string) => Promise<void>;
|
||||
deleteTempFile: (path: string) => Promise<void>;
|
||||
getAbsoluteDraftPath: any;
|
||||
getAbsoluteTempPath: any;
|
||||
openFileInFolder: any;
|
||||
readAttachmentData: (path: string) => Promise<Uint8Array>;
|
||||
readDraftData: (path: string) => Promise<Uint8Array>;
|
||||
saveAttachmentToDisk: (options: {
|
||||
data: Uint8Array;
|
||||
name: string;
|
||||
}) => Promise<null | { fullPath: string; name: string }>;
|
||||
writeNewDraftData: (data: Uint8Array) => Promise<string>;
|
||||
deleteAvatar: (path: string) => Promise<void>;
|
||||
getAbsoluteAvatarPath: (src: string) => string;
|
||||
writeNewAvatarData: (data: Uint8Array) => Promise<string>;
|
||||
getAbsoluteBadgeImageFilePath: (path: string) => string;
|
||||
writeNewBadgeImageFileData: (data: Uint8Array) => Promise<string>;
|
||||
};
|
||||
Types: {
|
||||
Message: {
|
||||
CURRENT_SCHEMA_VERSION: number;
|
||||
VERSION_NEEDED_FOR_DISPLAY: number;
|
||||
GROUP: 'group';
|
||||
PRIVATE: 'private';
|
||||
|
||||
initializeSchemaVersion: (version: {
|
||||
message: unknown;
|
||||
logger: unknown;
|
||||
}) => unknown & {
|
||||
schemaVersion: number;
|
||||
};
|
||||
hasExpiration: (json: string) => boolean;
|
||||
};
|
||||
Sticker: {
|
||||
emoji: string;
|
||||
packId: string;
|
||||
packKey: string;
|
||||
stickerId: number;
|
||||
data: {
|
||||
pending: boolean;
|
||||
path: string;
|
||||
};
|
||||
width: number;
|
||||
height: number;
|
||||
path: string;
|
||||
};
|
||||
UUID: typeof UUID;
|
||||
Address: typeof Address;
|
||||
QualifiedAddress: typeof QualifiedAddress;
|
||||
};
|
||||
Util: typeof Util;
|
||||
Components: {
|
||||
AttachmentList: typeof AttachmentList;
|
||||
ChatColorPicker: typeof ChatColorPicker;
|
||||
ConfirmationDialog: typeof ConfirmationDialog;
|
||||
ContactModal: typeof ContactModal;
|
||||
DisappearingTimeDialog: typeof DisappearingTimeDialog;
|
||||
MessageDetail: typeof MessageDetail;
|
||||
Quote: typeof Quote;
|
||||
StagedLinkPreview: typeof StagedLinkPreview;
|
||||
};
|
||||
OS: typeof OS;
|
||||
State: {
|
||||
createStore: typeof createStore;
|
||||
Roots: {
|
||||
createApp: typeof createApp;
|
||||
createChatColorPicker: typeof createChatColorPicker;
|
||||
createConversationDetails: typeof createConversationDetails;
|
||||
createForwardMessageModal: typeof createForwardMessageModal;
|
||||
createGroupLinkManagement: typeof createGroupLinkManagement;
|
||||
createGroupV1MigrationModal: typeof createGroupV1MigrationModal;
|
||||
createGroupV2JoinModal: typeof createGroupV2JoinModal;
|
||||
createGroupV2Permissions: typeof createGroupV2Permissions;
|
||||
createLeftPane: typeof createLeftPane;
|
||||
createMessageDetail: typeof createMessageDetail;
|
||||
createConversationNotificationsSettings: typeof createConversationNotificationsSettings;
|
||||
createPendingInvites: typeof createPendingInvites;
|
||||
createSafetyNumberViewer: typeof createSafetyNumberViewer;
|
||||
createShortcutGuideModal: typeof createShortcutGuideModal;
|
||||
createStickerManager: typeof createStickerManager;
|
||||
createStickerPreviewModal: typeof createStickerPreviewModal;
|
||||
};
|
||||
Ducks: {
|
||||
app: typeof appDuck;
|
||||
calling: typeof callingDuck;
|
||||
conversations: typeof conversationsDuck;
|
||||
emojis: typeof emojisDuck;
|
||||
expiration: typeof expirationDuck;
|
||||
items: typeof itemsDuck;
|
||||
linkPreviews: typeof linkPreviewsDuck;
|
||||
network: typeof networkDuck;
|
||||
updates: typeof updatesDuck;
|
||||
user: typeof userDuck;
|
||||
search: typeof searchDuck;
|
||||
stickers: typeof stickersDuck;
|
||||
};
|
||||
Selectors: {
|
||||
conversations: typeof conversationsSelectors;
|
||||
search: typeof searchSelectors;
|
||||
};
|
||||
};
|
||||
conversationControllerStart: WhatIsThis;
|
||||
Emojis: {
|
||||
getInitialState: () => WhatIsThis;
|
||||
load: () => void;
|
||||
};
|
||||
challengeHandler: ChallengeHandler;
|
||||
};
|
||||
Signal: SignalCoreType;
|
||||
|
||||
ConversationController: ConversationController;
|
||||
Events: IPCEventsType;
|
||||
|
@ -445,6 +369,14 @@ declare global {
|
|||
|
||||
// Context Isolation
|
||||
SignalContext: SignalContextType;
|
||||
|
||||
// Test only
|
||||
assert: typeof assert;
|
||||
// Used in test/index.html, and therefore not type-checked!
|
||||
testUtilities: {
|
||||
onComplete: (data: any) => void;
|
||||
prepareTests: () => void;
|
||||
};
|
||||
}
|
||||
|
||||
// We want to extend `Error`, so we need an interface.
|
||||
|
@ -453,6 +385,9 @@ declare global {
|
|||
originalError?: Event;
|
||||
reason?: any;
|
||||
stackForLog?: string;
|
||||
|
||||
// Used in sticker creator to attach messages to errors
|
||||
errorMessageI18nKey?: string;
|
||||
}
|
||||
|
||||
// We want to extend `Element`'s properties, so we need an interface.
|
||||
|
|
|
@ -127,7 +127,7 @@ export const createWriterForNew = (
|
|||
|
||||
export const createWriterForExisting = (
|
||||
root: string
|
||||
): ((options: { data: Uint8Array; path: string }) => Promise<string>) => {
|
||||
): ((options: { data?: Uint8Array; path?: string }) => Promise<string>) => {
|
||||
if (!isString(root)) {
|
||||
throw new TypeError("'root' must be a path");
|
||||
}
|
||||
|
@ -136,15 +136,15 @@ export const createWriterForExisting = (
|
|||
data: bytes,
|
||||
path: relativePath,
|
||||
}: {
|
||||
data: Uint8Array;
|
||||
path: string;
|
||||
data?: Uint8Array;
|
||||
path?: string;
|
||||
}): Promise<string> => {
|
||||
if (!isString(relativePath)) {
|
||||
throw new TypeError("'relativePath' must be a path");
|
||||
}
|
||||
|
||||
if (!isTypedArray(bytes)) {
|
||||
throw new TypeError("'arrayBuffer' must be an array buffer");
|
||||
if (!bytes) {
|
||||
throw new TypeError("'data' must be a Uint8Array");
|
||||
}
|
||||
|
||||
const buffer = Buffer.from(bytes);
|
||||
|
|
|
@ -3,8 +3,7 @@
|
|||
|
||||
import { ipcRenderer } from 'electron';
|
||||
import type { MenuItemConstructorOptions } from 'electron';
|
||||
import url from 'url';
|
||||
import type { ParsedUrlQuery } from 'querystring';
|
||||
|
||||
import type { MenuOptionsType, MenuActionType } from '../types/menu';
|
||||
import type { IPCEventsValuesType } from '../util/createIPCEvents';
|
||||
import type { LocalizerType } from '../types/Util';
|
||||
|
@ -12,6 +11,8 @@ 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';
|
||||
|
||||
import { Bytes } from '../context/Bytes';
|
||||
import { Crypto } from '../context/Crypto';
|
||||
import { Timers } from '../context/Timers';
|
||||
|
@ -29,7 +30,11 @@ import { waitForSettingsChange } from './waitForSettingsChange';
|
|||
import { createNativeThemeListener } from '../context/createNativeThemeListener';
|
||||
import { isWindows, isLinux, isMacOS } from '../OS';
|
||||
|
||||
const config = url.parse(window.location.toString(), true).query;
|
||||
const params = new URLSearchParams(document.location.search);
|
||||
const configParam = params.get('config');
|
||||
strictAssert(typeof configParam === 'string', 'config is not a string');
|
||||
const config = JSON.parse(configParam);
|
||||
|
||||
const { locale } = config;
|
||||
strictAssert(locale, 'locale could not be parsed from config');
|
||||
strictAssert(typeof locale === 'string', 'locale is not a string');
|
||||
|
@ -62,7 +67,7 @@ export type SignalContextType = {
|
|||
isLinux: typeof isLinux;
|
||||
isMacOS: typeof isMacOS;
|
||||
};
|
||||
config: ParsedUrlQuery;
|
||||
config: RendererConfigType;
|
||||
getAppInstance: () => string | undefined;
|
||||
getEnvironment: () => string;
|
||||
getNodeVersion: () => string;
|
||||
|
@ -94,7 +99,7 @@ export const SignalContext: SignalContextType = {
|
|||
getAppInstance: (): string | undefined =>
|
||||
config.appInstance ? String(config.appInstance) : undefined,
|
||||
getEnvironment,
|
||||
getNodeVersion: (): string => String(config.node_version),
|
||||
getNodeVersion: (): string => String(config.nodeVersion),
|
||||
getVersion: (): string => String(config.version),
|
||||
getPath: (name: 'userData' | 'home'): string => {
|
||||
return String(config[`${name}Path`]);
|
||||
|
|
376
ts/windows/main/phase1-ipc.ts
Normal file
376
ts/windows/main/phase1-ipc.ts
Normal file
|
@ -0,0 +1,376 @@
|
|||
// Copyright 2022 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { ipcRenderer as ipc } from 'electron';
|
||||
import * as semver from 'semver';
|
||||
import { mapValues, noop } from 'lodash';
|
||||
|
||||
import { parseIntWithFallback } from '../../util/parseIntWithFallback';
|
||||
import { UUIDKind } from '../../types/UUID';
|
||||
import { ThemeType } from '../../types/Util';
|
||||
import { getEnvironment, Environment } from '../../environment';
|
||||
import { SignalContext } from '../context';
|
||||
import * as log from '../../logging/log';
|
||||
|
||||
import { strictAssert } from '../../util/assert';
|
||||
|
||||
// It is important to call this as early as possible
|
||||
window.i18n = SignalContext.i18n;
|
||||
|
||||
// We are comfortable doing this because we verified the type on the other side!
|
||||
const { config } = window.SignalContext;
|
||||
|
||||
// Flags for testing
|
||||
window.GV2_ENABLE_SINGLE_CHANGE_PROCESSING = true;
|
||||
window.GV2_ENABLE_CHANGE_PROCESSING = true;
|
||||
window.GV2_ENABLE_STATE_PROCESSING = true;
|
||||
window.GV2_ENABLE_PRE_JOIN_FETCH = true;
|
||||
|
||||
window.GV2_MIGRATION_DISABLE_ADD = false;
|
||||
window.GV2_MIGRATION_DISABLE_INVITE = false;
|
||||
|
||||
window.RETRY_DELAY = false;
|
||||
|
||||
window.platform = process.platform;
|
||||
window.getTitle = () => title;
|
||||
window.getLocale = () => config.locale;
|
||||
window.getEnvironment = getEnvironment;
|
||||
window.getAppInstance = () => config.appInstance;
|
||||
window.getVersion = () => config.version;
|
||||
window.getBuildCreation = () => parseIntWithFallback(config.buildCreation, 0);
|
||||
window.getExpiration = () => {
|
||||
const sixtyDays = 60 * 86400 * 1000;
|
||||
const remoteBuildExpiration = window.storage.get('remoteBuildExpiration');
|
||||
const { buildExpiration } = config;
|
||||
|
||||
const localBuildExpiration = window.Events.getAutoDownloadUpdate()
|
||||
? buildExpiration
|
||||
: buildExpiration - sixtyDays;
|
||||
|
||||
if (remoteBuildExpiration) {
|
||||
return remoteBuildExpiration < localBuildExpiration
|
||||
? remoteBuildExpiration
|
||||
: localBuildExpiration;
|
||||
}
|
||||
return localBuildExpiration;
|
||||
};
|
||||
window.Accessibility = {
|
||||
reducedMotionSetting: Boolean(config.reducedMotionSetting),
|
||||
};
|
||||
window.getHostName = () => config.hostname;
|
||||
window.getServerTrustRoot = () => config.serverTrustRoot;
|
||||
window.getServerPublicParams = () => config.serverPublicParams;
|
||||
window.getSfuUrl = () => config.sfuUrl;
|
||||
window.isBehindProxy = () => Boolean(config.proxyUrl);
|
||||
|
||||
let title = config.name;
|
||||
if (getEnvironment() !== Environment.Production) {
|
||||
title += ` - ${getEnvironment()}`;
|
||||
}
|
||||
if (config.appInstance) {
|
||||
title += ` - ${config.appInstance}`;
|
||||
}
|
||||
|
||||
if (config.theme === 'light') {
|
||||
window.initialTheme = ThemeType.light;
|
||||
} else if (config.theme === 'dark') {
|
||||
window.initialTheme = ThemeType.dark;
|
||||
}
|
||||
|
||||
window.getAutoLaunch = () => {
|
||||
return ipc.invoke('get-auto-launch');
|
||||
};
|
||||
window.setAutoLaunch = value => {
|
||||
return ipc.invoke('set-auto-launch', value);
|
||||
};
|
||||
|
||||
window.isBeforeVersion = (toCheck, baseVersion) => {
|
||||
try {
|
||||
return semver.lt(toCheck, baseVersion);
|
||||
} catch (error) {
|
||||
log.error(
|
||||
`isBeforeVersion error: toCheck: ${toCheck}, baseVersion: ${baseVersion}`,
|
||||
error && error.stack ? error.stack : error
|
||||
);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
window.isAfterVersion = (toCheck, baseVersion) => {
|
||||
try {
|
||||
return semver.gt(toCheck, baseVersion);
|
||||
} catch (error) {
|
||||
log.error(
|
||||
`isBeforeVersion error: toCheck: ${toCheck}, baseVersion: ${baseVersion}`,
|
||||
error && error.stack ? error.stack : error
|
||||
);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
window.setBadgeCount = count => ipc.send('set-badge-count', count);
|
||||
|
||||
window.logAuthenticatedConnect = () => {
|
||||
if (window.preloadConnectTime === 0) {
|
||||
window.preloadConnectTime = Date.now();
|
||||
}
|
||||
};
|
||||
|
||||
window.logAppLoadedEvent = ({ processedCount }) =>
|
||||
ipc.send('signal-app-loaded', {
|
||||
preloadTime: window.preloadEndTime - window.preloadStartTime,
|
||||
connectTime: window.preloadConnectTime - window.preloadEndTime,
|
||||
processedCount,
|
||||
});
|
||||
|
||||
// We never do these in our code, so we'll prevent it everywhere
|
||||
window.open = () => null;
|
||||
|
||||
// Playwright uses `eval` for `.evaluate()` API
|
||||
if (!config.enableCI && config.environment !== 'test') {
|
||||
// eslint-disable-next-line no-eval, no-multi-assign
|
||||
window.eval = global.eval = () => null;
|
||||
}
|
||||
|
||||
window.drawAttention = () => {
|
||||
log.info('draw attention');
|
||||
ipc.send('draw-attention');
|
||||
};
|
||||
window.showWindow = () => {
|
||||
log.info('show window');
|
||||
ipc.send('show-window');
|
||||
};
|
||||
|
||||
window.titleBarDoubleClick = () => {
|
||||
ipc.send('title-bar-double-click');
|
||||
};
|
||||
|
||||
window.setAutoHideMenuBar = autoHide =>
|
||||
ipc.send('set-auto-hide-menu-bar', autoHide);
|
||||
|
||||
window.setMenuBarVisibility = visibility =>
|
||||
ipc.send('set-menu-bar-visibility', visibility);
|
||||
|
||||
window.updateSystemTraySetting = (
|
||||
systemTraySetting /* : Readonly<SystemTraySetting> */
|
||||
) => {
|
||||
ipc.send('update-system-tray-setting', systemTraySetting);
|
||||
};
|
||||
|
||||
window.restart = () => {
|
||||
log.info('restart');
|
||||
ipc.send('restart');
|
||||
};
|
||||
window.shutdown = () => {
|
||||
log.info('shutdown');
|
||||
ipc.send('shutdown');
|
||||
};
|
||||
window.showDebugLog = () => {
|
||||
log.info('showDebugLog');
|
||||
ipc.send('show-debug-log');
|
||||
};
|
||||
|
||||
window.closeAbout = () => ipc.send('close-about');
|
||||
window.readyForUpdates = () => ipc.send('ready-for-updates');
|
||||
|
||||
window.updateTrayIcon = unreadCount =>
|
||||
ipc.send('update-tray-icon', unreadCount);
|
||||
|
||||
ipc.on('additional-log-data-request', async event => {
|
||||
const ourConversation = window.ConversationController.getOurConversation();
|
||||
const ourCapabilities = ourConversation
|
||||
? ourConversation.get('capabilities')
|
||||
: undefined;
|
||||
|
||||
const remoteConfig = window.storage.get('remoteConfig') || {};
|
||||
|
||||
let statistics;
|
||||
try {
|
||||
statistics = await window.Signal.Data.getStatisticsForLogging();
|
||||
} catch (error) {
|
||||
statistics = {};
|
||||
}
|
||||
|
||||
const ourUuid = window.textsecure.storage.user.getUuid();
|
||||
const ourPni = window.textsecure.storage.user.getUuid(UUIDKind.PNI);
|
||||
|
||||
event.sender.send('additional-log-data-response', {
|
||||
capabilities: ourCapabilities || {},
|
||||
remoteConfig: mapValues(remoteConfig, ({ value, enabled }) => {
|
||||
const enableString = enabled ? 'enabled' : 'disabled';
|
||||
const valueString = value && value !== 'TRUE' ? ` ${value}` : '';
|
||||
return `${enableString}${valueString}`;
|
||||
}),
|
||||
statistics,
|
||||
user: {
|
||||
deviceId: window.textsecure.storage.user.getDeviceId(),
|
||||
e164: window.textsecure.storage.user.getNumber(),
|
||||
uuid: ourUuid && ourUuid.toString(),
|
||||
pni: ourPni && ourPni.toString(),
|
||||
conversationId: ourConversation && ourConversation.id,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
ipc.on('set-up-as-new-device', () => {
|
||||
window.Whisper.events.trigger('setupAsNewDevice');
|
||||
});
|
||||
|
||||
ipc.on('set-up-as-standalone', () => {
|
||||
window.Whisper.events.trigger('setupAsStandalone');
|
||||
});
|
||||
|
||||
ipc.on('challenge:response', (_event, response) => {
|
||||
window.Whisper.events.trigger('challengeResponse', response);
|
||||
});
|
||||
|
||||
ipc.on('power-channel:suspend', () => {
|
||||
window.Whisper.events.trigger('powerMonitorSuspend');
|
||||
});
|
||||
|
||||
ipc.on('power-channel:resume', () => {
|
||||
window.Whisper.events.trigger('powerMonitorResume');
|
||||
});
|
||||
|
||||
ipc.on('power-channel:lock-screen', () => {
|
||||
window.Whisper.events.trigger('powerMonitorLockScreen');
|
||||
});
|
||||
|
||||
ipc.on('window:set-window-stats', (_event, stats) => {
|
||||
if (!window.Whisper.events) {
|
||||
return;
|
||||
}
|
||||
window.Whisper.events.trigger('setWindowStats', stats);
|
||||
});
|
||||
|
||||
ipc.on('window:set-menu-options', (_event, options) => {
|
||||
if (!window.Whisper.events) {
|
||||
return;
|
||||
}
|
||||
window.Whisper.events.trigger('setMenuOptions', options);
|
||||
});
|
||||
|
||||
window.sendChallengeRequest = request => ipc.send('challenge:request', request);
|
||||
|
||||
{
|
||||
let isFullScreen = Boolean(config.isMainWindowFullScreen);
|
||||
|
||||
window.isFullScreen = () => isFullScreen;
|
||||
// This is later overwritten.
|
||||
window.onFullScreenChange = noop;
|
||||
|
||||
ipc.on('full-screen-change', (_event, isFull) => {
|
||||
isFullScreen = Boolean(isFull);
|
||||
window.onFullScreenChange(isFullScreen);
|
||||
});
|
||||
}
|
||||
|
||||
// Settings-related events
|
||||
|
||||
window.showSettings = () => ipc.send('show-settings');
|
||||
window.showPermissionsPopup = (forCalling, forCamera) =>
|
||||
ipc.invoke('show-permissions-popup', forCalling, forCamera);
|
||||
|
||||
ipc.on('show-keyboard-shortcuts', () => {
|
||||
window.Events.showKeyboardShortcuts();
|
||||
});
|
||||
ipc.on('add-dark-overlay', () => {
|
||||
window.Events.addDarkOverlay();
|
||||
});
|
||||
ipc.on('remove-dark-overlay', () => {
|
||||
window.Events.removeDarkOverlay();
|
||||
});
|
||||
|
||||
window.getBuiltInImages = () =>
|
||||
new Promise((resolve, reject) => {
|
||||
ipc.once('get-success-built-in-images', (_event, error, value) => {
|
||||
if (error) {
|
||||
return reject(new Error(error));
|
||||
}
|
||||
|
||||
return resolve(value);
|
||||
});
|
||||
ipc.send('get-built-in-images');
|
||||
});
|
||||
|
||||
ipc.on('delete-all-data', async () => {
|
||||
const { deleteAllData } = window.Events;
|
||||
if (!deleteAllData) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await deleteAllData();
|
||||
} catch (error) {
|
||||
log.error('delete-all-data: error', error && error.stack);
|
||||
}
|
||||
});
|
||||
|
||||
ipc.on('show-sticker-pack', (_event, info) => {
|
||||
const { packId, packKey } = info;
|
||||
const { showStickerPack } = window.Events;
|
||||
if (showStickerPack) {
|
||||
showStickerPack(packId, packKey);
|
||||
}
|
||||
});
|
||||
|
||||
ipc.on('show-group-via-link', (_event, info) => {
|
||||
const { hash } = info;
|
||||
const { showGroupViaLink } = window.Events;
|
||||
if (showGroupViaLink) {
|
||||
showGroupViaLink(hash);
|
||||
}
|
||||
});
|
||||
|
||||
ipc.on('show-conversation-via-signal.me', (_event, info) => {
|
||||
const { hash } = info;
|
||||
strictAssert(typeof hash === 'string', 'Got an invalid hash over IPC');
|
||||
|
||||
const { showConversationViaSignalDotMe } = window.Events;
|
||||
if (showConversationViaSignalDotMe) {
|
||||
showConversationViaSignalDotMe(hash);
|
||||
}
|
||||
});
|
||||
|
||||
ipc.on('unknown-sgnl-link', () => {
|
||||
const { unknownSignalLink } = window.Events;
|
||||
if (unknownSignalLink) {
|
||||
unknownSignalLink();
|
||||
}
|
||||
});
|
||||
|
||||
ipc.on('install-sticker-pack', (_event, info) => {
|
||||
const { packId, packKey } = info;
|
||||
const { installStickerPack } = window.Events;
|
||||
if (installStickerPack) {
|
||||
installStickerPack(packId, packKey);
|
||||
}
|
||||
});
|
||||
|
||||
ipc.on('get-ready-for-shutdown', async () => {
|
||||
const { shutdown } = window.Events || {};
|
||||
if (!shutdown) {
|
||||
log.error('preload shutdown handler: shutdown method not found');
|
||||
ipc.send('now-ready-for-shutdown');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await shutdown();
|
||||
ipc.send('now-ready-for-shutdown');
|
||||
} catch (error) {
|
||||
ipc.send(
|
||||
'now-ready-for-shutdown',
|
||||
error && error.stack ? error.stack : error
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
ipc.on('show-release-notes', () => {
|
||||
const { showReleaseNotes } = window.Events;
|
||||
if (showReleaseNotes) {
|
||||
showReleaseNotes();
|
||||
}
|
||||
});
|
||||
|
||||
window.addSetupMenuItems = () => ipc.send('add-setup-menu-items');
|
||||
window.removeSetupMenuItems = () => ipc.send('remove-setup-menu-items');
|
91
ts/windows/main/phase2-dependencies.ts
Normal file
91
ts/windows/main/phase2-dependencies.ts
Normal file
|
@ -0,0 +1,91 @@
|
|||
// Copyright 2022 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { ipcRenderer as ipc } from 'electron';
|
||||
import Backbone from 'backbone';
|
||||
import { PhoneNumberUtil, PhoneNumberFormat } from 'google-libphonenumber';
|
||||
import * as React from 'react';
|
||||
import * as ReactDOM from 'react-dom';
|
||||
import * as moment from 'moment';
|
||||
import 'moment/min/locales.min';
|
||||
import PQueue from 'p-queue';
|
||||
|
||||
import { textsecure } from '../../textsecure';
|
||||
import { imageToBlurHash } from '../../util/imageToBlurHash';
|
||||
import { ActiveWindowService } from '../../services/ActiveWindowService';
|
||||
import * as Attachments from '../attachments';
|
||||
import { setup } from '../../signal';
|
||||
import { addSensitivePath } from '../../util/privacy';
|
||||
import * as log from '../../logging/log';
|
||||
import { SignalContext } from '../context';
|
||||
|
||||
window.nodeSetImmediate = setImmediate;
|
||||
window.Backbone = Backbone;
|
||||
window.textsecure = textsecure;
|
||||
|
||||
const { config } = window.SignalContext;
|
||||
|
||||
window.WebAPI = window.textsecure.WebAPI.initialize({
|
||||
url: config.serverUrl,
|
||||
storageUrl: config.storageUrl,
|
||||
updatesUrl: config.updatesUrl,
|
||||
directoryVersion: config.directoryVersion,
|
||||
directoryUrl: config.directoryUrl,
|
||||
directoryEnclaveId: config.directoryEnclaveId,
|
||||
directoryTrustAnchor: config.directoryTrustAnchor,
|
||||
directoryV2Url: config.directoryV2Url,
|
||||
directoryV2PublicKey: config.directoryV2PublicKey,
|
||||
directoryV2CodeHashes: config.directoryV2CodeHashes,
|
||||
cdnUrlObject: {
|
||||
0: config.cdnUrl0,
|
||||
2: config.cdnUrl2,
|
||||
},
|
||||
certificateAuthority: config.certificateAuthority,
|
||||
contentProxyUrl: config.contentProxyUrl,
|
||||
proxyUrl: config.proxyUrl,
|
||||
version: config.version,
|
||||
});
|
||||
|
||||
window.imageToBlurHash = imageToBlurHash;
|
||||
window.libphonenumberInstance = PhoneNumberUtil.getInstance();
|
||||
window.libphonenumberFormat = PhoneNumberFormat;
|
||||
|
||||
const activeWindowService = new ActiveWindowService();
|
||||
activeWindowService.initialize(window.document, ipc);
|
||||
window.isActive = activeWindowService.isActive.bind(activeWindowService);
|
||||
window.registerForActive =
|
||||
activeWindowService.registerForActive.bind(activeWindowService);
|
||||
window.unregisterForActive =
|
||||
activeWindowService.unregisterForActive.bind(activeWindowService);
|
||||
|
||||
window.React = React;
|
||||
window.ReactDOM = ReactDOM;
|
||||
window.PQueue = PQueue;
|
||||
|
||||
const { locale } = config;
|
||||
moment.updateLocale(locale, {
|
||||
relativeTime: {
|
||||
s: window.i18n('timestamp_s'),
|
||||
m: window.i18n('timestamp_m'),
|
||||
h: window.i18n('timestamp_h'),
|
||||
},
|
||||
});
|
||||
moment.locale(locale);
|
||||
|
||||
const userDataPath = SignalContext.getPath('userData');
|
||||
window.baseAttachmentsPath = Attachments.getPath(userDataPath);
|
||||
window.baseStickersPath = Attachments.getStickersPath(userDataPath);
|
||||
window.baseTempPath = Attachments.getTempPath(userDataPath);
|
||||
window.baseDraftPath = Attachments.getDraftPath(userDataPath);
|
||||
|
||||
addSensitivePath(window.baseAttachmentsPath);
|
||||
if (config.crashDumpsPath) {
|
||||
addSensitivePath(config.crashDumpsPath);
|
||||
}
|
||||
|
||||
window.Signal = setup({
|
||||
Attachments,
|
||||
getRegionCode: () => window.storage.get('regionCode'),
|
||||
logger: log,
|
||||
userDataPath,
|
||||
});
|
13
ts/windows/main/phase3-post-signal.ts
Normal file
13
ts/windows/main/phase3-post-signal.ts
Normal file
|
@ -0,0 +1,13 @@
|
|||
// Copyright 2022 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
// These all need access to window.Signal:
|
||||
|
||||
import '../../models/messages';
|
||||
import '../../models/conversations';
|
||||
|
||||
import '../../backbone/views/whisper_view';
|
||||
import '../../views/conversation_view';
|
||||
import '../../views/inbox_view';
|
||||
import '../../SignalProtocolStore';
|
||||
import '../../background';
|
18
ts/windows/main/phase4-test.ts
Normal file
18
ts/windows/main/phase4-test.ts
Normal file
|
@ -0,0 +1,18 @@
|
|||
// Copyright 2017-2022 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
/* eslint-disable no-console */
|
||||
/* eslint-disable global-require */
|
||||
|
||||
const { config } = window.SignalContext;
|
||||
|
||||
if (config.environment === 'test') {
|
||||
console.log('Importing test infrastructure...');
|
||||
require('./preload_test');
|
||||
}
|
||||
if (config.enableCI) {
|
||||
console.log('Importing CI infrastructure...');
|
||||
const { CI } = require('../../CI');
|
||||
window.CI = new CI(window.getTitle());
|
||||
}
|
24
ts/windows/main/preload.ts
Normal file
24
ts/windows/main/preload.ts
Normal file
|
@ -0,0 +1,24 @@
|
|||
// Copyright 2017-2022 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
/* eslint-disable global-require */
|
||||
|
||||
import * as log from '../../logging/log';
|
||||
|
||||
window.preloadStartTime = Date.now();
|
||||
|
||||
try {
|
||||
require('./start');
|
||||
} catch (error) {
|
||||
/* eslint-disable no-console */
|
||||
if (console._log) {
|
||||
console._log('preload error!', error.stack);
|
||||
}
|
||||
console.log('preload error!', error.stack);
|
||||
/* eslint-enable no-console */
|
||||
|
||||
throw error;
|
||||
}
|
||||
|
||||
window.preloadEndTime = Date.now();
|
||||
log.info('preload complete');
|
28
ts/windows/main/preload_test.ts
Normal file
28
ts/windows/main/preload_test.ts
Normal file
|
@ -0,0 +1,28 @@
|
|||
// Copyright 2021-2022 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
/* eslint-disable no-console */
|
||||
|
||||
import { ipcRenderer as ipc } from 'electron';
|
||||
import { sync } from 'fast-glob';
|
||||
|
||||
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||
import { assert } from 'chai';
|
||||
|
||||
window.assert = assert;
|
||||
|
||||
// This is a hack to let us run TypeScript tests in the renderer process. See the
|
||||
// code in `test/index.html`.
|
||||
|
||||
window.testUtilities = {
|
||||
onComplete(info) {
|
||||
return ipc.invoke('ci:test-electron:done', info);
|
||||
},
|
||||
prepareTests() {
|
||||
console.log('Preparing tests...');
|
||||
sync('../../test-{both,electron}/**/*_test.js', {
|
||||
absolute: true,
|
||||
cwd: __dirname,
|
||||
}).forEach(require);
|
||||
},
|
||||
};
|
29
ts/windows/main/start.ts
Normal file
29
ts/windows/main/start.ts
Normal file
|
@ -0,0 +1,29 @@
|
|||
// Copyright 2017-2022 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import * as log from '../../logging/log';
|
||||
|
||||
import './phase1-ipc';
|
||||
import '../preload';
|
||||
import './phase2-dependencies';
|
||||
import './phase3-post-signal';
|
||||
import './phase4-test';
|
||||
|
||||
window.addEventListener('contextmenu', e => {
|
||||
const node = e.target as Element | null;
|
||||
|
||||
const isEditable = Boolean(
|
||||
node?.closest('textarea, input, [contenteditable="true"]')
|
||||
);
|
||||
const isLink = Boolean(node?.closest('a'));
|
||||
const isImage = Boolean(node?.closest('.Lightbox img'));
|
||||
const hasSelection = Boolean(window.getSelection()?.toString());
|
||||
|
||||
if (!isEditable && !hasSelection && !isLink && !isImage) {
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
|
||||
if (window.SignalContext.config.proxyUrl) {
|
||||
log.info('Using provided proxy url');
|
||||
}
|
|
@ -25,8 +25,7 @@ contextBridge.exposeInMainWorld(
|
|||
contextBridge.exposeInMainWorld('SignalContext', {
|
||||
...SignalContext,
|
||||
renderWindow: () => {
|
||||
const forCalling = SignalContext.config.forCalling === 'true';
|
||||
const forCamera = SignalContext.config.forCamera === 'true';
|
||||
const { forCalling, forCamera } = SignalContext.config;
|
||||
|
||||
let message;
|
||||
if (forCalling) {
|
||||
|
|
|
@ -253,7 +253,7 @@ const renderPreferences = async () => {
|
|||
editCustomColor: ipcEditCustomColor,
|
||||
getConversationsWithCustomColor: ipcGetConversationsWithCustomColor,
|
||||
initialSpellCheckSetting:
|
||||
SignalContext.config.appStartInitialSpellcheckSetting === 'true',
|
||||
SignalContext.config.appStartInitialSpellcheckSetting,
|
||||
makeSyncRequest: ipcMakeSyncRequest,
|
||||
removeCustomColor: ipcRemoveCustomColor,
|
||||
removeCustomColorOnConversations: ipcRemoveCustomColorOnConversations,
|
||||
|
|
|
@ -59,5 +59,5 @@
|
|||
// "experimentalDecorators": true, // Enables experimental support for ES7 decorators.
|
||||
// "emitDecoratorMetadata": true, // Enables experimental support for emitting type metadata for decorators.
|
||||
},
|
||||
"include": ["ts/**/*", "app/*", "package.json"]
|
||||
"include": ["ts/**/*", "app/**/*", "sticker-creator/**/*", "package.json"]
|
||||
}
|
||||
|
|
34
yarn.lock
34
yarn.lock
|
@ -4465,12 +4465,10 @@ atomic-sleep@^1.0.0:
|
|||
resolved "https://registry.yarnpkg.com/atomic-sleep/-/atomic-sleep-1.0.0.tgz#eb85b77a601fc932cfe432c5acd364a9e2c9075b"
|
||||
integrity sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==
|
||||
|
||||
attr-accept@^1.1.3:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/attr-accept/-/attr-accept-1.1.3.tgz#48230c79f93790ef2775fcec4f0db0f5db41ca52"
|
||||
integrity sha512-iT40nudw8zmCweivz6j58g+RT33I4KbaIvRUhjNmDwO2WmsQUxFEZZYZ5w3vXe5x5MX9D7mfvA/XaLOZYFR9EQ==
|
||||
dependencies:
|
||||
core-js "^2.5.0"
|
||||
attr-accept@^2.0.0:
|
||||
version "2.2.2"
|
||||
resolved "https://registry.yarnpkg.com/attr-accept/-/attr-accept-2.2.2.tgz#646613809660110749e92f2c10833b70968d929b"
|
||||
integrity sha512-7prDjvt9HmqiZ0cl5CRjtS84sEyhsHP2coDkaZKRKVfCDo9s7iw7ChVmar78Gu9pC4SoR/28wFu/G5JJhTnqEg==
|
||||
|
||||
autoprefixer@^9.8.6:
|
||||
version "9.8.8"
|
||||
|
@ -5979,7 +5977,7 @@ core-js-pure@^3.8.1:
|
|||
resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.22.8.tgz#f2157793b58719196ccf9673cc14f3683adc0957"
|
||||
integrity sha512-bOxbZIy9S5n4OVH63XaLVXZ49QKicjowDx/UELyJ68vxfCRpYsbyh/WNZNfEfAk+ekA8vSjt+gCDpvh672bc3w==
|
||||
|
||||
core-js@2.6.9, core-js@^2.4.0, core-js@^2.5.0:
|
||||
core-js@2.6.9, core-js@^2.4.0:
|
||||
version "2.6.9"
|
||||
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.9.tgz#6b4b214620c834152e179323727fc19741b084f2"
|
||||
integrity sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A==
|
||||
|
@ -8027,12 +8025,12 @@ file-loader@^6.2.0:
|
|||
loader-utils "^2.0.0"
|
||||
schema-utils "^3.0.0"
|
||||
|
||||
file-selector@^0.1.11:
|
||||
version "0.1.12"
|
||||
resolved "https://registry.yarnpkg.com/file-selector/-/file-selector-0.1.12.tgz#fe726547be219a787a9dcc640575a04a032b1fd0"
|
||||
integrity sha512-Kx7RTzxyQipHuiqyZGf+Nz4vY9R1XGxuQl/hLoJwq+J4avk/9wxxgZyHKtbyIPJmbD4A66DWGYfyykWNpcYutQ==
|
||||
file-selector@^0.1.12:
|
||||
version "0.1.19"
|
||||
resolved "https://registry.yarnpkg.com/file-selector/-/file-selector-0.1.19.tgz#8ecc9d069a6f544f2e4a096b64a8052e70ec8abf"
|
||||
integrity sha512-kCWw3+Aai8Uox+5tHCNgMFaUdgidxvMnLWO6fM5sZ0hA2wlHP5/DHGF0ECe84BiB95qdJbKNEJhWKVDvMN+JDQ==
|
||||
dependencies:
|
||||
tslib "^1.9.0"
|
||||
tslib "^2.0.1"
|
||||
|
||||
file-system-cache@^1.0.5:
|
||||
version "1.0.5"
|
||||
|
@ -13337,13 +13335,13 @@ react-dom@17.0.2:
|
|||
object-assign "^4.1.1"
|
||||
scheduler "^0.20.2"
|
||||
|
||||
react-dropzone@10.1.7:
|
||||
version "10.1.7"
|
||||
resolved "https://registry.yarnpkg.com/react-dropzone/-/react-dropzone-10.1.7.tgz#faba6b460a41a27a2d642316602924fe0ec2139c"
|
||||
integrity sha512-PT4DHJCQrY/2vVljupveqnlwzVRQGK7U6mhoO0ox6kSJV0EK6W1ZmZpRyHMA1S6/KUOzN+1pASUMSqV/4uAIXg==
|
||||
react-dropzone@10.2.2:
|
||||
version "10.2.2"
|
||||
resolved "https://registry.yarnpkg.com/react-dropzone/-/react-dropzone-10.2.2.tgz#67b4db7459589a42c3b891a82eaf9ade7650b815"
|
||||
integrity sha512-U5EKckXVt6IrEyhMMsgmHQiWTGLudhajPPG77KFSvgsMqNEHSyGpqWvOMc5+DhEah/vH4E1n+J5weBNLd5VtyA==
|
||||
dependencies:
|
||||
attr-accept "^1.1.3"
|
||||
file-selector "^0.1.11"
|
||||
attr-accept "^2.0.0"
|
||||
file-selector "^0.1.12"
|
||||
prop-types "^15.7.2"
|
||||
|
||||
react-element-to-jsx-string@^14.3.4:
|
||||
|
|
Loading…
Reference in a new issue