Identifiers.ts created, all identifiers used in app are located here.

Fixed warning involving messages in Thread.tsx not having a key prop.
This commit is contained in:
Qortal Dev 2024-08-23 15:33:33 -06:00
parent 8d54ec7d1c
commit 6ad071376b
9 changed files with 950 additions and 866 deletions

View File

@ -1,21 +1,22 @@
import React from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { setNotification } from '../../state/features/notificationsSlice'
import { RootState } from '../../state/store'
import ShortUniqueId from 'short-unique-id'
import React from "react";
import { useDispatch, useSelector } from "react-redux";
import { AUDIO_BASE } from "../../constants/Identifiers";
import { setNotification } from "../../state/features/notificationsSlice";
import { RootState } from "../../state/store";
import ShortUniqueId from "short-unique-id";
const uid = new ShortUniqueId()
const uid = new ShortUniqueId();
interface IPublishVideo {
title: string
description: string
base64: string
category: string
title: string;
description: string;
base64: string;
category: string;
}
export const usePublishAudio = () => {
const { user } = useSelector((state: RootState) => state.auth)
const dispatch = useDispatch()
const { user } = useSelector((state: RootState) => state.auth);
const dispatch = useDispatch();
const publishAudio = async ({
title,
description,
@ -23,84 +24,83 @@ export const usePublishAudio = () => {
category,
...rest
}: IPublishVideo) => {
let address
let name
let errorMsg = ''
let address;
let name;
let errorMsg = "";
address = user?.address
name = user?.name || ''
address = user?.address;
name = user?.name || "";
const missingFields = []
const missingFields = [];
if (!address) {
errorMsg = "Cannot post: your address isn't available"
errorMsg = "Cannot post: your address isn't available";
}
if (!name) {
errorMsg = 'Cannot post without a name'
errorMsg = "Cannot post without a name";
}
if (!title) missingFields.push('title')
if (!title) missingFields.push("title");
if (missingFields.length > 0) {
const missingFieldsString = missingFields.join(', ')
const errMsg = `Missing: ${missingFieldsString}`
errorMsg = errMsg
const missingFieldsString = missingFields.join(", ");
const errMsg = `Missing: ${missingFieldsString}`;
errorMsg = errMsg;
}
if (errorMsg) {
dispatch(
setNotification({
msg: errorMsg,
alertType: 'error'
alertType: "error",
})
)
throw new Error(errorMsg)
);
throw new Error(errorMsg);
}
try {
const id = uid()
const id = uid();
const identifier = `qaudio_qblog_${id}`
const identifier = AUDIO_BASE + id;
const resourceResponse = await qortalRequest({
action: 'PUBLISH_QDN_RESOURCE',
action: "PUBLISH_QDN_RESOURCE",
name: name,
service: 'AUDIO',
service: "AUDIO",
data64: base64,
title: title,
description: description,
category: category,
...rest,
identifier: identifier
})
identifier: identifier,
});
dispatch(
setNotification({
msg: 'Audio successfully published',
alertType: 'success'
msg: "Audio successfully published",
alertType: "success",
})
)
return resourceResponse
);
return resourceResponse;
} catch (error: any) {
let notificationObj = null
if (typeof error === 'string') {
let notificationObj = null;
if (typeof error === "string") {
notificationObj = {
msg: error || 'Failed to publish audio',
alertType: 'error'
}
} else if (typeof error?.error === 'string') {
msg: error || "Failed to publish audio",
alertType: "error",
};
} else if (typeof error?.error === "string") {
notificationObj = {
msg: error?.error || 'Failed to publish audio',
alertType: 'error'
}
msg: error?.error || "Failed to publish audio",
alertType: "error",
};
} else {
notificationObj = {
msg: error?.message || error?.message || 'Failed to publish audio',
alertType: 'error'
}
msg: error?.message || error?.message || "Failed to publish audio",
alertType: "error",
};
}
if (!notificationObj) return
dispatch(setNotification(notificationObj))
if (!notificationObj) return;
dispatch(setNotification(notificationObj));
}
}
};
return {
publishAudio
}
}
publishAudio,
};
};

View File

@ -0,0 +1,6 @@
export const AUDIO_BASE = "qaudio_qblog_";
export const THREAD_BASE = "qortal_qmail_thread_group";
export const ATTATCHMENT_BASE = "attachments_qmail_";
export const QMAIL_BASE = "_mail_qortal_qmail_";
export const THREAD_MESSAGE = "qortal_qmail_thmsg_group";

View File

