fix: linter and format issues

format
This commit is contained in:
2025-03-17 21:05:49 +01:00
parent a3e1db3d77
commit 3c71fddbd2
2 changed files with 191 additions and 188 deletions

View File

@ -11,138 +11,141 @@ import { getUserImages } from "./network";
type UserImages = Awaited<ReturnType<typeof getUserImages>>; type UserImages = Awaited<ReturnType<typeof getUserImages>>;
function App() { function App() {
const [searchResults, setSearchResults] = createSignal< const [searchResults, setSearchResults] = createSignal<
UserImages[number]["Text"] UserImages[number]["Text"]
>([]); >([]);
const [images] = createResource(getUserImages); const [images] = createResource(getUserImages);
const nav = useNavigate(); const nav = useNavigate();
let fuze = new Fuse<NonNullable<UserImages[number]["Text"]>[number]>([], { let fuze = new Fuse<NonNullable<UserImages[number]["Text"]>[number]>([], {
keys: ["Text.ImageText"], keys: ["Text.ImageText"],
}); });
// TODO: there's probably a better way? // TODO: there's probably a better way?
createEffect(() => { createEffect(() => {
const userImages = images(); const userImages = images();
console.log(userImages); console.log(userImages);
if (userImages == null) { if (userImages == null) {
return; return;
} }
const imageText = userImages.flatMap((i) => i.Text ?? []); const imageText = userImages.flatMap((i) => i.Text ?? []);
fuze = new Fuse(imageText, { fuze = new Fuse(imageText, {
keys: ["ImageText"], keys: ["ImageText"],
threshold: 0.3, threshold: 0.3,
}); });
}); });
const onInputChange = (query: string) => { const onInputChange = (query: string) => {
// TODO: we can migrate this searching to Rust, so we don't abuse the main thread. // TODO: we can migrate this searching to Rust, so we don't abuse the main thread.
// But, it's not too bad as is. // But, it's not too bad as is.
setSearchResults(fuze.search(query).flatMap((s) => s.item)); setSearchResults(fuze.search(query).flatMap((s) => s.item));
}; };
return ( return (
<main class="container pt-2"> <main class="container pt-2">
<div class="px-4"> <div class="px-4">
<Search <Search
triggerMode="focus" triggerMode="focus"
options={searchResults() ?? []} options={searchResults() ?? []}
onInputChange={onInputChange} onInputChange={onInputChange}
onChange={(item) => { onChange={(item) => {
if (item?.ImageID == null) { if (item?.ImageID == null) {
console.error("ImageID was null"); console.error("ImageID was null");
return; return;
} }
nav(`/image/${item.ImageID}`); nav(`/image/${item.ImageID}`);
}} }}
optionValue="ID" optionValue="ID"
optionLabel="ImageText" optionLabel="ImageText"
placeholder="Search for stuff..." placeholder="Search for stuff..."
itemComponent={(props) => ( itemComponent={(props) => (
<Search.Item <Search.Item
item={props.item} item={props.item}
class={clsx( class={clsx(
"text-2xl leading-none text-gray-900 rounded-md p-2 select-none outline-none grid justify-items-center w-full box-border", "text-2xl leading-none text-gray-900 rounded-md p-2 select-none outline-none grid justify-items-center w-full box-border",
"hover:bg-gray-100 ui-highlighted:bg-gray-100 ui-highlighted:shadow-[inset_0_0_0_2px_rgb(2,132,199)] ui-disabled:text-gray-400 ui-disabled:opacity-50 ui-disabled:pointer-events-none", "hover:bg-gray-100 ui-highlighted:bg-gray-100 ui-highlighted:shadow-[inset_0_0_0_2px_rgb(2,132,199)] ui-disabled:text-gray-400 ui-disabled:opacity-50 ui-disabled:pointer-events-none",
)} )}
> >
<Search.ItemLabel class="mx-[-100px]"> <Search.ItemLabel class="mx-[-100px]">
{props.item.rawValue.ImageText ?? ""} {props.item.rawValue.ImageText ?? ""}
</Search.ItemLabel> </Search.ItemLabel>
</Search.Item> </Search.Item>
)} )}
> >
<Search.Control <Search.Control
class="inline-flex justify-between w-full rounded-xl text-base leading-none outline-none bg-white border border-gray-200 text-gray-900 transition-colors duration-250 ui-invalid:border-red-500 ui-invalid:text-red-500" class="inline-flex justify-between w-full rounded-xl text-base leading-none outline-none bg-white border border-gray-200 text-gray-900 transition-colors duration-250 ui-invalid:border-red-500 ui-invalid:text-red-500"
aria-label="Emoji" aria-label="Emoji"
> >
<Search.Indicator <Search.Indicator
class="appearance-none inline-flex justify-center items-center w-auto outline-none rounded-l-md px-2.5 text-gray-900 text-base leading-none transition-colors duration-250" class="appearance-none inline-flex justify-center items-center w-auto outline-none rounded-l-md px-2.5 text-gray-900 text-base leading-none transition-colors duration-250"
loadingComponent={ loadingComponent={
<Search.Icon class="h-5 w-5 grid justify-items-center flex-none"> <Search.Icon class="h-5 w-5 grid justify-items-center flex-none">
<IconRefresh size={20} class="m-auto animate-spin" /> <IconRefresh
</Search.Icon> size={20}
} class="m-auto animate-spin"
> />
<Search.Icon class="h-5 w-5 grid justify-items-center flex-none"> </Search.Icon>
<IconSearch class="m-auto size-5 text-gray-600" /> }
</Search.Icon> >
</Search.Indicator> <Search.Icon class="h-5 w-5 grid justify-items-center flex-none">
<Search.Input class="appearance-none inline-flex w-full min-h-[40px] text-base bg-transparent rounded-l-md outline-none placeholder:text-gray-600" /> <IconSearch class="m-auto size-5 text-gray-600" />
</Search.Control> </Search.Icon>
<Search.Portal> </Search.Indicator>
<Search.Content <Search.Input class="appearance-none inline-flex w-full min-h-[40px] text-base bg-transparent rounded-l-md outline-none placeholder:text-gray-600" />
class="bg-white rounded-md border border-gray-200 shadow-md origin-[var(--kb-search-content-transform-origin)] w-[var(--kb-popper-anchor-width)] data-[expanded]:animate-contentShow" </Search.Control>
onCloseAutoFocus={(e) => e.preventDefault()} <Search.Portal>
> <Search.Content
<Search.Listbox class="overflow-y-auto max-h-[360px] p-2 flex flex-col justify-start gap-1.5 leading-none focus:outline-none" /> class="bg-white rounded-md border border-gray-200 shadow-md origin-[var(--kb-search-content-transform-origin)] w-[var(--kb-popper-anchor-width)] data-[expanded]:animate-contentShow"
<Search.NoResult class="text-center p-2 pb-6 m-auto text-gray-600"> onCloseAutoFocus={(e) => e.preventDefault()}
😬 No emoji found >
</Search.NoResult> <Search.Listbox class="overflow-y-auto max-h-[360px] p-2 flex flex-col justify-start gap-1.5 leading-none focus:outline-none" />
</Search.Content> <Search.NoResult class="text-center p-2 pb-6 m-auto text-gray-600">
</Search.Portal> 😬 No emoji found
</Search> </Search.NoResult>
</div> </Search.Content>
{/* <div class="mt-4 text-base leading-none"> </Search.Portal>
</Search>
</div>
{/* <div class="mt-4 text-base leading-none">
Emoji selected: {emoji()?.emoji} {emoji()?.name} Emoji selected: {emoji()?.emoji} {emoji()?.name}
</div> */} </div> */}
<ImageViewer /> <ImageViewer />
<div class="px-4 mt-4 bg-white rounded-t-2xl"> <div class="px-4 mt-4 bg-white rounded-t-2xl">
<div class="h-[254px] overflow-scroll scrollbar-hide"> <div class="h-[254px] overflow-scroll scrollbar-hide">
<div class="w-full grid grid-cols-9 grid-rows-9 gap-2 h-[480px] grid-flow-row-dense py-4"> <div class="w-full grid grid-cols-9 grid-rows-9 gap-2 h-[480px] grid-flow-row-dense py-4">
{/* <div class="col-span-3 row-span-3 bg-red-200 rounded-xl" /> {/* <div class="col-span-3 row-span-3 bg-red-200 rounded-xl" />
<div class="col-span-3 row-span-3 bg-green-200 rounded-xl" /> <div class="col-span-3 row-span-3 bg-green-200 rounded-xl" />
<div class="col-span-6 row-span-3 bg-yellow-200 rounded-xl" /> <div class="col-span-6 row-span-3 bg-yellow-200 rounded-xl" />
<div class="col-span-3 row-span-3 bg-green-200 rounded-xl" /> <div class="col-span-3 row-span-3 bg-green-200 rounded-xl" />
<div class="col-span-3 row-span-3 bg-blue-200 rounded-xl" /> <div class="col-span-3 row-span-3 bg-blue-200 rounded-xl" />
<div class="col-span-3 row-span-3 bg-green-200 rounded-xl" /> <div class="col-span-3 row-span-3 bg-green-200 rounded-xl" />
<div class="col-span-6 row-span-3 bg-yellow-200 rounded-xl" /> */} <div class="col-span-6 row-span-3 bg-yellow-200 rounded-xl" /> */}
{/* {JSON.stringify(images())} */} {/* {JSON.stringify(images())} */}
<For each={images()}> <For each={images()}>
{(image) => ( {(image) => (
<A href={`/image/${image.ID}`}> <A href={`/image/${image.ID}`}>
<img <img
src={`http://localhost:3040/image/${image.ID}`} src={`http://localhost:3040/image/${image.ID}`}
class="col-span-3 row-span-3 rounded-xl" class="col-span-3 row-span-3 rounded-xl"
alt="" alt=""
/> />
</A> </A>
)} )}
</For> </For>
</div> </div>
</div> </div>
</div> </div>
<div class="w-full border-t h-10 bg-white px-4 border-neutral-100"> <div class="w-full border-t h-10 bg-white px-4 border-neutral-100">
footer footer
</div> </div>
</main> </main>
); );
} }
export default App; export default App;

