Modern profile sharing in 1:1 and GroupV1 groups
This commit is contained in:
parent
60f2422e2a
commit
04b7a29229
22 changed files with 371 additions and 115 deletions
|
@ -16,12 +16,15 @@ import {
|
|||
MessageRequestActions,
|
||||
Props as MessageRequestActionsProps,
|
||||
} from './conversation/MessageRequestActions';
|
||||
import { MandatoryProfileSharingActions } from './conversation/MandatoryProfileSharingActions';
|
||||
import { countStickers } from './stickers/lib';
|
||||
import { LocalizerType } from '../types/Util';
|
||||
import { EmojiPickDataType } from './emoji/EmojiPicker';
|
||||
|
||||
export type OwnProps = {
|
||||
readonly i18n: LocalizerType;
|
||||
readonly groupVersion?: 1 | 2;
|
||||
readonly isMissingMandatoryProfileSharing?: boolean;
|
||||
readonly messageRequestsEnabled?: boolean;
|
||||
readonly acceptedMessageRequest?: boolean;
|
||||
readonly compositionApi?: React.MutableRefObject<{
|
||||
|
@ -113,7 +116,9 @@ export const CompositionArea = ({
|
|||
// Message Requests
|
||||
acceptedMessageRequest,
|
||||
conversationType,
|
||||
groupVersion,
|
||||
isBlocked,
|
||||
isMissingMandatoryProfileSharing,
|
||||
messageRequestsEnabled,
|
||||
name,
|
||||
onAccept,
|
||||
|
@ -326,7 +331,7 @@ export const CompositionArea = ({
|
|||
};
|
||||
}, [setLarge]);
|
||||
|
||||
if ((!acceptedMessageRequest || isBlocked) && messageRequestsEnabled) {
|
||||
if (messageRequestsEnabled && (!acceptedMessageRequest || isBlocked)) {
|
||||
return (
|
||||
<MessageRequestActions
|
||||
i18n={i18n}
|
||||
|
@ -345,6 +350,28 @@ export const CompositionArea = ({
|
|||
);
|
||||
}
|
||||
|
||||
// If no message request, but we haven't shared profile yet, we show profile-sharing UI
|
||||
if (
|
||||
(conversationType === 'direct' ||
|
||||
(conversationType === 'group' && groupVersion === 1)) &&
|
||||
isMissingMandatoryProfileSharing
|
||||
) {
|
||||
return (
|
||||
<MandatoryProfileSharingActions
|
||||
i18n={i18n}
|
||||
conversationType={conversationType}
|
||||
onBlock={onBlock}
|
||||
onBlockAndDelete={onBlockAndDelete}
|
||||
onDelete={onDelete}
|
||||
onAccept={onAccept}
|
||||
name={name}
|
||||
profileName={profileName}
|
||||
phoneNumber={phoneNumber}
|
||||
title={title}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="module-composition-area">
|
||||
<div className="module-composition-area__toggle-large">
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
import * as React from 'react';
|
||||
import { storiesOf } from '@storybook/react';
|
||||
import { text } from '@storybook/addon-knobs';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
|
||||
import {
|
||||
MandatoryProfileSharingActions,
|
||||
Props as MandatoryProfileSharingActionsProps,
|
||||
} from './MandatoryProfileSharingActions';
|
||||
import { setup as setupI18n } from '../../../js/modules/i18n';
|
||||
import enMessages from '../../../_locales/en/messages.json';
|
||||
|
||||
const i18n = setupI18n('en', enMessages);
|
||||
|
||||
const getBaseProps = (
|
||||
isGroup = false
|
||||
): MandatoryProfileSharingActionsProps => ({
|
||||
i18n,
|
||||
conversationType: isGroup ? 'group' : 'direct',
|
||||
firstName: text('firstName', 'Cayce'),
|
||||
title: isGroup
|
||||
? text('title', 'NYC Rock Climbers')
|
||||
: text('title', 'Cayce Bollard'),
|
||||
name: isGroup
|
||||
? text('name', 'NYC Rock Climbers')
|
||||
: text('name', 'Cayce Bollard'),
|
||||
onBlock: action('block'),
|
||||
onBlockAndDelete: action('onBlockAndDelete'),
|
||||
onDelete: action('delete'),
|
||||
onAccept: action('accept'),
|
||||
});
|
||||
|
||||
storiesOf('Components/Conversation/MandatoryProfileSharingActions', module)
|
||||
.add('Direct', () => {
|
||||
return (
|
||||
<div style={{ width: '480px' }}>
|
||||
<MandatoryProfileSharingActions {...getBaseProps()} />
|
||||
</div>
|
||||
);
|
||||
})
|
||||
.add('Group', () => {
|
||||
return (
|
||||
<div style={{ width: '480px' }}>
|
||||
<MandatoryProfileSharingActions {...getBaseProps(true)} />
|
||||
</div>
|
||||
);
|
||||
});
|
134
ts/components/conversation/MandatoryProfileSharingActions.tsx
Normal file
134
ts/components/conversation/MandatoryProfileSharingActions.tsx
Normal file
|
@ -0,0 +1,134 @@
|
|||
import * as React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { ContactName, PropsType as ContactNameProps } from './ContactName';
|
||||
import {
|
||||
MessageRequestActionsConfirmation,
|
||||
MessageRequestState,
|
||||
Props as MessageRequestActionsConfirmationProps,
|
||||
} from './MessageRequestActionsConfirmation';
|
||||
import { Intl } from '../Intl';
|
||||
import { LocalizerType } from '../../types/Util';
|
||||
|
||||
export type Props = {
|
||||
i18n: LocalizerType;
|
||||
firstName?: string;
|
||||
onAccept(): unknown;
|
||||
} & Omit<ContactNameProps, 'module' | 'i18n'> &
|
||||
Pick<
|
||||
MessageRequestActionsConfirmationProps,
|
||||
'conversationType' | 'onBlock' | 'onBlockAndDelete' | 'onDelete'
|
||||
>;
|
||||
|
||||
export const MandatoryProfileSharingActions = ({
|
||||
conversationType,
|
||||
firstName,
|
||||
i18n,
|
||||
name,
|
||||
onAccept,
|
||||
onBlock,
|
||||
onBlockAndDelete,
|
||||
onDelete,
|
||||
phoneNumber,
|
||||
profileName,
|
||||
title,
|
||||
}: Props): JSX.Element => {
|
||||
const [mrState, setMrState] = React.useState(MessageRequestState.default);
|
||||
|
||||
return (
|
||||
<>
|
||||
{mrState !== MessageRequestState.default ? (
|
||||
<MessageRequestActionsConfirmation
|
||||
i18n={i18n}
|
||||
onBlock={onBlock}
|
||||
onBlockAndDelete={onBlockAndDelete}
|
||||
onUnblock={() => {
|
||||
throw new Error(
|
||||
'Should not be able to unblock from MandatoryProfileSharingActions'
|
||||
);
|
||||
}}
|
||||
onDelete={onDelete}
|
||||
name={name}
|
||||
profileName={profileName}
|
||||
phoneNumber={phoneNumber}
|
||||
title={title}
|
||||
conversationType={conversationType}
|
||||
state={mrState}
|
||||
onChangeState={setMrState}
|
||||
/>
|
||||
) : null}
|
||||
<div className="module-message-request-actions">
|
||||
<p className="module-message-request-actions__message">
|
||||
<Intl
|
||||
i18n={i18n}
|
||||
id={`MessageRequests--profile-sharing--${conversationType}`}
|
||||
components={{
|
||||
firstName: (
|
||||
<strong
|
||||
key="name"
|
||||
className="module-message-request-actions__message__name"
|
||||
>
|
||||
<ContactName
|
||||
name={name}
|
||||
profileName={profileName}
|
||||
phoneNumber={phoneNumber}
|
||||
title={firstName || title}
|
||||
i18n={i18n}
|
||||
/>
|
||||
</strong>
|
||||
),
|
||||
learnMore: (
|
||||
<a
|
||||
href="https://support.signal.org/hc/articles/360007459591"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="module-message-request-actions__message__learn-more"
|
||||
>
|
||||
{i18n('MessageRequests--learn-more')}
|
||||
</a>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</p>
|
||||
<div className="module-message-request-actions__buttons">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
setMrState(MessageRequestState.blocking);
|
||||
}}
|
||||
tabIndex={0}
|
||||
className={classNames(
|
||||
'module-message-request-actions__buttons__button',
|
||||
'module-message-request-actions__buttons__button--deny'
|
||||
)}
|
||||
>
|
||||
{i18n('MessageRequests--block')}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
setMrState(MessageRequestState.deleting);
|
||||
}}
|
||||
tabIndex={0}
|
||||
className={classNames(
|
||||
'module-message-request-actions__buttons__button',
|
||||
'module-message-request-actions__buttons__button--deny'
|
||||
)}
|
||||
>
|
||||
{i18n('MessageRequests--delete')}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={onAccept}
|
||||
tabIndex={0}
|
||||
className={classNames(
|
||||
'module-message-request-actions__buttons__button',
|
||||
'module-message-request-actions__buttons__button--accept'
|
||||
)}
|
||||
>
|
||||
{i18n('MessageRequests--continue')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -45,6 +45,7 @@ const createProps = (overrideProps: Partial<Props> = {}): Props => ({
|
|||
authorTitle: text('authorTitle', overrideProps.authorTitle || ''),
|
||||
bodyRanges: overrideProps.bodyRanges,
|
||||
canReply: true,
|
||||
canDownload: true,
|
||||
canDeleteForEveryone: overrideProps.canDeleteForEveryone || false,
|
||||
clearSelectedMessage: action('clearSelectedMessage'),
|
||||
collapseMetadata: overrideProps.collapseMetadata,
|
||||
|
|
|
@ -137,6 +137,7 @@ export type PropsData = {
|
|||
deletedForEveryone?: boolean;
|
||||
|
||||
canReply: boolean;
|
||||
canDownload: boolean;
|
||||
canDeleteForEveryone: boolean;
|
||||
bodyRanges?: BodyRangesType;
|
||||
};
|
||||
|
@ -1159,6 +1160,7 @@ export class Message extends React.PureComponent<Props, State> {
|
|||
): JSX.Element | null {
|
||||
const {
|
||||
attachments,
|
||||
canDownload,
|
||||
canReply,
|
||||
direction,
|
||||
disableMenu,
|
||||
|
@ -1294,7 +1296,7 @@ export class Message extends React.PureComponent<Props, State> {
|
|||
)}
|
||||
>
|
||||
{canReply ? reactButton : null}
|
||||
{canReply ? downloadButton : null}
|
||||
{canDownload ? downloadButton : null}
|
||||
{canReply ? replyButton : null}
|
||||
{menuButton}
|
||||
</div>
|
||||
|
@ -1328,6 +1330,7 @@ export class Message extends React.PureComponent<Props, State> {
|
|||
public renderContextMenu(triggerId: string): JSX.Element {
|
||||
const {
|
||||
attachments,
|
||||
canDownload,
|
||||
canReply,
|
||||
deleteMessage,
|
||||
deleteMessageForEveryone,
|
||||
|
@ -1349,7 +1352,8 @@ export class Message extends React.PureComponent<Props, State> {
|
|||
|
||||
const menu = (
|
||||
<ContextMenu id={triggerId}>
|
||||
{!isSticker &&
|
||||
{canDownload &&
|
||||
!isSticker &&
|
||||
!multipleAttachments &&
|
||||
!isTapToView &&
|
||||
attachments &&
|
||||
|
|
|
@ -17,6 +17,7 @@ const defaultMessage: MessageProps = {
|
|||
authorTitle: 'Max',
|
||||
canReply: true,
|
||||
canDeleteForEveryone: true,
|
||||
canDownload: true,
|
||||
clearSelectedMessage: () => null,
|
||||
conversationId: 'my-convo',
|
||||
conversationType: 'direct',
|
||||
|
|
|
@ -20,6 +20,7 @@ const defaultMessageProps: MessagesProps = {
|
|||
authorTitle: 'Person X',
|
||||
canReply: true,
|
||||
canDeleteForEveryone: true,
|
||||
canDownload: true,
|
||||
clearSelectedMessage: () => null,
|
||||
conversationId: 'conversationId',
|
||||
conversationType: 'direct', // override
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue