Skip to content

Commit

Permalink
Merge pull request #43 from ankit-tailor/feat/indentity-component
Browse files Browse the repository at this point in the history
Feat/indentity component
  • Loading branch information
viral-sangani authored Jan 13, 2025
2 parents b6f44d3 + abea3ff commit 98dab35
Show file tree
Hide file tree
Showing 8 changed files with 283 additions and 0 deletions.
34 changes: 34 additions & 0 deletions apps/storybook/stories/identity.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import type { Meta, StoryObj } from "@storybook/react";
import {
Avatar,
Balance,
Identity,
Name,
Social,
} from "@composer-kit/ui/identity";

const meta: Meta<typeof Identity> = {
component: Identity,
argTypes: {},
};

export default meta;

type Story = StoryObj<typeof Identity>;

export const Primary: Story = {
render: () => {
return (
<Identity address="0x" className="flex gap-2 items-center" token="cUSD">
<Avatar />
<div className="flex flex-col">
<Name />
<Balance />
</div>
<Social tag="twitter" />
</Identity>
);
},
name: "Indentity",
args: {},
};
39 changes: 39 additions & 0 deletions packages/composer-kit/src/identity/avatar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import React from "react";
import { useIdentity } from "./indentity";

function DefaultAvatar(): JSX.Element {
return (
<svg
fill="none"
height="40"
viewBox="0 0 40 40"
width="40"
xmlns="http://www.w3.org/2000/svg"
>
<path
clipRule="evenodd"
d="M5.00778 17.7143C3.16266 16.0662 2.0011 13.6687 2.0011 11C2.0011 6.02944 6.03054 2 11.0011 2C15.9717 2 20.0011 6.02944 20.0011 11C20.0011 13.6687 18.8395 16.0662 16.9944 17.7143C16.88 15.2738 15.3075 13.2145 13.1284 12.388C14.2534 11.6802 15.0011 10.4274 15.0011 9C15.0011 6.79086 13.2102 5 11.0011 5C8.79196 5 7.0011 6.79086 7.0011 9C7.0011 10.4274 7.74875 11.6802 8.87381 12.388C6.69469 13.2145 5.12219 15.2738 5.00778 17.7143ZM5.57936 20.5732C2.24834 18.6827 0.00109863 15.1038 0.00109863 11C0.00109863 4.92487 4.92597 0 11.0011 0C17.0762 0 22.0011 4.92487 22.0011 11C22.0011 15.1038 19.7539 18.6827 16.4228 20.5732C16.3637 20.6068 16.3042 20.6399 16.2444 20.6724C14.6857 21.5191 12.8996 22 11.0011 22C9.02974 22 7.1795 21.4814 5.57936 20.5732Z"
fill="currentColor"
fillRule="evenodd"
/>
</svg>
);
}

export function Avatar(): JSX.Element {
const { avatar, name: ensName } = useIdentity();

return (
<>
{avatar ? (
<img
alt={`${ensName}-avatar`}
className="rounded-full h-10 w-10"
src={avatar}
/>
) : (
<DefaultAvatar />
)}
</>
);
}
26 changes: 26 additions & 0 deletions packages/composer-kit/src/identity/balance.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { useBalance } from "../core/internal/hooks/use-balance";
import { UTILITY_TOKENS } from "../core/internal/utils/constants";
import { cn } from "../utils/helper";
import { useIdentity } from "./indentity";

interface BalanceProps extends React.HTMLAttributes<HTMLParagraphElement> {
precison?: number;
}

export function Balance({
className,
precison = 3,
...props
}: BalanceProps): JSX.Element | null {
const { address, token } = useIdentity();
const { balance } = useBalance({
address,
tokenMetaData: UTILITY_TOKENS[token],
});

return (
<p {...props} className={cn("text-sm", className)}>
{`${parseFloat(balance).toFixed(precison)} ${token}`}
</p>
);
}
55 changes: 55 additions & 0 deletions packages/composer-kit/src/identity/hooks/use-social.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { useState, useEffect } from "react";
import { mainnet } from "viem/chains";
import { normalize } from "viem/ens";
import { getPublicClient } from "../../core/internal/config/viem-public-client";

