aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorFuwn <[email protected]>2024-10-28 15:32:46 -0700
committerFuwn <[email protected]>2024-10-28 15:32:46 -0700
commit39b677404558ae3b7eb34e818d7ca308f62f9cb0 (patch)
tree7f19fca39ecd4237e3c0d1aef2d8e9fa3cec7845 /src
parentfeat(graphql): paged badges query (diff)
downloaddue.moe-svelte-5.tar.xz
due.moe-svelte-5.zip
feat: update to svelte 5svelte-5
Diffstat (limited to 'src')
-rw-r--r--src/lib/Announcement.svelte4
-rw-r--r--src/lib/Error/AnimeRateLimited.svelte11
-rw-r--r--src/lib/Error/DotDotDot.svelte12
-rw-r--r--src/lib/Error/LogInRestricted.svelte2
-rw-r--r--src/lib/Error/Notice.svelte12
-rw-r--r--src/lib/Error/RateLimited.svelte33
-rw-r--r--src/lib/Events/Event.svelte54
-rw-r--r--src/lib/Events/Group.svelte6
-rw-r--r--src/lib/Hololive/Lives.svelte25
-rw-r--r--src/lib/Hololive/Stream.svelte19
-rw-r--r--src/lib/Home/HeadTitle.svelte8
-rw-r--r--src/lib/Home/LastActivity.svelte8
-rw-r--r--src/lib/Home/Root.svelte13
-rw-r--r--src/lib/Image/FallbackImage.svelte33
-rw-r--r--src/lib/Image/ParallaxImage.svelte40
-rw-r--r--src/lib/Landing.svelte6
-rw-r--r--src/lib/Layout/Dropdown.svelte23
-rw-r--r--src/lib/Layout/NumberTicker.svelte25
-rw-r--r--src/lib/Layout/Popup.svelte40
-rw-r--r--src/lib/Layout/TextTransition.svelte27
-rw-r--r--src/lib/Layout/Username.svelte6
-rw-r--r--src/lib/Lazy.svelte38
-rw-r--r--src/lib/List/Anime/AnimeListTemplate.svelte43
-rw-r--r--src/lib/List/Anime/CleanAnimeList.svelte129
-rw-r--r--src/lib/List/Anime/CompletedAnimeList.svelte14
-rw-r--r--src/lib/List/Anime/DueAnimeList.svelte10
-rw-r--r--src/lib/List/Anime/DueIndexColumn.svelte8
-rw-r--r--src/lib/List/Anime/PlaceholderList.svelte8
-rw-r--r--src/lib/List/Anime/UpcomingAnimeList.svelte18
-rw-r--r--src/lib/List/CleanGrid.svelte12
-rw-r--r--src/lib/List/CleanList.svelte26
-rw-r--r--src/lib/List/ListTitle.svelte29
-rw-r--r--src/lib/List/Manga/CleanMangaList.svelte143
-rw-r--r--src/lib/List/Manga/MangaListTemplate.svelte43
-rw-r--r--src/lib/List/MediaTitleDisplay.svelte17
-rw-r--r--src/lib/Loading/Ellipsis.svelte8
-rw-r--r--src/lib/Loading/Grid.svelte8
-rw-r--r--src/lib/Loading/Message.svelte29
-rw-r--r--src/lib/Loading/Ripple.svelte10
-rw-r--r--src/lib/Loading/Skeleton.svelte35
-rw-r--r--src/lib/MarkdownLink.svelte8
-rw-r--r--src/lib/Media/Anime/Airing/AiringTime.svelte18
-rw-r--r--src/lib/Media/Cover/HoverCover.svelte8
-rw-r--r--src/lib/Notification/Notification.svelte24
-rw-r--r--src/lib/Reader/Chapters/MangaDex.svelte6
-rw-r--r--src/lib/Reader/Chapters/Rawkuma.svelte6
-rw-r--r--src/lib/Schedule/CoverBypass.svelte17
-rw-r--r--src/lib/Schedule/Crunchyroll.svelte6
-rw-r--r--src/lib/Schedule/Days.svelte19
-rw-r--r--src/lib/Settings/Categories/Attributions.svelte2
-rw-r--r--src/lib/Settings/Categories/Cache.svelte6
-rw-r--r--src/lib/Settings/Categories/Calculation.svelte2
-rw-r--r--src/lib/Settings/Categories/Debug.svelte10
-rw-r--r--src/lib/Settings/Categories/Display.svelte24
-rw-r--r--src/lib/Settings/Categories/RSSFeeds.svelte10
-rw-r--r--src/lib/Settings/Categories/SettingSync.svelte12
-rw-r--r--src/lib/Settings/Category.svelte23
-rw-r--r--src/lib/Settings/SettingCheckboxToggle.svelte56
-rw-r--r--src/lib/Settings/SettingHint.svelte9
-rw-r--r--src/lib/Settings/SettingToggle.svelte30
-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
-rw-r--r--src/lib/Tooltip/LinkedTooltip.svelte57
-rw-r--r--src/lib/User/BadgeWall/AWC.svelte21
-rw-r--r--src/lib/User/BadgeWall/BadgePreview.svelte46
-rw-r--r--src/lib/User/BadgeWall/Badges.svelte31
-rw-r--r--src/lib/User/BadgeWall/FallbackBadge.svelte58
-rw-r--r--src/lib/Utility/Loading.svelte18
-rw-r--r--src/lib/Utility/oauth.ts2
-rw-r--r--src/routes/+error.svelte4
-rw-r--r--src/routes/+layout.svelte56
-rw-r--r--src/routes/+page.svelte4
-rw-r--r--src/routes/api/authentication/log-out/+server.ts2
-rw-r--r--src/routes/api/oauth/refresh/+server.ts2
-rw-r--r--src/routes/completed/+page.svelte4
-rw-r--r--src/routes/events/+page.svelte2
-rw-r--r--src/routes/events/group/[group]/+page.svelte10
-rw-r--r--src/routes/events/groups/+page.svelte4
-rw-r--r--src/routes/girls/+page.svelte4
-rw-r--r--src/routes/girls/[language]/+page.svelte2
-rw-r--r--src/routes/hololive/[[stream]]/+page.svelte10
-rw-r--r--src/routes/reader/+page.svelte4
-rw-r--r--src/routes/schedule/+page.svelte4
-rw-r--r--src/routes/settings/+page.svelte9
-rw-r--r--src/routes/tools/+page.svelte8
-rw-r--r--src/routes/tools/[tool]/+page.svelte14
-rw-r--r--src/routes/updates/+page.svelte8
-rw-r--r--src/routes/user/[user]/+page.svelte54
-rw-r--r--src/routes/user/[user]/badges/+page.svelte240
112 files changed, 1670 insertions, 1026 deletions
diff --git a/src/lib/Announcement.svelte b/src/lib/Announcement.svelte
index 7bcc9df7..0dc330c7 100644
--- a/src/lib/Announcement.svelte
+++ b/src/lib/Announcement.svelte
@@ -40,9 +40,9 @@
{line}<br />
{/each}
- <p />
+ <p></p>
- <button on:click={dismiss} class="dismiss">{dismissButton || 'Dismiss'}</button>
+ <button onclick={dismiss} class="dismiss">{dismissButton || 'Dismiss'}</button>
</Popup>
{/if}
diff --git a/src/lib/Error/AnimeRateLimited.svelte b/src/lib/Error/AnimeRateLimited.svelte
index 96df3ad5..4671d45b 100644
--- a/src/lib/Error/AnimeRateLimited.svelte
+++ b/src/lib/Error/AnimeRateLimited.svelte
@@ -1,15 +1,22 @@
<script>
import Popup from '$lib/Layout/Popup.svelte';
+ /**
+ * @typedef {Object} Props
+ * @property {import('svelte').Snippet} [children]
+ */
+
+ /** @type {Props} */
+ let { children } = $props();
</script>
<Popup locked fullscreen>
- <p><slot /></p>
+ <p>{@render children?.()}</p>
<span>It is likely that you have been rate-limited by AniList. Please try again later.</span>
{#await fetch('https://api.waifu.pics/sfw/cry') then response}
{#await response.json() then json}
- <p />
+ <p></p>
<a href={`https://trace.moe/?url=${encodeURIComponent(json.url)}`} target="_blank">
<img src={json.url} alt="" style="width: 30vw;" />
diff --git a/src/lib/Error/DotDotDot.svelte b/src/lib/Error/DotDotDot.svelte
index 73261eba..22cd895c 100644
--- a/src/lib/Error/DotDotDot.svelte
+++ b/src/lib/Error/DotDotDot.svelte
@@ -1,11 +1,15 @@
<script lang="ts">
import { onDestroy, onMount } from 'svelte';
- export let max: number | undefined = undefined;
- export let perMs = 1000;
- export let start = '';
+ interface Props {
+ max?: number | undefined;
+ perMs?: number;
+ start?: string;
+ }
- let dots = start;
+ let { max = undefined, perMs = 1000, start = '' }: Props = $props();
+
+ let dots = $state(start);
let interval: NodeJS.Timeout;
onMount(() => {
diff --git a/src/lib/Error/LogInRestricted.svelte b/src/lib/Error/LogInRestricted.svelte
index 07a9adec..d19dfd3d 100644
--- a/src/lib/Error/LogInRestricted.svelte
+++ b/src/lib/Error/LogInRestricted.svelte
@@ -7,7 +7,7 @@
<div class="message">
Please <a
href={`https://anilist.co/api/v2/oauth/authorize?client_id=${env.PUBLIC_ANILIST_CLIENT_ID}&redirect_uri=${env.PUBLIC_ANILIST_REDIRECT_URI}&response_type=code`}
- on:click={() => {
+ onclick={() => {
localStorage.setItem(
'redirect',
window.location.origin + window.location.pathname + window.location.search
diff --git a/src/lib/Error/Notice.svelte b/src/lib/Error/Notice.svelte
index d4cf96c5..6bdd7327 100644
--- a/src/lib/Error/Notice.svelte
+++ b/src/lib/Error/Notice.svelte
@@ -1,2 +1,12 @@
+<script>
+ /**
+ * @typedef {Object} Props
+ * @property {import('svelte').Snippet} [children]
+ */
+
+ /** @type {Props} */
+ let { children } = $props();
+</script>
+
<b>Notice:</b>
-<slot />
+{@render children?.()}
diff --git a/src/lib/Error/RateLimited.svelte b/src/lib/Error/RateLimited.svelte
index 973d75d9..15e779b3 100644
--- a/src/lib/Error/RateLimited.svelte
+++ b/src/lib/Error/RateLimited.svelte
@@ -1,10 +1,23 @@
<script lang="ts">
- export let type = 'Media';
- export let loginSessionError = true;
- export let contact = true;
- export let list = true;
- export let card = false;
- export let might = true;
+ interface Props {
+ type?: string;
+ loginSessionError?: boolean;
+ contact?: boolean;
+ list?: boolean;
+ card?: boolean;
+ might?: boolean;
+ children?: import('svelte').Snippet;
+ }
+
+ let {
+ type = 'Media',
+ loginSessionError = true,
+ contact = true,
+ list = true,
+ card = false,
+ might = true,
+ children
+ }: Props = $props();
</script>
<div class:card>
@@ -24,10 +37,10 @@
</p>
{/if}
- <slot />
+ {@render children?.()}
{#if contact}
- <p />
+ <p></p>
If the problem persists, please contact
<a href="https://anilist.co/user/fuwn" target="_blank">@fuwn</a> on AniList.
@@ -48,10 +61,10 @@
</p>
{/if}
- <slot />
+ {@render children?.()}
{#if contact}
- <p />
+ <p></p>
If the problem persists, please contact
<a href="https://anilist.co/user/fuwn" target="_blank">@fuwn</a> on AniList.
diff --git a/src/lib/Events/Event.svelte b/src/lib/Events/Event.svelte
index a2b1d2b5..f0a4f9f7 100644
--- a/src/lib/Events/Event.svelte
+++ b/src/lib/Events/Event.svelte
@@ -7,39 +7,39 @@
export let avatar = false;
</script>
-<a href={event.anilist_url} target="_blank">
- <div
- class="card"
- id="user-grid"
- style={`background-image: ${event.banner ? `url(${event.banner})` : 'none'}; padding: 0;`}
- >
- {#if event}
+<div
+ class="card"
+ id="user-grid"
+ style={`background-image: ${event.banner ? `url(${event.banner})` : 'none'}; padding: 0;`}
+>
+ {#if event}
+ <a href={event.anilist_url} target="_blank">
<img src={event.banner} alt="" id="cover-image" />
- {/if}
+ </a>
+ {/if}
- <div class="card" id="user-grid-content">
- {#if avatar}
- <div id="user-grid-avatar">
- <a href={root(`/events/group/${event.group.anilist_username}`)}>
- <img src={event.group.avatar} alt="" width="100vw" id="avatar" />
- </a>
- </div>
- {/if}
+ <div class="card" id="user-grid-content">
+ {#if avatar}
+ <div id="user-grid-avatar">
+ <a href={root(`/events/group/${event.group.anilist_username}`)}>
+ <img src={event.group.avatar} alt="" width="100vw" id="avatar" />
+ </a>
+ </div>
+ {/if}
- <div id="user-grid-content-text">
- <p>
- <a href={event.anilist_url} target="_blank" class="title-text">
- {event.title}
- </a>
- <br />
- {$locale().dateFormatter(new Date(event.created_at))}
- </p>
+ <div id="user-grid-content-text">
+ <p>
+ <a href={event.anilist_url} target="_blank" class="title-text">
+ {event.title}
+ </a>
+ <br />
+ {$locale().dateFormatter(new Date(event.created_at))}
+ </p>
- <p>{event.description}</p>
- </div>
+ <p>{event.description}</p>
</div>
</div>
-</a>
+</div>
<style>
#user-grid-content {
diff --git a/src/lib/Events/Group.svelte b/src/lib/Events/Group.svelte
index a3d24807..e88d9b2a 100644
--- a/src/lib/Events/Group.svelte
+++ b/src/lib/Events/Group.svelte
@@ -2,7 +2,11 @@
import type { Group } from '$lib/Database/SB/groups';
import tooltip from '$lib/Tooltip/tooltip';
- export let group: Group;
+ interface Props {
+ group: Group;
+ }
+
+ let { group }: Props = $props();
</script>
<div
diff --git a/src/lib/Hololive/Lives.svelte b/src/lib/Hololive/Lives.svelte
index 9e762df9..0f566df4 100644
--- a/src/lib/Hololive/Lives.svelte
+++ b/src/lib/Hololive/Lives.svelte
@@ -4,10 +4,19 @@
import type { Live, ParseResult } from './hololive';
import Stream from './Stream.svelte';
- export let schedule: ParseResult;
- export let pinnedStreams: string[];
- export let getPinnedStreams: () => void;
- export let filter: string | undefined;
+ interface Props {
+ schedule: ParseResult;
+ pinnedStreams: string[];
+ getPinnedStreams: () => void;
+ filter: string | undefined;
+ }
+
+ let {
+ schedule,
+ pinnedStreams,
+ getPinnedStreams,
+ filter
+ }: Props = $props();
const pinStream = (streamer: string) =>
fetch(root(`/api/preferences/pin?stream=${encodeURIComponent(streamer)}`), {
@@ -17,7 +26,7 @@
}
}).then(getPinnedStreams);
- $: categorisedStreams = schedule.lives
+ let categorisedStreams = $derived(schedule.lives
.filter((live) => (filter ? live.streamer === filter : true))
.sort((a, b) => new Date(a.time).getTime() - new Date(b.time).getTime())
.sort((a, b) => {
@@ -55,7 +64,7 @@
return acc;
},
{ live: [], upcoming: [], ended: [] }
- );
+ ));
</script>
{#if schedule.lives.length === 0}
@@ -68,7 +77,7 @@
{/each}
</div>
-<p />
+<p></p>
<div class="container">
{#each categorisedStreams.upcoming as live}
@@ -76,7 +85,7 @@
{/each}
</div>
-<p />
+<p></p>
<div class="container">
{#each categorisedStreams.ended as live}
diff --git a/src/lib/Hololive/Stream.svelte b/src/lib/Hololive/Stream.svelte
index 12681acb..9f2b5e7b 100644
--- a/src/lib/Hololive/Stream.svelte
+++ b/src/lib/Hololive/Stream.svelte
@@ -5,10 +5,19 @@
import locale from '$stores/locale';
import Icon from '@iconify/svelte';
- export let live: any;
- export let pinStream: (streamer: string) => void;
- export let pinnedStreams: string[];
- export let icon: string;
+ interface Props {
+ live: any;
+ pinStream: (streamer: string) => void;
+ pinnedStreams: string[];
+ icon: string;
+ }
+
+ let {
+ live,
+ pinStream,
+ pinnedStreams,
+ icon
+ }: Props = $props();
</script>
<div class="stream card">
@@ -16,7 +25,7 @@
<div class="pin-icon">
<a
href={'#'}
- on:click={(e) => {
+ onclick={(e) => {
e.preventDefault();
pinStream(live.streamer);
}}
diff --git a/src/lib/Home/HeadTitle.svelte b/src/lib/Home/HeadTitle.svelte
index 39a65ccd..a87da69a 100644
--- a/src/lib/Home/HeadTitle.svelte
+++ b/src/lib/Home/HeadTitle.svelte
@@ -1,6 +1,10 @@
<script lang="ts">
- export let route: string | undefined = undefined;
- export let path = '/';
+ interface Props {
+ route?: string | undefined;
+ path?: string;
+ }
+
+ let { route = undefined, path = '/' }: Props = $props();
const title = (route ? `${route} • ` : '') + 'due.moe';
</script>
diff --git a/src/lib/Home/LastActivity.svelte b/src/lib/Home/LastActivity.svelte
index 000c7f7b..cdbf4d44 100644
--- a/src/lib/Home/LastActivity.svelte
+++ b/src/lib/Home/LastActivity.svelte
@@ -5,9 +5,13 @@
import { lastActivityDate } from '../Data/AniList/activity';
import settings from '$stores/settings';
- export let user: AniListAuthorisation;
+ interface Props {
+ user: AniListAuthorisation;
+ }
- let lastActivityWasToday = true;
+ let { user }: Props = $props();
+
+ let lastActivityWasToday = $state(true);
onMount(async () => {
if (user !== undefined && !$settings.displayDisableLastActivityWarning) {
diff --git a/src/lib/Home/Root.svelte b/src/lib/Home/Root.svelte
index bc1bdea9..e17f9a77 100644
--- a/src/lib/Home/Root.svelte
+++ b/src/lib/Home/Root.svelte
@@ -2,14 +2,19 @@
import settings from '$stores/settings';
import { fly } from 'svelte/transition';
- export let data: any;
- export let way: number;
+ interface Props {
+ data: any;
+ way: number;
+ children?: import('svelte').Snippet;
+ }
+
+ let { data, way, children }: Props = $props();
const animationDelay = 100;
</script>
{#if $settings.displayDisableAnimations}
- <slot />
+ {@render children?.()}
{:else}
{#key data.url}
<div
@@ -20,7 +25,7 @@
}}
out:fly={{ x: -way, duration: animationDelay }}
>
- <slot />
+ {@render children?.()}
</div>
{/key}
{/if}
diff --git a/src/lib/Image/FallbackImage.svelte b/src/lib/Image/FallbackImage.svelte
index 2029cf0e..1eafdd4c 100644
--- a/src/lib/Image/FallbackImage.svelte
+++ b/src/lib/Image/FallbackImage.svelte
@@ -1,14 +1,27 @@
<script lang="ts">
- export let source: string | undefined | null;
- export let alternative: string | undefined | null;
- export let fallback: string | undefined | null;
- export let maxReplaceCount = 1;
- export let replaceDelay = 1000;
- export let error = 'https://i2.kym-cdn.com/photos/images/newsfeed/000/290/992/0aa.jpg';
- export let hideOnError = false;
- export let style = '';
+ interface Props {
+ source: string | undefined | null;
+ alternative: string | undefined | null;
+ fallback: string | undefined | null;
+ maxReplaceCount?: number;
+ replaceDelay?: number;
+ error?: string;
+ hideOnError?: boolean;
+ style?: string;
+ }
+
+ let {
+ source,
+ alternative,
+ fallback,
+ maxReplaceCount = 1,
+ replaceDelay = 1000,
+ error = 'https://i2.kym-cdn.com/photos/images/newsfeed/000/290/992/0aa.jpg',
+ hideOnError = false,
+ style = ''
+ }: Props = $props();
- let replaceCount = 0;
+ let replaceCount = $state(0);
const delayedReplace = (event: Event, image: string | undefined | null) => {
if (replaceCount >= maxReplaceCount) return;
@@ -27,7 +40,7 @@
alt={alternative}
loading="lazy"
class="badge"
- on:error={(e) => delayedReplace(e, fallback)}
+ onerror={(e) => delayedReplace(e, fallback)}
{style}
/>
{:else if !hideOnError}
diff --git a/src/lib/Image/ParallaxImage.svelte b/src/lib/Image/ParallaxImage.svelte
index 48565ce1..390b9c92 100644
--- a/src/lib/Image/ParallaxImage.svelte
+++ b/src/lib/Image/ParallaxImage.svelte
@@ -2,17 +2,31 @@
import { cubicOut } from 'svelte/easing';
import { tweened } from 'svelte/motion';
- export let source: string;
- export let duration = 300 * 1.75;
- export let easing = cubicOut;
- export let alternativeText: string;
- export let classList = '';
- export let style = '';
- export let factor = 1.25;
- export let limit = 300 * 1.75;
- export let borderRadius = '8px';
-
- let badgeReference: HTMLImageElement;
+ interface Props {
+ source: string;
+ duration?: any;
+ easing?: any;
+ alternativeText: string;
+ classList?: string;
+ style?: string;
+ factor?: number;
+ limit?: any;
+ borderRadius?: string;
+ }
+
+ let {
+ source,
+ duration = 300 * 1.75,
+ easing = cubicOut,
+ alternativeText,
+ classList = '',
+ style = '',
+ factor = 1.25,
+ limit = 300 * 1.75,
+ borderRadius = '8px'
+ }: Props = $props();
+
+ let badgeReference: HTMLImageElement = $state();
const mouse = tweened({ x: 0, y: 0 }, { duration, easing });
const handleMouseMove = (event: MouseEvent) => {
@@ -40,7 +54,7 @@
-$mouse.x / 10
}deg); border-radius: ${borderRadius}; ${style}`}
alt={alternativeText}
- on:mousemove={handleMouseMove}
- on:mouseleave={handleMouseLeave}
+ onmousemove={handleMouseMove}
+ onmouseleave={handleMouseLeave}
class={classList}
/>
diff --git a/src/lib/Landing.svelte b/src/lib/Landing.svelte
index 29a341f1..bfe69de9 100644
--- a/src/lib/Landing.svelte
+++ b/src/lib/Landing.svelte
@@ -39,7 +39,7 @@
</div>
</div>
-<p />
+<p></p>
<div class="example-item card">
<div class="card item-description">
@@ -65,7 +65,7 @@
</div>
</div>
-<p />
+<p></p>
<div class="example-item card">
<div class="item-content">
@@ -105,7 +105,7 @@
<span class="big-text">
<a
href={`https://anilist.co/api/v2/oauth/authorize?client_id=${env.PUBLIC_ANILIST_CLIENT_ID}&redirect_uri=${env.PUBLIC_ANILIST_REDIRECT_URI}&response_type=code`}
- on:click={() => {
+ onclick={() => {
localStorage.setItem(
'redirect',
window.location.origin + window.location.pathname + window.location.search
diff --git a/src/lib/Layout/Dropdown.svelte b/src/lib/Layout/Dropdown.svelte
index fb270dfe..aca94975 100644
--- a/src/lib/Layout/Dropdown.svelte
+++ b/src/lib/Layout/Dropdown.svelte
@@ -6,12 +6,14 @@
preventDefault?: boolean;
}
- export let items: Item[] = [];
- export let title: string | undefined = undefined;
- export let header = true;
- export let center = false;
+ let {
+ items = [] as Item[],
+ title = undefined as string | undefined,
+ header = true,
+ center = false
+ } = $props();
- let open = false;
+ let open = $state(false);
const handleClickOutside = (event: any) => {
if (!event.target.closest('.dropdown')) open = false;
@@ -30,8 +32,8 @@
<span
class={`${header ? 'header-item' : ''} dropdown-toggle`}
id="dropdown-toggle"
- on:click|preventDefault={() => (open = !open)}
- on:keydown={() => {}}
+ onclick={() => (open = !open)}
+ onkeydown={() => {}}
role="button"
tabindex="0"
>
@@ -47,7 +49,7 @@
<a
href={item.url}
class="header-item"
- on:click={(e) => {
+ onclick={(e) => {
if (item.preventDefault) e.preventDefault();
if (item.onClick) item.onClick();
}}
@@ -89,7 +91,10 @@
transform: translateY(-20px);
visibility: hidden;
$delay: 0.25s;
- transition: opacity $delay ease, transform $delay ease, visibility 0s linear $delay;
+ transition:
+ opacity $delay ease,
+ transform $delay ease,
+ visibility 0s linear $delay;
left: var(--dropdown-left);
transform: var(--dropdown-transform);
z-index: 1;
diff --git a/src/lib/Layout/NumberTicker.svelte b/src/lib/Layout/NumberTicker.svelte
index b5e2f49c..3bd7e6c8 100644
--- a/src/lib/Layout/NumberTicker.svelte
+++ b/src/lib/Layout/NumberTicker.svelte
@@ -1,11 +1,24 @@
<script>
+ import { run } from 'svelte/legacy';
+
import { tweened } from 'svelte/motion';
import { cubicOut } from 'svelte/easing';
- export let start = 0;
- export let end = 100;
- export let duration = 2000;
- export let delay = 0;
+ /**
+ * @typedef {Object} Props
+ * @property {number} [start]
+ * @property {number} [end]
+ * @property {number} [duration]
+ * @property {number} [delay]
+ */
+
+ /** @type {Props} */
+ let {
+ start = 0,
+ end = 100,
+ duration = 2000,
+ delay = 0
+ } = $props();
const count = tweened(start, {
duration: duration,
@@ -13,9 +26,9 @@
delay: delay
});
- $: {
+ run(() => {
count.set(end);
- }
+ });
</script>
<span class="counter" class:visible={$count !== start}>
diff --git a/src/lib/Layout/Popup.svelte b/src/lib/Layout/Popup.svelte
index b82e86c2..45b719f8 100644
--- a/src/lib/Layout/Popup.svelte
+++ b/src/lib/Layout/Popup.svelte
@@ -1,16 +1,32 @@
<script lang="ts">
+ import { run } from 'svelte/legacy';
+
import { browser } from '$app/environment';
import { onMount } from 'svelte';
- export let onLeave = () => {
+ interface Props {
+ onLeave?: any;
+ card?: boolean;
+ smallCard?: boolean;
+ fullscreen?: boolean;
+ show?: boolean;
+ locked?: boolean;
+ center?: boolean;
+ children?: import('svelte').Snippet;
+ }
+
+ let {
+ onLeave = () => {
return;
- };
- export let card = true;
- export let smallCard = false;
- export let fullscreen = false;
- export let show = true;
- export let locked = false;
- export let center = false;
+ },
+ card = true,
+ smallCard = false,
+ fullscreen = false,
+ show = $bindable(true),
+ locked = false,
+ center = false,
+ children
+ }: Props = $props();
const handleClickOutside = (event: any) => {
if (!locked && event.target.classList.contains('popup')) {
@@ -24,24 +40,24 @@
if (browser) document.body.style.overflow = 'auto';
});
- $: {
+ run(() => {
if (browser) {
document.body.style.overflow = 'auto';
if (show) document.body.style.overflow = 'hidden';
else document.body.style.overflow = 'auto';
}
- }
+ });
</script>
-<svelte:window on:click={handleClickOutside} />
+<svelte:window onclick={handleClickOutside} />
{#if show}
<div class={`popup ${fullscreen ? 'popup-fullscreen' : ''}`}>
<span
class={`${card ? `card ${smallCard ? 'card-small' : ''}` : ''} ${center ? 'centered' : ''}`}
>
- <slot />
+ {@render children?.()}
</span>
</div>
{/if}
diff --git a/src/lib/Layout/TextTransition.svelte b/src/lib/Layout/TextTransition.svelte
index 35cfe1ea..5f5bd036 100644
--- a/src/lib/Layout/TextTransition.svelte
+++ b/src/lib/Layout/TextTransition.svelte
@@ -1,24 +1,37 @@
<script>
+ import { run } from 'svelte/legacy';
+
import { tweened } from 'svelte/motion';
import { cubicOut } from 'svelte/easing';
- export let text = '';
- export let opacityTransitionDuration = 50;
- export let blurTransitionDuration = opacityTransitionDuration;
- export let easing = cubicOut;
+ /**
+ * @typedef {Object} Props
+ * @property {string} [text]
+ * @property {number} [opacityTransitionDuration]
+ * @property {any} [blurTransitionDuration]
+ * @property {any} [easing]
+ */
+
+ /** @type {Props} */
+ let {
+ text = '',
+ opacityTransitionDuration = 50,
+ blurTransitionDuration = opacityTransitionDuration,
+ easing = cubicOut
+ } = $props();
- let previousValue = '';
+ let previousValue = $state('');
let opacity = tweened(1, { duration: opacityTransitionDuration, easing });
let blur = tweened(0, { duration: blurTransitionDuration, easing });
- $: {
+ run(() => {
if (text !== previousValue)
Promise.all([opacity.set(0), blur.set(10)]).then(() => {
previousValue = text;
Promise.all([opacity.set(1), blur.set(0)]);
});
- }
+ });
</script>
<span
diff --git a/src/lib/Layout/Username.svelte b/src/lib/Layout/Username.svelte
index 8b89b708..82be59d0 100644
--- a/src/lib/Layout/Username.svelte
+++ b/src/lib/Layout/Username.svelte
@@ -1,5 +1,9 @@
<script lang="ts">
- export let username: string;
+ interface Props {
+ username: string;
+ }
+
+ let { username }: Props = $props();
</script>
<a href={`https://anilist.co/user/${username}/`}>@{username}</a>
diff --git a/src/lib/Lazy.svelte b/src/lib/Lazy.svelte
index da5b09a9..8167711d 100644
--- a/src/lib/Lazy.svelte
+++ b/src/lib/Lazy.svelte
@@ -1,17 +1,31 @@
<script lang="ts">
import { onMount, onDestroy } from 'svelte';
- export let top = 0;
- export let bottom = 0;
- export let left = 0;
- export let right = 0;
- export let steps = 100;
- export let threshold = 0.01;
- export let once = false;
-
- let element: Element;
- let percent: number = 0;
- let visible = false;
+ interface Props {
+ top?: number;
+ bottom?: number;
+ left?: number;
+ right?: number;
+ steps?: number;
+ threshold?: number;
+ once?: boolean;
+ children?: import('svelte').Snippet<[any]>;
+ }
+
+ let {
+ top = 0,
+ bottom = 0,
+ left = 0,
+ right = 0,
+ steps = 100,
+ threshold = 0.01,
+ once = false,
+ children
+ }: Props = $props();
+
+ let element: Element = $state();
+ let percent: number = $state(0);
+ let visible = $state(false);
let observer: IntersectionObserver;
const intersectPercent = (
@@ -45,5 +59,5 @@
</script>
<div bind:this={element}>
- <slot {visible} {percent} />
+ {@render children?.({ visible, percent, })}
</div>
diff --git a/src/lib/List/Anime/AnimeListTemplate.svelte b/src/lib/List/Anime/AnimeListTemplate.svelte
index 08583d7c..8b105df5 100644
--- a/src/lib/List/Anime/AnimeListTemplate.svelte
+++ b/src/lib/List/Anime/AnimeListTemplate.svelte
@@ -14,26 +14,41 @@
import subsPlease from '$stores/subsPlease';
import identity from '$stores/identity';
- export let endTime: number;
- export let cleanMedia: (
+ interface Props {
+ endTime: number;
+ 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;
+ animeLists: Promise<Media[]>;
+ user: AniListAuthorisation;
+ title: any;
+ completed?: boolean;
+ plannedOnly?: boolean;
+ upcoming?: boolean;
+ notYetReleased?: boolean;
+ dummy?: boolean;
+ }
- let lastUpdatedMedia = -1;
- let previousAnimeList: Media[];
- let pendingUpdate: number | null = null;
- let lastListSize = 8;
+ let {
+ endTime,
+ cleanMedia,
+ animeLists = $bindable(),
+ user,
+ title,
+ completed = false,
+ plannedOnly = false,
+ upcoming = false,
+ notYetReleased = false,
+ dummy = false
+ }: Props = $props();
+
+ let lastUpdatedMedia = $state(-1);
+ let previousAnimeList: Media[] = $state();
+ let pendingUpdate: number | null = $state(null);
+ let lastListSize = $state(8);
onMount(() => {
if (browser) {
diff --git a/src/lib/List/Anime/CleanAnimeList.svelte b/src/lib/List/Anime/CleanAnimeList.svelte
index 0220a366..b088964e 100644
--- a/src/lib/List/Anime/CleanAnimeList.svelte
+++ b/src/lib/List/Anime/CleanAnimeList.svelte
@@ -16,18 +16,35 @@
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;
+ interface Props {
+ media: Media[];
+ title: any;
+ animeLists: Promise<Media[]>;
+ user: AniListAuthorisation;
+ endTime: number;
+ lastUpdatedMedia: number;
+ completed?: boolean;
+ previousAnimeList: Media[];
+ pendingUpdate: number | null;
+ upcoming?: boolean;
+ notYetReleased?: boolean;
+ dummy?: boolean;
+ }
+
+ let {
+ media = $bindable(),
+ title,
+ animeLists = $bindable(),
+ user,
+ endTime,
+ lastUpdatedMedia = $bindable(),
+ completed = false,
+ previousAnimeList = $bindable(),
+ pendingUpdate = $bindable(),
+ upcoming = false,
+ notYetReleased = false,
+ dummy = false
+ }: Props = $props();
let keyCacher: NodeJS.Timeout;
let totalEpisodeDueCount = media
@@ -109,60 +126,64 @@
/>
{#if media.length === 0}
- No anime to display. <button on:click={() => (animeLists = cleanCache(user, $identity))}>
+ No anime to display. <button onclick={() => (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} />
+ {#snippet title({ title: anime, progress })}
+ <div >
+ {#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;' : ''}
+ onclick={() => 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}
- {:else}
- <AiringTime originalAnime={anime} upcoming={true} />
- {/if}
- </div>
+ </div>
+ {/snippet}
</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}]
+ {#snippet information({ title: anime, progress })}
+ <span >
+ {#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;' : ''}
+ onclick={() => 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} />
+ <AiringTime originalAnime={anime} upcoming={true} />
</span>
{/if}
- {:else}
- <span class:countdown={$settings.displayCountdownRightAligned}>
- <AiringTime originalAnime={anime} upcoming={true} />
- </span>
- {/if}
- </span>
+ </span>
+ {/snippet}
</CleanList>
{/if}
diff --git a/src/lib/List/Anime/CompletedAnimeList.svelte b/src/lib/List/Anime/CompletedAnimeList.svelte
index 76ec7207..a8cb6fd1 100644
--- a/src/lib/List/Anime/CompletedAnimeList.svelte
+++ b/src/lib/List/Anime/CompletedAnimeList.svelte
@@ -11,18 +11,22 @@
import identity from '$stores/identity';
import sampleAnime from '$lib/Data/Static/SampleMedia/anime.json';
- export let user: AniListAuthorisation = {
+ interface Props {
+ user?: AniListAuthorisation;
+ dummy?: boolean;
+ }
+
+ let { user = {
accessToken: '',
refreshToken: '',
expiresIn: 0,
tokenType: ''
- };
- export let dummy = false;
+ }, dummy = false }: Props = $props();
const { addNotification } = getNotificationsContext();
- let animeLists: Promise<Media[]>;
+ let animeLists: Promise<Media[]> = $state();
let startTime: number;
- let endTime: number;
+ let endTime: number = $state();
onMount(async () => {
startTime = performance.now();
diff --git a/src/lib/List/Anime/DueAnimeList.svelte b/src/lib/List/Anime/DueAnimeList.svelte
index 1453baeb..bbae319a 100644
--- a/src/lib/List/Anime/DueAnimeList.svelte
+++ b/src/lib/List/Anime/DueAnimeList.svelte
@@ -12,12 +12,16 @@
import locale from '$stores/locale';
import identity from '$stores/identity';
- export let user: AniListAuthorisation;
+ interface Props {
+ user: AniListAuthorisation;
+ }
+
+ let { user }: Props = $props();
const { addNotification } = getNotificationsContext();
- let animeLists: Promise<Media[]>;
+ let animeLists: Promise<Media[]> = $state();
let startTime: number;
- let endTime: number;
+ let endTime: number = $state();
const keyCacher = setInterval(() => {
startTime = performance.now();
diff --git a/src/lib/List/Anime/DueIndexColumn.svelte b/src/lib/List/Anime/DueIndexColumn.svelte
index 61f2a178..0f20a2a1 100644
--- a/src/lib/List/Anime/DueIndexColumn.svelte
+++ b/src/lib/List/Anime/DueIndexColumn.svelte
@@ -6,8 +6,12 @@
import ListTitle from '../ListTitle.svelte';
import AnimeList from '$lib/List/Anime/DueAnimeList.svelte';
- export let userIdentity: { id: number };
- export let user: AniListAuthorisation;
+ interface Props {
+ userIdentity: { id: number };
+ user: AniListAuthorisation;
+ }
+
+ let { userIdentity, user }: Props = $props();
</script>
<details open={!$settings.displayAnimeCollapsed} class="list list-due">
diff --git a/src/lib/List/Anime/PlaceholderList.svelte b/src/lib/List/Anime/PlaceholderList.svelte
index 1f701d79..2c9baab5 100644
--- a/src/lib/List/Anime/PlaceholderList.svelte
+++ b/src/lib/List/Anime/PlaceholderList.svelte
@@ -4,8 +4,12 @@
import ListTitle from '../ListTitle.svelte';
import type { Title } from '../mediaTitle';
- export let title: Title;
- export let count = 8;
+ interface Props {
+ title: Title;
+ count?: number;
+ }
+
+ let { title, count = 8 }: Props = $props();
</script>
<ListTitle {title} />
diff --git a/src/lib/List/Anime/UpcomingAnimeList.svelte b/src/lib/List/Anime/UpcomingAnimeList.svelte
index 7b01af86..4957cd45 100644
--- a/src/lib/List/Anime/UpcomingAnimeList.svelte
+++ b/src/lib/List/Anime/UpcomingAnimeList.svelte
@@ -1,4 +1,6 @@
<script lang="ts">
+ import { run } from 'svelte/legacy';
+
import { mediaListCollection, Type, type Media } from '$lib/Data/AniList/media';
import type { AniListAuthorisation } from '$lib/Data/AniList/identity';
import { onMount } from 'svelte';
@@ -13,12 +15,16 @@
import { injectAiringTime } from '$lib/Media/Anime/Airing/Subtitled/match';
import revalidateAnime from '$stores/revalidateAnime';
- export let user: AniListAuthorisation;
+ interface Props {
+ user: AniListAuthorisation;
+ }
+
+ let { user }: Props = $props();
const { addNotification } = getNotificationsContext();
- let animeLists: Promise<Media[]>;
+ let animeLists: Promise<Media[]> = $state();
let startTime: number;
- let endTime: number;
+ let endTime: number = $state();
onMount(async () => {
startTime = performance.now();
@@ -74,7 +80,7 @@
return upcomingAnime;
};
- $: {
+ run(() => {
if ($revalidateAnime) {
$revalidateAnime = false;
$lastPruneTimes.anime = -1;
@@ -83,7 +89,7 @@
notificationType: 'Upcoming Episodes'
});
}
- }
+ });
</script>
<AnimeList
@@ -96,7 +102,7 @@
/>
{#if $settings.displayPlannedAnime}
- <p />
+ <p></p>
<AnimeList
{endTime}
diff --git a/src/lib/List/CleanGrid.svelte b/src/lib/List/CleanGrid.svelte
index ec93a685..774303aa 100644
--- a/src/lib/List/CleanGrid.svelte
+++ b/src/lib/List/CleanGrid.svelte
@@ -7,11 +7,13 @@
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;
+ let {
+ media = [] as Media[],
+ dummy = false,
+ type = 'anime' as 'anime' | 'manga',
+ upcoming = false,
+ notYetReleased = false
+ } = $props();
let uniqueID = new Date().getTime();
</script>
diff --git a/src/lib/List/CleanList.svelte b/src/lib/List/CleanList.svelte
index 47811932..46946973 100644
--- a/src/lib/List/CleanList.svelte
+++ b/src/lib/List/CleanList.svelte
@@ -5,11 +5,23 @@
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;
+ interface Props {
+ media: Media[];
+ type: 'anime' | 'manga';
+ upcoming?: boolean;
+ notYetReleased?: boolean;
+ lastUpdatedMedia: number;
+ information?: import('svelte').Snippet<[any]>;
+ }
+
+ let {
+ media,
+ type,
+ upcoming = false,
+ notYetReleased = false,
+ lastUpdatedMedia,
+ information
+ }: Props = $props();
</script>
<ul>
@@ -32,7 +44,7 @@
href={$settings.displayCopyMediaTitleNotLink
? '#'
: outboundLink(title, type, $settings.displayOutboundLinksTo)}
- on:click={(e) => {
+ onclick={(e) => {
if ($settings.displayCopyMediaTitleNotLink) {
e.preventDefault();
@@ -54,7 +66,7 @@
[<a href={`https://anilist.co/${type}/${title.id}/social`} target="_blank">S</a>]
{/if}
- <slot name="information" {progress} {title} />
+ {@render information?.({ progress, title, })}
</span>
</li>
{/if}
diff --git a/src/lib/List/ListTitle.svelte b/src/lib/List/ListTitle.svelte
index 21013b52..cccc5731 100644
--- a/src/lib/List/ListTitle.svelte
+++ b/src/lib/List/ListTitle.svelte
@@ -2,15 +2,28 @@
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 = {
+ interface Props {
+ time?: number | undefined;
+ count?: any;
+ title?: Title;
+ progress?: undefined | number;
+ hideTime?: boolean;
+ hideCount?: boolean;
+ children?: import('svelte').Snippet;
+ }
+
+ let {
+ time = undefined,
+ count = -1337,
+ title = {
title: 'Media List',
hint: 'This is a media list.'
- };
- export let progress: undefined | number = undefined;
- export let hideTime = false;
- export let hideCount = false;
+ },
+ progress = undefined,
+ hideTime = false,
+ hideCount = false,
+ children
+ }: Props = $props();
</script>
<summary>
@@ -23,7 +36,7 @@
{#if !hideTime}
<small class="opaque">{time ? time.toFixed(3) : '...'}s</small>
{/if}
- <slot />
+ {@render children?.()}
{#if progress !== undefined}
<button class="badge unclickable-button button-badge badge-info">
{progress.toFixed(0)}%
diff --git a/src/lib/List/Manga/CleanMangaList.svelte b/src/lib/List/Manga/CleanMangaList.svelte
index dfaa187c..ec1a3d83 100644
--- a/src/lib/List/Manga/CleanMangaList.svelte
+++ b/src/lib/List/Manga/CleanMangaList.svelte
@@ -14,22 +14,37 @@
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: (
+ interface Props {
+ media: Media[];
+ cleanCache: () => void;
+ endTime: number;
+ lastUpdatedMedia: number;
+ 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;
+ pendingUpdate: number | null;
+ due: boolean;
+ rateLimited: boolean;
+ authorised: boolean;
+ dummy?: boolean;
+ }
- let serviceStatusResponse: Promise<Response>;
+ let {
+ media,
+ cleanCache,
+ endTime,
+ lastUpdatedMedia,
+ updateMedia,
+ pendingUpdate,
+ due,
+ rateLimited,
+ authorised,
+ dummy = false
+ }: Props = $props();
+
+ let serviceStatusResponse: Promise<Response> = $state();
onMount(() => {
serviceStatusResponse = fetch(proxy('https://api.mangadex.org/ping'));
@@ -58,7 +73,7 @@
<button
class="small-button"
title="Force a full refresh"
- on:click={cleanCache}
+ onclick={cleanCache}
data-umami-event="Force Refresh Manga">Refresh</button
>
{/if}
@@ -90,18 +105,18 @@
{#if media.length === 0 && !rateLimited}
{#if rateLimited}
- <p />
+ <p></p>
{/if}
<p>
- No manga to display. <button on:click={cleanCache} data-umami-event="Force Refresh No Manga"
+ No manga to display. <button onclick={cleanCache} data-umami-event="Force Refresh No Manga"
>Force refresh</button
>
</p>
<span>
Don't read manga? <button
- on:click={() => ($settings.disableManga = true)}
+ onclick={() => ($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>.
@@ -110,57 +125,61 @@
{#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}
+ {#snippet title({ title: manga, progress })}
+ <div >
+ {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;' : ''}
+ onclick={() => 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>
+ {#if volumes !== null && (volumeProgress || 0) < volumes}
+ <span style="color: lightcoral;">
+ Vol. {volumeProgress} &#8594; {volumes}
+ </span>
+ {/if}
+ {/await}
+ {/if}
+ </div>
+ {/snippet}
</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}
+ {#snippet information({ title: manga, progress })}
+ <span >
+ <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;' : ''}
+ onclick={() => 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>
+ {#if volumes !== null && (volumeProgress || 0) < volumes}
+ <span style="color: lightcoral;">
+ Vol. {volumeProgress} &#8594; {volumes}
+ </span>
+ {/if}
+ {/await}
+ {/if}
+ </span>
+ {/snippet}
</CleanList>
{/if}
diff --git a/src/lib/List/Manga/MangaListTemplate.svelte b/src/lib/List/Manga/MangaListTemplate.svelte
index 1303419f..107ff47d 100644
--- a/src/lib/List/Manga/MangaListTemplate.svelte
+++ b/src/lib/List/Manga/MangaListTemplate.svelte
@@ -21,28 +21,37 @@
import { browser } from '$app/environment';
import identity from '$stores/identity';
- export let user: AniListAuthorisation = {
+ interface Props {
+ user?: AniListAuthorisation;
+ displayUnresolved: boolean;
+ due: boolean;
+ dummy?: any;
+ }
+
+ let {
+ user = {
accessToken: '',
refreshToken: '',
expiresIn: 0,
tokenType: ''
- };
- export let displayUnresolved: boolean;
- export let due: boolean;
- export let dummy = $settings.debugDummyLists || false;
+ },
+ displayUnresolved,
+ due,
+ dummy = $settings.debugDummyLists || false
+ }: Props = $props();
const { addNotification } = getNotificationsContext();
const authorised = authorisedJson.includes($identity.id);
- let mangaLists: Promise<Media[]>;
+ let mangaLists: Promise<Media[]> = $state();
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;
+ let endTime: number = $state();
+ let lastUpdatedMedia = $state(-1);
+ let previousMangaList: Media[] = $state();
+ let pendingUpdate: number | null = $state(null);
+ let progress = $state(0);
+ let rateLimited = $state(false);
+ let forceFlag = $state(false);
+ let lastListSize = $state(5);
const keyCacher = setInterval(() => {
startTime = performance.now();
@@ -270,7 +279,7 @@
<button
data-umami-event="Force Refresh Manga"
title="Force a full refresh"
- on:click={() => {
+ onclick={() => {
cleanCache();
forceFlag = true;
@@ -317,7 +326,7 @@
<button
data-umami-event="Force Refresh Manga"
title="Force a full refresh"
- on:click={() => {
+ onclick={() => {
cleanCache();
forceFlag = true;
@@ -349,7 +358,7 @@
<button
data-umami-event="Force Refresh Manga"
title="Force a full refresh"
- on:click={() => {
+ onclick={() => {
cleanCache();
forceFlag = true;
diff --git a/src/lib/List/MediaTitleDisplay.svelte b/src/lib/List/MediaTitleDisplay.svelte
index 6a886704..0fd7cf17 100644
--- a/src/lib/List/MediaTitleDisplay.svelte
+++ b/src/lib/List/MediaTitleDisplay.svelte
@@ -6,10 +6,19 @@
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;
+ interface Props {
+ title: MediaTitle;
+ abbreviate?: boolean;
+ abbreviateTo?: number;
+ tooltip?: boolean;
+ }
+
+ let {
+ title,
+ abbreviate = false,
+ abbreviateTo = 20,
+ tooltip = false
+ }: Props = $props();
const compressToBase64 = (string: string) => LZString.compressToBase64(string);
</script>
diff --git a/src/lib/Loading/Ellipsis.svelte b/src/lib/Loading/Ellipsis.svelte
index ba1f30b8..7ee9dcfb 100644
--- a/src/lib/Loading/Ellipsis.svelte
+++ b/src/lib/Loading/Ellipsis.svelte
@@ -1,10 +1,14 @@
<script lang="ts">
- export let colour = 'var(--fg)';
+ interface Props {
+ colour?: string;
+ }
+
+ let { colour = 'var(--fg)' }: Props = $props();
</script>
<div class="ellipsis" style={`--loader-colour: ${colour};`}>
{#each Array.from({ length: 4 }) as _}
- <div />
+ <div></div>
{/each}
</div>
diff --git a/src/lib/Loading/Grid.svelte b/src/lib/Loading/Grid.svelte
index 1a64b3e0..107b985b 100644
--- a/src/lib/Loading/Grid.svelte
+++ b/src/lib/Loading/Grid.svelte
@@ -1,10 +1,14 @@
<script lang="ts">
- export let colour = 'var(--fg)';
+ interface Props {
+ colour?: string;
+ }
+
+ let { colour = 'var(--fg)' }: Props = $props();
</script>
<div class="grid" style={`--loader-colour: ${colour};`}>
{#each Array.from({ length: 9 }) as _}
- <div />
+ <div></div>
{/each}
</div>
diff --git a/src/lib/Loading/Message.svelte b/src/lib/Loading/Message.svelte
index 5aabec9c..c940a452 100644
--- a/src/lib/Loading/Message.svelte
+++ b/src/lib/Loading/Message.svelte
@@ -4,12 +4,25 @@
import Grid from './Grid.svelte';
import Popup from '$lib/Layout/Popup.svelte';
- export let message: string | undefined = undefined;
- export let loader: 'ellipsis' | 'ripple' | 'grid' = 'ellipsis';
- export let colour = 'var(--fg)';
- export let slot = false;
- export let withReload = false;
- export let fullscreen = true;
+ interface Props {
+ message?: string | undefined;
+ loader?: 'ellipsis' | 'ripple' | 'grid';
+ colour?: string;
+ slot?: boolean;
+ withReload?: boolean;
+ fullscreen?: boolean;
+ children?: import('svelte').Snippet;
+ }
+
+ let {
+ message = undefined,
+ loader = 'ellipsis',
+ colour = 'var(--fg)',
+ slot = false,
+ withReload = false,
+ fullscreen = true,
+ children
+ }: Props = $props();
</script>
<Popup {fullscreen} locked>
@@ -29,10 +42,10 @@
{:else if slot}
<br />
- <slot />
+ {@render children?.()}
{#if withReload}
- Please <a href={'#'} on:click={() => location.reload()}>try again</a> later.
+ Please <a href={'#'} onclick={() => location.reload()}>try again</a> later.
{/if}
{/if}
</div>
diff --git a/src/lib/Loading/Ripple.svelte b/src/lib/Loading/Ripple.svelte
index 05d62bb5..20b1447a 100644
--- a/src/lib/Loading/Ripple.svelte
+++ b/src/lib/Loading/Ripple.svelte
@@ -1,10 +1,14 @@
<script lang="ts">
- export let colour = 'var(--fg)';
+ interface Props {
+ colour?: string;
+ }
+
+ let { colour = 'var(--fg)' }: Props = $props();
</script>
<div class="ripple" style={`--loader-colour: ${colour};`}>
- <div />
- <div />
+ <div></div>
+ <div></div>
</div>
<style lang="scss">
diff --git a/src/lib/Loading/Skeleton.svelte b/src/lib/Loading/Skeleton.svelte
index 3f39beec..5ca3789a 100644
--- a/src/lib/Loading/Skeleton.svelte
+++ b/src/lib/Loading/Skeleton.svelte
@@ -1,12 +1,25 @@
<script lang="ts">
- export let count = 3;
- export let width = '100%';
- export let height = '100px';
- export let card = true;
- export let grid = false;
- export let list = false;
- export let pad = false;
- export let bigCard = false;
+ interface Props {
+ count?: number;
+ width?: string;
+ height?: string;
+ card?: boolean;
+ grid?: boolean;
+ list?: boolean;
+ pad?: boolean;
+ bigCard?: boolean;
+ }
+
+ let {
+ count = 3,
+ width = '100%',
+ height = '100px',
+ card = true,
+ grid = false,
+ list = false,
+ pad = false,
+ bigCard = false
+ }: Props = $props();
</script>
{#if grid}
@@ -14,7 +27,7 @@
{#each Array(count) as _, i}
<div class={card ? `${bigCard ? 'card' : ''} card-small` : ''} style={`width: ${width};`}>
<div class="skeleton-container" style={`--i: ${i};`}>
- <div class="skeleton" style={`width: ${width}; height: ${height};`} />
+ <div class="skeleton" style={`width: ${width}; height: ${height};`}></div>
</div>
</div>
{/each}
@@ -26,12 +39,12 @@
style={`width: ${width}; ${list ? 'padding-top: .75em;' : ''}; --i: ${i};`}
>
<div class="skeleton-container">
- <div class="skeleton" style={`width: ${width}; height: ${height};`} />
+ <div class="skeleton" style={`width: ${width}; height: ${height};`}></div>
</div>
</div>
{#if !list && i < count - 1}
- <p />
+ <p></p>
{/if}
{/each}
{/if}
diff --git a/src/lib/MarkdownLink.svelte b/src/lib/MarkdownLink.svelte
index 731eb263..a4c62112 100644
--- a/src/lib/MarkdownLink.svelte
+++ b/src/lib/MarkdownLink.svelte
@@ -1,6 +1,10 @@
<script lang="ts">
- export let href: string;
- export let text: string;
+ interface Props {
+ href: string;
+ text: string;
+ }
+
+ let { href = $bindable(), text }: Props = $props();
try {
let url = new URL(href);
diff --git a/src/lib/Media/Anime/Airing/AiringTime.svelte b/src/lib/Media/Anime/Airing/AiringTime.svelte
index 53a39c39..b9b224d7 100644
--- a/src/lib/Media/Anime/Airing/AiringTime.svelte
+++ b/src/lib/Media/Anime/Airing/AiringTime.svelte
@@ -6,16 +6,20 @@
import tooltip from '$lib/Tooltip/tooltip';
import locale from '$stores/locale';
- export let originalAnime: Media;
- export let upcoming = false;
+ interface Props {
+ originalAnime: Media;
+ upcoming?: boolean;
+ }
+
+ let { originalAnime, upcoming = false }: Props = $props();
const anime = originalAnime;
- let opacity = 100;
- let timeFrame = '';
- let time = '';
+ let opacity = $state(100);
+ let timeFrame = $state('');
+ let time = $state('');
let nextEpisode = anime.nextAiringEpisode?.episode || 0;
- let few = true;
- let dateString = '';
+ let few = $state(true);
+ let dateString = $state('');
let updateInterval: NodeJS.Timeout;
onMount(() => {
diff --git a/src/lib/Media/Cover/HoverCover.svelte b/src/lib/Media/Cover/HoverCover.svelte
index 51cbf5d2..f27be993 100644
--- a/src/lib/Media/Cover/HoverCover.svelte
+++ b/src/lib/Media/Cover/HoverCover.svelte
@@ -2,8 +2,12 @@
import settings from '$stores/settings';
import type { HoverCoverResponse } from './hoverCover';
- export let options: HoverCoverResponse;
- export let width = 250;
+ interface Props {
+ options: HoverCoverResponse;
+ width?: number;
+ }
+
+ let { options, width = 250 }: Props = $props();
</script>
{#if options.hovering}
diff --git a/src/lib/Notification/Notification.svelte b/src/lib/Notification/Notification.svelte
index 6764f46e..f2b6ac44 100644
--- a/src/lib/Notification/Notification.svelte
+++ b/src/lib/Notification/Notification.svelte
@@ -1,12 +1,18 @@
<script lang="ts">
+ import { preventDefault } from 'svelte/legacy';
+
import settings from '$stores/settings';
import { onMount } from 'svelte';
- export let notification: { [key: string]: any } = {};
- export let onRemove: () => void = () => {
+ interface Props {
+ notification?: { [key: string]: any };
+ onRemove?: () => void;
+ removed?: boolean;
+ }
+
+ let { notification = {}, onRemove = () => {
return;
- };
- export let removed = false;
+ }, removed = $bindable(false) }: Props = $props();
onMount(() => setTimeout(remove, notification.duration));
@@ -21,20 +27,20 @@
<div
id="notification-container"
class={removed ? 'fade-out' : 'fade-in'}
- on:click={remove}
- on:keydown={() => {
+ onclick={remove}
+ onkeydown={() => {
return;
}}
role="button"
tabindex="0"
>
{#if notification.description}
- <!-- svelte-ignore a11y-no-noninteractive-element-interactions -->
+ <!-- svelte-ignore a11y_no_noninteractive_element_interactions -->
<details
open
id="notification"
- on:click|preventDefault={(event) => event.preventDefault()}
- on:keydown={() => {
+ onclick={preventDefault((event) => event.preventDefault())}
+ onkeydown={() => {
return;
}}
>
diff --git a/src/lib/Reader/Chapters/MangaDex.svelte b/src/lib/Reader/Chapters/MangaDex.svelte
index 44c0d8f6..4be69d02 100644
--- a/src/lib/Reader/Chapters/MangaDex.svelte
+++ b/src/lib/Reader/Chapters/MangaDex.svelte
@@ -1,5 +1,9 @@
<script lang="ts">
- export let data: any;
+ interface Props {
+ data: any;
+ }
+
+ let { data }: Props = $props();
</script>
<ul>
diff --git a/src/lib/Reader/Chapters/Rawkuma.svelte b/src/lib/Reader/Chapters/Rawkuma.svelte
index 720c65d5..671a0572 100644
--- a/src/lib/Reader/Chapters/Rawkuma.svelte
+++ b/src/lib/Reader/Chapters/Rawkuma.svelte
@@ -2,7 +2,11 @@
import { getChaptersFromText } from '$lib/Data/Manga/raw';
import { onMount } from 'svelte';
- export let data: string;
+ interface Props {
+ data: string;
+ }
+
+ let { data }: Props = $props();
onMount(() => {
console.log();
diff --git a/src/lib/Schedule/CoverBypass.svelte b/src/lib/Schedule/CoverBypass.svelte
index e94ddd40..72b84eb8 100644
--- a/src/lib/Schedule/CoverBypass.svelte
+++ b/src/lib/Schedule/CoverBypass.svelte
@@ -8,10 +8,19 @@
import { abbreviate } from '$lib/Utility/string';
import settings from '$stores/settings';
- export let media: Media | null;
- export let entry: SubsPleaseEpisode;
- export let cover = true;
- export let showTooltip = true;
+ interface Props {
+ media: Media | null;
+ entry: SubsPleaseEpisode;
+ cover?: boolean;
+ showTooltip?: boolean;
+ }
+
+ let {
+ media,
+ entry,
+ cover = true,
+ showTooltip = true
+ }: Props = $props();
const abbreviateTo = 40;
diff --git a/src/lib/Schedule/Crunchyroll.svelte b/src/lib/Schedule/Crunchyroll.svelte
index d22cff42..8fc0e95a 100644
--- a/src/lib/Schedule/Crunchyroll.svelte
+++ b/src/lib/Schedule/Crunchyroll.svelte
@@ -29,7 +29,7 @@
(media) => media.day === 'soon'
);
- $: columnCount = Math.ceil(Object.keys(days).length / 2);
+ let columnCount = $derived(Math.ceil(Object.keys(days).length / 2));
const ordinalSuffix = (i: number) => {
const j = i % 10;
@@ -61,7 +61,7 @@
</details>
</div>
- <p />
+ <p></p>
{/each}
<div class="card day">
@@ -76,7 +76,7 @@
</details>
</div>
- <p />
+ <p></p>
<div class="card day">
<details open class="details-unstyled">
diff --git a/src/lib/Schedule/Days.svelte b/src/lib/Schedule/Days.svelte
index f7a49029..51558240 100644
--- a/src/lib/Schedule/Days.svelte
+++ b/src/lib/Schedule/Days.svelte
@@ -18,15 +18,24 @@
import Skeleton from '$lib/Loading/Skeleton.svelte';
import Error from '$lib/Error/RateLimited.svelte';
- export let subsPlease: SubsPlease;
- export let scheduledMedia: Partial<Media[]>;
- export let forceListMode = false;
- export let user;
+ interface Props {
+ subsPlease: SubsPlease;
+ scheduledMedia: Partial<Media[]>;
+ forceListMode?: boolean;
+ user: any;
+ }
+
+ let {
+ subsPlease,
+ scheduledMedia,
+ forceListMode = false,
+ user
+ }: Props = $props();
const urlParameters = browser ? new URLSearchParams(window.location.search) : null;
let day: string | null = parseOrDefault(urlParameters, 'day', null);
- let mediaListPromise: Promise<Media[]>;
+ let mediaListPromise: Promise<Media[]> = $state();
onMount(async () => {
if (user === undefined || $identity.id === -2) mediaListPromise = Promise.resolve([]);
diff --git a/src/lib/Settings/Categories/Attributions.svelte b/src/lib/Settings/Categories/Attributions.svelte
index 250b08c3..28f69a74 100644
--- a/src/lib/Settings/Categories/Attributions.svelte
+++ b/src/lib/Settings/Categories/Attributions.svelte
@@ -34,7 +34,7 @@
</li> -->
</ul>
-<p />
+<p></p>
<details open class="card-clear">
<summary>Outbound Link Disclaimer</summary>
diff --git a/src/lib/Settings/Categories/Cache.svelte b/src/lib/Settings/Categories/Cache.svelte
index e65b8081..df0165d5 100644
--- a/src/lib/Settings/Categories/Cache.svelte
+++ b/src/lib/Settings/Categories/Cache.svelte
@@ -11,7 +11,7 @@
<a href="https://due.moe">due.moe</a>'s site data will clear these caches too.
</small>
-<p />
+<p></p>
Re-cache AniList media lists every
<input
@@ -22,7 +22,7 @@ Re-cache AniList media lists every
max="60"
placeholder="30"
size="1"
- on:change={() =>
+ onchange={() =>
($settings.cacheMinutes < 1 && ($settings.cacheMinutes = 1)) ||
($settings.cacheMinutes > 60 && ($settings.cacheMinutes = 60))}
/>
@@ -39,7 +39,7 @@ Re-cache manga data every
max="1440"
placeholder="120"
size="2"
- on:change={() =>
+ onchange={() =>
($settings.cacheMangaMinutes < 5 && ($settings.cacheMangaMinutes = 5)) ||
($settings.cacheMangaMinutes > 1440 && ($settings.cacheMangaMinutes = 1440))}
/>
diff --git a/src/lib/Settings/Categories/Calculation.svelte b/src/lib/Settings/Categories/Calculation.svelte
index 4a202fff..1f6eeaa1 100644
--- a/src/lib/Settings/Categories/Calculation.svelte
+++ b/src/lib/Settings/Categories/Calculation.svelte
@@ -75,7 +75,7 @@
{#if !$settings.calculateGuessingDisabled}
<br />
- <select bind:value={$settings.calculateGuessMethod} on:change={pruneAllManga}>
+ <select bind:value={$settings.calculateGuessMethod} onchange={pruneAllManga}>
<option value="mode">Mode (fast, moderate to low accuracy)</option>
<option value="median">Median (moderate speed, high accuracy, recommended)</option>
<option value="iqr_median">Interquartile Range with Median (slower, high accuracy)</option>
diff --git a/src/lib/Settings/Categories/Debug.svelte b/src/lib/Settings/Categories/Debug.svelte
index 79e18c03..00c5852e 100644
--- a/src/lib/Settings/Categories/Debug.svelte
+++ b/src/lib/Settings/Categories/Debug.svelte
@@ -12,7 +12,7 @@
<SettingCheckboxToggle setting="debugDummyLists" text={$locale().debug.dummyLists} />
<button
- on:click={() => {
+ onclick={() => {
localStorage.removeItem('anime');
localStorage.removeItem('manga');
addNotification(
@@ -23,10 +23,10 @@
}}>{$locale().debug.clearCaches}</button
>
-<p />
+<p></p>
<button
- on:click={() => {
+ onclick={() => {
settings.reset();
addNotification(
options({
@@ -41,10 +41,10 @@
{$locale().debug.resetAllSettings.hint}
</SettingHint>
-<p />
+<p></p>
<button
- on:click={() => {
+ onclick={() => {
localStorage.clear();
addNotification(
options({
diff --git a/src/lib/Settings/Categories/Display.svelte b/src/lib/Settings/Categories/Display.svelte
index 3b572b7b..a83157fb 100644
--- a/src/lib/Settings/Categories/Display.svelte
+++ b/src/lib/Settings/Categories/Display.svelte
@@ -85,7 +85,7 @@
<SettingHint lineBreak>
Media where either the next episode's release date is unknown or the chapter count could not be
resolved is considered unresolved.
- <p />
+ <p></p>
<span>
Additionally, you hard exclude specific media from <a href={root('/')}>due.moe</a> on AniList.
To exclude any media from being included in <b>any</b> <a href={root('/')}>due.moe</a>
@@ -107,7 +107,7 @@
</span>
</SettingHint>
-<p />
+<p></p>
<b>{$locale().settings.display.categories.hidePanels}</b><br />
<SettingCheckboxToggle
@@ -160,7 +160,7 @@
}}
/>
-<p />
+<p></p>
<b>{$locale().settings.display.categories.collapsePanelsByDefault}</b><br />
<SettingCheckboxToggle
@@ -175,7 +175,7 @@
/>
<SettingCheckboxToggle setting="displayMangaCollapsed" text={$locale().settings.media.manga} />
-<p />
+<p></p>
<b>{$locale().settings.display.categories.motionAndAccessibility.title}</b><br />
<SettingCheckboxToggle
@@ -215,7 +215,7 @@
</button>
{/if}
-<p />
+<p></p>
<SettingCheckboxToggle
setting="displayAniListNotifications"
@@ -242,7 +242,7 @@
</SettingHint>
</SettingCheckboxToggle>
-<p />
+<p></p>
<b>{$locale().settings.display.categories.dateAndTime.title}</b><br />
<SettingCheckboxToggle
@@ -262,7 +262,7 @@
text={$locale().settings.display.categories.dateAndTime.fields.abbreviateCountdown}
/>
-<p />
+<p></p>
<SettingCheckboxToggle
setting="displayDisableLastActivityWarning"
@@ -277,7 +277,7 @@
</SettingHint>
</SettingCheckboxToggle>
-<p />
+<p></p>
<b>Show lists with media covers instead of text</b><br />
<SettingCheckboxToggle setting="displayCoverModeAnime" text="Anime" lineBreak={false} />
@@ -297,7 +297,7 @@
min="50"
placeholder="116.609"
size="3"
- on:change={() => {
+ onchange={() => {
if ($settings.displayCoverWidth === null) {
$settings.displayCoverWidth = 116.609;
@@ -313,7 +313,7 @@
<br />
{/if}
-<p />
+<p></p>
<b>{$locale().settings.display.categories.media.title}</b><br />
<SettingCheckboxToggle
@@ -357,7 +357,7 @@
<br />
{/if}
-<p />
+<p></p>
<select bind:value={$settings.displayOutboundLinksTo}>
<option value="anilist">AniList</option>
@@ -390,7 +390,7 @@
<br />
-<select bind:value={$settings.displayAoButa} on:change={onHelperChange}>
+<select bind:value={$settings.displayAoButa} onchange={onHelperChange}>
<option value="mai">{$locale().settings.display.categories.helper.options.mai}</option>
<option value="mai_2">{$locale().settings.display.categories.helper.options.mai} #2</option>
<option value="nodoka">{$locale().settings.display.categories.helper.options.nodoka}</option>
diff --git a/src/lib/Settings/Categories/RSSFeeds.svelte b/src/lib/Settings/Categories/RSSFeeds.svelte
index a225b137..9ced917b 100644
--- a/src/lib/Settings/Categories/RSSFeeds.svelte
+++ b/src/lib/Settings/Categories/RSSFeeds.svelte
@@ -6,13 +6,17 @@
import SettingHint from '../SettingHint.svelte';
import tooltip from '$lib/Tooltip/tooltip';
- export let user: any;
+ interface Props {
+ user: any;
+ }
+
+ let { user }: Props = $props();
const { addNotification } = getNotificationsContext();
</script>
<button
- on:click={() => {
+ onclick={() => {
addNotification(
options({
heading: 'RSS feed URL copied to clipboard'
@@ -31,7 +35,7 @@ Your AniList notifications RSS feed URL
<SettingHint lineBreak>
This <a
href={'#'}
- on:click={(e) => e.preventDefault()}
+ onclick={(e) => e.preventDefault()}
target="_blank"
title={$locale().settings.rssFeeds.tooltips.rss}
use:tooltip
diff --git a/src/lib/Settings/Categories/SettingSync.svelte b/src/lib/Settings/Categories/SettingSync.svelte
index 39e62954..24a35c79 100644
--- a/src/lib/Settings/Categories/SettingSync.svelte
+++ b/src/lib/Settings/Categories/SettingSync.svelte
@@ -13,7 +13,7 @@
{#if !$settings.settingsSync}
<button
- on:click={() => {
+ onclick={() => {
$settings.settingsSync = true;
fetch(root(`/api/configuration?id=${$identity.id}`)).then((response) => {
@@ -50,9 +50,9 @@
<SettingHint lineBreak>
{$locale().settings.settingsSync.buttons.pull.hint}
</SettingHint>
- <p />
+ <p></p>
<button
- on:click={() => {
+ onclick={() => {
$settings.settingsSync = true;
fetch(root(`/api/configuration`), {
@@ -76,7 +76,7 @@
</SettingHint>
{:else}
<button
- on:click={() => {
+ onclick={() => {
$settings.settingsSync = false;
addNotification(
@@ -89,7 +89,7 @@
{$locale().settings.settingsSync.buttons.disable}
</button>
<button
- on:click={() => {
+ onclick={() => {
fetch(root(`/api/configuration?id=${$identity.id}`), {
method: 'DELETE'
}).then((response) => {
@@ -108,7 +108,7 @@
{$locale().settings.settingsSync.buttons.delete}
</button>
- <p />
+ <p></p>
<b>Last Push</b>: {$locale().dateFormatter($settingsSyncTimes.lastPush)}
<br />
diff --git a/src/lib/Settings/Category.svelte b/src/lib/Settings/Category.svelte
index a5b3e211..aef86d77 100644
--- a/src/lib/Settings/Category.svelte
+++ b/src/lib/Settings/Category.svelte
@@ -1,8 +1,19 @@
<script lang="ts">
- export let title = '';
- export let id = title.toLowerCase();
- export let open = true;
- export let newLine = true;
+ interface Props {
+ title?: string;
+ id?: any;
+ open?: boolean;
+ newLine?: boolean;
+ children?: import('svelte').Snippet;
+ }
+
+ let {
+ title = '',
+ id = title.toLowerCase(),
+ open = true,
+ newLine = true,
+ children
+ }: Props = $props();
</script>
<details {open} {id}>
@@ -10,9 +21,9 @@
<summary>{title}</summary>
{/if}
- <slot />
+ {@render children?.()}
</details>
{#if newLine}
- <p />
+ <p></p>
{/if}
diff --git a/src/lib/Settings/SettingCheckboxToggle.svelte b/src/lib/Settings/SettingCheckboxToggle.svelte
index 6a16edec..85dbfcdc 100644
--- a/src/lib/Settings/SettingCheckboxToggle.svelte
+++ b/src/lib/Settings/SettingCheckboxToggle.svelte
@@ -1,4 +1,6 @@
<script lang="ts">
+ import { run } from 'svelte/legacy';
+
import settings, { type Settings } from '$stores/settings';
type BooleanSettingsKeys<T> = {
@@ -6,19 +8,37 @@
};
type SettingsBooleanKeys = BooleanSettingsKeys<Settings>;
- export let sectionBreak = false;
- export let disabled = false;
- export let text: string | (() => string);
- export let setting: SettingsBooleanKeys[keyof SettingsBooleanKeys];
- export let lineBreak = true;
- export let onChange: () => void = () => {
- return;
- };
- export let invert = false;
- export let id: string | null = null;
-
- $: checked = setting ? (invert ? !$settings[setting] : $settings[setting]) : false;
- $: field = text instanceof Function ? text() : text;
+ interface Props {
+ sectionBreak?: boolean;
+ disabled?: boolean;
+ text: string | (() => string);
+ setting: SettingsBooleanKeys[keyof SettingsBooleanKeys];
+ lineBreak?: boolean;
+ onChange?: () => void;
+ invert?: boolean;
+ id?: string | null;
+ children?: import('svelte').Snippet;
+ }
+
+ let {
+ sectionBreak = false,
+ disabled = false,
+ text,
+ setting,
+ lineBreak = true,
+ onChange = () => {
+ return;
+ },
+ invert = false,
+ id = null,
+ children
+ }: Props = $props();
+
+ let checked = $state(false);
+ run(() => {
+ checked = setting ? (invert ? !$settings[setting] : $settings[setting]) : false;
+ });
+ let field = $derived(text instanceof Function ? text() : text);
// const toggler = (key: keyof Settings) => [
// () =>
@@ -53,11 +73,11 @@
};
</script>
-<input type="checkbox" on:change={check} bind:checked {id} />
+<input type="checkbox" onchange={check} bind:checked {id} />
<span
- on:click={flip}
- on:keydown={() => {
+ onclick={flip}
+ onkeydown={() => {
return;
}}
role="button"
@@ -72,12 +92,12 @@
{/if}
</span>
-<slot />
+{@render children?.()}
{#if lineBreak}
<br />
{/if}
{#if sectionBreak}
- <p />
+ <p></p>
{/if}
diff --git a/src/lib/Settings/SettingHint.svelte b/src/lib/Settings/SettingHint.svelte
index f82f061c..c5a9072f 100644
--- a/src/lib/Settings/SettingHint.svelte
+++ b/src/lib/Settings/SettingHint.svelte
@@ -1,5 +1,10 @@
<script lang="ts">
- export let lineBreak = false;
+ interface Props {
+ lineBreak?: boolean;
+ children?: import('svelte').Snippet;
+ }
+
+ let { lineBreak = false, children }: Props = $props();
</script>
{#if lineBreak}
@@ -7,5 +12,5 @@
{/if}
<small style="opacity: 80%;">
- <slot />
+ {@render children?.()}
</small>
diff --git a/src/lib/Settings/SettingToggle.svelte b/src/lib/Settings/SettingToggle.svelte
index 0d177b50..0d3b893a 100644
--- a/src/lib/Settings/SettingToggle.svelte
+++ b/src/lib/Settings/SettingToggle.svelte
@@ -1,16 +1,28 @@
<script lang="ts">
import settings, { type Settings } from '$stores/settings';
- export let setting: keyof Settings;
- export let on = '';
- export let off = '';
- export let sectionBreak = false;
- export let disabled = false;
+ interface Props {
+ setting: keyof Settings;
+ on?: string;
+ off?: string;
+ sectionBreak?: boolean;
+ disabled?: boolean;
+ children?: import('svelte').Snippet;
+ }
+
+ let {
+ setting,
+ on = '',
+ off = '',
+ sectionBreak = false,
+ disabled = false,
+ children
+ }: Props = $props();
</script>
<a
href={'#'}
- on:click={() =>
+ onclick={() =>
disabled
? {}
: $settings[setting]
@@ -20,16 +32,16 @@
{#if disabled}
<strike>
{$settings[setting] ? on : off}
- <slot />
+ {@render children?.()}
</strike>
{:else}
{$settings[setting] ? on : off}
- <slot />
+ {@render children?.()}
{/if}
</a>
<br />
{#if sectionBreak}
- <p />
+ <p></p>
{/if}
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">
diff --git a/src/lib/Tooltip/LinkedTooltip.svelte b/src/lib/Tooltip/LinkedTooltip.svelte
index 6e468cd6..f4546d4c 100644
--- a/src/lib/Tooltip/LinkedTooltip.svelte
+++ b/src/lib/Tooltip/LinkedTooltip.svelte
@@ -2,23 +2,42 @@
import tooltipPosition from '$stores/tooltipPosition';
import { fade } from 'svelte/transition';
- export let id: string | undefined = undefined;
- export let pin: string | undefined = undefined;
- export let content: string;
- export let disable: boolean = false;
- export let pinPosition: 'top' | 'bottom' | 'left' | 'right' = 'top';
- export let offset = 10;
- export let tooltipTransitionTime = 200;
- export let tooltipHideDelay = 10;
- export let debounceDelay = 100;
- export let tooltipOpacityTransitionTime = 200;
- export let relative = false;
- export let ignoreAnchorStyling = false;
-
- let tooltipDiv: HTMLDivElement | null = null;
+ interface Props {
+ id?: string | undefined;
+ pin?: string | undefined;
+ content: string;
+ disable?: boolean;
+ pinPosition?: 'top' | 'bottom' | 'left' | 'right';
+ offset?: number;
+ tooltipTransitionTime?: number;
+ tooltipHideDelay?: number;
+ debounceDelay?: number;
+ tooltipOpacityTransitionTime?: number;
+ relative?: boolean;
+ ignoreAnchorStyling?: boolean;
+ children?: import('svelte').Snippet;
+ }
+
+ let {
+ id = undefined,
+ pin = undefined,
+ content,
+ disable = false,
+ pinPosition = 'top',
+ offset = 10,
+ tooltipTransitionTime = 200,
+ tooltipHideDelay = 10,
+ debounceDelay = 100,
+ tooltipOpacityTransitionTime = 200,
+ relative = false,
+ ignoreAnchorStyling = false,
+ children
+ }: Props = $props();
+
+ let tooltipDiv: HTMLDivElement | null = $state(null);
let hideTimeout: number | null = null;
let debounceTimer: number | null = null;
- let opacity = 0;
+ let opacity = $state(0);
const createTooltip = () => {
if (!tooltipDiv) {
@@ -202,12 +221,12 @@
<span
{id}
- on:mouseenter={handleMouseEnter}
- on:mousemove={handleMouseMove}
- on:mouseleave={handleMouseLeave}
+ onmouseenter={handleMouseEnter}
+ onmousemove={handleMouseMove}
+ onmouseleave={handleMouseLeave}
role="tooltip"
>
- <slot />
+ {@render children?.()}
</span>
{#if tooltipDiv}
diff --git a/src/lib/User/BadgeWall/AWC.svelte b/src/lib/User/BadgeWall/AWC.svelte
index 1cf82a1b..2bd21001 100644
--- a/src/lib/User/BadgeWall/AWC.svelte
+++ b/src/lib/User/BadgeWall/AWC.svelte
@@ -5,10 +5,19 @@
import FallbackBadge from './FallbackBadge.svelte';
import './badges.css';
- export let awcPromise: Promise<Response>;
- export let categoryFilter: string | null;
- export let isOwner: boolean;
- export let preferences: Preferences;
+ interface Props {
+ awcPromise: Promise<Response>;
+ categoryFilter: string | null;
+ isOwner: boolean;
+ preferences: Preferences;
+ }
+
+ let {
+ awcPromise,
+ categoryFilter,
+ isOwner,
+ preferences
+ }: Props = $props();
const awcBadgesGrouped = (awcResponse: string): AWCBadgesGroup[] => {
return Array.from(
@@ -72,7 +81,7 @@
{group.group}
</summary>
- <p />
+ <p></p>
<div class="badges">
{#each group.badges as badge, index}
@@ -92,7 +101,7 @@
</div>
</details>
- <p />
+ <p></p>
{/each}
{/if}
{/await}
diff --git a/src/lib/User/BadgeWall/BadgePreview.svelte b/src/lib/User/BadgeWall/BadgePreview.svelte
index 7a54cbc4..5daccdd2 100644
--- a/src/lib/User/BadgeWall/BadgePreview.svelte
+++ b/src/lib/User/BadgeWall/BadgePreview.svelte
@@ -1,4 +1,6 @@
<script lang="ts">
+ import { run } from 'svelte/legacy';
+
import { thumbnail } from '$lib/Utility/image';
import type { Badge } from '$lib/Database/SB/User/badges';
import { cdn } from '$lib/Utility/image';
@@ -8,16 +10,26 @@
import root from '$lib/Utility/root';
import ParallaxImage from '$lib/Image/ParallaxImage.svelte';
- export let selectedBadge: Badge | undefined;
- export let onNext: () => void = () => {};
- export let onPrevious: () => void = () => {};
- export let hasNext: boolean;
- export let hasPrevious: boolean;
+ interface Props {
+ selectedBadge: Badge | undefined;
+ onNext?: () => void;
+ onPrevious?: () => void;
+ hasNext: boolean;
+ hasPrevious: boolean;
+ }
+
+ let {
+ selectedBadge = $bindable(),
+ onNext = () => {},
+ onPrevious = () => {},
+ hasNext,
+ hasPrevious
+ }: Props = $props();
- let source = cdn(thumbnail(selectedBadge?.image || '')) || '';
+ let source = $state(cdn(thumbnail(selectedBadge?.image || '')) || '');
let badgeReference: HTMLImageElement;
- $: {
+ run(() => {
if (selectedBadge && selectedBadge.image) {
const image = new Image();
@@ -26,14 +38,14 @@
source = image.src;
};
}
- }
+ });
- $: {
+ run(() => {
if (selectedBadge)
fetch(root(`/api/badges?incrementClickCount=${selectedBadge.id}`), {
method: 'PUT'
});
- }
+ });
onMount(() => {
badgeReference = document.querySelector('.badge-container-image') as HTMLImageElement;
@@ -105,7 +117,7 @@
<div class="badge-preview-badge">
{#if selectedBadge.image}
<div role="img" class="badge-container">
- <a href={'#'} on:click={onClick} class="badge-container-image">
+ <a href={'#'} onclick={onClick} class="badge-container-image">
<ParallaxImage
{source}
alternativeText="selectedBadge.description"
@@ -115,7 +127,7 @@
</a>
</div>
- <p />
+ <p></p>
{/if}
</div>
@@ -124,7 +136,7 @@
{$locale().dateFormatter(databaseTimeToDate(selectedBadge.time))}
{#if (selectedBadge.designer || selectedBadge.source || selectedBadge.post) && !selectedBadge.description}
- <p />
+ <p></p>
{:else if selectedBadge.description}
<br />
{/if}
@@ -133,7 +145,7 @@
{#if selectedBadge.description}
{selectedBadge.description}
- <p />
+ <p></p>
{/if}
{#if selectedBadge.designer}
@@ -185,7 +197,7 @@
<a
href={`?category=${selectedBadge.category}`}
- on:click={() => (selectedBadge = undefined)}
+ onclick={() => (selectedBadge = undefined)}
>
{selectedBadge.category}
</a>
@@ -200,11 +212,11 @@
<div class="badge-preview-seek">
{#if hasPrevious}
- <button on:click={onPrevious}>Previous</button>
+ <button onclick={onPrevious}>Previous</button>
{/if}
{#if hasNext}
- <button on:click={onNext} style="float: right;">Next</button>
+ <button onclick={onNext} style="float: right;">Next</button>
{/if}
</div>
</div>
diff --git a/src/lib/User/BadgeWall/Badges.svelte b/src/lib/User/BadgeWall/Badges.svelte
index b233d0c3..1acd4cce 100644
--- a/src/lib/User/BadgeWall/Badges.svelte
+++ b/src/lib/User/BadgeWall/Badges.svelte
@@ -9,19 +9,30 @@
import type { Preferences } from '../../../graphql/$types';
import type { IndexedBadge } from './badge';
- export let ungroupedBadges: IndexedBadge[];
- export let groupedBadges: [string, IndexedBadge[]][];
- export let categoryFilter: string | null;
- export let editMode: boolean;
- export let preferences: Preferences | undefined;
- export let selectedBadge: IndexedBadge | undefined = undefined;
+ interface Props {
+ ungroupedBadges: IndexedBadge[];
+ groupedBadges: [string, IndexedBadge[]][];
+ categoryFilter: string | null;
+ editMode: boolean;
+ preferences: Preferences | undefined;
+ selectedBadge?: IndexedBadge | undefined;
+ }
+
+ let {
+ ungroupedBadges,
+ groupedBadges,
+ categoryFilter,
+ editMode,
+ preferences,
+ selectedBadge = $bindable(undefined)
+ }: Props = $props();
</script>
{#if ungroupedBadges.length === 0}
<div class="card">
No due.moe registered badges found for this user. <a
href={'#'}
- on:click={(e) => e.preventDefault()}
+ onclick={(e) => e.preventDefault()}
title="This alert does not include AWC badges."
use:tooltip>?</a
>
@@ -36,7 +47,7 @@
<details open={categoryFilter ? categoryFilter === category : true}>
<summary>{category}</summary>
- <p />
+ <p></p>
<div class="badges">
{#each badges as badge}
@@ -54,7 +65,7 @@
>
<a
href={`#`}
- on:click={() => {
+ onclick={() => {
selectedBadge = badge;
const hiddenInput = document.querySelector('input[name="hidden"]');
@@ -93,6 +104,6 @@
</details>
{#if groupedBadges[groupedBadges.length - 1][0] !== category}
- <p />
+ <p></p>
{/if}
{/each}
diff --git a/src/lib/User/BadgeWall/FallbackBadge.svelte b/src/lib/User/BadgeWall/FallbackBadge.svelte
index 35a50a7d..68667031 100644
--- a/src/lib/User/BadgeWall/FallbackBadge.svelte
+++ b/src/lib/User/BadgeWall/FallbackBadge.svelte
@@ -9,22 +9,40 @@
import { dev } from '$app/environment';
import type { Preferences } from '../../../graphql/$types';
- export let source: string | null | undefined;
- export let alternative: string | null | undefined;
- export let fallback: string | null | undefined;
- export let maxReplaceCount = 1;
- export let replaceDelay = 1000;
- export let error = 'https://i2.kym-cdn.com/photos/images/newsfeed/000/290/992/0aa.jpg';
- export let hideOnError = false;
- export let badge: Badge;
- export let style = '';
- export let selectedBadge: Badge | null = null;
- export let awc = false;
- export let index: number | null = null;
- export let preferences: Preferences | undefined;
-
- let replaceCount = 0;
- let badgeReference: HTMLImageElement;
+ interface Props {
+ source: string | null | undefined;
+ alternative: string | null | undefined;
+ fallback: string | null | undefined;
+ maxReplaceCount?: number;
+ replaceDelay?: number;
+ error?: string;
+ hideOnError?: boolean;
+ badge: Badge;
+ style?: string;
+ selectedBadge?: Badge | null;
+ awc?: boolean;
+ index?: number | null;
+ preferences: Preferences | undefined;
+ }
+
+ let {
+ source,
+ alternative,
+ fallback,
+ maxReplaceCount = 1,
+ replaceDelay = 1000,
+ error = 'https://i2.kym-cdn.com/photos/images/newsfeed/000/290/992/0aa.jpg',
+ hideOnError = false,
+ badge,
+ style = '',
+ selectedBadge = $bindable(null),
+ awc = false,
+ index = null,
+ preferences
+ }: Props = $props();
+
+ let replaceCount = $state(0);
+ let badgeReference: HTMLImageElement = $state();
const mouse = tweened(
{ x: 0, y: 0 },
{
@@ -82,9 +100,9 @@
href={awc ? badgeToAny(badge).link : '#'}
target="_blank"
class="badge-container badge"
- on:mousemove={handleMouseMove}
- on:mouseleave={handleMouseLeave}
- on:click={(e) => {
+ onmousemove={handleMouseMove}
+ onmouseleave={handleMouseLeave}
+ onclick={(e) => {
if (!awc) {
e.preventDefault();
@@ -100,7 +118,7 @@
bind:this={badgeReference}
style="transform: perspective(1000px) rotateX({$mouse.y / 10}deg) rotateY({-$mouse.x /
10}deg); ${style}"
- on:error={(e) => delayedReplace(e, fallback)}
+ onerror={(e) => delayedReplace(e, fallback)}
/>
</a>
</Tooltip>
diff --git a/src/lib/Utility/Loading.svelte b/src/lib/Utility/Loading.svelte
index 92cbc1ac..3d1eeec6 100644
--- a/src/lib/Utility/Loading.svelte
+++ b/src/lib/Utility/Loading.svelte
@@ -1,13 +1,23 @@
<script lang="ts">
- export let type: string | undefined = undefined;
- export let percent: number | undefined = undefined;
- export let card = true;
+ interface Props {
+ type?: string | undefined;
+ percent?: number | undefined;
+ card?: boolean;
+ children?: import('svelte').Snippet;
+ }
+
+ let {
+ type = undefined,
+ percent = undefined,
+ card = true,
+ children
+ }: Props = $props();
</script>
<div class:card>
{#if type}
Loading {type} ...{percent ? ` ${percent}%` : ''}
{:else}
- <slot />
+ {@render children?.()}
{/if}
</div>
diff --git a/src/lib/Utility/oauth.ts b/src/lib/Utility/oauth.ts
index 78f52bfa..bd824b75 100644
--- a/src/lib/Utility/oauth.ts
+++ b/src/lib/Utility/oauth.ts
@@ -47,5 +47,5 @@ export const callback = async (options: CallbackOptions) => {
}
);
- throw redirect(303, options.redirect ?? '/');
+ redirect(303, options.redirect ?? '/');
};
diff --git a/src/routes/+error.svelte b/src/routes/+error.svelte
index f822d521..26a7a69a 100644
--- a/src/routes/+error.svelte
+++ b/src/routes/+error.svelte
@@ -3,7 +3,7 @@
import { closest } from '$lib/Error/path';
import Popup from '$lib/Layout/Popup.svelte';
- $: suggestion = closest($page.url.pathname.replace('/', ''), [
+ let suggestion = $derived(closest($page.url.pathname.replace('/', ''), [
'birthdays',
'completed',
'schedule',
@@ -13,7 +13,7 @@
'updates',
'user',
'wrapped'
- ]);
+ ]));
</script>
<Popup>
diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte
index 6d7fe757..3431bc86 100644
--- a/src/routes/+layout.svelte
+++ b/src/routes/+layout.svelte
@@ -1,4 +1,6 @@
<script lang="ts">
+ import { run } from 'svelte/legacy';
+
import type { SubsPleaseEpisode } from '$lib/Media/Anime/Airing/Subtitled/subsPlease';
import { env } from '$env/dynamic/public';
import { userIdentity as getUserIdentity } from '$lib/Data/AniList/identity';
@@ -33,9 +35,9 @@
injectSpeedInsights();
- export let data;
+ let { data, children } = $props();
- let isHeaderVisible = true;
+ let isHeaderVisible = $state(true);
let previousScrollPosition = 0;
let notificationInterval: NodeJS.Timeout | undefined = undefined;
@@ -43,7 +45,9 @@
addMessages('ja', japanese as unknown as LocaleDictionary);
init({ fallbackLocale: 'en', initialLocale: $settings.displayLanguage });
- $: i18nLocale.set($settings.displayLanguage);
+ run(() => {
+ i18nLocale.set($settings.displayLanguage);
+ });
const navigationOrder = ['/', '/completed', '/schedule', '/updates', '/tools', '/settings'];
const previousPage: Readable<string | null> = readable(null, (set) => {
@@ -54,13 +58,15 @@
return () => unsubscribe();
});
- $: way = data.url.includes('/user')
- ? 200
- : $previousPage && $previousPage.includes('/user')
- ? -200
- : navigationOrder.indexOf(data.url) > navigationOrder.indexOf($previousPage ?? '/')
- ? 200
- : -200;
+ let way = $derived(
+ data.url.includes('/user')
+ ? 200
+ : $previousPage && $previousPage.includes('/user')
+ ? -200
+ : navigationOrder.indexOf(data.url) > navigationOrder.indexOf($previousPage ?? '/')
+ ? 200
+ : -200
+ );
const handleScroll = () => {
const currentScrollPosition = window.scrollY;
@@ -127,7 +133,7 @@
if (notificationInterval) clearInterval(notificationInterval);
});
- $: {
+ run(() => {
if ((data.url === '/' || data.url === '/completed' || data.url === '/schedule') && !$subsPlease)
fetch(root(`/api/subsplease?tz=${Intl.DateTimeFormat().resolvedOptions().timeZone}`))
.then((r) => r.json())
@@ -147,7 +153,7 @@
subsPlease.set(r);
});
- }
+ });
</script>
<HeadTitle />
@@ -225,7 +231,7 @@
<a
class="header-item"
href={`https://anilist.co/api/v2/oauth/authorize?client_id=${env.PUBLIC_ANILIST_CLIENT_ID}&redirect_uri=${env.PUBLIC_ANILIST_REDIRECT_URI}&response_type=code`}
- on:click={() => {
+ onclick={() => {
localStorage.setItem(
'redirect',
window.location.origin + window.location.pathname + window.location.search
@@ -242,12 +248,12 @@
</div>
</div>
- <p />
+ <p></p>
<Notifications item={EventNotification} zIndex={5000}>
<Root {data} {way}>
{#if $userIdentity.id !== -1}
- <slot />
+ {@render children?.()}
{:else if data.url === '/settings'}
<Skeleton grid={true} count={1} height="10vh" />
<Skeleton grid={true} count={1} height="30vh" />
@@ -263,8 +269,19 @@
<style lang="scss">
.header {
- font-family: 'DM Sans', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,
- Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
+ font-family:
+ 'DM Sans',
+ system-ui,
+ -apple-system,
+ BlinkMacSystemFont,
+ 'Segoe UI',
+ Roboto,
+ Oxygen,
+ Ubuntu,
+ Cantarell,
+ 'Open Sans',
+ 'Helvetica Neue',
+ sans-serif;
font-size: 1.05em;
font-weight: 600;
padding: 0.8rem 0.4rem;
@@ -320,7 +337,10 @@
display: inline-block;
vertical-align: middle;
border-radius: 8px;
- box-shadow: 0 1.5px 9px var(--base01), 0 0 0 4px var(--base0E), 0 4px 30px var(--base01);
+ box-shadow:
+ 0 1.5px 9px var(--base01),
+ 0 0 0 4px var(--base0E),
+ 0 4px 30px var(--base01);
}
.separator {
diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte
index 653c3836..5c17dd7d 100644
--- a/src/routes/+page.svelte
+++ b/src/routes/+page.svelte
@@ -13,7 +13,7 @@
import Landing from '$lib/Landing.svelte';
import IndexColumn from '$lib/List/Anime/DueIndexColumn.svelte';
- export let data;
+ let { data } = $props();
let heightObserver: NodeJS.Timeout;
@@ -29,7 +29,7 @@
{#if data.user === undefined}
<div class="card">Please log in to view due media.</div>
- <p />
+ <p></p>
<Landing />
{:else}
diff --git a/src/routes/api/authentication/log-out/+server.ts b/src/routes/api/authentication/log-out/+server.ts
index 305c846f..26b5dd2c 100644
--- a/src/routes/api/authentication/log-out/+server.ts
+++ b/src/routes/api/authentication/log-out/+server.ts
@@ -11,5 +11,5 @@ export const GET = ({ cookies }) => {
secure: false
});
- throw redirect(303, root('/'));
+ redirect(303, root('/'));
};
diff --git a/src/routes/api/oauth/refresh/+server.ts b/src/routes/api/oauth/refresh/+server.ts
index 66b4209c..13f4400c 100644
--- a/src/routes/api/oauth/refresh/+server.ts
+++ b/src/routes/api/oauth/refresh/+server.ts
@@ -25,6 +25,6 @@ export const GET = async ({ url, cookies }) => {
secure: false
});
- if (url.searchParams.get('redirect')) throw redirect(303, '/');
+ if (url.searchParams.get('redirect')) redirect(303, '/');
else return Response.json(newUser);
};
diff --git a/src/routes/completed/+page.svelte b/src/routes/completed/+page.svelte
index d483d7fe..73968227 100644
--- a/src/routes/completed/+page.svelte
+++ b/src/routes/completed/+page.svelte
@@ -12,7 +12,7 @@
import locale from '$stores/locale.js';
import Landing from '$lib/Landing.svelte';
- export let data;
+ let { data } = $props();
let heightObserver: NodeJS.Timeout;
@@ -28,7 +28,7 @@
{#if data.user === undefined}
<div class="card">Please log in to view completed media.</div>
- <p />
+ <p></p>
<Landing />
{:else}
diff --git a/src/routes/events/+page.svelte b/src/routes/events/+page.svelte
index d3270e30..1c6e2524 100644
--- a/src/routes/events/+page.svelte
+++ b/src/routes/events/+page.svelte
@@ -16,7 +16,7 @@
<Event event={rawEvent} avatar />
{#if i < events.length - 1}
- <p />
+ <p></p>
{/if}
{/each}
{/if}
diff --git a/src/routes/events/group/[group]/+page.svelte b/src/routes/events/group/[group]/+page.svelte
index 37c23c40..28974fd3 100644
--- a/src/routes/events/group/[group]/+page.svelte
+++ b/src/routes/events/group/[group]/+page.svelte
@@ -7,9 +7,9 @@
import Group from '$lib/Events/Group.svelte';
import Event from '$lib/Events/Event.svelte';
- export let data;
+ let { data } = $props();
- let groupsResponse: Promise<Response>;
+ let groupsResponse: Promise<Response> = $state();
onMount(async () => {
groupsResponse = fetch(root(`/api/events/group?slug=${data.group}`));
@@ -30,14 +30,14 @@
{#if json === null}
<Message message="" loader="ripple" slot>
This group may not exist. Please
- <a href={'#'} on:click={() => location.reload()}>try again</a> later.
+ <a href={'#'} onclick={() => location.reload()}>try again</a> later.
</Message>
{:else}
{@const group = asGroup(json)}
<Group {group} />
- <p />
+ <p></p>
<details open>
<summary>Events</summary>
@@ -53,7 +53,7 @@
<Event event={asEvent(rawEvent)} />
{#if i < events.length - 1}
- <p />
+ <p></p>
{/if}
{/each}
{/if}
diff --git a/src/routes/events/groups/+page.svelte b/src/routes/events/groups/+page.svelte
index d90cce34..930e2d37 100644
--- a/src/routes/events/groups/+page.svelte
+++ b/src/routes/events/groups/+page.svelte
@@ -5,7 +5,7 @@
import { onMount } from 'svelte';
import Group from '$lib/Events/Group.svelte';
- let groupsResponse: Promise<Response>;
+ let groupsResponse: Promise<Response> = $state();
onMount(async () => {
groupsResponse = fetch(root('/api/events/groups'));
@@ -29,7 +29,7 @@
</a>
{#if i < json.length - 1}
- <p />
+ <p></p>
{/if}
{/each}
{:catch}
diff --git a/src/routes/girls/+page.svelte b/src/routes/girls/+page.svelte
index 71982c32..ecd4acd0 100644
--- a/src/routes/girls/+page.svelte
+++ b/src/routes/girls/+page.svelte
@@ -27,7 +27,7 @@
<div>
The Senpy Club <span class="opaque">|</span> Anime Girls Holding Programming Books
- <p />
+ <p></p>
<ul>
<li>
@@ -65,7 +65,7 @@
</div>
</div>
-<p />
+<p></p>
<details class="languages" open>
<summary>Languages</summary>
diff --git a/src/routes/girls/[language]/+page.svelte b/src/routes/girls/[language]/+page.svelte
index 91b18628..d61af7db 100644
--- a/src/routes/girls/[language]/+page.svelte
+++ b/src/routes/girls/[language]/+page.svelte
@@ -4,7 +4,7 @@
import Skeleton from '$lib/Loading/Skeleton.svelte';
import '$styles/girls.scss';
- export let data;
+ let { data } = $props();
</script>
<div class="card">
diff --git a/src/routes/hololive/[[stream]]/+page.svelte b/src/routes/hololive/[[stream]]/+page.svelte
index d5419711..33126762 100644
--- a/src/routes/hololive/[[stream]]/+page.svelte
+++ b/src/routes/hololive/[[stream]]/+page.svelte
@@ -11,10 +11,10 @@
import Lives from '$lib/Hololive/Lives.svelte';
import { typeSchedule } from '$lib/Hololive/hololive';
- export let data;
+ let { data } = $props();
- let schedulePromise: Promise<Response>;
- let pinnedStreams: string[] = [];
+ let schedulePromise: Promise<Response> = $state();
+ let pinnedStreams: string[] = $state([]);
onMount(() => getPinnedStreams());
@@ -66,7 +66,7 @@
{:catch}
<Message loader="ripple" slot>
{$locale().hololive.parseError}
- <a href={'#'} on:click={() => location.reload()}>Try again?</a>
+ <a href={'#'} onclick={() => location.reload()}>Try again?</a>
</Message>
{/await}
{:else}
@@ -77,6 +77,6 @@
{:catch}
<Message loader="ripple" slot>
{$locale().hololive.loadError} Please
- <a href={'#'} on:click={() => location.reload()}>try again</a> later.
+ <a href={'#'} onclick={() => location.reload()}>try again</a> later.
</Message>
{/await}
diff --git a/src/routes/reader/+page.svelte b/src/routes/reader/+page.svelte
index 775d3659..cff5ca3e 100644
--- a/src/routes/reader/+page.svelte
+++ b/src/routes/reader/+page.svelte
@@ -6,9 +6,9 @@
import { decodeResource, fetchResource, identify, Resource } from '$lib/Reader/resource';
import InputTemplate from '$lib/Tools/InputTemplate.svelte';
- let submission = '';
+ let submission = $state('');
- $: resourceIdentity = identify(submission);
+ let resourceIdentity = $derived(identify(submission));
</script>
<InputTemplate field="Manga URL" bind:submission submitText="Read" preserveCase>
diff --git a/src/routes/schedule/+page.svelte b/src/routes/schedule/+page.svelte
index 70b1e1e4..3393cced 100644
--- a/src/routes/schedule/+page.svelte
+++ b/src/routes/schedule/+page.svelte
@@ -14,9 +14,9 @@
import Message from '$lib/Loading/Message.svelte';
import subsPlease from '$stores/subsPlease';
- export let data;
+ let { data } = $props();
- let scheduledMediaPromise: Promise<Partial<Media[]>>;
+ let scheduledMediaPromise: Promise<Partial<Media[]>> = $state();
const urlParameters = browser ? new URLSearchParams(window.location.search) : null;
// let crunchyrollExpanded = false;
let forceListMode = parseOrDefault(urlParameters, 'list', false);
diff --git a/src/routes/settings/+page.svelte b/src/routes/settings/+page.svelte
index 42ee4edd..816eaab7 100644
--- a/src/routes/settings/+page.svelte
+++ b/src/routes/settings/+page.svelte
@@ -1,4 +1,7 @@
<script lang="ts">
+ import { createBubbler, preventDefault } from 'svelte/legacy';
+
+ const bubble = createBubbler();
/* eslint svelte/no-at-html-tags: "off" */
import Attributions from '$lib/Settings/Categories/Attributions.svelte';
@@ -15,7 +18,7 @@
import SettingSync from '$lib/Settings/Categories/SettingSync.svelte';
import RssFeeds from '$lib/Settings/Categories/RSSFeeds.svelte';
- export let data;
+ let { data } = $props();
// const pruneUnresolved = async () => {
// const unresolved = await chapterDatabase.chapters.where('chapters').equals(-1).toArray();
@@ -71,7 +74,7 @@
</Category>
</div>
- <p />
+ <p></p>
<Category title={$locale().settings.display.title}><Display /></Category>
<Category title={$locale().settings.calculation.title}><Calculation /></Category>
@@ -83,7 +86,7 @@
class="smaller-button button-badge badge-info unclickable-button"
title={$locale().settings.debug.tooltips.version}
use:tooltip
- on:click|preventDefault
+ onclick={preventDefault(bubble('click'))}
>{data.commit.slice(0, 7)}
</button></summary
>
diff --git a/src/routes/tools/+page.svelte b/src/routes/tools/+page.svelte
index d1650b34..139588a7 100644
--- a/src/routes/tools/+page.svelte
+++ b/src/routes/tools/+page.svelte
@@ -4,7 +4,7 @@
import { tools } from '$lib/Tools/tools.js';
import root from '$lib/Utility/root';
- let tool = 'default';
+ let tool = $state('default');
</script>
<Picker {tool} />
@@ -14,13 +14,13 @@
<div class="card">
<div class="tool-grid">
{#each Object.keys(tools).filter((t) => t !== 'default' && !tools[t].hidden) as t}
- <a href={root(`/tools/${tools[t].id}`)} on:click={() => (tool = t)}>
+ <a href={root(`/tools/${tools[t].id}`)} onclick={() => (tool = t)}>
<div class="tool-grid-tool card">
<span class="title">
{tools[t].name()}
</span>
- <p />
+ <p></p>
{#if tools[t].description}
<span class="description">
@@ -32,7 +32,7 @@
{/each}
</div>
- <p />
+ <p></p>
<blockquote style="margin: 0 0 0 1.5rem;">
Have any requests for cool tools that you think others might find useful? Send a private message
diff --git a/src/routes/tools/[tool]/+page.svelte b/src/routes/tools/[tool]/+page.svelte
index c811cf2a..9c7796ba 100644
--- a/src/routes/tools/[tool]/+page.svelte
+++ b/src/routes/tools/[tool]/+page.svelte
@@ -1,4 +1,6 @@
<script lang="ts">
+ import { run } from 'svelte/legacy';
+
import Hayai from './../../../lib/Tools/Hayai.svelte';
import UmaMusumeBirthdays from './../../../lib/Tools/UmaMusumeBirthdays.svelte';
import ActivityHistory from '$lib/Tools/ActivityHistory/Tool.svelte';
@@ -21,17 +23,19 @@
import SequelCatcher from '$lib/Tools/SequelCatcher/Tool.svelte';
import Tracker from '$lib/Tools/Tracker/Tool.svelte';
- export let data;
+ let { data } = $props();
- let tool = data.tool ?? 'default';
+ let tool = $state(data.tool ?? 'default');
onMount(() => {
if (tool === 'default') goto(root('/tools'));
});
- $: suggestion = closest(tool, Object.keys(tools));
+ let suggestion = $derived(closest(tool, Object.keys(tools)));
- $: if (tool == 'girls') goto(root('/girls'));
+ run(() => {
+ if (tool == 'girls') goto(root('/girls'));
+ });
</script>
<Picker bind:tool />
@@ -47,7 +51,7 @@
<blockquote style="margin: 0 0 0 1.5rem;">
Did you mean "<a
href={root(`/tools/${tools[suggestion].id}`)}
- on:click={() => (tool = suggestion)}
+ onclick={() => (tool = suggestion)}
style={suggestion === '...' ? 'pointer-events: none; color: inherit;' : ''}
>
{suggestion === '...' ? '...' : tools[suggestion].name()}</a
diff --git a/src/routes/updates/+page.svelte b/src/routes/updates/+page.svelte
index 9af001b4..f2657ab0 100644
--- a/src/routes/updates/+page.svelte
+++ b/src/routes/updates/+page.svelte
@@ -9,17 +9,17 @@
import { onDestroy, onMount } from 'svelte';
let feed: { items: { title: string; link: string; content: string }[] } | null | undefined =
- undefined;
+ $state(undefined);
let novelFeed:
| {
data: {
items: { srcurl: string; postfix?: string; chapter: number; series: { name: string } }[];
};
}
- | undefined = undefined;
+ | undefined = $state(undefined);
let startTime: number;
- let mangaEndTime: number;
- let novelEndTime: number;
+ let mangaEndTime: number = $state();
+ let novelEndTime: number = $state();
let directLink = browser ? new URLSearchParams(window.location.search).has('d') : false;
let heightObserver: NodeJS.Timeout;
diff --git a/src/routes/user/[user]/+page.svelte b/src/routes/user/[user]/+page.svelte
index d60ea8e5..1a36ccb5 100644
--- a/src/routes/user/[user]/+page.svelte
+++ b/src/routes/user/[user]/+page.svelte
@@ -22,10 +22,10 @@
import LinkedTooltip from '$lib/Tooltip/LinkedTooltip.svelte';
import { graphql } from '$houdini';
- export let data;
+ let { data } = $props();
- $: ({ Profile } = data);
- $: preferences = $Profile.fetching ? undefined : ($Profile.data?.User.preferences as Preferences);
+ let { Profile } = $derived(data);
+ let preferences = $derived($Profile.fetching ? undefined : ($Profile.data?.User.preferences as Preferences));
const setCategoriesQuery = graphql(`
mutation SetCategories($categories: [String!]!) {
@@ -99,20 +99,20 @@
}
`);
- $: userData = data.userData;
+ let userData = $derived(data.userData);
let error = false;
- let schedule: ParseResult | undefined = undefined;
+ let schedule: ParseResult | undefined = $state(undefined);
let draggedCategory: string | null = null;
let draggedOverCategory: string | null = null;
- $: displayBadges = (username: string, badges: number | string) =>
+ let displayBadges = $derived((username: string, badges: number | string) =>
$locale({
values: {
badges: badges,
username
}
- }).user.profile.badges;
+ }).user.profile.badges);
const handleDragStart = (
event: DragEvent & { currentTarget: EventTarget & HTMLDivElement },
@@ -302,7 +302,7 @@
{#if schedule && preferences && preferences.biography && preferences.biography.length > 0}
<br />
{:else}
- <p />
+ <p></p>
{/if}
{#if $Profile.fetching}
@@ -325,7 +325,7 @@
{/if}
{#if schedule && preferences && preferences.pinned_hololive_streams.length > 0}
- <p />
+ <p></p>
<div class="card">
<div class="hololive-badges">
@@ -352,14 +352,14 @@
{/if}
{#if preferences && userData && userData.id === $identity.id}
- <p />
+ <p></p>
<details open>
<summary>{$locale().user.preferences.title}</summary>
<input
type="checkbox"
- on:change={() => {
+ onchange={() => {
if (userData) toggleHideMissingBadgesQuery.mutate(null).then();
}}
checked={preferences.hide_missing_badges}
@@ -367,18 +367,18 @@
{$locale().user.preferences.hideMissingBadges.title}
<SettingHint lineBreak>{$locale().user.preferences.hideMissingBadges.hint}</SettingHint>
- <p />
+ <p></p>
<input
type="checkbox"
- on:change={() => {
+ onchange={() => {
if (userData) toggleHideAWCBadgesQuery.mutate(null).then();
}}
checked={preferences.hide_awc_badges}
/>
{$locale().user.preferences.hideAWCBadges.title}
- <p />
+ <p></p>
Pinned Categories
@@ -387,11 +387,11 @@
<div
class="card card-small pinned-category"
draggable="true"
- on:dragstart={(event) => handleDragStart(event, category)}
- on:dragover={handleDragOver}
- on:dragenter={(event) => handleDragEnter(event, category)}
- on:dragleave={(event) => handleDragLeave(event, category)}
- on:drop={handleDrop}
+ ondragstart={(event) => handleDragStart(event, category)}
+ ondragover={handleDragOver}
+ ondragenter={(event) => handleDragEnter(event, category)}
+ ondragleave={(event) => handleDragLeave(event, category)}
+ ondrop={handleDrop}
role="button"
tabindex="0"
>
@@ -400,7 +400,7 @@
</span>
<button
- on:click={() => {
+ onclick={() => {
if (userData) toggleCategoryQuery.mutate({ category }).then();
}}>Remove</button
>
@@ -412,16 +412,16 @@
<input type="text" id="category" placeholder="Category" style="width: 10em;" />
</span>
- <button class="button-lined" on:click={toggleCategory}>Add</button>
+ <button class="button-lined" onclick={toggleCategory}>Add</button>
</span>
</div>
- <p />
+ <p></p>
Biography
<button
- on:click={() => {
+ onclick={() => {
if (userData)
setBiographyQuery
.mutate({
@@ -436,14 +436,14 @@
cols="100"
id="biography"
placeholder="Markdown supported!"
- />
+></textarea>
- <p />
+ <p></p>
Badge Wall Custom CSS
<button
- on:click={() => {
+ onclick={() => {
if (userData)
setBadgeWallCSSQuery
.mutate({
@@ -458,7 +458,7 @@
cols="100"
id="badgeWallCSS"
placeholder="/* Use classes and IDs such as .badges, #badges, .badge, or standard elements like body and details, or anything, as long as it's valid CSS! */"
- />
+></textarea>
</details>
{/if}
{/if}
diff --git a/src/routes/user/[user]/badges/+page.svelte b/src/routes/user/[user]/badges/+page.svelte
index 44dd852a..8fe2dbbb 100644
--- a/src/routes/user/[user]/badges/+page.svelte
+++ b/src/routes/user/[user]/badges/+page.svelte
@@ -1,4 +1,6 @@
<script lang="ts">
+ import { run } from 'svelte/legacy';
+
import AWC from './../../../../lib/User/BadgeWall/AWC.svelte';
import { user, type User } from '$lib/Data/AniList/user';
import type { Badge } from '../../../../graphql/$types';
@@ -24,32 +26,34 @@
import { graphql } from '$houdini';
import type { Preferences } from '../../../../graphql/user/$types';
- export let data;
+ let { data } = $props();
- $: ({ BadgeWallUser } = data);
- $: preferences = $BadgeWallUser.fetching
+ let { BadgeWallUser } = $derived(data);
+ let preferences = $derived($BadgeWallUser.fetching
? undefined
- : ($BadgeWallUser.data?.User.preferences as Preferences);
-
- $: if (browser && preferences && preferences.badge_wall_css) {
- const sanitise = (css: string) =>
- css
- .replace(/\/\*[\s\S]*?\*\//g, '')
- .replace(/<\/?[^>]+(>|$)/g, '')
- .replace(
- /(expression|javascript|vbscript|onerror|onload|onclick|onmouseover|onmouseout|onmouseup|onmousedown|onkeydown|onkeyup|onkeypress|onblur|onfocus|onsubmit|onreset|onselect|onchange|ondblclick):/gi,
- ''
- )
- .replace(/(behaviour|behavior|moz-binding|content):/gi, '')
- .replace(/\s+/g, ' ')
- .trim();
- const style = document.createElement('style');
-
- style.dataset.badgeWall = 'true';
- style.innerHTML = sanitise(preferences.badge_wall_css);
-
- document.head.appendChild(style);
- }
+ : ($BadgeWallUser.data?.User.preferences as Preferences));
+
+ run(() => {
+ if (browser && preferences && preferences.badge_wall_css) {
+ const sanitise = (css: string) =>
+ css
+ .replace(/\/\*[\s\S]*?\*\//g, '')
+ .replace(/<\/?[^>]+(>|$)/g, '')
+ .replace(
+ /(expression|javascript|vbscript|onerror|onload|onclick|onmouseover|onmouseout|onmouseup|onmousedown|onkeydown|onkeyup|onkeypress|onblur|onfocus|onsubmit|onreset|onselect|onchange|ondblclick):/gi,
+ ''
+ )
+ .replace(/(behaviour|behavior|moz-binding|content):/gi, '')
+ .replace(/\s+/g, ' ')
+ .trim();
+ const style = document.createElement('style');
+
+ style.dataset.badgeWall = 'true';
+ style.innerHTML = sanitise(preferences.badge_wall_css);
+
+ document.head.appendChild(style);
+ }
+ });
const updateBadgeQuery = graphql(`
mutation UpdateBadge(
@@ -176,26 +180,26 @@
image: string;
}
- let editMode = false;
- let importMode = false;
- let error: null | string;
- let awcPromise: Promise<Response>;
+ let editMode = $state(false);
+ let importMode = $state(false);
+ let error: null | string = $state();
+ let awcPromise: Promise<Response> = $state();
let confirmDelete = 0;
- let confirmPrune = 0;
- let selectedBadge: IndexedBadge | undefined = undefined;
- let loadError: string | null = null;
+ let confirmPrune = $state(0);
+ let selectedBadge: IndexedBadge | undefined = $state(undefined);
+ let loadError: string | null = $state(null);
const isId = /^\d+$/.test(data.username);
- let importImages: ImportImage[] | undefined = undefined;
- let importLinks = false;
+ let importImages: ImportImage[] | undefined = $state(undefined);
+ let importLinks = $state(false);
let importCategory = '';
- let importReplies = false;
+ let importReplies = $state(false);
let badger: Partial<User>;
- let migrateMode = false;
- let hideMode = false;
+ let migrateMode = $state(false);
+ let hideMode = $state(false);
const authorised = authorisedJson.includes($identity.id);
- let noticeDismissed = false;
+ let noticeDismissed = $state(false);
- $: categoryFilter = new URLSearchParams($page.url.searchParams).get('category');
+ let categoryFilter = $derived(new URLSearchParams($page.url.searchParams).get('category'));
type GroupedBadges = { [key: string]: IndexedBadge[] };
@@ -557,7 +561,7 @@
<b>Notice:</b> The Badge Wall overseer system has detected badges containing
AI-generated material on your wall. {shadowHiddenCount} of your badges have been shadow
hidden.
- <p />
+ <p></p>
You may use the "Un-shadow Hide Badges" button to unhide these badges, from where you will
be required to use the hide feature to hide these badges from the public, while allowing
them to stay visible to you as the account holder.
@@ -568,12 +572,12 @@
material, this includes Badge Wall. If you have collected badges with AI-generated
elements, kindly use the hide feature to hide these badges from the public, while
allowing them to stay visible to you as the account holder.
- <p />
+ <p></p>
Failure to comply with this request at your earliest convenience will result in the hiding
of all badges from your Badge Wall.
- <p />
+ <p></p>
<button
- on:click={() => {
+ onclick={() => {
noticeDismissed = true;
localStorage.setItem('badgeWallNoticeDismissed', 'true');
@@ -584,11 +588,11 @@
</div>
{/if}
- <p />
+ <p></p>
<div class="card">
{#if authorised}
- <button on:click={setShadowHide}>Shadow Hide Badges</button>
+ <button onclick={setShadowHide}>Shadow Hide Badges</button>
{/if}
{#if isOwner && authorised}
@@ -597,7 +601,7 @@
{#if isOwner}
<button
- on:click={() => {
+ onclick={() => {
selectedBadge = undefined;
editMode = !editMode;
}}
@@ -608,7 +612,7 @@
</button>
<span style="margin: 0 0.625rem;">•</span>
<button
- on:click={() => {
+ onclick={() => {
selectedBadge = undefined;
importMode = !importMode;
}}
@@ -619,7 +623,7 @@
</button>
<span style="margin: 0 0.625rem;">•</span>
<button
- on:click={() => {
+ onclick={() => {
selectedBadge = undefined;
migrateMode = !migrateMode;
}}
@@ -628,7 +632,7 @@
</button>
<span style="margin: 0 0.625rem;">•</span>
<button
- on:click={() => {
+ onclick={() => {
selectedBadge = undefined;
hideMode = !hideMode;
}}
@@ -640,7 +644,7 @@
{#if shadowHidden}
<span style="margin: 0 0.625rem;">•</span>
- <button on:click={setShadowHide}>Un-shadow Hide Badges</button>
+ <button onclick={setShadowHide}>Un-shadow Hide Badges</button>
{/if}
{#if editMode && isOwner}
@@ -659,7 +663,7 @@
)
])}
- <p />
+ <p></p>
{#if error}
<p style="color: red;">{error}</p>
@@ -709,22 +713,24 @@
header={false}
center={false}
>
- <span slot="title">
- <input
- type="text"
- placeholder={$locale().user.badges.editMode.category}
- name="category"
- minlength="1"
- maxlength="1000"
- size="15"
- value={selectedBadge
- ? selectedBadge.category === 'Uncategorised'
- ? ''
- : selectedBadge.category
- : ''}
- list="categories"
- />
- </span>
+ {#snippet title()}
+ <span >
+ <input
+ type="text"
+ placeholder={$locale().user.badges.editMode.category}
+ name="category"
+ minlength="1"
+ maxlength="1000"
+ size="15"
+ value={selectedBadge
+ ? selectedBadge.category === 'Uncategorised'
+ ? ''
+ : selectedBadge.category
+ : ''}
+ list="categories"
+ />
+ </span>
+ {/snippet}
</Dropdown>
<span style="float: right;">
<input
@@ -736,7 +742,7 @@
<small>Must be full date and time, defaults to now if any fields empty</small>
</span>
- <p />
+ <p></p>
<div class="edit-row-2">
<input
@@ -762,17 +768,19 @@
header={false}
center={false}
>
- <span slot="title">
- <input
- type="text"
- placeholder={$locale().user.badges.editMode.designer}
- name="designer"
- minlength="1"
- maxlength="1000"
- size="17"
- value={selectedBadge ? selectedBadge.designer : ''}
- />
- </span>
+ {#snippet title()}
+ <span >
+ <input
+ type="text"
+ placeholder={$locale().user.badges.editMode.designer}
+ name="designer"
+ minlength="1"
+ maxlength="1000"
+ size="17"
+ value={selectedBadge ? selectedBadge.designer : ''}
+ />
+ </span>
+ {/snippet}
</Dropdown>
<Dropdown
items={[false, true].map((hidden) => ({
@@ -788,23 +796,25 @@
header={false}
center={false}
>
- <span slot="title">
- <input
- type="text"
- placeholder="Shown"
- name="hidden"
- minlength="1"
- maxlength="1000"
- size="15"
- value={selectedBadge
- ? selectedBadge.hidden
- ? 'Hidden'
- : 'Shown'
- : 'Shown'}
- />
- </span>
+ {#snippet title()}
+ <span >
+ <input
+ type="text"
+ placeholder="Shown"
+ name="hidden"
+ minlength="1"
+ maxlength="1000"
+ size="15"
+ value={selectedBadge
+ ? selectedBadge.hidden
+ ? 'Hidden'
+ : 'Shown'
+ : 'Shown'}
+ />
+ </span>
+ {/snippet}
</Dropdown>
- <button class="button-lined" on:click={submitBadge}
+ <button class="button-lined" onclick={submitBadge}
>{selectedBadge
? $locale().user.badges.editMode.update
: $locale().user.badges.editMode.add}</button
@@ -813,7 +823,7 @@
{$locale().user.badges.editMode.or}
<button
class="button-lined"
- on:click={() => {
+ onclick={() => {
if (selectedBadge) removeBadge(selectedBadge);
}}>{$locale().user.badges.editMode.delete}</button
>
@@ -824,7 +834,7 @@
</div>
{/if}
- <p />
+ <p></p>
<Badges
{ungroupedBadges}
@@ -858,7 +868,7 @@
/>
{#if authorised}
- <button on:click={shadowHideBadge}>
+ <button onclick={shadowHideBadge}>
{#if selectedBadge && selectedBadge.shadow_hidden}
Un-shadow
{:else}
@@ -875,7 +885,7 @@
<Popup fullscreen onLeave={() => (importMode = false)} show={importMode}>
{$locale().user.badges.importMode.title}
- <p />
+ <p></p>
<input
type="text"
@@ -894,7 +904,7 @@
size="20"
/>
- <p />
+ <p></p>
<input type="checkbox" id="import_links" name="import_links" bind:checked={importLinks} />
{$locale().user.badges.importMode.importLinks.title}
@@ -902,15 +912,15 @@
{$locale().user.badges.importMode.importLinks.hint}
</SettingHint>
- <p />
+ <p></p>
<input type="checkbox" id="import_links" name="import_links" bind:checked={importReplies} />
{$locale().user.badges.importMode.importReplies}
- <p />
+ <p></p>
<button
- on:click={() => {
+ onclick={() => {
importMode = false;
importImages = undefined;
}}
@@ -918,11 +928,11 @@
>
{$locale().user.badges.importMode.cancel}
</button>
- <button on:click={() => parsePost()} class="button-lined" style="float: right;">
+ <button onclick={() => parsePost()} class="button-lined" style="float: right;">
{$locale().user.badges.importMode.fetch}
</button>
- <p />
+ <p></p>
<details>
<summary>{$locale().user.badges.importMode.dangerous}</summary>
@@ -930,7 +940,7 @@
<button
class="button-lined no-shadow"
data-umami-event="Remove All Badges"
- on:click={removeAllBadges}
+ onclick={removeAllBadges}
>
{$locale({
values: {
@@ -944,7 +954,7 @@
</details>
{#if importImages && importImages.length > 0}
- <p />
+ <p></p>
{$locale({
values: {
@@ -952,7 +962,7 @@
}
}).user.badges.importMode.importConfirm}&nbsp;
<button
- on:click={() => importBadges()}
+ onclick={() => importBadges()}
class="button-lined no-shadow"
data-umami-event="Import Badges"
>
@@ -969,7 +979,7 @@
<Popup fullscreen onLeave={() => (migrateMode = false)} show={migrateMode}>
Migrate Category
- <p />
+ <p></p>
<input
type="text"
@@ -989,10 +999,10 @@
/>
<SettingHint lineBreak>Leave category empty to migrate all to or from uncategorised.</SettingHint>
- <p />
+ <p></p>
<button
- on:click={() => {
+ onclick={() => {
importMode = false;
importImages = undefined;
}}
@@ -1000,7 +1010,7 @@
>
{$locale().user.badges.importMode.cancel}
</button>
- <button on:click={() => migrateCategory()} class="button-lined" style="float: right;">
+ <button onclick={() => migrateCategory()} class="button-lined" style="float: right;">
Migrate
</button>
</Popup>
@@ -1013,7 +1023,7 @@
versa.
</SettingHint>
- <p />
+ <p></p>
<input
type="text"
@@ -1025,10 +1035,10 @@
/>
<SettingHint lineBreak>Leave category field empty to hide all.</SettingHint>
- <p />
+ <p></p>
<button
- on:click={() => {
+ onclick={() => {
hideMode = false;
importImages = undefined;
}}
@@ -1036,7 +1046,7 @@
>
{$locale().user.badges.importMode.cancel}
</button>
- <button on:click={() => hideCategory()} class="button-lined" style="float: right;"
+ <button onclick={() => hideCategory()} class="button-lined" style="float: right;"
>Toggle Visibility</button
>
</Popup>