fix: better error handling on client

main
Gabe Farrell 2 weeks ago
parent fed2c5b956
commit fdaea6284e

@ -16,60 +16,65 @@ interface getActivityArgs {
track_id: number;
}
function getLastListens(
async function handleJson<T>(r: Response): Promise<T> {
if (!r.ok) {
const err = await r.json();
throw Error(err.error);
}
return (await r.json()) as T;
}
async function getLastListens(
args: getItemsArgs
): Promise<PaginatedResponse<Listen>> {
return fetch(
const r = await fetch(
`/apis/web/v1/listens?period=${args.period}&limit=${args.limit}&artist_id=${args.artist_id}&album_id=${args.album_id}&track_id=${args.track_id}&page=${args.page}`
).then((r) => r.json() as Promise<PaginatedResponse<Listen>>);
);
return handleJson<PaginatedResponse<Listen>>(r);
}
function getTopTracks(args: getItemsArgs): Promise<PaginatedResponse<Track>> {
if (args.artist_id) {
return fetch(
`/apis/web/v1/top-tracks?period=${args.period}&limit=${args.limit}&artist_id=${args.artist_id}&page=${args.page}`
).then((r) => r.json() as Promise<PaginatedResponse<Track>>);
} else if (args.album_id) {
return fetch(
`/apis/web/v1/top-tracks?period=${args.period}&limit=${args.limit}&album_id=${args.album_id}&page=${args.page}`
).then((r) => r.json() as Promise<PaginatedResponse<Track>>);
} else {
return fetch(
`/apis/web/v1/top-tracks?period=${args.period}&limit=${args.limit}&page=${args.page}`
).then((r) => r.json() as Promise<PaginatedResponse<Track>>);
}
}
async function getTopTracks(
args: getItemsArgs
): Promise<PaginatedResponse<Track>> {
let url = `/apis/web/v1/top-tracks?period=${args.period}&limit=${args.limit}&page=${args.page}`;
function getTopAlbums(args: getItemsArgs): Promise<PaginatedResponse<Album>> {
const baseUri = `/apis/web/v1/top-albums?period=${args.period}&limit=${args.limit}&page=${args.page}`;
if (args.artist_id) {
return fetch(baseUri + `&artist_id=${args.artist_id}`).then(
(r) => r.json() as Promise<PaginatedResponse<Album>>
);
} else {
return fetch(baseUri).then(
(r) => r.json() as Promise<PaginatedResponse<Album>>
);
if (args.artist_id) url += `&artist_id=${args.artist_id}`;
else if (args.album_id) url += `&album_id=${args.album_id}`;
const r = await fetch(url);
return handleJson<PaginatedResponse<Track>>(r);
}
async function getTopAlbums(
args: getItemsArgs
): Promise<PaginatedResponse<Album>> {
let url = `/apis/web/v1/top-albums?period=${args.period}&limit=${args.limit}&page=${args.page}`;
if (args.artist_id) url += `&artist_id=${args.artist_id}`;
const r = await fetch(url);
return handleJson<PaginatedResponse<Album>>(r);
}
function getTopArtists(args: getItemsArgs): Promise<PaginatedResponse<Artist>> {
const baseUri = `/apis/web/v1/top-artists?period=${args.period}&limit=${args.limit}&page=${args.page}`;
return fetch(baseUri).then(
(r) => r.json() as Promise<PaginatedResponse<Artist>>
);
async function getTopArtists(
args: getItemsArgs
): Promise<PaginatedResponse<Artist>> {
const url = `/apis/web/v1/top-artists?period=${args.period}&limit=${args.limit}&page=${args.page}`;
const r = await fetch(url);
return handleJson<PaginatedResponse<Artist>>(r);
}
function getActivity(args: getActivityArgs): Promise<ListenActivityItem[]> {
return fetch(
async function getActivity(
args: getActivityArgs
): Promise<ListenActivityItem[]> {
const r = await fetch(
`/apis/web/v1/listen-activity?step=${args.step}&range=${args.range}&month=${args.month}&year=${args.year}&album_id=${args.album_id}&artist_id=${args.artist_id}&track_id=${args.track_id}`
).then((r) => r.json() as Promise<ListenActivityItem[]>);
);
return handleJson<ListenActivityItem[]>(r);
}
function getStats(period: string): Promise<Stats> {
return fetch(`/apis/web/v1/stats?period=${period}`).then(
(r) => r.json() as Promise<Stats>
);
async function getStats(period: string): Promise<Stats> {
const r = await fetch(`/apis/web/v1/stats?period=${period}`);
return handleJson<Stats>(r);
}
function search(q: string): Promise<SearchResponse> {
@ -416,4 +421,5 @@ export type {
ApiError,
Config,
NowPlaying,
Stats,
};

@ -73,8 +73,14 @@ export default function ActivityGrid({
<p>Loading...</p>
</div>
);
} else if (isError) {
return (
<div className="w-[500px]">
<h2>Activity</h2>
<p className="error">Error: {error.message}</p>
</div>
);
}
if (isError) return <p className="error">Error:{error.message}</p>;
// from https://css-tricks.com/snippets/javascript/lighten-darken-color/
function LightenDarkenColor(hex: string, lum: number) {

@ -1,12 +1,11 @@
import { useQuery } from "@tanstack/react-query"
import { getStats } from "api/api"
import { useQuery } from "@tanstack/react-query";
import { getStats, type Stats, type ApiError } from "api/api";
export default function AllTimeStats() {
const { isPending, isError, data, error } = useQuery({
queryKey: ['stats', 'all_time'],
queryKey: ["stats", "all_time"],
queryFn: ({ queryKey }) => getStats(queryKey[1]),
})
});
if (isPending) {
return (
@ -14,19 +13,31 @@ export default function AllTimeStats() {
<h2>All Time Stats</h2>
<p>Loading...</p>
</div>
)
}
if (isError) {
return <p className="error">Error:{error.message}</p>
);
} else if (isError) {
return (
<>
<div>
<h2>All Time Stats</h2>
<p className="error">Error: {error.message}</p>
</div>
</>
);
}
const numberClasses = 'header-font font-bold text-xl'
const numberClasses = "header-font font-bold text-xl";
return (
<div>
<h2>All Time Stats</h2>
<div>
<span className={numberClasses} title={data.minutes_listened + " minutes"}>{Math.floor(data.minutes_listened / 60)}</span> Hours Listened
<span
className={numberClasses}
title={data.minutes_listened + " minutes"}
>
{Math.floor(data.minutes_listened / 60)}
</span>{" "}
Hours Listened
</div>
<div>
<span className={numberClasses}>{data.listen_count}</span> Plays
@ -41,5 +52,5 @@ export default function AllTimeStats() {
<span className={numberClasses}>{data.track_count}</span> Tracks
</div>
</div>
)
);
}

@ -67,12 +67,14 @@ export default function LastPlays(props: Props) {
<p>Loading...</p>
</div>
);
} else if (isError) {
return (
<div className="w-[300px] sm:w-[500px]">
<h2>Last Played</h2>
<p className="error">Error: {error.message}</p>
</div>
);
}
if (isError) {
return <p className="error">Error: {error.message}</p>;
}
if (!data.items) return;
const listens = items ?? data.items;

@ -37,11 +37,14 @@ export default function TopAlbums(props: Props) {
<p>Loading...</p>
</div>
);
} else if (isError) {
return (
<div className="w-[300px]">
<h2>Top Albums</h2>
<p className="error">Error: {error.message}</p>
</div>
);
}
if (isError) {
return <p className="error">Error:{error.message}</p>;
}
if (!data.items) return;
return (
<div>

@ -28,11 +28,14 @@ export default function TopArtists(props: Props) {
<p>Loading...</p>
</div>
);
} else if (isError) {
return (
<div className="w-[300px]">
<h2>Top Artists</h2>
<p className="error">Error: {error.message}</p>
</div>
);
}
if (isError) {
return <p className="error">Error:{error.message}</p>;
}
if (!data.items) return;
return (
<div>

@ -35,9 +35,13 @@ const TopTracks = (props: Props) => {
<p>Loading...</p>
</div>
);
}
if (isError) {
return <p className="error">Error:{error.message}</p>;
} else if (isError) {
return (
<div className="w-[300px]">
<h2>Top Tracks</h2>
<p className="error">Error: {error.message}</p>
</div>
);
}
if (!data.items) return;

Loading…
Cancel
Save