Skip to content

Commit 26fd841

Browse files
author
Sai Sridhar
committed
Add keep/delete emails choice when disconnecting Gmail
Shows a modal on disconnect letting users choose whether to keep or permanently delete their synced emails from Mailair.
1 parent 6aa0c09 commit 26fd841

3 files changed

Lines changed: 79 additions & 33 deletions

File tree

backend/routes/integrations.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,14 +70,24 @@ async def gmail_callback(request: Request, code: str, state: str):
7070

7171

7272
@router.delete("/gmail/disconnect", status_code=status.HTTP_204_NO_CONTENT)
73-
async def gmail_disconnect(current_user: Annotated[dict, Depends(get_current_user)]):
74-
"""Revoke Gmail access and remove stored tokens."""
75-
success = await disconnect_gmail(user_id=_user_id(current_user))
73+
async def gmail_disconnect(
74+
current_user: Annotated[dict, Depends(get_current_user)],
75+
delete_emails: bool = False,
76+
):
77+
"""Revoke Gmail access and remove stored tokens. Optionally delete synced emails."""
78+
user_id = _user_id(current_user)
79+
success = await disconnect_gmail(user_id=user_id)
7680
if not success:
7781
raise HTTPException(
7882
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
7983
detail="Failed to disconnect Gmail.",
8084
)
85+
if delete_emails:
86+
try:
87+
supabase = get_supabase()
88+
supabase.table("emails").delete().eq("user_id", user_id).execute()
89+
except Exception as exc:
90+
logger.error("Failed to delete emails on disconnect (user_id=%s): %s", user_id, exc)
8191
return None
8292

8393

frontend/lib/api.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -327,8 +327,8 @@ export const integrationsApi = {
327327
return data;
328328
},
329329

