aboutsummaryrefslogtreecommitdiff
path: root/src/lib/List
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/List')
-rw-r--r--src/lib/List/Anime/AnimeListTemplate.svelte58
-rw-r--r--src/lib/List/Anime/CleanAnimeList.svelte253
-rw-r--r--src/lib/List/Anime/CompletedAnimeList.svelte181
-rw-r--r--src/lib/List/Anime/DueAnimeList.svelte222
-rw-r--r--src/lib/List/Anime/DueIndexColumn.svelte16
-rw-r--r--src/lib/List/Anime/PlaceholderList.svelte8
-rw-r--r--src/lib/List/Anime/UpcomingAnimeList.svelte148
-rw-r--r--src/lib/List/CleanGrid.svelte19
-rw-r--r--src/lib/List/CleanList.svelte12
-rw-r--r--src/lib/List/ListTitle.svelte8
-rw-r--r--src/lib/List/Manga/CleanMangaList.svelte95
-rw-r--r--src/lib/List/Manga/MangaListTemplate.svelte492
-rw-r--r--src/lib/List/MediaRoulette.svelte92
-rw-r--r--src/lib/List/MediaTitleDisplay.svelte12
-rw-r--r--src/lib/List/covers.css84
-rw-r--r--src/lib/List/mediaTitle.ts21
16 files changed, 942 insertions, 779 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>
diff --git a/src/lib/List/CleanGrid.svelte b/src/lib/List/CleanGrid.svelte
index 08a7ef83..006a5e4e 100644
--- a/src/lib/List/CleanGrid.svelte
+++ b/src/lib/List/CleanGrid.svelte
@@ -1,15 +1,15 @@
<script lang="ts">
-import type { Media } from '$lib/Data/AniList/media';
-import ParallaxImage from '$lib/Image/ParallaxImage.svelte';
-import { outboundLink } from '$lib/Media/links';
-import LinkedTooltip from '$lib/Tooltip/LinkedTooltip.svelte';
-import settings from '$stores/settings';
-import { mediaTitle } from './mediaTitle';
-import './covers.css';
+import type { Media } from "$lib/Data/AniList/media";
+import ParallaxImage from "$lib/Image/ParallaxImage.svelte";
+import { outboundLink } from "$lib/Media/links";
+import LinkedTooltip from "$lib/Tooltip/LinkedTooltip.svelte";
+import settings from "$stores/settings";
+import { mediaTitle } from "./mediaTitle";
+import "./covers.css";
export let media: Media[];
export let dummy = false;
-export let type: 'anime' | 'manga';
+export let type: "anime" | "manga";
export let upcoming = false;
export let notYetReleased = false;
export let reverseSort = false;
@@ -18,7 +18,8 @@ export let limit: number | undefined = undefined;
let uniqueID = new Date().getTime();
$: sortedMedia = reverseSort ? [...media].reverse() : media;
-$: processedMedia = limit !== undefined ? sortedMedia.slice(0, limit) : sortedMedia;
+$: processedMedia =
+ limit !== undefined ? sortedMedia.slice(0, limit) : sortedMedia;
</script>
<div
diff --git a/src/lib/List/CleanList.svelte b/src/lib/List/CleanList.svelte
index 377b9302..0ae3c65b 100644
--- a/src/lib/List/CleanList.svelte
+++ b/src/lib/List/CleanList.svelte
@@ -1,12 +1,12 @@
<script lang="ts">
-import MediaTitleDisplay from '$lib/List/MediaTitleDisplay.svelte';
-import type { Media } from '$lib/Data/AniList/media';
-import { outboundLink } from '$lib/Media/links';
-import LinkedTooltip from '$lib/Tooltip/LinkedTooltip.svelte';
-import settings from '$stores/settings';
+import MediaTitleDisplay from "$lib/List/MediaTitleDisplay.svelte";
+import type { Media } from "$lib/Data/AniList/media";
+import { outboundLink } from "$lib/Media/links";
+import LinkedTooltip from "$lib/Tooltip/LinkedTooltip.svelte";
+import settings from "$stores/settings";
export let media: Media[];
-export let type: 'anime' | 'manga';
+export let type: "anime" | "manga";
export let upcoming = false;
export let notYetReleased = false;
export let lastUpdatedMedia: number;
diff --git a/src/lib/List/ListTitle.svelte b/src/lib/List/ListTitle.svelte
index 71b5e649..d000e91f 100644
--- a/src/lib/List/ListTitle.svelte
+++ b/src/lib/List/ListTitle.svelte
@@ -1,12 +1,12 @@
<script lang="ts">
-import tooltip from '$lib/Tooltip/tooltip';
-import type { Title } from './mediaTitle';
+import tooltip from "$lib/Tooltip/tooltip";
+import type { Title } from "./mediaTitle";
export let time: number | undefined = undefined;
export let count = -1337;
export let title: Title = {
- title: 'Media List',
- hint: 'This is a media list.'
+ title: "Media List",
+ hint: "This is a media list.",
};
export let progress: undefined | number = undefined;
export let hideTime = false;
diff --git a/src/lib/List/Manga/CleanMangaList.svelte b/src/lib/List/Manga/CleanMangaList.svelte
index 428c45f2..cacc7006 100644
--- a/src/lib/List/Manga/CleanMangaList.svelte
+++ b/src/lib/List/Manga/CleanMangaList.svelte
@@ -1,28 +1,32 @@
<script lang="ts">
-import Spacer from '$lib/Layout/Spacer.svelte';
-import type { Media } from '$lib/Data/AniList/media';
-import RateLimitedError from '$lib/Error/RateLimited.svelte';
-import { volumeCount } from '$lib/Media/Manga/volumes';
-import settings from '$stores/settings';
-import ListTitle from '../ListTitle.svelte';
-import { onMount } from 'svelte';
-import root from '$lib/Utility/root';
-import locale from '$stores/locale';
-import Skeleton from '$lib/Loading/Skeleton.svelte';
-import { browser } from '$app/environment';
-import proxy from '$lib/Utility/proxy';
-import '../covers.css';
-import CleanGrid from '../CleanGrid.svelte';
-import CleanList from '../CleanList.svelte';
-import stateBin from '$stores/stateBin';
-import localforage from 'localforage';
-import MediaRoulette from '../MediaRoulette.svelte';
+import Spacer from "$lib/Layout/Spacer.svelte";
+import type { Media } from "$lib/Data/AniList/media";
+import RateLimitedError from "$lib/Error/RateLimited.svelte";
+import { volumeCount } from "$lib/Media/Manga/volumes";
+import settings from "$stores/settings";
+import ListTitle from "../ListTitle.svelte";
+import { onMount } from "svelte";
+import root from "$lib/Utility/root";
+import locale from "$stores/locale";
+import Skeleton from "$lib/Loading/Skeleton.svelte";
+import { browser } from "$app/environment";
+import proxy from "$lib/Utility/proxy";
+import "../covers.css";
+import CleanGrid from "../CleanGrid.svelte";
+import CleanList from "../CleanList.svelte";
+import stateBin from "$stores/stateBin";
+import localforage from "localforage";
+import MediaRoulette from "../MediaRoulette.svelte";
export let media: Media[];
export let cleanCache: () => void;
export let endTime: number;
export let lastUpdatedMedia: number;
-export let updateMedia: (id: number, progress: number | undefined, media: Media[]) => Promise<void>;
+export let updateMedia: (
+ id: number,
+ progress: number | undefined,
+ media: Media[],
+) => Promise<void>;
export let pendingUpdate: number | null;
export let due: boolean;
export let rateLimited: boolean;
@@ -34,45 +38,48 @@ export let limit: number | undefined = undefined;
let showRoulette = false;
let serviceStatusResponse: Promise<Response>;
let totalEpisodeDueCount = media
- .map((manga) => {
- if ($settings.displayTotalEpisodes && !$settings.displayTotalDueEpisodes) return 1;
+ .map((manga) => {
+ if ($settings.displayTotalEpisodes && !$settings.displayTotalDueEpisodes)
+ return 1;
- if (!due && !$settings.displayTotalEpisodes) return 1;
+ if (!due && !$settings.displayTotalEpisodes) return 1;
- return (manga.episodes || 1) - (manga.mediaListEntry?.progress || 0);
- })
- .reduce((a, b) => a + b, 0);
+ return (manga.episodes || 1) - (manga.mediaListEntry?.progress || 0);
+ })
+ .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),
+ ),
);
-const filterKind = due ? 'due' : 'completed';
+const filterKind = due ? "due" : "completed";
const filterKey = `${filterKind}MangaListFilter`;
-$: 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]);
onMount(async () => {
- serviceStatusResponse = fetch(proxy('https://api.mangadex.org/ping'));
+ serviceStatusResponse = fetch(proxy("https://api.mangadex.org/ping"));
- if (browser)
- await localforage.setItem(
- `last${due ? '' : 'Completed'}MangaListLength`,
- media.length.toString()
- );
+ if (browser)
+ await localforage.setItem(
+ `last${due ? "" : "Completed"}MangaListLength`,
+ media.length.toString(),
+ );
});
const increment = (manga: Media) => {
- if (!(pendingUpdate === manga.id || dummy))
- updateMedia(manga.id, manga.mediaListEntry?.progress, media);
+ if (!(pendingUpdate === manga.id || dummy))
+ updateMedia(manga.id, manga.mediaListEntry?.progress, media);
};
</script>
diff --git a/src/lib/List/Manga/MangaListTemplate.svelte b/src/lib/List/Manga/MangaListTemplate.svelte
index 0b967cb2..c9f66dd4 100644
--- a/src/lib/List/Manga/MangaListTemplate.svelte
+++ b/src/lib/List/Manga/MangaListTemplate.svelte
@@ -1,32 +1,32 @@
<script lang="ts">
-import sampleManga from '$lib/Data/Static/SampleMedia/manga.json';
-import { mediaListCollection, Type, type Media } from '$lib/Data/AniList/media';
-import type { AniListAuthorisation } from '$lib/Data/AniList/identity';
-import { onDestroy, onMount } from 'svelte';
-import { chapterCount } from '$lib/Media/Manga/chapters';
-import { pruneAllManga } from '$lib/Media/Manga/cache';
-import manga from '$stores/manga';
-import { database } from '$lib/Database/IDB/chapters';
-import settings from '$stores/settings';
-import lastPruneTimes from '$stores/lastPruneTimes';
-import ListTitle from '../ListTitle.svelte';
-import RateLimitedError from '$lib/Error/RateLimited.svelte';
-import CleanMangaList from './CleanMangaList.svelte';
-import { incrementMediaProgress } from '$lib/Media/Anime/cache';
-import { addNotification } from '$lib/Notification/store';
-import { options } from '$lib/Notification/options';
-import Skeleton from '$lib/Loading/Skeleton.svelte';
-import locale from '$stores/locale';
-import { browser } from '$app/environment';
-import identity from '$stores/identity';
-import privilegedUser from '$lib/Utility/privilegedUser';
-import localforage from 'localforage';
+import sampleManga from "$lib/Data/Static/SampleMedia/manga.json";
+import { mediaListCollection, Type, type Media } from "$lib/Data/AniList/media";
+import type { AniListAuthorisation } from "$lib/Data/AniList/identity";
+import { onDestroy, onMount } from "svelte";
+import { chapterCount } from "$lib/Media/Manga/chapters";
+import { pruneAllManga } from "$lib/Media/Manga/cache";
+import manga from "$stores/manga";
+import { database } from "$lib/Database/IDB/chapters";
+import settings from "$stores/settings";
+import lastPruneTimes from "$stores/lastPruneTimes";
+import ListTitle from "../ListTitle.svelte";
+import RateLimitedError from "$lib/Error/RateLimited.svelte";
+import CleanMangaList from "./CleanMangaList.svelte";
+import { incrementMediaProgress } from "$lib/Media/Anime/cache";
+import { addNotification } from "$lib/Notification/store";
+import { options } from "$lib/Notification/options";
+import Skeleton from "$lib/Loading/Skeleton.svelte";
+import locale from "$stores/locale";
+import { browser } from "$app/environment";
+import identity from "$stores/identity";
+import privilegedUser from "$lib/Utility/privilegedUser";
+import localforage from "localforage";
export let user: AniListAuthorisation = {
- accessToken: '',
- refreshToken: '',
- expiresIn: 0,
- tokenType: ''
+ accessToken: "",
+ refreshToken: "",
+ expiresIn: 0,
+ tokenType: "",
};
export let displayUnresolved: boolean;
export let due: boolean;
@@ -49,215 +49,265 @@ 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;
- mangaLists = mediaListCollection(user, $identity, Type.Manga, $manga, $lastPruneTimes.manga, {
- addNotification
- });
- },
- cacheMinutes * 1000 * 60
- );
+ if (keyCacher) clearInterval(keyCacher);
+
+ keyCacheMinutes = cacheMinutes;
+ keyCacher = setInterval(
+ () => {
+ startTime = performance.now();
+ endTime = -1;
+ mangaLists = mediaListCollection(
+ user,
+ $identity,
+ Type.Manga,
+ $manga,
+ $lastPruneTimes.manga,
+ {
+ addNotification,
+ },
+ );
+ },
+ cacheMinutes * 1000 * 60,
+ );
};
onMount(async () => {
- restartKeyCacher(Math.max($settings.cacheMangaMinutes, 5));
-
- if (browser) {
- const lastStoredList = (await localforage.getItem(
- `last${due ? '' : 'Completed'}MangaListLength`
- )) as number | null;
-
- if (lastStoredList) lastListSize = parseInt(String(lastStoredList));
- }
-
- startTime = performance.now();
-
- if (dummy) {
- // Use deterministic selection for consistent display
- const filtered = sampleManga.filter(
- (manga) =>
- manga.chapters &&
- !manga.tags.some((tag) => tag.name === 'Nudity') &&
- !manga.tags.some((tag) => tag.name === 'Rape') &&
- !manga.tags.some((tag) => tag.name === 'Tragedy') &&
- !manga.tags.some((tag) => tag.name === 'Bondage') &&
- !manga.genres.some((genre) => genre === 'Hentai') &&
- manga.genres.some((genre) => genre === 'Comedy') &&
- manga.status !== 'NOT_YET_RELEASED'
- );
- mangaLists = Promise.resolve(
- filtered.slice(0, dummyCount).map((manga) => {
- manga.status = 'FINISHED';
- manga.episodes = Math.floor((manga.chapters || 10) * 0.7) as unknown as null;
- manga.mediaListEntry.progress = Math.floor((manga.episodes || 5) * 0.5) + 1;
- return manga;
- }) as unknown as Media[]
- );
- } else {
- mangaLists = mediaListCollection(user, $identity, Type.Manga, $manga, $lastPruneTimes.manga, {
- addNotification
- });
- }
+ restartKeyCacher(Math.max($settings.cacheMangaMinutes, 5));
+
+ if (browser) {
+ const lastStoredList = (await localforage.getItem(
+ `last${due ? "" : "Completed"}MangaListLength`,
+ )) as number | null;
+
+ if (lastStoredList) lastListSize = parseInt(String(lastStoredList));
+ }
+
+ startTime = performance.now();
+
+ if (dummy) {
+ // Use deterministic selection for consistent display
+ const filtered = sampleManga.filter(
+ (manga) =>
+ manga.chapters &&
+ !manga.tags.some((tag) => tag.name === "Nudity") &&
+ !manga.tags.some((tag) => tag.name === "Rape") &&
+ !manga.tags.some((tag) => tag.name === "Tragedy") &&
+ !manga.tags.some((tag) => tag.name === "Bondage") &&
+ !manga.genres.some((genre) => genre === "Hentai") &&
+ manga.genres.some((genre) => genre === "Comedy") &&
+ manga.status !== "NOT_YET_RELEASED",
+ );
+ mangaLists = Promise.resolve(
+ filtered.slice(0, dummyCount).map((manga) => {
+ manga.status = "FINISHED";
+ manga.episodes = Math.floor(
+ (manga.chapters || 10) * 0.7,
+ ) as unknown as null;
+ manga.mediaListEntry.progress =
+ Math.floor((manga.episodes || 5) * 0.5) + 1;
+ return manga;
+ }) as unknown as Media[],
+ );
+ } else {
+ mangaLists = mediaListCollection(
+ user,
+ $identity,
+ Type.Manga,
+ $manga,
+ $lastPruneTimes.manga,
+ {
+ addNotification,
+ },
+ );
+ }
});
-$: if (keyCacher && keyCacheMinutes !== Math.max($settings.cacheMangaMinutes, 5))
- restartKeyCacher(Math.max($settings.cacheMangaMinutes, 5));
+$: if (
+ keyCacher &&
+ keyCacheMinutes !== Math.max($settings.cacheMangaMinutes, 5)
+)
+ restartKeyCacher(Math.max($settings.cacheMangaMinutes, 5));
onDestroy(() => {
- if (keyCacher) clearInterval(keyCacher);
+ if (keyCacher) clearInterval(keyCacher);
});
-const cleanMedia = async (manga: Media[], displayUnresolved: boolean, force: boolean) => {
- progress = 0;
-
- if (manga && dummy) return manga;
-
- if (manga === undefined) return [];
-
- if (!authorised && (await database.chapters.toArray()).length <= 0 && !force) return [];
-
- if (authorised) {
- let refreshing = false;
-
- if ($lastPruneTimes.chapters === 1) {
- refreshing = true;
-
- lastPruneTimes.setKey('chapters', new Date().getTime());
- } else {
- const currentDate = new Date();
-
- if (
- (currentDate.getTime() - $lastPruneTimes.chapters) / 1000 / 60 >
- Math.max($settings.cacheMangaMinutes, 5)
- ) {
- refreshing = true;
-
- lastPruneTimes.setKey('chapters', currentDate.getTime());
- (async () => {
- await database.chapters.bulkDelete((await database.chapters.toArray()).map((m) => m.id));
- })();
- }
- }
-
- if (refreshing) {
- addNotification(
- options({
- heading: 'Manga',
- description: 'Re-freshing manga data ...'
- })
- );
- }
- }
-
- const releasingMedia = manga.filter(
- (media: Media) =>
- (due ? media.status === 'RELEASING' : media.status === 'FINISHED') &&
- (media.mediaListEntry || { status: 'DROPPED' }).status !==
- ($settings.displayPausedMedia ? '' : 'PAUSED') &&
- (media.mediaListEntry || { status: 'DROPPED' }).status !== 'DROPPED' &&
- (media.mediaListEntry || { progress: 0 }).progress >=
- ($settings.displayNotStarted === true ? 0 : 1)
- );
- let finalMedia = releasingMedia;
- const progressStep = 100 / finalMedia.length / 2;
- const chapterPromises = finalMedia.map((m: Media) =>
- database.chapters.get(m.id).then((c) => {
- if (progress < 100) progress += progressStep;
-
- if (!due) return new Promise((resolve) => resolve(m.chapters)) as Promise<number | null>;
-
- if (c !== undefined) return chapterCount($identity, m, $settings.calculateGuessingDisabled);
- else {
- // A = On 1 second interval,
- // B = a maximum of 5 requests per second are allowed.
- // C = chapterCount makes 3 requests per call.
- // F = A / (B / C) = 0.6 seconds
- return new Promise((resolve) => setTimeout(resolve, 600)).then(() =>
- chapterCount($identity, m, $settings.calculateGuessingDisabled)
- );
- }
- })
- );
- const chapterCounts: (number | null)[] = [];
-
- for (let i = 0; i < chapterPromises.length; i++) {
- const count = await chapterPromises[i];
-
- if (count === -22) {
- rateLimited = true;
-
- break;
- }
-
- chapterCounts.push(count);
-
- if (progress < 100) progress += progressStep;
- }
-
- finalMedia.forEach((m: Media, i) => {
- m.episodes = chapterCounts[i] || -1337;
- });
-
- if (!displayUnresolved) finalMedia = finalMedia.filter((m: Media) => m.episodes !== -1337);
-
- finalMedia.sort(
- (a: Media, b: Media) =>
- (a.episodes || 9999) -
- (a.mediaListEntry || { progress: 0 }).progress -
- ((b.episodes || 9999) - (b.mediaListEntry || { progress: 0 }).progress)
- );
-
- finalMedia = finalMedia.filter(
- (item, index, array) =>
- array.findIndex((i) => i.id === item.id) === index &&
- (item.episodes === -1337 && displayUnresolved
- ? true
- : (item.mediaListEntry?.progress || 0) <
- ($settings.calculateChaptersRoundedDown === true
- ? Math.floor(item.episodes)
- : item.episodes))
- );
-
- if (!endTime || endTime === -1) endTime = performance.now() - startTime;
-
- return finalMedia;
+const cleanMedia = async (
+ manga: Media[],
+ displayUnresolved: boolean,
+ force: boolean,
+) => {
+ progress = 0;
+
+ if (manga && dummy) return manga;
+
+ if (manga === undefined) return [];
+
+ if (!authorised && (await database.chapters.toArray()).length <= 0 && !force)
+ return [];
+
+ if (authorised) {
+ let refreshing = false;
+
+ if ($lastPruneTimes.chapters === 1) {
+ refreshing = true;
+
+ lastPruneTimes.setKey("chapters", new Date().getTime());
+ } else {
+ const currentDate = new Date();
+
+ if (
+ (currentDate.getTime() - $lastPruneTimes.chapters) / 1000 / 60 >
+ Math.max($settings.cacheMangaMinutes, 5)
+ ) {
+ refreshing = true;
+
+ lastPruneTimes.setKey("chapters", currentDate.getTime());
+ (async () => {
+ await database.chapters.bulkDelete(
+ (await database.chapters.toArray()).map((m) => m.id),
+ );
+ })();
+ }
+ }
+
+ if (refreshing) {
+ addNotification(
+ options({
+ heading: "Manga",
+ description: "Re-freshing manga data ...",
+ }),
+ );
+ }
+ }
+
+ const releasingMedia = manga.filter(
+ (media: Media) =>
+ (due ? media.status === "RELEASING" : media.status === "FINISHED") &&
+ (media.mediaListEntry || { status: "DROPPED" }).status !==
+ ($settings.displayPausedMedia ? "" : "PAUSED") &&
+ (media.mediaListEntry || { status: "DROPPED" }).status !== "DROPPED" &&
+ (media.mediaListEntry || { progress: 0 }).progress >=
+ ($settings.displayNotStarted === true ? 0 : 1),
+ );
+ let finalMedia = releasingMedia;
+ const progressStep = 100 / finalMedia.length / 2;
+ const chapterPromises = finalMedia.map((m: Media) =>
+ database.chapters.get(m.id).then((c) => {
+ if (progress < 100) progress += progressStep;
+
+ if (!due)
+ return new Promise((resolve) => resolve(m.chapters)) as Promise<
+ number | null
+ >;
+
+ if (c !== undefined)
+ return chapterCount($identity, m, $settings.calculateGuessingDisabled);
+ else {
+ // A = On 1 second interval,
+ // B = a maximum of 5 requests per second are allowed.
+ // C = chapterCount makes 3 requests per call.
+ // F = A / (B / C) = 0.6 seconds
+ return new Promise((resolve) => setTimeout(resolve, 600)).then(() =>
+ chapterCount($identity, m, $settings.calculateGuessingDisabled),
+ );
+ }
+ }),
+ );
+ const chapterCounts: (number | null)[] = [];
+
+ for (let i = 0; i < chapterPromises.length; i++) {
+ const count = await chapterPromises[i];
+
+ if (count === -22) {
+ rateLimited = true;
+
+ break;
+ }
+
+ chapterCounts.push(count);
+
+ if (progress < 100) progress += progressStep;
+ }
+
+ finalMedia.forEach((m: Media, i) => {
+ m.episodes = chapterCounts[i] || -1337;
+ });
+
+ if (!displayUnresolved)
+ finalMedia = finalMedia.filter((m: Media) => m.episodes !== -1337);
+
+ finalMedia.sort(
+ (a: Media, b: Media) =>
+ (a.episodes || 9999) -
+ (a.mediaListEntry || { progress: 0 }).progress -
+ ((b.episodes || 9999) - (b.mediaListEntry || { progress: 0 }).progress),
+ );
+
+ finalMedia = finalMedia.filter(
+ (item, index, array) =>
+ array.findIndex((i) => i.id === item.id) === index &&
+ (item.episodes === -1337 && displayUnresolved
+ ? true
+ : (item.mediaListEntry?.progress || 0) <
+ ($settings.calculateChaptersRoundedDown === true
+ ? Math.floor(item.episodes)
+ : item.episodes)),
+ );
+
+ if (!endTime || endTime === -1) endTime = performance.now() - startTime;
+
+ return finalMedia;
};
-const updateMedia = async (id: number, progress: number | undefined, media: Media[]) => {
- pendingUpdate = id;
- lastUpdatedMedia = id;
-
- await database.chapters.delete(id);
-
- incrementMediaProgress(id, progress, user, () => {
- previousMangaList = media;
-
- const foundEntry = media.find((m) => m.id === id);
-
- if (foundEntry && foundEntry.mediaListEntry)
- foundEntry.mediaListEntry.progress = (progress || 0) + 1;
-
- mangaLists = mediaListCollection(user, $identity, Type.Manga, $manga, $lastPruneTimes.manga, {
- forcePrune: true
- });
- pendingUpdate = null;
- });
+const updateMedia = async (
+ id: number,
+ progress: number | undefined,
+ media: Media[],
+) => {
+ pendingUpdate = id;
+ lastUpdatedMedia = id;
+
+ await database.chapters.delete(id);
+
+ incrementMediaProgress(id, progress, user, () => {
+ previousMangaList = media;
+
+ const foundEntry = media.find((m) => m.id === id);
+
+ if (foundEntry && foundEntry.mediaListEntry)
+ foundEntry.mediaListEntry.progress = (progress || 0) + 1;
+
+ mangaLists = mediaListCollection(
+ user,
+ $identity,
+ Type.Manga,
+ $manga,
+ $lastPruneTimes.manga,
+ {
+ forcePrune: true,
+ },
+ );
+ pendingUpdate = null;
+ });
};
const cleanCache = () => {
- startTime = performance.now();
- endTime = -1;
-
- pruneAllManga().then(() => {
- mangaLists = mediaListCollection(user, $identity, Type.Manga, $manga, $lastPruneTimes.manga, {
- forcePrune: true
- });
- });
+ startTime = performance.now();
+ endTime = -1;
+
+ pruneAllManga().then(() => {
+ mangaLists = mediaListCollection(
+ user,
+ $identity,
+ Type.Manga,
+ $manga,
+ $lastPruneTimes.manga,
+ {
+ forcePrune: true,
+ },
+ );
+ });
};
</script>
diff --git a/src/lib/List/MediaRoulette.svelte b/src/lib/List/MediaRoulette.svelte
index b4a6d527..6196c669 100644
--- a/src/lib/List/MediaRoulette.svelte
+++ b/src/lib/List/MediaRoulette.svelte
@@ -1,15 +1,15 @@
<script lang="ts">
-import type { Media } from '$lib/Data/AniList/media';
-import ParallaxImage from '$lib/Image/ParallaxImage.svelte';
-import { outboundLink } from '$lib/Media/links';
-import settings from '$stores/settings';
-import { mediaTitle } from './mediaTitle';
+import type { Media } from "$lib/Data/AniList/media";
+import ParallaxImage from "$lib/Image/ParallaxImage.svelte";
+import { outboundLink } from "$lib/Media/links";
+import settings from "$stores/settings";
+import { mediaTitle } from "./mediaTitle";
interface Props {
- media: Media[];
- type: 'anime' | 'manga';
- onClose: () => void;
- spinDuration?: number;
+ media: Media[];
+ type: "anime" | "manga";
+ onClose: () => void;
+ spinDuration?: number;
}
let { media, type, onClose, spinDuration = 2 }: Props = $props();
@@ -22,63 +22,63 @@ let isClosing = $state(false);
let currentMedia = $derived(media[displayIndex]);
const startRoulette = () => {
- if (media.length === 0 || isSpinning) return;
+ if (media.length === 0 || isSpinning) return;
- isSpinning = true;
- showResult = false;
- selectedIndex = Math.floor(Math.random() * media.length);
+ isSpinning = true;
+ showResult = false;
+ selectedIndex = Math.floor(Math.random() * media.length);
- const startTime = Date.now();
- const durationMs = spinDuration * 1000;
- const minSpeed = 50;
- const maxSpeed = 350;
- const slowdownStart = 0.8;
+ const startTime = Date.now();
+ const durationMs = spinDuration * 1000;
+ const minSpeed = 50;
+ const maxSpeed = 350;
+ const slowdownStart = 0.8;
- const spin = () => {
- displayIndex = (displayIndex + 1) % media.length;
+ const spin = () => {
+ displayIndex = (displayIndex + 1) % media.length;
- const elapsed = Date.now() - startTime;
- const progress = Math.min(elapsed / durationMs, 1);
- let speed = minSpeed;
+ const elapsed = Date.now() - startTime;
+ const progress = Math.min(elapsed / durationMs, 1);
+ let speed = minSpeed;
- if (progress > slowdownStart) {
- const slowdownProgress = (progress - slowdownStart) / (1 - slowdownStart);
+ if (progress > slowdownStart) {
+ const slowdownProgress = (progress - slowdownStart) / (1 - slowdownStart);
- speed = minSpeed + slowdownProgress * (maxSpeed - minSpeed);
- }
+ speed = minSpeed + slowdownProgress * (maxSpeed - minSpeed);
+ }
- if (progress >= 1 && displayIndex === selectedIndex) {
- spinTimeout = null;
- isSpinning = false;
- showResult = true;
+ if (progress >= 1 && displayIndex === selectedIndex) {
+ spinTimeout = null;
+ isSpinning = false;
+ showResult = true;
- return;
- }
+ return;
+ }
- spinTimeout = setTimeout(spin, speed);
- };
+ spinTimeout = setTimeout(spin, speed);
+ };
- spinTimeout = setTimeout(spin, minSpeed);
+ spinTimeout = setTimeout(spin, minSpeed);
};
const handleClose = () => {
- if (isClosing) return;
+ if (isClosing) return;
- if (spinTimeout) {
- clearTimeout(spinTimeout);
+ if (spinTimeout) {
+ clearTimeout(spinTimeout);
- spinTimeout = null;
- }
+ spinTimeout = null;
+ }
- isSpinning = false;
- showResult = false;
- isClosing = true;
+ isSpinning = false;
+ showResult = false;
+ isClosing = true;
- setTimeout(() => onClose(), 200);
+ setTimeout(() => onClose(), 200);
};
const handleOverlayClick = (e: MouseEvent) => {
- if (e.target === e.currentTarget) handleClose();
+ if (e.target === e.currentTarget) handleClose();
};
</script>
diff --git a/src/lib/List/MediaTitleDisplay.svelte b/src/lib/List/MediaTitleDisplay.svelte
index 4d379ee9..e7c26fb0 100644
--- a/src/lib/List/MediaTitleDisplay.svelte
+++ b/src/lib/List/MediaTitleDisplay.svelte
@@ -1,10 +1,10 @@
<script lang="ts">
-import type { MediaTitle } from '$lib/Data/AniList/media';
-import LinkedTooltip from '$lib/Tooltip/LinkedTooltip.svelte';
-import { abbreviate as abbreviated } from '$lib/Utility/string';
-import settings from '$stores/settings';
-import LZString from 'lz-string';
-import * as wanakana from 'wanakana';
+import type { MediaTitle } from "$lib/Data/AniList/media";
+import LinkedTooltip from "$lib/Tooltip/LinkedTooltip.svelte";
+import { abbreviate as abbreviated } from "$lib/Utility/string";
+import settings from "$stores/settings";
+import LZString from "lz-string";
+import * as wanakana from "wanakana";
export let title: MediaTitle;
export let abbreviate = false;
diff --git a/src/lib/List/covers.css b/src/lib/List/covers.css
index f2bf45ca..94266257 100644
--- a/src/lib/List/covers.css
+++ b/src/lib/List/covers.css
@@ -1,77 +1,77 @@
.covers {
- display: grid;
- justify-content: center;
- gap: 1em 0.5em;
- margin-top: 0.5rem;
+ display: grid;
+ justify-content: center;
+ gap: 1em 0.5em;
+ margin-top: 0.5rem;
}
.cover {
- background-size: cover;
- background-position: center;
- border-radius: 8px;
- aspect-ratio: 46 / 65;
- width: 100%;
- height: auto;
- object-fit: cover;
+ background-size: cover;
+ background-position: center;
+ border-radius: 8px;
+ aspect-ratio: 46 / 65;
+ width: 100%;
+ height: auto;
+ object-fit: cover;
}
.cover-title {
- text-align: center;
- margin: 0.25rem;
+ text-align: center;
+ margin: 0.25rem;
}
.cover-card {
- display: inline-block;
+ display: inline-block;
}
.cover-card-image {
- border-radius: 8px;
- transition:
- transform 0.45s ease,
- box-shadow 0.45s ease;
- margin-bottom: 0.5em;
+ border-radius: 8px;
+ transition:
+ transform 0.45s ease,
+ box-shadow 0.45s ease;
+ margin-bottom: 0.5em;
}
.cover-card-image:hover {
- transform: scale(1.1);
- position: relative;
- transition:
- transform 0.45s ease,
- box-shadow 0.45s ease;
+ transform: scale(1.1);
+ position: relative;
+ transition:
+ transform 0.45s ease,
+ box-shadow 0.45s ease;
}
.entry::after {
- content: "";
- display: table;
- clear: both;
+ content: "";
+ display: table;
+ clear: both;
}
.countdown {
- white-space: nowrap;
- float: right;
+ white-space: nowrap;
+ float: right;
}
.adult {
- filter: blur(10px) grayscale(50%) brightness(0.5);
- transition: filter 0.3s ease;
+ filter: blur(10px) grayscale(50%) brightness(0.5);
+ transition: filter 0.3s ease;
}
.adult:hover {
- filter: blur(0) !important;
- transition: filter 0.3s ease;
+ filter: blur(0) !important;
+ transition: filter 0.3s ease;
}
.cover-container {
- display: flex;
- justify-content: center;
- align-items: center;
- border-radius: 8px;
- aspect-ratio: 46 / 65;
- overflow: hidden;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ border-radius: 8px;
+ aspect-ratio: 46 / 65;
+ overflow: hidden;
}
button {
- margin-top: unset;
- vertical-align: unset;
- height: unset;
+ margin-top: unset;
+ vertical-align: unset;
+ height: unset;
}
diff --git a/src/lib/List/mediaTitle.ts b/src/lib/List/mediaTitle.ts
index e59a3091..0e1d65cf 100644
--- a/src/lib/List/mediaTitle.ts
+++ b/src/lib/List/mediaTitle.ts
@@ -1,16 +1,21 @@
-import type { Media } from '$lib/Data/AniList/media';
-import settings from '$stores/settings';
-import { get } from 'svelte/store';
+import type { Media } from "$lib/Data/AniList/media";
+import settings from "$stores/settings";
+import { get } from "svelte/store";
export interface Title {
- title: string;
- hint: string;
+ title: string;
+ hint: string;
}
export const mediaTitle = (media: Media) => {
- if (!media) return 'Loading ...';
+ if (!media) return "Loading ...";
- const title = media.title;
+ const title = media.title;
- return title[get(settings).displayTitleFormat] || title.english || title.romaji || title.native;
+ return (
+ title[get(settings).displayTitleFormat] ||
+ title.english ||
+ title.romaji ||
+ title.native
+ );
};