Skip to content

Commit

Permalink
feat: added new email html template
Browse files Browse the repository at this point in the history
  • Loading branch information
Molaryy committed Oct 1, 2023
1 parent 3bb32eb commit 623565a
Show file tree
Hide file tree
Showing 5 changed files with 145 additions and 39 deletions.
65 changes: 60 additions & 5 deletions pages/api/email.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,21 @@ import { NextApiRequest, NextApiResponse } from 'next';
import { mailOptions, transporter } from '../../src/config/nodemailer';

type BodyEmail = {
email: string,
subject: string,
message: string,
email: string;
subject: string;
message: string;
fileName: string;
fileContent: string;
userName: string;
}

const CONTACT_MESSAGE_FIELDS: BodyEmail = {
email: 'Email',
subject: 'Subject',
message: 'Message',
fileName: 'FileName',
fileContent: 'FileContent',
userName: 'UserName',
};

const generateEmailContent = (data: BodyEmail): { text: string, html: string } => {
Expand All @@ -20,11 +26,52 @@ const generateEmailContent = (data: BodyEmail): { text: string, html: string } =
}
return str;
}, '');
const htmlData = Object.entries(data).reduce((str, [key, val]) => (`${str}<h3 class='form-heading' align='left'>${CONTACT_MESSAGE_FIELDS[key as keyof BodyEmail]}</h3><p class='form-answer' align='left'>${val}</p>`), '');

return {
text: stringData,
html: `<!DOCTYPE html><html lang='en'> <head> <title></title> <meta charset='utf-8'/> <meta name='viewport' content='width=device-width, initial-scale=1'/> <meta http-equiv='X-UA-Compatible' content='IE=edge'/> <style type='text/css'> body, table, td, a{-webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%;}table{border-collapse: collapse !important;}body{height: 100% !important; margin: 0 !important; padding: 0 !important; width: 100% !important;}@media screen and (max-width: 525px){.wrapper{width: 100% !important; max-width: 100% !important;}.responsive-table{width: 100% !important;}.padding{padding: 10px 5% 15px 5% !important;}.section-padding{padding: 0 15px 50px 15px !important;}}.form-container{margin-bottom: 24px; padding: 20px; border: 1px dashed #ccc;}.form-heading{color: #2a2a2a; font-family: "Helvetica Neue", "Helvetica", "Arial", sans-serif; font-weight: 400; text-align: left; line-height: 20px; font-size: 18px; margin: 0 0 8px; padding: 0;}.form-answer{color: #2a2a2a; font-family: "Helvetica Neue", "Helvetica", "Arial", sans-serif; font-weight: 300; text-align: left; line-height: 20px; font-size: 16px; margin: 0 0 24px; padding: 0;}div[style*="margin: 16px 0;"]{margin: 0 !important;}</style> </head> <body style='margin: 0 !important; padding: 0 !important; background: #fff'> <div style=' display: none; font-size: 1px; color: #fefefe; line-height: 1px; max-height: 0px; max-width: 0px; opacity: 0; overflow: hidden; ' ></div><table border='0' cellpadding='0' cellspacing='0' width='100%'> <tr> <td bgcolor='#ffffff' align='center' style='padding: 10px 15px 30px 15px' class='section-padding' > <table border='0' cellpadding='0' cellspacing='0' width='100%' style='max-width: 500px' class='responsive-table' > <tr> <td> <table width='100%' border='0' cellspacing='0' cellpadding='0'> <tr> <td> <table width='100%' border='0' cellspacing='0' cellpadding='0' > <tr> <td style=' padding: 0 0 0 0; font-size: 16px; line-height: 25px; color: #232323; ' class='padding message-content' > <h2>New Contact Message</h2> <div class='form-container'>${htmlData}</div></td></tr></table> </td></tr></table> </td></tr></table> </td></tr></table> </body></html>`,
html: `<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<meta name='viewport' content='width=device-width, initial-scale=1.0'>
<title>Email Template</title>
<style>
body {
margin: 0;
padding: 0;
font-family: Arial, sans-serif;
background: #f4f4f4;
}
.container {
max-width: 100%;
margin: 0 auto;
padding: 20px;
background: linear-gradient(90deg, #0074D9 0%, #FF4136 100%);
color: white;
}
h1 {
font-size: 24px;
}
p {
font-size: 16px;
line-height: 1.5;
}
</style>
</head>
<body>
<div class='container'>
<h1>Hello,</h1>
<p>${data.userName} sent you
${data.message ? 'the following message:' : 'a file without a message.'}
</p>
${data.message ? `<p>${data.message}</p>` : ''}
</div>
</body>
</html>
`,
};
};

