Skip to content

Commit

Permalink
feat: migrate the /create-guild page without the icon selector
Browse files Browse the repository at this point in the history
  • Loading branch information
BrickheadJohnny committed Aug 14, 2024
1 parent b97993d commit f2cf7a2
Show file tree
Hide file tree
Showing 12 changed files with 281 additions and 277 deletions.
49 changes: 49 additions & 0 deletions src/app/create-guild/_components/CreateGuildButton.tsx
Original file line number Diff line number Diff line change
@@ -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<CreateGuildFormType>()
const { onSubmit, isLoading } = useCreateGuild()

const { isWeb3Connected } = useWeb3ConnectionManager()
const setIsWalletSelectorModalOpen = useSetAtom(walletSelectorModalAtom)

return (
<div className="flex flex-col gap-2">
<Collapsible open={!isWeb3Connected}>
<CollapsibleContent>
<Button
colorScheme="info"
size="xl"
disabled={isWeb3Connected}
onClick={() => setIsWalletSelectorModalOpen(true)}
className="w-full"
>
Connect wallet
</Button>
</CollapsibleContent>
</Collapsible>

<Button
colorScheme="success"
size="xl"
isLoading={isLoading}
loadingText="Creating guild"
onClick={handleSubmit(onSubmit)}
disabled={!isWeb3Connected}
>
Create guild
</Button>
</div>
)
}

export { CreateGuildButton }
66 changes: 66 additions & 0 deletions src/app/create-guild/_components/CreateGuildCard.tsx
Original file line number Diff line number Diff line change
@@ -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<CreateGuildFormType>()

return (
<Card className="flex flex-col px-5 py-6 shadow-lg md:px-6">
<h2 className="mb-7 text-center font-display font-extrabold text-2xl">
Begin your guild
</h2>

{/* <Center mb="6">
<IconSelector
uploader={iconUploader}
minW={512}
minH={512}
onGeneratedBlobChange={async (objectURL) => {
const generatedThemeColor = await getColorByImage(objectURL)
setValue("theme.color", generatedThemeColor)
}}
boxSize={28}
/>
</Center> */}

<div className="mb-8 flex flex-col gap-4">
<FormField
control={control}
name="name"
render={({ field }) => (
<FormItem>
<FormLabel>Guild name</FormLabel>
<FormControl>
<Input size="lg" {...field} />
</FormControl>

<FormErrorMessage />
</FormItem>
)}
/>

<EmailFormField />
</div>

<CreateGuildButton />
</Card>
)
}

export { CreateGuildCard }
48 changes: 48 additions & 0 deletions src/app/create-guild/_components/CreateGuildFormProvider.tsx
Original file line number Diff line number Diff line change
@@ -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<CreateGuildFormType>({
mode: "all",
resolver: zodResolver(schemas.GuildCreationPayloadSchema),
defaultValues,
})

return <FormProvider {...methods}>{children}</FormProvider>
}

export { CreateGuildFormProvider }
54 changes: 54 additions & 0 deletions src/app/create-guild/_components/EmailFormField.tsx
Original file line number Diff line number Diff line change
@@ -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<CreateGuildFormType>()
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 (
<FormField
control={control}
name="contacts.0.contact"
render={({ field }) => (
<FormItem>
<FormLabel>Your e-mail</FormLabel>
<FormControl>
<Input size="lg" {...field} />
</FormControl>

<FormErrorMessage />
</FormItem>
)}
/>
)
}

export { EmailFormField }
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
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"
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,
Expand All @@ -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<Guild> =>
Expand Down Expand Up @@ -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}`)
Expand All @@ -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,
Expand All @@ -110,4 +96,4 @@ const mutateGuildsCache = (prev: GuildBase[], createdGuild: Guild) => [
},
]

export default useCreateGuild
export { useCreateGuild }
45 changes: 45 additions & 0 deletions src/app/create-guild/page.tsx
Original file line number Diff line number Diff line change
@@ -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 = () => (
<CreateGuildFormProvider>
<Layout>
<div
className="-z-10 absolute inset-0 opacity-40 dark:opacity-60"
style={{
background: `radial-gradient(ellipse at center, transparent -250%, hsl(var(--background)) 80%), url("${svgToTinyDataUri(
`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="30" height="30" fill="none" stroke="#666"><path d="M0 .5H31.5V32"/></svg>`
)}")`,
}}
/>

<LayoutHero className="pb-52">
<LayoutBanner className="border-b border-dashed bg-banner-dark">
<div className="absolute inset-0 bg-[auto_115%] bg-[top_5px_right_0] bg-[url('/banner.svg')] bg-repeat opacity-5" />
<div
className="absolute inset-0"
style={{
background:
"radial-gradient(circle at bottom, transparent 5%, hsl(var(--banner-dark)))",
}}
/>
</LayoutBanner>

<Header />
</LayoutHero>

<LayoutMain className="max-w-lg">
<CreateGuildCard />
</LayoutMain>
</Layout>
</CreateGuildFormProvider>
)

export default Page
6 changes: 6 additions & 0 deletions src/app/create-guild/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { Schemas } from "@guildxyz/types"

export type CreateGuildFormType = Pick<
Schemas["GuildCreationPayload"],
"name" | "urlName" | "imageUrl" | "contacts" | "roles" | "theme"
>
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down
Loading

0 comments on commit f2cf7a2

Please sign in to comment.