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 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/database.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/deletes.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/expiring_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/group_member_list_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/confirmation_dialog_view.js'></script>
<script type='text/javascript' src='js/views/identicon_svg_view.js'></script>
@ -387,6 +386,6 @@
</div>
</div>
<script type='text/javascript' src='js/background.js'></script>
<script type='text/javascript' src='ts/background.js'></script>
</body>
</html>

View file

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

View file

@ -51,6 +51,7 @@
{{ toastMessage }}
</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/toast_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');
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>
<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>
</body>
</html>

View file

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

View file

@ -32,6 +32,7 @@
</div>
</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/confirmation_dialog_view.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.getThemeSetting = makeGetter('theme-setting');
window.setThemeSetting = makeSetter('theme-setting');
window.Backbone = require('backbone');

View file

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

View file

@ -164,6 +164,7 @@
</div>
</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/settings_view.js'></script>
<script type='text/javascript' src='js/settings_start.js'></script>

View file

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

View file

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

View file

@ -18,6 +18,7 @@ window.getEnvironment = () => config.environment;
window.getVersion = () => config.version;
window.getGuid = require('uuid/v4');
window.PQueue = require('p-queue').default;
window.Backbone = require('backbone');
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="../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="test.js"></script>

View file

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

View file

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

View file

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

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