@ -8,6 +8,7 @@ import React, {
} from "react";
import { useNavigate } from "react-router-dom";
import { useDispatch, useSelector } from "react-redux";
import { THREAD_BASE, THREAD_MESSAGE } from "../../constants/Identifiers";
import { RootState } from "../../state/store";
import EditIcon from "@mui/icons-material/Edit";
import {
@ -128,7 +129,7 @@ export const GroupMail = ({
if (isInitial) {
dispatch(setIsLoadingCustom("Loading threads"));
}
const query = `qortal_qmail_thread_group${groupId}`;
const query = `${THREAD_BASE}${groupId}`;
const url = `/arbitrary/resources/search?mode=ALL&service=${THREAD_SERVICE_TYPE}&query=${query}&limit=${20}&includemetadata=true&offset=${offset}&reverse=${isReverse}&excludeblocked=true`;
const response = await fetch(url, {
method: "GET",
@ -213,7 +214,7 @@ export const GroupMail = ({
.join("");
dispatch(setIsLoadingCustom("Loading recent threads"));
const query = `qortal_qmail_thmsg_group${groupId}`;
const query = `${THREAD_MESSAGE}${groupId}`;
const url = `/arbitrary/resources/search?mode=ALL&service=${MAIL_SERVICE_TYPE}&query=${query}&limit=100&includemetadata=false&offset=${0}&reverse=true&excludeblocked=true${queryString}`;
const response = await fetch(url, {
method: "GET",
@ -237,7 +238,7 @@ export const GroupMail = ({
.map(key => {
return {
...messagesForThread[key],
threadId: `qortal_qmail_thread_group${groupId}_${key}`,
threadId: `${THREAD_BASE}${groupId}_${key}`,
};
})
.sort((a, b) => b.created - a.created)

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,8 @@
import React, { Dispatch, useCallback, useEffect, useState } from "react";
import { ReusableModal } from "../../components/modals/ReusableModal";
import { Box, Button, Input, Typography, useTheme } from "@mui/material";
import { ATTATCHMENT_BASE, THREAD_BASE } from "../../constants/Identifiers";
import { getFileExtension } from "../../utils/helpers";
import { BuilderButton } from "../CreatePost/CreatePost-styles";
import BlogEditor from "../../components/editor/BlogEditor";
import EmailIcon from "@mui/icons-material/Email";
@ -19,7 +21,6 @@ import ModalCloseSVG from "../../assets/svgs/ModalClose.svg";
import AttachmentSVG from "../../assets/svgs/NewMessageAttachment.svg";
import CreateThreadSVG from "../../assets/svgs/CreateThread.svg";
import {
objectToBase64,
objectToUint8Array,
@ -70,7 +71,7 @@ interface NewMessageProps {
currentThread?: any;
isMessage?: boolean;
messageCallback?: (val: any) => void;
threadCallback?: (val: any)=> void;
threadCallback?: (val: any) => void;
refreshLatestThreads?: () => void;
members: any;
}
@ -83,7 +84,7 @@ export const NewThread = ({
isMessage = false,
messageCallback,
refreshLatestThreads,
threadCallback
threadCallback,
}: NewMessageProps) => {
const [isOpen, setIsOpen] = useState<boolean>(false);
const [value, setValue] = useState("");
@ -98,7 +99,6 @@ export const NewThread = ({
const [publishes, setPublishes] = useState<any>(null);
const [callbackContent, setCallbackContent] = useState<any>(null);
const theme = useTheme();
const navigate = useNavigate();
@ -115,7 +115,7 @@ export const NewThread = ({
files.push({
file: item,
mimetype: null,
extension: null,
extension: getFileExtension(item.name),
});
} else {
const extension = mime.getExtension(type);
@ -123,7 +123,7 @@ export const NewThread = ({
files.push({
file: item,
mimetype: type,
extension: null,
extension: getFileExtension(item.name),
});
} else {
files.push({
@ -169,7 +169,6 @@ export const NewThread = ({
setIsOpen(false);
};
useEffect(() => {
subscribeToEvent("openNewThreadModal", openModalFromEvent);
@ -264,7 +263,7 @@ export const NewThread = ({
const id = uid();
const id2 = uid();
const identifier = `attachments_qmail_${id}_${id2}`;
const identifier = `${ATTATCHMENT_BASE}${id}_${id2}`;
let fileExtension = attachment?.name?.split(".")?.pop();
if (!fileExtension) {
fileExtension = singleAttachment.extension;
@ -314,7 +313,7 @@ export const NewThread = ({
name,
};
const threadToBase64 = await objectToBase64(threadObject);
let identifierThread = `qortal_qmail_thread_group${groupInfo.id}_${idThread}`;
let identifierThread = `${THREAD_BASE}${groupInfo.id}_${idThread}`;
let requestBodyThread: any = {
name: name,
service: THREAD_SERVICE_TYPE,
@ -366,10 +365,10 @@ export const NewThread = ({
name,
threadId: identifierThread,
created: Date.now(),
service: 'MAIL_PRIVATE',
identifier: identifier
}
})
service: "MAIL_PRIVATE",
identifier: identifier,
},
});
}
closeModal();
} else {
@ -410,8 +409,8 @@ export const NewThread = ({
service: MAIL_SERVICE_TYPE,
created: Date.now(),
...mailObject,
}
})
},
});
// messageCallback({
// identifier,
// id: identifier,
@ -484,7 +483,7 @@ export const NewThread = ({
{isMessage ? "Post Message" : "New Thread"}
</NewMessageHeaderP>
<CloseContainer onClick={closeModal}>
<NewMessageCloseImg src={ModalCloseSVG} />
<NewMessageCloseImg src={ModalCloseSVG} />
</CloseContainer>
</InstanceListHeader>
<InstanceListContainer
@ -497,46 +496,46 @@ export const NewThread = ({
>
{!isMessage && (
<>
<Spacer height="10px" />
<NewMessageInputRow>
<Input
id="standard-adornment-name"
value={threadTitle}
onChange={(e) => {
setThreadTitle(e.target.value)
}}
placeholder="Thread Title"
disableUnderline
autoComplete='off'
autoCorrect='off'
sx={{
width: '100%',
color: 'var(--new-message-text)',
'& .MuiInput-input::placeholder': {
color: 'rgba(84, 84, 84, 0.70) !important',
fontSize: '20px',
fontStyle: 'normal',
fontWeight: 400,
lineHeight: '120%', // 24px
letterSpacing: '0.15px',
opacity: 1
},
'&:focus': {
outline: 'none',
},
// Add any additional styles for the input here
}}
/>
</NewMessageInputRow>
<Spacer height="10px" />
<NewMessageInputRow>
<Input
id="standard-adornment-name"
value={threadTitle}
onChange={e => {
setThreadTitle(e.target.value);
}}
placeholder="Thread Title"
disableUnderline
autoComplete="off"
autoCorrect="off"
sx={{
width: "100%",
color: "var(--new-message-text)",
"& .MuiInput-input::placeholder": {
color: "rgba(84, 84, 84, 0.70) !important",
fontSize: "20px",
fontStyle: "normal",
fontWeight: 400,
lineHeight: "120%", // 24px
letterSpacing: "0.15px",
opacity: 1,
},
"&:focus": {
outline: "none",
},
// Add any additional styles for the input here
}}
/>
</NewMessageInputRow>
</>
)}
<Spacer height="10px" />
<NewMessageInputRow sx={{
gap: '10px'
}}>
<Spacer height="10px" />
<NewMessageInputRow
sx={{
gap: "10px",
}}
>
<AttachmentContainer
{...getRootProps()}
sx={{
@ -562,6 +561,7 @@ export const NewThread = ({
alignItems: "center",
gap: "15px",
}}
key={file.name + index}
>
<Typography
sx={{
@ -628,55 +628,57 @@ export const NewThread = ({
{isMessage ? "Post" : "Create Thread"}
</NewMessageSendP>
{isMessage ? (
<SendNewMessage
color="red"
opacity={1}
height="25px"
width="25px"
/>
<SendNewMessage
color="red"
opacity={1}
height="25px"
width="25px"
/>
) : (
<CreateThreadIcon color="red"
opacity={1} height="25px" width="25px" />
<CreateThreadIcon
color="red"
opacity={1}
height="25px"
width="25px"
/>
)}
</NewMessageSendButton>
</InstanceFooter>
</ReusableModal>
{isOpenMultiplePublish && (
<MultiplePublish
isOpen={isOpenMultiplePublish}
onError={(messageNotification)=> {
onError={messageNotification => {
setIsOpenMultiplePublish(false);
setPublishes(null)
setCallbackContent(null)
if(messageNotification){
setPublishes(null);
setCallbackContent(null);
if (messageNotification) {
dispatch(
setNotification({
msg: messageNotification,
alertType: 'error'
alertType: "error",
})
)
);
}
}}
onSubmit={() => {
dispatch(
setNotification({
msg: 'Posted',
alertType: 'success'
msg: "Posted",
alertType: "success",
})
)
if(messageCallback && callbackContent?.message){
messageCallback(callbackContent.message)
);
if (messageCallback && callbackContent?.message) {
messageCallback(callbackContent.message);
}
if(threadCallback && callbackContent?.thread){
threadCallback(callbackContent.thread)
if (threadCallback && callbackContent?.thread) {
threadCallback(callbackContent.thread);
}
setCallbackContent(null)
setCallbackContent(null);
setIsOpenMultiplePublish(false);
setPublishes(null)
setPublishes(null);
closeModal()
closeModal();
}}
publishes={publishes}
/>

View File

@ -4,112 +4,125 @@ import React, {
useEffect,
useMemo,
useRef,
useState
} from 'react'
import { useNavigate } from 'react-router-dom'
import { useDispatch, useSelector } from 'react-redux'
import { RootState } from '../../state/store'
import EditIcon from '@mui/icons-material/Edit'
import { Box, Button, CircularProgress, Input, Typography, useTheme } from '@mui/material'
import { useFetchPosts } from '../../hooks/useFetchPosts'
import LazyLoad from '../../components/common/LazyLoad'
import { removePrefix } from '../../utils/blogIdformats'
import { NewMessage } from './NewMessage'
import Tabs from '@mui/material/Tabs'
import Tab from '@mui/material/Tab'
import { useFetchMail } from '../../hooks/useFetchMail'
import { ShowMessage } from './ShowMessage'
import { addToHashMapMail } from '../../state/features/mailSlice'
useState,
} from "react";
import { useNavigate } from "react-router-dom";
import { useDispatch, useSelector } from "react-redux";
import { QMAIL_BASE } from "../../constants/Identifiers";
import { RootState } from "../../state/store";
import EditIcon from "@mui/icons-material/Edit";
import {
Box,
Button,
CircularProgress,
Input,
Typography,
useTheme,
} from "@mui/material";
import { useFetchPosts } from "../../hooks/useFetchPosts";
import LazyLoad from "../../components/common/LazyLoad";
import { removePrefix } from "../../utils/blogIdformats";
import { NewMessage } from "./NewMessage";
import Tabs from "@mui/material/Tabs";
import Tab from "@mui/material/Tab";
import { useFetchMail } from "../../hooks/useFetchMail";
import { ShowMessage } from "./ShowMessage";
import { addToHashMapMail } from "../../state/features/mailSlice";
import {
setIsLoadingGlobal,
setUserAvatarHash
} from '../../state/features/globalSlice'
import SimpleTable from './MailTable'
import { MAIL_SERVICE_TYPE } from '../../constants/mail'
import { BlogPost } from '../../state/features/blogSlice'
import { setNotification } from '../../state/features/notificationsSlice'
import { useModal } from '../../components/common/useModal'
import { OpenMail } from './OpenMail'
import { MessagesContainer } from './Mail-styles'
import { MailMessageRow } from './MailMessageRow'
setUserAvatarHash,
} from "../../state/features/globalSlice";
import SimpleTable from "./MailTable";
import { MAIL_SERVICE_TYPE } from "../../constants/mail";
import { BlogPost } from "../../state/features/blogSlice";
import { setNotification } from "../../state/features/notificationsSlice";
import { useModal } from "../../components/common/useModal";
import { OpenMail } from "./OpenMail";
import { MessagesContainer } from "./Mail-styles";
import { MailMessageRow } from "./MailMessageRow";
interface SentMailProps {
onOpen: (user: string, identifier: string, content: any, to?:string)=> Promise<void>
onOpen: (
user: string,
identifier: string,
content: any,
to?: string
) => Promise<void>;
}
export const SentMail = ({onOpen}: SentMailProps) => {
const {isShow, onCancel, onOk, show} = useModal()
export const SentMail = ({ onOpen }: SentMailProps) => {
const { isShow, onCancel, onOk, show } = useModal();
const theme = useTheme()
const { user } = useSelector((state: RootState) => state.auth)
const [isOpen, setIsOpen] = useState<boolean>(false)
const theme = useTheme();
const { user } = useSelector((state: RootState) => state.auth);
const [isOpen, setIsOpen] = useState<boolean>(false);
const [isLoading, setIsLoading] = useState<boolean>(false);
const [message, setMessage] = useState<any>(null)
const [replyTo, setReplyTo] = useState<any>(null)
const [valueTab, setValueTab] = React.useState(0)
const [aliasValue, setAliasValue] = useState('')
const [alias, setAlias] = useState<string[]>([])
const [mailInfo, setMailInfo] = useState<any>(null)
const [message, setMessage] = useState<any>(null);
const [replyTo, setReplyTo] = useState<any>(null);
const [valueTab, setValueTab] = React.useState(0);
const [aliasValue, setAliasValue] = useState("");
const [alias, setAlias] = useState<string[]>([]);
const [mailInfo, setMailInfo] = useState<any>(null);
const hashMapPosts = useSelector(
(state: RootState) => state.blog.hashMapPosts
)
const [mailMessages, setMailMessages] = useState<any[]>([])
);
const [mailMessages, setMailMessages] = useState<any[]>([]);
const hashMapMailMessages = useSelector(
(state: RootState) => state.mail.hashMapMailMessages
)
);
const fullMailMessages = useMemo(() => {
return mailMessages.map((msg) => {
let message = msg
const existingMessage = hashMapMailMessages[msg.id]
return mailMessages.map(msg => {
let message = msg;
const existingMessage = hashMapMailMessages[msg.id];
if (existingMessage) {
message = existingMessage
message = existingMessage;
}
return message
})
}, [mailMessages, hashMapMailMessages, user])
const dispatch = useDispatch()
const navigate = useNavigate()
return message;
});
}, [mailMessages, hashMapMailMessages, user]);
const dispatch = useDispatch();
const navigate = useNavigate();
const getAvatar = async (user: string) => {
try {
let url = await qortalRequest({
action: 'GET_QDN_RESOURCE_URL',
action: "GET_QDN_RESOURCE_URL",
name: user,
service: 'THUMBNAIL',
identifier: 'qortal_avatar'
})
service: "THUMBNAIL",
identifier: "qortal_avatar",
});
dispatch(
setUserAvatarHash({
name: user,
url
url,
})
)
);
} catch (error) {}
}
};
const checkNewMessages = React.useCallback(
async (recipientName: string, recipientAddress: string) => {
try {
if (!user?.name) return
const query = `_mail_qortal_qmail_`
const url = `/arbitrary/resources/search?mode=ALL&service=${MAIL_SERVICE_TYPE}&identifier=_mail_&query=${query}&name=${user?.name}&limit=20&includemetadata=true&reverse=true&excludeblocked=true`
const response = await fetch(url, {
method: 'GET',
headers: {
'Content-Type': 'application/json'
}
})
const responseData = await response.json()
if (!user?.name) return;
const latestPost = mailMessages[0]
if (!latestPost) return
const url = `/arbitrary/resources/search?mode=ALL&service=${MAIL_SERVICE_TYPE}&identifier=_mail_&query=${QMAIL_BASE}&name=${user?.name}&limit=20&includemetadata=true&reverse=true&excludeblocked=true`;
const response = await fetch(url, {
method: "GET",
headers: {
"Content-Type": "application/json",
},
});
const responseData = await response.json();
const latestPost = mailMessages[0];
if (!latestPost) return;
const findPost = responseData?.findIndex(
(item: any) => item?.identifier === latestPost?.id
)
);
if (findPost === -1) {
return
return;
}
const newArray = responseData.slice(0, findPost)
const newArray = responseData.slice(0, findPost);
const structureData = newArray.map((post: any): BlogPost => {
return {
title: post?.metadata?.title,
@ -120,50 +133,50 @@ export const SentMail = ({onOpen}: SentMailProps) => {
createdAt: post?.created,
updated: post?.updated,
user: post.name,
id: post.identifier
}
})
setMailMessages((prev) => {
const updatedMessages = [...prev]
id: post.identifier,
};
});
setMailMessages(prev => {
const updatedMessages = [...prev];
structureData.forEach((newMessage: any) => {
const existingIndex = updatedMessages.findIndex(
(prevMessage) => prevMessage.id === newMessage.id
)
prevMessage => prevMessage.id === newMessage.id
);
if (existingIndex !== -1) {
// Replace existing message
updatedMessages[existingIndex] = newMessage
updatedMessages[existingIndex] = newMessage;
} else {
// Add new message
updatedMessages.unshift(newMessage)
updatedMessages.unshift(newMessage);
}
})
});
return updatedMessages
})
return
return updatedMessages;
});
return;
} catch (error) {}
},
[mailMessages]
)
);
const getMailMessages = React.useCallback(
async (recipientName: string, recipientAddress: string) => {
try {
if (!user?.name) return
const offset = mailMessages.length
if (!user?.name) return;
const offset = mailMessages.length;
// dispatch(setIsLoadingGlobal(true))
const query = `_mail_qortal_qmail_`
const url = `/arbitrary/resources/search?mode=ALL&service=${MAIL_SERVICE_TYPE}&identifier=_mail_&query=${query}&name=${user.name}&limit=20&includemetadata=true&offset=${offset}&reverse=true&excludeblocked=true`
const url = `/arbitrary/resources/search?mode=ALL&service=${MAIL_SERVICE_TYPE}&identifier=_mail_&query=${QMAIL_BASE}&name=${user.name}&limit=20&includemetadata=true&offset=${offset}&reverse=true&excludeblocked=true`;
const response = await fetch(url, {
method: 'GET',
method: "GET",
headers: {
'Content-Type': 'application/json'
}
})
const responseData = await response.json()
"Content-Type": "application/json",
},
});
const responseData = await response.json();
const structureData = responseData.map((post: any): BlogPost => {
return {
title: post?.metadata?.title,
@ -174,32 +187,32 @@ export const SentMail = ({onOpen}: SentMailProps) => {
createdAt: post?.created,
updated: post?.updated,
user: post.name,
id: post.identifier
}
})
setMailMessages((prev) => {
const updatedMessages = [...prev]
id: post.identifier,
};
});
setMailMessages(prev => {
const updatedMessages = [...prev];
structureData.forEach((newMessage: any) => {
const existingIndex = updatedMessages.findIndex(
(prevMessage) => prevMessage.id === newMessage.id
)
prevMessage => prevMessage.id === newMessage.id
);
if (existingIndex !== -1) {
// Replace existing message
updatedMessages[existingIndex] = newMessage
updatedMessages[existingIndex] = newMessage;
} else {
// Add new message
updatedMessages.push(newMessage)
updatedMessages.push(newMessage);
}
})
});
return updatedMessages
})
return updatedMessages;
});
for (const content of structureData) {
if (content.user && content.id) {
getAvatar(content.user)
getAvatar(content.user);
}
}
} catch (error) {
@ -208,19 +221,22 @@ export const SentMail = ({onOpen}: SentMailProps) => {
}
},
[mailMessages, hashMapMailMessages, user]
)
const getMessages = React.useCallback(async (isOnMount?: boolean) => {
if (!user?.name || !user?.address) return;
try {
if (isOnMount) {
setIsLoading(true);
);
const getMessages = React.useCallback(
async (isOnMount?: boolean) => {
if (!user?.name || !user?.address) return;
try {
if (isOnMount) {
setIsLoading(true);
}
await getMailMessages(user.name, user.address);
} catch (error) {
} finally {
setIsLoading(false);
}
await getMailMessages(user.name, user.address);
} catch (error) {
} finally {
setIsLoading(false);
}
}, [getMailMessages, user])
},
[getMailMessages, user]
);
const firstMount = useRef(false);
useEffect(() => {
@ -230,30 +246,27 @@ export const SentMail = ({onOpen}: SentMailProps) => {
}
}, [user]);
const interval = useRef<any>(null)
const interval = useRef<any>(null);
const checkNewMessagesFunc = useCallback(() => {
if (!user?.name || !user?.address) return
let isCalling = false
if (!user?.name || !user?.address) return;
let isCalling = false;
interval.current = setInterval(async () => {
if (isCalling || !user?.name || !user?.address) return
isCalling = true
const res = await checkNewMessages(user?.name, user.address)
isCalling = false
}, 30000)
}, [checkNewMessages, user])
if (isCalling || !user?.name || !user?.address) return;
isCalling = true;
const res = await checkNewMessages(user?.name, user.address);
isCalling = false;
}, 30000);
}, [checkNewMessages, user]);
useEffect(() => {
checkNewMessagesFunc()
checkNewMessagesFunc();
return () => {
if (interval?.current) {
clearInterval(interval.current)
clearInterval(interval.current);
}
}
}, [checkNewMessagesFunc])
};
}, [checkNewMessagesFunc]);
const openMessage = async (
user: string,
@ -262,16 +275,15 @@ export const SentMail = ({onOpen}: SentMailProps) => {
to?: string
) => {
try {
onOpen(user, messageIdentifier, {}, to)
onOpen(user, messageIdentifier, {}, to);
} finally {
}
}
};
return (
<>
{mailInfo && isShow && (
<OpenMail open={isShow} handleClose={onOk} fileInfo={mailInfo}/>
{mailInfo && isShow && (
<OpenMail open={isShow} handleClose={onOk} fileInfo={mailInfo} />
)}
{/* <NewMessage replyTo={replyTo} setReplyTo={setReplyTo} hideButton /> */}
<ShowMessage
@ -280,32 +292,34 @@ export const SentMail = ({onOpen}: SentMailProps) => {
message={message}
setReplyTo={setReplyTo}
/>
<MessagesContainer>
{fullMailMessages.map(item => {
return (
<MailMessageRow
messageData={item}
openMessage={openMessage}
isFromSent
/>
);
})}
<LazyLoad onLoadMore={getMessages}></LazyLoad>
{isLoading && (
<Box sx={{
display: 'flex',
width: '100%',
justifyContent: 'center'
}}>
<CircularProgress />
</Box>
)}
</MessagesContainer>
<MessagesContainer>
{fullMailMessages.map(item => {
return (
<MailMessageRow
messageData={item}
openMessage={openMessage}
isFromSent
/>
);
})}
<LazyLoad onLoadMore={getMessages}></LazyLoad>
{isLoading && (
<Box
sx={{
display: "flex",
width: "100%",
justifyContent: "center",
}}
>
<CircularProgress />
</Box>
)}
</MessagesContainer>
{/* <SimpleTable
openMessage={openMessage}
data={fullMailMessages}
></SimpleTable>
<LazyLoad onLoadMore={getMessages}></LazyLoad> */}
</>
)
}
);
};

View File

@ -63,7 +63,7 @@ export const ShowMessage = ({ message }: any) => {
height: "auto",
alignItems: "flex-start",
cursor: "default",
borderRadius: '35px 4px 4px 4px'
borderRadius: "35px 4px 4px 4px",
}}
>
<Box
@ -71,7 +71,7 @@ export const ShowMessage = ({ message }: any) => {
display: "flex",
flexDirection: "column",
alignItems: "flex-start",
width: '100%'
width: "100%",
}}
>
<Box
@ -79,7 +79,6 @@ export const ShowMessage = ({ message }: any) => {
display: "flex",
alignItems: "flex-start",
gap: "10px",
}}
>
<AvatarWrapper
@ -95,96 +94,99 @@ export const ShowMessage = ({ message }: any) => {
</ThreadInfoColumnTime>
</ThreadInfoColumn>
<div
style={{
display: "flex",
flexDirection: "column",
justifyContent: "center",
}}
>
{message?.attachments?.length > 0 && (
<Box
sx={{
width: "100%",
marginTop: "10px",
style={{
display: "flex",
flexDirection: "column",
justifyContent: "center",
}}
>
{message?.attachments
.map((file: any, index: number) => {
const isFirst = index === 0
return (
<Box
sx={{
display: expandAttachments ? "flex" : !expandAttachments && isFirst ? 'flex' : 'none',
alignItems: "center",
justifyContent: "flex-start",
width: "100%",
}}
>
{message?.attachments?.length > 0 && (
<Box
sx={{
width: "100%",
marginTop: "10px",
}}
>
{message?.attachments.map((file: any, index: number) => {
const isFirst = index === 0;
return (
<Box
sx={{
display: "flex",
display: expandAttachments
? "flex"
: !expandAttachments && isFirst
? "flex"
: "none",
alignItems: "center",
gap: "5px",
cursor: "pointer",
width: "auto",
justifyContent: "flex-start",
width: "100%",
}}
key={file.name + index}
>
<FileElement
fileInfo={{ ...file, mimeTypeSaved: file?.type }}
title={file?.filename}
mode="mail"
otherUser={message?.user}
<Box
sx={{
display: "flex",
alignItems: "center",
gap: "5px",
cursor: "pointer",
width: "auto",
}}
>
<MailAttachmentImg src={AttachmentMailSVG} />
<FileElement
fileInfo={{ ...file, mimeTypeSaved: file?.type }}
title={file?.filename}
mode="mail"
otherUser={message?.user}
>
<MailAttachmentImg src={AttachmentMailSVG} />
<Typography
sx={{
fontSize: "16px",
transition: '0.2s all',
"&:hover": {
color: 'rgba(255, 255, 255, 0.90)',
textDecoration: 'underline'
}
}}
>
{file?.originalFilename || file?.filename}
</Typography>
</FileElement>
{message?.attachments?.length > 1 && isFirst && (
<Box
sx={{
display: "flex",
alignItems: "center",
gap: "5px",
}}
onClick={() => {
setExpandAttachments(prev => !prev);
}}
>
<MoreImg
<Typography
sx={{
marginLeft: "5px",
transform: expandAttachments
? "rotate(180deg)"
: "unset",
fontSize: "16px",
transition: "0.2s all",
"&:hover": {
color: "rgba(255, 255, 255, 0.90)",
textDecoration: "underline",
},
}}
src={MoreSVG}
/>
<MoreP>
{expandAttachments ? 'hide' : `(${message?.attachments?.length - 1} more)`}
</MoreP>
</Box>
)}
>
{file?.originalFilename || file?.filename}
</Typography>
</FileElement>
{message?.attachments?.length > 1 && isFirst && (
<Box
sx={{
display: "flex",
alignItems: "center",
gap: "5px",
}}
onClick={() => {
setExpandAttachments(prev => !prev);
}}
>
<MoreImg
sx={{
marginLeft: "5px",
transform: expandAttachments
? "rotate(180deg)"
: "unset",
}}
src={MoreSVG}
/>
<MoreP>
{expandAttachments
? "hide"
: `(${message?.attachments?.length - 1} more)`}
</MoreP>
</Box>
)}
</Box>
</Box>
</Box>
);
})
}
</Box>
)}
</div>
);
})}
</Box>
)}
</div>
</Box>
<Spacer height="20px" />
@ -198,9 +200,6 @@ export const ShowMessage = ({ message }: any) => {
<div dangerouslySetInnerHTML={{ __html: cleanHTML }} />
)}
</Box>
</SingleTheadMessageParent>
);
};

View File

@ -8,6 +8,7 @@ import React, {
} from "react";
import { useNavigate } from "react-router-dom";
import { useDispatch, useSelector } from "react-redux";
import { THREAD_MESSAGE } from "../../constants/Identifiers";
import { RootState } from "../../state/store";
import {
@ -98,7 +99,7 @@ export const Thread = ({
let result = parts[0];
const threadId = result;
const offset = messages.length;
const query = `qortal_qmail_thmsg_group${groupInfo?.threadData?.groupId}_${threadId}`;
const query = `${THREAD_MESSAGE}${groupInfo?.threadData?.groupId}_${threadId}`;
const url = `/arbitrary/resources/search?mode=ALL&service=${MAIL_SERVICE_TYPE}&query=${query}&limit=20&includemetadata=false&offset=${offset}&reverse=true&excludeblocked=true`;
const response = await fetch(url, {
method: "GET",
@ -194,7 +195,7 @@ export const Thread = ({
let parts = str.split("_").reverse();
let result = parts[0];
const threadId = result;
const query = `qortal_qmail_thmsg_group${groupInfo?.threadData?.groupId}_${threadId}`;
const query = `${THREAD_MESSAGE}${groupInfo?.threadData?.groupId}_${threadId}`;
const url = `/arbitrary/resources/search?mode=ALL&service=${MAIL_SERVICE_TYPE}&query=${query}&limit=20&includemetadata=false&offset=${0}&reverse=true&excludeblocked=true`;
const response = await fetch(url, {
method: "GET",
@ -328,8 +329,7 @@ export const Thread = ({
}
return (
<SingleThreadParent>
key={message?.identifier}
<SingleThreadParent key={message?.identifier}>
<Skeleton
variant="rectangular"
style={{

View File

@ -1,12 +1,10 @@
import moment from "moment";
export const delay = (time: number) => new Promise((_, reject) =>
setTimeout(() => reject(new Error('Request timed out')), time)
);
export const delay = (time: number) =>
new Promise((_, reject) =>
setTimeout(() => reject(new Error("Request timed out")), time)
);
// const originalHtml = `<p>---------- Forwarded message ---------</p><p>From: Alex</p><p>Date: Mon, Jun 9 2014 9:32 PM</p><p>Subject: Batteries </p><p>To: Jessica</p><p><br></p><p><br></p>`;
// export function updateMessageDetails(newFrom: string, newDateMillis: number, newTo: string) {
// let htmlString = originalHtml
// // Use Moment.js to format the date from milliseconds
@ -22,13 +20,27 @@ export const delay = (time: number) => new Promise((_, reject) =>
const originalHtml = `<p>---------- Forwarded message ---------</p><p>From: Alex</p><p>Subject: Batteries </p><p>To: Jessica</p><p><br></p><p><br></p>`;
export function updateMessageDetails(
newFrom: string,
newSubject: string,
newTo: string
) {
let htmlString = originalHtml;
export function updateMessageDetails(newFrom: string, newSubject: string, newTo: string) {
let htmlString = originalHtml
htmlString = htmlString.replace(
/<p>From:.*?<\/p>/,
`<p>From: ${newFrom}</p>`
);
htmlString = htmlString.replace(
/<p>Subject:.*?<\/p>/,
`<p>Subject: ${newSubject}</p>`
);
htmlString = htmlString.replace(/<p>To:.*?<\/p>/, `<p>To: ${newTo}</p>`);
htmlString = htmlString.replace(/<p>From:.*?<\/p>/, `<p>From: ${newFrom}</p>`);
htmlString = htmlString.replace(/<p>Subject:.*?<\/p>/, `<p>Subject: ${newSubject}</p>`);
htmlString = htmlString.replace(/<p>To:.*?<\/p>/, `<p>To: ${newTo}</p>`);
return htmlString;
}
return htmlString;
}
export const getFileExtension = (fileName: string) => {
if (!fileName.includes(".")) return null;
return fileName.split(".").at(-1);
};