remove regionCode and PhoneNumber.ts

pull/1459/head
Audric Ackermann 4 years ago
parent 2b92386399
commit 02fb5783a4
No known key found for this signature in database
GPG Key ID: 999F434D76324AD4

@ -120,7 +120,6 @@
accountManager = new textsecure.AccountManager(USERNAME, PASSWORD);
accountManager.addEventListener('registration', () => {
const user = {
regionCode: window.storage.get('regionCode'),
ourNumber: textsecure.storage.user.getNumber(),
ourPrimary: window.textsecure.storage.get('primaryDevicePubKey'),
};

@ -22,7 +22,7 @@
window.Whisper = window.Whisper || {};
const { Contact, Conversation, Message, PhoneNumber } = window.Signal.Types;
const { Contact, Conversation, Message } = window.Signal.Types;
const {
upgradeMessageSchema,
loadAttachmentData,
@ -352,8 +352,6 @@
return this.get('groupAdmins') || this.get('moderators');
},
getProps() {
const { format } = PhoneNumber;
const regionCode = storage.get('regionCode');
const typingKeys = Object.keys(this.contactTypingTimers || {});
const groupAdmins = this.getGroupAdmins();
@ -377,9 +375,6 @@
unreadCount: this.get('unreadCount') || 0,
mentionedUs: this.get('mentionedUs') || false,
isBlocked: this.isBlocked(),
phoneNumber: format(this.id, {
ourRegionCode: regionCode,
}),
lastMessage: {
status: this.get('lastMessageStatus'),
text: this.get('lastMessage'),

@ -20,7 +20,7 @@
window.Whisper = window.Whisper || {};
const { Message: TypedMessage, Contact, PhoneNumber } = Signal.Types;
const { Message: TypedMessage, Contact } = Signal.Types;
const {
deleteExternalMessageFiles,
@ -326,9 +326,6 @@
return window.getConversationController().get(phoneNumber);
},
findAndFormatContact(phoneNumber) {
const { format } = PhoneNumber;
const regionCode = storage.get('regionCode');
const contactModel = this.findContact(phoneNumber);
let profileName;
if (phoneNumber === window.storage.get('primaryDevicePubKey')) {
@ -338,9 +335,7 @@
}
return {
phoneNumber: format(phoneNumber, {
ourRegionCode: regionCode,
}),
phoneNumber,
color: null,
avatarPath: contactModel ? contactModel.getAvatarPath() : null,
name: contactModel ? contactModel.getName() : null,
@ -520,7 +515,6 @@
timestamp: this.get('sent_at'),
serverTimestamp: this.get('serverTimestamp'),
status: this.getMessagePropStatus(),
contact: this.getPropsForEmbeddedContact(),
authorName: contact.name,
authorProfileName: contact.profileName,
authorPhoneNumber: contact.phoneNumber,
@ -567,38 +561,6 @@
return `${start}${newSpaces}${end}`;
});
},
getPropsForEmbeddedContact() {
const regionCode = storage.get('regionCode');
const { contactSelector } = Contact;
const contacts = this.get('contact');
if (!contacts || !contacts.length) {
return null;
}
const contact = contacts[0];
const firstNumber =
contact.number && contact.number[0] && contact.number[0].value;
const onSendMessage = firstNumber
? () => {
this.trigger('open-conversation', firstNumber);
}
: null;
const onClick = async () => {
// First let's be sure that the signal account check is complete.
this.trigger('show-contact-detail', {
contact,
});
};
return contactSelector(contact, {
regionCode,
getAbsoluteAttachmentPath,
onSendMessage,
onClick,
});
},
processQuoteAttachment(attachment) {
const { thumbnail } = attachment;
const path =
@ -652,15 +614,9 @@
return null;
}
const { format } = PhoneNumber;
const regionCode = storage.get('regionCode');
const { author, id, referencedMessageNotFound } = quote;
const contact = author && window.getConversationController().get(author);
const authorPhoneNumber = format(author, {
ourRegionCode: regionCode,
});
const authorName = contact ? contact.getName() : null;
const isFromMe = contact
? contact.id === textsecure.storage.user.getNumber()
@ -684,7 +640,7 @@
? this.processQuoteAttachment(firstAttachment)
: null,
isFromMe,
authorPhoneNumber,
authorPhoneNumber: author,
messageId: id,
authorName,
onClick,

@ -101,7 +101,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
@ -113,7 +112,6 @@ const MessageDataMigrator = require('./messages_data_migrator');
function initializeMigrations({
userDataPath,
getRegionCode,
Attachments,
Type,
VisualType,
@ -178,7 +176,6 @@ function initializeMigrations({
return MessageType.upgradeSchema(message, {
writeNewAttachmentData,
getRegionCode,
getAbsoluteAttachmentPath,
makeObjectUrl,
revokeObjectUrl,
@ -200,13 +197,12 @@ function initializeMigrations({
}
exports.setup = (options = {}) => {
const { Attachments, userDataPath, getRegionCode, logger } = options;
const { Attachments, userDataPath, logger } = options;
Data.init();
const Migrations = initializeMigrations({
userDataPath,
getRegionCode,
Attachments,
Type: AttachmentType,
VisualType: VisualAttachment,
@ -261,7 +257,6 @@ exports.setup = (options = {}) => {
Errors,
Message: MessageType,
MIME,
PhoneNumber,
Settings: SettingsType,
VisualAttachment,
};

@ -1,155 +0,0 @@
const { omit, compact, map } = require('lodash');
const { toLogFormat } = require('./errors');
const { SignalService } = require('../../../ts/protobuf');
const { parse: parsePhoneNumber } = require('../../../ts/types/PhoneNumber');
const DEFAULT_PHONE_TYPE = SignalService.DataMessage.Contact.Phone.Type.HOME;
const DEFAULT_EMAIL_TYPE = SignalService.DataMessage.Contact.Email.Type.HOME;
const DEFAULT_ADDRESS_TYPE =
SignalService.DataMessage.Contact.PostalAddress.Type.HOME;
exports.parseAndWriteAvatar = upgradeAttachment => async (
contact,
context = {}
) => {
const { message, regionCode, logger } = context;
const { avatar } = contact;
// This is to ensure that an omit() call doesn't pull in prototype props/methods
const contactShallowCopy = Object.assign({}, contact);
const contactWithUpdatedAvatar =
avatar && avatar.avatar
? Object.assign({}, contactShallowCopy, {
avatar: Object.assign({}, avatar, {
avatar: await upgradeAttachment(avatar.avatar, context),
}),
})
: omit(contactShallowCopy, ['avatar']);
// eliminates empty numbers, emails, and addresses; adds type if not provided
const parsedContact = parseContact(contactWithUpdatedAvatar, { regionCode });
const error = exports._validate(parsedContact, {
messageId: idForLogging(message),
});
if (error) {
logger.error(
'Contact.parseAndWriteAvatar: contact was malformed.',
toLogFormat(error)
);
}
return parsedContact;
};
function parseContact(contact, options = {}) {
const { regionCode } = options;
const boundParsePhone = phoneNumber =>
parsePhoneItem(phoneNumber, { regionCode });
return Object.assign(
{},
omit(contact, ['avatar', 'number', 'email', 'address']),
parseAvatar(contact.avatar),
createArrayKey('number', compact(map(contact.number, boundParsePhone))),
createArrayKey('email', compact(map(contact.email, parseEmailItem))),
createArrayKey('address', compact(map(contact.address, parseAddress)))
);
}
function idForLogging(message) {
return `${message.source}.${message.sourceDevice} ${message.sent_at}`;
}
exports._validate = (contact, options = {}) => {
const { messageId } = options;
const { name, number, email, address, organization } = contact;
if ((!name || !name.displayName) && !organization) {
return new Error(
`Message ${messageId}: Contact had neither 'displayName' nor 'organization'`
);
}
if (
(!number || !number.length) &&
(!email || !email.length) &&
(!address || !address.length)
) {
return new Error(
`Message ${messageId}: Contact had no included numbers, email or addresses`
);
}
return null;
};
function parsePhoneItem(item, options = {}) {
const { regionCode } = options;
if (!item.value) {
return null;
}
return Object.assign({}, item, {
type: item.type || DEFAULT_PHONE_TYPE,
value: parsePhoneNumber(item.value, { regionCode }),
});
}
function parseEmailItem(item) {
if (!item.value) {
return null;
}
return Object.assign({}, item, {
type: item.type || DEFAULT_EMAIL_TYPE,
});
}
function parseAddress(address) {
if (!address) {
return null;
}
if (
!address.street &&
!address.pobox &&
!address.neighborhood &&
!address.city &&
!address.region &&
!address.postcode &&
!address.country
) {
return null;
}
return Object.assign({}, address, {
type: address.type || DEFAULT_ADDRESS_TYPE,
});
}
function parseAvatar(avatar) {
if (!avatar) {
return null;
}
return {
avatar: Object.assign({}, avatar, {
isProfile: avatar.isProfile || false,
}),
};
}
function createArrayKey(key, array) {
if (!array || !array.length) {
return null;
}
return {
[key]: array,
};
}

@ -1,6 +1,5 @@
const { isFunction, isObject, isString, omit } = require('lodash');
const Contact = require('./contact');
const Attachment = require('./attachment');
const Errors = require('./errors');
const SchemaVersion = require('./schema_version');
@ -283,12 +282,6 @@ const toVersion5 = exports._withSchemaVersion({
schemaVersion: 5,
upgrade: initializeAttachmentMetadata,
});
const toVersion6 = exports._withSchemaVersion({
schemaVersion: 6,
upgrade: exports._mapContact(
Contact.parseAndWriteAvatar(Attachment.migrateDataToFileSystem)
),
});
// IMPORTANT: Weve updated our definition of `initializeAttachmentMetadata`, so
// we need to run it again on existing items that have previously been incorrectly
// classified:
@ -318,7 +311,6 @@ const VERSIONS = [
toVersion3,
toVersion4,
toVersion5,
toVersion6,
toVersion7,
toVersion8,
toVersion9,
@ -334,7 +326,6 @@ exports.upgradeSchema = async (
rawMessage,
{
writeNewAttachmentData,
getRegionCode,
getAbsoluteAttachmentPath,
makeObjectUrl,
revokeObjectUrl,
@ -348,9 +339,6 @@ exports.upgradeSchema = async (
if (!isFunction(writeNewAttachmentData)) {
throw new TypeError('context.writeNewAttachmentData is required');
}
if (!isFunction(getRegionCode)) {
throw new TypeError('context.getRegionCode is required');
}
if (!isFunction(getAbsoluteAttachmentPath)) {
throw new TypeError('context.getAbsoluteAttachmentPath is required');
}
@ -386,7 +374,6 @@ exports.upgradeSchema = async (
// eslint-disable-next-line no-await-in-loop
message = await currentVersion(message, {
writeNewAttachmentData,
regionCode: getRegionCode(),
getAbsoluteAttachmentPath,
makeObjectUrl,
revokeObjectUrl,

@ -129,7 +129,6 @@
await textsecure.storage.put('typing-indicators-setting', Boolean(true));
await textsecure.storage.user.setNumberAndDeviceId(pubKeyString, 1);
await textsecure.storage.put('regionCode', null);
},
async clearSessionsAndPreKeys() {
const store = textsecure.storage.protocol;

@ -407,7 +407,6 @@ window.moment.locale(locale);
window.Signal = Signal.setup({
Attachments,
userDataPath: app.getPath('userData'),
getRegionCode: () => window.storage.get('regionCode'),
logger: window.log,
});

@ -1,404 +0,0 @@
const { assert } = require('chai');
const sinon = require('sinon');
const Contact = require('../../../js/modules/types/contact');
const {
stringToArrayBuffer,
} = require('../../../js/modules/string_to_array_buffer');
describe('Contact', () => {
const NUMBER = '+12025550099';
const logger = {
error: () => null,
};
describe('parseAndWriteAvatar', () => {
it('handles message with no avatar in contact', async () => {
const upgradeAttachment = sinon
.stub()
.throws(new Error("Shouldn't be called"));
const upgradeVersion = Contact.parseAndWriteAvatar(upgradeAttachment);
const message = {
body: 'hey there!',
contact: [
{
name: {
displayName: 'Someone Somewhere',
},
number: [
{
type: 1,
value: NUMBER,
},
],
},
],
};
const result = await upgradeVersion(message.contact[0], {
message,
logger,
});
assert.deepEqual(result, message.contact[0]);
});
// LOKI: Phone number stays the same
it('turns phone numbers to e164 format', async () => {
const upgradeAttachment = sinon
.stub()
.throws(new Error("Shouldn't be called"));
const upgradeVersion = Contact.parseAndWriteAvatar(upgradeAttachment);
const message = {
body: 'hey there!',
contact: [
{
name: {
displayName: 'Someone Somewhere',
},
number: [
{
type: 1,
value: '(202) 555-0099',
},
],
},
],
};
const expected = {
name: {
displayName: 'Someone Somewhere',
},
number: [
{
type: 1,
value: '(202) 555-0099',
},
],
};
const result = await upgradeVersion(message.contact[0], {
message,
regionCode: 'US',
logger,
});
assert.deepEqual(result, expected);
});
it('removes contact avatar if it has no sub-avatar', async () => {
const upgradeAttachment = sinon
.stub()
.throws(new Error("Shouldn't be called"));
const upgradeVersion = Contact.parseAndWriteAvatar(upgradeAttachment);
const message = {
body: 'hey there!',
contact: [
{
name: {
displayName: 'Someone Somewhere',
},
number: [
{
type: 1,
value: NUMBER,
},
],
avatar: {
isProfile: true,
},
},
],
};
const expected = {
name: {
displayName: 'Someone Somewhere',
},
number: [
{
type: 1,
value: NUMBER,
},
],
};
const result = await upgradeVersion(message.contact[0], {
message,
logger,
});
assert.deepEqual(result, expected);
});
it('writes avatar to disk', async () => {
const upgradeAttachment = async () => {
return {
path: 'abc/abcdefg',
};
};
const upgradeVersion = Contact.parseAndWriteAvatar(upgradeAttachment);
const message = {
body: 'hey there!',
contact: [
{
name: {
displayName: 'Someone Somewhere',
},
number: [
{
type: 1,
value: NUMBER,
},
],
email: [
{
type: 2,
value: 'someone@somewhere.com',
},
],
address: [
{
type: 1,
street: '5 Somewhere Ave.',
},
],
avatar: {
otherKey: 'otherValue',
avatar: {
contentType: 'image/png',
data: stringToArrayBuffer('Its easy if you try'),
},
},
},
],
};
const expected = {
name: {
displayName: 'Someone Somewhere',
},
number: [
{
type: 1,
value: NUMBER,
},
],
email: [
{
type: 2,
value: 'someone@somewhere.com',
},
],
address: [
{
type: 1,
street: '5 Somewhere Ave.',
},
],
avatar: {
otherKey: 'otherValue',
isProfile: false,
avatar: {
path: 'abc/abcdefg',
},
},
};
const result = await upgradeVersion(message.contact[0], {
message,
logger,
});
assert.deepEqual(result, expected);
});
it('removes number element if it ends up with no value', async () => {
const upgradeAttachment = sinon
.stub()
.throws(new Error("Shouldn't be called"));
const upgradeVersion = Contact.parseAndWriteAvatar(upgradeAttachment);
const message = {
body: 'hey there!',
contact: [
{
name: {
displayName: 'Someone Somewhere',
},
number: [
{
type: 1,
},
],
email: [
{
value: 'someone@somewhere.com',
},
],
},
],
};
const expected = {
name: {
displayName: 'Someone Somewhere',
},
email: [
{
type: 1,
value: 'someone@somewhere.com',
},
],
};
const result = await upgradeVersion(message.contact[0], {
message,
logger,
});
assert.deepEqual(result, expected);
});
it('drops address if it has no real values', async () => {
const upgradeAttachment = sinon
.stub()
.throws(new Error("Shouldn't be called"));
const upgradeVersion = Contact.parseAndWriteAvatar(upgradeAttachment);
const message = {
body: 'hey there!',
contact: [
{
name: {
displayName: 'Someone Somewhere',
},
number: [
{
value: NUMBER,
},
],
address: [
{
type: 1,
},
],
},
],
};
const expected = {
name: {
displayName: 'Someone Somewhere',
},
number: [
{
value: NUMBER,
type: 1,
},
],
};
const result = await upgradeVersion(message.contact[0], {
message,
logger,
});
assert.deepEqual(result, expected);
});
it('removes invalid elements if no values remain in contact', async () => {
const upgradeAttachment = sinon
.stub()
.throws(new Error("Shouldn't be called"));
const upgradeVersion = Contact.parseAndWriteAvatar(upgradeAttachment);
const message = {
body: 'hey there!',
source: NUMBER,
sourceDevice: '1',
sent_at: 1232132,
contact: [
{
name: {
displayName: 'Someone Somewhere',
},
number: [
{
type: 1,
},
],
email: [
{
type: 1,
},
],
},
],
};
const expected = {
name: {
displayName: 'Someone Somewhere',
},
};
const result = await upgradeVersion(message.contact[0], {
message,
logger,
});
assert.deepEqual(result, expected);
});
it('handles a contact with just organization', async () => {
const upgradeAttachment = sinon
.stub()
.throws(new Error("Shouldn't be called"));
const upgradeVersion = Contact.parseAndWriteAvatar(upgradeAttachment);
const message = {
contact: [
{
organization: 'Somewhere Consulting',
number: [
{
type: 1,
value: NUMBER,
},
],
},
],
};
const result = await upgradeVersion(message.contact[0], {
message,
logger,
});
assert.deepEqual(result, message.contact[0]);
});
});
describe('_validate', () => {
it('returns error if contact has no name.displayName or organization', () => {
const messageId = 'the-message-id';
const contact = {
name: {
name: 'Someone',
},
number: [
{
type: 1,
value: NUMBER,
},
],
};
const expected =
"Message the-message-id: Contact had neither 'displayName' nor 'organization'";
const result = Contact._validate(contact, { messageId });
assert.deepEqual(result.message, expected);
});
it('logs if no values remain in contact', async () => {
const messageId = 'the-message-id';
const contact = {
name: {
displayName: 'Someone Somewhere',
},
number: [],
email: [],
};
const expected =
'Message the-message-id: Contact had no included numbers, email or addresses';
const result = Contact._validate(contact, { messageId });
assert.deepEqual(result.message, expected);
});
});
});

@ -299,7 +299,6 @@ describe('Message', () => {
assert.deepEqual(attachmentData, expectedAttachmentData);
return 'abc/abcdefg';
},
getRegionCode: () => 'US',
getAbsoluteAttachmentPath: () => 'some/path/on/disk',
makeObjectUrl: () => 'blob://FAKE',
revokeObjectUrl: () => null,

@ -15,7 +15,6 @@ export type SearchResultsProps = {
conversations: Array<ConversationListItemProps>;
hideMessagesHeader: boolean;
messages: Array<MessageSearchResultProps>;
regionCode: string;
searchTerm: string;
};

@ -6,7 +6,6 @@ import { PubKey } from '../session/types';
export type Props = {
contacts: Array<ConversationListItemProps>;
regionCode: string;
searchTerm: string;
selectedContact: number;
onContactSelected: any;

@ -24,24 +24,6 @@ const TypingBubbleContainer = styled.div<TypingBubbleProps>`
`;
export const TypingBubble = (props: TypingBubbleProps) => {
// const renderAvatar = () => {
// const { avatarPath, displayedName, conversationType, phoneNumber } = props;
// if (conversationType !== 'group') {
// return;
// }
// return (
// <div className="module-message__author-avatar">
// <Avatar
// avatarPath={avatarPath}
// name={displayedName || phoneNumber}
// size={36}
// pubkey={phoneNumber}
// />
// </div>
// );
// };
if (props.conversationType === 'group') {
return <></>;

@ -1,14 +1,13 @@
import React from 'react';
import { ConversationListItemWithDetails } from '../ConversationListItem';
import { LeftPane, RowRendererParamsType } from '../LeftPane';
import { RowRendererParamsType } from '../LeftPane';
import {
SessionButton,
SessionButtonColor,
SessionButtonType,
} from './SessionButton';
import { AutoSizer, List } from 'react-virtualized';
import { validateNumber } from '../../types/PhoneNumber';
import { ConversationType } from '../../state/ducks/conversations';
import {
SessionClosableOverlay,
@ -18,6 +17,7 @@ import { ToastUtils } from '../../session/utils';
import { DefaultTheme } from 'styled-components';
import { LeftPaneSectionHeader } from './LeftPaneSectionHeader';
import { ConversationController } from '../../session/conversations';
import { PubKey } from '../../session/types';
export interface Props {
directContacts: Array<ConversationType>;
@ -122,7 +122,7 @@ export class LeftPaneContactSection extends React.Component<Props, State> {
private handleOnAddContact() {
const sessionID = this.state.addContactRecipientID.trim();
const error = validateNumber(sessionID, window.i18n);
const error = PubKey.validateWithError(sessionID, window.i18n);
if (error) {
ToastUtils.pushToastError('addContact', error);

@ -13,8 +13,7 @@ import { SessionSearchInput } from './SessionSearchInput';
import { debounce } from 'lodash';
import { cleanSearchTerm } from '../../util/cleanSearchTerm';
import { SearchOptions } from '../../types/Search';
import { validateNumber } from '../../types/PhoneNumber';
import { LeftPane, RowRendererParamsType } from '../LeftPane';
import { RowRendererParamsType } from '../LeftPane';
import {
SessionClosableOverlay,
SessionClosableOverlayType,
@ -26,7 +25,7 @@ import {
SessionButtonColor,
SessionButtonType,
} from './SessionButton';
import { OpenGroup } from '../../session/types';
import { OpenGroup, PubKey } from '../../session/types';
import { ToastUtils } from '../../session/utils';
import { DefaultTheme } from 'styled-components';
import { LeftPaneSectionHeader } from './LeftPaneSectionHeader';
@ -266,7 +265,6 @@ export class LeftPaneMessageSection extends React.Component<Props, State> {
search(searchTerm, {
noteToSelf: window.i18n('noteToSelf').toLowerCase(),
ourNumber: window.textsecure.storage.user.getNumber(),
regionCode: '',
});
}
}
@ -392,7 +390,7 @@ export class LeftPaneMessageSection extends React.Component<Props, State> {
pubkey = this.state.valuePasted || this.props.searchTerm;
pubkey = pubkey.trim();
const error = validateNumber(pubkey);
const error = PubKey.validateWithError(pubkey);
if (!error) {
await ConversationController.getInstance().getOrCreateAndWait(
pubkey,

@ -191,7 +191,6 @@ export class SessionInboxView extends React.Component<Props, State> {
),
},
user: {
regionCode: window.storage.get('regionCode'),
ourPrimary: window.storage.get('primaryDevicePubKey'),
ourNumber:
window.storage.get('primaryDevicePubKey') ||

@ -1,3 +1,4 @@
import { LocalizerType } from '../../types/Util';
import { fromHexToArray } from '../utils/String';
export class PubKey {
@ -58,6 +59,10 @@ export class PubKey {
const valAny = value as PubKey;
const pk = value instanceof PubKey ? valAny.key : value;
if (!pk) {
throw new Error('PubkKey.shorten was given an invalid PubKey to shorten.')
}
return `(...${pk.substring(pk.length - 6)})`;
}
@ -76,10 +81,40 @@ export class PubKey {
return undefined;
}
/**
* Returns the pubkey as a string if it's valid, or undefined
*/
public static normalize(pubkeyString: string): string | undefined {
// Returns a new instance if the pubkey is valid
if (PubKey.validate(pubkeyString)) {
return pubkeyString;
}
return undefined;
}
public static validate(pubkeyString: string): boolean {
return this.regex.test(pubkeyString);
}
/**
* Returns a localized string of the error, or undefined in the given pubkey is valid.
*/
public static validateWithError(pubkey: string, i18n: LocalizerType = window.i18n): string | undefined {
// Check if it's hex
const isHex = pubkey.replace(/[\s]*/g, '').match(/^[0-9a-fA-F]+$/);
if (!isHex) {
return i18n('invalidSessionId');
}
// Check if the pubkey length is 33 and leading with 05 or of length 32
const len = pubkey.length;
if ((len !== 33 * 2 || !/^05/.test(pubkey)) && len !== 32 * 2) {
return i18n('invalidPubkeyFormat');
}
return undefined;
}
/**
* This removes the 05 prefix from a Pubkey which have it and have a length of 66
* @param keyWithOrWithoutPrefix the key with or without the prefix

@ -1,6 +1,5 @@
import { omit, reject } from 'lodash';
import { normalize } from '../../types/PhoneNumber';
import { AdvancedSearchOptions, SearchOptions } from '../../types/Search';
import { getMessageModel } from '../../shims/Whisper';
import { cleanSearchTerm } from '../../util/cleanSearchTerm';
@ -14,6 +13,7 @@ import {
RemoveAllConversationsActionType,
SelectedConversationChangedActionType,
} from './conversations';
import { PubKey } from '../../session/types';
// State
@ -89,8 +89,6 @@ async function doSearch(
query: string,
options: SearchOptions
): Promise<SearchResultsPayloadType> {
const { regionCode } = options;
const advancedSearchOptions = getAdvancedSearchOptionsFromQuery(query);
const processedQuery = advancedSearchOptions.query;
const isAdvancedQuery = query !== processedQuery;
@ -120,7 +118,7 @@ async function doSearch(
return {
query,
normalizedPhoneNumber: normalize(query, { regionCode }),
normalizedPhoneNumber: PubKey.normalize(query),
conversations,
contacts,
messages: getMessageProps(filteredMessages) || [],

@ -4,7 +4,6 @@ import { LocalizerType } from '../../types/Util';
export type UserStateType = {
ourNumber: string;
regionCode: string;
i18n: LocalizerType;
};
@ -15,7 +14,6 @@ type UserChangedActionType = {
payload: {
ourNumber: string;
ourPrimary: string;
regionCode: string;
};
};
@ -30,7 +28,6 @@ export const actions = {
function userChanged(attributes: {
ourNumber: string;
ourPrimary: string;
regionCode: string;
}): UserChangedActionType {
return {
type: 'USER_CHANGED',
@ -43,7 +40,6 @@ function userChanged(attributes: {
function getEmptyState(): UserStateType {
return {
ourNumber: 'missing',
regionCode: 'missing',
i18n: () => 'missing',
};
}

@ -1,5 +1,4 @@
import { createSelector } from 'reselect';
import { format } from '../../types/PhoneNumber';
import { LocalizerType } from '../../types/Util';
import { StateType } from '../reducer';
@ -10,7 +9,7 @@ import {
MessageTypeInConvo,
} from '../ducks/conversations';
import { getIntl, getOurNumber, getRegionCode } from './user';
import { getIntl, getOurNumber } from './user';
import { BlockedNumberController } from '../../util';
export const getConversations = (state: StateType): ConversationsStateType =>
@ -50,27 +49,21 @@ export const getMessagesOfSelectedConversation = createSelector(
(state: ConversationsStateType): Array<MessageTypeInConvo> => state.messages
);
function getConversationTitle(
conversation: ConversationType,
options: { i18n: LocalizerType; ourRegionCode: string }
): string {
function getConversationTitle(conversation: ConversationType): string {
if (conversation.name) {
return conversation.name;
}
if (conversation.type === 'group') {
const { i18n } = options;
const { i18n } = window;
return i18n('unknown');
}
return format(conversation.phoneNumber, options);
return conversation.id;
}
const collator = new Intl.Collator();
export const _getConversationComparator = (
i18n: LocalizerType,
ourRegionCode: string
) => {
export const _getConversationComparator = (i18n: LocalizerType) => {
return (left: ConversationType, right: ConversationType): number => {
const leftTimestamp = left.timestamp;
const rightTimestamp = right.timestamp;
@ -83,21 +76,14 @@ export const _getConversationComparator = (
if (leftTimestamp && rightTimestamp && leftTimestamp !== rightTimestamp) {
return rightTimestamp - leftTimestamp;
}
const leftTitle = getConversationTitle(left, {
i18n,
ourRegionCode,
}).toLowerCase();
const rightTitle = getConversationTitle(right, {
i18n,
ourRegionCode,
}).toLowerCase();
const leftTitle = getConversationTitle(left).toLowerCase();
const rightTitle = getConversationTitle(right).toLowerCase();
return collator.compare(leftTitle, rightTitle);
};
};
export const getConversationComparator = createSelector(
getIntl,
getRegionCode,
_getConversationComparator
);

@ -11,8 +11,6 @@ import {
} from './conversations';
import { ConversationLookupType } from '../ducks/conversations';
import { getRegionCode } from './user';
export const getSearch = (state: StateType): SearchStateType => state.search;
export const getQuery = createSelector(
@ -37,14 +35,12 @@ export const isSearching = createSelector(
export const getSearchResults = createSelector(
[
getSearch,
getRegionCode,
getConversationLookup,
getSelectedConversationKey,
getSelectedMessage,
],
(
state: SearchStateType,
regionCode: string,
lookup: ConversationLookupType,
selectedConversation?: string,
selectedMessage?: string
@ -94,7 +90,6 @@ export const getSearchResults = createSelector(
return message;
}),
regionCode: regionCode,
searchTerm: state.query,
};
}

@ -12,11 +12,6 @@ export const getOurNumber = createSelector(
(state: UserStateType): string => state.ourNumber
);
export const getRegionCode = createSelector(
getUser,
(state: UserStateType): string => state.regionCode
);
export const getIntl = createSelector(
getUser,
(state: UserStateType): LocalizerType => state.i18n

@ -3,7 +3,7 @@ import { LeftPane } from '../../components/LeftPane';
import { StateType } from '../reducer';
import { getQuery, getSearchResults, isSearching } from '../selectors/search';
import { getIntl, getOurNumber, getRegionCode } from '../selectors/user';
import { getIntl, getOurNumber } from '../selectors/user';
import {
getLeftPaneLists,
getOurPrimaryConversation,
@ -25,7 +25,6 @@ const mapStateToProps = (state: StateType) => {
...lists,
ourPrimaryConversation: getOurPrimaryConversation(state), // used in actionPanel
searchTerm: getQuery(state),
regionCode: getRegionCode(state),
ourNumber: getOurNumber(state),
searchResults,
i18n: getIntl(state),

@ -10,7 +10,6 @@ describe('state/selectors/conversations', () => {
describe('#getLeftPaneList', () => {
it('sorts conversations based on timestamp then by intl-friendly title', () => {
const i18n = (key: string) => key;
const regionCode = 'US';
const data: ConversationLookupType = {
id1: {
id: 'id1',
@ -101,7 +100,7 @@ describe('state/selectors/conversations', () => {
left: false,
},
};
const comparator = _getConversationComparator(i18n, regionCode);
const comparator = _getConversationComparator(i18n);
const { conversations } = _getLeftPaneLists(data, comparator);
assert.strictEqual(conversations[0].name, 'First!');

@ -1,189 +0,0 @@
import { assert } from 'chai';
import { contactSelector, getName } from '../../types/Contact';
describe('Contact', () => {
describe('getName', () => {
it('returns displayName if provided', () => {
const contact = {
name: {
displayName: 'displayName',
givenName: 'givenName',
familyName: 'familyName',
},
organization: 'Somewhere, Inc.',
};
const expected = 'displayName';
const actual = getName(contact);
assert.strictEqual(actual, expected);
});
it('returns organization if no displayName', () => {
const contact = {
name: {
givenName: 'givenName',
familyName: 'familyName',
},
organization: 'Somewhere, Inc.',
};
const expected = 'Somewhere, Inc.';
const actual = getName(contact);
assert.strictEqual(actual, expected);
});
it('returns givenName + familyName if no displayName or organization', () => {
const contact = {
name: {
givenName: 'givenName',
familyName: 'familyName',
},
};
const expected = 'givenName familyName';
const actual = getName(contact);
assert.strictEqual(actual, expected);
});
it('returns just givenName', () => {
const contact = {
name: {
givenName: 'givenName',
},
};
const expected = 'givenName';
const actual = getName(contact);
assert.strictEqual(actual, expected);
});
it('returns just familyName', () => {
const contact = {
name: {
familyName: 'familyName',
},
};
const expected = 'familyName';
const actual = getName(contact);
assert.strictEqual(actual, expected);
});
});
describe('contactSelector', () => {
const regionCode = '1';
const getAbsoluteAttachmentPath = (path: string) => `absolute:${path}`;
const onSendMessage = () => null;
const onClick = () => null;
it('eliminates avatar if it has had an attachment download error', () => {
const contact = {
name: {
displayName: 'displayName',
givenName: 'givenName',
familyName: 'familyName',
},
organization: 'Somewhere, Inc.',
avatar: {
isProfile: true,
avatar: {
error: true,
},
},
};
const expected = {
name: {
displayName: 'displayName',
givenName: 'givenName',
familyName: 'familyName',
},
organization: 'Somewhere, Inc.',
avatar: undefined,
onSendMessage,
onClick,
number: undefined,
};
const actual = contactSelector(contact, {
regionCode,
getAbsoluteAttachmentPath,
onSendMessage,
onClick,
});
assert.deepEqual(actual, expected);
});
it('does not calculate absolute path if avatar is pending', () => {
const contact = {
name: {
displayName: 'displayName',
givenName: 'givenName',
familyName: 'familyName',
},
organization: 'Somewhere, Inc.',
avatar: {
isProfile: true,
avatar: {
pending: true,
},
},
};
const expected = {
name: {
displayName: 'displayName',
givenName: 'givenName',
familyName: 'familyName',
},
organization: 'Somewhere, Inc.',
avatar: {
isProfile: true,
avatar: {
pending: true,
path: undefined,
},
},
onSendMessage,
onClick,
number: undefined,
};
const actual = contactSelector(contact, {
regionCode,
getAbsoluteAttachmentPath,
onSendMessage,
onClick,
});
assert.deepEqual(actual, expected);
});
it('calculates absolute path', () => {
const contact = {
name: {
displayName: 'displayName',
givenName: 'givenName',
familyName: 'familyName',
},
organization: 'Somewhere, Inc.',
avatar: {
isProfile: true,
avatar: {
path: 'somewhere',
},
},
};
const expected = {
name: {
displayName: 'displayName',
givenName: 'givenName',
familyName: 'familyName',
},
organization: 'Somewhere, Inc.',
avatar: {
isProfile: true,
avatar: {
path: 'absolute:somewhere',
},
},
onSendMessage,
onClick,
number: undefined,
};
const actual = contactSelector(contact, {
regionCode,
getAbsoluteAttachmentPath,
onSendMessage,
onClick,
});
assert.deepEqual(actual, expected);
});
});
});

@ -1,12 +1,7 @@
// @ts-ignore
import Attachments from '../../app/attachments';
import { format as formatPhoneNumber } from '../types/PhoneNumber';
export interface Contact {
name?: Name;
number?: Array<Phone>;
email?: Array<Email>;
address?: Array<PostalAddress>;
avatar?: Avatar;
organization?: string;
}
@ -39,24 +34,6 @@ export interface Phone {
label?: string;
}
export interface Email {
value: string;
type: ContactType;
label?: string;
}
export interface PostalAddress {
type: AddressType;
label?: string;
street?: string;
pobox?: string;
neighborhood?: string;
city?: string;
region?: string;
postcode?: string;
country?: string;
}
interface Avatar {
avatar: Attachment;
isProfile: boolean;
@ -68,55 +45,6 @@ interface Attachment {
pending?: boolean;
}
export function contactSelector(
contact: Contact,
options: {
regionCode: string;
getAbsoluteAttachmentPath: (path: string) => string;
onSendMessage: () => void;
onClick: () => void;
}
) {
const {
getAbsoluteAttachmentPath,
onClick,
onSendMessage,
regionCode,
} = options;
let { avatar } = contact;
if (avatar && avatar.avatar) {
if (avatar.avatar.error) {
avatar = undefined;
} else {
avatar = {
...avatar,
avatar: {
...avatar.avatar,
path: avatar.avatar.path
? getAbsoluteAttachmentPath(avatar.avatar.path)
: undefined,
},
};
}
}
return {
...contact,
onSendMessage,
onClick,
avatar,
number:
contact.number &&
contact.number.map(item => ({
...item,
value: formatPhoneNumber(item.value, {
ourRegionCode: regionCode,
}),
})),
};
}
export function getName(contact: Contact): string | undefined {
const { name, organization } = contact;
const displayName = (name && name.displayName) || undefined;

@ -38,7 +38,7 @@ type ExpirationTimerUpdate = Partial<
expirationTimerUpdate: Readonly<{
expireTimer: number;
fromSync: boolean;
source: string; // PhoneNumber
source: string;
}>;
}>
>;

@ -1,65 +0,0 @@
import { LocalizerType } from './Util';
export function format(
phoneNumber: string,
_options: {
ourRegionCode: string;
}
) {
return phoneNumber;
}
export function parse(
phoneNumber: string,
_options: {
regionCode: string;
}
): string {
return phoneNumber;
}
export function normalize(
phoneNumber: string,
_options: { regionCode: string }
): string | undefined {
try {
if (isValidNumber(phoneNumber)) {
return phoneNumber;
}
return;
} catch (error) {
return;
}
}
function validate(number: string) {
// Check if it's hex
const isHex = number.replace(/[\s]*/g, '').match(/^[0-9a-fA-F]+$/);
if (!isHex) {
return 'invalidSessionId';
}
// Check if the pubkey length is 33 and leading with 05 or of length 32
const len = number.length;
if ((len !== 33 * 2 || !/^05/.test(number)) && len !== 32 * 2) {
return 'invalidPubkeyFormat';
}
return null;
}
function isValidNumber(number: string) {
const error = validate(number);
return !error;
}
export function validateNumber(
number: string,
i18n: LocalizerType = window.i18n
) {
const error = validate(number);
return error && i18n(error);
}

@ -1,5 +1,4 @@
export type SearchOptions = {
regionCode: string;
ourNumber: string;
noteToSelf: string;
};

@ -496,15 +496,6 @@
"updated": "2018-09-19T21:59:32.770Z",
"reasonDetail": "Protected from arbitrary input"
},
{
"rule": "jQuery-$(",
"path": "js/views/phone-input-view.js",
"line": " const regionCode = this.$('li.active')",
"lineNumber": 22,
"reasonCategory": "usageTrusted",
"updated": "2018-09-19T21:59:32.770Z",
"reasonDetail": "Protected from arbitrary input"
},
{
"rule": "jQuery-$(",
"path": "js/views/phone-input-view.js",

Loading…
Cancel
Save