Skip to content

Commit

Permalink
✨feat(ui): add simplehash calls for ordis
Browse files Browse the repository at this point in the history
  • Loading branch information
LucasWerey committed Sep 10, 2024
1 parent 7f53095 commit 2d0c38b
Show file tree
Hide file tree
Showing 20 changed files with 369 additions and 157 deletions.
7 changes: 7 additions & 0 deletions .changeset/dry-parents-breathe.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"ledger-live-desktop": patch
"@ledgerhq/live-nft-react": patch
"@ledgerhq/live-nft": patch
---

Plug the front with simplehash api for the rare sats table and inscriptions table
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,14 @@ import React from "react";
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";
import { Text, Flex } from "@ledgerhq/react-ui";
import { RareSat } from "LLD/features/Collectibles/types/Ordinals";

const Item = ({
icons,
name,
year,
count,
utxo_size,
isMultipleRow,
}: RareSat & { isMultipleRow: boolean }) => {
const Item = ({ icons, displayed_names, year, count, utxo_size, isMultipleRow }: RareSat) => {
const firstColumn = (
<Flex columnGap={2}>
{icons && <IconContainer icons={Object.values(icons)} />}
<TokenTitle tokenName={[name]} complementaryData={count} isLoading={false} />
<TokenTitle tokenName={[displayed_names]} complementaryData={count} isLoading={false} />
</Flex>
);
const secondColumn = (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,107 +1,59 @@
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";
import {
Satributes,
Subrange,
SatRange,
MockedRareSat,
Sat,
Icons,
} from "LLD/features/Collectibles/types/RareSats";

export const processSatType = (
type: string,
satributes: Satributes,
icons: Icons,
displayNames: string[],
totalCount: number,
) => {
const attribute = satributes[type as MappingKeys];
if (attribute && attribute.count) {
displayNames.push(type);
if (mappingKeysWithIconAndName[type as MappingKeys]) {
icons[type] = mappingKeysWithIconAndName[type as MappingKeys].icon;
import { SimpleHashNft } from "@ledgerhq/live-nft/api/types";
import { SimpleHashNftWithIcons, RareSat } from "../../../types/Ordinals";

export function matchCorrespondingIcon(rareSats: SimpleHashNft[]): SimpleHashNftWithIcons[] {
return rareSats.map(rareSat => {
const iconKeys: string[] = [];
if (rareSat.name) {
iconKeys.push(rareSat.name.toLowerCase().replace(" ", "_"));
}
totalCount = attribute.count;
}
return { displayNames, totalCount };
};

export const processSatTypes = (satTypes: string[], satributes: Satributes) => {
let displayNames: string[] = [];
let totalCount = 0;
const icons: { [key: string]: ({ size, color, style }: IconProps) => JSX.Element } = {};

satTypes.forEach(type => {
const result = processSatType(type, satributes, icons, displayNames, totalCount);
displayNames = result.displayNames;
totalCount = result.totalCount;
});

return { displayNames, totalCount, icons };
};

export const processSubrange = (
subrange: Subrange,
satributes: Satributes,
year: string,
value: number,
) => {
const { sat_types } = subrange;
const { displayNames, totalCount, icons } = processSatTypes(sat_types, satributes);

const name = displayNames
.map(dn => mappingKeysWithIconAndName[dn.toLowerCase().replace(" ", "_") as MappingKeys]?.name)
.filter(Boolean)
.join(" / ");
if (rareSat.extra_metadata?.utxo_details?.satributes) {
iconKeys.push(...Object.keys(rareSat.extra_metadata.utxo_details.satributes));
}

return {
count: totalCount.toString() + (totalCount === 1 ? " sat" : " sats"),
display_name: displayNames.join(" / "),
year,
utxo_size: value.toString(),
icons,
name,
};
};
const icons = iconKeys
.map(
iconKey =>
mappingKeysWithIconAndName[iconKey as keyof typeof mappingKeysWithIconAndName]?.icon,
)
.filter(Boolean) as (({ size, color, style }: IconProps) => JSX.Element)[];

export const processSatRanges = (satRanges: SatRange[], satributes: Satributes) => {
return satRanges.flatMap(range => {
const { year, value, subranges } = range;
return subranges.flatMap(subrange => processSubrange(subrange, satributes, year, value));
return { ...rareSat, icons };
});
};

export const processRareSat = (sat: Sat) => {
const { extra_metadata } = sat;
const satributes = extra_metadata.utxo_details.satributes as Satributes;
const satRanges = extra_metadata.utxo_details.sat_ranges;
return processSatRanges(satRanges, satributes);
};

export const processRareSats = (rareSats: MockedRareSat[]) => {
return rareSats.flatMap(item => item.nfts.flatMap(processRareSat));
};

export const groupRareSats = (processedRareSats: RareSat[]) => {
return 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[]>,
);
};
}

export function createRareSatObject(
rareSats: Record<string, SimpleHashNftWithIcons[]>,
): Record<string, RareSat[]> {
const result: Record<string, RareSat[]> = {};

for (const [key, value] of Object.entries(rareSats)) {
result[key] = value.map(rareSat => {
const { icons, extra_metadata } = rareSat;
const year = extra_metadata?.utxo_details?.sat_ranges?.[0]?.year || "";
const displayed_names =
Object.values(extra_metadata?.utxo_details?.satributes || {})
.map(attr => attr.display_name)
.join(" / ") || "";
const utxo_size = extra_metadata?.utxo_details?.sat_ranges?.[0]?.value || "";
const name = rareSat.name || "";
const count = extra_metadata?.utxo_details?.value || 0;
const isMultipleRow = value.length > 1;

return {
year,
displayed_names,
utxo_size,
name,
count: `${count} ${count > 1 ? "sats" : "sat"}`,
isMultipleRow,
icons,
};
});
}

export const finalizeRareSats = (groupedRareSats: Record<string, RareSat[]>) => {
return Object.entries(groupedRareSats).map(([utxo_size, sats]) => ({
utxo_size,
sats,
isMultipleRow: sats.length > 1,
}));
};
return result;
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,40 +4,57 @@ import TableContainer from "~/renderer/components/TableContainer";
import TableHeader from "LLD/features/Collectibles/components/Collection/TableHeader";
import Item from "./Item";
import { TableHeaderTitleKey } from "LLD/features/Collectibles/types/Collection";
import { Box, Flex } from "@ledgerhq/react-ui";
import { Box, Flex, InfiniteLoader } from "@ledgerhq/react-ui";
import { TableHeader as TableHeaderContainer } from "./TableHeader";
import { SimpleHashNft } from "@ledgerhq/live-nft/api/types";
import { Status } from "LLD/features/Collectibles/types/Collectibles";
import { TanStackStatus } from "../../../types/enum/Collectibles";

type ViewProps = ReturnType<typeof useRareSatsModel>;
type ViewProps = ReturnType<typeof useRareSatsModel> & {
status: Status;
};

type Props = {
rareSats: SimpleHashNft[];
status: Status;
};

function View({ rareSats }: ViewProps) {
function View({ rareSats, status }: ViewProps) {
const isLoading = status === TanStackStatus.Pending;
const hasRareSats = Object.values(rareSats).length > 0;
console.log("rareSats", rareSats);
return (
<Box>
<TableContainer id="oridinals-inscriptions">
<TableHeader titleKey={TableHeaderTitleKey.RareSats} />
<TableHeaderContainer />
{isLoading && (
<Flex justifyContent="center" my={4}>
<InfiniteLoader />
</Flex>
)}
<Flex flexDirection="column">
{rareSats
? rareSats.map(rareSatGroup => (
<Flex key={rareSatGroup.utxo_size} flexDirection="column">
{rareSatGroup.sats.map(rareSat => (
<Item
key={rareSat.name}
{...rareSat}
isMultipleRow={rareSatGroup.isMultipleRow}
/>
))}
</Flex>
))
: null}
{/** wait for design */}
{hasRareSats ? (
Object.entries(rareSats).map(([key, rareSatGroup]) => (
<Flex key={key} flexDirection="column">
{rareSatGroup.map((rareSat, index) => (
<Item key={index} {...rareSat} />
))}
</Flex>
))
) : (
<Flex justifyContent="center" my={4}>
{"NOTHING TO SHOW WAITING FOR DESIGN"}
</Flex>
)}
</Flex>
</TableContainer>
</Box>
);
}

const RareSats = () => {
return <View {...useRareSatsModel({})} />;
const RareSats = ({ rareSats, status }: Props) => {
return <View status={status} {...useRareSatsModel({ rareSats })} />;
};

export default RareSats;
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { mockedRareSats } from "LLD/features/Collectibles/__integration__/mockedRareSats";
import { processRareSats, groupRareSats, finalizeRareSats } from "./helpers";
import { matchCorrespondingIcon, createRareSatObject } from "./helpers";
import { SimpleHashNft } from "@ledgerhq/live-nft/api/types";
import { regroupRareSatsByContractAddress } from "@ledgerhq/live-nft-react";

type RareSatsProps = {};

export const useRareSatsModel = (_props: RareSatsProps) => {
const processedRareSats = processRareSats(mockedRareSats);
const groupedRareSats = groupRareSats(processedRareSats);
const finalRareSats = finalizeRareSats(groupedRareSats);
type Props = {
rareSats: SimpleHashNft[];
};

return { rareSats: finalRareSats };
export const useRareSatsModel = ({ rareSats }: Props) => {
const matchedRareSats = matchCorrespondingIcon(rareSats);
const regroupedRareSats = regroupRareSatsByContractAddress(matchedRareSats);
const rareSatsCreated = createRareSatObject(regroupedRareSats);
return { rareSats: rareSatsCreated };
};
Original file line number Diff line number Diff line change
@@ -1,18 +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";
import useFetchOrdinals from "LLD/features/Collectibles/hooks/useFetchOrdinals";
import { BitcoinAccount } from "@ledgerhq/coin-bitcoin/lib/types";

type Props = {
account: Account;
account: BitcoinAccount;
};

const OrdinalsAccount: React.FC<Props> = ({ account }) => {
const { rareSats, status } = useFetchOrdinals({ account });
return (
<Flex mb={50} width="100%" flexDirection="column" rowGap={40}>
<Inscriptions account={account} />
<RareSats />
<RareSats rareSats={rareSats} status={status} />
</Flex>
);
};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { useFetchOrdinals as fetchOrdinalsFromSimpleHash } from "@ledgerhq/live-nft-react";
import { BitcoinAccount } from "@ledgerhq/coin-bitcoin/lib/types";

type Props = {
account: BitcoinAccount;
};

const useFetchOrdinals = ({ account }: Props) => {
const utxosAddresses = account.bitcoinResources?.utxos?.map(utxo => utxo.address).join(",") || "";
const { rareSats, inscriptions, status } = fetchOrdinalsFromSimpleHash({
addresses: utxosAddresses,
threshold: 0,
});

return { rareSats, inscriptions, status };
};

export default useFetchOrdinals;
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,5 @@ export type BaseNftsProps = {
nfts: (ProtoNFT | NFT)[];
account: Account;
};

export type Status = "error" | "success" | "pending";
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import { SimpleHashNft } from "@ledgerhq/live-nft/api/types";
import { mappingKeysWithIconAndName } from "../Ordinals/components/Icons";
import { IconProps } from "./Collection";

export type MappingKeys = keyof typeof mappingKeysWithIconAndName;

export type RareSat = {
count: string;
display_name: string | string[];
export interface RareSat {
displayed_names: string;
icons?: (({ size, color, style }: IconProps) => JSX.Element)[];
year: string;
utxo_size: string;
icons?: { [key: string]: ({ size, color, style }: IconProps) => JSX.Element };
name: string;
isDoubleRow?: boolean;
};
count: string;
utxo_size: string | number;
isMultipleRow: boolean;
}
export interface SimpleHashNftWithIcons extends SimpleHashNft {
icons?: (({ size, color, style }: IconProps) => JSX.Element)[];
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,3 @@ export type ExtraMetadata = {
animation_original_url: string | null;
metadata_original_url: string | null;
};

export type Sat = {
extra_metadata: ExtraMetadata;
};

export type MockedRareSat = {
nfts: Sat[];
};
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,8 @@ export enum ChainsEnum {
BITCOIN = "bitcoin",
UTXO = "utxo",
}

export enum OrdinalsStandardEnum {
INSCRIPTIONS = "inscriptions",
RARE_SATS = "raresats",
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,9 @@ export enum CollectibleTypeEnum {
NFT = "NFT",
Ordinal = "Ordinal",
}

export enum TanStackStatus {
Error = "error",
Success = "success",
Pending = "pending",
}
Loading

0 comments on commit 2d0c38b

Please sign in to comment.