mirror of
https://github.com/Psychotoxical/psysonic.git
synced 2026-07-21 23:05:46 +00:00
fix(settings): show including state on library index include action
Mirror the exclude UX: flushSync before bootstrap, block repeat clicks, show "Including…" on the row, and roll back exclusion if bind fails.
This commit is contained in:
@@ -60,6 +60,7 @@ export default function LibraryIndexSection() {
|
|||||||
const [progressByServer, setProgressByServer] = useState<Record<string, string | null>>({});
|
const [progressByServer, setProgressByServer] = useState<Record<string, string | null>>({});
|
||||||
const [busyServerId, setBusyServerId] = useState<string | null>(null);
|
const [busyServerId, setBusyServerId] = useState<string | null>(null);
|
||||||
const [excludingServerId, setExcludingServerId] = useState<string | null>(null);
|
const [excludingServerId, setExcludingServerId] = useState<string | null>(null);
|
||||||
|
const [includingServerId, setIncludingServerId] = useState<string | null>(null);
|
||||||
const [bootstrapping, setBootstrapping] = useState(false);
|
const [bootstrapping, setBootstrapping] = useState(false);
|
||||||
|
|
||||||
const pollTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
|
const pollTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||||
@@ -133,6 +134,7 @@ export default function LibraryIndexSection() {
|
|||||||
setProgressByServer({});
|
setProgressByServer({});
|
||||||
setBusyServerId(null);
|
setBusyServerId(null);
|
||||||
setExcludingServerId(null);
|
setExcludingServerId(null);
|
||||||
|
setIncludingServerId(null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
void runBootstrap();
|
void runBootstrap();
|
||||||
@@ -251,22 +253,38 @@ export default function LibraryIndexSection() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleIncludeServer = async (serverId: string) => {
|
const handleIncludeServer = async (serverId: string) => {
|
||||||
setServerSyncExcluded(serverId, false);
|
if (includingServerId || excludingServerId) return;
|
||||||
const srv = servers.find(s => s.id === serverId);
|
const srv = servers.find(s => s.id === serverId);
|
||||||
if (srv) {
|
if (!srv) return;
|
||||||
setBootstrapping(true);
|
flushSync(() => {
|
||||||
|
setIncludingServerId(serverId);
|
||||||
|
setServerSyncExcluded(serverId, false);
|
||||||
|
});
|
||||||
try {
|
try {
|
||||||
const result = await bootstrapIndexedServer(srv);
|
const result = await bootstrapIndexedServer(srv);
|
||||||
applyConnectionResults({ [serverId]: result });
|
applyConnectionResults({ [serverId]: result });
|
||||||
await refreshAllStatuses();
|
if (result === 'error') {
|
||||||
} finally {
|
setServerSyncExcluded(serverId, true);
|
||||||
setBootstrapping(false);
|
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) => {
|
const handleExcludeServer = async (serverId: string) => {
|
||||||
if (excludingServerId) return;
|
if (excludingServerId || includingServerId) return;
|
||||||
flushSync(() => setExcludingServerId(serverId));
|
flushSync(() => setExcludingServerId(serverId));
|
||||||
try {
|
try {
|
||||||
const syncing =
|
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 (
|
return (
|
||||||
<SettingsSubSection
|
<SettingsSubSection
|
||||||
@@ -344,7 +363,7 @@ export default function LibraryIndexSection() {
|
|||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={masterEnabled}
|
checked={masterEnabled}
|
||||||
disabled={servers.length === 0 || bootstrapping}
|
disabled={servers.length === 0 || bootstrapping || includingServerId != null || excludingServerId != null}
|
||||||
onChange={e => void handleMasterToggle(e.target.checked)}
|
onChange={e => void handleMasterToggle(e.target.checked)}
|
||||||
/>
|
/>
|
||||||
<span className="toggle-track" />
|
<span className="toggle-track" />
|
||||||
@@ -373,9 +392,12 @@ export default function LibraryIndexSection() {
|
|||||||
connection={connectionByServer[srv.id] ?? 'unknown'}
|
connection={connectionByServer[srv.id] ?? 'unknown'}
|
||||||
progressLabel={progressByServer[srv.id] ?? null}
|
progressLabel={progressByServer[srv.id] ?? null}
|
||||||
busy={busyServerId === srv.id}
|
busy={busyServerId === srv.id}
|
||||||
|
including={includingServerId === srv.id}
|
||||||
excluding={excludingServerId === srv.id}
|
excluding={excludingServerId === srv.id}
|
||||||
actionsDisabled={
|
actionsDisabled={
|
||||||
(globalBusy && busyServerId !== srv.id) || excludingServerId != null
|
(globalBusy && busyServerId !== srv.id)
|
||||||
|
|| excludingServerId != null
|
||||||
|
|| includingServerId != null
|
||||||
}
|
}
|
||||||
onFullSync={() => void runServerAction(srv.id, 'full')}
|
onFullSync={() => void runServerAction(srv.id, 'full')}
|
||||||
onDeltaSync={() => void runServerAction(srv.id, 'delta')}
|
onDeltaSync={() => void runServerAction(srv.id, 'delta')}
|
||||||
@@ -403,10 +425,15 @@ export default function LibraryIndexSection() {
|
|||||||
type="button"
|
type="button"
|
||||||
className="btn btn-surface"
|
className="btn btn-surface"
|
||||||
style={{ fontSize: 12, padding: '4px 10px' }}
|
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)}
|
onClick={() => void handleIncludeServer(srv.id)}
|
||||||
>
|
>
|
||||||
{t('settings.libraryIndexIncludeServer')}
|
{includingServerId === srv.id
|
||||||
|
? t('settings.libraryIndexIncludingServer')
|
||||||
|
: t('settings.libraryIndexIncludeServer')}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ interface LibraryIndexServerRowProps {
|
|||||||
connection: LibraryServerConnection;
|
connection: LibraryServerConnection;
|
||||||
progressLabel: string | null;
|
progressLabel: string | null;
|
||||||
busy: boolean;
|
busy: boolean;
|
||||||
|
including: boolean;
|
||||||
excluding: boolean;
|
excluding: boolean;
|
||||||
actionsDisabled: boolean;
|
actionsDisabled: boolean;
|
||||||
onFullSync: () => void;
|
onFullSync: () => void;
|
||||||
@@ -34,6 +35,7 @@ export default function LibraryIndexServerRow({
|
|||||||
connection,
|
connection,
|
||||||
progressLabel,
|
progressLabel,
|
||||||
busy,
|
busy,
|
||||||
|
including,
|
||||||
excluding,
|
excluding,
|
||||||
actionsDisabled,
|
actionsDisabled,
|
||||||
onFullSync,
|
onFullSync,
|
||||||
@@ -93,6 +95,9 @@ export default function LibraryIndexServerRow({
|
|||||||
{busy && (
|
{busy && (
|
||||||
<span style={{ fontSize: 11, color: 'var(--accent)' }}>{t('settings.libraryIndexServerSyncing')}</span>
|
<span style={{ fontSize: 11, color: 'var(--accent)' }}>{t('settings.libraryIndexServerSyncing')}</span>
|
||||||
)}
|
)}
|
||||||
|
{including && !busy && (
|
||||||
|
<span style={{ fontSize: 11, color: 'var(--accent)' }}>{t('settings.libraryIndexIncludingServer')}</span>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div style={{ fontSize: 12, color: 'var(--text-muted)', marginTop: 4, lineHeight: 1.45 }}>
|
<div style={{ fontSize: 12, color: 'var(--text-muted)', marginTop: 4, lineHeight: 1.45 }}>
|
||||||
{phaseLabel}
|
{phaseLabel}
|
||||||
|
|||||||
@@ -264,6 +264,7 @@ export const settings = {
|
|||||||
libraryIndexExcludingServer: 'Wird ausgeschlossen…',
|
libraryIndexExcludingServer: 'Wird ausgeschlossen…',
|
||||||
libraryIndexExcludedTitle: 'Von Synchronisation ausgeschlossen',
|
libraryIndexExcludedTitle: 'Von Synchronisation ausgeschlossen',
|
||||||
libraryIndexIncludeServer: 'Wieder einschließen',
|
libraryIndexIncludeServer: 'Wieder einschließen',
|
||||||
|
libraryIndexIncludingServer: 'Wird eingeschlossen…',
|
||||||
libraryIndexStatus: 'Status',
|
libraryIndexStatus: 'Status',
|
||||||
libraryIndexStatusIdle: 'Bereit',
|
libraryIndexStatusIdle: 'Bereit',
|
||||||
libraryIndexStatusProbing: 'Server wird geprüft…',
|
libraryIndexStatusProbing: 'Server wird geprüft…',
|
||||||
|
|||||||
@@ -267,6 +267,7 @@ export const settings = {
|
|||||||
libraryIndexExcludingServer: 'Excluding…',
|
libraryIndexExcludingServer: 'Excluding…',
|
||||||
libraryIndexExcludedTitle: 'Excluded from sync',
|
libraryIndexExcludedTitle: 'Excluded from sync',
|
||||||
libraryIndexIncludeServer: 'Include again',
|
libraryIndexIncludeServer: 'Include again',
|
||||||
|
libraryIndexIncludingServer: 'Including…',
|
||||||
libraryIndexStatus: 'Status',
|
libraryIndexStatus: 'Status',
|
||||||
libraryIndexStatusIdle: 'Idle',
|
libraryIndexStatusIdle: 'Idle',
|
||||||
libraryIndexStatusProbing: 'Checking server…',
|
libraryIndexStatusProbing: 'Checking server…',
|
||||||
|
|||||||
@@ -262,6 +262,7 @@ export const settings = {
|
|||||||
libraryIndexExcludingServer: 'Excluyendo…',
|
libraryIndexExcludingServer: 'Excluyendo…',
|
||||||
libraryIndexExcludedTitle: 'Excluidos de la sincronización',
|
libraryIndexExcludedTitle: 'Excluidos de la sincronización',
|
||||||
libraryIndexIncludeServer: 'Incluir de nuevo',
|
libraryIndexIncludeServer: 'Incluir de nuevo',
|
||||||
|
libraryIndexIncludingServer: 'Incluyendo…',
|
||||||
libraryIndexStatus: 'Estado',
|
libraryIndexStatus: 'Estado',
|
||||||
libraryIndexStatusIdle: 'Inactivo',
|
libraryIndexStatusIdle: 'Inactivo',
|
||||||
libraryIndexStatusProbing: 'Comprobando servidor…',
|
libraryIndexStatusProbing: 'Comprobando servidor…',
|
||||||
|
|||||||
@@ -260,6 +260,7 @@ export const settings = {
|
|||||||
libraryIndexExcludingServer: 'Exclusion…',
|
libraryIndexExcludingServer: 'Exclusion…',
|
||||||
libraryIndexExcludedTitle: 'Exclus de la synchronisation',
|
libraryIndexExcludedTitle: 'Exclus de la synchronisation',
|
||||||
libraryIndexIncludeServer: 'Réinclure',
|
libraryIndexIncludeServer: 'Réinclure',
|
||||||
|
libraryIndexIncludingServer: 'Réinclusion…',
|
||||||
libraryIndexStatus: 'État',
|
libraryIndexStatus: 'État',
|
||||||
libraryIndexStatusIdle: 'Inactif',
|
libraryIndexStatusIdle: 'Inactif',
|
||||||
libraryIndexStatusProbing: 'Vérification du serveur…',
|
libraryIndexStatusProbing: 'Vérification du serveur…',
|
||||||
|
|||||||
@@ -259,6 +259,7 @@ export const settings = {
|
|||||||
libraryIndexExcludingServer: 'Ekskluderer…',
|
libraryIndexExcludingServer: 'Ekskluderer…',
|
||||||
libraryIndexExcludedTitle: 'Ekskludert fra synkronisering',
|
libraryIndexExcludedTitle: 'Ekskludert fra synkronisering',
|
||||||
libraryIndexIncludeServer: 'Inkluder igjen',
|
libraryIndexIncludeServer: 'Inkluder igjen',
|
||||||
|
libraryIndexIncludingServer: 'Inkluderer…',
|
||||||
libraryIndexStatus: 'Status',
|
libraryIndexStatus: 'Status',
|
||||||
libraryIndexStatusIdle: 'Inaktiv',
|
libraryIndexStatusIdle: 'Inaktiv',
|
||||||
libraryIndexStatusProbing: 'Sjekker server…',
|
libraryIndexStatusProbing: 'Sjekker server…',
|
||||||
|
|||||||
@@ -260,6 +260,7 @@ export const settings = {
|
|||||||
libraryIndexExcludingServer: 'Uitsluiten…',
|
libraryIndexExcludingServer: 'Uitsluiten…',
|
||||||
libraryIndexExcludedTitle: 'Uitgesloten van synchronisatie',
|
libraryIndexExcludedTitle: 'Uitgesloten van synchronisatie',
|
||||||
libraryIndexIncludeServer: 'Weer opnemen',
|
libraryIndexIncludeServer: 'Weer opnemen',
|
||||||
|
libraryIndexIncludingServer: 'Opnemen…',
|
||||||
libraryIndexStatus: 'Status',
|
libraryIndexStatus: 'Status',
|
||||||
libraryIndexStatusIdle: 'Inactief',
|
libraryIndexStatusIdle: 'Inactief',
|
||||||
libraryIndexStatusProbing: 'Server controleren…',
|
libraryIndexStatusProbing: 'Server controleren…',
|
||||||
|
|||||||
@@ -266,6 +266,7 @@ export const settings = {
|
|||||||
libraryIndexExcludingServer: 'Se exclude…',
|
libraryIndexExcludingServer: 'Se exclude…',
|
||||||
libraryIndexExcludedTitle: 'Excluse din sincronizare',
|
libraryIndexExcludedTitle: 'Excluse din sincronizare',
|
||||||
libraryIndexIncludeServer: 'Include din nou',
|
libraryIndexIncludeServer: 'Include din nou',
|
||||||
|
libraryIndexIncludingServer: 'Se include…',
|
||||||
libraryIndexStatus: 'Stare',
|
libraryIndexStatus: 'Stare',
|
||||||
libraryIndexStatusIdle: 'Inactiv',
|
libraryIndexStatusIdle: 'Inactiv',
|
||||||
libraryIndexStatusProbing: 'Se verifică serverul…',
|
libraryIndexStatusProbing: 'Se verifică serverul…',
|
||||||
|
|||||||
@@ -272,6 +272,7 @@ export const settings = {
|
|||||||
libraryIndexExcludingServer: 'Отключается…',
|
libraryIndexExcludingServer: 'Отключается…',
|
||||||
libraryIndexExcludedTitle: 'Исключены из синхронизации',
|
libraryIndexExcludedTitle: 'Исключены из синхронизации',
|
||||||
libraryIndexIncludeServer: 'Включить снова',
|
libraryIndexIncludeServer: 'Включить снова',
|
||||||
|
libraryIndexIncludingServer: 'Подключается…',
|
||||||
libraryIndexStatus: 'Статус',
|
libraryIndexStatus: 'Статус',
|
||||||
libraryIndexStatusIdle: 'Ожидание',
|
libraryIndexStatusIdle: 'Ожидание',
|
||||||
libraryIndexStatusProbing: 'Проверка сервера…',
|
libraryIndexStatusProbing: 'Проверка сервера…',
|
||||||
|
|||||||
@@ -259,6 +259,7 @@ export const settings = {
|
|||||||
libraryIndexExcludingServer: '正在排除…',
|
libraryIndexExcludingServer: '正在排除…',
|
||||||
libraryIndexExcludedTitle: '已排除同步',
|
libraryIndexExcludedTitle: '已排除同步',
|
||||||
libraryIndexIncludeServer: '重新纳入',
|
libraryIndexIncludeServer: '重新纳入',
|
||||||
|
libraryIndexIncludingServer: '正在纳入…',
|
||||||
libraryIndexStatus: '状态',
|
libraryIndexStatus: '状态',
|
||||||
libraryIndexStatusIdle: '空闲',
|
libraryIndexStatusIdle: '空闲',
|
||||||
libraryIndexStatusProbing: '正在检查服务器…',
|
libraryIndexStatusProbing: '正在检查服务器…',
|
||||||
|
|||||||
Reference in New Issue
Block a user