Show attachment download progress, new stop button to cancel

Co-authored-by: Jamie Kyle <jamie@signal.org>
This commit is contained in:
Scott Nonnenberg 2024-12-10 08:54:18 +10:00 committed by GitHub
parent 025841e5bb
commit 2741fbb5d2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
78 changed files with 2192 additions and 562 deletions

View file

@ -20,6 +20,7 @@ import {
import { Image, CurveType } from './Image';
import type { LocalizerType, ThemeType } from '../../types/Util';
import { AttachmentDetailPill } from './AttachmentDetailPill';
export type DirectionType = 'incoming' | 'outgoing';
@ -39,7 +40,9 @@ export type Props = {
theme?: ThemeType;
onError: () => void;
onClick?: (attachment: AttachmentType) => void;
showVisualAttachment: (attachment: AttachmentType) => void;
cancelDownload: () => void;
startDownload: () => void;
};
const GAP = 1;
@ -108,7 +111,9 @@ export function ImageGrid({
isSticker,
stickerSize,
onError,
onClick,
showVisualAttachment,
cancelDownload,
startDownload,
shouldCollapseAbove,
shouldCollapseBelow,
tabIndex,
@ -127,10 +132,46 @@ export function ImageGrid({
const withBottomOverlay = Boolean(bottomOverlay && !withContentBelow);
const startDownloadClick = React.useCallback(
(event: React.MouseEvent) => {
if (startDownload) {
event.preventDefault();
event.stopPropagation();
startDownload();
}
},
[startDownload]
);
const startDownloadKeyDown = React.useCallback(
(event: React.KeyboardEvent<HTMLButtonElement>) => {
if (startDownload && (event.key === 'Enter' || event.key === 'Space')) {
event.preventDefault();
event.stopPropagation();
startDownload();
}
},
[startDownload]
);
if (!attachments || !attachments.length) {
return null;
}
const detailPill = (
<AttachmentDetailPill
attachments={attachments}
i18n={i18n}
startDownload={startDownload}
cancelDownload={cancelDownload}
/>
);
const downloadPill = renderDownloadPill({
attachments,
i18n,
startDownloadClick,
startDownloadKeyDown,
});
if (attachments.length === 1 || !areAllAttachmentsVisual(attachments)) {
const { height, width } = getImageDimensions(
attachments[0],
@ -165,9 +206,12 @@ export function ImageGrid({
getUrl(attachments[0]) ?? attachments[0].thumbnailFromBackup?.url
}
tabIndex={tabIndex}
onClick={onClick}
showVisualAttachment={showVisualAttachment}
cancelDownload={cancelDownload}
startDownload={startDownload}
onError={onError}
/>
{detailPill}
</div>
);
}
@ -190,7 +234,9 @@ export function ImageGrid({
width={150}
cropWidth={GAP}
url={getThumbnailUrl(attachments[0])}
onClick={onClick}
showVisualAttachment={showVisualAttachment}
cancelDownload={cancelDownload}
startDownload={downloadPill ? undefined : startDownload}
onError={onError}
/>
<Image
@ -207,9 +253,13 @@ export function ImageGrid({
width={150}
attachment={attachments[1]}
url={getThumbnailUrl(attachments[1])}
onClick={onClick}
showVisualAttachment={showVisualAttachment}
cancelDownload={cancelDownload}
startDownload={downloadPill ? undefined : startDownload}
onError={onError}
/>
{detailPill}
{downloadPill}
</div>
);
}
@ -232,7 +282,9 @@ export function ImageGrid({
width={200}
cropWidth={GAP}
url={getUrl(attachments[0])}
onClick={onClick}
showVisualAttachment={showVisualAttachment}
cancelDownload={cancelDownload}
startDownload={downloadPill ? undefined : startDownload}
onError={onError}
/>
<div className="module-image-grid__column">
@ -248,7 +300,9 @@ export function ImageGrid({
attachment={attachments[1]}
playIconOverlay={isVideoAttachment(attachments[1])}
url={getThumbnailUrl(attachments[1])}
onClick={onClick}
showVisualAttachment={showVisualAttachment}
cancelDownload={cancelDownload}
startDownload={downloadPill ? undefined : startDownload}
onError={onError}
/>
<Image
@ -264,10 +318,14 @@ export function ImageGrid({
attachment={attachments[2]}
playIconOverlay={isVideoAttachment(attachments[2])}
url={getThumbnailUrl(attachments[2])}
onClick={onClick}
showVisualAttachment={showVisualAttachment}
cancelDownload={cancelDownload}
startDownload={downloadPill ? undefined : startDownload}
onError={onError}
/>
</div>
{detailPill}
{downloadPill}
</div>
);
}
@ -291,7 +349,9 @@ export function ImageGrid({
cropHeight={GAP}
cropWidth={GAP}
url={getThumbnailUrl(attachments[0])}
onClick={onClick}
showVisualAttachment={showVisualAttachment}
cancelDownload={cancelDownload}
startDownload={downloadPill ? undefined : startDownload}
onError={onError}
/>
<Image
@ -307,7 +367,9 @@ export function ImageGrid({
cropHeight={GAP}
attachment={attachments[1]}
url={getThumbnailUrl(attachments[1])}
onClick={onClick}
showVisualAttachment={showVisualAttachment}
cancelDownload={cancelDownload}
startDownload={downloadPill ? undefined : startDownload}
onError={onError}
/>
</div>
@ -326,7 +388,9 @@ export function ImageGrid({
cropWidth={GAP}
attachment={attachments[2]}
url={getThumbnailUrl(attachments[2])}
onClick={onClick}
showVisualAttachment={showVisualAttachment}
cancelDownload={cancelDownload}
startDownload={downloadPill ? undefined : startDownload}
onError={onError}
/>
<Image
@ -342,11 +406,15 @@ export function ImageGrid({
width={150}
attachment={attachments[3]}
url={getThumbnailUrl(attachments[3])}
onClick={onClick}
showVisualAttachment={showVisualAttachment}
cancelDownload={cancelDownload}
startDownload={downloadPill ? undefined : startDownload}
onError={onError}
/>
</div>
</div>
{detailPill}
{downloadPill}
</div>
);
}
@ -372,7 +440,9 @@ export function ImageGrid({
width={150}
cropWidth={GAP}
url={getThumbnailUrl(attachments[0])}
onClick={onClick}
showVisualAttachment={showVisualAttachment}
cancelDownload={cancelDownload}
startDownload={downloadPill ? undefined : startDownload}
onError={onError}
/>
<Image
@ -386,7 +456,9 @@ export function ImageGrid({
width={150}
attachment={attachments[1]}
url={getThumbnailUrl(attachments[1])}
onClick={onClick}
showVisualAttachment={showVisualAttachment}
cancelDownload={cancelDownload}
startDownload={downloadPill ? undefined : startDownload}
onError={onError}
/>
</div>
@ -405,7 +477,9 @@ export function ImageGrid({
cropWidth={GAP}
attachment={attachments[2]}
url={getThumbnailUrl(attachments[2])}
onClick={onClick}
showVisualAttachment={showVisualAttachment}
cancelDownload={cancelDownload}
startDownload={downloadPill ? undefined : startDownload}
onError={onError}
/>
<Image
@ -421,7 +495,9 @@ export function ImageGrid({
cropWidth={GAP}
attachment={attachments[3]}
url={getThumbnailUrl(attachments[3])}
onClick={onClick}
showVisualAttachment={showVisualAttachment}
cancelDownload={cancelDownload}
startDownload={downloadPill ? undefined : startDownload}
onError={onError}
/>
<Image
@ -439,11 +515,51 @@ export function ImageGrid({
overlayText={moreMessagesOverlayText}
attachment={attachments[4]}
url={getThumbnailUrl(attachments[4])}
onClick={onClick}
showVisualAttachment={showVisualAttachment}
cancelDownload={undefined}
startDownload={undefined}
onError={onError}
/>
</div>
</div>
{detailPill}
{downloadPill}
</div>
);
}
function renderDownloadPill({
attachments,
i18n,
startDownloadClick,
startDownloadKeyDown,
}: {
attachments: ReadonlyArray<AttachmentForUIType>;
i18n: LocalizerType;
startDownloadClick: (event: React.MouseEvent) => void;
startDownloadKeyDown: (event: React.KeyboardEvent<HTMLButtonElement>) => void;
}): JSX.Element | null {
const downloadedOrPending = attachments.some(
attachment => attachment.path || attachment.pending
);
if (downloadedOrPending) {
return null;
}
return (
<button
type="button"
className="module-image-grid__download-pill"
aria-label={i18n('icu:startDownload')}
onClick={startDownloadClick}
onKeyDown={startDownloadKeyDown}
>
<div className="module-image-grid__download_pill__icon-wrapper">
<div className="module-image-grid__download_pill__download-icon" />
</div>
<div className="module-image-grid__download_pill__text-wrapper">
{i18n('icu:downloadNItems', { count: attachments.length })}
</div>
</button>
);
}