From 3c85251b260d7abbb1b3f48f2621be9b4e45124c Mon Sep 17 00:00:00 2001
From: Fuwn <contact@fuwn.me>
Date: Sun, 7 Jul 2024 21:55:32 -0700
Subject: feat(tools): sequel catcher

---
 src/lib/Tools/SequelCatcher.svelte | 112 +++++++++++++++++++++++++++++++++++++
 1 file changed, 112 insertions(+)
 create mode 100644 src/lib/Tools/SequelCatcher.svelte

(limited to 'src/lib/Tools/SequelCatcher.svelte')

diff --git a/src/lib/Tools/SequelCatcher.svelte b/src/lib/Tools/SequelCatcher.svelte
new file mode 100644
index 00000000..df6c0d3a
--- /dev/null
+++ b/src/lib/Tools/SequelCatcher.svelte
@@ -0,0 +1,112 @@
+<script lang="ts">
+	import { type AniListAuthorisation } from '$lib/Data/AniList/identity';
+	import userIdentity from '$stores/identity';
+	import { filterRelations, type Media, mediaListCollection, Type } from '$lib/Data/AniList/media';
+	import LogInRestricted from '$lib/Error/LogInRestricted.svelte';
+	import LinkedTooltip from '$lib/Tooltip/LinkedTooltip.svelte';
+	import anime from '$stores/anime';
+
+	import identity from '$stores/identity';
+	import { onMount } from 'svelte';
+	import lastPruneTimes from '$stores/lastPruneTimes';
+	import MediaTitleDisplay from '$lib/List/MediaTitleDisplay.svelte';
+	import { outboundLink } from '$lib/Media/links';
+	import settings from '$stores/settings';
+	import Message from '$lib/Loading/Message.svelte';
+	import Skeleton from '$lib/Loading/Skeleton.svelte';
+
+	export let user: AniListAuthorisation;
+
+	let mediaList: Promise<Media[]>;
+
+	onMount(async () => {
+		if (user === undefined || $identity.id === -2) return;
+
+		mediaList = mediaListCollection(
+			user,
+			$userIdentity,
+			Type.Anime,
+			$anime,
+			$lastPruneTimes.anime,
+			{
+				forcePrune: true,
+				includeCompleted: true,
+				all: true,
+				includeRelations: true
+			}
+		);
+	});
+</script>
+
+{#if user === undefined || $identity.id === -2}
+	<LogInRestricted />
+{:else}
+	<div class="card">
+		{#await mediaList}
+			<Message message="Cross-checking media ..." />
+
+			<Skeleton
+				card={false}
+				count={8}
+				pad={false}
+				height={'0.9rem'}
+				width={'100%'}
+				list
+				grid={false}
+			/>
+		{:then mediaListUnchecked}
+			{#if mediaListUnchecked}
+				{@const mediaList = mediaListUnchecked.filter(
+					(media) => media.mediaListEntry?.status === 'COMPLETED'
+				)}
+
+				<ul>
+					{#each filterRelations(mediaList) as { media, unwatchedRelations }}
+						<a href={outboundLink(media, 'anime', $settings.displayOutboundLinksTo)}>
+							<MediaTitleDisplay title={media.title} />
+						</a>
+
+						<ul>
+							{#each unwatchedRelations as relation}
+								<li>
+									<a href={outboundLink(relation.node, 'anime', $settings.displayOutboundLinksTo)}>
+										<MediaTitleDisplay title={relation.node.title} />
+									</a>
+								</li>
+							{/each}
+						</ul>
+					{/each}
+				</ul>
+			{:else}
+				<Message message="Cross-checking media ..." />
+
+				<Skeleton
+					card={false}
+					count={8}
+					pad={false}
+					height={'0.9rem'}
+					width={'100%'}
+					list
+					grid={false}
+				/>
+			{/if}
+		{:catch}
+			<Message message="" loader="ripple" slot withReload fullscreen>Error fetching media.</Message>
+		{/await}
+
+		<p />
+
+		<blockquote style="margin: 0 0 0 1.5rem;">
+			Thanks to <a href="https://anilist.co/user/sevengirl/">@sevengirl</a> and
+			<a href="https://anilist.co/user/esthereae/">@esthereae</a> for the idea!
+		</blockquote>
+	</div>
+{/if}
+
+<style>
+	.hint-toggle {
+		box-shadow: rgba(0, 0, 11, 0.2) 0px 7px 29px 0px, 0 0 0 5px var(--base02);
+		float: right;
+		border-radius: 8px;
+	}
+</style>
-- 
cgit v1.2.3

