Skip to content

Commit

Permalink
feat: ✨ Add spamFilteringTx in LLD (#7911)
Browse files Browse the repository at this point in the history
* feat: ✨ Add spamFilteringTx hook in global

feat: ✨ Add spamFilteringTx hook in global

review: Enums

* feat: 💄UX/UI Improvements on HiddenCollections section (#7912)

Fix some UI broken + improve SimpleHash tool

* bugfix:  empty state account + Check when adding new address
test: ✅ Add tests for new Hooks

Check when adding new address

✅ Add tests for new Hooks
  • Loading branch information
mcayuelas-ledger authored Oct 31, 2024
1 parent cd686cf commit 87218b1
Show file tree
Hide file tree
Showing 47 changed files with 796 additions and 400 deletions.
8 changes: 8 additions & 0 deletions .changeset/nervous-pumpkins-remain.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"@ledgerhq/types-live": patch
"ledger-live-desktop": patch
"@ledgerhq/live-common": patch
"@ledgerhq/live-nft-react": patch
---

Add useCheckNftAccount Hook
5 changes: 5 additions & 0 deletions .changeset/short-hotels-invite.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"ledger-live-desktop": patch
---

UX/UI Improvements on HiddenCollections section
7 changes: 7 additions & 0 deletions .changeset/short-spoons-notice.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"ledger-live-desktop": patch
"@ledgerhq/live-common": patch
"@ledgerhq/live-nft-react": patch
---

use Hook CheckNft in Default and handle global sync of NFTs every 12 hours
Original file line number Diff line number Diff line change
Expand Up @@ -2,39 +2,27 @@ import { useSelector } from "react-redux";
import { useHistory, useParams } from "react-router-dom";
import { State } from "~/renderer/reducers";
import { accountSelector } from "~/renderer/reducers/accounts";
import { nftsByCollections } from "@ledgerhq/live-nft";
import { useCallback, useMemo } from "react";
import { ProtoNFT } from "@ledgerhq/types-live";
import { DropDownItemType } from "~/renderer/components/DropDownSelector";
import { setTrackingSource } from "~/renderer/analytics/TrackPage";
import { useFeature } from "@ledgerhq/live-common/featureFlags/index";
import { isThresholdValid, useNftGalleryFilter } from "@ledgerhq/live-nft-react";
import { useNftCollections } from "~/renderer/hooks/nfts/useNftCollections";

const useBreadCrumbModel = () => {
const history = useHistory();
const { id, collectionAddress } = useParams<{ id?: string; collectionAddress?: string }>();
const nftsFromSimplehashFeature = useFeature("nftsFromSimplehash");
const thresold = nftsFromSimplehashFeature?.params?.threshold;

const account = useSelector((state: State) =>
id ? accountSelector(state, { accountId: id }) : null,
id ? accountSelector(state, { accountId: id }) : undefined,
);

const { nfts } = useNftGalleryFilter({
nftsOwned: account?.nfts || [],
addresses: String(account?.freshAddress),
chains: [String(account?.currency.id)],
threshold: isThresholdValid(thresold) ? Number(thresold) : 75,
const { collections } = useNftCollections({
account,
});

const collections = useMemo(
() => nftsByCollections(nftsFromSimplehashFeature?.enabled ? nfts : account?.nfts),
[account?.nfts, nfts, nftsFromSimplehashFeature],
);

const items: DropDownItemType<ProtoNFT>[] = useMemo(
() =>
Object.entries(collections).map(([contract, nfts]: [string, ProtoNFT[]]) => ({
collections.map(([contract, nfts]: [string, ProtoNFT[]]) => ({
key: contract,
label: contract,
content: nfts[0],
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,10 @@
import { useCallback, useEffect, useMemo, useState } from "react";
import { Account, ProtoNFT } from "@ledgerhq/types-live";
import { useDispatch, useSelector } from "react-redux";
import { useDispatch } from "react-redux";
import { useHistory } from "react-router-dom";
import { openModal } from "~/renderer/actions/modals";
import { hiddenNftCollectionsSelector } from "~/renderer/reducers/settings";
import { nftsByCollections } from "@ledgerhq/live-nft/index";
import { useFeature } from "@ledgerhq/live-common/featureFlags/index";
import { isThresholdValid, useNftGalleryFilter } from "@ledgerhq/live-nft-react";
import {
filterHiddenCollections,
mapCollectionsToStructure,
} from "LLD/features/Collectibles/utils/collectionUtils";
import { mapCollectionsToStructure } from "LLD/features/Collectibles/utils/collectionUtils";
import { useNftCollections } from "~/renderer/hooks/nfts/useNftCollections";

type NftsInTheCollections = {
contract: string;
Expand All @@ -27,9 +21,6 @@ const INCREMENT = 5;
export const useNftCollectionsModel = ({ account }: Props) => {
const history = useHistory();
const dispatch = useDispatch();
const nftsFromSimplehashFeature = useFeature("nftsFromSimplehash");
const thresold = nftsFromSimplehashFeature?.params?.threshold;
const hiddenNftCollections = useSelector(hiddenNftCollectionsSelector);
const [numberOfVisibleCollections, setNumberOfVisibleCollections] = useState(INCREMENT);
const [displayShowMore, setDisplayShowMore] = useState(false);

Expand All @@ -53,42 +44,26 @@ export const useNftCollectionsModel = ({ account }: Props) => {
history.push(`/account/${account.id}/nft-collection`);
}, [account.id, history]);

const { nfts, fetchNextPage, hasNextPage } = useNftGalleryFilter({
nftsOwned: account.nfts || [],
addresses: account.freshAddress,
chains: [account.currency.id],
threshold: isThresholdValid(thresold) ? Number(thresold) : 75,
const { fetchNextPage, hasNextPage, collections, collectionsLength } = useNftCollections({
account,
});

const collections = useMemo(
() => nftsByCollections(nftsFromSimplehashFeature?.enabled ? nfts : account.nfts),
[account.nfts, nfts, nftsFromSimplehashFeature],
);

const collectionsLength = Object.keys(collections).length;

const onShowMore = useCallback(() => {
setNumberOfVisibleCollections(numberOfVisibleCollections =>
Math.min(numberOfVisibleCollections + INCREMENT, collectionsLength),
);
if (hasNextPage) fetchNextPage();
}, [collectionsLength, fetchNextPage, hasNextPage]);

const filteredCollections = useMemo(
() => filterHiddenCollections(collections, hiddenNftCollections, account.id),
[account.id, collections, hiddenNftCollections],
);

const nftsInTheCollection: NftsInTheCollections[] = useMemo(
() =>
mapCollectionsToStructure(filteredCollections, numberOfVisibleCollections, onOpenCollection),
[filteredCollections, numberOfVisibleCollections, onOpenCollection],
() => mapCollectionsToStructure(collections, numberOfVisibleCollections, onOpenCollection),
[collections, numberOfVisibleCollections, onOpenCollection],
);

useEffect(() => {
const moreToShow = numberOfVisibleCollections < filteredCollections.length;
const moreToShow = numberOfVisibleCollections < collections.length;
setDisplayShowMore(moreToShow);
}, [numberOfVisibleCollections, filteredCollections.length]);
}, [numberOfVisibleCollections, collections.length]);

return {
nftsInTheCollection,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,10 @@ import { useHistory, useParams } from "react-router-dom";
import { useDispatch, useSelector } from "react-redux";
import { State } from "~/renderer/reducers";
import { accountSelector } from "~/renderer/reducers/accounts";
import { hiddenNftCollectionsSelector } from "~/renderer/reducers/settings";
import { useNftGalleryFilter, isThresholdValid } from "@ledgerhq/live-nft-react";
import { nftsByCollections } from "@ledgerhq/live-nft";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { useCallback, useEffect, useRef, useState } from "react";
import { openModal } from "~/renderer/actions/modals";
import { useOnScreen } from "LLD/hooks/useOnScreen";
import useFeature from "@ledgerhq/live-common/featureFlags/useFeature";
import { ChainsEnum } from "LLD/features/Collectibles/types/enum/Chains";
import { useNftCollections } from "~/renderer/hooks/nfts/useNftCollections";

const defaultNumberOfVisibleNfts = 10;

Expand All @@ -18,31 +14,17 @@ const useNftGalleryModel = () => {
const history = useHistory();
const { id } = useParams<{ id: string }>();

const nftsFromSimplehashFeature = useFeature("nftsFromSimplehash");
const threshold = nftsFromSimplehashFeature?.params?.threshold;

const listFooterRef = useRef<HTMLDivElement>(null);
const [maxVisibleNFTs, setMaxVisibleNFTs] = useState(defaultNumberOfVisibleNfts);

const { account, hiddenNftCollections } = useSelector((state: State) => ({
const { account } = useSelector((state: State) => ({
account: accountSelector(state, { accountId: id }),
hiddenNftCollections: hiddenNftCollectionsSelector(state),
}));

const { nfts, fetchNextPage, hasNextPage } = useNftGalleryFilter({
nftsOwned: account?.nfts || [],
addresses: account?.freshAddress || "",
chains: [account?.currency.id ?? ChainsEnum.ETHEREUM],
threshold: isThresholdValid(threshold) ? Number(threshold) : 75,
const { fetchNextPage, hasNextPage, collections, allNfts } = useNftCollections({
account,
});

const collections = useMemo(() => {
const allNfts = nftsFromSimplehashFeature?.enabled ? nfts : account?.nfts;
return Object.entries(nftsByCollections(allNfts)).filter(
([contract]) => !hiddenNftCollections.includes(`${account?.id}|${contract}`),
);
}, [account?.id, account?.nfts, hiddenNftCollections, nfts, nftsFromSimplehashFeature?.enabled]);

useEffect(() => {
if (collections.length < 1) {
history.push(`/account/${account?.id}/`);
Expand Down Expand Up @@ -70,13 +52,13 @@ const useNftGalleryModel = () => {
}, [hasNextPage, fetchNextPage]);

useOnScreen({
enabled: maxVisibleNFTs < nfts?.length,
enabled: maxVisibleNFTs < allNfts?.length,
onIntersect: updateMaxVisibleNtfs,
target: listFooterRef,
threshold: 0.5,
});

const nftsByCollection = nfts.reduce(
const nftsByCollection = allNfts.reduce(
(acc, nft) => {
const collectionKey = nft.contract || "-";
if (!acc[collectionKey]) {
Expand All @@ -85,12 +67,11 @@ const useNftGalleryModel = () => {
acc[collectionKey].push(nft);
return acc;
},
{} as Record<string, typeof nfts>,
{} as Record<string, typeof allNfts>,
);

return {
account,
hiddenNftCollections,
nftsByCollection,
listFooterRef,
collections,
Expand Down
4 changes: 4 additions & 0 deletions apps/ledger-live-desktop/src/renderer/Default.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ import { isLocked as isLockedSelector } from "~/renderer/reducers/application";
import { useAutoDismissPostOnboardingEntryPoint } from "@ledgerhq/live-common/postOnboarding/hooks/index";
import { setShareAnalytics, setSharePersonalizedRecommendations } from "./actions/settings";
import useEnv from "@ledgerhq/live-common/hooks/useEnv";
import { useSyncNFTsWithAccounts } from "./hooks/nfts/useSyncNFTsWithAccounts";

const PlatformCatalog = lazy(() => import("~/renderer/screens/platform"));
const Dashboard = lazy(() => import("~/renderer/screens/dashboard"));
Expand Down Expand Up @@ -202,12 +203,15 @@ export default function Default() {
useRecoverRestoreOnboarding();
useAutoDismissPostOnboardingEntryPoint();

useSyncNFTsWithAccounts();

const analyticsFF = useFeature("lldAnalyticsOptInPrompt");
const hasSeenAnalyticsOptInPrompt = useSelector(hasSeenAnalyticsOptInPromptSelector);
const nftReworked = useFeature("lldNftsGalleryNewArch");
const isLocked = useSelector(isLockedSelector);
const dispatch = useDispatch();
const isNftReworkedEnabled = nftReworked?.enabled;

useEffect(() => {
if (
!isLocked &&
Expand Down
10 changes: 10 additions & 0 deletions apps/ledger-live-desktop/src/renderer/actions/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,12 @@ export const hideNftCollection = (collectionId: string) => ({
type: "HIDE_NFT_COLLECTION",
payload: collectionId,
});

export const whitelistNftCollection = (collectionId: string) => ({
type: "WHITELIST_NFT_COLLECTION",
payload: collectionId,
});

export const hideOrdinalsAsset = (inscriptionId: string) => ({
type: "HIDE_ORDINALS_ASSET",
payload: inscriptionId,
Expand Down Expand Up @@ -252,6 +258,10 @@ export const unhideNftCollection = (collectionId: string) => ({
type: "UNHIDE_NFT_COLLECTION",
payload: collectionId,
});
export const unwhitelistNftCollection = (collectionId: string) => ({
type: "UNWHITELIST_NFT_COLLECTION",
payload: collectionId,
});
export const unhideOrdinalsAsset = (inscriptionId: string) => ({
type: "UNHIDE_ORDINALS_ASSET",
payload: inscriptionId,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import React, { useCallback, useMemo, memo } from "react";
import { useHistory, useParams } from "react-router-dom";
import { useSelector } from "react-redux";
import { nftsByCollections } from "@ledgerhq/live-nft";
import { accountSelector } from "~/renderer/reducers/accounts";
import DropDownSelector, { DropDownItemType } from "~/renderer/components/DropDownSelector";
import Button from "~/renderer/components/Button";
Expand All @@ -14,8 +13,7 @@ import { setTrackingSource } from "~/renderer/analytics/TrackPage";
import CollectionName from "~/renderer/components/Nft/CollectionName";
import { ProtoNFT } from "@ledgerhq/types-live";
import { State } from "~/renderer/reducers";
import { useFeature } from "@ledgerhq/live-common/featureFlags/index";
import { isThresholdValid, useNftGalleryFilter } from "@ledgerhq/live-nft-react";
import { useNftCollections } from "~/renderer/hooks/nfts/useNftCollections";

const LabelWithMeta = ({
item,
Expand All @@ -38,38 +36,29 @@ const LabelWithMeta = ({

const NFTCrumb = () => {
const history = useHistory();
const nftsFromSimplehashFeature = useFeature("nftsFromSimplehash");
const thresold = nftsFromSimplehashFeature?.params?.threshold;
const { id, collectionAddress } = useParams<{ id?: string; collectionAddress?: string }>();
const account = useSelector((state: State) =>
id
? accountSelector(state, {
accountId: id,
})
: null,
: undefined,
);

const { nfts } = useNftGalleryFilter({
nftsOwned: account?.nfts || [],
addresses: String(account?.freshAddress),
chains: [String(account?.currency.id)],
threshold: isThresholdValid(thresold) ? Number(thresold) : 75,
const { collections } = useNftCollections({
account,
});

const collections = useMemo(
() => nftsByCollections(nftsFromSimplehashFeature?.enabled ? nfts : account?.nfts),
[account?.nfts, nfts, nftsFromSimplehashFeature],
);

const items: DropDownItemType<ProtoNFT>[] = useMemo(
() =>
Object.entries(collections).map(([contract, nfts]: [string, ProtoNFT[]]) => ({
collections.map(([contract, nfts]: [string, ProtoNFT[]]) => ({
key: contract,
label: contract,
content: nfts[0],
})),
[collections],
);

const activeItem: DropDownItemType<ProtoNFT> | undefined | null = useMemo(
() => items.find(item => item.key === collectionAddress) || items[0],
[collectionAddress, items],
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { memo } from "react";
import ContextMenuItem from "./ContextMenuItem";
import { Account, ProtoNFT, NFTMetadata } from "@ledgerhq/types-live";
import useNftLinks from "~/renderer/hooks/useNftLinks";
import useNftLinks from "~/renderer/hooks/nfts/useNftLinks";

type Props = {
account: Account;
Expand Down
Loading

0 comments on commit 87218b1

Please sign in to comment.