diff --git a/js/modules/signal.js b/js/modules/signal.js index a5992190d..e23ecdfd8 100644 --- a/js/modules/signal.js +++ b/js/modules/signal.js @@ -137,7 +137,6 @@ const Errors = require('./types/errors'); const MediaGalleryMessage = require('../../ts/components/conversation/media-gallery/types/Message'); const MessageType = require('./types/message'); const MIME = require('../../ts/types/MIME'); -const PhoneNumber = require('../../ts/types/PhoneNumber'); const SettingsType = require('../../ts/types/Settings'); // Views @@ -417,7 +416,6 @@ exports.setup = (options = {}) => { Errors, Message: MessageType, MIME, - PhoneNumber, Settings: SettingsType, VisualAttachment, }; diff --git a/ts/models/conversations.ts b/ts/models/conversations.ts index c7741f249..6de601d58 100644 --- a/ts/models/conversations.ts +++ b/ts/models/conversations.ts @@ -2831,7 +2831,13 @@ export class ConversationModel extends window.Backbone return null; } - return number.error || 'Invalid phone number'; + 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; diff --git a/ts/models/messages.ts b/ts/models/messages.ts index 58fbdecf2..75197236c 100644 --- a/ts/models/messages.ts +++ b/ts/models/messages.ts @@ -13,7 +13,6 @@ import { import { find } from '../util/iterables'; import { DataMessageClass } from '../textsecure.d'; import { ConversationModel } from './conversations'; -import { ConversationType } from '../state/ducks/conversations'; import { MessageStatusType } from '../components/conversation/Message'; import { OwnProps as SmartMessageDetailPropsType, @@ -36,6 +35,7 @@ import { } from '../util/whatTypeOfConversation'; import { handleMessageSend } from '../util/handleMessageSend'; import { getSendOptions } from '../util/getSendOptions'; +import { findAndFormatContact } from '../util/findAndFormatContact'; import { getLastChallengeError, getMessagePropStatus, @@ -83,27 +83,6 @@ type PropsForMessageDetail = Pick< 'sentAt' | 'receivedAt' | 'message' | 'errors' | 'contacts' >; -type FormattedContact = Partial & - Pick< - ConversationType, - | 'acceptedMessageRequest' - | 'id' - | 'isMe' - | 'sharedGroupNames' - | 'title' - | 'type' - | 'unblurredAvatarPath' - >; - -export const PLACEHOLDER_CONTACT: FormattedContact = { - acceptedMessageRequest: false, - id: 'placeholder-contact', - isMe: false, - sharedGroupNames: [], - title: window.i18n('unknownContact'), - type: 'direct', -}; - declare const _: typeof window._; window.Whisper = window.Whisper || {}; @@ -113,7 +92,6 @@ const { Attachment, MIME, Contact, - PhoneNumber, Errors, } = window.Signal.Types; const { @@ -181,9 +159,6 @@ export class MessageModel extends window.Backbone.Model { isSelected?: boolean; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - quotedMessage: any; - syncPromise?: Promise; initialize(attributes: unknown): void { @@ -203,7 +178,6 @@ export class MessageModel extends window.Backbone.Model { this.OUR_NUMBER = window.textsecure.storage.user.getNumber(); this.OUR_UUID = window.textsecure.storage.user.getUuid(); - this.on('unload', this.unload); this.on('change', this.notifyRedux); } @@ -327,7 +301,7 @@ export class MessageModel extends window.Backbone.Model { this.isUnidentifiedDelivery(id, unidentifiedLookup); return { - ...this.findAndFormatContact(id), + ...findAndFormatContact(id), status: this.getStatus(id), errors: errorsForContact, @@ -363,7 +337,7 @@ export class MessageModel extends window.Backbone.Model { receivedAt: this.getReceivedAt(), message: getPropsForMessage( this.attributes, - (id?: string | undefined) => this.findAndFormatContact(id), + findAndFormatContact, window.ConversationController.getOurConversationIdOrThrow(), this.OUR_NUMBER, this.OUR_UUID, @@ -383,44 +357,6 @@ export class MessageModel extends window.Backbone.Model { } // Dependencies of prop-generation functions - findAndFormatContact(identifier?: string): FormattedContact { - if (!identifier) { - return PLACEHOLDER_CONTACT; - } - - const contactModel = this.findContact(identifier); - if (contactModel) { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - return contactModel.format()!; - } - - const { format, isValidNumber } = PhoneNumber; - const regionCode = window.storage.get('regionCode'); - - if (!isValidNumber(identifier, { regionCode })) { - return PLACEHOLDER_CONTACT; - } - - const phoneNumber = format(identifier, { - ourRegionCode: regionCode, - }); - - return { - acceptedMessageRequest: false, - id: 'phone-only', - isMe: false, - phoneNumber, - sharedGroupNames: [], - title: phoneNumber, - type: 'direct', - }; - } - - // eslint-disable-next-line class-methods-use-this - findContact(identifier?: string): ConversationModel | undefined { - return window.ConversationController.get(identifier); - } - getConversation(): ConversationModel | undefined { return window.ConversationController.get(this.get('conversationId')); } @@ -484,7 +420,7 @@ export class MessageModel extends window.Backbone.Model { if (isProfileChange(attributes)) { const change = this.get('profileChange'); const changedId = this.get('changedId'); - const changedContact = this.findAndFormatContact(changedId); + const changedContact = findAndFormatContact(changedId); if (!change) { throw new Error('getNotificationData: profileChange was missing!'); } @@ -694,7 +630,7 @@ export class MessageModel extends window.Backbone.Model { const state = window.reduxStore.getState(); const callingNotification = getPropsForCallHistory( attributes, - (id?: string | undefined) => this.findAndFormatContact(id), + findAndFormatContact, getCallSelector(state), getActiveCall(state) ); @@ -724,7 +660,7 @@ export class MessageModel extends window.Backbone.Model { if (isKeyChange(attributes)) { const identifier = this.get('key_changed'); - const conversation = this.findContact(identifier); + const conversation = window.ConversationController.get(identifier); return { text: window.i18n('safetyNumberChangedGroup', [ conversation ? conversation.getTitle() : null, @@ -752,7 +688,7 @@ export class MessageModel extends window.Backbone.Model { const bodyRanges = processBodyRanges( attributes.bodyRanges, - (id?: string | undefined) => this.findAndFormatContact(id) + findAndFormatContact ); if (bodyRanges) { return getTextWithMentions(bodyRanges, body); @@ -769,7 +705,7 @@ export class MessageModel extends window.Backbone.Model { const bodyRanges = processBodyRanges( attributes.bodyRanges, - (id?: string | undefined) => this.findAndFormatContact(id) + findAndFormatContact ); if (bodyRanges && bodyRanges.length) { @@ -839,7 +775,6 @@ export class MessageModel extends window.Backbone.Model { this.getConversation()?.debouncedUpdateLastMessage?.(); window.MessageController.unregister(this.id); - this.unload(); await this.deleteData(); } @@ -963,7 +898,7 @@ export class MessageModel extends window.Backbone.Model { } const { authorUuid, author, id: sentAt, referencedMessageNotFound } = quote; - const contact = this.findContact(authorUuid || author); + const contact = window.ConversationController.get(authorUuid || author); // Is the quote really without a reference? Check with our in memory store // first to make sure it's not there. @@ -1114,12 +1049,6 @@ export class MessageModel extends window.Backbone.Model { return !hasSomethingToDisplay; } - unload(): void { - if (this.quotedMessage) { - this.quotedMessage = null; - } - } - isUnidentifiedDelivery( contactId: string, lookup: Record diff --git a/ts/state/selectors/message.ts b/ts/state/selectors/message.ts index 1656ecc20..f8805b3ab 100644 --- a/ts/state/selectors/message.ts +++ b/ts/state/selectors/message.ts @@ -291,7 +291,7 @@ export function getContact( } export function getConversation( - message: MessageAttributesType, + message: Pick, conversationSelector: GetConversationByIdType ): ConversationType { return conversationSelector(message.conversationId); @@ -987,7 +987,7 @@ function getPropsForPreview( } export function getPropsForQuote( - message: MessageAttributesType, + message: Pick, conversationSelector: GetConversationByIdType, ourConversationId: string | undefined ): PropsData['quote'] { diff --git a/ts/types/PhoneNumber.ts b/ts/types/PhoneNumber.ts index b6f7fcec4..d176ef73e 100644 --- a/ts/types/PhoneNumber.ts +++ b/ts/types/PhoneNumber.ts @@ -7,7 +7,7 @@ import { instance, PhoneNumberFormat } from '../util/libphonenumberInstance'; function _format( phoneNumber: string, options: { - ourRegionCode: string; + ourRegionCode?: string; } ) { try { diff --git a/ts/util/findAndFormatContact.ts b/ts/util/findAndFormatContact.ts new file mode 100644 index 000000000..329f43743 --- /dev/null +++ b/ts/util/findAndFormatContact.ts @@ -0,0 +1,55 @@ +// Copyright 2021 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only + +import { ConversationType } from '../state/ducks/conversations'; +import { format, isValidNumber } from '../types/PhoneNumber'; + +type FormattedContact = Partial & + Pick< + ConversationType, + | 'acceptedMessageRequest' + | 'id' + | 'isMe' + | 'sharedGroupNames' + | 'title' + | 'type' + | 'unblurredAvatarPath' + >; + +const PLACEHOLDER_CONTACT: FormattedContact = { + acceptedMessageRequest: false, + id: 'placeholder-contact', + isMe: false, + sharedGroupNames: [], + title: window.i18n('unknownContact'), + type: 'direct', +}; + +export function findAndFormatContact(identifier?: string): FormattedContact { + if (!identifier) { + return PLACEHOLDER_CONTACT; + } + + const contactModel = window.ConversationController.get(identifier); + if (contactModel) { + return contactModel.format(); + } + + const regionCode = window.storage.get('regionCode'); + + if (!isValidNumber(identifier, { regionCode })) { + return PLACEHOLDER_CONTACT; + } + + const phoneNumber = format(identifier, { ourRegionCode: regionCode }); + + return { + acceptedMessageRequest: false, + id: 'phone-only', + isMe: false, + phoneNumber, + sharedGroupNames: [], + title: phoneNumber, + type: 'direct', + }; +} diff --git a/ts/views/conversation_view.ts b/ts/views/conversation_view.ts index d7b90cc07..72fe36595 100644 --- a/ts/views/conversation_view.ts +++ b/ts/views/conversation_view.ts @@ -23,6 +23,7 @@ import { isGroupV1, isMe, } from '../util/whatTypeOfConversation'; +import { findAndFormatContact } from '../util/findAndFormatContact'; import * as Bytes from '../Bytes'; import { canReply, @@ -3745,12 +3746,7 @@ Whisper.ConversationView = Whisper.View.extend({ }) : undefined; - if ( - message && - !canReply(message.attributes, (id?: string) => - message.findAndFormatContact(id) - ) - ) { + if (message && !canReply(message.attributes, findAndFormatContact)) { return; } @@ -3760,7 +3756,6 @@ Whisper.ConversationView = Whisper.View.extend({ this.quote = null; this.quotedMessage = null; - this.quoteHolder = null; const existing = model.get('quotedMessageId'); if (existing !== messageId) { @@ -3806,16 +3801,12 @@ Whisper.ConversationView = Whisper.View.extend({ return; } - const message = new Whisper.Message({ - conversationId: model.id, - quote: this.quote, - } as any); - message.quotedMessage = this.quotedMessage; - this.quoteHolder = message; - const props = getPropsForQuote( - message.attributes, - (id?: string) => message.findAndFormatContact(id), + { + conversationId: model.id, + quote: this.quote, + }, + findAndFormatContact, window.ConversationController.getOurConversationIdOrThrow() ); @@ -3829,7 +3820,7 @@ Whisper.ConversationView = Whisper.View.extend({ props: { ...props, withContentAbove: true, - onClick: () => this.scrollToMessage(message.quotedMessage.id), + onClick: () => this.scrollToMessage(this.quotedMessage.id), onClose: () => { // This can't be the normal 'onClose' because that is always run when this // view is removed from the DOM, and would clear the draft quote. diff --git a/ts/window.d.ts b/ts/window.d.ts index 08c7994d1..224fe46e2 100644 --- a/ts/window.d.ts +++ b/ts/window.d.ts @@ -220,8 +220,16 @@ declare global { getRegionCodeForNumber: (number: string) => string; parseNumber: ( e164: string, - regionCode: string - ) => typeof window.Signal.Types.PhoneNumber; + defaultRegionCode: string + ) => + | { isValidNumber: false; error: unknown } + | { + isValidNumber: true; + regionCode: string | undefined; + countryCode: string; + nationalNumber: string; + e164: string; + }; }; parse: (number: string) => string; getRegionCodeForNumber: (number: string) => string; @@ -400,20 +408,6 @@ declare global { options: unknown ) => Promise; }; - PhoneNumber: { - format: ( - identifier: string, - options: Record - ) => string; - isValidNumber( - phoneNumber: string, - options?: { - regionCode?: string; - } - ): boolean; - e164: string; - error: string; - }; Errors: typeof Errors; Message: { CURRENT_SCHEMA_VERSION: number;