Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
mcayuelas-ledger committed Oct 29, 2024
1 parent a96bcd1 commit 0cf8861
Show file tree
Hide file tree
Showing 18 changed files with 344 additions and 116 deletions.
11 changes: 11 additions & 0 deletions apps/ledger-live-mobile/src/actions/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ import {
SettingsAddStarredMarketcoinsPayload,
SettingsRemoveStarredMarketcoinsPayload,
SettingsSetFromLedgerSyncOnboardingPayload,
SettingsWhitelistNftCollectionPayload,
SettingsUnwhitelistNftCollectionPayload,
} from "./types";
import { ImageType } from "~/components/CustomImage/types";

Expand Down Expand Up @@ -146,6 +148,15 @@ export const hideNftCollection = createAction<SettingsHideNftCollectionPayload>(
export const unhideNftCollection = createAction<SettingsUnhideNftCollectionPayload>(
SettingsActionTypes.UNHIDE_NFT_COLLECTION,
);

export const whitelistNftCollection = createAction<SettingsWhitelistNftCollectionPayload>(
SettingsActionTypes.WHITELIST_NFT_COLLECTION,
);

export const unwhitelistNftCollection = createAction<SettingsUnwhitelistNftCollectionPayload>(
SettingsActionTypes.UNWHITELIST_NFT_COLLECTION,
);

export const dismissBanner = createAction<SettingsDismissBannerPayload>(
SettingsActionTypes.SETTINGS_DISMISS_BANNER,
);
Expand Down
6 changes: 6 additions & 0 deletions apps/ledger-live-mobile/src/actions/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,8 @@ export enum SettingsActionTypes {
BLACKLIST_TOKEN = "BLACKLIST_TOKEN",
HIDE_NFT_COLLECTION = "HIDE_NFT_COLLECTION",
UNHIDE_NFT_COLLECTION = "UNHIDE_NFT_COLLECTION",
UNWHITELIST_NFT_COLLECTION = "UNWHITELIST_NFT_COLLECTION",
WHITELIST_NFT_COLLECTION = "WHITELIST_NFT_COLLECTION",
SETTINGS_DISMISS_BANNER = "SETTINGS_DISMISS_BANNER",
SETTINGS_SET_AVAILABLE_UPDATE = "SETTINGS_SET_AVAILABLE_UPDATE",
DANGEROUSLY_OVERRIDE_STATE = "DANGEROUSLY_OVERRIDE_STATE",
Expand Down Expand Up @@ -316,6 +318,8 @@ export type SettingsShowTokenPayload = string;
export type SettingsBlacklistTokenPayload = string;
export type SettingsHideNftCollectionPayload = string;
export type SettingsUnhideNftCollectionPayload = string;
export type SettingsWhitelistNftCollectionPayload = string;
export type SettingsUnwhitelistNftCollectionPayload = string;
export type SettingsDismissBannerPayload = string;
export type SettingsSetAvailableUpdatePayload = SettingsState["hasAvailableUpdate"];
export type SettingsSetThemePayload = SettingsState["theme"];
Expand Down Expand Up @@ -412,6 +416,8 @@ export type SettingsPayload =
| SettingsBlacklistTokenPayload
| SettingsHideNftCollectionPayload
| SettingsUnhideNftCollectionPayload
| SettingsWhitelistNftCollectionPayload
| SettingsUnwhitelistNftCollectionPayload
| SettingsDismissBannerPayload
| SettingsSetAvailableUpdatePayload
| SettingsSetThemePayload
Expand Down
14 changes: 11 additions & 3 deletions apps/ledger-live-mobile/src/components/Nft/HideNftDrawer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@ import { useDispatch, useSelector } from "react-redux";
import { Account } from "@ledgerhq/types-live";
import { decodeNftId } from "@ledgerhq/coin-framework/nft/nftId";
import { track, TrackScreen } from "~/analytics";
import { hideNftCollection } from "~/actions/settings";
import { hideNftCollection, unwhitelistNftCollection } from "~/actions/settings";
import { accountSelector } from "~/reducers/accounts";
import { State } from "~/reducers/types";
import QueuedDrawer from "../QueuedDrawer";
import { whitelistedNftCollectionsSelector } from "~/reducers/settings";

type Props = {
nftId?: string;
Expand All @@ -24,6 +25,8 @@ const HideNftDrawer = ({ nftId, nftContract, collection, isOpened, onClose }: Pr
const navigation = useNavigation();
const dispatch = useDispatch();

const whitelistedNftCollections = useSelector(whitelistedNftCollectionsSelector);

const { accountId } = decodeNftId(nftId ?? "");
const account = useSelector<State, Account | undefined>(state =>
accountSelector(state, { accountId }),
Expand All @@ -35,10 +38,15 @@ const HideNftDrawer = ({ nftId, nftContract, collection, isOpened, onClose }: Pr
drawer: "Hide NFT Confirmation",
});

dispatch(hideNftCollection(`${account?.id}|${nftContract}`));
const collectionId = `${account?.id}|${nftContract}`;
if (whitelistedNftCollections.includes(collectionId)) {
dispatch(unwhitelistNftCollection(collectionId));
}

dispatch(hideNftCollection(collectionId));
onClose();
navigation.goBack();
}, [account?.id, dispatch, navigation, nftContract, onClose]);
}, [account?.id, dispatch, navigation, nftContract, onClose, whitelistedNftCollections]);

