diff options
Diffstat (limited to 'src/lib/Tools')
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"> |