aboutsummaryrefslogtreecommitdiff
path: root/src/lib/List/Anime/CleanAnimeList.svelte
diff options
context:
space:
mode:
authorFuwn <[email protected]>2026-03-01 14:20:08 -0800
committerFuwn <[email protected]>2026-03-01 15:24:03 -0800
commit3b10a1f47fd5838fe3b94c19673a52610b88cf1e (patch)
treed468a1fc12290e38686b255194ff6596b58cbf01 /src/lib/List/Anime/CleanAnimeList.svelte
parentperf(match): fast-path exact normalised title matches (diff)
downloaddue.moe-3b10a1f47fd5838fe3b94c19673a52610b88cf1e.tar.xz
due.moe-3b10a1f47fd5838fe3b94c19673a52610b88cf1e.zip
perf: optimise list hot paths and shared timers
Diffstat (limited to 'src/lib/List/Anime/CleanAnimeList.svelte')
-rw-r--r--src/lib/List/Anime/CleanAnimeList.svelte83
1 files changed, 58 insertions, 25 deletions
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) {