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

@ -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.$;

3303
ts/background.ts Normal file

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;
};