From c1d812a0e662a40073632989fe4d07d744022400 Mon Sep 17 00:00:00 2001 From: IrohDW Date: Mon, 26 Aug 2024 16:20:21 -0600 Subject: [PATCH] Removed most code from Mail.tsx. Mail.tsx renamed to Home.tsx and moved to its own directory. It shows a "New Forum" button if the user owns the app or Test Identifiers are being used. --- src/App.tsx | 29 +- src/constants/Identifiers.ts | 11 +- src/constants/Misc.ts | 1 + src/pages/Forum/Forum.tsx | 47 ++ src/pages/Home/Home.tsx | 108 ++++- src/pages/Mail/AliasMail.tsx | 294 ++++++------ src/pages/Mail/GroupMail.tsx | 2 +- src/pages/Mail/Mail.tsx | 524 --------------------- src/pages/Mail/MailMessageRow.tsx | 244 +++++----- src/pages/Mail/MailTable.tsx | 189 ++++---- src/pages/Mail/NewMessage.tsx | 2 +- src/pages/Mail/NewThread.tsx | 2 +- src/pages/Mail/SentMail.tsx | 2 +- src/pages/Mail/ShowMessageV2.tsx | 221 ++++----- src/pages/Mail/ShowMessageV2Replies.tsx | 349 +++++++------- src/pages/Mail/ShowMessageWithoutModal.tsx | 2 +- src/pages/Mail/Thread.tsx | 2 +- 17 files changed, 861 insertions(+), 1168 deletions(-) create mode 100644 src/constants/Misc.ts create mode 100644 src/pages/Forum/Forum.tsx delete mode 100644 src/pages/Mail/Mail.tsx diff --git a/src/App.tsx b/src/App.tsx index f0a958d..806087f 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,19 +1,19 @@ // @ts-nocheck -import { Routes, Route } from 'react-router-dom' +import { Routes, Route } from "react-router-dom"; -import { ThemeProvider } from '@mui/material/styles' -import { CssBaseline } from '@mui/material' -import { lightTheme, darkTheme } from './styles/theme' -import { store } from './state/store' -import { Provider } from 'react-redux' -import GlobalWrapper from './wrappers/GlobalWrapper' -import DownloadWrapper from './wrappers/DownloadWrapper' -import Notification from './components/common/Notification/Notification' -import { Mail } from './pages/Mail/Mail' +import { ThemeProvider } from "@mui/material/styles"; +import { CssBaseline } from "@mui/material"; +import { Home } from "./pages/Home/Home"; +import { lightTheme, darkTheme } from "./styles/theme"; +import { store } from "./state/store"; +import { Provider } from "react-redux"; +import GlobalWrapper from "./wrappers/GlobalWrapper"; +import DownloadWrapper from "./wrappers/DownloadWrapper"; +import Notification from "./components/common/Notification/Notification"; function App() { - const themeColor = window._qdnTheme + const themeColor = window._qdnTheme; return ( @@ -24,14 +24,13 @@ function App() { - } /> - } /> + } /> - ) + ); } -export default App +export default App; diff --git a/src/constants/Identifiers.ts b/src/constants/Identifiers.ts index 2e2fb17..7e3f3cd 100644 --- a/src/constants/Identifiers.ts +++ b/src/constants/Identifiers.ts @@ -1,8 +1,11 @@ -const useTestIdentifiers = false; +export const useTestIdentifiers = true; const appName = useTestIdentifiers ? "mintTEST" : "mintership"; -export const AUDIO_BASE = `${appName}_qaudio`; -export const THREAD_BASE = `${appName}_forum_thread`; -export const ATTATCHMENT_BASE = `${appName}_attachments`; + export const QMAIL_BASE = `${appName}_mail`; +export const ATTATCHMENT_BASE = `${appName}_attachments`; +export const AUDIO_BASE = `${appName}_qaudio`; + +export const FORUM_BASE = `${appName}_forum`; +export const THREAD_BASE = `${appName}_thread`; export const THREAD_MESSAGE = `${appName}_thread_message`; diff --git a/src/constants/Misc.ts b/src/constants/Misc.ts new file mode 100644 index 0000000..4937c15 --- /dev/null +++ b/src/constants/Misc.ts @@ -0,0 +1 @@ +export const appOwner = "Q-Mintership"; diff --git a/src/pages/Forum/Forum.tsx b/src/pages/Forum/Forum.tsx new file mode 100644 index 0000000..0afc6df --- /dev/null +++ b/src/pages/Forum/Forum.tsx @@ -0,0 +1,47 @@ +import { Box } from "@mui/material"; +import React, { useState } from "react"; +import ComposeIconSVG from "../../assets/svgs/ComposeIcon.svg"; +import { executeEvent } from "../../utils/events"; +import { + ComposeContainer, + ComposeIcon, + ComposeP, + InstanceContainer, +} from "../Home/Home-styles"; +import { GroupMail } from "../Mail/GroupMail"; + +export type EncryptionType = "None" | "Group" | "GroupAdmin"; +interface ForumProps { + title: string; + description: string; + encryption?: EncryptionType; +} + +export const Forum = ({ encryption = "None" }: ForumProps) => { + const [currentThread, setCurrentThread] = useState(null); + + const openNewThread = () => { + if (currentThread) { + executeEvent("openNewThreadMessageModal", {}); + return; + } + executeEvent("openNewThreadModal", {}); + }; + + return ( + + + + + {currentThread ? "New Post" : "New Thread"} + + + + {/**/} + + ); +}; diff --git a/src/pages/Home/Home.tsx b/src/pages/Home/Home.tsx index 8ab2ced..e6a54e4 100644 --- a/src/pages/Home/Home.tsx +++ b/src/pages/Home/Home.tsx @@ -1,7 +1,107 @@ -import React from 'react' +import { Box } from "@mui/material"; +import React, { + useCallback, + useEffect, + useMemo, + useRef, + useState, +} from "react"; +import { useSelector } from "react-redux"; +import ComposeIconSVG from "../../assets/svgs/ComposeIcon.svg"; +import { useTestIdentifiers } from "../../constants/Identifiers"; +import { appOwner } from "../../constants/Misc"; +import { useFetchMail } from "../../hooks/useFetchMail"; +import { RootState } from "../../state/store"; +import { executeEvent } from "../../utils/events"; + +import { + ComposeContainer, + ComposeIcon, + ComposeP, + InstanceContainer, + MailContainer, +} from "./Home-styles"; export const Home = () => { + const { user } = useSelector((state: RootState) => state.auth); + + const privateGroups = useSelector( + (state: RootState) => state.global.privateGroups + ); + + const options = useMemo(() => { + return Object.keys(privateGroups).map(key => { + return { + ...privateGroups[key], + name: privateGroups[key].groupName, + id: key, + }; + }); + }, [privateGroups]); + const hashMapMailMessages = useSelector( + (state: RootState) => state.mail.hashMapMailMessages + ); + + const userName = useMemo(() => { + if (!user?.name) return ""; + return user.name; + }, [user]); + + const { getMailMessages, checkNewMessages } = useFetchMail(); + const getMessages = React.useCallback( + async (isOnMount?: boolean) => { + if (!user?.name || !user?.address) return; + try { + await getMailMessages(user.name, user.address); + } catch (error) {} + }, + [getMailMessages, user] + ); + + const interval = useRef(null); + + const checkNewMessagesFunc = useCallback(() => { + 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]); + + useEffect(() => { + checkNewMessagesFunc(); + return () => { + if (interval?.current) { + clearInterval(interval.current); + } + }; + }, [checkNewMessagesFunc]); + + const firstMount = useRef(false); + useEffect(() => { + if (user?.name && !firstMount.current) { + getMessages(true); + firstMount.current = true; + } + }, [user]); + + const publishNewForum = () => { + executeEvent("openNewThreadModal", {}); + }; + return ( -
Home
- ) -} + + + {(user?.name === appOwner || useTestIdentifiers) && ( + + + {"New Forum"} + + )} + + + ); +}; diff --git a/src/pages/Mail/AliasMail.tsx b/src/pages/Mail/AliasMail.tsx index 965c5c3..d1ae5ae 100644 --- a/src/pages/Mail/AliasMail.tsx +++ b/src/pages/Mail/AliasMail.tsx @@ -4,112 +4,116 @@ 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, 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 { RootState } from "../../state/store"; +import EditIcon from "@mui/icons-material/Edit"; +import { Box, Button, 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 { 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 { useModal } from "../../components/common/useModal"; +import { OpenMail } from "./OpenMail"; +import { MessagesContainer } from "../Home/Home-styles"; +import { MailMessageRow } from "./MailMessageRow"; interface AliasMailProps { value: string; - onOpen: (user: string, identifier: string, content: any)=> Promise - messageOpenedId: number + onOpen: (user: string, identifier: string, content: any) => Promise; + messageOpenedId: number; } -export const AliasMail = ({ value, onOpen, messageOpenedId}: AliasMailProps) => { - const {isShow, onCancel, onOk, show} = useModal() +export const AliasMail = ({ + value, + onOpen, + messageOpenedId, +}: AliasMailProps) => { + const { isShow, onCancel, onOk, show } = useModal(); - const theme = useTheme() - const { user } = useSelector((state: RootState) => state.auth) - const [isOpen, setIsOpen] = useState(false) - const [message, setMessage] = useState(null) - const [replyTo, setReplyTo] = useState(null) + const theme = useTheme(); + const { user } = useSelector((state: RootState) => state.auth); + const [isOpen, setIsOpen] = useState(false); + const [message, setMessage] = useState(null); + const [replyTo, setReplyTo] = useState(null); const [forwardInfo, setForwardInfo] = useState(null); - const [valueTab, setValueTab] = React.useState(0) - const [aliasValue, setAliasValue] = useState('') - const [alias, setAlias] = useState([]) - const [mailInfo, setMailInfo] = useState(null) + const [valueTab, setValueTab] = React.useState(0); + const [aliasValue, setAliasValue] = useState(""); + const [alias, setAlias] = useState([]); + const [mailInfo, setMailInfo] = useState(null); const hashMapPosts = useSelector( (state: RootState) => state.blog.hashMapPosts - ) - const [mailMessages, setMailMessages] = useState([]) + ); + const [mailMessages, setMailMessages] = useState([]); 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 { - const query = `qortal_qmail_${value}_mail` - const url = `/arbitrary/resources/search?mode=ALL&service=${MAIL_SERVICE_TYPE}&query=${query}&limit=50&includemetadata=true&reverse=true&excludeblocked=true` + const query = `qortal_qmail_${value}_mail`; + const url = `/arbitrary/resources/search?mode=ALL&service=${MAIL_SERVICE_TYPE}&query=${query}&limit=50&includemetadata=true&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 latestPost = mailMessages[0] - if (!latestPost) return + 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,49 +124,49 @@ export const AliasMail = ({ value, onOpen, messageOpenedId}: AliasMailProps) => 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, value] - ) + ); const getMailMessages = React.useCallback( async (recipientName: string, recipientAddress: string) => { try { - const offset = mailMessages.length + const offset = mailMessages.length; // dispatch(setIsLoadingGlobal(true)) - const query = `qortal_qmail_${value}_mail` - const url = `/arbitrary/resources/search?mode=ALL&service=${MAIL_SERVICE_TYPE}&query=${query}&limit=50&includemetadata=true&offset=${offset}&reverse=true&excludeblocked=true` + const query = `qortal_qmail_${value}_mail`; + const url = `/arbitrary/resources/search?mode=ALL&service=${MAIL_SERVICE_TYPE}&query=${query}&limit=50&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, @@ -173,32 +177,32 @@ export const AliasMail = ({ value, onOpen, messageOpenedId}: AliasMailProps) => 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) { @@ -207,33 +211,33 @@ export const AliasMail = ({ value, onOpen, messageOpenedId}: AliasMailProps) => } }, [mailMessages, hashMapMailMessages, value] - ) + ); const getMessages = React.useCallback(async () => { - if (!user?.name || !user?.address) return - await getMailMessages(user.name, user.address) - }, [getMailMessages, user]) + if (!user?.name || !user?.address) return; + await getMailMessages(user.name, user.address); + }, [getMailMessages, user]); - const interval = useRef(null) + const interval = useRef(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, @@ -241,7 +245,7 @@ export const AliasMail = ({ value, onOpen, messageOpenedId}: AliasMailProps) => content: any ) => { try { - onOpen(user, messageIdentifier, {}) + onOpen(user, messageIdentifier, {}); // const existingMessage: any = hashMapMailMessages[messageIdentifier] // if (existingMessage && existingMessage.isValid && !existingMessage.unableToDecrypt) { // setMessage(existingMessage) @@ -264,18 +268,18 @@ export const AliasMail = ({ value, onOpen, messageOpenedId}: AliasMailProps) => } catch (error) { } finally { } - } + }; - const firstMount = useRef(null) + const firstMount = useRef(null); useEffect(() => { if (user?.name && (!firstMount.current || firstMount.current !== value)) { - setMailMessages([]) + setMailMessages([]); setTimeout(() => { - getMessages() + getMessages(); }, 100); - firstMount.current = value + firstMount.current = value; } - }, [user, value]) + }, [user, value]); return ( <> @@ -294,26 +298,26 @@ export const AliasMail = ({ value, onOpen, messageOpenedId}: AliasMailProps) => setReplyTo={setReplyTo} alias={value} /> - - {fullMailMessages.map(item => { - return ( - - ); - })} - - + + {fullMailMessages.map(item => { + return ( + + ); + })} + + {/* */} {mailMessages.length > 20 && ( @@ -321,9 +325,9 @@ export const AliasMail = ({ value, onOpen, messageOpenedId}: AliasMailProps) => )} {mailInfo && isShow && ( - + )} {/* */} - ) -} + ); +}; diff --git a/src/pages/Mail/GroupMail.tsx b/src/pages/Mail/GroupMail.tsx index 2ee959d..3353735 100644 --- a/src/pages/Mail/GroupMail.tsx +++ b/src/pages/Mail/GroupMail.tsx @@ -64,7 +64,7 @@ import { ThreadSingleLastMessageP, ThreadSingleLastMessageSpanP, ThreadSingleTitle, -} from "./Mail-styles"; +} from "../Home/Home-styles"; import { Spacer } from "../../components/common/Spacer"; interface AliasMailProps { groupInfo: any; diff --git a/src/pages/Mail/Mail.tsx b/src/pages/Mail/Mail.tsx deleted file mode 100644 index fba87e7..0000000 --- a/src/pages/Mail/Mail.tsx +++ /dev/null @@ -1,524 +0,0 @@ -import { Box, Popover } from "@mui/material"; -import React, { - useCallback, - useEffect, - useMemo, - useRef, - useState, -} from "react"; -import { Step } from "react-joyride"; -import { useSelector } from "react-redux"; -import AddAliasSVG from "../../assets/svgs/AddAlias.svg"; -import ArrowDownSVG from "../../assets/svgs/ArrowDown.svg"; - -import CheckSVG from "../../assets/svgs/Check.svg"; -import { CloseSVG } from "../../assets/svgs/CloseSVG"; -import ComposeIconSVG from "../../assets/svgs/ComposeIcon.svg"; -import GroupSVG from "../../assets/svgs/Group.svg"; -import SortSVG from "../../assets/svgs/Sort.svg"; -import { useFetchMail } from "../../hooks/useFetchMail"; -import { RootState } from "../../state/store"; -import { executeEvent } from "../../utils/events"; -import { GroupMail } from "./GroupMail"; -import { - ArrowDownIcon, - CloseParent, - ComposeContainer, - ComposeContainerBlank, - ComposeIcon, - ComposeP, - InstanceContainer, - InstanceFooter, - InstanceLabel, - InstanceListContainer, - InstanceListContainerRow, - InstanceListContainerRowCheck, - InstanceListContainerRowCheckIcon, - InstanceListContainerRowGroupIcon, - InstanceListContainerRowLabelContainer, - InstanceListContainerRowMain, - InstanceListContainerRowMainP, - InstanceListHeader, - InstanceListParent, - InstanceP, - MailContainer, - SelectInstanceContainer, - SelectInstanceContainerFilterInner, - SelectInstanceContainerInner, - TypeInAliasTextfield, -} from "./Mail-styles"; - -const filterOptions = ["Recently active", "Newest", "Oldest"]; - -const steps: Step[] = [ - { - content: ( -
-

Welcome To Q-Mail

-

- Let's take a tour -

-

- The Qortal community, along with its development team and the creators - of this application, cannot be held accountable for any content - published or displayed. Furthermore, they bear no responsibility for - any data loss that may occur as a result of using this application. -

-
- ), - placement: "center", - target: ".step-1", - }, - { - target: ".step-3", - content: ( -
-

Changing instances

- -

- Toggle between your main inbox, alias' and private groups. -

-
- ), - placement: "bottom", - }, - { - target: ".step-2", - content: ( -
-

Composing a mail message

-

- Compose a secure message featuring encrypted attachments (up to 40MB - per attachment). -

-

- To protect the identity of the recipient, assign them an alias for - added anonymity. -

-
- ), - placement: "bottom", - }, - - { - target: ".step-3", - content: ( -
-

What is an alias?

-

- To conceal the identity of the message recipient, utilize the alias - option when sending. -

-

- For instance, instruct your friend to address the message to you using - the alias 'FrederickGreat'. -

-

- To access messages sent to that alias, simply add the alias as an - instance. -

-
- ), - placement: "bottom", - }, -]; - -interface MailProps { - isFromTo: boolean; -} - -export const Mail = ({ isFromTo }: MailProps) => { - const { user } = useSelector((state: RootState) => state.auth); - const [isOpenInstanceList, setIsOpenInstanceList] = useState(false); - const [isOpenFilterList, setIsOpenFilterList] = useState(false); - - const anchorElInstance = useRef(null); - const anchorElInstanceFilter = useRef(null); - - const [currentThread, setCurrentThread] = useState(null); - - const [aliasValue, setAliasValue] = useState(""); - const [alias, setAlias] = useState([]); - - const [filterMode, setFilterMode] = useState("Recently active"); - const [selectedAlias, setSelectedAlias] = useState(null); - const [selectedGroup, setSelectedGroup] = useState(null); - const privateGroups = useSelector( - (state: RootState) => state.global.privateGroups - ); - - const options = useMemo(() => { - return Object.keys(privateGroups).map(key => { - return { - ...privateGroups[key], - name: privateGroups[key].groupName, - id: key, - }; - }); - }, [privateGroups]); - const hashMapMailMessages = useSelector( - (state: RootState) => state.mail.hashMapMailMessages - ); - - const userName = useMemo(() => { - if (!user?.name) return ""; - return user.name; - }, [user]); - - const { getMailMessages, checkNewMessages } = useFetchMail(); - const getMessages = React.useCallback( - async (isOnMount?: boolean) => { - if (!user?.name || !user?.address) return; - try { - await getMailMessages(user.name, user.address); - } catch (error) {} - }, - [getMailMessages, user] - ); - - const interval = useRef(null); - - const checkNewMessagesFunc = useCallback(() => { - 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]); - - useEffect(() => { - checkNewMessagesFunc(); - return () => { - if (interval?.current) { - clearInterval(interval.current); - } - }; - }, [checkNewMessagesFunc]); - - const firstMount = useRef(false); - useEffect(() => { - if (user?.name && !firstMount.current) { - getMessages(true); - firstMount.current = true; - } - }, [user]); - - useEffect(() => { - if (!userName) return; - const savedAlias = localStorage.getItem(`alias-qmail-${userName}`); - if (savedAlias) { - try { - setAlias(JSON.parse(savedAlias)); - } catch (error) { - console.error("Error parsing JSON from localStorage:", error); - } - } - }, [userName]); - - const handleCloseInstanceList = () => { - setIsOpenInstanceList(false); - }; - const handleCloseThreadFilterList = () => { - setIsOpenFilterList(false); - }; - - const addAlias = () => { - if (!aliasValue) return; - const newList = [...alias, aliasValue]; - if (userName) { - try { - localStorage.setItem( - `alias-qmail-${userName}`, - JSON.stringify(newList) - ); - } catch (error) {} - } - - setAlias(prev => [...prev, aliasValue]); - setAliasValue(""); - }; - - const handleInputKeyDown = (event: any) => { - if (event.key === "Enter") { - addAlias(); - } - }; - - const openNewThread = () => { - if (currentThread) { - executeEvent("openNewThreadMessageModal", {}); - return; - } - executeEvent("openNewThreadModal", {}); - }; - - useEffect(() => { - if (options[0] && !selectedGroup) setSelectedGroup(options[0]); - }, [options]); - return ( - - - - - {currentThread ? "New Post" : "New Thread"} - - - Current Instance: - { - setIsOpenInstanceList(true); - }} - ref={anchorElInstance} - > - - {selectedGroup ? selectedGroup?.name : user?.name} - - - - - - - - - - - - Instances: - - - - - - {options?.map(group => { - return ( - { - setSelectedAlias(null); - setSelectedGroup(group); - handleCloseInstanceList(); - }} - sx={{ - backgroundColor: - selectedGroup?.id === group?.id - ? "rgba(74, 158, 244, 1)" - : "unset", - }} - key={group?.id} - > - - {group?.id === selectedGroup?.id && ( - - )} - - - - {group?.name} - - - - - - - - - - ); - })} - - - - - - - { - setAliasValue(e.target.value); - }} - /> - - - - {/* */} - - - - - - {filterOptions?.map(filter => { - return ( - { - setFilterMode(filter); - handleCloseThreadFilterList(); - }} - sx={{ - backgroundColor: - filterMode === filter - ? "rgba(74, 158, 244, 1)" - : "unset", - }} - key={filter} - > - - {filter === filterMode && ( - - )} - - - - {filter} - - - - ); - })} - - - - - - {selectedGroup && !currentThread && ( - { - setIsOpenFilterList(true); - }} - ref={anchorElInstanceFilter} - > - - - - Sort by - - - - )} - - - - - - ); -}; - -interface TabPanelProps { - children?: React.ReactNode; - index: number; - value: number | null; -} - -export function TabPanel(props: TabPanelProps) { - const { children, value, index, ...other } = props; - - return ( - - ); -} diff --git a/src/pages/Mail/MailMessageRow.tsx b/src/pages/Mail/MailMessageRow.tsx index 0925ac2..e5808a7 100644 --- a/src/pages/Mail/MailMessageRow.tsx +++ b/src/pages/Mail/MailMessageRow.tsx @@ -9,11 +9,11 @@ import { MessageExtraDate, MessageExtraInfo, MessageExtraName, -} from "./Mail-styles"; +} from "../Home/Home-styles"; import { AvatarWrapper } from "./MailTable"; import { formatTimestamp } from "../../utils/time"; -import LockSVG from '../../assets/svgs/Lock.svg' -import AttachmentSVG from '../../assets/svgs/Attachment.svg' +import LockSVG from "../../assets/svgs/Lock.svg"; +import AttachmentSVG from "../../assets/svgs/Attachment.svg"; import { useSelector } from "react-redux"; import { RootState } from "../../state/store"; import { base64ToUint8Array, uint8ArrayToObject } from "../../utils/toBase64"; @@ -24,9 +24,9 @@ function parseQuery(query: string) { const match = query.match(regex); if (match) { - const recipientName = match[1]; - const recipientAddress = match[2] || null; // Will be null if the address part is not present - return { recipientName, recipientAddress }; + const recipientName = match[1]; + const recipientAddress = match[2] || null; // Will be null if the address part is not present + return { recipientName, recipientAddress }; } return { recipientName: null, recipientAddress: null }; @@ -35,141 +35,171 @@ interface MailMessageRowProp { messageData: any; openMessage: (user: string, id: string, content: any, alias?: string) => void; isOpen?: boolean; - isFromSent?: boolean + isFromSent?: boolean; } export const MailMessageRow = ({ messageData, openMessage, isOpen, - isFromSent + isFromSent, }: MailMessageRowProp) => { - const username = useSelector((state: RootState) => state.auth?.user?.name) - const [subjectInHashDecrypted, setSubjectInHashDecrypted] = useState(null) - const [hasAttachment, setHasAttachment] = useState(null) + const username = useSelector((state: RootState) => state.auth?.user?.name); + const [subjectInHashDecrypted, setSubjectInHashDecrypted] = useState< + null | string + >(null); + const [hasAttachment, setHasAttachment] = useState(null); const [sentToNameInfo, setSentToNameInfo] = useState({ - name: "" - }) - const [alias, setAlias] = useState(null) + name: "", + }); + const [alias, setAlias] = useState(null); - const identifier = useMemo(()=> { - return messageData?.id - }, [messageData]) + const identifier = useMemo(() => { + return messageData?.id; + }, [messageData]); - const getSentToName = useCallback(async (id: string)=> { + const getSentToName = useCallback(async (id: string) => { try { const { recipientName, recipientAddress } = parseQuery(id); - if(!recipientAddress && recipientName){ - setAlias(recipientName) - return + if (!recipientAddress && recipientName) { + setAlias(recipientName); + return; } - if(!recipientName || !recipientAddress) return + if (!recipientName || !recipientAddress) return; const response = await qortalRequest({ action: "SEARCH_NAMES", query: recipientName, - prefix: true, + prefix: true, limit: 10, - reverse: false - }) - const findName = response?.find((item: any)=> item?.owner?.includes(recipientAddress)) - if(findName){ + reverse: false, + }); + const findName = response?.find((item: any) => + item?.owner?.includes(recipientAddress) + ); + if (findName) { setSentToNameInfo({ - name: findName.name - }) + name: findName.name, + }); } - } catch (error) { - + } catch (error) {} + }, []); + useEffect(() => { + if (isFromSent && identifier) { + getSentToName(identifier); } - }, []) - useEffect(()=> { - if(isFromSent && identifier){ - getSentToName(identifier) + }, [isFromSent, identifier]); + let isEncrypted = true; + let hasAttachments = null; + let subject = ""; + const hashMapMailMessages = useSelector( + (state: RootState) => state.mail.hashMapMailMessages + ); + const hashMapSavedSubjects = useSelector( + (state: RootState) => state.mail.hashMapSavedSubjects + ); + const subjectInHash = hashMapSavedSubjects[messageData?.id]; + const data = hashMapMailMessages[messageData?.id]; + + if (subjectInHashDecrypted !== null) { + subject = subjectInHashDecrypted || "- no subject"; + hasAttachments = hasAttachment || false; + } + if (data && data?.isValid && !data?.unableToDecrypt) { + isEncrypted = false; + subject = data?.subject || "- no subject"; + hasAttachments = (data?.attachments || [])?.length > 0; + } + + const getSubjectFromHash = async ( + subject: string, + hasAttachmentParam: boolean + ) => { + if (subject === undefined || subject === null) + throw new Error("no subject"); + if (subject === "") { + setSubjectInHashDecrypted(""); + setHasAttachment(hasAttachmentParam); + return; } - }, [isFromSent, identifier]) - let isEncrypted = true; - let hasAttachments = null - let subject = "" - const hashMapMailMessages = useSelector( - (state: RootState) => state.mail.hashMapMailMessages - ); - const hashMapSavedSubjects = useSelector( - (state: RootState) => state.mail.hashMapSavedSubjects - ); -const subjectInHash = hashMapSavedSubjects[messageData?.id] -const data = hashMapMailMessages[messageData?.id] - -if(subjectInHashDecrypted !== null){ - subject = subjectInHashDecrypted || "- no subject" - hasAttachments = hasAttachment || false -} -if(data && data?.isValid && !data?.unableToDecrypt){ - isEncrypted = false - subject = data?.subject || "- no subject" - hasAttachments = (data?.attachments || [])?.length > 0 -} - - -const getSubjectFromHash = async (subject: string, hasAttachmentParam: boolean) => { - if(subject === undefined || subject === null) throw new Error('no subject') - if(subject === ""){ - setSubjectInHashDecrypted("") - setHasAttachment(hasAttachmentParam) - return - } - let requestEncryptSubject: any = { - action: 'DECRYPT_DATA', - encryptedData: subject - } - const resDecryptSubject = await qortalRequest(requestEncryptSubject) - const decryptToUnit8ArraySubject = - base64ToUint8Array(resDecryptSubject) - const responseDataSubject = uint8ArrayToObject( - decryptToUnit8ArraySubject - ) - if(responseDataSubject !== null || responseDataSubject !== undefined){ - setSubjectInHashDecrypted(responseDataSubject) - setHasAttachment(hasAttachmentParam) + let requestEncryptSubject: any = { + action: "DECRYPT_DATA", + encryptedData: subject, + }; + const resDecryptSubject = await qortalRequest(requestEncryptSubject); + const decryptToUnit8ArraySubject = base64ToUint8Array(resDecryptSubject); + const responseDataSubject = uint8ArrayToObject(decryptToUnit8ArraySubject); + if (responseDataSubject !== null || responseDataSubject !== undefined) { + setSubjectInHashDecrypted(responseDataSubject); + setHasAttachment(hasAttachmentParam); } -} + }; + useEffect(() => { + if (!isEncrypted) return; -useEffect(()=> { - if(!isEncrypted) return + if (subjectInHashDecrypted !== null) return; + if ( + !subjectInHash || + subjectInHash?.subject === undefined || + subjectInHash?.subject === null + ) + return; + getSubjectFromHash(subjectInHash?.subject, subjectInHash?.attachments); + }, [isEncrypted, subjectInHashDecrypted]); - if(subjectInHashDecrypted !== null) return - if(!subjectInHash || subjectInHash?.subject === undefined || subjectInHash?.subject === null ) return - getSubjectFromHash(subjectInHash?.subject, subjectInHash?.attachments) -}, [isEncrypted, subjectInHashDecrypted]) + const name = useMemo(() => { + if (isFromSent) { + return sentToNameInfo?.name || ""; + } + return messageData?.user; + }, [sentToNameInfo, isFromSent, messageData]); -const name = useMemo(()=> { - if(isFromSent){ - return sentToNameInfo?.name || "" - } - return messageData?.user -}, [sentToNameInfo, isFromSent, messageData]) - return ( - { - openMessage(messageData?.user, messageData?.id, messageData, isFromSent ? (alias || name) : username) - }}> + { + openMessage( + messageData?.user, + messageData?.id, + messageData, + isFromSent ? alias || name : username + ); + }} + > - + - {isFromSent ? "To: " : ""} {alias || name} - {formatTimestamp(messageData?.createdAt)} + + {isFromSent ? "To: " : ""} {alias || name} + + + {formatTimestamp(messageData?.createdAt)} + - {hasAttachments ? : hasAttachments === false ? null : isEncrypted ? : null} - + {hasAttachments ? ( + + ) : hasAttachments === false ? null : isEncrypted ? ( + + ) : null} {subject ? ( {subject} - ) : isEncrypted ? ( - ACCESS TO DECRYPT + ) : isEncrypted ? ( + + ACCESS TO DECRYPT + ) : null} diff --git a/src/pages/Mail/MailTable.tsx b/src/pages/Mail/MailTable.tsx index a38eadb..af9aa70 100644 --- a/src/pages/Mail/MailTable.tsx +++ b/src/pages/Mail/MailTable.tsx @@ -1,145 +1,143 @@ -import * as React from 'react' -import Table from '@mui/material/Table' -import TableBody from '@mui/material/TableBody' -import TableCell from '@mui/material/TableCell' -import TableContainer from '@mui/material/TableContainer' -import TableHead from '@mui/material/TableHead' -import TableRow from '@mui/material/TableRow' -import Paper from '@mui/material/Paper' -import { Avatar, Box } from '@mui/material' -import { useSelector } from 'react-redux' -import { RootState } from '../../state/store' -import { formatTimestamp } from '../../utils/time' -import AliasAvatar from '../../assets/svgs/AliasAvatar.svg' -import { AliasAvatarImg } from './Mail-styles' -const tableCellFontSize = '16px' +import * as React from "react"; +import Table from "@mui/material/Table"; +import TableBody from "@mui/material/TableBody"; +import TableCell from "@mui/material/TableCell"; +import TableContainer from "@mui/material/TableContainer"; +import TableHead from "@mui/material/TableHead"; +import TableRow from "@mui/material/TableRow"; +import Paper from "@mui/material/Paper"; +import { Avatar, Box } from "@mui/material"; +import { useSelector } from "react-redux"; +import { RootState } from "../../state/store"; +import { formatTimestamp } from "../../utils/time"; +import AliasAvatar from "../../assets/svgs/AliasAvatar.svg"; +import { AliasAvatarImg } from "../Home/Home-styles"; +const tableCellFontSize = "16px"; interface Data { - name: string - description: string - createdAt: number - user: string - id: string - tags: string[] - subject?: string + name: string; + description: string; + createdAt: number; + user: string; + id: string; + tags: string[]; + subject?: string; } interface ColumnData { - dataKey: keyof Data - label: string - numeric?: boolean - width?: number + dataKey: keyof Data; + label: string; + numeric?: boolean; + width?: number; } const columns: ColumnData[] = [ { - label: 'Sender', - dataKey: 'user', - width: 200 + label: "Sender", + dataKey: "user", + width: 200, }, { - label: 'Subject', - dataKey: 'description' + label: "Subject", + dataKey: "description", }, { - label: 'Date', - dataKey: 'createdAt', + label: "Date", + dataKey: "createdAt", numeric: true, - width: 200 - } -] - - + width: 200, + }, +]; function fixedHeaderContent() { return ( - {columns.map((column) => { + {columns.map(column => { return ( {column.label} - ) + ); })} - ) + ); } function rowContent(_index: number, row: Data, openMessage: any) { return ( - {columns.map((column) => { - let subject = '-' - if (column.dataKey === 'description' && row['subject']) { - subject = row['subject'] + {columns.map(column => { + let subject = "-"; + if (column.dataKey === "description" && row["subject"]) { + subject = row["subject"]; } return ( openMessage(row?.user, row?.id, row)} key={column.dataKey} - align={column.numeric || false ? 'right' : 'left'} - style={{ width: column.width, cursor: 'pointer' }} + align={column.numeric || false ? "right" : "left"} + style={{ width: column.width, cursor: "pointer" }} sx={{ fontSize: tableCellFontSize, - padding: '7px' + padding: "7px", }} > - {column.dataKey === 'user' && ( + {column.dataKey === "user" && ( {row[column.dataKey]} )} - {column.dataKey !== 'user' && ( + {column.dataKey !== "user" && ( <> - {column.dataKey === 'createdAt' + {column.dataKey === "createdAt" ? formatTimestamp(row[column.dataKey]) - : column.dataKey === 'description' + : column.dataKey === "description" ? subject : row[column.dataKey]} )} - ) + ); })} - ) + ); } interface SimpleTableProps { - openMessage: (user: string, messageIdentifier: string, content: any) => void - data: Data[] - children?: React.ReactNode + openMessage: (user: string, messageIdentifier: string, content: any) => void; + data: Data[]; + children?: React.ReactNode; } export default function SimpleTable({ openMessage, data, - children + children, }: SimpleTableProps) { return ( - + {fixedHeaderContent()} @@ -154,24 +152,45 @@ export default function SimpleTable({ {children} - ) + ); } -export const AvatarWrapper = ({ user, height , fallback, isAlias}: any) => { +export const AvatarWrapper = ({ user, height, fallback, isAlias }: any) => { const userAvatarHash = useSelector( (state: RootState) => state.global.userAvatarHash - ) + ); const avatarLink = React.useMemo(() => { - if (!user || !userAvatarHash) return '' - const findUserAvatar = userAvatarHash[user] - if (!findUserAvatar) return '' - return findUserAvatar - }, [userAvatarHash, user]) + if (!user || !userAvatarHash) return ""; + const findUserAvatar = userAvatarHash[user]; + if (!findUserAvatar) return ""; + return findUserAvatar; + }, [userAvatarHash, user]); -if(isAlias) return -if(!fallback) return - return {fallback?.charAt(0)} -} + if (isAlias) + return ( + + ); + if (!fallback) + return ( + + ); + return ( + + {fallback?.charAt(0)} + + ); +}; diff --git a/src/pages/Mail/NewMessage.tsx b/src/pages/Mail/NewMessage.tsx index c702845..0bb18fd 100644 --- a/src/pages/Mail/NewMessage.tsx +++ b/src/pages/Mail/NewMessage.tsx @@ -54,7 +54,7 @@ import { NewMessageInputRow, NewMessageSendButton, NewMessageSendP, -} from "./Mail-styles"; +} from "../Home/Home-styles"; import ComposeIconSVG from "../../assets/svgs/ComposeIcon.svg"; import ModalCloseSVG from "../../assets/svgs/ModalClose.svg"; import AttachmentSVG from "../../assets/svgs/NewMessageAttachment.svg"; diff --git a/src/pages/Mail/NewThread.tsx b/src/pages/Mail/NewThread.tsx index 0f84c8b..ffea22c 100644 --- a/src/pages/Mail/NewThread.tsx +++ b/src/pages/Mail/NewThread.tsx @@ -50,7 +50,7 @@ import { NewMessageInputRow, NewMessageSendButton, NewMessageSendP, -} from "./Mail-styles"; +} from "../Home/Home-styles"; import { Spacer } from "../../components/common/Spacer"; import { TextEditor } from "../../components/common/TextEditor/TextEditor"; import { SendNewMessage } from "../../assets/svgs/SendNewMessage"; diff --git a/src/pages/Mail/SentMail.tsx b/src/pages/Mail/SentMail.tsx index ca078fc..7c9c607 100644 --- a/src/pages/Mail/SentMail.tsx +++ b/src/pages/Mail/SentMail.tsx @@ -38,7 +38,7 @@ 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 { MessagesContainer } from "../Home/Home-styles"; import { MailMessageRow } from "./MailMessageRow"; interface SentMailProps { diff --git a/src/pages/Mail/ShowMessageV2.tsx b/src/pages/Mail/ShowMessageV2.tsx index e56a99f..2379af4 100644 --- a/src/pages/Mail/ShowMessageV2.tsx +++ b/src/pages/Mail/ShowMessageV2.tsx @@ -35,7 +35,7 @@ import { ShowMessageNameP, ShowMessageSubjectP, ShowMessageTimeP, -} from "./Mail-styles"; +} from "../Home/Home-styles"; import ReplySVG from "../../assets/svgs/Reply.svg"; import ForwardSVG from "../../assets/svgs/Forward.svg"; import AttachmentMailSVG from "../../assets/svgs/AttachmentMail.svg"; @@ -57,14 +57,14 @@ export const ShowMessageV2 = ({ message, setReplyTo, alias, - setForwardInfo + setForwardInfo, }: any) => { const [value, setValue] = useState(initialValue); const [title, setTitle] = useState(""); const [attachments, setAttachments] = useState([]); const [description, setDescription] = useState(""); const [expandAttachments, setExpandAttachments] = useState(false); - const [toUser, setToUser] = useState(null) + const [toUser, setToUser] = useState(null); const [destinationName, setDestinationName] = useState(""); const username = useSelector((state: RootState) => state.auth?.user?.name); const dispatch = useDispatch(); @@ -79,33 +79,33 @@ export const ShowMessageV2 = ({ setReplyTo(message); }; - - - - const handleForwardedMessage = ()=> { - if(!username) return - let secondpart = "" - if(message?.textContentV2){ - secondpart = message.textContentV2 + const handleForwardedMessage = () => { + if (!username) return; + let secondpart = ""; + if (message?.textContentV2) { + secondpart = message.textContentV2; } if (message?.htmlContent) { secondpart = DOMPurify.sanitize(message.htmlContent); } - let newTo = username - if(alias){ - newTo = `${alias} (alias)` + let newTo = username; + if (alias) { + newTo = `${alias} (alias)`; } - const firstPart = updateMessageDetails(message?.user, message?.subject || "", newTo) - const fullMessage = firstPart + secondpart - setForwardInfo(fullMessage) - } + const firstPart = updateMessageDetails( + message?.user, + message?.subject || "", + newTo + ); + const fullMessage = firstPart + secondpart; + setForwardInfo(fullMessage); + }; let cleanHTML = ""; if (message?.htmlContent) { cleanHTML = DOMPurify.sanitize(message.htmlContent); } - return ( {message?.user} - + to: {message?.to} @@ -181,78 +183,81 @@ export const ShowMessageV2 = ({ marginTop: "10px", }} > - {message?.attachments - .map((file: any, index: number) => { - const isFirst = index === 0 - return ( + {message?.attachments.map((file: any, index: number) => { + const isFirst = index === 0; + return ( + - - - + - + {file?.originalFilename || file?.filename} + + + {message?.attachments?.length > 1 && isFirst && ( + { + setExpandAttachments(prev => !prev); + }} + > + - {file?.originalFilename || file?.filename} - - - {message?.attachments?.length > 1 && isFirst && ( - { - setExpandAttachments(prev => !prev); - }} - > - - - {expandAttachments ? 'hide' : `(${message?.attachments?.length - 1} more)`} - - - - )} - + src={MoreSVG} + /> + + {expandAttachments + ? "hide" + : `(${message?.attachments?.length - 1} more)`} + + + )} - ); - }) - } + + ); + })} )} @@ -268,7 +273,7 @@ export const ShowMessageV2 = ({ display: "flex", gap: "25px", justifyContent: "center", - marginTop: '10px' + marginTop: "10px", }} > @@ -292,31 +297,33 @@ export const ShowMessageV2 = ({ gap: "8px", }} > - {message?.generalData?.threadV2 && [...(message?.generalData?.threadV2 || [])] // Create a copy of the array - .sort((a, b) => { - // Sort messages based on createdAt in descending order - if (!a.data || !b.data) return 0; - return a.data.createdAt - b.data.createdAt; - }) - .map(msg => { - // Render each message using a component - if (!msg?.data) return null; - return ; - }) - } + {message?.generalData?.threadV2 && + [...(message?.generalData?.threadV2 || [])] // Create a copy of the array + .sort((a, b) => { + // Sort messages based on createdAt in descending order + if (!a.data || !b.data) return 0; + return a.data.createdAt - b.data.createdAt; + }) + .map(msg => { + // Render each message using a component + if (!msg?.data) return null; + return ( + + ); + })} {message?.htmlContent && ( -
- )} - - - - {message?.textContent && ( +
+ )} + + {message?.textContent && ( )} - + ); diff --git a/src/pages/Mail/ShowMessageV2Replies.tsx b/src/pages/Mail/ShowMessageV2Replies.tsx index 3a9c154..55243b4 100644 --- a/src/pages/Mail/ShowMessageV2Replies.tsx +++ b/src/pages/Mail/ShowMessageV2Replies.tsx @@ -25,13 +25,22 @@ import FileElement from "../../components/FileElement"; import MailThreadWithoutCalling from "./MailThreadWithoutCalling"; import { DisplayHtml } from "../../components/common/TextEditor/DisplayHtml"; import { Spacer } from "../../components/common/Spacer"; -import { MailAttachmentImg, MoreImg, MoreP, ShowMessageButton, ShowMessageButtonImg, ShowMessageButtonP, ShowMessageNameP, ShowMessageSubjectP, ShowMessageTimeP } from "./Mail-styles"; -import ReplySVG from '../../assets/svgs/Reply.svg' -import ForwardSVG from '../../assets/svgs/Forward.svg' +import { + MailAttachmentImg, + MoreImg, + MoreP, + ShowMessageButton, + ShowMessageButtonImg, + ShowMessageButtonP, + ShowMessageNameP, + ShowMessageSubjectP, + ShowMessageTimeP, +} from "../Home/Home-styles"; +import ReplySVG from "../../assets/svgs/Reply.svg"; +import ForwardSVG from "../../assets/svgs/Forward.svg"; import MoreSVG from "../../assets/svgs/More.svg"; import AttachmentMailSVG from "../../assets/svgs/AttachmentMail.svg"; - const initialValue: Descendant[] = [ { type: "paragraph", @@ -40,21 +49,15 @@ const initialValue: Descendant[] = [ ]; const uid = new ShortUniqueId(); -export const ShowMessageV2Replies = ({ - message, -}: any) => { +export const ShowMessageV2Replies = ({ message }: any) => { const username = useSelector((state: RootState) => state.auth?.user?.name); const [expandAttachments, setExpandAttachments] = useState(false); - const [isExpanded, setIsExpanded] = useState(false); - - const isUser = useMemo(()=> { - return username === message?.user - }, [username, message]) - - + const isUser = useMemo(() => { + return username === message?.user; + }, [username, message]); let cleanHTML = ""; if (message?.htmlContent) { @@ -62,199 +65,203 @@ export const ShowMessageV2Replies = ({ } return ( - { - setIsExpanded((prev)=> !prev) - }} sx={{ - cursor: 'pointer', display: "flex", - gap: '20px', - justifyContent: "flex-start", - alignItems: "flex-start", + alignItems: "center", + flexDirection: "column", + gap: 1, + flexGrow: 1, + overflow: "auto", width: "100%", - flexDirection: !isUser ? 'row-reverse' : 'unset' + padding: "0 15px", }} > { + setIsExpanded(prev => !prev); + }} sx={{ + cursor: "pointer", display: "flex", + gap: "20px", + justifyContent: "flex-start", alignItems: "flex-start", - gap: "6px" + width: "100%", + flexDirection: !isUser ? "row-reverse" : "unset", }} > - - - - {message?.user} - - - {formatEmailDate(message?.createdAt)} - - + + + + {message?.user} + + + {formatEmailDate(message?.createdAt)} + + - - - - - {message?.subject} - - + + {message?.subject} + + - - {isExpanded && ( - <> - {message?.attachments?.length > 0 && ( - - {message?.attachments?.length > 0 && ( - - {message?.attachments - .map((file: any, index: number) => { - const isFirst = index === 0 - return ( - + {isExpanded && ( + <> + {message?.attachments?.length > 0 && ( + + {message?.attachments?.length > 0 && ( - - - - - {file?.originalFilename || file?.filename} - - - {message?.attachments?.length > 1 && isFirst && ( - { - setExpandAttachments(prev => !prev); - }} - > - { + const isFirst = index === 0; + return ( + - - ({message?.attachments?.length - 1} more) - - - )} + > + + + + + + {file?.originalFilename || file?.filename} + + + {message?.attachments?.length > 1 && isFirst && ( + { + setExpandAttachments(prev => !prev); + }} + > + + + ({message?.attachments?.length - 1} more) + + + )} + + + ); + })} - - ); - }) - } - - )} - - )} - - {message?.textContentV2 && ( - - )} - - )} - + )} + + )} + + {message?.textContentV2 && ( + + )} + + )} + - - ); }; diff --git a/src/pages/Mail/ShowMessageWithoutModal.tsx b/src/pages/Mail/ShowMessageWithoutModal.tsx index 46cf669..0b4d512 100644 --- a/src/pages/Mail/ShowMessageWithoutModal.tsx +++ b/src/pages/Mail/ShowMessageWithoutModal.tsx @@ -38,7 +38,7 @@ import { ThreadSingleLastMessageP, ThreadSingleLastMessageSpanP, ThreadSingleTitle, -} from "./Mail-styles"; +} from "../Home/Home-styles"; import { Spacer } from "../../components/common/Spacer"; const initialValue: Descendant[] = [ { diff --git a/src/pages/Mail/Thread.tsx b/src/pages/Mail/Thread.tsx index af788c2..0c810e4 100644 --- a/src/pages/Mail/Thread.tsx +++ b/src/pages/Mail/Thread.tsx @@ -37,7 +37,7 @@ import { SingleThreadParent, ThreadContainer, ThreadContainerFullWidth, -} from "./Mail-styles"; +} from "../Home/Home-styles"; import { Spacer } from "../../components/common/Spacer"; import ReturnSVG from "../../assets/svgs/Return.svg"; import LazyLoad from "../../components/common/LazyLoad";