Adjust some types
This commit is contained in:
parent
9ce4b8977d
commit
d7307934bc
15 changed files with 223 additions and 175 deletions
|
@ -21,11 +21,11 @@ import type {
|
|||
MessageAttributesType,
|
||||
ConversationAttributesType,
|
||||
ReactionAttributesType,
|
||||
ValidateConversationType,
|
||||
} from './model-types.d';
|
||||
import * as Bytes from './Bytes';
|
||||
import * as Timers from './Timers';
|
||||
import * as indexedDb from './indexeddb';
|
||||
import type { WhatIsThis } from './window.d';
|
||||
import type { MenuOptionsType } from './types/menu';
|
||||
import type { Receipt } from './types/Receipt';
|
||||
import { SocketStatus } from './types/SocketStatus';
|
||||
|
@ -133,8 +133,7 @@ import { onRetryRequest, onDecryptionError } from './util/handleRetry';
|
|||
import { themeChanged } from './shims/themeChanged';
|
||||
import { createIPCEvents } from './util/createIPCEvents';
|
||||
import { RemoveAllConfiguration } from './types/RemoveAllConfiguration';
|
||||
import { isValidUuid, UUIDKind } from './types/UUID';
|
||||
import type { UUID } from './types/UUID';
|
||||
import { isValidUuid, UUIDKind, UUID } from './types/UUID';
|
||||
import * as log from './logging/log';
|
||||
import { loadRecentEmojis } from './util/loadRecentEmojis';
|
||||
import { deleteAllLogs } from './util/deleteAllLogs';
|
||||
|
@ -155,6 +154,7 @@ import { conversationJobQueue } from './jobs/conversationJobQueue';
|
|||
import { SeenStatus } from './MessageSeenStatus';
|
||||
import MessageSender from './textsecure/SendMessage';
|
||||
import type AccountManager from './textsecure/AccountManager';
|
||||
import { validateConversation } from './util/validateConversation';
|
||||
|
||||
const MAX_ATTACHMENT_DOWNLOAD_AGE = 3600 * 72 * 1000;
|
||||
|
||||
|
@ -1227,7 +1227,7 @@ export async function startApp(): Promise<void> {
|
|||
window.reduxActions.user.userChanged({ menuOptions: options });
|
||||
});
|
||||
|
||||
let shortcutGuideView: WhatIsThis | null = null;
|
||||
let shortcutGuideView: ReactWrapperView | null = null;
|
||||
|
||||
window.showKeyboardShortcuts = () => {
|
||||
if (!shortcutGuideView) {
|
||||
|
@ -2724,12 +2724,13 @@ export async function startApp(): Promise<void> {
|
|||
function onContactReceived(ev: ContactEvent) {
|
||||
const details = ev.contactDetails;
|
||||
|
||||
const c = new window.Whisper.Conversation({
|
||||
const partialConversation: ValidateConversationType = {
|
||||
e164: details.number,
|
||||
uuid: details.uuid,
|
||||
uuid: UUID.fromString(details.uuid),
|
||||
type: 'private',
|
||||
} as Partial<ConversationAttributesType> as WhatIsThis);
|
||||
const validationError = c.validate();
|
||||
};
|
||||
|
||||
const validationError = validateConversation(partialConversation);
|
||||
if (validationError) {
|
||||
log.error(
|
||||
'Invalid contact received:',
|
||||
|
@ -3188,7 +3189,8 @@ export async function startApp(): Promise<void> {
|
|||
.filter(isNotNil);
|
||||
}
|
||||
|
||||
return new window.Whisper.Message({
|
||||
const partialMessage: MessageAttributesType = {
|
||||
id: UUID.generate().toString(),
|
||||
canReplyToStory: data.message.isStory
|
||||
? data.message.canReplyToStory
|
||||
: undefined,
|
||||
|
@ -3211,7 +3213,9 @@ export async function startApp(): Promise<void> {
|
|||
type: data.message.isStory ? 'story' : 'outgoing',
|
||||
storyDistributionListId: data.storyDistributionListId,
|
||||
unidentifiedDeliveries,
|
||||
} as Partial<MessageAttributesType> as WhatIsThis);
|
||||
};
|
||||
|
||||
return new window.Whisper.Message(partialMessage);
|
||||
}
|
||||
|
||||
// Works with 'sent' and 'message' data sent from MessageReceiver, with a little massage
|
||||
|
@ -3446,7 +3450,8 @@ export async function startApp(): Promise<void> {
|
|||
Boolean(data.receivedAtCounter),
|
||||
`Did not receive receivedAtCounter for message: ${data.timestamp}`
|
||||
);
|
||||
return new window.Whisper.Message({
|
||||
const partialMessage: MessageAttributesType = {
|
||||
id: UUID.generate().toString(),
|
||||
canReplyToStory: data.message.isStory
|
||||
? data.message.canReplyToStory
|
||||
: undefined,
|
||||
|
@ -3460,11 +3465,14 @@ export async function startApp(): Promise<void> {
|
|||
serverTimestamp: data.serverTimestamp,
|
||||
source: data.source,
|
||||
sourceDevice: data.sourceDevice,
|
||||
sourceUuid: data.sourceUuid,
|
||||
sourceUuid: data.sourceUuid
|
||||
? UUID.fromString(data.sourceUuid)
|
||||
: undefined,
|
||||
timestamp: data.timestamp,
|
||||
type: data.message.isStory ? 'story' : 'incoming',
|
||||
unidentifiedDeliveryReceived: data.unidentifiedDeliveryReceived,
|
||||
} as Partial<MessageAttributesType> as WhatIsThis);
|
||||
};
|
||||
return new window.Whisper.Message(partialMessage);
|
||||
}
|
||||
|
||||
// Returns `false` if this message isn't a group call message.
|
||||
|
|
|
@ -20,8 +20,9 @@ import type {
|
|||
} from '../../textsecure/SendMessage';
|
||||
import type { LinkPreviewType } from '../../types/message/LinkPreviews';
|
||||
import type { BodyRangesType, StoryContextType } from '../../types/Util';
|
||||
import type { WhatIsThis } from '../../window.d';
|
||||
import type { LoggerType } from '../../types/Logging';
|
||||
import type { StickerWithHydratedData } from '../../types/Stickers';
|
||||
import type { QuotedMessageType } from '../../model-types.d';
|
||||
import type {
|
||||
ConversationQueueJobBundle,
|
||||
NormalMessageSendJobData,
|
||||
|
@ -31,6 +32,7 @@ import { handleMultipleSendErrors } from './handleMultipleSendErrors';
|
|||
import { ourProfileKeyService } from '../../services/ourProfileKey';
|
||||
import { isConversationUnregistered } from '../../util/isConversationUnregistered';
|
||||
import { isConversationAccepted } from '../../util/isConversationAccepted';
|
||||
import { sendToGroup } from '../../util/sendToGroup';
|
||||
|
||||
export async function sendNormalMessage(
|
||||
conversation: ConversationModel,
|
||||
|
@ -207,7 +209,7 @@ export async function sendNormalMessage(
|
|||
innerPromise = conversation.queueJob(
|
||||
'conversationQueue/sendNormalMessage',
|
||||
abortSignal =>
|
||||
window.Signal.Util.sendToGroup({
|
||||
sendToGroup({
|
||||
abortSignal,
|
||||
contentHint: ContentHint.RESENDABLE,
|
||||
groupSendOptions: {
|
||||
|
@ -428,8 +430,8 @@ async function getMessageSendData({
|
|||
mentions: undefined | BodyRangesType;
|
||||
messageTimestamp: number;
|
||||
preview: Array<LinkPreviewType>;
|
||||
quote: WhatIsThis;
|
||||
sticker: WhatIsThis;
|
||||
quote: QuotedMessageType | null;
|
||||
sticker: StickerWithHydratedData | undefined;
|
||||
storyContext?: StoryContextType;
|
||||
}> {
|
||||
const {
|
||||
|
|
18
ts/model-types.d.ts
vendored
18
ts/model-types.d.ts
vendored
|
@ -17,7 +17,11 @@ import { ReadStatus } from './messages/MessageReadStatus';
|
|||
import { SendStateByConversationId } from './messages/MessageSendState';
|
||||
import { GroupNameCollisionsWithIdsByTitle } from './util/groupMemberNameCollisions';
|
||||
import { ConversationColorType } from './types/Colors';
|
||||
import { AttachmentDraftType, AttachmentType } from './types/Attachment';
|
||||
import {
|
||||
AttachmentDraftType,
|
||||
AttachmentType,
|
||||
ThumbnailType,
|
||||
} from './types/Attachment';
|
||||
import { EmbeddedContactType } from './types/EmbeddedContact';
|
||||
import { SignalService as Proto } from './protobuf';
|
||||
import { AvatarDataType } from './types/Avatar';
|
||||
|
@ -30,11 +34,10 @@ import { SeenStatus } from './MessageSeenStatus';
|
|||
import { GiftBadgeStates } from './components/conversation/Message';
|
||||
import { LinkPreviewType } from './types/message/LinkPreviews';
|
||||
|
||||
import type { ProcessedQuoteAttachment } from './textsecure/Types.d';
|
||||
import type { StickerType } from './types/Stickers';
|
||||
import { MIMEType } from './types/MIME';
|
||||
|
||||
export type WhatIsThis = any;
|
||||
|
||||
export type LastMessageStatus =
|
||||
| 'paused'
|
||||
| 'error'
|
||||
|
@ -73,7 +76,9 @@ export type QuotedAttachment = {
|
|||
};
|
||||
|
||||
export type QuotedMessageType = {
|
||||
attachments: Array<WhatIsThis /* QuotedAttachment */>;
|
||||
// TODO DESKTOP-3826
|
||||
// eslint-disable-next-line no-explicit-any
|
||||
attachments: Array<any>;
|
||||
// `author` is an old attribute that holds the author's E164. We shouldn't use it for
|
||||
// new messages, but old messages might have this attribute.
|
||||
author?: string;
|
||||
|
@ -245,6 +250,11 @@ export type ConversationLastProfileType = Readonly<{
|
|||
profileKeyVersion: string;
|
||||
}>;
|
||||
|
||||
export type ValidateConversationType = Pick<
|
||||
ConversationAttributesType,
|
||||
'e164' | 'uuid' | 'type' | 'groupId'
|
||||
>;
|
||||
|
||||
export type ConversationAttributesType = {
|
||||
accessKey?: string | null;
|
||||
addedBy?: string;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright 2020-2022 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { compact, isNumber, throttle, debounce } from 'lodash';
|
||||
import { compact, has, isNumber, throttle, debounce } from 'lodash';
|
||||
import { batch as batchDispatch } from 'react-redux';
|
||||
import PQueue from 'p-queue';
|
||||
import { v4 as generateGuid } from 'uuid';
|
||||
|
@ -15,28 +15,24 @@ import type {
|
|||
QuotedMessageType,
|
||||
SenderKeyInfoType,
|
||||
VerificationOptions,
|
||||
WhatIsThis,
|
||||
} from '../model-types.d';
|
||||
import { getInitials } from '../util/getInitials';
|
||||
import { normalizeUuid } from '../util/normalizeUuid';
|
||||
import {
|
||||
getRegionCodeForNumber,
|
||||
parseNumber,
|
||||
} from '../util/libphonenumberUtil';
|
||||
import { getRegionCodeForNumber } from '../util/libphonenumberUtil';
|
||||
import { clearTimeoutIfNecessary } from '../util/clearTimeoutIfNecessary';
|
||||
import type { AttachmentType, ThumbnailType } from '../types/Attachment';
|
||||
import { toDayMillis } from '../util/timestamp';
|
||||
import type { AttachmentType } from '../types/Attachment';
|
||||
import { isGIF } from '../types/Attachment';
|
||||
import type { CallHistoryDetailsType } from '../types/Calling';
|
||||
import { CallMode } from '../types/Calling';
|
||||
import * as EmbeddedContact from '../types/EmbeddedContact';
|
||||
import * as Conversation from '../types/Conversation';
|
||||
import type { StickerType, StickerWithHydratedData } from '../types/Stickers';
|
||||
import * as Stickers from '../types/Stickers';
|
||||
import type {
|
||||
ContactWithHydratedAvatar,
|
||||
GroupV1InfoType,
|
||||
GroupV2InfoType,
|
||||
StickerType,
|
||||
} from '../textsecure/SendMessage';
|
||||
import createTaskWithTimeout from '../textsecure/TaskWithTimeout';
|
||||
import MessageSender from '../textsecure/SendMessage';
|
||||
|
@ -58,7 +54,7 @@ import { sniffImageMimeType } from '../util/sniffImageMimeType';
|
|||
import { isValidE164 } from '../util/isValidE164';
|
||||
import type { MIMEType } from '../types/MIME';
|
||||
import { IMAGE_JPEG, IMAGE_GIF, IMAGE_WEBP } from '../types/MIME';
|
||||
import { UUID, isValidUuid, UUIDKind } from '../types/UUID';
|
||||
import { UUID, UUIDKind } from '../types/UUID';
|
||||
import type { UUIDStringType } from '../types/UUID';
|
||||
import { deriveAccessKey, decryptProfileName, decryptProfile } from '../Crypto';
|
||||
import * as Bytes from '../Bytes';
|
||||
|
@ -125,6 +121,7 @@ import { SeenStatus } from '../MessageSeenStatus';
|
|||
import { getConversationIdForLogging } from '../util/idForLogging';
|
||||
import { getSendTarget } from '../util/getSendTarget';
|
||||
import { getRecipients } from '../util/getRecipients';
|
||||
import { validateConversation } from '../util/validateConversation';
|
||||
|
||||
/* eslint-disable more/no-then */
|
||||
window.Whisper = window.Whisper || {};
|
||||
|
@ -3389,71 +3386,7 @@ export class ConversationModel extends window.Backbone
|
|||
}
|
||||
|
||||
override validate(attributes = this.attributes): string | null {
|
||||
const required = ['type'];
|
||||
const missing = window._.filter(required, attr => !attributes[attr]);
|
||||
if (missing.length) {
|
||||
return `Conversation must have ${missing}`;
|
||||
}
|
||||
|
||||
if (attributes.type !== 'private' && attributes.type !== 'group') {
|
||||
return `Invalid conversation type: ${attributes.type}`;
|
||||
}
|
||||
|
||||
const atLeastOneOf = ['e164', 'uuid', 'groupId'];
|
||||
const hasAtLeastOneOf =
|
||||
window._.filter(atLeastOneOf, attr => attributes[attr]).length > 0;
|
||||
|
||||
if (!hasAtLeastOneOf) {
|
||||
return 'Missing one of e164, uuid, or groupId';
|
||||
}
|
||||
|
||||
const error = this.validateNumber() || this.validateUuid();
|
||||
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
validateNumber(): string | null {
|
||||
if (isDirectConversation(this.attributes) && this.get('e164')) {
|
||||
const regionCode = window.storage.get('regionCode');
|
||||
if (!regionCode) {
|
||||
throw new Error('No region code');
|
||||
}
|
||||
const number = parseNumber(
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
this.get('e164')!,
|
||||
regionCode
|
||||
);
|
||||
if (number.isValidNumber) {
|
||||
this.set({ e164: number.e164 });
|
||||
return null;
|
||||
}
|
||||
|
||||
let errorMessage: undefined | string;
|
||||
if (number.error instanceof Error) {
|
||||
errorMessage = number.error.message;
|
||||
} else if (typeof number.error === 'string') {
|
||||
errorMessage = number.error;
|
||||
}
|
||||
return errorMessage || 'Invalid phone number';
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
validateUuid(): string | null {
|
||||
if (isDirectConversation(this.attributes) && this.get('uuid')) {
|
||||
if (isValidUuid(this.get('uuid'))) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return 'Invalid UUID';
|
||||
}
|
||||
|
||||
return null;
|
||||
return validateConversation(attributes);
|
||||
}
|
||||
|
||||
queueJob<T>(
|
||||
|
@ -3643,10 +3576,16 @@ export class ConversationModel extends window.Backbone
|
|||
}
|
||||
|
||||
async getQuoteAttachment(
|
||||
attachments?: Array<WhatIsThis>,
|
||||
preview?: Array<WhatIsThis>,
|
||||
sticker?: WhatIsThis
|
||||
): Promise<WhatIsThis> {
|
||||
attachments?: Array<AttachmentType>,
|
||||
preview?: Array<LinkPreviewType>,
|
||||
sticker?: StickerType
|
||||
): Promise<
|
||||
Array<{
|
||||
contentType: MIMEType;
|
||||
fileName: string | null;
|
||||
thumbnail: ThumbnailType | null;
|
||||
}>
|
||||
> {
|
||||
if (attachments && attachments.length) {
|
||||
const attachmentsToUse = Array.from(take(attachments, 1));
|
||||
const isGIFQuote = isGIF(attachmentsToUse);
|
||||
|
@ -3673,7 +3612,9 @@ export class ConversationModel extends window.Backbone
|
|||
thumbnail: thumbnail
|
||||
? {
|
||||
...(await loadAttachmentData(thumbnail)),
|
||||
objectUrl: getAbsoluteAttachmentPath(thumbnail.path),
|
||||
objectUrl: thumbnail.path
|
||||
? getAbsoluteAttachmentPath(thumbnail.path)
|
||||
: undefined,
|
||||
}
|
||||
: null,
|
||||
};
|
||||
|
@ -3708,7 +3649,9 @@ export class ConversationModel extends window.Backbone
|
|||
thumbnail: image
|
||||
? {
|
||||
...(await loadAttachmentData(image)),
|
||||
objectUrl: getAbsoluteAttachmentPath(image.path),
|
||||
objectUrl: image.path
|
||||
? getAbsoluteAttachmentPath(image.path)
|
||||
: undefined,
|
||||
}
|
||||
: null,
|
||||
};
|
||||
|
@ -3727,7 +3670,7 @@ export class ConversationModel extends window.Backbone
|
|||
fileName: null,
|
||||
thumbnail: {
|
||||
...(await loadAttachmentData(sticker.data)),
|
||||
objectUrl: getAbsoluteAttachmentPath(path),
|
||||
objectUrl: path ? getAbsoluteAttachmentPath(path) : undefined,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
@ -3797,7 +3740,7 @@ export class ConversationModel extends window.Backbone
|
|||
contentType = IMAGE_WEBP;
|
||||
}
|
||||
|
||||
const sticker = {
|
||||
const sticker: StickerWithHydratedData = {
|
||||
packId,
|
||||
stickerId,
|
||||
packKey: key,
|
||||
|
@ -3867,7 +3810,7 @@ export class ConversationModel extends window.Backbone
|
|||
mentions?: BodyRangesType;
|
||||
preview?: Array<LinkPreviewType>;
|
||||
quote?: QuotedMessageType;
|
||||
sticker?: StickerType;
|
||||
sticker?: StickerWithHydratedData;
|
||||
},
|
||||
{
|
||||
dontClearDraft,
|
||||
|
@ -5489,8 +5432,8 @@ window.Whisper.ConversationCollection = window.Backbone.Collection.extend({
|
|||
);
|
||||
},
|
||||
|
||||
reset(...args: Array<WhatIsThis>) {
|
||||
window.Backbone.Collection.prototype.reset.apply(this, args as WhatIsThis);
|
||||
reset(models?: Array<ConversationModel>, options?: Backbone.Silenceable) {
|
||||
window.Backbone.Collection.prototype.reset.call(this, models, options);
|
||||
this.resetLookups();
|
||||
},
|
||||
|
||||
|
@ -5534,8 +5477,14 @@ window.Whisper.ConversationCollection = window.Backbone.Collection.extend({
|
|||
this._byGroupId = Object.create(null);
|
||||
},
|
||||
|
||||
add(data: WhatIsThis | Array<WhatIsThis>) {
|
||||
let hydratedData;
|
||||
add(
|
||||
data:
|
||||
| ConversationModel
|
||||
| ConversationAttributesType
|
||||
| Array<ConversationModel>
|
||||
| Array<ConversationAttributesType>
|
||||
) {
|
||||
let hydratedData: Array<ConversationModel> | ConversationModel;
|
||||
|
||||
// First, we need to ensure that the data we're working with is Conversation models
|
||||
if (Array.isArray(data)) {
|
||||
|
@ -5544,16 +5493,20 @@ window.Whisper.ConversationCollection = window.Backbone.Collection.extend({
|
|||
const item = data[i];
|
||||
|
||||
// We create a new model if it's not already a model
|
||||
if (!item.get) {
|
||||
hydratedData.push(new window.Whisper.Conversation(item));
|
||||
if (has(item, 'get')) {
|
||||
hydratedData.push(item as ConversationModel);
|
||||
} else {
|
||||
hydratedData.push(item);
|
||||
hydratedData.push(
|
||||
new window.Whisper.Conversation(item as ConversationAttributesType)
|
||||
);
|
||||
}
|
||||
}
|
||||
} else if (!data.get) {
|
||||
hydratedData = new window.Whisper.Conversation(data);
|
||||
} else if (has(data, 'get')) {
|
||||
hydratedData = data as ConversationModel;
|
||||
} else {
|
||||
hydratedData = data;
|
||||
hydratedData = new window.Whisper.Conversation(
|
||||
data as ConversationAttributesType
|
||||
);
|
||||
}
|
||||
|
||||
// Next, we update our lookups first to prevent infinite loops on the 'add' event
|
||||
|
@ -5562,7 +5515,9 @@ window.Whisper.ConversationCollection = window.Backbone.Collection.extend({
|
|||
);
|
||||
|
||||
// Lastly, we fire off the add events related to this change
|
||||
window.Backbone.Collection.prototype.add.call(this, hydratedData);
|
||||
// Go home Backbone, you're drunk.
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
window.Backbone.Collection.prototype.add.call(this, hydratedData as any);
|
||||
|
||||
return hydratedData;
|
||||
},
|
||||
|
@ -5583,7 +5538,7 @@ window.Whisper.ConversationCollection = window.Backbone.Collection.extend({
|
|||
);
|
||||
},
|
||||
|
||||
comparator(m: WhatIsThis) {
|
||||
comparator(m: ConversationModel) {
|
||||
return -(m.get('active_at') || 0);
|
||||
},
|
||||
});
|
||||
|
|
|
@ -8,7 +8,6 @@ import type {
|
|||
MessageAttributesType,
|
||||
MessageReactionType,
|
||||
QuotedMessageType,
|
||||
WhatIsThis,
|
||||
} from '../model-types.d';
|
||||
import {
|
||||
filter,
|
||||
|
@ -46,7 +45,10 @@ import * as reactionUtil from '../reactions/util';
|
|||
import * as Stickers from '../types/Stickers';
|
||||
import * as Errors from '../types/errors';
|
||||
import * as EmbeddedContact from '../types/EmbeddedContact';
|
||||
import type { AttachmentType } from '../types/Attachment';
|
||||
import type {
|
||||
AttachmentType,
|
||||
AttachmentWithHydratedData,
|
||||
} from '../types/Attachment';
|
||||
import { isImage, isVideo } from '../types/Attachment';
|
||||
import * as Attachment from '../types/Attachment';
|
||||
import { stringToMIMEType } from '../types/MIME';
|
||||
|
@ -158,6 +160,8 @@ import { isNewReactionReplacingPrevious } from '../reactions/util';
|
|||
import { parseBoostBadgeListFromServer } from '../badges/parseBadgesFromServer';
|
||||
import { GiftBadgeStates } from '../components/conversation/Message';
|
||||
import { downloadAttachment } from '../util/downloadAttachment';
|
||||
import type { DeleteModel } from '../messageModifiers/Deletes';
|
||||
import type { StickerWithHydratedData } from '../types/Stickers';
|
||||
|
||||
/* eslint-disable more/no-then */
|
||||
|
||||
|
@ -176,9 +180,14 @@ const { getTextWithMentions, GoogleChrome } = window.Signal.Util;
|
|||
const { getMessageBySender } = window.Signal.Data;
|
||||
|
||||
export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
||||
static getLongMessageAttachment: (
|
||||
attachment: typeof window.WhatIsThis
|
||||
) => typeof window.WhatIsThis;
|
||||
static getLongMessageAttachment: (opts: {
|
||||
attachments: Array<AttachmentWithHydratedData>;
|
||||
body?: string;
|
||||
now: number;
|
||||
}) => {
|
||||
body?: string;
|
||||
attachments: Array<AttachmentWithHydratedData>;
|
||||
};
|
||||
|
||||
CURRENT_PROTOCOL_VERSION?: number;
|
||||
|
||||
|
@ -198,9 +207,9 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
|||
|
||||
cachedOutgoingPreviewData?: Array<LinkPreviewType>;
|
||||
|
||||
cachedOutgoingQuoteData?: WhatIsThis;
|
||||
cachedOutgoingQuoteData?: QuotedMessageType;
|
||||
|
||||
cachedOutgoingStickerData?: WhatIsThis;
|
||||
cachedOutgoingStickerData?: StickerWithHydratedData;
|
||||
|
||||
override initialize(attributes: unknown): void {
|
||||
if (_.isObject(attributes)) {
|
||||
|
@ -1910,7 +1919,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
|||
// eslint-disable-next-line no-param-reassign
|
||||
quote.attachments = [
|
||||
{
|
||||
contentType: 'image/jpeg',
|
||||
contentType: MIME.IMAGE_JPEG,
|
||||
},
|
||||
];
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
|
@ -1942,7 +1951,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
|||
// eslint-disable-next-line no-param-reassign
|
||||
quote.text = originalMessage.get('body');
|
||||
if (firstAttachment) {
|
||||
firstAttachment.thumbnail = undefined;
|
||||
firstAttachment.thumbnail = null;
|
||||
}
|
||||
|
||||
if (
|
||||
|
@ -2336,7 +2345,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
|||
return;
|
||||
}
|
||||
|
||||
const messageId = UUID.generate().toString();
|
||||
const messageId = message.get('id') || UUID.generate().toString();
|
||||
|
||||
// Send delivery receipts, but only for incoming sealed sender messages
|
||||
// and not for messages from unaccepted conversations
|
||||
|
@ -2379,7 +2388,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
|||
const urls = LinkPreview.findLinks(dataMessage.body || '');
|
||||
const incomingPreview = dataMessage.preview || [];
|
||||
const preview = incomingPreview.filter(
|
||||
(item: typeof window.WhatIsThis) =>
|
||||
(item: LinkPreviewType) =>
|
||||
(item.image || item.title) &&
|
||||
urls.includes(item.url) &&
|
||||
LinkPreview.shouldPreviewHref(item.url)
|
||||
|
@ -3215,7 +3224,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
|||
}
|
||||
|
||||
async handleDeleteForEveryone(
|
||||
del: typeof window.WhatIsThis,
|
||||
del: DeleteModel,
|
||||
shouldPersist = true
|
||||
): Promise<void> {
|
||||
log.info('Handling DOE.', {
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
// Copyright 2021 Signal Messenger, LLC
|
||||
// Copyright 2021-2022 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { getThemeType } from '../util/getThemeType';
|
||||
|
||||
export function themeChanged(): void {
|
||||
if (window.reduxActions && window.reduxActions.user) {
|
||||
const theme = window.Events.getThemeSetting();
|
||||
window.reduxActions.user.userChanged({
|
||||
theme: theme === 'system' ? window.systemTheme : theme,
|
||||
});
|
||||
const theme = getThemeType();
|
||||
window.reduxActions.user.userChanged({ theme });
|
||||
}
|
||||
}
|
||||
|
|
|
@ -113,7 +113,7 @@ type MigrationsModuleType = {
|
|||
getAbsoluteStickerPath: (path: string) => string;
|
||||
getAbsoluteTempPath: (path: string) => string;
|
||||
loadAttachmentData: (
|
||||
attachment: AttachmentType
|
||||
attachment: Pick<AttachmentType, 'data' | 'path'>
|
||||
) => Promise<AttachmentWithHydratedData>;
|
||||
loadContactData: (
|
||||
contact: Array<EmbeddedContactType> | undefined
|
||||
|
|
|
@ -32,6 +32,7 @@ import type { MenuOptionsType } from '../types/menu';
|
|||
import { UUIDKind } from '../types/UUID';
|
||||
import { getEmojiReducerState as emojis } from '../util/loadRecentEmojis';
|
||||
import type { MainWindowStatsType } from '../windows/context';
|
||||
import { getThemeType } from '../util/getThemeType';
|
||||
|
||||
export function getInitialState({
|
||||
badges,
|
||||
|
@ -63,8 +64,7 @@ export function getInitialState({
|
|||
window.ConversationController.getOurConversationId();
|
||||
const ourDeviceId = window.textsecure.storage.user.getDeviceId();
|
||||
|
||||
const themeSetting = window.Events.getThemeSetting();
|
||||
const theme = themeSetting === 'system' ? window.systemTheme : themeSetting;
|
||||
const theme = getThemeType();
|
||||
|
||||
return {
|
||||
accounts: accounts(),
|
||||
|
|
|
@ -14,6 +14,7 @@ import {
|
|||
SenderKeyDistributionMessage,
|
||||
} from '@signalapp/libsignal-client';
|
||||
|
||||
import type { QuotedMessageType } from '../model-types.d';
|
||||
import { GLOBAL_ZONE } from '../SignalProtocolStore';
|
||||
import { assert } from '../util/assert';
|
||||
import { parseIntOrThrow } from '../util/parseIntOrThrow';
|
||||
|
@ -110,15 +111,6 @@ export type StickerType = StickerWithHydratedData & {
|
|||
attachmentPointer?: Proto.IAttachmentPointer;
|
||||
};
|
||||
|
||||
export type QuoteType = {
|
||||
attachments?: Array<AttachmentType>;
|
||||
authorUuid?: string;
|
||||
bodyRanges?: BodyRangesType;
|
||||
id?: number;
|
||||
isGiftBadge?: boolean;
|
||||
text?: string;
|
||||
};
|
||||
|
||||
export type ReactionType = {
|
||||
emoji?: string;
|
||||
remove?: boolean;
|
||||
|
@ -192,9 +184,9 @@ export type MessageOptionsType = {
|
|||
needsSync?: boolean;
|
||||
preview?: ReadonlyArray<LinkPreviewType>;
|
||||
profileKey?: Uint8Array;
|
||||
quote?: QuoteType;
|
||||
quote?: QuotedMessageType | null;
|
||||
recipients: ReadonlyArray<string>;
|
||||
sticker?: StickerType;
|
||||
sticker?: StickerWithHydratedData;
|
||||
reaction?: ReactionType;
|
||||
deletedForEveryoneTimestamp?: number;
|
||||
timestamp: number;
|
||||
|
@ -215,9 +207,9 @@ export type GroupSendOptionsType = {
|
|||
messageText?: string;
|
||||
preview?: ReadonlyArray<LinkPreviewType>;
|
||||
profileKey?: Uint8Array;
|
||||
quote?: QuoteType;
|
||||
quote?: QuotedMessageType | null;
|
||||
reaction?: ReactionType;
|
||||
sticker?: StickerType;
|
||||
sticker?: StickerWithHydratedData;
|
||||
storyContext?: StoryContextType;
|
||||
timestamp: number;
|
||||
};
|
||||
|
@ -246,7 +238,7 @@ class Message {
|
|||
|
||||
profileKey?: Uint8Array;
|
||||
|
||||
quote?: QuoteType;
|
||||
quote?: QuotedMessageType | null;
|
||||
|
||||
recipients: ReadonlyArray<string>;
|
||||
|
||||
|
@ -1195,9 +1187,9 @@ export default class MessageSender {
|
|||
options?: SendOptionsType;
|
||||
preview?: ReadonlyArray<LinkPreviewType> | undefined;
|
||||
profileKey?: Uint8Array;
|
||||
quote?: QuoteType;
|
||||
quote?: QuotedMessageType | null;
|
||||
reaction?: ReactionType;
|
||||
sticker?: StickerType;
|
||||
sticker?: StickerWithHydratedData;
|
||||
storyContext?: StoryContextType;
|
||||
timestamp: number;
|
||||
urgent: boolean;
|
||||
|
|
|
@ -168,13 +168,10 @@ export type AttachmentDraftType =
|
|||
size: number;
|
||||
};
|
||||
|
||||
export type ThumbnailType = {
|
||||
height?: number;
|
||||
width?: number;
|
||||
url?: string;
|
||||
contentType: MIME.MIMEType;
|
||||
path?: string;
|
||||
data?: Uint8Array;
|
||||
export type ThumbnailType = Pick<
|
||||
AttachmentType,
|
||||
'height' | 'width' | 'url' | 'contentType' | 'path' | 'data'
|
||||
> & {
|
||||
// Only used when quote needed to make an in-memory thumbnail
|
||||
objectUrl?: string;
|
||||
};
|
||||
|
@ -236,7 +233,7 @@ export async function migrateDataToFileSystem(
|
|||
// Over time, we can expand this definition to become more narrow, e.g. require certain
|
||||
// fields, etc.
|
||||
export function isValid(
|
||||
rawAttachment?: AttachmentType
|
||||
rawAttachment?: Pick<AttachmentType, 'data' | 'path'>
|
||||
): rawAttachment is AttachmentType {
|
||||
// NOTE: We cannot use `_.isPlainObject` because `rawAttachment` is
|
||||
// deserialized by protobuf:
|
||||
|
@ -396,14 +393,14 @@ export function hasData(attachment: AttachmentType): boolean {
|
|||
|
||||
export function loadData(
|
||||
readAttachmentData: (path: string) => Promise<Uint8Array>
|
||||
): (attachment: AttachmentType) => Promise<AttachmentWithHydratedData> {
|
||||
): (
|
||||
attachment: Pick<AttachmentType, 'data' | 'path'>
|
||||
) => Promise<AttachmentWithHydratedData> {
|
||||
if (!is.function_(readAttachmentData)) {
|
||||
throw new TypeError("'readAttachmentData' must be a function");
|
||||
}
|
||||
|
||||
return async (
|
||||
attachment: AttachmentType
|
||||
): Promise<AttachmentWithHydratedData> => {
|
||||
return async attachment => {
|
||||
if (!isValid(attachment)) {
|
||||
throw new TypeError("'attachment' is not valid");
|
||||
}
|
||||
|
|
|
@ -651,7 +651,7 @@ export const processNewSticker = async (
|
|||
};
|
||||
|
||||
type LoadAttachmentType = (
|
||||
attachment: AttachmentType
|
||||
attachment: Pick<AttachmentType, 'data' | 'path'>
|
||||
) => Promise<AttachmentWithHydratedData>;
|
||||
|
||||
export const createAttachmentLoader = (
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { z } from 'zod';
|
||||
|
||||
import type { StorageAccessType } from './Storage.d';
|
||||
|
||||
export const themeSettingSchema = z.enum(['system', 'light', 'dark']);
|
||||
|
|
18
ts/util/getThemeType.ts
Normal file
18
ts/util/getThemeType.ts
Normal file
|
@ -0,0 +1,18 @@
|
|||
// Copyright 2022 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { ThemeType } from '../types/Util';
|
||||
|
||||
export function getThemeType(): ThemeType {
|
||||
const themeSetting = window.Events.getThemeSetting();
|
||||
|
||||
if (themeSetting === 'light') {
|
||||
return ThemeType.light;
|
||||
}
|
||||
|
||||
if (themeSetting === 'dark') {
|
||||
return ThemeType.dark;
|
||||
}
|
||||
|
||||
return window.systemTheme;
|
||||
}
|
62
ts/util/validateConversation.ts
Normal file
62
ts/util/validateConversation.ts
Normal file
|
@ -0,0 +1,62 @@
|
|||
// Copyright 2022 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import type { ValidateConversationType } from '../model-types.d';
|
||||
import { isDirectConversation } from './whatTypeOfConversation';
|
||||
import { parseNumber } from './libphonenumberUtil';
|
||||
import { isValidUuid } from '../types/UUID';
|
||||
|
||||
export function validateConversation(
|
||||
attributes: ValidateConversationType
|
||||
): string | null {
|
||||
if (attributes.type !== 'private' && attributes.type !== 'group') {
|
||||
return `Invalid conversation type: ${attributes.type}`;
|
||||
}
|
||||
|
||||
if (!attributes.e164 && !attributes.uuid && !attributes.groupId) {
|
||||
return 'Missing one of e164, uuid, or groupId';
|
||||
}
|
||||
|
||||
const error = validateNumber(attributes) || validateUuid(attributes);
|
||||
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function validateNumber(attributes: ValidateConversationType): string | null {
|
||||
if (isDirectConversation(attributes) && attributes.e164) {
|
||||
const regionCode = window.storage.get('regionCode');
|
||||
if (!regionCode) {
|
||||
throw new Error('No region code');
|
||||
}
|
||||
const number = parseNumber(attributes.e164, regionCode);
|
||||
if (number.isValidNumber) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let errorMessage: undefined | string;
|
||||
if (number.error instanceof Error) {
|
||||
errorMessage = number.error.message;
|
||||
} else if (typeof number.error === 'string') {
|
||||
errorMessage = number.error;
|
||||
}
|
||||
return errorMessage || 'Invalid phone number';
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function validateUuid(attributes: ValidateConversationType): string | null {
|
||||
if (isDirectConversation(attributes) && attributes.uuid) {
|
||||
if (isValidUuid(attributes.uuid)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return 'Invalid UUID';
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
14
ts/window.d.ts
vendored
14
ts/window.d.ts
vendored
|
@ -98,8 +98,6 @@ import { RendererConfigType } from './types/RendererConfig';
|
|||
|
||||
export { Long } from 'long';
|
||||
|
||||
export type WhatIsThis = any;
|
||||
|
||||
// Synced with the type in ts/shims/showConfirmationDialog
|
||||
// we are duplicating it here because that file cannot import/export.
|
||||
type ConfirmationDialogViewProps = {
|
||||
|
@ -253,8 +251,6 @@ declare global {
|
|||
};
|
||||
WebAudioRecorder: typeof WebAudioRecorderClass;
|
||||
|
||||
WhatIsThis: WhatIsThis;
|
||||
|
||||
addSetupMenuItems: () => void;
|
||||
attachmentDownloadQueue: Array<MessageModel> | undefined;
|
||||
startupProcessingQueue: StartupQueue | undefined;
|
||||
|
@ -305,7 +301,7 @@ declare global {
|
|||
nodeSetImmediate: typeof setImmediate;
|
||||
onFullScreenChange: (fullScreen: boolean, maximized: boolean) => void;
|
||||
platform: string;
|
||||
preloadedImages: Array<WhatIsThis>;
|
||||
preloadedImages: Array<HTMLImageElement>;
|
||||
reduxActions: ReduxActions;
|
||||
reduxStore: Store<StateType>;
|
||||
restart: () => void;
|
||||
|
@ -315,14 +311,14 @@ declare global {
|
|||
shutdown: () => void;
|
||||
showDebugLog: () => void;
|
||||
sendChallengeRequest: (request: IPCChallengeRequest) => void;
|
||||
setAutoHideMenuBar: (value: WhatIsThis) => void;
|
||||
setAutoHideMenuBar: (value: boolean) => void;
|
||||
setBadgeCount: (count: number) => void;
|
||||
setMenuBarVisibility: (value: WhatIsThis) => void;
|
||||
setMenuBarVisibility: (value: boolean) => void;
|
||||
updateSystemTraySetting: (value: SystemTraySetting) => void;
|
||||
showConfirmationDialog: (options: ConfirmationDialogViewProps) => void;
|
||||
showKeyboardShortcuts: () => void;
|
||||
storage: Storage;
|
||||
systemTheme: WhatIsThis;
|
||||
systemTheme: ThemeType;
|
||||
textsecure: typeof textsecure;
|
||||
titleBarDoubleClick: () => void;
|
||||
updateTrayIcon: (count: number) => void;
|
||||
|
@ -341,7 +337,7 @@ declare global {
|
|||
WebAPI: WebAPIConnectType;
|
||||
Whisper: WhisperType;
|
||||
|
||||
getServerTrustRoot: () => WhatIsThis;
|
||||
getServerTrustRoot: () => string;
|
||||
readyForUpdates: () => void;
|
||||
logAppLoadedEvent?: (options: { processedCount?: number }) => void;
|
||||
logAuthenticatedConnect?: () => void;
|
||||
|
|
Loading…
Reference in a new issue