diff --git a/pages/api/email.ts b/pages/api/email.ts
index 2b060688..bf5d57af 100644
--- a/pages/api/email.ts
+++ b/pages/api/email.ts
@@ -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 } => {
@@ -20,11 +26,52 @@ const generateEmailContent = (data: BodyEmail): { text: string, html: string } =
}
return str;
}, '');
- const htmlData = Object.entries(data).reduce((str, [key, val]) => (`${str}
${val}
`), '');
return {
text: stringData,
- html: ` New Contact Message ${htmlData} | | | |
`,
+ html: `
+
+
+
+
+ Email Template
+
+
+
+
+
Hello,
+
${data.userName} sent you
+ ${data.message ? 'the following message:' : 'a file without a message.'}
+
+ ${data.message ? `
${data.message}
` : ''}
+
+
+
+`,
};
};
@@ -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),
diff --git a/src/components/file/SendEmailFile.tsx b/src/components/file/SendEmailFile.tsx
index a606ab7d..96688f31 100644
--- a/src/components/file/SendEmailFile.tsx
+++ b/src/components/file/SendEmailFile.tsx
@@ -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;
@@ -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();
@@ -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 () => {
@@ -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);
@@ -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
diff --git a/src/components/file/detailsFile/DrawerDetailsFile.tsx b/src/components/file/detailsFile/DrawerDetailsFile.tsx
index d495d891..7c9f1c36 100644
--- a/src/components/file/detailsFile/DrawerDetailsFile.tsx
+++ b/src/components/file/detailsFile/DrawerDetailsFile.tsx
@@ -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;
@@ -59,65 +59,65 @@ const DrawerDetailsFile = ({
>
{!isDrawer && }
-
-
-
+
+
+
{' '}
{file.name}{' '}
-
+
-
+
Created at{' '}
{formatDate(file.createdAt)}
-
+
Size of:
{' '}
{formatFileSize(file.size)}
-
+
IPFS hash:
{' '}
{file.hash}
-
+
Type:
{' '}
{getFileType(file.name)}
-
-
+
+
-
+
This file is shared with:
{` ${list.length} ${list.length > 1 ? 'contacts' : 'contact'}`}
{showShared ? (
list.map((item) => (
-
- {item.name}
- {item.address}
+
+ {item.name}
+ {item.address}
))
) : (
@@ -125,18 +125,18 @@ const DrawerDetailsFile = ({
)}
-
+
-
+
History:{' '}
{file.logs.map((log) => (
-
+
{log.action}
{formatDate(log.date)}
diff --git a/src/config/nodemailer.ts b/src/config/nodemailer.ts
index 43f9ef30..25ee12d7 100644
--- a/src/config/nodemailer.ts
+++ b/src/config/nodemailer.ts
@@ -14,4 +14,8 @@ export const transporter = nodemailer.createTransport({
export const mailOptions = {
from: email,
to: '',
+ attachments: [{
+ filename: '',
+ content: '',
+ }],
};
diff --git a/src/lib/drive.ts b/src/lib/drive.ts
index d250f4f9..0ade50cc 100644
--- a/src/lib/drive.ts
+++ b/src/lib/drive.ts
@@ -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;