Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
1a0ad90
draft fixes:
ahmetskilinc Apr 19, 2025
1622cca
some fixes for saving attachments to draft
ahmetskilinc Apr 19, 2025
eea8827
fix for empty draft loading
ahmetskilinc Apr 19, 2025
3a2860e
fix draft list recipient name/address
ahmetskilinc Apr 19, 2025
397090c
also show 'No Recipient' if empty
ahmetskilinc Apr 19, 2025
cc52f73
remove comments
ahmetskilinc Apr 19, 2025
b66393c
Merge branch 'staging' into fix-some-draft-issues
ahmetskilinc Apr 20, 2025
aaf0678
Merge branch 'staging' into fix-some-draft-issues
ahmetskilinc Apr 21, 2025
0214edb
switch to mimetext for draft saving to keep formatting consistent
ahmetskilinc Apr 21, 2025
3253ec3
add message title to draft list
ahmetskilinc Apr 21, 2025
181f606
Merge branch 'staging' into fix-some-draft-issues
ahmetskilinc Apr 21, 2025
281e215
feat: single api for oauth connections
BlankParticle Apr 21, 2025
de0ea3f
Merge branch 'staging' into fix-some-draft-issues
ahmetskilinc Apr 21, 2025
80e1af9
Merge pull request #723 from Mail-0/fix-some-draft-issues
ahmetskilinc Apr 21, 2025
2763b45
fix: add extra error handling
BlankParticle Apr 21, 2025
3ac1712
Merge branch 'staging' into feat/single-point-of-oauth
ahmetskilinc Apr 21, 2025
37e5190
Merge pull request #741 from BlankParticle/feat/single-point-of-oauth
ahmetskilinc Apr 21, 2025
b3415c3
chore: simplify and fix the dev env
BlankParticle Apr 21, 2025
3364807
Merge branch 'staging' into chore/fix-dev-env
MrgSub Apr 21, 2025
d57570c
Merge pull request #742 from BlankParticle/chore/fix-dev-env
MrgSub Apr 21, 2025
1d37659
Ai generate security (#706)
ripgrim Apr 21, 2025
797a8cd
Add a new Vietnamese translation file to support Vietnamese language …
ncdai Apr 21, 2025
57936a1
Update es.json (#710)
danibaldomir Apr 22, 2025
3b338fb
Update app manifest and add new icons for PWA (#739)
humbernieto Apr 22, 2025
9a75453
feat: allow sending from email aliases added through gmail (#743)
atharvadeosthale Apr 22, 2025
acbf4b6
Refactor IP handling in early-access routes
MrgSub Apr 22, 2025
f59fb33
Add unauthorized error handling in sign out function
MrgSub Apr 22, 2025
156c2fa
Redirect from Home Page on Session (#701)
nikitadrokin Apr 22, 2025
d8065ba
Refactor settings handling and golden ticket logic
MrgSub Apr 22, 2025
f892073
Feat: og:image Generation on /compose route (#730)
ripgrim Apr 22, 2025
9d5ff36
Merge branch 'main' into staging
MrgSub Apr 22, 2025
934e336
Update session check to include user id before redirecting
MrgSub Apr 22, 2025
67a393a
Fix unauthorized error handling in multiple actions
MrgSub Apr 22, 2025
f443556
Enable shortcuts settings in navigation
MrgSub Apr 22, 2025
aac4405
Refactor error handling to return unauthorized gracefully
MrgSub Apr 22, 2025
84fce88
Update Hero component with new imports and link adjustments
MrgSub Apr 22, 2025
0ff71d8
Update redirect URL to use hostname from req object
MrgSub Apr 22, 2025
c504834
Fix redirect URL formatting and add log for missing user ID
MrgSub Apr 22, 2025
46e49d4
Fix error handling in API routes for unauthorized requests
MrgSub Apr 22, 2025
89e730a
Refactor throwUnauthorizedGracefully function for readability
MrgSub Apr 22, 2025
69500a8
Fix error handling in driver routes
MrgSub Apr 22, 2025
f8d0d2e
Handle unauthorized gracefully when getting connections
MrgSub Apr 22, 2025
89af662
Refactor mail actions for better error handling
MrgSub Apr 22, 2025
bd6b50d
Refactor deleteActiveConnection function for readability
MrgSub Apr 22, 2025
d1b1b30
fixed (#752)
ahmetskilinc Apr 22, 2025
2aa5007
Refactor error handling in mail actions to return null or specific er…
MrgSub Apr 23, 2025
b086ebc
Update Google auth provider configuration
MrgSub Apr 23, 2025
b80a60c
Delete connection and update hero text
MrgSub Apr 23, 2025
47d4ffc
Refactor error handling to use StandardizedError class
MrgSub Apr 23, 2025
21f52a0
Refactor error handling for Google API driver
MrgSub Apr 23, 2025
56cd464
Add labels to sidebar, labels settings and useLabels hook (#746)
ahmetskilinc Apr 23, 2025
0474e57
bin count of unread messages
Adarsh9977 Apr 18, 2025
bf38622
dixes drafts not saving persistently
ahmetskilinc Apr 24, 2025
ae40c1f
dont show from field is no aliases
ahmetskilinc Apr 24, 2025
56665d0
limited height of attachment dialog
Adarsh9977 Apr 24, 2025
7b8efca
added delete page
ahmetskilinc Apr 24, 2025
e5edcbe
correct way to delete accounts
ahmetskilinc Apr 24, 2025
00bfe7c
- adds new revokeRefreshToken method to Google driver
ahmetskilinc Apr 24, 2025
29d6be2
Merge branch 'main' into staging
MrgSub Apr 25, 2025
c438279
Add posthog-js dependency and implement label filtering in NavMain co…
MrgSub Apr 25, 2025
c7ddfde
Enhance NavItem component to support onClick event handling
MrgSub Apr 25, 2025
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
24 changes: 24 additions & 0 deletions apps/mail/actions/user.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
'use server';

import { headers } from 'next/headers';
import { auth } from '@/lib/auth';

export async function deleteUser() {
const headersList = await headers();
const session = await auth.api.getSession({ headers: headersList });

if (!session) return { success: false, error: 'Session not found' };

const userId = session.user?.id;

if (!userId) return { success: false, error: 'User not found' };

const { success, message } = await auth.api.deleteUser({
body: {
callbackURL: '/',
},
headers: headersList,
});
Comment on lines +16 to +21
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Verify redirection after account deletion

The implementation uses a root path ('/') as the callbackURL for redirecting after account deletion. Ensure this is the intended behavior and consider whether a more specific landing page or confirmation page would be more appropriate.


🏁 Script executed:

#!/bin/bash
# Check if there's a specific landing page for post-deletion in the codebase

# Look for any references to post-deletion pages or routes
echo "Checking for post-deletion routes or pages:"
rg -i "after.*delet|post.*delet|account.*removed" --type ts --type tsx

Length of output: 197


🏁 Script executed:

#!/bin/bash
# Search for any pages/components related to deletion confirmation
echo "Searching for TS/TSX files with ‘delete’ in their name:"
find . -type f \( -iname "*delete*.ts" -o -iname "*delete*.tsx" \)

echo
echo "Searching for all occurrences of callbackURL:"
rg "callbackURL" -n

Length of output: 618


Ensure post-deletion redirect
The call in apps/mail/actions/user.ts (line 18) uses callbackURL: '/'. Currently there’s no dedicated “account deleted” or confirmation page in the codebase—other flows send users to /settings/connections (email verify) or /mail (login). Please update this to a more appropriate route or create a confirmation page, for example:

  • Create /account/deleted (or /goodbye) and set callbackURL to that.
  • Or ensure the homepage (/) clearly displays a post-deletion message.


return { success, message };
}
2 changes: 2 additions & 0 deletions apps/mail/app/(routes)/settings/[...settings]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import SecurityPage from '../security/page';
import { useParams } from 'next/navigation';
import { useTranslations } from 'next-intl';
import GeneralPage from '../general/page';
import LabelsPage from '../labels/page';

const settingsPages: Record<string, React.ComponentType> = {
general: GeneralPage,
Expand All @@ -16,6 +17,7 @@ const settingsPages: Record<string, React.ComponentType> = {
appearance: AppearancePage,
shortcuts: ShortcutsPage,
notifications: NotificationsPage,
labels: LabelsPage,
};

export default function SettingsPage() {
Expand Down
132 changes: 132 additions & 0 deletions apps/mail/app/(routes)/settings/danger-zone/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
'use client';

import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
DialogTrigger,
} from '@/components/ui/dialog';
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
} from '@/components/ui/form';
import { SettingsCard } from '@/components/settings/settings-card';
import { zodResolver } from '@hookform/resolvers/zod';
import { AlertTriangle, Route } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { useRouter } from 'next/navigation';
import { deleteUser } from '@/actions/user';
import { useTranslations } from 'next-intl';
import { useForm } from 'react-hook-form';
import { useState } from 'react';
import { toast } from 'sonner';
import * as z from 'zod';

const CONFIRMATION_TEXT = 'DELETE';

const formSchema = z.object({
confirmText: z.string().refine((val) => val === CONFIRMATION_TEXT, {
message: `Please type ${CONFIRMATION_TEXT} to confirm`,
}),
});

function DeleteAccountDialog() {
const [isOpen, setIsOpen] = useState(false);
const [isDeleting, setIsDeleting] = useState(false);
const t = useTranslations();
const router = useRouter();

const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
defaultValues: {
confirmText: '' as 'DELETE',
},
});

async function onSubmit(values: z.infer<typeof formSchema>) {
setIsDeleting(true);
try {
const { success, message } = await deleteUser();
if (!success) {
toast.error(message);
return;
}
toast.success('Account deleted successfully');
router.push('/');
setIsOpen(false);
} catch (error) {
console.error('Failed to delete account:', error);
toast.error('Failed to delete account');
} finally {
setIsDeleting(false);
form.reset();
}
}

return (
<Dialog open={isOpen} onOpenChange={setIsOpen}>
<DialogTrigger asChild>
<Button variant="destructive">{t('pages.settings.danger-zone.deleteAccount')}</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>{t('pages.settings.danger-zone.title')}</DialogTitle>
<DialogDescription>{t('pages.settings.danger-zone.description')}</DialogDescription>
</DialogHeader>

<div className="border-destructive/50 bg-destructive/10 flex items-center gap-2 rounded-md border px-3 py-2 text-sm text-red-600 dark:text-red-400">
<AlertTriangle className="h-4 w-4" />
<span>{t('pages.settings.danger-zone.warning')}</span>
</div>

<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6">
<FormField
control={form.control}
name="confirmText"
render={({ field }) => (
<FormItem>
<FormLabel>Confirmation</FormLabel>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Missing translation for form label.

The form label "Confirmation" is not using the translation system, unlike other UI text in this component.

Replace the hardcoded string with a translation key:

-                  <FormLabel>Confirmation</FormLabel>
+                  <FormLabel>{t('pages.settings.danger-zone.confirmation_label')}</FormLabel>

Make sure to add the corresponding translation key in your localization files.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<FormLabel>Confirmation</FormLabel>
<FormLabel>{t('pages.settings.danger-zone.confirmation_label')}</FormLabel>

<FormDescription>{t('pages.settings.danger-zone.confirmation')}</FormDescription>
<FormControl>
<Input placeholder="DELETE" {...field} className="max-w-[200px]" />
</FormControl>
</FormItem>
)}
/>

<div className="flex justify-end">
<Button type="submit" variant="destructive" disabled={isDeleting}>
{isDeleting
? t('pages.settings.danger-zone.deleting')
: t('pages.settings.danger-zone.deleteAccount')}
</Button>
</div>
</form>
</Form>
</DialogContent>
</Dialog>
);
}

export default function DangerPage() {
const t = useTranslations();

return (
<div className="grid gap-6">
<SettingsCard
title={t('pages.settings.danger-zone.title')}
description={t('pages.settings.danger-zone.description')}
>
<DeleteAccountDialog />
</SettingsCard>
</div>
);
}
104 changes: 104 additions & 0 deletions apps/mail/app/(routes)/settings/labels/colors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
export const COLORS = [
'#000000',
'#434343',
'#666666',
'#999999',
'#cccccc',
'#efefef',
'#f3f3f3',
'#ffffff',
'#fb4c2f',
'#ffad47',
'#fad165',
'#16a766',
'#43d692',
'#4a86e8',
'#a479e2',
'#f691b3',
'#f6c5be',
'#ffe6c7',
'#fef1d1',
'#b9e4d0',
'#c6f3de',
'#c9daf8',
'#e4d7f5',
'#fcdee8',
'#efa093',
'#ffd6a2',
'#fce8b3',
'#89d3b2',
'#a0eac9',
'#a4c2f4',
'#d0bcf1',
'#fbc8d9',
'#e66550',
'#ffbc6b',
'#fcda83',
'#44b984',
'#68dfa9',
'#6d9eeb',
'#b694e8',
'#f7a7c0',
'#cc3a21',
'#eaa041',
'#f2c960',
'#149e60',
'#3dc789',
'#3c78d8',
'#8e63ce',
'#e07798',
'#ac2b16',
'#cf8933',
'#d5ae49',
'#0b804b',
'#2a9c68',
'#285bac',
'#653e9b',
'#b65775',
'#822111',
'#a46a21',
'#aa8831',
'#076239',
'#1a764d',
'#1c4587',
'#41236d',
'#83334c',
'#464646',
'#e7e7e7',
'#0d3472',
'#b6cff5',
'#0d3b44',
'#98d7e4',
'#3d188e',
'#e3d7ff',
'#711a36',
'#fbd3e0',
'#8a1c0a',
'#f2b2a8',
'#7a2e0b',
'#ffc8af',
'#7a4706',
'#ffdeb5',
'#594c05',
'#fbe983',
'#684e07',
'#fdedc1',
'#0b4f30',
'#b3efd3',
'#04502e',
'#a2dcc1',
'#c2c2c2',
'#4986e7',
'#2da2bb',
'#b99aff',
'#994a64',
'#f691b2',
'#ff7537',
'#ffad46',
'#662e37',
'#ebdbde',
'#cca6ac',
'#094228',
'#42d692',
'#16a765',
];
Loading