mirror of
https://github.com/Qortal/Qortal-Hub.git
synced 2025-05-15 22:26:58 +00:00
Merge branch 'Qortal:develop' into feature/i18n-continue
This commit is contained in:
commit
37a2694678
@ -1,3 +1,5 @@
|
|||||||
|
//TODO
|
||||||
|
|
||||||
import { useRef, useState, useCallback, useMemo } from 'react';
|
import { useRef, useState, useCallback, useMemo } from 'react';
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
@ -34,7 +36,7 @@ export const useModal = () => {
|
|||||||
const onCancel = useCallback(() => {
|
const onCancel = useCallback(() => {
|
||||||
const { reject } = promiseConfig.current || {};
|
const { reject } = promiseConfig.current || {};
|
||||||
hide();
|
hide();
|
||||||
reject?.();
|
reject?.('Declined');
|
||||||
}, [hide]);
|
}, [hide]);
|
||||||
|
|
||||||
return useMemo(
|
return useMemo(
|
||||||
|
@ -31,17 +31,27 @@ import {
|
|||||||
subscribeToEvent,
|
subscribeToEvent,
|
||||||
unsubscribeFromEvent,
|
unsubscribeFromEvent,
|
||||||
} from '../../utils/events';
|
} from '../../utils/events';
|
||||||
import { Box, ButtonBase, Divider, Typography, useTheme } from '@mui/material';
|
import {
|
||||||
|
Box,
|
||||||
|
ButtonBase,
|
||||||
|
Divider,
|
||||||
|
IconButton,
|
||||||
|
Tooltip,
|
||||||
|
Typography,
|
||||||
|
useTheme,
|
||||||
|
} from '@mui/material';
|
||||||
import ShortUniqueId from 'short-unique-id';
|
import ShortUniqueId from 'short-unique-id';
|
||||||
import { ReplyPreview } from './MessageItem';
|
import { ReplyPreview } from './MessageItem';
|
||||||
import { ExitIcon } from '../../assets/Icons/ExitIcon';
|
import { ExitIcon } from '../../assets/Icons/ExitIcon';
|
||||||
import { RESOURCE_TYPE_NUMBER_GROUP_CHAT_REACTIONS } from '../../constants/resourceTypes';
|
import { RESOURCE_TYPE_NUMBER_GROUP_CHAT_REACTIONS } from '../../constants/resourceTypes';
|
||||||
import { isExtMsg } from '../../background';
|
import { getFee, isExtMsg } from '../../background';
|
||||||
import AppViewerContainer from '../Apps/AppViewerContainer';
|
import AppViewerContainer from '../Apps/AppViewerContainer';
|
||||||
import CloseIcon from '@mui/icons-material/Close';
|
import CloseIcon from '@mui/icons-material/Close';
|
||||||
import { throttle } from 'lodash';
|
import { throttle } from 'lodash';
|
||||||
|
import ImageIcon from '@mui/icons-material/Image';
|
||||||
|
import { messageHasImage } from '../../utils/chat';
|
||||||
const uid = new ShortUniqueId({ length: 5 });
|
const uid = new ShortUniqueId({ length: 5 });
|
||||||
|
const uidImages = new ShortUniqueId({ length: 12 });
|
||||||
|
|
||||||
export const ChatGroup = ({
|
export const ChatGroup = ({
|
||||||
selectedGroup,
|
selectedGroup,
|
||||||
@ -59,7 +69,7 @@ export const ChatGroup = ({
|
|||||||
hideView,
|
hideView,
|
||||||
isPrivate,
|
isPrivate,
|
||||||
}) => {
|
}) => {
|
||||||
const { isUserBlocked } = useContext(MyContext);
|
const { isUserBlocked, show } = useContext(MyContext);
|
||||||
const [messages, setMessages] = useState([]);
|
const [messages, setMessages] = useState([]);
|
||||||
const [chatReferences, setChatReferences] = useState({});
|
const [chatReferences, setChatReferences] = useState({});
|
||||||
const [isSending, setIsSending] = useState(false);
|
const [isSending, setIsSending] = useState(false);
|
||||||
@ -72,8 +82,9 @@ export const ChatGroup = ({
|
|||||||
const [replyMessage, setReplyMessage] = useState(null);
|
const [replyMessage, setReplyMessage] = useState(null);
|
||||||
const [onEditMessage, setOnEditMessage] = useState(null);
|
const [onEditMessage, setOnEditMessage] = useState(null);
|
||||||
const [isOpenQManager, setIsOpenQManager] = useState(null);
|
const [isOpenQManager, setIsOpenQManager] = useState(null);
|
||||||
|
const [isDeleteImage, setIsDeleteImage] = useState(false);
|
||||||
const [messageSize, setMessageSize] = useState(0);
|
const [messageSize, setMessageSize] = useState(0);
|
||||||
|
const [chatImagesToSave, setChatImagesToSave] = useState([]);
|
||||||
const hasInitializedWebsocket = useRef(false);
|
const hasInitializedWebsocket = useRef(false);
|
||||||
const socketRef = useRef(null); // WebSocket reference
|
const socketRef = useRef(null); // WebSocket reference
|
||||||
const timeoutIdRef = useRef(null); // Timeout ID reference
|
const timeoutIdRef = useRef(null); // Timeout ID reference
|
||||||
@ -778,11 +789,71 @@ export const ChatGroup = ({
|
|||||||
: {
|
: {
|
||||||
isEdited: chatReference ? true : false,
|
isEdited: chatReference ? true : false,
|
||||||
};
|
};
|
||||||
|
const imagesToPublish = [];
|
||||||
|
const deleteImage =
|
||||||
|
onEditMessage && isDeleteImage && messageHasImage(onEditMessage);
|
||||||
|
if (deleteImage) {
|
||||||
|
const fee = await getFee('ARBITRARY');
|
||||||
|
|
||||||
|
await show({
|
||||||
|
publishFee: fee.fee + ' QORT',
|
||||||
|
message: 'Would you like to delete your previous chat image?',
|
||||||
|
});
|
||||||
|
await window.sendMessage('publishOnQDN', {
|
||||||
|
data: 'RA==',
|
||||||
|
identifier: onEditMessage?.images[0]?.identifier,
|
||||||
|
service: onEditMessage?.images[0]?.service,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (chatImagesToSave?.length > 0) {
|
||||||
|
const imageToSave = chatImagesToSave[0];
|
||||||
|
|
||||||
|
const base64ToSave = isPrivate
|
||||||
|
? await encryptChatMessage(imageToSave, secretKeyObject)
|
||||||
|
: imageToSave;
|
||||||
|
// 1 represents public group, 0 is private
|
||||||
|
const identifier = `grp-q-manager_${isPrivate ? 0 : 1}_group_${selectedGroup}_${uidImages.rnd()}`;
|
||||||
|
imagesToPublish.push({
|
||||||
|
service: 'IMAGE',
|
||||||
|
identifier,
|
||||||
|
name: myName,
|
||||||
|
base64: base64ToSave,
|
||||||
|
});
|
||||||
|
|
||||||
|
const res = await window.sendMessage(
|
||||||
|
'PUBLISH_MULTIPLE_QDN_RESOURCES',
|
||||||
|
|
||||||
|
{
|
||||||
|
resources: imagesToPublish,
|
||||||
|
},
|
||||||
|
240000,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
if (res !== true) throw new Error('Unable to publish images');
|
||||||
|
}
|
||||||
|
|
||||||
|
const images =
|
||||||
|
imagesToPublish?.length > 0
|
||||||
|
? imagesToPublish.map((item) => {
|
||||||
|
return {
|
||||||
|
name: item.name,
|
||||||
|
identifier: item.identifier,
|
||||||
|
service: item.service,
|
||||||
|
timestamp: Date.now(),
|
||||||
|
};
|
||||||
|
})
|
||||||
|
: chatReference
|
||||||
|
? isDeleteImage
|
||||||
|
? []
|
||||||
|
: onEditMessage?.images || []
|
||||||
|
: [];
|
||||||
|
|
||||||
const otherData = {
|
const otherData = {
|
||||||
repliedTo,
|
repliedTo,
|
||||||
...(onEditMessage?.decryptedData || {}),
|
...(onEditMessage?.decryptedData || {}),
|
||||||
type: chatReference ? 'edit' : '',
|
type: chatReference ? 'edit' : '',
|
||||||
specialId: uid.rnd(),
|
specialId: uid.rnd(),
|
||||||
|
images: images,
|
||||||
...publicData,
|
...publicData,
|
||||||
};
|
};
|
||||||
const objectMessage = {
|
const objectMessage = {
|
||||||
@ -790,6 +861,7 @@ export const ChatGroup = ({
|
|||||||
[isPrivate ? 'message' : 'messageText']: message,
|
[isPrivate ? 'message' : 'messageText']: message,
|
||||||
version: 3,
|
version: 3,
|
||||||
};
|
};
|
||||||
|
|
||||||
const message64: any = await objectToBase64(objectMessage);
|
const message64: any = await objectToBase64(objectMessage);
|
||||||
|
|
||||||
const encryptSingle =
|
const encryptSingle =
|
||||||
@ -824,6 +896,8 @@ export const ChatGroup = ({
|
|||||||
clearEditorContent();
|
clearEditorContent();
|
||||||
setReplyMessage(null);
|
setReplyMessage(null);
|
||||||
setOnEditMessage(null);
|
setOnEditMessage(null);
|
||||||
|
setIsDeleteImage(false);
|
||||||
|
setChatImagesToSave([]);
|
||||||
}
|
}
|
||||||
// send chat message
|
// send chat message
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -889,6 +963,8 @@ export const ChatGroup = ({
|
|||||||
}
|
}
|
||||||
setReplyMessage(message);
|
setReplyMessage(message);
|
||||||
setOnEditMessage(null);
|
setOnEditMessage(null);
|
||||||
|
setIsDeleteImage(false);
|
||||||
|
setChatImagesToSave([]);
|
||||||
editorRef?.current?.chain().focus();
|
editorRef?.current?.chain().focus();
|
||||||
},
|
},
|
||||||
[onEditMessage]
|
[onEditMessage]
|
||||||
@ -986,6 +1062,23 @@ export const ChatGroup = ({
|
|||||||
|
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
|
||||||
|
const insertImage = useCallback(
|
||||||
|
(img) => {
|
||||||
|
if (
|
||||||
|
chatImagesToSave?.length > 0 ||
|
||||||
|
(messageHasImage(onEditMessage) && !isDeleteImage)
|
||||||
|
) {
|
||||||
|
setInfoSnack({
|
||||||
|
type: 'error',
|
||||||
|
message: 'This message already has an image',
|
||||||
|
});
|
||||||
|
setOpenSnack(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setChatImagesToSave((prev) => [...prev, img]);
|
||||||
|
},
|
||||||
|
[chatImagesToSave, onEditMessage?.images, isDeleteImage]
|
||||||
|
);
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
@ -1049,6 +1142,116 @@ export const ChatGroup = ({
|
|||||||
width: 'calc(100% - 100px)',
|
width: 'calc(100% - 100px)',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
alignItems: 'flex-start',
|
||||||
|
display: 'flex',
|
||||||
|
width: '100%',
|
||||||
|
gap: '10px',
|
||||||
|
flexWrap: 'wrap',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{!isDeleteImage &&
|
||||||
|
onEditMessage &&
|
||||||
|
messageHasImage(onEditMessage) &&
|
||||||
|
onEditMessage?.images?.map((_, index) => (
|
||||||
|
<div
|
||||||
|
key={index}
|
||||||
|
style={{
|
||||||
|
position: 'relative',
|
||||||
|
height: '50px',
|
||||||
|
width: '50px',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ImageIcon
|
||||||
|
color="primary"
|
||||||
|
sx={{
|
||||||
|
height: '100%',
|
||||||
|
width: '100%',
|
||||||
|
borderRadius: '3px',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Tooltip title="Delete image">
|
||||||
|
<IconButton
|
||||||
|
onClick={() => setIsDeleteImage(true)}
|
||||||
|
size="small"
|
||||||
|
sx={{
|
||||||
|
position: 'absolute',
|
||||||
|
top: '50%',
|
||||||
|
left: '50%',
|
||||||
|
transform: 'translate(-50%, -50%)',
|
||||||
|
backgroundColor: (theme) =>
|
||||||
|
theme.palette.background.paper,
|
||||||
|
color: (theme) => theme.palette.text.primary,
|
||||||
|
borderRadius: '50%',
|
||||||
|
opacity: 0,
|
||||||
|
transition: 'opacity 0.2s',
|
||||||
|
boxShadow: (theme) => theme.shadows[2],
|
||||||
|
'&:hover': {
|
||||||
|
backgroundColor: (theme) =>
|
||||||
|
theme.palette.background.default,
|
||||||
|
opacity: 1,
|
||||||
|
},
|
||||||
|
pointerEvents: 'auto',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CloseIcon fontSize="small" />
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
{chatImagesToSave.map((imgBase64, index) => (
|
||||||
|
<div
|
||||||
|
key={index}
|
||||||
|
style={{
|
||||||
|
position: 'relative',
|
||||||
|
height: '50px',
|
||||||
|
width: '50px',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src={`data:image/webp;base64,${imgBase64}`}
|
||||||
|
style={{
|
||||||
|
height: '100%',
|
||||||
|
width: '100%',
|
||||||
|
objectFit: 'contain',
|
||||||
|
borderRadius: '3px',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Tooltip title="Remove image">
|
||||||
|
<IconButton
|
||||||
|
onClick={() =>
|
||||||
|
setChatImagesToSave((prev) =>
|
||||||
|
prev.filter((_, i) => i !== index)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
size="small"
|
||||||
|
sx={{
|
||||||
|
position: 'absolute',
|
||||||
|
top: '50%',
|
||||||
|
left: '50%',
|
||||||
|
transform: 'translate(-50%, -50%)',
|
||||||
|
backgroundColor: (theme) =>
|
||||||
|
theme.palette.background.paper,
|
||||||
|
color: (theme) => theme.palette.text.primary,
|
||||||
|
borderRadius: '50%',
|
||||||
|
opacity: 0,
|
||||||
|
transition: 'opacity 0.2s',
|
||||||
|
boxShadow: (theme) => theme.shadows[2],
|
||||||
|
'&:hover': {
|
||||||
|
backgroundColor: (theme) =>
|
||||||
|
theme.palette.background.default,
|
||||||
|
opacity: 1,
|
||||||
|
},
|
||||||
|
pointerEvents: 'auto',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CloseIcon fontSize="small" />
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</Box>
|
||||||
{replyMessage && (
|
{replyMessage && (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
@ -1065,6 +1268,8 @@ export const ChatGroup = ({
|
|||||||
setReplyMessage(null);
|
setReplyMessage(null);
|
||||||
|
|
||||||
setOnEditMessage(null);
|
setOnEditMessage(null);
|
||||||
|
setIsDeleteImage(false);
|
||||||
|
setChatImagesToSave([]);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ExitIcon />
|
<ExitIcon />
|
||||||
@ -1086,7 +1291,8 @@ export const ChatGroup = ({
|
|||||||
onClick={() => {
|
onClick={() => {
|
||||||
setReplyMessage(null);
|
setReplyMessage(null);
|
||||||
setOnEditMessage(null);
|
setOnEditMessage(null);
|
||||||
|
setIsDeleteImage(false);
|
||||||
|
setChatImagesToSave([]);
|
||||||
clearEditorContent();
|
clearEditorContent();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@ -1104,6 +1310,7 @@ export const ChatGroup = ({
|
|||||||
isFocusedParent={isFocusedParent}
|
isFocusedParent={isFocusedParent}
|
||||||
setIsFocusedParent={setIsFocusedParent}
|
setIsFocusedParent={setIsFocusedParent}
|
||||||
membersWithNames={members}
|
membersWithNames={members}
|
||||||
|
insertImage={insertImage}
|
||||||
/>
|
/>
|
||||||
{messageSize > 750 && (
|
{messageSize > 750 && (
|
||||||
<Box
|
<Box
|
||||||
|
@ -292,6 +292,11 @@ export const ChatList = ({
|
|||||||
message.editTimestamp =
|
message.editTimestamp =
|
||||||
chatReferences[message.signature]?.edit?.timestamp;
|
chatReferences[message.signature]?.edit?.timestamp;
|
||||||
}
|
}
|
||||||
|
if (chatReferences[message.signature]?.edit?.images) {
|
||||||
|
message.images =
|
||||||
|
chatReferences[message.signature]?.edit?.images;
|
||||||
|
message.isEdit = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if message is updating
|
// Check if message is updating
|
||||||
|
@ -45,6 +45,8 @@ import level7Img from '../../assets/badges/level-7.png';
|
|||||||
import level8Img from '../../assets/badges/level-8.png';
|
import level8Img from '../../assets/badges/level-8.png';
|
||||||
import level9Img from '../../assets/badges/level-9.png';
|
import level9Img from '../../assets/badges/level-9.png';
|
||||||
import level10Img from '../../assets/badges/level-10.png';
|
import level10Img from '../../assets/badges/level-10.png';
|
||||||
|
import { Embed } from '../Embeds/Embed';
|
||||||
|
import { buildImageEmbedLink, messageHasImage } from '../../utils/chat';
|
||||||
|
|
||||||
const getBadgeImg = (level) => {
|
const getBadgeImg = (level) => {
|
||||||
switch (level?.toString()) {
|
switch (level?.toString()) {
|
||||||
@ -366,7 +368,9 @@ export const MessageItem = React.memo(
|
|||||||
) : (
|
) : (
|
||||||
<MessageDisplay htmlContent={message.text} />
|
<MessageDisplay htmlContent={message.text} />
|
||||||
)}
|
)}
|
||||||
|
{message?.images && messageHasImage(message) && (
|
||||||
|
<Embed embedLink={buildImageEmbedLink(message.images[0])} />
|
||||||
|
)}
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
|
@ -30,6 +30,7 @@ import MentionList from './MentionList.jsx';
|
|||||||
import { isDisabledEditorEnterAtom } from '../../atoms/global.js';
|
import { isDisabledEditorEnterAtom } from '../../atoms/global.js';
|
||||||
import { Box, Checkbox, Typography, useTheme } from '@mui/material';
|
import { Box, Checkbox, Typography, useTheme } from '@mui/material';
|
||||||
import { useAtom } from 'jotai';
|
import { useAtom } from 'jotai';
|
||||||
|
import { fileToBase64 } from '../../utils/fileReading/index.js';
|
||||||
|
|
||||||
function textMatcher(doc, from) {
|
function textMatcher(doc, from) {
|
||||||
const textBeforeCursor = doc.textBetween(0, from, ' ', ' ');
|
const textBeforeCursor = doc.textBetween(0, from, ' ', ' ');
|
||||||
@ -114,13 +115,13 @@ const MenuBar = React.memo(
|
|||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (editor) {
|
if (editor && !isChat) {
|
||||||
editor.view.dom.addEventListener('paste', handlePaste);
|
editor.view.dom.addEventListener('paste', handlePaste);
|
||||||
return () => {
|
return () => {
|
||||||
editor.view.dom.removeEventListener('paste', handlePaste);
|
editor.view.dom.removeEventListener('paste', handlePaste);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}, [editor]);
|
}, [editor, isChat]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="control-group">
|
<div className="control-group">
|
||||||
@ -366,12 +367,46 @@ export default ({
|
|||||||
customEditorHeight,
|
customEditorHeight,
|
||||||
membersWithNames,
|
membersWithNames,
|
||||||
enableMentions,
|
enableMentions,
|
||||||
|
insertImage,
|
||||||
}) => {
|
}) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const [isDisabledEditorEnter, setIsDisabledEditorEnter] = useAtom(
|
const [isDisabledEditorEnter, setIsDisabledEditorEnter] = useAtom(
|
||||||
isDisabledEditorEnterAtom
|
isDisabledEditorEnterAtom
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const handleImageUpload = useCallback(
|
||||||
|
async (file) => {
|
||||||
|
try {
|
||||||
|
if (!file.type.includes('image')) return;
|
||||||
|
let compressedFile = file;
|
||||||
|
if (file.type !== 'image/gif') {
|
||||||
|
await new Promise<void>((resolve) => {
|
||||||
|
new Compressor(file, {
|
||||||
|
quality: 0.6,
|
||||||
|
maxWidth: 1200,
|
||||||
|
mimeType: 'image/webp',
|
||||||
|
success(result) {
|
||||||
|
compressedFile = result;
|
||||||
|
resolve();
|
||||||
|
},
|
||||||
|
error(err) {
|
||||||
|
console.error('Image compression error:', err);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (compressedFile) {
|
||||||
|
const toBase64 = await fileToBase64(compressedFile);
|
||||||
|
insertImage(toBase64);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[insertImage]
|
||||||
|
);
|
||||||
|
|
||||||
const extensionsFiltered = isChat
|
const extensionsFiltered = isChat
|
||||||
? extensions.filter((item) => item?.name !== 'image')
|
? extensions.filter((item) => item?.name !== 'image')
|
||||||
: extensions;
|
: extensions;
|
||||||
@ -543,6 +578,24 @@ export default ({
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
|
handlePaste(view, event) {
|
||||||
|
if (!isChat) return;
|
||||||
|
const items = event.clipboardData?.items;
|
||||||
|
if (!items) return false;
|
||||||
|
|
||||||
|
for (const item of items) {
|
||||||
|
if (item.type.startsWith('image/')) {
|
||||||
|
const file = item.getAsFile();
|
||||||
|
if (file) {
|
||||||
|
event.preventDefault(); // Block the default paste
|
||||||
|
handleImageUpload(file); // Custom handler
|
||||||
|
return true; // Let ProseMirror know we handled it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false; // fallback to default behavior otherwise
|
||||||
|
},
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
@ -1694,7 +1694,7 @@ export const sendChatMessage = async (data, isFromExtension, appInfo) => {
|
|||||||
? fullMessageObject
|
? fullMessageObject
|
||||||
: {
|
: {
|
||||||
messageText: tiptapJson,
|
messageText: tiptapJson,
|
||||||
images: [''],
|
images: [],
|
||||||
repliedTo: '',
|
repliedTo: '',
|
||||||
version: 3,
|
version: 3,
|
||||||
};
|
};
|
||||||
|
22
src/utils/chat.ts
Normal file
22
src/utils/chat.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
export function buildImageEmbedLink(image?: {
|
||||||
|
name?: string;
|
||||||
|
identifier?: string;
|
||||||
|
service?: string;
|
||||||
|
timestamp?: number;
|
||||||
|
}): string | null {
|
||||||
|
if (!image?.name || !image.identifier || !image.service) return null;
|
||||||
|
|
||||||
|
const base = `qortal://use-embed/IMAGE?name=${image.name}&identifier=${image.identifier}&service=${image.service}&mimeType=image%2Fpng×tamp=${image?.timestamp || ''}`;
|
||||||
|
|
||||||
|
const isEncrypted = image.identifier.startsWith('grp-q-manager_0');
|
||||||
|
return isEncrypted ? `${base}&encryptionType=group` : base;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const messageHasImage = (message) => {
|
||||||
|
return (
|
||||||
|
Array.isArray(message?.images) &&
|
||||||
|
message.images[0]?.identifier &&
|
||||||
|
message.images[0]?.name &&
|
||||||
|
message.images[0]?.service
|
||||||
|
);
|
||||||
|
};
|
Loading…
x
Reference in New Issue
Block a user