mirror of
				https://github.com/cinnyapp/cinny.git
				synced 2025-11-04 14:30:29 +03:00 
			
		
		
		
	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
				
			
		| 
						 | 
					@ -1,22 +1,81 @@
 | 
				
			||||||
import { Badge, Box, Text, as, toRem } from 'folds';
 | 
					import { Badge, Box, Icon, IconButton, Icons, Spinner, Text, as, toRem } from 'folds';
 | 
				
			||||||
import React from 'react';
 | 
					import React, { ReactNode, useCallback } from 'react';
 | 
				
			||||||
 | 
					import { EncryptedAttachmentInfo } from 'browser-encrypt-attachment';
 | 
				
			||||||
 | 
					import FileSaver from 'file-saver';
 | 
				
			||||||
import { mimeTypeToExt } from '../../utils/mimeTypes';
 | 
					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) };
 | 
					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 = {
 | 
					export type FileHeaderProps = {
 | 
				
			||||||
  body: string;
 | 
					  body: string;
 | 
				
			||||||
  mimeType: 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}>
 | 
					  <Box alignItems="Center" gap="200" grow="Yes" {...props} ref={ref}>
 | 
				
			||||||
    <Badge style={badgeStyles} variant="Secondary" radii="Pill">
 | 
					    <Box shrink="No">
 | 
				
			||||||
      <Text size="O400" truncate>
 | 
					      <Badge style={badgeStyles} variant="Secondary" radii="Pill">
 | 
				
			||||||
        {mimeTypeToExt(mimeType)}
 | 
					        <Text size="O400" truncate>
 | 
				
			||||||
 | 
					          {mimeTypeToExt(mimeType)}
 | 
				
			||||||
 | 
					        </Text>
 | 
				
			||||||
 | 
					      </Badge>
 | 
				
			||||||
 | 
					    </Box>
 | 
				
			||||||
 | 
					    <Box grow="Yes">
 | 
				
			||||||
 | 
					      <Text size="T300" truncate>
 | 
				
			||||||
 | 
					        {body}
 | 
				
			||||||
      </Text>
 | 
					      </Text>
 | 
				
			||||||
    </Badge>
 | 
					    </Box>
 | 
				
			||||||
    <Text size="T300" truncate>
 | 
					    {after}
 | 
				
			||||||
      {body}
 | 
					 | 
				
			||||||
    </Text>
 | 
					 | 
				
			||||||
  </Box>
 | 
					  </Box>
 | 
				
			||||||
));
 | 
					));
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -28,7 +28,7 @@ import {
 | 
				
			||||||
import { FALLBACK_MIMETYPE, getBlobSafeMimeType } from '../../utils/mimeTypes';
 | 
					import { FALLBACK_MIMETYPE, getBlobSafeMimeType } from '../../utils/mimeTypes';
 | 
				
			||||||
import { parseGeoUri, scaleYDimension } from '../../utils/common';
 | 
					import { parseGeoUri, scaleYDimension } from '../../utils/common';
 | 
				
			||||||
import { Attachment, AttachmentBox, AttachmentContent, AttachmentHeader } from './attachment';
 | 
					import { Attachment, AttachmentBox, AttachmentContent, AttachmentHeader } from './attachment';
 | 
				
			||||||
import { FileHeader } from './FileHeader';
 | 
					import { FileHeader, FileDownloadButton } from './FileHeader';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function MBadEncrypted() {
 | 
					export function MBadEncrypted() {
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
| 
						 | 
					@ -243,8 +243,24 @@ export function MVideo({ content, renderAsFile, renderVideoContent, outlined }:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const height = scaleYDimension(videoInfo.w || 400, 400, videoInfo.h || 400);
 | 
					  const height = scaleYDimension(videoInfo.w || 400, 400, videoInfo.h || 400);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const filename = content.filename ?? content.body ?? 'Video';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <Attachment outlined={outlined}>
 | 
					    <Attachment outlined={outlined}>
 | 
				
			||||||
 | 
					      <AttachmentHeader>
 | 
				
			||||||
 | 
					        <FileHeader
 | 
				
			||||||
 | 
					          body={filename}
 | 
				
			||||||
 | 
					          mimeType={safeMimeType}
 | 
				
			||||||
 | 
					          after={
 | 
				
			||||||
 | 
					            <FileDownloadButton
 | 
				
			||||||
 | 
					              filename={filename}
 | 
				
			||||||
 | 
					              url={mxcUrl}
 | 
				
			||||||
 | 
					              mimeType={safeMimeType}
 | 
				
			||||||
 | 
					              encInfo={content.file}
 | 
				
			||||||
 | 
					            />
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        />
 | 
				
			||||||
 | 
					      </AttachmentHeader>
 | 
				
			||||||
      <AttachmentBox
 | 
					      <AttachmentBox
 | 
				
			||||||
        style={{
 | 
					        style={{
 | 
				
			||||||
          height: toRem(height < 48 ? 48 : height),
 | 
					          height: toRem(height < 48 ? 48 : height),
 | 
				
			||||||
| 
						 | 
					@ -286,10 +302,22 @@ export function MAudio({ content, renderAsFile, renderAudioContent, outlined }:
 | 
				
			||||||
    return <BrokenContent />;
 | 
					    return <BrokenContent />;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const filename = content.filename ?? content.body ?? 'Audio';
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <Attachment outlined={outlined}>
 | 
					    <Attachment outlined={outlined}>
 | 
				
			||||||
      <AttachmentHeader>
 | 
					      <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>
 | 
					      </AttachmentHeader>
 | 
				
			||||||
      <AttachmentBox>
 | 
					      <AttachmentBox>
 | 
				
			||||||
        <AttachmentContent>
 | 
					        <AttachmentContent>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue