Skip to content

Commit

Permalink
✨(lld): add actions in ordis drawer (#7951)
Browse files Browse the repository at this point in the history
  • Loading branch information
LucasWerey authored Oct 1, 2024
1 parent 92df3b4 commit a7696cc
Show file tree
Hide file tree
Showing 15 changed files with 322 additions and 164 deletions.
6 changes: 6 additions & 0 deletions .changeset/lazy-tips-report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"ledger-live-desktop": patch
"@ledgerhq/live-nft": patch
---

Refactor nft links to use it for ordinals and simplehash
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
import React from "react";
import { Flex, Icons, IconsLegacy, Text } from "@ledgerhq/react-ui";
import { Flex, Icons, Text } from "@ledgerhq/react-ui";
import Button from "~/renderer/components/Button";
import { useTranslation } from "react-i18next";
import { ExternalViewerButton } from "LLD/features/Collectibles/components/DetailDrawer/components";
import { SimpleHashNft } from "@ledgerhq/live-nft/api/types";
import { Account } from "@ledgerhq/types-live";

const Actions = () => {
type ActionsProps = {
inscription: SimpleHashNft;
account: Account;
};

const Actions: React.FC<ActionsProps> = ({ inscription, account }) => {
const { t } = useTranslation();
return (
<Flex alignItems="center" columnGap={12}>
Expand All @@ -23,9 +31,11 @@ const Actions = () => {
</Text>
</Flex>
</Button>
<Button outlineGrey>
<IconsLegacy.OthersMedium />
</Button>
<ExternalViewerButton
nft={inscription}
account={account}
metadata={inscription.extra_metadata}
/>
</Flex>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,22 @@ import { CollectibleTypeEnum } from "LLD/features/Collectibles/types/enum/Collec
import useInscriptionDetailDrawer from "./useInscriptionDetailDrawer";
import Actions from "./Actions";
import SubTitle from "./SubTitle";
import { BitcoinAccount } from "@ledgerhq/coin-bitcoin/lib/types";

type ViewProps = ReturnType<typeof useInscriptionDetailDrawer> & {
account: BitcoinAccount;
onClose: () => void;
};

type Props = {
inscription: SimpleHashNft;
correspondingRareSat: SimpleHashNft | null | undefined;
isLoading: boolean;
account: BitcoinAccount;
onClose: () => void;
};

const View: React.FC<ViewProps> = ({ data, rareSat, onClose }) => (
const View: React.FC<ViewProps> = ({ data, rareSat, account, inscription, onClose }) => (
<DetailDrawer
collectibleType={CollectibleTypeEnum.NFT}
areFieldsLoading={data.areFieldsLoading}
Expand All @@ -44,7 +47,7 @@ const View: React.FC<ViewProps> = ({ data, rareSat, onClose }) => (
</DetailDrawer.Subtitle>
)}
<DetailDrawer.Actions>
<Actions />
<Actions account={account} inscription={inscription} />
</DetailDrawer.Actions>
</DetailDrawer>
);
Expand All @@ -53,12 +56,14 @@ const InscriptionDetailDrawer = ({
inscription,
isLoading,
correspondingRareSat,
account,
onClose,
}: Props) => {
return (
<View
onClose={onClose}
{...useInscriptionDetailDrawer({ isLoading, inscription, correspondingRareSat })}
onClose={onClose}
account={account}
/>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ import { BitcoinAccount } from "@ledgerhq/coin-bitcoin/lib/types";
import { useBitcoinAccountModel } from "./useBitcoinAccountModel";
import InscriptionDetailsDrawer from "../../components/Inscriptions/DetailsDrawer";

type ViewProps = ReturnType<typeof useBitcoinAccountModel>;
type ViewProps = ReturnType<typeof useBitcoinAccountModel> & {
account: BitcoinAccount;
};

interface Props {
account: BitcoinAccount;
Expand All @@ -21,6 +23,7 @@ const View: React.FC<ViewProps> = ({
selectedInscription,
correspondingRareSat,
inscriptionsGroupedWithRareSats,
account,
handleDrawerClose,
onReceive,
onInscriptionClick,
Expand All @@ -42,13 +45,14 @@ const View: React.FC<ViewProps> = ({
correspondingRareSat={correspondingRareSat}
onClose={onDetailsDrawerClose}
isLoading={rest.isLoading}
account={account}
/>
)}
</Flex>
);

const OrdinalsAccount: React.FC<Props> = ({ account }) => (
<View {...useBitcoinAccountModel({ account })} />
<View {...useBitcoinAccountModel({ account })} account={account} />
);

export default OrdinalsAccount;
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import React from "react";
import { render, screen, waitFor } from "tests/testUtils";
import { BitcoinPage } from "./shared";
import { openURL } from "~/renderer/linking";
import { DeviceModelId } from "@ledgerhq/devices";

jest.mock(
"electron",
Expand All @@ -22,6 +23,7 @@ describe("displayBitcoinPage", () => {
initialState: {
settings: {
hasSeenOrdinalsDiscoveryDrawer: true,
devicesModelList: [DeviceModelId.stax, DeviceModelId.europa],
},
},
});
Expand Down Expand Up @@ -49,6 +51,7 @@ describe("displayBitcoinPage", () => {
initialState: {
settings: {
hasSeenOrdinalsDiscoveryDrawer: true,
devicesModelList: [DeviceModelId.stax, DeviceModelId.europa],
},
},
});
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import { useMemo } from "react";
import { TFunction } from "i18next";
import { ContextMenuItemType } from "~/renderer/components/ContextMenu/ContextMenuWrapper";
import { openURL } from "~/renderer/linking";
import { safeList } from "LLD/features/Collectibles/utils/useSafeList";
import { ExternalIdEnum, ItemType, ViewerEnum } from "LLD/features/Collectibles/types/enum/Links";
import IconOpensea from "~/renderer/icons/Opensea";
import IconRarible from "~/renderer/icons/Rarible";
import IconMagicEden from "~/renderer/icons/MagicEden";
import { IconsLegacy } from "@ledgerhq/react-ui";
import { SimpleHashNft } from "@ledgerhq/live-nft/api/types";
import { NFTMetadata, ProtoNFT } from "@ledgerhq/types-live";
import { isNFTMetadata } from "../utils/typeGuardsChecker";
import { createOrdinalExplorerUrl } from "../utils/createOrdinalExplorerUrl";

const createLinks = (
t: TFunction,
links: Record<string, string> | SimpleHashNft["collection"]["marketplace_pages"] | undefined,
isSimpleHash: boolean,
explorerLink?: string,
): ContextMenuItemType[] => {
if (isSimpleHash) {
const marketplacePages = Array.isArray(links) ? links : [];
return safeList(
[
...marketplacePages
.filter(link => link.marketplace_id === ExternalIdEnum.MAGICEDEN)
.map(link => ({
id: link.marketplace_id,
label: t("NFT.viewer.actions.open", { viewer: ViewerEnum.MAGICEDEN }),
Icon: IconMagicEden,
type: ItemType.EXTERNAL,
callback: () => openURL(link.nft_url),
})),
...(marketplacePages.length > 0 ? [{ id: "sep2", type: "separator", label: "" }] : []),
...(explorerLink
? [
{
id: ExternalIdEnum.EXPLORER,
label: t("NFT.viewer.actions.open", { viewer: ViewerEnum.EXPLORER }),
Icon: IconsLegacy.GlobeMedium,
type: ItemType.EXTERNAL,
callback: () => openURL(explorerLink),
},
]
: []),
].filter(Boolean),
);
} else {
const linksRecord = links as Record<string, string>;
return safeList(
[
linksRecord.opensea && {
id: ExternalIdEnum.OPENSEA,
label: t("NFT.viewer.actions.open", { viewer: ViewerEnum.OPENSEA }),
Icon: IconOpensea,
type: ItemType.EXTERNAL,
callback: () => openURL(linksRecord.opensea),
},
linksRecord.rarible && {
id: ExternalIdEnum.RARIBLE,
label: t("NFT.viewer.actions.open", { viewer: ViewerEnum.RARIBLE }),
Icon: IconRarible,
type: ItemType.EXTERNAL,
callback: () => openURL(linksRecord.rarible),
},
{ id: "sep2", type: "separator", label: "" },
linksRecord.explorer && {
id: ExternalIdEnum.EXPLORER,
label: t("NFT.viewer.actions.open", { viewer: ViewerEnum.EXPLORER }),
Icon: IconsLegacy.GlobeMedium,
type: ItemType.EXTERNAL,
callback: () => openURL(linksRecord.explorer),
},
].filter(Boolean),
);
}
};

export const useCreateLinks = (
t: TFunction,
metadata: NFTMetadata | SimpleHashNft["extra_metadata"],
nft: ProtoNFT | SimpleHashNft,
isSimpleHash: boolean,
): ContextMenuItemType[] => {
const inscriptionNumber = (nft as SimpleHashNft).extra_metadata?.ordinal_details
?.inscription_number;

return useMemo(() => {
const links = isNFTMetadata(metadata)
? metadata.links
: (nft as SimpleHashNft).collection?.marketplace_pages;
const explorerLink = isNFTMetadata(metadata)
? metadata.links?.explorer
: createOrdinalExplorerUrl(inscriptionNumber);

return createLinks(t, links, isSimpleHash, explorerLink);
}, [t, metadata, nft, isSimpleHash, inscriptionNumber]);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { isCustomLockScreenSupported } from "@ledgerhq/live-common/device/use-cases/screenSpecs";
import { SimpleHashNft } from "@ledgerhq/live-nft/api/types";
import { IconsLegacy } from "@ledgerhq/react-ui";
import { NFTMetadata, NFTMedias } from "@ledgerhq/types-live";
import { TFunction } from "i18next";
import { useMemo } from "react";
import { useSelector } from "react-redux";
import { getMetadataMediaTypes } from "~/helpers/nft";
import { setDrawer } from "~/renderer/drawers/Provider";
import { devicesModelListSelector } from "~/renderer/reducers/settings";
import { isNFTMetadata } from "LLD/features/Collectibles/utils/typeGuardsChecker";
import CustomImage from "~/renderer/screens/customImage";

export const useCustomImage = (
t: TFunction,
metadata: NFTMetadata | SimpleHashNft["extra_metadata"],
inscription?: SimpleHashNft,
) => {
const devicesModelList = useSelector(devicesModelListSelector);
const customImageUri = useMemo(() => {
if (!isNFTMetadata(metadata))
return inscription?.previews?.image_large_url || inscription?.image_url;

const mediaTypes = metadata ? getMetadataMediaTypes(metadata) : null;
const mediaSizeForCustomImage = mediaTypes
? ["original", "big", "preview"].find(size => mediaTypes[size] === "image")
: null;
return mediaSizeForCustomImage
? metadata?.medias?.[mediaSizeForCustomImage as keyof NFTMedias]?.uri
: null;
}, [inscription?.image_url, inscription?.previews?.image_large_url, metadata]);

const customImageDeviceModelIds = devicesModelList.filter(isCustomLockScreenSupported);
const customImageDeviceModelId =
customImageDeviceModelIds.length === 1 ? customImageDeviceModelIds[0] : null;
const showCustomImageButton = Boolean(customImageUri) && customImageDeviceModelIds.length > 0;

return useMemo(
() => ({
customImageUri,
customImageDeviceModelId,
showCustomImageButton,
customImage: {
id: "custom-image",
label: t("customImage.cta"),
Icon: IconsLegacy.PhotographMedium,
callback: () => {
if (customImageUri)
setDrawer(CustomImage, {
imageUri: customImageUri,
deviceModelId: customImageDeviceModelId,
isFromNFTEntryPoint: true,
});
},
},
}),
[customImageDeviceModelId, customImageUri, showCustomImageButton, t],
);
};
Loading

0 comments on commit a7696cc

Please sign in to comment.