Skip to content
Merged
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
11 changes: 0 additions & 11 deletions apps/mail/app/(routes)/mail/page.tsx

This file was deleted.

3 changes: 3 additions & 0 deletions apps/mail/app/api/driver/google.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,9 @@ export const driver = async (config: IConfig): Promise<MailManager> => {
if (folder === 'trash') {
return { folder: undefined, q: `in:trash ${q}` };
}
if (folder === "archive") {
return { folder: undefined, q: `in:archive ${q}` };
}
return { folder, q };
};
const gmail = google.gmail({ version: 'v1', auth });
Expand Down
203 changes: 92 additions & 111 deletions apps/mail/components/context/thread-context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,15 @@ import {
Star,
Trash,
} from 'lucide-react';
import { moveThreadsTo, ThreadDestination } from '@/lib/thread-actions';
import { useSearchValue } from '@/hooks/use-search-value';
import { useParams, useRouter } from 'next/navigation';
import { useThreads } from '@/hooks/use-threads';
import { modifyLabels } from '@/actions/mail';
import { LABELS, FOLDERS } from '@/lib/utils';
import { useStats } from '@/hooks/use-stats';
import { useParams } from 'next/navigation';
import { useMail } from '../mail/use-mail';
import { useTranslations } from 'use-intl'; // Keep this if localization is needed
import { useTranslations } from 'use-intl';
import { type ReactNode } from 'react';
import { LABELS } from '@/lib/utils';
import { toast } from 'sonner';

