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

Issue/190 #229

Merged
merged 2 commits into from
Dec 29, 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
1 change: 0 additions & 1 deletion pkgs/frontend/app/components/common/CommonButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { Button, ButtonProps } from "~/components/ui/button";
interface CommonButtonProps extends Omit<ButtonProps, "width"> {
children: React.ReactNode;
width?: "full" | number;
size?: "sm" | "md" | "lg";
backgroundColor?: string;
color?: string;
}
Expand Down
52 changes: 52 additions & 0 deletions pkgs/frontend/app/components/splits/SplitRecipientsList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { Box, Flex, Text } from "@chakra-ui/react";
import { useNamesByAddresses } from "hooks/useENS";
import { FC, useMemo } from "react";
import { UserIcon } from "../icon/UserIcon";
import { ipfs2https } from "utils/ipfs";
import { abbreviateAddress } from "utils/wallet";

interface SplitRecipientsListProps {
recipients: { address: string; percentAllocation: number }[];
}

export const SplitRecipientsList: FC<SplitRecipientsListProps> = ({
recipients,
}) => {
const addresses = useMemo(() => {
return recipients.map((r) => r.address);
}, [recipients]);
const { names } = useNamesByAddresses(addresses);

const totalAllocation = useMemo(() => {
return recipients.reduce((acc, r) => acc + r.percentAllocation, 0);
}, [recipients]);

return (
<>
{recipients.map((recipient) => {
const name = names.find(
(name) => name[0]?.address === recipient.address
)?.[0];
return (
<Flex key={recipient.address} my={2} alignItems="center" gap={2}>
<UserIcon
size="40px"
userImageUrl={ipfs2https(name?.text_records?.avatar)}
/>
<Box flexGrow={1}>
<Text textStyle="sm">{name?.name}</Text>
<Text textStyle="sm">
{abbreviateAddress(name?.address || "")}
</Text>
</Box>
{(
(Number(recipient.percentAllocation) / totalAllocation) *
100
).toFixed(2)}{" "}
%
</Flex>
);
})}
</>
);
};
210 changes: 137 additions & 73 deletions pkgs/frontend/app/routes/$treeId_.splits._index.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,49 @@
import { type MouseEvent } from "react";
import { Box, Collapsible, Flex, Text } from "@chakra-ui/react";
import { useParams } from "@remix-run/react";
import { useTreeInfo } from "hooks/useHats";
import { useEffect, useMemo, type MouseEvent } from "react";
import { Box, Collapsible, Flex, Text, VStack } from "@chakra-ui/react";
import { Link, useParams } from "@remix-run/react";
import { FC, useCallback, useState } from "react";
import { CommonButton } from "~/components/common/CommonButton";
import { FaAngleDown, FaRegCopy } from "react-icons/fa6";
import { UserIcon } from "~/components/icon/UserIcon";
import { useCopyToClipboard } from "hooks/useCopyToClipboard";
import { useGetWorkspace } from "hooks/useWorkspace";
import { useSplitsCreatorRelatedSplits } from "hooks/useSplitsCreator";
import { Address } from "viem";
import { Split } from "@0xsplits/splits-sdk";
import { abbreviateAddress } from "utils/wallet";
import { currentChain, publicClient } from "hooks/useViem";
import dayjs from "dayjs";
import { SplitRecipientsList } from "~/components/splits/SplitRecipientsList";

interface SplitInfoItemProps {
split: Split;
}

