Skip to content

Commit

Permalink
feat: ✨ Add spamFilteringTx hook in global
Browse files Browse the repository at this point in the history
  • Loading branch information
mcayuelas-ledger committed Sep 25, 2024
1 parent 46a6479 commit fe927b7
Show file tree
Hide file tree
Showing 6 changed files with 124 additions and 7 deletions.
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
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 @@ -55,6 +55,7 @@ import {
import { isLocked as isLockedSelector } from "~/renderer/reducers/application";
import { useAutoDismissPostOnboardingEntryPoint } from "@ledgerhq/live-common/postOnboarding/hooks/index";
import { setShareAnalytics, setSharePersonalizedRecommendations } from "./actions/settings";
import { useSyncNFTsWithAccounts } from "./hooks/useSyncNFTsWithAccounts";

const PlatformCatalog = lazy(() => import("~/renderer/screens/platform"));
const Dashboard = lazy(() => import("~/renderer/screens/dashboard"));
Expand Down Expand Up @@ -200,12 +201,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
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { useEffect, useMemo, useState } from "react";
import { useHideSpamCollection } from "./useHideSpamCollection";
import { isThresholdValid, useCheckNftAccount } from "@ledgerhq/live-nft-react";
import { useFeature } from "@ledgerhq/live-common/featureFlags/index";
import { useSelector } from "react-redux";
import { accountsSelector, orderedVisibleNftsSelector } from "../reducers/accounts";
import isEqual from "lodash/isEqual";

/**
* Represents the size of groups for batching address fetching.
* @constant {number}
*/
const GROUP_SIZE = 20;

/**
* Represents the timer duration for updating address groups.
* 5 hours = 18,000,000 ms.
* @constant {number}
*/
const TIMER = 5 * 60 * 60 * 1000; // 5 hours = 18000000 ms

/**
* A React hook that synchronizes NFT accounts by fetching their data in groups.
* It utilizes address batching and manages updates based on a timer.
*
* @returns {void}
*
* @example
* import { useSyncNFTsWithAccounts } from './path/to/hook';
*
* const MyComponent = () => {
* useSyncNFTsWithAccounts();
* return <div>Syncing NFT Accounts...</div>;
* };
*/

export function useSyncNFTsWithAccounts() {
const nftsFromSimplehashFeature = useFeature("nftsFromSimplehash");
const threshold = isThresholdValid(Number(nftsFromSimplehashFeature?.params?.threshold))
? Number(nftsFromSimplehashFeature?.params?.threshold)
: 75;

const { enabled, hideSpamCollection } = useHideSpamCollection();

const chains = ["ethereum", "polygon"];
const accounts = useSelector(accountsSelector);
const nftsOwned = useSelector(orderedVisibleNftsSelector, isEqual);

const addressGroups = useMemo(() => {
const uniqueAddresses = [
...new Set(
accounts.map(account => account.freshAddress).filter(addr => addr.startsWith("0x")),
),
];

return uniqueAddresses.reduce<string[][]>((acc, _, i, arr) => {
if (i % GROUP_SIZE === 0) {
acc.push(arr.slice(i, i + GROUP_SIZE));
}
return acc;
}, []);
}, [accounts]);

const [groupToFetch, setGroupToFetch] = useState(addressGroups[0]);
const [, setCurrentIndex] = useState(0);

useEffect(() => {
if (!enabled) return;
const interval = setInterval(() => {
setCurrentIndex(prevIndex => {
const nextIndex = (prevIndex + 1) % addressGroups.length;
setGroupToFetch(addressGroups[nextIndex]);

return nextIndex;
});
}, TIMER);

return () => clearInterval(interval);
}, [addressGroups, enabled]);

useCheckNftAccount({
addresses: groupToFetch.join(","),
nftsOwned,
chains,
threshold,
action: hideSpamCollection,
enabled,
});
}
1 change: 1 addition & 0 deletions libs/ledger-live-common/src/market/utils/timers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ export const REFETCH_TIME_ONE_MINUTE = 60 * 1000;
export const BASIC_REFETCH = 3; // nb minutes

export const ONE_DAY = 24 * 60 * 60 * 1000;
export const HALF_DAY = ONE_DAY / 2;
1 change: 1 addition & 0 deletions libs/live-nft-react/src/hooks/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export type HookProps = {
chains: string[];
threshold: number;
action?: (collection: string) => void;
enabled?: boolean;
};

export type PartialProtoNFT = Partial<ProtoNFT>;
Expand Down
29 changes: 22 additions & 7 deletions libs/live-nft-react/src/hooks/useCheckNftAccount.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,32 @@ import { nftsByCollections } from "@ledgerhq/live-nft/index";
import { hashProtoNFT } from "./helpers";

/**
* useCheckNftAccount() will apply a spam filtering on top of existing NFT data.
* - addresses: a list of wallet addresses separated by a ","
* - nftOwned: the array of all nfts as found by all user's account on Ledger Live
* - chains: a list of selected network to search for NFTs
* - action: custom action to handle collections
* NB: for performance, make sure that addresses, nftOwned and chains are memoized
* A React hook that checks NFT accounts against specified criteria and provides filtering functionality for managing NFT collections.
*
* @param {Object} params - The parameters for the hook.
* @param {string} params.addresses - A comma-separated string of NFT addresses to check.
* @param {Array} params.nftsOwned - An array of owned NFTs.
* @param {Array} params.chains - An array representing the blockchain chains.
* @param {number} params.threshold - A numeric threshold for filtering NFTs.
* @param {Function} params.action - A callback function to execute when spam is detected.
* @param {boolean} [params.enabled=false] - A flag to enable or disable the hook's functionality.
*
* @returns {Object} The result of the hook.
* @returns {Array} returns.nfts - An array of filtered NFTs.
* @returns {Object} returns.queryResult - The result of the infinite query, containing pagination and loading states.
*
*/

export const ONE_DAY = 24 * 60 * 60 * 1000;
export const HALF_DAY = ONE_DAY / 2;

export function useCheckNftAccount({
addresses,
nftsOwned,
chains,
threshold,
action,
enabled,
}: HookProps): NftsFilterResult {
// for performance, we hashmap the list of nfts by hash.
const nftsWithProperties = useMemo(
Expand All @@ -36,7 +49,9 @@ export function useCheckNftAccount({
fetchNftsFromSimpleHash({ addresses, chains, cursor: pageParam, threshold }),
initialPageParam: undefined,
getNextPageParam: lastPage => lastPage.next_cursor,
enabled: addresses.length > 0,
enabled: enabled && addresses.length > 0,
refetchInterval: HALF_DAY,
staleTime: HALF_DAY,
});

useEffect(() => {
Expand Down

0 comments on commit fe927b7

Please sign in to comment.