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