'init'
This commit is contained in:
61
app/components/MessageAttachments/MessageAttachments.tsx
Normal file
61
app/components/MessageAttachments/MessageAttachments.tsx
Normal file
@@ -0,0 +1,61 @@
|
||||
import { Attachment, AttachmentType } from "@/app/providers/ProtocolProvider/protocol/packets/packet.message";
|
||||
import { DeliveredMessageState } from "@/app/providers/DialogProvider/DialogProvider";
|
||||
import { Flex } from "@mantine/core";
|
||||
import { MessageImage } from "./MessageImage";
|
||||
import { MessageReplyMessages } from "./MessageReplyMessages";
|
||||
import { MessageFile } from "./MessageFile";
|
||||
import { ErrorBoundaryProvider } from "@/app/providers/ErrorBoundaryProvider/ErrorBoundaryProvider";
|
||||
import { AttachmentError } from "../AttachmentError/AttachmentError";
|
||||
import { MessageAvatar } from "./MessageAvatar";
|
||||
import { MessageProps } from "../Messages/Message";
|
||||
|
||||
export interface MessageAttachmentsProps {
|
||||
attachments: Attachment[];
|
||||
delivered: DeliveredMessageState;
|
||||
timestamp: number;
|
||||
text: string;
|
||||
chacha_key_plain: string;
|
||||
parent: MessageProps;
|
||||
}
|
||||
|
||||
export interface AttachmentProps {
|
||||
attachment: Attachment;
|
||||
attachments: Attachment[];
|
||||
delivered: DeliveredMessageState;
|
||||
timestamp: number;
|
||||
text: string;
|
||||
chacha_key_plain: string;
|
||||
parent: MessageProps;
|
||||
}
|
||||
|
||||
export function MessageAttachments(props: MessageAttachmentsProps) {
|
||||
return (
|
||||
<ErrorBoundaryProvider fallback={<AttachmentError></AttachmentError>}>
|
||||
<Flex gap={'xs'} direction={'column'} mt={'sm'} wrap={'wrap'}>
|
||||
{props.attachments.map((att, index) => {
|
||||
const attachProps : AttachmentProps = {
|
||||
chacha_key_plain: props.chacha_key_plain,
|
||||
attachment: att,
|
||||
attachments: props.attachments,
|
||||
delivered: props.delivered,
|
||||
timestamp: props.timestamp,
|
||||
text: props.text,
|
||||
parent: props.parent,
|
||||
}
|
||||
switch (att.type) {
|
||||
case AttachmentType.MESSAGES:
|
||||
return <MessageReplyMessages {...attachProps} key={index}></MessageReplyMessages>
|
||||
case AttachmentType.IMAGE:
|
||||
return <MessageImage {...attachProps} key={index}></MessageImage>
|
||||
case AttachmentType.FILE:
|
||||
return <MessageFile {...attachProps} key={index}></MessageFile>
|
||||
case AttachmentType.AVATAR:
|
||||
return <MessageAvatar {...attachProps} key={index}></MessageAvatar>
|
||||
default:
|
||||
return <AttachmentError key={index}></AttachmentError>;
|
||||
}
|
||||
})}
|
||||
</Flex>
|
||||
</ErrorBoundaryProvider>
|
||||
);
|
||||
}
|
||||
111
app/components/MessageAttachments/MessageAvatar.tsx
Normal file
111
app/components/MessageAttachments/MessageAvatar.tsx
Normal file
@@ -0,0 +1,111 @@
|
||||
import { useRosettaColors } from "@/app/hooks/useRosettaColors";
|
||||
import { useImageViewer } from "@/app/providers/ImageViewerProvider/useImageViewer";
|
||||
import { AspectRatio, Button, Flex, Paper, Text } from "@mantine/core";
|
||||
import { IconArrowDown } from "@tabler/icons-react";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { AttachmentProps } from "./MessageAttachments";
|
||||
import { blurhashToBase64Image } from "@/app/utils/utils";
|
||||
import { AnimatedRoundedProgress } from "../AnimatedRoundedProgress/AnimatedRoundedProgress";
|
||||
import { ImageToView } from "@/app/providers/ImageViewerProvider/ImageViewerProvider";
|
||||
import { DownloadStatus, useAttachment } from "@/app/providers/AttachmentProvider/useAttachment";
|
||||
import { PopoverLockIconAvatar } from "../PopoverLockIconAvatar/PopoverLockIconAvatar";
|
||||
import { useRosettaBreakpoints } from "@/app/hooks/useRosettaBreakpoints";
|
||||
|
||||
export function MessageAvatar(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 { open } = useImageViewer();
|
||||
const preview = getPreview();
|
||||
const [blob, setBlob] = useState(props.attachment.blob);
|
||||
const {lg} = useRosettaBreakpoints();
|
||||
|
||||
|
||||
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 (
|
||||
<Paper withBorder p={'sm'}>
|
||||
<Flex gap={'sm'} direction={'row'}>
|
||||
<AspectRatio onClick={onClick} ref={mainRef} style={{
|
||||
height: 60,
|
||||
width: 60,
|
||||
userSelect: 'none'
|
||||
}} ratio={16 / 9} pos={'relative'}>
|
||||
{blob != "" && (
|
||||
<img style={{
|
||||
height: 60,
|
||||
width: 60,
|
||||
borderRadius: '50%',
|
||||
objectFit: 'cover'
|
||||
}} src={blob}></img>)}
|
||||
{downloadStatus != DownloadStatus.DOWNLOADED && downloadStatus != DownloadStatus.PENDING && preview.length >= 20 && (
|
||||
<>
|
||||
<img style={{
|
||||
width: 60,
|
||||
height: 60,
|
||||
borderRadius: '50%',
|
||||
objectFit: 'cover'
|
||||
}} src={/*block render???*/blurhashToBase64Image(preview, 200, 220)}></img>
|
||||
</>
|
||||
)}
|
||||
</AspectRatio>
|
||||
<Flex direction={"column"}>
|
||||
<Flex direction={'row'} gap={5} align={'center'}>
|
||||
<Text fw={500} fz={'sm'}>Avatar</Text>
|
||||
<PopoverLockIconAvatar></PopoverLockIconAvatar>
|
||||
</Flex>
|
||||
<Text fz={'xs'} c={'dimmed'}>
|
||||
An avatar image shared in the message.
|
||||
</Text>
|
||||
{downloadStatus != DownloadStatus.DOWNLOADED && (
|
||||
<Flex direction={'row'} mt={'xs'} justify={'flex-end'} align={'center'} gap={'sm'}>
|
||||
{lg && <Text fz={9} c={'dimmed'}>Avatars are end-to-end encrypted</Text>}
|
||||
<Flex align={'center'}>
|
||||
{downloadStatus == DownloadStatus.NOT_DOWNLOADED &&
|
||||
<Button leftSection={<IconArrowDown size={14}></IconArrowDown>} size={'xs'} variant={'light'} onClick={download}>Download</Button>
|
||||
}
|
||||
{downloadStatus == DownloadStatus.DOWNLOADING &&
|
||||
<Button leftSection={<AnimatedRoundedProgress size={14} color={colors.brandColor} value={downloadPercentage}></AnimatedRoundedProgress>} size={'xs'} variant={'light'}>Download</Button>
|
||||
}
|
||||
</Flex>
|
||||
</Flex>
|
||||
)}
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Paper>
|
||||
);
|
||||
}
|
||||
133
app/components/MessageAttachments/MessageFile.tsx
Normal file
133
app/components/MessageAttachments/MessageFile.tsx
Normal file
@@ -0,0 +1,133 @@
|
||||
import { DownloadStatus, useAttachment } from "@/app/providers/AttachmentProvider/useAttachment";
|
||||
import { AttachmentProps } from "./MessageAttachments";
|
||||
import { useRosettaColors } from "@/app/hooks/useRosettaColors";
|
||||
import { Avatar, Box, Flex, Loader, Text } from "@mantine/core";
|
||||
import { IconArrowDown, IconFile, IconX } from "@tabler/icons-react";
|
||||
import { dotCenterIfNeeded, humanFilesize } from "@/app/utils/utils";
|
||||
import { AnimatedRoundedProgress } from "../AnimatedRoundedProgress/AnimatedRoundedProgress";
|
||||
import { DeliveredMessageState } from "@/app/providers/DialogProvider/DialogProvider";
|
||||
|
||||
export function MessageFile(props : AttachmentProps) {
|
||||
const colors = useRosettaColors();
|
||||
const {
|
||||
downloadPercentage,
|
||||
downloadStatus,
|
||||
uploadedPercentage,
|
||||
download,
|
||||
getPreview,
|
||||
} =
|
||||
useAttachment(
|
||||
props.attachment,
|
||||
props.chacha_key_plain,
|
||||
);
|
||||
const preview = getPreview();
|
||||
const error = downloadStatus == DownloadStatus.ERROR;
|
||||
const filesize = parseInt(preview.split("::")[0]);
|
||||
const filename = preview.split("::")[1];
|
||||
const filetype = filename.split(".")[filename.split(".").length - 1];
|
||||
const isEncrypting = props.delivered == DeliveredMessageState.WAITING && uploadedPercentage <= 0;
|
||||
const isUploading = props.delivered == DeliveredMessageState.WAITING && uploadedPercentage > 0 && uploadedPercentage < 100;
|
||||
|
||||
const onClick = async () => {
|
||||
if(downloadStatus == DownloadStatus.ERROR){
|
||||
return;
|
||||
}
|
||||
if(downloadStatus == DownloadStatus.DOWNLOADED){
|
||||
//let content = await getBlob();
|
||||
//let buffer = Buffer.from(content.split(",")[1], 'base64');
|
||||
let pathInDownloads = window.downloadsPath + "/Rosetta Downloads/" + filename;
|
||||
//await writeFile(pathInDownloads, buffer, false);
|
||||
window.shell.showItemInFolder(pathInDownloads);
|
||||
return;
|
||||
}
|
||||
if(downloadStatus == DownloadStatus.NOT_DOWNLOADED){
|
||||
download();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Box p={'sm'} onClick={onClick} style={{
|
||||
background: colors.mainColor,
|
||||
border: '1px solid ' + colors.borderColor,
|
||||
borderRadius: 8,
|
||||
minWidth: 200,
|
||||
minHeight: 60
|
||||
}}>
|
||||
<Flex gap={'sm'} direction={'row'}>
|
||||
<Avatar bg={error ? colors.error : colors.brandColor} size={40}>
|
||||
{!error && <>
|
||||
{(downloadStatus == DownloadStatus.DOWNLOADING && downloadPercentage > 0 && downloadPercentage < 100) && (
|
||||
<div style={{
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
}}>
|
||||
<AnimatedRoundedProgress size={40} value={downloadPercentage}></AnimatedRoundedProgress>
|
||||
</div>
|
||||
)}
|
||||
{downloadStatus != DownloadStatus.DOWNLOADED && (
|
||||
<IconArrowDown color={'white'} size={22}></IconArrowDown>
|
||||
)}
|
||||
{isUploading && (
|
||||
<div style={{
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
}}>
|
||||
<AnimatedRoundedProgress size={40} value={uploadedPercentage}></AnimatedRoundedProgress>
|
||||
</div>
|
||||
)}
|
||||
{downloadStatus == DownloadStatus.DOWNLOADED && <IconFile color={'white'} size={22}></IconFile>}
|
||||
</>}
|
||||
{error && <>
|
||||
<IconX color={'white'} size={22}></IconX>
|
||||
</>}
|
||||
</Avatar>
|
||||
<Flex direction={'column'} gap={5}>
|
||||
<Text size={'sm'}>{dotCenterIfNeeded(filename, 25, 8)}</Text>
|
||||
{!error && !isEncrypting && !isUploading && (downloadStatus == DownloadStatus.DOWNLOADED || downloadStatus == DownloadStatus.NOT_DOWNLOADED) &&
|
||||
<Text size={'xs'} c={colors.chevrons.active}>
|
||||
{humanFilesize(filesize)} {filetype.toUpperCase()}
|
||||
</Text>
|
||||
}
|
||||
{downloadStatus == DownloadStatus.DOWNLOADING &&
|
||||
<Flex gap={5} align={'center'}>
|
||||
<Loader size={10}></Loader>
|
||||
<Text size={'xs'} c={colors.chevrons.active}>
|
||||
Downloading... {downloadPercentage}%
|
||||
</Text>
|
||||
</Flex>
|
||||
}
|
||||
{isEncrypting &&
|
||||
<Flex gap={5} align={'center'}>
|
||||
<Loader size={10}></Loader>
|
||||
<Text size={'xs'} c={colors.chevrons.active}>
|
||||
Encrypting...
|
||||
</Text>
|
||||
</Flex>
|
||||
}
|
||||
{isUploading &&
|
||||
<Flex gap={5} align={'center'}>
|
||||
<Loader size={10}></Loader>
|
||||
<Text size={'xs'} c={colors.chevrons.active}>
|
||||
Uploading... {uploadedPercentage}%
|
||||
</Text>
|
||||
</Flex>
|
||||
}
|
||||
{downloadStatus == DownloadStatus.DECRYPTING &&
|
||||
<Flex gap={5} align={'center'}>
|
||||
<Loader size={10}></Loader>
|
||||
<Text size={'xs'} c={colors.chevrons.active}>
|
||||
Decrypting...
|
||||
</Text>
|
||||
</Flex>
|
||||
}
|
||||
{error && <Text size={'xs'} c={colors.error}>
|
||||
File expired
|
||||
</Text>}
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
154
app/components/MessageAttachments/MessageImage.tsx
Normal file
154
app/components/MessageAttachments/MessageImage.tsx
Normal file
@@ -0,0 +1,154 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
61
app/components/MessageAttachments/MessageReplyMessages.tsx
Normal file
61
app/components/MessageAttachments/MessageReplyMessages.tsx
Normal file
@@ -0,0 +1,61 @@
|
||||
import { Alert, Flex, Skeleton, Text } from "@mantine/core";
|
||||
import { AttachmentProps } from "./MessageAttachments";
|
||||
import { useRosettaColors } from "@/app/hooks/useRosettaColors";
|
||||
import { ReplyedMessage } from "../ReplyedMessage/ReplyedMessage";
|
||||
import { IconX } from "@tabler/icons-react";
|
||||
import { useSetting } from "@/app/providers/SettingsProvider/useSetting";
|
||||
import { modals } from "@mantine/modals";
|
||||
|
||||
export function MessageReplyMessages(props: AttachmentProps) {
|
||||
const colors = useRosettaColors();
|
||||
const [showAlertInReplyMessages, setShowAlertInReplyMessages] = useSetting<boolean>
|
||||
('showAlertInReplyMessages', true);
|
||||
const [bgInReplyMessages] = useSetting<string>
|
||||
('bgInReplyMessages', '');
|
||||
const reply = JSON.parse(props.attachment.blob);
|
||||
|
||||
//console.info("Mreply", reply);
|
||||
|
||||
const closeAlert = () => {
|
||||
modals.openConfirmModal({
|
||||
title: 'Disable Warning',
|
||||
centered: true,
|
||||
children: (
|
||||
<Text size="sm">
|
||||
Are you sure you want to disable the warning about forged messages in replies?
|
||||
</Text>
|
||||
),
|
||||
labels: { confirm: 'Yes, disable', cancel: 'No, keep it' },
|
||||
|
||||
onCancel: () => {},
|
||||
onConfirm: () => setShowAlertInReplyMessages(false),
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<Flex maw={'100%'} direction={'column'} bg={bgInReplyMessages != "" ? `var(--mantine-color-${bgInReplyMessages}-light)` : undefined} p={0} mb={'xs'} style={{
|
||||
borderLeft: '2px solid ' + (bgInReplyMessages != "" ? `var(--mantine-color-${bgInReplyMessages}-6)` : colors.error),
|
||||
borderRadius: 2
|
||||
}}>
|
||||
{reply.length <= 0 &&
|
||||
<Skeleton h={50} w={'100%'}></Skeleton>
|
||||
}
|
||||
{reply.map((msg, index) => (
|
||||
<ReplyedMessage parent={props.parent} chacha_key_plain={props.chacha_key_plain} key={index} messageReply={msg}></ReplyedMessage>
|
||||
))}
|
||||
{showAlertInReplyMessages && <Alert style={{
|
||||
borderTopLeftRadius: 0,
|
||||
borderTopRightRadius: 0,
|
||||
borderBottomLeftRadius: 0,
|
||||
borderBottomRightRadius: 4,
|
||||
}} variant="light" p={5} color={'red'}>
|
||||
<Flex align={'center'} gap={'sm'}>
|
||||
<Text c={'red'} fz={8}>
|
||||
Due to the use of encryption, these messages may be forged by the sender
|
||||
</Text>
|
||||
<IconX size={11} color={'red'} onClick={closeAlert}></IconX>
|
||||
</Flex>
|
||||
</Alert>}
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user