mirror of
https://github.com/Qortal/Qortal-Hub.git
synced 2025-05-15 22:26:58 +00:00
added publishing and deleting of images
This commit is contained in:
parent
380e7d2387
commit
4767ddd9fa
@ -1,3 +1,5 @@
|
||||
//TODO
|
||||
|
||||
import { useRef, useState, useCallback, useMemo } from 'react';
|
||||
|
||||
interface State {
|
||||
@ -34,7 +36,7 @@ export const useModal = () => {
|
||||
const onCancel = useCallback(() => {
|
||||
const { reject } = promiseConfig.current || {};
|
||||
hide();
|
||||
reject?.();
|
||||
reject?.('Declined');
|
||||
}, [hide]);
|
||||
|
||||
return useMemo(
|
||||
|
@ -31,18 +31,28 @@ import {
|
||||
subscribeToEvent,
|
||||
unsubscribeFromEvent,
|
||||
} 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 { ReplyPreview } from './MessageItem';
|
||||
import { ExitIcon } from '../../assets/Icons/ExitIcon';
|
||||
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 CloseIcon from '@mui/icons-material/Close';
|
||||
import { throttle } from 'lodash';
|
||||
|
||||
import ImageIcon from '@mui/icons-material/Image';
|
||||
import { messageHasImage } from '../../utils/chat';
|
||||
const uid = new ShortUniqueId({ length: 5 });
|
||||
const uidImages = new ShortUniqueId({ length: 12 });
|
||||
|
||||
export const ChatGroup = ({
|
||||
selectedGroup,
|
||||
secretKey,
|
||||
@ -59,7 +69,7 @@ export const ChatGroup = ({
|
||||
hideView,
|
||||
isPrivate,
|
||||
}) => {
|
||||
const { isUserBlocked } = useContext(MyContext);
|
||||
const { isUserBlocked, show } = useContext(MyContext);
|
||||
const [messages, setMessages] = useState([]);
|
||||
const [chatReferences, setChatReferences] = useState({});
|
||||
const [isSending, setIsSending] = useState(false);
|
||||
@ -72,7 +82,7 @@ export const ChatGroup = ({
|
||||
const [replyMessage, setReplyMessage] = useState(null);
|
||||
const [onEditMessage, setOnEditMessage] = useState(null);
|
||||
const [isOpenQManager, setIsOpenQManager] = useState(null);
|
||||
|
||||
const [isDeleteImage, setIsDeleteImage] = useState(false);
|
||||
const [messageSize, setMessageSize] = useState(0);
|
||||
const [chatImagesToSave, setChatImagesToSave] = useState([]);
|
||||
const hasInitializedWebsocket = useRef(false);
|
||||
@ -780,15 +790,34 @@ export const ChatGroup = ({
|
||||
isEdited: chatReference ? true : false,
|
||||
};
|
||||
const imagesToPublish = [];
|
||||
if (!chatReference && chatImagesToSave?.length > 0) {
|
||||
chatImagesToSave.forEach((base64Img) => {
|
||||
const identifier = `qchat_1_group_${selectedGroup}_${uidImages.rnd()}`;
|
||||
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: base64Img,
|
||||
});
|
||||
base64: base64ToSave,
|
||||
});
|
||||
|
||||
const res = await window.sendMessage(
|
||||
@ -800,24 +829,29 @@ export const ChatGroup = ({
|
||||
240000,
|
||||
true
|
||||
);
|
||||
console.log('res', res);
|
||||
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
|
||||
? onEditMessage?.images || []
|
||||
: [];
|
||||
|
||||
const otherData = {
|
||||
repliedTo,
|
||||
...(onEditMessage?.decryptedData || {}),
|
||||
type: chatReference ? 'edit' : '',
|
||||
specialId: uid.rnd(),
|
||||
images:
|
||||
onEditMessage?.images ||
|
||||
imagesToPublish.map((item) => {
|
||||
return {
|
||||
name: item.name,
|
||||
identifier: item.identifier,
|
||||
service: item.service,
|
||||
};
|
||||
}),
|
||||
images: images,
|
||||
...publicData,
|
||||
};
|
||||
const objectMessage = {
|
||||
@ -825,6 +859,7 @@ export const ChatGroup = ({
|
||||
[isPrivate ? 'message' : 'messageText']: message,
|
||||
version: 3,
|
||||
};
|
||||
|
||||
const message64: any = await objectToBase64(objectMessage);
|
||||
|
||||
const encryptSingle =
|
||||
@ -859,6 +894,7 @@ export const ChatGroup = ({
|
||||
clearEditorContent();
|
||||
setReplyMessage(null);
|
||||
setOnEditMessage(null);
|
||||
setIsDeleteImage(false);
|
||||
setChatImagesToSave([]);
|
||||
}
|
||||
// send chat message
|
||||
@ -925,6 +961,8 @@ export const ChatGroup = ({
|
||||
}
|
||||
setReplyMessage(message);
|
||||
setOnEditMessage(null);
|
||||
setIsDeleteImage(false);
|
||||
setChatImagesToSave([]);
|
||||
editorRef?.current?.chain().focus();
|
||||
},
|
||||
[onEditMessage]
|
||||
@ -1022,10 +1060,23 @@ export const ChatGroup = ({
|
||||
|
||||
const theme = useTheme();
|
||||
|
||||
const insertImage = useCallback((img) => {
|
||||
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 (
|
||||
<div
|
||||
style={{
|
||||
@ -1098,19 +1149,106 @@ export const ChatGroup = ({
|
||||
flexWrap: 'wrap',
|
||||
}}
|
||||
>
|
||||
{chatImagesToSave?.map((imgBase64) => {
|
||||
return (
|
||||
<img
|
||||
{!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',
|
||||
}}
|
||||
src={`data:image/webp;base64,${imgBase64}`}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
<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 && (
|
||||
<Box
|
||||
@ -1128,6 +1266,8 @@ export const ChatGroup = ({
|
||||
setReplyMessage(null);
|
||||
|
||||
setOnEditMessage(null);
|
||||
setIsDeleteImage(false);
|
||||
setChatImagesToSave([]);
|
||||
}}
|
||||
>
|
||||
<ExitIcon />
|
||||
@ -1149,7 +1289,8 @@ export const ChatGroup = ({
|
||||
onClick={() => {
|
||||
setReplyMessage(null);
|
||||
setOnEditMessage(null);
|
||||
|
||||
setIsDeleteImage(false);
|
||||
setChatImagesToSave([]);
|
||||
clearEditorContent();
|
||||
}}
|
||||
>
|
||||
|
@ -292,6 +292,11 @@ export const ChatList = ({
|
||||
message.editTimestamp =
|
||||
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
|
||||
|
@ -45,6 +45,8 @@ import level7Img from '../../assets/badges/level-7.png';
|
||||
import level8Img from '../../assets/badges/level-8.png';
|
||||
import level9Img from '../../assets/badges/level-9.png';
|
||||
import level10Img from '../../assets/badges/level-10.png';
|
||||
import { Embed } from '../Embeds/Embed';
|
||||
import { buildImageEmbedLink, messageHasImage } from '../../utils/chat';
|
||||
|
||||
const getBadgeImg = (level) => {
|
||||
switch (level?.toString()) {
|
||||
@ -366,7 +368,9 @@ export const MessageItem = React.memo(
|
||||
) : (
|
||||
<MessageDisplay htmlContent={message.text} />
|
||||
)}
|
||||
|
||||
{message?.images && messageHasImage(message) && (
|
||||
<Embed embedLink={buildImageEmbedLink(message.images[0])} />
|
||||
)}
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
|
@ -400,7 +400,6 @@ export default ({
|
||||
if (compressedFile) {
|
||||
const toBase64 = await fileToBase64(compressedFile);
|
||||
insertImage(toBase64);
|
||||
console.log('toBase64', toBase64);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
|
@ -1228,7 +1228,6 @@ export const publishMultipleQDNResources = async (
|
||||
sender,
|
||||
isFromExtension
|
||||
) => {
|
||||
console.log('data', data);
|
||||
const requiredFields = ['resources'];
|
||||
const missingFields: string[] = [];
|
||||
let feeAmount = null;
|
||||
@ -1695,7 +1694,7 @@ export const sendChatMessage = async (data, isFromExtension, appInfo) => {
|
||||
? fullMessageObject
|
||||
: {
|
||||
messageText: tiptapJson,
|
||||
images: [''],
|
||||
images: [],
|
||||
repliedTo: '',
|
||||
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