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 ccf1446
Show file tree
Hide file tree
Showing 26 changed files with 440 additions and 172 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,5 +1,4 @@
import React from "react";
import { Account } from "@ledgerhq/types-live";
import { Box, Flex } from "@ledgerhq/react-ui";
import { useInscriptionsModel } from "./useInscriptionsModel";
import TableContainer from "~/renderer/components/TableContainer";
Expand All @@ -8,11 +7,17 @@ import { OrdinalsRowProps, TableHeaderTitleKey } from "LLD/features/Collectibles
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,12 +49,15 @@ const Item: React.FC<InscriptionsItemProps> = ({
);
};

function View({ displayedObjects, displayShowMore, onShowMore }: ViewProps) {
function View({ displayedObjects, displayShowMore, status, 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 */}
<Loader isLoading={isLoading} />
<Error hasError={hasError} />
{displayedObjects ? (
displayedObjects.map((item, index) => (
<Item
Expand All @@ -64,17 +72,17 @@ function View({ displayedObjects, displayShowMore, onShowMore }: ViewProps) {
))
) : (
<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;
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,38 +6,57 @@ import Item from "./Item";
import { TableHeaderTitleKey } from "LLD/features/Collectibles/types/Collection";
import { Box, Flex } 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";
import Loader from "../Loader";
import Error from "../Error";

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

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

function View({ rareSats, status }: ViewProps) {
const isLoading = status === TanStackStatus.Pending;
const hasError = status === TanStackStatus.Error;
const isLoaded = status === TanStackStatus.Success;
const hasRareSats = Object.values(rareSats).length > 0;
const dataReady = isLoaded && hasRareSats;

function View({ rareSats }: ViewProps) {
return (
<Box>
<TableContainer id="oridinals-inscriptions">
<TableHeader titleKey={TableHeaderTitleKey.RareSats} />
<TableHeaderContainer />
<Loader isLoading={isLoading} />
<Error hasError={hasError} />
<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 */}
{dataReady &&
Object.entries(rareSats).map(([key, rareSatGroup]) => (
<Flex key={key} flexDirection="column">
{rareSatGroup.map((rareSat, index) => (
<Item key={index} {...rareSat} />
))}
</Flex>
))}
{isLoaded && !hasRareSats && (
<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;
Loading

0 comments on commit ccf1446

Please sign in to comment.