fix(stats): fix data loss and error handling in anime merge/move

- flush pending telemetry before merge/move so in-progress session time isn't dropped from lifetime totals
- repoint subtitle lines by video_id instead of anime_id so lines recorded before the async title parse assigns a link aren't stranded
- stop absorbing metadata into the target when moving an episode out of an emptied entry (a move isn't a same-show claim)
- return 404 only for missing episode/target, not storage failures; return 404 when a merge folds nothing
- keep AnimeMergeDialog open while a merge is in flight instead of letting dismiss race the request
- surface library load failures in LibraryEntryPicker instead of showing an empty list
This commit is contained in:
2026-08-10 23:06:00 -07:00
parent 9c503fc67d
commit 5abc3b84f4
9 changed files with 164 additions and 17 deletions
@@ -25,6 +25,7 @@ export function LibraryEntryPicker({
onClose,
}: LibraryEntryPickerProps) {
const [entries, setEntries] = useState<AnimeLibraryItem[] | null>(null);
const [loadFailed, setLoadFailed] = useState(false);
const [query, setQuery] = useState(initialQuery);
const inputRef = useRef<HTMLInputElement>(null);
@@ -37,7 +38,11 @@ export function LibraryEntryPicker({
if (!cancelled) setEntries(data);
})
.catch(() => {
if (!cancelled) setEntries([]);
// Distinct from an empty library: telling the user "no other titles"
// when the request failed hides a retryable error.
if (cancelled) return;
setEntries([]);
setLoadFailed(true);
});
return () => {
cancelled = true;
@@ -84,7 +89,12 @@ export function LibraryEntryPicker({
<div className="flex-1 overflow-y-auto p-2">
{entries === null && <div className="text-xs text-ctp-overlay2 p-3">Loading...</div>}
{entries !== null && visible.length === 0 && (
{loadFailed && (
<div className="text-xs text-ctp-red p-3">
Could not load the library. Close this dialog and try again.
</div>
)}
{!loadFailed && entries !== null && visible.length === 0 && (
<div className="text-xs text-ctp-overlay2 p-3">No other titles</div>
)}
{visible.map((entry) => (