Allow manually retrying attachment downloads

This commit is contained in:
Fedor Indutny 2022-05-23 16:07:41 -07:00 committed by GitHub
parent 59b45399e4
commit dfc310805a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 265 additions and 58 deletions

View file

@ -19,6 +19,7 @@ import {
IMAGE_PNG,
IMAGE_WEBP,
VIDEO_MP4,
LONG_MESSAGE,
stringToMIMEType,
IMAGE_GIF,
} from '../../types/MIME';
@ -205,7 +206,11 @@ const createProps = (overrideProps: Partial<Props> = {}): Props => ({
status: overrideProps.status || 'sent',
text: overrideProps.text || text('text', ''),
textDirection: overrideProps.textDirection || TextDirection.Default,
textPending: boolean('textPending', overrideProps.textPending || false),
textAttachment: overrideProps.textAttachment || {
contentType: LONG_MESSAGE,
size: 123,
pending: boolean('textPending', false),
},
theme: ThemeType.light,
timestamp: number('timestamp', overrideProps.timestamp || Date.now()),
});
@ -420,7 +425,27 @@ story.add('Will expire but still sending', () => {
story.add('Pending', () => {
const props = createProps({
text: 'Hello there from a pal! I am sending a long message so that it will wrap a bit, since I like that look.',
textPending: true,
textAttachment: {
contentType: LONG_MESSAGE,
size: 123,
pending: true,
},
});
return renderBothDirections(props);
});
story.add('Long body can be downloaded', () => {
const props = createProps({
text: 'Hello there from a pal! I am sending a long message so that it will wrap a bit, since I like that look.',
textAttachment: {
contentType: LONG_MESSAGE,
size: 123,
pending: false,
error: true,
digest: 'abc',
key: 'def',
},
});
return renderBothDirections(props);

View file

@ -201,7 +201,7 @@ export type PropsData = {
displayLimit?: number;
text?: string;
textDirection: TextDirection;
textPending?: boolean;
textAttachment?: AttachmentType;
isSticker?: boolean;
isSelected?: boolean;
isSelectedCounter?: number;
@ -818,7 +818,7 @@ export class Message extends React.PureComponent<Props, State> {
status,
i18n,
text,
textPending,
textAttachment,
timestamp,
id,
showMessageDetail,
@ -842,7 +842,7 @@ export class Message extends React.PureComponent<Props, State> {
onWidthMeasured={isInline ? this.updateMetadataWidth : undefined}
showMessageDetail={showMessageDetail}
status={status}
textPending={textPending}
textPending={textAttachment?.pending}
timestamp={timestamp}
/>
);
@ -903,7 +903,7 @@ export class Message extends React.PureComponent<Props, State> {
shouldCollapseBelow,
status,
text,
textPending,
textAttachment,
theme,
timestamp,
} = this.props;
@ -1031,7 +1031,7 @@ export class Message extends React.PureComponent<Props, State> {
played,
showMessageDetail,
status,
textPending,
textPending: textAttachment?.pending,
timestamp,
kickOffAttachmentDownload() {
@ -1206,6 +1206,7 @@ export class Message extends React.PureComponent<Props, State> {
width={72}
url={first.image.url}
attachment={first.image}
blurHash={first.image.blurHash}
onError={this.handleImageError}
i18n={i18n}
onClick={onPreviewImageClick}
@ -1699,10 +1700,11 @@ export class Message extends React.PureComponent<Props, State> {
id,
messageExpanded,
openConversation,
kickOffAttachmentDownload,
status,
text,
textDirection,
textPending,
textAttachment,
} = this.props;
const { metadataWidth } = this.state;
const isRTL = textDirection === TextDirection.RightToLeft;
@ -1741,8 +1743,17 @@ export class Message extends React.PureComponent<Props, State> {
id={id}
messageExpanded={messageExpanded}
openConversation={openConversation}
kickOffBodyDownload={() => {
if (!textAttachment) {
return;
}
kickOffAttachmentDownload({
attachment: textAttachment,
messageId: id,
});
}}
text={contents || ''}
textPending={textPending}
textAttachment={textAttachment}
/>
{!isRTL &&
this.getMetadataPlacement() === MetadataPlacement.InlineWithText && (

View file

@ -25,7 +25,9 @@ const createProps = (overrideProps: Partial<Props> = {}): Props => ({
direction: 'incoming',
i18n,
text: text('text', overrideProps.text || ''),
textPending: boolean('textPending', overrideProps.textPending || false),
textAttachment: overrideProps.textAttachment || {
pending: boolean('textPending', false),
},
});
story.add('Links Enabled', () => {
@ -91,7 +93,9 @@ story.add('Jumbomoji Disabled by Text', () => {
story.add('Text Pending', () => {
const props = createProps({
text: 'Check out https://www.signal.org',
textPending: true,
textAttachment: {
pending: true,
},
});
return <MessageBody {...props} />;

View file

@ -4,6 +4,8 @@
import type { KeyboardEvent } from 'react';
import React from 'react';
import type { AttachmentType } from '../../types/Attachment';
import { canBeDownloaded } from '../../types/Attachment';
import type { SizeClassType } from '../emoji/lib';
import { getSizeClass } from '../emoji/lib';
import { AtMentionify } from './AtMentionify';
@ -25,7 +27,7 @@ type OpenConversationActionType = (
export type Props = {
direction?: 'incoming' | 'outgoing';
text: string;
textPending?: boolean;
textAttachment?: Pick<AttachmentType, 'pending' | 'digest' | 'key'>;
/** If set, all emoji will be the same size. Otherwise, just one emoji will be large. */
disableJumbomoji?: boolean;
/** If set, links will be left alone instead of turned into clickable `<a>` tags. */
@ -34,6 +36,7 @@ export type Props = {
bodyRanges?: BodyRangesType;
onIncreaseTextLength?: () => unknown;
openConversation?: OpenConversationActionType;
kickOffBodyDownload?: () => void;
};
const renderEmoji = ({
@ -71,10 +74,12 @@ export function MessageBody({
onIncreaseTextLength,
openConversation,
text,
textPending,
textAttachment,
kickOffBodyDownload,
}: Props): JSX.Element {
const hasReadMore = Boolean(onIncreaseTextLength);
const textWithSuffix = textPending || hasReadMore ? `${text}...` : text;
const textWithSuffix =
textAttachment?.pending || hasReadMore ? `${text}...` : text;
const sizeClass = disableJumbomoji ? undefined : getSizeClass(text);
const processedText = AtMentionify.preprocessMentions(
@ -103,6 +108,40 @@ export function MessageBody({
);
};
let pendingContent: React.ReactNode;
if (hasReadMore) {
pendingContent = null;
} else if (textAttachment?.pending) {
pendingContent = (
<span className="MessageBody__highlight"> {i18n('downloading')}</span>
);
} else if (
textAttachment &&
canBeDownloaded(textAttachment) &&
kickOffBodyDownload
) {
pendingContent = (
<span>
{' '}
<button
className="MessageBody__download-body"
onClick={() => {
kickOffBodyDownload();
}}
onKeyDown={(ev: KeyboardEvent) => {
if (ev.key === 'Space' || ev.key === 'Enter') {
kickOffBodyDownload();
}
}}
tabIndex={0}
type="button"
>
{i18n('downloadFullMessage')}
</button>
</span>
);
}
return (
<span>
{disableLinks ? (
@ -127,9 +166,7 @@ export function MessageBody({
}}
/>
)}
{textPending ? (
<span className="MessageBody__highlight"> {i18n('downloading')}</span>
) : null}
{pendingContent}
{onIncreaseTextLength ? (
<button
className="MessageBody__read-more"

View file

@ -11,11 +11,12 @@ export type Props = Pick<
MessageBodyPropsType,
| 'direction'
| 'text'
| 'textPending'
| 'textAttachment'
| 'disableLinks'
| 'i18n'
| 'bodyRanges'
| 'openConversation'
| 'kickOffBodyDownload'
> & {
id: string;
displayLimit?: number;
@ -39,8 +40,9 @@ export function MessageBodyReadMore({
id,
messageExpanded,
openConversation,
kickOffBodyDownload,
text,
textPending,
textAttachment,
}: Props): JSX.Element {
const maxLength = displayLimit || INITIAL_LENGTH;
@ -64,8 +66,9 @@ export function MessageBodyReadMore({
i18n={i18n}
onIncreaseTextLength={onIncreaseTextLength}
openConversation={openConversation}
kickOffBodyDownload={kickOffBodyDownload}
text={slicedText}
textPending={textPending}
textAttachment={textAttachment}
/>
);
}