Skip to content

Commit

Permalink
refactor: simplify the image uploader
Browse files Browse the repository at this point in the history
  • Loading branch information
BrickheadJohnny committed Nov 25, 2024
1 parent 04cb824 commit 753910a
Show file tree
Hide file tree
Showing 3 changed files with 25 additions and 73 deletions.
28 changes: 12 additions & 16 deletions src/app/create-guild/components/CreateGuildForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import { useFormContext } from "react-hook-form";

import { ControlledImageUploader } from "@/components/ImageUploader";
import { ImageUploader } from "@/components/ImageUploader";
import {
FormControl,
FormErrorMessage,
Expand All @@ -14,24 +14,20 @@ import { Input } from "@/components/ui/Input";
import type { CreateGuildForm as CreateGuildFormType } from "@/lib/schemas/guild";

export const CreateGuildForm = () => {
const { control } = useFormContext<CreateGuildFormType>();
const { control, setValue } = useFormContext<CreateGuildFormType>();

return (
<>
<FormField
name="imageUrl"
render={() => (
<FormItem>
<div className="mx-auto size-32 rounded-full bg-input-background">
<ControlledImageUploader
fieldName="imageUrl"
className="size-32"
/>
</div>
<FormErrorMessage useToast />
</FormItem>
)}
/>
<div className="mx-auto size-32 rounded-full bg-input-background">
<ImageUploader
onSuccess={(imageUrl) =>
setValue("imageUrl", imageUrl, {
shouldDirty: true,
})
}
className="size-32"
/>
</div>

<FormField
control={control}
Expand Down
56 changes: 11 additions & 45 deletions src/components/ImageUploader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,14 @@
import { getPinataKey } from "@/actions/getPinataKey";
import { pinata } from "@/config/pinata.client";
import { cn } from "@/lib/cssUtils";
import { CircleNotch, UploadSimple } from "@phosphor-icons/react/dist/ssr";
import {
CircleNotch,
UploadSimple,
XCircle,
} from "@phosphor-icons/react/dist/ssr";
import { useMutation } from "@tanstack/react-query";
import { type InputHTMLAttributes, useCallback, useRef, useState } from "react";
import {
type FieldValues,
type Path,
useController,
useFormContext,
} from "react-hook-form";
import { toast } from "sonner";
import { Button, type ButtonProps } from "./ui/Button";

type Props = Omit<ButtonProps, "variant" | "onClick" | "onError"> & {
Expand Down Expand Up @@ -49,6 +48,11 @@ export const ImageUploader = ({
}
},
onError: (error) => {
toast("Upload error", {
description: error.message,
icon: <XCircle weight="fill" className="text-icon-error" />,
});

if (typeof onError === "function") {
onError(error.message);
}
Expand Down Expand Up @@ -111,41 +115,3 @@ export const ImageUploader = ({
</Button>
);
};

type ControlledProps<TFieldValues extends FieldValues, _TContext> = Omit<
Props,
"onSuccess" | "onError" | "onFileInputChange"
> & {
fieldName: Path<TFieldValues>;
};

export const ControlledImageUploader = <
TFieldValues extends FieldValues,
TContext,
>({
fieldName,
...imageUploaderProps
}: ControlledProps<TFieldValues, TContext>) => {
const { control, setError, clearErrors } = useFormContext<TFieldValues>();

const {
field: { onChange },
} = useController<TFieldValues>({
control,
name: fieldName,
});

return (
<ImageUploader
{...imageUploaderProps}
onSuccess={(imageUrl) => onChange(imageUrl)}
onError={(errorMessage) =>
setError(fieldName, {
type: "custom",
message: errorMessage,
})
}
onFileInputChange={() => clearErrors(fieldName)}
/>
);
};
14 changes: 2 additions & 12 deletions src/components/ui/Form.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { XCircle } from "@phosphor-icons/react/dist/ssr";
import type * as LabelPrimitive from "@radix-ui/react-label";
import { Slot } from "@radix-ui/react-slot";
import { useDebouncedState } from "foxact/use-debounced-state";
Expand All @@ -19,7 +18,6 @@ import {
type FieldValues,
useFormContext,
} from "react-hook-form";
import { toast } from "sonner";
import { Collapsible, CollapsibleContent } from "./Collapsible";
import { Label } from "./Label";

Expand Down Expand Up @@ -156,20 +154,12 @@ FormDescription.displayName = "FormDescription";

const FormErrorMessage = forwardRef<
HTMLParagraphElement,
HTMLAttributes<HTMLParagraphElement> & { useToast?: boolean }
>(({ useToast, className, children, ...props }, ref) => {
HTMLAttributes<HTMLParagraphElement>
>(({ className, children, ...props }, ref) => {
const { error, formMessageId } = useFormField();
const body = error ? String(error?.message) : children;
const [debouncedBody] = useDebouncedState(body, 200);

if (useToast && body) {
toast("An error occurred", {
icon: <XCircle weight="fill" className="text-icon-error" />,
description: body ?? debouncedBody,
});
return null;
}

return (
<Collapsible open={!!body}>
<CollapsibleContent>
Expand Down

0 comments on commit 753910a

Please sign in to comment.