Restore ability to message someone from embedded contact
This commit is contained in:
parent
f77175f6b3
commit
302604f67e
18 changed files with 311 additions and 236 deletions
|
@ -1207,15 +1207,15 @@
|
||||||
@include font-body-2-bold;
|
@include font-body-2-bold;
|
||||||
|
|
||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
margin-bottom: -10px;
|
margin-bottom: -8px;
|
||||||
margin-left: -12px;
|
margin-left: -12px;
|
||||||
margin-right: -12px;
|
margin-right: -12px;
|
||||||
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
|
|
||||||
border-bottom-left-radius: 16px;
|
border-bottom-left-radius: 18px;
|
||||||
border-bottom-right-radius: 16px;
|
border-bottom-right-radius: 18px;
|
||||||
|
|
||||||
@include light-theme {
|
@include light-theme {
|
||||||
color: $color-ultramarine;
|
color: $color-ultramarine;
|
||||||
|
@ -1235,6 +1235,13 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.module-message__send-message-button--no-bottom-left-curve {
|
||||||
|
border-bottom-left-radius: 4px;
|
||||||
|
}
|
||||||
|
.module-message__send-message-button--no-bottom-right-curve {
|
||||||
|
border-bottom-right-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
.module-message__author-avatar-container {
|
.module-message__author-avatar-container {
|
||||||
align-items: flex-end;
|
align-items: flex-end;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
|
@ -1,179 +0,0 @@
|
||||||
// Copyright 2020 Signal Messenger, LLC
|
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
|
||||||
|
|
||||||
import * as React from 'react';
|
|
||||||
|
|
||||||
import { action } from '@storybook/addon-actions';
|
|
||||||
import { boolean, number } from '@storybook/addon-knobs';
|
|
||||||
import { storiesOf } from '@storybook/react';
|
|
||||||
|
|
||||||
import type { Props } from './EmbeddedContact';
|
|
||||||
import { EmbeddedContact } from './EmbeddedContact';
|
|
||||||
import { setupI18n } from '../../util/setupI18n';
|
|
||||||
import enMessages from '../../../_locales/en/messages.json';
|
|
||||||
import { ContactFormType } from '../../types/EmbeddedContact';
|
|
||||||
import { IMAGE_GIF } from '../../types/MIME';
|
|
||||||
|
|
||||||
import { fakeAttachment } from '../../test-both/helpers/fakeAttachment';
|
|
||||||
|
|
||||||
const i18n = setupI18n('en', enMessages);
|
|
||||||
|
|
||||||
const story = storiesOf('Components/Conversation/EmbeddedContact', module);
|
|
||||||
|
|
||||||
const createProps = (overrideProps: Partial<Props> = {}): Props => ({
|
|
||||||
contact: overrideProps.contact || {},
|
|
||||||
i18n,
|
|
||||||
isIncoming: boolean('isIncoming', overrideProps.isIncoming || false),
|
|
||||||
onClick: action('onClick'),
|
|
||||||
tabIndex: number('tabIndex', overrideProps.tabIndex || 0),
|
|
||||||
withContentAbove: boolean(
|
|
||||||
'withContentAbove',
|
|
||||||
overrideProps.withContentAbove || false
|
|
||||||
),
|
|
||||||
withContentBelow: boolean(
|
|
||||||
'withContentBelow',
|
|
||||||
overrideProps.withContentBelow || false
|
|
||||||
),
|
|
||||||
});
|
|
||||||
|
|
||||||
const fullContact = {
|
|
||||||
avatar: {
|
|
||||||
avatar: fakeAttachment({
|
|
||||||
path: '/fixtures/giphy-GVNvOUpeYmI7e.gif',
|
|
||||||
contentType: IMAGE_GIF,
|
|
||||||
}),
|
|
||||||
isProfile: true,
|
|
||||||
},
|
|
||||||
email: [
|
|
||||||
{
|
|
||||||
value: 'jerjor@fakemail.com',
|
|
||||||
type: ContactFormType.HOME,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
name: {
|
|
||||||
givenName: 'Jerry',
|
|
||||||
familyName: 'Jordan',
|
|
||||||
prefix: 'Dr.',
|
|
||||||
suffix: 'Jr.',
|
|
||||||
middleName: 'James',
|
|
||||||
displayName: 'Jerry Jordan',
|
|
||||||
},
|
|
||||||
number: [
|
|
||||||
{
|
|
||||||
value: '555-444-2323',
|
|
||||||
type: ContactFormType.HOME,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
story.add('Full Contact', () => {
|
|
||||||
const props = createProps({
|
|
||||||
contact: fullContact,
|
|
||||||
});
|
|
||||||
return <EmbeddedContact {...props} />;
|
|
||||||
});
|
|
||||||
|
|
||||||
story.add('Only Email', () => {
|
|
||||||
const props = createProps({
|
|
||||||
contact: {
|
|
||||||
email: fullContact.email,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return <EmbeddedContact {...props} />;
|
|
||||||
});
|
|
||||||
|
|
||||||
story.add('Given Name', () => {
|
|
||||||
const props = createProps({
|
|
||||||
contact: {
|
|
||||||
name: {
|
|
||||||
givenName: 'Jerry',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return <EmbeddedContact {...props} />;
|
|
||||||
});
|
|
||||||
|
|
||||||
story.add('Organization', () => {
|
|
||||||
const props = createProps({
|
|
||||||
contact: {
|
|
||||||
organization: 'Company 5',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return <EmbeddedContact {...props} />;
|
|
||||||
});
|
|
||||||
|
|
||||||
story.add('Given + Family Name', () => {
|
|
||||||
const props = createProps({
|
|
||||||
contact: {
|
|
||||||
name: {
|
|
||||||
givenName: 'Jerry',
|
|
||||||
familyName: 'FamilyName',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return <EmbeddedContact {...props} />;
|
|
||||||
});
|
|
||||||
|
|
||||||
story.add('Family Name', () => {
|
|
||||||
const props = createProps({
|
|
||||||
contact: {
|
|
||||||
name: {
|
|
||||||
familyName: 'FamilyName',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return <EmbeddedContact {...props} />;
|
|
||||||
});
|
|
||||||
|
|
||||||
story.add('Loading Avatar', () => {
|
|
||||||
const props = createProps({
|
|
||||||
contact: {
|
|
||||||
name: {
|
|
||||||
displayName: 'Jerry Jordan',
|
|
||||||
},
|
|
||||||
avatar: {
|
|
||||||
avatar: fakeAttachment({
|
|
||||||
pending: true,
|
|
||||||
contentType: IMAGE_GIF,
|
|
||||||
}),
|
|
||||||
isProfile: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
return <EmbeddedContact {...props} />;
|
|
||||||
});
|
|
||||||
|
|
||||||
story.add('Incoming', () => {
|
|
||||||
const props = createProps({
|
|
||||||
contact: {
|
|
||||||
name: fullContact.name,
|
|
||||||
},
|
|
||||||
isIncoming: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Wrapped in a <div> to provide a background for light color of text
|
|
||||||
return (
|
|
||||||
<div style={{ backgroundColor: 'darkgreen' }}>
|
|
||||||
<EmbeddedContact {...props} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
story.add('Content Above and Below', () => {
|
|
||||||
const props = createProps({
|
|
||||||
withContentAbove: true,
|
|
||||||
withContentBelow: true,
|
|
||||||
});
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div>Content Above</div>
|
|
||||||
<EmbeddedContact {...props} />
|
|
||||||
<div>Content Below</div>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
});
|
|
|
@ -20,6 +20,7 @@ import {
|
||||||
IMAGE_WEBP,
|
IMAGE_WEBP,
|
||||||
VIDEO_MP4,
|
VIDEO_MP4,
|
||||||
stringToMIMEType,
|
stringToMIMEType,
|
||||||
|
IMAGE_GIF,
|
||||||
} from '../../types/MIME';
|
} from '../../types/MIME';
|
||||||
import { ReadStatus } from '../../messages/MessageReadStatus';
|
import { ReadStatus } from '../../messages/MessageReadStatus';
|
||||||
import { MessageAudio } from './MessageAudio';
|
import { MessageAudio } from './MessageAudio';
|
||||||
|
@ -30,6 +31,7 @@ import { pngUrl } from '../../storybook/Fixtures';
|
||||||
import { getDefaultConversation } from '../../test-both/helpers/getDefaultConversation';
|
import { getDefaultConversation } from '../../test-both/helpers/getDefaultConversation';
|
||||||
import { WidthBreakpoint } from '../_util';
|
import { WidthBreakpoint } from '../_util';
|
||||||
import { MINUTE } from '../../util/durations';
|
import { MINUTE } from '../../util/durations';
|
||||||
|
import { ContactFormType } from '../../types/EmbeddedContact';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
fakeAttachment,
|
fakeAttachment,
|
||||||
|
@ -37,6 +39,7 @@ import {
|
||||||
} from '../../test-both/helpers/fakeAttachment';
|
} from '../../test-both/helpers/fakeAttachment';
|
||||||
import { getFakeBadge } from '../../test-both/helpers/getFakeBadge';
|
import { getFakeBadge } from '../../test-both/helpers/getFakeBadge';
|
||||||
import { ThemeType } from '../../types/Util';
|
import { ThemeType } from '../../types/Util';
|
||||||
|
import { UUID } from '../../types/UUID';
|
||||||
|
|
||||||
const i18n = setupI18n('en', enMessages);
|
const i18n = setupI18n('en', enMessages);
|
||||||
|
|
||||||
|
@ -118,6 +121,7 @@ const createProps = (overrideProps: Partial<Props> = {}): Props => ({
|
||||||
select('conversationColor', ConversationColors, ConversationColors[0]),
|
select('conversationColor', ConversationColors, ConversationColors[0]),
|
||||||
conversationId: text('conversationId', overrideProps.conversationId || ''),
|
conversationId: text('conversationId', overrideProps.conversationId || ''),
|
||||||
conversationType: overrideProps.conversationType || 'direct',
|
conversationType: overrideProps.conversationType || 'direct',
|
||||||
|
contact: overrideProps.contact,
|
||||||
deletedForEveryone: overrideProps.deletedForEveryone,
|
deletedForEveryone: overrideProps.deletedForEveryone,
|
||||||
deleteMessage: action('deleteMessage'),
|
deleteMessage: action('deleteMessage'),
|
||||||
deleteMessageForEveryone: action('deleteMessageForEveryone'),
|
deleteMessageForEveryone: action('deleteMessageForEveryone'),
|
||||||
|
@ -191,6 +195,7 @@ const createProps = (overrideProps: Partial<Props> = {}): Props => ({
|
||||||
showForwardMessageModal: action('showForwardMessageModal'),
|
showForwardMessageModal: action('showForwardMessageModal'),
|
||||||
showMessageDetail: action('showMessageDetail'),
|
showMessageDetail: action('showMessageDetail'),
|
||||||
showVisualAttachment: action('showVisualAttachment'),
|
showVisualAttachment: action('showVisualAttachment'),
|
||||||
|
startConversation: action('startConversation'),
|
||||||
status: overrideProps.status || 'sent',
|
status: overrideProps.status || 'sent',
|
||||||
text: overrideProps.text || text('text', ''),
|
text: overrideProps.text || text('text', ''),
|
||||||
textDirection: overrideProps.textDirection || TextDirection.Default,
|
textDirection: overrideProps.textDirection || TextDirection.Default,
|
||||||
|
@ -1516,3 +1521,139 @@ story.add('Story reply', () => {
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const fullContact = {
|
||||||
|
avatar: {
|
||||||
|
avatar: fakeAttachment({
|
||||||
|
path: '/fixtures/giphy-GVNvOUpeYmI7e.gif',
|
||||||
|
contentType: IMAGE_GIF,
|
||||||
|
}),
|
||||||
|
isProfile: true,
|
||||||
|
},
|
||||||
|
email: [
|
||||||
|
{
|
||||||
|
value: 'jerjor@fakemail.com',
|
||||||
|
type: ContactFormType.HOME,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
name: {
|
||||||
|
givenName: 'Jerry',
|
||||||
|
familyName: 'Jordan',
|
||||||
|
prefix: 'Dr.',
|
||||||
|
suffix: 'Jr.',
|
||||||
|
middleName: 'James',
|
||||||
|
displayName: 'Jerry Jordan',
|
||||||
|
},
|
||||||
|
number: [
|
||||||
|
{
|
||||||
|
value: '555-444-2323',
|
||||||
|
type: ContactFormType.HOME,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
story.add('EmbeddedContact: Full Contact', () => {
|
||||||
|
const props = createProps({
|
||||||
|
contact: fullContact,
|
||||||
|
});
|
||||||
|
return renderBothDirections(props);
|
||||||
|
});
|
||||||
|
|
||||||
|
story.add('EmbeddedContact: 2x Incoming, with Send Message', () => {
|
||||||
|
const props = createProps({
|
||||||
|
contact: {
|
||||||
|
...fullContact,
|
||||||
|
firstNumber: fullContact.number[0].value,
|
||||||
|
uuid: UUID.generate().toString(),
|
||||||
|
},
|
||||||
|
direction: 'incoming',
|
||||||
|
});
|
||||||
|
return renderMany([props, props]);
|
||||||
|
});
|
||||||
|
|
||||||
|
story.add('EmbeddedContact: 2x Outgoing, with Send Message', () => {
|
||||||
|
const props = createProps({
|
||||||
|
contact: {
|
||||||
|
...fullContact,
|
||||||
|
firstNumber: fullContact.number[0].value,
|
||||||
|
uuid: UUID.generate().toString(),
|
||||||
|
},
|
||||||
|
direction: 'outgoing',
|
||||||
|
});
|
||||||
|
return renderMany([props, props]);
|
||||||
|
});
|
||||||
|
|
||||||
|
story.add('EmbeddedContact: Only Email', () => {
|
||||||
|
const props = createProps({
|
||||||
|
contact: {
|
||||||
|
email: fullContact.email,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return renderBothDirections(props);
|
||||||
|
});
|
||||||
|
|
||||||
|
story.add('EmbeddedContact: Given Name', () => {
|
||||||
|
const props = createProps({
|
||||||
|
contact: {
|
||||||
|
name: {
|
||||||
|
givenName: 'Jerry',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return renderBothDirections(props);
|
||||||
|
});
|
||||||
|
|
||||||
|
story.add('EmbeddedContact: Organization', () => {
|
||||||
|
const props = createProps({
|
||||||
|
contact: {
|
||||||
|
organization: 'Company 5',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return renderBothDirections(props);
|
||||||
|
});
|
||||||
|
|
||||||
|
story.add('EmbeddedContact: Given + Family Name', () => {
|
||||||
|
const props = createProps({
|
||||||
|
contact: {
|
||||||
|
name: {
|
||||||
|
givenName: 'Jerry',
|
||||||
|
familyName: 'FamilyName',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return renderBothDirections(props);
|
||||||
|
});
|
||||||
|
|
||||||
|
story.add('EmbeddedContact: Family Name', () => {
|
||||||
|
const props = createProps({
|
||||||
|
contact: {
|
||||||
|
name: {
|
||||||
|
familyName: 'FamilyName',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return renderBothDirections(props);
|
||||||
|
});
|
||||||
|
|
||||||
|
story.add('EmbeddedContact: Loading Avatar', () => {
|
||||||
|
const props = createProps({
|
||||||
|
contact: {
|
||||||
|
name: {
|
||||||
|
displayName: 'Jerry Jordan',
|
||||||
|
},
|
||||||
|
avatar: {
|
||||||
|
avatar: fakeAttachment({
|
||||||
|
pending: true,
|
||||||
|
contentType: IMAGE_GIF,
|
||||||
|
}),
|
||||||
|
isProfile: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return renderBothDirections(props);
|
||||||
|
});
|
||||||
|
|
|
@ -83,6 +83,7 @@ import { getCustomColorStyle } from '../../util/getCustomColorStyle';
|
||||||
import { offsetDistanceModifier } from '../../util/popperUtil';
|
import { offsetDistanceModifier } from '../../util/popperUtil';
|
||||||
import * as KeyboardLayout from '../../services/keyboardLayout';
|
import * as KeyboardLayout from '../../services/keyboardLayout';
|
||||||
import { StopPropagation } from '../StopPropagation';
|
import { StopPropagation } from '../StopPropagation';
|
||||||
|
import type { UUIDStringType } from '../../types/UUID';
|
||||||
|
|
||||||
type Trigger = {
|
type Trigger = {
|
||||||
handleContextClick: (event: React.MouseEvent<HTMLDivElement>) => void;
|
handleContextClick: (event: React.MouseEvent<HTMLDivElement>) => void;
|
||||||
|
@ -279,7 +280,7 @@ export type PropsActions = {
|
||||||
clearSelectedMessage: () => unknown;
|
clearSelectedMessage: () => unknown;
|
||||||
doubleCheckMissingQuoteReference: (messageId: string) => unknown;
|
doubleCheckMissingQuoteReference: (messageId: string) => unknown;
|
||||||
messageExpanded: (id: string, displayLimit: number) => unknown;
|
messageExpanded: (id: string, displayLimit: number) => unknown;
|
||||||
checkForAccount: (identifier: string) => unknown;
|
checkForAccount: (phoneNumber: string) => unknown;
|
||||||
|
|
||||||
reactToMessage: (
|
reactToMessage: (
|
||||||
id: string,
|
id: string,
|
||||||
|
@ -293,10 +294,14 @@ export type PropsActions = {
|
||||||
deleteMessageForEveryone: (id: string) => void;
|
deleteMessageForEveryone: (id: string) => void;
|
||||||
showMessageDetail: (id: string) => void;
|
showMessageDetail: (id: string) => void;
|
||||||
|
|
||||||
|
startConversation: (e164: string, uuid: UUIDStringType) => void;
|
||||||
openConversation: (conversationId: string, messageId?: string) => void;
|
openConversation: (conversationId: string, messageId?: string) => void;
|
||||||
showContactDetail: (options: {
|
showContactDetail: (options: {
|
||||||
contact: EmbeddedContactType;
|
contact: EmbeddedContactType;
|
||||||
signalAccount?: string;
|
signalAccount?: {
|
||||||
|
phoneNumber: string;
|
||||||
|
uuid: UUIDStringType;
|
||||||
|
};
|
||||||
}) => void;
|
}) => void;
|
||||||
showContactModal: (contactId: string, conversationId?: string) => void;
|
showContactModal: (contactId: string, conversationId?: string) => void;
|
||||||
|
|
||||||
|
@ -501,7 +506,7 @@ export class Message extends React.PureComponent<Props, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
const { contact, checkForAccount } = this.props;
|
const { contact, checkForAccount } = this.props;
|
||||||
if (contact && contact.firstNumber && !contact.isNumberOnSignal) {
|
if (contact && contact.firstNumber && !contact.uuid) {
|
||||||
checkForAccount(contact.firstNumber);
|
checkForAccount(contact.firstNumber);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1336,8 +1341,7 @@ export class Message extends React.PureComponent<Props, State> {
|
||||||
this.getMetadataPlacement() !== MetadataPlacement.NotRendered;
|
this.getMetadataPlacement() !== MetadataPlacement.NotRendered;
|
||||||
|
|
||||||
const otherContent =
|
const otherContent =
|
||||||
(contact && contact.firstNumber && contact.isNumberOnSignal) ||
|
(contact && contact.firstNumber && contact.uuid) || withCaption;
|
||||||
withCaption;
|
|
||||||
const tabIndex = otherContent ? 0 : -1;
|
const tabIndex = otherContent ? 0 : -1;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -1346,7 +1350,18 @@ export class Message extends React.PureComponent<Props, State> {
|
||||||
isIncoming={direction === 'incoming'}
|
isIncoming={direction === 'incoming'}
|
||||||
i18n={i18n}
|
i18n={i18n}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
showContactDetail({ contact, signalAccount: contact.firstNumber });
|
const signalAccount =
|
||||||
|
contact.firstNumber && contact.uuid
|
||||||
|
? {
|
||||||
|
phoneNumber: contact.firstNumber,
|
||||||
|
uuid: contact.uuid,
|
||||||
|
}
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
showContactDetail({
|
||||||
|
contact,
|
||||||
|
signalAccount,
|
||||||
|
});
|
||||||
}}
|
}}
|
||||||
withContentAbove={withContentAbove}
|
withContentAbove={withContentAbove}
|
||||||
withContentBelow={withContentBelow}
|
withContentBelow={withContentBelow}
|
||||||
|
@ -1356,20 +1371,30 @@ export class Message extends React.PureComponent<Props, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public renderSendMessageButton(): JSX.Element | null {
|
public renderSendMessageButton(): JSX.Element | null {
|
||||||
const { contact, openConversation, i18n } = this.props;
|
const { contact, direction, shouldCollapseBelow, startConversation, i18n } =
|
||||||
|
this.props;
|
||||||
|
const noBottomLeftCurve = direction === 'incoming' && shouldCollapseBelow;
|
||||||
|
const noBottomRightCurve = direction === 'outgoing' && shouldCollapseBelow;
|
||||||
|
|
||||||
if (!contact) {
|
if (!contact) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const { firstNumber, isNumberOnSignal } = contact;
|
const { firstNumber, uuid } = contact;
|
||||||
if (!firstNumber || !isNumberOnSignal) {
|
if (!firstNumber || !uuid) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => openConversation(firstNumber)}
|
onClick={() => startConversation(firstNumber, uuid)}
|
||||||
className="module-message__send-message-button"
|
className={classNames(
|
||||||
|
'module-message__send-message-button',
|
||||||
|
noBottomLeftCurve &&
|
||||||
|
'module-message__send-message-button--no-bottom-left-curve',
|
||||||
|
noBottomRightCurve &&
|
||||||
|
'module-message__send-message-button--no-bottom-right-curve'
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
{i18n('sendMessageToContact')}
|
{i18n('sendMessageToContact')}
|
||||||
</button>
|
</button>
|
||||||
|
@ -2484,7 +2509,7 @@ export class Message extends React.PureComponent<Props, State> {
|
||||||
this.audioButtonRef.current.click();
|
this.audioButtonRef.current.click();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (contact && contact.firstNumber && contact.isNumberOnSignal) {
|
if (contact && contact.firstNumber && contact.uuid) {
|
||||||
openConversation(contact.firstNumber);
|
openConversation(contact.firstNumber);
|
||||||
|
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
@ -2492,7 +2517,14 @@ export class Message extends React.PureComponent<Props, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (contact) {
|
if (contact) {
|
||||||
showContactDetail({ contact, signalAccount: contact.firstNumber });
|
const signalAccount =
|
||||||
|
contact.firstNumber && contact.uuid
|
||||||
|
? {
|
||||||
|
phoneNumber: contact.firstNumber,
|
||||||
|
uuid: contact.uuid,
|
||||||
|
}
|
||||||
|
: undefined;
|
||||||
|
showContactDetail({ contact, signalAccount });
|
||||||
|
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
|
|
|
@ -99,6 +99,7 @@ const createProps = (overrideProps: Partial<Props> = {}): Props => ({
|
||||||
),
|
),
|
||||||
showForwardMessageModal: action('showForwardMessageModal'),
|
showForwardMessageModal: action('showForwardMessageModal'),
|
||||||
showVisualAttachment: action('showVisualAttachment'),
|
showVisualAttachment: action('showVisualAttachment'),
|
||||||
|
startConversation: action('startConversation'),
|
||||||
});
|
});
|
||||||
|
|
||||||
story.add('Delivered Incoming', () => {
|
story.add('Delivered Incoming', () => {
|
||||||
|
|
|
@ -87,6 +87,7 @@ export type PropsBackboneActions = Pick<
|
||||||
| 'showExpiredOutgoingTapToViewToast'
|
| 'showExpiredOutgoingTapToViewToast'
|
||||||
| 'showForwardMessageModal'
|
| 'showForwardMessageModal'
|
||||||
| 'showVisualAttachment'
|
| 'showVisualAttachment'
|
||||||
|
| 'startConversation'
|
||||||
>;
|
>;
|
||||||
|
|
||||||
export type PropsReduxActions = Pick<
|
export type PropsReduxActions = Pick<
|
||||||
|
@ -297,6 +298,7 @@ export class MessageDetail extends React.Component<Props> {
|
||||||
showExpiredOutgoingTapToViewToast,
|
showExpiredOutgoingTapToViewToast,
|
||||||
showForwardMessageModal,
|
showForwardMessageModal,
|
||||||
showVisualAttachment,
|
showVisualAttachment,
|
||||||
|
startConversation,
|
||||||
theme,
|
theme,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
|
@ -364,6 +366,7 @@ export class MessageDetail extends React.Component<Props> {
|
||||||
log.warn('MessageDetail: deleteMessageForEveryone called!');
|
log.warn('MessageDetail: deleteMessageForEveryone called!');
|
||||||
}}
|
}}
|
||||||
showVisualAttachment={showVisualAttachment}
|
showVisualAttachment={showVisualAttachment}
|
||||||
|
startConversation={startConversation}
|
||||||
theme={theme}
|
theme={theme}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -96,6 +96,7 @@ const defaultMessageProps: MessagesProps = {
|
||||||
showForwardMessageModal: action('default--showForwardMessageModal'),
|
showForwardMessageModal: action('default--showForwardMessageModal'),
|
||||||
showMessageDetail: action('default--showMessageDetail'),
|
showMessageDetail: action('default--showMessageDetail'),
|
||||||
showVisualAttachment: action('default--showVisualAttachment'),
|
showVisualAttachment: action('default--showVisualAttachment'),
|
||||||
|
startConversation: action('default--startConversation'),
|
||||||
status: 'sent',
|
status: 'sent',
|
||||||
text: 'This is really interesting.',
|
text: 'This is really interesting.',
|
||||||
textDirection: TextDirection.Default,
|
textDirection: TextDirection.Default,
|
||||||
|
|
|
@ -398,6 +398,7 @@ const actions = () => ({
|
||||||
downloadNewVersion: action('downloadNewVersion'),
|
downloadNewVersion: action('downloadNewVersion'),
|
||||||
|
|
||||||
startCallingLobby: action('startCallingLobby'),
|
startCallingLobby: action('startCallingLobby'),
|
||||||
|
startConversation: action('startConversation'),
|
||||||
returnToActiveCall: action('returnToActiveCall'),
|
returnToActiveCall: action('returnToActiveCall'),
|
||||||
|
|
||||||
contactSupport: action('contactSupport'),
|
contactSupport: action('contactSupport'),
|
||||||
|
|
|
@ -253,6 +253,7 @@ const getActions = createSelector(
|
||||||
'scrollToQuotedMessage',
|
'scrollToQuotedMessage',
|
||||||
'showExpiredIncomingTapToViewToast',
|
'showExpiredIncomingTapToViewToast',
|
||||||
'showExpiredOutgoingTapToViewToast',
|
'showExpiredOutgoingTapToViewToast',
|
||||||
|
'startConversation',
|
||||||
|
|
||||||
'showIdentity',
|
'showIdentity',
|
||||||
|
|
||||||
|
|
|
@ -93,6 +93,7 @@ const getDefaultProps = () => ({
|
||||||
downloadNewVersion: action('downloadNewVersion'),
|
downloadNewVersion: action('downloadNewVersion'),
|
||||||
showIdentity: action('showIdentity'),
|
showIdentity: action('showIdentity'),
|
||||||
startCallingLobby: action('startCallingLobby'),
|
startCallingLobby: action('startCallingLobby'),
|
||||||
|
startConversation: action('startConversation'),
|
||||||
returnToActiveCall: action('returnToActiveCall'),
|
returnToActiveCall: action('returnToActiveCall'),
|
||||||
shouldCollapseAbove: false,
|
shouldCollapseAbove: false,
|
||||||
shouldCollapseBelow: false,
|
shouldCollapseBelow: false,
|
||||||
|
|
|
@ -2,15 +2,19 @@
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
import type { ThunkAction } from 'redux-thunk';
|
import type { ThunkAction } from 'redux-thunk';
|
||||||
|
|
||||||
|
import * as Errors from '../../types/errors';
|
||||||
|
import * as log from '../../logging/log';
|
||||||
|
|
||||||
import type { StateType as RootStateType } from '../reducer';
|
import type { StateType as RootStateType } from '../reducer';
|
||||||
import { UUID } from '../../types/UUID';
|
import type { UUIDStringType } from '../../types/UUID';
|
||||||
|
|
||||||
import type { NoopActionType } from './noop';
|
import type { NoopActionType } from './noop';
|
||||||
|
|
||||||
// State
|
// State
|
||||||
|
|
||||||
export type AccountsStateType = {
|
export type AccountsStateType = {
|
||||||
accounts: Record<string, boolean>;
|
accounts: Record<string, UUIDStringType | undefined>;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Actions
|
// Actions
|
||||||
|
@ -18,8 +22,8 @@ export type AccountsStateType = {
|
||||||
type AccountUpdateActionType = {
|
type AccountUpdateActionType = {
|
||||||
type: 'accounts/UPDATE';
|
type: 'accounts/UPDATE';
|
||||||
payload: {
|
payload: {
|
||||||
identifier: string;
|
phoneNumber: string;
|
||||||
hasAccount: boolean;
|
uuid?: UUIDStringType;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -32,14 +36,14 @@ export const actions = {
|
||||||
};
|
};
|
||||||
|
|
||||||
function checkForAccount(
|
function checkForAccount(
|
||||||
identifier: string
|
phoneNumber: string
|
||||||
): ThunkAction<
|
): ThunkAction<
|
||||||
void,
|
void,
|
||||||
RootStateType,
|
RootStateType,
|
||||||
unknown,
|
unknown,
|
||||||
AccountUpdateActionType | NoopActionType
|
AccountUpdateActionType | NoopActionType
|
||||||
> {
|
> {
|
||||||
return async dispatch => {
|
return async (dispatch, getState) => {
|
||||||
if (!window.textsecure.messaging) {
|
if (!window.textsecure.messaging) {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: 'NOOP',
|
type: 'NOOP',
|
||||||
|
@ -48,21 +52,50 @@ function checkForAccount(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let hasAccount = false;
|
const conversation = window.ConversationController.get(phoneNumber);
|
||||||
|
if (conversation && conversation.get('uuid')) {
|
||||||
|
log.error(`checkForAccount: found ${phoneNumber} in existing contacts`);
|
||||||
|
const uuid = conversation.get('uuid');
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
type: 'accounts/UPDATE',
|
||||||
|
payload: {
|
||||||
|
phoneNumber,
|
||||||
|
uuid,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const state = getState();
|
||||||
|
const existing = Object.prototype.hasOwnProperty.call(
|
||||||
|
state.accounts.accounts,
|
||||||
|
phoneNumber
|
||||||
|
);
|
||||||
|
if (existing) {
|
||||||
|
dispatch({
|
||||||
|
type: 'NOOP',
|
||||||
|
payload: null,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let uuid: UUIDStringType | undefined;
|
||||||
|
|
||||||
|
log.error(`checkForAccount: looking ${phoneNumber} up on server`);
|
||||||
try {
|
try {
|
||||||
hasAccount = await window.textsecure.messaging.checkAccountExistence(
|
const uuidLookup = await window.textsecure.messaging.getUuidsForE164s([
|
||||||
new UUID(identifier)
|
phoneNumber,
|
||||||
);
|
]);
|
||||||
} catch (_error) {
|
uuid = uuidLookup[phoneNumber] || undefined;
|
||||||
// Doing nothing with this failed fetch
|
} catch (error) {
|
||||||
|
log.error('checkForAccount:', Errors.toLogFormat(error));
|
||||||
}
|
}
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: 'accounts/UPDATE',
|
type: 'accounts/UPDATE',
|
||||||
payload: {
|
payload: {
|
||||||
identifier,
|
phoneNumber,
|
||||||
hasAccount,
|
uuid,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -86,13 +119,13 @@ export function reducer(
|
||||||
|
|
||||||
if (action.type === 'accounts/UPDATE') {
|
if (action.type === 'accounts/UPDATE') {
|
||||||
const { payload } = action;
|
const { payload } = action;
|
||||||
const { identifier, hasAccount } = payload;
|
const { phoneNumber, uuid } = payload;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
accounts: {
|
accounts: {
|
||||||
...state.accounts,
|
...state.accounts,
|
||||||
[identifier]: hasAccount,
|
[phoneNumber]: uuid,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,20 +5,23 @@ import { createSelector } from 'reselect';
|
||||||
|
|
||||||
import type { StateType } from '../reducer';
|
import type { StateType } from '../reducer';
|
||||||
import type { AccountsStateType } from '../ducks/accounts';
|
import type { AccountsStateType } from '../ducks/accounts';
|
||||||
|
import type { UUIDStringType } from '../../types/UUID';
|
||||||
|
|
||||||
export const getAccounts = (state: StateType): AccountsStateType =>
|
export const getAccounts = (state: StateType): AccountsStateType =>
|
||||||
state.accounts;
|
state.accounts;
|
||||||
|
|
||||||
export type AccountSelectorType = (identifier?: string) => boolean;
|
export type AccountSelectorType = (
|
||||||
|
identifier?: string
|
||||||
|
) => UUIDStringType | undefined;
|
||||||
export const getAccountSelector = createSelector(
|
export const getAccountSelector = createSelector(
|
||||||
getAccounts,
|
getAccounts,
|
||||||
(accounts: AccountsStateType): AccountSelectorType => {
|
(accounts: AccountsStateType): AccountSelectorType => {
|
||||||
return (identifier?: string) => {
|
return (identifier?: string) => {
|
||||||
if (!identifier) {
|
if (!identifier) {
|
||||||
return false;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
return accounts.accounts[identifier] || false;
|
return accounts.accounts[identifier] || undefined;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
@ -1443,7 +1443,7 @@ export function getMessagePropStatus(
|
||||||
export function getPropsForEmbeddedContact(
|
export function getPropsForEmbeddedContact(
|
||||||
message: MessageWithUIFieldsType,
|
message: MessageWithUIFieldsType,
|
||||||
regionCode: string | undefined,
|
regionCode: string | undefined,
|
||||||
accountSelector: (identifier?: string) => boolean
|
accountSelector: (identifier?: string) => UUIDStringType | undefined
|
||||||
): EmbeddedContactType | undefined {
|
): EmbeddedContactType | undefined {
|
||||||
const contacts = message.contact;
|
const contacts = message.contact;
|
||||||
if (!contacts || !contacts.length) {
|
if (!contacts || !contacts.length) {
|
||||||
|
@ -1459,7 +1459,7 @@ export function getPropsForEmbeddedContact(
|
||||||
getAbsoluteAttachmentPath:
|
getAbsoluteAttachmentPath:
|
||||||
window.Signal.Migrations.getAbsoluteAttachmentPath,
|
window.Signal.Migrations.getAbsoluteAttachmentPath,
|
||||||
firstNumber,
|
firstNumber,
|
||||||
isNumberOnSignal: accountSelector(firstNumber),
|
uuid: accountSelector(firstNumber),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -56,6 +56,7 @@ const mapStateToProps = (
|
||||||
showExpiredOutgoingTapToViewToast,
|
showExpiredOutgoingTapToViewToast,
|
||||||
showForwardMessageModal,
|
showForwardMessageModal,
|
||||||
showVisualAttachment,
|
showVisualAttachment,
|
||||||
|
startConversation,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const contactNameColor =
|
const contactNameColor =
|
||||||
|
@ -102,6 +103,7 @@ const mapStateToProps = (
|
||||||
showExpiredOutgoingTapToViewToast,
|
showExpiredOutgoingTapToViewToast,
|
||||||
showForwardMessageModal,
|
showForwardMessageModal,
|
||||||
showVisualAttachment,
|
showVisualAttachment,
|
||||||
|
startConversation,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -98,6 +98,7 @@ export type TimelinePropsType = ExternalProps &
|
||||||
| 'showIdentity'
|
| 'showIdentity'
|
||||||
| 'showMessageDetail'
|
| 'showMessageDetail'
|
||||||
| 'showVisualAttachment'
|
| 'showVisualAttachment'
|
||||||
|
| 'startConversation'
|
||||||
| 'unblurAvatar'
|
| 'unblurAvatar'
|
||||||
| 'updateSharedGroups'
|
| 'updateSharedGroups'
|
||||||
>;
|
>;
|
||||||
|
|
|
@ -14,6 +14,7 @@ import {
|
||||||
parseAndWriteAvatar,
|
parseAndWriteAvatar,
|
||||||
} from '../../types/EmbeddedContact';
|
} from '../../types/EmbeddedContact';
|
||||||
import { fakeAttachment } from '../../test-both/helpers/fakeAttachment';
|
import { fakeAttachment } from '../../test-both/helpers/fakeAttachment';
|
||||||
|
import { UUID } from '../../types/UUID';
|
||||||
|
|
||||||
describe('Contact', () => {
|
describe('Contact', () => {
|
||||||
const NUMBER = '+12025550099';
|
const NUMBER = '+12025550099';
|
||||||
|
@ -113,7 +114,7 @@ describe('Contact', () => {
|
||||||
describe('embeddedContactSelector', () => {
|
describe('embeddedContactSelector', () => {
|
||||||
const regionCode = '1';
|
const regionCode = '1';
|
||||||
const firstNumber = '+1202555000';
|
const firstNumber = '+1202555000';
|
||||||
const isNumberOnSignal = false;
|
const uuid = undefined;
|
||||||
const getAbsoluteAttachmentPath = (path: string) => `absolute:${path}`;
|
const getAbsoluteAttachmentPath = (path: string) => `absolute:${path}`;
|
||||||
|
|
||||||
it('eliminates avatar if it has had an attachment download error', () => {
|
it('eliminates avatar if it has had an attachment download error', () => {
|
||||||
|
@ -141,13 +142,13 @@ describe('Contact', () => {
|
||||||
organization: 'Somewhere, Inc.',
|
organization: 'Somewhere, Inc.',
|
||||||
avatar: undefined,
|
avatar: undefined,
|
||||||
firstNumber,
|
firstNumber,
|
||||||
isNumberOnSignal,
|
uuid,
|
||||||
number: undefined,
|
number: undefined,
|
||||||
};
|
};
|
||||||
const actual = embeddedContactSelector(contact, {
|
const actual = embeddedContactSelector(contact, {
|
||||||
regionCode,
|
regionCode,
|
||||||
firstNumber,
|
firstNumber,
|
||||||
isNumberOnSignal,
|
uuid,
|
||||||
getAbsoluteAttachmentPath,
|
getAbsoluteAttachmentPath,
|
||||||
});
|
});
|
||||||
assert.deepEqual(actual, expected);
|
assert.deepEqual(actual, expected);
|
||||||
|
@ -185,19 +186,21 @@ describe('Contact', () => {
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
firstNumber,
|
firstNumber,
|
||||||
isNumberOnSignal,
|
uuid,
|
||||||
number: undefined,
|
number: undefined,
|
||||||
};
|
};
|
||||||
const actual = embeddedContactSelector(contact, {
|
const actual = embeddedContactSelector(contact, {
|
||||||
regionCode,
|
regionCode,
|
||||||
firstNumber,
|
firstNumber,
|
||||||
isNumberOnSignal,
|
uuid,
|
||||||
getAbsoluteAttachmentPath,
|
getAbsoluteAttachmentPath,
|
||||||
});
|
});
|
||||||
assert.deepEqual(actual, expected);
|
assert.deepEqual(actual, expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('calculates absolute path', () => {
|
it('calculates absolute path', () => {
|
||||||
|
const fullUuid = UUID.generate().toString();
|
||||||
|
|
||||||
const contact = {
|
const contact = {
|
||||||
name: {
|
name: {
|
||||||
displayName: 'displayName',
|
displayName: 'displayName',
|
||||||
|
@ -228,13 +231,13 @@ describe('Contact', () => {
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
firstNumber,
|
firstNumber,
|
||||||
isNumberOnSignal: true,
|
uuid: fullUuid,
|
||||||
number: undefined,
|
number: undefined,
|
||||||
};
|
};
|
||||||
const actual = embeddedContactSelector(contact, {
|
const actual = embeddedContactSelector(contact, {
|
||||||
regionCode,
|
regionCode,
|
||||||
firstNumber,
|
firstNumber,
|
||||||
isNumberOnSignal: true,
|
uuid: fullUuid,
|
||||||
getAbsoluteAttachmentPath,
|
getAbsoluteAttachmentPath,
|
||||||
});
|
});
|
||||||
assert.deepEqual(actual, expected);
|
assert.deepEqual(actual, expected);
|
||||||
|
|
|
@ -14,6 +14,7 @@ import {
|
||||||
import type { AttachmentType, migrateDataToFileSystem } from './Attachment';
|
import type { AttachmentType, migrateDataToFileSystem } from './Attachment';
|
||||||
import { toLogFormat } from './errors';
|
import { toLogFormat } from './errors';
|
||||||
import type { LoggerType } from './Logging';
|
import type { LoggerType } from './Logging';
|
||||||
|
import type { UUIDStringType } from './UUID';
|
||||||
|
|
||||||
export type EmbeddedContactType = {
|
export type EmbeddedContactType = {
|
||||||
name?: Name;
|
name?: Name;
|
||||||
|
@ -25,7 +26,7 @@ export type EmbeddedContactType = {
|
||||||
|
|
||||||
// Populated by selector
|
// Populated by selector
|
||||||
firstNumber?: string;
|
firstNumber?: string;
|
||||||
isNumberOnSignal?: boolean;
|
uuid?: UUIDStringType;
|
||||||
};
|
};
|
||||||
|
|
||||||
type Name = {
|
type Name = {
|
||||||
|
@ -133,16 +134,11 @@ export function embeddedContactSelector(
|
||||||
options: {
|
options: {
|
||||||
regionCode?: string;
|
regionCode?: string;
|
||||||
firstNumber?: string;
|
firstNumber?: string;
|
||||||
isNumberOnSignal?: boolean;
|
uuid?: UUIDStringType;
|
||||||
getAbsoluteAttachmentPath: (path: string) => string;
|
getAbsoluteAttachmentPath: (path: string) => string;
|
||||||
}
|
}
|
||||||
): EmbeddedContactType {
|
): EmbeddedContactType {
|
||||||
const {
|
const { getAbsoluteAttachmentPath, firstNumber, uuid, regionCode } = options;
|
||||||
getAbsoluteAttachmentPath,
|
|
||||||
firstNumber,
|
|
||||||
isNumberOnSignal,
|
|
||||||
regionCode,
|
|
||||||
} = options;
|
|
||||||
|
|
||||||
let { avatar } = contact;
|
let { avatar } = contact;
|
||||||
if (avatar && avatar.avatar) {
|
if (avatar && avatar.avatar) {
|
||||||
|
@ -164,7 +160,7 @@ export function embeddedContactSelector(
|
||||||
return {
|
return {
|
||||||
...contact,
|
...contact,
|
||||||
firstNumber,
|
firstNumber,
|
||||||
isNumberOnSignal,
|
uuid,
|
||||||
avatar,
|
avatar,
|
||||||
number:
|
number:
|
||||||
contact.number &&
|
contact.number &&
|
||||||
|
|
|
@ -173,7 +173,10 @@ type MessageActionsType = {
|
||||||
retryDeleteForEveryone: (messageId: string) => unknown;
|
retryDeleteForEveryone: (messageId: string) => unknown;
|
||||||
showContactDetail: (options: {
|
showContactDetail: (options: {
|
||||||
contact: EmbeddedContactType;
|
contact: EmbeddedContactType;
|
||||||
signalAccount?: string;
|
signalAccount?: {
|
||||||
|
phoneNumber: string;
|
||||||
|
uuid: UUIDStringType;
|
||||||
|
};
|
||||||
}) => unknown;
|
}) => unknown;
|
||||||
showContactModal: (contactId: string) => unknown;
|
showContactModal: (contactId: string) => unknown;
|
||||||
showSafetyNumber: (contactId: string) => unknown;
|
showSafetyNumber: (contactId: string) => unknown;
|
||||||
|
@ -187,6 +190,7 @@ type MessageActionsType = {
|
||||||
messageId: string;
|
messageId: string;
|
||||||
showSingle?: boolean;
|
showSingle?: boolean;
|
||||||
}) => unknown;
|
}) => unknown;
|
||||||
|
startConversation: (e164: string, uuid: UUIDStringType) => unknown;
|
||||||
};
|
};
|
||||||
|
|
||||||
type MediaType = {
|
type MediaType = {
|
||||||
|
@ -768,7 +772,10 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
|
||||||
};
|
};
|
||||||
const showContactDetail = (options: {
|
const showContactDetail = (options: {
|
||||||
contact: EmbeddedContactType;
|
contact: EmbeddedContactType;
|
||||||
signalAccount?: string;
|
signalAccount?: {
|
||||||
|
phoneNumber: string;
|
||||||
|
uuid: UUIDStringType;
|
||||||
|
};
|
||||||
}) => {
|
}) => {
|
||||||
this.showContactDetail(options);
|
this.showContactDetail(options);
|
||||||
};
|
};
|
||||||
|
@ -866,6 +873,7 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
|
||||||
};
|
};
|
||||||
|
|
||||||
const showForwardMessageModal = this.showForwardMessageModal.bind(this);
|
const showForwardMessageModal = this.showForwardMessageModal.bind(this);
|
||||||
|
const startConversation = this.startConversation.bind(this);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
deleteMessage,
|
deleteMessage,
|
||||||
|
@ -891,6 +899,7 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
|
||||||
showIdentity,
|
showIdentity,
|
||||||
showMessageDetail,
|
showMessageDetail,
|
||||||
showVisualAttachment,
|
showVisualAttachment,
|
||||||
|
startConversation,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2368,7 +2377,10 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
|
||||||
signalAccount,
|
signalAccount,
|
||||||
}: {
|
}: {
|
||||||
contact: EmbeddedContactType;
|
contact: EmbeddedContactType;
|
||||||
signalAccount?: string;
|
signalAccount?: {
|
||||||
|
phoneNumber: string;
|
||||||
|
uuid: UUIDStringType;
|
||||||
|
};
|
||||||
}): void {
|
}): void {
|
||||||
const view = new Whisper.ReactWrapperView({
|
const view = new Whisper.ReactWrapperView({
|
||||||
Component: window.Signal.Components.ContactDetail,
|
Component: window.Signal.Components.ContactDetail,
|
||||||
|
@ -2378,7 +2390,10 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
|
||||||
hasSignalAccount: Boolean(signalAccount),
|
hasSignalAccount: Boolean(signalAccount),
|
||||||
onSendMessage: () => {
|
onSendMessage: () => {
|
||||||
if (signalAccount) {
|
if (signalAccount) {
|
||||||
this.openConversation(signalAccount);
|
this.startConversation(
|
||||||
|
signalAccount.phoneNumber,
|
||||||
|
signalAccount.uuid
|
||||||
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -2390,6 +2405,19 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
|
||||||
this.listenBack(view);
|
this.listenBack(view);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
startConversation(e164: string, uuid: UUIDStringType): void {
|
||||||
|
const conversationId = window.ConversationController.ensureContactIds({
|
||||||
|
e164,
|
||||||
|
uuid,
|
||||||
|
});
|
||||||
|
strictAssert(
|
||||||
|
conversationId,
|
||||||
|
`startConversation failed given ${e164}/${uuid} combination`
|
||||||
|
);
|
||||||
|
|
||||||
|
this.openConversation(conversationId);
|
||||||
|
}
|
||||||
|
|
||||||
async openConversation(
|
async openConversation(
|
||||||
conversationId: string,
|
conversationId: string,
|
||||||
messageId?: string
|
messageId?: string
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue