From 11c2a0c9af526a048b83b0f90d6307b116f1c71d Mon Sep 17 00:00:00 2001 From: Agent Zero Date: Mon, 20 Jul 2026 13:26:39 +0200 Subject: [PATCH] =?UTF-8?q?fix:=20deleted=20mails=20showing=20in=20inbox?= =?UTF-8?q?=20=E2=80=94=20set=20deleted=5Fat=20on=20move=20to=20Trash,=20s?= =?UTF-8?q?how=20in=20Trash=20folder?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - delete_mail: set deleted_at when moving to Trash (not just folder_id) - empty_folder: same — set deleted_at when moving to Trash - list_mails: show soft-deleted mails when viewing Trash folder - This prevents sync from moving deleted mails back to inbox - Trash folder shows deleted_at mails, all other folders hide them --- app/plugins/builtins/mail/routes.py | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/app/plugins/builtins/mail/routes.py b/app/plugins/builtins/mail/routes.py index 5925f04..0f42655 100644 --- a/app/plugins/builtins/mail/routes.py +++ b/app/plugins/builtins/mail/routes.py @@ -669,6 +669,7 @@ async def empty_folder( else: # Move to Trash folder (keep visible in Trash) mail.folder_id = trash_folder.id + mail.deleted_at = now try: await mail_services.imap_delete_mail(db, mail.id, tenant_id, permanent=False) except Exception as exc: @@ -1648,7 +1649,11 @@ async def delete_mail( await db.flush() elif trash_folder: # Mail is NOT in Trash → move to Trash folder + # Set deleted_at so the mail disappears from the source folder + # and the sync doesn't move it back. list_mails shows deleted_at + # mails when viewing the Trash folder. mail.folder_id = trash_folder.id + mail.deleted_at = datetime.now(UTC) await db.flush() try: await mail_services.imap_delete_mail(db, m_id, tenant_id, permanent=False) @@ -1757,11 +1762,28 @@ async def list_mails( if folder_id: f_id = _parse_uuid(folder_id, "folder_id") stmt = stmt.where(Mail.folder_id == f_id) + # Check if this folder is a Trash folder — if so, show soft-deleted mails too + folder = ( + await db.execute( + select(MailFolder).where( + and_(MailFolder.id == f_id, MailFolder.tenant_id == tenant_id) + ) + ) + ).scalar_one_or_none() + is_trash = folder and ( + 'trash' in (folder.imap_name or '').lower() + or 'papierkorb' in (folder.name or '').lower() + or 'trash' in (folder.name or '').lower() + ) + if not is_trash: + # Exclude soft-deleted mails in non-Trash folders + stmt = stmt.where(Mail.deleted_at.is_(None)) + else: + # No folder specified — exclude soft-deleted mails + stmt = stmt.where(Mail.deleted_at.is_(None)) if account_id: a_id = _parse_uuid(account_id, "account_id") stmt = stmt.where(Mail.account_id == a_id) - # Exclude soft-deleted mails - stmt = stmt.where(Mail.deleted_at.is_(None)) total = (await db.execute(select(func.count()).select_from(stmt.subquery()))).scalar() # Dynamic sorting sort_columns = {