From 3b10a1f47fd5838fe3b94c19673a52610b88cf1e Mon Sep 17 00:00:00 2001
From: Fuwn <contact@fuwn.net>
Date: Sun, 1 Mar 2026 14:20:08 -0800
Subject: perf: optimise list hot paths and shared timers

---
 src/lib/List/Anime/CleanAnimeList.svelte | 83 ++++++++++++++++++++++----------
 1 file changed, 58 insertions(+), 25 deletions(-)

(limited to 'src/lib/List/Anime/CleanAnimeList.svelte')

diff --git a/src/lib/List/Anime/CleanAnimeList.svelte b/src/lib/List/Anime/CleanAnimeList.svelte
index 561b3b1d..aee035a4 100644
--- a/src/lib/List/Anime/CleanAnimeList.svelte
+++ b/src/lib/List/Anime/CleanAnimeList.svelte
@@ -37,7 +37,8 @@
   export let limit: number | undefined = undefined;
 
   let showRoulette = false;
-  let keyCacher: ReturnType<typeof setInterval>;
+  let airingRefreshTimeout: ReturnType<typeof setTimeout> | undefined;
+  let scheduledAiringAt: number | null = null;
   let totalEpisodeDueCount = media
     .map((anime) => {
       if ($settings.displayTotalEpisodes && !$settings.displayTotalDueEpisodes) return 1;
@@ -81,35 +82,61 @@
       ? media
       : media.filter((m) => m.mediaListEntry?.customLists?.[selectedList]);
 
-  onMount(async () => {
-    if (dummy) return;
+  const clearAiringRefreshTimeout = () => {
+    if (airingRefreshTimeout) clearTimeout(airingRefreshTimeout);
+
+    airingRefreshTimeout = undefined;
+    scheduledAiringAt = null;
+  };
+
+  const scheduleAiringRefresh = () => {
+    if (!browser) return;
+
+    if (dummy || media.length === 0) {
+      clearAiringRefreshTimeout();
+
+      return;
+    }
+
+    const nextAiringAt = media.reduce<number | null>((closest, currentMedia) => {
+      if (currentMedia.status !== 'RELEASING' && currentMedia.status !== 'NOT_YET_RELEASED')
+        return closest;
+
+      const airingAt = currentMedia.nextAiringEpisode?.airingAt;
+
+      if (!airingAt) return closest;
+      if (closest === null) return airingAt;
+
+      return airingAt < closest ? airingAt : closest;
+    }, null);
 
-    keyCacher = setInterval(
+    if (!nextAiringAt) {
+      clearAiringRefreshTimeout();
+
+      return;
+    }
+
+    if (airingRefreshTimeout && scheduledAiringAt === nextAiringAt) return;
+
+    clearAiringRefreshTimeout();
+    scheduledAiringAt = nextAiringAt;
+    airingRefreshTimeout = setTimeout(
       () => {
-        media = media;
+        const now = Date.now() / 1000;
 
-        if (
-          media.some(
-            (m) => m.nextAiringEpisode?.airingAt && m.nextAiringEpisode.airingAt < Date.now() / 1000
-          )
-        )
+        if (media.some((m) => m.nextAiringEpisode?.airingAt && m.nextAiringEpisode.airingAt < now))
           animeLists = cleanCache(user, $identity);
+
+        scheduleAiringRefresh();
       },
-      (() => {
-        const airingAt = media
-          .filter(
-            (m) =>
-              (m.status === 'RELEASING' || m.status === 'NOT_YET_RELEASED') &&
-              m.nextAiringEpisode?.airingAt
-          )
-          .find((m) => m.nextAiringEpisode?.airingAt)?.nextAiringEpisode?.airingAt;
-        const untilAiring = airingAt
-          ? Math.round((airingAt - Date.now() / 1000) * 100) / 100
-          : undefined;
-
-        return untilAiring ? (untilAiring < 0 ? 1000 : untilAiring) : 1000;
-      })()
+      Math.max(1000, nextAiringAt * 1000 - Date.now() + 250)
     );
+  };
+
+  onMount(async () => {
+    if (dummy) return;
+
+    scheduleAiringRefresh();
 
     if (browser)
       await localforage.setItem(
@@ -120,7 +147,13 @@
       );
   });
 
-  onDestroy(() => clearInterval(keyCacher));
+  $: if (browser && !dummy) {
+    media;
+
+    scheduleAiringRefresh();
+  }
+
+  onDestroy(() => clearAiringRefreshTimeout());
 
   const increment = (anime: Media, progress: number) => {
     if (!dummy && pendingUpdate !== anime.id) {
-- 
cgit v1.2.3

