Skip to content
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

feat: add user connection status avatar and dropdown #977

Merged
merged 8 commits into from
Sep 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion examples/ui-demo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,15 @@
"@account-kit/core": "^4.0.0-beta.0",
"@account-kit/infra": "^4.0.0-beta.0",
"@account-kit/react": "^4.0.0-beta.0",
"@phun-ky/moebius": "^1.0.6",
"@radix-ui/react-popover": "^1.0.7",
"@radix-ui/react-switch": "^1.0.3",
"@radix-ui/react-tooltip": "^1.1.2",
"@t3-oss/env-core": "^0.7.1",
"@t3-oss/env-nextjs": "^0.7.1",
"@tanstack/react-query": "^5.28.9",
"@uiw/react-color": "^2.3.0",
"boring-avatars": "^1.11.2",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1",
"dedent": "^1.5.3",
Expand All @@ -29,8 +31,8 @@
"react-dom": "^18",
"react-syntax-highlighter": "^15.5.0",
"tailwind-merge": "^2.3.0",
"wagmi": "^2.12.7",
"viem": "^2.20.0",
"wagmi": "^2.12.7",
"zod": "^3.0.0"
},
"devDependencies": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ export const Authentication = ({ className }: { className?: string }) => {
/>
<AuthMethod
className="flex-0 shrink-0 grow min-w-full"
icon={<BiometricIcon/>}
icon={<BiometricIcon />}
iconClassName="mt-[2px] self-start"
name="Passkeys"
details={
Expand Down
20 changes: 20 additions & 0 deletions examples/ui-demo/src/components/icons/check-icon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { SVGProps } from "react";

export const CheckIcon = ({
fill = "#16A34A",
...props
}: JSX.IntrinsicAttributes & SVGProps<SVGSVGElement>) => (
<svg
width="100%"
height="100%"
viewBox="0 0 18 18"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<path
fill={fill}
d="M13.1667 1.78357C14.4236 2.50928 15.4691 3.55067 16.1999 4.80465C16.9306 6.05863 17.3211 7.48171 17.3329 8.93302C17.3446 10.3843 16.9771 11.8135 16.2667 13.0791C15.5563 14.3448 14.5277 15.4029 13.2827 16.1488C12.0377 16.8948 10.6195 17.3026 9.16842 17.332C7.71736 17.3613 6.2838 17.0112 5.00963 16.3163C3.73547 15.6213 2.6649 14.6057 1.90389 13.3698C1.14289 12.134 0.717846 10.7208 0.670839 9.27024L0.666672 9.00024L0.670839 8.73024C0.717508 7.29106 1.13629 5.88854 1.88637 4.6594C2.63645 3.43026 3.69222 2.41645 4.95075 1.7168C6.20928 1.01716 7.62763 0.655567 9.06751 0.667267C10.5074 0.678968 11.9197 1.06357 13.1667 1.78357ZM12.0892 6.7444C11.9457 6.60092 11.7548 6.51473 11.5522 6.502C11.3497 6.48926 11.1495 6.55086 10.9892 6.67524L10.9108 6.7444L8.16667 9.48774L7.08917 8.41107L7.01084 8.3419C6.85049 8.21761 6.65031 8.15609 6.44784 8.16886C6.24537 8.18164 6.05451 8.26783 5.91106 8.41129C5.7676 8.55474 5.68141 8.7456 5.66863 8.94807C5.65586 9.15054 5.71738 9.35072 5.84167 9.51107L5.91084 9.5894L7.57751 11.2561L7.65584 11.3252C7.80198 11.4386 7.9817 11.5002 8.16667 11.5002C8.35165 11.5002 8.53136 11.4386 8.67751 11.3252L8.75584 11.2561L12.0892 7.92274L12.1583 7.8444C12.2827 7.68407 12.3443 7.48386 12.3316 7.28133C12.3188 7.07881 12.2327 6.8879 12.0892 6.7444Z"
/>
</svg>
);
23 changes: 23 additions & 0 deletions examples/ui-demo/src/components/icons/chevron-down.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { SVGProps } from "react";

export const ChevronDown = ({
stroke = "#020617",
...props
}: JSX.IntrinsicAttributes & SVGProps<SVGSVGElement>) => (
<svg
width="10"
height="7"
viewBox="0 0 10 7"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<path
d="M9 1.5L5 5.5L1 1.5"
stroke={stroke}
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
22 changes: 22 additions & 0 deletions examples/ui-demo/src/components/icons/external-link.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { SVGProps } from "react";

export const ExternalLinkIcon = ({
stroke = "#475569",
...props
}: JSX.IntrinsicAttributes & SVGProps<SVGSVGElement>) => (
<svg
width="100%"
height="100%"
viewBox="0 0 14 15"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<path
d="M6.2001 1.09961H3.0001C1.67461 1.09961 0.600098 2.17413 0.600098 3.49961V11.4997C0.600098 12.8252 1.67461 13.8997 3.0001 13.8997H11.0001C12.3256 13.8997 13.4001 12.8252 13.4001 11.4997V8.29961M9.3997 1.0998L13.4001 1.09961M13.4001 1.09961V4.69971M13.4001 1.09961L6.59951 7.89942"
stroke={stroke}
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
21 changes: 21 additions & 0 deletions examples/ui-demo/src/components/icons/logout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { SVGProps } from "react";

export const LogoutIcon = ({
stroke = "#363FF9",
...props
}: JSX.IntrinsicAttributes & SVGProps<SVGSVGElement>) => (
<svg
width="100%"
height="100%"
viewBox="0 0 14 14"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M4.85354 1.40039H2.38296C2.00853 1.40039 1.64944 1.54789 1.38469 1.81044C1.11993 2.07299 0.971191 2.42909 0.971191 2.80039V11.2004C0.971191 11.5717 1.11993 11.9278 1.38469 12.1903C1.64944 12.4529 2.00853 12.6004 2.38296 12.6004H4.85354M5.029 7.00039H13.029M13.029 7.00039L9.97224 3.80039M13.029 7.00039L9.97224 10.2004"
stroke={stroke}
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
27 changes: 11 additions & 16 deletions examples/ui-demo/src/components/preview/AuthCardWrapper.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
import { useConfig } from "@/app/state";
import { cn } from "@/lib/utils";
import {
AuthCard,
useLogout,
useUser,
} from "@account-kit/react";
import { useMemo } from "react";
import { AuthCard, useLogout, useUser } from "@account-kit/react";

export function AuthCardWrapper({ className }: { className?: string }) {
const user = useUser();
Expand All @@ -19,18 +14,18 @@ export function AuthCardWrapper({ className }: { className?: string }) {
className
)}
>
{!user ? (
<>
<div className="flex flex-col gap-2 w-[368px]">
<div className="modal bg-surface-default shadow-md overflow-hidden">
<AuthCard />
{
!user ? (
<>
<div className="flex flex-col gap-2 w-[368px]">
<div className="modal bg-surface-default shadow-md overflow-hidden">
<AuthCard />
</div>
</div>
</div>
</>
) : (
null
</>
) : null
// In flight- will be uncommented in the mint pr, a fast follow <MintDemoWrapper />
)}
}
{user && (
<button
className="text-primary font-semibold text-sm px-3 py-[11px] bg-white border border-gray-300 rounded-lg hover:shadow-md"
Expand Down
7 changes: 6 additions & 1 deletion examples/ui-demo/src/components/preview/CodePreview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,12 @@ function getConfigCode(config: Config) {
}

if (config.auth.showExternalWallets) {
sections.push([{ type: "external_wallets", walletConnect: { projectId: "your-project-id" } }]);
sections.push([
{
type: "external_wallets",
walletConnect: { projectId: "your-project-id" },
},
]);
}

return dedent`
Expand Down
32 changes: 16 additions & 16 deletions examples/ui-demo/src/components/preview/MintDemoWrapper.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
export function MintDemoWrapper () {
return (
<div>
<div>
{
// Iyk
}
</div>
<div>
{
// Rob
}
</div>
</div>
)
}
import { UserConnectionAvatarWithPopover } from "@/components/shared/user-connection-avatar/UserConnectionAvatarWithPopover";

export function MintDemoWrapper() {
return (
<div>
<div>
<UserConnectionAvatarWithPopover deploymentStatus={true} />
</div>
<div>
{
// Rob
}
</div>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { cn } from "@/lib/utils";
import { CheckIcon } from "@/components/icons/check-icon";
import { HTMLAttributes } from "react";
type DeploymentStatusIndicatorProps = {
isDeployed: boolean;
showCheckIcon?: boolean;
};

const DeploymentStatusIndicator = ({
isDeployed,
showCheckIcon = false,
className,
...props
}: DeploymentStatusIndicatorProps & HTMLAttributes<HTMLDivElement>) => {
const displayCheckIcon = isDeployed && showCheckIcon;

const indicatorBackgroundColor = isDeployed
? displayCheckIcon
? "#FFF"
: "bg-bg-surface-success"
: "bg-bg-surface-warning";

return (
<div
className={cn(
"w-[16px] h-[16px] rounded-full",
indicatorBackgroundColor,
className
)}
{...props}
>
{displayCheckIcon && <CheckIcon />}
</div>
);
};

export { DeploymentStatusIndicator };
48 changes: 48 additions & 0 deletions examples/ui-demo/src/components/shared/UserAvatar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import Moebius from "@phun-ky/moebius";
import Avatar from "boring-avatars";
import { useDebounceEffect } from "@/utils/hooks/useDebounceEffect";
import { useState } from "react";

type UserAvatarProps = {
address: string;
primaryColor: string;
};

const BASE_COLOR = "#37BCFA";
const UserAvatar = ({ address, primaryColor }: UserAvatarProps) => {
const [avatarColors, setAvatarColors] = useState<string[]>([]);

const getMoebiusColors = async (): Promise<string[]> => {
const { MoebiusColor, MoebiusPalettes } = await Moebius();

const palette = new MoebiusPalettes({
baseColor: new MoebiusColor(BASE_COLOR, "primary"),
secondaryColor: new MoebiusColor(primaryColor, "secondary"),
diverging: true,
bezier: true,
});

return palette.colors.interpolate as string[];
};

useDebounceEffect(
() => {
getMoebiusColors().then((moebius: string[]) => {
setAvatarColors(moebius);
});
},
[primaryColor],
2000
);

return (
<Avatar
size={40}
name={address}
variant="marble"
colors={avatarColors.length > 0 ? avatarColors : [primaryColor]}
/>
);
};

export { UserAvatar };
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import truncateAddress from "@/utils/truncate-address";
import { useConnection } from "@account-kit/react";
import { useMemo } from "react";

export const UserAddressLink = ({ address }: { address: string | null }) => {
const connection = useConnection();
const truncatedAddress = truncateAddress(address ?? "");
const addressBlockExplorerUrl = useMemo(() => {
if (!address || !connection.chain.blockExplorers) {
return null;
}

return `${connection.chain.blockExplorers?.default.url}/address/${address}`;
}, [address, connection]);

return (
<a
target="_blank"
className="text-fg-primary underline text-sm"
href={addressBlockExplorerUrl ?? "#"}
>
{truncatedAddress}
</a>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { cn } from "@/lib/utils";
import { useConfig } from "@/app/state";
import { UserAvatar } from "@/components/shared/UserAvatar";

import { useUser } from "@account-kit/react";
import { ChevronDown } from "@/components/icons/chevron-down";

import { DeploymentStatusIndicator } from "@/components/shared/DeploymentStatusIndicator";

interface UserConnectionAvatarProps {
isFocused?: boolean;
deploymentStatus: boolean;
}
const UserConnectionAvatar = ({
isFocused,
deploymentStatus,
}: UserConnectionAvatarProps) => {
const { config } = useConfig();
const user = useUser();

const currentTheme = config.ui.theme;

if (!user) {
return null;
}

return (
<div className="flex flex-row items-center">
<div className="relative w-[40px] h-[40px]">
<UserAvatar
address={user.address}
primaryColor={config.ui.primaryColor[currentTheme]}
/>
<div
className={cn(
"bg-[#fff] p-[2px] rounded-full absolute bottom-[-4px] left-[23px]",
deploymentStatus && "p-[1px]"
)}
>
<DeploymentStatusIndicator
isDeployed={deploymentStatus}
showCheckIcon
/>
</div>
</div>
<div className="flex flex-col ml-3">
<span className="text-fg-secondary text-left text-sm">Hello,</span>
<div className="flex flex-row items-center">
<h3 className="text-fg-primary font-semibold text-left text-lg">
{user.email}
</h3>
<div className="ml-1 w-[20px] h-[20px] flex items-center justify-center">
<ChevronDown
stroke={currentTheme === "dark" ? "#fff" : "#000"}
className={cn("transition", isFocused && "rotate-180")}
/>
</div>
</div>
</div>
</div>
);
};

export { UserConnectionAvatar };
Loading
Loading