@ -1,17 +1,27 @@
import * as Backbone from 'backbone';
import { GroupV2ChangeType } from './groups';
import { LocalizerType } from './types/Util';
import { LocalizerType, BodyRangesType } from './types/Util';
import { CallHistoryDetailsType } from './types/Calling';
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 { 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 {
[key: string]: any;
}
export type WhatIsThis = any;
type DeletesAttributesType = {
fromId: string;
serverTimestamp: number;
@ -21,18 +31,89 @@ type DeletesAttributesType = {
export declare class DeletesModelType extends Backbone.Model<
DeletesAttributesType
> {
forMessage(message: MessageModelType): Array<DeletesModelType>;
forMessage(message: MessageModel): Array<DeletesModelType>;
onDelete(doe: DeletesAttributesType): Promise<void>;
}
type TaskResultType = any;
export interface CustomError extends Error {
identifier?: string;
number?: string;
}
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;
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?: {
expireTimer: number;
fromSync?: unknown;
source?: 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
schemaVersion: number;
serverTimestamp?: number;
source?: string;
sourceUuid?: string;
unread: number;
timestamp: number;
};
export declare class MessageModelType extends Backbone.Model<
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 ConversationAttributesTypeType = 'private' | 'group';
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;
type: ConversationTypeType;
timestamp: number;
type: ConversationAttributesTypeType;
timestamp: number | null;
// Shared fields
active_at?: number | null;
draft?: string;
draft?: string | null;
isArchived?: boolean;
lastMessage?: string;
lastMessage?: string | null;
name?: string;
needsStorageServiceSync?: boolean;
needsVerification?: boolean;
@ -93,9 +182,9 @@ export type ConversationAttributesType = {
e164?: string;
// Private other fields
profileFamilyName?: string | null;
profileKey?: string | null;
profileName?: string | null;
profileFamilyName?: string;
profileKey?: string;
profileName?: string;
verified?: number;
// Group-only
@ -121,7 +210,7 @@ export type ConversationAttributesType = {
url: string;
path: string;
hash: string;
};
} | null;
expireTimer?: number;
membersV2?: Array<GroupV2MemberType>;
pendingMembersV2?: Array<GroupV2PendingMemberType>;
@ -138,80 +227,19 @@ export type GroupV2PendingMemberType = {
timestamp: number;
};
type VerificationOptions = {
export type VerificationOptions = {
key?: null | ArrayBuffer;
viaContactSync?: boolean;
viaStorageServiceSync?: 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<
ConversationModelType
ConversationModel
> {
resetLookups(): void;
}
declare class MessageModelCollectionType extends Backbone.Collection<
MessageModelType
export declare class MessageModelCollectionType extends Backbone.Collection<
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,
} from '../state/ducks/calling';
import { CallingMessageClass, EnvelopeClass } from '../textsecure.d';
import { ConversationModelType } from '../model-types.d';
import {
AudioDevice,
CallHistoryDetailsType,
MediaDeviceSettings,
} from '../types/Calling';
import { ConversationModel } from '../models/conversations';
export {
CallState,
@ -87,7 +87,7 @@ export class CallingClass {
}
async startOutgoingCall(
conversation: ConversationModelType,
conversation: ConversationModel,
isVideoCall: boolean
): Promise<void> {
window.log.info('CallingClass.startOutgoingCall()');
@ -626,7 +626,7 @@ export class CallingClass {
this.addCallHistoryForAutoEndedIncomingCall(conversation, reason);
}
private attachToCall(conversation: ConversationModelType, call: Call): void {
private attachToCall(conversation: ConversationModel, call: Call): void {
const { uxActions } = this;
if (!uxActions) {
return;
@ -679,8 +679,8 @@ export class CallingClass {
}
private getRemoteUserIdFromConversation(
conversation: ConversationModelType
): UserId | undefined {
conversation: ConversationModel
): UserId | undefined | null {
const recipients = conversation.getRecipients();
if (recipients.length !== 1) {
return undefined;
@ -705,7 +705,7 @@ export class CallingClass {
}
private async getCallSettings(
conversation: ConversationModelType
conversation: ConversationModel
): Promise<CallSettings> {
if (!window.textsecure.messaging) {
throw new Error('getCallSettings: offline!');
@ -725,9 +725,12 @@ export class CallingClass {
}
private getUxCallDetails(
conversation: ConversationModelType,
conversation: ConversationModel,
call: Call
): CallDetailsType {
// Does not meet CallDetailsType interface requirements
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
return {
...conversation.cachedProps,
@ -738,7 +741,7 @@ export class CallingClass {
}
private addCallHistoryForEndedCall(
conversation: ConversationModelType,
conversation: ConversationModel,
call: Call,
acceptedTimeParam: number | undefined
) {
@ -770,7 +773,7 @@ export class CallingClass {
}
private addCallHistoryForFailedIncomingCall(
conversation: ConversationModelType,
conversation: ConversationModel,
call: Call
) {
const callHistoryDetails: CallHistoryDetailsType = {
@ -785,7 +788,7 @@ export class CallingClass {
}
private addCallHistoryForAutoEndedIncomingCall(
conversation: ConversationModelType,
conversation: ConversationModel,
_reason: CallEndedReason
) {
const callHistoryDetails: CallHistoryDetailsType = {

View file

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

View file

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

View file

@ -21,9 +21,7 @@ import { createBatcher } from '../util/batcher';
import {
ConversationModelCollectionType,
ConversationModelType,
MessageModelCollectionType,
MessageModelType,
} from '../model-types.d';
import {
@ -45,6 +43,8 @@ import {
StickerType,
UnprocessedType,
} 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
// 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(
id: string,
{ Conversation }: { Conversation: typeof ConversationModelType }
{ Conversation }: { Conversation: typeof ConversationModel }
) {
const data = await channels.getConversationById(id);
@ -756,7 +756,7 @@ async function updateConversations(array: Array<ConversationType>) {
async function removeConversation(
id: string,
{ Conversation }: { Conversation: typeof ConversationModelType }
{ Conversation }: { Conversation: typeof ConversationModel }
) {
const existing = await getConversationById(id, { Conversation });
@ -869,10 +869,7 @@ async function getMessageCount(conversationId?: string) {
async function saveMessage(
data: MessageType,
{
forceSave,
Message,
}: { forceSave?: boolean; Message: typeof MessageModelType }
{ forceSave, Message }: { forceSave?: boolean; Message: typeof MessageModel }
) {
const id = await channels.saveMessage(_cleanData(data), { forceSave });
Message.updateTimers();
@ -889,7 +886,7 @@ async function saveMessages(
async function removeMessage(
id: string,
{ Message }: { Message: typeof MessageModelType }
{ Message }: { Message: typeof MessageModel }
) {
const message = await getMessageById(id, { Message });
@ -908,7 +905,7 @@ async function _removeMessages(ids: Array<string>) {
async function getMessageById(
id: string,
{ Message }: { Message: typeof MessageModelType }
{ Message }: { Message: typeof MessageModel }
) {
const message = await channels.getMessageById(id);
if (!message) {
@ -947,7 +944,7 @@ async function getMessageBySender(
sourceDevice: string;
sent_at: number;
},
{ Message }: { Message: typeof MessageModelType }
{ Message }: { Message: typeof MessageModel }
) {
const messages = await channels.getMessageBySender({
source,
@ -1027,9 +1024,9 @@ async function getNewerMessagesByConversation(
async function getLastConversationActivity(
conversationId: string,
options: {
Message: typeof MessageModelType;
Message: typeof MessageModel;
}
): Promise<MessageModelType | undefined> {
): Promise<MessageModel | undefined> {
const { Message } = options;
const result = await channels.getLastConversationActivity(conversationId);
if (result) {
@ -1040,9 +1037,9 @@ async function getLastConversationActivity(
async function getLastConversationPreview(
conversationId: string,
options: {
Message: typeof MessageModelType;
Message: typeof MessageModel;
}
): Promise<MessageModelType | undefined> {
): Promise<MessageModel | undefined> {
const { Message } = options;
const result = await channels.getLastConversationPreview(conversationId);
if (result) {
@ -1083,12 +1080,12 @@ async function removeAllMessagesInConversation(
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
// we need to delete all associated on-disk files along with the database delete.
await Promise.all(
messages.map(async (message: MessageModelType) => message.cleanup())
messages.map(async (message: MessageModel) => message.cleanup())
);
await channels.removeMessage(ids);
@ -1129,7 +1126,7 @@ async function getOutgoingWithoutExpiresAt({
async function getNextExpiringMessage({
Message,
}: {
Message: typeof MessageModelType;
Message: typeof MessageModel;
}) {
const message = await channels.getNextExpiringMessage();
@ -1143,7 +1140,7 @@ async function getNextExpiringMessage({
async function getNextTapToViewMessageToAgeOut({
Message,
}: {
Message: typeof MessageModelType;
Message: typeof MessageModel;
}) {
const message = await channels.getNextTapToViewMessageToAgeOut();
if (!message) {

View file

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

View file

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

View file

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

19
ts/textsecure.d.ts vendored
View file

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

View file

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

View file

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

View file

@ -12,10 +12,10 @@ export type RenderTextCallbackType = (options: {
}) => JSX.Element | string;
export type ReplacementValuesType = {
[key: string]: string;
[key: string]: string | undefined;
};
export type LocalizerType = (
key: string,
values?: Array<string> | ReplacementValuesType
values?: Array<string | null> | ReplacementValuesType
) => 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;
export async function deleteForEveryone(
message: MessageModelType,
message: MessageModel,
doe: DeletesModelType,
shouldPersist = true
): Promise<void> {

View file

@ -46,10 +46,14 @@ const excludedFilesRegexps = [
'\\.d\\.ts$',
// High-traffic files in our project
'^js/models/messages.js',
'^js/models/conversations.js',
'^js/views/conversation_view.js',
'^js/background.js',
'^ts/models/messages.js',
'^ts/models/messages.ts',
'^ts/models/conversations.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.ts',
'^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 {
ConversationModelCollectionType,
ConversationModelType,
MessageModelCollectionType,
MessageModelType,
MessageAttributesType,
} from './model-types.d';
import {
LibSignalType,
@ -19,7 +18,7 @@ import { WebAPIConnectType } from './textsecure/WebAPI';
import { CallingClass } from './services/calling';
import * as Crypto from './Crypto';
import * as RemoteConfig from './RemoteConfig';
import { LocalizerType } from './types/Util';
import { LocalizerType, BodyRangesType } from './types/Util';
import { CallHistoryDetailsType } from './types/Calling';
import { ColorType } from './types/Colors';
import { ConversationController } from './ConversationController';
@ -27,16 +26,47 @@ import { ReduxActions } from './state/types';
import { SendOptionsType } from './textsecure/SendMessage';
import AccountManager from './textsecure/AccountManager';
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';
type TaskResultType = any;
type WhatIsThis = any;
declare global {
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;
enterKeyboardMode: () => void;
enterMouseMode: () => void;
getAccountManager: () => AccountManager | undefined;
getAlwaysRelayCalls: () => Promise<boolean>;
getBuiltInImages: () => Promise<Array<WhatIsThis>>;
getCallRingtoneNotification: () => Promise<boolean>;
getCallSystemNotification: () => Promise<boolean>;
getConversations: () => ConversationModelCollectionType;
@ -46,20 +76,33 @@ declare global {
getGuid: () => string;
getInboxCollection: () => ConversationModelCollectionType;
getIncomingCallNotification: () => Promise<boolean>;
getInteractionMode: () => string;
getMediaCameraPermissions: () => Promise<boolean>;
getMediaPermissions: () => Promise<boolean>;
getServerPublicParams: () => string;
getSocketStatus: () => number;
getSyncRequest: () => WhatIsThis;
getTitle: () => string;
waitForEmptyEventQueue: () => Promise<void>;
getVersion: () => string;
showCallingPermissionsPopup: (forCamera: boolean) => Promise<void>;
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: {
util: {
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;
log: {
@ -67,28 +110,69 @@ declare global {
warn: LoggerType;
error: LoggerType;
};
nodeSetImmediate: typeof setImmediate;
normalizeUuids: (obj: any, paths: Array<string>, context: string) => any;
owsDesktopApp: WhatIsThis;
platform: string;
preloadedImages: Array<WhatIsThis>;
reduxActions: ReduxActions;
reduxStore: WhatIsThis;
registerForActive: (handler: WhatIsThis) => void;
resetActiveTimer: () => void;
restart: () => void;
setImmediate: typeof setImmediate;
showWindow: () => void;
showSettings: () => void;
shutdown: () => void;
setAutoHideMenuBar: (value: WhatIsThis) => void;
setBadgeCount: (count: number) => void;
setMenuBarVisibility: (value: WhatIsThis) => void;
showKeyboardShortcuts: () => void;
storage: {
put: (key: string, value: any) => void;
remove: (key: string) => Promise<void>;
get: <T = any>(key: string) => T | undefined;
addBlockedGroup: (group: 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;
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;
removeBlockedUuid: (uuid: string) => void;
};
systemTheme: WhatIsThis;
textsecure: TextSecureType;
unregisterForActive: (handler: WhatIsThis) => void;
updateTrayIcon: (count: number) => void;
Backbone: typeof Backbone;
Signal: {
Backbone: any;
AttachmentDownloads: {
addJob: <T = unknown>(
attachment: unknown,
options: unknown
) => Promise<T>;
start: (options: WhatIsThis) => void;
stop: () => void;
};
Crypto: typeof Crypto;
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: {
SecretSessionCipher: typeof SecretSessionCipherClass;
createCertificateValidator: (
@ -98,22 +182,301 @@ declare global {
RemoteConfig: typeof RemoteConfig;
Services: {
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: {
readTempData: any;
deleteAttachmentData: (path: string) => Promise<void>;
doesAttachmentExist: () => unknown;
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: {
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: {
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;
Events: WhatIsThis;
MessageController: MessageControllerType;
WebAPI: WebAPIConnectType;
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
CALLING: boolean;
GV2: boolean;
@ -132,12 +495,13 @@ export type DCodeIOType = {
};
Long: Long & {
fromBits: (low: number, high: number, unsigned: boolean) => number;
fromString: (str: string) => Long;
fromString: (str: string | null) => Long;
};
};
type MessageControllerType = {
register: (id: string, model: MessageModelType) => MessageModelType;
register: (id: string, model: MessageModel) => MessageModel;
unregister: (id: string) => void;
};
export class CertificateValidatorType {
@ -211,7 +575,7 @@ export type LoggerType = (...args: Array<any>) => void;
export type WhisperType = {
events: {
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: {
open: () => Promise<IDBDatabase>;
@ -221,8 +585,120 @@ export type WhisperType = {
reject: Function
) => void;
};
GroupConversationCollection: typeof ConversationModelCollectionType;
ConversationCollection: typeof ConversationModelCollectionType;
Conversation: typeof ConversationModelType;
ConversationCollectionType: ConversationModelCollectionType;
Conversation: typeof ConversationModel;
ConversationType: ConversationModel;
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/emoji/**",
"ts/components/stickers/**",
"ts/models/**",
"ts/notifications/**",
"ts/protobuf/**",
"ts/scripts/**",
@ -196,7 +197,8 @@
"ts/test/**",
"ts/types/**",
"ts/updater/**",
"ts/util/**"
"ts/util/**",
"ts/views/**"
]
}
}