feat: implement smart watchlist refresh on navigation
Added intelligent watchlist refresh mechanism that only refetches data when changes are actually made, preventing unnecessary API calls and improving performance. Changes: - Added watchlistNeedsRefresh store to track when watchlist data has changed - Implemented reactive watcher in App.svelte that uses svelte-spa-router's loc store to detect navigation to home - Set dirty flag in Anime.svelte after successful status updates and entry deletions - Added conditional refresh logic that checks user's primary service (AniList, MAL, or Simkl) - Parallel refresh support for multiple services when logged in This resolves the issue where clicking the logo would cause full page reloads and unnecessary re-authentication checks, while also ensuring watchlist data stays current when users make changes. Technical details: - Uses $loc.location to detect route changes - IIFE pattern for async operations in reactive statements - Only refreshes for logged-in primary services - Flag resets after successful refresh Related to: Header.svelte client-side routing fix
This commit is contained in:
@@ -3,40 +3,67 @@
|
|||||||
aniListLoggedIn,
|
aniListLoggedIn,
|
||||||
malLoggedIn,
|
malLoggedIn,
|
||||||
simklLoggedIn,
|
simklLoggedIn,
|
||||||
|
watchlistNeedsRefresh,
|
||||||
|
aniListPrimary,
|
||||||
|
malPrimary,
|
||||||
|
simklPrimary,
|
||||||
|
malWatchList,
|
||||||
|
simklWatchList,
|
||||||
} from "./helperModules/GlobalVariablesAndHelperFunctions.svelte";
|
} from "./helperModules/GlobalVariablesAndHelperFunctions.svelte";
|
||||||
import {onMount} from "svelte";
|
import { onMount } from "svelte";
|
||||||
import Router from "svelte-spa-router"
|
import Router from "svelte-spa-router";
|
||||||
import Home from "./routes/Home.svelte";
|
import Home from "./routes/Home.svelte";
|
||||||
import {wrap} from "svelte-spa-router/wrap";
|
import { wrap } from "svelte-spa-router/wrap";
|
||||||
import Spinner from "./helperComponents/Spinner.svelte";
|
import Spinner from "./helperComponents/Spinner.svelte";
|
||||||
import Header from "./helperComponents/Header.svelte";
|
import Header from "./helperComponents/Header.svelte";
|
||||||
import {CheckIfAniListLoggedInAndLoadWatchList} from "./helperModules/CheckIfAniListLoggedInAndLoadWatchList.svelte";
|
import { CheckIfAniListLoggedInAndLoadWatchList } from "./helperModules/CheckIfAniListLoggedInAndLoadWatchList.svelte";
|
||||||
import { CheckIfMALLoggedInAndSetUser } from "./helperModules/CheckIfMyAnimeListLoggedIn.svelte";
|
import { CheckIfMALLoggedInAndSetUser } from "./helperModules/CheckIfMyAnimeListLoggedIn.svelte";
|
||||||
import {CheckIfSimklLoggedInAndSetUser} from "./helperModules/CheckIsSimklLoggedIn.svelte"
|
import { CheckIfSimklLoggedInAndSetUser } from "./helperModules/CheckIsSimklLoggedIn.svelte";
|
||||||
import {CheckIfAniListLoggedIn} from "../wailsjs/go/main/App";
|
import {
|
||||||
|
CheckIfAniListLoggedIn,
|
||||||
|
GetMyAnimeList,
|
||||||
|
SimklGetUserWatchlist,
|
||||||
|
} from "../wailsjs/go/main/App";
|
||||||
|
import { loc } from "svelte-spa-router"; onMount(async () => {
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
let isAniListLoggedIn: boolean
|
let isAniListLoggedIn: boolean;
|
||||||
let isMALLoggedIn: boolean
|
let isMALLoggedIn: boolean;
|
||||||
let isSimklLoggedIn: boolean
|
let isSimklLoggedIn: boolean;
|
||||||
aniListLoggedIn.subscribe((value) => isAniListLoggedIn = value)
|
aniListLoggedIn.subscribe((value) => (isAniListLoggedIn = value));
|
||||||
malLoggedIn.subscribe((value) => isMALLoggedIn = value)
|
malLoggedIn.subscribe((value) => (isMALLoggedIn = value));
|
||||||
simklLoggedIn.subscribe((value) => isSimklLoggedIn = value)
|
simklLoggedIn.subscribe((value) => (isSimklLoggedIn = value));
|
||||||
|
|
||||||
console.log(isAniListLoggedIn)
|
!isAniListLoggedIn && (await CheckIfAniListLoggedInAndLoadWatchList());
|
||||||
!isAniListLoggedIn && await CheckIfAniListLoggedInAndLoadWatchList()
|
!isMALLoggedIn && (await CheckIfMALLoggedInAndSetUser());
|
||||||
!isMALLoggedIn && await CheckIfMALLoggedInAndSetUser()
|
!isSimklLoggedIn && (await CheckIfSimklLoggedInAndSetUser());
|
||||||
!isSimklLoggedIn && await CheckIfSimklLoggedInAndSetUser()
|
});
|
||||||
})
|
|
||||||
|
$: if ($loc?.location === "/" && $watchlistNeedsRefresh) {
|
||||||
|
(async () => {
|
||||||
|
if ($aniListLoggedIn && $aniListPrimary) {
|
||||||
|
await CheckIfAniListLoggedInAndLoadWatchList();
|
||||||
|
}
|
||||||
|
if ($malLoggedIn && $malPrimary) {
|
||||||
|
await GetMyAnimeList(1000).then((w) => malWatchList.set(w));
|
||||||
|
}
|
||||||
|
if ($simklLoggedIn && $simklPrimary) {
|
||||||
|
await SimklGetUserWatchlist().then((w) => simklWatchList.set(w));
|
||||||
|
}
|
||||||
|
|
||||||
|
watchlistNeedsRefresh.set(false);
|
||||||
|
})();
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Header />
|
<Header />
|
||||||
<Router routes={{
|
<Router
|
||||||
'/': Home,
|
routes={{
|
||||||
'/anime/:id': wrap({
|
"/": Home,
|
||||||
asyncComponent: () => import('./routes/AnimeRoutePage.svelte'),
|
"/anime/:id": wrap({
|
||||||
|
asyncComponent: () => import("./routes/AnimeRoutePage.svelte"),
|
||||||
conditions: [async () => await CheckIfAniListLoggedIn()],
|
conditions: [async () => await CheckIfAniListLoggedIn()],
|
||||||
loadingComponent: Spinner
|
loadingComponent: Spinner,
|
||||||
}),
|
}),
|
||||||
// '*': "Not Found"
|
// '*': "Not Found"
|
||||||
}} />
|
}}
|
||||||
|
/>
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
malLoggedIn,
|
malLoggedIn,
|
||||||
simklAnime,
|
simklAnime,
|
||||||
simklLoggedIn,
|
simklLoggedIn,
|
||||||
|
watchlistNeedsRefresh,
|
||||||
} from "../helperModules/GlobalVariablesAndHelperFunctions.svelte";
|
} from "../helperModules/GlobalVariablesAndHelperFunctions.svelte";
|
||||||
import { push } from "svelte-spa-router";
|
import { push } from "svelte-spa-router";
|
||||||
import WebsiteLink from "./WebsiteLink.svelte";
|
import WebsiteLink from "./WebsiteLink.svelte";
|
||||||
@@ -362,6 +363,7 @@
|
|||||||
} finally {
|
} finally {
|
||||||
submitting.set(false);
|
submitting.set(false);
|
||||||
submitSuccess.set(true);
|
submitSuccess.set(true);
|
||||||
|
watchlistNeedsRefresh.set(true);
|
||||||
setTimeout(() => submitSuccess.set(false), 2000);
|
setTimeout(() => submitSuccess.set(false), 2000);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -422,6 +424,7 @@
|
|||||||
} finally {
|
} finally {
|
||||||
submitting.set(false);
|
submitting.set(false);
|
||||||
submitSuccess.set(true);
|
submitSuccess.set(true);
|
||||||
|
watchlistNeedsRefresh.set(true);
|
||||||
setTimeout(() => submitSuccess.set(false), 2000);
|
setTimeout(() => submitSuccess.set(false), 2000);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -702,6 +705,7 @@
|
|||||||
Sync Changes
|
Sync Changes
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
|
type="button"
|
||||||
class="text-white bg-gray-800 border border-gray-600 focus:outline-none hover:bg-gray-700 focus:ring-4
|
class="text-white bg-gray-800 border border-gray-600 focus:outline-none hover:bg-gray-700 focus:ring-4
|
||||||
focus:ring-gray-700 font-medium rounded-lg text-sm px-5 py-2.5 me-2 mb-2
|
focus:ring-gray-700 font-medium rounded-lg text-sm px-5 py-2.5 me-2 mb-2
|
||||||
hover:border-gray-600"
|
hover:border-gray-600"
|
||||||
|
|||||||
@@ -10,153 +10,178 @@
|
|||||||
LogoutMyAnimeList,
|
LogoutMyAnimeList,
|
||||||
LogoutSimkl,
|
LogoutSimkl,
|
||||||
SimklGetUserWatchlist,
|
SimklGetUserWatchlist,
|
||||||
SimklSearch
|
SimklSearch,
|
||||||
} from "../../wailsjs/go/main/App";
|
} from "../../wailsjs/go/main/App";
|
||||||
import type {
|
import type {
|
||||||
AniListCurrentUserWatchList,
|
AniListCurrentUserWatchList,
|
||||||
AniListGetSingleAnime
|
AniListGetSingleAnime,
|
||||||
} from "../anilist/types/AniListCurrentUserWatchListType.js";
|
} from "../anilist/types/AniListCurrentUserWatchListType.js";
|
||||||
import {writable} from 'svelte/store'
|
import { writable } from "svelte/store";
|
||||||
import type {SimklAnime, SimklUser, SimklWatchList} from "../simkl/types/simklTypes";
|
import type {
|
||||||
import {type AniListUser, MediaListSort} from "../anilist/types/AniListTypes";
|
SimklAnime,
|
||||||
import type {MALAnime, MALWatchlist, MyAnimeListUser} from "../mal/types/MALTypes";
|
SimklUser,
|
||||||
import type {TableItems} from "../helperTypes/TableTypes";
|
SimklWatchList,
|
||||||
import {AniListGetSingleAnimeDefaultData} from "../helperDefaults/AniListGetSingleAnime";
|
} from "../simkl/types/simklTypes";
|
||||||
|
import {
|
||||||
|
type AniListUser,
|
||||||
|
MediaListSort,
|
||||||
|
} from "../anilist/types/AniListTypes";
|
||||||
|
import type {
|
||||||
|
MALAnime,
|
||||||
|
MALWatchlist,
|
||||||
|
MyAnimeListUser,
|
||||||
|
} from "../mal/types/MALTypes";
|
||||||
|
import type { TableItems } from "../helperTypes/TableTypes";
|
||||||
|
import { AniListGetSingleAnimeDefaultData } from "../helperDefaults/AniListGetSingleAnime";
|
||||||
|
|
||||||
export let aniListAnime = writable(AniListGetSingleAnimeDefaultData)
|
export let aniListAnime = writable(AniListGetSingleAnimeDefaultData);
|
||||||
export let title = writable("")
|
export let title = writable("");
|
||||||
export let aniListLoggedIn = writable(false)
|
export let aniListLoggedIn = writable(false);
|
||||||
export let simklLoggedIn = writable(false)
|
export let simklLoggedIn = writable(false);
|
||||||
export let malLoggedIn = writable(false)
|
export let malLoggedIn = writable(false);
|
||||||
export let simklWatchList = writable({} as SimklWatchList)
|
export let simklWatchList = writable({} as SimklWatchList);
|
||||||
export let aniListPrimary = writable(true)
|
export let aniListPrimary = writable(true);
|
||||||
export let simklPrimary = writable(false)
|
export let simklPrimary = writable(false);
|
||||||
export let malPrimary = writable(false)
|
export let malPrimary = writable(false);
|
||||||
export let simklUser = writable({} as SimklUser)
|
export let simklUser = writable({} as SimklUser);
|
||||||
export let aniListUser = writable({} as AniListUser)
|
export let aniListUser = writable({} as AniListUser);
|
||||||
export let malUser = writable({} as MyAnimeListUser)
|
export let malUser = writable({} as MyAnimeListUser);
|
||||||
export let aniListWatchlist = writable({} as AniListCurrentUserWatchList)
|
export let aniListWatchlist = writable({} as AniListCurrentUserWatchList);
|
||||||
export let malWatchList = writable({} as MALWatchlist)
|
export let malWatchList = writable({} as MALWatchlist);
|
||||||
export let malAnime = writable({} as MALAnime)
|
export let malAnime = writable({} as MALAnime);
|
||||||
export let simklAnime = writable({} as SimklAnime)
|
export let simklAnime = writable({} as SimklAnime);
|
||||||
export let loading = writable(false)
|
export let loading = writable(false);
|
||||||
export let tableItems = writable([] as TableItems)
|
export let tableItems = writable([] as TableItems);
|
||||||
|
export let watchlistNeedsRefresh = writable(false);
|
||||||
|
|
||||||
export let watchListPage = writable(1)
|
export let watchListPage = writable(1);
|
||||||
export let animePerPage = writable(20)
|
export let animePerPage = writable(20);
|
||||||
|
|
||||||
let isAniListPrimary: boolean
|
let isAniListPrimary: boolean;
|
||||||
let page: number
|
let page: number;
|
||||||
let perPage: number
|
let perPage: number;
|
||||||
let aniWatchlist: AniListCurrentUserWatchList
|
let aniWatchlist: AniListCurrentUserWatchList;
|
||||||
let currentAniListAnime: AniListGetSingleAnime
|
let currentAniListAnime: AniListGetSingleAnime;
|
||||||
|
|
||||||
let isMalLoggedIn: boolean
|
let isMalLoggedIn: boolean;
|
||||||
let isSimklLoggedIn: boolean
|
let isSimklLoggedIn: boolean;
|
||||||
|
|
||||||
aniListPrimary.subscribe(value => isAniListPrimary = value)
|
aniListPrimary.subscribe((value) => (isAniListPrimary = value));
|
||||||
watchListPage.subscribe(value => page = value)
|
watchListPage.subscribe((value) => (page = value));
|
||||||
animePerPage.subscribe(value => perPage = value)
|
animePerPage.subscribe((value) => (perPage = value));
|
||||||
aniListWatchlist.subscribe(value => aniWatchlist = value)
|
aniListWatchlist.subscribe((value) => (aniWatchlist = value));
|
||||||
malLoggedIn.subscribe(value => isMalLoggedIn = value)
|
malLoggedIn.subscribe((value) => (isMalLoggedIn = value));
|
||||||
simklLoggedIn.subscribe(value => isSimklLoggedIn = value)
|
simklLoggedIn.subscribe((value) => (isSimklLoggedIn = value));
|
||||||
aniListAnime.subscribe(value => currentAniListAnime = value)
|
aniListAnime.subscribe((value) => (currentAniListAnime = value));
|
||||||
|
|
||||||
|
export async function GetAnimeSingleItem(
|
||||||
export async function GetAnimeSingleItem(aniId: number, login: boolean): Promise<""> {
|
aniId: number,
|
||||||
await GetAniListItem(aniId, login).then(aniListResult => {
|
login: boolean,
|
||||||
let finalResult: AniListGetSingleAnime
|
): Promise<""> {
|
||||||
finalResult = aniListResult
|
await GetAniListItem(aniId, login).then((aniListResult) => {
|
||||||
|
let finalResult: AniListGetSingleAnime;
|
||||||
|
finalResult = aniListResult;
|
||||||
if (login === false) {
|
if (login === false) {
|
||||||
finalResult.data.MediaList.status = ""
|
finalResult.data.MediaList.status = "";
|
||||||
finalResult.data.MediaList.score = 0
|
finalResult.data.MediaList.score = 0;
|
||||||
finalResult.data.MediaList.progress = 0
|
finalResult.data.MediaList.progress = 0;
|
||||||
finalResult.data.MediaList.notes = ""
|
finalResult.data.MediaList.notes = "";
|
||||||
finalResult.data.MediaList.repeat = 0
|
finalResult.data.MediaList.repeat = 0;
|
||||||
finalResult.data.MediaList.startedAt.day = 0
|
finalResult.data.MediaList.startedAt.day = 0;
|
||||||
finalResult.data.MediaList.startedAt.month = 0
|
finalResult.data.MediaList.startedAt.month = 0;
|
||||||
finalResult.data.MediaList.startedAt.year = 0
|
finalResult.data.MediaList.startedAt.year = 0;
|
||||||
finalResult.data.MediaList.completedAt.day = 0
|
finalResult.data.MediaList.completedAt.day = 0;
|
||||||
finalResult.data.MediaList.completedAt.month = 0
|
finalResult.data.MediaList.completedAt.month = 0;
|
||||||
finalResult.data.MediaList.completedAt.year = 0
|
finalResult.data.MediaList.completedAt.year = 0;
|
||||||
}
|
}
|
||||||
aniListAnime.set(finalResult)
|
aniListAnime.set(finalResult);
|
||||||
title.set(currentAniListAnime.data.MediaList.media.title.english === "" ?
|
title.set(
|
||||||
currentAniListAnime.data.MediaList.media.title.romaji :
|
currentAniListAnime.data.MediaList.media.title.english === ""
|
||||||
currentAniListAnime.data.MediaList.media.title.english)
|
? currentAniListAnime.data.MediaList.media.title.romaji
|
||||||
})
|
: currentAniListAnime.data.MediaList.media.title.english,
|
||||||
|
);
|
||||||
|
});
|
||||||
if (isMalLoggedIn) {
|
if (isMalLoggedIn) {
|
||||||
await GetMyAnimeListAnime(currentAniListAnime.data.MediaList.media.idMal).then(malResult => {
|
await GetMyAnimeListAnime(
|
||||||
malAnime.set(malResult)
|
currentAniListAnime.data.MediaList.media.idMal,
|
||||||
})
|
).then((malResult) => {
|
||||||
|
malAnime.set(malResult);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
if (isSimklLoggedIn) {
|
if (isSimklLoggedIn) {
|
||||||
await SimklSearch(currentAniListAnime.data.MediaList).then((value: SimklAnime) => {
|
await SimklSearch(currentAniListAnime.data.MediaList).then(
|
||||||
simklAnime.set(value)
|
(value: SimklAnime) => {
|
||||||
})
|
simklAnime.set(value);
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return ""
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
export function loginToSimkl(): void {
|
export function loginToSimkl(): void {
|
||||||
GetSimklLoggedInUser().then(user => {
|
GetSimklLoggedInUser().then((user) => {
|
||||||
if (Object.keys(user).length === 0) {
|
if (Object.keys(user).length === 0) {
|
||||||
simklLoggedIn.set(false)
|
simklLoggedIn.set(false);
|
||||||
} else {
|
} else {
|
||||||
simklUser.set(user)
|
simklUser.set(user);
|
||||||
SimklGetUserWatchlist().then(result => {
|
SimklGetUserWatchlist().then((result) => {
|
||||||
simklWatchList.set(result)
|
simklWatchList.set(result);
|
||||||
simklLoggedIn.set(true)
|
simklLoggedIn.set(true);
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function loginToAniList(): void {
|
export function loginToAniList(): void {
|
||||||
GetAniListLoggedInUser().then(result => {
|
GetAniListLoggedInUser().then((result) => {
|
||||||
aniListUser.set(result)
|
aniListUser.set(result);
|
||||||
if (isAniListPrimary) {
|
if (isAniListPrimary) {
|
||||||
GetAniListUserWatchingList(page, perPage, MediaListSort.UpdatedTimeDesc).then((result) => {
|
GetAniListUserWatchingList(
|
||||||
aniListWatchlist.set(result)
|
page,
|
||||||
aniListLoggedIn.set(true)
|
perPage,
|
||||||
})
|
MediaListSort.UpdatedTimeDesc,
|
||||||
|
).then((result) => {
|
||||||
|
aniListWatchlist.set(result);
|
||||||
|
aniListLoggedIn.set(true);
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
aniListLoggedIn.set(true)
|
aniListLoggedIn.set(true);
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function loginToMAL(): void {
|
export function loginToMAL(): void {
|
||||||
GetMyAnimeListLoggedInUser().then(result => {
|
GetMyAnimeListLoggedInUser().then((result) => {
|
||||||
malUser.set(result)
|
malUser.set(result);
|
||||||
malLoggedIn.set(true)
|
malLoggedIn.set(true);
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function logoutOfAniList(): void {
|
export function logoutOfAniList(): void {
|
||||||
LogoutAniList().then(result => {
|
LogoutAniList().then((result) => {
|
||||||
console.log(result)
|
console.log(result);
|
||||||
if (Object.keys(aniWatchlist).length !== 0) {
|
if (Object.keys(aniWatchlist).length !== 0) {
|
||||||
aniListWatchlist.set({} as AniListCurrentUserWatchList)
|
aniListWatchlist.set({} as AniListCurrentUserWatchList);
|
||||||
}
|
}
|
||||||
aniListUser.set({} as AniListUser)
|
aniListUser.set({} as AniListUser);
|
||||||
aniListLoggedIn.set(false)
|
aniListLoggedIn.set(false);
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function logoutOfMAL(): void {
|
export function logoutOfMAL(): void {
|
||||||
LogoutMyAnimeList().then(result => {
|
LogoutMyAnimeList().then((result) => {
|
||||||
console.log(result)
|
console.log(result);
|
||||||
malUser.set({} as MyAnimeListUser)
|
malUser.set({} as MyAnimeListUser);
|
||||||
malLoggedIn.set(false)
|
malLoggedIn.set(false);
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function logoutOfSimkl(): void {
|
export function logoutOfSimkl(): void {
|
||||||
LogoutSimkl().then(result => {
|
LogoutSimkl().then((result) => {
|
||||||
console.log(result)
|
console.log(result);
|
||||||
simklUser.set({} as SimklUser)
|
simklUser.set({} as SimklUser);
|
||||||
simklLoggedIn.set(false)
|
simklLoggedIn.set(false);
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user