Convert js/modules/types/message to Typescript
This commit is contained in:
parent
9975758fde
commit
924c271b13
18 changed files with 748 additions and 398 deletions
|
@ -110,7 +110,7 @@ const searchSelectors = require('../../ts/state/selectors/search');
|
||||||
// Types
|
// Types
|
||||||
const AttachmentType = require('../../ts/types/Attachment');
|
const AttachmentType = require('../../ts/types/Attachment');
|
||||||
const VisualAttachment = require('../../ts/types/VisualAttachment');
|
const VisualAttachment = require('../../ts/types/VisualAttachment');
|
||||||
const MessageType = require('./types/message');
|
const MessageType = require('../../ts/types/Message2');
|
||||||
const { UUID } = require('../../ts/types/UUID');
|
const { UUID } = require('../../ts/types/UUID');
|
||||||
const { Address } = require('../../ts/types/Address');
|
const { Address } = require('../../ts/types/Address');
|
||||||
const { QualifiedAddress } = require('../../ts/types/QualifiedAddress');
|
const { QualifiedAddress } = require('../../ts/types/QualifiedAddress');
|
||||||
|
@ -281,6 +281,8 @@ function initializeMigrations({
|
||||||
makeVideoScreenshot,
|
makeVideoScreenshot,
|
||||||
logger,
|
logger,
|
||||||
maxVersion,
|
maxVersion,
|
||||||
|
getAbsoluteStickerPath,
|
||||||
|
writeNewStickerData,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
writeMessageAttachments: MessageType.createAttachmentDataWriter({
|
writeMessageAttachments: MessageType.createAttachmentDataWriter({
|
||||||
|
|
4
js/modules/types/message.d.ts
vendored
4
js/modules/types/message.d.ts
vendored
|
@ -1,4 +0,0 @@
|
||||||
// Copyright 2020 Signal Messenger, LLC
|
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
|
||||||
|
|
||||||
export const CURRENT_SCHEMA_VERSION: number;
|
|
|
@ -62,7 +62,7 @@ import type {
|
||||||
GroupLogResponseType,
|
GroupLogResponseType,
|
||||||
} from './textsecure/WebAPI';
|
} from './textsecure/WebAPI';
|
||||||
import type MessageSender from './textsecure/SendMessage';
|
import type MessageSender from './textsecure/SendMessage';
|
||||||
import { CURRENT_SCHEMA_VERSION as MAX_MESSAGE_SCHEMA } from '../js/modules/types/message';
|
import { CURRENT_SCHEMA_VERSION as MAX_MESSAGE_SCHEMA } from './types/Message2';
|
||||||
import type { ConversationModel } from './models/conversations';
|
import type { ConversationModel } from './models/conversations';
|
||||||
import { getGroupSizeHardLimit } from './groups/limits';
|
import { getGroupSizeHardLimit } from './groups/limits';
|
||||||
import {
|
import {
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
import { isFunction, isNumber } from 'lodash';
|
import { isFunction, isNumber } from 'lodash';
|
||||||
import * as Message from '../../js/modules/types/message';
|
import { CURRENT_SCHEMA_VERSION } from '../types/Message2';
|
||||||
import type { MessageAttributesType } from '../model-types.d';
|
import type { MessageAttributesType } from '../model-types.d';
|
||||||
import type { UUIDStringType } from '../types/UUID';
|
import type { UUIDStringType } from '../types/UUID';
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ export async function migrateMessageData({
|
||||||
upgradeMessageSchema,
|
upgradeMessageSchema,
|
||||||
getMessagesNeedingUpgrade,
|
getMessagesNeedingUpgrade,
|
||||||
saveMessage,
|
saveMessage,
|
||||||
maxVersion = Message.CURRENT_SCHEMA_VERSION,
|
maxVersion = CURRENT_SCHEMA_VERSION,
|
||||||
}: Readonly<{
|
}: Readonly<{
|
||||||
numMessagesPerBatch: number;
|
numMessagesPerBatch: number;
|
||||||
upgradeMessageSchema: (
|
upgradeMessageSchema: (
|
||||||
|
|
17
ts/model-types.d.ts
vendored
17
ts/model-types.d.ts
vendored
|
@ -61,8 +61,14 @@ export type GroupMigrationType = {
|
||||||
droppedMemberIds: Array<string>;
|
droppedMemberIds: Array<string>;
|
||||||
invitedMembers: Array<GroupV2PendingMemberType>;
|
invitedMembers: Array<GroupV2PendingMemberType>;
|
||||||
};
|
};
|
||||||
|
export type PreviewType = {
|
||||||
|
domain: string;
|
||||||
|
image: AttachmentType;
|
||||||
|
title: string;
|
||||||
|
url: string;
|
||||||
|
};
|
||||||
|
|
||||||
export type PreviewMessageType = Array<WhatIsThis>;
|
export type PreviewMessageType = Array<PreviewType>;
|
||||||
|
|
||||||
export type QuotedMessageType = {
|
export type QuotedMessageType = {
|
||||||
attachments: Array<typeof window.WhatIsThis>;
|
attachments: Array<typeof window.WhatIsThis>;
|
||||||
|
@ -90,6 +96,9 @@ export type StickerMessageType = {
|
||||||
stickerId: number;
|
stickerId: number;
|
||||||
packKey: string;
|
packKey: string;
|
||||||
data?: AttachmentType;
|
data?: AttachmentType;
|
||||||
|
path?: string;
|
||||||
|
width?: number;
|
||||||
|
height?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type RetryOptions = Readonly<{
|
export type RetryOptions = Readonly<{
|
||||||
|
@ -129,9 +138,9 @@ export type MessageAttributesType = {
|
||||||
expireTimer?: number;
|
expireTimer?: number;
|
||||||
groupMigration?: GroupMigrationType;
|
groupMigration?: GroupMigrationType;
|
||||||
group_update?: GroupV1Update;
|
group_update?: GroupV1Update;
|
||||||
hasAttachments?: boolean;
|
hasAttachments?: boolean | 0 | 1;
|
||||||
hasFileAttachments?: boolean;
|
hasFileAttachments?: boolean | 0 | 1;
|
||||||
hasVisualMediaAttachments?: boolean;
|
hasVisualMediaAttachments?: boolean | 0 | 1;
|
||||||
isErased?: boolean;
|
isErased?: boolean;
|
||||||
isTapToViewInvalid?: boolean;
|
isTapToViewInvalid?: boolean;
|
||||||
isViewOnce?: boolean;
|
isViewOnce?: boolean;
|
||||||
|
|
|
@ -30,7 +30,7 @@ import { deleteExternalFiles } from '../types/Conversation';
|
||||||
import { expiringMessagesDeletionService } from '../services/expiringMessagesDeletion';
|
import { expiringMessagesDeletionService } from '../services/expiringMessagesDeletion';
|
||||||
import { tapToViewMessagesDeletionService } from '../services/tapToViewMessagesDeletionService';
|
import { tapToViewMessagesDeletionService } from '../services/tapToViewMessagesDeletionService';
|
||||||
import * as Bytes from '../Bytes';
|
import * as Bytes from '../Bytes';
|
||||||
import { CURRENT_SCHEMA_VERSION } from '../../js/modules/types/message';
|
import { CURRENT_SCHEMA_VERSION } from '../types/Message2';
|
||||||
import { createBatcher } from '../util/batcher';
|
import { createBatcher } from '../util/batcher';
|
||||||
import { assert, strictAssert } from '../util/assert';
|
import { assert, strictAssert } from '../util/assert';
|
||||||
import { cleanDataForIpc } from './cleanDataForIpc';
|
import { cleanDataForIpc } from './cleanDataForIpc';
|
||||||
|
|
|
@ -1491,7 +1491,7 @@ export function getPropsForAttachment(
|
||||||
url: path
|
url: path
|
||||||
? window.Signal.Migrations.getAbsoluteAttachmentPath(path)
|
? window.Signal.Migrations.getAbsoluteAttachmentPath(path)
|
||||||
: undefined,
|
: undefined,
|
||||||
screenshot: screenshot
|
screenshot: screenshot?.path
|
||||||
? {
|
? {
|
||||||
...screenshot,
|
...screenshot,
|
||||||
url: window.Signal.Migrations.getAbsoluteAttachmentPath(
|
url: window.Signal.Migrations.getAbsoluteAttachmentPath(
|
||||||
|
@ -1499,7 +1499,7 @@ export function getPropsForAttachment(
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
: undefined,
|
: undefined,
|
||||||
thumbnail: thumbnail
|
thumbnail: thumbnail?.path
|
||||||
? {
|
? {
|
||||||
...thumbnail,
|
...thumbnail,
|
||||||
url: window.Signal.Migrations.getAbsoluteAttachmentPath(
|
url: window.Signal.Migrations.getAbsoluteAttachmentPath(
|
||||||
|
|
|
@ -270,7 +270,7 @@ describe('Contact', () => {
|
||||||
const result = await upgradeVersion(message.contact[0], {
|
const result = await upgradeVersion(message.contact[0], {
|
||||||
message,
|
message,
|
||||||
logger,
|
logger,
|
||||||
regionCode: '1',
|
getRegionCode: () => '1',
|
||||||
writeNewAttachmentData,
|
writeNewAttachmentData,
|
||||||
});
|
});
|
||||||
assert.deepEqual(result, message.contact[0]);
|
assert.deepEqual(result, message.contact[0]);
|
||||||
|
@ -311,7 +311,7 @@ describe('Contact', () => {
|
||||||
};
|
};
|
||||||
const result = await upgradeVersion(message.contact[0], {
|
const result = await upgradeVersion(message.contact[0], {
|
||||||
message,
|
message,
|
||||||
regionCode: 'US',
|
getRegionCode: () => 'US',
|
||||||
logger,
|
logger,
|
||||||
writeNewAttachmentData,
|
writeNewAttachmentData,
|
||||||
});
|
});
|
||||||
|
@ -355,7 +355,7 @@ describe('Contact', () => {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
const result = await upgradeVersion(message.contact[0], {
|
const result = await upgradeVersion(message.contact[0], {
|
||||||
regionCode: '1',
|
getRegionCode: () => '1',
|
||||||
writeNewAttachmentData,
|
writeNewAttachmentData,
|
||||||
message,
|
message,
|
||||||
logger,
|
logger,
|
||||||
|
@ -440,7 +440,7 @@ describe('Contact', () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const result = await upgradeVersion(message.contact[0], {
|
const result = await upgradeVersion(message.contact[0], {
|
||||||
regionCode: '1',
|
getRegionCode: () => '1',
|
||||||
writeNewAttachmentData,
|
writeNewAttachmentData,
|
||||||
message,
|
message,
|
||||||
logger,
|
logger,
|
||||||
|
@ -487,7 +487,7 @@ describe('Contact', () => {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
const result = await upgradeVersion(message.contact[0], {
|
const result = await upgradeVersion(message.contact[0], {
|
||||||
regionCode: '1',
|
getRegionCode: () => '1',
|
||||||
writeNewAttachmentData,
|
writeNewAttachmentData,
|
||||||
message,
|
message,
|
||||||
logger,
|
logger,
|
||||||
|
@ -534,7 +534,7 @@ describe('Contact', () => {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
const result = await upgradeVersion(message.contact[0], {
|
const result = await upgradeVersion(message.contact[0], {
|
||||||
regionCode: '1',
|
getRegionCode: () => '1',
|
||||||
writeNewAttachmentData,
|
writeNewAttachmentData,
|
||||||
message,
|
message,
|
||||||
logger,
|
logger,
|
||||||
|
@ -577,7 +577,7 @@ describe('Contact', () => {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
const result = await upgradeVersion(message.contact[0], {
|
const result = await upgradeVersion(message.contact[0], {
|
||||||
regionCode: '1',
|
getRegionCode: () => '1',
|
||||||
writeNewAttachmentData,
|
writeNewAttachmentData,
|
||||||
message,
|
message,
|
||||||
logger,
|
logger,
|
||||||
|
@ -606,7 +606,7 @@ describe('Contact', () => {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
const result = await upgradeVersion(message.contact[0], {
|
const result = await upgradeVersion(message.contact[0], {
|
||||||
regionCode: '1',
|
getRegionCode: () => '1',
|
||||||
writeNewAttachmentData,
|
writeNewAttachmentData,
|
||||||
message,
|
message,
|
||||||
logger,
|
logger,
|
||||||
|
|
|
@ -1,30 +1,97 @@
|
||||||
// Copyright 2018-2020 Signal Messenger, LLC
|
// Copyright 2018-2020 Signal Messenger, LLC
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
const { assert } = require('chai');
|
import { assert } from 'chai';
|
||||||
const sinon = require('sinon');
|
import * as sinon from 'sinon';
|
||||||
|
|
||||||
const Message = require('../../../js/modules/types/message');
|
import * as Message from '../../types/Message2';
|
||||||
const { SignalService } = require('../../../ts/protobuf');
|
import { SignalService } from '../../protobuf';
|
||||||
const Bytes = require('../../../ts/Bytes');
|
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 { AttachmentType } from '../../types/Attachment';
|
||||||
|
import type { LoggerType } from '../../types/Logging';
|
||||||
|
|
||||||
describe('Message', () => {
|
describe('Message', () => {
|
||||||
const logger = {
|
const logger: LoggerType = {
|
||||||
warn: () => null,
|
warn: () => null,
|
||||||
error: () => null,
|
error: () => null,
|
||||||
|
fatal: () => null,
|
||||||
|
info: () => null,
|
||||||
|
debug: () => null,
|
||||||
|
trace: () => null,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function getDefaultMessage(
|
||||||
|
props?: Partial<MessageAttributesType>
|
||||||
|
): MessageAttributesType {
|
||||||
|
return {
|
||||||
|
id: 'some-id',
|
||||||
|
type: 'incoming',
|
||||||
|
sent_at: 45,
|
||||||
|
received_at: 45,
|
||||||
|
timestamp: 45,
|
||||||
|
conversationId: 'some-conversation-id',
|
||||||
|
...props,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDefaultContext(
|
||||||
|
props?: Partial<Message.ContextType>
|
||||||
|
): Message.ContextType {
|
||||||
|
return {
|
||||||
|
getAbsoluteAttachmentPath: (_path: string) =>
|
||||||
|
'fake-absolute-attachment-path',
|
||||||
|
getAbsoluteStickerPath: (_path: string) => 'fake-absolute-sticker-path',
|
||||||
|
getImageDimensions: async (_params: {
|
||||||
|
objectUrl: string;
|
||||||
|
logger: LoggerType;
|
||||||
|
}) => ({
|
||||||
|
width: 10,
|
||||||
|
height: 20,
|
||||||
|
}),
|
||||||
|
getRegionCode: () => 'region-code',
|
||||||
|
logger,
|
||||||
|
makeImageThumbnail: async (_params: {
|
||||||
|
size: number;
|
||||||
|
objectUrl: string;
|
||||||
|
contentType: MIME.MIMEType;
|
||||||
|
logger: LoggerType;
|
||||||
|
}) => new Blob(),
|
||||||
|
makeObjectUrl: (
|
||||||
|
_data: Uint8Array | ArrayBuffer,
|
||||||
|
_contentType: MIME.MIMEType
|
||||||
|
) => 'fake-object-url',
|
||||||
|
makeVideoScreenshot: async (_params: {
|
||||||
|
objectUrl: string;
|
||||||
|
contentType: MIME.MIMEType;
|
||||||
|
logger: LoggerType;
|
||||||
|
}) => new Blob(),
|
||||||
|
revokeObjectUrl: (_objectUrl: string) => undefined,
|
||||||
|
writeNewAttachmentData: async (_data: Uint8Array) =>
|
||||||
|
'fake-attachment-path',
|
||||||
|
writeNewStickerData: async (_sticker: StickerMessageType) =>
|
||||||
|
'fake-sticker-path',
|
||||||
|
...props,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const writeExistingAttachmentData = () => Promise.resolve();
|
||||||
|
|
||||||
describe('createAttachmentDataWriter', () => {
|
describe('createAttachmentDataWriter', () => {
|
||||||
it('should ignore messages that didn’t go through attachment migration', async () => {
|
it('should ignore messages that didn’t go through attachment migration', async () => {
|
||||||
const input = {
|
const input = getDefaultMessage({
|
||||||
body: 'Imagine there is no heaven…',
|
body: 'Imagine there is no heaven…',
|
||||||
schemaVersion: 2,
|
schemaVersion: 2,
|
||||||
};
|
});
|
||||||
const expected = {
|
const expected = getDefaultMessage({
|
||||||
body: 'Imagine there is no heaven…',
|
body: 'Imagine there is no heaven…',
|
||||||
schemaVersion: 2,
|
schemaVersion: 2,
|
||||||
};
|
});
|
||||||
const writeExistingAttachmentData = () => {};
|
|
||||||
|
|
||||||
const actual = await Message.createAttachmentDataWriter({
|
const actual = await Message.createAttachmentDataWriter({
|
||||||
writeExistingAttachmentData,
|
writeExistingAttachmentData,
|
||||||
|
@ -34,17 +101,16 @@ describe('Message', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should ignore messages without attachments', async () => {
|
it('should ignore messages without attachments', async () => {
|
||||||
const input = {
|
const input = getDefaultMessage({
|
||||||
body: 'Imagine there is no heaven…',
|
body: 'Imagine there is no heaven…',
|
||||||
schemaVersion: 4,
|
schemaVersion: 4,
|
||||||
attachments: [],
|
attachments: [],
|
||||||
};
|
});
|
||||||
const expected = {
|
const expected = getDefaultMessage({
|
||||||
body: 'Imagine there is no heaven…',
|
body: 'Imagine there is no heaven…',
|
||||||
schemaVersion: 4,
|
schemaVersion: 4,
|
||||||
attachments: [],
|
attachments: [],
|
||||||
};
|
});
|
||||||
const writeExistingAttachmentData = () => {};
|
|
||||||
|
|
||||||
const actual = await Message.createAttachmentDataWriter({
|
const actual = await Message.createAttachmentDataWriter({
|
||||||
writeExistingAttachmentData,
|
writeExistingAttachmentData,
|
||||||
|
@ -54,32 +120,39 @@ describe('Message', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should write attachments to file system on original path', async () => {
|
it('should write attachments to file system on original path', async () => {
|
||||||
const input = {
|
const input = getDefaultMessage({
|
||||||
body: 'Imagine there is no heaven…',
|
body: 'Imagine there is no heaven…',
|
||||||
schemaVersion: 4,
|
schemaVersion: 4,
|
||||||
attachments: [
|
attachments: [
|
||||||
{
|
{
|
||||||
|
contentType: MIME.IMAGE_GIF,
|
||||||
|
size: 3534,
|
||||||
path: 'ab/abcdefghi',
|
path: 'ab/abcdefghi',
|
||||||
data: Bytes.fromString('It’s easy if you try'),
|
data: Bytes.fromString('It’s easy if you try'),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
});
|
||||||
const expected = {
|
const expected = getDefaultMessage({
|
||||||
body: 'Imagine there is no heaven…',
|
body: 'Imagine there is no heaven…',
|
||||||
schemaVersion: 4,
|
schemaVersion: 4,
|
||||||
attachments: [
|
attachments: [
|
||||||
{
|
{
|
||||||
|
contentType: MIME.IMAGE_GIF,
|
||||||
|
size: 3534,
|
||||||
path: 'ab/abcdefghi',
|
path: 'ab/abcdefghi',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
contact: [],
|
contact: [],
|
||||||
preview: [],
|
preview: [],
|
||||||
};
|
});
|
||||||
|
|
||||||
const writeExistingAttachmentData = attachment => {
|
// eslint-disable-next-line @typescript-eslint/no-shadow
|
||||||
|
const writeExistingAttachmentData = async (
|
||||||
|
attachment: Pick<AttachmentType, 'data' | 'path'>
|
||||||
|
) => {
|
||||||
assert.equal(attachment.path, 'ab/abcdefghi');
|
assert.equal(attachment.path, 'ab/abcdefghi');
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
Bytes.toString(attachment.data),
|
Bytes.toString(attachment.data || new Uint8Array()),
|
||||||
'It’s easy if you try'
|
'It’s easy if you try'
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -92,11 +165,15 @@ describe('Message', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should process quote attachment thumbnails', async () => {
|
it('should process quote attachment thumbnails', async () => {
|
||||||
const input = {
|
const input = getDefaultMessage({
|
||||||
body: 'Imagine there is no heaven…',
|
body: 'Imagine there is no heaven…',
|
||||||
schemaVersion: 4,
|
schemaVersion: 4,
|
||||||
attachments: [],
|
attachments: [],
|
||||||
quote: {
|
quote: {
|
||||||
|
id: 3523,
|
||||||
|
isViewOnce: false,
|
||||||
|
messageId: 'some-message-id',
|
||||||
|
referencedMessageNotFound: false,
|
||||||
attachments: [
|
attachments: [
|
||||||
{
|
{
|
||||||
thumbnail: {
|
thumbnail: {
|
||||||
|
@ -106,12 +183,16 @@ describe('Message', () => {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
};
|
});
|
||||||
const expected = {
|
const expected = getDefaultMessage({
|
||||||
body: 'Imagine there is no heaven…',
|
body: 'Imagine there is no heaven…',
|
||||||
schemaVersion: 4,
|
schemaVersion: 4,
|
||||||
attachments: [],
|
attachments: [],
|
||||||
quote: {
|
quote: {
|
||||||
|
id: 3523,
|
||||||
|
isViewOnce: false,
|
||||||
|
messageId: 'some-message-id',
|
||||||
|
referencedMessageNotFound: false,
|
||||||
attachments: [
|
attachments: [
|
||||||
{
|
{
|
||||||
thumbnail: {
|
thumbnail: {
|
||||||
|
@ -122,12 +203,15 @@ describe('Message', () => {
|
||||||
},
|
},
|
||||||
contact: [],
|
contact: [],
|
||||||
preview: [],
|
preview: [],
|
||||||
};
|
});
|
||||||
|
|
||||||
const writeExistingAttachmentData = attachment => {
|
// eslint-disable-next-line @typescript-eslint/no-shadow
|
||||||
|
const writeExistingAttachmentData = async (
|
||||||
|
attachment: Pick<AttachmentType, 'data' | 'path'>
|
||||||
|
) => {
|
||||||
assert.equal(attachment.path, 'ab/abcdefghi');
|
assert.equal(attachment.path, 'ab/abcdefghi');
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
Bytes.toString(attachment.data),
|
Bytes.toString(attachment.data || new Uint8Array()),
|
||||||
'It’s easy if you try'
|
'It’s easy if you try'
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -140,45 +224,52 @@ describe('Message', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should process contact avatars', async () => {
|
it('should process contact avatars', async () => {
|
||||||
const input = {
|
const input = getDefaultMessage({
|
||||||
body: 'Imagine there is no heaven…',
|
body: 'Imagine there is no heaven…',
|
||||||
schemaVersion: 4,
|
schemaVersion: 4,
|
||||||
attachments: [],
|
attachments: [],
|
||||||
contact: [
|
contact: [
|
||||||
{
|
{
|
||||||
name: 'john',
|
name: { givenName: 'john' },
|
||||||
avatar: {
|
avatar: {
|
||||||
isProfile: false,
|
isProfile: false,
|
||||||
avatar: {
|
avatar: {
|
||||||
|
contentType: MIME.IMAGE_PNG,
|
||||||
|
size: 47,
|
||||||
path: 'ab/abcdefghi',
|
path: 'ab/abcdefghi',
|
||||||
data: Bytes.fromString('It’s easy if you try'),
|
data: Bytes.fromString('It’s easy if you try'),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
});
|
||||||
const expected = {
|
const expected = getDefaultMessage({
|
||||||
body: 'Imagine there is no heaven…',
|
body: 'Imagine there is no heaven…',
|
||||||
schemaVersion: 4,
|
schemaVersion: 4,
|
||||||
attachments: [],
|
attachments: [],
|
||||||
contact: [
|
contact: [
|
||||||
{
|
{
|
||||||
name: 'john',
|
name: { givenName: 'john' },
|
||||||
avatar: {
|
avatar: {
|
||||||
isProfile: false,
|
isProfile: false,
|
||||||
avatar: {
|
avatar: {
|
||||||
|
contentType: MIME.IMAGE_PNG,
|
||||||
|
size: 47,
|
||||||
path: 'ab/abcdefghi',
|
path: 'ab/abcdefghi',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
preview: [],
|
preview: [],
|
||||||
};
|
});
|
||||||
|
|
||||||
const writeExistingAttachmentData = attachment => {
|
// eslint-disable-next-line @typescript-eslint/no-shadow
|
||||||
|
const writeExistingAttachmentData = async (
|
||||||
|
attachment: Pick<AttachmentType, 'data' | 'path'>
|
||||||
|
) => {
|
||||||
assert.equal(attachment.path, 'ab/abcdefghi');
|
assert.equal(attachment.path, 'ab/abcdefghi');
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
Bytes.toString(attachment.data),
|
Bytes.toString(attachment.data || new Uint8Array()),
|
||||||
'It’s easy if you try'
|
'It’s easy if you try'
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -193,14 +284,14 @@ describe('Message', () => {
|
||||||
|
|
||||||
describe('initializeSchemaVersion', () => {
|
describe('initializeSchemaVersion', () => {
|
||||||
it('should ignore messages with previously inherited schema', () => {
|
it('should ignore messages with previously inherited schema', () => {
|
||||||
const input = {
|
const input = getDefaultMessage({
|
||||||
body: 'Imagine there is no heaven…',
|
body: 'Imagine there is no heaven…',
|
||||||
schemaVersion: 2,
|
schemaVersion: 2,
|
||||||
};
|
});
|
||||||
const expected = {
|
const expected = getDefaultMessage({
|
||||||
body: 'Imagine there is no heaven…',
|
body: 'Imagine there is no heaven…',
|
||||||
schemaVersion: 2,
|
schemaVersion: 2,
|
||||||
};
|
});
|
||||||
|
|
||||||
const actual = Message.initializeSchemaVersion({
|
const actual = Message.initializeSchemaVersion({
|
||||||
message: input,
|
message: input,
|
||||||
|
@ -211,15 +302,15 @@ describe('Message', () => {
|
||||||
|
|
||||||
context('for message without attachments', () => {
|
context('for message without attachments', () => {
|
||||||
it('should initialize schema version to zero', () => {
|
it('should initialize schema version to zero', () => {
|
||||||
const input = {
|
const input = getDefaultMessage({
|
||||||
body: 'Imagine there is no heaven…',
|
body: 'Imagine there is no heaven…',
|
||||||
attachments: [],
|
attachments: [],
|
||||||
};
|
});
|
||||||
const expected = {
|
const expected = getDefaultMessage({
|
||||||
body: 'Imagine there is no heaven…',
|
body: 'Imagine there is no heaven…',
|
||||||
attachments: [],
|
attachments: [],
|
||||||
schemaVersion: 0,
|
schemaVersion: 0,
|
||||||
};
|
});
|
||||||
|
|
||||||
const actual = Message.initializeSchemaVersion({
|
const actual = Message.initializeSchemaVersion({
|
||||||
message: input,
|
message: input,
|
||||||
|
@ -231,26 +322,28 @@ describe('Message', () => {
|
||||||
|
|
||||||
context('for message with attachments', () => {
|
context('for message with attachments', () => {
|
||||||
it('should inherit existing attachment schema version', () => {
|
it('should inherit existing attachment schema version', () => {
|
||||||
const input = {
|
const input = getDefaultMessage({
|
||||||
body: 'Imagine there is no heaven…',
|
body: 'Imagine there is no heaven…',
|
||||||
attachments: [
|
attachments: [
|
||||||
{
|
{
|
||||||
contentType: 'image/jpeg',
|
contentType: MIME.IMAGE_JPEG,
|
||||||
|
size: 45,
|
||||||
fileName: 'lennon.jpg',
|
fileName: 'lennon.jpg',
|
||||||
schemaVersion: 7,
|
schemaVersion: 7,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
});
|
||||||
const expected = {
|
const expected = getDefaultMessage({
|
||||||
body: 'Imagine there is no heaven…',
|
body: 'Imagine there is no heaven…',
|
||||||
attachments: [
|
attachments: [
|
||||||
{
|
{
|
||||||
contentType: 'image/jpeg',
|
contentType: MIME.IMAGE_JPEG,
|
||||||
|
size: 45,
|
||||||
fileName: 'lennon.jpg',
|
fileName: 'lennon.jpg',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
schemaVersion: 7,
|
schemaVersion: 7,
|
||||||
};
|
});
|
||||||
|
|
||||||
const actual = Message.initializeSchemaVersion({
|
const actual = Message.initializeSchemaVersion({
|
||||||
message: input,
|
message: input,
|
||||||
|
@ -263,10 +356,10 @@ describe('Message', () => {
|
||||||
|
|
||||||
describe('upgradeSchema', () => {
|
describe('upgradeSchema', () => {
|
||||||
it('should upgrade an unversioned message to the latest version', async () => {
|
it('should upgrade an unversioned message to the latest version', async () => {
|
||||||
const input = {
|
const input = getDefaultMessage({
|
||||||
attachments: [
|
attachments: [
|
||||||
{
|
{
|
||||||
contentType: 'audio/aac',
|
contentType: MIME.AUDIO_AAC,
|
||||||
flags: SignalService.AttachmentPointer.Flags.VOICE_MESSAGE,
|
flags: SignalService.AttachmentPointer.Flags.VOICE_MESSAGE,
|
||||||
data: Bytes.fromString('It’s easy if you try'),
|
data: Bytes.fromString('It’s easy if you try'),
|
||||||
fileName: 'test\u202Dfig.exe',
|
fileName: 'test\u202Dfig.exe',
|
||||||
|
@ -274,11 +367,11 @@ describe('Message', () => {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
schemaVersion: 0,
|
schemaVersion: 0,
|
||||||
};
|
});
|
||||||
const expected = {
|
const expected = getDefaultMessage({
|
||||||
attachments: [
|
attachments: [
|
||||||
{
|
{
|
||||||
contentType: 'audio/aac',
|
contentType: MIME.AUDIO_AAC,
|
||||||
flags: 1,
|
flags: 1,
|
||||||
path: 'abc/abcdefg',
|
path: 'abc/abcdefg',
|
||||||
fileName: 'test\uFFFDfig.exe',
|
fileName: 'test\uFFFDfig.exe',
|
||||||
|
@ -290,10 +383,10 @@ describe('Message', () => {
|
||||||
hasFileAttachments: undefined,
|
hasFileAttachments: undefined,
|
||||||
schemaVersion: Message.CURRENT_SCHEMA_VERSION,
|
schemaVersion: Message.CURRENT_SCHEMA_VERSION,
|
||||||
contact: [],
|
contact: [],
|
||||||
};
|
});
|
||||||
|
|
||||||
const expectedAttachmentData = 'It’s easy if you try';
|
const expectedAttachmentData = 'It’s easy if you try';
|
||||||
const context = {
|
const context = getDefaultContext({
|
||||||
writeNewAttachmentData: async attachmentData => {
|
writeNewAttachmentData: async attachmentData => {
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
Bytes.toString(attachmentData),
|
Bytes.toString(attachmentData),
|
||||||
|
@ -301,58 +394,46 @@ describe('Message', () => {
|
||||||
);
|
);
|
||||||
return 'abc/abcdefg';
|
return 'abc/abcdefg';
|
||||||
},
|
},
|
||||||
getRegionCode: () => 'US',
|
});
|
||||||
getAbsoluteAttachmentPath: () => 'some/path/on/disk',
|
|
||||||
makeObjectUrl: () => 'blob://FAKE',
|
|
||||||
revokeObjectUrl: () => null,
|
|
||||||
getImageDimensions: () => ({ height: 10, width: 15 }),
|
|
||||||
makeImageThumbnail: () => new Blob(),
|
|
||||||
makeVideoScreenshot: () => new Blob(),
|
|
||||||
logger: {
|
|
||||||
warn: () => null,
|
|
||||||
error: () => null,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
const actual = await Message.upgradeSchema(input, context);
|
const actual = await Message.upgradeSchema(input, context);
|
||||||
assert.deepEqual(actual, expected);
|
assert.deepEqual(actual, expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
context('with multiple upgrade steps', () => {
|
context('with multiple upgrade steps', () => {
|
||||||
it('should return last valid message when any upgrade step fails', async () => {
|
it('should return last valid message when any upgrade step fails', async () => {
|
||||||
const input = {
|
const input = getDefaultMessage({
|
||||||
attachments: [
|
attachments: [
|
||||||
{
|
{
|
||||||
contentType: 'application/json',
|
contentType: MIME.APPLICATION_JSON,
|
||||||
data: null,
|
|
||||||
fileName: 'test\u202Dfig.exe',
|
fileName: 'test\u202Dfig.exe',
|
||||||
size: 1111,
|
size: 1111,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
body: 'start',
|
||||||
schemaVersion: 0,
|
schemaVersion: 0,
|
||||||
};
|
});
|
||||||
const expected = {
|
const expected = getDefaultMessage({
|
||||||
attachments: [
|
attachments: [
|
||||||
{
|
{
|
||||||
contentType: 'application/json',
|
contentType: MIME.APPLICATION_JSON,
|
||||||
data: null,
|
|
||||||
fileName: 'test\u202Dfig.exe',
|
fileName: 'test\u202Dfig.exe',
|
||||||
size: 1111,
|
size: 1111,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
hasUpgradedToVersion1: true,
|
body: 'start +1',
|
||||||
schemaVersion: 1,
|
schemaVersion: 1,
|
||||||
};
|
});
|
||||||
|
|
||||||
const v1 = async message => ({
|
const v1 = async (message: MessageAttributesType) => ({
|
||||||
...message,
|
...message,
|
||||||
hasUpgradedToVersion1: true,
|
body: `${message.body} +1`,
|
||||||
});
|
});
|
||||||
const v2 = async () => {
|
const v2 = async () => {
|
||||||
throw new Error('boom');
|
throw new Error('boom');
|
||||||
};
|
};
|
||||||
const v3 = async message => ({
|
const v3 = async (message: MessageAttributesType) => ({
|
||||||
...message,
|
...message,
|
||||||
hasUpgradedToVersion3: true,
|
body: `${message.body} +3`,
|
||||||
});
|
});
|
||||||
|
|
||||||
const toVersion1 = Message._withSchemaVersion({
|
const toVersion1 = Message._withSchemaVersion({
|
||||||
|
@ -368,8 +449,8 @@ describe('Message', () => {
|
||||||
upgrade: v3,
|
upgrade: v3,
|
||||||
});
|
});
|
||||||
|
|
||||||
const context = { logger };
|
const context = getDefaultContext({ logger });
|
||||||
const upgradeSchema = async message =>
|
const upgradeSchema = async (message: MessageAttributesType) =>
|
||||||
toVersion3(
|
toVersion3(
|
||||||
await toVersion2(await toVersion1(message, context), context),
|
await toVersion2(await toVersion1(message, context), context),
|
||||||
context
|
context
|
||||||
|
@ -380,42 +461,40 @@ describe('Message', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should skip out-of-order upgrade steps', async () => {
|
it('should skip out-of-order upgrade steps', async () => {
|
||||||
const input = {
|
const input = getDefaultMessage({
|
||||||
attachments: [
|
attachments: [
|
||||||
{
|
{
|
||||||
contentType: 'application/json',
|
contentType: MIME.APPLICATION_JSON,
|
||||||
data: null,
|
|
||||||
fileName: 'test\u202Dfig.exe',
|
fileName: 'test\u202Dfig.exe',
|
||||||
size: 1111,
|
size: 1111,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
body: 'start',
|
||||||
schemaVersion: 0,
|
schemaVersion: 0,
|
||||||
};
|
});
|
||||||
const expected = {
|
const expected = getDefaultMessage({
|
||||||
attachments: [
|
attachments: [
|
||||||
{
|
{
|
||||||
contentType: 'application/json',
|
contentType: MIME.APPLICATION_JSON,
|
||||||
data: null,
|
|
||||||
fileName: 'test\u202Dfig.exe',
|
fileName: 'test\u202Dfig.exe',
|
||||||
size: 1111,
|
size: 1111,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
body: 'start +1 +2',
|
||||||
schemaVersion: 2,
|
schemaVersion: 2,
|
||||||
hasUpgradedToVersion1: true,
|
});
|
||||||
hasUpgradedToVersion2: true,
|
|
||||||
};
|
|
||||||
|
|
||||||
const v1 = async attachment => ({
|
const v1 = async (message: MessageAttributesType) => ({
|
||||||
...attachment,
|
...message,
|
||||||
hasUpgradedToVersion1: true,
|
body: `${message.body} +1`,
|
||||||
});
|
});
|
||||||
const v2 = async attachment => ({
|
const v2 = async (message: MessageAttributesType) => ({
|
||||||
...attachment,
|
...message,
|
||||||
hasUpgradedToVersion2: true,
|
body: `${message.body} +2`,
|
||||||
});
|
});
|
||||||
const v3 = async attachment => ({
|
const v3 = async (message: MessageAttributesType) => ({
|
||||||
...attachment,
|
...message,
|
||||||
hasUpgradedToVersion3: true,
|
body: `${message.body} +3`,
|
||||||
});
|
});
|
||||||
|
|
||||||
const toVersion1 = Message._withSchemaVersion({
|
const toVersion1 = Message._withSchemaVersion({
|
||||||
|
@ -431,15 +510,13 @@ describe('Message', () => {
|
||||||
upgrade: v3,
|
upgrade: v3,
|
||||||
});
|
});
|
||||||
|
|
||||||
const context = { logger };
|
const context = getDefaultContext({ logger });
|
||||||
// NOTE: We upgrade to 3 before 2, i.e. the pipeline should abort:
|
const atVersion1 = await toVersion1(input, context);
|
||||||
const upgradeSchema = async attachment =>
|
|
||||||
toVersion2(
|
|
||||||
await toVersion3(await toVersion1(attachment, context), context),
|
|
||||||
context
|
|
||||||
);
|
|
||||||
|
|
||||||
const actual = await upgradeSchema(input);
|
// Note: this will fail to apply and log, since it's jumping two versions up
|
||||||
|
const atVersion3 = await toVersion3(atVersion1, context);
|
||||||
|
|
||||||
|
const actual = await toVersion2(atVersion3, context);
|
||||||
assert.deepEqual(actual, expected);
|
assert.deepEqual(actual, expected);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -447,37 +524,49 @@ describe('Message', () => {
|
||||||
|
|
||||||
describe('_withSchemaVersion', () => {
|
describe('_withSchemaVersion', () => {
|
||||||
it('should require a version number', () => {
|
it('should require a version number', () => {
|
||||||
const toVersionX = () => {};
|
const toVersionX = () => null;
|
||||||
assert.throws(
|
assert.throws(
|
||||||
() =>
|
() =>
|
||||||
Message._withSchemaVersion({ schemaVersion: toVersionX, upgrade: 2 }),
|
Message._withSchemaVersion({
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
schemaVersion: toVersionX as any,
|
||||||
|
upgrade: () => Promise.resolve(getDefaultMessage()),
|
||||||
|
}),
|
||||||
'_withSchemaVersion: schemaVersion is invalid'
|
'_withSchemaVersion: schemaVersion is invalid'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should require an upgrade function', () => {
|
it('should require an upgrade function', () => {
|
||||||
assert.throws(
|
assert.throws(
|
||||||
() => Message._withSchemaVersion({ schemaVersion: 2, upgrade: 3 }),
|
() =>
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
Message._withSchemaVersion({ schemaVersion: 2, upgrade: 3 as any }),
|
||||||
'_withSchemaVersion: upgrade must be a function'
|
'_withSchemaVersion: upgrade must be a function'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should skip upgrading if message has already been upgraded', async () => {
|
it('should skip upgrading if message has already been upgraded', async () => {
|
||||||
const upgrade = async message => ({ ...message, foo: true });
|
const upgrade = async (message: MessageAttributesType) => ({
|
||||||
|
...message,
|
||||||
|
foo: true,
|
||||||
|
});
|
||||||
const upgradeWithVersion = Message._withSchemaVersion({
|
const upgradeWithVersion = Message._withSchemaVersion({
|
||||||
schemaVersion: 3,
|
schemaVersion: 3,
|
||||||
upgrade,
|
upgrade,
|
||||||
});
|
});
|
||||||
|
|
||||||
const input = {
|
const input = getDefaultMessage({
|
||||||
id: 'guid-guid-guid-guid',
|
id: 'guid-guid-guid-guid',
|
||||||
schemaVersion: 4,
|
schemaVersion: 4,
|
||||||
};
|
});
|
||||||
const expected = {
|
const expected = getDefaultMessage({
|
||||||
id: 'guid-guid-guid-guid',
|
id: 'guid-guid-guid-guid',
|
||||||
schemaVersion: 4,
|
schemaVersion: 4,
|
||||||
};
|
});
|
||||||
const actual = await upgradeWithVersion(input, { logger });
|
const actual = await upgradeWithVersion(
|
||||||
|
input,
|
||||||
|
getDefaultContext({ logger })
|
||||||
|
);
|
||||||
assert.deepEqual(actual, expected);
|
assert.deepEqual(actual, expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -490,15 +579,18 @@ describe('Message', () => {
|
||||||
upgrade,
|
upgrade,
|
||||||
});
|
});
|
||||||
|
|
||||||
const input = {
|
const input = getDefaultMessage({
|
||||||
id: 'guid-guid-guid-guid',
|
id: 'guid-guid-guid-guid',
|
||||||
schemaVersion: 0,
|
schemaVersion: 0,
|
||||||
};
|
});
|
||||||
const expected = {
|
const expected = getDefaultMessage({
|
||||||
id: 'guid-guid-guid-guid',
|
id: 'guid-guid-guid-guid',
|
||||||
schemaVersion: 0,
|
schemaVersion: 0,
|
||||||
};
|
});
|
||||||
const actual = await upgradeWithVersion(input, { logger });
|
const actual = await upgradeWithVersion(
|
||||||
|
input,
|
||||||
|
getDefaultContext({ logger })
|
||||||
|
);
|
||||||
assert.deepEqual(actual, expected);
|
assert.deepEqual(actual, expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -506,18 +598,22 @@ describe('Message', () => {
|
||||||
const upgrade = async () => null;
|
const upgrade = async () => null;
|
||||||
const upgradeWithVersion = Message._withSchemaVersion({
|
const upgradeWithVersion = Message._withSchemaVersion({
|
||||||
schemaVersion: 3,
|
schemaVersion: 3,
|
||||||
upgrade,
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
upgrade: upgrade as any,
|
||||||
});
|
});
|
||||||
|
|
||||||
const input = {
|
const input = getDefaultMessage({
|
||||||
id: 'guid-guid-guid-guid',
|
id: 'guid-guid-guid-guid',
|
||||||
schemaVersion: 0,
|
schemaVersion: 0,
|
||||||
};
|
});
|
||||||
const expected = {
|
const expected = getDefaultMessage({
|
||||||
id: 'guid-guid-guid-guid',
|
id: 'guid-guid-guid-guid',
|
||||||
schemaVersion: 0,
|
schemaVersion: 0,
|
||||||
};
|
});
|
||||||
const actual = await upgradeWithVersion(input, { logger });
|
const actual = await upgradeWithVersion(
|
||||||
|
input,
|
||||||
|
getDefaultContext({ logger })
|
||||||
|
);
|
||||||
assert.deepEqual(actual, expected);
|
assert.deepEqual(actual, expected);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -529,10 +625,10 @@ describe('Message', () => {
|
||||||
.throws(new Error("Shouldn't be called"));
|
.throws(new Error("Shouldn't be called"));
|
||||||
const upgradeVersion = Message._mapQuotedAttachments(upgradeAttachment);
|
const upgradeVersion = Message._mapQuotedAttachments(upgradeAttachment);
|
||||||
|
|
||||||
const message = {
|
const message = getDefaultMessage({
|
||||||
body: 'hey there!',
|
body: 'hey there!',
|
||||||
};
|
});
|
||||||
const result = await upgradeVersion(message);
|
const result = await upgradeVersion(message, getDefaultContext());
|
||||||
assert.deepEqual(result, message);
|
assert.deepEqual(result, message);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -542,20 +638,32 @@ describe('Message', () => {
|
||||||
.throws(new Error("Shouldn't be called"));
|
.throws(new Error("Shouldn't be called"));
|
||||||
const upgradeVersion = Message._mapQuotedAttachments(upgradeAttachment);
|
const upgradeVersion = Message._mapQuotedAttachments(upgradeAttachment);
|
||||||
|
|
||||||
const message = {
|
const message = getDefaultMessage({
|
||||||
body: 'hey there!',
|
body: 'hey there!',
|
||||||
quote: {
|
quote: {
|
||||||
text: 'hey!',
|
text: 'hey!',
|
||||||
},
|
id: 34233,
|
||||||
};
|
isViewOnce: false,
|
||||||
const expected = {
|
messageId: 'message-id',
|
||||||
|
referencedMessageNotFound: false,
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
} as any,
|
||||||
|
});
|
||||||
|
const expected = getDefaultMessage({
|
||||||
body: 'hey there!',
|
body: 'hey there!',
|
||||||
quote: {
|
quote: {
|
||||||
text: 'hey!',
|
text: 'hey!',
|
||||||
attachments: [],
|
attachments: [],
|
||||||
|
id: 34233,
|
||||||
|
isViewOnce: false,
|
||||||
|
messageId: 'message-id',
|
||||||
|
referencedMessageNotFound: false,
|
||||||
},
|
},
|
||||||
};
|
});
|
||||||
const result = await upgradeVersion(message, { logger });
|
const result = await upgradeVersion(
|
||||||
|
message,
|
||||||
|
getDefaultContext({ logger })
|
||||||
|
);
|
||||||
assert.deepEqual(result, expected);
|
assert.deepEqual(result, expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -565,14 +673,21 @@ describe('Message', () => {
|
||||||
.throws(new Error("Shouldn't be called"));
|
.throws(new Error("Shouldn't be called"));
|
||||||
const upgradeVersion = Message._mapQuotedAttachments(upgradeAttachment);
|
const upgradeVersion = Message._mapQuotedAttachments(upgradeAttachment);
|
||||||
|
|
||||||
const message = {
|
const message = getDefaultMessage({
|
||||||
body: 'hey there!',
|
body: 'hey there!',
|
||||||
quote: {
|
quote: {
|
||||||
text: 'hey!',
|
text: 'hey!',
|
||||||
attachments: [],
|
attachments: [],
|
||||||
|
id: 34233,
|
||||||
|
isViewOnce: false,
|
||||||
|
messageId: 'message-id',
|
||||||
|
referencedMessageNotFound: false,
|
||||||
},
|
},
|
||||||
};
|
});
|
||||||
const result = await upgradeVersion(message, { logger });
|
const result = await upgradeVersion(
|
||||||
|
message,
|
||||||
|
getDefaultContext({ logger })
|
||||||
|
);
|
||||||
assert.deepEqual(result, message);
|
assert.deepEqual(result, message);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -582,7 +697,7 @@ describe('Message', () => {
|
||||||
.throws(new Error("Shouldn't be called"));
|
.throws(new Error("Shouldn't be called"));
|
||||||
const upgradeVersion = Message._mapQuotedAttachments(upgradeAttachment);
|
const upgradeVersion = Message._mapQuotedAttachments(upgradeAttachment);
|
||||||
|
|
||||||
const message = {
|
const message = getDefaultMessage({
|
||||||
body: 'hey there!',
|
body: 'hey there!',
|
||||||
quote: {
|
quote: {
|
||||||
text: 'hey!',
|
text: 'hey!',
|
||||||
|
@ -592,9 +707,16 @@ describe('Message', () => {
|
||||||
contentType: 'text/plain',
|
contentType: 'text/plain',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
id: 34233,
|
||||||
|
isViewOnce: false,
|
||||||
|
messageId: 'message-id',
|
||||||
|
referencedMessageNotFound: false,
|
||||||
},
|
},
|
||||||
};
|
});
|
||||||
const result = await upgradeVersion(message, { logger });
|
const result = await upgradeVersion(
|
||||||
|
message,
|
||||||
|
getDefaultContext({ logger })
|
||||||
|
);
|
||||||
assert.deepEqual(result, message);
|
assert.deepEqual(result, message);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -604,7 +726,7 @@ describe('Message', () => {
|
||||||
.returns({ fileName: 'processed!' });
|
.returns({ fileName: 'processed!' });
|
||||||
const upgradeVersion = Message._mapQuotedAttachments(upgradeAttachment);
|
const upgradeVersion = Message._mapQuotedAttachments(upgradeAttachment);
|
||||||
|
|
||||||
const message = {
|
const message = getDefaultMessage({
|
||||||
body: 'hey there!',
|
body: 'hey there!',
|
||||||
quote: {
|
quote: {
|
||||||
text: 'hey!',
|
text: 'hey!',
|
||||||
|
@ -617,9 +739,13 @@ describe('Message', () => {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
id: 34233,
|
||||||
|
isViewOnce: false,
|
||||||
|
messageId: 'message-id',
|
||||||
|
referencedMessageNotFound: false,
|
||||||
},
|
},
|
||||||
};
|
});
|
||||||
const expected = {
|
const expected = getDefaultMessage({
|
||||||
body: 'hey there!',
|
body: 'hey there!',
|
||||||
quote: {
|
quote: {
|
||||||
text: 'hey!',
|
text: 'hey!',
|
||||||
|
@ -632,9 +758,16 @@ describe('Message', () => {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
id: 34233,
|
||||||
|
isViewOnce: false,
|
||||||
|
messageId: 'message-id',
|
||||||
|
referencedMessageNotFound: false,
|
||||||
},
|
},
|
||||||
};
|
});
|
||||||
const result = await upgradeVersion(message, { logger });
|
const result = await upgradeVersion(
|
||||||
|
message,
|
||||||
|
getDefaultContext({ logger })
|
||||||
|
);
|
||||||
assert.deepEqual(result, expected);
|
assert.deepEqual(result, expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -644,7 +777,7 @@ describe('Message', () => {
|
||||||
});
|
});
|
||||||
const upgradeVersion = Message._mapQuotedAttachments(upgradeAttachment);
|
const upgradeVersion = Message._mapQuotedAttachments(upgradeAttachment);
|
||||||
|
|
||||||
const message = {
|
const message = getDefaultMessage({
|
||||||
body: 'hey there!',
|
body: 'hey there!',
|
||||||
quote: {
|
quote: {
|
||||||
text: 'hey!',
|
text: 'hey!',
|
||||||
|
@ -655,9 +788,13 @@ describe('Message', () => {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
id: 34233,
|
||||||
|
isViewOnce: false,
|
||||||
|
messageId: 'message-id',
|
||||||
|
referencedMessageNotFound: false,
|
||||||
},
|
},
|
||||||
};
|
});
|
||||||
const expected = {
|
const expected = getDefaultMessage({
|
||||||
body: 'hey there!',
|
body: 'hey there!',
|
||||||
quote: {
|
quote: {
|
||||||
text: 'hey!',
|
text: 'hey!',
|
||||||
|
@ -668,9 +805,16 @@ describe('Message', () => {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
id: 34233,
|
||||||
|
isViewOnce: false,
|
||||||
|
messageId: 'message-id',
|
||||||
|
referencedMessageNotFound: false,
|
||||||
},
|
},
|
||||||
};
|
});
|
||||||
const result = await upgradeVersion(message, { logger });
|
const result = await upgradeVersion(
|
||||||
|
message,
|
||||||
|
getDefaultContext({ logger })
|
||||||
|
);
|
||||||
assert.deepEqual(result, expected);
|
assert.deepEqual(result, expected);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -682,22 +826,23 @@ describe('Message', () => {
|
||||||
.throws(new Error("Shouldn't be called"));
|
.throws(new Error("Shouldn't be called"));
|
||||||
const upgradeVersion = Message._mapContact(upgradeContact);
|
const upgradeVersion = Message._mapContact(upgradeContact);
|
||||||
|
|
||||||
const message = {
|
const message = getDefaultMessage({
|
||||||
body: 'hey there!',
|
body: 'hey there!',
|
||||||
};
|
});
|
||||||
const expected = {
|
const expected = getDefaultMessage({
|
||||||
body: 'hey there!',
|
body: 'hey there!',
|
||||||
contact: [],
|
contact: [],
|
||||||
};
|
});
|
||||||
const result = await upgradeVersion(message);
|
const result = await upgradeVersion(message, getDefaultContext());
|
||||||
assert.deepEqual(result, expected);
|
assert.deepEqual(result, expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('handles one contact', async () => {
|
it('handles one contact', async () => {
|
||||||
const upgradeContact = contact => Promise.resolve(contact);
|
const upgradeContact = (contact: EmbeddedContactType) =>
|
||||||
|
Promise.resolve(contact);
|
||||||
const upgradeVersion = Message._mapContact(upgradeContact);
|
const upgradeVersion = Message._mapContact(upgradeContact);
|
||||||
|
|
||||||
const message = {
|
const message = getDefaultMessage({
|
||||||
body: 'hey there!',
|
body: 'hey there!',
|
||||||
contact: [
|
contact: [
|
||||||
{
|
{
|
||||||
|
@ -706,8 +851,8 @@ describe('Message', () => {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
});
|
||||||
const expected = {
|
const expected = getDefaultMessage({
|
||||||
body: 'hey there!',
|
body: 'hey there!',
|
||||||
contact: [
|
contact: [
|
||||||
{
|
{
|
||||||
|
@ -716,8 +861,8 @@ describe('Message', () => {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
});
|
||||||
const result = await upgradeVersion(message);
|
const result = await upgradeVersion(message, getDefaultContext());
|
||||||
assert.deepEqual(result, expected);
|
assert.deepEqual(result, expected);
|
||||||
});
|
});
|
||||||
});
|
});
|
|
@ -4,15 +4,29 @@
|
||||||
import { assert } from 'chai';
|
import { assert } from 'chai';
|
||||||
|
|
||||||
import * as Message from '../../../types/message/initializeAttachmentMetadata';
|
import * as Message from '../../../types/message/initializeAttachmentMetadata';
|
||||||
import type { IncomingMessage } from '../../../types/Message';
|
|
||||||
import { SignalService } from '../../../protobuf';
|
import { SignalService } from '../../../protobuf';
|
||||||
import * as MIME from '../../../types/MIME';
|
import * as MIME from '../../../types/MIME';
|
||||||
import * as Bytes from '../../../Bytes';
|
import * as Bytes from '../../../Bytes';
|
||||||
|
import type { MessageAttributesType } from '../../../model-types.d';
|
||||||
|
|
||||||
|
function getDefaultMessage(
|
||||||
|
props?: Partial<MessageAttributesType>
|
||||||
|
): MessageAttributesType {
|
||||||
|
return {
|
||||||
|
id: 'some-id',
|
||||||
|
type: 'incoming',
|
||||||
|
sent_at: 45,
|
||||||
|
received_at: 45,
|
||||||
|
timestamp: 45,
|
||||||
|
conversationId: 'some-conversation-id',
|
||||||
|
...props,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
describe('Message', () => {
|
describe('Message', () => {
|
||||||
describe('initializeAttachmentMetadata', () => {
|
describe('initializeAttachmentMetadata', () => {
|
||||||
it('should classify visual media attachments', async () => {
|
it('should classify visual media attachments', async () => {
|
||||||
const input: IncomingMessage = {
|
const input = getDefaultMessage({
|
||||||
type: 'incoming',
|
type: 'incoming',
|
||||||
conversationId: 'foo',
|
conversationId: 'foo',
|
||||||
id: '11111111-1111-1111-1111-111111111111',
|
id: '11111111-1111-1111-1111-111111111111',
|
||||||
|
@ -27,8 +41,8 @@ describe('Message', () => {
|
||||||
size: 1111,
|
size: 1111,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
});
|
||||||
const expected: IncomingMessage = {
|
const expected = getDefaultMessage({
|
||||||
type: 'incoming',
|
type: 'incoming',
|
||||||
conversationId: 'foo',
|
conversationId: 'foo',
|
||||||
id: '11111111-1111-1111-1111-111111111111',
|
id: '11111111-1111-1111-1111-111111111111',
|
||||||
|
@ -46,14 +60,14 @@ describe('Message', () => {
|
||||||
hasAttachments: 1,
|
hasAttachments: 1,
|
||||||
hasVisualMediaAttachments: 1,
|
hasVisualMediaAttachments: 1,
|
||||||
hasFileAttachments: undefined,
|
hasFileAttachments: undefined,
|
||||||
};
|
});
|
||||||
|
|
||||||
const actual = await Message.initializeAttachmentMetadata(input);
|
const actual = await Message.initializeAttachmentMetadata(input);
|
||||||
assert.deepEqual(actual, expected);
|
assert.deepEqual(actual, expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should classify file attachments', async () => {
|
it('should classify file attachments', async () => {
|
||||||
const input: IncomingMessage = {
|
const input = getDefaultMessage({
|
||||||
type: 'incoming',
|
type: 'incoming',
|
||||||
conversationId: 'foo',
|
conversationId: 'foo',
|
||||||
id: '11111111-1111-1111-1111-111111111111',
|
id: '11111111-1111-1111-1111-111111111111',
|
||||||
|
@ -68,8 +82,8 @@ describe('Message', () => {
|
||||||
size: 1111,
|
size: 1111,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
});
|
||||||
const expected: IncomingMessage = {
|
const expected = getDefaultMessage({
|
||||||
type: 'incoming',
|
type: 'incoming',
|
||||||
conversationId: 'foo',
|
conversationId: 'foo',
|
||||||
id: '11111111-1111-1111-1111-111111111111',
|
id: '11111111-1111-1111-1111-111111111111',
|
||||||
|
@ -87,14 +101,14 @@ describe('Message', () => {
|
||||||
hasAttachments: 1,
|
hasAttachments: 1,
|
||||||
hasVisualMediaAttachments: undefined,
|
hasVisualMediaAttachments: undefined,
|
||||||
hasFileAttachments: 1,
|
hasFileAttachments: 1,
|
||||||
};
|
});
|
||||||
|
|
||||||
const actual = await Message.initializeAttachmentMetadata(input);
|
const actual = await Message.initializeAttachmentMetadata(input);
|
||||||
assert.deepEqual(actual, expected);
|
assert.deepEqual(actual, expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should classify voice message attachments', async () => {
|
it('should classify voice message attachments', async () => {
|
||||||
const input: IncomingMessage = {
|
const input = getDefaultMessage({
|
||||||
type: 'incoming',
|
type: 'incoming',
|
||||||
conversationId: 'foo',
|
conversationId: 'foo',
|
||||||
id: '11111111-1111-1111-1111-111111111111',
|
id: '11111111-1111-1111-1111-111111111111',
|
||||||
|
@ -110,8 +124,8 @@ describe('Message', () => {
|
||||||
size: 1111,
|
size: 1111,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
});
|
||||||
const expected: IncomingMessage = {
|
const expected = getDefaultMessage({
|
||||||
type: 'incoming',
|
type: 'incoming',
|
||||||
conversationId: 'foo',
|
conversationId: 'foo',
|
||||||
id: '11111111-1111-1111-1111-111111111111',
|
id: '11111111-1111-1111-1111-111111111111',
|
||||||
|
@ -130,14 +144,14 @@ describe('Message', () => {
|
||||||
hasAttachments: 1,
|
hasAttachments: 1,
|
||||||
hasVisualMediaAttachments: undefined,
|
hasVisualMediaAttachments: undefined,
|
||||||
hasFileAttachments: undefined,
|
hasFileAttachments: undefined,
|
||||||
};
|
});
|
||||||
|
|
||||||
const actual = await Message.initializeAttachmentMetadata(input);
|
const actual = await Message.initializeAttachmentMetadata(input);
|
||||||
assert.deepEqual(actual, expected);
|
assert.deepEqual(actual, expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not include long message attachments', async () => {
|
it('does not include long message attachments', async () => {
|
||||||
const input: IncomingMessage = {
|
const input = getDefaultMessage({
|
||||||
type: 'incoming',
|
type: 'incoming',
|
||||||
conversationId: 'foo',
|
conversationId: 'foo',
|
||||||
id: '11111111-1111-1111-1111-111111111111',
|
id: '11111111-1111-1111-1111-111111111111',
|
||||||
|
@ -152,8 +166,8 @@ describe('Message', () => {
|
||||||
size: 1111,
|
size: 1111,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
});
|
||||||
const expected: IncomingMessage = {
|
const expected = getDefaultMessage({
|
||||||
type: 'incoming',
|
type: 'incoming',
|
||||||
conversationId: 'foo',
|
conversationId: 'foo',
|
||||||
id: '11111111-1111-1111-1111-111111111111',
|
id: '11111111-1111-1111-1111-111111111111',
|
||||||
|
@ -171,14 +185,14 @@ describe('Message', () => {
|
||||||
hasAttachments: 0,
|
hasAttachments: 0,
|
||||||
hasVisualMediaAttachments: undefined,
|
hasVisualMediaAttachments: undefined,
|
||||||
hasFileAttachments: undefined,
|
hasFileAttachments: undefined,
|
||||||
};
|
});
|
||||||
|
|
||||||
const actual = await Message.initializeAttachmentMetadata(input);
|
const actual = await Message.initializeAttachmentMetadata(input);
|
||||||
assert.deepEqual(actual, expected);
|
assert.deepEqual(actual, expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('handles not attachments', async () => {
|
it('handles not attachments', async () => {
|
||||||
const input: IncomingMessage = {
|
const input = getDefaultMessage({
|
||||||
type: 'incoming',
|
type: 'incoming',
|
||||||
conversationId: 'foo',
|
conversationId: 'foo',
|
||||||
id: '11111111-1111-1111-1111-111111111111',
|
id: '11111111-1111-1111-1111-111111111111',
|
||||||
|
@ -186,8 +200,8 @@ describe('Message', () => {
|
||||||
received_at: 1523317140899,
|
received_at: 1523317140899,
|
||||||
sent_at: 1523317140800,
|
sent_at: 1523317140800,
|
||||||
attachments: [],
|
attachments: [],
|
||||||
};
|
});
|
||||||
const expected: IncomingMessage = {
|
const expected = getDefaultMessage({
|
||||||
type: 'incoming',
|
type: 'incoming',
|
||||||
conversationId: 'foo',
|
conversationId: 'foo',
|
||||||
id: '11111111-1111-1111-1111-111111111111',
|
id: '11111111-1111-1111-1111-111111111111',
|
||||||
|
@ -198,7 +212,7 @@ describe('Message', () => {
|
||||||
hasAttachments: 0,
|
hasAttachments: 0,
|
||||||
hasVisualMediaAttachments: undefined,
|
hasVisualMediaAttachments: undefined,
|
||||||
hasFileAttachments: undefined,
|
hasFileAttachments: undefined,
|
||||||
};
|
});
|
||||||
|
|
||||||
const actual = await Message.initializeAttachmentMetadata(input);
|
const actual = await Message.initializeAttachmentMetadata(input);
|
||||||
assert.deepEqual(actual, expected);
|
assert.deepEqual(actual, expected);
|
||||||
|
|
|
@ -24,8 +24,8 @@ import {
|
||||||
} from '../util/GoogleChrome';
|
} from '../util/GoogleChrome';
|
||||||
import type { LocalizerType } from './Util';
|
import type { LocalizerType } from './Util';
|
||||||
import { ThemeType } from './Util';
|
import { ThemeType } from './Util';
|
||||||
import * as GoogleChrome from '../util/GoogleChrome';
|
|
||||||
import { scaleImageToLevel } from '../util/scaleImageToLevel';
|
import { scaleImageToLevel } from '../util/scaleImageToLevel';
|
||||||
|
import * as GoogleChrome from '../util/GoogleChrome';
|
||||||
import { parseIntOrThrow } from '../util/parseIntOrThrow';
|
import { parseIntOrThrow } from '../util/parseIntOrThrow';
|
||||||
import { getValue } from '../RemoteConfig';
|
import { getValue } from '../RemoteConfig';
|
||||||
|
|
||||||
|
@ -58,6 +58,7 @@ export type AttachmentType = {
|
||||||
url?: string;
|
url?: string;
|
||||||
contentType: MIME.MIMEType;
|
contentType: MIME.MIMEType;
|
||||||
path: string;
|
path: string;
|
||||||
|
data?: Uint8Array;
|
||||||
};
|
};
|
||||||
screenshotData?: Uint8Array;
|
screenshotData?: Uint8Array;
|
||||||
screenshotPath?: string;
|
screenshotPath?: string;
|
||||||
|
@ -74,6 +75,9 @@ export type AttachmentType = {
|
||||||
/** Legacy field. Used only for downloading old attachments */
|
/** Legacy field. Used only for downloading old attachments */
|
||||||
id?: number;
|
id?: number;
|
||||||
|
|
||||||
|
/** Legacy field, used long ago for migrating attachments to disk. */
|
||||||
|
schemaVersion?: number;
|
||||||
|
|
||||||
/** Removed once we download the attachment */
|
/** Removed once we download the attachment */
|
||||||
digest?: string;
|
digest?: string;
|
||||||
key?: string;
|
key?: string;
|
||||||
|
@ -159,11 +163,12 @@ export type AttachmentDraftType =
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ThumbnailType = {
|
export type ThumbnailType = {
|
||||||
height: number;
|
height?: number;
|
||||||
width: number;
|
width?: number;
|
||||||
url?: string;
|
url?: string;
|
||||||
contentType: MIME.MIMEType;
|
contentType: MIME.MIMEType;
|
||||||
path: string;
|
path?: string;
|
||||||
|
data?: Uint8Array;
|
||||||
// Only used when quote needed to make an in-memory thumbnail
|
// Only used when quote needed to make an in-memory thumbnail
|
||||||
objectUrl?: string;
|
objectUrl?: string;
|
||||||
};
|
};
|
||||||
|
@ -432,16 +437,19 @@ export async function captureDimensionsAndScreenshot(
|
||||||
attachment: AttachmentType,
|
attachment: AttachmentType,
|
||||||
params: {
|
params: {
|
||||||
writeNewAttachmentData: (data: Uint8Array) => Promise<string>;
|
writeNewAttachmentData: (data: Uint8Array) => Promise<string>;
|
||||||
getAbsoluteAttachmentPath: (path: string) => Promise<string>;
|
getAbsoluteAttachmentPath: (path: string) => string;
|
||||||
makeObjectUrl: (
|
makeObjectUrl: (
|
||||||
data: Uint8Array | ArrayBuffer,
|
data: Uint8Array | ArrayBuffer,
|
||||||
contentType: MIME.MIMEType
|
contentType: MIME.MIMEType
|
||||||
) => string;
|
) => string;
|
||||||
revokeObjectUrl: (path: string) => void;
|
revokeObjectUrl: (path: string) => void;
|
||||||
getImageDimensions: (params: { objectUrl: string; logger: LoggerType }) => {
|
getImageDimensions: (params: {
|
||||||
|
objectUrl: string;
|
||||||
|
logger: LoggerType;
|
||||||
|
}) => Promise<{
|
||||||
width: number;
|
width: number;
|
||||||
height: number;
|
height: number;
|
||||||
};
|
}>;
|
||||||
makeImageThumbnail: (params: {
|
makeImageThumbnail: (params: {
|
||||||
size: number;
|
size: number;
|
||||||
objectUrl: string;
|
objectUrl: string;
|
||||||
|
@ -481,7 +489,7 @@ export async function captureDimensionsAndScreenshot(
|
||||||
return attachment;
|
return attachment;
|
||||||
}
|
}
|
||||||
|
|
||||||
const absolutePath = await getAbsoluteAttachmentPath(attachment.path);
|
const absolutePath = getAbsoluteAttachmentPath(attachment.path);
|
||||||
|
|
||||||
if (GoogleChrome.isImageTypeSupported(contentType)) {
|
if (GoogleChrome.isImageTypeSupported(contentType)) {
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -191,12 +191,12 @@ export function parseAndWriteAvatar(
|
||||||
contact: EmbeddedContactType,
|
contact: EmbeddedContactType,
|
||||||
context: {
|
context: {
|
||||||
message: MessageAttributesType;
|
message: MessageAttributesType;
|
||||||
regionCode: string;
|
getRegionCode: () => string | undefined;
|
||||||
logger: Pick<LoggerType, 'error'>;
|
logger: Pick<LoggerType, 'error'>;
|
||||||
writeNewAttachmentData: (data: Uint8Array) => Promise<string>;
|
writeNewAttachmentData: (data: Uint8Array) => Promise<string>;
|
||||||
}
|
}
|
||||||
): Promise<EmbeddedContactType> => {
|
): Promise<EmbeddedContactType> => {
|
||||||
const { message, regionCode, logger } = context;
|
const { message, getRegionCode, logger } = context;
|
||||||
const { avatar } = contact;
|
const { avatar } = contact;
|
||||||
|
|
||||||
const contactWithUpdatedAvatar =
|
const contactWithUpdatedAvatar =
|
||||||
|
@ -212,7 +212,7 @@ export function parseAndWriteAvatar(
|
||||||
|
|
||||||
// eliminates empty numbers, emails, and addresses; adds type if not provided
|
// eliminates empty numbers, emails, and addresses; adds type if not provided
|
||||||
const parsedContact = parseContact(contactWithUpdatedAvatar, {
|
const parsedContact = parseContact(contactWithUpdatedAvatar, {
|
||||||
regionCode,
|
regionCode: getRegionCode(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const error = _validate(parsedContact, {
|
const error = _validate(parsedContact, {
|
||||||
|
@ -231,7 +231,7 @@ export function parseAndWriteAvatar(
|
||||||
|
|
||||||
function parseContact(
|
function parseContact(
|
||||||
contact: EmbeddedContactType,
|
contact: EmbeddedContactType,
|
||||||
{ regionCode }: { regionCode: string }
|
{ regionCode }: { regionCode: string | undefined }
|
||||||
): EmbeddedContactType {
|
): EmbeddedContactType {
|
||||||
const boundParsePhone = (phoneNumber: Phone): Phone | undefined =>
|
const boundParsePhone = (phoneNumber: Phone): Phone | undefined =>
|
||||||
parsePhoneItem(phoneNumber, { regionCode });
|
parsePhoneItem(phoneNumber, { regionCode });
|
||||||
|
@ -294,7 +294,7 @@ export function _validate(
|
||||||
|
|
||||||
function parsePhoneItem(
|
function parsePhoneItem(
|
||||||
item: Phone,
|
item: Phone,
|
||||||
{ regionCode }: { regionCode: string }
|
{ regionCode }: { regionCode: string | undefined }
|
||||||
): Phone | undefined {
|
): Phone | undefined {
|
||||||
if (!item.value) {
|
if (!item.value) {
|
||||||
return undefined;
|
return undefined;
|
||||||
|
|
|
@ -75,13 +75,13 @@ export type ProfileChangeNotificationMessage = Readonly<
|
||||||
ExpirationTimerUpdate
|
ExpirationTimerUpdate
|
||||||
>;
|
>;
|
||||||
|
|
||||||
type SharedMessageProperties = Readonly<{
|
export type SharedMessageProperties = Readonly<{
|
||||||
conversationId: string;
|
conversationId: string;
|
||||||
sent_at: number;
|
sent_at: number;
|
||||||
timestamp: number;
|
timestamp: number;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
type ExpirationTimerUpdate = Partial<
|
export type ExpirationTimerUpdate = Partial<
|
||||||
Readonly<{
|
Readonly<{
|
||||||
expirationTimerUpdate: Readonly<{
|
expirationTimerUpdate: Readonly<{
|
||||||
expireTimer: number;
|
expireTimer: number;
|
||||||
|
@ -91,7 +91,7 @@ type ExpirationTimerUpdate = Partial<
|
||||||
}>
|
}>
|
||||||
>;
|
>;
|
||||||
|
|
||||||
type MessageSchemaVersion5 = Partial<
|
export type MessageSchemaVersion5 = Partial<
|
||||||
Readonly<{
|
Readonly<{
|
||||||
hasAttachments: IndexableBoolean;
|
hasAttachments: IndexableBoolean;
|
||||||
hasVisualMediaAttachments: IndexablePresence;
|
hasVisualMediaAttachments: IndexablePresence;
|
||||||
|
@ -99,7 +99,7 @@ type MessageSchemaVersion5 = Partial<
|
||||||
}>
|
}>
|
||||||
>;
|
>;
|
||||||
|
|
||||||
type MessageSchemaVersion6 = Partial<
|
export type MessageSchemaVersion6 = Partial<
|
||||||
Readonly<{
|
Readonly<{
|
||||||
contact: Array<EmbeddedContactType>;
|
contact: Array<EmbeddedContactType>;
|
||||||
}>
|
}>
|
||||||
|
|
|
@ -1,19 +1,80 @@
|
||||||
// Copyright 2018-2021 Signal Messenger, LLC
|
// Copyright 2018-2021 Signal Messenger, LLC
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
const { isFunction, isObject, isString, omit } = require('lodash');
|
import { isFunction, isObject, isString, omit } from 'lodash';
|
||||||
|
|
||||||
const Contact = require('../../../ts/types/EmbeddedContact');
|
import * as Contact from './EmbeddedContact';
|
||||||
const Attachment = require('../../../ts/types/Attachment');
|
import type { AttachmentType } from './Attachment';
|
||||||
const Errors = require('../../../ts/types/errors');
|
import {
|
||||||
const SchemaVersion = require('../../../ts/types/SchemaVersion');
|
autoOrientJPEG,
|
||||||
const {
|
captureDimensionsAndScreenshot,
|
||||||
initializeAttachmentMetadata,
|
hasData,
|
||||||
} = require('../../../ts/types/message/initializeAttachmentMetadata');
|
migrateDataToFileSystem,
|
||||||
const MessageTS = require('../../../ts/types/Message');
|
removeSchemaVersion,
|
||||||
|
replaceUnicodeOrderOverrides,
|
||||||
|
replaceUnicodeV2,
|
||||||
|
} from './Attachment';
|
||||||
|
import * as Errors from './errors';
|
||||||
|
import * as SchemaVersion from './SchemaVersion';
|
||||||
|
import { initializeAttachmentMetadata } from './message/initializeAttachmentMetadata';
|
||||||
|
|
||||||
const GROUP = 'group';
|
import type * as MIME from './MIME';
|
||||||
const PRIVATE = 'private';
|
import type { LoggerType } from './Logging';
|
||||||
|
import type { EmbeddedContactType } from './EmbeddedContact';
|
||||||
|
|
||||||
|
import type {
|
||||||
|
MessageAttributesType,
|
||||||
|
PreviewMessageType,
|
||||||
|
PreviewType,
|
||||||
|
QuotedMessageType,
|
||||||
|
StickerMessageType,
|
||||||
|
} from '../model-types.d';
|
||||||
|
|
||||||
|
export { hasExpiration } from './Message';
|
||||||
|
|
||||||
|
export const GROUP = 'group';
|
||||||
|
export const PRIVATE = 'private';
|
||||||
|
|
||||||
|
export type ContextType = {
|
||||||
|
getAbsoluteAttachmentPath: (path: string) => string;
|
||||||
|
getAbsoluteStickerPath: (path: string) => string;
|
||||||
|
getImageDimensions: (params: {
|
||||||
|
objectUrl: string;
|
||||||
|
logger: LoggerType;
|
||||||
|
}) => Promise<{
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
}>;
|
||||||
|
getRegionCode: () => string;
|
||||||
|
logger: LoggerType;
|
||||||
|
makeImageThumbnail: (params: {
|
||||||
|
size: number;
|
||||||
|
objectUrl: string;
|
||||||
|
contentType: MIME.MIMEType;
|
||||||
|
logger: LoggerType;
|
||||||
|
}) => Promise<Blob>;
|
||||||
|
makeObjectUrl: (
|
||||||
|
data: Uint8Array | ArrayBuffer,
|
||||||
|
contentType: MIME.MIMEType
|
||||||
|
) => string;
|
||||||
|
makeVideoScreenshot: (params: {
|
||||||
|
objectUrl: string;
|
||||||
|
contentType: MIME.MIMEType;
|
||||||
|
logger: LoggerType;
|
||||||
|
}) => Promise<Blob>;
|
||||||
|
maxVersion?: number;
|
||||||
|
revokeObjectUrl: (objectUrl: string) => void;
|
||||||
|
writeNewAttachmentData: (data: Uint8Array) => Promise<string>;
|
||||||
|
writeNewStickerData: (sticker: StickerMessageType) => Promise<string>;
|
||||||
|
};
|
||||||
|
|
||||||
|
type WriteExistingAttachmentDataType = (
|
||||||
|
attachment: Pick<AttachmentType, 'data' | 'path'>
|
||||||
|
) => Promise<void>;
|
||||||
|
|
||||||
|
export type ContextWithMessageType = ContextType & {
|
||||||
|
message: MessageAttributesType;
|
||||||
|
};
|
||||||
|
|
||||||
// Schema version history
|
// Schema version history
|
||||||
//
|
//
|
||||||
|
@ -55,32 +116,30 @@ const PRIVATE = 'private';
|
||||||
|
|
||||||
const INITIAL_SCHEMA_VERSION = 0;
|
const INITIAL_SCHEMA_VERSION = 0;
|
||||||
|
|
||||||
// Public API
|
|
||||||
exports.GROUP = GROUP;
|
|
||||||
exports.PRIVATE = PRIVATE;
|
|
||||||
|
|
||||||
// Placeholder until we have stronger preconditions:
|
// Placeholder until we have stronger preconditions:
|
||||||
exports.isValid = () => true;
|
export const isValid = (_message: MessageAttributesType): boolean => true;
|
||||||
|
|
||||||
// Schema
|
// Schema
|
||||||
exports.initializeSchemaVersion = ({ message, logger }) => {
|
export const initializeSchemaVersion = ({
|
||||||
|
message,
|
||||||
|
logger,
|
||||||
|
}: {
|
||||||
|
message: MessageAttributesType;
|
||||||
|
logger: LoggerType;
|
||||||
|
}): MessageAttributesType => {
|
||||||
const isInitialized =
|
const isInitialized =
|
||||||
SchemaVersion.isValid(message.schemaVersion) && message.schemaVersion >= 1;
|
SchemaVersion.isValid(message.schemaVersion) && message.schemaVersion >= 1;
|
||||||
if (isInitialized) {
|
if (isInitialized) {
|
||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
|
|
||||||
const numAttachments = Array.isArray(message.attachments)
|
const firstAttachment = message?.attachments?.[0];
|
||||||
? message.attachments.length
|
if (!firstAttachment) {
|
||||||
: 0;
|
|
||||||
const hasAttachments = numAttachments > 0;
|
|
||||||
if (!hasAttachments) {
|
|
||||||
return { ...message, schemaVersion: INITIAL_SCHEMA_VERSION };
|
return { ...message, schemaVersion: INITIAL_SCHEMA_VERSION };
|
||||||
}
|
}
|
||||||
|
|
||||||
// All attachments should have the same schema version, so we just pick
|
// All attachments should have the same schema version, so we just pick
|
||||||
// the first one:
|
// the first one:
|
||||||
const firstAttachment = message.attachments[0];
|
|
||||||
const inheritedSchemaVersion = SchemaVersion.isValid(
|
const inheritedSchemaVersion = SchemaVersion.isValid(
|
||||||
firstAttachment.schemaVersion
|
firstAttachment.schemaVersion
|
||||||
)
|
)
|
||||||
|
@ -89,9 +148,10 @@ exports.initializeSchemaVersion = ({ message, logger }) => {
|
||||||
const messageWithInitialSchema = {
|
const messageWithInitialSchema = {
|
||||||
...message,
|
...message,
|
||||||
schemaVersion: inheritedSchemaVersion,
|
schemaVersion: inheritedSchemaVersion,
|
||||||
attachments: message.attachments.map(attachment =>
|
attachments:
|
||||||
Attachment.removeSchemaVersion({ attachment, logger })
|
message?.attachments?.map(attachment =>
|
||||||
),
|
removeSchemaVersion({ attachment, logger })
|
||||||
|
) || [],
|
||||||
};
|
};
|
||||||
|
|
||||||
return messageWithInitialSchema;
|
return messageWithInitialSchema;
|
||||||
|
@ -101,7 +161,19 @@ exports.initializeSchemaVersion = ({ message, logger }) => {
|
||||||
// type UpgradeStep = (Message, Context) -> Promise Message
|
// type UpgradeStep = (Message, Context) -> Promise Message
|
||||||
|
|
||||||
// SchemaVersion -> UpgradeStep -> UpgradeStep
|
// SchemaVersion -> UpgradeStep -> UpgradeStep
|
||||||
exports._withSchemaVersion = ({ schemaVersion, upgrade }) => {
|
export const _withSchemaVersion = ({
|
||||||
|
schemaVersion,
|
||||||
|
upgrade,
|
||||||
|
}: {
|
||||||
|
schemaVersion: number;
|
||||||
|
upgrade: (
|
||||||
|
message: MessageAttributesType,
|
||||||
|
context: ContextType
|
||||||
|
) => Promise<MessageAttributesType>;
|
||||||
|
}): ((
|
||||||
|
message: MessageAttributesType,
|
||||||
|
context: ContextType
|
||||||
|
) => Promise<MessageAttributesType>) => {
|
||||||
if (!SchemaVersion.isValid(schemaVersion)) {
|
if (!SchemaVersion.isValid(schemaVersion)) {
|
||||||
throw new TypeError('_withSchemaVersion: schemaVersion is invalid');
|
throw new TypeError('_withSchemaVersion: schemaVersion is invalid');
|
||||||
}
|
}
|
||||||
|
@ -109,7 +181,7 @@ exports._withSchemaVersion = ({ schemaVersion, upgrade }) => {
|
||||||
throw new TypeError('_withSchemaVersion: upgrade must be a function');
|
throw new TypeError('_withSchemaVersion: upgrade must be a function');
|
||||||
}
|
}
|
||||||
|
|
||||||
return async (message, context) => {
|
return async (message: MessageAttributesType, context: ContextType) => {
|
||||||
if (!context || !isObject(context.logger)) {
|
if (!context || !isObject(context.logger)) {
|
||||||
throw new TypeError(
|
throw new TypeError(
|
||||||
'_withSchemaVersion: context must have logger object'
|
'_withSchemaVersion: context must have logger object'
|
||||||
|
@ -117,7 +189,7 @@ exports._withSchemaVersion = ({ schemaVersion, upgrade }) => {
|
||||||
}
|
}
|
||||||
const { logger } = context;
|
const { logger } = context;
|
||||||
|
|
||||||
if (!exports.isValid(message)) {
|
if (!isValid(message)) {
|
||||||
logger.error(
|
logger.error(
|
||||||
'Message._withSchemaVersion: Invalid input message:',
|
'Message._withSchemaVersion: Invalid input message:',
|
||||||
message
|
message
|
||||||
|
@ -125,7 +197,7 @@ exports._withSchemaVersion = ({ schemaVersion, upgrade }) => {
|
||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
|
|
||||||
const isAlreadyUpgraded = message.schemaVersion >= schemaVersion;
|
const isAlreadyUpgraded = (message.schemaVersion || 0) >= schemaVersion;
|
||||||
if (isAlreadyUpgraded) {
|
if (isAlreadyUpgraded) {
|
||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
|
@ -152,7 +224,7 @@ exports._withSchemaVersion = ({ schemaVersion, upgrade }) => {
|
||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!exports.isValid(upgradedMessage)) {
|
if (!isValid(upgradedMessage)) {
|
||||||
logger.error(
|
logger.error(
|
||||||
'Message._withSchemaVersion: Invalid upgraded message:',
|
'Message._withSchemaVersion: Invalid upgraded message:',
|
||||||
upgradedMessage
|
upgradedMessage
|
||||||
|
@ -168,34 +240,59 @@ exports._withSchemaVersion = ({ schemaVersion, upgrade }) => {
|
||||||
// _mapAttachments :: (Attachment -> Promise Attachment) ->
|
// _mapAttachments :: (Attachment -> Promise Attachment) ->
|
||||||
// (Message, Context) ->
|
// (Message, Context) ->
|
||||||
// Promise Message
|
// Promise Message
|
||||||
exports._mapAttachments = upgradeAttachment => async (message, context) => {
|
export type UpgradeAttachmentType = (
|
||||||
const upgradeWithContext = attachment =>
|
attachment: AttachmentType,
|
||||||
upgradeAttachment(attachment, context, message);
|
context: ContextType,
|
||||||
const attachments = await Promise.all(
|
message: MessageAttributesType
|
||||||
(message.attachments || []).map(upgradeWithContext)
|
) => Promise<AttachmentType>;
|
||||||
);
|
|
||||||
return { ...message, attachments };
|
export const _mapAttachments =
|
||||||
};
|
(upgradeAttachment: UpgradeAttachmentType) =>
|
||||||
|
async (
|
||||||
|
message: MessageAttributesType,
|
||||||
|
context: ContextType
|
||||||
|
): Promise<MessageAttributesType> => {
|
||||||
|
const upgradeWithContext = (attachment: AttachmentType) =>
|
||||||
|
upgradeAttachment(attachment, context, message);
|
||||||
|
const attachments = await Promise.all(
|
||||||
|
(message.attachments || []).map(upgradeWithContext)
|
||||||
|
);
|
||||||
|
return { ...message, attachments };
|
||||||
|
};
|
||||||
|
|
||||||
// Public API
|
// Public API
|
||||||
// _mapContact :: (Contact -> Promise Contact) ->
|
// _mapContact :: (Contact -> Promise Contact) ->
|
||||||
// (Message, Context) ->
|
// (Message, Context) ->
|
||||||
// Promise Message
|
// Promise Message
|
||||||
exports._mapContact = upgradeContact => async (message, context) => {
|
|
||||||
const contextWithMessage = { ...context, message };
|
export type UpgradeContactType = (
|
||||||
const upgradeWithContext = contact =>
|
contact: EmbeddedContactType,
|
||||||
upgradeContact(contact, contextWithMessage);
|
contextWithMessage: ContextWithMessageType
|
||||||
const contact = await Promise.all(
|
) => Promise<EmbeddedContactType>;
|
||||||
(message.contact || []).map(upgradeWithContext)
|
export const _mapContact =
|
||||||
);
|
(upgradeContact: UpgradeContactType) =>
|
||||||
return { ...message, contact };
|
async (
|
||||||
};
|
message: MessageAttributesType,
|
||||||
|
context: ContextType
|
||||||
|
): Promise<MessageAttributesType> => {
|
||||||
|
const contextWithMessage = { ...context, message };
|
||||||
|
const upgradeWithContext = (contact: EmbeddedContactType) =>
|
||||||
|
upgradeContact(contact, contextWithMessage);
|
||||||
|
const contact = await Promise.all(
|
||||||
|
(message.contact || []).map(upgradeWithContext)
|
||||||
|
);
|
||||||
|
return { ...message, contact };
|
||||||
|
};
|
||||||
|
|
||||||
// _mapQuotedAttachments :: (QuotedAttachment -> Promise QuotedAttachment) ->
|
// _mapQuotedAttachments :: (QuotedAttachment -> Promise QuotedAttachment) ->
|
||||||
// (Message, Context) ->
|
// (Message, Context) ->
|
||||||
// Promise Message
|
// Promise Message
|
||||||
exports._mapQuotedAttachments =
|
export const _mapQuotedAttachments =
|
||||||
upgradeAttachment => async (message, context) => {
|
(upgradeAttachment: UpgradeAttachmentType) =>
|
||||||
|
async (
|
||||||
|
message: MessageAttributesType,
|
||||||
|
context: ContextType
|
||||||
|
): Promise<MessageAttributesType> => {
|
||||||
if (!message.quote) {
|
if (!message.quote) {
|
||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
|
@ -203,13 +300,19 @@ exports._mapQuotedAttachments =
|
||||||
throw new Error('_mapQuotedAttachments: context must have logger object');
|
throw new Error('_mapQuotedAttachments: context must have logger object');
|
||||||
}
|
}
|
||||||
|
|
||||||
const upgradeWithContext = async attachment => {
|
const upgradeWithContext = async (
|
||||||
|
attachment: AttachmentType
|
||||||
|
): Promise<AttachmentType> => {
|
||||||
const { thumbnail } = attachment;
|
const { thumbnail } = attachment;
|
||||||
if (!thumbnail) {
|
if (!thumbnail) {
|
||||||
return attachment;
|
return attachment;
|
||||||
}
|
}
|
||||||
|
|
||||||
const upgradedThumbnail = await upgradeAttachment(thumbnail, context);
|
const upgradedThumbnail = await upgradeAttachment(
|
||||||
|
thumbnail as AttachmentType,
|
||||||
|
context,
|
||||||
|
message
|
||||||
|
);
|
||||||
return { ...attachment, thumbnail: upgradedThumbnail };
|
return { ...attachment, thumbnail: upgradedThumbnail };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -225,8 +328,12 @@ exports._mapQuotedAttachments =
|
||||||
// _mapPreviewAttachments :: (PreviewAttachment -> Promise PreviewAttachment) ->
|
// _mapPreviewAttachments :: (PreviewAttachment -> Promise PreviewAttachment) ->
|
||||||
// (Message, Context) ->
|
// (Message, Context) ->
|
||||||
// Promise Message
|
// Promise Message
|
||||||
exports._mapPreviewAttachments =
|
export const _mapPreviewAttachments =
|
||||||
upgradeAttachment => async (message, context) => {
|
(upgradeAttachment: UpgradeAttachmentType) =>
|
||||||
|
async (
|
||||||
|
message: MessageAttributesType,
|
||||||
|
context: ContextType
|
||||||
|
): Promise<MessageAttributesType> => {
|
||||||
if (!message.preview) {
|
if (!message.preview) {
|
||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
|
@ -236,13 +343,13 @@ exports._mapPreviewAttachments =
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const upgradeWithContext = async preview => {
|
const upgradeWithContext = async (preview: PreviewType) => {
|
||||||
const { image } = preview;
|
const { image } = preview;
|
||||||
if (!image) {
|
if (!image) {
|
||||||
return preview;
|
return preview;
|
||||||
}
|
}
|
||||||
|
|
||||||
const upgradedImage = await upgradeAttachment(image, context);
|
const upgradedImage = await upgradeAttachment(image, context, message);
|
||||||
return { ...preview, image: upgradedImage };
|
return { ...preview, image: upgradedImage };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -252,58 +359,59 @@ exports._mapPreviewAttachments =
|
||||||
return { ...message, preview };
|
return { ...message, preview };
|
||||||
};
|
};
|
||||||
|
|
||||||
const toVersion0 = async (message, context) =>
|
const toVersion0 = async (
|
||||||
exports.initializeSchemaVersion({ message, logger: context.logger });
|
message: MessageAttributesType,
|
||||||
const toVersion1 = exports._withSchemaVersion({
|
context: ContextType
|
||||||
|
) => initializeSchemaVersion({ message, logger: context.logger });
|
||||||
|
const toVersion1 = _withSchemaVersion({
|
||||||
schemaVersion: 1,
|
schemaVersion: 1,
|
||||||
upgrade: exports._mapAttachments(Attachment.autoOrientJPEG),
|
upgrade: _mapAttachments(autoOrientJPEG),
|
||||||
});
|
});
|
||||||
const toVersion2 = exports._withSchemaVersion({
|
const toVersion2 = _withSchemaVersion({
|
||||||
schemaVersion: 2,
|
schemaVersion: 2,
|
||||||
upgrade: exports._mapAttachments(Attachment.replaceUnicodeOrderOverrides),
|
upgrade: _mapAttachments(replaceUnicodeOrderOverrides),
|
||||||
});
|
});
|
||||||
const toVersion3 = exports._withSchemaVersion({
|
const toVersion3 = _withSchemaVersion({
|
||||||
schemaVersion: 3,
|
schemaVersion: 3,
|
||||||
upgrade: exports._mapAttachments(Attachment.migrateDataToFileSystem),
|
upgrade: _mapAttachments(migrateDataToFileSystem),
|
||||||
});
|
});
|
||||||
const toVersion4 = exports._withSchemaVersion({
|
const toVersion4 = _withSchemaVersion({
|
||||||
schemaVersion: 4,
|
schemaVersion: 4,
|
||||||
upgrade: exports._mapQuotedAttachments(Attachment.migrateDataToFileSystem),
|
upgrade: _mapQuotedAttachments(migrateDataToFileSystem),
|
||||||
});
|
});
|
||||||
const toVersion5 = exports._withSchemaVersion({
|
const toVersion5 = _withSchemaVersion({
|
||||||
schemaVersion: 5,
|
schemaVersion: 5,
|
||||||
upgrade: initializeAttachmentMetadata,
|
upgrade: initializeAttachmentMetadata,
|
||||||
});
|
});
|
||||||
const toVersion6 = exports._withSchemaVersion({
|
const toVersion6 = _withSchemaVersion({
|
||||||
schemaVersion: 6,
|
schemaVersion: 6,
|
||||||
upgrade: exports._mapContact(
|
upgrade: _mapContact(Contact.parseAndWriteAvatar(migrateDataToFileSystem)),
|
||||||
Contact.parseAndWriteAvatar(Attachment.migrateDataToFileSystem)
|
|
||||||
),
|
|
||||||
});
|
});
|
||||||
// IMPORTANT: We’ve updated our definition of `initializeAttachmentMetadata`, so
|
// IMPORTANT: We’ve updated our definition of `initializeAttachmentMetadata`, so
|
||||||
// we need to run it again on existing items that have previously been incorrectly
|
// we need to run it again on existing items that have previously been incorrectly
|
||||||
// classified:
|
// classified:
|
||||||
const toVersion7 = exports._withSchemaVersion({
|
const toVersion7 = _withSchemaVersion({
|
||||||
schemaVersion: 7,
|
schemaVersion: 7,
|
||||||
upgrade: initializeAttachmentMetadata,
|
upgrade: initializeAttachmentMetadata,
|
||||||
});
|
});
|
||||||
|
|
||||||
const toVersion8 = exports._withSchemaVersion({
|
const toVersion8 = _withSchemaVersion({
|
||||||
schemaVersion: 8,
|
schemaVersion: 8,
|
||||||
upgrade: exports._mapAttachments(Attachment.captureDimensionsAndScreenshot),
|
upgrade: _mapAttachments(captureDimensionsAndScreenshot),
|
||||||
});
|
});
|
||||||
|
|
||||||
const toVersion9 = exports._withSchemaVersion({
|
const toVersion9 = _withSchemaVersion({
|
||||||
schemaVersion: 9,
|
schemaVersion: 9,
|
||||||
upgrade: exports._mapAttachments(Attachment.replaceUnicodeV2),
|
upgrade: _mapAttachments(replaceUnicodeV2),
|
||||||
});
|
});
|
||||||
const toVersion10 = exports._withSchemaVersion({
|
const toVersion10 = _withSchemaVersion({
|
||||||
schemaVersion: 10,
|
schemaVersion: 10,
|
||||||
upgrade: async (message, context) => {
|
upgrade: async (message, context) => {
|
||||||
const processPreviews = exports._mapPreviewAttachments(
|
const processPreviews = _mapPreviewAttachments(migrateDataToFileSystem);
|
||||||
Attachment.migrateDataToFileSystem
|
const processSticker = async (
|
||||||
);
|
stickerMessage: MessageAttributesType,
|
||||||
const processSticker = async (stickerMessage, stickerContext) => {
|
stickerContext: ContextType
|
||||||
|
): Promise<MessageAttributesType> => {
|
||||||
const { sticker } = stickerMessage;
|
const { sticker } = stickerMessage;
|
||||||
if (!sticker || !sticker.data || !sticker.data.data) {
|
if (!sticker || !sticker.data || !sticker.data.data) {
|
||||||
return stickerMessage;
|
return stickerMessage;
|
||||||
|
@ -313,10 +421,7 @@ const toVersion10 = exports._withSchemaVersion({
|
||||||
...stickerMessage,
|
...stickerMessage,
|
||||||
sticker: {
|
sticker: {
|
||||||
...sticker,
|
...sticker,
|
||||||
data: await Attachment.migrateDataToFileSystem(
|
data: await migrateDataToFileSystem(sticker.data, stickerContext),
|
||||||
sticker.data,
|
|
||||||
stickerContext
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -341,27 +446,29 @@ const VERSIONS = [
|
||||||
toVersion9,
|
toVersion9,
|
||||||
toVersion10,
|
toVersion10,
|
||||||
];
|
];
|
||||||
exports.CURRENT_SCHEMA_VERSION = VERSIONS.length - 1;
|
export const CURRENT_SCHEMA_VERSION = VERSIONS.length - 1;
|
||||||
|
|
||||||
// We need dimensions and screenshots for images for proper display
|
// We need dimensions and screenshots for images for proper display
|
||||||
exports.VERSION_NEEDED_FOR_DISPLAY = 9;
|
export const VERSION_NEEDED_FOR_DISPLAY = 9;
|
||||||
|
|
||||||
// UpgradeStep
|
// UpgradeStep
|
||||||
exports.upgradeSchema = async (
|
export const upgradeSchema = async (
|
||||||
rawMessage,
|
rawMessage: MessageAttributesType,
|
||||||
{
|
{
|
||||||
writeNewAttachmentData,
|
writeNewAttachmentData,
|
||||||
getRegionCode,
|
getRegionCode,
|
||||||
getAbsoluteAttachmentPath,
|
getAbsoluteAttachmentPath,
|
||||||
|
getAbsoluteStickerPath,
|
||||||
makeObjectUrl,
|
makeObjectUrl,
|
||||||
revokeObjectUrl,
|
revokeObjectUrl,
|
||||||
getImageDimensions,
|
getImageDimensions,
|
||||||
makeImageThumbnail,
|
makeImageThumbnail,
|
||||||
makeVideoScreenshot,
|
makeVideoScreenshot,
|
||||||
|
writeNewStickerData,
|
||||||
logger,
|
logger,
|
||||||
maxVersion = exports.CURRENT_SCHEMA_VERSION,
|
maxVersion = CURRENT_SCHEMA_VERSION,
|
||||||
} = {}
|
}: ContextType
|
||||||
) => {
|
): Promise<MessageAttributesType> => {
|
||||||
if (!isFunction(writeNewAttachmentData)) {
|
if (!isFunction(writeNewAttachmentData)) {
|
||||||
throw new TypeError('context.writeNewAttachmentData is required');
|
throw new TypeError('context.writeNewAttachmentData is required');
|
||||||
}
|
}
|
||||||
|
@ -389,6 +496,12 @@ exports.upgradeSchema = async (
|
||||||
if (!isObject(logger)) {
|
if (!isObject(logger)) {
|
||||||
throw new TypeError('context.logger is required');
|
throw new TypeError('context.logger is required');
|
||||||
}
|
}
|
||||||
|
if (!isFunction(getAbsoluteStickerPath)) {
|
||||||
|
throw new TypeError('context.getAbsoluteStickerPath is required');
|
||||||
|
}
|
||||||
|
if (!isFunction(writeNewStickerData)) {
|
||||||
|
throw new TypeError('context.writeNewStickerData is required');
|
||||||
|
}
|
||||||
|
|
||||||
let message = rawMessage;
|
let message = rawMessage;
|
||||||
for (let index = 0, max = VERSIONS.length; index < max; index += 1) {
|
for (let index = 0, max = VERSIONS.length; index < max; index += 1) {
|
||||||
|
@ -402,7 +515,6 @@ exports.upgradeSchema = async (
|
||||||
// eslint-disable-next-line no-await-in-loop
|
// eslint-disable-next-line no-await-in-loop
|
||||||
message = await currentVersion(message, {
|
message = await currentVersion(message, {
|
||||||
writeNewAttachmentData,
|
writeNewAttachmentData,
|
||||||
regionCode: getRegionCode(),
|
|
||||||
getAbsoluteAttachmentPath,
|
getAbsoluteAttachmentPath,
|
||||||
makeObjectUrl,
|
makeObjectUrl,
|
||||||
revokeObjectUrl,
|
revokeObjectUrl,
|
||||||
|
@ -410,6 +522,9 @@ exports.upgradeSchema = async (
|
||||||
makeImageThumbnail,
|
makeImageThumbnail,
|
||||||
makeVideoScreenshot,
|
makeVideoScreenshot,
|
||||||
logger,
|
logger,
|
||||||
|
getAbsoluteStickerPath,
|
||||||
|
getRegionCode,
|
||||||
|
writeNewStickerData,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -418,8 +533,8 @@ exports.upgradeSchema = async (
|
||||||
|
|
||||||
// Runs on attachments outside of the schema upgrade process, since attachments are
|
// Runs on attachments outside of the schema upgrade process, since attachments are
|
||||||
// downloaded out of band.
|
// downloaded out of band.
|
||||||
exports.processNewAttachment = async (
|
export const processNewAttachment = async (
|
||||||
attachment,
|
attachment: AttachmentType,
|
||||||
{
|
{
|
||||||
writeNewAttachmentData,
|
writeNewAttachmentData,
|
||||||
getAbsoluteAttachmentPath,
|
getAbsoluteAttachmentPath,
|
||||||
|
@ -429,8 +544,8 @@ exports.processNewAttachment = async (
|
||||||
makeImageThumbnail,
|
makeImageThumbnail,
|
||||||
makeVideoScreenshot,
|
makeVideoScreenshot,
|
||||||
logger,
|
logger,
|
||||||
} = {}
|
}: ContextType
|
||||||
) => {
|
): Promise<AttachmentType> => {
|
||||||
if (!isFunction(writeNewAttachmentData)) {
|
if (!isFunction(writeNewAttachmentData)) {
|
||||||
throw new TypeError('context.writeNewAttachmentData is required');
|
throw new TypeError('context.writeNewAttachmentData is required');
|
||||||
}
|
}
|
||||||
|
@ -456,16 +571,13 @@ exports.processNewAttachment = async (
|
||||||
throw new TypeError('context.logger is required');
|
throw new TypeError('context.logger is required');
|
||||||
}
|
}
|
||||||
|
|
||||||
const rotatedAttachment = await Attachment.autoOrientJPEG(
|
const rotatedAttachment = await autoOrientJPEG(attachment, undefined, {
|
||||||
attachment,
|
isIncoming: true,
|
||||||
undefined,
|
});
|
||||||
{ isIncoming: true }
|
const onDiskAttachment = await migrateDataToFileSystem(rotatedAttachment, {
|
||||||
);
|
writeNewAttachmentData,
|
||||||
const onDiskAttachment = await Attachment.migrateDataToFileSystem(
|
});
|
||||||
rotatedAttachment,
|
const finalAttachment = await captureDimensionsAndScreenshot(
|
||||||
{ writeNewAttachmentData }
|
|
||||||
);
|
|
||||||
const finalAttachment = await Attachment.captureDimensionsAndScreenshot(
|
|
||||||
onDiskAttachment,
|
onDiskAttachment,
|
||||||
{
|
{
|
||||||
writeNewAttachmentData,
|
writeNewAttachmentData,
|
||||||
|
@ -482,15 +594,15 @@ exports.processNewAttachment = async (
|
||||||
return finalAttachment;
|
return finalAttachment;
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.processNewSticker = async (
|
export const processNewSticker = async (
|
||||||
stickerData,
|
stickerData: StickerMessageType,
|
||||||
{
|
{
|
||||||
writeNewStickerData,
|
writeNewStickerData,
|
||||||
getAbsoluteStickerPath,
|
getAbsoluteStickerPath,
|
||||||
getImageDimensions,
|
getImageDimensions,
|
||||||
logger,
|
logger,
|
||||||
} = {}
|
}: ContextType
|
||||||
) => {
|
): Promise<{ path: string; width: number; height: number }> => {
|
||||||
if (!isFunction(writeNewStickerData)) {
|
if (!isFunction(writeNewStickerData)) {
|
||||||
throw new TypeError('context.writeNewStickerData is required');
|
throw new TypeError('context.writeNewStickerData is required');
|
||||||
}
|
}
|
||||||
|
@ -519,25 +631,41 @@ exports.processNewSticker = async (
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.createAttachmentLoader = loadAttachmentData => {
|
type LoadAttachmentType = (
|
||||||
|
attachment: AttachmentType
|
||||||
|
) => Promise<AttachmentType>;
|
||||||
|
|
||||||
|
export const createAttachmentLoader = (
|
||||||
|
loadAttachmentData: LoadAttachmentType
|
||||||
|
): ((message: MessageAttributesType) => Promise<MessageAttributesType>) => {
|
||||||
if (!isFunction(loadAttachmentData)) {
|
if (!isFunction(loadAttachmentData)) {
|
||||||
throw new TypeError(
|
throw new TypeError(
|
||||||
'createAttachmentLoader: loadAttachmentData is required'
|
'createAttachmentLoader: loadAttachmentData is required'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return async message => ({
|
return async (
|
||||||
|
message: MessageAttributesType
|
||||||
|
): Promise<MessageAttributesType> => ({
|
||||||
...message,
|
...message,
|
||||||
attachments: await Promise.all(message.attachments.map(loadAttachmentData)),
|
attachments: await Promise.all(
|
||||||
|
(message.attachments || []).map(loadAttachmentData)
|
||||||
|
),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.loadQuoteData = loadAttachmentData => {
|
export const loadQuoteData = (
|
||||||
|
loadAttachmentData: LoadAttachmentType
|
||||||
|
): ((
|
||||||
|
quote: QuotedMessageType | undefined | null
|
||||||
|
) => Promise<QuotedMessageType | null>) => {
|
||||||
if (!isFunction(loadAttachmentData)) {
|
if (!isFunction(loadAttachmentData)) {
|
||||||
throw new TypeError('loadQuoteData: loadAttachmentData is required');
|
throw new TypeError('loadQuoteData: loadAttachmentData is required');
|
||||||
}
|
}
|
||||||
|
|
||||||
return async quote => {
|
return async (
|
||||||
|
quote: QuotedMessageType | undefined | null
|
||||||
|
): Promise<QuotedMessageType | null> => {
|
||||||
if (!quote) {
|
if (!quote) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -562,48 +690,58 @@ exports.loadQuoteData = loadAttachmentData => {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.loadContactData = loadAttachmentData => {
|
export const loadContactData = (
|
||||||
|
loadAttachmentData: LoadAttachmentType
|
||||||
|
): ((
|
||||||
|
contact: Array<EmbeddedContactType> | undefined
|
||||||
|
) => Promise<Array<EmbeddedContactType> | null>) => {
|
||||||
if (!isFunction(loadAttachmentData)) {
|
if (!isFunction(loadAttachmentData)) {
|
||||||
throw new TypeError('loadContactData: loadAttachmentData is required');
|
throw new TypeError('loadContactData: loadAttachmentData is required');
|
||||||
}
|
}
|
||||||
|
|
||||||
return async contact => {
|
return async (
|
||||||
|
contact: Array<EmbeddedContactType> | undefined
|
||||||
|
): Promise<Array<EmbeddedContactType> | null> => {
|
||||||
if (!contact) {
|
if (!contact) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Promise.all(
|
return Promise.all(
|
||||||
contact.map(async item => {
|
contact.map(
|
||||||
if (
|
async (item: EmbeddedContactType): Promise<EmbeddedContactType> => {
|
||||||
!item ||
|
if (
|
||||||
!item.avatar ||
|
!item ||
|
||||||
!item.avatar.avatar ||
|
!item.avatar ||
|
||||||
!item.avatar.avatar.path
|
!item.avatar.avatar ||
|
||||||
) {
|
!item.avatar.avatar.path
|
||||||
return item;
|
) {
|
||||||
}
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...item,
|
...item,
|
||||||
avatar: {
|
|
||||||
...item.avatar,
|
|
||||||
avatar: {
|
avatar: {
|
||||||
...item.avatar.avatar,
|
...item.avatar,
|
||||||
...(await loadAttachmentData(item.avatar.avatar)),
|
avatar: {
|
||||||
|
...item.avatar.avatar,
|
||||||
|
...(await loadAttachmentData(item.avatar.avatar)),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
};
|
||||||
};
|
}
|
||||||
})
|
)
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.loadPreviewData = loadAttachmentData => {
|
export const loadPreviewData = (
|
||||||
|
loadAttachmentData: LoadAttachmentType
|
||||||
|
): ((preview: PreviewMessageType) => Promise<PreviewMessageType>) => {
|
||||||
if (!isFunction(loadAttachmentData)) {
|
if (!isFunction(loadAttachmentData)) {
|
||||||
throw new TypeError('loadPreviewData: loadAttachmentData is required');
|
throw new TypeError('loadPreviewData: loadAttachmentData is required');
|
||||||
}
|
}
|
||||||
|
|
||||||
return async preview => {
|
return async (preview: PreviewMessageType) => {
|
||||||
if (!preview || !preview.length) {
|
if (!preview || !preview.length) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
@ -623,12 +761,14 @@ exports.loadPreviewData = loadAttachmentData => {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.loadStickerData = loadAttachmentData => {
|
export const loadStickerData = (
|
||||||
|
loadAttachmentData: LoadAttachmentType
|
||||||
|
): ((sticker: StickerMessageType) => Promise<StickerMessageType | null>) => {
|
||||||
if (!isFunction(loadAttachmentData)) {
|
if (!isFunction(loadAttachmentData)) {
|
||||||
throw new TypeError('loadStickerData: loadAttachmentData is required');
|
throw new TypeError('loadStickerData: loadAttachmentData is required');
|
||||||
}
|
}
|
||||||
|
|
||||||
return async sticker => {
|
return async (sticker: StickerMessageType) => {
|
||||||
if (!sticker || !sticker.data) {
|
if (!sticker || !sticker.data) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -640,7 +780,13 @@ exports.loadStickerData = loadAttachmentData => {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.deleteAllExternalFiles = ({ deleteAttachmentData, deleteOnDisk }) => {
|
export const deleteAllExternalFiles = ({
|
||||||
|
deleteAttachmentData,
|
||||||
|
deleteOnDisk,
|
||||||
|
}: {
|
||||||
|
deleteAttachmentData: (attachment: AttachmentType) => Promise<void>;
|
||||||
|
deleteOnDisk: (path: string) => Promise<void>;
|
||||||
|
}): ((message: MessageAttributesType) => Promise<void>) => {
|
||||||
if (!isFunction(deleteAttachmentData)) {
|
if (!isFunction(deleteAttachmentData)) {
|
||||||
throw new TypeError(
|
throw new TypeError(
|
||||||
'deleteAllExternalFiles: deleteAttachmentData must be a function'
|
'deleteAllExternalFiles: deleteAttachmentData must be a function'
|
||||||
|
@ -653,7 +799,7 @@ exports.deleteAllExternalFiles = ({ deleteAttachmentData, deleteOnDisk }) => {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return async message => {
|
return async (message: MessageAttributesType) => {
|
||||||
const { attachments, quote, contact, preview, sticker } = message;
|
const { attachments, quote, contact, preview, sticker } = message;
|
||||||
|
|
||||||
if (attachments && attachments.length) {
|
if (attachments && attachments.length) {
|
||||||
|
@ -712,10 +858,13 @@ exports.deleteAllExternalFiles = ({ deleteAttachmentData, deleteOnDisk }) => {
|
||||||
// createAttachmentDataWriter :: (RelativePath -> IO Unit)
|
// createAttachmentDataWriter :: (RelativePath -> IO Unit)
|
||||||
// Message ->
|
// Message ->
|
||||||
// IO (Promise Message)
|
// IO (Promise Message)
|
||||||
exports.createAttachmentDataWriter = ({
|
export const createAttachmentDataWriter = ({
|
||||||
writeExistingAttachmentData,
|
writeExistingAttachmentData,
|
||||||
logger,
|
logger,
|
||||||
}) => {
|
}: {
|
||||||
|
writeExistingAttachmentData: WriteExistingAttachmentDataType;
|
||||||
|
logger: LoggerType;
|
||||||
|
}): ((message: MessageAttributesType) => Promise<MessageAttributesType>) => {
|
||||||
if (!isFunction(writeExistingAttachmentData)) {
|
if (!isFunction(writeExistingAttachmentData)) {
|
||||||
throw new TypeError(
|
throw new TypeError(
|
||||||
'createAttachmentDataWriter: writeExistingAttachmentData must be a function'
|
'createAttachmentDataWriter: writeExistingAttachmentData must be a function'
|
||||||
|
@ -725,12 +874,14 @@ exports.createAttachmentDataWriter = ({
|
||||||
throw new TypeError('createAttachmentDataWriter: logger must be an object');
|
throw new TypeError('createAttachmentDataWriter: logger must be an object');
|
||||||
}
|
}
|
||||||
|
|
||||||
return async rawMessage => {
|
return async (
|
||||||
if (!exports.isValid(rawMessage)) {
|
rawMessage: MessageAttributesType
|
||||||
|
): Promise<MessageAttributesType> => {
|
||||||
|
if (!isValid(rawMessage)) {
|
||||||
throw new TypeError("'rawMessage' is not valid");
|
throw new TypeError("'rawMessage' is not valid");
|
||||||
}
|
}
|
||||||
|
|
||||||
const message = exports.initializeSchemaVersion({
|
const message = initializeSchemaVersion({
|
||||||
message: rawMessage,
|
message: rawMessage,
|
||||||
logger,
|
logger,
|
||||||
});
|
});
|
||||||
|
@ -748,13 +899,13 @@ exports.createAttachmentDataWriter = ({
|
||||||
|
|
||||||
const lastVersionWithAttachmentDataInMemory = 2;
|
const lastVersionWithAttachmentDataInMemory = 2;
|
||||||
const willAttachmentsGoToFileSystemOnUpgrade =
|
const willAttachmentsGoToFileSystemOnUpgrade =
|
||||||
message.schemaVersion <= lastVersionWithAttachmentDataInMemory;
|
(message.schemaVersion || 0) <= lastVersionWithAttachmentDataInMemory;
|
||||||
if (willAttachmentsGoToFileSystemOnUpgrade) {
|
if (willAttachmentsGoToFileSystemOnUpgrade) {
|
||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
|
|
||||||
(attachments || []).forEach(attachment => {
|
(attachments || []).forEach(attachment => {
|
||||||
if (!Attachment.hasData(attachment)) {
|
if (!hasData(attachment)) {
|
||||||
throw new TypeError(
|
throw new TypeError(
|
||||||
"'attachment.data' is required during message import"
|
"'attachment.data' is required during message import"
|
||||||
);
|
);
|
||||||
|
@ -767,27 +918,41 @@ exports.createAttachmentDataWriter = ({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const writeThumbnails = exports._mapQuotedAttachments(async thumbnail => {
|
const writeQuoteAttachment = async (attachment: AttachmentType) => {
|
||||||
|
const { thumbnail } = attachment;
|
||||||
|
if (!thumbnail) {
|
||||||
|
return attachment;
|
||||||
|
}
|
||||||
|
|
||||||
const { data, path } = thumbnail;
|
const { data, path } = thumbnail;
|
||||||
|
|
||||||
// we want to be bulletproof to thumbnails without data
|
// we want to be bulletproof to attachments without data
|
||||||
if (!data || !path) {
|
if (!data || !path) {
|
||||||
logger.warn(
|
logger.warn(
|
||||||
'Thumbnail had neither data nor path.',
|
'quote attachment had neither data nor path.',
|
||||||
'id:',
|
'id:',
|
||||||
message.id,
|
message.id,
|
||||||
'source:',
|
'source:',
|
||||||
message.source
|
message.source
|
||||||
);
|
);
|
||||||
return thumbnail;
|
return attachment;
|
||||||
}
|
}
|
||||||
|
|
||||||
await writeExistingAttachmentData(thumbnail);
|
await writeExistingAttachmentData(thumbnail);
|
||||||
return omit(thumbnail, ['data']);
|
return {
|
||||||
});
|
...attachment,
|
||||||
|
thumbnail: omit(thumbnail, ['data']),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
const writeContactAvatar = async messageContact => {
|
const writeContactAvatar = async (
|
||||||
|
messageContact: EmbeddedContactType
|
||||||
|
): Promise<EmbeddedContactType> => {
|
||||||
const { avatar } = messageContact;
|
const { avatar } = messageContact;
|
||||||
|
if (!avatar) {
|
||||||
|
return messageContact;
|
||||||
|
}
|
||||||
|
|
||||||
if (avatar && !avatar.avatar) {
|
if (avatar && !avatar.avatar) {
|
||||||
return omit(messageContact, ['avatar']);
|
return omit(messageContact, ['avatar']);
|
||||||
}
|
}
|
||||||
|
@ -800,7 +965,9 @@ exports.createAttachmentDataWriter = ({
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const writePreviewImage = async item => {
|
const writePreviewImage = async (
|
||||||
|
item: PreviewType
|
||||||
|
): Promise<PreviewType> => {
|
||||||
const { image } = item;
|
const { image } = item;
|
||||||
if (!image) {
|
if (!image) {
|
||||||
return omit(item, ['image']);
|
return omit(item, ['image']);
|
||||||
|
@ -812,7 +979,17 @@ exports.createAttachmentDataWriter = ({
|
||||||
};
|
};
|
||||||
|
|
||||||
const messageWithoutAttachmentData = {
|
const messageWithoutAttachmentData = {
|
||||||
...(await writeThumbnails(message, { logger })),
|
...message,
|
||||||
|
...(quote
|
||||||
|
? {
|
||||||
|
quote: {
|
||||||
|
...quote,
|
||||||
|
attachments: await Promise.all(
|
||||||
|
(quote?.attachments || []).map(writeQuoteAttachment)
|
||||||
|
),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
: undefined),
|
||||||
contact: await Promise.all((contact || []).map(writeContactAvatar)),
|
contact: await Promise.all((contact || []).map(writeContactAvatar)),
|
||||||
preview: await Promise.all((preview || []).map(writePreviewImage)),
|
preview: await Promise.all((preview || []).map(writePreviewImage)),
|
||||||
attachments: await Promise.all(
|
attachments: await Promise.all(
|
||||||
|
@ -842,5 +1019,3 @@ exports.createAttachmentDataWriter = ({
|
||||||
return messageWithoutAttachmentData;
|
return messageWithoutAttachmentData;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.hasExpiration = MessageTS.hasExpiration;
|
|
|
@ -51,7 +51,7 @@ export const format = memoizee(_format, {
|
||||||
export function parse(
|
export function parse(
|
||||||
phoneNumber: string,
|
phoneNumber: string,
|
||||||
options: {
|
options: {
|
||||||
regionCode: string;
|
regionCode: string | undefined;
|
||||||
}
|
}
|
||||||
): string {
|
): string {
|
||||||
const { regionCode } = options;
|
const { regionCode } = options;
|
||||||
|
|
|
@ -3,6 +3,6 @@
|
||||||
|
|
||||||
import { isNumber } from 'lodash';
|
import { isNumber } from 'lodash';
|
||||||
|
|
||||||
export const isValid = (value: unknown): boolean => {
|
export const isValid = (value: unknown): value is number => {
|
||||||
return Boolean(isNumber(value) && value >= 0);
|
return Boolean(isNumber(value) && value >= 0);
|
||||||
};
|
};
|
||||||
|
|
|
@ -3,19 +3,20 @@
|
||||||
|
|
||||||
import * as Attachment from '../Attachment';
|
import * as Attachment from '../Attachment';
|
||||||
import * as IndexedDB from '../IndexedDB';
|
import * as IndexedDB from '../IndexedDB';
|
||||||
import type { Message, UserMessage } from '../Message';
|
|
||||||
|
import type { MessageAttributesType } from '../../model-types.d';
|
||||||
|
|
||||||
const hasAttachment =
|
const hasAttachment =
|
||||||
(predicate: (value: Attachment.AttachmentType) => boolean) =>
|
(predicate: (value: Attachment.AttachmentType) => boolean) =>
|
||||||
(message: UserMessage): IndexedDB.IndexablePresence =>
|
(message: MessageAttributesType): IndexedDB.IndexablePresence =>
|
||||||
IndexedDB.toIndexablePresence(message.attachments.some(predicate));
|
IndexedDB.toIndexablePresence((message.attachments || []).some(predicate));
|
||||||
|
|
||||||
const hasFileAttachment = hasAttachment(Attachment.isFile);
|
const hasFileAttachment = hasAttachment(Attachment.isFile);
|
||||||
const hasVisualMediaAttachment = hasAttachment(Attachment.isVisualMedia);
|
const hasVisualMediaAttachment = hasAttachment(Attachment.isVisualMedia);
|
||||||
|
|
||||||
export const initializeAttachmentMetadata = async (
|
export const initializeAttachmentMetadata = async (
|
||||||
message: Message
|
message: MessageAttributesType
|
||||||
): Promise<Message> => {
|
): Promise<MessageAttributesType> => {
|
||||||
if (message.type === 'verified-change') {
|
if (message.type === 'verified-change') {
|
||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
|
@ -26,7 +27,7 @@ export const initializeAttachmentMetadata = async (
|
||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
|
|
||||||
const attachments = message.attachments.filter(
|
const attachments = (message.attachments || []).filter(
|
||||||
(attachment: Attachment.AttachmentType) =>
|
(attachment: Attachment.AttachmentType) =>
|
||||||
attachment.contentType !== 'text/x-signal-plain'
|
attachment.contentType !== 'text/x-signal-plain'
|
||||||
);
|
);
|
||||||
|
|
|
@ -1598,7 +1598,7 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
|
||||||
return {
|
return {
|
||||||
path: attachment.path,
|
path: attachment.path,
|
||||||
objectURL: getAbsoluteAttachmentPath(attachment.path),
|
objectURL: getAbsoluteAttachmentPath(attachment.path),
|
||||||
thumbnailObjectUrl: thumbnail
|
thumbnailObjectUrl: thumbnail?.path
|
||||||
? getAbsoluteAttachmentPath(thumbnail.path)
|
? getAbsoluteAttachmentPath(thumbnail.path)
|
||||||
: undefined,
|
: undefined,
|
||||||
contentType: attachment.contentType,
|
contentType: attachment.contentType,
|
||||||
|
@ -2566,7 +2566,7 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
objectURL: getAbsoluteAttachmentPath(attachment.path || ''),
|
objectURL: getAbsoluteAttachmentPath(attachment.path || ''),
|
||||||
thumbnailObjectUrl: thumbnail
|
thumbnailObjectUrl: thumbnail?.path
|
||||||
? getAbsoluteAttachmentPath(thumbnail.path)
|
? getAbsoluteAttachmentPath(thumbnail.path)
|
||||||
: '',
|
: '',
|
||||||
contentType: attachment.contentType,
|
contentType: attachment.contentType,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue