diff --git a/apps/mail/actions/settings.ts b/apps/mail/actions/settings.ts
index 7222256c00..2d3aaa2ac6 100644
--- a/apps/mail/actions/settings.ts
+++ b/apps/mail/actions/settings.ts
@@ -22,7 +22,12 @@ function validateSettings(settings: unknown): UserSettings {
export async function saveUserSettings(settings: UserSettings) {
try {
const userId = await getAuthenticatedUserId();
+ console.log(settings, 'before');
+
settings = validateSettings(settings);
+
+ console.log(settings, 'after');
+
const timestamp = new Date();
const [existingSettings] = await db
diff --git a/apps/mail/app/api/driver/google.ts b/apps/mail/app/api/driver/google.ts
index 63b4050250..73d39d00cb 100644
--- a/apps/mail/app/api/driver/google.ts
+++ b/apps/mail/app/api/driver/google.ts
@@ -268,10 +268,20 @@ export const driver = async (config: IConfig): Promise => {
}
return false;
})
- .map((recipient) => ({
- name: recipient.name || '',
- addr: recipient.email,
- }));
+ .map((recipient) => {
+ // Parse the email address from the recipient string
+ const emailMatch = recipient.email.match(/<([^>]+)>/);
+ const email = emailMatch ? emailMatch[1] : recipient.email;
+ // Ensure we have a valid email address
+ if (!email) {
+ console.error('Debug - Invalid email address:', recipient.email);
+ throw new Error('Invalid email address');
+ }
+ return {
+ name: recipient.name || '',
+ addr: email,
+ };
+ });
console.log('Debug - Filtered to recipients:', JSON.stringify(toRecipients, null, 2));
diff --git a/apps/mail/components/mail/mail-iframe.tsx b/apps/mail/components/mail/mail-iframe.tsx
index 2f4261660b..7875a57883 100644
--- a/apps/mail/components/mail/mail-iframe.tsx
+++ b/apps/mail/components/mail/mail-iframe.tsx
@@ -6,14 +6,14 @@ import { getBrowserTimezone } from '@/lib/timezones';
import { useSettings } from '@/hooks/use-settings';
import { useTranslations } from 'next-intl';
import { useTheme } from 'next-themes';
+import DOMPurify from 'dompurify';
import { cn } from '@/lib/utils';
import { toast } from 'sonner';
-import DOMPurify from 'dompurify';
export function MailIframe({ html, senderEmail }: { html: string; senderEmail: string }) {
const { settings, mutate } = useSettings();
const isTrustedSender = settings?.trustedSenders?.includes(senderEmail);
- const [cspViolation, setCspViolation] = useState(false)
+ const [cspViolation, setCspViolation] = useState(false);
const [imagesEnabled, setImagesEnabled] = useState(
isTrustedSender || settings?.externalImages || false,
);
@@ -21,27 +21,32 @@ export function MailIframe({ html, senderEmail }: { html: string; senderEmail: s
const [height, setHeight] = useState(300);
const { resolvedTheme } = useTheme();
- const onTrustSender = useCallback(async (senderEmail: string) => {
- setImagesEnabled(true);
+ const onTrustSender = useCallback(
+ async (senderEmail: string) => {
+ setImagesEnabled(true);
- const existingSettings = settings ?? {
- ...defaultUserSettings,
- timezone: getBrowserTimezone(),
- };
+ console.log(settings);
- const { success } = await saveUserSettings({
- ...existingSettings,
- trustedSenders: settings?.trustedSenders
- ? settings.trustedSenders.concat(senderEmail)
- : [senderEmail],
- });
+ const existingSettings = settings ?? {
+ ...defaultUserSettings,
+ timezone: getBrowserTimezone(),
+ };
- if (!success) {
- toast.error('Failed to trust sender');
- } else {
- mutate();
- }
- }, [settings, mutate]);
+ const { success } = await saveUserSettings({
+ ...existingSettings,
+ trustedSenders: settings?.trustedSenders
+ ? settings.trustedSenders.concat(senderEmail)
+ : [senderEmail],
+ });
+
+ if (!success) {
+ toast.error('Failed to trust sender');
+ } else {
+ mutate();
+ }
+ },
+ [settings, mutate],
+ );
const iframeDoc = useMemo(() => template(html, imagesEnabled), [html, imagesEnabled]);
@@ -95,7 +100,7 @@ export function MailIframe({ html, senderEmail }: { html: string; senderEmail: s
'message',
(event) => {
if (event.data.type === 'csp-violation') {
- setCspViolation(true)
+ setCspViolation(true);
}
},
{ signal: ctrl.signal },
@@ -254,7 +259,7 @@ export function DynamicIframe({
const parentStyle = window.getComputedStyle(iframe.parentElement || document.body);
const parentColor = parentStyle.color;
const parentBg = parentStyle.backgroundColor;
-
+
const styleElement = iframeDoc.createElement('style');
styleElement.textContent = `
body {
@@ -265,21 +270,21 @@ export function DynamicIframe({
iframeDoc.head.appendChild(styleElement);
}
};
-
+
updateStyles();
// Size adjustment
const resizeIframe = () => {
if (!iframe.contentWindow || !iframeDoc.body) return;
-
+
const newHeight = iframeDoc.body.scrollHeight;
const newWidth = iframeDoc.body.scrollWidth;
-
+
if (newHeight !== height) {
setHeight(newHeight);
iframe.style.height = `${newHeight}px`;
}
-
+
if (newWidth !== width && newWidth > iframe.clientWidth) {
setWidth(newWidth);
}
@@ -289,12 +294,12 @@ export function DynamicIframe({
const resizeObserver = new ResizeObserver(() => {
resizeIframe();
});
-
+
resizeObserver.observe(iframeDoc.body);
-
+
// Additional event listeners for images loading
const images = iframeDoc.querySelectorAll('img');
- images.forEach(img => {
+ images.forEach((img) => {
img.addEventListener('load', resizeIframe);
img.addEventListener('error', (e) => {
console.error('Image failed to load:', e);
@@ -304,7 +309,7 @@ export function DynamicIframe({
// Handle link clicks to open in new tab
const links = iframeDoc.querySelectorAll('a');
- links.forEach(link => {
+ links.forEach((link) => {
link.setAttribute('target', '_blank');
link.setAttribute('rel', 'noopener noreferrer');
});
@@ -316,7 +321,7 @@ export function DynamicIframe({
// Cleanup
return () => {
resizeObserver.disconnect();
- images.forEach(img => {
+ images.forEach((img) => {
img.removeEventListener('load', resizeIframe);
img.removeEventListener('error', resizeIframe);
});
@@ -333,4 +338,4 @@ export function DynamicIframe({
{...props}
/>
);
-}
\ No newline at end of file
+}
diff --git a/apps/mail/components/mail/mail-list.tsx b/apps/mail/components/mail/mail-list.tsx
index fb909968f3..6dbadcaaa1 100644
--- a/apps/mail/components/mail/mail-list.tsx
+++ b/apps/mail/components/mail/mail-list.tsx
@@ -220,18 +220,22 @@ const Thread = memo(
) : null}
- {Math.random() > 0.5 ? (
-
-
-
- {Math.random() * 10}
-
-
-
- {t('common.mail.replies', { count: Math.random() * 10 })}
-
-
- ) : null}
+ {Math.random() > 0.5 &&
+ (() => {
+ const count = Math.floor(Math.random() * 10) + 1;
+ return (
+
+
+
+ {count}
+
+
+
+ {t('common.mail.replies', { count })}
+
+
+ );
+ })()}
{latestMessage.receivedOn ? (
{
if (!emailData || !id) return;
const unreadEmails = emailData.messages.filter((e) => e.unread);
- if (unreadEmails.length === 0) {
- console.log('Marking email as read:', id);
- markAsRead({ ids: [id] })
- .catch((error) => {
- console.error('Failed to mark email as read:', error);
- toast.error(t('common.mail.failedToMarkAsRead'));
- })
- .then(() => Promise.all([mutateThread(), mutateStats()]));
- } else {
- console.log('Marking email as read:', id, ...unreadEmails.map((e) => e.id));
+ console.log({
+ totalReplies: emailData.totalReplies,
+ unreadEmails: unreadEmails.length,
+ });
+ if (unreadEmails.length > 0) {
const ids = [id, ...unreadEmails.map((e) => e.id)];
markAsRead({ ids })
.catch((error) => {
console.error('Failed to mark email as read:', error);
toast.error(t('common.mail.failedToMarkAsRead'));
})
- .then(() => Promise.all([mutateThread(), mutateStats()]));
+ .then(() => Promise.allSettled([mutateThread(), mutateStats()]));
}
}, [emailData, id]);
@@ -232,7 +227,7 @@ export function ThreadDisplay({ isMobile, id }: ThreadDisplayProps) {
if (!result.success) throw new Error('Failed to mark as unread');
setMail((prev) => ({ ...prev, bulkSelected: [] }));
- await Promise.all([mutateStats(), mutateThread()]);
+ await Promise.allSettled([mutateStats(), mutateThread()]);
handleClose();
};
diff --git a/apps/mail/components/ui/nav-main.tsx b/apps/mail/components/ui/nav-main.tsx
index ed3fb7a347..5d1937bacf 100644
--- a/apps/mail/components/ui/nav-main.tsx
+++ b/apps/mail/components/ui/nav-main.tsx
@@ -11,7 +11,10 @@ import { Collapsible, CollapsibleTrigger } from '@/components/ui/collapsible';
import { usePathname, useSearchParams } from 'next/navigation';
import { clearBulkSelectionAtom } from '../mail/use-mail';
import { type MessageKey } from '@/config/navigation';
+import { type NavItem } from '@/config/navigation';
+import { useSession } from '@/lib/auth-client';
import { Badge } from '@/components/ui/badge';
+import { GoldenTicketModal } from '../golden';
import { useStats } from '@/hooks/use-stats';
import { useTranslations } from 'next-intl';
import { useRef, useCallback } from 'react';
@@ -20,9 +23,6 @@ import { cn } from '@/lib/utils';
import { useAtom } from 'jotai';
import * as React from 'react';
import Link from 'next/link';
-import {type NavItem} from '@/config/navigation'
-import { GoldenTicketModal } from '../golden';
-import { useSession } from '@/lib/auth-client';
interface IconProps extends React.SVGProps {
ref?: React.Ref;
@@ -86,7 +86,7 @@ export function NavMain({ items }: NavMainProps) {
// Handle settings navigation
// if (item.isSettingsButton) {
- // Include current path with category query parameter if present
+ // Include current path with category query parameter if present
// const currentPath = category
// ? `${pathname}?category=${encodeURIComponent(category)}`
// : pathname;
@@ -179,9 +179,9 @@ export function NavMain({ items }: NavMainProps) {
}
function NavItem(item: NavItemProps & { href: string }) {
- const iconRef = useRef(null);
- const { data: stats } = useStats();
- const t = useTranslations();
+ const iconRef = useRef(null);
+ const { data: stats } = useStats();
+ const t = useTranslations();
if (item.disabled) {
return (
@@ -190,7 +190,7 @@ function NavItem(item: NavItemProps & { href: string }) {
className="flex cursor-not-allowed items-center opacity-50"
>
{item.icon && }
- {t(item.title as MessageKey)}
+ {t(item.title as MessageKey)}
);
}
@@ -214,14 +214,16 @@ function NavItem(item: NavItemProps & { href: string }) {
onClick={() => setOpenMobile(false)}
>
{item.icon && }
- {t(item.title as MessageKey)}
- {stats && stats.find((stat) => stat.label?.toLowerCase() === item.id?.toLowerCase()) && (
-
- {stats
- .find((stat) => stat.label?.toLowerCase() === item.id?.toLowerCase())
- ?.count?.toLocaleString() || '0'}
-
- )}
+ {t(item.title as MessageKey)}
+ {stats
+ ? stats.find((stat) => stat.label?.toLowerCase() === item.id?.toLowerCase()) && (
+
+ {stats
+ .find((stat) => stat.label?.toLowerCase() === item.id?.toLowerCase())
+ ?.count?.toLocaleString() || '0'}
+
+ )
+ : null}
);
@@ -232,7 +234,9 @@ function NavItem(item: NavItemProps & { href: string }) {
return (
- {buttonContent}
+
+ {buttonContent}
+
);