interface SocialProps {
ensName: string;
tag: "github" | "twitter" | "url" | "farcaster";
}

const getSocialUrl = (tag: string, data: string) => {
switch (tag) {
case "github":
return `https://github.com/${data}`;
case "twitter":
return `https://x.com/${data}`;
case "url":
return data;
case "farcaster":
return `https://warpcast.com/${data}`;
default:
return null;
}
};

export const useSocial = ({ ensName, tag }: SocialProps) => {
const [socialData, setSocialData] = useState<{
tag: string;
url: string | null;
} | null>(null);

useEffect(() => {
const fetchSocialData = async () => {
const client = getPublicClient(mainnet);

const data = await client.getEnsText({
name: normalize(ensName),
key: `com.${tag}`,
});

if (data) {
setSocialData({
url: getSocialUrl(tag, data),
tag,
});
} else {
setSocialData(null);
}
};

fetchSocialData();
}, [ensName]);

return socialData;
};
73 changes: 73 additions & 0 deletions packages/composer-kit/src/identity/indentity.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import React, {
createContext,
useContext,
useMemo,
type HTMLAttributes,
} from "react";
import { type Address } from "viem";
import { mainnet } from "viem/chains";
import { useEnsAvatar, useEnsName } from "wagmi";

interface IdentityProps extends HTMLAttributes<HTMLDivElement> {
address: Address;
token?: "CELO" | "cUSD" | "USDT";
}

interface IdentityContextType {
address: Address;
name: string;
avatar: string;
balance: string;
token: "CELO" | "cUSD" | "USDT";
}

const IdentityContext = createContext<IdentityContextType | null>(null);

export function Identity({
children,
token = "CELO",
address,
...props
}: IdentityProps): JSX.Element {
const { data: ensName, isLoading: isEnsLoading } = useEnsName({
address,
chainId: mainnet.id,
query: {
enabled: true,
},
});
const { data: avatar, isLoading: isAvatarLoading } = useEnsAvatar({
name: ensName ?? "",
chainId: mainnet.id,
});

const contextValue = useMemo(() => {
return {
address,
name: ensName ?? "",
avatar: avatar ?? "",
balance: "0",
token,
};
}, [address, avatar, ensName, token]);

if (isEnsLoading || isAvatarLoading) {
return <div />;
}

return (
<IdentityContext.Provider value={contextValue}>
<div {...props}> {children}</div>
</IdentityContext.Provider>
);
}

export const useIdentity = (): IdentityContextType => {
const context = useContext(IdentityContext);

if (!context) {
throw new Error("useIdentity must be used within an IdentityProvider");
}

return context;
};
5 changes: 5 additions & 0 deletions packages/composer-kit/src/identity/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export { Identity } from "./indentity";
export { Avatar } from "./avatar";
export { Name } from "./name";
export { Balance } from "./balance";
export { Social } from "./social";
24 changes: 24 additions & 0 deletions packages/composer-kit/src/identity/name.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { cn } from "../utils/helper";
import { useIdentity } from "./indentity";

interface NameProps extends React.HTMLAttributes<HTMLParagraphElement> {
isTruncated?: boolean;
}

const getTruncatedAddress = (address: string): string => {
return `${address.slice(0, 6)}...${address.slice(-4)}`;
};

export function Name({
isTruncated = true,
className,
...props
}: NameProps): JSX.Element | null {
const { address, name } = useIdentity();

return (
<p {...props} className={cn("font-bold text-base", className)}>
{name || isTruncated ? getTruncatedAddress(address) : address}
</p>
);
}
27 changes: 27 additions & 0 deletions packages/composer-kit/src/identity/social.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { useSocial } from "./hooks/use-social";
import { useIdentity } from "./indentity";

interface SocialProps extends React.HTMLAttributes<HTMLAnchorElement> {
tag: "github" | "twitter" | "url" | "farcaster";
}

export function Social({
className,
tag,
children,
...props
}: SocialProps): JSX.Element | null {
const { name } = useIdentity();
const data = useSocial({
ensName: name,
tag,
});

if (!data) return null;

return (
<a href={data?.url ?? "#"} target="_blank" className={className} {...props}>
{children ?? tag}
</a>
);
}

0 comments on commit 98dab35

Please sign in to comment.