From f2cf7a2098d9e134452e4500ab35bc9b8addb65e Mon Sep 17 00:00:00 2001 From: BrickheadJohnny Date: Wed, 14 Aug 2024 10:43:14 +0200 Subject: [PATCH] feat: migrate the `/create-guild` page without the icon selector --- .../_components/CreateGuildButton.tsx | 49 ++++++++ .../_components/CreateGuildCard.tsx | 66 ++++++++++ .../_components/CreateGuildFormProvider.tsx | 48 ++++++++ .../_components/EmailFormField.tsx | 54 ++++++++ .../create-guild/_hooks/useCreateGuild.ts} | 36 ++---- src/app/create-guild/page.tsx | 45 +++++++ src/app/create-guild/types.ts | 6 + .../BasicInfo/components/ContactInfo.tsx | 2 +- .../create-guild/CreateGuildButton.tsx | 46 ------- .../create-guild/CreateGuildForm.tsx | 115 ------------------ .../IconSelector/IconSelector.tsx | 2 +- src/pages/create-guild.tsx | 89 -------------- 12 files changed, 281 insertions(+), 277 deletions(-) create mode 100644 src/app/create-guild/_components/CreateGuildButton.tsx create mode 100644 src/app/create-guild/_components/CreateGuildCard.tsx create mode 100644 src/app/create-guild/_components/CreateGuildFormProvider.tsx create mode 100644 src/app/create-guild/_components/EmailFormField.tsx rename src/{components/create-guild/hooks/useCreateGuild.tsx => app/create-guild/_hooks/useCreateGuild.ts} (76%) create mode 100644 src/app/create-guild/page.tsx create mode 100644 src/app/create-guild/types.ts delete mode 100644 src/components/create-guild/CreateGuildButton.tsx delete mode 100644 src/components/create-guild/CreateGuildForm.tsx delete mode 100644 src/pages/create-guild.tsx diff --git a/src/app/create-guild/_components/CreateGuildButton.tsx b/src/app/create-guild/_components/CreateGuildButton.tsx new file mode 100644 index 0000000000..438ac05f71 --- /dev/null +++ b/src/app/create-guild/_components/CreateGuildButton.tsx @@ -0,0 +1,49 @@ +"use client" + +import { walletSelectorModalAtom } from "@/components/Providers/atoms" +import { useWeb3ConnectionManager } from "@/components/Web3ConnectionManager/hooks/useWeb3ConnectionManager" +import { Button } from "@/components/ui/Button" +import { Collapsible, CollapsibleContent } from "@/components/ui/Collapsible" +import { useSetAtom } from "jotai" +import { useFormContext } from "react-hook-form" +import { useCreateGuild } from "../_hooks/useCreateGuild" +import { CreateGuildFormType } from "../types" + +const CreateGuildButton = () => { + const { handleSubmit } = useFormContext() + const { onSubmit, isLoading } = useCreateGuild() + + const { isWeb3Connected } = useWeb3ConnectionManager() + const setIsWalletSelectorModalOpen = useSetAtom(walletSelectorModalAtom) + + return ( +
+ + + + + + + +
+ ) +} + +export { CreateGuildButton } diff --git a/src/app/create-guild/_components/CreateGuildCard.tsx b/src/app/create-guild/_components/CreateGuildCard.tsx new file mode 100644 index 0000000000..39b55afa34 --- /dev/null +++ b/src/app/create-guild/_components/CreateGuildCard.tsx @@ -0,0 +1,66 @@ +"use client" + +import { Card } from "@/components/ui/Card" +import { + FormControl, + FormErrorMessage, + FormField, + FormItem, + FormLabel, +} from "@/components/ui/Form" +import { Input } from "@/components/ui/Input" +import { useFormContext } from "react-hook-form" +import { CreateGuildFormType } from "../types" +import { CreateGuildButton } from "./CreateGuildButton" +import { EmailFormField } from "./EmailFormField" + +const CreateGuildCard = () => { + const { + control, + formState: { errors }, + } = useFormContext() + + return ( + +

+ Begin your guild +

+ + {/*
+ { + const generatedThemeColor = await getColorByImage(objectURL) + setValue("theme.color", generatedThemeColor) + }} + boxSize={28} + /> +
*/} + +
+ ( + + Guild name + + + + + + + )} + /> + + +
+ + +
+ ) +} + +export { CreateGuildCard } diff --git a/src/app/create-guild/_components/CreateGuildFormProvider.tsx b/src/app/create-guild/_components/CreateGuildFormProvider.tsx new file mode 100644 index 0000000000..e3491e6a22 --- /dev/null +++ b/src/app/create-guild/_components/CreateGuildFormProvider.tsx @@ -0,0 +1,48 @@ +"use client" + +import { schemas } from "@guildxyz/types" +import { zodResolver } from "@hookform/resolvers/zod" +import { PropsWithChildren } from "react" +import { FormProvider, useForm } from "react-hook-form" +import getRandomInt from "utils/getRandomInt" +import { CreateGuildFormType } from "../types" + +const defaultValues = { + name: "", + imageUrl: "", + contacts: [ + { + type: "EMAIL", + contact: "", + }, + ], + /** + * We need to define these values so the Zod resolver won't throw errors, but we'll actually overwrite these with proper values in the `useCreateGuild` hook + * + * Temporarily creating a default Member role, later the users will be able to pick from Guild Templates + */ + urlName: "", + roles: [ + { + name: "Member", + imageUrl: `/guildLogos/${getRandomInt(286)}.svg`, + requirements: [ + { + type: "FREE", + }, + ], + }, + ], +} satisfies CreateGuildFormType + +const CreateGuildFormProvider = ({ children }: PropsWithChildren) => { + const methods = useForm({ + mode: "all", + resolver: zodResolver(schemas.GuildCreationPayloadSchema), + defaultValues, + }) + + return {children} +} + +export { CreateGuildFormProvider } diff --git a/src/app/create-guild/_components/EmailFormField.tsx b/src/app/create-guild/_components/EmailFormField.tsx new file mode 100644 index 0000000000..01f780436c --- /dev/null +++ b/src/app/create-guild/_components/EmailFormField.tsx @@ -0,0 +1,54 @@ +import { + FormControl, + FormErrorMessage, + FormField, + FormItem, + FormLabel, +} from "@/components/ui/Form" +import { Input } from "@/components/ui/Input" +import useUser from "components/[guild]/hooks/useUser" +import { useEffect } from "react" +import { useFormContext, useWatch } from "react-hook-form" +import { CreateGuildFormType } from "../types" + +const EmailFormField = () => { + const { + control, + setValue, + formState: { touchedFields }, + } = useFormContext() + const { emails, platformUsers } = useUser() + + const providedEmail = useWatch({ control, name: "contacts.0.contact" }) + useEffect(() => { + if (!!providedEmail || touchedFields.contacts?.[0]?.contact) return + + const emailAddress = emails?.emailAddress + const googleEmailAddress = platformUsers?.find( + (pu) => pu.platformName === "GOOGLE" + )?.platformUserId + + if (!emailAddress && !googleEmailAddress) return + + setValue("contacts.0.contact", emailAddress ?? googleEmailAddress) + }, [touchedFields.contacts, emails, platformUsers, providedEmail, setValue]) + + return ( + ( + + Your e-mail + + + + + + + )} + /> + ) +} + +export { EmailFormField } diff --git a/src/components/create-guild/hooks/useCreateGuild.tsx b/src/app/create-guild/_hooks/useCreateGuild.ts similarity index 76% rename from src/components/create-guild/hooks/useCreateGuild.tsx rename to src/app/create-guild/_hooks/useCreateGuild.ts index feefdb0f04..4a7136f788 100644 --- a/src/components/create-guild/hooks/useCreateGuild.tsx +++ b/src/app/create-guild/_hooks/useCreateGuild.ts @@ -1,4 +1,5 @@ import { usePostHogContext } from "@/components/Providers/PostHogProvider" +import { useToast } from "@/components/ui/hooks/useToast" import { useYourGuilds } from "@/hooks/useYourGuilds" import { Schemas } from "@guildxyz/types" import processConnectorError from "components/[guild]/JoinModal/utils/processConnectorError" @@ -6,13 +7,12 @@ import useJsConfetti from "components/create-guild/hooks/useJsConfetti" import useMatchMutate from "hooks/useMatchMutate" import useShowErrorToast from "hooks/useShowErrorToast" import { SignedValidation, useSubmitWithSign } from "hooks/useSubmit" -import useToast from "hooks/useToast" -import { useRouter } from "next/router" +import { useRouter } from "next/navigation" import { Guild, GuildBase } from "types" import fetcher from "utils/fetcher" import getRandomInt from "utils/getRandomInt" import slugify from "utils/slugify" -import { CreateGuildFormType } from "../CreateGuildForm" +import { CreateGuildFormType } from "../types" const useCreateGuild = ({ onError, @@ -26,9 +26,9 @@ const useCreateGuild = ({ const { mutate: mutateYourGuilds } = useYourGuilds() const matchMutate = useMatchMutate() - const toast = useToast() + const { toast } = useToast() const showErrorToast = useShowErrorToast() - const triggerConfetti = useJsConfetti() + const triggerConfetti = useJsConfetti() // TODO: use the new confetti? const router = useRouter() const fetchData = async (signedValidation: SignedValidation): Promise => @@ -61,9 +61,9 @@ const useCreateGuild = ({ ) toast({ - title: `Guild successfully created!`, + title: "Guild successfully created!", description: "You're being redirected to its page", - status: "success", + variant: "success", }) onSuccess?.() router.push(`/${response_.urlName}`) @@ -72,32 +72,18 @@ const useCreateGuild = ({ return { ...useSubmitResponse, - /** - * Temporarily creating a default Member role, later the users will be able to - * pick from Guild Templates - */ + onSubmit: (data: CreateGuildFormType) => useSubmitResponse.onSubmit({ ...data, urlName: slugify(data.name), imageUrl: data.imageUrl || `/guildLogos/${getRandomInt(286)}.svg`, - roles: [ - { - name: "Member", - imageUrl: `/guildLogos/${getRandomInt(286)}.svg`, - requirements: [ - { - type: "FREE", - }, - ], - }, - ], } satisfies Schemas["GuildCreationPayload"]), } } -const mutateGuildsCache = (prev: GuildBase[], createdGuild: Guild) => [ - ...prev, +const mutateGuildsCache = (prev: GuildBase[] | undefined, createdGuild: Guild) => [ + ...(prev ?? []), { id: createdGuild.id, name: createdGuild.name, @@ -110,4 +96,4 @@ const mutateGuildsCache = (prev: GuildBase[], createdGuild: Guild) => [ }, ] -export default useCreateGuild +export { useCreateGuild } diff --git a/src/app/create-guild/page.tsx b/src/app/create-guild/page.tsx new file mode 100644 index 0000000000..cda9f3b740 --- /dev/null +++ b/src/app/create-guild/page.tsx @@ -0,0 +1,45 @@ +import { Header } from "@/components/Header" +import { Layout, LayoutBanner, LayoutHero, LayoutMain } from "@/components/Layout" +import svgToTinyDataUri from "mini-svg-data-uri" +import { CreateGuildCard } from "./_components/CreateGuildCard" +import { CreateGuildFormProvider } from "./_components/CreateGuildFormProvider" + +export const metadata = { + title: "Begin your guild", +} + +const Page = () => ( + + +
` + )}")`, + }} + /> + + + +
+
+ + +
+ + + + + + + +) + +export default Page diff --git a/src/app/create-guild/types.ts b/src/app/create-guild/types.ts new file mode 100644 index 0000000000..11cb102897 --- /dev/null +++ b/src/app/create-guild/types.ts @@ -0,0 +1,6 @@ +import { Schemas } from "@guildxyz/types" + +export type CreateGuildFormType = Pick< + Schemas["GuildCreationPayload"], + "name" | "urlName" | "imageUrl" | "contacts" | "roles" | "theme" +> diff --git a/src/components/create-guild/BasicInfo/components/ContactInfo.tsx b/src/components/create-guild/BasicInfo/components/ContactInfo.tsx index 61774aa32b..61e9ae8400 100644 --- a/src/components/create-guild/BasicInfo/components/ContactInfo.tsx +++ b/src/components/create-guild/BasicInfo/components/ContactInfo.tsx @@ -12,11 +12,11 @@ import { Text, } from "@chakra-ui/react" import { ArrowSquareOut, Plus, TrashSimple } from "@phosphor-icons/react" +import { CreateGuildFormType } from "app/create-guild/types" import Button from "components/common/Button" import ClientOnly from "components/common/ClientOnly" import FormErrorMessage from "components/common/FormErrorMessage" import StyledSelect from "components/common/StyledSelect" -import { CreateGuildFormType } from "components/create-guild/CreateGuildForm" import { Controller, useFieldArray, useFormContext } from "react-hook-form" import { SelectOption } from "types" diff --git a/src/components/create-guild/CreateGuildButton.tsx b/src/components/create-guild/CreateGuildButton.tsx deleted file mode 100644 index 3fd5675e2d..0000000000 --- a/src/components/create-guild/CreateGuildButton.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import { walletSelectorModalAtom } from "@/components/Providers/atoms" -import { useWeb3ConnectionManager } from "@/components/Web3ConnectionManager/hooks/useWeb3ConnectionManager" -import { Collapse, Stack } from "@chakra-ui/react" -import Button from "components/common/Button" -import { useSetAtom } from "jotai" -import { useFormContext } from "react-hook-form" -import { CreateGuildFormType } from "./CreateGuildForm" -import useCreateGuild from "./hooks/useCreateGuild" - -const CreateGuildButton = () => { - const { handleSubmit } = useFormContext() - const { onSubmit, isLoading } = useCreateGuild() - - const { isWeb3Connected } = useWeb3ConnectionManager() - const setIsWalletSelectorModalOpen = useSetAtom(walletSelectorModalAtom) - - return ( - - - - - - - ) -} - -export default CreateGuildButton diff --git a/src/components/create-guild/CreateGuildForm.tsx b/src/components/create-guild/CreateGuildForm.tsx deleted file mode 100644 index 78fee1a47c..0000000000 --- a/src/components/create-guild/CreateGuildForm.tsx +++ /dev/null @@ -1,115 +0,0 @@ -import { - Center, - FormControl, - FormLabel, - Heading, - Input, - Stack, -} from "@chakra-ui/react" -import { Schemas } from "@guildxyz/types" -import useUser from "components/[guild]/hooks/useUser" -import Card from "components/common/Card" -import FormErrorMessage from "components/common/FormErrorMessage" -import usePinata from "hooks/usePinata" -import { useEffect } from "react" -import { useFormContext, useWatch } from "react-hook-form" -import getColorByImage from "utils/getColorByImage" -import CreateGuildButton from "./CreateGuildButton" -import IconSelector from "./IconSelector" -import Name from "./Name" - -export type CreateGuildFormType = Pick< - Schemas["GuildCreationPayload"], - "name" | "imageUrl" | "contacts" | "theme" -> - -const CreateGuildForm = () => { - const { - control, - register, - setValue, - formState: { errors, touchedFields }, - } = useFormContext() - - const iconUploader = usePinata({ - fieldToSetOnSuccess: "imageUrl", - fieldToSetOnError: "imageUrl", - control, - }) - - const { emails, platformUsers } = useUser() - - const providedEmail = useWatch({ control, name: "contacts.0.contact" }) - useEffect(() => { - if (!!providedEmail || touchedFields.contacts?.[0]?.contact) return - - const emailAddress = emails?.emailAddress - const googleEmailAddress = platformUsers?.find( - (pu) => pu.platformName === "GOOGLE" - )?.platformUserId - - if (!emailAddress && !googleEmailAddress) return - - setValue("contacts.0.contact", emailAddress ?? googleEmailAddress) - }, [touchedFields.contacts, emails, platformUsers, providedEmail, setValue]) - - return ( - - - - Begin your guild - - -
- { - const generatedThemeColor = await getColorByImage(objectURL) - setValue("theme.color", generatedThemeColor) - }} - boxSize={28} - /> -
- - - Guild name - - - - - Your email - - - {errors.contacts?.[0]?.contact?.message} - - - - -
-
- ) -} - -export default CreateGuildForm diff --git a/src/components/create-guild/IconSelector/IconSelector.tsx b/src/components/create-guild/IconSelector/IconSelector.tsx index aada9e673d..59ac5cf266 100644 --- a/src/components/create-guild/IconSelector/IconSelector.tsx +++ b/src/components/create-guild/IconSelector/IconSelector.tsx @@ -24,13 +24,13 @@ import { useRadioGroup, } from "@chakra-ui/react" import { Image } from "@phosphor-icons/react" +import { CreateGuildFormType } from "app/create-guild/types" import LogicDivider from "components/[guild]/LogicDivider" import GuildLogo from "components/common/GuildLogo" import { Modal } from "components/common/Modal" import { Uploader } from "hooks/usePinata/usePinata" import React, { ComponentProps, useEffect } from "react" import { useController, useFormContext } from "react-hook-form" -import { CreateGuildFormType } from "../CreateGuildForm" import PhotoUploader from "./components/PhotoUploader" import SelectorButton from "./components/SelectorButton" import icons from "./icons.json" diff --git a/src/pages/create-guild.tsx b/src/pages/create-guild.tsx deleted file mode 100644 index 15e4c0a645..0000000000 --- a/src/pages/create-guild.tsx +++ /dev/null @@ -1,89 +0,0 @@ -import { Box, useColorModeValue } from "@chakra-ui/react" -import { ThemeProvider } from "components/[guild]/ThemeContext" -import ClientOnly from "components/common/ClientOnly" -import { Layout } from "components/common/Layout" -import CreateGuildForm, { - CreateGuildFormType, -} from "components/create-guild/CreateGuildForm" -import DynamicDevTool from "components/create-guild/DynamicDevTool" -import svgToTinyDataUri from "mini-svg-data-uri" -import { FormProvider, useForm } from "react-hook-form" - -const CreateGuildPage = (): JSX.Element => { - const methods = useForm({ - mode: "all", - defaultValues: { - name: "", - imageUrl: "", - contacts: [ - { - type: "EMAIL", - contact: "", - }, - ], - }, - }) - - const bgColor = useColorModeValue("var(--chakra-colors-gray-800)", "#1d1d1f") - const bgOpacity = useColorModeValue(0.06, 0.06) - const pageBgColor = useColorModeValue( - "var(--chakra-colors-gray-100)", - "var(--chakra-colors-gray-800)" - ) - const bgPatternColor = useColorModeValue("#c5c5ca", "#52525b") - - return ( - <> - - ` - )}")`} - bgPosition="top 16px left 0px" - minH="100vh" - > - - - - - - - - - - - - - - - - - ) -} - -const CreateGuildPageWrapper = (): JSX.Element => ( - - - -) - -export default CreateGuildPageWrapper