diff options
| author | Fuwn <[email protected]> | 2026-03-01 16:20:51 -0800 |
|---|---|---|
| committer | Fuwn <[email protected]> | 2026-03-01 16:21:02 -0800 |
| commit | eae5d24d9e79e59a19d4721caaeaa0ca650ecb33 (patch) | |
| tree | 1b685bb248e051dfa26d2bfdebe6689402dd93c5 /src/lib/List/Anime | |
| parent | chore(tooling): remove legacy eslint and prettier (diff) | |
| download | due.moe-eae5d24d9e79e59a19d4721caaeaa0ca650ecb33.tar.xz due.moe-eae5d24d9e79e59a19d4721caaeaa0ca650ecb33.zip | |
chore(biome): drop formatter style overrides
Diffstat (limited to 'src/lib/List/Anime')
| -rw-r--r-- | src/lib/List/Anime/AnimeListTemplate.svelte | 58 | ||||
| -rw-r--r-- | src/lib/List/Anime/CleanAnimeList.svelte | 253 | ||||
| -rw-r--r-- | src/lib/List/Anime/CompletedAnimeList.svelte | 181 | ||||
| -rw-r--r-- | src/lib/List/Anime/DueAnimeList.svelte | 222 | ||||
| -rw-r--r-- | src/lib/List/Anime/DueIndexColumn.svelte | 16 | ||||
| -rw-r--r-- | src/lib/List/Anime/PlaceholderList.svelte | 8 | ||||
| -rw-r--r-- | src/lib/List/Anime/UpcomingAnimeList.svelte | 148 |
7 files changed, 493 insertions, 393 deletions
diff --git a/src/lib/List/Anime/AnimeListTemplate.svelte b/src/lib/List/Anime/AnimeListTemplate.svelte index 8f2846c6..2bf6df77 100644 --- a/src/lib/List/Anime/AnimeListTemplate.svelte +++ b/src/lib/List/Anime/AnimeListTemplate.svelte @@ -1,27 +1,27 @@ <script lang="ts"> /* eslint svelte/no-at-html-tags: "off" */ -import type { AniListAuthorisation } from '$lib/Data/AniList/identity'; -import type { Media } from '$lib/Data/AniList/media'; -import RateLimitedError from '$lib/Error/RateLimited.svelte'; -import settings from '$stores/settings'; -import CleanAnimeList from './CleanAnimeList.svelte'; -import ListTitle from '../ListTitle.svelte'; -import type { SubsPlease } from '$lib/Media/Anime/Airing/Subtitled/subsPlease'; -import PlaceholderList from './PlaceholderList.svelte'; -import { browser } from '$app/environment'; -import { onMount } from 'svelte'; -import subsPlease from '$stores/subsPlease'; -import identity from '$stores/identity'; -import localforage from 'localforage'; -import type { Title } from '../mediaTitle'; +import type { AniListAuthorisation } from "$lib/Data/AniList/identity"; +import type { Media } from "$lib/Data/AniList/media"; +import RateLimitedError from "$lib/Error/RateLimited.svelte"; +import settings from "$stores/settings"; +import CleanAnimeList from "./CleanAnimeList.svelte"; +import ListTitle from "../ListTitle.svelte"; +import type { SubsPlease } from "$lib/Media/Anime/Airing/Subtitled/subsPlease"; +import PlaceholderList from "./PlaceholderList.svelte"; +import { browser } from "$app/environment"; +import { onMount } from "svelte"; +import subsPlease from "$stores/subsPlease"; +import identity from "$stores/identity"; +import localforage from "localforage"; +import type { Title } from "../mediaTitle"; export let endTime: number; export let cleanMedia: ( - media: Media[], - displayUnresolved: boolean, - subsPlease: SubsPlease | null, - plannedOnly?: boolean + media: Media[], + displayUnresolved: boolean, + subsPlease: SubsPlease | null, + plannedOnly?: boolean, ) => Media[]; export let animeLists: Promise<Media[]>; export let user: AniListAuthorisation; @@ -40,15 +40,21 @@ let pendingUpdate: number | null = null; let lastListSize = 8; onMount(async () => { - if (browser) { - const lastStoredList = (await localforage.getItem( - `last${ - notYetReleased ? 'NotYetReleased' : upcoming ? 'Upcoming' : completed ? 'Completed' : '' - }AnimeListLength` - )) as string | null; + if (browser) { + const lastStoredList = (await localforage.getItem( + `last${ + notYetReleased + ? "NotYetReleased" + : upcoming + ? "Upcoming" + : completed + ? "Completed" + : "" + }AnimeListLength`, + )) as string | null; - if (lastStoredList) lastListSize = parseInt(lastStoredList); - } + if (lastStoredList) lastListSize = parseInt(lastStoredList); + } }); </script> diff --git a/src/lib/List/Anime/CleanAnimeList.svelte b/src/lib/List/Anime/CleanAnimeList.svelte index 14406f04..d1efa3a8 100644 --- a/src/lib/List/Anime/CleanAnimeList.svelte +++ b/src/lib/List/Anime/CleanAnimeList.svelte @@ -1,25 +1,25 @@ <script lang="ts"> -import Spacer from '$lib/Layout/Spacer.svelte'; +import Spacer from "$lib/Layout/Spacer.svelte"; /* eslint svelte/no-at-html-tags: "off" */ -import settings from '$stores/settings'; -import type { Media } from '$lib/Data/AniList/media'; -import { cleanCache, incrementMediaProgress } from '$lib/Media/Anime/cache'; -import { totalEpisodes } from '$lib/Media/Anime/episodes'; -import type { AniListAuthorisation } from '$lib/Data/AniList/identity'; -import ListTitle from '../ListTitle.svelte'; -import { onDestroy, onMount } from 'svelte'; -import AiringTime from '$lib/Media/Anime/Airing/AiringTime.svelte'; -import { browser } from '$app/environment'; -import identity from '$stores/identity'; -import '../covers.css'; -import revalidateAnime from '$stores/revalidateAnime'; -import CleanGrid from '$lib/List/CleanGrid.svelte'; -import CleanList from '../CleanList.svelte'; -import stateBin from '$stores/stateBin'; -import localforage from 'localforage'; -import MediaRoulette from '../MediaRoulette.svelte'; -import type { Title } from '../mediaTitle'; +import settings from "$stores/settings"; +import type { Media } from "$lib/Data/AniList/media"; +import { cleanCache, incrementMediaProgress } from "$lib/Media/Anime/cache"; +import { totalEpisodes } from "$lib/Media/Anime/episodes"; +import type { AniListAuthorisation } from "$lib/Data/AniList/identity"; +import ListTitle from "../ListTitle.svelte"; +import { onDestroy, onMount } from "svelte"; +import AiringTime from "$lib/Media/Anime/Airing/AiringTime.svelte"; +import { browser } from "$app/environment"; +import identity from "$stores/identity"; +import "../covers.css"; +import revalidateAnime from "$stores/revalidateAnime"; +import CleanGrid from "$lib/List/CleanGrid.svelte"; +import CleanList from "../CleanList.svelte"; +import stateBin from "$stores/stateBin"; +import localforage from "localforage"; +import MediaRoulette from "../MediaRoulette.svelte"; +import type { Title } from "../mediaTitle"; export let media: Media[]; export let title: Title; @@ -40,136 +40,165 @@ let showRoulette = false; let airingRefreshTimeout: ReturnType<typeof setTimeout> | undefined; let scheduledAiringAt: number | null = null; let totalEpisodeDueCount = media - .map((anime) => { - if ($settings.displayTotalEpisodes && !$settings.displayTotalDueEpisodes) return 1; - - if ($settings.displayTotalDueEpisodes && completed && !$settings.displayTotalEpisodes) return 1; - - if ($settings.displayTotalEpisodes && anime.status === 'FINISHED') - return anime.episodes - (anime.mediaListEntry?.progress || 0); - - if (anime.status === 'NOT_YET_RELEASED') return 1; - - return ( - (anime.nextAiringEpisode?.episode || 1) - - (anime.mediaListEntry?.progress || 0) - - (upcoming || notYetReleased ? 0 : 1) - ); - }) - .reduce((a, b) => a + b, 0); + .map((anime) => { + if ($settings.displayTotalEpisodes && !$settings.displayTotalDueEpisodes) + return 1; + + if ( + $settings.displayTotalDueEpisodes && + completed && + !$settings.displayTotalEpisodes + ) + return 1; + + if ($settings.displayTotalEpisodes && anime.status === "FINISHED") + return anime.episodes - (anime.mediaListEntry?.progress || 0); + + if (anime.status === "NOT_YET_RELEASED") return 1; + + return ( + (anime.nextAiringEpisode?.episode || 1) - + (anime.mediaListEntry?.progress || 0) - + (upcoming || notYetReleased ? 0 : 1) + ); + }) + .reduce((a, b) => a + b, 0); const lists = Array.from( - new Set( - media - .flatMap((m) => Object.entries(m.mediaListEntry?.customLists ?? {})) - .filter(([_key, value]) => value) - .map(([key]) => key) - ) + new Set( + media + .flatMap((m) => Object.entries(m.mediaListEntry?.customLists ?? {})) + .filter(([_key, value]) => value) + .map(([key]) => key), + ), ); let filterKind = upcoming - ? 'Upcoming' - : notYetReleased - ? 'NotYetReleased' - : completed - ? 'Completed' - : 'Due'; + ? "Upcoming" + : notYetReleased + ? "NotYetReleased" + : completed + ? "Completed" + : "Due"; const filterKey = `${filterKind}AnimeListFilter`; -$: selectedList = disableFilter ? 'All' : ($stateBin[filterKey] as string) || 'All'; +$: selectedList = disableFilter + ? "All" + : ($stateBin[filterKey] as string) || "All"; $: filteredMedia = - selectedList === 'All' || !$settings.displayMediaListFilter - ? media - : media.filter((m) => m.mediaListEntry?.customLists?.[selectedList]); + selectedList === "All" || !$settings.displayMediaListFilter + ? media + : media.filter((m) => m.mediaListEntry?.customLists?.[selectedList]); const clearAiringRefreshTimeout = () => { - if (airingRefreshTimeout) clearTimeout(airingRefreshTimeout); + if (airingRefreshTimeout) clearTimeout(airingRefreshTimeout); - airingRefreshTimeout = undefined; - scheduledAiringAt = null; + airingRefreshTimeout = undefined; + scheduledAiringAt = null; }; const scheduleAiringRefresh = () => { - if (!browser) return; + if (!browser) return; - if (dummy || media.length === 0) { - clearAiringRefreshTimeout(); + if (dummy || media.length === 0) { + clearAiringRefreshTimeout(); - return; - } + return; + } - const nextAiringAt = media.reduce<number | null>((closest, currentMedia) => { - if (currentMedia.status !== 'RELEASING' && currentMedia.status !== 'NOT_YET_RELEASED') - return closest; + const nextAiringAt = media.reduce<number | null>((closest, currentMedia) => { + if ( + currentMedia.status !== "RELEASING" && + currentMedia.status !== "NOT_YET_RELEASED" + ) + return closest; - const airingAt = currentMedia.nextAiringEpisode?.airingAt; + const airingAt = currentMedia.nextAiringEpisode?.airingAt; - if (!airingAt) return closest; - if (closest === null) return airingAt; + if (!airingAt) return closest; + if (closest === null) return airingAt; - return airingAt < closest ? airingAt : closest; - }, null); + return airingAt < closest ? airingAt : closest; + }, null); - if (!nextAiringAt) { - clearAiringRefreshTimeout(); + if (!nextAiringAt) { + clearAiringRefreshTimeout(); - return; - } + return; + } - if (airingRefreshTimeout && scheduledAiringAt === nextAiringAt) return; + if (airingRefreshTimeout && scheduledAiringAt === nextAiringAt) return; - clearAiringRefreshTimeout(); - scheduledAiringAt = nextAiringAt; - airingRefreshTimeout = setTimeout( - () => { - const now = Date.now() / 1000; + clearAiringRefreshTimeout(); + scheduledAiringAt = nextAiringAt; + airingRefreshTimeout = setTimeout( + () => { + const now = Date.now() / 1000; - if (media.some((m) => m.nextAiringEpisode?.airingAt && m.nextAiringEpisode.airingAt < now)) - animeLists = cleanCache(user, $identity); + if ( + media.some( + (m) => + m.nextAiringEpisode?.airingAt && m.nextAiringEpisode.airingAt < now, + ) + ) + animeLists = cleanCache(user, $identity); - scheduleAiringRefresh(); - }, - Math.max(1000, nextAiringAt * 1000 - Date.now() + 250) - ); + scheduleAiringRefresh(); + }, + Math.max(1000, nextAiringAt * 1000 - Date.now() + 250), + ); }; onMount(async () => { - if (dummy) return; - - scheduleAiringRefresh(); - - if (browser) - await localforage.setItem( - `last${ - notYetReleased ? 'NotYetReleased' : upcoming ? 'Upcoming' : completed ? 'Completed' : '' - }AnimeListLength`, - media.length.toString() - ); + if (dummy) return; + + scheduleAiringRefresh(); + + if (browser) + await localforage.setItem( + `last${ + notYetReleased + ? "NotYetReleased" + : upcoming + ? "Upcoming" + : completed + ? "Completed" + : "" + }AnimeListLength`, + media.length.toString(), + ); }); $: if (browser && !dummy) { - media; + media; - scheduleAiringRefresh(); + scheduleAiringRefresh(); } onDestroy(() => clearAiringRefreshTimeout()); const increment = (anime: Media, progress: number) => { - if (!dummy && pendingUpdate !== anime.id) { - $revalidateAnime = true; - lastUpdatedMedia = anime.id; - pendingUpdate = anime.id; - - incrementMediaProgress(anime.id, anime.mediaListEntry?.progress, user, () => { - const mediaListEntry = media.find((m) => m.id === anime.id)?.mediaListEntry; - - if (mediaListEntry) mediaListEntry.progress = progress + 1; - - previousAnimeList = media; - animeLists = cleanCache(user, $identity); - pendingUpdate = null; - }); - } + if (!dummy && pendingUpdate !== anime.id) { + $revalidateAnime = true; + lastUpdatedMedia = anime.id; + pendingUpdate = anime.id; + + incrementMediaProgress( + anime.id, + anime.mediaListEntry?.progress, + user, + () => { + const mediaListEntry = media.find( + (m) => m.id === anime.id, + )?.mediaListEntry; + + if (mediaListEntry) mediaListEntry.progress = progress + 1; + + previousAnimeList = media; + animeLists = cleanCache(user, $identity); + pendingUpdate = null; + }, + ); + } }; </script> diff --git a/src/lib/List/Anime/CompletedAnimeList.svelte b/src/lib/List/Anime/CompletedAnimeList.svelte index e308a230..6a393dff 100644 --- a/src/lib/List/Anime/CompletedAnimeList.svelte +++ b/src/lib/List/Anime/CompletedAnimeList.svelte @@ -1,21 +1,21 @@ <script lang="ts"> -import { mediaListCollection, Type, type Media } from '$lib/Data/AniList/media'; -import type { AniListAuthorisation } from '$lib/Data/AniList/identity'; -import { onMount } from 'svelte'; -import anime from '$stores/anime'; -import lastPruneTimes from '$stores/lastPruneTimes'; -import settings from '$stores/settings'; -import AnimeList from './AnimeListTemplate.svelte'; -import { addNotification } from '$lib/Notification/store'; -import locale from '$stores/locale'; -import identity from '$stores/identity'; -import sampleAnime from '$lib/Data/Static/SampleMedia/anime.json'; +import { mediaListCollection, Type, type Media } from "$lib/Data/AniList/media"; +import type { AniListAuthorisation } from "$lib/Data/AniList/identity"; +import { onMount } from "svelte"; +import anime from "$stores/anime"; +import lastPruneTimes from "$stores/lastPruneTimes"; +import settings from "$stores/settings"; +import AnimeList from "./AnimeListTemplate.svelte"; +import { addNotification } from "$lib/Notification/store"; +import locale from "$stores/locale"; +import identity from "$stores/identity"; +import sampleAnime from "$lib/Data/Static/SampleMedia/anime.json"; export let user: AniListAuthorisation = { - accessToken: '', - refreshToken: '', - expiresIn: 0, - tokenType: '' + accessToken: "", + refreshToken: "", + expiresIn: 0, + tokenType: "", }; export let dummy = false; export let dummyCount = 7; @@ -26,89 +26,102 @@ let startTime: number; let endTime: number; onMount(async () => { - startTime = performance.now(); + startTime = performance.now(); - if (dummy) { - // Use deterministic selection for consistent display - const filtered = sampleAnime.filter( - (anime) => - anime.episodes && - !anime.tags.some((tag) => tag.name === 'Nudity') && - !anime.tags.some((tag) => tag.name === 'Rape') && - !anime.tags.some((tag) => tag.name === 'Tragedy') && - !anime.tags.some((tag) => tag.name === 'Bondage') && - !anime.genres.some((genre) => genre === 'Hentai') && - anime.genres.some((genre) => genre === 'Comedy') && - anime.status !== 'NOT_YET_RELEASED' && - anime.episodes > 1 - ); - animeLists = Promise.resolve( - filtered.slice(0, dummyCount).map((anime, index) => { - anime.status = 'FINISHED'; - anime.nextAiringEpisode = { - airingAt: Math.floor(Date.now() / 1000) + (index + 1) * 24 * 60 * 60, - episode: Math.floor((anime.episodes || 12) * 0.8) - }; - anime.mediaListEntry.progress = Math.floor((anime.nextAiringEpisode.episode || 5) * 0.6); - return anime; - }) as unknown as Media[] - ); - } else { - animeLists = mediaListCollection(user, $identity, Type.Anime, $anime, $lastPruneTimes.anime, { - addNotification - }); - } + if (dummy) { + // Use deterministic selection for consistent display + const filtered = sampleAnime.filter( + (anime) => + anime.episodes && + !anime.tags.some((tag) => tag.name === "Nudity") && + !anime.tags.some((tag) => tag.name === "Rape") && + !anime.tags.some((tag) => tag.name === "Tragedy") && + !anime.tags.some((tag) => tag.name === "Bondage") && + !anime.genres.some((genre) => genre === "Hentai") && + anime.genres.some((genre) => genre === "Comedy") && + anime.status !== "NOT_YET_RELEASED" && + anime.episodes > 1, + ); + animeLists = Promise.resolve( + filtered.slice(0, dummyCount).map((anime, index) => { + anime.status = "FINISHED"; + anime.nextAiringEpisode = { + airingAt: Math.floor(Date.now() / 1000) + (index + 1) * 24 * 60 * 60, + episode: Math.floor((anime.episodes || 12) * 0.8), + }; + anime.mediaListEntry.progress = Math.floor( + (anime.nextAiringEpisode.episode || 5) * 0.6, + ); + return anime; + }) as unknown as Media[], + ); + } else { + animeLists = mediaListCollection( + user, + $identity, + Type.Anime, + $anime, + $lastPruneTimes.anime, + { + addNotification, + }, + ); + } }); const cleanMedia = (anime: Media[]) => { - if (anime && dummy) return anime; + if (anime && dummy) return anime; - if (anime === undefined) return []; + if (anime === undefined) return []; - const outdatedCompletedAnime = anime.filter( - (media: Media) => - media.status === 'FINISHED' && - (media.mediaListEntry || { status: 'DROPPED' }).status !== 'DROPPED' && - (media.mediaListEntry || { status: 'DROPPED' }).status !== - ($settings.displayPausedMedia ? '' : 'PAUSED') && - (media.mediaListEntry || { progress: 0 }).progress >= ($settings.displayNotStarted ? 0 : 1) - ); + const outdatedCompletedAnime = anime.filter( + (media: Media) => + media.status === "FINISHED" && + (media.mediaListEntry || { status: "DROPPED" }).status !== "DROPPED" && + (media.mediaListEntry || { status: "DROPPED" }).status !== + ($settings.displayPausedMedia ? "" : "PAUSED") && + (media.mediaListEntry || { progress: 0 }).progress >= + ($settings.displayNotStarted ? 0 : 1), + ); - outdatedCompletedAnime.sort((a: Media, b: Media) => { - switch ($settings.displayAnimeSort) { - case 'difference': { - const difference = (anime: Media) => - (anime.nextAiringEpisode?.episode === -1 - ? 99999 - : anime.nextAiringEpisode?.episode || -1) - - (anime.mediaListEntry || { progress: 0 }).progress; + outdatedCompletedAnime.sort((a: Media, b: Media) => { + switch ($settings.displayAnimeSort) { + case "difference": { + const difference = (anime: Media) => + (anime.nextAiringEpisode?.episode === -1 + ? 99999 + : anime.nextAiringEpisode?.episode || -1) - + (anime.mediaListEntry || { progress: 0 }).progress; - return difference(a) - difference(b); - } + return difference(a) - difference(b); + } - case 'end_date': - return ( - new Date(a.endDate.year, a.endDate.month - 1).getTime() - - new Date(b.endDate.year, b.endDate.month - 1).getTime() - ); + case "end_date": + return ( + new Date(a.endDate.year, a.endDate.month - 1).getTime() - + new Date(b.endDate.year, b.endDate.month - 1).getTime() + ); - case 'start_date': - return ( - new Date(a.startDate.year, a.startDate.month - 1).getTime() - - new Date(b.startDate.year, b.startDate.month - 1).getTime() - ); + case "start_date": + return ( + new Date(a.startDate.year, a.startDate.month - 1).getTime() - + new Date(b.startDate.year, b.startDate.month - 1).getTime() + ); - case 'time_remaining': - return (a.nextAiringEpisode?.airingAt || 9999) - (b.nextAiringEpisode?.airingAt || 9999); + case "time_remaining": + return ( + (a.nextAiringEpisode?.airingAt || 9999) - + (b.nextAiringEpisode?.airingAt || 9999) + ); - default: - return 0; - } - }); + default: + return 0; + } + }); - if (!endTime) endTime = performance.now() - startTime; + if (!endTime) endTime = performance.now() - startTime; - return outdatedCompletedAnime; + return outdatedCompletedAnime; }; </script> diff --git a/src/lib/List/Anime/DueAnimeList.svelte b/src/lib/List/Anime/DueAnimeList.svelte index 69b84b8f..4da6836f 100644 --- a/src/lib/List/Anime/DueAnimeList.svelte +++ b/src/lib/List/Anime/DueAnimeList.svelte @@ -1,17 +1,20 @@ <script lang="ts"> -import { mediaListCollection, Type, type Media } from '$lib/Data/AniList/media'; -import type { AniListAuthorisation } from '$lib/Data/AniList/identity'; -import { onDestroy, onMount } from 'svelte'; -import anime from '$stores/anime'; -import settings from '$stores/settings'; -import lastPruneTimes from '$stores/lastPruneTimes'; -import AnimeList from './AnimeListTemplate.svelte'; -import type { SubsPlease } from '$lib/Media/Anime/Airing/Subtitled/subsPlease'; -import { injectAiringTime } from '$lib/Media/Anime/Airing/Subtitled/match'; -import { hasDueEpisodes, hasNoAiredEpisodes } from '$lib/Media/Anime/Airing/classify'; -import { addNotification } from '$lib/Notification/store'; -import locale from '$stores/locale'; -import identity from '$stores/identity'; +import { mediaListCollection, Type, type Media } from "$lib/Data/AniList/media"; +import type { AniListAuthorisation } from "$lib/Data/AniList/identity"; +import { onDestroy, onMount } from "svelte"; +import anime from "$stores/anime"; +import settings from "$stores/settings"; +import lastPruneTimes from "$stores/lastPruneTimes"; +import AnimeList from "./AnimeListTemplate.svelte"; +import type { SubsPlease } from "$lib/Media/Anime/Airing/Subtitled/subsPlease"; +import { injectAiringTime } from "$lib/Media/Anime/Airing/Subtitled/match"; +import { + hasDueEpisodes, + hasNoAiredEpisodes, +} from "$lib/Media/Anime/Airing/classify"; +import { addNotification } from "$lib/Notification/store"; +import locale from "$stores/locale"; +import identity from "$stores/identity"; export let user: AniListAuthorisation; let animeLists: Promise<Media[]>; @@ -21,101 +24,124 @@ let keyCacher: ReturnType<typeof setInterval> | undefined; let keyCacheMinutes = -1; const restartKeyCacher = (cacheMinutes: number) => { - if (keyCacher) clearInterval(keyCacher); - - keyCacheMinutes = cacheMinutes; - keyCacher = setInterval( - () => { - startTime = performance.now(); - endTime = -1; - animeLists = mediaListCollection(user, $identity, Type.Anime, $anime, $lastPruneTimes.anime, { - forcePrune: true, - addNotification - }); - }, - cacheMinutes * 1000 * 60 - ); + if (keyCacher) clearInterval(keyCacher); + + keyCacheMinutes = cacheMinutes; + keyCacher = setInterval( + () => { + startTime = performance.now(); + endTime = -1; + animeLists = mediaListCollection( + user, + $identity, + Type.Anime, + $anime, + $lastPruneTimes.anime, + { + forcePrune: true, + addNotification, + }, + ); + }, + cacheMinutes * 1000 * 60, + ); }; onMount(async () => { - restartKeyCacher($settings.cacheMinutes); - - startTime = performance.now(); - animeLists = mediaListCollection(user, $identity, Type.Anime, $anime, $lastPruneTimes.anime, { - addNotification - }); + restartKeyCacher($settings.cacheMinutes); + + startTime = performance.now(); + animeLists = mediaListCollection( + user, + $identity, + Type.Anime, + $anime, + $lastPruneTimes.anime, + { + addNotification, + }, + ); }); $: if (keyCacher && keyCacheMinutes !== $settings.cacheMinutes) - restartKeyCacher($settings.cacheMinutes); + restartKeyCacher($settings.cacheMinutes); onDestroy(() => { - if (keyCacher) clearInterval(keyCacher); + if (keyCacher) clearInterval(keyCacher); }); -const cleanMedia = (anime: Media[], displayUnresolved: boolean, subsPlease: SubsPlease | null) => { - if (anime === undefined) return []; - - let dueAnime = anime - .map((media) => injectAiringTime(media, subsPlease)) - .filter( - // Releasing media - (media: Media) => - media.status === 'RELEASING' && - (media.mediaListEntry || { status: 'DROPPED' }).status !== - ($settings.displayPausedMedia ? '' : 'PAUSED') && - (media.mediaListEntry || { progress: 0 }).progress >= - ($settings.displayNotStarted === true ? 0 : 1) && - (media.mediaListEntry || { status: 'DROPPED' }).status !== 'DROPPED' - ) - .filter((media: Media) => - // Outdated media - hasDueEpisodes(media) - ) - .map((media: Media) => { - if (hasNoAiredEpisodes(media)) media.nextAiringEpisode = { episode: -1 }; - - return media; - }); - - if (!displayUnresolved) - dueAnime = dueAnime.filter((media: Media) => media.nextAiringEpisode?.episode !== -1); - - dueAnime.sort((a: Media, b: Media) => { - switch ($settings.displayAnimeSort) { - case 'difference': { - const difference = (anime: Media) => - (anime.nextAiringEpisode?.episode === -1 - ? 99999 - : anime.nextAiringEpisode?.episode || -1) - - (anime.mediaListEntry || { progress: 0 }).progress; - - return difference(a) - difference(b); - } - - case 'end_date': - return ( - new Date(a.endDate.year, a.endDate.month - 1).getTime() - - new Date(b.endDate.year, b.endDate.month - 1).getTime() - ); - - case 'start_date': - return ( - new Date(a.startDate.year, a.startDate.month - 1).getTime() - - new Date(b.startDate.year, b.startDate.month - 1).getTime() - ); - - case 'time_remaining': - return (a.nextAiringEpisode?.airingAt || 9999) - (b.nextAiringEpisode?.airingAt || 9999); - - default: - return 0; - } - }); - - if (!endTime || endTime === -1) endTime = performance.now() - startTime; - - return dueAnime; +const cleanMedia = ( + anime: Media[], + displayUnresolved: boolean, + subsPlease: SubsPlease | null, +) => { + if (anime === undefined) return []; + + let dueAnime = anime + .map((media) => injectAiringTime(media, subsPlease)) + .filter( + // Releasing media + (media: Media) => + media.status === "RELEASING" && + (media.mediaListEntry || { status: "DROPPED" }).status !== + ($settings.displayPausedMedia ? "" : "PAUSED") && + (media.mediaListEntry || { progress: 0 }).progress >= + ($settings.displayNotStarted === true ? 0 : 1) && + (media.mediaListEntry || { status: "DROPPED" }).status !== "DROPPED", + ) + .filter((media: Media) => + // Outdated media + hasDueEpisodes(media), + ) + .map((media: Media) => { + if (hasNoAiredEpisodes(media)) media.nextAiringEpisode = { episode: -1 }; + + return media; + }); + + if (!displayUnresolved) + dueAnime = dueAnime.filter( + (media: Media) => media.nextAiringEpisode?.episode !== -1, + ); + + dueAnime.sort((a: Media, b: Media) => { + switch ($settings.displayAnimeSort) { + case "difference": { + const difference = (anime: Media) => + (anime.nextAiringEpisode?.episode === -1 + ? 99999 + : anime.nextAiringEpisode?.episode || -1) - + (anime.mediaListEntry || { progress: 0 }).progress; + + return difference(a) - difference(b); + } + + case "end_date": + return ( + new Date(a.endDate.year, a.endDate.month - 1).getTime() - + new Date(b.endDate.year, b.endDate.month - 1).getTime() + ); + + case "start_date": + return ( + new Date(a.startDate.year, a.startDate.month - 1).getTime() - + new Date(b.startDate.year, b.startDate.month - 1).getTime() + ); + + case "time_remaining": + return ( + (a.nextAiringEpisode?.airingAt || 9999) - + (b.nextAiringEpisode?.airingAt || 9999) + ); + + default: + return 0; + } + }); + + if (!endTime || endTime === -1) endTime = performance.now() - startTime; + + return dueAnime; }; </script> diff --git a/src/lib/List/Anime/DueIndexColumn.svelte b/src/lib/List/Anime/DueIndexColumn.svelte index 6ffc17be..1df355a4 100644 --- a/src/lib/List/Anime/DueIndexColumn.svelte +++ b/src/lib/List/Anime/DueIndexColumn.svelte @@ -1,17 +1,17 @@ <script lang="ts"> -import type { AniListAuthorisation } from '$lib/Data/AniList/identity'; -import Skeleton from '$lib/Loading/Skeleton.svelte'; -import locale from '$stores/locale'; -import ListTitle from '../ListTitle.svelte'; -import AnimeList from '$lib/List/Anime/DueAnimeList.svelte'; -import { onMount } from 'svelte'; -import stateBin from '$stores/stateBin'; +import type { AniListAuthorisation } from "$lib/Data/AniList/identity"; +import Skeleton from "$lib/Loading/Skeleton.svelte"; +import locale from "$stores/locale"; +import ListTitle from "../ListTitle.svelte"; +import AnimeList from "$lib/List/Anime/DueAnimeList.svelte"; +import { onMount } from "svelte"; +import stateBin from "$stores/stateBin"; export let userIdentity: { id: number }; export let user: AniListAuthorisation; onMount(() => { - $stateBin.dueAnimeListOpen ??= true; + $stateBin.dueAnimeListOpen ??= true; }); </script> diff --git a/src/lib/List/Anime/PlaceholderList.svelte b/src/lib/List/Anime/PlaceholderList.svelte index 4ac0d8ff..4009d003 100644 --- a/src/lib/List/Anime/PlaceholderList.svelte +++ b/src/lib/List/Anime/PlaceholderList.svelte @@ -1,8 +1,8 @@ <script lang="ts"> -import Skeleton from '$lib/Loading/Skeleton.svelte'; -import settings from '$stores/settings'; -import ListTitle from '../ListTitle.svelte'; -import type { Title } from '../mediaTitle'; +import Skeleton from "$lib/Loading/Skeleton.svelte"; +import settings from "$stores/settings"; +import ListTitle from "../ListTitle.svelte"; +import type { Title } from "../mediaTitle"; export let title: Title; export let count = 8; diff --git a/src/lib/List/Anime/UpcomingAnimeList.svelte b/src/lib/List/Anime/UpcomingAnimeList.svelte index 109584f0..2dd69a68 100644 --- a/src/lib/List/Anime/UpcomingAnimeList.svelte +++ b/src/lib/List/Anime/UpcomingAnimeList.svelte @@ -1,19 +1,22 @@ <script lang="ts"> -import Spacer from '$lib/Layout/Spacer.svelte'; -import { mediaListCollection, Type, type Media } from '$lib/Data/AniList/media'; -import type { AniListAuthorisation } from '$lib/Data/AniList/identity'; -import { onMount } from 'svelte'; -import anime from '$stores/anime'; -import lastPruneTimes from '$stores/lastPruneTimes'; -import AnimeList from './AnimeListTemplate.svelte'; -import settings from '$stores/settings'; -import type { SubsPlease } from '$lib/Media/Anime/Airing/Subtitled/subsPlease'; -import { addNotification } from '$lib/Notification/store'; -import locale from '$stores/locale'; -import identity from '$stores/identity'; -import { injectAiringTime } from '$lib/Media/Anime/Airing/Subtitled/match'; -import { hasDueEpisodes, hasNoAiredEpisodes } from '$lib/Media/Anime/Airing/classify'; -import revalidateAnime from '$stores/revalidateAnime'; +import Spacer from "$lib/Layout/Spacer.svelte"; +import { mediaListCollection, Type, type Media } from "$lib/Data/AniList/media"; +import type { AniListAuthorisation } from "$lib/Data/AniList/identity"; +import { onMount } from "svelte"; +import anime from "$stores/anime"; +import lastPruneTimes from "$stores/lastPruneTimes"; +import AnimeList from "./AnimeListTemplate.svelte"; +import settings from "$stores/settings"; +import type { SubsPlease } from "$lib/Media/Anime/Airing/Subtitled/subsPlease"; +import { addNotification } from "$lib/Notification/store"; +import locale from "$stores/locale"; +import identity from "$stores/identity"; +import { injectAiringTime } from "$lib/Media/Anime/Airing/Subtitled/match"; +import { + hasDueEpisodes, + hasNoAiredEpisodes, +} from "$lib/Media/Anime/Airing/classify"; +import revalidateAnime from "$stores/revalidateAnime"; export let user: AniListAuthorisation; let animeLists: Promise<Media[]>; @@ -21,65 +24,88 @@ let startTime: number; let endTime: number; onMount(async () => { - startTime = performance.now(); - animeLists = mediaListCollection(user, $identity, Type.Anime, $anime, $lastPruneTimes.anime, { - addNotification, - notificationType: 'Upcoming Episodes' - }); + startTime = performance.now(); + animeLists = mediaListCollection( + user, + $identity, + Type.Anime, + $anime, + $lastPruneTimes.anime, + { + addNotification, + notificationType: "Upcoming Episodes", + }, + ); }); const cleanMedia = ( - anime: Media[], - displayUnresolved: boolean, - subsPlease: SubsPlease | null, - plannedOnly = true + anime: Media[], + displayUnresolved: boolean, + subsPlease: SubsPlease | null, + plannedOnly = true, ) => { - if (anime === undefined) return []; + if (anime === undefined) return []; - const filterAnime = (status: 'RELEASING' | 'NOT_YET_RELEASED') => - anime - .filter((media: Media) => media.status === status && media.nextAiringEpisode !== null) - .map((media) => injectAiringTime(media, subsPlease)) - .filter( - (media: Media) => - // Outdated media - ($settings.displayPlannedAnime ? media.mediaListEntry?.status === 'PLANNING' : false) || - !hasDueEpisodes(media) - ) - .map((media: Media) => { - // Adjust for planned anime - if ( - ($settings.displayPlannedAnime ? media.episodes !== 1 : true) && - hasNoAiredEpisodes(media) - ) - media.nextAiringEpisode = { episode: -1 }; + const filterAnime = (status: "RELEASING" | "NOT_YET_RELEASED") => + anime + .filter( + (media: Media) => + media.status === status && media.nextAiringEpisode !== null, + ) + .map((media) => injectAiringTime(media, subsPlease)) + .filter( + (media: Media) => + // Outdated media + ($settings.displayPlannedAnime + ? media.mediaListEntry?.status === "PLANNING" + : false) || !hasDueEpisodes(media), + ) + .map((media: Media) => { + // Adjust for planned anime + if ( + ($settings.displayPlannedAnime ? media.episodes !== 1 : true) && + hasNoAiredEpisodes(media) + ) + media.nextAiringEpisode = { episode: -1 }; - return media; - }); - let upcomingAnime = filterAnime(plannedOnly ? 'NOT_YET_RELEASED' : 'RELEASING'); + return media; + }); + let upcomingAnime = filterAnime( + plannedOnly ? "NOT_YET_RELEASED" : "RELEASING", + ); - if (!displayUnresolved) - upcomingAnime = upcomingAnime.filter((media: Media) => media.nextAiringEpisode?.episode !== -1); + if (!displayUnresolved) + upcomingAnime = upcomingAnime.filter( + (media: Media) => media.nextAiringEpisode?.episode !== -1, + ); - upcomingAnime.sort( - (a: Media, b: Media) => - (a.nextAiringEpisode?.airingAt || 9999) - (b.nextAiringEpisode?.airingAt || 9999) - ); + upcomingAnime.sort( + (a: Media, b: Media) => + (a.nextAiringEpisode?.airingAt || 9999) - + (b.nextAiringEpisode?.airingAt || 9999), + ); - if (!endTime) endTime = performance.now() - startTime; + if (!endTime) endTime = performance.now() - startTime; - return upcomingAnime; + return upcomingAnime; }; $: { - if ($revalidateAnime) { - $revalidateAnime = false; - $lastPruneTimes.anime = -1; - animeLists = mediaListCollection(user, $identity, Type.Anime, $anime, $lastPruneTimes.anime, { - addNotification, - notificationType: 'Upcoming Episodes' - }); - } + if ($revalidateAnime) { + $revalidateAnime = false; + $lastPruneTimes.anime = -1; + animeLists = mediaListCollection( + user, + $identity, + Type.Anime, + $anime, + $lastPruneTimes.anime, + { + addNotification, + notificationType: "Upcoming Episodes", + }, + ); + } } </script> |