fix(frontend): Svelte 5 mount API and route-driven refresh

- loader action used new Component() plus $destroy, both removed in
  Svelte 5 (component_api_invalid_new crashed every refresh click
  before the reload ran); rewritten on mount/unmount with a real
  action destroy for the leaked subscription
- homecoming refresh moves off the legacy $: block, which never
  tracked the runes-backed router.location, onto the sanctioned
  onRouteLoaded callback with identical guard semantics; unused
  router import dropped so the check stays warning-free
This commit is contained in:
John O'Keefe
2026-09-16 16:30:08 -04:00
parent e8a5768a2f
commit 515678f035
2 changed files with 44 additions and 25 deletions
+15 -7
View File
@@ -21,7 +21,6 @@
import { CheckIfMALLoggedInAndSetUser } from "./helperModules/CheckIfMyAnimeListLoggedIn.svelte";
import { CheckIfSimklLoggedInAndSetUser } from "./helperModules/CheckIsSimklLoggedIn.svelte";
import {App} from "../bindings/AniTrack";
import { router } from "svelte-spa-router";
import ErrorModal from "./helperComponents/ErrorModal.svelte";
onMount(async () => {
@@ -39,26 +38,35 @@
!isSimklLoggedIn && (await CheckIfSimklLoggedInAndSetUser());
});
$: if (router.location === "/" && $watchlistNeedsRefresh) {
(async () => {
if ($aniListLoggedIn && $aniListPrimary) {
import { get } from "svelte/store";
// Reloads all watchlists when returning home with the refresh flag set
// (e.g. after saving changes on a detail page). Driven by the router's
// onRouteLoaded callback: the old `$: ... router.location ...` reactive
// statement did not reliably re-fire on navigation under Svelte 5,
// so homecoming refreshes silently never ran.
async function refreshHomeWatchlists(location: string): Promise<void> {
if (location !== "/" || !get(watchlistNeedsRefresh)) return;
if (get(aniListLoggedIn) && get(aniListPrimary)) {
await CheckIfAniListLoggedInAndLoadWatchList();
}
if ($malLoggedIn && $malPrimary) {
if (get(malLoggedIn) && get(malPrimary)) {
await App.GetMyAnimeList(1000).then((w) => malWatchList.set(w));
}
if ($simklLoggedIn && $simklPrimary) {
if (get(simklLoggedIn) && get(simklPrimary)) {
await App.SimklGetUserWatchlist().then((w) => simklWatchList.set(w));
}
watchlistNeedsRefresh.set(false);
})();
}
</script>
<Header />
<ErrorModal />
<Router
onRouteLoaded={(detail) => {
void refreshHomeWatchlists(detail.location);
}}
routes={{
"/": Home,
"/anime/:id": wrap({
+21 -10
View File
@@ -1,18 +1,29 @@
import { mount, unmount } from 'svelte';
import Spinner from '../helperComponents/Spinner.svelte';
export default (node: any, loading: any) => {
let Spin: any
loading.subscribe((loading: any) => {
if(loading){
Spin = new Spinner({
let app: any;
const unsubscribe = loading.subscribe((isLoading: any) => {
if (isLoading && !app) {
app = mount(Spinner, {
target: node,
intro: true
})
} else {
if(Spin){
Spin?.$destroy?.()
Spin = undefined;
});
} else if (!isLoading && app) {
const current = app;
app = undefined;
// Teardown must never break the caller.
void unmount(current).catch(() => {});
}
});
return {
destroy() {
unsubscribe();
if (app) {
const current = app;
app = undefined;
void unmount(current).catch(() => {});
}
}
})
};
}