Files
desktop/app/components/MessageAttachments/MessageImage.tsx
rosetta 83f38dc63f 'init'
2026-01-30 05:01:05 +02:00

155 lines
6.9 KiB
TypeScript

import { useRosettaColors } from "@/app/hooks/useRosettaColors";
import { DeliveredMessageState } from "@/app/providers/DialogProvider/DialogProvider";
import { useImageViewer } from "@/app/providers/ImageViewerProvider/useImageViewer";
import { AspectRatio, Box, Flex, Overlay, Portal, Text } from "@mantine/core";
import { IconArrowDown, IconCircleX, IconFlameFilled } from "@tabler/icons-react";
import { useEffect, useRef, useState } from "react";
import { AttachmentProps } from "./MessageAttachments";
import { blurhashToBase64Image, isMessageDeliveredByTime } from "@/app/utils/utils";
import { AnimatedRoundedProgress } from "../AnimatedRoundedProgress/AnimatedRoundedProgress";
import { ImageToView } from "@/app/providers/ImageViewerProvider/ImageViewerProvider";
import { DownloadStatus, useAttachment } from "@/app/providers/AttachmentProvider/useAttachment";
export function MessageImage(props: AttachmentProps) {
const colors = useRosettaColors();
const {
downloadPercentage,
uploadedPercentage,
download,
downloadStatus,
getBlob,
getPreview} = useAttachment(props.attachment, props.chacha_key_plain);
const mainRef = useRef<HTMLDivElement>(null);
const error = downloadStatus == DownloadStatus.ERROR;
const { open } = useImageViewer();
const preview = getPreview();
const [blob, setBlob] = useState(props.attachment.blob);
const [loadedImage, setLoadedImage] = useState(false);
useEffect(() => {
constructBlob();
}, [downloadStatus]);
const constructBlob = async () => {
let blob = await getBlob();
setBlob(blob);
}
const openImageViewer = () => {
const images: ImageToView[] = [{
src: blob,
caption: props.text,
timestamp: props.timestamp
}];
open(images, 0);
}
const onClick = () => {
if (downloadStatus == DownloadStatus.DOWNLOADED) {
openImageViewer();
return;
}
if (downloadStatus == DownloadStatus.NOT_DOWNLOADED) {
download();
return;
}
}
return (
<AspectRatio onClick={onClick} ref={mainRef} style={{
minWidth: 200,
minHeight: 220,
maxWidth: 200,
maxHeight: 220,
borderRadius: 8,
cursor: 'pointer',
userSelect: 'none'
}} pos={'relative'}>
{blob != "" && (
<img style={{
minHeight: 220,
width: '100%',
borderRadius: 8,
objectFit: 'cover',
backgroundColor: '#FFFFFF',
border: '1px solid ' + colors.borderColor,
display: loadedImage ? 'block' : 'none'
}} src={blob} onLoad={() => setLoadedImage(true)}></img>)}
{((downloadStatus != DownloadStatus.DOWNLOADED && downloadStatus != DownloadStatus.PENDING) || !loadedImage) && preview.length >= 20 && (
<>
<img style={{
minHeight: 220,
width: '100%',
borderRadius: 8,
objectFit: 'cover',
border: '1px solid ' + colors.borderColor
}} src={/*block render???*/blurhashToBase64Image(preview, 200, 220)}></img>
<Portal target={mainRef.current!}>
<Flex direction={'column'} pos={'absolute'} justify={'center'} top={0} h={'100%'} align={'center'} gap={'xs'}>
{!error && (
<Box style={{
backgroundColor: 'rgba(0, 0, 0, 0.3)',
borderRadius: 50,
height: 40,
width: 40,
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
}}>
{downloadPercentage > 0 ? (
<AnimatedRoundedProgress size={40} value={downloadPercentage} color="white"></AnimatedRoundedProgress>
) : (
<IconArrowDown size={25} color={'white'} />
)}
</Box>
)}
{error && (
<Box p={'xs'} style={{
backgroundColor: 'rgba(0, 0, 0, 0.3)',
borderRadius: 8,
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
gap: 4
}}>
<Text size={'xs'} c={'white'}>
Image expired
</Text>
<IconFlameFilled size={15} style={{
fontSmooth: 'always'
}} color={'white'} />
</Box>
)}
</Flex>
</Portal>
</>
)}
{(props.delivered == DeliveredMessageState.WAITING && uploadedPercentage > 0 && isMessageDeliveredByTime(props.timestamp || 0, props.attachments.length)) &&
<Portal target={mainRef.current!}>
<Flex direction={'column'} pos={'absolute'} justify={'center'} top={0} h={'100%'} align={'center'} gap={'xs'}>
<Box style={{
backgroundColor: 'rgba(0, 0, 0, 0.3)',
borderRadius: 50,
height: 40,
width: 40,
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
}}>
<AnimatedRoundedProgress size={40} value={uploadedPercentage > 95 ? 95 : uploadedPercentage}></AnimatedRoundedProgress>
</Box>
</Flex>
</Portal>}
{(props.delivered == DeliveredMessageState.ERROR || (props.delivered != DeliveredMessageState.DELIVERED &&
!isMessageDeliveredByTime(props.timestamp || 0, props.attachments.length)
)) && (
<Overlay center h={'100%'} radius={8} color="#000" opacity={0.85}>
<IconCircleX size={40} color={colors.error} />
</Overlay>
)}
</AspectRatio>
);
}