-
Notifications
You must be signed in to change notification settings - Fork 2
feat(namekit-react): Identity #439
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
base: main
Are you sure you want to change the base?
Changes from 2 commits
db46f78
450998f
1d92fb1
105a487
ef9dbc5
3ddd190
ec2cb9c
752ac3a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,59 @@ | ||
| import React from "react"; | ||
| import type { Meta, StoryObj } from "@storybook/react"; | ||
| import { Identity } from "@namehash/namekit-react/client"; | ||
|
|
||
| const meta: Meta<typeof Identity.Root> = { | ||
| title: "Namekit/Identity", | ||
| component: Identity.Root, | ||
| argTypes: { | ||
| address: { control: "text" }, | ||
| network: { | ||
| control: { | ||
| type: "select", | ||
| options: ["mainnet", "sepolia"], | ||
| }, | ||
| }, | ||
| className: { control: "text" }, | ||
| }, | ||
| }; | ||
|
|
||
| export default meta; | ||
|
|
||
| type Story = StoryObj<typeof Identity.Root>; | ||
|
|
||
| const IdentityCard: React.FC<{ | ||
| address: string; | ||
| network?: "mainnet" | "sepolia"; | ||
| }> = ({ address, network }) => ( | ||
| <Identity.Root address={address} network={network}> | ||
| <Identity.Avatar /> | ||
| <Identity.Name /> | ||
| <Identity.Address /> | ||
| <Identity.NameGuardShield /> | ||
| </Identity.Root> | ||
| ); | ||
|
|
||
| export const Default: Story = { | ||
| args: { | ||
| address: "0x838aD0EAE54F99F1926dA7C3b6bFbF617389B4D9", | ||
| network: "mainnet", | ||
| className: "rounded-xl", | ||
| }, | ||
| render: (args) => <IdentityCard {...args} />, | ||
| }; | ||
|
|
||
| export const MultipleCards: Story = { | ||
| render: () => ( | ||
| <> | ||
| <IdentityCard address="0x838aD0EAE54F99F1926dA7C3b6bFbF617389B4D9" /> | ||
| <IdentityCard address="0x123456789abcdef123456789abcdef123456789a" /> | ||
| <IdentityCard address="0xabcdef123456789abcdef123456789abcdef1234" /> | ||
| <IdentityCard address="0x987654321fedcba987654321fedcba987654321f" /> | ||
| <IdentityCard | ||
| address="0xfedcba987654321fedcba987654321fedcba9876" | ||
| network="sepolia" | ||
| /> | ||
| <IdentityCard address="0x111222333444555666777888999000aaabbbcccd" /> | ||
| </> | ||
| ), | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -43,6 +43,7 @@ | |
| "@headlessui/react": "1.7.17", | ||
| "@namehash/ens-utils": "workspace:*", | ||
| "@namehash/ens-webfont": "workspace:*", | ||
| "@namehash/nameguard": "workspace:*", | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Your suggestion to make this a peer dependency sounds good 👍
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. FYI: This hasn't been changed yet. |
||
| "classcat": "5.0.5" | ||
| }, | ||
| "devDependencies": { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,145 @@ | ||
| import React, { | ||
| createContext, | ||
| useContext, | ||
| useState, | ||
| useEffect, | ||
| ReactNode, | ||
| } from "react"; | ||
| import { | ||
| createClient, | ||
| Network, | ||
| type SecurePrimaryNameResult, | ||
| } from "@namehash/nameguard"; | ||
|
|
||
| interface IdentityContextType extends SecurePrimaryNameResult { | ||
| address: string; | ||
| } | ||
|
|
||
| const IdentityContext = createContext<IdentityContextType | null>(null); | ||
|
|
||
| const useIdentity = () => { | ||
| const context = useContext(IdentityContext); | ||
|
|
||
| if (!context) { | ||
| throw new Error("useIdentity must be used within an IdentityProvider"); | ||
| } | ||
|
|
||
| return context; | ||
| }; | ||
|
|
||
| interface SubComponentProps { | ||
| className?: string; | ||
| children?: ReactNode; | ||
| } | ||
|
|
||
| interface RootProps { | ||
| address: string; | ||
| network?: Network; | ||
| className?: string; | ||
| children: ReactNode; | ||
| } | ||
|
|
||
| const Root = ({ | ||
| address, | ||
| network = "mainnet", | ||
| className, | ||
| children, | ||
| ...props | ||
| }: RootProps) => { | ||
| const [data, setData] = useState<IdentityContextType | null>(null); | ||
| const [loading, setLoading] = useState(true); | ||
| const [error, setError] = useState<string | null>(null); | ||
|
|
||
| useEffect(() => { | ||
| const fetchData = async () => { | ||
| try { | ||
| setLoading(true); | ||
|
|
||
| const nameguard = createClient({ network }); | ||
|
|
||
| const result = await nameguard.getSecurePrimaryName(address, { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Suggest that we add ReactQuery as a dependency / peer dependency for NameKit React. The web3 community has generally adopted ReactQuery as an unofficial standard. There's a ton of various libraries and packages already using it or built on it. For example, strongly suggest having a look at the following:
In fact, suggest that we not only add ReactQuery as a dependency, but we also add wagmi as a dependency. We shouldn't try to reinvent the whole universe. Pretty much everyone who might use NameKit React is also going to be using wagmi, so we better make it convenient for everything to work together. |
||
| returnNameGuardReport: true, | ||
notrab marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| }); | ||
|
|
||
| setData({ ...result, address }); | ||
| } catch (err) { | ||
| setError( | ||
| err instanceof Error ? err.message : "An unknown error occurred", | ||
| ); | ||
| } finally { | ||
| setLoading(false); | ||
| } | ||
| }; | ||
|
|
||
| fetchData(); | ||
| }, [address, network]); | ||
|
|
||
| if (loading) return <div>Loading...</div>; | ||
| if (error) return <div>Error: {error}</div>; | ||
| if (!data) return null; | ||
|
||
|
|
||
| return ( | ||
| <IdentityContext.Provider value={data}> | ||
| <div className={`namekit-identity ${className}`} {...props}> | ||
| {children} | ||
| </div> | ||
| </IdentityContext.Provider> | ||
| ); | ||
| }; | ||
|
|
||
| const Avatar = ({ className, children, ...props }: SubComponentProps) => { | ||
| const { display_name } = useIdentity(); | ||
|
|
||
| return ( | ||
| <div className={`namekit-avatar ${className}`} {...props}> | ||
| <img | ||
| src={`https://avatar.example.com/${display_name}`} | ||
| alt={display_name} | ||
| /> | ||
| {children} | ||
| </div> | ||
| ); | ||
| }; | ||
|
|
||
| const Name = ({ className, ...props }: SubComponentProps) => { | ||
| const { display_name } = useIdentity(); | ||
|
|
||
| return ( | ||
| <div className={`namekit-name ${className}`} {...props}> | ||
| {display_name} | ||
| </div> | ||
| ); | ||
| }; | ||
|
|
||
| const Address = ({ className, ...props }: SubComponentProps) => { | ||
| const { address } = useIdentity(); | ||
|
|
||
| return ( | ||
| <div className={`namekit-address ${className}`} {...props}> | ||
| {address} | ||
| </div> | ||
| ); | ||
| }; | ||
|
|
||
| const NameGuardShield = ({ className, ...props }: SubComponentProps) => { | ||
| const { nameguard_report } = useIdentity(); | ||
|
|
||
| return ( | ||
| <div className={`namekit-nameguard-shield ${className}`} {...props}> | ||
| <div className="namekit-nameguard-rating"> | ||
| Rating: {nameguard_report?.rating} | ||
| </div> | ||
| <div className="namekit-nameguard-risk-count"> | ||
| Risks: {nameguard_report?.risk_count} | ||
| </div> | ||
| </div> | ||
| ); | ||
| }; | ||
|
|
||
| export const Identity = { | ||
| Root, | ||
| Avatar, | ||
| Name, | ||
| Address, | ||
| NameGuardShield, | ||
| }; | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please check all places in the code where you have an address param. These should all use the
Addresstype defined in viem.