aboutsummaryrefslogtreecommitdiff
path: root/src/lib/List
diff options
context:
space:
mode:
authorFuwn <[email protected]>2024-10-09 00:41:20 -0700
committerFuwn <[email protected]>2024-10-09 00:41:43 -0700
commit998b63a35256ac985a5a2714dd1ca451af4dfd8a (patch)
tree50796121a9d5ab0330fdc5d7e098bda2860d9726 /src/lib/List
parentfeat(graphql): add badgeCount field (diff)
downloaddue.moe-998b63a35256ac985a5a2714dd1ca451af4dfd8a.tar.xz
due.moe-998b63a35256ac985a5a2714dd1ca451af4dfd8a.zip
chore(prettier): use spaces instead of tabs
Diffstat (limited to 'src/lib/List')
-rw-r--r--src/lib/List/Anime/AnimeListTemplate.svelte170
-rw-r--r--src/lib/List/Anime/CleanAnimeList.svelte312
-rw-r--r--src/lib/List/Anime/CompletedAnimeList.svelte180
-rw-r--r--src/lib/List/Anime/DueAnimeList.svelte154
-rw-r--r--src/lib/List/Anime/DueIndexColumn.svelte28
-rw-r--r--src/lib/List/Anime/PlaceholderList.svelte26
-rw-r--r--src/lib/List/Anime/UpcomingAnimeList.svelte180
-rw-r--r--src/lib/List/CleanGrid.svelte116
-rw-r--r--src/lib/List/CleanList.svelte106
-rw-r--r--src/lib/List/ListTitle.svelte46
-rw-r--r--src/lib/List/Manga/CleanMangaList.svelte284
-rw-r--r--src/lib/List/Manga/MangaListTemplate.svelte762
-rw-r--r--src/lib/List/MediaTitleDisplay.svelte138
-rw-r--r--src/lib/List/covers.css66
-rw-r--r--src/lib/List/mediaTitle.ts10
15 files changed, 1289 insertions, 1289 deletions
diff --git a/src/lib/List/Anime/AnimeListTemplate.svelte b/src/lib/List/Anime/AnimeListTemplate.svelte
index 0bb2dfe8..08583d7c 100644
--- a/src/lib/List/Anime/AnimeListTemplate.svelte
+++ b/src/lib/List/Anime/AnimeListTemplate.svelte
@@ -1,96 +1,96 @@
<script lang="ts">
- /* eslint svelte/no-at-html-tags: "off" */
+ /* eslint svelte/no-at-html-tags: "off" */
- import type { AniListAuthorisation } from '$lib/Data/AniList/identity';
- import type { Media } from '$lib/Data/AniList/media';
- import Error 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 type { AniListAuthorisation } from '$lib/Data/AniList/identity';
+ import type { Media } from '$lib/Data/AniList/media';
+ import Error 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';
- export let endTime: number;
- export let cleanMedia: (
- media: Media[],
- displayUnresolved: boolean,
- subsPlease: SubsPlease | null,
- plannedOnly?: boolean
- ) => Media[];
- export let animeLists: Promise<Media[]>;
- export let user: AniListAuthorisation;
- export let title: any;
- export let completed = false;
- export let plannedOnly = false;
- export let upcoming = false;
- export let notYetReleased = false;
- export let dummy = false;
+ export let endTime: number;
+ export let cleanMedia: (
+ media: Media[],
+ displayUnresolved: boolean,
+ subsPlease: SubsPlease | null,
+ plannedOnly?: boolean
+ ) => Media[];
+ export let animeLists: Promise<Media[]>;
+ export let user: AniListAuthorisation;
+ export let title: any;
+ export let completed = false;
+ export let plannedOnly = false;
+ export let upcoming = false;
+ export let notYetReleased = false;
+ export let dummy = false;
- let lastUpdatedMedia = -1;
- let previousAnimeList: Media[];
- let pendingUpdate: number | null = null;
- let lastListSize = 8;
+ let lastUpdatedMedia = -1;
+ let previousAnimeList: Media[];
+ let pendingUpdate: number | null = null;
+ let lastListSize = 8;
- onMount(() => {
- if (browser) {
- const lastStoredList = localStorage.getItem(
- `last${
- notYetReleased ? 'NotYetReleased' : upcoming ? 'Upcoming' : completed ? 'Completed' : ''
- }AnimeListLength`
- );
- if (lastStoredList) lastListSize = parseInt(lastStoredList);
- }
- });
+ onMount(() => {
+ if (browser) {
+ const lastStoredList = localStorage.getItem(
+ `last${
+ notYetReleased ? 'NotYetReleased' : upcoming ? 'Upcoming' : completed ? 'Completed' : ''
+ }AnimeListLength`
+ );
+ if (lastStoredList) lastListSize = parseInt(lastStoredList);
+ }
+ });
</script>
{#if !$subsPlease && !dummy}
- <PlaceholderList count={lastListSize} {title} />
+ <PlaceholderList count={lastListSize} {title} />
{:else}
- {#await animeLists}
- {#if previousAnimeList}
- <CleanAnimeList
- media={previousAnimeList}
- {title}
- bind:animeLists
- {user}
- {endTime}
- bind:lastUpdatedMedia
- {completed}
- {notYetReleased}
- {upcoming}
- bind:previousAnimeList
- bind:pendingUpdate
- {dummy}
- />
- {:else}
- <PlaceholderList count={lastListSize} {title} />
- {/if}
- {:then media}
- {#if $identity.id === -2 && !dummy}
- <PlaceholderList count={lastListSize} {title} />
- {:else}
- <CleanAnimeList
- media={cleanMedia(media, $settings.displayUnresolved, $subsPlease, plannedOnly)}
- {title}
- bind:animeLists
- {user}
- {endTime}
- bind:lastUpdatedMedia
- {completed}
- {notYetReleased}
- {upcoming}
- bind:previousAnimeList
- bind:pendingUpdate
- {dummy}
- />
- {/if}
- {:catch}
- <ListTitle time={0} count={-1337} {title} />
+ {#await animeLists}
+ {#if previousAnimeList}
+ <CleanAnimeList
+ media={previousAnimeList}
+ {title}
+ bind:animeLists
+ {user}
+ {endTime}
+ bind:lastUpdatedMedia
+ {completed}
+ {notYetReleased}
+ {upcoming}
+ bind:previousAnimeList
+ bind:pendingUpdate
+ {dummy}
+ />
+ {:else}
+ <PlaceholderList count={lastListSize} {title} />
+ {/if}
+ {:then media}
+ {#if $identity.id === -2 && !dummy}
+ <PlaceholderList count={lastListSize} {title} />
+ {:else}
+ <CleanAnimeList
+ media={cleanMedia(media, $settings.displayUnresolved, $subsPlease, plannedOnly)}
+ {title}
+ bind:animeLists
+ {user}
+ {endTime}
+ bind:lastUpdatedMedia
+ {completed}
+ {notYetReleased}
+ {upcoming}
+ bind:previousAnimeList
+ bind:pendingUpdate
+ {dummy}
+ />
+ {/if}
+ {:catch}
+ <ListTitle time={0} count={-1337} {title} />
- <Error />
- {/await}
+ <Error />
+ {/await}
{/if}
diff --git a/src/lib/List/Anime/CleanAnimeList.svelte b/src/lib/List/Anime/CleanAnimeList.svelte
index f9584659..0220a366 100644
--- a/src/lib/List/Anime/CleanAnimeList.svelte
+++ b/src/lib/List/Anime/CleanAnimeList.svelte
@@ -1,168 +1,168 @@
<script lang="ts">
- /* 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';
-
- export let media: Media[];
- export let title: any;
- export let animeLists: Promise<Media[]>;
- export let user: AniListAuthorisation;
- export let endTime: number;
- export let lastUpdatedMedia: number;
- export let completed = false;
- export let previousAnimeList: Media[];
- export let pendingUpdate: number | null;
- export let upcoming = false;
- export let notYetReleased = false;
- export let dummy = false;
-
- let keyCacher: NodeJS.Timeout;
- let totalEpisodeDueCount = media
- .map((anime) => {
- if (anime.mediaListEntry?.status === 'COMPLETED') return 0;
-
- return (anime.nextAiringEpisode?.episode || 1) - (anime.mediaListEntry?.progress || 0) - 1;
- })
- .reduce((a, b) => a + b, 0);
-
- onMount(() => {
- if (dummy) return;
-
- keyCacher = setInterval(
- () => {
- media = media;
-
- if (
- media.some(
- (m) => m.nextAiringEpisode?.airingAt && m.nextAiringEpisode.airingAt < Date.now() / 1000
- )
- )
- animeLists = cleanCache(user, $identity);
- },
- (() => {
- const airingAt = media
- .filter(
- (m) =>
- (m.status === 'RELEASING' || m.status === 'NOT_YET_RELEASED') &&
- m.nextAiringEpisode?.airingAt
- )
- .find((m) => m.nextAiringEpisode?.airingAt)?.nextAiringEpisode?.airingAt;
- const untilAiring = airingAt
- ? Math.round((airingAt - Date.now() / 1000) * 100) / 100
- : undefined;
-
- return untilAiring ? (untilAiring < 0 ? 1000 : untilAiring) : 1000;
- })()
- );
-
- if (browser)
- localStorage.setItem(
- `last${
- notYetReleased ? 'NotYetReleased' : upcoming ? 'Upcoming' : completed ? 'Completed' : ''
- }AnimeListLength`,
- media.length.toString()
- );
- });
-
- onDestroy(() => clearInterval(keyCacher));
-
- 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;
- });
- }
- };
+ /* 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';
+
+ export let media: Media[];
+ export let title: any;
+ export let animeLists: Promise<Media[]>;
+ export let user: AniListAuthorisation;
+ export let endTime: number;
+ export let lastUpdatedMedia: number;
+ export let completed = false;
+ export let previousAnimeList: Media[];
+ export let pendingUpdate: number | null;
+ export let upcoming = false;
+ export let notYetReleased = false;
+ export let dummy = false;
+
+ let keyCacher: NodeJS.Timeout;
+ let totalEpisodeDueCount = media
+ .map((anime) => {
+ if (anime.mediaListEntry?.status === 'COMPLETED') return 0;
+
+ return (anime.nextAiringEpisode?.episode || 1) - (anime.mediaListEntry?.progress || 0) - 1;
+ })
+ .reduce((a, b) => a + b, 0);
+
+ onMount(() => {
+ if (dummy) return;
+
+ keyCacher = setInterval(
+ () => {
+ media = media;
+
+ if (
+ media.some(
+ (m) => m.nextAiringEpisode?.airingAt && m.nextAiringEpisode.airingAt < Date.now() / 1000
+ )
+ )
+ animeLists = cleanCache(user, $identity);
+ },
+ (() => {
+ const airingAt = media
+ .filter(
+ (m) =>
+ (m.status === 'RELEASING' || m.status === 'NOT_YET_RELEASED') &&
+ m.nextAiringEpisode?.airingAt
+ )
+ .find((m) => m.nextAiringEpisode?.airingAt)?.nextAiringEpisode?.airingAt;
+ const untilAiring = airingAt
+ ? Math.round((airingAt - Date.now() / 1000) * 100) / 100
+ : undefined;
+
+ return untilAiring ? (untilAiring < 0 ? 1000 : untilAiring) : 1000;
+ })()
+ );
+
+ if (browser)
+ localStorage.setItem(
+ `last${
+ notYetReleased ? 'NotYetReleased' : upcoming ? 'Upcoming' : completed ? 'Completed' : ''
+ }AnimeListLength`,
+ media.length.toString()
+ );
+ });
+
+ onDestroy(() => clearInterval(keyCacher));
+
+ 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;
+ });
+ }
+ };
</script>
<ListTitle
- time={endTime / 1000}
- count={$settings.displayTotalDueEpisodes && !notYetReleased && !completed && !upcoming
- ? totalEpisodeDueCount
- : media.length}
- {title}
- hideTime={dummy}
- hideCount={dummy}
+ time={endTime / 1000}
+ count={$settings.displayTotalDueEpisodes && !notYetReleased && !completed && !upcoming
+ ? totalEpisodeDueCount
+ : media.length}
+ {title}
+ hideTime={dummy}
+ hideCount={dummy}
/>
{#if media.length === 0}
- No anime to display. <button on:click={() => (animeLists = cleanCache(user, $identity))}>
- Force refresh
- </button>
+ No anime to display. <button on:click={() => (animeLists = cleanCache(user, $identity))}>
+ Force refresh
+ </button>
{/if}
{#if $settings.displayCoverModeAnime}
- <CleanGrid {media} {dummy} type="anime" {upcoming} {notYetReleased}>
- <div slot="title" let:title={anime} let:progress>
- {#if !upcoming && !notYetReleased}
- {pendingUpdate === anime.id ? progress + 1 : progress}{@html totalEpisodes(anime)}
- <button
- class={`button-square button-action ${pendingUpdate === anime.id ? 'opaque' : ''}`}
- style={pendingUpdate === anime.id ? 'pointer-events: none;' : ''}
- on:click={() => increment(anime, progress)}>+</button
- >
- {#if !completed || dummy}
- [{anime.nextAiringEpisode?.episode === -1
- ? '?'
- : (anime.nextAiringEpisode?.episode || 1) - 1}]
- <br />
- <AiringTime originalAnime={anime} />
- {/if}
- {:else}
- <AiringTime originalAnime={anime} upcoming={true} />
- {/if}
- </div>
- </CleanGrid>
+ <CleanGrid {media} {dummy} type="anime" {upcoming} {notYetReleased}>
+ <div slot="title" let:title={anime} let:progress>
+ {#if !upcoming && !notYetReleased}
+ {pendingUpdate === anime.id ? progress + 1 : progress}{@html totalEpisodes(anime)}
+ <button
+ class={`button-square button-action ${pendingUpdate === anime.id ? 'opaque' : ''}`}
+ style={pendingUpdate === anime.id ? 'pointer-events: none;' : ''}
+ on:click={() => increment(anime, progress)}>+</button
+ >
+ {#if !completed || dummy}
+ [{anime.nextAiringEpisode?.episode === -1
+ ? '?'
+ : (anime.nextAiringEpisode?.episode || 1) - 1}]
+ <br />
+ <AiringTime originalAnime={anime} />
+ {/if}
+ {:else}
+ <AiringTime originalAnime={anime} upcoming={true} />
+ {/if}
+ </div>
+ </CleanGrid>
{:else}
- <CleanList {media} type="anime" {upcoming} {notYetReleased} {lastUpdatedMedia}>
- <span slot="information" let:title={anime} let:progress>
- {#if !upcoming || notYetReleased || !$settings.displayCountdownRightAligned}
- <span class="opaque">|</span>
- {/if}
- {#if !upcoming || notYetReleased}
- <!-- {anime.mediaListEntry?.progress || 0}{@html totalEpisodes(anime)} -->
- {pendingUpdate === anime.id ? progress + 1 : progress}{@html totalEpisodes(anime)}
- <button
- class={`button-square button-action ${pendingUpdate === anime.id ? 'opaque' : ''}`}
- style={pendingUpdate === anime.id ? 'pointer-events: none;' : ''}
- on:click={() => increment(anime, progress)}>+</button
- >
- {#if !completed}
- [{anime.nextAiringEpisode?.episode === -1
- ? '?'
- : (anime.nextAiringEpisode?.episode || 1) - 1}]
- <span class:countdown={$settings.displayCountdownRightAligned}>
- <AiringTime originalAnime={anime} />
- </span>
- {/if}
- {:else}
- <span class:countdown={$settings.displayCountdownRightAligned}>
- <AiringTime originalAnime={anime} upcoming={true} />
- </span>
- {/if}
- </span>
- </CleanList>
+ <CleanList {media} type="anime" {upcoming} {notYetReleased} {lastUpdatedMedia}>
+ <span slot="information" let:title={anime} let:progress>
+ {#if !upcoming || notYetReleased || !$settings.displayCountdownRightAligned}
+ <span class="opaque">|</span>
+ {/if}
+ {#if !upcoming || notYetReleased}
+ <!-- {anime.mediaListEntry?.progress || 0}{@html totalEpisodes(anime)} -->
+ {pendingUpdate === anime.id ? progress + 1 : progress}{@html totalEpisodes(anime)}
+ <button
+ class={`button-square button-action ${pendingUpdate === anime.id ? 'opaque' : ''}`}
+ style={pendingUpdate === anime.id ? 'pointer-events: none;' : ''}
+ on:click={() => increment(anime, progress)}>+</button
+ >
+ {#if !completed}
+ [{anime.nextAiringEpisode?.episode === -1
+ ? '?'
+ : (anime.nextAiringEpisode?.episode || 1) - 1}]
+ <span class:countdown={$settings.displayCountdownRightAligned}>
+ <AiringTime originalAnime={anime} />
+ </span>
+ {/if}
+ {:else}
+ <span class:countdown={$settings.displayCountdownRightAligned}>
+ <AiringTime originalAnime={anime} upcoming={true} />
+ </span>
+ {/if}
+ </span>
+ </CleanList>
{/if}
diff --git a/src/lib/List/Anime/CompletedAnimeList.svelte b/src/lib/List/Anime/CompletedAnimeList.svelte
index 29d62724..76ec7207 100644
--- a/src/lib/List/Anime/CompletedAnimeList.svelte
+++ b/src/lib/List/Anime/CompletedAnimeList.svelte
@@ -1,108 +1,108 @@
<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 { getNotificationsContext } from 'svelte-notifications';
- 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 { getNotificationsContext } from 'svelte-notifications';
+ 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: ''
- };
- export let dummy = false;
+ export let user: AniListAuthorisation = {
+ accessToken: '',
+ refreshToken: '',
+ expiresIn: 0,
+ tokenType: ''
+ };
+ export let dummy = false;
- const { addNotification } = getNotificationsContext();
- let animeLists: Promise<Media[]>;
- let startTime: number;
- let endTime: number;
+ const { addNotification } = getNotificationsContext();
+ let animeLists: Promise<Media[]>;
+ let startTime: number;
+ let endTime: number;
- onMount(async () => {
- startTime = performance.now();
+ onMount(async () => {
+ startTime = performance.now();
- if (dummy) {
- animeLists = Promise.resolve(
- 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
- )
- .sort(() => 0.5 - Math.random())
- .map((anime) => {
- const nextEpisode = Math.floor(Math.random() * (anime.episodes || 0)) + 1 || 1;
+ if (dummy) {
+ animeLists = Promise.resolve(
+ 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
+ )
+ .sort(() => 0.5 - Math.random())
+ .map((anime) => {
+ const nextEpisode = Math.floor(Math.random() * (anime.episodes || 0)) + 1 || 1;
- anime.status = 'FINISHED';
- anime.nextAiringEpisode = {
- airingAt:
- Math.floor(Date.now() / 1000) +
- Math.floor(Math.random() * 7 * 24 * 60 * 60) +
- 60 * 60,
- episode: Math.floor(Math.random() * (anime.episodes || 0)) + 1 || 1
- };
- anime.mediaListEntry.progress = Math.floor(Math.random() * nextEpisode) || 1;
+ anime.status = 'FINISHED';
+ anime.nextAiringEpisode = {
+ airingAt:
+ Math.floor(Date.now() / 1000) +
+ Math.floor(Math.random() * 7 * 24 * 60 * 60) +
+ 60 * 60,
+ episode: Math.floor(Math.random() * (anime.episodes || 0)) + 1 || 1
+ };
+ anime.mediaListEntry.progress = Math.floor(Math.random() * nextEpisode) || 1;
- return anime;
- })
- .sort(
- (a, b) => (a.nextAiringEpisode?.airingAt || 0) - (b.nextAiringEpisode?.airingAt || 0)
- )
- .slice(0, 7) as unknown as Media[]
- );
- } else {
- animeLists = mediaListCollection(user, $identity, Type.Anime, $anime, $lastPruneTimes.anime, {
- addNotification
- });
- }
- });
+ return anime;
+ })
+ .sort(
+ (a, b) => (a.nextAiringEpisode?.airingAt || 0) - (b.nextAiringEpisode?.airingAt || 0)
+ )
+ .slice(0, 7) as unknown as Media[]
+ );
+ } else {
+ animeLists = mediaListCollection(user, $identity, Type.Anime, $anime, $lastPruneTimes.anime, {
+ addNotification
+ });
+ }
+ });
- const cleanMedia = (anime: Media[]) => {
- if (anime && dummy) return anime;
+ const cleanMedia = (anime: Media[]) => {
+ 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) => {
- const difference = (anime: Media) =>
- anime.episodes - (anime.mediaListEntry || { progress: 0 }).progress;
+ outdatedCompletedAnime.sort((a: Media, b: Media) => {
+ const difference = (anime: Media) =>
+ anime.episodes - (anime.mediaListEntry || { progress: 0 }).progress;
- return difference(a) - difference(b);
- });
+ return difference(a) - difference(b);
+ });
- if (!endTime) endTime = performance.now() - startTime;
+ if (!endTime) endTime = performance.now() - startTime;
- return outdatedCompletedAnime;
- };
+ return outdatedCompletedAnime;
+ };
</script>
<AnimeList
- {endTime}
- {cleanMedia}
- bind:animeLists
- {user}
- title={$locale().lists.completed.anime}
- completed
- {dummy}
+ {endTime}
+ {cleanMedia}
+ bind:animeLists
+ {user}
+ title={$locale().lists.completed.anime}
+ completed
+ {dummy}
/>
diff --git a/src/lib/List/Anime/DueAnimeList.svelte b/src/lib/List/Anime/DueAnimeList.svelte
index 28744b01..0109d5fa 100644
--- a/src/lib/List/Anime/DueAnimeList.svelte
+++ b/src/lib/List/Anime/DueAnimeList.svelte
@@ -1,94 +1,94 @@
<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 { getNotificationsContext } from 'svelte-notifications';
- 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 { getNotificationsContext } from 'svelte-notifications';
+ import locale from '$stores/locale';
+ import identity from '$stores/identity';
- export let user: AniListAuthorisation;
+ export let user: AniListAuthorisation;
- const { addNotification } = getNotificationsContext();
- let animeLists: Promise<Media[]>;
- let startTime: number;
- let endTime: number;
+ const { addNotification } = getNotificationsContext();
+ let animeLists: Promise<Media[]>;
+ let startTime: number;
+ let endTime: number;
- const keyCacher = setInterval(() => {
- startTime = performance.now();
- endTime = -1;
- animeLists = mediaListCollection(user, $identity, Type.Anime, $anime, $lastPruneTimes.anime, {
- forcePrune: true,
- addNotification
- });
- }, $settings.cacheMinutes * 1000 * 60);
+ const keyCacher = setInterval(() => {
+ startTime = performance.now();
+ endTime = -1;
+ animeLists = mediaListCollection(user, $identity, Type.Anime, $anime, $lastPruneTimes.anime, {
+ forcePrune: true,
+ addNotification
+ });
+ }, $settings.cacheMinutes * 1000 * 60);
- onMount(async () => {
- startTime = performance.now();
- animeLists = mediaListCollection(user, $identity, Type.Anime, $anime, $lastPruneTimes.anime, {
- addNotification
- });
- });
+ onMount(async () => {
+ startTime = performance.now();
+ animeLists = mediaListCollection(user, $identity, Type.Anime, $anime, $lastPruneTimes.anime, {
+ addNotification
+ });
+ });
- onDestroy(() => clearInterval(keyCacher));
+ onDestroy(() => clearInterval(keyCacher));
- const cleanMedia = (
- anime: Media[],
- displayUnresolved: boolean,
- subsPlease: SubsPlease | null
- ) => {
- if (anime === undefined) return [];
+ 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)
- )
- .filter(
- (media: Media) =>
- // Outdated media
- (media.nextAiringEpisode || { episode: 0 }).episode - 1 >
- (media.mediaListEntry || { progress: 0 }).progress
- )
- .map((media: Media) => {
- if ((media.nextAiringEpisode || { episode: 0 }).episode - 1 <= 0)
- media.nextAiringEpisode = { episode: -1 };
+ 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)
+ )
+ .filter(
+ (media: Media) =>
+ // Outdated media
+ (media.nextAiringEpisode || { episode: 0 }).episode - 1 >
+ (media.mediaListEntry || { progress: 0 }).progress
+ )
+ .map((media: Media) => {
+ if ((media.nextAiringEpisode || { episode: 0 }).episode - 1 <= 0)
+ media.nextAiringEpisode = { episode: -1 };
- return media;
- });
+ return media;
+ });
- if (!displayUnresolved)
- dueAnime = dueAnime.filter((media: Media) => media.nextAiringEpisode?.episode !== -1);
+ if (!displayUnresolved)
+ dueAnime = dueAnime.filter((media: Media) => media.nextAiringEpisode?.episode !== -1);
- dueAnime.sort((a: Media, b: Media) => {
- if ($settings.displaySortedByDifference === true) {
- const difference = (anime: Media) =>
- (anime.nextAiringEpisode?.episode === -1
- ? 99999
- : anime.nextAiringEpisode?.episode || -1) -
- (anime.mediaListEntry || { progress: 0 }).progress;
+ dueAnime.sort((a: Media, b: Media) => {
+ if ($settings.displaySortedByDifference === true) {
+ const difference = (anime: Media) =>
+ (anime.nextAiringEpisode?.episode === -1
+ ? 99999
+ : anime.nextAiringEpisode?.episode || -1) -
+ (anime.mediaListEntry || { progress: 0 }).progress;
- return difference(a) - difference(b);
- } else {
- return (a.nextAiringEpisode?.airingAt || 9999) - (b.nextAiringEpisode?.airingAt || 9999);
- }
- });
+ return difference(a) - difference(b);
+ } else {
+ return (a.nextAiringEpisode?.airingAt || 9999) - (b.nextAiringEpisode?.airingAt || 9999);
+ }
+ });
- if (!endTime || endTime === -1) endTime = performance.now() - startTime;
+ if (!endTime || endTime === -1) endTime = performance.now() - startTime;
- return dueAnime;
- };
+ return dueAnime;
+ };
</script>
<AnimeList {endTime} {cleanMedia} bind:animeLists {user} title={$locale().lists.due.episodes} />
diff --git a/src/lib/List/Anime/DueIndexColumn.svelte b/src/lib/List/Anime/DueIndexColumn.svelte
index 6d81a661..61f2a178 100644
--- a/src/lib/List/Anime/DueIndexColumn.svelte
+++ b/src/lib/List/Anime/DueIndexColumn.svelte
@@ -1,21 +1,21 @@
<script lang="ts">
- import type { AniListAuthorisation } from '$lib/Data/AniList/identity';
- import Skeleton from '$lib/Loading/Skeleton.svelte';
- import locale from '$stores/locale';
- import settings from '$stores/settings';
- import ListTitle from '../ListTitle.svelte';
- import AnimeList from '$lib/List/Anime/DueAnimeList.svelte';
+ import type { AniListAuthorisation } from '$lib/Data/AniList/identity';
+ import Skeleton from '$lib/Loading/Skeleton.svelte';
+ import locale from '$stores/locale';
+ import settings from '$stores/settings';
+ import ListTitle from '../ListTitle.svelte';
+ import AnimeList from '$lib/List/Anime/DueAnimeList.svelte';
- export let userIdentity: { id: number };
- export let user: AniListAuthorisation;
+ export let userIdentity: { id: number };
+ export let user: AniListAuthorisation;
</script>
<details open={!$settings.displayAnimeCollapsed} class="list list-due">
- {#if userIdentity.id !== -2}
- <AnimeList {user} />
- {:else}
- <ListTitle title={$locale().lists.due.episodes} />
+ {#if userIdentity.id !== -2}
+ <AnimeList {user} />
+ {:else}
+ <ListTitle title={$locale().lists.due.episodes} />
- <Skeleton card={false} count={5} height="0.9rem" list />
- {/if}
+ <Skeleton card={false} count={5} height="0.9rem" list />
+ {/if}
</details>
diff --git a/src/lib/List/Anime/PlaceholderList.svelte b/src/lib/List/Anime/PlaceholderList.svelte
index 5d7eb908..1f701d79 100644
--- a/src/lib/List/Anime/PlaceholderList.svelte
+++ b/src/lib/List/Anime/PlaceholderList.svelte
@@ -1,21 +1,21 @@
<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;
+ export let title: Title;
+ export let count = 8;
</script>
<ListTitle {title} />
<Skeleton
- card={false}
- {count}
- pad={$settings.displayCoverModeAnime}
- height={$settings.displayCoverModeAnime ? '8rem' : '0.9rem'}
- width={$settings.displayCoverModeAnime ? `${$settings.displayCoverWidth / 1.05}px` : '100%'}
- list={!$settings.displayCoverModeAnime}
- grid={$settings.displayCoverModeAnime}
+ card={false}
+ {count}
+ pad={$settings.displayCoverModeAnime}
+ height={$settings.displayCoverModeAnime ? '8rem' : '0.9rem'}
+ width={$settings.displayCoverModeAnime ? `${$settings.displayCoverWidth / 1.05}px` : '100%'}
+ list={!$settings.displayCoverModeAnime}
+ grid={$settings.displayCoverModeAnime}
/>
diff --git a/src/lib/List/Anime/UpcomingAnimeList.svelte b/src/lib/List/Anime/UpcomingAnimeList.svelte
index 7e1e2462..7b01af86 100644
--- a/src/lib/List/Anime/UpcomingAnimeList.svelte
+++ b/src/lib/List/Anime/UpcomingAnimeList.svelte
@@ -1,110 +1,110 @@
<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 AnimeList from './AnimeListTemplate.svelte';
- import settings from '$stores/settings';
- import type { SubsPlease } from '$lib/Media/Anime/Airing/Subtitled/subsPlease';
- import { getNotificationsContext } from 'svelte-notifications';
- import locale from '$stores/locale';
- import identity from '$stores/identity';
- import { injectAiringTime } from '$lib/Media/Anime/Airing/Subtitled/match';
- import revalidateAnime from '$stores/revalidateAnime';
+ 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 { getNotificationsContext } from 'svelte-notifications';
+ import locale from '$stores/locale';
+ import identity from '$stores/identity';
+ import { injectAiringTime } from '$lib/Media/Anime/Airing/Subtitled/match';
+ import revalidateAnime from '$stores/revalidateAnime';
- export let user: AniListAuthorisation;
+ export let user: AniListAuthorisation;
- const { addNotification } = getNotificationsContext();
- let animeLists: Promise<Media[]>;
- let startTime: number;
- let endTime: number;
+ const { addNotification } = getNotificationsContext();
+ let animeLists: Promise<Media[]>;
+ let startTime: number;
+ let endTime: number;
- onMount(async () => {
- startTime = performance.now();
- animeLists = mediaListCollection(user, $identity, Type.Anime, $anime, $lastPruneTimes.anime, {
- addNotification,
- notificationType: 'Upcoming Episodes'
- });
- });
+ onMount(async () => {
+ 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
- ) => {
- if (anime === undefined) return [];
+ const cleanMedia = (
+ anime: Media[],
+ displayUnresolved: boolean,
+ subsPlease: SubsPlease | null,
+ plannedOnly = true
+ ) => {
+ 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) ||
- (media.nextAiringEpisode || { episode: 0 }).episode - 1 <=
- (media.mediaListEntry || { progress: 0 }).progress
- )
- .map((media: Media) => {
- // Adjust for planned anime
- if (
- ($settings.displayPlannedAnime ? media.episodes !== 1 : true) &&
- (media.nextAiringEpisode || { episode: 0 }).episode - 1 <= 0
- )
- 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) ||
+ (media.nextAiringEpisode || { episode: 0 }).episode - 1 <=
+ (media.mediaListEntry || { progress: 0 }).progress
+ )
+ .map((media: Media) => {
+ // Adjust for planned anime
+ if (
+ ($settings.displayPlannedAnime ? media.episodes !== 1 : true) &&
+ (media.nextAiringEpisode || { episode: 0 }).episode - 1 <= 0
+ )
+ 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>
<AnimeList
- {endTime}
- {cleanMedia}
- bind:animeLists
- {user}
- title={$locale().lists.upcoming.episodes}
- upcoming
+ {endTime}
+ {cleanMedia}
+ bind:animeLists
+ {user}
+ title={$locale().lists.upcoming.episodes}
+ upcoming
/>
{#if $settings.displayPlannedAnime}
- <p />
+ <p />
- <AnimeList
- {endTime}
- {cleanMedia}
- bind:animeLists
- {user}
- title={$locale().lists.upcoming.notYetReleased}
- notYetReleased
- plannedOnly
- />
+ <AnimeList
+ {endTime}
+ {cleanMedia}
+ bind:animeLists
+ {user}
+ title={$locale().lists.upcoming.notYetReleased}
+ notYetReleased
+ plannedOnly
+ />
{/if}
diff --git a/src/lib/List/CleanGrid.svelte b/src/lib/List/CleanGrid.svelte
index 18ee51f6..ec93a685 100644
--- a/src/lib/List/CleanGrid.svelte
+++ b/src/lib/List/CleanGrid.svelte
@@ -1,69 +1,69 @@
<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 upcoming = false;
- export let notYetReleased = false;
+ export let media: Media[];
+ export let dummy = false;
+ export let type: 'anime' | 'manga';
+ export let upcoming = false;
+ export let notYetReleased = false;
- let uniqueID = new Date().getTime();
+ let uniqueID = new Date().getTime();
</script>
<div
- class="covers"
- style={`grid-template-columns: repeat(auto-fill, minmax(${$settings.displayCoverWidth}px, 1fr))`}
+ class="covers"
+ style={`grid-template-columns: repeat(auto-fill, minmax(${$settings.displayCoverWidth}px, 1fr))`}
>
- {#each media as title}
- {@const progress = (title.mediaListEntry || { progress: 0 }).progress}
+ {#each media as title}
+ {@const progress = (title.mediaListEntry || { progress: 0 }).progress}
- {#if type === 'anime' ? upcoming || notYetReleased || progress !== (title.nextAiringEpisode?.episode || 9999) - 1 : progress != title.episodes}
- <div class="cover-card" id={`${type}-${title.id}-${uniqueID}`}>
- <LinkedTooltip
- pin={`${type}-${title.id}-${uniqueID}`}
- content={title ? mediaTitle(title) : ''}
- relative={dummy}
- >
- <div class="cover-card-image">
- <a
- href={$settings.displayCopyMediaTitleNotLink
- ? '#'
- : outboundLink(title, type, $settings.displayOutboundLinksTo)}
- on:click={(e) => {
- if ($settings.displayCopyMediaTitleNotLink) {
- e.preventDefault();
+ {#if type === 'anime' ? upcoming || notYetReleased || progress !== (title.nextAiringEpisode?.episode || 9999) - 1 : progress != title.episodes}
+ <div class="cover-card" id={`${type}-${title.id}-${uniqueID}`}>
+ <LinkedTooltip
+ pin={`${type}-${title.id}-${uniqueID}`}
+ content={title ? mediaTitle(title) : ''}
+ relative={dummy}
+ >
+ <div class="cover-card-image">
+ <a
+ href={$settings.displayCopyMediaTitleNotLink
+ ? '#'
+ : outboundLink(title, type, $settings.displayOutboundLinksTo)}
+ on:click={(e) => {
+ if ($settings.displayCopyMediaTitleNotLink) {
+ e.preventDefault();
- navigator.clipboard.writeText(title.title.romaji);
- }
- }}
- target="_blank"
- >
- <span class="cover-container">
- <ParallaxImage
- source={$settings.displayDataSaver
- ? title.coverImage.medium
- : title.coverImage.extraLarge}
- alternativeText="Cover"
- limit={12.5}
- classList={`cover${
- title.isAdult && $settings.displayBlurAdultContent ? ' adult' : ''
- }`}
- />
- </span>
- </a>
- </div>
- </LinkedTooltip>
+ navigator.clipboard.writeText(title.title.romaji);
+ }
+ }}
+ target="_blank"
+ >
+ <span class="cover-container">
+ <ParallaxImage
+ source={$settings.displayDataSaver
+ ? title.coverImage.medium
+ : title.coverImage.extraLarge}
+ alternativeText="Cover"
+ limit={12.5}
+ classList={`cover${
+ title.isAdult && $settings.displayBlurAdultContent ? ' adult' : ''
+ }`}
+ />
+ </span>
+ </a>
+ </div>
+ </LinkedTooltip>
- <div class="cover-title">
- <slot name="title" {progress} {title} />
- </div>
- </div>
- {/if}
- {/each}
+ <div class="cover-title">
+ <slot name="title" {progress} {title} />
+ </div>
+ </div>
+ {/if}
+ {/each}
</div>
diff --git a/src/lib/List/CleanList.svelte b/src/lib/List/CleanList.svelte
index d07f45f1..47811932 100644
--- a/src/lib/List/CleanList.svelte
+++ b/src/lib/List/CleanList.svelte
@@ -1,62 +1,62 @@
<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 upcoming = false;
- export let notYetReleased = false;
- export let lastUpdatedMedia: number;
+ export let media: Media[];
+ export let type: 'anime' | 'manga';
+ export let upcoming = false;
+ export let notYetReleased = false;
+ export let lastUpdatedMedia: number;
</script>
<ul>
- {#each media as title}
- {@const progress = (title.mediaListEntry || { progress: 0 }).progress}
+ {#each media as title}
+ {@const progress = (title.mediaListEntry || { progress: 0 }).progress}
- {#if type === 'anime' ? upcoming || notYetReleased || progress !== (title.nextAiringEpisode?.episode || 9999) - 1 : progress !== title.episodes}
- <li class="entry">
- <span class="content">
- <LinkedTooltip
- id={`${type}-${title.id}`}
- content={`<img src="${
- $settings.displayDataSaver ? title.coverImage.medium : title.coverImage.extraLarge
- }" style="width: 250px; object-fit: cover; border-radius: 8px;" />`}
- pin={`${type}-${title.id}`}
- pinPosition={type === 'anime' ? 'right' : 'left'}
- disable={!$settings.displayHoverCover}
- >
- <a
- href={$settings.displayCopyMediaTitleNotLink
- ? '#'
- : outboundLink(title, type, $settings.displayOutboundLinksTo)}
- on:click={(e) => {
- if ($settings.displayCopyMediaTitleNotLink) {
- e.preventDefault();
+ {#if type === 'anime' ? upcoming || notYetReleased || progress !== (title.nextAiringEpisode?.episode || 9999) - 1 : progress !== title.episodes}
+ <li class="entry">
+ <span class="content">
+ <LinkedTooltip
+ id={`${type}-${title.id}`}
+ content={`<img src="${
+ $settings.displayDataSaver ? title.coverImage.medium : title.coverImage.extraLarge
+ }" style="width: 250px; object-fit: cover; border-radius: 8px;" />`}
+ pin={`${type}-${title.id}`}
+ pinPosition={type === 'anime' ? 'right' : 'left'}
+ disable={!$settings.displayHoverCover}
+ >
+ <a
+ href={$settings.displayCopyMediaTitleNotLink
+ ? '#'
+ : outboundLink(title, type, $settings.displayOutboundLinksTo)}
+ on:click={(e) => {
+ if ($settings.displayCopyMediaTitleNotLink) {
+ e.preventDefault();
- navigator.clipboard.writeText(title.title.romaji);
- }
- }}
- target="_blank"
- >
- <span
- style={lastUpdatedMedia === title.id && title.chapters !== progress
- ? 'color: lightcoral'
- : ''}
- >
- <MediaTitleDisplay title={title.title} />
- </span>
- </a>
- </LinkedTooltip>
- {#if $settings.displaySocialButton}
- [<a href={`https://anilist.co/${type}/${title.id}/social`} target="_blank">S</a>]
- {/if}
+ navigator.clipboard.writeText(title.title.romaji);
+ }
+ }}
+ target="_blank"
+ >
+ <span
+ style={lastUpdatedMedia === title.id && title.chapters !== progress
+ ? 'color: lightcoral'
+ : ''}
+ >
+ <MediaTitleDisplay title={title.title} />
+ </span>
+ </a>
+ </LinkedTooltip>
+ {#if $settings.displaySocialButton}
+ [<a href={`https://anilist.co/${type}/${title.id}/social`} target="_blank">S</a>]
+ {/if}
- <slot name="information" {progress} {title} />
- </span>
- </li>
- {/if}
- {/each}
+ <slot name="information" {progress} {title} />
+ </span>
+ </li>
+ {/if}
+ {/each}
</ul>
diff --git a/src/lib/List/ListTitle.svelte b/src/lib/List/ListTitle.svelte
index 2c597c09..21013b52 100644
--- a/src/lib/List/ListTitle.svelte
+++ b/src/lib/List/ListTitle.svelte
@@ -1,32 +1,32 @@
<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.'
- };
- export let progress: undefined | number = undefined;
- export let hideTime = false;
- export let hideCount = false;
+ export let time: number | undefined = undefined;
+ export let count = -1337;
+ export let title: Title = {
+ title: 'Media List',
+ hint: 'This is a media list.'
+ };
+ export let progress: undefined | number = undefined;
+ export let hideTime = false;
+ export let hideCount = false;
</script>
<summary>
- <span title={title.hint || undefined} use:tooltip>{title.title}</span>
- {#if !hideCount}[{count === -1337 ? '...' : count}]{/if}
- <!-- {#if !hideCount}[{#if count === -1337}...{:else}<NumberTicker
+ <span title={title.hint || undefined} use:tooltip>{title.title}</span>
+ {#if !hideCount}[{count === -1337 ? '...' : count}]{/if}
+ <!-- {#if !hideCount}[{#if count === -1337}...{:else}<NumberTicker
end={count}
duration={Math.min(2500, Math.max(500, Math.abs(count - 0) * 10))}
/>{/if}]{/if} -->
- {#if !hideTime}
- <small class="opaque">{time ? time.toFixed(3) : '...'}s</small>
- {/if}
- <slot />
- {#if progress !== undefined}
- <button class="badge unclickable-button button-badge badge-info">
- {progress.toFixed(0)}%
- </button>
- {/if}
+ {#if !hideTime}
+ <small class="opaque">{time ? time.toFixed(3) : '...'}s</small>
+ {/if}
+ <slot />
+ {#if progress !== undefined}
+ <button class="badge unclickable-button button-badge badge-info">
+ {progress.toFixed(0)}%
+ </button>
+ {/if}
</summary>
diff --git a/src/lib/List/Manga/CleanMangaList.svelte b/src/lib/List/Manga/CleanMangaList.svelte
index be02bace..dfaa187c 100644
--- a/src/lib/List/Manga/CleanMangaList.svelte
+++ b/src/lib/List/Manga/CleanMangaList.svelte
@@ -1,166 +1,166 @@
<script lang="ts">
- import type { Media } from '$lib/Data/AniList/media';
- import Error 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 type { Media } from '$lib/Data/AniList/media';
+ import Error 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';
- 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 pendingUpdate: number | null;
- export let due: boolean;
- export let rateLimited: boolean;
- export let authorised: boolean;
- export let dummy = false;
+ 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 pendingUpdate: number | null;
+ export let due: boolean;
+ export let rateLimited: boolean;
+ export let authorised: boolean;
+ export let dummy = false;
- let serviceStatusResponse: Promise<Response>;
+ let serviceStatusResponse: Promise<Response>;
- onMount(() => {
- serviceStatusResponse = fetch(proxy('https://api.mangadex.org/ping'));
+ onMount(() => {
+ serviceStatusResponse = fetch(proxy('https://api.mangadex.org/ping'));
- if (browser)
- localStorage.setItem(`last${due ? '' : 'Completed'}MangaListLength`, media.length.toString());
- });
+ if (browser)
+ localStorage.setItem(`last${due ? '' : 'Completed'}MangaListLength`, media.length.toString());
+ });
- const increment = (manga: Media) => {
- if (!(pendingUpdate === manga.id || dummy))
- updateMedia(manga.id, manga.mediaListEntry?.progress, media);
- };
+ const increment = (manga: Media) => {
+ if (!(pendingUpdate === manga.id || dummy))
+ updateMedia(manga.id, manga.mediaListEntry?.progress, media);
+ };
</script>
{#if authorised}
- <ListTitle
- count={media.length}
- time={endTime / 1000}
- title={due
- ? $locale().lists.due.mangaAndLightNovels
- : $locale().lists.completed.mangaAndLightNovels}
- hideTime={dummy}
- hideCount={dummy}
- >
- {#if !dummy}
- <button
- class="small-button"
- title="Force a full refresh"
- on:click={cleanCache}
- data-umami-event="Force Refresh Manga">Refresh</button
- >
- {/if}
- </ListTitle>
+ <ListTitle
+ count={media.length}
+ time={endTime / 1000}
+ title={due
+ ? $locale().lists.due.mangaAndLightNovels
+ : $locale().lists.completed.mangaAndLightNovels}
+ hideTime={dummy}
+ hideCount={dummy}
+ >
+ {#if !dummy}
+ <button
+ class="small-button"
+ title="Force a full refresh"
+ on:click={cleanCache}
+ data-umami-event="Force Refresh Manga">Refresh</button
+ >
+ {/if}
+ </ListTitle>
{/if}
{#if rateLimited}
- {#await serviceStatusResponse}
- <Skeleton card={false} count={1} height="0.9rem" list />
- {:then status}
- {#if status}
- {#if status.status === 503}
- <a href="https://due.moe">due.moe</a>'s manga data source is currently down for maintenance.
- Please check back later.
- {:else if status.status !== 200}
- <a href="https://due.moe">due.moe</a>'s manga data source is currently unavailable. Please
- check back later.
- {:else}
- <Error />
- {/if}
- {:else}
- <Skeleton card={false} count={1} height="0.9rem" list />
- {/if}
- {:catch}
- <a href="https://due.moe">due.moe</a>'s manga data source is currently unreachable. Please check
- back later.
- {/await}
+ {#await serviceStatusResponse}
+ <Skeleton card={false} count={1} height="0.9rem" list />
+ {:then status}
+ {#if status}
+ {#if status.status === 503}
+ <a href="https://due.moe">due.moe</a>'s manga data source is currently down for maintenance.
+ Please check back later.
+ {:else if status.status !== 200}
+ <a href="https://due.moe">due.moe</a>'s manga data source is currently unavailable. Please
+ check back later.
+ {:else}
+ <Error />
+ {/if}
+ {:else}
+ <Skeleton card={false} count={1} height="0.9rem" list />
+ {/if}
+ {:catch}
+ <a href="https://due.moe">due.moe</a>'s manga data source is currently unreachable. Please check
+ back later.
+ {/await}
{/if}
{#if media.length === 0 && !rateLimited}
- {#if rateLimited}
- <p />
- {/if}
+ {#if rateLimited}
+ <p />
+ {/if}
- <p>
- No manga to display. <button on:click={cleanCache} data-umami-event="Force Refresh No Manga"
- >Force refresh</button
- >
- </p>
+ <p>
+ No manga to display. <button on:click={cleanCache} data-umami-event="Force Refresh No Manga"
+ >Force refresh</button
+ >
+ </p>
- <span>
- Don't read manga? <button
- on:click={() => ($settings.disableManga = true)}
- data-umami-event="Disable No Manga">Hide the manga panel</button
- >
- You can re-enable it later in the <a href={root('/settings')}>Settings</a>.
- </span>
+ <span>
+ Don't read manga? <button
+ on:click={() => ($settings.disableManga = true)}
+ data-umami-event="Disable No Manga">Hide the manga panel</button
+ >
+ You can re-enable it later in the <a href={root('/settings')}>Settings</a>.
+ </span>
{/if}
{#if $settings.displayCoverModeManga || dummy}
- <CleanGrid {media} {dummy} type="manga">
- <div slot="title" let:title={manga} let:progress>
- {pendingUpdate === manga.id ? progress + 1 : progress}{#if !due}
- <span class="opaque">/{manga.chapters || '?'}</span>
- {/if}
- <button
- class={`button-square button-action ${pendingUpdate === manga.id ? 'opaque' : ''}`}
- style={pendingUpdate === manga.id ? 'pointer-events: none;' : ''}
- on:click={() => increment(manga)}
- >
- +
- </button>
- {#if due || Math.floor(manga.episodes) < manga.chapters}
- [{manga.episodes || '?'}]
- {#await volumeCount(manga) then volumes}
- {@const volumeProgress = manga.mediaListEntry?.progressVolumes}
+ <CleanGrid {media} {dummy} type="manga">
+ <div slot="title" let:title={manga} let:progress>
+ {pendingUpdate === manga.id ? progress + 1 : progress}{#if !due}
+ <span class="opaque">/{manga.chapters || '?'}</span>
+ {/if}
+ <button
+ class={`button-square button-action ${pendingUpdate === manga.id ? 'opaque' : ''}`}
+ style={pendingUpdate === manga.id ? 'pointer-events: none;' : ''}
+ on:click={() => increment(manga)}
+ >
+ +
+ </button>
+ {#if due || Math.floor(manga.episodes) < manga.chapters}
+ [{manga.episodes || '?'}]
+ {#await volumeCount(manga) then volumes}
+ {@const volumeProgress = manga.mediaListEntry?.progressVolumes}
- {#if volumes !== null && (volumeProgress || 0) < volumes}
- <span style="color: lightcoral;">
- Vol. {volumeProgress} &#8594; {volumes}
- </span>
- {/if}
- {/await}
- {/if}
- </div>
- </CleanGrid>
+ {#if volumes !== null && (volumeProgress || 0) < volumes}
+ <span style="color: lightcoral;">
+ Vol. {volumeProgress} &#8594; {volumes}
+ </span>
+ {/if}
+ {/await}
+ {/if}
+ </div>
+ </CleanGrid>
{:else}
- <CleanList {media} type="manga" {lastUpdatedMedia}>
- <span slot="information" let:title={manga} let:progress>
- <span class="opaque">|</span>
- {pendingUpdate === manga.id ? progress + 1 : progress}{#if !due}
- <span class="opaque">/{manga.chapters || '?'}</span>
- {/if}
- <button
- class={`button-square button-action ${pendingUpdate === manga.id ? 'opaque' : ''}`}
- style={pendingUpdate === manga.id ? 'pointer-events: none;' : ''}
- on:click={() => increment(manga)}
- >
- +
- </button>
- {#if due || Math.floor(manga.episodes) < manga.chapters}
- [{manga.episodes || '?'}]
- {#await volumeCount(manga) then volumes}
- {@const volumeProgress = manga.mediaListEntry?.progressVolumes}
+ <CleanList {media} type="manga" {lastUpdatedMedia}>
+ <span slot="information" let:title={manga} let:progress>
+ <span class="opaque">|</span>
+ {pendingUpdate === manga.id ? progress + 1 : progress}{#if !due}
+ <span class="opaque">/{manga.chapters || '?'}</span>
+ {/if}
+ <button
+ class={`button-square button-action ${pendingUpdate === manga.id ? 'opaque' : ''}`}
+ style={pendingUpdate === manga.id ? 'pointer-events: none;' : ''}
+ on:click={() => increment(manga)}
+ >
+ +
+ </button>
+ {#if due || Math.floor(manga.episodes) < manga.chapters}
+ [{manga.episodes || '?'}]
+ {#await volumeCount(manga) then volumes}
+ {@const volumeProgress = manga.mediaListEntry?.progressVolumes}
- {#if volumes !== null && (volumeProgress || 0) < volumes}
- <span style="color: lightcoral;">
- Vol. {volumeProgress} &#8594; {volumes}
- </span>
- {/if}
- {/await}
- {/if}
- </span>
- </CleanList>
+ {#if volumes !== null && (volumeProgress || 0) < volumes}
+ <span style="color: lightcoral;">
+ Vol. {volumeProgress} &#8594; {volumes}
+ </span>
+ {/if}
+ {/await}
+ {/if}
+ </span>
+ </CleanList>
{/if}
diff --git a/src/lib/List/Manga/MangaListTemplate.svelte b/src/lib/List/Manga/MangaListTemplate.svelte
index fe01465f..1303419f 100644
--- a/src/lib/List/Manga/MangaListTemplate.svelte
+++ b/src/lib/List/Manga/MangaListTemplate.svelte
@@ -1,387 +1,387 @@
<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 Error from '$lib/Error/RateLimited.svelte';
- import CleanMangaList from './CleanMangaList.svelte';
- import authorisedJson from '$lib/Data/Static/authorised.json';
- import { incrementMediaProgress } from '$lib/Media/Anime/cache';
- import { getNotificationsContext } from 'svelte-notifications';
- 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';
-
- export let user: AniListAuthorisation = {
- accessToken: '',
- refreshToken: '',
- expiresIn: 0,
- tokenType: ''
- };
- export let displayUnresolved: boolean;
- export let due: boolean;
- export let dummy = $settings.debugDummyLists || false;
-
- const { addNotification } = getNotificationsContext();
- const authorised = authorisedJson.includes($identity.id);
- let mangaLists: Promise<Media[]>;
- let startTime: number;
- let endTime: number;
- let lastUpdatedMedia = -1;
- let previousMangaList: Media[];
- let pendingUpdate: number | null = null;
- let progress = 0;
- let rateLimited = false;
- let forceFlag = false;
- let lastListSize = 5;
-
- const keyCacher = setInterval(() => {
- startTime = performance.now();
- endTime = -1;
- mangaLists = mediaListCollection(user, $identity, Type.Manga, $manga, $lastPruneTimes.manga, {
- addNotification
- });
- }, $settings.cacheMinutes * 1000 * 60);
-
- onMount(async () => {
- if (browser) {
- const lastStoredList = localStorage.getItem(`last${due ? '' : 'Completed'}MangaListLength`);
-
- if (lastStoredList) lastListSize = parseInt(lastStoredList);
- }
-
- startTime = performance.now();
-
- if (dummy) {
- mangaLists = Promise.resolve(
- 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'
- )
- .sort(() => 0.5 - Math.random())
- .map((manga) => {
- manga.status = 'FINISHED';
- manga.episodes = Math.floor(Math.random() * (manga.chapters || 0)) as unknown as null;
- manga.mediaListEntry.progress = Math.floor(Math.random() * (manga.episodes || 0)) + 1;
-
- return manga;
- })
- .slice(0, 7) as unknown as Media[]
- );
- } else {
- mangaLists = mediaListCollection(user, $identity, Type.Manga, $manga, $lastPruneTimes.manga, {
- addNotification
- });
- }
- });
-
- onDestroy(() => 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 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
- });
- });
- };
+ 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 Error from '$lib/Error/RateLimited.svelte';
+ import CleanMangaList from './CleanMangaList.svelte';
+ import authorisedJson from '$lib/Data/Static/authorised.json';
+ import { incrementMediaProgress } from '$lib/Media/Anime/cache';
+ import { getNotificationsContext } from 'svelte-notifications';
+ 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';
+
+ export let user: AniListAuthorisation = {
+ accessToken: '',
+ refreshToken: '',
+ expiresIn: 0,
+ tokenType: ''
+ };
+ export let displayUnresolved: boolean;
+ export let due: boolean;
+ export let dummy = $settings.debugDummyLists || false;
+
+ const { addNotification } = getNotificationsContext();
+ const authorised = authorisedJson.includes($identity.id);
+ let mangaLists: Promise<Media[]>;
+ let startTime: number;
+ let endTime: number;
+ let lastUpdatedMedia = -1;
+ let previousMangaList: Media[];
+ let pendingUpdate: number | null = null;
+ let progress = 0;
+ let rateLimited = false;
+ let forceFlag = false;
+ let lastListSize = 5;
+
+ const keyCacher = setInterval(() => {
+ startTime = performance.now();
+ endTime = -1;
+ mangaLists = mediaListCollection(user, $identity, Type.Manga, $manga, $lastPruneTimes.manga, {
+ addNotification
+ });
+ }, $settings.cacheMinutes * 1000 * 60);
+
+ onMount(async () => {
+ if (browser) {
+ const lastStoredList = localStorage.getItem(`last${due ? '' : 'Completed'}MangaListLength`);
+
+ if (lastStoredList) lastListSize = parseInt(lastStoredList);
+ }
+
+ startTime = performance.now();
+
+ if (dummy) {
+ mangaLists = Promise.resolve(
+ 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'
+ )
+ .sort(() => 0.5 - Math.random())
+ .map((manga) => {
+ manga.status = 'FINISHED';
+ manga.episodes = Math.floor(Math.random() * (manga.chapters || 0)) as unknown as null;
+ manga.mediaListEntry.progress = Math.floor(Math.random() * (manga.episodes || 0)) + 1;
+
+ return manga;
+ })
+ .slice(0, 7) as unknown as Media[]
+ );
+ } else {
+ mangaLists = mediaListCollection(user, $identity, Type.Manga, $manga, $lastPruneTimes.manga, {
+ addNotification
+ });
+ }
+ });
+
+ onDestroy(() => 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 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
+ });
+ });
+ };
</script>
{#await mangaLists}
- {#if previousMangaList}
- <CleanMangaList
- media={previousMangaList}
- {cleanCache}
- {lastUpdatedMedia}
- {updateMedia}
- {endTime}
- {pendingUpdate}
- {due}
- {rateLimited}
- {authorised}
- {dummy}
- />
- {:else}
- {#if !authorised}
- <ListTitle
- count={0}
- time={endTime / 1000}
- title={$locale().lists.due.mangaAndLightNovels}
- hideTime={dummy}
- hideCount={dummy}
- >
- {#if !dummy}
- <button
- data-umami-event="Force Refresh Manga"
- title="Force a full refresh"
- on:click={() => {
- cleanCache();
-
- forceFlag = true;
- }}>Refresh</button
- >
- {/if}
- </ListTitle>
- {:else}
- <ListTitle
- {progress}
- title={$locale().lists.due.mangaAndLightNovels}
- hideTime={dummy}
- hideCount={dummy}
- />
- {/if}
-
- <Skeleton card={false} count={lastListSize} height="0.9rem" list />
- {/if}
+ {#if previousMangaList}
+ <CleanMangaList
+ media={previousMangaList}
+ {cleanCache}
+ {lastUpdatedMedia}
+ {updateMedia}
+ {endTime}
+ {pendingUpdate}
+ {due}
+ {rateLimited}
+ {authorised}
+ {dummy}
+ />
+ {:else}
+ {#if !authorised}
+ <ListTitle
+ count={0}
+ time={endTime / 1000}
+ title={$locale().lists.due.mangaAndLightNovels}
+ hideTime={dummy}
+ hideCount={dummy}
+ >
+ {#if !dummy}
+ <button
+ data-umami-event="Force Refresh Manga"
+ title="Force a full refresh"
+ on:click={() => {
+ cleanCache();
+
+ forceFlag = true;
+ }}>Refresh</button
+ >
+ {/if}
+ </ListTitle>
+ {:else}
+ <ListTitle
+ {progress}
+ title={$locale().lists.due.mangaAndLightNovels}
+ hideTime={dummy}
+ hideCount={dummy}
+ />
+ {/if}
+
+ <Skeleton card={false} count={lastListSize} height="0.9rem" list />
+ {/if}
{:then media}
- {#await cleanMedia(media, displayUnresolved, forceFlag)}
- {#if previousMangaList}
- <CleanMangaList
- media={previousMangaList}
- {cleanCache}
- {lastUpdatedMedia}
- {updateMedia}
- {endTime}
- {pendingUpdate}
- {due}
- {rateLimited}
- {authorised}
- {dummy}
- />
- {:else}
- {#if !authorised}
- <ListTitle
- count={0}
- time={endTime / 1000}
- title={$locale().lists.due.mangaAndLightNovels}
- hideTime={dummy}
- hideCount={dummy}
- >
- {#if !dummy}
- <button
- data-umami-event="Force Refresh Manga"
- title="Force a full refresh"
- on:click={() => {
- cleanCache();
-
- forceFlag = true;
- }}>Refresh</button
- >
- {/if}
- </ListTitle>
- {:else}
- <ListTitle
- {progress}
- title={$locale().lists.due.mangaAndLightNovels}
- hideTime={dummy}
- hideCount={dummy}
- />
- {/if}
-
- <Skeleton card={false} count={lastListSize} height="0.9rem" list />
- {/if}
- {:then cleanedMedia}
- {#if !authorised}
- <ListTitle
- count={cleanedMedia.length}
- time={endTime / 1000}
- title={$locale().lists.due.mangaAndLightNovels}
- hideTime={dummy}
- hideCount={dummy}
- >
- {#if !dummy}
- <button
- data-umami-event="Force Refresh Manga"
- title="Force a full refresh"
- on:click={() => {
- cleanCache();
-
- forceFlag = true;
- }}>Refresh</button
- >
- {/if}
- </ListTitle>
- {/if}
-
- <CleanMangaList
- media={cleanedMedia}
- {cleanCache}
- {lastUpdatedMedia}
- {updateMedia}
- {endTime}
- {pendingUpdate}
- {due}
- {rateLimited}
- {authorised}
- {dummy}
- />
- {:catch}
- {#if authorised}
- <ListTitle
- count={-1337}
- time={0}
- title={$locale().lists.due.mangaAndLightNovels}
- hideTime={dummy}
- hideCount={dummy}
- />
- {/if}
-
- <Error list={false} />
- {/await}
+ {#await cleanMedia(media, displayUnresolved, forceFlag)}
+ {#if previousMangaList}
+ <CleanMangaList
+ media={previousMangaList}
+ {cleanCache}
+ {lastUpdatedMedia}
+ {updateMedia}
+ {endTime}
+ {pendingUpdate}
+ {due}
+ {rateLimited}
+ {authorised}
+ {dummy}
+ />
+ {:else}
+ {#if !authorised}
+ <ListTitle
+ count={0}
+ time={endTime / 1000}
+ title={$locale().lists.due.mangaAndLightNovels}
+ hideTime={dummy}
+ hideCount={dummy}
+ >
+ {#if !dummy}
+ <button
+ data-umami-event="Force Refresh Manga"
+ title="Force a full refresh"
+ on:click={() => {
+ cleanCache();
+
+ forceFlag = true;
+ }}>Refresh</button
+ >
+ {/if}
+ </ListTitle>
+ {:else}
+ <ListTitle
+ {progress}
+ title={$locale().lists.due.mangaAndLightNovels}
+ hideTime={dummy}
+ hideCount={dummy}
+ />
+ {/if}
+
+ <Skeleton card={false} count={lastListSize} height="0.9rem" list />
+ {/if}
+ {:then cleanedMedia}
+ {#if !authorised}
+ <ListTitle
+ count={cleanedMedia.length}
+ time={endTime / 1000}
+ title={$locale().lists.due.mangaAndLightNovels}
+ hideTime={dummy}
+ hideCount={dummy}
+ >
+ {#if !dummy}
+ <button
+ data-umami-event="Force Refresh Manga"
+ title="Force a full refresh"
+ on:click={() => {
+ cleanCache();
+
+ forceFlag = true;
+ }}>Refresh</button
+ >
+ {/if}
+ </ListTitle>
+ {/if}
+
+ <CleanMangaList
+ media={cleanedMedia}
+ {cleanCache}
+ {lastUpdatedMedia}
+ {updateMedia}
+ {endTime}
+ {pendingUpdate}
+ {due}
+ {rateLimited}
+ {authorised}
+ {dummy}
+ />
+ {:catch}
+ {#if authorised}
+ <ListTitle
+ count={-1337}
+ time={0}
+ title={$locale().lists.due.mangaAndLightNovels}
+ hideTime={dummy}
+ hideCount={dummy}
+ />
+ {/if}
+
+ <Error list={false} />
+ {/await}
{/await}
diff --git a/src/lib/List/MediaTitleDisplay.svelte b/src/lib/List/MediaTitleDisplay.svelte
index 51c6cd13..6a886704 100644
--- a/src/lib/List/MediaTitleDisplay.svelte
+++ b/src/lib/List/MediaTitleDisplay.svelte
@@ -1,77 +1,77 @@
<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;
- export let abbreviateTo = 20;
- export let tooltip = false;
+ export let title: MediaTitle;
+ export let abbreviate = false;
+ export let abbreviateTo = 20;
+ export let tooltip = false;
- const compressToBase64 = (string: string) => LZString.compressToBase64(string);
+ const compressToBase64 = (string: string) => LZString.compressToBase64(string);
</script>
<span id={`title-display-${compressToBase64(title.native)}`}>
- {#if $settings.displayTitleFormat === 'native'}
- {#if $settings.displayFurigana}
- {@const kana = abbreviate
- ? abbreviated(wanakana.toKana(title.native), abbreviateTo)
- : wanakana.toKana(title.native)}
- {@const native = abbreviate ? abbreviated(title.native, abbreviateTo) : title.native}
+ {#if $settings.displayTitleFormat === 'native'}
+ {#if $settings.displayFurigana}
+ {@const kana = abbreviate
+ ? abbreviated(wanakana.toKana(title.native), abbreviateTo)
+ : wanakana.toKana(title.native)}
+ {@const native = abbreviate ? abbreviated(title.native, abbreviateTo) : title.native}
- <LinkedTooltip
- content={title.english || title.romaji || title.native}
- disable={tooltip}
- pin={`title-display-${compressToBase64(title.native)}`}
- relative
- ignoreAnchorStyling
- >
- {#if kana === native}
- {native}
- {:else}
- <ruby>
- {native}
- <rt>
- {kana}
- </rt>
- </ruby>
- {/if}
- </LinkedTooltip>
- {:else}
- <LinkedTooltip
- content={title.english || title.romaji || title.native}
- disable={tooltip}
- pin={`title-display-${compressToBase64(title.native)}`}
- relative
- ignoreAnchorStyling
- >
- {abbreviate ? abbreviated(title.native, abbreviateTo) : title.native}
- </LinkedTooltip>
- {/if}
- {:else if $settings.displayTitleFormat === 'romaji'}
- <LinkedTooltip
- content={title.english || title.romaji || title.native}
- disable={tooltip}
- pin={`title-display-${compressToBase64(title.native)}`}
- relative
- ignoreAnchorStyling
- >
- {abbreviate ? abbreviated(title.romaji, abbreviateTo) : title.romaji}
- </LinkedTooltip>
- {:else}
- <LinkedTooltip
- content={title.romaji || title.native}
- disable={tooltip}
- pin={`title-display-${compressToBase64(title.native)}`}
- relative
- ignoreAnchorStyling
- >
- {abbreviate
- ? abbreviated(title.english || title.romaji || title.native, abbreviateTo)
- : title.english || title.romaji || title.native}
- </LinkedTooltip>
- {/if}
+ <LinkedTooltip
+ content={title.english || title.romaji || title.native}
+ disable={tooltip}
+ pin={`title-display-${compressToBase64(title.native)}`}
+ relative
+ ignoreAnchorStyling
+ >
+ {#if kana === native}
+ {native}
+ {:else}
+ <ruby>
+ {native}
+ <rt>
+ {kana}
+ </rt>
+ </ruby>
+ {/if}
+ </LinkedTooltip>
+ {:else}
+ <LinkedTooltip
+ content={title.english || title.romaji || title.native}
+ disable={tooltip}
+ pin={`title-display-${compressToBase64(title.native)}`}
+ relative
+ ignoreAnchorStyling
+ >
+ {abbreviate ? abbreviated(title.native, abbreviateTo) : title.native}
+ </LinkedTooltip>
+ {/if}
+ {:else if $settings.displayTitleFormat === 'romaji'}
+ <LinkedTooltip
+ content={title.english || title.romaji || title.native}
+ disable={tooltip}
+ pin={`title-display-${compressToBase64(title.native)}`}
+ relative
+ ignoreAnchorStyling
+ >
+ {abbreviate ? abbreviated(title.romaji, abbreviateTo) : title.romaji}
+ </LinkedTooltip>
+ {:else}
+ <LinkedTooltip
+ content={title.romaji || title.native}
+ disable={tooltip}
+ pin={`title-display-${compressToBase64(title.native)}`}
+ relative
+ ignoreAnchorStyling
+ >
+ {abbreviate
+ ? abbreviated(title.english || title.romaji || title.native, abbreviateTo)
+ : title.english || title.romaji || title.native}
+ </LinkedTooltip>
+ {/if}
</span>
diff --git a/src/lib/List/covers.css b/src/lib/List/covers.css
index 7038ffbf..e0b69c78 100644
--- a/src/lib/List/covers.css
+++ b/src/lib/List/covers.css
@@ -1,68 +1,68 @@
.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;
+ background-size: cover;
+ background-position: center;
+ border-radius: 8px;
}
.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 {
- /* overflow: hidden; */
- display: flex;
- justify-content: center;
- align-items: center;
- border-radius: 8px;
+ /* overflow: hidden; */
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ border-radius: 8px;
}
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 9186b5d1..e59a3091 100644
--- a/src/lib/List/mediaTitle.ts
+++ b/src/lib/List/mediaTitle.ts
@@ -3,14 +3,14 @@ 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;
};