Migrate messages, conversations, conversation_view, background to TS

Co-authored-by: Sidney Keese <sidney@carbonfive.com>
This commit is contained in:
Chris Svenningsen 2020-09-24 13:57:54 -07:00 committed by Josh Perez
parent 6e7930f7a9
commit b5df9b4067
42 changed files with 11676 additions and 10516 deletions

View file

@ -326,6 +326,7 @@
</script> </script>
<script type='text/javascript' src='js/components.js'></script> <script type='text/javascript' src='js/components.js'></script>
<script type='text/javascript' src='ts/backboneJquery.js'></script>
<script type='text/javascript' src='js/reliable_trigger.js'></script> <script type='text/javascript' src='js/reliable_trigger.js'></script>
<script type='text/javascript' src='js/database.js'></script> <script type='text/javascript' src='js/database.js'></script>
<script type='text/javascript' src='js/storage.js'></script> <script type='text/javascript' src='js/storage.js'></script>
@ -341,8 +342,6 @@
<script type='text/javascript' src='js/reactions.js'></script> <script type='text/javascript' src='js/reactions.js'></script>
<script type='text/javascript' src='js/deletes.js'></script> <script type='text/javascript' src='js/deletes.js'></script>
<script type='text/javascript' src='js/libphonenumber-util.js'></script> <script type='text/javascript' src='js/libphonenumber-util.js'></script>
<script type='text/javascript' src='js/models/messages.js'></script>
<script type='text/javascript' src='js/models/conversations.js'></script>
<script type='text/javascript' src='js/models/blockedNumbers.js'></script> <script type='text/javascript' src='js/models/blockedNumbers.js'></script>
<script type='text/javascript' src='js/expiring_messages.js'></script> <script type='text/javascript' src='js/expiring_messages.js'></script>
<script type='text/javascript' src='js/expiring_tap_to_view_messages.js'></script> <script type='text/javascript' src='js/expiring_tap_to_view_messages.js'></script>
@ -358,7 +357,7 @@
<script type='text/javascript' src='js/views/key_verification_view.js'></script> <script type='text/javascript' src='js/views/key_verification_view.js'></script>
<script type='text/javascript' src='js/views/group_member_list_view.js'></script> <script type='text/javascript' src='js/views/group_member_list_view.js'></script>
<script type='text/javascript' src='js/views/recorder_view.js'></script> <script type='text/javascript' src='js/views/recorder_view.js'></script>
<script type='text/javascript' src='js/views/conversation_view.js'></script> <script type='text/javascript' src='ts/views/conversation_view.js'></script>
<script type='text/javascript' src='js/views/inbox_view.js'></script> <script type='text/javascript' src='js/views/inbox_view.js'></script>
<script type='text/javascript' src='js/views/confirmation_dialog_view.js'></script> <script type='text/javascript' src='js/views/confirmation_dialog_view.js'></script>
<script type='text/javascript' src='js/views/identicon_svg_view.js'></script> <script type='text/javascript' src='js/views/identicon_svg_view.js'></script>
@ -387,6 +386,6 @@
</div> </div>
</div> </div>
<script type='text/javascript' src='js/background.js'></script> <script type='text/javascript' src='ts/background.js'></script>
</body> </body>
</html> </html>

View file

@ -5,7 +5,6 @@
"license": "GPLV3", "license": "GPLV3",
"private": true, "private": true,
"dependencies": { "dependencies": {
"indexeddb-backbonejs-adapter": "*",
"mp3lameencoder": "https://github.com/higuma/mp3-lame-encoder-js.git", "mp3lameencoder": "https://github.com/higuma/mp3-lame-encoder-js.git",
"protobuf": "~3.8.0", "protobuf": "~3.8.0",
"qrcode": "https://github.com/davidshimjs/qrcodejs.git#1c78ccd71", "qrcode": "https://github.com/davidshimjs/qrcodejs.git#1c78ccd71",
@ -18,9 +17,6 @@
"bytebuffer": [ "bytebuffer": [
"dist/ByteBufferAB.js" "dist/ByteBufferAB.js"
], ],
"indexeddb-backbonejs-adapter": [
"backbone-indexeddb.js"
],
"long": [ "long": [
"dist/Long.js" "dist/Long.js"
], ],
@ -49,8 +45,6 @@
"components/protobuf/**/*.js", "components/protobuf/**/*.js",
"node_modules/mustache/mustache.js", "node_modules/mustache/mustache.js",
"node_modules/underscore/underscore.js", "node_modules/underscore/underscore.js",
"node_modules/backbone/backbone.js",
"components/indexeddb-backbonejs-adapter/**/*.js",
"components/qrcode/**/*.js", "components/qrcode/**/*.js",
"node_modules/intl-tel-input/build/js/intlTelInput.js", "node_modules/intl-tel-input/build/js/intlTelInput.js",
"components/autosize/**/*.js", "components/autosize/**/*.js",

View file

@ -51,6 +51,7 @@
{{ toastMessage }} {{ toastMessage }}
</script> </script>
<script type='text/javascript' src='js/components.js'></script> <script type='text/javascript' src='js/components.js'></script>
<script type='text/javascript' src='ts/backboneJquery.js'></script>
<script type='text/javascript' src='js/views/whisper_view.js'></script> <script type='text/javascript' src='js/views/whisper_view.js'></script>
<script type='text/javascript' src='js/views/toast_view.js'></script> <script type='text/javascript' src='js/views/toast_view.js'></script>
<script type='text/javascript' src='js/views/debug_log_view.js'></script> <script type='text/javascript' src='js/views/debug_log_view.js'></script>

View file

@ -23,3 +23,4 @@ window.getEnvironment = () => config.environment;
require('./js/logging'); require('./js/logging');
window.closeDebugLog = () => ipcRenderer.send('close-debug-log'); window.closeDebugLog = () => ipcRenderer.send('close-debug-log');
window.Backbone = require('backbone');

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -19,6 +19,7 @@
<div class="message"></div> <div class="message"></div>
</div> </div>
<script type="text/javascript" src="js/components.js"></script> <script type="text/javascript" src="js/components.js"></script>
<script type='text/javascript' src='ts/backboneJquery.js'></script>
<script type="text/javascript" src="js/loading_start.js"></script> <script type="text/javascript" src="js/loading_start.js"></script>
</body> </body>
</html> </html>

View file

@ -10,3 +10,4 @@ const { locale } = config;
const localeMessages = ipcRenderer.sendSync('locale-data'); const localeMessages = ipcRenderer.sendSync('locale-data');
window.i18n = i18n.setup(locale, localeMessages); window.i18n = i18n.setup(locale, localeMessages);
window.Backbone = require('backbone');

View file

@ -32,6 +32,7 @@
</div> </div>
</script> </script>
<script type='text/javascript' src='js/components.js'></script> <script type='text/javascript' src='js/components.js'></script>
<script type='text/javascript' src='ts/backboneJquery.js'></script>
<script type='text/javascript' src='js/views/whisper_view.js'></script> <script type='text/javascript' src='js/views/whisper_view.js'></script>
<script type='text/javascript' src='js/views/confirmation_dialog_view.js'></script> <script type='text/javascript' src='js/views/confirmation_dialog_view.js'></script>
<script type='text/javascript' src='js/permissions_popup_start.js'></script> <script type='text/javascript' src='js/permissions_popup_start.js'></script>

View file

@ -40,3 +40,4 @@ window.setMediaPermissions = makeSetter('media-permissions');
window.setMediaCameraPermissions = makeSetter('media-camera-permissions'); window.setMediaCameraPermissions = makeSetter('media-camera-permissions');
window.getThemeSetting = makeGetter('theme-setting'); window.getThemeSetting = makeGetter('theme-setting');
window.setThemeSetting = makeSetter('theme-setting'); window.setThemeSetting = makeSetter('theme-setting');
window.Backbone = require('backbone');

View file

@ -424,6 +424,7 @@ try {
window.ReactDOM = require('react-dom'); window.ReactDOM = require('react-dom');
window.moment = require('moment'); window.moment = require('moment');
window.PQueue = require('p-queue').default; window.PQueue = require('p-queue').default;
window.Backbone = require('backbone');
const Signal = require('./js/modules/signal'); const Signal = require('./js/modules/signal');
const i18n = require('./js/modules/i18n'); const i18n = require('./js/modules/i18n');
@ -452,6 +453,10 @@ try {
logger: window.log, logger: window.log,
}); });
// these need access to window.Signal:
require('./ts/models/messages');
require('./ts/models/conversations');
function wrapWithPromise(fn) { function wrapWithPromise(fn) {
return (...args) => Promise.resolve(fn(...args)); return (...args) => Promise.resolve(fn(...args));
} }

View file

@ -164,6 +164,7 @@
</div> </div>
</script> </script>
<script type='text/javascript' src='js/components.js'></script> <script type='text/javascript' src='js/components.js'></script>
<script type='text/javascript' src='ts/backboneJquery.js'></script>
<script type='text/javascript' src='js/views/whisper_view.js'></script> <script type='text/javascript' src='js/views/whisper_view.js'></script>
<script type='text/javascript' src='js/views/settings_view.js'></script> <script type='text/javascript' src='js/views/settings_view.js'></script>
<script type='text/javascript' src='js/settings_start.js'></script> <script type='text/javascript' src='js/settings_start.js'></script>

View file

@ -118,3 +118,5 @@ function makeSetter(name) {
} }
require('./js/logging'); require('./js/logging');
window.Backbone = require('backbone');

View file

@ -7,6 +7,7 @@
<div id="root"></div> <div id="root"></div>
<script type="text/javascript" src="../../js/components.js"></script> <script type="text/javascript" src="../../js/components.js"></script>
<script type="text/javascript" src="../../js/signal_protocol_store.js"></script> <script type="text/javascript" src="../../js/signal_protocol_store.js"></script>
<script type="text/javascript" src="../../ts/backbonejQuery.js"></script>
<script type="text/javascript" src="../../js/storage.js"></script> <script type="text/javascript" src="../../js/storage.js"></script>
<script type="text/javascript" src="../../js/libtextsecure.js"></script> <script type="text/javascript" src="../../js/libtextsecure.js"></script>
</body> </body>

View file

@ -18,6 +18,7 @@ window.getEnvironment = () => config.environment;
window.getVersion = () => config.version; window.getVersion = () => config.version;
window.getGuid = require('uuid/v4'); window.getGuid = require('uuid/v4');
window.PQueue = require('p-queue').default; window.PQueue = require('p-queue').default;
window.Backbone = require('backbone');
window.localeMessages = ipc.sendSync('locale-data'); window.localeMessages = ipc.sendSync('locale-data');

View file

@ -332,6 +332,7 @@
<script type="text/javascript" src="../libtextsecure/test/fake_web_api.js"></script> <script type="text/javascript" src="../libtextsecure/test/fake_web_api.js"></script>
<script type="text/javascript" src="../js/components.js"></script> <script type="text/javascript" src="../js/components.js"></script>
<script type='text/javascript' src='../ts/backboneJquery.js'></script>
<script type="text/javascript" src="../js/reliable_trigger.js" data-cover></script> <script type="text/javascript" src="../js/reliable_trigger.js" data-cover></script>
<script type="text/javascript" src="test.js"></script> <script type="text/javascript" src="test.js"></script>

View file

