Allow manually retrying attachment downloads
This commit is contained in:
parent
59b45399e4
commit
dfc310805a
16 changed files with 265 additions and 58 deletions
|
@ -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);
|
||||
|
|
|
@ -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 && (
|
||||
|
|
|
@ -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} />;
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue