Skip to content

Commit

Permalink
✨feat(lld): UI of rare sats table
Browse files Browse the repository at this point in the history
  • Loading branch information
LucasWerey committed Sep 6, 2024
1 parent 917045c commit 294c442
Show file tree
Hide file tree
Showing 15 changed files with 2,268 additions and 54 deletions.
5 changes: 5 additions & 0 deletions .changeset/friendly-rings-laugh.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"ledger-live-desktop": patch
---

add the ui of rare sats table for ordinals
Original file line number Diff line number Diff line change
@@ -1,33 +1,73 @@
import React from "react";
import { Icons } from "@ledgerhq/react-ui";
import { IconProps } from "../../types/Collection";

export const mappingKeysWithIconAndName = {
alpha: { icon: <Icons.OrdinalsAlpha />, name: "Alpha" },
black_epic: { icon: <Icons.OrdinalsBlackEpic />, name: "Black Epic" },
black_legendary: { icon: <Icons.OrdinalsBlackLegendary />, name: "Black Legendary" },
black_mythic: { icon: <Icons.OrdinalsBlackMythic />, name: "Black Mythic" },
black_rare: { icon: <Icons.OrdinalsBlackRare />, name: "Black Rare" },
black_uncommon: { icon: <Icons.OrdinalsBlackUncommon />, name: "Black Uncommon" },
block_9: { icon: <Icons.OrdinalsBlock9 />, name: "Block 9" },
block_9_450x: { icon: <Icons.OrdinalsBlock9450X />, name: "Block 9 450x" },
block_78: { icon: <Icons.OrdinalsBlock78 />, name: "Block 78" },
block_286: { icon: <Icons.OrdinalsBlock286 />, name: "Block 286" },
block_666: { icon: <Icons.OrdinalsBlock666 />, name: "Block 666" },
common: { icon: <Icons.OrdinalsCommon />, name: "Common" },
epic: { icon: <Icons.OrdinalsEpic />, name: "Epic" },
first_tx: { icon: <Icons.OrdinalsFirstTx />, name: "First Transaction" },
hitman: { icon: <Icons.OrdinalsHitman />, name: "Hitman" },
jpeg: { icon: <Icons.OrdinalsJpeg />, name: "JPEG" },
legacy: { icon: <Icons.OrdinalsLegacy />, name: "Legacy" },
legendary: { icon: <Icons.OrdinalsLegendary />, name: "Legendary" },
mythic: { icon: <Icons.OrdinalsMythic />, name: "Mythic" },
nakamoto: { icon: <Icons.OrdinalsNakamoto />, name: "Nakamoto" },
omega: { icon: <Icons.OrdinalsOmega />, name: "Omega" },
paliblock: { icon: <Icons.OrdinalsPaliblockPalindrome />, name: "PaliBlock Palindrome" },
palindrome: { icon: <Icons.OrdinalsPalindrome />, name: "Palindrome" },
palinception: { icon: <Icons.OrdinalsPalinception />, name: "Palinception" },
pizza: { icon: <Icons.OrdinalsPizza />, name: "Pizza" },
rare: { icon: <Icons.OrdinalsRare />, name: "Rare" },
uncommon: { icon: <Icons.OrdinalsUncommon />, name: "Uncommon" },
vintage: { icon: <Icons.OrdinalsVintage />, name: "Vintage" },
alpha: { icon: (props: IconProps) => <Icons.OrdinalsAlpha {...props} />, name: "Alpha" },
black_epic: {
icon: (props: IconProps) => <Icons.OrdinalsBlackEpic {...props} />,
name: "Black Epic",
},
black_legendary: {
icon: (props: IconProps) => <Icons.OrdinalsBlackLegendary {...props} />,
name: "Black Legendary",
},
black_mythic: {
icon: (props: IconProps) => <Icons.OrdinalsBlackMythic {...props} />,
name: "Black Mythic",
},
black_rare: {
icon: (props: IconProps) => <Icons.OrdinalsBlackRare {...props} />,
name: "Black Rare",
},
black_uncommon: {
icon: (props: IconProps) => <Icons.OrdinalsBlackUncommon {...props} />,
name: "Black Uncommon",
},
block_9: { icon: (props: IconProps) => <Icons.OrdinalsBlock9 {...props} />, name: "Block 9" },
block_9_450x: {
icon: (props: IconProps) => <Icons.OrdinalsBlock9450X {...props} />,
name: "Block 9 450x",
},
block_78: { icon: (props: IconProps) => <Icons.OrdinalsBlock78 {...props} />, name: "Block 78" },
block_286: {
icon: (props: IconProps) => <Icons.OrdinalsBlock286 {...props} />,
name: "Block 286",
},
block_666: {
icon: (props: IconProps) => <Icons.OrdinalsBlock666 {...props} />,
name: "Block 666",
},
common: { icon: (props: IconProps) => <Icons.OrdinalsCommon {...props} />, name: "Common" },
epic: { icon: (props: IconProps) => <Icons.OrdinalsEpic {...props} />, name: "Epic" },
first_tx: {
icon: (props: IconProps) => <Icons.OrdinalsFirstTx {...props} />,
name: "First Transaction",
},
hitman: { icon: (props: IconProps) => <Icons.OrdinalsHitman {...props} />, name: "Hitman" },
jpeg: { icon: (props: IconProps) => <Icons.OrdinalsJpeg {...props} />, name: "JPEG" },
legacy: { icon: (props: IconProps) => <Icons.OrdinalsLegacy {...props} />, name: "Legacy" },
legendary: {
icon: (props: IconProps) => <Icons.OrdinalsLegendary {...props} />,
name: "Legendary",
},
mythic: { icon: (props: IconProps) => <Icons.OrdinalsMythic {...props} />, name: "Mythic" },
nakamoto: { icon: (props: IconProps) => <Icons.OrdinalsNakamoto {...props} />, name: "Nakamoto" },
omega: { icon: (props: IconProps) => <Icons.OrdinalsOmega {...props} />, name: "Omega" },
paliblock: {
icon: (props: IconProps) => <Icons.OrdinalsPaliblockPalindrome {...props} />,
name: "PaliBlock Palindrome",
},
palindrome: {
icon: (props: IconProps) => <Icons.OrdinalsPalindrome {...props} />,
name: "Palindrome",
},
palinception: {
icon: (props: IconProps) => <Icons.OrdinalsPalinception {...props} />,
name: "Palinception",
},
pizza: { icon: (props: IconProps) => <Icons.OrdinalsPizza {...props} />, name: "Pizza" },
rare: { icon: (props: IconProps) => <Icons.OrdinalsRare {...props} />, name: "Rare" },
uncommon: { icon: (props: IconProps) => <Icons.OrdinalsUncommon {...props} />, name: "Uncommon" },
vintage: { icon: (props: IconProps) => <Icons.OrdinalsVintage {...props} />, name: "Vintage" },
};
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import React from "react";
import { Flex } from "@ledgerhq/react-ui";
import styled from "styled-components";

type Props = {
firstColumnElement: JSX.Element;
secondColumnElement: JSX.Element;
thirdColumnElement?: JSX.Element;
bgColor?: string;
isMultipleRow?: boolean;
};

const Container = styled(Flex)`
&:first-child {
border-top: 1px solid ${p => p.theme.colors.palette.text.shade10};
padding-top: 15px;
}
&:last-child {
padding-bottom: 15px;
}
`;

const RowLayout: React.FC<Props> = ({
firstColumnElement,
secondColumnElement,
thirdColumnElement,
bgColor,
isMultipleRow,
}) => {
const verticalPadding = isMultipleRow ? 1 : 3;
return (
<Container
py={verticalPadding}
px={4}
flexDirection={"row"}
maxHeight={64}
alignItems={"center"}
bg={bgColor}
>
<Flex flex={1}>{firstColumnElement}</Flex>
<Flex flex={1} flexDirection={"row"} columnGap={20}>
<Flex flex={1} justifyContent={"flex-end"}>
{secondColumnElement}
</Flex>
<Flex flex={0.2} justifyContent={"flex-end"}>
{thirdColumnElement}
</Flex>
</Flex>
</Container>
);
};

export default RowLayout;
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import React from "react";
import { Text } from "@ledgerhq/react-ui";
import { useTranslation } from "react-i18next";
import RowLayout from "./RowLayout";

export const TableHeader = () => {
const { t } = useTranslation();

const firstColumn = (
<Text variant={"bodyLineHeight"} fontSize={12} color={"neutral.c70"}>
{t("ordinals.rareSats.table.type")}
</Text>
);

const secondColumn = (
<Text variant={"bodyLineHeight"} fontSize={12} color={"neutral.c70"}>
{t("ordinals.rareSats.table.year")}
</Text>
);

const thirdColumn = (
<Text variant={"bodyLineHeight"} fontSize={12} color={"neutral.c70"}>
{t("ordinals.rareSats.table.utxo")}
</Text>
);

return (
<RowLayout
firstColumnElement={firstColumn}
secondColumnElement={secondColumn}
thirdColumnElement={thirdColumn}
bgColor={"opacityDefault.c05"}
/>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import React from "react";
import { useRareSatsModel } from "./useRareSatsModel";
import TableContainer from "~/renderer/components/TableContainer";
import TableHeader from "LLD/features/Collectibles/components/Collection/TableHeader";
import { TableHeaderTitleKey } from "LLD/features/Collectibles/types/Collection";
import { Box, Text, Flex } from "@ledgerhq/react-ui";
import { TableHeader as TableHeaderContainer } from "./TableHeader";
import RowLayout from "LLD/features/Collectibles/Ordinals/components/RareSats/RowLayout";
import IconContainer from "LLD/features/Collectibles/components/Collection/TableRow/IconContainer";
import TokenTitle from "LLD/features/Collectibles/components/Collection/TableRow/TokenTitle";
import { RareSat } from "LLD/features/Collectibles/types/Ordinals";

type ViewProps = ReturnType<typeof useRareSatsModel>;

const Item = ({
icons,
name,
year,
count,
utxo_size,
isMultipleRow,
}: RareSat & { isMultipleRow: boolean }) => {
const firstColumn = (
<Flex columnGap={2}>
{icons && <IconContainer icons={Object.values(icons)} />}
<TokenTitle tokenName={[name]} complementaryData={count} isLoading={false} />
</Flex>
);
const secondColumn = (
<Text variant={"bodyLineHeight"} fontSize={12} color={"neutral.c70"}>
{year}
</Text>
);
const thirdColumn = (
<Text variant={"bodyLineHeight"} fontSize={12} color={"neutral.c70"}>
{utxo_size}
</Text>
);

return (
<RowLayout
isMultipleRow={isMultipleRow}
firstColumnElement={firstColumn}
secondColumnElement={secondColumn}
thirdColumnElement={thirdColumn}
/>
);
};

function View(props: ViewProps) {
return (
<Box>
<TableContainer id="oridinals-inscriptions">
<TableHeader titleKey={TableHeaderTitleKey.RareSats} />
<TableHeaderContainer />
<Flex flexDirection={"column"}>
{props.rareSats.map((rareSatGroup, index) => (
<Flex key={index} flexDirection={"column"}>
{rareSatGroup.sats.map((rareSat, subIndex) => (
<Item
key={subIndex}
icons={rareSat.icons}
{...rareSat}
isMultipleRow={rareSatGroup.isMultipleRow}
/>
))}
</Flex>
))}
</Flex>
</TableContainer>
</Box>
);
}

const RareSats = () => {
return <View {...useRareSatsModel({})} />;
};

export default RareSats;
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import { mockedRareSats } from "LLD/features/Collectibles/__integration__/mockedRareSats";
import { mappingKeysWithIconAndName } from "../Icons";
import { MappingKeys } from "LLD/features/Collectibles/types/Ordinals";
import { IconProps } from "LLD/features/Collectibles/types/Collection";
import { RareSat } from "LLD/features/Collectibles/types/Ordinals";

type RareSatsProps = {};

type Satributes = {
[key in MappingKeys]?: {
count: number;
display_name: string | string[];
description?: string;
icon?: string;
};
};

export const useRareSatsModel = (_props: RareSatsProps) => {
const processedRareSats: RareSat[] = mockedRareSats.flatMap(item =>
item.nfts
.map(sat => {
const { extra_metadata } = sat;
const satributes = extra_metadata.utxo_details.satributes as Satributes;
let display_name: string | string[] | undefined;
let count: number | undefined;

for (const key in satributes) {
if (
satributes[key as MappingKeys] &&
satributes[key as MappingKeys]!.display_name &&
satributes[key as MappingKeys]!.count
) {
display_name = satributes[key as MappingKeys]!.display_name;
count = satributes[key as MappingKeys]!.count;
break;
}
}

if (!display_name || !count) {
return null;
}

const { year, value } = extra_metadata.utxo_details.sat_ranges[0];
const icons: { [key: string]: ({ size, color, style }: IconProps) => JSX.Element } = {};
let name: string;

if (Array.isArray(display_name)) {
name = display_name
.map(
dn =>
mappingKeysWithIconAndName[dn.toLowerCase().replace(" ", "_") as MappingKeys]?.name,
)
.join(" / ");
display_name.forEach(dn => {
const key = dn.toLowerCase().replace(" ", "_") as MappingKeys;
icons[key] = mappingKeysWithIconAndName[key]?.icon;
});
} else {
const key = display_name.toLowerCase().replace(" ", "_") as MappingKeys;
icons[key] = mappingKeysWithIconAndName[key]?.icon;
name = mappingKeysWithIconAndName[key]?.name;
}

return {
count: count.toString(),
display_name,
year,
utxo_size: value.toString(),
icons: icons || {},
name,
};
})
.filter((sat): sat is RareSat => sat !== null && sat.icons !== undefined),
);

// Group the processed RareSats by utxo_size
const groupedRareSats: Record<string, RareSat[]> = processedRareSats.reduce(
(acc, sat) => {
if (!acc[sat.utxo_size]) {
acc[sat.utxo_size] = [];
}
acc[sat.utxo_size].push(sat);
return acc;
},
{} as Record<string, RareSat[]>,
);

// Create final array with all items of the same utxo_size in the same object
const finalRareSats = Object.entries(groupedRareSats).map(([utxo_size, sats]) => ({
utxo_size,
sats,
isMultipleRow: sats.length > 1,
}));

console.log("finalRareSats", finalRareSats);

return { rareSats: finalRareSats };
};
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
import React from "react";
import { Account } from "@ledgerhq/types-live";
import Inscriptions from "../../components/Inscriptions";
import RareSats from "../../components/RareSats";
import { Flex } from "@ledgerhq/react-ui";

type Props = {
account: Account;
};

const OrdinalsAccount: React.FC<Props> = ({ account }) => {
return <Inscriptions account={account} />;
// here we will add the rare sats table
return (
<Flex mb={50} width={"100%"} flexDirection={"column"} rowGap={40}>
<Inscriptions account={account} />
<RareSats />
</Flex>
);
};

export default OrdinalsAccount;
Loading

0 comments on commit 294c442

Please sign in to comment.