Files
spotizerr-dev/spotizerr-ui/src/router.tsx
2025-08-26 21:11:11 +02:00

127 lines
3.9 KiB
TypeScript

import { createRouter, createRootRoute, createRoute, lazyRouteComponent } from "@tanstack/react-router";
import Root from "./routes/root";
import apiClient from "./lib/api-client";
import type { SearchResult, SearchApiResponse } from "./types/spotify";
import { isValidSearchResult } from "./types/spotify";
// Lazy load route components for code splitting
const Album = lazyRouteComponent(() => import("./routes/album").then(m => ({ default: m.Album })));
const Artist = lazyRouteComponent(() => import("./routes/artist").then(m => ({ default: m.Artist })));
const Track = lazyRouteComponent(() => import("./routes/track").then(m => ({ default: m.Track })));
const Home = lazyRouteComponent(() => import("./routes/home").then(m => ({ default: m.Home })));
const Config = lazyRouteComponent(() => import("./routes/config").then(m => ({ default: m.Config })));
const Playlist = lazyRouteComponent(() => import("./routes/playlist").then(m => ({ default: m.Playlist })));
const History = lazyRouteComponent(() => import("./routes/history").then(m => ({ default: m.History })));
const Watchlist = lazyRouteComponent(() => import("./routes/watchlist").then(m => ({ default: m.Watchlist })));
const rootRoute = createRootRoute({
component: Root,
});
export const indexRoute = createRoute({
getParentRoute: () => rootRoute,
path: "/",
component: Home,
validateSearch: (
search: Record<string, unknown>,
): { q?: string; type?: "track" | "album" | "artist" | "playlist" } => {
return {
q: search.q as string | undefined,
type: search.type as "track" | "album" | "artist" | "playlist" | undefined,
};
},
loaderDeps: ({ search: { q, type } }) => ({ q, type: type || "track" }),
loader: async ({ deps: { q, type } }) => {
if (!q || q.length < 3) return { items: [] };
const spotifyUrlRegex = /https:\/\/open\.spotify\.com\/(?:[a-zA-Z0-9-]+\/)*(playlist|album|artist|track)\/([a-zA-Z0-9]+)/;
const match = q.match(spotifyUrlRegex);
if (match) {
const [, urlType, id] = match;
const response = await apiClient.get<SearchResult>(`/${urlType}/info?id=${id}`);
return { items: [{ ...response.data, model: urlType as "track" | "album" | "artist" | "playlist" }] };
}
const response = await apiClient.get<SearchApiResponse>(`/search?q=${q}&search_type=${type}&limit=50`);
// Filter out null values and add the model property
const validResults = response.data.items
.filter(isValidSearchResult)
.map((item) => ({
...item,
model: type,
}));
return { items: validResults };
},
gcTime: 5 * 60 * 1000, // 5 minutes
staleTime: 5 * 60 * 1000, // 5 minutes
});
const albumRoute = createRoute({
getParentRoute: () => rootRoute,
path: "/album/$albumId",
component: Album,
});
const artistRoute = createRoute({
getParentRoute: () => rootRoute,
path: "/artist/$artistId",
component: Artist,
});
const trackRoute = createRoute({
getParentRoute: () => rootRoute,
path: "/track/$trackId",
component: Track,
});
const configRoute = createRoute({
getParentRoute: () => rootRoute,
path: "/config",
component: Config,
validateSearch: (search: Record<string, unknown>): { tab?: string } => {
return {
tab: typeof search.tab === "string" ? search.tab : undefined,
};
},
});
const playlistRoute = createRoute({
getParentRoute: () => rootRoute,
path: "/playlist/$playlistId",
component: Playlist,
});
const historyRoute = createRoute({
getParentRoute: () => rootRoute,
path: "/history",
component: History,
});
const watchlistRoute = createRoute({
getParentRoute: () => rootRoute,
path: "/watchlist",
component: Watchlist,
});
const routeTree = rootRoute.addChildren([
indexRoute,
albumRoute,
artistRoute,
trackRoute,
configRoute,
playlistRoute,
historyRoute,
watchlistRoute,
]);
export const router = createRouter({ routeTree });
declare module "@tanstack/react-router" {
interface Register {
router: typeof router;
}
}