Support for message retry requests

This commit is contained in:
Scott Nonnenberg 2021-05-28 12:11:19 -07:00 committed by GitHub
parent 28f016ce48
commit ee513a1965
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
37 changed files with 1996 additions and 359 deletions

View file

@ -4,6 +4,8 @@
import * as React from 'react';
import classNames from 'classnames';
import { Modal } from '../Modal';
import { LocalizerType } from '../../types/Util';
export type PropsType = {
@ -12,47 +14,48 @@ export type PropsType = {
onClose: () => unknown;
};
// TODO: This should use <Modal>. See DESKTOP-1038.
export function ChatSessionRefreshedDialog(
props: PropsType
): React.ReactElement {
const { i18n, contactSupport, onClose } = props;
return (
<div className="module-chat-session-refreshed-dialog">
<div className="module-chat-session-refreshed-dialog__image">
<img
src="images/chat-session-refresh.svg"
height="110"
width="200"
alt=""
/>
<Modal hasXButton={false} i18n={i18n}>
<div className="module-chat-session-refreshed-dialog">
<div className="module-chat-session-refreshed-dialog__image">
<img
src="images/chat-session-refresh.svg"
height="110"
width="200"
alt=""
/>
</div>
<div className="module-chat-session-refreshed-dialog__title">
{i18n('ChatRefresh--notification')}
</div>
<div className="module-chat-session-refreshed-dialog__description">
{i18n('ChatRefresh--summary')}
</div>
<div className="module-chat-session-refreshed-dialog__buttons">
<button
type="button"
onClick={contactSupport}
className={classNames(
'module-chat-session-refreshed-dialog__button',
'module-chat-session-refreshed-dialog__button--secondary'
)}
>
{i18n('ChatRefresh--contactSupport')}
</button>
<button
type="button"
onClick={onClose}
className="module-chat-session-refreshed-dialog__button"
>
{i18n('Confirmation--confirm')}
</button>
</div>
</div>
<div className="module-chat-session-refreshed-dialog__title">
{i18n('ChatRefresh--notification')}
</div>
<div className="module-chat-session-refreshed-dialog__description">
{i18n('ChatRefresh--summary')}
</div>
<div className="module-chat-session-refreshed-dialog__buttons">
<button
type="button"
onClick={contactSupport}
className={classNames(
'module-chat-session-refreshed-dialog__button',
'module-chat-session-refreshed-dialog__button--secondary'
)}
>
{i18n('ChatRefresh--contactSupport')}
</button>
<button
type="button"
onClick={onClose}
className="module-chat-session-refreshed-dialog__button"
>
{i18n('Confirmation--confirm')}
</button>
</div>
</div>
</Modal>
);
}

View file

@ -5,7 +5,6 @@ import React, { useCallback, useState, ReactElement } from 'react';
import { LocalizerType } from '../../types/Util';
import { ModalHost } from '../ModalHost';
import { ChatSessionRefreshedDialog } from './ChatSessionRefreshedDialog';
type PropsHousekeepingType = {
@ -50,13 +49,11 @@ export function ChatSessionRefreshedNotification(
{i18n('ChatRefresh--learnMore')}
</button>
{isDialogOpen ? (
<ModalHost onClose={closeDialog}>
<ChatSessionRefreshedDialog
onClose={closeDialog}
contactSupport={wrappedContactSupport}
i18n={i18n}
/>
</ModalHost>
<ChatSessionRefreshedDialog
onClose={closeDialog}
contactSupport={wrappedContactSupport}
i18n={i18n}
/>
) : null}
</div>
);

View file

@ -0,0 +1,27 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import * as React from 'react';
import { storiesOf } from '@storybook/react';
import { action } from '@storybook/addon-actions';
import { setup as setupI18n } from '../../../js/modules/i18n';
import enMessages from '../../../_locales/en/messages.json';
import { DeliveryIssueDialog } from './DeliveryIssueDialog';
import { getDefaultConversation } from '../../test-both/helpers/getDefaultConversation';
const i18n = setupI18n('en', enMessages);
const sender = getDefaultConversation();
storiesOf('Components/Conversation/DeliveryIssueDialog', module).add(
'Default',
() => {
return (
<DeliveryIssueDialog
i18n={i18n}
sender={sender}
onClose={action('onClose')}
/>
);
}
);

View file

@ -0,0 +1,57 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import * as React from 'react';
import { ConversationType } from '../../state/ducks/conversations';
import { Modal } from '../Modal';
import { Intl } from '../Intl';
import { Emojify } from './Emojify';
import { LocalizerType } from '../../types/Util';
export type PropsType = {
i18n: LocalizerType;
sender: ConversationType;
onClose: () => unknown;
};
export function DeliveryIssueDialog(props: PropsType): React.ReactElement {
const { i18n, sender, onClose } = props;
return (
<Modal hasXButton={false} i18n={i18n}>
<div className="module-delivery-issue-dialog">
<div className="module-delivery-issue-dialog__image">
<img
src="images/delivery-issue.svg"
height="110"
width="200"
alt=""
/>
</div>
<div className="module-delivery-issue-dialog__title">
{i18n('DeliveryIssue--title')}
</div>
<div className="module-delivery-issue-dialog__description">
<Intl
id="DeliveryIssue--summary"
components={{
sender: <Emojify text={sender.title} />,
}}
i18n={i18n}
/>
</div>
<div className="module-delivery-issue-dialog__buttons">
<button
type="button"
onClick={onClose}
className="module-delivery-issue-dialog__button"
>
{i18n('Confirmation--confirm')}
</button>
</div>
</div>
</Modal>
);
}

View file

@ -0,0 +1,23 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import * as React from 'react';
import { storiesOf } from '@storybook/react';
import { setup as setupI18n } from '../../../js/modules/i18n';
import enMessages from '../../../_locales/en/messages.json';
import { DeliveryIssueNotification } from './DeliveryIssueNotification';
import { getDefaultConversation } from '../../test-both/helpers/getDefaultConversation';
const i18n = setupI18n('en', enMessages);
const sender = getDefaultConversation();
storiesOf('Components/Conversation/DeliveryIssueNotification', module).add(
'Default',
() => {
return <DeliveryIssueNotification i18n={i18n} sender={sender} />;
}
);

View file

@ -0,0 +1,68 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React, { useCallback, useState, ReactElement } from 'react';
import { ConversationType } from '../../state/ducks/conversations';
import { LocalizerType } from '../../types/Util';
import { Intl } from '../Intl';
import { Emojify } from './Emojify';
import { DeliveryIssueDialog } from './DeliveryIssueDialog';
export type PropsDataType = {
sender?: ConversationType;
};
type PropsHousekeepingType = {
i18n: LocalizerType;
};
export type PropsType = PropsDataType & PropsHousekeepingType;
export function DeliveryIssueNotification(
props: PropsType
): ReactElement | null {
const { i18n, sender } = props;
const [isDialogOpen, setIsDialogOpen] = useState<boolean>(false);
const openDialog = useCallback(() => {
setIsDialogOpen(true);
}, [setIsDialogOpen]);
const closeDialog = useCallback(() => {
setIsDialogOpen(false);
}, [setIsDialogOpen]);
if (!sender) {
return null;
}
return (
<div className="module-delivery-issue-notification">
<div className="module-delivery-issue-notification__first-line">
<span className="module-delivery-issue-notification__icon" />
<Intl
id="DeliveryIssue--notification"
components={{
sender: <Emojify text={sender.firstName || sender.title} />,
}}
i18n={i18n}
/>
</div>
<button
type="button"
onClick={openDialog}
className="module-delivery-issue-notification__button"
>
{i18n('DeliveryIssue--learnMore')}
</button>
{isDialogOpen ? (
<DeliveryIssueDialog
i18n={i18n}
sender={sender}
onClose={closeDialog}
/>
) : null}
</div>
);
}

View file

@ -11,6 +11,7 @@ import { setup as setupI18n } from '../../../js/modules/i18n';
import enMessages from '../../../_locales/en/messages.json';
import { PropsType as TimelineItemProps, TimelineItem } from './TimelineItem';
import { CallMode } from '../../types/Calling';
import { getDefaultConversation } from '../../test-both/helpers/getDefaultConversation';
const i18n = setupI18n('en', enMessages);
@ -99,9 +100,19 @@ storiesOf('Components/Conversation/TimelineItem', module)
{
type: 'timerNotification',
data: {
type: 'fromOther',
phoneNumber: '(202) 555-0000',
timespan: '1 hour',
expireTimer: 60,
...getDefaultConversation(),
type: 'fromOther',
},
},
{
type: 'chatSessionRefreshed',
},
{
type: 'deliveryIssue',
data: {
sender: getDefaultConversation(),
},
},
{
@ -367,7 +378,6 @@ storiesOf('Components/Conversation/TimelineItem', module)
item={item as TimelineItemProps['item']}
i18n={i18n}
/>
<hr />
</React.Fragment>
))}
</>