Expand All @@ -37,6 +84,14 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)

try {
mailOptions.to = data.email;
if (data.fileContent) {
mailOptions.attachments = [
{
filename: data.fileName,
content: data.fileContent,
},
];
}
await transporter.sendMail({
...mailOptions,
...generateEmailContent(data),
Expand Down
26 changes: 22 additions & 4 deletions src/components/file/SendEmailFile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import Button from '../Button';
import Modal from '../Modal';
import { textColorMode } from '../../config/colorMode';
import sendContactForm from '../../lib/email';
import { useUserContext } from '../../contexts/user';

type SendEmailFileProps = {
file: IPCFile;
Expand All @@ -30,14 +31,18 @@ type EmailElements = {
email: string;
subject: string;
message: string;
fileName: string;
fileContent: string;
userName: string;
}

const initValues: EmailElements = { email: '', subject: '', message: '' };
const initValues: EmailElements = { email: '', subject: '', message: '', fileContent: '', fileName: '', userName: '' };


const initState = { isLoading: false, error: '', values: initValues };

const SendEmailFile = ({ onClosePopover }: SendEmailFileProps): JSX.Element => {
const SendEmailFile = ({ file, onClosePopover }: SendEmailFileProps): JSX.Element => {
const { user } = useUserContext();
const isDrawer = useBreakpointValue({ base: true, sm: false }) || false;
const { isOpen, onOpen, onClose } = useDisclosure();
const { colorMode } = useColorMode();
Expand All @@ -52,9 +57,18 @@ const SendEmailFile = ({ onClosePopover }: SendEmailFileProps): JSX.Element => {

const { isLoading, error, values } = state;

const arrayBufferToStringAndBase64 = async () => {
const data = await user.drive.getContentFile(file);
const uint8Array = new Uint8Array(data);

const decoder = new TextDecoder('utf-8');
return decoder.decode(uint8Array);
};


// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
const onBlur = ({ target }) =>
const onBlur = async ({ target }) =>
setTouched((prev) => ({ ...prev, [target.name]: true }));

const sendEmail = async () => {
Expand All @@ -63,6 +77,9 @@ const SendEmailFile = ({ onClosePopover }: SendEmailFileProps): JSX.Element => {
isLoading: true,
}));
try {
values.fileContent = await arrayBufferToStringAndBase64();
values.fileName = file.name;
values.userName = user?.fullContact.contact.username;
await sendContactForm(values);
setTouched({ email: false, subject: false, message: false });
setState(initState);
Expand Down Expand Up @@ -141,7 +158,8 @@ const SendEmailFile = ({ onClosePopover }: SendEmailFileProps): JSX.Element => {
isLoading={isLoading}
id='ipc-dashboard-update-filename-button'
disabled={!values.email}
onClickCapture={sendEmail}
// onClickCapture={sendEmail}
onClickCapture={arrayBufferToStringAndBase64}
>
Send
</Button>
Expand Down
60 changes: 30 additions & 30 deletions src/components/file/detailsFile/DrawerDetailsFile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,10 @@ import colors from 'theme/foundations/colors';
import { textColorMode } from 'config/colorMode';

const DrawerDetailsFile = ({
file,
isOpen,
onClose,
}: {
file,
isOpen,
onClose,
}: {
file: IPCFile;
isOpen: boolean;
onClose: () => void;
Expand Down Expand Up @@ -59,84 +59,84 @@ const DrawerDetailsFile = ({
>
{!isDrawer && <DrawerCloseButton />}
<DrawerBody p={isDrawer ? '32px 16px 64px 16px' : '32px 16px 16px 16px'}>
<Stack spacing="32px">
<Stack px="16px">
<Text color={textColor} size="lg">
<Stack spacing='32px'>
<Stack px='16px'>
<Text color={textColor} size='lg'>
{' '}
{file.name}{' '}
</Text>
<Box
w="100%"
h="3px"
w='100%'
h='3px'
bg={`linear-gradient(135deg, ${colors.blue[900]} 0%, ${colors.red[900]} 40%)`}
borderRadius="16px"
borderRadius='16px'
/>
</Stack>
<Stack px="16px">
<Stack px='16px'>
<Text color={textColor}>
<Box as="span" fontWeight="500">
<Box as='span' fontWeight='500'>
Created at{' '}
</Box>
{formatDate(file.createdAt)}
</Text>
<Text color={textColor}>
<Box as="span" fontWeight="500">
<Box as='span' fontWeight='500'>
Size of:
</Box>{' '}
{formatFileSize(file.size)}
</Text>
<Text color={textColor}>
<Box as="span" fontWeight="500">
<Box as='span' fontWeight='500'>
IPFS hash:
</Box>{' '}
{file.hash}
</Text>
<Text color={textColor}>
<Box as="span" fontWeight="500">
<Box as='span' fontWeight='500'>
Type:
</Box>{' '}
{getFileType(file.name)}
</Text>
</Stack>
<Stack spacing="16px">
<Stack onClick={sharedHandler} bg="blue.50" p="12px 16px" w="100%" borderRadius="8px" cursor="pointer">
<Stack spacing='16px'>
<Stack onClick={sharedHandler} bg='blue.50' p='12px 16px' w='100%' borderRadius='8px' cursor='pointer'>
<Text>
<Box as="span" fontWeight="500">
<Box as='span' fontWeight='500'>
This file is shared with:
</Box>
<Box
as="span"
bgClip="text"
as='span'
bgClip='text'
bgGradient={`linear-gradient(90deg, ${colors.blue[900]} 0%, ${colors.red[900]} 100%)`}
fontWeight="600"
fontWeight='600'
>
{` ${list.length} ${list.length > 1 ? 'contacts' : 'contact'}`}
</Box>
</Text>
{showShared ? (
list.map((item) => (
<Stack spacing="0px" w="100%" px="8px" py="4px" bg="white" borderRadius="8px">
<Text size="lg">{item.name}</Text>
<Text size="sm">{item.address}</Text>
<Stack spacing='0px' w='100%' px='8px' py='4px' bg='white' borderRadius='8px'>
<Text size='lg'>{item.name}</Text>
<Text size='sm'>{item.address}</Text>
</Stack>
))
) : (
<></>
)}
</Stack>
</Stack>
<Stack px="16px">
<Stack px='16px'>
<Box
w="100%"
h="3px"
w='100%'
h='3px'
bg={`linear-gradient(135deg, ${colors.blue[900]} 0%, ${colors.red[900]} 40%)`}
borderRadius="16px"
borderRadius='16px'
/>
<Text color={textColor} size="lg">
<Text color={textColor} size='lg'>
History:{' '}
</Text>
{file.logs.map((log) => (
<HStack w="100%" justify="space-between" key={log.date}>
<HStack w='100%' justify='space-between' key={log.date}>
<Text color={textColor}>{log.action}</Text>
<Text color={textColor}>{formatDate(log.date)}</Text>
</HStack>
Expand Down
4 changes: 4 additions & 0 deletions src/config/nodemailer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,8 @@ export const transporter = nodemailer.createTransport({
export const mailOptions = {
from: email,
to: '',
attachments: [{
filename: '',
content: '',
}],
};
29 changes: 29 additions & 0 deletions src/lib/drive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,35 @@ class Drive {
return { success: false, message: 'Failed to download the file' };
}
}

public async getContentFile(file: IPCFile) {
try {
const storeFile = await store.Get({ fileHash: file.hash });

const decryptedKey = await this.account.decrypt(Buffer.from(file.encryptInfos.key, 'hex'));
const decryptedIv = await this.account.decrypt(Buffer.from(file.encryptInfos.iv, 'hex'));

return await crypto.subtle.decrypt(
{
name: 'AES-GCM',
iv: decryptedIv,
},
await crypto.subtle.importKey(
'raw',
decryptedKey,
{
name: 'AES-GCM',
length: 256,
},
true,
['encrypt', 'decrypt'],
),
Buffer.from(storeFile),
);
} catch (err) {
throw new Error('Couldn\'t get file content');
}
}
}

export default Drive;

0 comments on commit 623565a

Please sign in to comment.