View File

@ -1,102 +1,102 @@
import { import {
array, type InferOutput,
InferOutput, null as Null,
null as Null, array,
nullable, nullable,
object, object,
parse, parse,
pipe, pipe,
string, string,
uuid, uuid,
} from "valibot"; } from "valibot";
type BaseRequestParams = Partial<{ type BaseRequestParams = Partial<{
path: string; path: string;
body: RequestInit["body"]; body: RequestInit["body"];
method: "GET" | "POST"; method: "GET" | "POST";
}>; }>;
const getBaseRequest = ({ path, body, method }: BaseRequestParams): Request => { const getBaseRequest = ({ path, body, method }: BaseRequestParams): Request => {
return new Request(`http://localhost:3040/${path}`, { return new Request(`http://localhost:3040/${path}`, {
headers: { userId: "fcc22dbb-7792-4595-be8e-d0439e13990a" }, headers: { userId: "fcc22dbb-7792-4595-be8e-d0439e13990a" },
body, body,
method, method,
}); });
}; };
const sendImageResponseValidator = object({ const sendImageResponseValidator = object({
ID: pipe(string(), uuid()), ID: pipe(string(), uuid()),
ImageID: pipe(string(), uuid()), ImageID: pipe(string(), uuid()),
UserID: pipe(string(), uuid()), UserID: pipe(string(), uuid()),
}); });
export const sendImage = async ( export const sendImage = async (
imageName: string, imageName: string,
base64Image: string, base64Image: string,
): Promise<InferOutput<typeof sendImageResponseValidator>> => { ): Promise<InferOutput<typeof sendImageResponseValidator>> => {
const request = getBaseRequest({ const request = getBaseRequest({
path: `image/${imageName}`, path: `image/${imageName}`,
body: base64Image, body: base64Image,
method: "POST", method: "POST",
}); });
request.headers.set("Content-Type", "application/base64"); request.headers.set("Content-Type", "application/base64");
const res = await fetch(request).then((res) => res.json()); const res = await fetch(request).then((res) => res.json());
return parse(sendImageResponseValidator, res); return parse(sendImageResponseValidator, res);
}; };
const getUserImagesResponseValidator = array( const getUserImagesResponseValidator = array(
object({ object({
ID: pipe(string(), uuid()), ID: pipe(string(), uuid()),
Image: object({ Image: object({
ID: pipe(string(), uuid()), ID: pipe(string(), uuid()),
ImageName: string(), ImageName: string(),
Image: Null(), Image: Null(),
}), }),
Tags: nullable( Tags: nullable(
array( array(
object({ object({
ID: pipe(string(), uuid()), ID: pipe(string(), uuid()),
TagID: pipe(string(), uuid()), TagID: pipe(string(), uuid()),
ImageID: pipe(string(), uuid()), ImageID: pipe(string(), uuid()),
Tag: object({ Tag: object({
ID: pipe(string(), uuid()), ID: pipe(string(), uuid()),
Tag: string(), Tag: string(),
UserID: pipe(string(), uuid()), UserID: pipe(string(), uuid()),
}), }),
}), }),
), ),
), ),
Links: nullable( Links: nullable(
array( array(
object({ object({
ID: pipe(string(), uuid()), ID: pipe(string(), uuid()),
Links: string(), Links: string(),
ImageID: pipe(string(), uuid()), ImageID: pipe(string(), uuid()),
}), }),
), ),
), ),
Text: nullable( Text: nullable(
array( array(
object({ object({
ID: pipe(string(), uuid()), ID: pipe(string(), uuid()),
ImageText: string(), ImageText: string(),
ImageID: pipe(string(), uuid()), ImageID: pipe(string(), uuid()),
}), }),
), ),
), ),
}), }),
); );
export const getUserImages = async (): Promise< export const getUserImages = async (): Promise<
InferOutput<typeof getUserImagesResponseValidator> InferOutput<typeof getUserImagesResponseValidator>
> => { > => {
const request = getBaseRequest({ path: "image" }); const request = getBaseRequest({ path: "image" });
const res = await fetch(request).then((res) => res.json()); const res = await fetch(request).then((res) => res.json());
return parse(getUserImagesResponseValidator, res); return parse(getUserImagesResponseValidator, res);
}; };