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 11, 2024
1 parent 7f53095 commit 1bb85d6
Show file tree
Hide file tree
Showing 26 changed files with 485 additions and 182 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
@@ -0,0 +1,21 @@
import React from "react";
import { Flex, Icons, Text } from "@ledgerhq/react-ui";
import { useTranslation } from "react-i18next";

type Props = {
hasError: boolean;
};

const Error: React.FC<Props> = ({ hasError }) => {
const { t } = useTranslation();
if (!hasError) return null;

return (
<Flex justifyContent="center" my={4} columnGap={2}>
<Icons.Warning size={"S"} color="palette.error.c60" />
<Text color="palette.error.c60">{t("crash.title")}</Text>
</Flex>
);
};

export default Error;
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@
import React from "react";
import { Account } from "@ledgerhq/types-live";
import { Box, Flex } from "@ledgerhq/react-ui";
import { Box, Flex, Icons } from "@ledgerhq/react-ui";
import { useInscriptionsModel } from "./useInscriptionsModel";
import TableContainer from "~/renderer/components/TableContainer";
import TableHeader from "LLD/features/Collectibles/components/Collection/TableHeader";
import { OrdinalsRowProps, TableHeaderTitleKey } from "LLD/features/Collectibles/types/Collection";
import TableRow from "LLD/features/Collectibles/components/Collection/TableRow";
import ShowMore from "LLD/features/Collectibles/components/Collection/ShowMore";
import { MediaProps } from "LLD/features/Collectibles/types/Media";
import { SimpleHashNft } from "@ledgerhq/live-nft/api/types";
import { Status } from "LLD/features/Collectibles/types/Collectibles";
import { TanStackStatus } from "../../../types/enum/Collectibles";
import Loader from "../Loader";
import Error from "../Error";

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

type Props = {
account: Account;
inscriptions: SimpleHashNft[];
status: Status;
};

export type InscriptionsItemProps = {
Expand Down Expand Up @@ -44,37 +49,46 @@ const Item: React.FC<InscriptionsItemProps> = ({
);
};

function View({ displayedObjects, displayShowMore, onShowMore }: ViewProps) {
function View({ displayShowMore, status, inscriptions, onShowMore }: ViewProps) {
const isLoading = status === TanStackStatus.Pending;
const hasError = status === TanStackStatus.Error;
return (
<Box>
<TableContainer id="oridinals-inscriptions">
<TableHeader titleKey={TableHeaderTitleKey.Inscriptions} />
{/** titlekey doesn't need to be translated so we keep it hard coded */}
{displayedObjects ? (
displayedObjects.map((item, index) => (
<Loader isLoading={isLoading} />
<Error hasError={hasError} />
{inscriptions ? (
inscriptions.map((item, index) => (
<Item
key={index}
isLoading={item.isLoading}
tokenName={item.tokenName}
collectionName={item.collectionName}
tokenIcons={item.tokenIcons}
media={item.media}
onClick={item.onClick}
isLoading={isLoading}
tokenName={item.contract.name || ""}
collectionName={item.collection.name}
tokenIcons={[Icons.OrdinalsAlpha, Icons.OrdinalsBlackEpic]}
media={{
uri: item.image_url || item.previews?.image_small_url,
isLoading: isLoading,
useFallback: true,
contentType: item.extra_metadata?.ordinal_details?.content_type,
mediaType: "image",
}}
onClick={() => {}}
/>
))
) : (
<Flex justifyContent={"center"} my={12}>
{"NOTHING TO SHOW"}
{"NOTHING TO SHOW WAITING FOR DESIGN"}
</Flex>
)}
{displayShowMore && <ShowMore onShowMore={onShowMore} />}
{displayShowMore && <ShowMore onShowMore={onShowMore} isInscriptions />}
</TableContainer>
</Box>
);
}

const Inscriptions: React.FC<Props> = ({ account }: Props) => {
return <View {...useInscriptionsModel({ account })} />;
const Inscriptions: React.FC<Props> = ({ inscriptions, status }: Props) => {
return <View status={status} {...useInscriptionsModel({ inscriptions })} />;
};

export default Inscriptions;
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { useEffect, useMemo, useState } from "react";
import { Account } from "@ledgerhq/types-live";
import { InscriptionsItemProps } from "./index";
import { mockedItems as InscriptionsMocked } from "LLD/features/Collectibles/__integration__/mockedInscriptions";
import { SimpleHashNft } from "@ledgerhq/live-nft/api/types";

type Props = {
account: Account;
inscriptions: SimpleHashNft[];
};

export const useInscriptionsModel = ({ account }: Props) => {
export const useInscriptionsModel = ({ inscriptions }: Props) => {
const [displayShowMore, setDisplayShowMore] = useState(false);
const [displayedObjects, setDisplayedObjects] = useState<InscriptionsItemProps[]>([]);

Expand All @@ -28,5 +29,5 @@ export const useInscriptionsModel = ({ account }: Props) => {
});
};

return { account, displayedObjects, displayShowMore, onShowMore };
return { inscriptions, displayedObjects, displayShowMore, onShowMore };
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Flex, InfiniteLoader } from "@ledgerhq/react-ui";
import React from "react";

type Props = {
isLoading: boolean;
};

const Loader: React.FC<Props> = ({ isLoading }) => {
if (!isLoading) return null;

return (
<Flex justifyContent="center" my={4}>
<InfiniteLoader />
</Flex>
);
};

export default Loader;
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;
}
Loading

0 comments on commit 1bb85d6

Please sign in to comment.