From 50b90c235f432c3a442cdb7720b550d42faa3be9 Mon Sep 17 00:00:00 2001 From: Riyanshu Patro Date: Wed, 18 Dec 2024 15:56:18 +0530 Subject: [PATCH 01/14] Email app --- examples/email/package.json | 3 +- examples/email/src/App.tsx | 51 ++- examples/email/src/components/Header.tsx | 178 ++++++++ examples/email/src/components/email-card.tsx | 78 +++- .../email/src/components/email-layout.tsx | 8 +- examples/email/src/components/email-list.tsx | 6 +- .../email/src/components/email-viewer.tsx | 150 ++++--- .../src/components/fileUpload/FileUpload.tsx | 59 +++ .../email/src/components/fileUpload/index.ts | 1 + .../email/src/components/logged-in-view.tsx | 186 ++++++--- examples/email/src/components/login.tsx | 251 ++++++++++- examples/email/src/components/new-email.tsx | 319 +++++++++----- .../email/src/components/select/Select.tsx | 391 ++++++++++++++++++ .../email/src/components/select/index.tsx | 1 + .../email/src/components/ui/alert-bar.tsx | 120 ++++++ examples/email/src/components/ui/card.tsx | 53 +-- examples/email/src/components/ui/popover.tsx | 22 +- .../src/components/ui/recipient-input.tsx | 55 +++ examples/email/src/components/ui/select.tsx | 209 +++------- examples/email/src/context/app-context.tsx | 4 + examples/email/src/index.css | 115 +++--- examples/email/src/lib/utils.ts | 80 +++- examples/email/src/providers/app-provider.tsx | 10 + examples/email/src/routes.tsx | 36 ++ examples/email/src/types/index.ts | 1 + examples/email/tsconfig.app.tsbuildinfo | 2 +- examples/email/tsconfig.node.tsbuildinfo | 2 +- examples/email/yarn.lock | 10 + 28 files changed, 1884 insertions(+), 517 deletions(-) create mode 100644 examples/email/src/components/Header.tsx create mode 100644 examples/email/src/components/fileUpload/FileUpload.tsx create mode 100644 examples/email/src/components/fileUpload/index.ts create mode 100644 examples/email/src/components/select/Select.tsx create mode 100644 examples/email/src/components/select/index.tsx create mode 100644 examples/email/src/components/ui/alert-bar.tsx create mode 100644 examples/email/src/components/ui/recipient-input.tsx create mode 100644 examples/email/src/routes.tsx diff --git a/examples/email/package.json b/examples/email/package.json index b20335a9..8604a73b 100644 --- a/examples/email/package.json +++ b/examples/email/package.json @@ -5,7 +5,7 @@ "type": "module", "scripts": { "dev": "vite", - "build": "tsc -b && vite build", + "build": "vite build", "lint": "eslint .", "preview": "vite preview", "deploy:vercel": "vercel build --prod && vercel deploy --prebuilt --prod", @@ -16,6 +16,7 @@ "@privy-io/wagmi": "^0.2.12", "@pushprotocol/node-core": "^0.0.29", "@pushprotocol/push-chain": "^0.1.7", + "@pushprotocol/pushchain-ui-kit": "^0.0.8", "@radix-ui/react-icons": "^1.3.0", "@radix-ui/react-popover": "^1.1.2", "@radix-ui/react-scroll-area": "^1.2.0", diff --git a/examples/email/src/App.tsx b/examples/email/src/App.tsx index ffddd575..90747bbe 100644 --- a/examples/email/src/App.tsx +++ b/examples/email/src/App.tsx @@ -1,25 +1,48 @@ import { usePrivy } from '@privy-io/react-auth'; import { useAppContext } from './context/app-context'; -import LoggedInView from './components/logged-in-view'; -import Login from './components/login'; +import { getBlocksCSSVariables, Spinner, themeConfig } from 'shared-components'; + +import { createGlobalStyle, ThemeProvider } from 'styled-components'; +import AppRoutes from './routes'; +import { BrowserRouter } from 'react-router-dom'; +import { ENV, WalletProvider } from '@pushprotocol/pushchain-ui-kit'; + +const GlobalStyle = createGlobalStyle` + :root{ + /* Font Family */ + --font-family: 'FK Grotesk Neu', Helvetica, sans-serif; + + /* New blocks theme css variables*/ + + ${(props) => getBlocksCSSVariables(props.theme.blocksTheme)} + } +`; function App() { const { ready, authenticated } = usePrivy(); const { pushAccount } = useAppContext(); + return ( - <> - {ready ? ( -
- {authenticated || pushAccount ? : } -
- ) : ( -
-
-

Loading

-
- )} - + + + + + {ready ? ( +
+ +
+ ) : ( +
+ +
+ )} +
+
+
); } diff --git a/examples/email/src/components/Header.tsx b/examples/email/src/components/Header.tsx new file mode 100644 index 00000000..a0163e82 --- /dev/null +++ b/examples/email/src/components/Header.tsx @@ -0,0 +1,178 @@ +import { useAppContext } from '@/context/app-context'; +import { trimAddress } from '@/lib/utils'; +import { usePrivy } from '@privy-io/react-auth'; +import { TokenBNB, TokenETH, TokenPUSH, TokenSOL } from '@web3icons/react'; +import { MenuIcon } from 'lucide-react'; +import { FC } from 'react'; +import { + Box, + PushLogo, + Text, + Button, + Menu, + Dropdown, + MenuItem, +} from 'shared-components'; +import { css } from 'styled-components'; + +const Header: FC = () => { + const { user, authenticated, logout } = usePrivy(); + const { pushAccount, setPushAccount, setSelectedEmail } = useAppContext(); + + const logoutHandler = () => { + if (pushAccount) { + setPushAccount(null); + } else if (authenticated) { + logout(); + } + setSelectedEmail(null); + }; + + return ( + + + + + Push + + + Email + + + + + {pushAccount + ? trimAddress(pushAccount.split(':')[2]) + : user?.wallet?.address && + trimAddress(user.wallet.address)} + + ) + } + icon={ + + {pushAccount ? ( + + ) : user?.wallet?.chainType === 'solana' ? ( + + ) : user?.wallet?.chainId === 'eip155:56' ? ( + + ) : ( + + )} + + } + /> + + + } + > + + + + + + + + + + ); +}; + +export { Header }; diff --git a/examples/email/src/components/email-card.tsx b/examples/email/src/components/email-card.tsx index 0437b2b9..223b38f0 100644 --- a/examples/email/src/components/email-card.tsx +++ b/examples/email/src/components/email-card.tsx @@ -1,9 +1,13 @@ import React from 'react'; -import { Card, CardDescription, CardHeader, CardTitle } from './ui/card'; +import { Card } from './ui/card'; import { IEmail } from '@/types'; import { useAppContext } from '@/context/app-context'; import { formatTimestamp, trimAddress } from '@/lib/utils'; import { EMAIL_BOX } from '@/constants'; +import BlockiesSvg from 'blockies-react-svg'; +import { Box, Text } from 'shared-components'; +import { css } from 'styled-components'; +import { useNavigate } from 'react-router-dom'; const EmailCard: React.FC = ({ from, @@ -13,34 +17,70 @@ const EmailCard: React.FC = ({ body, type, attachments, + txHash, }) => { - const { setSelectedEmail, selectedEmail } = useAppContext(); + const navigate = useNavigate(); + const { currTab, setSelectedEmail, selectedEmail } = useAppContext(); + return ( { - setSelectedEmail({ from, to, subject, timestamp, body, attachments }); + setSelectedEmail({ + from, + to, + subject, + timestamp, + body, + attachments, + txHash, + }); + navigate(`/${currTab}/${txHash}`); }} className={`cursor-pointer ${ type === EMAIL_BOX.INBOX - ? selectedEmail?.from === from && 'bg-primary-foreground' + ? selectedEmail?.from === from && 'bg-secondary-foreground' : selectedEmail?.to === to && 'bg-primary-foreground' }`} > - - -
-

{subject}

-

- {formatTimestamp(timestamp.toString())} -

-
-
- - {type === EMAIL_BOX.INBOX - ? trimAddress(from.split(':')[2]) - : to.map((t) => trimAddress(t.split(':')[2])).join(', ')} - -
+ + + + + + + {subject} + + {trimAddress(from)} + + + + {formatTimestamp(timestamp.toString())} + + + + {body} + +
); }; diff --git a/examples/email/src/components/email-layout.tsx b/examples/email/src/components/email-layout.tsx index 822b70dc..a60781eb 100644 --- a/examples/email/src/components/email-layout.tsx +++ b/examples/email/src/components/email-layout.tsx @@ -1,12 +1,12 @@ -import React, { useState } from 'react'; +import React from 'react'; import { IEmail } from '@/types'; -import NewEmail from './new-email'; import EmailViewer from './email-viewer'; +import { useAppContext } from '@/context/app-context'; const EmailLayout: React.FC = () => { - const [replyTo, setReplyTo] = useState(undefined); + const { setReplyTo } = useAppContext(); const handleReply = (email: IEmail) => { setReplyTo(email); @@ -15,8 +15,6 @@ const EmailLayout: React.FC = () => { return (
- -
); }; diff --git a/examples/email/src/components/email-list.tsx b/examples/email/src/components/email-list.tsx index e9828327..c7e51e98 100644 --- a/examples/email/src/components/email-list.tsx +++ b/examples/email/src/components/email-list.tsx @@ -2,6 +2,7 @@ import EmailCard from './email-card'; import { ScrollArea } from './ui/scroll-area'; import { useAppContext } from '@/context/app-context'; import { EMAIL_BOX } from '@/constants'; +import { Box } from 'shared-components'; const EmailList = ({ type }: { type: EMAIL_BOX.INBOX | EMAIL_BOX.SENT }) => { const { searchInput, emails } = useAppContext(); @@ -26,11 +27,12 @@ const EmailList = ({ type }: { type: EMAIL_BOX.INBOX | EMAIL_BOX.SENT }) => { return ( -
+ {filteredEmails.map((email, index) => ( ))} -
+ +
); }; diff --git a/examples/email/src/components/email-viewer.tsx b/examples/email/src/components/email-viewer.tsx index 8d5cb32b..24f2b5a6 100644 --- a/examples/email/src/components/email-viewer.tsx +++ b/examples/email/src/components/email-viewer.tsx @@ -1,13 +1,6 @@ import React, { useState } from 'react'; import { useAppContext } from '@/context/app-context'; -import { - Card, - CardDescription, - CardFooter, - CardHeader, - CardTitle, -} from './ui/card'; -import { Button } from './ui/button'; +import { Card, CardFooter } from './ui/card'; import { File, FileText, @@ -16,18 +9,22 @@ import { Video, Archive, Code, - Reply, + ReplyIcon, } from 'lucide-react'; -import { formatTimestamp } from '@/lib/utils'; -import { Badge } from './ui/badge'; +import { formatTimestamp, trimAddress } from '@/lib/utils'; import { FileAttachment, FileAttachments, IEmail } from '@/types'; +import { Box, Text, Button, Back } from 'shared-components'; +import BlockiesSvg from 'blockies-react-svg'; +import { useNavigate } from 'react-router-dom'; +import { css } from 'styled-components'; interface EmailViewerProps { onReply: (email: IEmail) => void; } const EmailViewer: React.FC = ({ onReply }) => { - const { selectedEmail } = useAppContext(); + const { currTab, selectedEmail, setSelectedEmail } = useAppContext(); + const navigate = useNavigate(); const handleReply = () => { if (selectedEmail) { @@ -37,10 +34,10 @@ const EmailViewer: React.FC = ({ onReply }) => { const formatEmailBody = (body: string) => { const lines = body.split('\n'); - let formattedBody = []; + const formattedBody = []; let inQuote = false; - for (let line of lines) { + for (const line of lines) { if (line.startsWith('On') && line.includes('wrote:')) { inQuote = true; formattedBody.push( @@ -65,53 +62,108 @@ const EmailViewer: React.FC = ({ onReply }) => { return formattedBody; }; + const handleBack = () => { + navigate(`/${currTab}`); + setSelectedEmail(null); + }; + return ( <> {selectedEmail && ( - - - -
-

{selectedEmail.subject}

-

- {formatTimestamp(selectedEmail.timestamp.toString())} -

-
- -
- Sender - - {selectedEmail.from.split(':')[2]} - -
-
- Recipients -
- {selectedEmail.to.map((to) => ( - - {to.split(':')[2]} - - ))} -
-
-
-
- + + + + + + + + {selectedEmail.subject} + + + {formatTimestamp(selectedEmail.timestamp.toString(), true)} + + + + + + + + + + {trimAddress(selectedEmail.from)} + + + To:{' '} + {selectedEmail.to + .map((address) => trimAddress(address)) + .join(', ')} + + + + + + + {formatEmailBody(selectedEmail.body)} - + {selectedEmail.attachments && selectedEmail.attachments.length > 0 && ( )} -
- - - +
)} diff --git a/examples/email/src/components/fileUpload/FileUpload.tsx b/examples/email/src/components/fileUpload/FileUpload.tsx new file mode 100644 index 00000000..89d89a2a --- /dev/null +++ b/examples/email/src/components/fileUpload/FileUpload.tsx @@ -0,0 +1,59 @@ +import { DragEventHandler, forwardRef, ReactNode, useRef } from 'react'; +import styled, { FlattenSimpleInterpolation } from 'styled-components'; + +export type FileUploadProps = { + children?: ReactNode; + css?: FlattenSimpleInterpolation; + disabled?: boolean; + onChange?: (e: React.ChangeEvent) => void; + onDrop?: DragEventHandler; + id: string; +}; + +const Container = styled.div<{ css?: FlattenSimpleInterpolation }>` + align-items: center; + display: flex; + flex-direction: column; + flex: 1 0 0; + gap: var(--spacing-xxs, 8px); + + /* Custom CSS applied via styled component css prop */ + ${(props) => props.css || ''}; +`; + +export const FileUpload = forwardRef( + ({ disabled, children, onChange, onDrop, id }, ref) => { + const inputRef = useRef(null); + + const handleDragOver: DragEventHandler = (e) => { + e.preventDefault(); + }; + + const handleClick = () => { + if (inputRef.current && !disabled) { + inputRef.current.click(); + } + }; + + return ( + + {children} + + + ); + } +); diff --git a/examples/email/src/components/fileUpload/index.ts b/examples/email/src/components/fileUpload/index.ts new file mode 100644 index 00000000..fca98b5b --- /dev/null +++ b/examples/email/src/components/fileUpload/index.ts @@ -0,0 +1 @@ +export * from './FileUpload'; diff --git a/examples/email/src/components/logged-in-view.tsx b/examples/email/src/components/logged-in-view.tsx index e9776994..23ed2266 100644 --- a/examples/email/src/components/logged-in-view.tsx +++ b/examples/email/src/components/logged-in-view.tsx @@ -1,61 +1,143 @@ -import SearchBar from './ui/search-bar'; -import EmailList from './email-list'; -import { - ResizableHandle, - ResizablePanel, - ResizablePanelGroup, -} from '@/components/ui/resizable'; -import ConnectedWalletInfo from './connected-wallet-info'; -import { Tabs, TabsContent, TabsList, TabsTrigger } from './ui/tabs'; import NewEmail from './new-email'; -import { EMAIL_BOX } from '@/constants'; import EmailLayout from './email-layout'; -import { Inbox, Send } from 'lucide-react'; +import { Header } from './Header'; +import { Text, Box, TextInput, Tabs } from 'shared-components'; +import { useAppContext } from '@/context/app-context'; +import { css } from 'styled-components'; +import { EMAIL_BOX } from '@/constants'; +import EmailList from './email-list'; +import { useEffect } from 'react'; +import { useLocation, useNavigate, useParams } from 'react-router-dom'; const LoggedInView = () => { + const navigate = useNavigate(); + const location = useLocation(); + const { id } = useParams(); + const { + currTab, + setCurrTab, + emails, + searchInput, + setSearchInput, + setSelectedEmail, + selectedEmail, + replyTo, + } = useAppContext(); + + const handleTabSwitch = (tab: 'inbox' | 'sent') => { + setCurrTab(tab); + // navigate(`/${tab}`); + }; + + useEffect(() => { + if (location.pathname.includes('sent')) { + setCurrTab('sent'); + } else { + setCurrTab('inbox'); + } + }, [location.pathname]); + + useEffect(() => { + if (id) { + const emailList = emails[currTab]; + if (emailList && emailList.length > 0) { + const email = emailList.find((email) => email.txHash === id); + + if (email) { + setSelectedEmail(email); + } else { + navigate(`/${currTab}`); + } + } + } else { + setSelectedEmail(null); + } + }, [id, emails, currTab, navigate]); + + console.log(selectedEmail); + return ( -
-
- - - -
{' '} - - - -

- Emails -

- - - - - Inbox - - - - Sent - - - - - - - - - -
- - +
+
+ + {/*
+ +
*/} + + + + + Inbox + setSearchInput(e.target.value)} + css={css` + width: 100%; + `} + /> + handleTabSwitch(tab as 'inbox' | 'sent')} + items={[ + { + key: 'inbox', + label: Inbox, + children: null, + }, + { + key: 'sent', + label: Sent, + children: null, + }, + ]} + /> + + {currTab === 'inbox' && } + {currTab === 'sent' && } + + - - + +
); }; diff --git a/examples/email/src/components/login.tsx b/examples/email/src/components/login.tsx index 345154bc..a3b65210 100644 --- a/examples/email/src/components/login.tsx +++ b/examples/email/src/components/login.tsx @@ -1,9 +1,28 @@ import { usePrivy } from '@privy-io/react-auth'; import { useAppContext } from '@/context/app-context'; -import { Button } from './ui/button'; import { toBytes } from 'viem'; +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-nocheck +import React from 'react'; +import styled from 'styled-components'; +import { Box } from 'shared-components'; +import { ConnectPushWalletButton } from '@pushprotocol/pushchain-ui-kit'; -const Login = () => { +import ChainAlertBar from './ui/alert-bar'; + +const featuresCard = [ + { + text: 'Webtwo ipsum unigo. Elgg skype woopra fleck ifttt imvu, hipmunk handango.', + }, + { + text: 'Webtwo ipsum unigo. Elgg skype woopra fleck ifttt imvu, hipmunk handango.', + }, + { + text: 'Webtwo ipsum unigo. Elgg skype woopra fleck ifttt imvu, hipmunk handango.', + }, +]; + +const Template = () => { const { login } = usePrivy(); const { pushNetwork, setPushAccount } = useAppContext(); const pushWalletLoginHandler = async () => { @@ -21,14 +40,228 @@ const Login = () => { } }; + const StarIcon = () => { + return ( + + + + ); + }; + return ( -
- - -
+ + + + + + + + Email + Powered by Push Chain + + + + + Webtwo ipsum unigo. Elgg skype woopra fleck ifttt imvu, hipmunk + handango empressr. + + + {pushNetwork && ( + + + + )} + + {/* */} + + {featuresCard.map((item) => ( + + + {item.text} + + ))} + + + {/* add image here */} + + + ); }; -export default Login; +export default Template; + +const TemplateWrapper = styled.div` + height: 100vh; + background: #fff; + + @media (max-width: 768px) { + min-height: 100vh; + height: auto; + } +`; + +const TemplateContent = styled.div` + margin: auto auto; + height: 100%; + display: flex; + align-items: center; + + @media (max-width: 2560px) { + width: 1400px; + } + + @media (max-width: 2000px) { + width: 1200px; + } + + @media (max-width: 1548px) { + width: 100%; + padding: 0 48px; + } + + @media (max-width: 768px) { + padding: 0 24px; + margin: 24px auto; + } + + @media (max-width: 470px) { + padding: 0 16px; + } +`; + +const ItemContainer = styled.div` + display: flex; + flex-direction: row; + align-items: center; + gap: 150px; + width: 100%; + + @media (max-width: 768px) { + flex-direction: column; + } +`; + +const TextContainer = styled.div` + display: flex; + flex-direction: column; + align-items: flex-start; +`; + +const TextTitle = styled.span` + color: #17181b; + leading-trim: both; + text-edge: cap; + font-family: N27; + font-size: 128px; + font-style: normal; + font-weight: 500; + line-height: 90px; + letter-spacing: 5.12px; +`; + +const TextSubtitle = styled.span` + color: #d796fd; + font-family: N27; + font-size: 16px; + font-style: normal; + font-weight: 500; + line-height: 140%; +`; + +const TextSpan = styled.span` + color: #17181b; + font-family: N27; + font-size: 20px; + font-style: normal; + font-weight: 400; + line-height: 140%; + margin-top: 24px; + + @media (max-width: 768px) { + margin-top: 16px; + } + + @media (max-width: 470px) { + font-size: 17px; + } +`; + +const Features = styled.div` + display: flex; + border-bottom: 1px solid #c4cbd5; + padding: 24px 0; + gap: 24px; + align-items: center; + width: 100%; +`; + +const FeaturesSpan = styled.span` + color: #313338; + font-family: N27; + font-size: 18px; + font-style: normal; + font-weight: 400; + line-height: 140%; + + @media (max-width: 470px) { + font-size: 15px; + } +`; + +const Button = styled.button` + background: #e21d48; + width: 100%; + margin: 48px 0 32px 0; + height: 58px; + padding: 16px 32px; + justify-content: center; + align-items: center; + align-self: stretch; + color: #fff; + font-family: N27; + font-size: 18px; + font-style: normal; + font-weight: 500; + border-radius: 1000px; + border: none; + line-height: 16px; + cursor: pointer; +`; + +const DesktopImageItem = styled.div` + height: 700px; + background: #202020; + border-radius: 32px; + width: 500px; + display: block; + + @media (max-width: 768px) { + display: none; + } +`; diff --git a/examples/email/src/components/new-email.tsx b/examples/email/src/components/new-email.tsx index e1c23faf..f9758e54 100644 --- a/examples/email/src/components/new-email.tsx +++ b/examples/email/src/components/new-email.tsx @@ -6,26 +6,28 @@ import { } from '@/components/ui/popover'; import PushMail from 'push-mail'; import { ENV } from '@pushprotocol/push-chain/src/lib/constants'; -import { Button } from '@/components/ui/button'; -import { Input } from '@/components/ui/input'; -import { Textarea } from '@/components/ui/textarea'; -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from '@/components/ui/select'; import { useAppContext } from '@/context/app-context'; import { usePrivy, useSolanaWallets } from '@privy-io/react-auth'; import { useSignMessage } from 'wagmi'; import { TokenBNB, TokenETH, TokenPUSH, TokenSOL } from '@web3icons/react'; import { hexToBytes } from 'viem'; import { IEmail } from '@/types'; -import { X } from 'lucide-react'; -import { Badge } from '@/components/ui/badge'; +import { PaperclipIcon } from 'lucide-react'; import { trimAddress, formatTimestamp } from '@/lib/utils'; import { PushNetwork } from '@pushprotocol/push-chain'; +import { + Box, + Button, + SendNotification, + Text, + TextArea, + TextInput, +} from 'shared-components'; +import { css } from 'styled-components'; +import Select from './ui/select'; +import { Cross1Icon } from '@radix-ui/react-icons'; +import styled from 'styled-components'; +import { FileUpload } from './fileUpload'; interface FileData { filename: string; @@ -55,7 +57,7 @@ const NewEmail: React.FC = ({ replyTo }) => { const [fileAttachment, setFileAttachment] = useState(null); const [isOpen, setIsOpen] = useState(false); - const { pushAccount, pushNetwork, setEmails } = useAppContext(); + const { pushAccount, pushNetwork, setEmails, setReplyTo } = useAppContext(); const { signMessageAsync } = useSignMessage(); const { user } = usePrivy(); const { wallets } = useSolanaWallets(); @@ -104,27 +106,36 @@ ${email.body return 'push'; }; - const handleInputChange = useCallback( - (e: ChangeEvent) => { - const { name, value } = e.target; - setEmailData((prev) => ({ ...prev, [name]: value })); + const handleSubjectChange = useCallback( + (e: ChangeEvent) => { + setEmailData((prev) => ({ ...prev, subject: e.target.value })); + }, + [] + ); + + const handleMessageChange = useCallback( + (e: ChangeEvent) => { + setEmailData((prev) => ({ ...prev, message: e.target.value })); }, [] ); const handleNewRecipientChange = useCallback( (e: ChangeEvent) => { - setNewRecipient((prev) => ({ ...prev, address: e.target.value })); + setNewRecipient((prev) => ({ ...prev, address: e.target.value.trim() })); }, [] ); - const handleAddRecipient = useCallback(() => { - if (newRecipient.address) { - setRecipients((prev) => [...prev, newRecipient]); - setNewRecipient({ address: '', chain: 'eth' }); - } - }, [newRecipient]); + const handleAddRecipient = useCallback( + (event: React.KeyboardEvent) => { + if (event.key === 'Enter' && newRecipient.address) { + setRecipients((prev) => [...prev, newRecipient]); + setNewRecipient({ address: '', chain: 'eth' }); + } + }, + [newRecipient] + ); const handleRemoveRecipient = useCallback((index: number) => { setRecipients((prev) => prev.filter((_, i) => i !== index)); @@ -207,6 +218,7 @@ ${email.body timestamp: Date.now(), body: message, attachments: fileAttachment ? [fileAttachment] : [], + txHash: txHash, }, ], inbox: prevEmails.inbox, @@ -234,95 +246,193 @@ ${email.body signMessageAsync, ]); + const handleOpenChange = (open: boolean) => { + setIsOpen(open); + if (!open) { + console.log('Popover closed'); + setEmailData({ + subject: '', + message: '', + }); + setRecipients([]); + setReplyTo(undefined); + } + }; + return ( -
- +
+ - + -

Compose an email

-
-
- {recipients.map((recipient, index) => ( -
- - {recipient.chain === 'eth' ? ( - - ) : recipient.chain === 'sol' ? ( - - ) : recipient.chain === 'bnb' ? ( - - ) : ( - - )} - - - {trimAddress(recipient.address)} - - -
- ))} -
-
+ + {trimAddress(recipient.address)} + + handleRemoveRecipient(index)} + width={12} + height={12} + cursor="pointer" + /> + + ))} + + - , + label: 'Ethereum', + value: 'eth', + }, + { + icon: , + label: 'Solana', + value: 'sol', + }, + { + icon: , + label: 'Push', + value: 'push', + }, + { + icon: , + label: 'BNB', + value: 'bnb', + }, + ]} /> - -
-
- -