add option to download audio/video file (#2253)
* add option to download audio file * add button to download video
This commit is contained in:
parent
9bb30fbd92
commit
d8009978e5
2 changed files with 99 additions and 12 deletions
src/app/components/message
|
@ -1,22 +1,81 @@
|
|||
import { Badge, Box, Text, as, toRem } from 'folds';
|
||||
import React from 'react';
|
||||
import { Badge, Box, Icon, IconButton, Icons, Spinner, Text, as, toRem } from 'folds';
|
||||
import React, { ReactNode, useCallback } from 'react';
|
||||
import { EncryptedAttachmentInfo } from 'browser-encrypt-attachment';
|
||||
import FileSaver from 'file-saver';
|
||||
import { mimeTypeToExt } from '../../utils/mimeTypes';
|
||||
import { useMatrixClient } from '../../hooks/useMatrixClient';
|
||||
import { useMediaAuthentication } from '../../hooks/useMediaAuthentication';
|
||||
import { AsyncStatus, useAsyncCallback } from '../../hooks/useAsyncCallback';
|
||||
import {
|
||||
decryptFile,
|
||||
downloadEncryptedMedia,
|
||||
downloadMedia,
|
||||
mxcUrlToHttp,
|
||||
} from '../../utils/matrix';
|
||||
|
||||
const badgeStyles = { maxWidth: toRem(100) };
|
||||
|
||||
type FileDownloadButtonProps = {
|
||||
filename: string;
|
||||
url: string;
|
||||
mimeType: string;
|
||||
encInfo?: EncryptedAttachmentInfo;
|
||||
};
|
||||
export function FileDownloadButton({ filename, url, mimeType, encInfo }: FileDownloadButtonProps) {
|
||||
const mx = useMatrixClient();
|
||||
const useAuthentication = useMediaAuthentication();
|
||||
|
||||
const [downloadState, download] = useAsyncCallback(
|
||||
useCallback(async () => {
|
||||
const mediaUrl = mxcUrlToHttp(mx, url, useAuthentication) ?? url;
|
||||
const fileContent = encInfo
|
||||
? await downloadEncryptedMedia(mediaUrl, (encBuf) => decryptFile(encBuf, mimeType, encInfo))
|
||||
: await downloadMedia(mediaUrl);
|
||||
|
||||
const fileURL = URL.createObjectURL(fileContent);
|
||||
FileSaver.saveAs(fileURL, filename);
|
||||
return fileURL;
|
||||
}, [mx, url, useAuthentication, mimeType, encInfo, filename])
|
||||
);
|
||||
|
||||
const downloading = downloadState.status === AsyncStatus.Loading;
|
||||
const hasError = downloadState.status === AsyncStatus.Error;
|
||||
return (
|
||||
<IconButton
|
||||
disabled={downloading}
|
||||
onClick={download}
|
||||
variant={hasError ? 'Critical' : 'SurfaceVariant'}
|
||||
size="300"
|
||||
radii="300"
|
||||
>
|
||||
{downloading ? (
|
||||
<Spinner size="100" variant={hasError ? 'Critical' : 'Secondary'} />
|
||||
) : (
|
||||
<Icon size="100" src={Icons.Download} />
|
||||
)}
|
||||
</IconButton>
|
||||
);
|
||||
}
|
||||
|
||||
export type FileHeaderProps = {
|
||||
body: string;
|
||||
mimeType: string;
|
||||
after?: ReactNode;
|
||||
};
|
||||
export const FileHeader = as<'div', FileHeaderProps>(({ body, mimeType, ...props }, ref) => (
|
||||
export const FileHeader = as<'div', FileHeaderProps>(({ body, mimeType, after, ...props }, ref) => (
|
||||
<Box alignItems="Center" gap="200" grow="Yes" {...props} ref={ref}>
|
||||
<Badge style={badgeStyles} variant="Secondary" radii="Pill">
|
||||
<Text size="O400" truncate>
|
||||
{mimeTypeToExt(mimeType)}
|
||||
<Box shrink="No">
|
||||
<Badge style={badgeStyles} variant="Secondary" radii="Pill">
|
||||
<Text size="O400" truncate>
|
||||
{mimeTypeToExt(mimeType)}
|
||||
</Text>
|
||||
</Badge>
|
||||
</Box>
|
||||
<Box grow="Yes">
|
||||
<Text size="T300" truncate>
|
||||
{body}
|
||||
</Text>
|
||||
</Badge>
|
||||
<Text size="T300" truncate>
|
||||
{body}
|
||||
</Text>
|
||||
</Box>
|
||||
{after}
|
||||
</Box>
|
||||
));
|
||||
|
|
|
@ -28,7 +28,7 @@ import {
|
|||
import { FALLBACK_MIMETYPE, getBlobSafeMimeType } from '../../utils/mimeTypes';
|
||||
import { parseGeoUri, scaleYDimension } from '../../utils/common';
|
||||
import { Attachment, AttachmentBox, AttachmentContent, AttachmentHeader } from './attachment';
|
||||
import { FileHeader } from './FileHeader';
|
||||
import { FileHeader, FileDownloadButton } from './FileHeader';
|
||||
|
||||
export function MBadEncrypted() {
|
||||
return (
|
||||
|
@ -243,8 +243,24 @@ export function MVideo({ content, renderAsFile, renderVideoContent, outlined }:
|
|||
|
||||
const height = scaleYDimension(videoInfo.w || 400, 400, videoInfo.h || 400);
|
||||
|
||||
const filename = content.filename ?? content.body ?? 'Video';
|
||||
|
||||
return (
|
||||
<Attachment outlined={outlined}>
|
||||
<AttachmentHeader>
|
||||
<FileHeader
|
||||
body={filename}
|
||||
mimeType={safeMimeType}
|
||||
after={
|
||||
<FileDownloadButton
|
||||
filename={filename}
|
||||
url={mxcUrl}
|
||||
mimeType={safeMimeType}
|
||||
encInfo={content.file}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</AttachmentHeader>
|
||||
<AttachmentBox
|
||||
style={{
|
||||
height: toRem(height < 48 ? 48 : height),
|
||||
|
@ -286,10 +302,22 @@ export function MAudio({ content, renderAsFile, renderAudioContent, outlined }:
|
|||
return <BrokenContent />;
|
||||
}
|
||||
|
||||
const filename = content.filename ?? content.body ?? 'Audio';
|
||||
return (
|
||||
<Attachment outlined={outlined}>
|
||||
<AttachmentHeader>
|
||||
<FileHeader body={content.filename ?? content.body ?? 'Audio'} mimeType={safeMimeType} />
|
||||
<FileHeader
|
||||
body={filename}
|
||||
mimeType={safeMimeType}
|
||||
after={
|
||||
<FileDownloadButton
|
||||
filename={filename}
|
||||
url={mxcUrl}
|
||||
mimeType={safeMimeType}
|
||||
encInfo={content.file}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</AttachmentHeader>
|
||||
<AttachmentBox>
|
||||
<AttachmentContent>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue