mirror of
https://github.com/kilyabin/psysonic.git
synced 2026-07-22 14:35:41 +00:00
feat(servers): scan actions + edit existing server profiles (#780)
* feat(server-scan): plumbing for triggering library scans
Adds `startScan` / `getScanStatus` against the Subsonic API
(`fullScan=true` is Navidrome's extension), a small per-server scan
store, and a global polling hook (2 s cadence) that emits a toast when
each scan finishes. Scans can run on any configured server, including
inactive ones, by reusing `apiForServer`.
UI surfaces follow in the next commit.
* feat(server-scan): expose Quick / Full Scan in switcher + settings cards
Adds a `ServerScanActions` component with two variants (compact for the
server-switcher dropdown, card for the Settings server cards) backed by
the scan store from the previous commit. Full Scan requires a second
click within 3 s to confirm, matching the playlist-delete pattern.
Status slot shows a spinner with running track count while scanning, a
green check when finished, and a red icon on error.
The switcher row is converted from a single button to a flex container
so per-server scan controls don't hijack the server-switch click.
i18n added across all 9 locales.
* fix(server-scan): reorder switcher row to check / name / scan actions
Moves the check / spinner slot from the right edge to the left so the
spinner pop-in on server switch doesn't sit next to the scan icons.
Removes the layout shift that briefly hovered the Quick scan button
when the row re-rendered.
* feat(servers): edit existing server profiles in Settings → Servers
* Pencil-button on each server card opens an inline edit form that
replaces the card (prefilled name / URL / username / password).
* `AddServerForm` reused with an `editingServer` prop — title flips to
"Edit Server", submit label to "Save", magic-string field hidden (the
edit scope is manual fields; magic-string remains an add-time invite
shortcut).
* Edit saves unconditionally — ping runs post-save as a status indicator
(analog to the existing Test button) instead of gating the save. Lets
users update a profile when the server is currently unreachable.
* Translations across all 9 locales (`editServer`, `editServerTitle`).
* fix(servers): submit Add/Edit Server form on Enter
Wrapped the form body in a real <form>, made the submit button
type="submit", marked Cancel as type="button" so Enter no longer
cancels. Add-Mode now also responds to Enter — same flow, consistent
across both modes.
* fix(servers): collapse card action buttons to icon-only on narrow screens
* Quick-Scan / Full-Scan / Test buttons in each server card hide their
text label below 1100px viewport via the .server-card-btn-label class
and a single media query in connection-indicator.css.
* Labels remain accessible via data-tooltip and aria-label so screen
readers + hover both keep working in the collapsed state.
* No content reflow above the breakpoint — pure additive CSS.
* fix(servers): include Use button in icon-only narrow-screen collapse
The Use ("Verwenden") button on inactive server cards lacked the
.server-card-btn-label wrapper, so its text stayed visible at narrow
viewports and pushed Edit/Delete off-screen. Added a Power icon and
wrapped the label so it collapses alongside the other action buttons.
* docs(changelog,credits): #780 server scan + edit
This commit is contained in:
committed by
GitHub
parent
562218f447
commit
bca45d5a80
@@ -9,6 +9,7 @@ import { useAuthStore } from '../store/authStore';
|
||||
import { switchActiveServer } from '../utils/server/switchActiveServer';
|
||||
import { showToast } from '../utils/ui/toast';
|
||||
import { serverListDisplayLabel } from '../utils/server/serverDisplayName';
|
||||
import ServerScanActions from './settings/ServerScanActions';
|
||||
|
||||
interface Props {
|
||||
status: ConnectionStatus;
|
||||
@@ -163,23 +164,34 @@ export default function ConnectionIndicator({ status, isLan, serverName }: Props
|
||||
const busy = switchingId !== null;
|
||||
const labelText = serverListDisplayLabel(srv, servers);
|
||||
return (
|
||||
<button
|
||||
<div
|
||||
key={srv.id}
|
||||
type="button"
|
||||
role="menuitem"
|
||||
className={`nav-library-dropdown-item${active ? ' nav-library-dropdown-item--selected' : ''}`}
|
||||
disabled={busy}
|
||||
onClick={() => onPickServer(srv)}
|
||||
style={{ padding: 0 }}
|
||||
>
|
||||
<span className="nav-library-dropdown-item-label">{labelText}</span>
|
||||
{switchingId === srv.id ? (
|
||||
<div className="spinner" style={{ width: 14, height: 14, flexShrink: 0 }} aria-hidden />
|
||||
) : active ? (
|
||||
<Check size={16} className="nav-library-dropdown-check" aria-hidden />
|
||||
) : (
|
||||
<span className="nav-library-dropdown-check-spacer" aria-hidden />
|
||||
)}
|
||||
</button>
|
||||
<span style={{ flexShrink: 0, marginLeft: 'var(--space-3)', display: 'inline-flex', width: 16, height: 16, alignItems: 'center', justifyContent: 'center' }} aria-hidden>
|
||||
{switchingId === srv.id ? (
|
||||
<div className="spinner" style={{ width: 14, height: 14 }} />
|
||||
) : active ? (
|
||||
<Check size={16} className="nav-library-dropdown-check" />
|
||||
) : null}
|
||||
</span>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => onPickServer(srv)}
|
||||
disabled={busy}
|
||||
className="nav-library-dropdown-item-label"
|
||||
style={{
|
||||
flex: 1, minWidth: 0, padding: 'var(--space-2) var(--space-3)',
|
||||
background: 'transparent', border: 'none', color: 'inherit',
|
||||
font: 'inherit', textAlign: 'left', cursor: busy ? 'default' : 'pointer',
|
||||
}}
|
||||
>
|
||||
{labelText}
|
||||
</button>
|
||||
<ServerScanActions serverId={srv.id} variant="compact" />
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
<div
|
||||
|
||||
Reference in New Issue
Block a user