@ -2,10 +2,11 @@ import { debounce, reduce, uniq, without } from 'lodash';
import dataInterface from './sql/Client'; import dataInterface from './sql/Client';
import { import {
ConversationModelCollectionType, ConversationModelCollectionType,
ConversationModelType, WhatIsThis,
ConversationTypeType, ConversationAttributesTypeType,
} from './model-types.d'; } from './model-types.d';
import { SendOptionsType } from './textsecure/SendMessage'; import { SendOptionsType, CallbackResultType } from './textsecure/SendMessage';
import { ConversationModel } from './models/conversations';
const MAX_MESSAGE_BODY_LENGTH = 64 * 1024; const MAX_MESSAGE_BODY_LENGTH = 64 * 1024;
@ -40,7 +41,7 @@ export function start(): void {
this.on('add remove change:unreadCount', debouncedUpdateUnreadCount); this.on('add remove change:unreadCount', debouncedUpdateUnreadCount);
window.Whisper.events.on('updateUnreadCount', debouncedUpdateUnreadCount); window.Whisper.events.on('updateUnreadCount', debouncedUpdateUnreadCount);
}, },
addActive(model: ConversationModelType) { addActive(model: ConversationModel) {
if (model.get('active_at')) { if (model.get('active_at')) {
this.add(model); this.add(model);
} else { } else {
@ -52,7 +53,7 @@ export function start(): void {
'badge-count-muted-conversations' 'badge-count-muted-conversations'
); );
const newUnreadCount = reduce( const newUnreadCount = reduce(
this.map((m: ConversationModelType) => this.map((m: ConversationModel) =>
!canCountMutedConversations && m.isMuted() ? 0 : m.get('unreadCount') !canCountMutedConversations && m.isMuted() ? 0 : m.get('unreadCount')
), ),
(item: number, memo: number) => (item || 0) + memo, (item: number, memo: number) => (item || 0) + memo,
@ -91,7 +92,7 @@ export class ConversationController {
this._conversations = conversations; this._conversations = conversations;
} }
get(id?: string | null): ConversationModelType | undefined { get(id?: string | null): ConversationModel | undefined {
if (!this._initialFetchComplete) { if (!this._initialFetchComplete) {
throw new Error( throw new Error(
'ConversationController.get() needs complete initial fetch' 'ConversationController.get() needs complete initial fetch'
@ -103,16 +104,16 @@ export class ConversationController {
} }
dangerouslyCreateAndAdd( dangerouslyCreateAndAdd(
attributes: Partial<ConversationModelType> attributes: Partial<ConversationModel>
): ConversationModelType { ): ConversationModel {
return this._conversations.add(attributes); return this._conversations.add(attributes);
} }
getOrCreate( getOrCreate(
identifier: string, identifier: string | null,
type: ConversationTypeType, type: ConversationAttributesTypeType,
additionalInitialProps = {} additionalInitialProps = {}
): ConversationModelType { ): ConversationModel {
if (typeof identifier !== 'string') { if (typeof identifier !== 'string') {
throw new TypeError("'id' must be a string"); throw new TypeError("'id' must be a string");
} }
@ -202,10 +203,10 @@ export class ConversationController {
} }
async getOrCreateAndWait( async getOrCreateAndWait(
id: string, id: string | null,
type: ConversationTypeType, type: ConversationAttributesTypeType,
additionalInitialProps = {} additionalInitialProps = {}
): Promise<ConversationModelType> { ): Promise<ConversationModel> {
await this._initialPromise; await this._initialPromise;
const conversation = this.getOrCreate(id, type, additionalInitialProps); const conversation = this.getOrCreate(id, type, additionalInitialProps);
@ -217,7 +218,7 @@ export class ConversationController {
throw new Error('getOrCreateAndWait: did not get conversation'); throw new Error('getOrCreateAndWait: did not get conversation');
} }
getConversationId(address: string): string | null { getConversationId(address: string | null): string | null {
if (!address) { if (!address) {
return null; return null;
} }
@ -251,8 +252,8 @@ export class ConversationController {
uuid, uuid,
highTrust, highTrust,
}: { }: {
e164?: string; e164?: string | null;
uuid?: string; uuid?: string | null;
highTrust?: boolean; highTrust?: boolean;
}): string | undefined { }): string | undefined {
// Check for at least one parameter being provided. This is necessary // Check for at least one parameter being provided. This is necessary
@ -488,8 +489,8 @@ export class ConversationController {
} }
async combineContacts( async combineContacts(
current: ConversationModelType, current: ConversationModel,
obsolete: ConversationModelType obsolete: ConversationModel
): Promise<void> { ): Promise<void> {
const obsoleteId = obsolete.get('id'); const obsoleteId = obsolete.get('id');
const currentId = current.get('id'); const currentId = current.get('id');
@ -503,7 +504,11 @@ export class ConversationController {
'combineContacts: Copying profile key from old to new contact' 'combineContacts: Copying profile key from old to new contact'
); );
await current.setProfileKey(obsolete.get('profileKey')); const profileKey = obsolete.get('profileKey');
if (profileKey) {
await current.setProfileKey(profileKey);
}
} }
window.log.warn( window.log.warn(
@ -583,7 +588,7 @@ export class ConversationController {
async getConversationForTargetMessage( async getConversationForTargetMessage(
targetFromId: string, targetFromId: string,
targetTimestamp: number targetTimestamp: number
): Promise<boolean | ConversationModelType | null | undefined> { ): Promise<boolean | ConversationModel | null | undefined> {
const messages = await getMessagesBySentAt(targetTimestamp, { const messages = await getMessagesBySentAt(targetTimestamp, {
MessageCollection: window.Whisper.MessageCollection, MessageCollection: window.Whisper.MessageCollection,
}); });
@ -605,11 +610,13 @@ export class ConversationController {
return null; return null;
} }
prepareForSend<T>( prepareForSend(
id: string, id: string | undefined,
options?: unknown options?: WhatIsThis
): { ): {
wrap: (promise: Promise<T>) => Promise<T>; wrap: (
promise: Promise<CallbackResultType | void | null>
) => Promise<CallbackResultType | void | null>;
sendOptions: SendOptionsType | undefined; sendOptions: SendOptionsType | undefined;
} { } {
// id is any valid conversation identifier // id is any valid conversation identifier
@ -619,14 +626,14 @@ export class ConversationController {
: undefined; : undefined;
const wrap = conversation const wrap = conversation
? conversation.wrapSend.bind(conversation) ? conversation.wrapSend.bind(conversation)
: async (promise: Promise<T>) => promise; : async (promise: Promise<CallbackResultType | void | null>) => promise;
return { wrap, sendOptions }; return { wrap, sendOptions };
} }
async getAllGroupsInvolvingId( async getAllGroupsInvolvingId(
conversationId: string conversationId: string
): Promise<Array<ConversationModelType>> { ): Promise<Array<ConversationModel>> {
const groups = await getAllGroupsInvolvingId(conversationId, { const groups = await getAllGroupsInvolvingId(conversationId, {
ConversationCollection: window.Whisper.ConversationCollection, ConversationCollection: window.Whisper.ConversationCollection,
}); });

3
ts/backboneJquery.ts Normal file
View file

@ -0,0 +1,3 @@
// we are requiring backbone in preload.js, and we need to tell backbone where
// jquery is after it's loaded.
window.Backbone.$ = window.Backbone.$ || window.$;

File diff suppressed because it is too large Load diff

View file

@ -15,8 +15,10 @@ interface Contact {
isMe?: boolean; isMe?: boolean;
} }
export type ChangeType = 'add' | 'remove' | 'name' | 'avatar' | 'general';
interface Change { interface Change {
type: 'add' | 'remove' | 'name' | 'avatar' | 'general'; type: ChangeType;
newName?: string; newName?: string;
contacts?: Array<Contact>; contacts?: Array<Contact>;
} }

View file

@ -5,8 +5,14 @@ import { ContactName } from './ContactName';
import { Intl } from '../Intl'; import { Intl } from '../Intl';
import { LocalizerType } from '../../types/Util'; import { LocalizerType } from '../../types/Util';
export type TimerNotificationType =
| 'fromOther'
| 'fromMe'
| 'fromSync'
| 'fromMember';
export type PropsData = { export type PropsData = {
type: 'fromOther' | 'fromMe' | 'fromSync' | 'fromMember'; type: TimerNotificationType;
phoneNumber?: string; phoneNumber?: string;
profileName?: string; profileName?: string;
title: string; title: string;

View file

@ -15,7 +15,6 @@ import {
} from './services/groupCredentialFetcher'; } from './services/groupCredentialFetcher';
import { import {
ConversationAttributesType, ConversationAttributesType,
ConversationModelType,
GroupV2MemberType, GroupV2MemberType,
GroupV2PendingMemberType, GroupV2PendingMemberType,
MessageAttributesType, MessageAttributesType,
@ -50,6 +49,7 @@ import {
} from './textsecure.d'; } from './textsecure.d';
import { GroupCredentialsType } from './textsecure/WebAPI'; import { GroupCredentialsType } from './textsecure/WebAPI';
import { CURRENT_SCHEMA_VERSION as MAX_MESSAGE_SCHEMA } from '../js/modules/types/message'; import { CURRENT_SCHEMA_VERSION as MAX_MESSAGE_SCHEMA } from '../js/modules/types/message';
import { ConversationModel } from './models/conversations';
export type GroupV2AccessAttributesChangeType = { export type GroupV2AccessAttributesChangeType = {
type: 'access-attributes'; type: 'access-attributes';
@ -265,7 +265,7 @@ export function deriveGroupFields(
// Fetching and applying group changes // Fetching and applying group changes
type MaybeUpdatePropsType = { type MaybeUpdatePropsType = {
conversation: ConversationModelType; conversation: ConversationModel;
groupChangeBase64?: string; groupChangeBase64?: string;
newRevision?: number; newRevision?: number;
receivedAt?: number; receivedAt?: number;
@ -548,7 +548,8 @@ function generateBasicMessage() {
return { return {
id: getGuid(), id: getGuid(),
schemaVersion: MAX_MESSAGE_SCHEMA, schemaVersion: MAX_MESSAGE_SCHEMA,
}; // this is missing most properties to fulfill this type
} as MessageAttributesType;
} }
function generateLeftGroupChanges( function generateLeftGroupChanges(

218
ts/model-types.d.ts vendored
View file

@ -1,17 +1,27 @@
import * as Backbone from 'backbone'; import * as Backbone from 'backbone';
import { GroupV2ChangeType } from './groups'; import { GroupV2ChangeType } from './groups';
import { LocalizerType } from './types/Util'; import { LocalizerType, BodyRangesType } from './types/Util';
import { CallHistoryDetailsType } from './types/Calling'; import { CallHistoryDetailsType } from './types/Calling';
import { ColorType } from './types/Colors'; import { ColorType } from './types/Colors';
import { ConversationType } from './state/ducks/conversations'; import {
ConversationType,
MessageType,
LastMessageStatus,
} from './state/ducks/conversations';
import { SendOptionsType } from './textsecure/SendMessage'; import { SendOptionsType } from './textsecure/SendMessage';
import { SyncMessageClass } from './textsecure.d'; import { SyncMessageClass } from './textsecure.d';
import { UserMessage } from './types/Message';
import { MessageModel } from './models/messages';
import { ConversationModel } from './models/conversations';
import { ProfileNameChangeType } from './util/getStringForProfileChange';
interface ModelAttributesInterface { interface ModelAttributesInterface {
[key: string]: any; [key: string]: any;
} }
export type WhatIsThis = any;
type DeletesAttributesType = { type DeletesAttributesType = {
fromId: string; fromId: string;
serverTimestamp: number; serverTimestamp: number;
@ -21,18 +31,89 @@ type DeletesAttributesType = {
export declare class DeletesModelType extends Backbone.Model< export declare class DeletesModelType extends Backbone.Model<
DeletesAttributesType DeletesAttributesType
> { > {
forMessage(message: MessageModelType): Array<DeletesModelType>; forMessage(message: MessageModel): Array<DeletesModelType>;
onDelete(doe: DeletesAttributesType): Promise<void>; onDelete(doe: DeletesAttributesType): Promise<void>;
} }
type TaskResultType = any; type TaskResultType = any;
export interface CustomError extends Error {
identifier?: string;
number?: string;
}
export type MessageAttributesType = { export type MessageAttributesType = {
bodyPending: boolean;
bodyRanges: BodyRangesType;
callHistoryDetails: CallHistoryDetailsType;
changedId: string;
dataMessage: ArrayBuffer | null;
decrypted_at: number;
deletedForEveryone: boolean;
delivered: number;
delivered_to: Array<string | null>;
errors: Array<CustomError> | null;
expirationStartTimestamp: number | null;
expireTimer: number;
expires_at: number;
group_update: {
avatarUpdated: boolean;
joined: Array<string>;
left: string | 'You';
name: string;
};
hasAttachments: boolean;
hasFileAttachments: boolean;
hasVisualMediaAttachments: boolean;
isErased: boolean;
isTapToViewInvalid: boolean;
isViewOnce: boolean;
key_changed: string;
local: boolean;
logger: unknown;
message: unknown;
messageTimer: unknown;
profileChange: ProfileNameChangeType;
quote: {
attachments: Array<typeof window.WhatIsThis>;
author: string;
authorUuid: string;
bodyRanges: BodyRangesType;
id: string;
referencedMessageNotFound: boolean;
text: string;
} | null;
reactions: Array<{ fromId: string; emoji: unknown; timestamp: unknown }>;
read_by: Array<string | null>;
requiredProtocolVersion: number;
sent: boolean;
sourceDevice: string | number;
snippet: unknown;
supportedVersionAtReceive: unknown;
synced: boolean;
unidentifiedDeliveryReceived: boolean;
verified: boolean;
verifiedChanged: string;
id: string; id: string;
type?: string; type?: string;
body: string;
attachments: Array<WhatIsThis>;
preview: Array<WhatIsThis>;
sticker: WhatIsThis;
sent_at: WhatIsThis;
sent_to: Array<string>;
unidentifiedDeliveries: Array<string>;
contact: Array<WhatIsThis>;
conversationId: string;
recipients: Array<WhatIsThis>;
reaction: WhatIsThis;
destination?: WhatIsThis;
destinationUuid?: string;
expirationTimerUpdate?: { expirationTimerUpdate?: {
expireTimer: number; expireTimer: number;
fromSync?: unknown;
source?: string; source?: string;
sourceUuid?: string; sourceUuid?: string;
}; };
@ -46,39 +127,47 @@ export type MessageAttributesType = {
// We set this so that the idle message upgrade process doesn't pick this message up // We set this so that the idle message upgrade process doesn't pick this message up
schemaVersion: number; schemaVersion: number;
serverTimestamp?: number; serverTimestamp?: number;
source?: string;
sourceUuid?: string; sourceUuid?: string;
unread: number;
timestamp: number;
}; };
export declare class MessageModelType extends Backbone.Model< export type ConversationAttributesTypeType = 'private' | 'group';
MessageAttributesType
> {
id: string;
static updateTimers(): void;
getContact(): ConversationModelType | undefined | null;
getConversation(): ConversationModelType | undefined | null;
getPropsForSearchResult(): any;
getPropsForBubble(): any;
cleanup(): Promise<void>;
handleDeleteForEveryone(
doe: DeletesModelType,
shouldPersist: boolean
): Promise<void>;
}
export type ConversationTypeType = 'private' | 'group';
export type ConversationAttributesType = { export type ConversationAttributesType = {
accessKey: string | null;
addedBy: string;
capabilities: { uuid: string };
color?: ColorType;
discoveredUnregisteredAt: number;
draftAttachments: Array<unknown>;
draftTimestamp: number | null;
inbox_position: number;
lastMessageDeletedForEveryone: unknown;
lastMessageStatus: LastMessageStatus | null;
messageCount: number;
messageCountBeforeMessageRequests: number;
messageRequestResponseType: number;
muteExpiresAt: number;
profileAvatar: WhatIsThis;
profileKeyCredential: unknown | null;
profileKeyVersion: string;
quotedMessageId: string;
sealedSender: unknown;
sentMessageCount: number;
sharedGroupNames: Array<string>;
id: string; id: string;
type: ConversationTypeType; type: ConversationAttributesTypeType;
timestamp: number; timestamp: number | null;
// Shared fields // Shared fields
active_at?: number | null; active_at?: number | null;
draft?: string; draft?: string | null;
isArchived?: boolean; isArchived?: boolean;
lastMessage?: string; lastMessage?: string | null;
name?: string; name?: string;
needsStorageServiceSync?: boolean; needsStorageServiceSync?: boolean;
needsVerification?: boolean; needsVerification?: boolean;
@ -93,9 +182,9 @@ export type ConversationAttributesType = {
e164?: string; e164?: string;
// Private other fields // Private other fields
profileFamilyName?: string | null; profileFamilyName?: string;
profileKey?: string | null; profileKey?: string;
profileName?: string | null; profileName?: string;
verified?: number; verified?: number;
// Group-only // Group-only
@ -121,7 +210,7 @@ export type ConversationAttributesType = {
url: string; url: string;
path: string; path: string;
hash: string; hash: string;
}; } | null;
expireTimer?: number; expireTimer?: number;
membersV2?: Array<GroupV2MemberType>; membersV2?: Array<GroupV2MemberType>;
pendingMembersV2?: Array<GroupV2PendingMemberType>; pendingMembersV2?: Array<GroupV2PendingMemberType>;
@ -138,80 +227,19 @@ export type GroupV2PendingMemberType = {
timestamp: number; timestamp: number;
}; };
type VerificationOptions = { export type VerificationOptions = {
key?: null | ArrayBuffer; key?: null | ArrayBuffer;
viaContactSync?: boolean; viaContactSync?: boolean;
viaStorageServiceSync?: boolean; viaStorageServiceSync?: boolean;
viaSyncMessage?: boolean; viaSyncMessage?: boolean;
}; };
export declare class ConversationModelType extends Backbone.Model<
ConversationAttributesType
> {
id: string;
cachedProps: ConversationType;
initialPromise: Promise<any>;
messageRequestEnum: typeof SyncMessageClass.MessageRequestResponse.Type;
addCallHistory(details: CallHistoryDetailsType): void;
applyMessageRequestResponse(
response: number,
options?: { fromSync: boolean; viaStorageServiceSync?: boolean }
): void;
cleanup(): Promise<void>;
disableProfileSharing(options?: { viaStorageServiceSync?: boolean }): void;
dropProfileKey(): Promise<void>;
enableProfileSharing(options?: { viaStorageServiceSync?: boolean }): void;
generateProps(): void;
getAccepted(): boolean;
getAvatarPath(): string | undefined;
getColor(): ColorType | undefined;
getName(): string | undefined;
getNumber(): string;
getProfileName(): string | undefined;
getProfiles(): Promise<Array<Promise<void>>>;
getRecipients: () => Array<string>;
getSendOptions(options?: any): SendOptionsType | undefined;
getTitle(): string;
idForLogging(): string;
debugID(): string;
isFromOrAddedByTrustedContact(): boolean;
isBlocked(): boolean;
isMe(): boolean;
isMuted(): boolean;
isPrivate(): boolean;
isVerified(): boolean;
maybeRepairGroupV2(data: {
masterKey: string;
secretParams: string;
publicParams: string;
}): void;
queueJob(job: () => Promise<void>): Promise<void>;
safeGetVerified(): Promise<number>;
setArchived(isArchived: boolean): void;
setProfileKey(
profileKey?: string | null,
options?: { viaStorageServiceSync?: boolean }
): Promise<void>;
setProfileAvatar(avatarPath: string): Promise<void>;
setUnverified(options: VerificationOptions): Promise<TaskResultType>;
setVerified(options: VerificationOptions): Promise<TaskResultType>;
setVerifiedDefault(options: VerificationOptions): Promise<TaskResultType>;
toggleVerified(): Promise<TaskResultType>;
block(options?: { viaStorageServiceSync?: boolean }): void;
unblock(options?: { viaStorageServiceSync?: boolean }): boolean;
updateE164: (e164?: string) => void;
updateLastMessage: () => Promise<void>;
updateUuid: (uuid?: string) => void;
wrapSend: (sendPromise: Promise<any>) => Promise<any>;
}
export declare class ConversationModelCollectionType extends Backbone.Collection< export declare class ConversationModelCollectionType extends Backbone.Collection<
ConversationModelType ConversationModel
> { > {
resetLookups(): void; resetLookups(): void;
} }
declare class MessageModelCollectionType extends Backbone.Collection< export declare class MessageModelCollectionType extends Backbone.Collection<
MessageModelType MessageModel
> {} > {}

3673
ts/models/conversations.ts Normal file

File diff suppressed because it is too large Load diff

3420
ts/models/messages.ts Normal file

File diff suppressed because it is too large Load diff

View file

@ -22,12 +22,12 @@ import {
CallDetailsType, CallDetailsType,
} from '../state/ducks/calling'; } from '../state/ducks/calling';
import { CallingMessageClass, EnvelopeClass } from '../textsecure.d'; import { CallingMessageClass, EnvelopeClass } from '../textsecure.d';
import { ConversationModelType } from '../model-types.d';
import { import {
AudioDevice, AudioDevice,
CallHistoryDetailsType, CallHistoryDetailsType,
MediaDeviceSettings, MediaDeviceSettings,
} from '../types/Calling'; } from '../types/Calling';
import { ConversationModel } from '../models/conversations';
export { export {
CallState, CallState,
@ -87,7 +87,7 @@ export class CallingClass {
} }
async startOutgoingCall( async startOutgoingCall(
conversation: ConversationModelType, conversation: ConversationModel,
isVideoCall: boolean isVideoCall: boolean
): Promise<void> { ): Promise<void> {
window.log.info('CallingClass.startOutgoingCall()'); window.log.info('CallingClass.startOutgoingCall()');
@ -626,7 +626,7 @@ export class CallingClass {
this.addCallHistoryForAutoEndedIncomingCall(conversation, reason); this.addCallHistoryForAutoEndedIncomingCall(conversation, reason);
} }
private attachToCall(conversation: ConversationModelType, call: Call): void { private attachToCall(conversation: ConversationModel, call: Call): void {
const { uxActions } = this; const { uxActions } = this;
if (!uxActions) { if (!uxActions) {
return; return;
@ -679,8 +679,8 @@ export class CallingClass {
} }
private getRemoteUserIdFromConversation( private getRemoteUserIdFromConversation(
conversation: ConversationModelType conversation: ConversationModel
): UserId | undefined { ): UserId | undefined | null {
const recipients = conversation.getRecipients(); const recipients = conversation.getRecipients();
if (recipients.length !== 1) { if (recipients.length !== 1) {
return undefined; return undefined;
@ -705,7 +705,7 @@ export class CallingClass {
} }
private async getCallSettings( private async getCallSettings(
conversation: ConversationModelType conversation: ConversationModel
): Promise<CallSettings> { ): Promise<CallSettings> {
if (!window.textsecure.messaging) { if (!window.textsecure.messaging) {
throw new Error('getCallSettings: offline!'); throw new Error('getCallSettings: offline!');
@ -725,9 +725,12 @@ export class CallingClass {
} }
private getUxCallDetails( private getUxCallDetails(
conversation: ConversationModelType, conversation: ConversationModel,
call: Call call: Call
): CallDetailsType { ): CallDetailsType {
// Does not meet CallDetailsType interface requirements
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
return { return {
...conversation.cachedProps, ...conversation.cachedProps,
@ -738,7 +741,7 @@ export class CallingClass {
} }
private addCallHistoryForEndedCall( private addCallHistoryForEndedCall(
conversation: ConversationModelType, conversation: ConversationModel,
call: Call, call: Call,
acceptedTimeParam: number | undefined acceptedTimeParam: number | undefined
) { ) {
@ -770,7 +773,7 @@ export class CallingClass {
} }
private addCallHistoryForFailedIncomingCall( private addCallHistoryForFailedIncomingCall(
conversation: ConversationModelType, conversation: ConversationModel,
call: Call call: Call
) { ) {
const callHistoryDetails: CallHistoryDetailsType = { const callHistoryDetails: CallHistoryDetailsType = {
@ -785,7 +788,7 @@ export class CallingClass {
} }
private addCallHistoryForAutoEndedIncomingCall( private addCallHistoryForAutoEndedIncomingCall(
conversation: ConversationModelType, conversation: ConversationModel,
_reason: CallEndedReason _reason: CallEndedReason
) { ) {
const callHistoryDetails: CallHistoryDetailsType = { const callHistoryDetails: CallHistoryDetailsType = {

View file

@ -17,7 +17,6 @@ import {
StorageManifestClass, StorageManifestClass,
StorageRecordClass, StorageRecordClass,
} from '../textsecure.d'; } from '../textsecure.d';
import { ConversationModelType } from '../model-types.d';
import { isEnabled } from '../RemoteConfig'; import { isEnabled } from '../RemoteConfig';
import { import {
mergeAccountRecord, mergeAccountRecord,
@ -29,6 +28,7 @@ import {
toGroupV1Record, toGroupV1Record,
toGroupV2Record, toGroupV2Record,
} from './storageRecordOps'; } from './storageRecordOps';
import { ConversationModel } from '../models/conversations';
const { const {
eraseStorageServiceStateFromConversations, eraseStorageServiceStateFromConversations,
@ -98,7 +98,7 @@ function generateStorageID(): ArrayBuffer {
return Crypto.getRandomBytes(16); return Crypto.getRandomBytes(16);
} }
function isGroupV1(conversation: ConversationModelType): boolean { function isGroupV1(conversation: ConversationModel): boolean {
const groupID = conversation.get('groupId'); const groupID = conversation.get('groupId');
if (!groupID) { if (!groupID) {
return false; return false;
@ -109,7 +109,7 @@ function isGroupV1(conversation: ConversationModelType): boolean {
type GeneratedManifestType = { type GeneratedManifestType = {
conversationsToUpdate: Array<{ conversationsToUpdate: Array<{
conversation: ConversationModelType; conversation: ConversationModel;
storageID: string | undefined; storageID: string | undefined;
}>; }>;
deleteKeys: Array<ArrayBuffer>; deleteKeys: Array<ArrayBuffer>;
@ -602,7 +602,7 @@ async function processManifest(
const localKeys = window const localKeys = window
.getConversations() .getConversations()
.map((conversation: ConversationModelType) => conversation.get('storageID')) .map((conversation: ConversationModel) => conversation.get('storageID'))
.filter(Boolean); .filter(Boolean);
const unknownRecordsArray = const unknownRecordsArray =

View file

@ -14,7 +14,7 @@ import {
GroupV2RecordClass, GroupV2RecordClass,
} from '../textsecure.d'; } from '../textsecure.d';
import { deriveGroupFields, waitThenMaybeUpdateGroup } from '../groups'; import { deriveGroupFields, waitThenMaybeUpdateGroup } from '../groups';
import { ConversationModelType } from '../model-types.d'; import { ConversationModel } from '../models/conversations';
const { updateConversation } = dataInterface; const { updateConversation } = dataInterface;
@ -40,7 +40,7 @@ function toRecordVerified(verified: number): number {
function addUnknownFields( function addUnknownFields(
record: RecordClass, record: RecordClass,
conversation: ConversationModelType conversation: ConversationModel
): void { ): void {
if (record.__unknownFields) { if (record.__unknownFields) {
window.log.info( window.log.info(
@ -56,7 +56,7 @@ function addUnknownFields(
function applyUnknownFields( function applyUnknownFields(
record: RecordClass, record: RecordClass,
conversation: ConversationModelType conversation: ConversationModel
): void { ): void {
if (conversation.get('storageUnknownFields')) { if (conversation.get('storageUnknownFields')) {
window.log.info( window.log.info(
@ -72,7 +72,7 @@ function applyUnknownFields(
} }
export async function toContactRecord( export async function toContactRecord(
conversation: ConversationModelType conversation: ConversationModel
): Promise<ContactRecordClass> { ): Promise<ContactRecordClass> {
const contactRecord = new window.textsecure.protobuf.ContactRecord(); const contactRecord = new window.textsecure.protobuf.ContactRecord();
if (conversation.get('uuid')) { if (conversation.get('uuid')) {
@ -113,7 +113,7 @@ export async function toContactRecord(
} }
export async function toAccountRecord( export async function toAccountRecord(
conversation: ConversationModelType conversation: ConversationModel
): Promise<AccountRecordClass> { ): Promise<AccountRecordClass> {
const accountRecord = new window.textsecure.protobuf.AccountRecord(); const accountRecord = new window.textsecure.protobuf.AccountRecord();
@ -147,7 +147,7 @@ export async function toAccountRecord(
} }
export async function toGroupV1Record( export async function toGroupV1Record(
conversation: ConversationModelType conversation: ConversationModel
): Promise<GroupV1RecordClass> { ): Promise<GroupV1RecordClass> {
const groupV1Record = new window.textsecure.protobuf.GroupV1Record(); const groupV1Record = new window.textsecure.protobuf.GroupV1Record();
@ -164,7 +164,7 @@ export async function toGroupV1Record(
} }
export async function toGroupV2Record( export async function toGroupV2Record(
conversation: ConversationModelType conversation: ConversationModel
): Promise<GroupV2RecordClass> { ): Promise<GroupV2RecordClass> {
const groupV2Record = new window.textsecure.protobuf.GroupV2Record(); const groupV2Record = new window.textsecure.protobuf.GroupV2Record();
@ -185,7 +185,7 @@ type MessageRequestCapableRecord = ContactRecordClass | GroupV1RecordClass;
function applyMessageRequestState( function applyMessageRequestState(
record: MessageRequestCapableRecord, record: MessageRequestCapableRecord,
conversation: ConversationModelType conversation: ConversationModel
): void { ): void {
if (record.blocked) { if (record.blocked) {
conversation.applyMessageRequestResponse( conversation.applyMessageRequestResponse(
@ -218,7 +218,7 @@ type RecordClassObject = {
function doRecordsConflict( function doRecordsConflict(
localRecord: RecordClassObject, localRecord: RecordClassObject,
remoteRecord: RecordClassObject, remoteRecord: RecordClassObject,
conversation: ConversationModelType conversation: ConversationModel
): boolean { ): boolean {
const debugID = conversation.debugID(); const debugID = conversation.debugID();
@ -277,7 +277,7 @@ function doRecordsConflict(
function doesRecordHavePendingChanges( function doesRecordHavePendingChanges(
mergedRecord: RecordClass, mergedRecord: RecordClass,
serviceRecord: RecordClass, serviceRecord: RecordClass,
conversation: ConversationModelType conversation: ConversationModel
): boolean { ): boolean {
const shouldSync = Boolean(conversation.get('needsStorageServiceSync')); const shouldSync = Boolean(conversation.get('needsStorageServiceSync'));

View file

@ -21,9 +21,7 @@ import { createBatcher } from '../util/batcher';
import { import {
ConversationModelCollectionType, ConversationModelCollectionType,
ConversationModelType,
MessageModelCollectionType, MessageModelCollectionType,
MessageModelType,
} from '../model-types.d'; } from '../model-types.d';
import { import {
@ -45,6 +43,8 @@ import {
StickerType, StickerType,
UnprocessedType, UnprocessedType,
} from './Interface'; } from './Interface';
import { MessageModel } from '../models/messages';
import { ConversationModel } from '../models/conversations';
// We listen to a lot of events on ipcRenderer, often on the same channel. This prevents // We listen to a lot of events on ipcRenderer, often on the same channel. This prevents
// any warnings that might be sent to the console in that case. // any warnings that might be sent to the console in that case.
@ -726,7 +726,7 @@ async function saveConversations(array: Array<ConversationType>) {
async function getConversationById( async function getConversationById(
id: string, id: string,
{ Conversation }: { Conversation: typeof ConversationModelType } { Conversation }: { Conversation: typeof ConversationModel }
) { ) {
const data = await channels.getConversationById(id); const data = await channels.getConversationById(id);
@ -756,7 +756,7 @@ async function updateConversations(array: Array<ConversationType>) {
async function removeConversation( async function removeConversation(
id: string, id: string,
{ Conversation }: { Conversation: typeof ConversationModelType } { Conversation }: { Conversation: typeof ConversationModel }
) { ) {
const existing = await getConversationById(id, { Conversation }); const existing = await getConversationById(id, { Conversation });
@ -869,10 +869,7 @@ async function getMessageCount(conversationId?: string) {
async function saveMessage( async function saveMessage(
data: MessageType, data: MessageType,
{ { forceSave, Message }: { forceSave?: boolean; Message: typeof MessageModel }
forceSave,
Message,
}: { forceSave?: boolean; Message: typeof MessageModelType }
) { ) {
const id = await channels.saveMessage(_cleanData(data), { forceSave }); const id = await channels.saveMessage(_cleanData(data), { forceSave });
Message.updateTimers(); Message.updateTimers();
@ -889,7 +886,7 @@ async function saveMessages(
async function removeMessage( async function removeMessage(
id: string, id: string,
{ Message }: { Message: typeof MessageModelType } { Message }: { Message: typeof MessageModel }
) { ) {
const message = await getMessageById(id, { Message }); const message = await getMessageById(id, { Message });
@ -908,7 +905,7 @@ async function _removeMessages(ids: Array<string>) {
async function getMessageById( async function getMessageById(
id: string, id: string,
{ Message }: { Message: typeof MessageModelType } { Message }: { Message: typeof MessageModel }
) { ) {
const message = await channels.getMessageById(id); const message = await channels.getMessageById(id);
if (!message) { if (!message) {
@ -947,7 +944,7 @@ async function getMessageBySender(
sourceDevice: string; sourceDevice: string;
sent_at: number; sent_at: number;
}, },
{ Message }: { Message: typeof MessageModelType } { Message }: { Message: typeof MessageModel }
) { ) {
const messages = await channels.getMessageBySender({ const messages = await channels.getMessageBySender({
source, source,
@ -1027,9 +1024,9 @@ async function getNewerMessagesByConversation(
async function getLastConversationActivity( async function getLastConversationActivity(
conversationId: string, conversationId: string,
options: { options: {
Message: typeof MessageModelType; Message: typeof MessageModel;
} }
): Promise<MessageModelType | undefined> { ): Promise<MessageModel | undefined> {
const { Message } = options; const { Message } = options;
const result = await channels.getLastConversationActivity(conversationId); const result = await channels.getLastConversationActivity(conversationId);
if (result) { if (result) {
@ -1040,9 +1037,9 @@ async function getLastConversationActivity(
async function getLastConversationPreview( async function getLastConversationPreview(
conversationId: string, conversationId: string,
options: { options: {
Message: typeof MessageModelType; Message: typeof MessageModel;
} }
): Promise<MessageModelType | undefined> { ): Promise<MessageModel | undefined> {
const { Message } = options; const { Message } = options;
const result = await channels.getLastConversationPreview(conversationId); const result = await channels.getLastConversationPreview(conversationId);
if (result) { if (result) {
@ -1083,12 +1080,12 @@ async function removeAllMessagesInConversation(
return; return;
} }
const ids = messages.map((message: MessageModelType) => message.id); const ids = messages.map((message: MessageModel) => message.id);
// Note: It's very important that these models are fully hydrated because // Note: It's very important that these models are fully hydrated because
// we need to delete all associated on-disk files along with the database delete. // we need to delete all associated on-disk files along with the database delete.
await Promise.all( await Promise.all(
messages.map(async (message: MessageModelType) => message.cleanup()) messages.map(async (message: MessageModel) => message.cleanup())
); );
await channels.removeMessage(ids); await channels.removeMessage(ids);
@ -1129,7 +1126,7 @@ async function getOutgoingWithoutExpiresAt({
async function getNextExpiringMessage({ async function getNextExpiringMessage({
Message, Message,
}: { }: {
Message: typeof MessageModelType; Message: typeof MessageModel;
}) { }) {
const message = await channels.getNextExpiringMessage(); const message = await channels.getNextExpiringMessage();
@ -1143,7 +1140,7 @@ async function getNextExpiringMessage({
async function getNextTapToViewMessageToAgeOut({ async function getNextTapToViewMessageToAgeOut({
Message, Message,
}: { }: {
Message: typeof MessageModelType; Message: typeof MessageModel;
}) { }) {
const message = await channels.getNextTapToViewMessageToAgeOut(); const message = await channels.getNextTapToViewMessageToAgeOut();
if (!message) { if (!message) {

View file

@ -19,10 +19,10 @@ export type UnprocessedType = any;
import { import {
ConversationModelCollectionType, ConversationModelCollectionType,
ConversationModelType,
MessageModelCollectionType, MessageModelCollectionType,
MessageModelType,
} from '../model-types.d'; } from '../model-types.d';
import { MessageModel } from '../models/messages';
import { ConversationModel } from '../models/conversations';
export interface DataInterface { export interface DataInterface {
close: () => Promise<void>; close: () => Promise<void>;
@ -204,7 +204,11 @@ export type ServerInterface = DataInterface & {
getMessagesBySentAt: (sentAt: number) => Promise<Array<MessageType>>; getMessagesBySentAt: (sentAt: number) => Promise<Array<MessageType>>;
getOlderMessagesByConversation: ( getOlderMessagesByConversation: (
conversationId: string, conversationId: string,
options?: { limit?: number; receivedAt?: number; messageId?: string } options?: {
limit?: number;
receivedAt?: number;
messageId?: string;
}
) => Promise<Array<MessageTypeUnhydrated>>; ) => Promise<Array<MessageTypeUnhydrated>>;
getNewerMessagesByConversation: ( getNewerMessagesByConversation: (
conversationId: string, conversationId: string,
@ -228,7 +232,7 @@ export type ServerInterface = DataInterface & {
saveMessage: ( saveMessage: (
data: MessageType, data: MessageType,
options: { forceSave?: boolean } options: { forceSave?: boolean }
) => Promise<number>; ) => Promise<string>;
updateConversation: (data: ConversationType) => Promise<void>; updateConversation: (data: ConversationType) => Promise<void>;
// For testing only // For testing only
@ -272,8 +276,8 @@ export type ClientInterface = DataInterface & {
}) => Promise<ConversationModelCollectionType>; }) => Promise<ConversationModelCollectionType>;
getConversationById: ( getConversationById: (
id: string, id: string,
{ Conversation }: { Conversation: typeof ConversationModelType } { Conversation }: { Conversation: typeof ConversationModel }
) => Promise<ConversationModelType>; ) => Promise<ConversationModel>;
getExpiredMessages: ({ getExpiredMessages: ({
MessageCollection, MessageCollection,
}: { }: {
@ -281,7 +285,7 @@ export type ClientInterface = DataInterface & {
}) => Promise<MessageModelCollectionType>; }) => Promise<MessageModelCollectionType>;
getMessageById: ( getMessageById: (
id: string, id: string,
{ Message }: { Message: typeof MessageModelType } { Message }: { Message: typeof MessageModel }
) => Promise<MessageType | undefined>; ) => Promise<MessageType | undefined>;
getMessageBySender: ( getMessageBySender: (
options: { options: {
@ -290,8 +294,8 @@ export type ClientInterface = DataInterface & {
sourceDevice: string; sourceDevice: string;
sent_at: number; sent_at: number;
}, },
{ Message }: { Message: typeof MessageModelType } { Message }: { Message: typeof MessageModel }
) => Promise<MessageModelType | null>; ) => Promise<MessageModel | null>;
getMessagesBySentAt: ( getMessagesBySentAt: (
sentAt: number, sentAt: number,
{ {
@ -302,6 +306,7 @@ export type ClientInterface = DataInterface & {
conversationId: string, conversationId: string,
options: { options: {
limit?: number; limit?: number;
messageId?: string;
receivedAt?: number; receivedAt?: number;
MessageCollection: typeof MessageModelCollectionType; MessageCollection: typeof MessageModelCollectionType;
} }
@ -317,25 +322,25 @@ export type ClientInterface = DataInterface & {
getLastConversationActivity: ( getLastConversationActivity: (
conversationId: string, conversationId: string,
options: { options: {
Message: typeof MessageModelType; Message: typeof MessageModel;
} }
) => Promise<MessageModelType | undefined>; ) => Promise<MessageModel | undefined>;
getLastConversationPreview: ( getLastConversationPreview: (
conversationId: string, conversationId: string,
options: { options: {
Message: typeof MessageModelType; Message: typeof MessageModel;
} }
) => Promise<MessageModelType | undefined>; ) => Promise<MessageModel | undefined>;
getNextExpiringMessage: ({ getNextExpiringMessage: ({
Message, Message,
}: { }: {
Message: typeof MessageModelType; Message: typeof MessageModel;
}) => Promise<MessageModelType | null>; }) => Promise<MessageModel | null>;
getNextTapToViewMessageToAgeOut: ({ getNextTapToViewMessageToAgeOut: ({
Message, Message,
}: { }: {
Message: typeof MessageModelType; Message: typeof MessageModel;
}) => Promise<MessageModelType | null>; }) => Promise<MessageModel | null>;
getOutgoingWithoutExpiresAt: ({ getOutgoingWithoutExpiresAt: ({
MessageCollection, MessageCollection,
}: { }: {
@ -354,17 +359,17 @@ export type ClientInterface = DataInterface & {
) => Promise<MessageModelCollectionType>; ) => Promise<MessageModelCollectionType>;
removeConversation: ( removeConversation: (
id: string, id: string,
{ Conversation }: { Conversation: typeof ConversationModelType } { Conversation }: { Conversation: typeof ConversationModel }
) => Promise<void>; ) => Promise<void>;
removeMessage: ( removeMessage: (
id: string, id: string,
{ Message }: { Message: typeof MessageModelType } { Message }: { Message: typeof MessageModel }
) => Promise<void>; ) => Promise<void>;
saveMessage: ( saveMessage: (
data: MessageType, data: MessageType,
options: { forceSave?: boolean; Message: typeof MessageModelType } options: { forceSave?: boolean; Message: typeof MessageModel }
) => Promise<number>; ) => Promise<string>;
updateConversation: (data: ConversationType) => void; updateConversation: (data: ConversationType, extra?: unknown) => void;
// Test-only // Test-only

View file

@ -2674,7 +2674,11 @@ async function getOlderMessagesByConversation(
limit = 100, limit = 100,
receivedAt = Number.MAX_VALUE, receivedAt = Number.MAX_VALUE,
messageId, messageId,
}: { limit?: number; receivedAt?: number; messageId?: string } = {} }: {
limit?: number;
receivedAt?: number;
messageId?: string;
} = {}
) { ) {
const db = getInstance(); const db = getInstance();
let rows; let rows;

View file

@ -23,6 +23,17 @@ export type DBConversationType = {
lastMessage: string; lastMessage: string;
type: string; type: string;
}; };
export type LastMessageStatus =
| 'error'
| 'partial-sent'
| 'sending'
| 'sent'
| 'delivered'
| 'read';
export type ConversationTypeType = 'direct' | 'group';
export type ConversationType = { export type ConversationType = {
id: string; id: string;
uuid?: string; uuid?: string;
@ -39,19 +50,13 @@ export type ConversationType = {
timestamp?: number; timestamp?: number;
inboxPosition?: number; inboxPosition?: number;
lastMessage?: { lastMessage?: {
status: status: LastMessageStatus;
| 'error'
| 'partial-sent'
| 'sending'
| 'sent'
| 'delivered'
| 'read';
text: string; text: string;
}; };
phoneNumber?: string; phoneNumber?: string;
membersCount?: number; membersCount?: number;
muteExpiresAt?: number; muteExpiresAt?: number;
type: 'direct' | 'group'; type: ConversationTypeType;
isMe?: boolean; isMe?: boolean;
lastUpdated: number; lastUpdated: number;
title: string; title: string;
@ -59,14 +64,14 @@ export type ConversationType = {
isSelected?: boolean; isSelected?: boolean;
typingContact?: { typingContact?: {
avatarPath?: string; avatarPath?: string;
color: string; color?: ColorType;
name?: string; name?: string;
phoneNumber: string; phoneNumber?: string;
profileName?: string; profileName?: string;
}; } | null;
shouldShowDraft?: boolean; shouldShowDraft?: boolean;
draftText?: string; draftText?: string | null;
draftPreview?: string; draftPreview?: string;
messageRequestsEnabled?: boolean; messageRequestsEnabled?: boolean;

19
ts/textsecure.d.ts vendored
View file

@ -39,7 +39,7 @@ export type StorageServiceCredentials = {
export type TextSecureType = { export type TextSecureType = {
createTaskWithTimeout: ( createTaskWithTimeout: (
task: () => Promise<any>, task: () => Promise<any> | any,
id?: string, id?: string,
options?: { timeout?: number } options?: { timeout?: number }
) => () => Promise<any>; ) => () => Promise<any>;
@ -77,12 +77,16 @@ export type TextSecureType = {
protocol: StorageProtocolType; protocol: StorageProtocolType;
}; };
messageReceiver: MessageReceiver; messageReceiver: MessageReceiver;
messaging?: SendMessage; messageSender: MessageSender;
messaging: SendMessage;
protobuf: ProtobufCollectionType; protobuf: ProtobufCollectionType;
utils: typeof utils; utils: typeof utils;
EventTarget: typeof EventTarget; EventTarget: typeof EventTarget;
MessageReceiver: typeof MessageReceiver; MessageReceiver: typeof MessageReceiver;
AccountManager: WhatIsThis;
MessageSender: WhatIsThis;
SyncRequest: WhatIsThis;
}; };
type StoredSignedPreKeyType = SignedPreKeyType & { type StoredSignedPreKeyType = SignedPreKeyType & {
@ -108,11 +112,13 @@ export type StorageProtocolType = StorageType & {
removeSession: (identifier: string) => Promise<void>; removeSession: (identifier: string) => Promise<void>;
getDeviceIds: (identifier: string) => Promise<Array<number>>; getDeviceIds: (identifier: string) => Promise<Array<number>>;
getIdentityRecord: (identifier: string) => IdentityKeyRecord | undefined; getIdentityRecord: (identifier: string) => IdentityKeyRecord | undefined;
getVerified: (id: string) => Promise<number>;
hydrateCaches: () => Promise<void>; hydrateCaches: () => Promise<void>;
clearPreKeyStore: () => Promise<void>; clearPreKeyStore: () => Promise<void>;
clearSignedPreKeysStore: () => Promise<void>; clearSignedPreKeysStore: () => Promise<void>;
clearSessionStore: () => Promise<void>; clearSessionStore: () => Promise<void>;
isTrustedIdentity: () => void; isTrustedIdentity: () => void;
isUntrusted: (id: string) => Promise<boolean>;
storePreKey: (keyId: number, keyPair: KeyPairType) => Promise<void>; storePreKey: (keyId: number, keyPair: KeyPairType) => Promise<void>;
storeSignedPreKey: ( storeSignedPreKey: (
keyId: number, keyId: number,
@ -131,6 +137,7 @@ export type StorageProtocolType = StorageType & {
number: string, number: string,
options: IdentityKeyRecord options: IdentityKeyRecord
) => Promise<void>; ) => Promise<void>;
setApproval: (id: string, something: boolean) => void;
setVerified: ( setVerified: (
encodedAddress: string, encodedAddress: string,
verifiedStatus: number, verifiedStatus: number,
@ -138,6 +145,8 @@ export type StorageProtocolType = StorageType & {
) => Promise<void>; ) => Promise<void>;
removeSignedPreKey: (keyId: number) => Promise<void>; removeSignedPreKey: (keyId: number) => Promise<void>;
removeAllData: () => Promise<void>; removeAllData: () => Promise<void>;
on: (key: string, callback: () => void) => WhatIsThis;
removeAllConfiguration: () => Promise<void>;
}; };
// Protobufs // Protobufs
@ -279,6 +288,7 @@ export declare class AccessControlClass {
// Note: we need to use namespaces to express nested classes in Typescript // Note: we need to use namespaces to express nested classes in Typescript
export declare namespace AccessControlClass { export declare namespace AccessControlClass {
class AccessRequired { class AccessRequired {
static ANY: number;
static UNKNOWN: number; static UNKNOWN: number;
static MEMBER: number; static MEMBER: number;
static ADMINISTRATOR: number; static ADMINISTRATOR: number;
@ -445,6 +455,10 @@ export declare class AttachmentPointerClass {
encoding?: string encoding?: string
) => AttachmentPointerClass; ) => AttachmentPointerClass;
static Flags: {
VOICE_MESSAGE: number;
};
cdnId?: ProtoBigNumberType; cdnId?: ProtoBigNumberType;
cdnKey?: string; cdnKey?: string;
contentType?: string; contentType?: string;
@ -1144,6 +1158,7 @@ export declare class VerifiedClass {
data: ArrayBuffer | ByteBufferClass, data: ArrayBuffer | ByteBufferClass,
encoding?: string encoding?: string
) => VerifiedClass; ) => VerifiedClass;
static State: WhatIsThis;
destination?: string; destination?: string;
destinationUuid?: string; destinationUuid?: string;

View file

@ -586,7 +586,7 @@ export default class MessageSender {
messageProto: DataMessageClass, messageProto: DataMessageClass,
silent?: boolean, silent?: boolean,
options?: SendOptionsType options?: SendOptionsType
) { ): Promise<CallbackResultType> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const callback = (result: CallbackResultType) => { const callback = (result: CallbackResultType) => {
if (result && result.errors && result.errors.length > 0) { if (result && result.errors && result.errors.length > 0) {
@ -615,7 +615,7 @@ export default class MessageSender {
timestamp: number, timestamp: number,
silent?: boolean, silent?: boolean,
options?: SendOptionsType options?: SendOptionsType
) { ): Promise<CallbackResultType> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const callback = (res: CallbackResultType) => { const callback = (res: CallbackResultType) => {
if (res && res.errors && res.errors.length > 0) { if (res && res.errors && res.errors.length > 0) {
@ -651,8 +651,8 @@ export default class MessageSender {
async sendSyncMessage( async sendSyncMessage(
encodedDataMessage: ArrayBuffer, encodedDataMessage: ArrayBuffer,
timestamp: number, timestamp: number,
destination: string, destination: string | undefined,
destinationUuid: string | null, destinationUuid: string | null | undefined,
expirationStartTimestamp: number | null, expirationStartTimestamp: number | null,
sentTo: Array<string> = [], sentTo: Array<string> = [],
unidentifiedDeliveries: Array<string> = [], unidentifiedDeliveries: Array<string> = [],
@ -927,11 +927,11 @@ export default class MessageSender {
async sendTypingMessage( async sendTypingMessage(
options: { options: {
recipientId: string; recipientId?: string;
groupId: string; groupId?: string;
groupMembers: Array<string>; groupMembers: Array<string>;
isTyping: boolean; isTyping: boolean;
timestamp: number; timestamp?: number;
}, },
sendOptions: SendOptionsType = {} sendOptions: SendOptionsType = {}
) { ) {
@ -950,9 +950,9 @@ export default class MessageSender {
throw new Error('Need to provide either recipientId or groupId!'); throw new Error('Need to provide either recipientId or groupId!');
} }
const recipients = groupId const recipients = (groupId
? (without(groupMembers, myNumber, myUuid) as Array<string>) ? without(groupMembers, myNumber, myUuid)
: [recipientId]; : [recipientId]) as Array<string>;
const groupIdBuffer = groupId const groupIdBuffer = groupId
? fromEncodedBinaryToArrayBuffer(groupId) ? fromEncodedBinaryToArrayBuffer(groupId)
: null; : null;
@ -1035,7 +1035,7 @@ export default class MessageSender {
recipientUuid: string, recipientUuid: string,
timestamps: Array<number>, timestamps: Array<number>,
options?: SendOptionsType options?: SendOptionsType
) { ): Promise<CallbackResultType | void> {
const myNumber = window.textsecure.storage.user.getNumber(); const myNumber = window.textsecure.storage.user.getNumber();
const myUuid = window.textsecure.storage.user.getUuid(); const myUuid = window.textsecure.storage.user.getUuid();
const myDevice = window.textsecure.storage.user.getDeviceId(); const myDevice = window.textsecure.storage.user.getDeviceId();
@ -1129,7 +1129,7 @@ export default class MessageSender {
senderUuid: string, senderUuid: string,
timestamp: number, timestamp: number,
options?: SendOptionsType options?: SendOptionsType
) { ): Promise<CallbackResultType | null> {
const myNumber = window.textsecure.storage.user.getNumber(); const myNumber = window.textsecure.storage.user.getNumber();
const myUuid = window.textsecure.storage.user.getUuid(); const myUuid = window.textsecure.storage.user.getUuid();
const myDevice = window.textsecure.storage.user.getDeviceId(); const myDevice = window.textsecure.storage.user.getDeviceId();
@ -1207,7 +1207,7 @@ export default class MessageSender {
installed: boolean; installed: boolean;
}>, }>,
options?: SendOptionsType options?: SendOptionsType
) { ): Promise<CallbackResultType | null> {
const myDevice = window.textsecure.storage.user.getDeviceId(); const myDevice = window.textsecure.storage.user.getDeviceId();
if (myDevice === 1 || myDevice === '1') { if (myDevice === 1 || myDevice === '1') {
return null; return null;
@ -1359,10 +1359,10 @@ export default class MessageSender {
async getMessageProto( async getMessageProto(
destination: string, destination: string,
body: string, body: string | undefined,
attachments: Array<AttachmentType> | null, attachments: Array<AttachmentType>,
quote: any, quote: any,
preview: Array<PreviewType> | null, preview: Array<PreviewType>,
sticker: any, sticker: any,
reaction: any, reaction: any,
timestamp: number, timestamp: number,
@ -1402,10 +1402,10 @@ export default class MessageSender {
async sendMessageToIdentifier( async sendMessageToIdentifier(
identifier: string, identifier: string,
messageText: string, messageText: string | undefined,
attachments: Array<AttachmentType> | null, attachments: Array<AttachmentType> | undefined,
quote: any, quote: any,
preview: Array<PreviewType> | null, preview: Array<PreviewType> | undefined,
sticker: any, sticker: any,
reaction: any, reaction: any,
timestamp: number, timestamp: number,

View file

@ -419,7 +419,7 @@ async function _promiseAjax(
// Build expired! // Build expired!
if (response.status === 499) { if (response.status === 499) {
window.log.error('Error: build expired'); window.log.error('Error: build expired');
window.storage.put('remoteBuildExpiration', Date.now()); await window.storage.put('remoteBuildExpiration', Date.now());
window.reduxActions.expiration.hydrateExpirationStatus(true); window.reduxActions.expiration.hydrateExpirationStatus(true);
} }

View file

@ -12,10 +12,10 @@ export type RenderTextCallbackType = (options: {
}) => JSX.Element | string; }) => JSX.Element | string;
export type ReplacementValuesType = { export type ReplacementValuesType = {
[key: string]: string; [key: string]: string | undefined;
}; };
export type LocalizerType = ( export type LocalizerType = (
key: string, key: string,
values?: Array<string> | ReplacementValuesType values?: Array<string | null> | ReplacementValuesType
) => string; ) => string;

View file

@ -1,9 +1,10 @@
import { DeletesModelType, MessageModelType } from '../model-types.d'; import { DeletesModelType } from '../model-types.d';
import { MessageModel } from '../models/messages';
const ONE_DAY = 24 * 60 * 60 * 1000; const ONE_DAY = 24 * 60 * 60 * 1000;
export async function deleteForEveryone( export async function deleteForEveryone(
message: MessageModelType, message: MessageModel,
doe: DeletesModelType, doe: DeletesModelType,
shouldPersist = true shouldPersist = true
): Promise<void> { ): Promise<void> {

View file

@ -46,10 +46,14 @@ const excludedFilesRegexps = [
'\\.d\\.ts$', '\\.d\\.ts$',
// High-traffic files in our project // High-traffic files in our project
'^js/models/messages.js', '^ts/models/messages.js',
'^js/models/conversations.js', '^ts/models/messages.ts',
'^js/views/conversation_view.js', '^ts/models/conversations.js',
'^js/background.js', '^ts/models/conversations.ts',
'^ts/views/conversation_view.js',
'^ts/views/conversation_view.ts',
'^ts/background.js',
'^ts/background.ts',
'^ts/Crypto.js', '^ts/Crypto.js',
'^ts/Crypto.ts', '^ts/Crypto.ts',
'^ts/textsecure/MessageReceiver.js', '^ts/textsecure/MessageReceiver.js',

File diff suppressed because it is too large Load diff

500
ts/window.d.ts vendored
View file

@ -5,9 +5,8 @@ import * as Underscore from 'underscore';
import { Ref } from 'react'; import { Ref } from 'react';
import { import {
ConversationModelCollectionType, ConversationModelCollectionType,
ConversationModelType,
MessageModelCollectionType, MessageModelCollectionType,
MessageModelType, MessageAttributesType,
} from './model-types.d'; } from './model-types.d';
import { import {
LibSignalType, LibSignalType,
@ -19,7 +18,7 @@ import { WebAPIConnectType } from './textsecure/WebAPI';
import { CallingClass } from './services/calling'; import { CallingClass } from './services/calling';
import * as Crypto from './Crypto'; import * as Crypto from './Crypto';
import * as RemoteConfig from './RemoteConfig'; import * as RemoteConfig from './RemoteConfig';
import { LocalizerType } from './types/Util'; import { LocalizerType, BodyRangesType } from './types/Util';
import { CallHistoryDetailsType } from './types/Calling'; import { CallHistoryDetailsType } from './types/Calling';
import { ColorType } from './types/Colors'; import { ColorType } from './types/Colors';
import { ConversationController } from './ConversationController'; import { ConversationController } from './ConversationController';
@ -27,16 +26,47 @@ import { ReduxActions } from './state/types';
import { SendOptionsType } from './textsecure/SendMessage'; import { SendOptionsType } from './textsecure/SendMessage';
import AccountManager from './textsecure/AccountManager'; import AccountManager from './textsecure/AccountManager';
import Data from './sql/Client'; import Data from './sql/Client';
import { UserMessage } from './types/Message';
import PQueue from 'p-queue/dist';
import { PhoneNumberFormat } from 'google-libphonenumber';
import { MessageModel } from './models/messages';
import { ConversationModel } from './models/conversations';
import { combineNames } from './util';
import { BatcherType } from './util/batcher';
export { Long } from 'long'; export { Long } from 'long';
type TaskResultType = any; type TaskResultType = any;
type WhatIsThis = any;
declare global { declare global {
interface Window { interface Window {
_: typeof Underscore;
$: typeof jQuery;
extension: any;
moment: any;
imageToBlurHash: any;
autoOrientImage: any;
dataURLToBlobSync: any;
loadImage: any;
isBehindProxy: any;
PQueue: typeof PQueue;
PQueueType: PQueue;
WhatIsThis: WhatIsThis;
baseAttachmentsPath: string;
baseStickersPath: string;
baseTempPath: string;
dcodeIO: DCodeIOType; dcodeIO: DCodeIOType;
enterKeyboardMode: () => void;
enterMouseMode: () => void;
getAccountManager: () => AccountManager | undefined; getAccountManager: () => AccountManager | undefined;
getAlwaysRelayCalls: () => Promise<boolean>; getAlwaysRelayCalls: () => Promise<boolean>;
getBuiltInImages: () => Promise<Array<WhatIsThis>>;
getCallRingtoneNotification: () => Promise<boolean>; getCallRingtoneNotification: () => Promise<boolean>;
getCallSystemNotification: () => Promise<boolean>; getCallSystemNotification: () => Promise<boolean>;
getConversations: () => ConversationModelCollectionType; getConversations: () => ConversationModelCollectionType;
@ -46,20 +76,33 @@ declare global {
getGuid: () => string; getGuid: () => string;
getInboxCollection: () => ConversationModelCollectionType; getInboxCollection: () => ConversationModelCollectionType;
getIncomingCallNotification: () => Promise<boolean>; getIncomingCallNotification: () => Promise<boolean>;
getInteractionMode: () => string;
getMediaCameraPermissions: () => Promise<boolean>; getMediaCameraPermissions: () => Promise<boolean>;
getMediaPermissions: () => Promise<boolean>; getMediaPermissions: () => Promise<boolean>;
getServerPublicParams: () => string; getServerPublicParams: () => string;
getSocketStatus: () => number; getSocketStatus: () => number;
getSyncRequest: () => WhatIsThis;
getTitle: () => string; getTitle: () => string;
waitForEmptyEventQueue: () => Promise<void>; waitForEmptyEventQueue: () => Promise<void>;
getVersion: () => string; getVersion: () => string;
showCallingPermissionsPopup: (forCamera: boolean) => Promise<void>; showCallingPermissionsPopup: (forCamera: boolean) => Promise<void>;
i18n: LocalizerType; i18n: LocalizerType;
isValidGuid: (maybeGuid: string) => boolean; isActive: () => boolean;
isAfterVersion: (version: WhatIsThis, anotherVersion: string) => boolean;
isBeforeVersion: (version: WhatIsThis, anotherVersion: string) => boolean;
isValidGuid: (maybeGuid: string | null) => boolean;
isValidE164: (maybeE164: unknown) => boolean;
libphonenumber: { libphonenumber: {
util: { util: {
getRegionCodeForNumber: (number: string) => string; getRegionCodeForNumber: (number: string) => string;
parseNumber: (
e164: string,
regionCode: string
) => typeof window.Signal.Types.PhoneNumber;
}; };
parse: (number: string) => string;
getRegionCodeForNumber: (number: string) => string;
format: (number: string, format: PhoneNumberFormat) => string;
}; };
libsignal: LibSignalType; libsignal: LibSignalType;
log: { log: {
@ -67,28 +110,69 @@ declare global {
warn: LoggerType; warn: LoggerType;
error: LoggerType; error: LoggerType;
}; };
nodeSetImmediate: typeof setImmediate;
normalizeUuids: (obj: any, paths: Array<string>, context: string) => any; normalizeUuids: (obj: any, paths: Array<string>, context: string) => any;
owsDesktopApp: WhatIsThis;
platform: string; platform: string;
preloadedImages: Array<WhatIsThis>;
reduxActions: ReduxActions; reduxActions: ReduxActions;
reduxStore: WhatIsThis;
registerForActive: (handler: WhatIsThis) => void;
resetActiveTimer: () => void;
restart: () => void; restart: () => void;
setImmediate: typeof setImmediate;
showWindow: () => void; showWindow: () => void;
showSettings: () => void; showSettings: () => void;
shutdown: () => void;
setAutoHideMenuBar: (value: WhatIsThis) => void;
setBadgeCount: (count: number) => void; setBadgeCount: (count: number) => void;
setMenuBarVisibility: (value: WhatIsThis) => void;
showKeyboardShortcuts: () => void;
storage: { storage: {
put: (key: string, value: any) => void; addBlockedGroup: (group: string) => void;
remove: (key: string) => Promise<void>;
get: <T = any>(key: string) => T | undefined;
addBlockedNumber: (number: string) => void; addBlockedNumber: (number: string) => void;
addBlockedUuid: (uuid: string) => void;
fetch: () => void;
get: <T = any>(key: string, defaultValue?: T) => T | undefined;
getItemsState: () => WhatIsThis;
isBlocked: (number: string) => boolean; isBlocked: (number: string) => boolean;
isGroupBlocked: (group: unknown) => boolean;
isUuidBlocked: (uuid: string) => boolean;
onready: WhatIsThis;
put: (key: string, value: any) => Promise<void>;
remove: (key: string) => Promise<void>;
removeBlockedGroup: (group: string) => void;
removeBlockedNumber: (number: string) => void; removeBlockedNumber: (number: string) => void;
removeBlockedUuid: (uuid: string) => void;
}; };
systemTheme: WhatIsThis;
textsecure: TextSecureType; textsecure: TextSecureType;
unregisterForActive: (handler: WhatIsThis) => void;
updateTrayIcon: (count: number) => void; updateTrayIcon: (count: number) => void;
Backbone: typeof Backbone; Backbone: typeof Backbone;
Signal: { Signal: {
Backbone: any;
AttachmentDownloads: {
addJob: <T = unknown>(
attachment: unknown,
options: unknown
) => Promise<T>;
start: (options: WhatIsThis) => void;
stop: () => void;
};
Crypto: typeof Crypto; Crypto: typeof Crypto;
Data: typeof Data; Data: typeof Data;
Groups: {
maybeUpdateGroup: (options: unknown) => Promise<void>;
waitThenMaybeUpdateGroup: (options: unknown) => Promise<void>;
uploadGroupChange: (
options: unknown
) => Promise<{ toArrayBuffer: () => ArrayBuffer }>;
buildDisappearingMessagesTimerChange: (
options: unknown
) => { version: number };
};
Metadata: { Metadata: {
SecretSessionCipher: typeof SecretSessionCipherClass; SecretSessionCipher: typeof SecretSessionCipherClass;
createCertificateValidator: ( createCertificateValidator: (
@ -98,22 +182,301 @@ declare global {
RemoteConfig: typeof RemoteConfig; RemoteConfig: typeof RemoteConfig;
Services: { Services: {
calling: CallingClass; calling: CallingClass;
eraseAllStorageServiceState: () => Promise<void>;
handleUnknownRecords: (param: WhatIsThis) => void;
initializeGroupCredentialFetcher: () => void;
initializeNetworkObserver: (network: WhatIsThis) => void;
initializeUpdateListener: (
updates: WhatIsThis,
events: WhatIsThis
) => void;
runStorageServiceSyncJob: () => Promise<void>;
storageServiceUploadJob: () => void;
}; };
Migrations: { Migrations: {
readTempData: any;
deleteAttachmentData: (path: string) => Promise<void>; deleteAttachmentData: (path: string) => Promise<void>;
doesAttachmentExist: () => unknown;
writeNewAttachmentData: (data: ArrayBuffer) => Promise<string>; writeNewAttachmentData: (data: ArrayBuffer) => Promise<string>;
deleteExternalMessageFiles: (attributes: unknown) => Promise<void>;
getAbsoluteAttachmentPath: (path: string) => string;
loadAttachmentData: (attachment: WhatIsThis) => WhatIsThis;
loadQuoteData: (quote: unknown) => WhatIsThis;
loadPreviewData: (preview: unknown) => WhatIsThis;
loadStickerData: (sticker: unknown) => WhatIsThis;
readStickerData: (path: string) => Promise<ArrayBuffer>;
upgradeMessageSchema: (attributes: unknown) => WhatIsThis;
copyIntoTempDirectory: any;
deleteDraftFile: any;
deleteTempFile: any;
getAbsoluteDraftPath: any;
getAbsoluteTempPath: any;
openFileInFolder: any;
readAttachmentData: any;
readDraftData: any;
saveAttachmentToDisk: any;
writeNewDraftData: any;
};
Stickers: {
getDataFromLink: any;
copyStickerToAttachments: (
packId: string,
stickerId: number
) => Promise<typeof window.Signal.Types.Sticker>;
deletePackReference: (id: string, packId: string) => Promise<void>;
downloadEphemeralPack: (
packId: string,
key: WhatIsThis
) => Promise<void>;
downloadQueuedPacks: () => void;
downloadStickerPack: (
id: string,
key: string,
options: WhatIsThis
) => void;
getInitialState: () => WhatIsThis;
load: () => void;
removeEphemeralPack: (packId: string) => Promise<void>;
savePackMetadata: (
packId: string,
packKey: string,
metadata: unknown
) => void;
getStickerPackStatus: (packId: string) => 'downloaded' | 'installed';
getSticker: (
packId: string,
stickerId: number
) => typeof window.Signal.Types.Sticker;
getStickerPack: (packId: string) => WhatIsThis;
getInstalledStickerPacks: () => WhatIsThis;
}; };
Types: { Types: {
Attachment: {
save: any;
path: string;
pending: boolean;
flags: number;
size: number;
screenshot: {
path: string;
};
thumbnail: {
path: string;
objectUrl: string;
};
contentType: string;
error: unknown;
migrateDataToFileSystem: (
attachment: WhatIsThis,
options: unknown
) => WhatIsThis;
isVoiceMessage: (attachments: unknown) => boolean;
isImage: (attachments: unknown) => boolean;
isVideo: (attachments: unknown) => boolean;
isAudio: (attachments: unknown) => boolean;
};
MIME: {
IMAGE_GIF: unknown;
isImage: any;
isJPEG: any;
};
Contact: {
avatar?: { avatar?: unknown };
number: Array<{ value: string }>;
signalAccount: unknown;
contactSelector: (
contact: typeof window.Signal.Types.Contact,
options: unknown
) => typeof window.Signal.Types.Contact;
getName: (contact: typeof window.Signal.Types.Contact) => string;
};
Conversation: {
computeHash: (data: string) => Promise<string>;
deleteExternalFiles: (
attributes: unknown,
options: unknown
) => Promise<void>;
maybeUpdateProfileAvatar: (
attributes: unknown,
decrypted: unknown,
options: unknown
) => Promise<Record<string, unknown>>;
maybeUpdateAvatar: (
attributes: unknown,
data: unknown,
options: unknown
) => Promise<WhatIsThis>;
};
PhoneNumber: {
format: (
identifier: string,
options: Record<string, unknown>
) => string;
isValidNumber(
phoneNumber: string,
options?: {
regionCode?: string;
}
): boolean;
e164: string;
error: string;
};
Errors: {
toLogFormat(error: Error): void;
};
Message: { Message: {
CURRENT_SCHEMA_VERSION: number; CURRENT_SCHEMA_VERSION: number;
VERSION_NEEDED_FOR_DISPLAY: number;
GROUP: 'group';
PRIVATE: 'private';
initializeSchemaVersion: (version: {
message: unknown;
logger: unknown;
}) => unknown & {
schemaVersion: number;
}; };
hasExpiration: (json: string) => boolean;
}; };
Sticker: {
emoji: string;
packId: string;
packKey: string;
stickerId: number;
data: {
pending: boolean;
path: string;
}; };
width: number;
height: number;
path: string;
};
VisualAttachment: any;
};
Util: {
isFileDangerous: any;
GoogleChrome: {
isImageTypeSupported: (contentType: string) => unknown;
isVideoTypeSupported: (contentType: string) => unknown;
};
downloadAttachment: (attachment: WhatIsThis) => WhatIsThis;
getStringForProfileChange: (
change: unknown,
changedContact: unknown,
i18n: unknown
) => string;
getTextWithMentions: (
bodyRanges: BodyRangesType,
text: string
) => string;
deleteForEveryone: (
message: unknown,
del: unknown,
bool: boolean
) => void;
zkgroup: {
generateProfileKeyCredentialRequest: (
clientZkProfileCipher: unknown,
uuid: string,
profileKey: unknown
) => { requestHex: string; context: unknown };
getClientZkProfileOperations: (params: unknown) => unknown;
handleProfileKeyCredential: (
clientZkProfileCipher: unknown,
profileCredentialRequestContext: unknown,
credential: unknown
) => unknown;
deriveProfileKeyVersion: (
profileKey: unknown,
uuid: string
) => string;
};
combineNames: typeof combineNames;
migrateColor: (color: string) => ColorType;
createBatcher: (options: WhatIsThis) => WhatIsThis;
Registration: {
everDone: () => boolean;
markDone: () => void;
markEverDone: () => void;
remove: () => void;
};
hasExpired: () => boolean;
makeLookup: (conversations: WhatIsThis, key: string) => void;
parseRemoteClientExpiration: (value: WhatIsThis) => WhatIsThis;
};
LinkPreviews: {
isMediaLinkInWhitelist: any;
getTitleMetaTag: any;
getImageMetaTag: any;
assembleChunks: any;
getChunkPattern: any;
isLinkInWhitelist: any;
isStickerPack: (url: string) => boolean;
isLinkSafeToPreview: (url: string) => boolean;
findLinks: (body: string, unknown?: any) => Array<string>;
getDomain: (url: string) => string;
};
GroupChange: {
renderChange: (change: unknown, things: unknown) => Array<string>;
};
Components: {
StagedLinkPreview: any;
Quote: any;
ContactDetail: any;
MessageDetail: any;
Lightbox: any;
MediaGallery: any;
CaptionEditor: any;
ConversationHeader: any;
AttachmentList: any;
getCallingNotificationText: (
callHistoryDetails: unknown,
i18n: unknown
) => string;
LightboxGallery: any;
};
OS: {
isLinux: () => boolean;
};
Workflow: {
IdleDetector: WhatIsThis;
MessageDataMigrator: WhatIsThis;
};
IndexedDB: {
removeDatabase: WhatIsThis;
doesDatabaseExist: WhatIsThis;
};
Views: WhatIsThis;
State: WhatIsThis;
Logs: WhatIsThis;
conversationControllerStart: WhatIsThis;
Emojis: {
getInitialState: () => WhatIsThis;
load: () => void;
};
RefreshSenderCertificate: WhatIsThis;
};
ConversationController: ConversationController; ConversationController: ConversationController;
Events: WhatIsThis;
MessageController: MessageControllerType; MessageController: MessageControllerType;
WebAPI: WebAPIConnectType; WebAPI: WebAPIConnectType;
Whisper: WhisperType; Whisper: WhisperType;
AccountCache: Record<string, boolean>;
AccountJobs: Record<string, Promise<void>>;
doesAccountCheckJobExist: (number: string) => boolean;
checkForSignalAccount: (number: string) => Promise<void>;
isSignalAccountCheckComplete: (number: string) => boolean;
hasSignalAccount: (number: string) => boolean;
getServerTrustRoot: () => WhatIsThis;
readyForUpdates: () => void;
// Flags // Flags
CALLING: boolean; CALLING: boolean;
GV2: boolean; GV2: boolean;
@ -132,12 +495,13 @@ export type DCodeIOType = {
}; };
Long: Long & { Long: Long & {
fromBits: (low: number, high: number, unsigned: boolean) => number; fromBits: (low: number, high: number, unsigned: boolean) => number;
fromString: (str: string) => Long; fromString: (str: string | null) => Long;
}; };
}; };
type MessageControllerType = { type MessageControllerType = {
register: (id: string, model: MessageModelType) => MessageModelType; register: (id: string, model: MessageModel) => MessageModel;
unregister: (id: string) => void;
}; };
export class CertificateValidatorType { export class CertificateValidatorType {
@ -211,7 +575,7 @@ export type LoggerType = (...args: Array<any>) => void;
export type WhisperType = { export type WhisperType = {
events: { events: {
on: (name: string, callback: (param1: any, param2?: any) => void) => void; on: (name: string, callback: (param1: any, param2?: any) => void) => void;
trigger: (name: string, param1: any, param2?: any) => void; trigger: (name: string, param1?: any, param2?: any) => void;
}; };
Database: { Database: {
open: () => Promise<IDBDatabase>; open: () => Promise<IDBDatabase>;
@ -221,8 +585,120 @@ export type WhisperType = {
reject: Function reject: Function
) => void; ) => void;
}; };
GroupConversationCollection: typeof ConversationModelCollectionType;
ConversationCollection: typeof ConversationModelCollectionType; ConversationCollection: typeof ConversationModelCollectionType;
Conversation: typeof ConversationModelType; ConversationCollectionType: ConversationModelCollectionType;
Conversation: typeof ConversationModel;
ConversationType: ConversationModel;
MessageCollection: typeof MessageModelCollectionType; MessageCollection: typeof MessageModelCollectionType;
Message: typeof MessageModelType; MessageCollectionType: MessageModelCollectionType;
MessageAttributesType: MessageAttributesType;
Message: typeof MessageModel;
MessageType: MessageModel;
GroupMemberConversation: WhatIsThis;
KeyChangeListener: WhatIsThis;
ConfirmationDialogView: WhatIsThis;
ClearDataView: WhatIsThis;
ReactWrapperView: WhatIsThis;
activeConfirmationView: WhatIsThis;
ToastView: WhatIsThis;
ConversationArchivedToast: WhatIsThis;
ConversationUnarchivedToast: WhatIsThis;
AppView: WhatIsThis;
WallClockListener: WhatIsThis;
MessageRequests: WhatIsThis;
BannerView: any;
RecorderView: any;
GroupMemberList: any;
KeyVerificationPanelView: any;
SafetyNumberChangeDialogView: any;
ExpirationTimerOptions: {
map: any;
getName: (number: number) => string;
getAbbreviated: (number: number) => string;
};
Notifications: {
removeBy: (filter: Partial<unknown>) => void;
add: (notification: unknown) => void;
clear: () => void;
disable: () => void;
enable: () => void;
fastClear: () => void;
on: (
event: string,
callback: (id: string, messageId: string) => void
) => void;
};
DeliveryReceipts: {
add: (reciept: WhatIsThis) => void;
forMessage: (conversation: unknown, message: unknown) => Array<WhatIsThis>;
onReceipt: (receipt: WhatIsThis) => void;
};
ReadReceipts: {
add: (receipt: WhatIsThis) => WhatIsThis;
forMessage: (conversation: unknown, message: unknown) => Array<WhatIsThis>;
onReceipt: (receipt: WhatIsThis) => void;
};
ReadSyncs: {
add: (sync: WhatIsThis) => WhatIsThis;
forMessage: (message: unknown) => WhatIsThis;
onReceipt: (receipt: WhatIsThis) => WhatIsThis;
};
ViewSyncs: {
add: (sync: WhatIsThis) => WhatIsThis;
forMessage: (message: unknown) => Array<WhatIsThis>;
onSync: (sync: WhatIsThis) => WhatIsThis;
};
Reactions: {
forMessage: (message: unknown) => Array<WhatIsThis>;
add: (reaction: unknown) => WhatIsThis;
onReaction: (reactionModel: unknown) => unknown;
};
Deletes: {
add: (model: WhatIsThis) => WhatIsThis;
forMessage: (message: unknown) => Array<WhatIsThis>;
onDelete: (model: WhatIsThis) => void;
};
IdenticonSVGView: WhatIsThis;
ExpiringMessagesListener: WhatIsThis;
TapToViewMessagesListener: WhatIsThis;
deliveryReceiptQueue: PQueue<WhatIsThis>;
deliveryReceiptBatcher: BatcherType<WhatIsThis>;
RotateSignedPreKeyListener: WhatIsThis;
ExpiredToast: any;
BlockedToast: any;
BlockedGroupToast: any;
LeftGroupToast: any;
OriginalNotFoundToast: any;
OriginalNoLongerAvailableToast: any;
FoundButNotLoadedToast: any;
VoiceNoteLimit: any;
VoiceNoteMustBeOnlyAttachmentToast: any;
TapToViewExpiredIncomingToast: any;
TapToViewExpiredOutgoingToast: any;
FileSavedToast: any;
ReactionFailedToast: any;
MessageBodyTooLongToast: any;
FileSizeToast: any;
UnableToLoadToast: any;
DangerousFileTypeToast: any;
OneNonImageAtATimeToast: any;
CannotMixImageAndNonImageAttachmentsToast: any;
MaxAttachmentsToast: any;
TimerConflictToast: any;
ConversationLoadingScreen: any;
ConversationView: any;
View: any;
}; };

View file

@ -186,6 +186,7 @@
"ts/components/conversation/**", "ts/components/conversation/**",
"ts/components/emoji/**", "ts/components/emoji/**",
"ts/components/stickers/**", "ts/components/stickers/**",
"ts/models/**",
"ts/notifications/**", "ts/notifications/**",
"ts/protobuf/**", "ts/protobuf/**",
"ts/scripts/**", "ts/scripts/**",
@ -196,7 +197,8 @@
"ts/test/**", "ts/test/**",
"ts/types/**", "ts/types/**",
"ts/updater/**", "ts/updater/**",
"ts/util/**" "ts/util/**",
"ts/views/**"
] ]
} }
} }