aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorFuwn <[email protected]>2023-09-05 23:55:06 -0700
committerFuwn <[email protected]>2023-09-05 23:55:06 -0700
commit247d33f6df4e61d807c7543036a2518fc90fca77 (patch)
tree2a944f5afe89067ad5b7c42b2e9faee79bff58f3 /src
parentfeat(anime): optionally link to livechart.me (diff)
downloaddue.moe-247d33f6df4e61d807c7543036a2518fc90fca77.tar.xz
due.moe-247d33f6df4e61d807c7543036a2518fc90fca77.zip
feat: show completed anime section
Diffstat (limited to 'src')
-rw-r--r--src/lib/List/WatchingAnimeList.svelte132
-rw-r--r--src/routes/+page.svelte42
-rw-r--r--src/routes/settings/+page.svelte10
-rw-r--r--src/stores/settings.ts4
4 files changed, 159 insertions, 29 deletions
diff --git a/src/lib/List/WatchingAnimeList.svelte b/src/lib/List/WatchingAnimeList.svelte
new file mode 100644
index 00000000..19ef81ce
--- /dev/null
+++ b/src/lib/List/WatchingAnimeList.svelte
@@ -0,0 +1,132 @@
+<script lang="ts">
+ /* eslint svelte/no-at-html-tags: "off" */
+
+ import { mediaListCollection, Type, flattenLists, type Media } from '$lib/AniList/media';
+ import type { UserIdentity, AniListAuthorisation } from '$lib/AniList/identity';
+ import { onMount } from 'svelte';
+ import anime from '../../stores/anime';
+ import lastPruneTimes from '../../stores/lastPruneTimes';
+ import settings from '../../stores/settings';
+
+ export let user: AniListAuthorisation;
+ export let identity: UserIdentity;
+
+ let animeLists: Promise<{ entries: { media: Media }[] }[]>;
+ let startTime: number;
+ let endTime: number;
+
+ onMount(async () => {
+ startTime = performance.now();
+ animeLists = mediaListCollection(user, identity, Type.Anime, $anime, $lastPruneTimes.anime);
+ });
+
+ const cleanMedia = (media: { entries: { media: Media }[] }[]) => {
+ if (media === undefined) {
+ return [];
+ }
+
+ const flattenedLists = flattenLists(media);
+ const releasingMedia = flattenedLists.filter(
+ (media: Media) =>
+ media.status !== 'RELEASING' &&
+ media.status !== 'NOT_YET_RELEASED' &&
+ (media.mediaListEntry || { progress: 0 }).progress >=
+ ($settings.displayNotStarted === true ? 0 : 1)
+ );
+ let finalMedia = releasingMedia;
+
+ finalMedia.sort((a: Media, b: Media) => {
+ const difference = (anime: Media) => {
+ return (
+ (anime.nextAiringEpisode?.episode === -1
+ ? 99999
+ : anime.nextAiringEpisode?.episode || -1) -
+ (anime.mediaListEntry || { progress: 0 }).progress
+ );
+ };
+
+ return difference(a) - difference(b);
+ });
+
+ finalMedia = finalMedia.filter((item, index, array) => {
+ return (
+ array.findIndex((i) => {
+ return i.id === item.id;
+ }) === index
+ );
+ });
+
+ if (!endTime) {
+ endTime = performance.now() - startTime;
+ }
+
+ return finalMedia;
+ };
+
+ const totalEpisodes = (anime: Media) => {
+ return anime.episodes === null ? '' : `<span style="opacity: 50%">/${anime.episodes}</span>`;
+ };
+
+ const updateMedia = async (id: number, progress: number | undefined) => {
+ fetch(`/anilist/increment?id=${id}&progress=${(progress || 0) + 1}`).then(() => {
+ animeLists = mediaListCollection(
+ user,
+ identity,
+ Type.Anime,
+ $anime,
+ $lastPruneTimes.anime,
+ true
+ );
+ });
+ };
+</script>
+
+{#await animeLists}
+ <summary>Completed Anime [...] <small style="opacity: 50%">...s</small></summary>
+
+ <ul><li>Loading ...</li></ul>
+{:then media}
+ {@const cleanedMedia = cleanMedia(media)}
+
+ <summary
+ >Completed Anime [{cleanedMedia.length}]
+ <small style="opacity: 50%">{endTime / 1000}s</small></summary
+ >
+
+ <ul>
+ {#each cleanedMedia as anime}
+ <li>
+ <a
+ href={$settings.linkToAniList
+ ? `https://anilist.co/anime/${anime.id}`
+ : `https://www.livechart.me/search?q=${
+ anime.title.native || anime.title.english || anime.title.romaji
+ }`}
+ target="_blank"
+ >
+ {anime.title.english || anime.title.romaji || anime.title.native}
+ </a>
+ <span style="opacity: 50%;">|</span>
+ {anime.mediaListEntry?.progress || 0}{@html totalEpisodes(anime)}
+ <a href={'#'} on:click={() => updateMedia(anime.id, anime.mediaListEntry?.progress)}>+</a>
+ </li>
+ {/each}
+ </ul>
+{:catch}
+ <summary>Upcoming Episodes [?] <small style="opacity: 50%">0s</small></summary>
+
+ <ul>
+ <li>
+ <p>
+ Media could not be loaded. You might have been <a
+ href="https://en.wikipedia.org/wiki/Rate_limiting"
+ target="_blank">rate limited</a
+ >.
+ </p>
+ <p>
+ Try again in a few minutes. If the problem persists, please contact
+ <a href="https://anilist.co/user/fuwn" target="_blank">@fuwn</a> on AniList.
+ </p>
+ </li>
+ </ul>
+{/await}
diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte
index e981ea3f..fdae3fcb 100644
--- a/src/routes/+page.svelte
+++ b/src/routes/+page.svelte
@@ -6,6 +6,7 @@
import UpcomingAnimeList from '$lib/List/UpcomingAnimeList.svelte';
import userIdentity from '../stores/userIdentity';
import settings from '../stores/settings';
+ import WatchingAnimeList from '$lib/List/WatchingAnimeList.svelte';
export let data;
@@ -31,8 +32,6 @@
{#if data.user === undefined}
Please log in to view due media.
{:else}
- <p />
-
<details open={!$settings.closeAnimeByDefault} class="list">
{#if currentUserIdentity.id != -1}
<UpcomingAnimeList
@@ -46,8 +45,6 @@
{/if}
</details>
- <p />
-
<details open={!$settings.closeAnimeByDefault} class="list">
{#if currentUserIdentity.id != -1}
<AnimeList
@@ -61,8 +58,6 @@
{/if}
</details>
- <p />
-
<details open={!$settings.closeMangaByDefault} class="list">
{#if currentUserIdentity.id != -1}
<MangaList
@@ -75,32 +70,23 @@
<ul><li>Loading ...</li></ul>
{/if}
</details>
+
+ {#if $settings.showCompletedAnime}
+ <details open={!$settings.closeAnimeByDefault} class="list">
+ {#if currentUserIdentity.id != -1}
+ <WatchingAnimeList user={data.user} identity={currentUserIdentity} />
+ {:else}
+ <summary>Completed Anime [...] <small style="opacity: 50%">...s</small></summary>
+ <ul><li>Loading ...</li></ul>
+ {/if}
+ </details>
+ {/if}
{/if}
</div>
<style>
#list-container {
- display: flex;
- flex-wrap: wrap;
- }
-
- /* (1440 / 2) + (1440/8) = 720 + 180 = 900 */
- @media (max-width: 900px) {
- .list {
- flex: 0 0 100%;
- }
- }
-
- /* (1920 / 2) + (1920 / 4) = 960 + 480 = 1440 */
- @media (min-width: 901px) and (max-width: 1440px) {
- .list {
- flex: 0 0 50%;
- }
- }
-
- @media (min-width: 1441px) {
- .list {
- flex: 0 0 33.33%;
- }
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
}
</style>
diff --git a/src/routes/settings/+page.svelte b/src/routes/settings/+page.svelte
index 54add148..5889caf9 100644
--- a/src/routes/settings/+page.svelte
+++ b/src/routes/settings/+page.svelte
@@ -54,6 +54,16 @@
>
<br />
+ <a
+ href={'#'}
+ on:click={() =>
+ $settings.showCompletedAnime
+ ? settings.setKey('showCompletedAnime', false)
+ : settings.setKey('showCompletedAnime', true)}
+ >{$settings.showCompletedAnime ? 'Hide' : 'Show'} completed anime</a
+ >
+ <br />
+
<p />
<a
diff --git a/src/stores/settings.ts b/src/stores/settings.ts
index 01580ec3..2edc31cb 100644
--- a/src/stores/settings.ts
+++ b/src/stores/settings.ts
@@ -12,6 +12,7 @@ interface Settings {
sortByDifference: boolean;
forceLightTheme: boolean;
linkToAniList: boolean;
+ showCompletedAnime: boolean;
}
const defaultSettings: Settings = {
@@ -24,7 +25,8 @@ const defaultSettings: Settings = {
roundDownChapters: true,
sortByDifference: false,
forceLightTheme: false,
- linkToAniList: true
+ linkToAniList: true,
+ showCompletedAnime: true
};
const createStore = () => {