Refactor: Prepare Message function props for conversation scope
This commit is contained in:
parent
7e58594038
commit
d342b23cbc
13 changed files with 300 additions and 256 deletions
|
@ -162,17 +162,8 @@
|
|||
isUnread() {
|
||||
return !!this.get('unread');
|
||||
},
|
||||
// Important to allow for this.unset('unread'), save to db, then fetch()
|
||||
// to propagate. We don't want the unset key in the db so our unread index
|
||||
// stays small.
|
||||
merge(model) {
|
||||
const attributes = model.attributes || model;
|
||||
|
||||
const { unread } = attributes;
|
||||
if (typeof unread === 'undefined') {
|
||||
this.unset('unread');
|
||||
}
|
||||
|
||||
this.set(attributes);
|
||||
},
|
||||
getNameForNumber(number) {
|
||||
|
@ -311,13 +302,12 @@
|
|||
const conversation = this.getConversation();
|
||||
const isGroup = conversation && !conversation.isPrivate();
|
||||
const phoneNumber = this.get('key_changed');
|
||||
const onVerify = () =>
|
||||
this.trigger('show-identity', this.findContact(phoneNumber));
|
||||
const showIdentity = id => this.trigger('show-identity', id);
|
||||
|
||||
return {
|
||||
isGroup,
|
||||
contact: this.findAndFormatContact(phoneNumber),
|
||||
onVerify,
|
||||
showIdentity,
|
||||
};
|
||||
},
|
||||
getPropsForVerificationNotification() {
|
||||
|
@ -339,21 +329,17 @@
|
|||
return ConversationController.get(phoneNumber);
|
||||
},
|
||||
findAndFormatContact(phoneNumber) {
|
||||
const contactModel = this.findContact(phoneNumber);
|
||||
if (contactModel) {
|
||||
return contactModel.getProps();
|
||||
}
|
||||
|
||||
const { format } = PhoneNumber;
|
||||
const regionCode = storage.get('regionCode');
|
||||
|
||||
const contactModel = this.findContact(phoneNumber);
|
||||
const color = contactModel ? contactModel.getColor() : null;
|
||||
|
||||
return {
|
||||
phoneNumber: format(phoneNumber, {
|
||||
ourRegionCode: regionCode,
|
||||
}),
|
||||
color,
|
||||
avatarPath: contactModel ? contactModel.getAvatarPath() : null,
|
||||
name: contactModel ? contactModel.getName() : null,
|
||||
profileName: contactModel ? contactModel.getProfileName() : null,
|
||||
title: contactModel ? contactModel.getTitle() : null,
|
||||
};
|
||||
},
|
||||
getPropsForGroupNotification() {
|
||||
|
@ -460,7 +446,7 @@
|
|||
snippet: this.get('snippet'),
|
||||
};
|
||||
},
|
||||
getPropsForMessage(options) {
|
||||
getPropsForMessage() {
|
||||
const phoneNumber = this.getSource();
|
||||
const contact = this.findAndFormatContact(phoneNumber);
|
||||
const contactModel = this.findContact(phoneNumber);
|
||||
|
@ -479,9 +465,7 @@
|
|||
|
||||
const conversation = this.getConversation();
|
||||
const isGroup = conversation && !conversation.isPrivate();
|
||||
|
||||
const attachments = this.get('attachments') || [];
|
||||
const firstAttachment = attachments[0];
|
||||
|
||||
return {
|
||||
text: this.createNonBreakingLastSeparator(this.get('body')),
|
||||
|
@ -500,28 +484,30 @@
|
|||
.filter(attachment => !attachment.error)
|
||||
.map(attachment => this.getPropsForAttachment(attachment)),
|
||||
previews: this.getPropsForPreview(),
|
||||
quote: this.getPropsForQuote(options),
|
||||
quote: this.getPropsForQuote(),
|
||||
authorAvatarPath,
|
||||
isExpired: this.hasExpired,
|
||||
expirationLength,
|
||||
expirationTimestamp,
|
||||
onReply: () => this.trigger('reply', this),
|
||||
onRetrySend: () => this.retrySend(),
|
||||
onShowDetail: () => this.trigger('show-message-detail', this),
|
||||
onDelete: () => this.trigger('delete', this),
|
||||
onClickLinkPreview: url => this.trigger('navigate-to', url),
|
||||
onClickAttachment: attachment =>
|
||||
this.trigger('show-lightbox', {
|
||||
attachment,
|
||||
message: this,
|
||||
}),
|
||||
|
||||
onDownload: isDangerous =>
|
||||
this.trigger('download', {
|
||||
attachment: firstAttachment,
|
||||
message: this,
|
||||
isDangerous,
|
||||
}),
|
||||
replyToMessage: id => this.trigger('reply', id),
|
||||
retrySend: id => this.trigger('retry', id),
|
||||
deleteMessage: id => this.trigger('delete', id),
|
||||
showMessageDetail: id => this.trigger('show-message-detail', id),
|
||||
|
||||
openConversation: conversationId =>
|
||||
this.trigger('open-conversation', conversationId),
|
||||
showContactDetail: contactOptions =>
|
||||
this.trigger('show-contact-detail', contactOptions),
|
||||
|
||||
showVisualAttachment: lightboxOptions =>
|
||||
this.trigger('show-lightbox', lightboxOptions),
|
||||
downloadAttachment: downloadOptions =>
|
||||
this.trigger('download', downloadOptions),
|
||||
|
||||
openLink: url => this.trigger('navigate-to', url),
|
||||
scrollToMessage: scrollOptions =>
|
||||
this.trigger('scroll-to-message', scrollOptions),
|
||||
};
|
||||
},
|
||||
createNonBreakingLastSeparator(text) {
|
||||
|
@ -551,20 +537,6 @@
|
|||
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.
|
||||
await window.checkForSignalAccount(firstNumber);
|
||||
|
||||
this.trigger('show-contact-detail', {
|
||||
contact,
|
||||
hasSignalAccount: window.hasSignalAccount(firstNumber),
|
||||
});
|
||||
};
|
||||
|
||||
// Would be nice to do this before render, on initial load of message
|
||||
if (!window.isSignalAccountCheckComplete(firstNumber)) {
|
||||
|
@ -576,9 +548,9 @@
|
|||
return contactSelector(contact, {
|
||||
regionCode,
|
||||
getAbsoluteAttachmentPath,
|
||||
onSendMessage,
|
||||
onClick,
|
||||
hasSignalAccount: window.hasSignalAccount(firstNumber),
|
||||
signalAccount: window.hasSignalAccount(firstNumber)
|
||||
? firstNumber
|
||||
: null,
|
||||
});
|
||||
},
|
||||
processQuoteAttachment(attachment) {
|
||||
|
@ -610,8 +582,7 @@
|
|||
image: preview.image ? this.getPropsForAttachment(preview.image) : null,
|
||||
}));
|
||||
},
|
||||
getPropsForQuote(options = {}) {
|
||||
const { noClick } = options;
|
||||
getPropsForQuote() {
|
||||
const quote = this.get('quote');
|
||||
if (!quote) {
|
||||
return null;
|
||||
|
@ -620,7 +591,7 @@
|
|||
const { format } = PhoneNumber;
|
||||
const regionCode = storage.get('regionCode');
|
||||
|
||||
const { author, id, referencedMessageNotFound } = quote;
|
||||
const { author, id: sentAt, referencedMessageNotFound } = quote;
|
||||
const contact = author && ConversationController.get(author);
|
||||
const authorColor = contact ? contact.getColor() : 'grey';
|
||||
|
||||
|
@ -630,16 +601,6 @@
|
|||
const authorProfileName = contact ? contact.getProfileName() : null;
|
||||
const authorName = contact ? contact.getName() : null;
|
||||
const isFromMe = contact ? contact.id === this.OUR_NUMBER : false;
|
||||
const onClick = noClick
|
||||
? null
|
||||
: () => {
|
||||
this.trigger('scroll-to-message', {
|
||||
author,
|
||||
id,
|
||||
referencedMessageNotFound,
|
||||
});
|
||||
};
|
||||
|
||||
const firstAttachment = quote.attachments && quote.attachments[0];
|
||||
|
||||
return {
|
||||
|
@ -648,11 +609,12 @@
|
|||
? this.processQuoteAttachment(firstAttachment)
|
||||
: null,
|
||||
isFromMe,
|
||||
sentAt,
|
||||
authorId: author,
|
||||
authorPhoneNumber,
|
||||
authorProfileName,
|
||||
authorName,
|
||||
authorColor,
|
||||
onClick,
|
||||
referencedMessageNotFound,
|
||||
};
|
||||
},
|
||||
|
@ -740,6 +702,7 @@
|
|||
|
||||
return {
|
||||
...this.findAndFormatContact(id),
|
||||
|
||||
status: this.getStatus(id),
|
||||
errors: errorsForContact,
|
||||
isOutgoingKeyError,
|
||||
|
@ -765,8 +728,9 @@
|
|||
sentAt: this.get('sent_at'),
|
||||
receivedAt: this.get('received_at'),
|
||||
message: {
|
||||
...this.getPropsForMessage({ noClick: true }),
|
||||
...this.getPropsForMessage(),
|
||||
disableMenu: true,
|
||||
disableScroll: true,
|
||||
// To ensure that group avatar doesn't show up
|
||||
conversationType: 'direct',
|
||||
},
|
||||
|
|
|
@ -114,6 +114,7 @@
|
|||
'reply',
|
||||
this.setQuoteMessage
|
||||
);
|
||||
this.listenTo(this.model.messageCollection, 'retry', this.retrySend);
|
||||
this.listenTo(
|
||||
this.model.messageCollection,
|
||||
'show-contact-detail',
|
||||
|
@ -705,8 +706,16 @@
|
|||
}
|
||||
},
|
||||
|
||||
async retrySend(messageId) {
|
||||
const message = this.model.messageCollection.get(messageId);
|
||||
if (!message) {
|
||||
throw new Error(`retrySend: Did not find message for id ${messageId}`);
|
||||
}
|
||||
await message.retrySend();
|
||||
},
|
||||
|
||||
async scrollToMessage(options = {}) {
|
||||
const { author, id, referencedMessageNotFound } = options;
|
||||
const { author, sentAt, referencedMessageNotFound } = options;
|
||||
|
||||
// For simplicity's sake, we show the 'not found' toast no matter what if we were
|
||||
// not able to find the referenced message when the quote was received.
|
||||
|
@ -724,7 +733,7 @@
|
|||
if (!messageAuthor || author !== messageAuthor.id) {
|
||||
return false;
|
||||
}
|
||||
if (id !== item.get('sent_at')) {
|
||||
if (sentAt !== item.get('sent_at')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -734,13 +743,16 @@
|
|||
// If there's no message already in memory, we won't be scrolling. So we'll gather
|
||||
// some more information then show an informative toast to the user.
|
||||
if (!targetMessage) {
|
||||
const collection = await window.Signal.Data.getMessagesBySentAt(id, {
|
||||
MessageCollection: Whisper.MessageCollection,
|
||||
});
|
||||
const collection = await window.Signal.Data.getMessagesBySentAt(
|
||||
sentAt,
|
||||
{
|
||||
MessageCollection: Whisper.MessageCollection,
|
||||
}
|
||||
);
|
||||
|
||||
const found = Boolean(
|
||||
collection.find(item => {
|
||||
const messageAuthor = item.getContact();
|
||||
|
||||
return messageAuthor && author === messageAuthor.id;
|
||||
})
|
||||
);
|
||||
|
@ -765,7 +777,7 @@
|
|||
toast.render();
|
||||
|
||||
window.log.info(
|
||||
`Error: had target message ${id} in messageCollection, but it was not in DOM`
|
||||
`Error: had target message ${targetMessage.idForLogging()} in messageCollection, but it was not in DOM`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
@ -1202,23 +1214,25 @@
|
|||
dialog.focusCancel();
|
||||
},
|
||||
|
||||
showSafetyNumber(providedModel) {
|
||||
let model = providedModel;
|
||||
showSafetyNumber(id) {
|
||||
let conversation;
|
||||
|
||||
if (!model && this.model.isPrivate()) {
|
||||
if (!id && this.model.isPrivate()) {
|
||||
// eslint-disable-next-line prefer-destructuring
|
||||
model = this.model;
|
||||
conversation = this.model;
|
||||
} else {
|
||||
conversation = ConversationController.get(id);
|
||||
}
|
||||
if (model) {
|
||||
if (conversation) {
|
||||
const view = new Whisper.KeyVerificationPanelView({
|
||||
model,
|
||||
model: conversation,
|
||||
});
|
||||
this.listenBack(view);
|
||||
this.updateHeader();
|
||||
}
|
||||
},
|
||||
|
||||
downloadAttachment({ attachment, message, isDangerous }) {
|
||||
downloadAttachment({ attachment, timestamp, isDangerous }) {
|
||||
if (isDangerous) {
|
||||
const toast = new Whisper.DangerousFileTypeToast();
|
||||
toast.$el.appendTo(this.$el);
|
||||
|
@ -1230,11 +1244,18 @@
|
|||
attachment,
|
||||
document,
|
||||
getAbsolutePath: getAbsoluteAttachmentPath,
|
||||
timestamp: message.get('sent_at'),
|
||||
timestamp,
|
||||
});
|
||||
},
|
||||
|
||||
deleteMessage(message) {
|
||||
deleteMessage(messageId) {
|
||||
const message = this.model.messageCollection.get(messageId);
|
||||
if (!message) {
|
||||
throw new Error(
|
||||
`deleteMessage: Did not find message for id ${messageId}`
|
||||
);
|
||||
}
|
||||
|
||||
const dialog = new Whisper.ConfirmationDialogView({
|
||||
message: i18n('deleteWarning'),
|
||||
okText: i18n('delete'),
|
||||
|
@ -1253,7 +1274,13 @@
|
|||
dialog.focusCancel();
|
||||
},
|
||||
|
||||
showLightbox({ attachment, message }) {
|
||||
showLightbox({ attachment, messageId }) {
|
||||
const message = this.model.messageCollection.get(messageId);
|
||||
if (!message) {
|
||||
throw new Error(
|
||||
`showLightbox: did not find message for id ${messageId}`
|
||||
);
|
||||
}
|
||||
const { contentType, path } = attachment;
|
||||
|
||||
if (
|
||||
|
@ -1333,7 +1360,14 @@
|
|||
Signal.Backbone.Views.Lightbox.show(this.lightboxGalleryView.el);
|
||||
},
|
||||
|
||||
showMessageDetail(message) {
|
||||
showMessageDetail(messageId) {
|
||||
const message = this.model.messageCollection.get(messageId);
|
||||
if (!message) {
|
||||
throw new Error(
|
||||
`showMessageDetail: Did not find message for id ${messageId}`
|
||||
);
|
||||
}
|
||||
|
||||
const onClose = () => {
|
||||
this.stopListening(message, 'change', update);
|
||||
this.resetPanel();
|
||||
|
@ -1358,24 +1392,16 @@
|
|||
view.render();
|
||||
},
|
||||
|
||||
showContactDetail({ contact, hasSignalAccount }) {
|
||||
const regionCode = storage.get('regionCode');
|
||||
const { contactSelector } = Signal.Types.Contact;
|
||||
|
||||
showContactDetail({ contact, signalAccount }) {
|
||||
const view = new Whisper.ReactWrapperView({
|
||||
Component: Signal.Components.ContactDetail,
|
||||
className: 'contact-detail-pane panel',
|
||||
props: {
|
||||
contact: contactSelector(contact, {
|
||||
regionCode,
|
||||
getAbsoluteAttachmentPath,
|
||||
}),
|
||||
hasSignalAccount,
|
||||
contact,
|
||||
signalAccount,
|
||||
onSendMessage: () => {
|
||||
const number =
|
||||
contact.number && contact.number[0] && contact.number[0].value;
|
||||
if (number) {
|
||||
this.openConversation(number);
|
||||
if (signalAccount) {
|
||||
this.openConversation(signalAccount);
|
||||
}
|
||||
},
|
||||
},
|
||||
|
@ -1592,20 +1618,25 @@
|
|||
this.focusMessageField();
|
||||
},
|
||||
|
||||
async setQuoteMessage(message) {
|
||||
async setQuoteMessage(messageId) {
|
||||
this.quote = null;
|
||||
this.quotedMessage = message;
|
||||
this.quotedMessage = null;
|
||||
|
||||
if (this.quoteHolder) {
|
||||
this.quoteHolder.unload();
|
||||
this.quoteHolder = null;
|
||||
}
|
||||
|
||||
const message = this.model.messageCollection.get(messageId);
|
||||
if (message) {
|
||||
const quote = await this.model.makeQuote(this.quotedMessage);
|
||||
this.quote = quote;
|
||||
this.quotedMessage = message;
|
||||
|
||||
this.focusMessageFieldAndClearDisabled();
|
||||
if (message) {
|
||||
const quote = await this.model.makeQuote(this.quotedMessage);
|
||||
this.quote = quote;
|
||||
|
||||
this.focusMessageFieldAndClearDisabled();
|
||||
}
|
||||
}
|
||||
|
||||
this.renderQuotedMessage();
|
||||
|
|
|
@ -2,7 +2,7 @@ import React from 'react';
|
|||
|
||||
import {
|
||||
AddressType,
|
||||
Contact,
|
||||
ContactFormType,
|
||||
ContactType,
|
||||
Email,
|
||||
Phone,
|
||||
|
@ -19,7 +19,7 @@ import {
|
|||
import { LocalizerType } from '../../types/Util';
|
||||
|
||||
interface Props {
|
||||
contact: Contact;
|
||||
contact: ContactType;
|
||||
hasSignalAccount: boolean;
|
||||
i18n: LocalizerType;
|
||||
onSendMessage: () => void;
|
||||
|
@ -27,13 +27,13 @@ interface Props {
|
|||
|
||||
function getLabelForEmail(method: Email, i18n: LocalizerType): string {
|
||||
switch (method.type) {
|
||||
case ContactType.CUSTOM:
|
||||
case ContactFormType.CUSTOM:
|
||||
return method.label || i18n('email');
|
||||
case ContactType.HOME:
|
||||
case ContactFormType.HOME:
|
||||
return i18n('home');
|
||||
case ContactType.MOBILE:
|
||||
case ContactFormType.MOBILE:
|
||||
return i18n('mobile');
|
||||
case ContactType.WORK:
|
||||
case ContactFormType.WORK:
|
||||
return i18n('work');
|
||||
default:
|
||||
throw missingCaseError(method.type);
|
||||
|
@ -42,13 +42,13 @@ function getLabelForEmail(method: Email, i18n: LocalizerType): string {
|
|||
|
||||
function getLabelForPhone(method: Phone, i18n: LocalizerType): string {
|
||||
switch (method.type) {
|
||||
case ContactType.CUSTOM:
|
||||
case ContactFormType.CUSTOM:
|
||||
return method.label || i18n('phone');
|
||||
case ContactType.HOME:
|
||||
case ContactFormType.HOME:
|
||||
return i18n('home');
|
||||
case ContactType.MOBILE:
|
||||
case ContactFormType.MOBILE:
|
||||
return i18n('mobile');
|
||||
case ContactType.WORK:
|
||||
case ContactFormType.WORK:
|
||||
return i18n('work');
|
||||
default:
|
||||
throw missingCaseError(method.type);
|
||||
|
|
|
@ -20,7 +20,7 @@ const contact = {
|
|||
},
|
||||
onClick: () => console.log('onClick'),
|
||||
onSendMessage: () => console.log('onSendMessage'),
|
||||
hasSignalAccount: true,
|
||||
signalAccount: '+12025550000',
|
||||
};
|
||||
<util.ConversationContext theme={util.theme} ios={util.ios}>
|
||||
<li>
|
||||
|
@ -86,7 +86,7 @@ const contact = {
|
|||
},
|
||||
onClick: () => console.log('onClick'),
|
||||
onSendMessage: () => console.log('onSendMessage'),
|
||||
hasSignalAccount: true,
|
||||
signalAccount: '+12025550000',
|
||||
};
|
||||
<util.ConversationContext theme={util.theme} ios={util.ios}>
|
||||
<li>
|
||||
|
@ -129,7 +129,6 @@ const contact = {
|
|||
path: util.gifObjectUrl,
|
||||
},
|
||||
},
|
||||
hasSignalAccount: true,
|
||||
};
|
||||
<util.ConversationContext theme={util.theme} ios={util.ios}>
|
||||
<li>
|
||||
|
@ -170,7 +169,7 @@ const contact = {
|
|||
path: util.gifObjectUrl,
|
||||
},
|
||||
},
|
||||
hasSignalAccount: true,
|
||||
signalAccount: '+12025550000',
|
||||
};
|
||||
<util.ConversationContext theme={util.theme} type="group" ios={util.ios}>
|
||||
<li>
|
||||
|
@ -230,7 +229,6 @@ const contact = {
|
|||
path: util.gifObjectUrl,
|
||||
},
|
||||
},
|
||||
hasSignalAccount: false,
|
||||
};
|
||||
<util.ConversationContext theme={util.theme} ios={util.ios}>
|
||||
<li>
|
||||
|
@ -292,7 +290,6 @@ const contact = {
|
|||
path: util.gifObjectUrl,
|
||||
},
|
||||
},
|
||||
hasSignalAccount: false,
|
||||
};
|
||||
<util.ConversationContext theme={util.theme} ios={util.ios}>
|
||||
<li>
|
||||
|
@ -356,7 +353,7 @@ const contact = {
|
|||
path: util.gifObjectUrl,
|
||||
},
|
||||
},
|
||||
hasSignalAccount: false,
|
||||
signalAccount: '+12025551000',
|
||||
};
|
||||
<util.ConversationContext theme={util.theme} ios={util.ios}>
|
||||
<li>
|
||||
|
@ -415,7 +412,6 @@ const contact = {
|
|||
type: 1,
|
||||
},
|
||||
],
|
||||
hasSignalAccount: true,
|
||||
};
|
||||
<util.ConversationContext theme={util.theme} ios={util.ios}>
|
||||
<li>
|
||||
|
@ -527,7 +523,7 @@ const contactWithAccount = {
|
|||
path: util.gifObjectUrl,
|
||||
},
|
||||
},
|
||||
hasSignalAccount: true,
|
||||
signalAccount: '+12025550000',
|
||||
};
|
||||
const contactWithoutAccount = {
|
||||
name: {
|
||||
|
@ -544,7 +540,6 @@ const contactWithoutAccount = {
|
|||
path: util.gifObjectUrl,
|
||||
},
|
||||
},
|
||||
hasSignalAccount: false,
|
||||
};
|
||||
<util.ConversationContext theme={util.theme} ios={util.ios}>
|
||||
<li>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { Contact } from '../../types/Contact';
|
||||
import { ContactType } from '../../types/Contact';
|
||||
|
||||
import { LocalizerType } from '../../types/Util';
|
||||
import {
|
||||
|
@ -11,8 +11,7 @@ import {
|
|||
} from './_contactUtil';
|
||||
|
||||
interface Props {
|
||||
contact: Contact;
|
||||
hasSignalAccount: boolean;
|
||||
contact: ContactType;
|
||||
i18n: LocalizerType;
|
||||
isIncoming: boolean;
|
||||
withContentAbove: boolean;
|
||||
|
|
|
@ -24,7 +24,7 @@ interface Props {
|
|||
i18n: LocalizerType;
|
||||
|
||||
onError: () => void;
|
||||
onClickAttachment?: (attachment: AttachmentType) => void;
|
||||
onClick?: (attachment: AttachmentType) => void;
|
||||
}
|
||||
|
||||
export class ImageGrid extends React.Component<Props> {
|
||||
|
@ -35,7 +35,7 @@ export class ImageGrid extends React.Component<Props> {
|
|||
bottomOverlay,
|
||||
i18n,
|
||||
onError,
|
||||
onClickAttachment,
|
||||
onClick,
|
||||
withContentAbove,
|
||||
withContentBelow,
|
||||
} = this.props;
|
||||
|
@ -76,7 +76,7 @@ export class ImageGrid extends React.Component<Props> {
|
|||
height={height}
|
||||
width={width}
|
||||
url={getUrl(attachments[0])}
|
||||
onClick={onClickAttachment}
|
||||
onClick={onClick}
|
||||
onError={onError}
|
||||
/>
|
||||
</div>
|
||||
|
@ -97,7 +97,7 @@ export class ImageGrid extends React.Component<Props> {
|
|||
height={149}
|
||||
width={149}
|
||||
url={getThumbnailUrl(attachments[0])}
|
||||
onClick={onClickAttachment}
|
||||
onClick={onClick}
|
||||
onError={onError}
|
||||
/>
|
||||
<Image
|
||||
|
@ -111,7 +111,7 @@ export class ImageGrid extends React.Component<Props> {
|
|||
width={149}
|
||||
attachment={attachments[1]}
|
||||
url={getThumbnailUrl(attachments[1])}
|
||||
onClick={onClickAttachment}
|
||||
onClick={onClick}
|
||||
onError={onError}
|
||||
/>
|
||||
</div>
|
||||
|
@ -132,7 +132,7 @@ export class ImageGrid extends React.Component<Props> {
|
|||
height={200}
|
||||
width={199}
|
||||
url={getUrl(attachments[0])}
|
||||
onClick={onClickAttachment}
|
||||
onClick={onClick}
|
||||
onError={onError}
|
||||
/>
|
||||
<div className="module-image-grid__column">
|
||||
|
@ -145,7 +145,7 @@ export class ImageGrid extends React.Component<Props> {
|
|||
attachment={attachments[1]}
|
||||
playIconOverlay={isVideoAttachment(attachments[1])}
|
||||
url={getThumbnailUrl(attachments[1])}
|
||||
onClick={onClickAttachment}
|
||||
onClick={onClick}
|
||||
onError={onError}
|
||||
/>
|
||||
<Image
|
||||
|
@ -158,7 +158,7 @@ export class ImageGrid extends React.Component<Props> {
|
|||
attachment={attachments[2]}
|
||||
playIconOverlay={isVideoAttachment(attachments[2])}
|
||||
url={getThumbnailUrl(attachments[2])}
|
||||
onClick={onClickAttachment}
|
||||
onClick={onClick}
|
||||
onError={onError}
|
||||
/>
|
||||
</div>
|
||||
|
@ -180,7 +180,7 @@ export class ImageGrid extends React.Component<Props> {
|
|||
height={149}
|
||||
width={149}
|
||||
url={getThumbnailUrl(attachments[0])}
|
||||
onClick={onClickAttachment}
|
||||
onClick={onClick}
|
||||
onError={onError}
|
||||
/>
|
||||
<Image
|
||||
|
@ -192,7 +192,7 @@ export class ImageGrid extends React.Component<Props> {
|
|||
width={149}
|
||||
attachment={attachments[1]}
|
||||
url={getThumbnailUrl(attachments[1])}
|
||||
onClick={onClickAttachment}
|
||||
onClick={onClick}
|
||||
onError={onError}
|
||||
/>
|
||||
</div>
|
||||
|
@ -207,7 +207,7 @@ export class ImageGrid extends React.Component<Props> {
|
|||
width={149}
|
||||
attachment={attachments[2]}
|
||||
url={getThumbnailUrl(attachments[2])}
|
||||
onClick={onClickAttachment}
|
||||
onClick={onClick}
|
||||
onError={onError}
|
||||
/>
|
||||
<Image
|
||||
|
@ -220,7 +220,7 @@ export class ImageGrid extends React.Component<Props> {
|
|||
width={149}
|
||||
attachment={attachments[3]}
|
||||
url={getThumbnailUrl(attachments[3])}
|
||||
onClick={onClickAttachment}
|
||||
onClick={onClick}
|
||||
onError={onError}
|
||||
/>
|
||||
</div>
|
||||
|
@ -247,7 +247,7 @@ export class ImageGrid extends React.Component<Props> {
|
|||
height={149}
|
||||
width={149}
|
||||
url={getThumbnailUrl(attachments[0])}
|
||||
onClick={onClickAttachment}
|
||||
onClick={onClick}
|
||||
onError={onError}
|
||||
/>
|
||||
<Image
|
||||
|
@ -259,7 +259,7 @@ export class ImageGrid extends React.Component<Props> {
|
|||
width={149}
|
||||
attachment={attachments[1]}
|
||||
url={getThumbnailUrl(attachments[1])}
|
||||
onClick={onClickAttachment}
|
||||
onClick={onClick}
|
||||
onError={onError}
|
||||
/>
|
||||
</div>
|
||||
|
@ -274,7 +274,7 @@ export class ImageGrid extends React.Component<Props> {
|
|||
width={99}
|
||||
attachment={attachments[2]}
|
||||
url={getThumbnailUrl(attachments[2])}
|
||||
onClick={onClickAttachment}
|
||||
onClick={onClick}
|
||||
onError={onError}
|
||||
/>
|
||||
<Image
|
||||
|
@ -286,7 +286,7 @@ export class ImageGrid extends React.Component<Props> {
|
|||
width={98}
|
||||
attachment={attachments[3]}
|
||||
url={getThumbnailUrl(attachments[3])}
|
||||
onClick={onClickAttachment}
|
||||
onClick={onClick}
|
||||
onError={onError}
|
||||
/>
|
||||
<Image
|
||||
|
@ -301,7 +301,7 @@ export class ImageGrid extends React.Component<Props> {
|
|||
overlayText={moreMessagesOverlayText}
|
||||
attachment={attachments[4]}
|
||||
url={getThumbnailUrl(attachments[4])}
|
||||
onClick={onClickAttachment}
|
||||
onClick={onClick}
|
||||
onError={onError}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
@ -25,7 +25,7 @@ import {
|
|||
isVideo,
|
||||
} from '../../../ts/types/Attachment';
|
||||
import { AttachmentType } from '../../types/Attachment';
|
||||
import { Contact } from '../../types/Contact';
|
||||
import { ContactType } from '../../types/Contact';
|
||||
|
||||
import { getIncrement } from '../../util/timer';
|
||||
import { isFileDangerous } from '../../util/isFileDangerous';
|
||||
|
@ -46,22 +46,14 @@ interface LinkPreviewType {
|
|||
image?: AttachmentType;
|
||||
}
|
||||
|
||||
export interface Props {
|
||||
disableMenu?: boolean;
|
||||
type PropsData = {
|
||||
id: string;
|
||||
text?: string;
|
||||
textPending?: boolean;
|
||||
id?: string;
|
||||
collapseMetadata?: boolean;
|
||||
direction: 'incoming' | 'outgoing';
|
||||
timestamp: number;
|
||||
status?: 'sending' | 'sent' | 'delivered' | 'read' | 'error';
|
||||
// What if changed this over to a single contact like quote, and put the events on it?
|
||||
contact?: Contact & {
|
||||
hasSignalAccount: boolean;
|
||||
onSendMessage?: () => void;
|
||||
onClick?: () => void;
|
||||
};
|
||||
i18n: LocalizerType;
|
||||
contact?: ContactType;
|
||||
authorName?: string;
|
||||
authorProfileName?: string;
|
||||
/** Note: this should be formatted for display */
|
||||
|
@ -73,11 +65,12 @@ export interface Props {
|
|||
text: string;
|
||||
attachment?: QuotedAttachmentType;
|
||||
isFromMe: boolean;
|
||||
sentAt: number;
|
||||
authorId: string;
|
||||
authorPhoneNumber: string;
|
||||
authorProfileName?: string;
|
||||
authorName?: string;
|
||||
authorColor?: ColorType;
|
||||
onClick?: () => void;
|
||||
referencedMessageNotFound: boolean;
|
||||
};
|
||||
previews: Array<LinkPreviewType>;
|
||||
|
@ -85,14 +78,48 @@ export interface Props {
|
|||
isExpired: boolean;
|
||||
expirationLength?: number;
|
||||
expirationTimestamp?: number;
|
||||
onClickAttachment?: (attachment: AttachmentType) => void;
|
||||
onClickLinkPreview?: (url: string) => void;
|
||||
onReply?: () => void;
|
||||
onRetrySend?: () => void;
|
||||
onDownload?: (isDangerous: boolean) => void;
|
||||
onDelete?: () => void;
|
||||
onShowDetail: () => void;
|
||||
}
|
||||
};
|
||||
|
||||
type PropsHousekeeping = {
|
||||
i18n: LocalizerType;
|
||||
disableMenu?: boolean;
|
||||
disableScroll?: boolean;
|
||||
collapseMetadata?: boolean;
|
||||
};
|
||||
|
||||
export type PropsActions = {
|
||||
replyToMessage: (id: string) => void;
|
||||
retrySend: (id: string) => void;
|
||||
deleteMessage: (id: string) => void;
|
||||
showMessageDetail: (id: string) => void;
|
||||
|
||||
openConversation: (conversationId: string, messageId?: string) => void;
|
||||
showContactDetail: (
|
||||
options: { contact: ContactType; signalAccount?: string }
|
||||
) => void;
|
||||
|
||||
showVisualAttachment: (
|
||||
options: { attachment: AttachmentType; messageId: string }
|
||||
) => void;
|
||||
downloadAttachment: (
|
||||
options: {
|
||||
attachment: AttachmentType;
|
||||
timestamp: number;
|
||||
isDangerous: boolean;
|
||||
}
|
||||
) => void;
|
||||
|
||||
openLink: (url: string) => void;
|
||||
scrollToMessage: (
|
||||
options: {
|
||||
author: string;
|
||||
sentAt: number;
|
||||
referencedMessageNotFound: boolean;
|
||||
}
|
||||
) => void;
|
||||
};
|
||||
|
||||
export type Props = PropsData & PropsHousekeeping & PropsActions;
|
||||
|
||||
interface State {
|
||||
expiring: boolean;
|
||||
|
@ -301,6 +328,7 @@ export class Message extends React.PureComponent<Props, State> {
|
|||
// tslint:disable-next-line max-func-body-length cyclomatic-complexity
|
||||
public renderAttachment() {
|
||||
const {
|
||||
id,
|
||||
attachments,
|
||||
text,
|
||||
collapseMetadata,
|
||||
|
@ -308,7 +336,7 @@ export class Message extends React.PureComponent<Props, State> {
|
|||
direction,
|
||||
i18n,
|
||||
quote,
|
||||
onClickAttachment,
|
||||
showVisualAttachment,
|
||||
} = this.props;
|
||||
const { imageBroken } = this.state;
|
||||
|
||||
|
@ -349,7 +377,9 @@ export class Message extends React.PureComponent<Props, State> {
|
|||
bottomOverlay={!collapseMetadata}
|
||||
i18n={i18n}
|
||||
onError={this.handleImageErrorBound}
|
||||
onClickAttachment={onClickAttachment}
|
||||
onClick={attachment => {
|
||||
showVisualAttachment({ attachment, messageId: id });
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
@ -438,7 +468,7 @@ export class Message extends React.PureComponent<Props, State> {
|
|||
conversationType,
|
||||
direction,
|
||||
i18n,
|
||||
onClickLinkPreview,
|
||||
openLink,
|
||||
previews,
|
||||
quote,
|
||||
} = this.props;
|
||||
|
@ -475,9 +505,7 @@ export class Message extends React.PureComponent<Props, State> {
|
|||
: null
|
||||
)}
|
||||
onClick={() => {
|
||||
if (onClickLinkPreview) {
|
||||
onClickLinkPreview(first.url);
|
||||
}
|
||||
openLink(first.url);
|
||||
}}
|
||||
>
|
||||
{first.image && previewHasImage && isFullSizeImage ? (
|
||||
|
@ -537,8 +565,10 @@ export class Message extends React.PureComponent<Props, State> {
|
|||
conversationType,
|
||||
authorColor,
|
||||
direction,
|
||||
disableScroll,
|
||||
i18n,
|
||||
quote,
|
||||
scrollToMessage,
|
||||
} = this.props;
|
||||
|
||||
if (!quote) {
|
||||
|
@ -550,10 +580,21 @@ export class Message extends React.PureComponent<Props, State> {
|
|||
const quoteColor =
|
||||
direction === 'incoming' ? authorColor : quote.authorColor;
|
||||
|
||||
const { referencedMessageNotFound } = quote;
|
||||
const clickHandler = disableScroll
|
||||
? undefined
|
||||
: () => {
|
||||
scrollToMessage({
|
||||
author: quote.authorId,
|
||||
sentAt: quote.sentAt,
|
||||
referencedMessageNotFound,
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Quote
|
||||
i18n={i18n}
|
||||
onClick={quote.onClick}
|
||||
onClick={clickHandler}
|
||||
text={quote.text}
|
||||
attachment={quote.attachment}
|
||||
isIncoming={direction === 'incoming'}
|
||||
|
@ -561,7 +602,7 @@ export class Message extends React.PureComponent<Props, State> {
|
|||
authorProfileName={quote.authorProfileName}
|
||||
authorName={quote.authorName}
|
||||
authorColor={quoteColor}
|
||||
referencedMessageNotFound={quote.referencedMessageNotFound}
|
||||
referencedMessageNotFound={referencedMessageNotFound}
|
||||
isFromMe={quote.isFromMe}
|
||||
withContentAbove={withContentAbove}
|
||||
/>
|
||||
|
@ -575,6 +616,7 @@ export class Message extends React.PureComponent<Props, State> {
|
|||
conversationType,
|
||||
direction,
|
||||
i18n,
|
||||
showContactDetail,
|
||||
text,
|
||||
} = this.props;
|
||||
if (!contact) {
|
||||
|
@ -589,10 +631,11 @@ export class Message extends React.PureComponent<Props, State> {
|
|||
return (
|
||||
<EmbeddedContact
|
||||
contact={contact}
|
||||
hasSignalAccount={contact.hasSignalAccount}
|
||||
isIncoming={direction === 'incoming'}
|
||||
i18n={i18n}
|
||||
onClick={contact.onClick}
|
||||
onClick={() => {
|
||||
showContactDetail({ contact, signalAccount: contact.signalAccount });
|
||||
}}
|
||||
withContentAbove={withContentAbove}
|
||||
withContentBelow={withContentBelow}
|
||||
/>
|
||||
|
@ -600,15 +643,19 @@ export class Message extends React.PureComponent<Props, State> {
|
|||
}
|
||||
|
||||
public renderSendMessageButton() {
|
||||
const { contact, i18n } = this.props;
|
||||
if (!contact || !contact.hasSignalAccount) {
|
||||
const { contact, openConversation, i18n } = this.props;
|
||||
if (!contact || !contact.signalAccount) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
role="button"
|
||||
onClick={contact.onSendMessage}
|
||||
onClick={() => {
|
||||
if (contact.signalAccount) {
|
||||
openConversation(contact.signalAccount);
|
||||
}
|
||||
}}
|
||||
className="module-message__send-message-button"
|
||||
>
|
||||
{i18n('sendMessageToContact')}
|
||||
|
@ -718,8 +765,10 @@ export class Message extends React.PureComponent<Props, State> {
|
|||
attachments,
|
||||
direction,
|
||||
disableMenu,
|
||||
onDownload,
|
||||
onReply,
|
||||
downloadAttachment,
|
||||
id,
|
||||
replyToMessage,
|
||||
timestamp,
|
||||
} = this.props;
|
||||
|
||||
if (!isCorrectSide || disableMenu) {
|
||||
|
@ -736,9 +785,11 @@ export class Message extends React.PureComponent<Props, State> {
|
|||
!multipleAttachments && firstAttachment && !firstAttachment.pending ? (
|
||||
<div
|
||||
onClick={() => {
|
||||
if (onDownload) {
|
||||
onDownload(isDangerous);
|
||||
}
|
||||
downloadAttachment({
|
||||
isDangerous,
|
||||
attachment: firstAttachment,
|
||||
timestamp,
|
||||
});
|
||||
}}
|
||||
role="button"
|
||||
className={classNames(
|
||||
|
@ -750,7 +801,9 @@ export class Message extends React.PureComponent<Props, State> {
|
|||
|
||||
const replyButton = (
|
||||
<div
|
||||
onClick={onReply}
|
||||
onClick={() => {
|
||||
replyToMessage(id);
|
||||
}}
|
||||
role="button"
|
||||
className={classNames(
|
||||
'module-message__buttons__reply',
|
||||
|
@ -793,13 +846,15 @@ export class Message extends React.PureComponent<Props, State> {
|
|||
const {
|
||||
attachments,
|
||||
direction,
|
||||
status,
|
||||
onDelete,
|
||||
onDownload,
|
||||
onReply,
|
||||
onRetrySend,
|
||||
onShowDetail,
|
||||
downloadAttachment,
|
||||
i18n,
|
||||
id,
|
||||
deleteMessage,
|
||||
showMessageDetail,
|
||||
replyToMessage,
|
||||
retrySend,
|
||||
status,
|
||||
timestamp,
|
||||
} = this.props;
|
||||
|
||||
const showRetry = status === 'error' && direction === 'outgoing';
|
||||
|
@ -816,9 +871,11 @@ export class Message extends React.PureComponent<Props, State> {
|
|||
className: 'module-message__context__download',
|
||||
}}
|
||||
onClick={() => {
|
||||
if (onDownload) {
|
||||
onDownload(isDangerous);
|
||||
}
|
||||
downloadAttachment({
|
||||
attachment: attachments[0],
|
||||
timestamp,
|
||||
isDangerous,
|
||||
});
|
||||
}}
|
||||
>
|
||||
{i18n('downloadAttachment')}
|
||||
|
@ -828,7 +885,9 @@ export class Message extends React.PureComponent<Props, State> {
|
|||
attributes={{
|
||||
className: 'module-message__context__reply',
|
||||
}}
|
||||
onClick={onReply}
|
||||
onClick={() => {
|
||||
replyToMessage(id);
|
||||
}}
|
||||
>
|
||||
{i18n('replyToMessage')}
|
||||
</MenuItem>
|
||||
|
@ -836,7 +895,9 @@ export class Message extends React.PureComponent<Props, State> {
|
|||
attributes={{
|
||||
className: 'module-message__context__more-info',
|
||||
}}
|
||||
onClick={onShowDetail}
|
||||
onClick={() => {
|
||||
showMessageDetail(id);
|
||||
}}
|
||||
>
|
||||
{i18n('moreInfo')}
|
||||
</MenuItem>
|
||||
|
@ -845,7 +906,9 @@ export class Message extends React.PureComponent<Props, State> {
|
|||
attributes={{
|
||||
className: 'module-message__context__retry-send',
|
||||
}}
|
||||
onClick={onRetrySend}
|
||||
onClick={() => {
|
||||
retrySend(id);
|
||||
}}
|
||||
>
|
||||
{i18n('retrySend')}
|
||||
</MenuItem>
|
||||
|
@ -854,7 +917,9 @@ export class Message extends React.PureComponent<Props, State> {
|
|||
attributes={{
|
||||
className: 'module-message__context__delete-message',
|
||||
}}
|
||||
onClick={onDelete}
|
||||
onClick={() => {
|
||||
deleteMessage(id);
|
||||
}}
|
||||
>
|
||||
{i18n('deleteMessage')}
|
||||
</MenuItem>
|
||||
|
|
|
@ -59,7 +59,9 @@ export class MessageDetail extends React.Component<Props> {
|
|||
return (
|
||||
<div className="module-message-detail__delete-button-container">
|
||||
<button
|
||||
onClick={message.onDelete}
|
||||
onClick={() => {
|
||||
message.deleteMessage(message.id);
|
||||
}}
|
||||
className="module-message-detail__delete-button"
|
||||
>
|
||||
{i18n('deleteThisMessage')}
|
||||
|
|
|
@ -5,22 +5,31 @@ import { ContactName } from './ContactName';
|
|||
import { Intl } from '../Intl';
|
||||
import { LocalizerType } from '../../types/Util';
|
||||
|
||||
interface Contact {
|
||||
interface ContactType {
|
||||
id: string;
|
||||
phoneNumber: string;
|
||||
profileName?: string;
|
||||
name?: string;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
type PropsData = {
|
||||
isGroup: boolean;
|
||||
contact: Contact;
|
||||
contact: ContactType;
|
||||
};
|
||||
|
||||
type PropsHousekeeping = {
|
||||
i18n: LocalizerType;
|
||||
onVerify: () => void;
|
||||
}
|
||||
};
|
||||
|
||||
export type PropsActions = {
|
||||
showIdentity: (id: string) => void;
|
||||
};
|
||||
|
||||
type Props = PropsData & PropsHousekeeping & PropsActions;
|
||||
|
||||
export class SafetyNumberNotification extends React.Component<Props> {
|
||||
public render() {
|
||||
const { contact, isGroup, i18n, onVerify } = this.props;
|
||||
const { contact, isGroup, i18n, showIdentity } = this.props;
|
||||
const changeKey = isGroup
|
||||
? 'safetyNumberChangedGroup'
|
||||
: 'safetyNumberChanged';
|
||||
|
@ -50,7 +59,9 @@ export class SafetyNumberNotification extends React.Component<Props> {
|
|||
</div>
|
||||
<div
|
||||
role="button"
|
||||
onClick={onVerify}
|
||||
onClick={() => {
|
||||
showIdentity(contact.id);
|
||||
}}
|
||||
className="module-verification-notification__button"
|
||||
>
|
||||
{i18n('verifyNewNumber')}
|
||||
|
|
|
@ -5,7 +5,7 @@ import { Avatar } from '../Avatar';
|
|||
import { Spinner } from '../Spinner';
|
||||
|
||||
import { LocalizerType } from '../../types/Util';
|
||||
import { Contact, getName } from '../../types/Contact';
|
||||
import { ContactType, getName } from '../../types/Contact';
|
||||
|
||||
// This file starts with _ to keep it from showing up in the StyleGuide.
|
||||
|
||||
|
@ -15,7 +15,7 @@ export function renderAvatar({
|
|||
size,
|
||||
direction,
|
||||
}: {
|
||||
contact: Contact;
|
||||
contact: ContactType;
|
||||
i18n: LocalizerType;
|
||||
size: number;
|
||||
direction?: string;
|
||||
|
@ -52,7 +52,7 @@ export function renderName({
|
|||
isIncoming,
|
||||
module,
|
||||
}: {
|
||||
contact: Contact;
|
||||
contact: ContactType;
|
||||
isIncoming: boolean;
|
||||
module: string;
|
||||
}) {
|
||||
|
@ -73,7 +73,7 @@ export function renderContactShorthand({
|
|||
isIncoming,
|
||||
module,
|
||||
}: {
|
||||
contact: Contact;
|
||||
contact: ContactType;
|
||||
isIncoming: boolean;
|
||||
module: string;
|
||||
}) {
|
||||
|
|
|
@ -63,10 +63,8 @@ describe('Contact', () => {
|
|||
});
|
||||
describe('contactSelector', () => {
|
||||
const regionCode = '1';
|
||||
const hasSignalAccount = true;
|
||||
const signalAccount = '+1202555000';
|
||||
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 = {
|
||||
|
@ -91,17 +89,13 @@ describe('Contact', () => {
|
|||
},
|
||||
organization: 'Somewhere, Inc.',
|
||||
avatar: undefined,
|
||||
hasSignalAccount,
|
||||
onSendMessage,
|
||||
onClick,
|
||||
signalAccount,
|
||||
number: undefined,
|
||||
};
|
||||
const actual = contactSelector(contact, {
|
||||
regionCode,
|
||||
hasSignalAccount,
|
||||
signalAccount,
|
||||
getAbsoluteAttachmentPath,
|
||||
onSendMessage,
|
||||
onClick,
|
||||
});
|
||||
assert.deepEqual(actual, expected);
|
||||
});
|
||||
|
@ -135,17 +129,13 @@ describe('Contact', () => {
|
|||
path: undefined,
|
||||
},
|
||||
},
|
||||
hasSignalAccount,
|
||||
onSendMessage,
|
||||
onClick,
|
||||
signalAccount,
|
||||
number: undefined,
|
||||
};
|
||||
const actual = contactSelector(contact, {
|
||||
regionCode,
|
||||
hasSignalAccount,
|
||||
signalAccount,
|
||||
getAbsoluteAttachmentPath,
|
||||
onSendMessage,
|
||||
onClick,
|
||||
});
|
||||
assert.deepEqual(actual, expected);
|
||||
});
|
||||
|
@ -178,17 +168,13 @@ describe('Contact', () => {
|
|||
path: 'absolute:somewhere',
|
||||
},
|
||||
},
|
||||
hasSignalAccount,
|
||||
onSendMessage,
|
||||
onClick,
|
||||
signalAccount,
|
||||
number: undefined,
|
||||
};
|
||||
const actual = contactSelector(contact, {
|
||||
regionCode,
|
||||
hasSignalAccount,
|
||||
signalAccount,
|
||||
getAbsoluteAttachmentPath,
|
||||
onSendMessage,
|
||||
onClick,
|
||||
});
|
||||
assert.deepEqual(actual, expected);
|
||||
});
|
||||
|
|
|
@ -2,13 +2,14 @@
|
|||
import Attachments from '../../app/attachments';
|
||||
import { format as formatPhoneNumber } from '../types/PhoneNumber';
|
||||
|
||||
export interface Contact {
|
||||
export interface ContactType {
|
||||
name?: Name;
|
||||
number?: Array<Phone>;
|
||||
email?: Array<Email>;
|
||||
address?: Array<PostalAddress>;
|
||||
avatar?: Avatar;
|
||||
organization?: string;
|
||||
signalAccount?: string;
|
||||
}
|
||||
|
||||
interface Name {
|
||||
|
@ -20,7 +21,7 @@ interface Name {
|
|||
displayName?: string;
|
||||
}
|
||||
|
||||
export enum ContactType {
|
||||
export enum ContactFormType {
|
||||
HOME = 1,
|
||||
MOBILE = 2,
|
||||
WORK = 3,
|
||||
|
@ -35,13 +36,13 @@ export enum AddressType {
|
|||
|
||||
export interface Phone {
|
||||
value: string;
|
||||
type: ContactType;
|
||||
type: ContactFormType;
|
||||
label?: string;
|
||||
}
|
||||
|
||||
export interface Email {
|
||||
value: string;
|
||||
type: ContactType;
|
||||
type: ContactFormType;
|
||||
label?: string;
|
||||
}
|
||||
|
||||
|
@ -69,22 +70,14 @@ interface Attachment {
|
|||
}
|
||||
|
||||
export function contactSelector(
|
||||
contact: Contact,
|
||||
contact: ContactType,
|
||||
options: {
|
||||
regionCode: string;
|
||||
hasSignalAccount: boolean;
|
||||
signalAccount?: string;
|
||||
getAbsoluteAttachmentPath: (path: string) => string;
|
||||
onSendMessage: () => void;
|
||||
onClick: () => void;
|
||||
}
|
||||
) {
|
||||
const {
|
||||
getAbsoluteAttachmentPath,
|
||||
hasSignalAccount,
|
||||
onClick,
|
||||
onSendMessage,
|
||||
regionCode,
|
||||
} = options;
|
||||
const { getAbsoluteAttachmentPath, signalAccount, regionCode } = options;
|
||||
|
||||
let { avatar } = contact;
|
||||
if (avatar && avatar.avatar) {
|
||||
|
@ -105,9 +98,7 @@ export function contactSelector(
|
|||
|
||||
return {
|
||||
...contact,
|
||||
hasSignalAccount,
|
||||
onSendMessage,
|
||||
onClick,
|
||||
signalAccount,
|
||||
avatar,
|
||||
number:
|
||||
contact.number &&
|
||||
|
@ -120,7 +111,7 @@ export function contactSelector(
|
|||
};
|
||||
}
|
||||
|
||||
export function getName(contact: Contact): string | undefined {
|
||||
export function getName(contact: ContactType): string | undefined {
|
||||
const { name, organization } = contact;
|
||||
const displayName = (name && name.displayName) || undefined;
|
||||
const givenName = (name && name.givenName) || undefined;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { Attachment } from './Attachment';
|
||||
import { Contact } from './Contact';
|
||||
import { ContactType } from './Contact';
|
||||
import { IndexableBoolean, IndexablePresence } from './IndexedDB';
|
||||
|
||||
export type Message = UserMessage | VerifiedChangeMessage;
|
||||
|
@ -87,7 +87,7 @@ type MessageSchemaVersion5 = Partial<
|
|||
|
||||
type MessageSchemaVersion6 = Partial<
|
||||
Readonly<{
|
||||
contact: Array<Contact>;
|
||||
contact: Array<ContactType>;
|
||||
}>
|
||||
>;
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue