diff --git a/.changeset/dry-otters-shop.md b/.changeset/dry-otters-shop.md
new file mode 100644
index 000000000000..187563b2688a
--- /dev/null
+++ b/.changeset/dry-otters-shop.md
@@ -0,0 +1,6 @@
+---
+"ledger-live-desktop": patch
+"@ledgerhq/live-nft": patch
+---
+
+Add detail drawers for inscriptions of ordinals protocol
diff --git a/.changeset/rare-yaks-deny.md b/.changeset/rare-yaks-deny.md
new file mode 100644
index 000000000000..e5c8877cb941
--- /dev/null
+++ b/.changeset/rare-yaks-deny.md
@@ -0,0 +1,8 @@
+---
+"@ledgerhq/coin-near": patch
+"ledger-live-desktop": patch
+"live-mobile": patch
+"@ledgerhq/live-common": patch
+---
+
+remove rewards feature from near
diff --git a/.changeset/six-hairs-raise.md b/.changeset/six-hairs-raise.md
new file mode 100644
index 000000000000..4ad083d7a294
--- /dev/null
+++ b/.changeset/six-hairs-raise.md
@@ -0,0 +1,5 @@
+---
+"live-mobile": patch
+---
+
+Run tests files at `e2e/specs/*.spec.ts`
diff --git a/.github/workflows/build-and-test-external.yml b/.github/workflows/build-and-test-external.yml
index af5ed98ceefc..96769045a1fd 100644
--- a/.github/workflows/build-and-test-external.yml
+++ b/.github/workflows/build-and-test-external.yml
@@ -14,7 +14,7 @@ permissions:
jobs:
determine-affected:
name: "Turbo Affected"
- if: ${{contains(needs.determine-affected.outputs.paths, 'ledger-live-desktop') && github.event.pull_request.head.repo.full_name != github.repository }}
+ if: ${{github.event.pull_request.head.repo.full_name != github.repository }}
uses: LedgerHQ/ledger-live/.github/workflows/turbo-affected-reusable.yml@develop
with:
head_branch: ${{ github.event.pull_request.head.ref || github.event.merge_group.head_ref }}
@@ -26,18 +26,30 @@ jobs:
needs: determine-affected
if: ${{contains(needs.determine-affected.outputs.paths, 'ledger-live-desktop') && github.event.pull_request.head.repo.full_name != github.repository }}
uses: LedgerHQ/ledger-live/.github/workflows/build-desktop-external-reusable.yml@develop
+ with:
+ ref: ${{ github.event.pull_request.head.ref }}
+ repository: ${{ github.event.pull_request.head.repo.full_name }}
+ secrets: inherit
test-desktop-external:
name: "[External] Test Desktop"
needs: determine-affected
if: ${{contains(needs.determine-affected.outputs.paths, 'ledger-live-desktop') && github.event.pull_request.head.repo.full_name != github.repository }}
uses: LedgerHQ/ledger-live/.github/workflows/test-desktop-external-reusable.yml@develop
+ with:
+ ref: ${{ github.event.pull_request.head.ref }}
+ repository: ${{ github.event.pull_request.head.repo.full_name }}
+ secrets: inherit
build-mobile-external:
name: "[External] Build Mobile"
needs: determine-affected
if: ${{contains(needs.determine-affected.outputs.paths, 'ledger-live-mobile') && github.event.pull_request.head.repo.full_name != github.repository}}
uses: LedgerHQ/ledger-live/.github/workflows/build-mobile-external-reusable.yml@develop
+ with:
+ ref: ${{ github.event.pull_request.head.ref }}
+ repository: ${{ github.event.pull_request.head.repo.full_name }}
+ secrets: inherit
# Final Check required
ok:
diff --git a/.github/workflows/build-and-test-pr.yml b/.github/workflows/build-and-test-pr.yml
index cf2b38919b9e..37c55935c2e0 100644
--- a/.github/workflows/build-and-test-pr.yml
+++ b/.github/workflows/build-and-test-pr.yml
@@ -14,6 +14,7 @@ permissions:
jobs:
determine-affected:
name: "Turbo Affected"
+ if: ${{github.event.pull_request.head.repo.full_name == github.repository }}
uses: LedgerHQ/ledger-live/.github/workflows/turbo-affected-reusable.yml@develop
with:
head_branch: ${{ github.event.pull_request.head.ref || github.event.merge_group.head_ref }}
diff --git a/.github/workflows/build-desktop-external-reusable.yml b/.github/workflows/build-desktop-external-reusable.yml
index e22f9a170bca..67bc572856df 100644
--- a/.github/workflows/build-desktop-external-reusable.yml
+++ b/.github/workflows/build-desktop-external-reusable.yml
@@ -2,20 +2,18 @@ name: "@Desktop • Build App (external)"
on:
workflow_call:
- workflow_dispatch:
inputs:
ref:
+ type: string
description: |
If you run this manually, and want to run on a PR, the correct ref should be refs/pull/{PR_NUMBER}/merge to
have the "normal" scenario involving checking out a merge commit between your branch and the base branch.
If you want to run only on a branch or specific commit, you can use either the sha or the branch name instead (prefer the first verion for PRs).
- required: false
- login:
- description: The GitHub username that triggered the workflow
- required: false
- base_ref:
- description: The base branch to merge the head into when checking out the code
- required: false
+ required: true
+ repository:
+ description: The repository to checkout the code from
+ type: string
+ required: true
jobs:
build-desktop-app:
@@ -46,7 +44,9 @@ jobs:
}
- uses: actions/checkout@v4
with:
- ref: ${{ github.ref_name }}
+ ref: ${{ inputs.ref }}
+ repository: ${{ inputs.repository }}
+ token: ${{ secrets.GITHUB_TOKEN }}
persist-credentials: false
- name: Setup git user
uses: LedgerHQ/ledger-live/tools/actions/composites/setup-git-user@develop
diff --git a/.github/workflows/build-mobile-external-reusable.yml b/.github/workflows/build-mobile-external-reusable.yml
index 30b9dc52fd37..fd729a3cae3e 100644
--- a/.github/workflows/build-mobile-external-reusable.yml
+++ b/.github/workflows/build-mobile-external-reusable.yml
@@ -2,23 +2,18 @@ name: "@Mobile • Build App (external)"
on:
workflow_call:
- workflow_dispatch:
inputs:
ref:
+ type: string
description: |
If you run this manually, and want to run on a PR, the correct ref should be refs/pull/{PR_NUMBER}/merge to
have the "normal" scenario involving checking out a merge commit between your branch and the base branch.
If you want to run only on a branch or specific commit, you can use either the sha or the branch name instead (prefer the first verion for PRs).
- login:
- description: The GitHub username that triggered the workflow
- required: false
- base_ref:
- description: The base branch to merge the head into when checking out the code
- required: false
-
-concurrency:
- group: ${{ github.workflow }}-${{ github.ref_name != 'develop' && github.ref || github.run_id }}
- cancel-in-progress: true
+ required: true
+ repository:
+ description: The repository to checkout the code from
+ type: string
+ required: true
permissions:
contents: read
@@ -26,13 +21,15 @@ permissions:
jobs:
build-mobile-app-android:
name: "Build Ledger Live Mobile (Android)"
- runs-on: ubuntu-latest
+ runs-on: ubuntu-22.04
env:
NODE_OPTIONS: "--max-old-space-size=7168"
steps:
- uses: actions/checkout@v4
with:
- ref: ${{ github.ref_name }}
+ ref: ${{ inputs.ref }}
+ repository: ${{ inputs.repository }}
+ token: ${{ secrets.GITHUB_TOKEN }}
persist-credentials: false
- name: Setup git user
uses: LedgerHQ/ledger-live/tools/actions/composites/setup-git-user@develop
@@ -92,7 +89,9 @@ jobs:
steps:
- uses: actions/checkout@v4
with:
- ref: ${{ github.ref_name }}
+ ref: ${{ inputs.ref }}
+ repository: ${{ inputs.repository }}
+ token: ${{ secrets.GITHUB_TOKEN }}
persist-credentials: false
- name: Setup git user
uses: LedgerHQ/ledger-live/tools/actions/composites/setup-git-user@develop
@@ -105,7 +104,7 @@ jobs:
run: pnpm i --filter="live-mobile..." --filter="ledger-live" --no-frozen-lockfile --unsafe-perm
report:
- runs-on: ubuntu-latest
+ runs-on: ubuntu-22.04
needs: [build-mobile-app-android, build-mobile-app-ios]
if: ${{ !cancelled() && github.event_name == 'workflow_dispatch' }}
steps:
diff --git a/.github/workflows/test-desktop-external-reusable.yml b/.github/workflows/test-desktop-external-reusable.yml
index 3069cf438fd1..d39b21a02ecb 100644
--- a/.github/workflows/test-desktop-external-reusable.yml
+++ b/.github/workflows/test-desktop-external-reusable.yml
@@ -2,20 +2,18 @@ name: "@Desktop • Test App (external)"
on:
workflow_call:
- workflow_dispatch:
inputs:
ref:
+ type: string
description: |
If you run this manually, and want to run on a PR, the correct ref should be refs/pull/{PR_NUMBER}/merge to
have the "normal" scenario involving checking out a merge commit between your branch and the base branch.
If you want to run only on a branch or specific commit, you can use either the sha or the branch name instead (prefer the first verion for PRs).
- required: false
- login:
- description: The GitHub username that triggered the workflow
- required: false
- base_ref:
- description: The base branch to merge the head into when checking out the code
- required: false
+ required: true
+ repository:
+ description: The repository to checkout the code from
+ type: string
+ required: true
permissions:
id-token: write
@@ -27,12 +25,14 @@ jobs:
env:
NODE_OPTIONS: "--max-old-space-size=7168"
FORCE_COLOR: 3
- CI_OS: ubuntu-latest
- runs-on: ubuntu-latest
+ CI_OS: ubuntu-22.04
+ runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
with:
- ref: ${{ github.ref_name }}
+ ref: ${{ inputs.ref }}
+ repository: ${{ inputs.repository }}
+ token: ${{ secrets.GITHUB_TOKEN }}
persist-credentials: false
- name: Setup the toolchain
uses: LedgerHQ/ledger-live/tools/actions/composites/setup-toolchain@develop
@@ -63,12 +63,14 @@ jobs:
env:
NODE_OPTIONS: "--max-old-space-size=7168"
FORCE_COLOR: 3
- CI_OS: ubuntu-latest
- runs-on: ubuntu-latest
+ CI_OS: ubuntu-22.04
+ runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
with:
- ref: ${{ github.ref_name }}
+ ref: ${{ inputs.ref }}
+ repository: ${{ inputs.repository }}
+ token: ${{ secrets.GITHUB_TOKEN }}
persist-credentials: false
- name: Setup the toolchain
uses: LedgerHQ/ledger-live/tools/actions/composites/setup-toolchain@develop
@@ -84,17 +86,19 @@ jobs:
env:
NODE_OPTIONS: "--max-old-space-size=7168"
FORCE_COLOR: 3
- CI_OS: ubuntu-latest
+ CI_OS: ubuntu-22.04
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1
# DEBUG: "pw:browser*"
# DEBUG_LOGS: 1
- runs-on: ubuntu-latest
+ runs-on: ubuntu-22.04
outputs:
status: ${{ steps.tests.outcome }}
steps:
- uses: actions/checkout@v4
with:
- ref: ${{ github.ref_name }}
+ ref: ${{ inputs.ref }}
+ repository: ${{ inputs.repository }}
+ token: ${{ secrets.GITHUB_TOKEN }}
persist-credentials: false
- name: Setup the toolchain
uses: LedgerHQ/ledger-live/tools/actions/composites/setup-toolchain@develop
@@ -147,12 +151,14 @@ jobs:
report:
needs: [codechecks, unit-tests, e2e-tests-linux]
- runs-on: ubuntu-latest
+ runs-on: ubuntu-22.04
if: ${{ !cancelled() && github.event_name == 'workflow_dispatch' }}
steps:
- uses: actions/checkout@v4
with:
- ref: ${{ github.ref_name }}
+ ref: ${{ inputs.ref }}
+ repository: ${{ inputs.repository }}
+ token: ${{ secrets.GITHUB_TOKEN }}
persist-credentials: false
- name: "download linter results"
uses: actions/download-artifact@v4
@@ -318,22 +324,3 @@ jobs:
with:
name: summary.json
path: ${{ github.workspace }}/summary.json
-
- allure-report:
- name: "Allure Reports Export on Server"
- needs: [e2e-tests-linux]
- runs-on: [ledger-live-medium]
- if: ${{ !cancelled() && github.ref_name == 'develop' }}
- steps:
- - name: checkout
- uses: actions/checkout@v4
- with:
- ref: ${{ github.ref_name }}
- - name: Send Results and Generate Allure Report - Linux
- uses: LedgerHQ/ledger-live/tools/actions/composites/upload-allure-report@develop
- if: ${{ !cancelled() }}
- with:
- platform: linux
- login: ${{ vars.ALLURE_USERNAME }}
- password: ${{ secrets.ALLURE_LEDGER_LIVE_PASSWORD }}
- path: allure-results-linux
diff --git a/apps/ledger-live-desktop/index-types.d.ts b/apps/ledger-live-desktop/index-types.d.ts
index 64587364fbb3..d1e41cc52d9f 100644
--- a/apps/ledger-live-desktop/index-types.d.ts
+++ b/apps/ledger-live-desktop/index-types.d.ts
@@ -63,6 +63,7 @@ interface Window {
getAllFeatureFlags: (appLanguage: string) => Partial<{ [key in FeatureId]: Feature }>;
getAllEnvs: () => { [key in EnvName]: unknown };
+ saveLogs: (path: string) => void;
// for mocking purposes apparently?
// eslint-disable-next-line
diff --git a/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/Inscriptions/DetailsDrawer/Actions.tsx b/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/Inscriptions/DetailsDrawer/Actions.tsx
new file mode 100644
index 000000000000..f81c2a28202b
--- /dev/null
+++ b/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/Inscriptions/DetailsDrawer/Actions.tsx
@@ -0,0 +1,32 @@
+import React from "react";
+import { Flex, Icons, IconsLegacy, Text } from "@ledgerhq/react-ui";
+import Button from "~/renderer/components/Button";
+import { useTranslation } from "react-i18next";
+
+const Actions = () => {
+ const { t } = useTranslation();
+ return (
+
+
+
+
+ );
+};
+export default Actions;
diff --git a/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/Inscriptions/DetailsDrawer/SubTitle.tsx b/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/Inscriptions/DetailsDrawer/SubTitle.tsx
new file mode 100644
index 000000000000..21c43cae7fe2
--- /dev/null
+++ b/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/Inscriptions/DetailsDrawer/SubTitle.tsx
@@ -0,0 +1,25 @@
+import React from "react";
+import { Flex, Text } from "@ledgerhq/react-ui";
+import IconContainer from "LLD/features/Collectibles/components/Collection/TableRow/IconContainer";
+import { IconProps } from "LLD/features/Collectibles/types/Collection";
+import { MappingKeys } from "LLD/features/Collectibles/types/Ordinals";
+import { useTranslation } from "react-i18next";
+
+type Props = {
+ icons: (({ size, color, style }: IconProps) => JSX.Element)[];
+ names: MappingKeys[];
+};
+
+const SubTitle: React.FC = ({ icons, names }) => {
+ const { t } = useTranslation();
+ return (
+
+
+
+ {t("ordinals.inscriptions.detailsDrawer.storedOnChain")}
+
+
+ );
+};
+
+export default SubTitle;
diff --git a/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/Inscriptions/DetailsDrawer/index.tsx b/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/Inscriptions/DetailsDrawer/index.tsx
index eb7ff49015cf..abedd3a803ae 100644
--- a/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/Inscriptions/DetailsDrawer/index.tsx
+++ b/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/Inscriptions/DetailsDrawer/index.tsx
@@ -1,18 +1,66 @@
import React from "react";
-import { SideDrawer } from "~/renderer/components/SideDrawer";
import { SimpleHashNft } from "@ledgerhq/live-nft/api/types";
+import { DetailDrawer } from "LLD/features/Collectibles/components";
+import { CollectibleTypeEnum } from "LLD/features/Collectibles/types/enum/Collectibles";
+import useInscriptionDetailDrawer from "./useInscriptionDetailDrawer";
+import Actions from "./Actions";
+import SubTitle from "./SubTitle";
+
+type ViewProps = ReturnType & {
+ onClose: () => void;
+};
type Props = {
inscription: SimpleHashNft;
+ correspondingRareSat: SimpleHashNft | null | undefined;
+ isLoading: boolean;
onClose: () => void;
};
-const InscriptionDetailsDrawer: React.FC = ({ inscription, onClose }) => {
- // will be replaced by DetailsDrawer from collectibles
+
+const View: React.FC = ({ data, rareSat, onClose }) => (
+
+ {rareSat?.icons && (
+
+
+
+ )}
+
+
+
+
+);
+
+const InscriptionDetailDrawer = ({
+ inscription,
+ isLoading,
+ correspondingRareSat,
+ onClose,
+}: Props) => {
return (
-
- {inscription.name || inscription.contract.name}
-
+
);
};
-export default InscriptionDetailsDrawer;
+export default InscriptionDetailDrawer;
diff --git a/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/Inscriptions/DetailsDrawer/useInscriptionDetailDrawer.ts b/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/Inscriptions/DetailsDrawer/useInscriptionDetailDrawer.ts
new file mode 100644
index 000000000000..c82cae275514
--- /dev/null
+++ b/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/Inscriptions/DetailsDrawer/useInscriptionDetailDrawer.ts
@@ -0,0 +1,64 @@
+import { useMemo, useState } from "react";
+import { SimpleHashNft } from "@ledgerhq/live-nft/api/types";
+import useCollectibles from "LLD/features/Collectibles/hooks/useCollectibles";
+import { createDetails } from "LLD/features/Collectibles/utils/createInscriptionDetailsArrays";
+import { useCalendarFormatted } from "~/renderer/hooks/useDateFormatter";
+import { Tag } from "LLD/features/Collectibles/types/DetailDrawer";
+import { processRareSat } from "../helpers";
+
+type Props = {
+ isLoading: boolean;
+ inscription: SimpleHashNft;
+ correspondingRareSat: SimpleHashNft | null | undefined;
+};
+
+const useInscriptionDetailDrawer = ({ isLoading, inscription, correspondingRareSat }: Props) => {
+ const [useFallback, setUseFallback] = useState(false);
+ const imageUri =
+ inscription.video_url || inscription.previews?.image_large_url || inscription.image_url;
+
+ const isVideo = !!inscription.video_url;
+
+ const contentType = isVideo ? "video" : imageUri ? "image" : "";
+
+ const { isPanAndZoomOpen, openCollectiblesPanAndZoom, closeCollectiblesPanAndZoom } =
+ useCollectibles();
+
+ const createdDateFromTimestamp = new Date(inscription.first_created?.timestamp || 0);
+ const formattedCreatedDate = useCalendarFormatted(createdDateFromTimestamp);
+ const createdDate = createdDateFromTimestamp === new Date(0) ? "" : formattedCreatedDate;
+ const details = createDetails(inscription, createdDate);
+
+ const tags: Tag[] =
+ inscription.extra_metadata?.attributes?.map(attr => ({
+ key: attr.trait_type,
+ value: attr.value,
+ })) || [];
+
+ const rareSat = useMemo(() => {
+ if (correspondingRareSat) return processRareSat(correspondingRareSat);
+ }, [correspondingRareSat]);
+
+ const data = {
+ areFieldsLoading: isLoading,
+ collectibleName: inscription.name || inscription.contract.name || "",
+ contentType,
+ collectionName: inscription.collection.name || "",
+ details: details,
+ previewUri: imageUri,
+ originalUri: imageUri,
+ isPanAndZoomOpen,
+ mediaType: inscription.video_properties?.mime_type || "image",
+ tags: tags,
+ useFallback: useFallback,
+ tokenId: inscription.nft_id,
+ isOpened: true,
+ closeCollectiblesPanAndZoom,
+ openCollectiblesPanAndZoom,
+ setUseFallback: setUseFallback,
+ };
+
+ return { inscription, data, rareSat };
+};
+
+export default useInscriptionDetailDrawer;
diff --git a/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/Inscriptions/DiscoveryDrawer/ProtectBox.tsx b/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/Inscriptions/DiscoveryDrawer/ProtectBox.tsx
index 482a46ebcbff..8c428e9f8a18 100644
--- a/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/Inscriptions/DiscoveryDrawer/ProtectBox.tsx
+++ b/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/Inscriptions/DiscoveryDrawer/ProtectBox.tsx
@@ -4,9 +4,10 @@ import Switch from "~/renderer/components/Switch";
import { hasProtectedOrdinalsAssetsSelector } from "~/renderer/reducers/settings";
import { setHasProtectedOrdinalsAssets } from "~/renderer/actions/settings";
import { useDispatch, useSelector } from "react-redux";
-import { t } from "i18next";
+import { useTranslation } from "react-i18next";
const ProtectBox: React.FC = () => {
+ const { t } = useTranslation();
const dispatch = useDispatch();
const hasProtectedOrdinals = useSelector(hasProtectedOrdinalsAssetsSelector);
diff --git a/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/Inscriptions/Item.tsx b/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/Inscriptions/Item.tsx
deleted file mode 100644
index dc3f2ab8ffd8..000000000000
--- a/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/Inscriptions/Item.tsx
+++ /dev/null
@@ -1,29 +0,0 @@
-import { InscriptionsItemProps } from "LLD/features/Collectibles/types/Inscriptions";
-import TableRow from "LLD/features/Collectibles/components/Collection/TableRow";
-import React from "react";
-
-type ItemProps = {
- isLoading: boolean;
-} & InscriptionsItemProps;
-
-const Item: React.FC = ({
- isLoading,
- tokenName,
- collectionName,
- tokenIcons,
- media,
- rareSatName,
- onClick,
-}) => (
-
-);
-
-export default Item;
diff --git a/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/Inscriptions/Item/index.tsx b/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/Inscriptions/Item/index.tsx
new file mode 100644
index 000000000000..b9c404527346
--- /dev/null
+++ b/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/Inscriptions/Item/index.tsx
@@ -0,0 +1,40 @@
+import React, { useMemo } from "react";
+import { InscriptionsItemProps } from "LLD/features/Collectibles/types/Inscriptions";
+import TableRow from "LLD/features/Collectibles/components/Collection/TableRow";
+import { GroupedNftOrdinals } from "@ledgerhq/live-nft-react/index";
+import { findCorrespondingSat } from "LLD/features/Collectibles/utils/findCorrespondingSat";
+import { processRareSat } from "../helpers";
+
+type ItemProps = {
+ isLoading: boolean;
+ inscriptionsGroupedWithRareSats: GroupedNftOrdinals[];
+} & InscriptionsItemProps;
+
+const Item: React.FC = ({
+ isLoading,
+ tokenName,
+ collectionName,
+ media,
+ nftId,
+ inscriptionsGroupedWithRareSats,
+ onClick,
+}) => {
+ const correspondingRareSat = findCorrespondingSat(inscriptionsGroupedWithRareSats, nftId);
+ const rareSat = useMemo(() => {
+ if (correspondingRareSat) return processRareSat(correspondingRareSat.rareSat);
+ }, [correspondingRareSat]);
+
+ return (
+
+ );
+};
+
+export default Item;
diff --git a/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/Inscriptions/helpers.ts b/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/Inscriptions/helpers.ts
index 7ea111c9c9c9..695f0a214b1d 100644
--- a/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/Inscriptions/helpers.ts
+++ b/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/Inscriptions/helpers.ts
@@ -1,40 +1,14 @@
import { SimpleHashNft } from "@ledgerhq/live-nft/api/types";
-import { IconProps } from "LLD/features/Collectibles/types/Collection";
-import { mappingKeysWithIconAndName } from "../Icons";
-import { MappingKeys } from "LLD/features/Collectibles/types/Ordinals";
-
-function matchCorrespondingIcon(
- rareSats: SimpleHashNft[],
-): Array JSX.Element> }> {
- return rareSats.map(rareSat => {
- const iconKeys: string[] = [];
- const rarity = rareSat.extra_metadata?.ordinal_details?.sat_rarity?.toLowerCase();
-
- if (rarity && rarity !== "common") {
- iconKeys.push(rarity.replace(" ", "_"));
- }
-
- const icons = iconKeys
- .map(
- iconKey =>
- mappingKeysWithIconAndName[iconKey as keyof typeof mappingKeysWithIconAndName]?.icon,
- )
- .filter(Boolean) as Array<({ size, color, style }: IconProps) => JSX.Element>;
-
- return { ...rareSat, icons };
- });
-}
+import { createRareSatObject, matchCorrespondingIcon } from "../helpers";
export function getInscriptionsData(
inscriptions: SimpleHashNft[],
onInscriptionClick: (inscription: SimpleHashNft) => void,
) {
- const inscriptionsWithIcons = matchCorrespondingIcon(inscriptions);
- return inscriptionsWithIcons.map(item => ({
+ return inscriptions.map(item => ({
tokenName: item.name || item.contract.name || "",
+ nftId: item.nft_id,
collectionName: item.collection.name,
- tokenIcons: item.icons,
- rareSatName: [item.extra_metadata?.ordinal_details?.sat_rarity] as MappingKeys[],
media: {
uri: item.image_url || item.previews?.image_small_url,
isLoading: false,
@@ -45,3 +19,9 @@ export function getInscriptionsData(
onClick: () => onInscriptionClick(item),
}));
}
+
+export function processRareSat(inscription: SimpleHashNft) {
+ const matchedRareSatsIcons = matchCorrespondingIcon(inscription);
+ const rareSatObject = createRareSatObject({ rareSat: matchedRareSatsIcons });
+ return rareSatObject.rareSat[0];
+}
diff --git a/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/Inscriptions/index.tsx b/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/Inscriptions/index.tsx
index 3e88f98b0e75..e6269cb4034b 100644
--- a/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/Inscriptions/index.tsx
+++ b/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/Inscriptions/index.tsx
@@ -10,14 +10,16 @@ import Loader from "../Loader";
import Error from "../Error";
import Item from "./Item";
import EmptyCollection from "LLD/features/Collectibles/components/Collection/EmptyCollection";
-import { CollectibleTypeEnum } from "../../../types/enum/Collectibles";
+import { CollectibleTypeEnum } from "LLD/features/Collectibles/types/enum/Collectibles";
import Button from "~/renderer/components/Button";
import { useTranslation } from "react-i18next";
+import { GroupedNftOrdinals } from "@ledgerhq/live-nft-react/index";
type ViewProps = ReturnType & {
isLoading: boolean;
isError: boolean;
error: Error | null;
+ inscriptionsGroupedWithRareSats: GroupedNftOrdinals[];
onReceive: () => void;
};
@@ -26,6 +28,7 @@ type Props = {
isLoading: boolean;
isError: boolean;
error: Error | null;
+ inscriptionsGroupedWithRareSats: GroupedNftOrdinals[];
onReceive: () => void;
onInscriptionClick: (inscription: SimpleHashNft) => void;
};
@@ -36,6 +39,7 @@ const View: React.FC = ({
isError,
inscriptions,
error,
+ inscriptionsGroupedWithRareSats,
onShowMore,
onReceive,
}) => {
@@ -55,12 +59,8 @@ const View: React.FC = ({
))}
{nothingToShow && (
@@ -84,6 +84,7 @@ const Inscriptions: React.FC = ({
isLoading,
isError,
error,
+ inscriptionsGroupedWithRareSats,
onReceive,
onInscriptionClick,
}) => (
@@ -92,6 +93,7 @@ const Inscriptions: React.FC = ({
isError={isError}
error={error}
onReceive={onReceive}
+ inscriptionsGroupedWithRareSats={inscriptionsGroupedWithRareSats}
{...useInscriptionsModel({ inscriptions, onInscriptionClick })}
/>
);
diff --git a/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/RareSats/helpers.ts b/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/RareSats/helpers.ts
deleted file mode 100644
index 9a7423bbe53b..000000000000
--- a/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/RareSats/helpers.ts
+++ /dev/null
@@ -1,62 +0,0 @@
-import { mappingKeysWithIconAndName } from "../Icons";
-import { SimpleHashNft } from "@ledgerhq/live-nft/api/types";
-import {
- SimpleHashNftWithIcons,
- RareSat,
- MappingKeys,
-} from "LLD/features/Collectibles/types/Ordinals";
-
-export function matchCorrespondingIcon(rareSats: SimpleHashNft[]): SimpleHashNftWithIcons[] {
- return rareSats.map(rareSat => {
- const iconKeys: string[] = [];
- if (rareSat.name) {
- iconKeys.push(rareSat.name.toLowerCase().replace(" ", "_"));
- }
-
- if (rareSat.extra_metadata?.utxo_details?.satributes) {
- iconKeys.push(...Object.keys(rareSat.extra_metadata.utxo_details.satributes));
- }
-
- const icons = iconKeys
- .map(
- iconKey =>
- mappingKeysWithIconAndName[iconKey as keyof typeof mappingKeysWithIconAndName]?.icon,
- )
- .filter(Boolean) as RareSat["icons"];
-
- return { ...rareSat, icons };
- });
-}
-
-export function createRareSatObject(
- rareSats: Record,
-): Record {
- const result: Record = {};
-
- 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 names = rareSat.extra_metadata?.utxo_details?.satributes
- ? (Object.keys(rareSat.extra_metadata.utxo_details.satributes) as MappingKeys[])
- : [];
- const count = extra_metadata?.utxo_details?.value || 0;
- const isMultipleRow = value.length > 1;
-
- return {
- year,
- displayed_names,
- names,
- count: `${count} ${count > 1 ? "sats" : "sat"}`,
- isMultipleRow,
- icons,
- };
- });
- }
-
- return result;
-}
diff --git a/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/RareSats/useRareSatsModel.ts b/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/RareSats/useRareSatsModel.ts
index a59b1eafc054..e73078536530 100644
--- a/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/RareSats/useRareSatsModel.ts
+++ b/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/RareSats/useRareSatsModel.ts
@@ -1,4 +1,4 @@
-import { matchCorrespondingIcon, createRareSatObject } from "./helpers";
+import { matchCorrespondingIcon, createRareSatObject } from "../helpers";
import { SimpleHashNft } from "@ledgerhq/live-nft/api/types";
import { regroupRareSatsByContractAddress } from "@ledgerhq/live-nft-react";
diff --git a/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/helpers.ts b/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/helpers.ts
new file mode 100644
index 000000000000..c323b37e8bd9
--- /dev/null
+++ b/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/helpers.ts
@@ -0,0 +1,78 @@
+import { mappingKeysWithIconAndName } from "./Icons";
+import { SimpleHashNft } from "@ledgerhq/live-nft/api/types";
+import {
+ SimpleHashNftWithIcons,
+ RareSat,
+ MappingKeys,
+} from "LLD/features/Collectibles/types/Ordinals";
+
+function processSingleRareSat(rareSat: SimpleHashNft): SimpleHashNftWithIcons {
+ const iconKeys: string[] = [];
+ if (rareSat.name) {
+ iconKeys.push(rareSat.name.toLowerCase().replace(" ", "_"));
+ }
+
+ if (rareSat.extra_metadata?.utxo_details?.satributes) {
+ iconKeys.push(...Object.keys(rareSat.extra_metadata.utxo_details.satributes));
+ }
+
+ const icons = iconKeys
+ .map(
+ iconKey =>
+ mappingKeysWithIconAndName[iconKey as keyof typeof mappingKeysWithIconAndName]?.icon,
+ )
+ .filter(Boolean) as RareSat["icons"];
+
+ return { ...rareSat, icons };
+}
+
+function mapToRareSat(rareSat: SimpleHashNftWithIcons, isMultipleRow: boolean): 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 names = rareSat.extra_metadata?.utxo_details?.satributes
+ ? (Object.keys(rareSat.extra_metadata.utxo_details.satributes) as MappingKeys[])
+ : [];
+ const count = extra_metadata?.utxo_details?.value || 0;
+
+ return {
+ year,
+ displayed_names,
+ names,
+ count: `${count} ${count > 1 ? "sats" : "sat"}`,
+ isMultipleRow,
+ icons,
+ };
+}
+
+function processRareSatObject(
+ rareSats: Record,
+): Record {
+ const result: Record = {};
+
+ for (const [key, value] of Object.entries(rareSats)) {
+ result[key] = value.map(rareSat => mapToRareSat(rareSat, value.length > 1));
+ }
+
+ return result;
+}
+
+export function matchCorrespondingIcon(
+ rareSats: SimpleHashNft | SimpleHashNft[],
+): SimpleHashNftWithIcons[] {
+ const rareSatArray = Array.isArray(rareSats) ? rareSats : [rareSats];
+ return rareSatArray.map(processSingleRareSat);
+}
+
+export function createRareSatObject(
+ rareSats: Record | SimpleHashNftWithIcons[],
+): Record {
+ if (Array.isArray(rareSats)) {
+ return { default: rareSats.map(rareSat => mapToRareSat(rareSat, rareSats.length > 1)) };
+ } else {
+ return processRareSatObject(rareSats);
+ }
+}
diff --git a/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/screens/Account/index.tsx b/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/screens/Account/index.tsx
index f1683da348f9..cbec04e52909 100644
--- a/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/screens/Account/index.tsx
+++ b/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/screens/Account/index.tsx
@@ -19,6 +19,8 @@ const View: React.FC = ({
rareSats,
isDrawerOpen,
selectedInscription,
+ correspondingRareSat,
+ inscriptionsGroupedWithRareSats,
handleDrawerClose,
onReceive,
onInscriptionClick,
@@ -26,15 +28,21 @@ const View: React.FC = ({
}) => (
{selectedInscription && (
-
+
)}
);
diff --git a/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/screens/Account/useBitcoinAccountModel.ts b/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/screens/Account/useBitcoinAccountModel.ts
index b6bbe99d37d3..c1c8d0ad620b 100644
--- a/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/screens/Account/useBitcoinAccountModel.ts
+++ b/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/screens/Account/useBitcoinAccountModel.ts
@@ -6,6 +6,7 @@ import { useDispatch, useSelector } from "react-redux";
import { openModal } from "~/renderer/actions/modals";
import { setHasSeenOrdinalsDiscoveryDrawer } from "~/renderer/actions/settings";
import { hasSeenOrdinalsDiscoveryDrawerSelector } from "~/renderer/reducers/settings";
+import { findCorrespondingSat } from "LLD/features/Collectibles/utils/findCorrespondingSat";
interface Props {
account: BitcoinAccount;
@@ -15,8 +16,13 @@ export const useBitcoinAccountModel = ({ account }: Props) => {
const dispatch = useDispatch();
const hasSeenDiscoveryDrawer = useSelector(hasSeenOrdinalsDiscoveryDrawerSelector);
const [selectedInscription, setSelectedInscription] = useState(null);
+ const [correspondingRareSat, setCorrespondingRareSat] = useState<
+ SimpleHashNft | null | undefined
+ >(null);
- const { rareSats, inscriptions, ...rest } = useFetchOrdinals({ account });
+ const { rareSats, inscriptions, inscriptionsGroupedWithRareSats, ...rest } = useFetchOrdinals({
+ account,
+ });
const [isDrawerOpen, setIsDrawerOpen] = useState(!hasSeenDiscoveryDrawer);
@@ -40,7 +46,11 @@ export const useBitcoinAccountModel = ({ account }: Props) => {
);
}, [dispatch, account]);
- const onInscriptionClick = (inscription: SimpleHashNft) => setSelectedInscription(inscription);
+ const onInscriptionClick = (inscription: SimpleHashNft) => {
+ const groupedNft = findCorrespondingSat(inscriptionsGroupedWithRareSats, inscription.nft_id);
+ setCorrespondingRareSat(groupedNft?.rareSat ?? null);
+ setSelectedInscription(inscription);
+ };
const onDetailsDrawerClose = () => setSelectedInscription(null);
@@ -50,6 +60,8 @@ export const useBitcoinAccountModel = ({ account }: Props) => {
rest,
isDrawerOpen,
selectedInscription,
+ correspondingRareSat,
+ inscriptionsGroupedWithRareSats,
onReceive,
handleDrawerClose,
onInscriptionClick,
diff --git a/apps/ledger-live-desktop/src/newArch/features/Collectibles/__integration__/bitcoinPage.test.tsx b/apps/ledger-live-desktop/src/newArch/features/Collectibles/__integration__/bitcoinPage.test.tsx
index e1046b592629..66797fa60c8e 100644
--- a/apps/ledger-live-desktop/src/newArch/features/Collectibles/__integration__/bitcoinPage.test.tsx
+++ b/apps/ledger-live-desktop/src/newArch/features/Collectibles/__integration__/bitcoinPage.test.tsx
@@ -18,24 +18,24 @@ jest.mock("~/renderer/linking", () => ({
describe("displayBitcoinPage", () => {
it("should display Bitcoin page with rare sats and inscriptions", async () => {
- const { user } = render();
+ const { user } = render(, {
+ initialState: {
+ settings: {
+ hasSeenOrdinalsDiscoveryDrawer: true,
+ },
+ },
+ });
- await waitFor(() => expect(screen.getByText(/inscription #63691311/i)).toBeVisible());
- await waitFor(() => expect(screen.getByTestId(/raresaticon-palindrome-0/i)).toBeVisible());
- await user.hover(screen.getByTestId(/raresaticon-palindrome-0/i));
- await waitFor(() => expect(screen.getByText(/in a playful twist/i)).toBeVisible());
- await waitFor(() =>
- expect(
- screen.getByText(/block 9 \/ first transaction \/ nakamoto \/ vintage/i),
- ).toBeVisible(),
- );
+ await waitFor(() => expect(screen.getByText(/the great war #3695/i)).toBeVisible());
await waitFor(() => expect(screen.getByText(/see more inscriptions/i)).toBeVisible());
await user.click(screen.getByText(/see more inscriptions/i));
- await waitFor(() => expect(screen.getByText(/timechain #136/i)).toBeVisible());
- await waitFor(() => expect(screen.getByTestId(/raresaticon-jpeg-0/i)).toBeVisible());
- await user.hover(screen.getByTestId(/raresaticon-jpeg-0/i));
- await waitFor(() => expect(screen.getByText(/journey into the past with jpeg/i)).toBeVisible());
+ await user.click(screen.getByText(/see more inscriptions/i));
+ await waitFor(() => expect(screen.getByText(/bitcoin puppet #71/i)).toBeVisible());
+ await waitFor(() => expect(screen.queryAllByTestId(/raresaticon-pizza-0/i)).toHaveLength(2));
+ await user.hover(screen.queryAllByTestId(/raresaticon-pizza-0/i)[0]);
+ await waitFor(() => expect(screen.getByText(/papa john's pizza/i)).toBeVisible());
});
+
it("should open discovery drawer when it is the first time feature is activated", async () => {
const { user } = render();
@@ -43,4 +43,20 @@ describe("displayBitcoinPage", () => {
await user.click(screen.getByText(/learn more/i));
expect(openURL).toHaveBeenCalledWith("https://www.ledger.com/academy/bitcoin-ordinals");
});
+
+ it("should open inscription detail drawer", async () => {
+ const { user } = render(, {
+ initialState: {
+ settings: {
+ hasSeenOrdinalsDiscoveryDrawer: true,
+ },
+ },
+ });
+
+ await waitFor(() => expect(screen.getByText(/the great war #3695/i)).toBeVisible());
+ await user.click(screen.getByText(/the great war #3695/i));
+ await expect(screen.getByText(/hide/i)).toBeVisible();
+ // sat name
+ await expect(screen.getByText(/dlngbapxjdv/i)).toBeVisible();
+ });
});
diff --git a/apps/ledger-live-desktop/src/newArch/features/Collectibles/components/Collection/HeaderActions.tsx b/apps/ledger-live-desktop/src/newArch/features/Collectibles/components/Collection/HeaderActions.tsx
index 5a3074491d6d..8f993d1b3d24 100644
--- a/apps/ledger-live-desktop/src/newArch/features/Collectibles/components/Collection/HeaderActions.tsx
+++ b/apps/ledger-live-desktop/src/newArch/features/Collectibles/components/Collection/HeaderActions.tsx
@@ -1,7 +1,7 @@
-import Button from "~/renderer/components/Button";
-import { t } from "i18next";
import React from "react";
+import Button from "~/renderer/components/Button";
import Box from "~/renderer/components/Box";
+import { useTranslation } from "react-i18next";
type Props = {
children?: JSX.Element;
@@ -9,6 +9,7 @@ type Props = {
};
const HeaderActions: React.FC = ({ children, textKey }) => {
+ const { t } = useTranslation();
return (