diff --git a/crates/core/src/account/state.rs b/crates/core/src/account/state.rs index 14089dc..c415006 100644 --- a/crates/core/src/account/state.rs +++ b/crates/core/src/account/state.rs @@ -136,7 +136,12 @@ impl DownloadState { let mut updated = current.clone(); updated.last_trigger_at = utc_now!(); - if let Some(old_session) = updated.active_session.take() { + if let Some(mut old_session) = updated.active_session.take() { + if old_session.status == DownloadStatus::Running { + old_session.status = DownloadStatus::Cancelled; + old_session.end_time = Some(utc_now!()); + old_session.message = Some("Interrupted by a new download session.".into()); + } updated.history.push(old_session); if updated.history.len() > 30 { updated.history.remove(0); diff --git a/crates/core/src/cache/imap/download/download_folders.rs b/crates/core/src/cache/imap/download/download_folders.rs index 4d8d0de..6144597 100644 --- a/crates/core/src/cache/imap/download/download_folders.rs +++ b/crates/core/src/cache/imap/download/download_folders.rs @@ -175,6 +175,16 @@ pub async fn detect_mailbox_changes( "Account {}: New folders detected: {:?}", account.id, new_folders ); + if account.auto_download_new_mailboxes.unwrap_or(false) { + let mut updated: Vec = download_folders.to_vec(); + updated.extend(new_folders.iter().cloned()); + AccountModel::update_download_folders(account.id, updated)?; + info!( + "Account {}: Auto-added {} new folders to download list", + account.id, + new_folders.len() + ); + } } // Update known folders only if there were changes diff --git a/crates/core/src/cache/imap/task.rs b/crates/core/src/cache/imap/task.rs index 7d0ea08..0bac419 100644 --- a/crates/core/src/cache/imap/task.rs +++ b/crates/core/src/cache/imap/task.rs @@ -62,10 +62,22 @@ impl AccountDownTask { } } - async fn is_busy(&self, account_id: u64) -> bool { - self.busy_accounts.lock().await.contains(&account_id) + /// Atomically check and set busy. Returns true if we claimed the slot, + /// false if another task is already busy on this account. + async fn try_set_busy(&self, account_id: u64) -> bool { + let mut guard = self.busy_accounts.lock().await; + if guard.contains(&account_id) { + false + } else { + guard.insert(account_id); + true + } } + // async fn is_busy(&self, account_id: u64) -> bool { + // self.busy_accounts.lock().await.contains(&account_id) + // } + pub async fn start_download_task(&self, account_id: u64, email: String) { let task_name = format!("account-download-task-{}-{}", account_id, &email); let periodic_task = PeriodicTask::new(&task_name); @@ -85,7 +97,7 @@ impl AccountDownTask { return Ok(()); } - if SYNC_TASKS.is_busy(account_id).await { + if !SYNC_TASKS.try_set_busy(account_id).await { warn!( "Account {}: Scheduled task skipped (Previous sync still active).", account_id @@ -93,7 +105,6 @@ impl AccountDownTask { return Ok(()); } - SYNC_TASKS.set_busy(account_id, true).await; let _busy_guard = scopeguard::guard(account_id, |id| { tokio::spawn(async move { SYNC_TASKS.set_busy(id, false).await; @@ -205,9 +216,9 @@ impl AccountDownTask { ErrorCode::Forbidden )); } - if self.is_busy(account_id).await { + if !self.try_set_busy(account_id).await { return Err(raise_error!( - "The background synchronization is currently active. Please try again in a few seconds.".into(), + "The background synchronization is currently active. Please try again in a few seconds.".into(), ErrorCode::Forbidden )); } @@ -216,7 +227,7 @@ impl AccountDownTask { let cancel_token = CancellationToken::new(); let token_clone = cancel_token.clone(); let handle = tokio::spawn(async move { - SYNC_TASKS.set_busy(account_id, true).await; + // busy already claimed by caller via try_set_busy let _cleanup = scopeguard::guard(account_id, |id| { tokio::spawn(async move { SYNC_TASKS.set_busy(id, false).await; diff --git a/web/src/features/accounts/components/action-dialog.tsx b/web/src/features/accounts/components/action-dialog.tsx index b9481e7..6ab046c 100644 --- a/web/src/features/accounts/components/action-dialog.tsx +++ b/web/src/features/accounts/components/action-dialog.tsx @@ -114,6 +114,7 @@ export type Account = { folder_limit?: number; download_interval_min: number; download_batch_size: number; + auto_download_new_mailboxes: boolean; }; const getAccountSchema = (isEdit: boolean, t: (key: string) => string) => @@ -138,6 +139,7 @@ const getAccountSchema = (isEdit: boolean, t: (key: string) => string) => .int() .min(10, { message: t('validation.singleRequestBatchSizeTooSmall') }) .max(200, { message: t('validation.singleRequestBatchSizeTooLarge') }), + auto_download_new_mailboxes: z.boolean(), }); type Step = { @@ -151,7 +153,7 @@ export type Steps = [...Step[]]; const getSteps = (t: (key: string) => string): Steps => [ { id: "step-1", name: t('accounts.steps.emailAddress'), fields: ["email", "account_name"] }, { id: "step-2", name: t('accounts.steps.imap'), fields: ["imap", "use_dangerous", "login_name"] }, - { id: "step-3", name: t('accounts.steps.syncPreferences'), fields: ["enabled", "date_since", "date_before", "folder_limit", "download_interval_min", "download_batch_size"] }, + { id: "step-3", name: t('accounts.steps.syncPreferences'), fields: ["enabled", "date_since", "date_before", "folder_limit", "download_interval_min", "download_batch_size", "auto_download_new_mailboxes"] }, { id: "step-4", name: t('accounts.steps.summary'), fields: [] }, ]; @@ -184,6 +186,7 @@ const defaultValues: Account = { folder_limit: undefined, download_interval_min: 60, download_batch_size: 30, + auto_download_new_mailboxes: true, }; const emptyImap: ImapConfig = { @@ -212,6 +215,7 @@ const mapCurrentRowToFormValues = (currentRow: AccountModel): Account => { folder_limit: currentRow.folder_limit ?? undefined, download_interval_min: currentRow.download_interval_min ?? 60, download_batch_size: currentRow.download_batch_size ?? 30, + auto_download_new_mailboxes: currentRow.auto_download_new_mailboxes ?? true, }; }; @@ -293,6 +297,7 @@ export function AccountActionDialog({ currentRow, open, onOpenChange }: Props) { folder_limit: data.folder_limit, download_interval_min: data.download_interval_min, download_batch_size: data.download_batch_size, + auto_download_new_mailboxes: data.auto_download_new_mailboxes, }; if (isEdit) { const isAllMode = !data.date_since && !data.date_before; diff --git a/web/src/features/accounts/components/step3.tsx b/web/src/features/accounts/components/step3.tsx index 7810a62..1a947b2 100644 --- a/web/src/features/accounts/components/step3.tsx +++ b/web/src/features/accounts/components/step3.tsx @@ -232,6 +232,24 @@ export default function Step3() {
+ ( + + + + +
+ {t('accounts.autoDownloadNewMailboxes')} + {t('accounts.autoDownloadNewMailboxesDescription')} +
+
+ )} + /> + +
+ {t('accounts.downloadBatchSize')}: {summaryData.download_batch_size} + + + {t('accounts.autoDownloadNewMailboxes')}: + {summaryData.auto_download_new_mailboxes ? t('common.yes') : t('common.no')} + ); diff --git a/web/src/locales/ar.json b/web/src/locales/ar.json index 076c4a8..d6475b6 100644 --- a/web/src/locales/ar.json +++ b/web/src/locales/ar.json @@ -100,6 +100,7 @@ "auth": "المصادقة", "authType": "نوع_المصادقة", "autoConfiguring": "جارٍ التكوين التلقائي...", + "autoDownloadNewMailboxesDescription": "إضافة المجلدات الجديدة المكتشفة تلقائيًا إلى قائمة التنزيل.", "beforeRelative": "تنزيل رسائل البريد الإلكتروني القديمة فقط", "beforeRelativeValue": "تنزيل رسائل البريد الإلكتروني قبل {{value}} {{unit}} مضت", "cancelDownload": "إلغاء التنزيل", diff --git a/web/src/locales/da.json b/web/src/locales/da.json index 9550b84..9bfbeb9 100644 --- a/web/src/locales/da.json +++ b/web/src/locales/da.json @@ -100,6 +100,7 @@ "auth": "Godkendelse", "authType": "godkendelsestype", "autoConfiguring": "Konfigurerer automatisk...", + "autoDownloadNewMailboxesDescription": "Føj automatisk nye mapper til downloadlisten.", "beforeRelative": "Download kun gamle e-mails", "beforeRelativeValue": "Download e-mails før {{value}} {{unit}} siden", "cancelDownload": "Annuller download", diff --git a/web/src/locales/de.json b/web/src/locales/de.json index 54b1bcd..554919a 100644 --- a/web/src/locales/de.json +++ b/web/src/locales/de.json @@ -100,6 +100,7 @@ "auth": "Authentifizierung", "authType": "Authentifizierungstyp", "autoConfiguring": "Automatische Konfiguration läuft...", + "autoDownloadNewMailboxesDescription": "Neu entdeckte Ordner automatisch zur Download-Liste hinzufügen.", "beforeRelative": "Nur alte E-Mails herunterladen", "beforeRelativeValue": "E-Mails von vor {{value}} {{unit}} herunterladen", "cancelDownload": "Download abbrechen", diff --git a/web/src/locales/en.json b/web/src/locales/en.json index bb25118..04996a0 100644 --- a/web/src/locales/en.json +++ b/web/src/locales/en.json @@ -100,6 +100,8 @@ "auth": "Auth", "authType": "auth_type", "autoConfiguring": "Auto-configuring...", + "autoDownloadNewMailboxes": "Auto-add new mailboxes", + "autoDownloadNewMailboxesDescription": "Automatically add newly discovered folders to the download list.", "beforeRelative": "Download Old Emails Only", "beforeRelativeValue": "Download emails before {{value}} {{unit}} ago", "cancelDownload": "Cancel download", diff --git a/web/src/locales/es.json b/web/src/locales/es.json index 6aae412..eef88d4 100644 --- a/web/src/locales/es.json +++ b/web/src/locales/es.json @@ -100,6 +100,7 @@ "auth": "Autenticación", "authType": "tipo de autenticación", "autoConfiguring": "Autoconfigurando...", + "autoDownloadNewMailboxesDescription": "Añadir automáticamente las nuevas carpetas a la lista de descarga.", "beforeRelative": "Descargar solo correos antiguos", "beforeRelativeValue": "Descargar correos de hace {{value}} {{unit}}", "cancelDownload": "Cancelar descarga", diff --git a/web/src/locales/fi.json b/web/src/locales/fi.json index 0ad0537..a7e4686 100644 --- a/web/src/locales/fi.json +++ b/web/src/locales/fi.json @@ -100,6 +100,7 @@ "auth": "Todennus", "authType": "todennustyyppi", "autoConfiguring": "Automaattinen määritys...", + "autoDownloadNewMailboxesDescription": "Lisää uudet löydetyt kansiot automaattisesti latausluetteloon.", "beforeRelative": "Lataa vain vanhat sähköpostit", "beforeRelativeValue": "Lataa sähköpostit {{value}} {{unit}} sitten", "cancelDownload": "Peruuta lataus", diff --git a/web/src/locales/fr.json b/web/src/locales/fr.json index fe7521d..e5483d0 100644 --- a/web/src/locales/fr.json +++ b/web/src/locales/fr.json @@ -100,6 +100,7 @@ "auth": "Auth.", "authType": "type_auth", "autoConfiguring": "Configuration automatique...", + "autoDownloadNewMailboxesDescription": "Ajouter automatiquement les nouveaux dossiers à la liste de téléchargement.", "beforeRelative": "Télécharger uniquement les anciens e-mails", "beforeRelativeValue": "Télécharger les e-mails d'il y a {{value}} {{unit}}", "cancelDownload": "Annuler le téléchargement", diff --git a/web/src/locales/it.json b/web/src/locales/it.json index f13929b..7b4f169 100644 --- a/web/src/locales/it.json +++ b/web/src/locales/it.json @@ -100,6 +100,7 @@ "auth": "Autenticazione", "authType": "tipo_autenticazione", "autoConfiguring": "Configurazione automatica...", + "autoDownloadNewMailboxesDescription": "Aggiungi automaticamente le nuove cartelle all'elenco di download.", "beforeRelative": "Scarica solo le vecchie email", "beforeRelativeValue": "Scarica email da {{value}} {{unit}} fa", "cancelDownload": "Annulla download", diff --git a/web/src/locales/jp.json b/web/src/locales/jp.json index fc0ccc0..f6442fe 100644 --- a/web/src/locales/jp.json +++ b/web/src/locales/jp.json @@ -100,6 +100,7 @@ "auth": "認証", "authType": "認証タイプ", "autoConfiguring": "自動設定中...", + "autoDownloadNewMailboxesDescription": "新しく見つかったフォルダーを自動的にダウンロード一覧に追加します。", "beforeRelative": "古いメールのみダウンロード", "beforeRelativeValue": "{{value}} {{unit}}前より古いメールをダウンロード", "cancelDownload": "ダウンロードをキャンセル", diff --git a/web/src/locales/ko.json b/web/src/locales/ko.json index 22c782e..7c536fb 100644 --- a/web/src/locales/ko.json +++ b/web/src/locales/ko.json @@ -100,6 +100,7 @@ "auth": "인증", "authType": "인증 유형", "autoConfiguring": "자동 구성 중...", + "autoDownloadNewMailboxesDescription": "새로 발견된 폴더를 다운로드 목록에 자동으로 추가합니다.", "beforeRelative": "이전 이메일만 다운로드", "beforeRelativeValue": "{{value}} {{unit}} 전의 이메일 다운로드", "cancelDownload": "다운로드 취소", diff --git a/web/src/locales/nl.json b/web/src/locales/nl.json index f6c3b4d..24d3311 100644 --- a/web/src/locales/nl.json +++ b/web/src/locales/nl.json @@ -100,6 +100,7 @@ "auth": "Authenticatie", "authType": "authenticatie_type", "autoConfiguring": "Automatisch configureren...", + "autoDownloadNewMailboxesDescription": "Voeg automatisch nieuw ontdekte mappen toe aan de downloadlijst.", "beforeRelative": "Download alleen oude e-mails", "beforeRelativeValue": "Download e-mails van {{value}} {{unit}} geleden", "cancelDownload": "Download annuleren", diff --git a/web/src/locales/no.json b/web/src/locales/no.json index 78770ed..72d722b 100644 --- a/web/src/locales/no.json +++ b/web/src/locales/no.json @@ -100,6 +100,7 @@ "auth": "Autentisering", "authType": "autentiseringstype", "autoConfiguring": "Konfigurerer automatisk...", + "autoDownloadNewMailboxesDescription": "Legg automatisk til nye mapper i nedlastingslisten.", "beforeRelative": "Last ned kun gamle e-poster", "beforeRelativeValue": "Last ned e-poster fra {{value}} {{unit}} siden", "cancelDownload": "Avbryt nedlasting", diff --git a/web/src/locales/pl.json b/web/src/locales/pl.json index e236078..5b8e4c0 100644 --- a/web/src/locales/pl.json +++ b/web/src/locales/pl.json @@ -100,6 +100,7 @@ "auth": "Auth", "authType": "auth_type", "autoConfiguring": "Auto konfiguracja...", + "autoDownloadNewMailboxesDescription": "Automatycznie dodawaj nowo wykryte foldery do listy pobierania.", "beforeRelative": "Pobierz tylko stare e-maile", "beforeRelativeValue": "Pobierz e-maile sprzed {{value}} {{unit}}", "cancelDownload": "Anuluj pobieranie", diff --git a/web/src/locales/pt.json b/web/src/locales/pt.json index ec5e6da..d847af9 100644 --- a/web/src/locales/pt.json +++ b/web/src/locales/pt.json @@ -100,6 +100,7 @@ "auth": "Autenticação", "authType": "Tipo de Autenticação", "autoConfiguring": "Configurando Automaticamente...", + "autoDownloadNewMailboxesDescription": "Adicionar automaticamente novas pastas à lista de download.", "beforeRelative": "Baixar apenas e-mails antigos", "beforeRelativeValue": "Baixar e-mails de {{value}} {{unit}} atrás", "cancelDownload": "Cancelar download", diff --git a/web/src/locales/ru.json b/web/src/locales/ru.json index d8737e0..08174b0 100644 --- a/web/src/locales/ru.json +++ b/web/src/locales/ru.json @@ -100,6 +100,7 @@ "auth": "Авторизация", "authType": "тип_авторизации", "autoConfiguring": "Автонастройка...", + "autoDownloadNewMailboxesDescription": "Автоматически добавлять новые папки в список загрузки.", "beforeRelative": "Скачать только старые письма", "beforeRelativeValue": "Скачать письма за {{value}} {{unit}} назад", "cancelDownload": "Отменить загрузку", diff --git a/web/src/locales/sv.json b/web/src/locales/sv.json index a8a600a..289e607 100644 --- a/web/src/locales/sv.json +++ b/web/src/locales/sv.json @@ -100,6 +100,7 @@ "auth": "Auth", "authType": "auth_typ", "autoConfiguring": "Konfigurerar automatiskt...", + "autoDownloadNewMailboxesDescription": "Lägg automatiskt till nya mappar i hämtningslistan.", "beforeRelative": "Ladda ner endast gamla e-postmeddelanden", "beforeRelativeValue": "Ladda ner e-post från {{value}} {{unit}} sedan", "cancelDownload": "Avbryt hämtning", diff --git a/web/src/locales/zh-tw.json b/web/src/locales/zh-tw.json index 8bf4975..aea3ef7 100644 --- a/web/src/locales/zh-tw.json +++ b/web/src/locales/zh-tw.json @@ -100,6 +100,7 @@ "auth": "驗證", "authType": "驗證類型", "autoConfiguring": "正在自動設定...", + "autoDownloadNewMailboxesDescription": "自動將新發現的郵件夾添加到下載列表中。", "beforeRelative": "僅下載舊郵件", "beforeRelativeValue": "下載 {{value}} {{unit}} 之前的郵件", "cancelDownload": "取消下載", diff --git a/web/src/locales/zh.json b/web/src/locales/zh.json index 11f73db..3840e53 100644 --- a/web/src/locales/zh.json +++ b/web/src/locales/zh.json @@ -100,6 +100,8 @@ "auth": "认证", "authType": "认证类型", "autoConfiguring": "自动配置中...", + "autoDownloadNewMailboxes": "自动添加新邮件夹", + "autoDownloadNewMailboxesDescription": "自动将新发现的邮件夹添加到下载列表中。", "beforeRelative": "仅下载旧邮件", "beforeRelativeValue": "下载 {{value}} {{unit}} 之前的邮件", "cancelDownload": "取消下载",