finished_at lands on series (0016 seeds it from the pre-flip finished bookmarks, then flips those bookmarks to archived), the poll gate and eligible count read the flag instead of a per-Reader vote, PUT rejects the finished status like any unknown value, and the web UI drops the Finished tab, badge and finish button. Novel merge rank is archived > reading. Per-Reader disagreement (one Reader keeps a finished Series in reading forever) is what the flag repairs; the cutover keeps polling state unchanged for every Series.
This commit is contained in:
@@ -268,8 +268,9 @@
|
||||
// Duplicate case (spec user story 2): one row must survive, and it is
|
||||
// the one already under the repaired key — with the stale row's
|
||||
// progress carried across when it is ahead, favourite OR'd, and the
|
||||
// stronger lifecycle bucket kept (finished > archived > reading, so a
|
||||
// merge can never silently un-archive or un-finish a row).
|
||||
// stronger lifecycle bucket kept (archived > reading, so a merge can
|
||||
// never silently un-archive a row). finished is not a bucket the
|
||||
// script can see (issue #157).
|
||||
const merged = Object.assign({}, existing);
|
||||
if (
|
||||
existing.last_chapter_num == null ||
|
||||
@@ -280,7 +281,7 @@
|
||||
merged.last_chapter_url = stale.last_chapter_url;
|
||||
}
|
||||
merged.favorite = !!(existing.favorite || stale.favorite);
|
||||
const rank = (s) => ({ finished: 2, archived: 1, reading: 0 }[s || "reading"] || 0);
|
||||
const rank = (s) => ({ archived: 1, reading: 0 }[s || "reading"] || 0);
|
||||
merged.status = rank(stale.status) > rank(existing.status) ? stale.status : existing.status;
|
||||
merged.updated_at = Math.max(existing.updated_at || 0, stale.updated_at || 0);
|
||||
listOut = list.filter((b) => b.key !== oldKey).map((b) => (b.key === newKey ? merged : b));
|
||||
@@ -405,9 +406,10 @@
|
||||
}
|
||||
|
||||
// Only an explicit archive/restore has an opinion about the bucket. Every
|
||||
// other write omits `status`, so the server keeps the stored one — otherwise
|
||||
// a cached value would resend "finished" (which the API rejects with 400) or
|
||||
// silently un-archive a series archived on another device.
|
||||
// other write omits `status`, so the server keeps the stored one —
|
||||
// otherwise a cached value would resend a stale status (the API rejects
|
||||
// unknown values with 400) or silently un-archive a series archived on
|
||||
// another device.
|
||||
async function apiPut(key, obj, { sendStatus = false } = {}) {
|
||||
const body = Object.assign({}, obj);
|
||||
if (!sendStatus) delete body.status;
|
||||
@@ -792,8 +794,8 @@
|
||||
}
|
||||
|
||||
// Archive parks a series: it leaves All and Favourites but the server keeps
|
||||
// polling it for new chapters. "finished" is deliberately not reachable from
|
||||
// here — the API rejects that value, it is a web-UI decision.
|
||||
// polling it for new chapters. finished is not a bucket the script can
|
||||
// reach — it is a fact about the Series now (issue #157).
|
||||
async function toggleArchive(key) {
|
||||
const existing = state.byKey[key];
|
||||
if (!existing) return;
|
||||
@@ -878,7 +880,6 @@
|
||||
const now = Date.now();
|
||||
const due = state.list
|
||||
.filter((b) => b.site === site && b.series_url)
|
||||
.filter((b) => statusOf(b) !== "finished")
|
||||
.filter((b) => now - (checked[b.key] || 0) >= LATEST_CHECK_THROTTLE_MS)
|
||||
.sort((a, b) => (checked[a.key] || 0) - (checked[b.key] || 0))
|
||||
.slice(0, LATEST_CHECK_BATCH);
|
||||
@@ -1241,8 +1242,8 @@
|
||||
}
|
||||
|
||||
// Tabs narrow what is drawn; state.list always holds every bookmark.
|
||||
// Archived rows are hidden from All and Favourites, and finished ones —
|
||||
// which only the web UI can set — are hidden from every tab.
|
||||
// Archived rows are hidden from All and Favourites; a Series that is
|
||||
// finished is the web UI's business and arrives archived (issue #157).
|
||||
for (const [id, tab] of [["tabAll", "all"], ["tabFav", "favorites"], ["tabArc", "archived"]]) {
|
||||
root.getElementById(id).classList.toggle("active", activeTab === tab);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user