330-
disconnectGmail: async (): Promise<void> => {
331-
await api.delete('/api/integrations/gmail/disconnect');
330+
disconnectGmail: async (deleteEmails = false): Promise<void> => {
331+
await api.delete('/api/integrations/gmail/disconnect', { params: { delete_emails: deleteEmails } });
332332
},
333333

334334
saveSlackWebhook: async (webhookUrl: string): Promise<{ success: boolean }> => {

frontend/pages/settings/index.tsx

Lines changed: 64 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -573,6 +573,7 @@ function IntegrationsTab({
573573
const [isTestingSlack, setIsTestingSlack] = useState(false);
574574
const [isConnectingGmail, setIsConnectingGmail] = useState(false);
575575
const [isDisconnectingGmail, setIsDisconnectingGmail] = useState(false);
576+
const [showDisconnectModal, setShowDisconnectModal] = useState(false);
576577

577578
const handleConnectGmail = async () => {
578579
setIsConnectingGmail(true);
@@ -585,13 +586,13 @@ function IntegrationsTab({
585586
}
586587
};
587588

588-
const handleDisconnectGmail = async () => {
589-
if (!confirm('Disconnect Gmail? You will stop receiving new emails in Mailair.')) return;
589+
const handleDisconnectGmail = async (deleteEmails: boolean) => {
590+
setShowDisconnectModal(false);
590591
setIsDisconnectingGmail(true);
591592
try {
592-
await integrationsApi.disconnectGmail();
593+
await integrationsApi.disconnectGmail(deleteEmails);
593594
await mutateGmail();
594-
toast.success('Gmail disconnected');
595+
toast.success(deleteEmails ? 'Gmail disconnected and emails deleted' : 'Gmail disconnected');
595596
} catch {
596597
toast.error('Failed to disconnect Gmail');
597598
} finally {
@@ -660,31 +661,66 @@ function IntegrationsTab({
660661
</div>
661662

662663
{gmailStatus?.connected ? (
663-
<div className="space-y-3">
664-
<div className="rounded-lg bg-gray-50 dark:bg-gray-800 border border-gray-200 dark:border-gray-700 p-3 text-sm text-gray-700 dark:text-gray-300">
665-
<p><span className="font-medium">Account:</span> {gmailStatus.email}</p>
666-
{gmailStatus.last_sync && (
667-
<p className="mt-0.5 text-gray-500 dark:text-gray-400">
668-
Last synced: {new Date(gmailStatus.last_sync).toLocaleString()}
669-
</p>
670-
)}
671-
{gmailStatus.total_synced != null && (
672-
<p className="mt-0.5 text-gray-500 dark:text-gray-400">Total emails synced: {gmailStatus.total_synced?.toLocaleString()}</p>
673-
)}
664+
<>
665+
<div className="space-y-3">
666+
<div className="rounded-lg bg-gray-50 dark:bg-gray-800 border border-gray-200 dark:border-gray-700 p-3 text-sm text-gray-700 dark:text-gray-300">
667+
<p><span className="font-medium">Account:</span> {gmailStatus.email}</p>
668+
{gmailStatus.last_sync && (
669+
<p className="mt-0.5 text-gray-500 dark:text-gray-400">
670+
Last synced: {new Date(gmailStatus.last_sync).toLocaleString()}
671+
</p>
672+
)}
673+
{gmailStatus.total_synced != null && (
674+
<p className="mt-0.5 text-gray-500 dark:text-gray-400">Total emails synced: {gmailStatus.total_synced?.toLocaleString()}</p>
675+
)}
676+
</div>
677+
<button
678+
onClick={() => setShowDisconnectModal(true)}
679+
disabled={isDisconnectingGmail}
680+
className="inline-flex items-center gap-2 rounded-lg border border-red-200 bg-red-50 px-3 py-2 text-sm font-medium text-red-600 hover:bg-red-100 transition-colors disabled:opacity-50"
681+
>
682+
{isDisconnectingGmail ? (
683+
<Loader2 className="h-4 w-4 animate-spin" />
684+
) : (
685+
<Trash2 className="h-4 w-4" />
686+
)}
687+
Disconnect Gmail
688+
</button>
674689
</div>
675-
<button
676-
onClick={handleDisconnectGmail}
677-
disabled={isDisconnectingGmail}
678-
className="inline-flex items-center gap-2 rounded-lg border border-red-200 bg-red-50 px-3 py-2 text-sm font-medium text-red-600 hover:bg-red-100 transition-colors disabled:opacity-50"
679-
>
680-
{isDisconnectingGmail ? (
681-
<Loader2 className="h-4 w-4 animate-spin" />
682-
) : (
683-
<Trash2 className="h-4 w-4" />
684-
)}
685-
Disconnect Gmail
686-
</button>
687-
</div>
690+
691+
{showDisconnectModal && (
692+
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/40 px-4">
693+
<div className="w-full max-w-sm rounded-2xl bg-white dark:bg-gray-900 p-6 shadow-xl">
694+
<h3 className="text-base font-semibold text-gray-900 dark:text-gray-100 mb-1">Disconnect Gmail</h3>
695+
<p className="text-sm text-gray-500 dark:text-gray-400 mb-5">
696+
What would you like to do with your existing emails in Mailair?
697+
</p>
698+
<div className="space-y-3 mb-6">
699+
<button
700+
onClick={() => handleDisconnectGmail(false)}
701+
className="w-full rounded-xl border border-gray-200 dark:border-gray-700 p-4 text-left hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors"
702+
>
703+
<p className="text-sm font-medium text-gray-900 dark:text-gray-100">Keep my emails</p>
704+
<p className="text-xs text-gray-500 dark:text-gray-400 mt-0.5">Disconnect Gmail but keep all synced emails in Mailair.</p>
705+
</button>
706+
<button
707+
onClick={() => handleDisconnectGmail(true)}
708+
className="w-full rounded-xl border border-red-200 dark:border-red-800 p-4 text-left hover:bg-red-50 dark:hover:bg-red-900/20 transition-colors"
709+
>
710+
<p className="text-sm font-medium text-red-600 dark:text-red-400">Delete my emails</p>
711+
<p className="text-xs text-gray-500 dark:text-gray-400 mt-0.5">Disconnect Gmail and permanently delete all synced emails.</p>
712+
</button>
713+
</div>
714+
<button
715+
onClick={() => setShowDisconnectModal(false)}
716+
className="w-full text-sm text-gray-400 hover:text-gray-600 dark:hover:text-gray-200 transition-colors"
717+
>
718+
Cancel
719+
</button>
720+
</div>
721+
</div>
722+
)}
723+
</>
688724
) : (
689725
<button
690726
onClick={handleConnectGmail}

0 commit comments

Comments
 (0)