Skip to content

Commit

Permalink
Merge pull request #174 from blockful-io/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
FrancoAguzzi authored Sep 5, 2024
2 parents e39dcde + 9757d5d commit d7a67ed
Show file tree
Hide file tree
Showing 14 changed files with 2,057 additions and 144 deletions.
31 changes: 15 additions & 16 deletions components/02-molecules/edit/FieldsContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import React, { createContext, useContext, useState, ReactNode } from "react";
import { isAddress } from "viem";
import _ from "lodash";
import validateBitcoinAddress from "bitcoin-address-validation";
import { DomainData, TextRecords } from "@/lib/domain-page";
import { DomainData } from "@/lib/domain-page";

interface FieldsContextType {
profileFields: Field[];
Expand Down Expand Up @@ -320,13 +320,10 @@ const FieldsProvider: React.FC<FieldsProviderProps> = ({ children }) => {

// Update profile fields with corresponding text values
const newProfileFields: Field[] = profileFields.map((field) => {
if (textsKeys.includes(field.label)) {
return {
...field,
value: String(texts[field.label]),
};
}
return field;
return {
...field,
value: String(texts[field.label] ?? ""),
};
});

// Get the names of the coins from the addresses, or an empty array if no addresses exist
Expand All @@ -351,22 +348,24 @@ const FieldsProvider: React.FC<FieldsProviderProps> = ({ children }) => {
};
}

return addressField; // Return the address field unchanged if no match is found
return {
...addressField,
value: "",
}; // Return the address field unchanged if no match is found
});
const newAccountsFields = accountsFields.map((field) => {
if (textsKeys.includes(field.label)) {
return {
...field,
value: String(texts[field.label]),
};
}
return field;
return {
...field,
value: String(texts[field.label] ?? ""),
};
});

const newFieldsByTab = {
[Tab.Profile]: newProfileFields,
[Tab.Accounts]: newAccountsFields,
[Tab.Addresses]: populatedAddressFields,
};

[Tab.Profile, Tab.Accounts, Tab.Addresses].forEach((tab) => {
setFields(tab, newFieldsByTab[tab]);
setInitialFields(tab, newFieldsByTab[tab]);
Expand Down
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>
</>
);
};
12 changes: 7 additions & 5 deletions components/organisms/ProfileTabBody.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -133,11 +133,13 @@ export const ProfileTabBody = ({ domainData }: ProfileTabProps) => {
)}
</Skeleton>
<Skeleton>
<ProfileRecordItem
icon={EthTransparentSVG}
label="parent"
text="ETH"
/>
{domainData?.parent && (
<ProfileRecordItem
icon={EthTransparentSVG}
label="parent"
text={domainData.parent}
/>
)}
</Skeleton>
</div>
</div>
Expand Down
1 change: 0 additions & 1 deletion components/organisms/SubdomainsTabBody.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@ export const SubdomainsTabBody = ({
<Skeleton>
{subdomainsArray?.length ? (
<Table
clickable={false}
title="Names"
names={subdomainsArray}
withRoleColumn={false}
Expand Down
Loading

0 comments on commit d7a67ed

Please sign in to comment.