interface EmailAction {
id: string;
Expand All @@ -45,13 +44,13 @@ interface EmailAction {
}

interface EmailContextMenuProps {
children: React.ReactNode;
emailId: string;
threadId?: string;
isInbox?: boolean;
isSpam?: boolean;
isSent?: boolean;
refreshCallback?: () => void;
children: ReactNode;
emailId: string;
threadId?: string;
isInbox?: boolean;
isSpam?: boolean;
isSent?: boolean;
refreshCallback?: () => void;
}

export function ThreadContextMenu({
Expand All @@ -64,90 +63,72 @@ export function ThreadContextMenu({
refreshCallback,
}: EmailContextMenuProps) {
const { folder } = useParams<{ folder: string }>();
const [searchValue] = useSearchValue();
const [mail, setMail] = useMail();
const { mutate } = useThreads(folder, undefined, searchValue.value, 20);
const { mutate } = useThreads();
const currentFolder = folder ?? '';
const isArchiveFolder = currentFolder === 'archive';
const isArchiveFolder = currentFolder === FOLDERS.ARCHIVE;
const { mutate: mutateStats } = useStats();
const t = useTranslations();

const noopAction = () => async () => {
console.log('Action will be implemented later');
};
const noopAction = () => async () => {
console.log('Action will be implemented later');
};

// const handleArchive = () => async () => {
// try {
// const targetId = threadId ? `thread:${threadId}` : emailId;
// console.log(`🗃️ EmailContextMenu: Archiving ${threadId ? 'thread' : 'email'}: ${targetId}`);
const handleMove = (from: string, to: string) => async () => {
try {
let targets = [];
if (mail.bulkSelected.length) {
targets = mail.bulkSelected.map((id) => `thread:${id}`);
} else {
targets = [threadId ? `thread:${threadId}` : emailId];
}

// const success = await archiveMail(targetId);
// if (success) {
// toast.success(`${threadId ? 'Email' : 'Thread'} archived`);
// if (refreshCallback) refreshCallback();
// } else {
// throw new Error(`Failed to archive ${threadId ? 'email' : 'thread'}`);
// }
// } catch (error) {
// console.error(`Error archiving ${threadId ? 'email' : 'thread'}:`, error);
// toast.error(`Error archiving ${threadId ? 'email' : 'thread'}`);
// }
// };
let destination: ThreadDestination = null;
if (to === LABELS.INBOX) destination = FOLDERS.INBOX;
else if (to === LABELS.SPAM) destination = FOLDERS.SPAM;
else if (from && !to) destination = FOLDERS.ARCHIVE;

const handleMove = (from: string, to: string) => async () => {
try {
let targets = [];
if (mail.bulkSelected.length) {
targets = mail.bulkSelected.map((id) => `thread:${id}`);
} else {
targets = [threadId ? `thread:${threadId}` : emailId];
}
return toast.promise(
modifyLabels({
threadId: targets,
addLabels: to ? [to] : [],
removeLabels: from ? [from] : [],
}).then(async () => {
await mutate().then(() => mutateStats());
return setMail({ ...mail, bulkSelected: [] });
}),
{
loading: t('common.mail.moving'),
success: () => t('common.mail.moved'),
error: t('common.mail.errorMoving'),
},
);
} catch (error) {
console.error(`Error moving ${threadId ? 'email' : 'thread'}`, error);
}
};
return moveThreadsTo({
threadIds: targets,
currentFolder: currentFolder,
destination,
onSuccess: async () => {
await mutate();
await mutateStats();
setMail({ ...mail, bulkSelected: [] });
},
});
} catch (error) {
console.error(`Error moving ${threadId ? 'email' : 'thread'}`, error);
}
};

const primaryActions: EmailAction[] = [
{
id: 'reply',
label: 'Reply',
icon: <Reply className="mr-2.5 h-4 w-4" />,
shortcut: 'R',
action: noopAction,
disabled: true, // TODO: Reply functionality to be implemented
},
{
id: 'reply-all',
label: 'Reply All',
icon: <ReplyAll className="mr-2.5 h-4 w-4" />,
shortcut: '⇧R',
action: noopAction,
disabled: true, // TODO: Reply All functionality to be implemented
},
{
id: 'forward',
label: 'Forward',
icon: <Forward className="mr-2.5 h-4 w-4" />,
shortcut: 'F',
action: noopAction,
disabled: true, // TODO: Forward functionality to be implemented
},
];
const primaryActions: EmailAction[] = [
{
id: 'reply',
label: t('common.mail.reply'),
icon: <Reply className="mr-2.5 h-4 w-4" />,
shortcut: 'R',
action: noopAction,
disabled: true, // TODO: Reply functionality to be implemented
},
{
id: 'reply-all',
label: t('common.mail.replyAll'),
icon: <ReplyAll className="mr-2.5 h-4 w-4" />,
shortcut: '⇧R',
action: noopAction,
disabled: true, // TODO: Reply All functionality to be implemented
},
{
id: 'forward',
label: t('common.mail.forward'),
icon: <Forward className="mr-2.5 h-4 w-4" />,
shortcut: 'F',
action: noopAction,
disabled: true, // TODO: Forward functionality to be implemented
},
];

const getActions = () => {
if (isSpam) {
Expand Down Expand Up @@ -257,11 +238,11 @@ export function ThreadContextMenu({
);
};

return (
<ContextMenu>
<ContextMenuTrigger className="w-full">{children}</ContextMenuTrigger>
<ContextMenuContent className="w-56">
{primaryActions.map(renderAction)}
return (
<ContextMenu>
<ContextMenuTrigger className="w-full">{children}</ContextMenuTrigger>
<ContextMenuContent className="w-56" onContextMenu={(e) => e.preventDefault()}>
{primaryActions.map(renderAction)}

<ContextMenuSeparator />

Expand All @@ -274,23 +255,23 @@ export function ThreadContextMenu({

<ContextMenuSeparator />

<ContextMenuSub>
<ContextMenuSubTrigger className="font-normal">
<Tag className="mr-2.5 h-4 w-4" />
Labels
</ContextMenuSubTrigger>
<ContextMenuSubContent className="w-48">
<ContextMenuItem className="font-normal">
<MailPlus className="mr-2.5 h-4 w-4" />
Create New Label
</ContextMenuItem>
<ContextMenuSeparator />
<ContextMenuItem disabled className="text-muted-foreground italic">
No labels available
</ContextMenuItem>
</ContextMenuSubContent>
</ContextMenuSub>
</ContextMenuContent>
</ContextMenu>
);
<ContextMenuSub>
<ContextMenuSubTrigger className="font-normal">
<Tag className="mr-2.5 h-4 w-4" />
{t('common.mail.labels')}
</ContextMenuSubTrigger>
<ContextMenuSubContent className="w-48">
<ContextMenuItem className="font-normal">
<MailPlus className="mr-2.5 h-4 w-4" />
{t('common.mail.createNewLabel')}
</ContextMenuItem>
<ContextMenuSeparator />
<ContextMenuItem disabled className="text-muted-foreground italic">
{t('common.mail.noLabelsAvailable')}
</ContextMenuItem>
</ContextMenuSubContent>
</ContextMenuSub>
</ContextMenuContent>
</ContextMenu>
);
}
21 changes: 1 addition & 20 deletions apps/mail/components/draft/drafts-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,26 +16,7 @@ import { useSession } from '@/lib/auth-client';
import { useRouter } from 'next/navigation';
import { useTranslations } from 'use-intl';
import { toast } from 'sonner';

const highlightText = (text: string, highlight: string) => {
if (!highlight?.trim()) return text;

const regex = new RegExp(`(${highlight})`, 'gi');
const parts = text.split(regex);

return parts.map((part, i) => {
return i % 2 === 1 ? (
<span
key={i}
className="ring-0.5 bg-primary/10 inline-flex items-center justify-center rounded px-1"
>
{part}
</span>
) : (
part
);
});
};
import { highlightText } from '@/lib/email-utils.client';

const Draft = ({ message, onClick }: ThreadProps) => {
const [mail] = useMail();
Expand Down
Loading