aboutsummaryrefslogtreecommitdiff
path: root/src/lib/Tools
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/Tools')
-rw-r--r--src/lib/Tools/ActivityHistory/Grid.svelte20
-rw-r--r--src/lib/Tools/ActivityHistory/Tool.svelte20
-rw-r--r--src/lib/Tools/Birthdays.svelte14
-rw-r--r--src/lib/Tools/DumpProfile.svelte6
-rw-r--r--src/lib/Tools/EpisodeDiscussionCollector.svelte4
-rw-r--r--src/lib/Tools/FollowFix.svelte16
-rw-r--r--src/lib/Tools/Hayai.svelte6
-rw-r--r--src/lib/Tools/HololiveBirthdays.svelte14
-rw-r--r--src/lib/Tools/InputTemplate.svelte50
-rw-r--r--src/lib/Tools/Likes.svelte12
-rw-r--r--src/lib/Tools/Picker.svelte8
-rw-r--r--src/lib/Tools/RandomFollower.svelte6
-rw-r--r--src/lib/Tools/SequelCatcher/List.svelte12
-rw-r--r--src/lib/Tools/SequelCatcher/Tool.svelte10
-rw-r--r--src/lib/Tools/SequelSpy/Prequels.svelte6
-rw-r--r--src/lib/Tools/SequelSpy/Tool.svelte24
-rw-r--r--src/lib/Tools/Tracker/Tool.svelte24
-rw-r--r--src/lib/Tools/UmaMusumeBirthdays.svelte12
-rw-r--r--src/lib/Tools/Wrapped/ActivityHistory.svelte17
-rw-r--r--src/lib/Tools/Wrapped/Media.svelte33
-rw-r--r--src/lib/Tools/Wrapped/MediaExtras.svelte21
-rw-r--r--src/lib/Tools/Wrapped/Tool.svelte312
-rw-r--r--src/lib/Tools/Wrapped/Top/Activity.svelte22
-rw-r--r--src/lib/Tools/Wrapped/Top/Anime.svelte10
-rw-r--r--src/lib/Tools/Wrapped/Top/Manga.svelte8
25 files changed, 399 insertions, 288 deletions
diff --git a/src/lib/Tools/ActivityHistory/Grid.svelte b/src/lib/Tools/ActivityHistory/Grid.svelte
index db9f3839..8789a786 100644
--- a/src/lib/Tools/ActivityHistory/Grid.svelte
+++ b/src/lib/Tools/ActivityHistory/Grid.svelte
@@ -12,12 +12,16 @@
import tooltip from '$lib/Tooltip/tooltip';
import LogInRestricted from '$lib/Error/LogInRestricted.svelte';
- export let user: AniListAuthorisation;
- export let activityData: ActivityHistoryEntry[] | null = null;
- export let currentYear = new Date().getFullYear();
+ interface Props {
+ user: AniListAuthorisation;
+ activityData?: ActivityHistoryEntry[] | null;
+ currentYear?: any;
+ }
+
+ let { user, activityData = null, currentYear = new Date().getFullYear() }: Props = $props();
- let activityHistoryData: ActivityHistoryEntry[];
- let baseHue = Math.floor(Math.random() * 360);
+ let activityHistoryData: ActivityHistoryEntry[] = $state();
+ let baseHue = $state(Math.floor(Math.random() * 360));
onMount(async () => {
clearAllParameters();
@@ -45,8 +49,8 @@
<div
class="grid-item"
style="background-color: {gradientColour(activity.amount, highestActivity, baseHue)}"
- on:click={() => (baseHue = Math.floor(Math.random() * 360))}
- on:keydown={() => {
+ onclick={() => (baseHue = Math.floor(Math.random() * 360))}
+ onkeydown={() => {
return;
}}
role="button"
@@ -55,7 +59,7 @@
title={`Date: ${new Date(activity.date * 1000).toLocaleDateString()}\nAmount: ${
activity.amount
}`}
- />
+></div>
{/each}
</div>
{/if}
diff --git a/src/lib/Tools/ActivityHistory/Tool.svelte b/src/lib/Tools/ActivityHistory/Tool.svelte
index b6e66a5e..07b2096a 100644
--- a/src/lib/Tools/ActivityHistory/Tool.svelte
+++ b/src/lib/Tools/ActivityHistory/Tool.svelte
@@ -14,10 +14,14 @@
import Skeleton from '$lib/Loading/Skeleton.svelte';
import LogInRestricted from '$lib/Error/LogInRestricted.svelte';
- export let user: AniListAuthorisation;
+ interface Props {
+ user: AniListAuthorisation;
+ }
- let activityHistoryData: Promise<ActivityHistoryEntry[]>;
- let generated = false;
+ let { user }: Props = $props();
+
+ let activityHistoryData: Promise<ActivityHistoryEntry[]> = $state();
+ let generated = $state(false);
onMount(async () => {
clearAllParameters();
@@ -79,18 +83,18 @@
<div class="card">
<ActivityHistoryGrid {user} />
- <p />
+ <p></p>
- <div id="grid-final" />
+ <div id="grid-final"></div>
{#if generated}
- <p />
+ <p></p>
{/if}
- <button on:click={screenshot}>Generate grid image</button>
+ <button onclick={screenshot}>Generate grid image</button>
</div>
- <p />
+ <p></p>
<details open>
<summary>Days in risk of developing an activity history hole</summary>
diff --git a/src/lib/Tools/Birthdays.svelte b/src/lib/Tools/Birthdays.svelte
index 97ff40d8..17c91709 100644
--- a/src/lib/Tools/Birthdays.svelte
+++ b/src/lib/Tools/Birthdays.svelte
@@ -1,4 +1,6 @@
<script lang="ts">
+ import { run } from 'svelte/legacy';
+
import { browser } from '$app/environment';
import { page } from '$app/stores';
import { ACDBBirthdays, type ACDBBirthday } from '$lib/Data/Birthday/secondary';
@@ -18,12 +20,12 @@
const urlParameters = browser ? new URLSearchParams(window.location.search) : null;
let date = new Date();
- let month = parseOrDefault(urlParameters, 'month', date.getMonth() + 1);
- let day = parseOrDefault(urlParameters, 'day', date.getDate());
- let anisearchBirthdays: Promise<aniSearchBirthday[]>;
- let acdbBirthdays: Promise<ACDBBirthday[]>;
+ let month = $state(parseOrDefault(urlParameters, 'month', date.getMonth() + 1));
+ let day = $state(parseOrDefault(urlParameters, 'day', date.getDate()));
+ let anisearchBirthdays: Promise<aniSearchBirthday[]> = $state();
+ let acdbBirthdays: Promise<ACDBBirthday[]> = $state();
- $: {
+ run(() => {
month = Math.min(month, 12);
month = Math.max(month, 1);
day = Math.min(day, new Date(new Date().getFullYear(), month, 0).getDate());
@@ -39,7 +41,7 @@
clearAllParameters(['month', 'day']);
history.replaceState(null, '', `?${$page.url.searchParams.toString()}`);
}
- }
+ });
onMount(() => clearAllParameters(['month', 'day']));
diff --git a/src/lib/Tools/DumpProfile.svelte b/src/lib/Tools/DumpProfile.svelte
index 45d4ffc9..ac0184ae 100644
--- a/src/lib/Tools/DumpProfile.svelte
+++ b/src/lib/Tools/DumpProfile.svelte
@@ -5,7 +5,7 @@
import InputTemplate from './InputTemplate.svelte';
import LZString from 'lz-string';
- let submission = '';
+ let submission = $state('');
// Credit: @hoh
const decodeJSON = (about: string): JSON | null => {
@@ -26,7 +26,7 @@
};
</script>
-<!-- svelte-ignore missing-declaration -->
+<!-- svelte-ignore missing_declaration -->
<InputTemplate field="Username" bind:submission event="Dump User" submitText="Dump">
{#await dumpUser(submission)}
<Skeleton card={false} count={1} height="500px" />
@@ -36,7 +36,7 @@
<pre>{JSON.stringify(dump, null, 2)}</pre>
{#if decoded && (dump.about || '').includes('[](json')}
- <p />
+ <p></p>
<pre>{JSON.stringify(decoded, null, 2).replaceAll(/\\n/g, '\n')}</pre>
{/if}
diff --git a/src/lib/Tools/EpisodeDiscussionCollector.svelte b/src/lib/Tools/EpisodeDiscussionCollector.svelte
index 4c61f3cf..746dff22 100644
--- a/src/lib/Tools/EpisodeDiscussionCollector.svelte
+++ b/src/lib/Tools/EpisodeDiscussionCollector.svelte
@@ -6,7 +6,7 @@
import InputTemplate from './InputTemplate.svelte';
import tooltip from '$lib/Tooltip/tooltip';
- let submission = '';
+ let submission = $state('');
onMount(clearAllParameters);
</script>
@@ -54,7 +54,7 @@
</p>
{/await}
{:else}
- <p />
+ <p></p>
Enter a username to search for to continue.
{/if}
diff --git a/src/lib/Tools/FollowFix.svelte b/src/lib/Tools/FollowFix.svelte
index b9fddeff..411b1a92 100644
--- a/src/lib/Tools/FollowFix.svelte
+++ b/src/lib/Tools/FollowFix.svelte
@@ -3,23 +3,27 @@
import type { AniListAuthorisation } from '$lib/Data/AniList/identity';
import LogInRestricted from '$lib/Error/LogInRestricted.svelte';
- export let user: AniListAuthorisation;
+ interface Props {
+ user: AniListAuthorisation;
+ }
- let input = '';
- let submit = '';
+ let { user }: Props = $props();
+
+ let input = $state('');
+ let submit = $state('');
</script>
{#if user === undefined}
<LogInRestricted />
{:else}
<p>
- <!-- svelte-ignore missing-declaration -->
+ <!-- svelte-ignore missing_declaration -->
<input
type="text"
minlength="1"
placeholder="Username"
bind:value={input}
- on:keypress={(e) => {
+ onkeypress={(e) => {
if (e.key === 'Enter') {
submit = input;
@@ -28,7 +32,7 @@
}
}}
/>
- <a href={'#'} on:click={() => (submit = input)}>
+ <a href={'#'} onclick={() => (submit = input)}>
Toggle follow for {input.length === 0 ? '...' : input}
</a>
</p>
diff --git a/src/lib/Tools/Hayai.svelte b/src/lib/Tools/Hayai.svelte
index 1790af53..127c07b9 100644
--- a/src/lib/Tools/Hayai.svelte
+++ b/src/lib/Tools/Hayai.svelte
@@ -90,13 +90,13 @@
)}
</small>
- <p />
+ <p></p>
{@html applyBionicReadingToString(
`After selecting an EPUB file, 早い will apply a bionic reading filter over any and all words, and return the newly created "bionic" EPUB file.`
)}
- <p />
+ <p></p>
- <input type="file" id="epub-file" accept=".epub" on:change={handleFileUpload} />
+ <input type="file" id="epub-file" accept=".epub" onchange={handleFileUpload} />
</div>
diff --git a/src/lib/Tools/HololiveBirthdays.svelte b/src/lib/Tools/HololiveBirthdays.svelte
index 68a591de..769f5d6f 100644
--- a/src/lib/Tools/HololiveBirthdays.svelte
+++ b/src/lib/Tools/HololiveBirthdays.svelte
@@ -1,4 +1,6 @@
<script lang="ts">
+ import { run } from 'svelte/legacy';
+
import { browser } from '$app/environment';
import { page } from '$app/stores';
import { onMount } from 'svelte';
@@ -9,14 +11,14 @@
const urlParameters = browser ? new URLSearchParams(window.location.search) : null;
let date = new Date();
- let month = parseOrDefault(urlParameters, 'month', date.getMonth() + 1);
- let day = parseOrDefault(urlParameters, 'day', date.getDate());
+ let month = $state(parseOrDefault(urlParameters, 'month', date.getMonth() + 1));
+ let day = $state(parseOrDefault(urlParameters, 'day', date.getDate()));
- $: todaysBirthdays = birthdays.filter(
+ let todaysBirthdays = $derived(birthdays.filter(
(birthday) => birthday.month === month && birthday.day === day
- );
+ ));
- $: {
+ run(() => {
month = Math.min(month, 12);
month = Math.max(month, 1);
day = Math.min(day, new Date(new Date().getFullYear(), month, 0).getDate());
@@ -28,7 +30,7 @@
clearAllParameters(['month', 'day']);
history.replaceState(null, '', `?${$page.url.searchParams.toString()}`);
}
- }
+ });
onMount(() => clearAllParameters(['month', 'day']));
</script>
diff --git a/src/lib/Tools/InputTemplate.svelte b/src/lib/Tools/InputTemplate.svelte
index 72e2f807..52b9fd46 100644
--- a/src/lib/Tools/InputTemplate.svelte
+++ b/src/lib/Tools/InputTemplate.svelte
@@ -3,33 +3,49 @@
import { onMount } from 'svelte';
import SettingHint from '$lib/Settings/SettingHint.svelte';
- export let field: string;
- export let submission: string;
- export let event: string | undefined = undefined;
- export let submitText: string;
- export let saveParameters: string[] = [];
- export let onSubmit = () => {
+ interface Props {
+ field: string;
+ submission: string;
+ event?: string | undefined;
+ submitText: string;
+ saveParameters?: string[];
+ onSubmit?: any;
+ preserveCase?: boolean;
+ prompt?: any;
+ hint?: string | undefined;
+ children?: import('svelte').Snippet;
+ }
+
+ let {
+ field,
+ submission = $bindable(),
+ event = undefined,
+ submitText,
+ saveParameters = [],
+ onSubmit = () => {
return;
- };
- export let preserveCase = false;
- export let prompt = `Enter a ${
+ },
+ preserveCase = false,
+ prompt = `Enter a ${
preserveCase ? field : field.toLowerCase()
- } to search for to continue.`;
- export let hint: string | undefined = undefined;
+ } to search for to continue.`,
+ hint = undefined,
+ children
+ }: Props = $props();
- let input = '';
+ let input = $state('');
onMount(() => clearAllParameters(saveParameters));
</script>
<div class="card">
<p>
- <!-- svelte-ignore missing-declaration -->
+ <!-- svelte-ignore missing_declaration -->
<input
type="text"
placeholder={field}
bind:value={input}
- on:keypress={(e) => {
+ onkeypress={(e) => {
if (e.key === 'Enter') {
submission = input;
@@ -42,7 +58,7 @@
/>
<button
class="button-lined"
- on:click={() => {
+ onclick={() => {
submission = input;
onSubmit();
@@ -62,9 +78,9 @@
</p>
{#if submission !== ''}
- <slot />
+ {@render children?.()}
{:else}
- <p />
+ <p></p>
{prompt}
{/if}
diff --git a/src/lib/Tools/Likes.svelte b/src/lib/Tools/Likes.svelte
index 7b626c94..d86c5e12 100644
--- a/src/lib/Tools/Likes.svelte
+++ b/src/lib/Tools/Likes.svelte
@@ -7,16 +7,16 @@
import settings from '$stores/settings';
import InputTemplate from './InputTemplate.svelte';
- let submission = '';
+ let submission = $state('');
- $: normalisedSubmission = submission.replace(/.*\/(activity|thread)\/(\d+).*/, '$2');
- $: submissionType = submission.replace(/.*\/(activity|thread)\/(\d+).*/, '$1');
- $: likesPromise =
- submissionType === 'activity'
+ let normalisedSubmission = $derived(submission.replace(/.*\/(activity|thread)\/(\d+).*/, '$2'));
+ let submissionType = $derived(submission.replace(/.*\/(activity|thread)\/(\d+).*/, '$1'));
+ let likesPromise =
+ $derived(submissionType === 'activity'
? activityLikes(Number(normalisedSubmission))
: submissionType === 'thread'
? threadLikes(Number(normalisedSubmission))
- : Promise.resolve(null);
+ : Promise.resolve(null));
</script>
<InputTemplate
diff --git a/src/lib/Tools/Picker.svelte b/src/lib/Tools/Picker.svelte
index 3f20300f..8628f757 100644
--- a/src/lib/Tools/Picker.svelte
+++ b/src/lib/Tools/Picker.svelte
@@ -4,13 +4,17 @@
import root from '$lib/Utility/root';
import { tools } from './tools';
- export let tool: string;
+ interface Props {
+ tool: string;
+ }
+
+ let { tool = $bindable() }: Props = $props();
</script>
<blockquote>
<select
bind:value={tool}
- on:change={() => {
+ onchange={() => {
if (browser) goto(root(`/tools/${tool}`));
}}
>
diff --git a/src/lib/Tools/RandomFollower.svelte b/src/lib/Tools/RandomFollower.svelte
index acb5a33a..30b58470 100644
--- a/src/lib/Tools/RandomFollower.svelte
+++ b/src/lib/Tools/RandomFollower.svelte
@@ -5,8 +5,8 @@
import TextSwap from '$lib/Layout/TextTransition.svelte';
import InputTemplate from './InputTemplate.svelte';
- let submission = '';
- let randomSeed = 0;
+ let submission = $state('');
+ let randomSeed = $state(0);
</script>
<InputTemplate
@@ -21,7 +21,7 @@
{:then users}
{@const user = users[Math.floor(randomSeed * users.length)]}
- <p />
+ <p></p>
<a href={`https://anilist.co/user/${user.id}`} target="_blank">
<TextSwap text={user.name} />
diff --git a/src/lib/Tools/SequelCatcher/List.svelte b/src/lib/Tools/SequelCatcher/List.svelte
index 009df219..788e8e01 100644
--- a/src/lib/Tools/SequelCatcher/List.svelte
+++ b/src/lib/Tools/SequelCatcher/List.svelte
@@ -4,10 +4,14 @@
import { outboundLink } from '$lib/Media/links';
import settings from '$stores/settings';
- export let mediaListUnchecked: Media[];
+ interface Props {
+ mediaListUnchecked: Media[];
+ }
+
+ let { mediaListUnchecked }: Props = $props();
- let includeCurrent = false;
- let includeSideStories = false;
+ let includeCurrent = $state(false);
+ let includeSideStories = $state(false);
const matchCheck = (media: Media | undefined, swap = false) =>
(media &&
@@ -30,7 +34,7 @@ paused)
<input type="checkbox" bind:checked={includeSideStories} /> Include side stories (e.g., OVAs,
specials, etc.)
-<p />
+<p></p>
<ol class="media-list">
{#each filterRelations( mediaListUnchecked.filter((media) => media.mediaListEntry?.status === 'COMPLETED'), includeSideStories ) as { media, unwatchedRelations }}
diff --git a/src/lib/Tools/SequelCatcher/Tool.svelte b/src/lib/Tools/SequelCatcher/Tool.svelte
index a954b4d7..547903b7 100644
--- a/src/lib/Tools/SequelCatcher/Tool.svelte
+++ b/src/lib/Tools/SequelCatcher/Tool.svelte
@@ -12,9 +12,13 @@
import Skeleton from '$lib/Loading/Skeleton.svelte';
import Username from '$lib/Layout/Username.svelte';
- export let user: AniListAuthorisation;
+ interface Props {
+ user: AniListAuthorisation;
+ }
- let mediaList: Promise<Media[]>;
+ let { user }: Props = $props();
+
+ let mediaList: Promise<Media[]> = $state();
onMount(async () => {
if (user === undefined || $identity.id === -2) return;
@@ -71,7 +75,7 @@
<Message message="" loader="ripple" slot withReload fullscreen>Error fetching media.</Message>
{/await}
- <p />
+ <p></p>
<blockquote style="margin: 0 0 0 1.5rem;">
Thanks to <Username username="sevengirl" /> and <Username username="esthereae" /> for the idea!
diff --git a/src/lib/Tools/SequelSpy/Prequels.svelte b/src/lib/Tools/SequelSpy/Prequels.svelte
index b22db3af..0b01b646 100644
--- a/src/lib/Tools/SequelSpy/Prequels.svelte
+++ b/src/lib/Tools/SequelSpy/Prequels.svelte
@@ -6,7 +6,11 @@
import settings from '$stores/settings';
import type { Media } from '$lib/Data/AniList/media';
- export let currentPrequels: MediaPrequel[];
+ interface Props {
+ currentPrequels: MediaPrequel[];
+ }
+
+ let { currentPrequels }: Props = $props();
const prequelAiringTime = (prequel: MediaPrequel) =>
airingTime(prequel as unknown as Media, null, false, true);
diff --git a/src/lib/Tools/SequelSpy/Tool.svelte b/src/lib/Tools/SequelSpy/Tool.svelte
index caec4a46..b50e2f84 100644
--- a/src/lib/Tools/SequelSpy/Tool.svelte
+++ b/src/lib/Tools/SequelSpy/Tool.svelte
@@ -1,4 +1,6 @@
<script lang="ts">
+ import { run } from 'svelte/legacy';
+
import type { AniListAuthorisation } from '$lib/Data/AniList/identity';
import { prequels, type MediaPrequel } from '$lib/Data/AniList/prequels';
import { onMount } from 'svelte';
@@ -11,25 +13,29 @@
import LogInRestricted from '$lib/Error/LogInRestricted.svelte';
import Prequels from './Prequels.svelte';
- export let user: AniListAuthorisation;
+ interface Props {
+ user: AniListAuthorisation;
+ }
+
+ let { user }: Props = $props();
- let currentPrequels: Promise<MediaPrequel[]> = Promise.resolve([]) as Promise<MediaPrequel[]>;
+ let currentPrequels: Promise<MediaPrequel[]> = $state(Promise.resolve([]) as Promise<MediaPrequel[]>);
const urlParameters = browser ? new URLSearchParams(window.location.search) : null;
- let year = parseOrDefault(urlParameters, 'year', new Date().getFullYear());
- let season = parseOrDefault(urlParameters, 'season', getSeason());
+ let year = $state(parseOrDefault(urlParameters, 'year', new Date().getFullYear()));
+ let season = $state(parseOrDefault(urlParameters, 'season', getSeason()));
- $: {
+ run(() => {
if (year.toString().length === 4 && $identity.id !== -2 && user)
currentPrequels = prequels(user, year, season);
- }
- $: {
+ });
+ run(() => {
if (browser) {
$page.url.searchParams.set('year', year.toString());
$page.url.searchParams.set('season', season.toString());
clearAllParameters(['year', 'season']);
history.replaceState(null, '', `?${$page.url.searchParams.toString()}`);
}
- }
+ });
onMount(() => clearAllParameters(['year', 'season']));
</script>
@@ -54,7 +60,7 @@
<Prequels {currentPrequels} />
{/await}
- <p />
+ <p></p>
The count ratio is the number of episodes you've seen of any direct prequels, and the total
number of episodes of all direct prequels.
diff --git a/src/lib/Tools/Tracker/Tool.svelte b/src/lib/Tools/Tracker/Tool.svelte
index 8906e72d..2a1b38d7 100644
--- a/src/lib/Tools/Tracker/Tool.svelte
+++ b/src/lib/Tools/Tracker/Tool.svelte
@@ -4,14 +4,14 @@
import { onMount } from 'svelte';
import Message from '$lib/Loading/Message.svelte';
- let url = '';
- let title = '';
- let progress = 0;
- let error = '';
- let masterList: TrackerEntry[] | null = null;
+ let url = $state('');
+ let title = $state('');
+ let progress = $state(0);
+ let error = $state('');
+ let masterList: TrackerEntry[] | null = $state(null);
let confirmDelete = 0;
- $: listAccess = masterList || [];
+ let listAccess = $derived(masterList || []);
onMount(async () => {
masterList = await database.entries.toArray();
@@ -74,9 +74,9 @@
<input type="url" placeholder="URL" bind:value={url} />
<input type="text" placeholder="Title" bind:value={title} />
<input type="number" placeholder="Progress (defaults to 0)" bind:value={progress} />
- <button class="button-lined" on:click={() => addEntry(url, title, progress)}> Add </button>
+ <button class="button-lined" onclick={() => addEntry(url, title, progress)}> Add </button>
- <p />
+ <p></p>
{#if masterList === null}
<Message message="Loading entries ..." />
@@ -95,7 +95,7 @@
type="number"
value={entry.progress}
size={3}
- on:change={(e) =>
+ onchange={(e) =>
adjustEntry(entry.id, e.target ? e.target.value || entry.progress : entry.progress)}
/>
@@ -103,17 +103,17 @@
<span class="opaque">|</span>
<button
class="button-square button-action"
- on:click={() => adjustEntry(entry.id, entry.progress - 1)}
+ onclick={() => adjustEntry(entry.id, entry.progress - 1)}
>-
</button>
<button
class="button-square button-action"
- on:click={() => adjustEntry(entry.id, entry.progress + 1)}
+ onclick={() => adjustEntry(entry.id, entry.progress + 1)}
>
+
</button>
<span class="opaque">|</span>
- <button on:click={() => deleteEntry(entry.id)}>Remove</button>
+ <button onclick={() => deleteEntry(entry.id)}>Remove</button>
</span>
</div>
</li>
diff --git a/src/lib/Tools/UmaMusumeBirthdays.svelte b/src/lib/Tools/UmaMusumeBirthdays.svelte
index 29b1faa6..c9b03287 100644
--- a/src/lib/Tools/UmaMusumeBirthdays.svelte
+++ b/src/lib/Tools/UmaMusumeBirthdays.svelte
@@ -1,4 +1,6 @@
<script lang="ts">
+ import { run } from 'svelte/legacy';
+
import { browser } from '$app/environment';
import { page } from '$app/stores';
import Error from '$lib/Error/RateLimited.svelte';
@@ -23,11 +25,11 @@
const urlParameters = browser ? new URLSearchParams(window.location.search) : null;
let date = new Date();
- let month = parseOrDefault(urlParameters, 'month', date.getMonth() + 1);
- let day = parseOrDefault(urlParameters, 'day', date.getDate());
- let umapyoi: Promise<Birthday[]>;
+ let month = $state(parseOrDefault(urlParameters, 'month', date.getMonth() + 1));
+ let day = $state(parseOrDefault(urlParameters, 'day', date.getDate()));
+ let umapyoi: Promise<Birthday[]> = $state();
- $: {
+ run(() => {
month = Math.min(month, 12);
month = Math.max(month, 1);
day = Math.min(day, new Date(new Date().getFullYear(), month, 0).getDate());
@@ -39,7 +41,7 @@
clearAllParameters(['month', 'day']);
history.replaceState(null, '', `?${$page.url.searchParams.toString()}`);
}
- }
+ });
onMount(() => {
clearAllParameters(['month', 'day']);
diff --git a/src/lib/Tools/Wrapped/ActivityHistory.svelte b/src/lib/Tools/Wrapped/ActivityHistory.svelte
index 3da401d4..e652a487 100644
--- a/src/lib/Tools/Wrapped/ActivityHistory.svelte
+++ b/src/lib/Tools/Wrapped/ActivityHistory.svelte
@@ -3,10 +3,19 @@
import type { AniListAuthorisation } from '$lib/Data/AniList/identity';
import ActivityHistoryGrid from '../ActivityHistory/Grid.svelte';
- export let user: AniListAuthorisation;
- export let activities: ActivityHistoryEntry[];
- export let year: number;
- export let activityHistoryPosition: 'TOP' | 'BELOW_TOP' | 'ORIGINAL';
+ interface Props {
+ user: AniListAuthorisation;
+ activities: ActivityHistoryEntry[];
+ year: number;
+ activityHistoryPosition: 'TOP' | 'BELOW_TOP' | 'ORIGINAL';
+ }
+
+ let {
+ user,
+ activities,
+ year,
+ activityHistoryPosition
+ }: Props = $props();
</script>
<div
diff --git a/src/lib/Tools/Wrapped/Media.svelte b/src/lib/Tools/Wrapped/Media.svelte
index ea8a989b..7f8d4f66 100644
--- a/src/lib/Tools/Wrapped/Media.svelte
+++ b/src/lib/Tools/Wrapped/Media.svelte
@@ -4,14 +4,27 @@
import MediaTitleDisplay from '$lib/List/MediaTitleDisplay.svelte';
import proxy from '$lib/Utility/proxy';
- export let animeList: Media[] | undefined;
- export let mangaList: Media[] | undefined;
- export let wrapped: Wrapped;
- export let updateWidth: () => void;
- export let highestRatedMediaPercentage: boolean;
- export let highestRatedCount: number;
- export let animeMostTitle: string;
- export let mangaMostTitle: string;
+ interface Props {
+ animeList: Media[] | undefined;
+ mangaList: Media[] | undefined;
+ wrapped: Wrapped;
+ updateWidth: () => void;
+ highestRatedMediaPercentage: boolean;
+ highestRatedCount: number;
+ animeMostTitle: string;
+ mangaMostTitle: string;
+ }
+
+ let {
+ animeList,
+ mangaList,
+ wrapped,
+ updateWidth,
+ highestRatedMediaPercentage,
+ highestRatedCount,
+ animeMostTitle,
+ mangaMostTitle
+ }: Props = $props();
</script>
{#if animeList !== undefined || mangaList !== undefined}
@@ -28,7 +41,7 @@
)}
alt="Highest Rated Anime Cover"
class="cover-image"
- on:load={updateWidth}
+ onload={updateWidth}
/>
</a>
<div>
@@ -67,7 +80,7 @@
)}
alt="Highest Rated Manga Cover"
class="cover-image"
- on:load={updateWidth}
+ onload={updateWidth}
/>
</a>
<div>
diff --git a/src/lib/Tools/Wrapped/MediaExtras.svelte b/src/lib/Tools/Wrapped/MediaExtras.svelte
index 9e755ea5..3e083c0b 100644
--- a/src/lib/Tools/Wrapped/MediaExtras.svelte
+++ b/src/lib/Tools/Wrapped/MediaExtras.svelte
@@ -2,10 +2,19 @@
import type { TopMedia } from '$lib/Data/AniList/wrapped';
import proxy from '$lib/Utility/proxy';
- export let topMedia: TopMedia;
- export let updateWidth: () => void;
- export let highestRatedGenreTagPercentage: boolean;
- export let genreTagTitle: string;
+ interface Props {
+ topMedia: TopMedia;
+ updateWidth: () => void;
+ highestRatedGenreTagPercentage: boolean;
+ genreTagTitle: string;
+ }
+
+ let {
+ topMedia,
+ updateWidth,
+ highestRatedGenreTagPercentage,
+ genreTagTitle
+ }: Props = $props();
</script>
<div class="categories-grid" style="padding-top: 0;">
@@ -22,7 +31,7 @@
src={proxy(topMedia.topGenreMedia.coverImage.extraLarge)}
alt="Highest Rated Genre Cover"
class="cover-image"
- on:load={updateWidth}
+ onload={updateWidth}
/>
</a>
<div>
@@ -53,7 +62,7 @@
src={proxy(topMedia.topTagMedia.coverImage.extraLarge)}
alt="Highest Rated Tag Cover"
class="cover-image"
- on:load={updateWidth}
+ onload={updateWidth}
/>
</a>
<div>
diff --git a/src/lib/Tools/Wrapped/Tool.svelte b/src/lib/Tools/Wrapped/Tool.svelte
index 1484ab5c..af817051 100644
--- a/src/lib/Tools/Wrapped/Tool.svelte
+++ b/src/lib/Tools/Wrapped/Tool.svelte
@@ -1,4 +1,6 @@
<script lang="ts">
+ import { run } from 'svelte/legacy';
+
import userIdentity from '$stores/identity';
import type { AniListAuthorisation } from '$lib/Data/AniList/identity';
import { onMount } from 'svelte';
@@ -30,153 +32,50 @@
import tooltip from '$lib/Tooltip/tooltip';
import LogInRestricted from '$lib/Error/LogInRestricted.svelte';
- export let user: AniListAuthorisation;
-
- const currentYear = new Date(Date.now()).getFullYear();
- let selectedYear = new Date(Date.now()).getFullYear();
- let episodes = 0;
- let chapters = 0;
- let minutesWatched = 0;
- let animeList: Media[] | undefined = undefined;
- let mangaList: Media[] | undefined = undefined;
- let calculatedAnimeList: Media[] | undefined = undefined;
- let calculatedMangaList: Media[] | undefined = undefined;
- let originalAnimeList: Media[] | undefined = undefined;
- let originalMangaList: Media[] | undefined = undefined;
- let transparency = false;
- let lightTheme = true;
- let watermark = false;
- let includeMusic = false;
- let includeSpecials = true;
- let includeRepeats = false;
- let width = 1920;
- let lightMode = false;
- let highestRatedCount = 5;
- let genreTagCount = 5;
- let mounted = false;
- let generated = false;
- let disableActivityHistory = true;
- let excludedKeywordsInput = '';
- let excludedKeywords: string[] = [];
- let useFullActivityHistory = false;
- let topGenresTags = true;
- let topMedia: TopMedia;
- let highestRatedMediaPercentage = true;
- let highestRatedGenreTagPercentage = true;
- let genreTagsSort = SortOptions.SCORE;
- let mediaSort = SortOptions.SCORE;
- let includeMovies = true;
- let includeOVAs = true;
- let activityHistoryPosition: 'TOP' | 'BELOW_TOP' | 'ORIGINAL' = 'ORIGINAL';
- let includeOngoingMediaFromPreviousYears = false;
-
- $: {
- if (browser && mounted) {
- $page.url.searchParams.set('transparency', transparency.toString());
- $page.url.searchParams.set('lightTheme', lightTheme.toString());
- $page.url.searchParams.set('watermark', watermark.toString());
- $page.url.searchParams.set('includeMusic', includeMusic.toString());
- $page.url.searchParams.set('includeSpecials', includeSpecials.toString());
- $page.url.searchParams.set('includeRepeats', includeRepeats.toString());
- $page.url.searchParams.set('lightMode', lightMode.toString());
- $page.url.searchParams.set('highestRatedCount', highestRatedCount.toString());
- $page.url.searchParams.set('genreTagCount', genreTagCount.toString());
- $page.url.searchParams.set('disableActivityHistory', disableActivityHistory.toString());
- $page.url.searchParams.set(
- 'highestRatedMediaPercentage',
- highestRatedMediaPercentage.toString()
- );
- $page.url.searchParams.set(
- 'highestRatedGenreTagPercentage',
- highestRatedGenreTagPercentage.toString()
- );
- $page.url.searchParams.set('genreTagsSort', genreTagsSort.toString());
- $page.url.searchParams.set('mediaSort', mediaSort.toString());
- $page.url.searchParams.set('includeMovies', includeMovies.toString());
- $page.url.searchParams.set('includeOVAs', includeOVAs.toString());
-
- history.replaceState(null, '', `?${$page.url.searchParams.toString()}`);
- }
- }
- $: {
- includeMusic = includeMusic;
- includeSpecials = includeSpecials;
- includeRepeats = includeRepeats;
- disableActivityHistory = disableActivityHistory;
- highestRatedMediaPercentage = highestRatedMediaPercentage;
- highestRatedGenreTagPercentage = highestRatedGenreTagPercentage;
- topGenresTags = topGenresTags;
- genreTagsSort = genreTagsSort;
- mediaSort = mediaSort;
- includeMovies = includeMovies;
- includeOVAs = includeOVAs;
- selectedYear = selectedYear;
- includeOngoingMediaFromPreviousYears = includeOngoingMediaFromPreviousYears;
-
- update().then(updateWidth).catch(updateWidth);
- }
- $: {
- animeList = animeList;
- mangaList = mangaList;
- highestRatedCount = highestRatedCount;
-
- new Promise((resolve) => setTimeout(resolve, 1)).then(updateWidth);
+ interface Props {
+ user: AniListAuthorisation;
}
- $: {
- genreTagCount = genreTagCount;
-
- if (animeList && mangaList)
- topMedia = tops(
- [...(animeList || []), ...(mangaList || [])],
- genreTagCount,
- genreTagsSort,
- excludedKeywords
- );
- new Promise((resolve) => setTimeout(resolve, 1)).then(updateWidth);
- }
- $: {
- excludedKeywords = excludedKeywords;
+ let { user }: Props = $props();
- if (excludedKeywords.length > 0 && animeList !== undefined && mangaList !== undefined) {
- animeList = originalAnimeList;
- mangaList = originalMangaList;
- animeList = excludeKeywords(animeList as Media[]);
- mangaList = excludeKeywords(mangaList as Media[]);
- }
+ const currentYear = new Date(Date.now()).getFullYear();
+ let selectedYear = $state(new Date(Date.now()).getFullYear());
+ let episodes = $state(0);
+ let chapters = $state(0);
+ let minutesWatched = $state(0);
+ let animeList: Media[] | undefined = $state(undefined);
+ let mangaList: Media[] | undefined = $state(undefined);
+ let calculatedAnimeList: Media[] | undefined = $state(undefined);
+ let calculatedMangaList: Media[] | undefined = $state(undefined);
+ let originalAnimeList: Media[] | undefined = $state(undefined);
+ let originalMangaList: Media[] | undefined = $state(undefined);
+ let transparency = $state(false);
+ let lightTheme = $state(true);
+ let watermark = $state(false);
+ let includeMusic = $state(false);
+ let includeSpecials = $state(true);
+ let includeRepeats = $state(false);
+ let width = $state(1920);
+ let lightMode = $state(false);
+ let highestRatedCount = $state(5);
+ let genreTagCount = $state(5);
+ let mounted = $state(false);
+ let generated = $state(false);
+ let disableActivityHistory = $state(true);
+ let excludedKeywordsInput = $state('');
+ let excludedKeywords: string[] = $state([]);
+ let useFullActivityHistory = $state(false);
+ let topGenresTags = $state(true);
+ let topMedia: TopMedia = $state();
+ let highestRatedMediaPercentage = $state(true);
+ let highestRatedGenreTagPercentage = $state(true);
+ let genreTagsSort = $state(SortOptions.SCORE);
+ let mediaSort = $state(SortOptions.SCORE);
+ let includeMovies = $state(true);
+ let includeOVAs = $state(true);
+ let activityHistoryPosition: 'TOP' | 'BELOW_TOP' | 'ORIGINAL' = $state('ORIGINAL');
+ let includeOngoingMediaFromPreviousYears = $state(false);
- updateWidth();
- }
- $: genreTagTitle = (() => {
- switch (genreTagsSort) {
- case SortOptions.SCORE:
- return 'Highest Rated';
- case SortOptions.MINUTES_WATCHED:
- return 'Most Watched';
- case SortOptions.COUNT:
- return 'Most Common';
- }
- })();
- $: animeMostTitle = (() => {
- switch (mediaSort) {
- case SortOptions.SCORE:
- return 'Highest Rated';
- case SortOptions.MINUTES_WATCHED:
- return 'Most Watched';
- case SortOptions.COUNT:
- return 'Most Common';
- }
- })();
- $: mangaMostTitle = (() => {
- switch (mediaSort) {
- case SortOptions.SCORE:
- return 'Highest Rated';
- case SortOptions.MINUTES_WATCHED:
- return 'Most Read';
- case SortOptions.COUNT:
- return 'Most Common';
- }
- })();
const updateWidth = () => {
if (!browser) return;
@@ -562,6 +461,113 @@
// return mediaCover(top[Math.floor(Math.random() * top.length)].mediaIds[0]);
// };
+ run(() => {
+ includeMusic = includeMusic;
+ includeSpecials = includeSpecials;
+ includeRepeats = includeRepeats;
+ disableActivityHistory = disableActivityHistory;
+ highestRatedMediaPercentage = highestRatedMediaPercentage;
+ highestRatedGenreTagPercentage = highestRatedGenreTagPercentage;
+ topGenresTags = topGenresTags;
+ genreTagsSort = genreTagsSort;
+ mediaSort = mediaSort;
+ includeMovies = includeMovies;
+ includeOVAs = includeOVAs;
+ selectedYear = selectedYear;
+ includeOngoingMediaFromPreviousYears = includeOngoingMediaFromPreviousYears;
+
+ update().then(updateWidth).catch(updateWidth);
+ });
+ run(() => {
+ animeList = animeList;
+ mangaList = mangaList;
+ highestRatedCount = highestRatedCount;
+
+ new Promise((resolve) => setTimeout(resolve, 1)).then(updateWidth);
+ });
+ run(() => {
+ excludedKeywords = excludedKeywords;
+
+ if (excludedKeywords.length > 0 && animeList !== undefined && mangaList !== undefined) {
+ animeList = originalAnimeList;
+ mangaList = originalMangaList;
+ animeList = excludeKeywords(animeList as Media[]);
+ mangaList = excludeKeywords(mangaList as Media[]);
+ }
+
+ updateWidth();
+ });
+ run(() => {
+ genreTagCount = genreTagCount;
+
+ if (animeList && mangaList)
+ topMedia = tops(
+ [...(animeList || []), ...(mangaList || [])],
+ genreTagCount,
+ genreTagsSort,
+ excludedKeywords
+ );
+
+ new Promise((resolve) => setTimeout(resolve, 1)).then(updateWidth);
+ });
+ run(() => {
+ if (browser && mounted) {
+ $page.url.searchParams.set('transparency', transparency.toString());
+ $page.url.searchParams.set('lightTheme', lightTheme.toString());
+ $page.url.searchParams.set('watermark', watermark.toString());
+ $page.url.searchParams.set('includeMusic', includeMusic.toString());
+ $page.url.searchParams.set('includeSpecials', includeSpecials.toString());
+ $page.url.searchParams.set('includeRepeats', includeRepeats.toString());
+ $page.url.searchParams.set('lightMode', lightMode.toString());
+ $page.url.searchParams.set('highestRatedCount', highestRatedCount.toString());
+ $page.url.searchParams.set('genreTagCount', genreTagCount.toString());
+ $page.url.searchParams.set('disableActivityHistory', disableActivityHistory.toString());
+ $page.url.searchParams.set(
+ 'highestRatedMediaPercentage',
+ highestRatedMediaPercentage.toString()
+ );
+ $page.url.searchParams.set(
+ 'highestRatedGenreTagPercentage',
+ highestRatedGenreTagPercentage.toString()
+ );
+ $page.url.searchParams.set('genreTagsSort', genreTagsSort.toString());
+ $page.url.searchParams.set('mediaSort', mediaSort.toString());
+ $page.url.searchParams.set('includeMovies', includeMovies.toString());
+ $page.url.searchParams.set('includeOVAs', includeOVAs.toString());
+
+ history.replaceState(null, '', `?${$page.url.searchParams.toString()}`);
+ }
+ });
+ let genreTagTitle = $derived((() => {
+ switch (genreTagsSort) {
+ case SortOptions.SCORE:
+ return 'Highest Rated';
+ case SortOptions.MINUTES_WATCHED:
+ return 'Most Watched';
+ case SortOptions.COUNT:
+ return 'Most Common';
+ }
+ })());
+ let animeMostTitle = $derived((() => {
+ switch (mediaSort) {
+ case SortOptions.SCORE:
+ return 'Highest Rated';
+ case SortOptions.MINUTES_WATCHED:
+ return 'Most Watched';
+ case SortOptions.COUNT:
+ return 'Most Common';
+ }
+ })());
+ let mangaMostTitle = $derived((() => {
+ switch (mediaSort) {
+ case SortOptions.SCORE:
+ return 'Highest Rated';
+ case SortOptions.MINUTES_WATCHED:
+ return 'Most Read';
+ case SortOptions.COUNT:
+ return 'Most Common';
+ }
+ })());
</script>
{#if $userIdentity.id === -2 || user === undefined}
@@ -630,10 +636,10 @@
</div>
<div class="list">
<div class:card={generated}>
- <div id="wrapped-final" />
+ <div id="wrapped-final"></div>
{#if generated}
- <p />
+ <p></p>
<blockquote style="margin: 0 0 0 1.5rem;">
Click on the image to download, or right click and select "Save Image As...".
@@ -642,11 +648,11 @@
</div>
{#if generated}
- <p />
+ <p></p>
{/if}
<div id="options" class="card">
- <button on:click={screenshot} data-umami-event="Generate Wrapped">
+ <button onclick={screenshot} data-umami-event="Generate Wrapped">
Generate image
</button>
@@ -690,9 +696,9 @@
{/each}
</select>
Highest genre and tag count<br />
- <button on:click={updateWidth}>Find best fit</button>
- <button on:click={() => (width -= 25)}>-25px</button>
- <button on:click={() => (width += 25)}>+25px</button>
+ <button onclick={updateWidth}>Find best fit</button>
+ <button onclick={() => (width -= 25)}>-25px</button>
+ <button onclick={() => (width += 25)}>+25px</button>
Width adjustment<br />
</details>
@@ -700,7 +706,7 @@
<summary>Calculation</summary>
<input type="checkbox" bind:checked={useFullActivityHistory} />
- Enable full-year activity<button class="smaller-button" on:click={pruneFullYear}
+ Enable full-year activity<button class="smaller-button" onclick={pruneFullYear}
>Refresh data</button
>
<br />
@@ -732,12 +738,12 @@
<input
type="text"
bind:value={excludedKeywordsInput}
- on:keypress={(e) => {
+ onkeypress={(e) => {
e.key === 'Enter' && submitExcludedKeywords();
}}
/>
Excluded keywords
- <button on:click={submitExcludedKeywords} title="Or click your Enter key" use:tooltip
+ <button onclick={submitExcludedKeywords} title="Or click your Enter key" use:tooltip
>Submit</button
>
<br />
diff --git a/src/lib/Tools/Wrapped/Top/Activity.svelte b/src/lib/Tools/Wrapped/Top/Activity.svelte
index a91bedfb..27dea6a2 100644
--- a/src/lib/Tools/Wrapped/Top/Activity.svelte
+++ b/src/lib/Tools/Wrapped/Top/Activity.svelte
@@ -4,18 +4,28 @@
import type { Wrapped } from '$lib/Data/AniList/wrapped';
import proxy from '$lib/Utility/proxy';
- export let wrapped: Wrapped;
- export let year: number;
- export let activities: ActivityHistoryEntry[];
- export let useFullActivityHistory: boolean;
- export let updateWidth: () => void;
+ interface Props {
+ wrapped: Wrapped;
+ year: number;
+ activities: ActivityHistoryEntry[];
+ useFullActivityHistory: boolean;
+ updateWidth: () => void;
+ }
+
+ let {
+ wrapped,
+ year,
+ activities,
+ useFullActivityHistory,
+ updateWidth
+ }: Props = $props();
const currentYear = new Date(Date.now()).getFullYear();
</script>
<div class="grid-item image-grid avatar-grid category top-category">
<a href={`https://anilist.co/user/${$identity.name}`} target="_blank">
- <img src={proxy(wrapped.avatar.large)} alt="User Avatar" on:load={updateWidth} />
+ <img src={proxy(wrapped.avatar.large)} alt="User Avatar" onload={updateWidth} />
</a>
<div>
<div>
diff --git a/src/lib/Tools/Wrapped/Top/Anime.svelte b/src/lib/Tools/Wrapped/Top/Anime.svelte
index 08df7fd3..275adadf 100644
--- a/src/lib/Tools/Wrapped/Top/Anime.svelte
+++ b/src/lib/Tools/Wrapped/Top/Anime.svelte
@@ -1,9 +1,13 @@
<script lang="ts">
import type { Media } from '$lib/Data/AniList/media';
- export let minutesWatched: number;
- export let animeList: Media[] | undefined;
- export let episodes: number;
+ interface Props {
+ minutesWatched: number;
+ animeList: Media[] | undefined;
+ episodes: number;
+ }
+
+ let { minutesWatched, animeList, episodes }: Props = $props();
</script>
<div class="category-grid pure-category category top-category">
diff --git a/src/lib/Tools/Wrapped/Top/Manga.svelte b/src/lib/Tools/Wrapped/Top/Manga.svelte
index a36f7724..a49d1067 100644
--- a/src/lib/Tools/Wrapped/Top/Manga.svelte
+++ b/src/lib/Tools/Wrapped/Top/Manga.svelte
@@ -2,8 +2,12 @@
import type { Media } from '$lib/Data/AniList/media';
import { estimatedDayReading } from '$lib/Media/Manga/time';
- export let mangaList: Media[] | undefined;
- export let chapters: number;
+ interface Props {
+ mangaList: Media[] | undefined;
+ chapters: number;
+ }
+
+ let { mangaList, chapters }: Props = $props();
</script>
<div class="category-grid pure-category category top-category">