Skip to content

Commit

Permalink
Merge pull request #172 from blockful-io/170-feat-create-new-subdomai…
Browse files Browse the repository at this point in the history
…n-flow-db-resolver

170 feat create new subdomain flow db resolver
  • Loading branch information
eduramme authored Sep 5, 2024
2 parents a488062 + 69aee77 commit 9757d5d
Show file tree
Hide file tree
Showing 9 changed files with 2,010 additions and 110 deletions.
176 changes: 145 additions & 31 deletions components/organisms/CreateSubdomainModalContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ import { buildENSName } from "@namehash/ens-utils";
import { normalize } from "viem/ens";
import { useState } from "react";
import toast from "react-hot-toast";
import { Address } from "viem";
import { Address, isAddress } from "viem";
import { useAccount } from "wagmi";
import { NewSubdomainInfo } from "./NewSubdomainInfo";

interface CreateSubdomainModalContentProps {
name: string;
Expand All @@ -15,6 +16,13 @@ interface CreateSubdomainModalContentProps {
alreadyCreatedSubdomains?: string[];
}

enum CreateSubdomainModalSteps {
SubdomainInput = "SubdomainInput",
ProfileSettings = "ProfileSettings",
Confirmation = "Confirmation",
Success = "Success",
}

export const CreateSubdomainModalContent = ({
currentResolverAddress,
onCloseModal,
Expand All @@ -23,9 +31,14 @@ export const CreateSubdomainModalContent = ({
alreadyCreatedSubdomains,
}: CreateSubdomainModalContentProps) => {
const [newSubdomain, setNewSubdomain] = useState<string>("");
const [transactionSuccess, setTransactionSuccess] = useState(false);
const [subdomainAddress, setSubdomainAddress] = useState<string>("");
const [website, setWebsite] = useState<string>("");
const [description, setDescription] = useState<string>("");
const [isLoading, setIsloading] = useState(false);
const authedUser = useAccount();
const [currentStep, setCurrentStep] = useState<CreateSubdomainModalSteps>(
CreateSubdomainModalSteps.SubdomainInput
);

const isSubdomainInvalid = () => {
try {
Expand All @@ -47,11 +60,14 @@ export const CreateSubdomainModalContent = ({
resolverAddress: currentResolverAddress,
signerAddress: authedUser.address!,
name: `${newSubdomain}.${name}`,
address: subdomainAddress,
website: website,
description: description,
});
if (response?.ok) {
!!onRecordsEdited && onRecordsEdited();
setTransactionSuccess(true);
toast.success("Subdomain created successfully 🙂");
setCurrentStep(CreateSubdomainModalSteps.Success);
}
} catch (error) {
console.log(error);
Expand All @@ -60,40 +76,127 @@ export const CreateSubdomainModalContent = ({
setIsloading(false);
};

var urlRegex =
/^(?:([A-Za-z]+):)?(\/{0,3})([0-9.\-A-Za-z]+)(?::(\d+))?(?:\/([^?#]*))?(?:\?([^#]*))?(?:#(.*))?$/;

// Map each step to a corresponding JSX element
const stepComponents: Record<CreateSubdomainModalSteps, JSX.Element> = {
[CreateSubdomainModalSteps.SubdomainInput]: (
<Input
clearable
label={"subdomain"}
placeholder={""}
type="text"
value={newSubdomain}
onChange={(e) => setNewSubdomain(e.target.value.toLowerCase())}
suffix={`.${name}`}
error={isSubdomainInvalid()}
/>
),
[CreateSubdomainModalSteps.ProfileSettings]: (
<>
<p className="text-gray-400">
Adjust your information and personalize your profile.
</p>
<Input
clearable
label={"ETH Address"}
placeholder={""}
type="text"
value={subdomainAddress}
onChange={(e) => setSubdomainAddress(e.target.value.toLowerCase())}
error={
subdomainAddress.length &&
!isAddress(subdomainAddress) &&
"Invalid Address"
}
/>
<Input
clearable
label={"Website"}
placeholder={""}
type="text"
value={website}
onChange={(e) => setWebsite(e.target.value.toLowerCase())}
error={
website !== "" && !website.match(urlRegex) && "Invalid Website"
}
/>
<Input
clearable
label={"Description"}
placeholder={""}
type="text"
value={description}
onChange={(e) => setDescription(e.target.value.toLowerCase())}
/>
</>
),
[CreateSubdomainModalSteps.Confirmation]: (
<>
<NewSubdomainInfo
domain={`${newSubdomain}.${name}`}
description={description}
website={website}
ethAddress={subdomainAddress}
/>
</>
),
[CreateSubdomainModalSteps.Success]: (
<>
<p className="text-7xl"> 🎉</p>
<div>
<p className="text-lg">
<span className="font-bold">Congratulations!</span>
</p>
<p>New subdomain created!</p>
</div>
</>
),
};

// Map each step to a corresponding validation function
const stepValidation: Record<CreateSubdomainModalSteps, () => boolean> = {
[CreateSubdomainModalSteps.SubdomainInput]: () => {
return !!newSubdomain.length;
},
[CreateSubdomainModalSteps.ProfileSettings]: () => {
return subdomainAddress === "" || isAddress(subdomainAddress);
},
[CreateSubdomainModalSteps.Confirmation]: () => true,
[CreateSubdomainModalSteps.Success]: () => true,
};

const handleNextStep = () => {
switch (currentStep) {
case CreateSubdomainModalSteps.SubdomainInput:
setCurrentStep(CreateSubdomainModalSteps.ProfileSettings);
break;
case CreateSubdomainModalSteps.ProfileSettings:
setCurrentStep(CreateSubdomainModalSteps.Confirmation);
break;
case CreateSubdomainModalSteps.Confirmation:
setCurrentStep(CreateSubdomainModalSteps.Success);
break;
case CreateSubdomainModalSteps.Success:
setCurrentStep(CreateSubdomainModalSteps.SubdomainInput);
break;
}
};

return (
<div className="w-[480px] border rounded-xl overflow-hidden">
<div className="py-5 px-6 flex justify-between w-full bg-gray-50 border-b font-semibold text-black">
New subdomain
</div>
<div className="bg-white text-black border-b border-gray-200 p-6 flex flex-col gap-4">
{transactionSuccess ? (
<>
<p className="text-7xl"> 🎉</p>
<div>
<p className="text-lg">
<span className="font-bold">Congratulations!</span>
</p>
<p>New subdomain created!</p>
</div>
</>
) : (
<Input
clearable
label={"subdomain"}
placeholder={""}
type="text"
value={newSubdomain}
onChange={(e) => setNewSubdomain(e.target.value.toLowerCase())}
suffix={`.${name}`}
error={isSubdomainInvalid()}
/>
)}
{stepComponents[currentStep]}

{isLoading && <h1>Check your wallet</h1>}
</div>

<div className="py-5 px-6 flex justify-end w-full bg-white gap-4">
{transactionSuccess ? (
{currentStep === CreateSubdomainModalSteps.Success ? (
<>
<div>
<Button onClick={onCloseModal}>Done</Button>
Expand All @@ -106,11 +209,22 @@ export const CreateSubdomainModalContent = ({
Cancel
</Button>
</div>
<div>
<Button disabled={isLoading} onClick={handleSaveAction}>
{isLoading ? <Spinner color="blue" /> : "Save"}
</Button>
</div>
{currentStep === CreateSubdomainModalSteps.Confirmation ? (
<div>
<Button disabled={isLoading} onClick={handleSaveAction}>
{isLoading ? <Spinner color="blue" /> : "Save"}
</Button>
</div>
) : (
<div>
<Button
disabled={isLoading || !stepValidation[currentStep]()}
onClick={handleNextStep}
>
Next
</Button>
</div>
)}
</>
)}
</div>
Expand Down
52 changes: 52 additions & 0 deletions components/organisms/NewSubdomainInfo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { EthSVG } from "@ensdomains/thorin";
import Avatar from "boring-avatars";

interface NewSubdomainInfoProps {
domain: string;
website: string;
description: string;
ethAddress: string;
}

export const NewSubdomainInfo = ({
domain = "new domain",
website,
description,
ethAddress,
}: NewSubdomainInfoProps) => {
return (
<>
<p className="text-gray-400">
Check your information before confirming in your wallet
</p>
<div className="rounded-md flex flex-col border border-gray-200 overflow-hidden">
<div className="flex flex-col p-4 border-b border-gray-200 bg-gray-50 gap-4">
<div className="flex gap-4">
<Avatar
size={40}
square
name="Margaret Bourke"
variant="marble"
colors={["#44BCF0", "#7298F8", "#A099FF", "#FFFFFF"]}
/>
<div className="flex flex-col">
<p className="font-bold">{domain}</p>
<p className=" text-blue-500 text-sm">{website}</p>
</div>
</div>
<p>{description}</p>
</div>

<div className="p-4 flex flex-col">
<div className="flex w-full justify-between">
<div className="flex items-center gap-2">
<EthSVG className="w-4 h-4 text-gray-400" />
<p className="text-gray-400">ETH Address</p>
</div>
<p className="max-w-32 truncate">{ethAddress}</p>
</div>
</div>
</div>
</>
);
};
1,724 changes: 1,723 additions & 1 deletion lib/abi/database-resolver.json

Large diffs are not rendered by default.

Loading

0 comments on commit 9757d5d

Please sign in to comment.