const onPressClose = useCallback(() => {
track("button_clicked", {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import React, { useCallback } from "react";

import { useDispatch } from "react-redux";
import { useDispatch, useSelector } from "react-redux";
import { Text, IconsLegacy, BoxedIcon, Button, Flex } from "@ledgerhq/native-ui";
import { Account, ProtoNFT } from "@ledgerhq/types-live";
import { useTranslation } from "react-i18next";
import { hideNftCollection } from "~/actions/settings";
import { hideNftCollection, unwhitelistNftCollection } from "~/actions/settings";
import QueuedDrawer from "../QueuedDrawer";
import { whitelistedNftCollectionsSelector } from "~/reducers/settings";

type Props = {
isOpen: boolean;
Expand All @@ -18,10 +19,17 @@ const NftCollectionOptionsMenu = ({ isOpen, onClose, collection, account }: Prop
const { t } = useTranslation();
const dispatch = useDispatch();

const whitelistedNftCollections = useSelector(whitelistedNftCollectionsSelector);

const onConfirm = useCallback(() => {
dispatch(hideNftCollection(`${account.id}|${collection?.[0]?.contract}`));
const collectionId = `${account.id}|${collection?.[0]?.contract}`;
if (whitelistedNftCollections.includes(collectionId)) {
dispatch(unwhitelistNftCollection(collectionId));
}

dispatch(hideNftCollection(collectionId));
onClose();
}, [dispatch, account.id, collection, onClose]);
}, [account.id, collection, whitelistedNftCollections, dispatch, onClose]);

return (
<QueuedDrawer isRequestingToBeOpened={isOpen} onClose={onClose}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,14 @@ import { useCallback, useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { useDispatch, useSelector } from "react-redux";
import { BackHandler } from "react-native";
import { hideNftCollection } from "../../../actions/settings";
import { hideNftCollection, unwhitelistNftCollection } from "~/actions/settings";
import { track } from "../../../analytics";
import { NavigatorName, ScreenName } from "~/const";
import { updateMainNavigatorVisibility } from "../../../actions/appstate";
import {
galleryFilterDrawerVisibleSelector,
galleryChainFiltersSelector,
} from "../../../reducers/nft";
import { setGalleryChainFilter, setGalleryFilterDrawerVisible } from "../../../actions/nft";
import { updateMainNavigatorVisibility } from "~/actions/appstate";
import { galleryFilterDrawerVisibleSelector, galleryChainFiltersSelector } from "~/reducers/nft";
import { setGalleryChainFilter, setGalleryFilterDrawerVisible } from "~/actions/nft";
import { NftGalleryChainFiltersState } from "../../../reducers/types";
import { whitelistedNftCollectionsSelector } from "~/reducers/settings";

const TOAST_ID = "SUCCESS_HIDE";

Expand All @@ -30,6 +28,8 @@ export function useNftList({ nftList }: { nftList?: ProtoNFT[] }) {
const isFilterDrawerVisible = useSelector(galleryFilterDrawerVisibleSelector);
const chainFilters = useSelector(galleryChainFiltersSelector);

const whitelistedNftCollections = useSelector(whitelistedNftCollectionsSelector);

const [nftsToHide, setNftsToHide] = useState<ProtoNFT[]>([]);

// Multi Select ------------------------
Expand Down Expand Up @@ -66,7 +66,12 @@ export function useNftList({ nftList }: { nftList?: ProtoNFT[] }) {
exitMultiSelectMode();
nftsToHide.forEach(nft => {
const { accountId } = decodeNftId(nft.id ?? "");
dispatch(hideNftCollection(`${accountId}|${nft.contract}`));
const collectionId = `${accountId}|${nft.contract}`;
if (whitelistedNftCollections.includes(collectionId)) {
dispatch(unwhitelistNftCollection(collectionId));
}

dispatch(hideNftCollection(collectionId));
});

pushToast({
Expand All @@ -77,7 +82,7 @@ export function useNftList({ nftList }: { nftList?: ProtoNFT[] }) {
count: nftsToHide.length,
}),
});
}, [exitMultiSelectMode, dispatch, nftsToHide, pushToast, t]);
}, [exitMultiSelectMode, nftsToHide, pushToast, t, whitelistedNftCollections, dispatch]);

const triggerMultiSelectMode = useCallback(() => {
setNftsToHide([]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ const RefreshableCollapsibleHeaderFlatList = globalSyncRefreshControl<FlatListPr
type Props = {
data: ProtoNFT[];
fetchNextPage: () => void;
hasNextPage: boolean;
isLoading: boolean;
error: unknown;
refetch: () => void;
Expand All @@ -51,7 +50,7 @@ const NB_COLUMNS = 2;

const keyExtractor = (item: ProtoNFT) => item.id;

const NftList = ({ data, hasNextPage, fetchNextPage, isLoading }: Props) => {
const NftList = ({ data, fetchNextPage, isLoading }: Props) => {
const { space, colors } = useTheme();
const dataWithAdd = data.concat(ADD_NEW);

Expand Down Expand Up @@ -172,14 +171,14 @@ const NftList = ({ data, hasNextPage, fetchNextPage, isLoading }: Props) => {
marginBottom: multiSelectModeEnabled ? 0 : space[3],
}}
ListFooterComponent={
!isLoading && hasNextPage ? (
isLoading ? (
<Flex paddingBottom={25} paddingTop={25}>
<InfiniteLoader />
</Flex>
) : null
}
ListEmptyComponent={
isLoading ? (
data.length === 0 && isLoading ? (
<Flex flexGrow={1} justifyContent="center" paddingBottom={150}>
<InfiniteLoader />
</Flex>
Expand Down
25 changes: 25 additions & 0 deletions apps/ledger-live-mobile/src/hooks/nfts/useHideSpamCollection.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { useCallback } from "react";
import { useDispatch, useSelector } from "react-redux";
import { useFeature } from "@ledgerhq/live-common/featureFlags/index";
import { hideNftCollection } from "~/actions/settings";
import { whitelistedNftCollectionsSelector } from "~/reducers/settings";

export function useHideSpamCollection() {
const spamFilteringTxFeature = useFeature("spamFilteringTx");
const whitelistedNftCollections = useSelector(whitelistedNftCollectionsSelector);

const dispatch = useDispatch();
const hideSpamCollection = useCallback(
(collection: string) => {
if (!whitelistedNftCollections.includes(collection)) {
dispatch(hideNftCollection(collection));
}
},
[dispatch, whitelistedNftCollections],
);

return {
hideSpamCollection,
enabled: spamFilteringTxFeature?.enabled,
};
}
76 changes: 76 additions & 0 deletions apps/ledger-live-mobile/src/hooks/nfts/useNftCollections.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { useFeature } from "@ledgerhq/live-common/featureFlags/index";
import { decodeCollectionId, getThreshold, useNftGalleryFilter } from "@ledgerhq/live-nft-react";
import { nftsByCollections } from "@ledgerhq/live-nft/index";
import { BlockchainEVM } from "@ledgerhq/live-nft/supported";
import { Account, ProtoNFT } from "@ledgerhq/types-live";
import { useMemo } from "react";
import { useSelector } from "react-redux";
import {
hiddenNftCollectionsSelector,
whitelistedNftCollectionsSelector,
} from "~/reducers/settings";

export function useNftCollections({
account,
nftsOwned,
addresses,
chains,
}: {
account?: Account;
nftsOwned?: ProtoNFT[];
addresses?: string;
chains?: string[];
}) {
const nftsFromSimplehashFeature = useFeature("nftsFromSimplehash");
const threshold = nftsFromSimplehashFeature?.params?.threshold;
const simplehashEnabled = nftsFromSimplehashFeature?.enabled;

const whitelistNft = useSelector(whitelistedNftCollectionsSelector);
const hiddenNftCollections = useSelector(hiddenNftCollectionsSelector);

const nftsOwnedToCheck = useMemo(() => account?.nfts ?? nftsOwned, [account?.nfts, nftsOwned]);

const whitelistedNfts = useMemo(
() =>
nftsOwnedToCheck?.filter(nft =>
whitelistNft
.map(collection => decodeCollectionId(collection).contractAddress)
.includes(nft.contract),
) ?? [],
[nftsOwnedToCheck, whitelistNft],
);

const { nfts, fetchNextPage, hasNextPage, ...rest } = useNftGalleryFilter({
nftsOwned: account?.nfts ?? nftsOwned ?? [],
addresses: account?.freshAddress ?? addresses ?? "",
chains: account
? [account.currency.id || BlockchainEVM.Ethereum]
: chains ?? [BlockchainEVM.Ethereum],
threshold: getThreshold(threshold),
});

const allNfts = useMemo(
() => (simplehashEnabled ? [...nfts, ...whitelistedNfts] : account?.nfts || nftsOwned || []),
[simplehashEnabled, nfts, whitelistedNfts, account, nftsOwned],
);

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

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

return {
collections,
collectionsLength,
fetchNextPage,
hasNextPage,
nfts,
allNfts,
...rest,
};
}
Loading

0 comments on commit 0cf8861

Please sign in to comment.