fix(plex): round-2 deep-review follow-ups on the review fixes
- collections: the resync-failed-after-successful-write path now returns 422 (honest "reconcile on next sync" detail), NOT 502. The frontend treats 502/503/504 as gateway-unreachable and raises a sticky offline banner (api/core.ts RETRIABLE_GATEWAY); the app answered, so a 422 action-error toast is correct. Removes the false-offline regression the 502 introduced, and — since 422 isn't retried — the create double-write vector with it. - tests: make the mutate-then-resync rollback assertion non-vacuous — the resync stub now flushes a partial local change before raising, so the test proves db.rollback() actually undoes it; split into a write-failed (422) case and a resync-failed (422 + reconcile detail + rollback) case. - New-collection dup hint is now muted-tone with its own nameMaybeTaken copy (per-library uniqueness means the name may be free here) instead of the red authoritative nameTaken, which the still-enabled create flow contradicts. - history: cover the navCounter post-reload collision guard (ensureNavId advances the counter past a higher persisted sfScroll id).
This commit is contained in:
@@ -140,7 +140,7 @@ function NewCollectionPanel({
|
||||
className="glass-card w-full rounded-lg px-3 py-1.5 text-sm focus:outline-hidden focus:ring-2 focus:ring-accent/40"
|
||||
/>
|
||||
{dupHint ? (
|
||||
<p className="mt-1 mb-2 text-xs text-danger">{t("plex.collection.nameTaken")}</p>
|
||||
<p className="mt-1 mb-2 text-xs text-muted">{t("plex.collection.nameMaybeTaken")}</p>
|
||||
) : (
|
||||
<p className="mt-1 mb-2 text-xs text-muted">{t("plex.collection.newHint")}</p>
|
||||
)}
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
"enableEdit": "Enable editing",
|
||||
"removeItem": "Remove from collection",
|
||||
"removeConfirm": "Remove \"{{title}}\" from this collection? This affects all users on Plex.",
|
||||
"nameMaybeTaken": "A collection with this name may already exist in another library — you can still create it here.",
|
||||
"readOnly": "Read-only",
|
||||
"empty": "This collection has no items.",
|
||||
"more": "More",
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
"enableEdit": "Szerkesztés engedélyezése",
|
||||
"removeItem": "Eltávolítás a kollekcióból",
|
||||
"removeConfirm": "Eltávolítod a(z) „{{title}}” elemet ebből a kollekcióból? Ez a Plexen minden felhasználót érint.",
|
||||
"nameMaybeTaken": "Lehet, hogy már létezik ilyen nevű kollekció egy másik könyvtárban — itt akkor is létrehozhatod.",
|
||||
"readOnly": "Csak olvasható",
|
||||
"empty": "Ennek a kollekciónak nincsenek elemei.",
|
||||
"more": "Több",
|
||||
|
||||
@@ -42,6 +42,16 @@ describe("nav scroll ids", () => {
|
||||
expect(second).toBeGreaterThan(first);
|
||||
});
|
||||
|
||||
it("ensureNavId advances the counter past a higher persisted id (post-reload collision guard)", () => {
|
||||
// After a full reload the module counter resets to 0 while sfScroll ids persist in history.state.
|
||||
// Landing on an entry whose id is above the counter must push the counter past it, so the next
|
||||
// minted id can't duplicate a live back-stack entry and mis-restore its scroll.
|
||||
const high = 999999;
|
||||
window.history.replaceState({ sfScroll: high }, "");
|
||||
expect(ensureNavId()).toBe(high); // returns the persisted id, doesn't reassign
|
||||
expect(nextNavId()).toBeGreaterThan(high); // and the next mint clears it
|
||||
});
|
||||
|
||||
it("notifyNav fires subscribers until they unsubscribe", () => {
|
||||
const cb = vi.fn();
|
||||
const unsub = subscribeNav(cb);
|
||||
|
||||
Reference in New Issue
Block a user