Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: create application form #222

Merged
merged 1 commit into from
Jul 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 4 additions & 17 deletions src/components/ImageUpload.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ import { Controller, useFormContext } from "react-hook-form";
import { toast } from "sonner";

import { IconButton } from "~/components/ui/Button";
import { Spinner } from "~/components/ui/Spinner";
import { useUploadMetadata } from "~/hooks/useMetadata";

export interface IImageUploadProps extends ComponentProps<"img"> {
name?: string;
Expand All @@ -22,7 +20,6 @@ export const ImageUpload = ({
const ref = useRef<HTMLInputElement>(null);
const { control } = useFormContext();

const upload = useUploadMetadata();
const select = useMutation({
mutationFn: async (file: File) => {
if (file.size >= maxSize) {
Expand All @@ -46,16 +43,10 @@ export const ImageUpload = ({
className={clsx("relative cursor-pointer overflow-hidden", className)}
onClick={() => ref.current?.click()}
>
<IconButton
className="absolute bottom-1 right-1"
disabled={upload.isPending}
icon={upload.isPending ? Spinner : ImageIcon}
/>
<IconButton className="absolute bottom-1 right-1" icon={ImageIcon} />

<div
className={clsx("h-full rounded-xl bg-gray-200 bg-cover bg-center bg-no-repeat", {
"animate-pulse opacity-50": upload.isPending,
})}
className={clsx("h-full rounded-xl bg-gray-200 bg-cover bg-center bg-no-repeat")}
style={{
backgroundImage: `url("${select.data ?? value}")`,
}}
Expand All @@ -72,12 +63,8 @@ export const ImageUpload = ({
const [file] = event.target.files ?? [];
if (file) {
select.mutate(file, {
onSuccess: () => {
upload.mutate(file, {
onSuccess: (data) => {
onChange(data.url);
},
});
onSuccess: (objectUrl) => {
onChange(objectUrl);
},
});
}
Expand Down
2 changes: 1 addition & 1 deletion src/components/ui/Tooltip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export const Tooltip = ({ description }: ITooltipProps): JSX.Element => {
}, [setShowBlock]);

return (
<div className="relative cursor-pointer text-gray-500 dark:text-white">
<div className="relative z-10 cursor-pointer text-gray-500 dark:text-white">
<CiCircleQuestion onMouseEnter={handleShowBlock} onMouseLeave={handleHideBlock} />

{showBlock && (
Expand Down
89 changes: 65 additions & 24 deletions src/features/applications/components/ApplicationButtons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,35 @@ export const ApplicationButtons = ({

const form = useFormContext<Application>();

const application = useMemo(() => form.getValues(), [form]);
const [
name,
bio,
payoutAddress,
websiteUrl,
profileImageUrl,
bannerImageUrl,
contributionDescription,
impactDescription,
impactCategory,
contributionLinks,
fundingSources,
] = useMemo(
() =>
form.watch([
"name",
"bio",
"payoutAddress",
"websiteUrl",
"profileImageUrl",
"bannerImageUrl",
"contributionDescription",
"impactDescription",
"impactCategory",
"contributionLinks",
"fundingSources",
]),
[form],
);

const checkLinks = (
links: Pick<ContributionLink | ImpactMetrix | FundingSource, "description">[] | undefined,
Expand All @@ -48,20 +76,6 @@ export const ApplicationButtons = ({
links.reduce((prev, link) => prev && link.description !== undefined && link.description.length > 0, true);

const stepComplete = useMemo((): boolean => {
const {
name,
bio,
payoutAddress,
websiteUrl,
profileImageUrl,
bannerImageUrl,
contributionDescription,
impactDescription,
impactCategory,
contributionLinks,
fundingSources,
} = application;

if (step === EApplicationStep.PROFILE) {
return (
bannerImageUrl !== undefined &&
Expand All @@ -76,6 +90,7 @@ export const ApplicationButtons = ({
if (step === EApplicationStep.ADVANCED) {
return (
impactCategory !== undefined &&
impactCategory.length > 0 &&
contributionDescription.length > 0 &&
impactDescription.length > 0 &&
checkLinks(contributionLinks) &&
Expand All @@ -84,15 +99,41 @@ export const ApplicationButtons = ({
}

return true;
}, [step, application]);
}, [
step,
bannerImageUrl,
profileImageUrl,
bio,
name,
payoutAddress,
websiteUrl,
impactCategory,
contributionDescription,
impactDescription,
contributionLinks,
fundingSources,
]);

const handleOnClickNextStep = useCallback(
(event: UIEvent) => {
event.preventDefault();

if (stepComplete) {
onNextStep();
} else {
setShowDialog(true);
}
},
[onNextStep, setShowDialog, stepComplete],
);

const handleOnClickNextStep = useCallback(() => {
if (stepComplete) {
onNextStep();
} else {
setShowDialog(true);
}
}, [onNextStep, setShowDialog, stepComplete]);
const handleOnClickBackStep = useCallback(
(event: UIEvent) => {
event.preventDefault();
onBackStep();
},
[onBackStep],
);

const handleOnOpenChange = useCallback(() => {
setShowDialog(false);
Expand All @@ -109,7 +150,7 @@ export const ApplicationButtons = ({
/>

{step !== EApplicationStep.PROFILE && (
<Button className="text-gray-300 underline" size="auto" variant="ghost" onClick={onBackStep}>
<Button className="text-gray-300 underline" size="auto" variant="ghost" onClick={handleOnClickBackStep}>
Back
</Button>
)}
Expand Down
15 changes: 13 additions & 2 deletions src/features/applications/components/ApplicationForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -117,11 +117,22 @@ export const ApplicationForm = (): JSX.Element => {
</div>

<div className="mb-4 gap-4 md:flex">
<FormControl required label="Project avatar" name="profileImageUrl">
<FormControl
required
hint="The size should be smaller than 1MB."
label="Project avatar"
name="profileImageUrl"
>
<ImageUpload className="h-48 w-48 " />
</FormControl>

<FormControl required className="flex-1" label="Project background image" name="bannerImageUrl">
<FormControl
required
className="flex-1"
hint="The size should be smaller than 1MB."
label="Project background image"
name="bannerImageUrl"
>
<ImageUpload className="h-48 " />
</FormControl>
</div>
Expand Down
25 changes: 21 additions & 4 deletions src/features/applications/hooks/useCreateApplication.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,25 @@ export function useCreateApplication(options: {
const upload = useUploadMetadata();

const mutation = useMutation({
mutationFn: async (values: Application) =>
Promise.all([
upload.mutateAsync(values).then(({ url: metadataPtr }) =>
mutationFn: async (values: Application) => {
if (!values.bannerImageUrl || !values.profileImageUrl) {
throw new Error("No images included.");
}

const [profileImageFile, bannerImageFile] = await Promise.all([
fetch(values.profileImageUrl),
fetch(values.bannerImageUrl),
]).then(([profileImage, bannerImage]) => Promise.all([profileImage.blob(), bannerImage.blob()]));

const [profileImageUrl, bannerImageUrl] = await Promise.all([
upload.mutateAsync(new File([profileImageFile], "profileImage")),
upload.mutateAsync(new File([bannerImageFile], "bannerImage")),
]);

const metadataValues = { ...values, profileImageUrl: profileImageUrl.url, bannerImageUrl: bannerImageUrl.url };

return Promise.all([
upload.mutateAsync(metadataValues).then(({ url: metadataPtr }) =>
attestation.mutateAsync({
schemaUID: eas.schemas.metadata,
values: {
Expand All @@ -40,7 +56,8 @@ export function useCreateApplication(options: {
},
}),
),
]).then((attestations) => attest.mutateAsync(attestations.map((att) => ({ ...att, data: [att.data] })))),
]).then((attestations) => attest.mutateAsync(attestations.map((att) => ({ ...att, data: [att.data] }))));
},

...options,
});
Expand Down
2 changes: 1 addition & 1 deletion src/pages/applications/confirmation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const ConfirmProjectPage = (): JSX.Element => {
const state = useAppState();

const searchParams = useSearchParams();
const txHash = searchParams.get("txHash");
const txHash = useMemo(() => searchParams.get("txHash"), [searchParams]);
const project = useApplicationByTxHash(txHash ?? "");

const attestation = useMemo(() => project.data, [project]);
Expand Down
Loading