View file

@ -19,6 +19,10 @@ import {
ChatSessionRefreshedNotification,
PropsActionsType as PropsChatSessionRefreshedActionsType,
} from './ChatSessionRefreshedNotification';
import {
DeliveryIssueNotification,
PropsDataType as DeliveryIssueProps,
} from './DeliveryIssueNotification';
import { CallingNotificationType } from '../../util/callingNotification';
import { InlineNotificationWrapper } from './InlineNotificationWrapper';
import {
@ -66,6 +70,10 @@ type ChatSessionRefreshedType = {
type: 'chatSessionRefreshed';
data: null;
};
type DeliveryIssueType = {
type: 'deliveryIssue';
data: DeliveryIssueProps;
};
type LinkNotificationType = {
type: 'linkNotification';
data: null;
@ -114,6 +122,7 @@ type ProfileChangeNotificationType = {
export type TimelineItemType =
| CallHistoryType
| ChatSessionRefreshedType
| DeliveryIssueType
| GroupNotificationType
| GroupV1MigrationType
| GroupV2ChangeType
@ -203,6 +212,8 @@ export class TimelineItem extends React.PureComponent<PropsType> {
i18n={i18n}
/>
);
} else if (item.type === 'deliveryIssue') {
notification = <DeliveryIssueNotification {...item.data} i18n={i18n} />;
} else if (item.type === 'linkNotification') {
notification = (
<div className="module-message-unsynced">