import clsx from "clsx";
import { Suspense, useRef, useState } from "react";
import {
useLoaderData,
LoaderFunctionArgs,
Await,
useAsyncError,
} from "react-router";
import { PAYSITE_LIST } from "#env/env-vars";
import {
ARTISTS_OR_CREATORS_LOWERCASE,
AVAILABLE_PAYSITE_LIST,
} from "#env/derived-vars";
import { createArtistsPageURL } from "#lib/urls";
import { parseOffset } from "#lib/pagination";
import { PageSkeleton } from "#components/pages";
import { FooterAd, HeaderAd, SliderAd } from "#components/advs";
import { Paginator } from "#components/pagination";
import { CardList, ArtistCard } from "#components/cards";
import { ButtonSubmit, FormRouter, FormSection } from "#components/forms";
import { LoadingIcon } from "#components/loading";
import { getArtists } from "#entities/profiles";
import * as styles from "./profiles.module.scss";
interface IProps {
results: ReturnType;
query?: string;
service?: string;
sort_by?: ISortField;
order?: "asc" | "desc";
offset?: number;
true_count?: number;
}
const sortFields = [
"favorited",
"indexed",
"updated",
"name",
"service",
] as const;
type ISortField = (typeof sortFields)[number];
function validateSortField(input: unknown): asserts input is ISortField {
if (!sortFields.includes(input as ISortField)) {
throw new Error(`Invalid sort field value "${input}".`);
}
}
export function ArtistsPage() {
const { results, query, service, sort_by, order, offset } =
useLoaderData() as IProps;
const title = "Artists";
const heading = "Artists";
return (
}>
>} resolve={results}>
{(resolvedResult: Awaited) => (
{
const url = createArtistsPageURL(
offset,
query,
service,
sort_by,
order
);
return String(url);
}}
/>
)}
Loading creators... please wait!
}
>
}>
{(resolvedResult: Awaited) =>
resolvedResult.artists.length === 0 ? (
No {ARTISTS_OR_CREATORS_LOWERCASE} found for your query.
) : (
resolvedResult.artists.map((artist) => (
))
)
}
}>
>} resolve={results}>
{(resolvedResult: Awaited) => (
{
const url = createArtistsPageURL(
offset,
query,
service,
sort_by,
order
);
return String(url);
}}
/>
)}
);
}
interface ISearchFormProps
extends Pick { }
function SearchForm({ query, service, sort_by, order }: ISearchFormProps) {
const sortRef = useRef(null);
const timeoutRef = useRef(null);
const [sortDirection, setSortDirection] = useState(order);
const onSortChange = (e: React.MouseEvent) => {
e.preventDefault();
setSortDirection(sortDirection === "asc" ? "desc" : "asc");
if (sortRef.current) {
sortRef.current.value = sortDirection === "asc" ? "desc" : "asc";
sortRef.current.form?.requestSubmit();
}
}
const onSelectChange = (e: React.ChangeEvent) => e.currentTarget.form?.requestSubmit();
const onInputChange = (e: React.ChangeEvent) => {
if (timeoutRef.current) clearTimeout(timeoutRef.current);
const target = e.currentTarget as HTMLInputElement;
timeoutRef.current = setTimeout(() => {
if (target.form) target.form.requestSubmit();
}, 500);
};
return (
{(state) => (
<>
>
)}
);
}
function CollectionError() {
const error = useAsyncError();
console.error(error);
return (
Failed to load artists.
Details
{/* @ts-expect-error vague type definition */}
{error?.statusText || error?.message}
);
}
export async function loader({
request,
}: LoaderFunctionArgs): Promise {
const searchParams = new URL(request.url).searchParams;
let offset: IProps["offset"] | undefined = undefined;
{
const inputOffset = searchParams.get("o")?.trim();
if (inputOffset) {
offset = parseOffset(inputOffset);
}
}
let query: IProps["query"] | undefined = searchParams.get("q")?.trim();
let sort_by: IProps["sort_by"] | undefined = undefined;
{
const inputValue = searchParams.get("sort_by")?.trim();
if (inputValue) {
validateSortField(inputValue);
sort_by = inputValue;
}
}
let order_by: IProps["order"] | undefined = undefined;
{
const inputValue = searchParams.get("order")?.trim();
if (inputValue) {
if (inputValue !== "asc" && inputValue !== "desc") {
throw new Error(`Invalid order by field "${inputValue}".`);
}
order_by = inputValue;
}
}
let service: IProps["service"] = undefined;
{
const inputValue = searchParams.get("service")?.trim();
if (inputValue) {
if (!PAYSITE_LIST.includes(inputValue)) {
throw new Error(`Unknown service "${inputValue}".`);
}
}
service = inputValue;
}
const results = getArtists({
offset,
order: order_by,
service,
sort_by,
query,
});
const pageProps = {
results,
sort_by,
order: order_by,
offset,
service,
query,
} satisfies IProps;
return pageProps;
}