From 376edbfe1808ced758651ef06a6363e67c548360 Mon Sep 17 00:00:00 2001 From: Maxim Isaev Date: Fri, 22 May 2026 14:16:19 +0300 Subject: [PATCH] fix(settings): show including state on library index include action MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mirror the exclude UX: flushSync before bootstrap, block repeat clicks, show "Including…" on the row, and roll back exclusion if bind fails. --- .../settings/LibraryIndexSection.tsx | 57 ++++++++++++++----- .../settings/LibraryIndexServerRow.tsx | 5 ++ src/locales/de/settings.ts | 1 + src/locales/en/settings.ts | 1 + src/locales/es/settings.ts | 1 + src/locales/fr/settings.ts | 1 + src/locales/nb/settings.ts | 1 + src/locales/nl/settings.ts | 1 + src/locales/ro/settings.ts | 1 + src/locales/ru/settings.ts | 1 + src/locales/zh/settings.ts | 1 + 11 files changed, 56 insertions(+), 15 deletions(-) diff --git a/src/components/settings/LibraryIndexSection.tsx b/src/components/settings/LibraryIndexSection.tsx index ceda5e13..01ba34e7 100644 --- a/src/components/settings/LibraryIndexSection.tsx +++ b/src/components/settings/LibraryIndexSection.tsx @@ -60,6 +60,7 @@ export default function LibraryIndexSection() { const [progressByServer, setProgressByServer] = useState>({}); const [busyServerId, setBusyServerId] = useState(null); const [excludingServerId, setExcludingServerId] = useState(null); + const [includingServerId, setIncludingServerId] = useState(null); const [bootstrapping, setBootstrapping] = useState(false); const pollTimer = useRef | null>(null); @@ -133,6 +134,7 @@ export default function LibraryIndexSection() { setProgressByServer({}); setBusyServerId(null); setExcludingServerId(null); + setIncludingServerId(null); return; } void runBootstrap(); @@ -251,22 +253,38 @@ export default function LibraryIndexSection() { }; const handleIncludeServer = async (serverId: string) => { - setServerSyncExcluded(serverId, false); + if (includingServerId || excludingServerId) return; const srv = servers.find(s => s.id === serverId); - if (srv) { - setBootstrapping(true); - try { - const result = await bootstrapIndexedServer(srv); - applyConnectionResults({ [serverId]: result }); - await refreshAllStatuses(); - } finally { - setBootstrapping(false); + if (!srv) return; + flushSync(() => { + setIncludingServerId(serverId); + setServerSyncExcluded(serverId, false); + }); + try { + const result = await bootstrapIndexedServer(srv); + applyConnectionResults({ [serverId]: result }); + if (result === 'error') { + setServerSyncExcluded(serverId, true); + showToast(t('settings.libraryIndexBindError', { error: t('settings.libraryIndexStatusError') }), 5000, 'error'); + return; } + try { + const fresh = await libraryGetStatus(serverId); + syncPhaseRef.current[serverId] = fresh.syncPhase; + setStatusByServer(prev => ({ ...prev, [serverId]: fresh })); + } catch { + /* status poll is best-effort */ + } + } catch (e) { + setServerSyncExcluded(serverId, true); + showToast(t('settings.libraryIndexBindError', { error: String(e) }), 5000, 'error'); + } finally { + setIncludingServerId(null); } }; const handleExcludeServer = async (serverId: string) => { - if (excludingServerId) return; + if (excludingServerId || includingServerId) return; flushSync(() => setExcludingServerId(serverId)); try { const syncing = @@ -316,7 +334,8 @@ export default function LibraryIndexSection() { } }; - const globalBusy = bootstrapping || busyServerId != null || excludingServerId != null; + const globalBusy = + bootstrapping || busyServerId != null || excludingServerId != null || includingServerId != null; return ( void handleMasterToggle(e.target.checked)} /> @@ -373,9 +392,12 @@ export default function LibraryIndexSection() { connection={connectionByServer[srv.id] ?? 'unknown'} progressLabel={progressByServer[srv.id] ?? null} busy={busyServerId === srv.id} + including={includingServerId === srv.id} excluding={excludingServerId === srv.id} actionsDisabled={ - (globalBusy && busyServerId !== srv.id) || excludingServerId != null + (globalBusy && busyServerId !== srv.id) + || excludingServerId != null + || includingServerId != null } onFullSync={() => void runServerAction(srv.id, 'full')} onDeltaSync={() => void runServerAction(srv.id, 'delta')} @@ -403,10 +425,15 @@ export default function LibraryIndexSection() { type="button" className="btn btn-surface" style={{ fontSize: 12, padding: '4px 10px' }} - disabled={bootstrapping || excludingServerId != null} + disabled={ + includingServerId != null || excludingServerId != null + } + aria-busy={includingServerId === srv.id} onClick={() => void handleIncludeServer(srv.id)} > - {t('settings.libraryIndexIncludeServer')} + {includingServerId === srv.id + ? t('settings.libraryIndexIncludingServer') + : t('settings.libraryIndexIncludeServer')} ))} diff --git a/src/components/settings/LibraryIndexServerRow.tsx b/src/components/settings/LibraryIndexServerRow.tsx index 38995336..0bf613c4 100644 --- a/src/components/settings/LibraryIndexServerRow.tsx +++ b/src/components/settings/LibraryIndexServerRow.tsx @@ -18,6 +18,7 @@ interface LibraryIndexServerRowProps { connection: LibraryServerConnection; progressLabel: string | null; busy: boolean; + including: boolean; excluding: boolean; actionsDisabled: boolean; onFullSync: () => void; @@ -34,6 +35,7 @@ export default function LibraryIndexServerRow({ connection, progressLabel, busy, + including, excluding, actionsDisabled, onFullSync, @@ -93,6 +95,9 @@ export default function LibraryIndexServerRow({ {busy && ( {t('settings.libraryIndexServerSyncing')} )} + {including && !busy && ( + {t('settings.libraryIndexIncludingServer')} + )}
{phaseLabel} diff --git a/src/locales/de/settings.ts b/src/locales/de/settings.ts index d01b26ab..ecfa21d6 100644 --- a/src/locales/de/settings.ts +++ b/src/locales/de/settings.ts @@ -264,6 +264,7 @@ export const settings = { libraryIndexExcludingServer: 'Wird ausgeschlossen…', libraryIndexExcludedTitle: 'Von Synchronisation ausgeschlossen', libraryIndexIncludeServer: 'Wieder einschließen', + libraryIndexIncludingServer: 'Wird eingeschlossen…', libraryIndexStatus: 'Status', libraryIndexStatusIdle: 'Bereit', libraryIndexStatusProbing: 'Server wird geprüft…', diff --git a/src/locales/en/settings.ts b/src/locales/en/settings.ts index 49ba9cad..1bede627 100644 --- a/src/locales/en/settings.ts +++ b/src/locales/en/settings.ts @@ -267,6 +267,7 @@ export const settings = { libraryIndexExcludingServer: 'Excluding…', libraryIndexExcludedTitle: 'Excluded from sync', libraryIndexIncludeServer: 'Include again', + libraryIndexIncludingServer: 'Including…', libraryIndexStatus: 'Status', libraryIndexStatusIdle: 'Idle', libraryIndexStatusProbing: 'Checking server…', diff --git a/src/locales/es/settings.ts b/src/locales/es/settings.ts index 955e08fe..7d64187c 100644 --- a/src/locales/es/settings.ts +++ b/src/locales/es/settings.ts @@ -262,6 +262,7 @@ export const settings = { libraryIndexExcludingServer: 'Excluyendo…', libraryIndexExcludedTitle: 'Excluidos de la sincronización', libraryIndexIncludeServer: 'Incluir de nuevo', + libraryIndexIncludingServer: 'Incluyendo…', libraryIndexStatus: 'Estado', libraryIndexStatusIdle: 'Inactivo', libraryIndexStatusProbing: 'Comprobando servidor…', diff --git a/src/locales/fr/settings.ts b/src/locales/fr/settings.ts index 1c3e546a..56488e97 100644 --- a/src/locales/fr/settings.ts +++ b/src/locales/fr/settings.ts @@ -260,6 +260,7 @@ export const settings = { libraryIndexExcludingServer: 'Exclusion…', libraryIndexExcludedTitle: 'Exclus de la synchronisation', libraryIndexIncludeServer: 'Réinclure', + libraryIndexIncludingServer: 'Réinclusion…', libraryIndexStatus: 'État', libraryIndexStatusIdle: 'Inactif', libraryIndexStatusProbing: 'Vérification du serveur…', diff --git a/src/locales/nb/settings.ts b/src/locales/nb/settings.ts index 63e8dd91..ac836c86 100644 --- a/src/locales/nb/settings.ts +++ b/src/locales/nb/settings.ts @@ -259,6 +259,7 @@ export const settings = { libraryIndexExcludingServer: 'Ekskluderer…', libraryIndexExcludedTitle: 'Ekskludert fra synkronisering', libraryIndexIncludeServer: 'Inkluder igjen', + libraryIndexIncludingServer: 'Inkluderer…', libraryIndexStatus: 'Status', libraryIndexStatusIdle: 'Inaktiv', libraryIndexStatusProbing: 'Sjekker server…', diff --git a/src/locales/nl/settings.ts b/src/locales/nl/settings.ts index dcbf7c78..82564ebc 100644 --- a/src/locales/nl/settings.ts +++ b/src/locales/nl/settings.ts @@ -260,6 +260,7 @@ export const settings = { libraryIndexExcludingServer: 'Uitsluiten…', libraryIndexExcludedTitle: 'Uitgesloten van synchronisatie', libraryIndexIncludeServer: 'Weer opnemen', + libraryIndexIncludingServer: 'Opnemen…', libraryIndexStatus: 'Status', libraryIndexStatusIdle: 'Inactief', libraryIndexStatusProbing: 'Server controleren…', diff --git a/src/locales/ro/settings.ts b/src/locales/ro/settings.ts index a8d2f356..2be0cf8a 100644 --- a/src/locales/ro/settings.ts +++ b/src/locales/ro/settings.ts @@ -266,6 +266,7 @@ export const settings = { libraryIndexExcludingServer: 'Se exclude…', libraryIndexExcludedTitle: 'Excluse din sincronizare', libraryIndexIncludeServer: 'Include din nou', + libraryIndexIncludingServer: 'Se include…', libraryIndexStatus: 'Stare', libraryIndexStatusIdle: 'Inactiv', libraryIndexStatusProbing: 'Se verifică serverul…', diff --git a/src/locales/ru/settings.ts b/src/locales/ru/settings.ts index bc51b323..3be265ca 100644 --- a/src/locales/ru/settings.ts +++ b/src/locales/ru/settings.ts @@ -272,6 +272,7 @@ export const settings = { libraryIndexExcludingServer: 'Отключается…', libraryIndexExcludedTitle: 'Исключены из синхронизации', libraryIndexIncludeServer: 'Включить снова', + libraryIndexIncludingServer: 'Подключается…', libraryIndexStatus: 'Статус', libraryIndexStatusIdle: 'Ожидание', libraryIndexStatusProbing: 'Проверка сервера…', diff --git a/src/locales/zh/settings.ts b/src/locales/zh/settings.ts index e08d7fc6..49b1ca55 100644 --- a/src/locales/zh/settings.ts +++ b/src/locales/zh/settings.ts @@ -259,6 +259,7 @@ export const settings = { libraryIndexExcludingServer: '正在排除…', libraryIndexExcludedTitle: '已排除同步', libraryIndexIncludeServer: '重新纳入', + libraryIndexIncludingServer: '正在纳入…', libraryIndexStatus: '状态', libraryIndexStatusIdle: '空闲', libraryIndexStatusProbing: '正在检查服务器…',