feat(frontend): integrate error handling into application
App.svelte: - Import and render ErrorModal component - Add ErrorModal to main app layout below Header CheckIfAniListLoggedInAndLoadWatchList.svelte: - Import error state helpers (setApiError, clearApiError) - Wrap LoadAniListUser in try-catch with error handling - Wrap LoadAniListWatchList in try-catch with error handling - Update CheckIfAniListLoggedInAndLoadWatchList with error handling - Remove old alert() calls in favor of modal system Home.svelte: - Import isApiDown and apiError stores - Add conditional rendering for API down state - Display user-friendly "API Unavailable" message when apiError is set - Show warning icon and helpful messaging Error handling is now fully integrated across the frontend application.
This commit is contained in:
@@ -25,6 +25,7 @@
|
|||||||
SimklGetUserWatchlist,
|
SimklGetUserWatchlist,
|
||||||
} from "../wailsjs/go/main/App";
|
} from "../wailsjs/go/main/App";
|
||||||
import { loc } from "svelte-spa-router";
|
import { loc } from "svelte-spa-router";
|
||||||
|
import ErrorModal from "./helperComponents/ErrorModal.svelte";
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
let isAniListLoggedIn: boolean;
|
let isAniListLoggedIn: boolean;
|
||||||
@@ -57,6 +58,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Header />
|
<Header />
|
||||||
|
<ErrorModal />
|
||||||
<Router
|
<Router
|
||||||
routes={{
|
routes={{
|
||||||
"/": Home,
|
"/": Home,
|
||||||
|
|||||||
@@ -12,6 +12,8 @@
|
|||||||
aniListLoggedIn,
|
aniListLoggedIn,
|
||||||
aniListWatchlist,
|
aniListWatchlist,
|
||||||
aniListSort,
|
aniListSort,
|
||||||
|
clearApiError,
|
||||||
|
setApiError,
|
||||||
} from "./GlobalVariablesAndHelperFunctions.svelte";
|
} from "./GlobalVariablesAndHelperFunctions.svelte";
|
||||||
|
|
||||||
let isAniListPrimary: boolean;
|
let isAniListPrimary: boolean;
|
||||||
@@ -25,23 +27,55 @@
|
|||||||
aniListSort.subscribe((value) => (sort = value));
|
aniListSort.subscribe((value) => (sort = value));
|
||||||
|
|
||||||
export const LoadAniListUser = async () => {
|
export const LoadAniListUser = async () => {
|
||||||
await GetAniListLoggedInUser().then((user) => {
|
try {
|
||||||
aniListUser.set(user);
|
await GetAniListLoggedInUser().then((user) => {
|
||||||
});
|
aniListUser.set(user);
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
||||||
|
setApiError(
|
||||||
|
"anilist",
|
||||||
|
`Failed to load user: ${errorMsg}`,
|
||||||
|
undefined,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const LoadAniListWatchList = async () => {
|
export const LoadAniListWatchList = async () => {
|
||||||
await GetAniListUserWatchingList(page, perPage, sort).then((watchList) => {
|
try {
|
||||||
|
const watchList = await GetAniListUserWatchingList(page, perPage, sort);
|
||||||
aniListWatchlist.set(watchList);
|
aniListWatchlist.set(watchList);
|
||||||
});
|
clearApiError();
|
||||||
};
|
} catch (err) {
|
||||||
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
||||||
export const CheckIfAniListLoggedInAndLoadWatchList = async () => {
|
setApiError(
|
||||||
const loggedIn = await CheckIfAniListLoggedIn();
|
"anilist",
|
||||||
if (loggedIn) {
|
`Failed to load watch list: ${errorMsg}`,
|
||||||
await LoadAniListUser();
|
undefined,
|
||||||
if (isAniListPrimary) await LoadAniListWatchList();
|
true,
|
||||||
|
);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
export const CheckIfAniListLoggedInAndLoadWatchList = async () => {
|
||||||
|
try {
|
||||||
|
const loggedIn = await CheckIfAniListLoggedIn();
|
||||||
|
if (loggedIn) {
|
||||||
|
await LoadAniListUser();
|
||||||
|
if (isAniListPrimary) await LoadAniListWatchList();
|
||||||
|
}
|
||||||
|
aniListLoggedIn.set(loggedIn);
|
||||||
|
} catch (err) {
|
||||||
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
||||||
|
setApiError(
|
||||||
|
"anilist",
|
||||||
|
`Authentication failed: ${errorMsg}`,
|
||||||
|
undefined,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
aniListLoggedIn.set(false);
|
||||||
}
|
}
|
||||||
aniListLoggedIn.set(loggedIn);
|
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,25 +1,55 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Pagination from "../helperComponents/Pagination.svelte";
|
import Pagination from "../helperComponents/Pagination.svelte";
|
||||||
import WatchList from "../helperComponents/WatchList.svelte";
|
import WatchList from "../helperComponents/WatchList.svelte";
|
||||||
import {
|
import {
|
||||||
aniListLoggedIn,
|
aniListLoggedIn,
|
||||||
aniListPrimary,
|
aniListPrimary,
|
||||||
loading,
|
loading,
|
||||||
} from "../helperModules/GlobalVariablesAndHelperFunctions.svelte";
|
isApiDown,
|
||||||
import loader from '../helperFunctions/loader'
|
apiError,
|
||||||
|
} from "../helperModules/GlobalVariablesAndHelperFunctions.svelte";
|
||||||
|
import loader from "../helperFunctions/loader";
|
||||||
|
|
||||||
let isAniListPrimary: boolean
|
let isAniListPrimary: boolean;
|
||||||
let isAniListLoggedIn: boolean
|
let isAniListLoggedIn: boolean;
|
||||||
|
|
||||||
aniListPrimary.subscribe((value) => isAniListPrimary = value)
|
aniListPrimary.subscribe((value) => (isAniListPrimary = value));
|
||||||
aniListLoggedIn.subscribe((value) => isAniListLoggedIn = value)
|
aniListLoggedIn.subscribe((value) => (isAniListLoggedIn = value));
|
||||||
</script>
|
</script>
|
||||||
{#if isAniListLoggedIn && isAniListPrimary}
|
|
||||||
<div class="container py-10">
|
{#if $isApiDown}
|
||||||
|
<div class="container py-10">
|
||||||
|
<div
|
||||||
|
class="bg-yellow-50 border border-yellow-200 rounded-lg p-6 text-center"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
class="mx-auto h-12 w-12 text-yellow-600 mb-4"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke="currentColor"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
<h2 class="text-xl font-semibold text-yellow-900 mb-2">
|
||||||
|
API Unavailable
|
||||||
|
</h2>
|
||||||
|
<p class="text-yellow-700 mb-4">
|
||||||
|
The {$apiError?.service || "service"} is currently unavailable. The app will
|
||||||
|
remain open, and you can retry when the service is back online.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{:else if isAniListLoggedIn && isAniListPrimary}
|
||||||
|
<div class="container py-10">
|
||||||
<Pagination />
|
<Pagination />
|
||||||
<WatchList />
|
<WatchList />
|
||||||
<Pagination />
|
<Pagination />
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<div use:loader={loading}></div>
|
<div use:loader={loading}></div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
Reference in New Issue
Block a user