const SplitInfoItem: FC<SplitInfoItemProps> = ({ split }) => {
const [createdTime, setCreatedTime] = useState<string>();

const consolidatedRecipients = useMemo(() => {
const consolidated = split.recipients.reduce(
(acc, recipient) => {
const address = recipient.recipient.address;
acc[address] =
(acc[address] || 0) + Number(recipient.percentAllocation);
return acc;
},
{} as Record<string, number>
);

return Object.entries(consolidated).map(([address, percentAllocation]) => ({
address,
percentAllocation,
}));
}, [split.recipients]);

const SplitInfoItem = () => {
const dummyFromAddress = "0xabc89dsakdfasdfasdd123sdafsdfasdf";
const [open, setOpen] = useState(false);
const onOpen = useCallback(() => {
setOpen(true);
}, [setOpen]);

const { copyToClipboardAction } = useCopyToClipboard(dummyFromAddress);
const { copyToClipboardAction } = useCopyToClipboard(split.address);

const onClickCopyButton = useCallback(
(event: MouseEvent<HTMLButtonElement>) => {
Expand All @@ -25,88 +53,124 @@ const SplitInfoItem = () => {
[copyToClipboardAction]
);

const dummyRatioBreakdown = [
{ name: "Ryoma", address: "0x12344sdfasdfadfaweifsd", ratio: 0.2 },
{ name: "Taro", address: "0x12344sdfasdfadfaweifsd", ratio: 0.8 },
];
useEffect(() => {
const fetch = async () => {
const data = await publicClient.getBlock({
blockNumber: BigInt(split.createdBlock),
});

setCreatedTime(
dayjs(Number(data.timestamp) * 1000).format("YYYY/MM/DD HH:mm:ss")
);
};
fetch();
}, [split]);

return (
<Collapsible.Root disabled={open} onOpenChange={onOpen}>
<Collapsible.Trigger w="full" textAlign={"start"} cursor="pointer">
<Flex mb={2}>
<Text as="span" textStyle="xs">
Optimism
</Text>
</Flex>
<Text textStyle="md">2024Q3_Rewards</Text>
<Text textStyle="sm">Created at 2024/7/11</Text>
<Flex mt={4} placeItems="center">
<Text textStyle="sm" flexGrow={1}>
{dummyFromAddress}
</Text>
<CommonButton
color="#333"
background="transparent"
w="auto"
h="auto"
p="4px"
minW="unset"
onClick={onClickCopyButton}
>
<FaRegCopy
style={{ width: "16px", height: "16px", objectFit: "cover" }}
/>
</CommonButton>
</Flex>
{open || (
<CommonButton color="#333" background="transparent" w="full" h="auto">
<FaAngleDown
style={{ width: "16px", height: "16px", objectFit: "cover" }}
/>
</CommonButton>
)}
</Collapsible.Trigger>
<Collapsible.Content>
<Box
mx={2}
my={4}
borderTop="1px solid #868e96"
role="presentation"
></Box>
{dummyRatioBreakdown.map((breakdown) => (
<Flex key={breakdown.name} my={2} alignItems="center" gap={2}>
<UserIcon size="40px" />
<Box flexGrow={1}>
<Text textStyle="sm">{breakdown.name}</Text>
<Text textStyle="sm">{breakdown.address}</Text>
</Box>
{breakdown.ratio * 100} %
<Box
w="100%"
px={4}
py={4}
borderRadius={8}
border="1px solid #333"
bg="white"
>
<Collapsible.Root disabled={open} onOpenChange={onOpen}>
<Collapsible.Trigger w="full" textAlign={"start"} cursor="pointer">
<Flex alignItems="center" justifyContent="space-between">
<Text textStyle="md">{abbreviateAddress(split.address)}</Text>
<Link
target="_blank"
to={`https://app.splits.org/accounts/${split.address}/?chainId=${currentChain.id}`}
>
<CommonButton size="xs" w="100" bgColor="blue.400">
詳細を確認
</CommonButton>
</Link>
</Flex>
))}
</Collapsible.Content>
</Collapsible.Root>
<Text textStyle="sm">Created at {createdTime}</Text>
<Flex mt={4} placeItems="center">
<Text textStyle="sm" flexGrow={1}>
{split.address}
</Text>
<CommonButton
color="#333"
background="transparent"
w="auto"
h="auto"
p="4px"
minW="unset"
onClick={onClickCopyButton}
>
<FaRegCopy
style={{ width: "16px", height: "16px", objectFit: "cover" }}
/>
</CommonButton>
</Flex>
{open || (
<CommonButton
color="#333"
background="transparent"
w="full"
h="auto"
>
<FaAngleDown
style={{ width: "16px", height: "16px", objectFit: "cover" }}
/>
</CommonButton>
)}
</Collapsible.Trigger>
<Collapsible.Content>
<Box
mx={2}
my={4}
borderTop="1px solid #868e96"
role="presentation"
></Box>
<SplitRecipientsList recipients={consolidatedRecipients} />
</Collapsible.Content>
</Collapsible.Root>
</Box>
);
};

const SplitsIndex: FC = () => {
const { treeId } = useParams();

treeId;
// const tree = useTreeInfo(Number(treeId));
const { data } = useGetWorkspace(treeId);

const splitCreatorAddress = useMemo(() => {
return data?.workspace?.splitCreator as Address;
}, [data]);

const { splits, isLoading } =
useSplitsCreatorRelatedSplits(splitCreatorAddress);

return (
<Box w="100%">
<Flex my={4} placeItems="center">
<Text fontSize="lg" flexGrow={1}>
Splits
</Text>
<CommonButton w={"auto"} size="sm">
Create New
</CommonButton>
<Link to={`/${treeId}/splits/new`}>
<CommonButton w={"auto"} size="sm">
Create New
</CommonButton>
</Link>
</Flex>
<Box px={4} py={4} borderRadius={8} border="1px solid #333">
<SplitInfoItem />
</Box>

{isLoading ? (
<></>
) : (
<VStack gap={3} mb={10}>
{splits
.slice()
.sort((a, b) => Number(b.createdBlock) - Number(a.createdBlock))
.map((split) => (
<SplitInfoItem key={split.address} split={split} />
))}
</VStack>
)}
</Box>
);
};
Expand Down
Loading
Loading