Wire up all contact behaviors, refactor Contact type/selector

This commit is contained in:
Scott Nonnenberg 2018-05-04 18:19:54 -07:00
parent 41be7f126b
commit 37821e5a1b
13 changed files with 198 additions and 192 deletions

View File

@ -238,6 +238,12 @@
appView.openInbox();
}
});
Whisper.events.on('showConversation', function(conversation) {
if (appView) {
appView.openConversation(conversation);
}
});
Whisper.Notifications.on('click', function(conversation) {
showWindow();
if (conversation) {

1
js/modules/types/errors.d.ts vendored Normal file
View File

@ -0,0 +1 @@
export function toLogFormat(error: any): string;

View File

@ -1007,15 +1007,17 @@
},
showContactDetail(contact) {
console.log('showContactDetail', contact); // TODO
// TODO: need to run contact through selector to format email, get absolute path
// think it's probably time to move it to typescript
const regionCode = storage.get('regionCode');
const { contactSelector } = Signal.Types.Contact;
const { getAbsoluteAttachmentPath } = window.Signal.Migrations;
const view = new Whisper.ReactWrapperView({
Component: Signal.Components.MediaGallery,
Component: Signal.Components.ContactDetail,
props: {
contact,
contact: contactSelector(contact, {
regionCode,
getAbsoluteAttachmentPath,
}),
hasSignalAccount: true,
onSendMessage: () => {
const number =
@ -1032,13 +1034,11 @@
},
async openConversation(number) {
console.log('openConversation', number); // TODO
const conversation = await window.ConversationController.getOrCreateAndWait(
number,
'private'
);
window.Whisper.Events.trigger('click', conversation);
window.Whisper.events.trigger('showConversation', conversation);
},
listenBack(view) {
@ -1244,7 +1244,6 @@
if (message) {
const quote = await this.model.makeQuote(this.quotedMessage);
console.log({ quote });
this.quote = quote;
this.focusMessageFieldAndClearDisabled();

View File

@ -5,15 +5,17 @@
/* global emoji_util: false */
/* global Mustache: false */
/* global $: false */
/* global libphonenumber: false */
/* global storage: false */
/* global Signal: false */
// eslint-disable-next-line func-names
(function() {
'use strict';
const { Signal } = window;
const { loadAttachmentData } = window.Signal.Migrations;
const {
loadAttachmentData,
getAbsoluteAttachmentPath,
} = window.Signal.Migrations;
window.Whisper = window.Whisper || {};
@ -441,46 +443,6 @@
});
this.$('.inner-bubble').prepend(this.quoteView.el);
},
formatPhoneNumber(number, options = {}) {
const { ourRegionCode } = options;
const parsedNumber = libphonenumber.parse(number);
const regionCode = libphonenumber.getRegionCodeForNumber(parsedNumber);
if (ourRegionCode && regionCode === ourRegionCode) {
return libphonenumber.format(
parsedNumber,
libphonenumber.PhoneNumberFormat.NATIONAL
);
}
return libphonenumber.format(
parsedNumber,
libphonenumber.PhoneNumberFormat.INTERNATIONAL
);
},
contactSelector(contact) {
const { getAbsoluteAttachmentPath } = Signal.Migrations;
const region = storage.get('regionCode');
let { avatar } = contact;
if (avatar && avatar.avatar && avatar.avatar.path) {
avatar = Object.assign({}, avatar, {
avatar: Object.assign({}, avatar.avatar, {
path: getAbsoluteAttachmentPath(avatar.avatar.path),
}),
});
}
return Object.assign({}, contact, {
avatar,
number:
contact.number &&
contact.number.map(item =>
Object.assign({}, item, {
value: this.formatPhoneNumber(item.value, {
ourRegionCode: region,
}),
})
),
});
},
renderContact() {
const contacts = this.model.get('contact');
if (!contacts || !contacts.length) {
@ -488,6 +450,9 @@
}
const contact = contacts[0];
const regionCode = storage.get('regionCode');
const { contactSelector } = Signal.Types.Contact;
const number =
contact.number && contact.number[0] && contact.number[0].value;
const haveConversation =
@ -503,14 +468,15 @@
this.model.trigger('show-contact-detail', contact);
};
const getProps = () => {
return {
contact: this.contactSelector(contact),
hasSignalAccount,
onSendMessage,
onOpenContact,
};
};
const getProps = () => ({
contact: contactSelector(contact, {
regionCode,
getAbsoluteAttachmentPath,
}),
hasSignalAccount,
onSendMessage,
onOpenContact,
});
if (this.contactView) {
this.contactView.remove();

View File

@ -172,10 +172,13 @@ const { Quote } = require('./ts/components/conversation/Quote');
const {
EmbeddedContact,
} = require('./ts/components/conversation/EmbeddedContact');
const { ContactDetail } = require('./ts/components/conversation/ContactDetail');
const MediaGalleryMessage = require('./ts/components/conversation/media-gallery/types/Message');
window.Signal.Components = {
ContactDetail,
EmbeddedContact,
Lightbox,
LightboxGallery,
MediaGallery,
@ -183,7 +186,6 @@ window.Signal.Components = {
Message: MediaGalleryMessage,
},
Quote,
EmbeddedContact,
};
window.Signal.Migrations = {};
@ -210,6 +212,7 @@ window.Signal.Startup = require('./js/modules/startup');
window.Signal.Types = {};
window.Signal.Types.Attachment = Attachment;
window.Signal.Types.Contact = require('./ts/types/Contact');
window.Signal.Types.Conversation = require('./ts/types/Conversation');
window.Signal.Types.Errors = require('./js/modules/types/errors');

View File

@ -852,8 +852,11 @@ span.status {
.send-message {
color: white;
border-top: 1px solid rgba(255, 255, 255, 0.5);
border-bottom: 1px solid rgba(255, 255, 255, 0.5);
// We would like to use these border colors for incoming messages, but the version
// of Chromium in our Electron version doesn't render these appropriately. Both
// borders disappear for some reason, and it seems to have to do with transparency.
// border-top: 1px solid rgba(255, 255, 255, 0.5);
// border-bottom: 1px solid rgba(255, 255, 255, 0.5);
.bubble-icon {
background-color: white;
@ -963,6 +966,10 @@ span.status {
}
}
.conversation .contact-detail {
margin-top: 40px;
}
.quoted-message {
@include message-replies-colors;
@include twenty-percent-colors;

View File

@ -1,5 +1,13 @@
import React from 'react';
import {
Contact,
ContactType,
AddressType,
Phone,
Email,
PostalAddress,
} from '../../types/Contact';
import { missingCaseError } from '../../util/missingCaseError';
type Localizer = (key: string, values?: Array<string>) => string;
@ -11,70 +19,6 @@ interface Props {
onSendMessage: () => void;
}
interface Contact {
name: Name;
number?: Array<Phone>;
email?: Array<Email>;
address?: Array<PostalAddress>;
avatar?: Avatar;
organization?: string;
}
interface Name {
givenName?: string;
familyName?: string;
prefix?: string;
suffix?: string;
middleName?: string;
displayName: string;
}
enum ContactType {
HOME = 1,
MOBILE = 2,
WORK = 3,
CUSTOM = 4,
}
enum AddressType {
HOME = 1,
WORK = 2,
CUSTOM = 3,
}
interface Phone {
value: string;
type: ContactType;
label?: string;
}
interface Email {
value: string;
type: ContactType;
label?: string;
}
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;
}
interface Attachment {
path: string;
}
function getLabelForContactMethod(method: Phone | Email, i18n: Localizer) {
switch (method.type) {
case ContactType.CUSTOM:

View File

@ -1,4 +1,5 @@
import React from 'react';
import { Contact } from '../../types/Contact';
interface Props {
contact: Contact;
@ -8,70 +9,6 @@ interface Props {
onOpenContact: () => void;
}
interface Contact {
name: Name;
number?: Array<Phone>;
email?: Array<Email>;
address?: Array<PostalAddress>;
avatar?: Avatar;
organization?: string;
}
interface Name {
givenName?: string;
familyName?: string;
prefix?: string;
suffix?: string;
middleName?: string;
displayName: string;
}
enum ContactType {
HOME = 1,
MOBILE = 2,
WORK = 3,
CUSTOM = 4,
}
enum AddressType {
HOME = 1,
WORK = 2,
CUSTOM = 3,
}
interface Phone {
value: string;
type: ContactType;
label?: string;
}
interface Email {
value: string;
type: ContactType;
label?: string;
}
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;
}
interface Attachment {
path: string;
}
function getInitials(name: string): string {
return name.trim()[0] || '#';
}

View File

@ -19,6 +19,8 @@ export { BackboneWrapper } from '../components/utility/BackboneWrapper';
import { Quote } from '../components/conversation/Quote';
import { EmbeddedContact } from '../components/conversation/EmbeddedContact';
import * as Contact from '../types/Contact';
import * as HTML from '../html';
import * as Attachment from '../../ts/types/Attachment';
@ -130,6 +132,7 @@ parent.ReactDOM = ReactDOM;
parent.Signal.HTML = HTML;
parent.Signal.Types.MIME = MIME;
parent.Signal.Types.Attachment = Attachment;
parent.Signal.Types.Contact = Contact;
parent.Signal.Components = {
Quote,
EmbeddedContact,

99
ts/types/Contact.ts Normal file
View File

@ -0,0 +1,99 @@
// @ts-ignore
import Attachments from '../../app/attachments';
import { formatPhoneNumber } from '../util/formatPhoneNumber';
export interface Contact {
name: Name;
number?: Array<Phone>;
email?: Array<Email>;
address?: Array<PostalAddress>;
avatar?: Avatar;
organization?: string;
}
interface Name {
givenName?: string;
familyName?: string;
prefix?: string;
suffix?: string;
middleName?: string;
displayName: string;
}
export enum ContactType {
HOME = 1,
MOBILE = 2,
WORK = 3,
CUSTOM = 4,
}
export enum AddressType {
HOME = 1,
WORK = 2,
CUSTOM = 3,
}
export interface Phone {
value: string;
type: ContactType;
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;
}
interface Attachment {
path: string;
}
export function contactSelector(
contact: Contact,
options: {
regionCode: string;
getAbsoluteAttachmentPath: (path: string) => string;
}
) {
const { regionCode, getAbsoluteAttachmentPath } = options;
let { avatar } = contact;
if (avatar && avatar.avatar && avatar.avatar.path) {
avatar = {
...avatar,
avatar: {
...avatar.avatar,
path: getAbsoluteAttachmentPath(avatar.avatar.path),
},
};
}
return Object.assign({}, contact, {
avatar,
number:
contact.number &&
contact.number.map(item => ({
...item,
value: formatPhoneNumber(item.value, {
ourRegionCode: regionCode,
}),
})),
});
}

View File

@ -1,4 +1,5 @@
import { Attachment } from './Attachment';
import { Contact } from './Contact';
import { IndexableBoolean, IndexablePresence } from './IndexedDB';
export type Message = UserMessage | VerifiedChangeMessage;
@ -21,6 +22,7 @@ export type IncomingMessage = Readonly<
sourceDevice?: number;
} & SharedMessageProperties &
MessageSchemaVersion5 &
MessageSchemaVersion6 &
ExpirationTimerUpdate
>;
@ -81,3 +83,9 @@ type MessageSchemaVersion5 = Partial<
hasFileAttachments: IndexablePresence;
}>
>;
type MessageSchemaVersion6 = Partial<
Readonly<{
contact: Array<Contact>;
}>
>;

View File

@ -0,0 +1,27 @@
import { toLogFormat } from '../../js/modules/types/errors';
import { instance, PhoneNumberFormat } from './libphonenumberInstance';
export function formatPhoneNumber(
number: string,
options: {
ourRegionCode: string;
}
) {
try {
const { ourRegionCode } = options;
const parsedNumber = instance.parse(number);
const regionCode = instance.getRegionCodeForNumber(parsedNumber);
if (ourRegionCode && regionCode === ourRegionCode) {
return instance.format(parsedNumber, PhoneNumberFormat.NATIONAL);
}
return instance.format(parsedNumber, PhoneNumberFormat.INTERNATIONAL);
} catch (error) {
console.log(
'formatPhoneNumber - had problems formatting number:',
toLogFormat(error)
);
return number;
}
}

View File

@ -0,0 +1,6 @@
import libphonenumber from 'google-libphonenumber';
const instance = libphonenumber.PhoneNumberUtil.getInstance();
const PhoneNumberFormat = libphonenumber.PhoneNumberFormat;
export { instance, PhoneNumberFormat };