modified web/src/routes/articles/[id]/+page.svelte +7 -6
| @@ -58,17 +58,18 @@ |
| 58 | 58 | annotations = data.annotations; |
| 59 | 59 | }); |
| 60 | 60 | |
| 61 | | - // Sync local optimistic state when navigating between articles. |
| 62 | | - $effect(() => { |
| 63 | | - read = data.article.is_read; |
| 64 | | - }); |
| 61 | + // Tracks which article id we've already auto-marked read, so the effect |
| 62 | + // fires once per navigation instead of fighting toggleRead's updates. |
| 63 | + let markedReadId = $state<number | null>(null); |
| 65 | 64 | |
| 66 | 65 | // Mark the article read on actual navigation. The server's detail handler |
| 67 | 66 | // no longer marks read, so a hover/touch preload won't mark every hovered |
| 68 | | - // card as read; only a real visit does. |
| 67 | + // card as read; only a real visit does. Runs once per article, so |
| 68 | + // toggleRead stays in control of subsequent state changes. |
| 69 | 69 | $effect(() => { |
| 70 | 70 | const id = data.article.id; |
| 71 | | - if (!read) { |
| 71 | + if (markedReadId !== id) { |
| 72 | + markedReadId = id; |
| 72 | 73 | read = true; |
| 73 | 74 | endpoints.markRead(id).catch(() => { |
| 74 | 75 | if (data.article.id === id) read = false; |
| @@ -58,17 +58,18 @@ |
| 58 | annotations = data.annotations; | 58 | annotations = data.annotations; |
| 59 | }); | 59 | }); |
| 60 | | 60 | |
| 61 | - // Sync local optimistic state when navigating between articles. | 61 | + // Tracks which article id we've already auto-marked read, so the effect |
| 62 | - $effect(() => { | 62 | + // fires once per navigation instead of fighting toggleRead's updates. |
| 63 | - read = data.article.is_read; | 63 | + let markedReadId = $state<number | null>(null); |
| 64 | - }); | | |
| 65 | | 64 | |
| 66 | // Mark the article read on actual navigation. The server's detail handler | 65 | // Mark the article read on actual navigation. The server's detail handler |
| 67 | // no longer marks read, so a hover/touch preload won't mark every hovered | 66 | // no longer marks read, so a hover/touch preload won't mark every hovered |
| 68 | - // card as read; only a real visit does. | 67 | + // card as read; only a real visit does. Runs once per article, so |
| | 68 | + // toggleRead stays in control of subsequent state changes. |
| 69 | $effect(() => { | 69 | $effect(() => { |
| 70 | const id = data.article.id; | 70 | const id = data.article.id; |
| 71 | - if (!read) { | 71 | + if (markedReadId !== id) { |
| | 72 | + markedReadId = id; |
| 72 | read = true; | 73 | read = true; |
| 73 | endpoints.markRead(id).catch(() => { | 74 | endpoints.markRead(id).catch(() => { |
| 74 | if (data.article.id === id) read = false; | 75 | if (data.article.id === id) read = false; |