Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/mail/app/(routes)/mail/[folder]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ export default function MailPage() {
if (isLoading) {
return (
<div className="flex h-screen w-full items-center justify-center">
<Loader2 className="text-primary h-8 w-8 animate-spin" />
<Loader2 className="text-primary h-8 w-8 animate-spin-fast" />
</div>
);
}
Expand Down
4 changes: 4 additions & 0 deletions apps/mail/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,10 @@
animation: blink 0.8s ease-in-out infinite;
}

.animate-spin-fast {
animation: spin 0.5s linear infinite;
}

.tiptap p {
margin-bottom: 0.3rem;
}
Expand Down
2 changes: 1 addition & 1 deletion apps/mail/app/root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ export function Layout({ children }: PropsWithChildren) {
export function HydrateFallback() {
return (
<div className="flex h-screen w-full items-center justify-center">
<Loader2 className="h-10 w-10 animate-spin" />
<Loader2 className="h-10 w-10 animate-spin-fast" />
</div>
);
}
Expand Down
191 changes: 93 additions & 98 deletions apps/mail/components/mail/mail-display.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -560,57 +560,57 @@ const downloadAttachment = (attachment: { body: string; mimeType: string; filena

const handleDownloadAllAttachments =
(subject: string, attachments: { body: string; mimeType: string; filename: string }[]) =>
async () => {
if (!attachments.length) return;

const JSZip = (await import('jszip')).default;
const zip = new JSZip();

console.log('attachments', attachments);
attachments.forEach((attachment) => {
try {
const byteCharacters = atob(attachment.body);
const byteNumbers = new Array(byteCharacters.length);
for (let i = 0; i < byteCharacters.length; i++) {
byteNumbers[i] = byteCharacters.charCodeAt(i);
async () => {
if (!attachments.length) return;

const JSZip = (await import('jszip')).default;
const zip = new JSZip();

console.log('attachments', attachments);
attachments.forEach((attachment) => {
try {
const byteCharacters = atob(attachment.body);
const byteNumbers = new Array(byteCharacters.length);
for (let i = 0; i < byteCharacters.length; i++) {
byteNumbers[i] = byteCharacters.charCodeAt(i);
}
const byteArray = new Uint8Array(byteNumbers);

zip.file(attachment.filename, byteArray, {
binary: true,
date: new Date(),
unixPermissions: 0o644,
});
} catch (error) {
console.error(`Error adding ${attachment.filename} to zip:`, error);
}
const byteArray = new Uint8Array(byteNumbers);
});

zip.file(attachment.filename, byteArray, {
binary: true,
date: new Date(),
unixPermissions: 0o644,
// Generate and download the zip file
zip
.generateAsync({
type: 'blob',
compression: 'DEFLATE',
compressionOptions: {
level: 9,
},
})
.then((content) => {
const url = window.URL.createObjectURL(content);
const link = document.createElement('a');
link.href = url;
link.download = `attachments-${subject || 'email'}.zip`;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
window.URL.revokeObjectURL(url);
})
.catch((error) => {
console.error('Error generating zip file:', error);
});
} catch (error) {
console.error(`Error adding ${attachment.filename} to zip:`, error);
}
});

// Generate and download the zip file
zip
.generateAsync({
type: 'blob',
compression: 'DEFLATE',
compressionOptions: {
level: 9,
},
})
.then((content) => {
const url = window.URL.createObjectURL(content);
const link = document.createElement('a');
link.href = url;
link.download = `attachments-${subject || 'email'}.zip`;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
window.URL.revokeObjectURL(url);
})
.catch((error) => {
console.error('Error generating zip file:', error);
});

console.log('downloaded', subject, attachments);
};
console.log('downloaded', subject, attachments);
};

const openAttachment = (attachment: { body: string; mimeType: string; filename: string }) => {
try {
Expand Down Expand Up @@ -706,13 +706,13 @@ const MoreAboutPerson = ({
</DialogHeader>
<div className="mt-4 flex justify-center">
{isPending ? (
<Loader2 className="animate-spin" />
<Loader2 className="animate-spin-fast" />
) : data ? (
<StreamingText text={replaceSourcesInText(data.text)} />
) : error ? (
<p>Error: {error.message}</p>
) : (
<Loader2 className="animate-spin" />
<Loader2 className="animate-spin-fast" />
)}
</div>
</DialogContent>
Expand Down Expand Up @@ -779,13 +779,13 @@ const MoreAboutQuery = ({
</DialogHeader>
<div className="mt-4 flex justify-center">
{isPending ? (
<Loader2 className="animate-spin" />
<Loader2 className="animate-spin-fast" />
) : data ? (
<StreamingText text={replaceSourcesInText(data.text)} />
) : error ? (
<p>Error: {error.message}</p>
) : (
<Loader2 className="animate-spin" />
<Loader2 className="animate-spin-fast" />
)}
</div>
</DialogContent>
Expand Down Expand Up @@ -1130,17 +1130,16 @@ const MailDisplay = ({ emailData, index, totalEmails, demo, threadAttachments }:
<div class="email-header">
<h1 class="email-title">${emailData.subject || 'No Subject'}</h1>

${
emailData?.tags && emailData.tags.length > 0
? `
${emailData?.tags && emailData.tags.length > 0
? `
<div class="labels-section">
${emailData.tags
.map((tag) => `<span class="label-badge">${tag.name}</span>`)
.join('')}
.map((tag) => `<span class="label-badge">${tag.name}</span>`)
.join('')}
</div>
`
: ''
}
: ''
}

<div class="email-meta">
<div class="meta-row">
Expand All @@ -1151,59 +1150,56 @@ const MailDisplay = ({ emailData, index, totalEmails, demo, threadAttachments }:
</span>
</div>

${
emailData.to && emailData.to.length > 0
? `
${emailData.to && emailData.to.length > 0
? `
<div class="meta-row">
<span class="meta-label">To:</span>
<span class="meta-value">
${emailData.to
.map(
(recipient) =>
`${cleanNameDisplay(recipient.name)} &lt;${recipient.email}&gt;`,
)
.join(', ')}
.map(
(recipient) =>
`${cleanNameDisplay(recipient.name)} &lt;${recipient.email}&gt;`,
)
.join(', ')}
</span>
</div>
`
: ''
}
: ''
}

${
emailData.cc && emailData.cc.length > 0
? `
${emailData.cc && emailData.cc.length > 0
? `
<div class="meta-row">
<span class="meta-label">CC:</span>
<span class="meta-value">
${emailData.cc
.map(
(recipient) =>
`${cleanNameDisplay(recipient.name)} &lt;${recipient.email}&gt;`,
)
.join(', ')}
.map(
(recipient) =>
`${cleanNameDisplay(recipient.name)} &lt;${recipient.email}&gt;`,
)
.join(', ')}
</span>
</div>
`
: ''
}
: ''
}

${
emailData.bcc && emailData.bcc.length > 0
? `
${emailData.bcc && emailData.bcc.length > 0
? `
<div class="meta-row">
<span class="meta-label">BCC:</span>
<span class="meta-value">
${emailData.bcc
.map(
(recipient) =>
`${cleanNameDisplay(recipient.name)} &lt;${recipient.email}&gt;`,
)
.join(', ')}
.map(
(recipient) =>
`${cleanNameDisplay(recipient.name)} &lt;${recipient.email}&gt;`,
)
.join(', ')}
</span>
</div>
`
: ''
}
: ''
}

<div class="meta-row">
<span class="meta-label">Date:</span>
Expand All @@ -1222,25 +1218,24 @@ const MailDisplay = ({ emailData, index, totalEmails, demo, threadAttachments }:
</div>

<!-- Attachments -->
${
emailData.attachments && emailData.attachments.length > 0
? `
${emailData.attachments && emailData.attachments.length > 0
? `
<div class="attachments-section">
<h2 class="attachments-title">Attachments (${emailData.attachments.length})</h2>
${emailData.attachments
.map(
(attachment, index) => `
.map(
(attachment, index) => `
<div class="attachment-item">
<span class="attachment-name">${attachment.filename}</span>
${formatFileSize(attachment.size) ? ` - <span class="attachment-size">${formatFileSize(attachment.size)}</span>` : ''}
</div>
`,
)
.join('')}
)
.join('')}
</div>
`
: ''
}
: ''
}
</div>
</body>
</html>
Expand Down Expand Up @@ -1551,7 +1546,7 @@ const MailDisplay = ({ emailData, index, totalEmails, demo, threadAttachments }:
</span>
<span className="text-muted-foreground ml-3 text-nowrap">
{emailData?.receivedOn &&
!isNaN(new Date(emailData.receivedOn).getTime())
!isNaN(new Date(emailData.receivedOn).getTime())
? format(new Date(emailData.receivedOn), 'PPpp')
: ''}
</span>
Expand Down
2 changes: 1 addition & 1 deletion apps/mail/components/ui/toast.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const Toaster = () => {
error: <ExclamationCircle2 className="h-4.5 w-4.5 fill-[#FF0000]" />,
warning: <ExclamationTriangle className="h-4.5 w-4.5 fill-[#FFC107]" />,
info: <InfoCircle className="h-4.5 w-4.5 fill-[#5767fb]" />,
loading: <Loader2 className="stroke-muted-foreground h-[17px] w-[17px] animate-spin" />,
loading: <Loader2 className="stroke-muted-foreground h-[17px] w-[17px] animate-spin-fast" />,
}}
toastOptions={{
classNames: {
Expand Down
2 changes: 1 addition & 1 deletion apps/mail/components/voice-button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export function VoiceButton() {
<div className="dark:bg[#141414] flex h-7 items-center justify-center rounded-sm bg-[#262626] px-2">
{isInitializing && (
<div className="flex items-center justify-center gap-2">
<Loader2 className="h-4 w-4 animate-spin" />
<Loader2 className="h-4 w-4 animate-spin-fast" />
</div>
)}
{!isInitializing &&
Expand Down