Skip to content
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
54 changes: 49 additions & 5 deletions components/hypercert/contributors.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,60 @@ import {
} from "@/components/ui/command";
import { UserCircle2 } from "lucide-react";
import { useState } from "react";
import { isAddress } from "viem";
import { getAddress, isAddress } from "viem";
import { HypercertState } from "@/hypercerts/fragments/hypercert-state.fragment";
import { useGetUser } from "@/users/hooks";
import { UserIcon } from "../user-icon";
import { ImageIcon } from "../user-icon/ImageIcon";
import { SvgIcon } from "../user-icon/SvgIcon";
import { Skeleton } from "../ui/skeleton";
import { UserName } from "../user-name";
const MAX_CONTRIBUTORS_DISPLAYED = 10;

function Contributor({ contributor }: { contributor: string }) {
return isAddress(contributor) ? (
<EthAddress address={contributor} />
const address = isAddress(contributor.trim())
? getAddress(contributor.trim())
: undefined;

const { data: userData, isFetching } = useGetUser({
address: address,
});

if (isFetching) {
return (
<div className="flex flex-row gap-2 items-center">
<Skeleton className="h-8 w-8 rounded-full" />
<Skeleton className="h-8 w-32" />
</div>
);
}

return userData?.user ? (
<div className="flex gap-2">
{userData.user.avatar ? (
<ImageIcon url={userData.user.avatar} size="tiny" />
) : (
<SvgIcon size="tiny" />
)}
<div className="flex flex-col justify-center items-start">
<UserName
address={userData.user.address}
userName={userData.user.display_name}
/>
</div>
</div>
) : address ? (
<div className="flex gap-2">
<UserIcon address={address} size="tiny" />
<div className="flex flex-col justify-center items-start">
<EthAddress address={address} showEnsName={true} />
</div>
</div>
) : (
<div>{contributor}</div>
<div className="flex items-center flex-row">
<UserCircle2 className="mr-2 h-4 w-4" />
{contributor}
</div>
);
}

Expand Down Expand Up @@ -58,7 +103,6 @@ export default function Contributors({
<CommandGroup>
{hypercert.metadata?.contributors.map((contributor) => (
<CommandItem key={contributor}>
<UserCircle2 className="mr-2 h-4 w-4" />
<Contributor contributor={contributor} />
</CommandItem>
))}
Expand Down
79 changes: 67 additions & 12 deletions components/hypercert/fractions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,26 +8,82 @@ import {
CommandItem,
CommandList,
} from "@/components/ui/command";
import { truncateEthereumAddress } from "@/lib/utils";
import { UserCircle2 } from "lucide-react";
import { useState } from "react";
import { FormattedUnits } from "../formatted-units";
import { HypercertState } from "@/hypercerts/fragments/hypercert-state.fragment";

import { getAddress, isAddress } from "viem";
import { useGetUser } from "@/users/hooks";
import { Skeleton } from "../ui/skeleton";
import { UserIcon } from "../user-icon";
import { ImageIcon } from "../user-icon/ImageIcon";
import { SvgIcon } from "../user-icon/SvgIcon";
import EthAddress from "../eth-address";
import { UserName } from "../user-name";
import { calculateBigIntPercentage } from "@/lib/calculateBigIntPercentage";
const MAX_FRACTIONS_DISPLAYED = 5;

function Fraction({
ownerAddress,
totalUnits,
units,
}: {
ownerAddress: string | null;
units: unknown;
ownerAddress: string;
totalUnits: string | null | undefined;
units: string | null | undefined;
}) {
const address = isAddress(ownerAddress.trim())
? getAddress(ownerAddress.trim())
: undefined;

const { data: userData, isFetching } = useGetUser({
address: address,
});

const calculatedPercentage = calculateBigIntPercentage(
units as string,
totalUnits as string,
);

const roundedPercentage =
calculatedPercentage! < 1
? "<1"
: Math.round(calculatedPercentage!).toString();

if (isFetching) {
return (
<div className="flex flex-row gap-2 items-center">
<Skeleton className="h-8 w-8 rounded-full" />
<Skeleton className="h-8 w-32" />
</div>
);
}
return (
<div className="flex flex-col w-full">
{truncateEthereumAddress(ownerAddress as `0x${string}`)} &mdash;{" "}
<FormattedUnits>{units as string}</FormattedUnits>
</div>
<>
{userData?.user ? (
<div className="flex flex-row gap-2">
{userData?.user?.avatar ? (
<ImageIcon url={userData.user.avatar} size="tiny" />
) : (
<SvgIcon size="tiny" />
)}
<div className="flex justify-center items-start">
<UserName
address={userData.user.address}
userName={userData.user.display_name}
/>
</div>
</div>
) : (
<div className="flex flex-row items-center gap-2">
<UserIcon address={address} size="tiny" />
<EthAddress address={address} showEnsName={true} />
</div>
)}
<div className="flex flex-row items-center gap-2">
&mdash; <FormattedUnits>{units as string}</FormattedUnits>
<span>{`(${roundedPercentage}%)`}</span>
</div>
</>
);
}

Expand Down Expand Up @@ -77,10 +133,9 @@ export default function Fractions({
key={fraction.fraction_id}
title={fraction.owner_address || ""}
>
<UserCircle2 className="mr-2 h-4 w-4" />
<span className="hidden">{fraction.owner_address}</span>
<Fraction
ownerAddress={fraction.owner_address}
ownerAddress={fraction.owner_address as string}
totalUnits={hypercert.units}
units={fraction.units}
/>
</CommandItem>
Expand Down
30 changes: 30 additions & 0 deletions components/user-name.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import React from "react";
import { CopyButton } from "./copy-button";

export function UserName({
address,
userName,
}: {
address: string | undefined | null;
userName: string | undefined | null;
}) {
const copyAddress = (event: React.MouseEvent<HTMLDivElement>) => {
event.stopPropagation();
void navigator.clipboard.writeText(address as string);
};

if (!address) {
return <div>Unknown</div>;
}
return (
<div className="flex items-center gap-2 content-center cursor-pointer px-1 py-0.5 bg-slate-100 rounded-md w-max text-sm">
<div onClick={copyAddress}>
<p>{userName}</p>
</div>
<CopyButton
textToCopy={address}
className="w-4 h-4 bg-transparent focus:opacity-70 focus:scale-90"
/>
</div>
);
}