Composer: Various improvements for attachments (audio, video, photo) (#1443)
This commit is contained in:
parent
d121b82de7
commit
fdda362cb3
@ -515,6 +515,7 @@ async function uploadMedia(localMessage: ApiMessage, attachment: ApiAttachment,
|
|||||||
duration,
|
duration,
|
||||||
w: width,
|
w: width,
|
||||||
h: height,
|
h: height,
|
||||||
|
supportsStreaming: true,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -156,12 +156,11 @@ const Audio: FC<OwnProps> = ({
|
|||||||
transitionClassNames: spinnerClassNames,
|
transitionClassNames: spinnerClassNames,
|
||||||
} = useShowTransition(isTransferring);
|
} = useShowTransition(isTransferring);
|
||||||
|
|
||||||
const handleButtonClick = useCallback(() => {
|
const shouldRenderCross = shouldRenderSpinner && (isLoadingForPlaying || isUploading);
|
||||||
if (isUploading && !isPlaying) {
|
|
||||||
if (onCancelUpload) {
|
|
||||||
onCancelUpload();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
const handleButtonClick = useCallback(() => {
|
||||||
|
if (isUploading) {
|
||||||
|
onCancelUpload?.();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -269,7 +268,7 @@ const Audio: FC<OwnProps> = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const buttonClassNames = ['toggle-play'];
|
const buttonClassNames = ['toggle-play'];
|
||||||
if (isLoadingForPlaying) {
|
if (shouldRenderCross) {
|
||||||
buttonClassNames.push('loading');
|
buttonClassNames.push('loading');
|
||||||
} else if (isPlaying) {
|
} else if (isPlaying) {
|
||||||
buttonClassNames.push('pause');
|
buttonClassNames.push('pause');
|
||||||
@ -334,13 +333,13 @@ const Audio: FC<OwnProps> = ({
|
|||||||
<i className="icon-pause" />
|
<i className="icon-pause" />
|
||||||
</Button>
|
</Button>
|
||||||
{shouldRenderSpinner && (
|
{shouldRenderSpinner && (
|
||||||
<div className={buildClassName('media-loading', spinnerClassNames, isLoadingForPlaying && 'interactive')}>
|
<div className={buildClassName('media-loading', spinnerClassNames, shouldRenderCross && 'interactive')}>
|
||||||
<ProgressSpinner
|
<ProgressSpinner
|
||||||
progress={transferProgress}
|
progress={transferProgress}
|
||||||
transparent
|
transparent
|
||||||
size="m"
|
size="m"
|
||||||
onClick={isLoadingForPlaying ? handleButtonClick : undefined}
|
onClick={shouldRenderCross ? handleButtonClick : undefined}
|
||||||
noCross={!isLoadingForPlaying}
|
noCross={!shouldRenderCross}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -151,12 +151,15 @@ const AttachmentModal: FC<OwnProps> = ({
|
|||||||
|
|
||||||
const areAllPhotos = renderingAttachments.every((a) => a.mimeType.startsWith('image/'));
|
const areAllPhotos = renderingAttachments.every((a) => a.mimeType.startsWith('image/'));
|
||||||
const areAllVideos = renderingAttachments.every((a) => a.mimeType.startsWith('video/'));
|
const areAllVideos = renderingAttachments.every((a) => a.mimeType.startsWith('video/'));
|
||||||
|
const areAllAudios = renderingAttachments.every((a) => a.mimeType.startsWith('audio/'));
|
||||||
|
|
||||||
let title = '';
|
let title = '';
|
||||||
if (areAllPhotos) {
|
if (areAllPhotos) {
|
||||||
title = lang('PreviewSender.SendPhoto', renderingAttachments.length, 'i');
|
title = lang('PreviewSender.SendPhoto', renderingAttachments.length, 'i');
|
||||||
} else if (areAllVideos) {
|
} else if (areAllVideos) {
|
||||||
title = lang('PreviewSender.SendVideo', renderingAttachments.length, 'i');
|
title = lang('PreviewSender.SendVideo', renderingAttachments.length, 'i');
|
||||||
|
} else if (areAllAudios) {
|
||||||
|
title = lang('PreviewSender.SendAudio', renderingAttachments.length, 'i');
|
||||||
} else {
|
} else {
|
||||||
title = lang('PreviewSender.SendFile', renderingAttachments.length, 'i');
|
title = lang('PreviewSender.SendFile', renderingAttachments.length, 'i');
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,12 @@
|
|||||||
import { ApiAttachment } from '../../../../api/types';
|
import { ApiAttachment } from '../../../../api/types';
|
||||||
import { preloadImage, preloadVideo, createPosterForVideo } from '../../../../util/files';
|
import {
|
||||||
|
preloadImage,
|
||||||
|
preloadVideo,
|
||||||
|
createPosterForVideo,
|
||||||
|
fetchBlob,
|
||||||
|
} from '../../../../util/files';
|
||||||
|
import { scaleImage } from '../../../../util/imageResize';
|
||||||
|
|
||||||
const MAX_QUICK_VIDEO_SIZE = 10 * 1024 ** 2; // 10 MB
|
|
||||||
const MAX_QUICK_IMG_SIZE = 1280; // px
|
const MAX_QUICK_IMG_SIZE = 1280; // px
|
||||||
|
|
||||||
export default async function buildAttachment(
|
export default async function buildAttachment(
|
||||||
@ -18,13 +23,10 @@ export default async function buildAttachment(
|
|||||||
const { width, height } = img;
|
const { width, height } = img;
|
||||||
|
|
||||||
if (width > MAX_QUICK_IMG_SIZE || height > MAX_QUICK_IMG_SIZE || mimeType !== 'image/jpeg') {
|
if (width > MAX_QUICK_IMG_SIZE || height > MAX_QUICK_IMG_SIZE || mimeType !== 'image/jpeg') {
|
||||||
const newBlob = await squeezeImage(img);
|
const resizedUrl = await scaleImage(blobUrl, MAX_QUICK_IMG_SIZE / Math.max(width, height), 'image/jpeg');
|
||||||
if (newBlob) {
|
URL.revokeObjectURL(blobUrl);
|
||||||
URL.revokeObjectURL(blobUrl);
|
const newBlob = await fetchBlob(resizedUrl);
|
||||||
return buildAttachment(filename, newBlob, true, options);
|
return buildAttachment(filename, newBlob, true, options);
|
||||||
} else {
|
|
||||||
return buildAttachment(filename, blob, false, options);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
quick = { width, height };
|
quick = { width, height };
|
||||||
@ -32,12 +34,8 @@ export default async function buildAttachment(
|
|||||||
previewBlobUrl = blobUrl;
|
previewBlobUrl = blobUrl;
|
||||||
}
|
}
|
||||||
} else if (mimeType.startsWith('video/')) {
|
} else if (mimeType.startsWith('video/')) {
|
||||||
// Videos < 10 MB are always sent in quick mode (in other clients).
|
const { videoWidth: width, videoHeight: height, duration } = await preloadVideo(blobUrl);
|
||||||
// Quick mode for videos > 10 MB is not supported until client-side video squeezing is implemented.
|
quick = { width, height, duration };
|
||||||
if (size < MAX_QUICK_VIDEO_SIZE) {
|
|
||||||
const { videoWidth: width, videoHeight: height, duration } = await preloadVideo(blobUrl);
|
|
||||||
quick = { width, height, duration };
|
|
||||||
}
|
|
||||||
|
|
||||||
previewBlobUrl = await createPosterForVideo(blobUrl);
|
previewBlobUrl = await createPosterForVideo(blobUrl);
|
||||||
}
|
}
|
||||||
@ -52,28 +50,3 @@ export default async function buildAttachment(
|
|||||||
...options,
|
...options,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function squeezeImage(img: HTMLImageElement): Promise<Blob | null> {
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
const canvas = document.createElement('canvas');
|
|
||||||
const ctx = canvas.getContext('2d')!;
|
|
||||||
|
|
||||||
let { width, height } = img;
|
|
||||||
|
|
||||||
if (width > MAX_QUICK_IMG_SIZE || height > MAX_QUICK_IMG_SIZE) {
|
|
||||||
if (width >= height) {
|
|
||||||
height *= MAX_QUICK_IMG_SIZE / width;
|
|
||||||
width = MAX_QUICK_IMG_SIZE;
|
|
||||||
} else {
|
|
||||||
width *= MAX_QUICK_IMG_SIZE / height;
|
|
||||||
height = MAX_QUICK_IMG_SIZE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
canvas.width = width;
|
|
||||||
canvas.height = height;
|
|
||||||
|
|
||||||
ctx.drawImage(img, 0, 0, img.width, img.height, 0, 0, width, height);
|
|
||||||
canvas.toBlob(resolve, 'image/jpeg', 100);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|||||||
@ -194,7 +194,9 @@ const Video: FC<OwnProps> = ({
|
|||||||
<i className="icon-download" />
|
<i className="icon-download" />
|
||||||
)}
|
)}
|
||||||
{isTransferring ? (
|
{isTransferring ? (
|
||||||
<span className="message-upload-progress">...</span>
|
<span className="message-upload-progress">
|
||||||
|
{isUploading ? `${Math.round(transferProgress * 100)}%` : '...'}
|
||||||
|
</span>
|
||||||
) : (
|
) : (
|
||||||
<div className="message-media-duration">
|
<div className="message-media-duration">
|
||||||
{video.isGif ? 'GIF' : formatMediaDuration(Math.max(duration - playProgress, 0))}
|
{video.isGif ? 'GIF' : formatMediaDuration(Math.max(duration - playProgress, 0))}
|
||||||
|
|||||||
@ -122,7 +122,7 @@ export const MENU_TRANSITION_DURATION = 200;
|
|||||||
export const SLIDE_TRANSITION_DURATION = 450;
|
export const SLIDE_TRANSITION_DURATION = 450;
|
||||||
|
|
||||||
export const CONTENT_TYPES_FOR_QUICK_UPLOAD = new Set([
|
export const CONTENT_TYPES_FOR_QUICK_UPLOAD = new Set([
|
||||||
'image/png', 'image/gif', 'image/jpeg', 'video/mp4', 'video/avi', 'video/quicktime',
|
'image/png', 'image/gif', 'image/jpeg',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// eslint-disable-next-line max-len
|
// eslint-disable-next-line max-len
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user