From 3c69f284a33ab50a6cd41bf1508d7a5a409c10a8 Mon Sep 17 00:00:00 2001 From: L Date: Tue, 5 Apr 2022 16:50:22 +0200 Subject: [PATCH 01/31] (Market): updated logic to limit refreshes in between pages (#4861) --- package.json | 2 +- .../screens/market/MarketCoinScreen/index.tsx | 13 ++---- src/renderer/screens/market/MarketRowItem.tsx | 6 ++- yarn.lock | 44 +++++++++---------- 4 files changed, 31 insertions(+), 34 deletions(-) diff --git a/package.json b/package.json index fe2afdec15..64b9d46042 100644 --- a/package.json +++ b/package.json @@ -50,7 +50,7 @@ "@ledgerhq/hw-transport-http": "6.26.0", "@ledgerhq/hw-transport-node-hid-singleton": "6.26.0", "@ledgerhq/ledger-core": "6.14.5", - "@ledgerhq/live-common": "21.36.2", + "@ledgerhq/live-common": "git+https://github.com/LedgerHQ/ledger-live-common.git#feat/LL-7742-marketprovider-updated", "@ledgerhq/logs": "6.10.0", "@ledgerhq/react-ui": "^0.7.4", "@open-wc/webpack-import-meta-loader": "^0.4.7", diff --git a/src/renderer/screens/market/MarketCoinScreen/index.tsx b/src/renderer/screens/market/MarketCoinScreen/index.tsx index 50b96d31c2..da149198f3 100644 --- a/src/renderer/screens/market/MarketCoinScreen/index.tsx +++ b/src/renderer/screens/market/MarketCoinScreen/index.tsx @@ -5,10 +5,7 @@ import { useHistory, useParams } from "react-router-dom"; import { useTranslation } from "react-i18next"; import { setTrackingSource } from "~/renderer/analytics/TrackPage"; import { starredMarketCoinsSelector, localeSelector } from "~/renderer/reducers/settings"; -import { - useSingleCoinMarketData, - useMarketData, -} from "@ledgerhq/live-common/lib/market/MarketDataProvider"; +import { useSingleCoinMarketData } from "@ledgerhq/live-common/lib/market/MarketDataProvider"; import styled, { useTheme } from "styled-components"; import CryptoCurrencyIcon from "~/renderer/components/CryptoCurrencyIcon"; import { getCurrencyColor } from "~/renderer/getCurrencyColor"; @@ -56,7 +53,6 @@ const Title = styled(Text).attrs({ variant: "h3" })` `; export default function MarketCoinScreen() { - const { refresh, selectCurrency } = useMarketData(); const { t } = useTranslation(); const history = useHistory(); const { currencyId } = useParams<{ currencyId: string }>(); @@ -85,6 +81,7 @@ export default function MarketCoinScreen() { counterCurrency, setCounterCurrency, supportedCounterCurrencies, + selectCurrency, } = useSingleCoinMarketData(currencyId); const rampCatalog = useRampCatalog(); @@ -126,11 +123,9 @@ export default function MarketCoinScreen() { useEffect(() => { return () => { - // @ts-expect-error can be an input - selectCurrency(undefined); - refresh({}); + selectCurrency(); }; - }, [selectCurrency, refresh]); + }, [selectCurrency]); const color = internalCurrency ? getCurrencyColor(internalCurrency, colors.background.main) diff --git a/src/renderer/screens/market/MarketRowItem.tsx b/src/renderer/screens/market/MarketRowItem.tsx index 79221db53f..d2fa0dc1fc 100644 --- a/src/renderer/screens/market/MarketRowItem.tsx +++ b/src/renderer/screens/market/MarketRowItem.tsx @@ -46,6 +46,7 @@ type Props = { selectCurrency: (currencyId: string) => void; availableOnBuy: boolean; availableOnSwap: boolean; + range?: string; }; function MarketRowItem({ @@ -59,6 +60,7 @@ function MarketRowItem({ selectCurrency, availableOnBuy, availableOnSwap, + range, }: Props) { const { t } = useTranslation(); const dispatch = useDispatch(); @@ -80,13 +82,13 @@ function MarketRowItem({ const flattenedAccounts = flattenAccounts(allAccounts); const onCurrencyClick = useCallback(() => { - selectCurrency(currency.id); + selectCurrency(currency.id, currency, range); setTrackingSource("Page Market"); history.push({ pathname: `/market/${currency.id}`, state: currency, }); - }, [currency, history, selectCurrency]); + }, [currency, history, range, selectCurrency]); const onBuy = useCallback( e => { diff --git a/yarn.lock b/yarn.lock index a615a59a44..598f9c3d64 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2739,17 +2739,17 @@ dependencies: commander "^2.20.0" -"@ledgerhq/cryptoassets@6.27.0": - version "6.27.0" - resolved "https://registry.yarnpkg.com/@ledgerhq/cryptoassets/-/cryptoassets-6.27.0.tgz#1a4efdef07858e8cb1b2dd4c1b9e110f1ed60f3f" - integrity sha512-fM1tm+xJSkbgEB73RtWeTeogcWRkpAcgZX+hBGbvq7anhbRwF2jD4EL897SRNwDuvCKFfpSMxpSUaSvEmj9Lcw== +"@ledgerhq/cryptoassets@6.26.1": + version "6.26.1" + resolved "https://registry.yarnpkg.com/@ledgerhq/cryptoassets/-/cryptoassets-6.26.1.tgz#de810a62199913aeaf36e52b0f826cc0d7289e1e" + integrity sha512-p8DG+E7KO32O0gZc1Q9bBMzKc8c0jMwAO3X7Xn7DV8DvYwMd2WjjrPreBsQdEvm9ku4iz6AUXbLRWI/xwxE5sA== dependencies: invariant "2" -"@ledgerhq/cryptoassets@^6.27.0": - version "6.28.0" - resolved "https://registry.yarnpkg.com/@ledgerhq/cryptoassets/-/cryptoassets-6.28.0.tgz#218a41c5184a176ceb3ec16dc21b37589f673c08" - integrity sha512-j3fBnjsOi2qijWO7p/PNoiEHdzjxP849pO02Q4YWW4Ms4lByv7ysmNLMwrset91We2yyVrdHsjdWY8X5JE97qQ== +"@ledgerhq/cryptoassets@^6.26.1": + version "6.27.0" + resolved "https://registry.yarnpkg.com/@ledgerhq/cryptoassets/-/cryptoassets-6.27.0.tgz#1a4efdef07858e8cb1b2dd4c1b9e110f1ed60f3f" + integrity sha512-fM1tm+xJSkbgEB73RtWeTeogcWRkpAcgZX+hBGbvq7anhbRwF2jD4EL897SRNwDuvCKFfpSMxpSUaSvEmj9Lcw== dependencies: invariant "2" @@ -2835,18 +2835,18 @@ "@ledgerhq/hw-transport" "^6.24.1" bip32-path "^0.4.2" -"@ledgerhq/hw-app-eth@6.27.0": - version "6.27.0" - resolved "https://registry.yarnpkg.com/@ledgerhq/hw-app-eth/-/hw-app-eth-6.27.0.tgz#944f901a95ed3d82759e3fb011859b1b357620a9" - integrity sha512-7uyXu7dCsFmgGWSaXqasxb9Cegrw54HtCeMcZIkq1yqR9ik0ipQIPG1/qW+TqWfS6VYNkorUSsnKc67Cc+0MwA== +"@ledgerhq/hw-app-eth@6.26.1": + version "6.26.1" + resolved "https://registry.yarnpkg.com/@ledgerhq/hw-app-eth/-/hw-app-eth-6.26.1.tgz#c807087a563c4e1fb539116344ce114f5c84286b" + integrity sha512-maA5h+i92uoB+LXhkix1ZZWqI1qAxLBI5vEncuAbJubIQaJVv/BIlR3oAlZ+slNcq+9QUZ8vnzjRksn67kvoYA== dependencies: "@ethersproject/abi" "^5.5.0" "@ethersproject/rlp" "^5.5.0" - "@ledgerhq/cryptoassets" "^6.27.0" + "@ledgerhq/cryptoassets" "^6.26.1" "@ledgerhq/errors" "^6.10.0" "@ledgerhq/hw-transport" "^6.24.1" "@ledgerhq/logs" "^6.10.0" - axios "^0.26.1" + axios "^0.26.0" bignumber.js "^9.0.2" "@ledgerhq/hw-app-eth@~5.11.0": @@ -3027,10 +3027,9 @@ bignumber.js "^9.0.1" json-rpc-2.0 "^0.2.16" -"@ledgerhq/live-common@21.36.2": - version "21.36.2" - resolved "https://registry.yarnpkg.com/@ledgerhq/live-common/-/live-common-21.36.2.tgz#8fc1770827901d9b9a11f6904dbd1718838c3062" - integrity sha512-/YEItWpyQxPW2vkrhmNm7VHV6Id+hpngxadWXkc4q1hzWI5gvGKWYaZDjRPDSAhpk5CdNjobPEl77vlol8+oeg== +"@ledgerhq/live-common@git+https://github.com/LedgerHQ/ledger-live-common.git#feat/LL-7742-marketprovider-updated": + version "21.35.0" + resolved "git+https://github.com/LedgerHQ/ledger-live-common.git#2863240253ee9da2a7c8247d18ccc7459435fa59" dependencies: "@celo/contractkit" "^1.5.2" "@celo/wallet-base" "^1.5.2" @@ -3043,15 +3042,15 @@ "@ethereumjs/common" "^2.6.2" "@ethereumjs/tx" "^3.5.0" "@ledgerhq/compressjs" "1.3.2" - "@ledgerhq/cryptoassets" "6.27.0" + "@ledgerhq/cryptoassets" "6.26.1" "@ledgerhq/devices" "6.24.1" "@ledgerhq/errors" "6.10.0" "@ledgerhq/hw-app-algorand" "6.24.1" "@ledgerhq/hw-app-btc" "6.24.1" "@ledgerhq/hw-app-cosmos" "6.24.1" - "@ledgerhq/hw-app-eth" "6.27.0" + "@ledgerhq/hw-app-eth" "6.26.1" "@ledgerhq/hw-app-polkadot" "6.24.1" - "@ledgerhq/hw-app-solana" "^6.27.0" + "@ledgerhq/hw-app-solana" "^6.26.0" "@ledgerhq/hw-app-str" "6.24.1" "@ledgerhq/hw-app-tezos" "6.24.1" "@ledgerhq/hw-app-trx" "6.24.1" @@ -3075,7 +3074,7 @@ "@zondax/ledger-filecoin" "^0.11.2" algosdk "1.13.0" async "^3.2.3" - axios "0.26.1" + axios "0.26.0" axios-retry "^3.2.4" base32-decode "^1.0.0" bchaddrjs "^0.5.2" @@ -3126,6 +3125,7 @@ triple-beam "^1.3.0" winston "^3.4.0" xstate "^4.28.1" + zcash-bitcore-lib "^0.13.20-rc3" "@ledgerhq/logs@6.10.0", "@ledgerhq/logs@^6.10.0": version "6.10.0" From a05562b13bb96773b37e940dc33b561ad4b98f25 Mon Sep 17 00:00:00 2001 From: "@greweb" Date: Tue, 5 Apr 2022 21:57:36 +0100 Subject: [PATCH 02/31] Sunset Libcore (#4867) * Update latest live-common on develop * update * Sunset libcore * Revert unrelated package bump * Remove experimental JS setting * Target LLC branch sunset-libcore * trigger CI Co-authored-by: haammar-ledger --- package.json | 3 +- src/index.js | 2 +- src/internal/commands/index.js | 4 - src/internal/commands/libcoreGetVersion.js | 18 -- src/internal/commands/libcoreReset.js | 8 - src/internal/implement-libcore.js | 11 -- src/internal/live-common-setup.js | 2 - src/main/internal-lifecycle.js | 1 - src/renderer/Default.js | 2 - src/renderer/bridge/BridgeSyncContext.js | 5 +- .../components/LibcoreBusyIndicator.js | 41 ----- src/renderer/events.js | 5 - src/renderer/experimental.js | 10 -- src/renderer/init.js | 4 - src/renderer/reset.js | 28 --- yarn.lock | 170 +++--------------- 16 files changed, 31 insertions(+), 283 deletions(-) delete mode 100644 src/internal/commands/libcoreGetVersion.js delete mode 100644 src/internal/commands/libcoreReset.js delete mode 100644 src/internal/implement-libcore.js delete mode 100644 src/renderer/components/LibcoreBusyIndicator.js diff --git a/package.json b/package.json index 64b9d46042..3b71c1a885 100644 --- a/package.json +++ b/package.json @@ -49,8 +49,7 @@ "@ledgerhq/hw-transport": "6.24.1", "@ledgerhq/hw-transport-http": "6.26.0", "@ledgerhq/hw-transport-node-hid-singleton": "6.26.0", - "@ledgerhq/ledger-core": "6.14.5", - "@ledgerhq/live-common": "git+https://github.com/LedgerHQ/ledger-live-common.git#feat/LL-7742-marketprovider-updated", + "@ledgerhq/live-common": "https://github.com/LedgerHQ/ledger-live-common.git\\#sunset-libcore", "@ledgerhq/logs": "6.10.0", "@ledgerhq/react-ui": "^0.7.4", "@open-wc/webpack-import-meta-loader": "^0.4.7", diff --git a/src/index.js b/src/index.js index 7c9ea5749c..0ef0aa6f22 100644 --- a/src/index.js +++ b/src/index.js @@ -9,6 +9,6 @@ if (!process.env.IS_INTERNAL_PROCESS) { // Main electron thread require("./main"); } else { - // Internal thread (libcore, hardware) + // Internal thread (coins, hardware) require("./internal"); } diff --git a/src/internal/commands/index.js b/src/internal/commands/index.js index 305fe6889a..87f3424258 100644 --- a/src/internal/commands/index.js +++ b/src/internal/commands/index.js @@ -7,8 +7,6 @@ import flushDevice from "./flushDevice"; import firmwareUpdating from "./firmwareUpdating"; import getLatestFirmwareForDevice from "./getLatestFirmwareForDevice"; import getSatStackStatus from "./getSatStackStatus"; -import libcoreGetVersion from "./libcoreGetVersion"; -import libcoreReset from "./libcoreReset"; import listenDevices from "./listenDevices"; import listApps from "./listApps"; import signMessage from "./signMessage"; @@ -40,8 +38,6 @@ export const commandsById = { firmwareUpdating, getLatestFirmwareForDevice, getSatStackStatus, - libcoreGetVersion, - libcoreReset, listenDevices, connectApp, connectManager, diff --git a/src/internal/commands/libcoreGetVersion.js b/src/internal/commands/libcoreGetVersion.js deleted file mode 100644 index 61cabbc419..0000000000 --- a/src/internal/commands/libcoreGetVersion.js +++ /dev/null @@ -1,18 +0,0 @@ -// @flow - -import type { Observable } from "rxjs"; -import { from } from "rxjs"; -import { withLibcore } from "@ledgerhq/live-common/lib/libcore/access"; - -type Result = { stringVersion: string, intVersion: number }; - -const cmd = (): Observable => - from( - withLibcore(async ledgerCore => { - const stringVersion = await ledgerCore.LedgerCore.getStringVersion(); - const intVersion = await ledgerCore.LedgerCore.getIntVersion(); - return { stringVersion, intVersion }; - }), - ); - -export default cmd; diff --git a/src/internal/commands/libcoreReset.js b/src/internal/commands/libcoreReset.js deleted file mode 100644 index ddd5007128..0000000000 --- a/src/internal/commands/libcoreReset.js +++ /dev/null @@ -1,8 +0,0 @@ -// @flow -import type { Observable } from "rxjs"; -import { from } from "rxjs"; -import { reset } from "@ledgerhq/live-common/lib/libcore/access"; - -const cmd = (): Observable => from(reset()); - -export default cmd; diff --git a/src/internal/implement-libcore.js b/src/internal/implement-libcore.js deleted file mode 100644 index 2922e512b3..0000000000 --- a/src/internal/implement-libcore.js +++ /dev/null @@ -1,11 +0,0 @@ -// @flow -import invariant from "invariant"; -import implementLibcore from "@ledgerhq/live-common/lib/libcore/platforms/nodejs"; - -const dbPath = process.env.LEDGER_LIVE_SQLITE_PATH; -invariant(dbPath, "process.env.LEDGER_LIVE_SQLITE_PATH required"); - -implementLibcore({ - lib: require("@ledgerhq/ledger-core"), - dbPath, -}); diff --git a/src/internal/live-common-setup.js b/src/internal/live-common-setup.js index a13c8e4af7..e682cf44bd 100644 --- a/src/internal/live-common-setup.js +++ b/src/internal/live-common-setup.js @@ -1,7 +1,5 @@ // @flow import "~/live-common-setup"; -import "./implement-libcore"; - import { throwError } from "rxjs"; import usbDetect from "usb-detection"; import throttle from "lodash/throttle"; diff --git a/src/main/internal-lifecycle.js b/src/main/internal-lifecycle.js index a27f9ac92f..b6162a2385 100644 --- a/src/main/internal-lifecycle.js +++ b/src/main/internal-lifecycle.js @@ -123,7 +123,6 @@ function handleGlobalInternalMessage(payload) { // captureException(err) break; } - case "setLibcoreBusy": case "setDeviceBusy": { const win = getMainWindow && getMainWindow(); if (!win) { diff --git a/src/renderer/Default.js b/src/renderer/Default.js index b9d12b138b..eca0bc34df 100644 --- a/src/renderer/Default.js +++ b/src/renderer/Default.js @@ -31,7 +31,6 @@ import OnboardingOrElse from "~/renderer/components/OnboardingOrElse"; import AppRegionDrag from "~/renderer/components/AppRegionDrag"; import IsNewVersion from "~/renderer/components/IsNewVersion"; import IsSystemLanguageAvailable from "~/renderer/components/IsSystemLanguageAvailable"; -import LibcoreBusyIndicator from "~/renderer/components/LibcoreBusyIndicator"; import DeviceBusyIndicator from "~/renderer/components/DeviceBusyIndicator"; import KeyboardContent from "~/renderer/components/KeyboardContent"; import PerfIndicator from "~/renderer/components/PerfIndicator"; @@ -232,7 +231,6 @@ export default function Default() { {__NIGHTLY__ ? : null} - diff --git a/src/renderer/bridge/BridgeSyncContext.js b/src/renderer/bridge/BridgeSyncContext.js index f6595fdeff..862bfb19ca 100644 --- a/src/renderer/bridge/BridgeSyncContext.js +++ b/src/renderer/bridge/BridgeSyncContext.js @@ -8,7 +8,7 @@ import logger from "~/logger"; import { updateAccountWithUpdater } from "~/renderer/actions/accounts"; import { accountsSelector } from "~/renderer/reducers/accounts"; import { recentlyChangedExperimental } from "~/renderer/experimental"; -import { recentlyKilledInternalProcess, onUnusualInternalProcessError } from "~/renderer/reset"; +import { recentlyKilledInternalProcess } from "~/renderer/reset"; import { track } from "~/renderer/analytics/segment"; import { prepareCurrency, hydrateCurrency } from "./cache"; import { hasOngoingSync } from "./proxy"; @@ -45,9 +45,6 @@ export const BridgeSyncProvider = ({ children }: { children: React$Node }) => { // This error is normal because the thread was recently killed. we silent it for the user. return; } - if (isInternalProcessError) { - onUnusualInternalProcessError(); - } logger.critical(error); return error; }, []); diff --git a/src/renderer/components/LibcoreBusyIndicator.js b/src/renderer/components/LibcoreBusyIndicator.js deleted file mode 100644 index ffe8d2d864..0000000000 --- a/src/renderer/components/LibcoreBusyIndicator.js +++ /dev/null @@ -1,41 +0,0 @@ -// @flow -import React, { PureComponent } from "react"; -import styled from "styled-components"; -import type { ThemedComponent } from "~/renderer/styles/StyleProvider"; - -const Indicator: ThemedComponent<*> = styled.div` - opacity: ${p => (p.busy ? 0.1 : 0)}; - width: 6px; - height: 6px; - border-radius: 3px; - background-color: ${p => p.theme.colors.palette.text.shade100}; - position: fixed; - bottom: 4px; - right: 4px; - z-index: 999; -`; - -// NB this is done like this to be extremely performant. we don't want redux for this.. -let busy = false; -const instances = []; -export const onSetLibcoreBusy = (b: boolean) => { - busy = b; - instances.forEach(i => i.forceUpdate()); -}; - -class LibcoreBusyIndicator extends PureComponent<{}> { - componentDidMount() { - instances.push(this); - } - - componentWillUnmount() { - const i = instances.indexOf(this); - instances.splice(i, 1); - } - - render() { - return ; - } -} - -export default LibcoreBusyIndicator; diff --git a/src/renderer/events.js b/src/renderer/events.js index fbf8c037a1..a4b02fa1ed 100644 --- a/src/renderer/events.js +++ b/src/renderer/events.js @@ -4,7 +4,6 @@ import debug from "debug"; import { killInternalProcess } from "./reset"; import { lock } from "./actions/application"; import { onSetDeviceBusy } from "~/renderer/components/DeviceBusyIndicator"; -import { onSetLibcoreBusy } from "~/renderer/components/LibcoreBusyIndicator"; import { hasEncryptionKey } from "~/renderer/storage"; const CHECK_UPDATE_DELAY = 5000; @@ -32,10 +31,6 @@ export default ({ store }: { store: Object }) => { } }); - ipcRenderer.on("setLibcoreBusy", (event: any, { busy }) => { - onSetLibcoreBusy(busy); - }); - ipcRenderer.on("setDeviceBusy", (event: any, { busy }) => { onSetDeviceBusy(busy); }); diff --git a/src/renderer/experimental.js b/src/renderer/experimental.js index 302a0d0ab2..ebc5568c23 100644 --- a/src/renderer/experimental.js +++ b/src/renderer/experimental.js @@ -46,16 +46,6 @@ const deltaExperimentalExplorers = Object.keys(explorerConfig) const experimentalCurrencies = ""; export const experimentalFeatures: Feature[] = [ - { - type: "toggle", - name: "EXPERIMENTAL_CURRENCIES_JS_BRIDGE", - title: , - description: ( - - ), - valueOn: "tezos,cosmos", - valueOff: "", - }, ...(experimentalCurrencies.length ? [ { diff --git a/src/renderer/init.js b/src/renderer/init.js index e03b5bd337..1ece0ff2ef 100644 --- a/src/renderer/init.js +++ b/src/renderer/init.js @@ -25,7 +25,6 @@ import LoggerTransport from "~/logger/logger-transport-renderer"; import { enableGlobalTab, disableGlobalTab, isGlobalTabEnabled } from "~/config/global-tab"; import sentry from "~/sentry/browser"; import { setEnvOnAllThreads } from "~/helpers/env"; -import { command } from "~/renderer/commands"; import dbMiddleware from "~/renderer/middlewares/db"; import createStore from "~/renderer/createStore"; import events from "~/renderer/events"; @@ -142,9 +141,6 @@ async function init() { events({ store }); - const libcoreVersion = await command("libcoreGetVersion")().toPromise(); - logger.log("libcore", libcoreVersion); - window.addEventListener("keydown", (e: SyntheticKeyboardEvent) => { if (e.which === TAB_KEY) { if (!isGlobalTabEnabled()) enableGlobalTab(); diff --git a/src/renderer/reset.js b/src/renderer/reset.js index 3c21a69617..167ab4cdc3 100644 --- a/src/renderer/reset.js +++ b/src/renderer/reset.js @@ -1,9 +1,7 @@ // @flow import { ipcRenderer, shell, remote } from "electron"; -import path from "path"; import { useCallback } from "react"; import { useDispatch } from "react-redux"; -import rimraf from "rimraf"; import { log } from "@ledgerhq/logs"; import { delay } from "@ledgerhq/live-common/lib/promise"; import { useCountervaluesPolling } from "@ledgerhq/live-common/lib/countervalues/react"; @@ -25,29 +23,6 @@ export async function killInternalProcess() { return delay(1000); } -const removeSQLite = () => - new Promise((resolve, reject) => - rimraf(path.resolve(resolveUserDataDirectory(), "sqlite/"), e => { - if (e) reject(e); - else resolve(); - }), - ); - -async function resetLibcore() { - log("clear-cache", "resetLibcore..."); - // we need to stop everything that is happening right now, like syncs - await killInternalProcess(); - log("clear-cache", "killed."); - - await removeSQLite(); - log("clear-cache", "removeSQLite done."); -} - -export function onUnusualInternalProcessError() { - if (recentlyKilledInternalProcess()) return; - resetLibcore(); -} - function reload() { require("@electron/remote") .getCurrentWindow() @@ -61,8 +36,6 @@ export async function hardReset() { disableDBMiddleware(); resetAll(); window.localStorage.clear(); - await delay(500); - await resetLibcore(); } export function useHardReset() { @@ -85,7 +58,6 @@ export function useSoftReset() { log("clear-cache", "cleanCache()"); await cleanCache(); wipe(); - await resetLibcore(); log("clear-cache", "reload()"); reload(); }, [dispatch, wipe]); diff --git a/yarn.lock b/yarn.lock index 598f9c3d64..599bacd7a4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2739,10 +2739,10 @@ dependencies: commander "^2.20.0" -"@ledgerhq/cryptoassets@6.26.1": - version "6.26.1" - resolved "https://registry.yarnpkg.com/@ledgerhq/cryptoassets/-/cryptoassets-6.26.1.tgz#de810a62199913aeaf36e52b0f826cc0d7289e1e" - integrity sha512-p8DG+E7KO32O0gZc1Q9bBMzKc8c0jMwAO3X7Xn7DV8DvYwMd2WjjrPreBsQdEvm9ku4iz6AUXbLRWI/xwxE5sA== +"@ledgerhq/cryptoassets@6.27.0", "@ledgerhq/cryptoassets@^6.27.0": + version "6.27.0" + resolved "https://registry.yarnpkg.com/@ledgerhq/cryptoassets/-/cryptoassets-6.27.0.tgz#1a4efdef07858e8cb1b2dd4c1b9e110f1ed60f3f" + integrity sha512-fM1tm+xJSkbgEB73RtWeTeogcWRkpAcgZX+hBGbvq7anhbRwF2jD4EL897SRNwDuvCKFfpSMxpSUaSvEmj9Lcw== dependencies: invariant "2" @@ -2835,18 +2835,18 @@ "@ledgerhq/hw-transport" "^6.24.1" bip32-path "^0.4.2" -"@ledgerhq/hw-app-eth@6.26.1": - version "6.26.1" - resolved "https://registry.yarnpkg.com/@ledgerhq/hw-app-eth/-/hw-app-eth-6.26.1.tgz#c807087a563c4e1fb539116344ce114f5c84286b" - integrity sha512-maA5h+i92uoB+LXhkix1ZZWqI1qAxLBI5vEncuAbJubIQaJVv/BIlR3oAlZ+slNcq+9QUZ8vnzjRksn67kvoYA== +"@ledgerhq/hw-app-eth@6.27.0": + version "6.27.0" + resolved "https://registry.yarnpkg.com/@ledgerhq/hw-app-eth/-/hw-app-eth-6.27.0.tgz#944f901a95ed3d82759e3fb011859b1b357620a9" + integrity sha512-7uyXu7dCsFmgGWSaXqasxb9Cegrw54HtCeMcZIkq1yqR9ik0ipQIPG1/qW+TqWfS6VYNkorUSsnKc67Cc+0MwA== dependencies: "@ethersproject/abi" "^5.5.0" "@ethersproject/rlp" "^5.5.0" - "@ledgerhq/cryptoassets" "^6.26.1" + "@ledgerhq/cryptoassets" "^6.27.0" "@ledgerhq/errors" "^6.10.0" "@ledgerhq/hw-transport" "^6.24.1" "@ledgerhq/logs" "^6.10.0" - axios "^0.26.0" + axios "^0.26.1" bignumber.js "^9.0.2" "@ledgerhq/hw-app-eth@~5.11.0": @@ -3008,17 +3008,6 @@ dependencies: bignumber.js "^9.0.1" -"@ledgerhq/ledger-core@6.14.5": - version "6.14.5" - resolved "https://registry.yarnpkg.com/@ledgerhq/ledger-core/-/ledger-core-6.14.5.tgz#c2365e42c79ac4e8a60d4a06698da6d62f2fc676" - integrity sha512-SnN3Q97i4njYRmt/lzioBXwEdqjMWCJWYnju/J68QUhiNODxFjCjGZfev2rEUZN3G8VBRmf/BVLXotTxN9lwWg== - dependencies: - "@ledgerhq/node-pre-gyp-github" "^1.0.2" - "@mapbox/node-pre-gyp" "^1.0.5" - bindings "1.5.0" - nan "^2.14.2" - node-gyp "^7.1.2" - "@ledgerhq/live-app-sdk@^0.2.0": version "0.2.0" resolved "https://registry.yarnpkg.com/@ledgerhq/live-app-sdk/-/live-app-sdk-0.2.0.tgz#d50ca7ddd07eeeb564728a1998bf0fb154533315" @@ -3027,9 +3016,9 @@ bignumber.js "^9.0.1" json-rpc-2.0 "^0.2.16" -"@ledgerhq/live-common@git+https://github.com/LedgerHQ/ledger-live-common.git#feat/LL-7742-marketprovider-updated": +"@ledgerhq/live-common@https://github.com/LedgerHQ/ledger-live-common.git\\#sunset-libcore": version "21.35.0" - resolved "git+https://github.com/LedgerHQ/ledger-live-common.git#2863240253ee9da2a7c8247d18ccc7459435fa59" + resolved "https://github.com/LedgerHQ/ledger-live-common.git\\#8425eca243858ef01eea99874541047c0823d5cc" dependencies: "@celo/contractkit" "^1.5.2" "@celo/wallet-base" "^1.5.2" @@ -3042,15 +3031,15 @@ "@ethereumjs/common" "^2.6.2" "@ethereumjs/tx" "^3.5.0" "@ledgerhq/compressjs" "1.3.2" - "@ledgerhq/cryptoassets" "6.26.1" + "@ledgerhq/cryptoassets" "6.27.0" "@ledgerhq/devices" "6.24.1" "@ledgerhq/errors" "6.10.0" "@ledgerhq/hw-app-algorand" "6.24.1" "@ledgerhq/hw-app-btc" "6.24.1" "@ledgerhq/hw-app-cosmos" "6.24.1" - "@ledgerhq/hw-app-eth" "6.26.1" + "@ledgerhq/hw-app-eth" "6.27.0" "@ledgerhq/hw-app-polkadot" "6.24.1" - "@ledgerhq/hw-app-solana" "^6.26.0" + "@ledgerhq/hw-app-solana" "^6.27.0" "@ledgerhq/hw-app-str" "6.24.1" "@ledgerhq/hw-app-tezos" "6.24.1" "@ledgerhq/hw-app-trx" "6.24.1" @@ -3074,7 +3063,7 @@ "@zondax/ledger-filecoin" "^0.11.2" algosdk "1.13.0" async "^3.2.3" - axios "0.26.0" + axios "0.26.1" axios-retry "^3.2.4" base32-decode "^1.0.0" bchaddrjs "^0.5.2" @@ -3125,7 +3114,6 @@ triple-beam "^1.3.0" winston "^3.4.0" xstate "^4.28.1" - zcash-bitcore-lib "^0.13.20-rc3" "@ledgerhq/logs@6.10.0", "@ledgerhq/logs@^6.10.0": version "6.10.0" @@ -3137,15 +3125,6 @@ resolved "https://registry.yarnpkg.com/@ledgerhq/logs/-/logs-5.50.0.tgz#29c6419e8379d496ab6d0426eadf3c4d100cd186" integrity sha512-swKHYCOZUGyVt4ge0u8a7AwNcA//h4nx5wIi0sruGye1IJ5Cva0GyK9L2/WdX+kWVTKp92ZiEo1df31lrWGPgA== -"@ledgerhq/node-pre-gyp-github@^1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@ledgerhq/node-pre-gyp-github/-/node-pre-gyp-github-1.0.2.tgz#6cd9b76f8e28c2f3c6b423c6be2d33b96d47ab3e" - integrity sha512-JxoXlQrNVMMMRKLOD975CZBCj7qPJHZMUQ8vlZka4YTgM0CnbBA3DwI4uv1c0ox3CWPjLqvbNS8DPuYehmUv3w== - dependencies: - "@octokit/rest" "^18.12.0" - commander "^8.2.0" - mime-types "^2.1.33" - "@ledgerhq/react-ui@^0.7.4": version "0.7.4" resolved "https://registry.yarnpkg.com/@ledgerhq/react-ui/-/react-ui-0.7.4.tgz#265cb0a1729dae4cfbb96a55f4ca4b1cae88b9f6" @@ -3195,21 +3174,6 @@ lodash "^4.17.15" tmp-promise "^3.0.2" -"@mapbox/node-pre-gyp@^1.0.5": - version "1.0.6" - resolved "https://registry.yarnpkg.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.6.tgz#f859d601a210537e27530f363028cde56e0cf962" - integrity sha512-qK1ECws8UxuPqOA8F5LFD90vyVU33W7N3hGfgsOVfrJaRVc8McC3JClTDHpeSbL9CBrOHly/4GsNPAvIgNZE+g== - dependencies: - detect-libc "^1.0.3" - https-proxy-agent "^5.0.0" - make-dir "^3.1.0" - node-fetch "^2.6.5" - nopt "^5.0.0" - npmlog "^5.0.1" - rimraf "^3.0.2" - semver "^7.3.5" - tar "^6.1.11" - "@mdx-js/loader@^1.6.22": version "1.6.22" resolved "https://registry.npmjs.org/@mdx-js/loader/-/loader-1.6.22.tgz" @@ -5958,11 +5922,6 @@ abab@^2.0.3: resolved "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz" integrity sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q== -abbrev@1: - version "1.1.1" - resolved "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz" - integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== - accepts@~1.3.5, accepts@~1.3.7: version "1.3.7" resolved "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz" @@ -6293,11 +6252,6 @@ aproba@^1.0.3, aproba@^1.1.1: resolved "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz" integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== -"aproba@^1.0.3 || ^2.0.0": - version "2.0.0" - resolved "https://registry.yarnpkg.com/aproba/-/aproba-2.0.0.tgz#52520b8ae5b569215b354efc0caa3fe1e45a8adc" - integrity sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ== - arch@^2.1.1: version "2.2.0" resolved "https://registry.yarnpkg.com/arch/-/arch-2.2.0.tgz#1bc47818f305764f23ab3306b0bfc086c5a29d11" @@ -6308,14 +6262,6 @@ archy@^1.0.0: resolved "https://registry.yarnpkg.com/archy/-/archy-1.0.0.tgz#f9c8c13757cc1dd7bc379ac77b2c62a5c2868c40" integrity sha1-+cjBN1fMHde8N5rHeyxipcKGjEA= -are-we-there-yet@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz#372e0e7bd279d8e94c653aaa1f67200884bf3e1c" - integrity sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw== - dependencies: - delegates "^1.0.0" - readable-stream "^3.6.0" - are-we-there-yet@~1.1.2: version "1.1.5" resolved "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz" @@ -6633,7 +6579,7 @@ axios@0.25.0: dependencies: follow-redirects "^1.14.7" -axios@0.26.1, axios@^0.26.1: +axios@0.26.1, axios@^0.26.0, axios@^0.26.1: version "0.26.1" resolved "https://registry.yarnpkg.com/axios/-/axios-0.26.1.tgz#1ede41c51fcf51bbbd6fd43669caaa4f0495aaa9" integrity sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA== @@ -6647,13 +6593,6 @@ axios@^0.21.1, axios@^0.21.2: dependencies: follow-redirects "^1.14.0" -axios@^0.26.0: - version "0.26.0" - resolved "https://registry.yarnpkg.com/axios/-/axios-0.26.0.tgz#9a318f1c69ec108f8cd5f3c3d390366635e13928" - integrity sha512-lKoGLMYtHvFrPVt3r+RBMp9nh34N0M8zEfCWqdWZx6phynIEhQqAdydpyBAAG211zlhX9Rgu08cOamy6XjE5Og== - dependencies: - follow-redirects "^1.14.8" - b4a@^1.0.1: version "1.3.1" resolved "https://registry.yarnpkg.com/b4a/-/b4a-1.3.1.tgz#5ead1402bd4a2dcfea35cc83928815d53315ff32" @@ -7210,7 +7149,7 @@ binary@~0.3.0: buffers "~0.1.1" chainsaw "~0.1.0" -bindings@1.5.0, bindings@^1.2.1, bindings@^1.3.0, bindings@^1.5.0: +bindings@^1.2.1, bindings@^1.3.0, bindings@^1.5.0: version "1.5.0" resolved "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz" integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== @@ -8500,11 +8439,6 @@ color-string@^1.5.2, color-string@^1.6.0: color-name "^1.0.0" simple-swizzle "^0.2.2" -color-support@^1.1.2: - version "1.1.3" - resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" - integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== - color@3.0.x: version "3.0.0" resolved "https://registry.npmjs.org/color/-/color-3.0.0.tgz" @@ -8696,7 +8630,7 @@ console-browserify@^1.1.0: resolved "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz" integrity sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA== -console-control-strings@^1.0.0, console-control-strings@^1.1.0, console-control-strings@~1.1.0: +console-control-strings@^1.0.0, console-control-strings@~1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz" integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= @@ -11538,21 +11472,6 @@ fuse.js@^3.6.1: resolved "https://registry.npmjs.org/fuse.js/-/fuse.js-3.6.1.tgz" integrity sha512-hT9yh/tiinkmirKrlv4KWOjztdoZo1mx9Qh4KvWqC7isoXwdUY3PNWUxceF4/qO9R6riA2C29jdTOeQOIROjgw== -gauge@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/gauge/-/gauge-3.0.1.tgz#4bea07bcde3782f06dced8950e51307aa0f4a346" - integrity sha512-6STz6KdQgxO4S/ko+AbjlFGGdGcknluoqU+79GOFCDqqyYj5OanQf9AjxwN0jCidtT+ziPMmPSt9E4hfQ0CwIQ== - dependencies: - aproba "^1.0.3 || ^2.0.0" - color-support "^1.1.2" - console-control-strings "^1.0.0" - has-unicode "^2.0.1" - object-assign "^4.1.1" - signal-exit "^3.0.0" - string-width "^1.0.1 || ^2.0.0" - strip-ansi "^3.0.1 || ^4.0.0" - wide-align "^1.1.2" - gauge@~2.7.3: version "2.7.4" resolved "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz" @@ -11952,7 +11871,7 @@ graceful-fs@4.1.15: resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz" integrity sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA== -graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.4, graceful-fs@^4.1.6, graceful-fs@^4.1.9, graceful-fs@^4.2.0, graceful-fs@^4.2.3, graceful-fs@^4.2.4: +graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.4, graceful-fs@^4.1.6, graceful-fs@^4.1.9, graceful-fs@^4.2.0, graceful-fs@^4.2.4: version "4.2.8" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.8.tgz#e412b8d33f5e006593cbd3cee6df9f2cebbe802a" integrity sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg== @@ -12070,7 +11989,7 @@ has-tostringtag@^1.0.0: dependencies: has-symbols "^1.0.2" -has-unicode@^2.0.0, has-unicode@^2.0.1: +has-unicode@^2.0.0: version "2.0.1" resolved "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz" integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk= @@ -15024,7 +14943,7 @@ mime-types@2.1.18: dependencies: mime-db "~1.33.0" -mime-types@^2.1.12, mime-types@^2.1.16, mime-types@^2.1.27, mime-types@^2.1.33, mime-types@~2.1.19, mime-types@~2.1.24: +mime-types@^2.1.12, mime-types@^2.1.16, mime-types@^2.1.27, mime-types@~2.1.19, mime-types@~2.1.24: version "2.1.34" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.34.tgz#5a712f9ec1503511a945803640fafe09d3793c24" integrity sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A== @@ -15325,7 +15244,7 @@ multihashes@^0.4.15, multihashes@~0.4.15: multibase "^0.7.0" varint "^5.0.0" -nan@^2.12.1, nan@^2.13.2, nan@^2.14.0, nan@^2.14.2, nan@^2.15.0, nan@^2.2.1: +nan@^2.12.1, nan@^2.13.2, nan@^2.14.0, nan@^2.15.0, nan@^2.2.1: version "2.15.0" resolved "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz" integrity sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ== @@ -15451,7 +15370,7 @@ node-environment-flags@1.0.5: object.getownpropertydescriptors "^2.0.3" semver "^5.7.0" -node-fetch@2.6.7, node-fetch@^2.1.2, node-fetch@^2.6.1, node-fetch@^2.6.5: +node-fetch@2.6.7, node-fetch@^2.1.2, node-fetch@^2.6.1: version "2.6.7" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== @@ -15463,22 +15382,6 @@ node-gyp-build@^4.1.0, node-gyp-build@^4.2.0, node-gyp-build@^4.2.2, node-gyp-bu resolved "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.3.0.tgz" integrity sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q== -node-gyp@^7.1.2: - version "7.1.2" - resolved "https://registry.npmjs.org/node-gyp/-/node-gyp-7.1.2.tgz" - integrity sha512-CbpcIo7C3eMu3dL1c3d0xw449fHIGALIJsRP4DDPHpyiW8vcriNY7ubh9TE4zEKfSxscY7PjeFnshE7h75ynjQ== - dependencies: - env-paths "^2.2.0" - glob "^7.1.4" - graceful-fs "^4.2.3" - nopt "^5.0.0" - npmlog "^4.1.2" - request "^2.88.2" - rimraf "^3.0.2" - semver "^7.3.2" - tar "^6.0.2" - which "^2.0.2" - node-hid@2.1.1: version "2.1.1" resolved "https://registry.npmjs.org/node-hid/-/node-hid-2.1.1.tgz" @@ -15573,13 +15476,6 @@ nofilter@^3.1.0: resolved "https://registry.yarnpkg.com/nofilter/-/nofilter-3.1.0.tgz#c757ba68801d41ff930ba2ec55bab52ca184aa66" integrity sha512-l2NNj07e9afPnhAhvgVrCD/oy2Ai1yfLpuo3EpiO1jFTsB4sFz6oIfAfSZyQzVpkZQ9xS8ZS5g1jCBgq4Hwo0g== -nopt@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz" - integrity sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ== - dependencies: - abbrev "1" - normalize-css-color@^1.0.1: version "1.0.2" resolved "https://registry.npmjs.org/normalize-css-color/-/normalize-css-color-1.0.2.tgz" @@ -15658,16 +15554,6 @@ npmlog@^4.0.1, npmlog@^4.1.2: gauge "~2.7.3" set-blocking "~2.0.0" -npmlog@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-5.0.1.tgz#f06678e80e29419ad67ab964e0fa69959c1eb8b0" - integrity sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw== - dependencies: - are-we-there-yet "^2.0.0" - console-control-strings "^1.1.0" - gauge "^3.0.0" - set-blocking "^2.0.0" - nth-check@^2.0.0: version "2.0.1" resolved "https://registry.npmjs.org/nth-check/-/nth-check-2.0.1.tgz" @@ -19316,7 +19202,7 @@ string-width@^1.0.1: is-fullwidth-code-point "^1.0.0" strip-ansi "^3.0.0" -"string-width@^1.0.1 || ^2.0.0", "string-width@^1.0.2 || 2", string-width@^2.0.0, string-width@^2.1.1: +"string-width@^1.0.2 || 2", string-width@^2.0.0, string-width@^2.1.1: version "2.1.1" resolved "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz" integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== @@ -19418,7 +19304,7 @@ strip-ansi@^3.0.0, strip-ansi@^3.0.1: dependencies: ansi-regex "^2.0.0" -"strip-ansi@^3.0.1 || ^4.0.0", strip-ansi@^4.0.0: +strip-ansi@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz" integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= @@ -19728,7 +19614,7 @@ tar@^4.0.2: safe-buffer "^5.2.1" yallist "^3.1.1" -tar@^6.0.2, tar@^6.1.11: +tar@^6.0.2: version "6.1.11" resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.11.tgz#6760a38f003afa1b2ffd0ffe9e9abbd0eab3d621" integrity sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA== @@ -21553,7 +21439,7 @@ wide-align@1.1.3: dependencies: string-width "^1.0.2 || 2" -wide-align@^1.1.0, wide-align@^1.1.2: +wide-align@^1.1.0: version "1.1.5" resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.5.tgz#df1d4c206854369ecf3c9a4898f1b23fbd9d15d3" integrity sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg== From a08a7e5fd7c31978cb9f2ca7d372e26f0708708a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Lambert?= <44363395+lambertkevin@users.noreply.github.com> Date: Tue, 5 Apr 2022 23:15:07 +0200 Subject: [PATCH 03/31] [LIVE-1174] - Feature: Upgrade NFT architecture (#4870) * Add nftMetadataResolver to currencyBridge Proxy * Update DropDowns for NFTs * Update NFT send flow for new NFT model * Update NFT Gallery for new NFT model * Update NFT Collections for new NFT model * Update NFT Viewer for new NFT model * Update operations for new NFT model * Add missing error translation for NFT quantity * Add LLC dependency * Fix NFT send summary showing wrong NFT * Remove all $FlowFixMe for exported memo * Update LLC dep to future v22.0.0 --- package.json | 3 +- src/helpers/nftLinksFactory.js | 74 +++++++++++++++ src/renderer/bridge/proxy.js | 1 + .../components/Breadcrumb/NFTCrumb.js | 36 +++++--- .../components/ContextMenu/NFTContextMenu.js | 57 +++--------- src/renderer/components/DropDownSelector.js | 1 + .../NFTViewerDrawer/ExternalViewerButton.js | 72 +++++---------- src/renderer/drawers/NFTViewerDrawer/index.js | 92 ++++++++++--------- .../OperationDetails/NFTOperationDetails.js | 4 +- .../drawers/OperationDetails/index.js | 17 +++- .../families/ethereum/operationDetails.js | 6 +- src/renderer/modals/Send/Body.js | 4 +- src/renderer/modals/Send/steps/StepAmount.js | 2 +- src/renderer/screens/nft/CollectionName.js | 24 ++--- .../screens/nft/Collections/Collections.js | 38 +++++--- src/renderer/screens/nft/Collections/Row.js | 9 +- .../screens/nft/Gallery/Collection.js | 29 +++--- src/renderer/screens/nft/Gallery/Gallery.js | 39 ++++---- .../screens/nft/Gallery/TokensList/Item.js | 34 ++++--- .../nft/Gallery/TokensList/TokensList.js | 16 +--- src/renderer/screens/nft/Send/Option.js | 15 ++- src/renderer/screens/nft/Send/SelectNFT.js | 6 +- src/renderer/screens/nft/Send/Summary.js | 18 ++-- yarn.lock | 24 ++--- 24 files changed, 335 insertions(+), 286 deletions(-) create mode 100644 src/helpers/nftLinksFactory.js diff --git a/package.json b/package.json index 3b71c1a885..f5854bbbb5 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,8 @@ "@ledgerhq/hw-transport": "6.24.1", "@ledgerhq/hw-transport-http": "6.26.0", "@ledgerhq/hw-transport-node-hid-singleton": "6.26.0", - "@ledgerhq/live-common": "https://github.com/LedgerHQ/ledger-live-common.git\\#sunset-libcore", + "@ledgerhq/ledger-core": "6.14.5", + "@ledgerhq/live-common": "https://github.com/LedgerHQ/ledger-live-common.git#921fef7bfbd31642322c8cbfb0b746dae5d6774e", "@ledgerhq/logs": "6.10.0", "@ledgerhq/react-ui": "^0.7.4", "@open-wc/webpack-import-meta-loader": "^0.4.7", diff --git a/src/helpers/nftLinksFactory.js b/src/helpers/nftLinksFactory.js new file mode 100644 index 0000000000..21a9e41fc8 --- /dev/null +++ b/src/helpers/nftLinksFactory.js @@ -0,0 +1,74 @@ +import IconOpensea from "~/renderer/icons/Opensea"; +import IconRarible from "~/renderer/icons/Rarible"; +import IconGlobe from "~/renderer/icons/Globe"; +import { openURL } from "~/renderer/linking"; + +const linksPerCurrency = { + ethereum: (t, links) => [ + links?.opensea && { + key: "opensea", + id: "opensea", + label: t("NFT.viewer.actions.open", { viewer: "Opensea.io" }), + Icon: IconOpensea, + type: "external", + callback: () => openURL(links.opensea), + }, + links?.rarible && { + key: "rarible", + id: "rarible", + label: t("NFT.viewer.actions.open", { viewer: "Rarible" }), + Icon: IconRarible, + type: "external", + callback: () => openURL(links.rarible), + }, + { + key: "sep2", + id: "sep2", + type: "separator", + label: "", + }, + links?.etherscan && { + key: "etherscan", + id: "etherscan", + label: t("NFT.viewer.actions.open", { viewer: "Explorer" }), + Icon: IconGlobe, + type: "external", + callback: () => openURL(links.etherscan), + }, + ], + polygon: (t, links) => [ + links?.opensea && { + key: "opensea", + id: "opensea", + label: t("NFT.viewer.actions.open", { viewer: "Opensea.io" }), + Icon: IconOpensea, + type: "external", + callback: () => openURL(links.opensea), + }, + links?.rarible && { + key: "rarible", + id: "rarible", + label: t("NFT.viewer.actions.open", { viewer: "Rarible" }), + Icon: IconRarible, + type: "external", + callback: () => openURL(links.rarible), + }, + { + key: "sep2", + id: "sep2", + type: "separator", + label: "", + }, + links?.polygonscan && { + key: "polygonscan", + id: "polygonscan", + label: t("NFT.viewer.actions.open", { viewer: "Explorer" }), + Icon: IconGlobe, + type: "external", + callback: () => openURL(links.polygonscan), + }, + ], +}; + +export default (currencyId, t, links) => + linksPerCurrency?.[currencyId]?.(t, links).filter(x => x) || []; diff --git a/src/renderer/bridge/proxy.js b/src/renderer/bridge/proxy.js index 835e071353..f4dc4793d3 100644 --- a/src/renderer/bridge/proxy.js +++ b/src/renderer/bridge/proxy.js @@ -51,6 +51,7 @@ export const getCurrencyBridge = (currency: CryptoCurrency): CurrencyBridge => { hydrate: value => bridgeImpl.getCurrencyBridge(currency).hydrate(value, currency), scanAccounts, + nftMetadataResolver: bridgeImpl.getCurrencyBridge(currency).nftMetadataResolver, }; if (getPreloadStrategy) { diff --git a/src/renderer/components/Breadcrumb/NFTCrumb.js b/src/renderer/components/Breadcrumb/NFTCrumb.js index 977f49b421..a793e7ddc3 100644 --- a/src/renderer/components/Breadcrumb/NFTCrumb.js +++ b/src/renderer/components/Breadcrumb/NFTCrumb.js @@ -1,5 +1,5 @@ // @flow -import React, { useCallback, useMemo } from "react"; +import React, { useCallback, useMemo, memo } from "react"; import { useHistory, useParams } from "react-router-dom"; import { useSelector } from "react-redux"; import { nftsByCollections } from "@ledgerhq/live-common/lib/nft"; @@ -13,6 +13,7 @@ import IconAngleUp from "~/renderer/icons/AngleUp"; import { Separator, Item, TextLink, AngleDown, Check } from "./common"; import { setTrackingSource } from "~/renderer/analytics/TrackPage"; import CollectionName from "~/renderer/screens/nft/CollectionName"; +import type { ProtoNFT } from "@ledgerhq/live-common/lib/nft"; const LabelWithMeta = ({ item, @@ -21,12 +22,12 @@ const LabelWithMeta = ({ isActive: boolean, item: { label: string, - collection: { nfts: any[], contract: string, standard: string }, + content: ProtoNFT, }, }) => ( - + {isActive && ( @@ -36,23 +37,26 @@ const LabelWithMeta = ({ ); -export default function NFTCrumb() { +const NFTCrumb = () => { const history = useHistory(); const { id, collectionAddress } = useParams(); const account = useSelector(state => accountSelector(state, { accountId: id })); - const collections = nftsByCollections(account.nfts); + const collections = useMemo(() => nftsByCollections(account.nfts), [account.nfts]); const items = useMemo( () => - collections.map(collection => ({ - key: collection.contract, - label: collection.contract, - collection, + Object.entries(collections).map(([contract, nfts]: any) => ({ + key: contract, + label: contract, + content: nfts[0], })), [collections], ); - const activeItem = - items.find((item: any) => item.collection.contract === collectionAddress) || items[0]; + + const activeItem = useMemo( + () => items.find((item: any) => item.nft?.contract === collectionAddress) || items[0], + [collectionAddress, items], + ); const onCollectionSelected = useCallback( item => { @@ -91,12 +95,12 @@ export default function NFTCrumb() { renderItem={LabelWithMeta} onChange={onCollectionSelected} > - {({ isOpen, value }) => ( + {({ isOpen }) => ( @@ -109,4 +113,6 @@ export default function NFTCrumb() { ) : null} ); -} +}; + +export default memo<{}>(NFTCrumb); diff --git a/src/renderer/components/ContextMenu/NFTContextMenu.js b/src/renderer/components/ContextMenu/NFTContextMenu.js index 586471c462..50a52a1c18 100644 --- a/src/renderer/components/ContextMenu/NFTContextMenu.js +++ b/src/renderer/components/ContextMenu/NFTContextMenu.js @@ -1,62 +1,33 @@ // @flow -import React from "react"; +import React, { useMemo, memo } from "react"; import { useTranslation } from "react-i18next"; import { useNftMetadata } from "@ledgerhq/live-common/lib/nft/NftMetadataProvider"; -import IconOpensea from "~/renderer/icons/Opensea"; -import IconRarible from "~/renderer/icons/Rarible"; -import IconGlobe from "~/renderer/icons/Globe"; -import { openURL } from "~/renderer/linking"; import ContextMenuItem from "./ContextMenuItem"; +import nftLinksFactory from "~/helpers/nftLinksFactory"; type Props = { contract: string, tokenId: string, + currencyId: string, leftClick?: boolean, children: any, }; -export default function NFTContextMenu({ leftClick, children, contract, tokenId }: Props) { +const NFTContextMenu = ({ leftClick, children, contract, tokenId, currencyId }: Props) => { const { t } = useTranslation(); - const { metadata } = useNftMetadata(contract, tokenId); - - const defaultLinks = { - openSea: `https://opensea.io/assets/${contract}/${tokenId}`, - rarible: `https://rarible.com/token/${contract}:${tokenId}`, - etherscan: `https://etherscan.io/token/${contract}?a=${tokenId}`, - }; - - const menuItems = [ - { - key: "opensea", - label: t("NFT.viewer.actions.open", { viewer: "Opensea.io" }), - Icon: IconOpensea, - type: "external", - callback: () => openURL(metadata?.links?.opensea || defaultLinks.openSea), - }, - { - key: "rarible", - label: t("NFT.viewer.actions.open", { viewer: "Rarible" }), - Icon: IconRarible, - type: "external", - callback: () => openURL(metadata?.links?.rarible || defaultLinks.rarible), - }, - { - key: "sep2", - type: "separator", - label: "", - }, - { - key: "etherscan", - label: t("NFT.viewer.actions.open", { viewer: "Explorer" }), - Icon: IconGlobe, - type: "external", - callback: () => openURL(metadata?.links?.etherscan || defaultLinks.etherscan), - }, - ]; + const { status, metadata } = useNftMetadata(contract, tokenId, currencyId); + const links = useMemo(() => nftLinksFactory(currencyId, t, metadata?.links), [ + currencyId, + metadata?.links, + t, + ]); + const menuItems = useMemo(() => (status === "loaded" ? links : []), [links, status]); return ( {children} ); -} +}; + +export default memo(NFTContextMenu); diff --git a/src/renderer/components/DropDownSelector.js b/src/renderer/components/DropDownSelector.js index fbde2612ec..a1d7052911 100644 --- a/src/renderer/components/DropDownSelector.js +++ b/src/renderer/components/DropDownSelector.js @@ -52,6 +52,7 @@ export type DropDownItemType = { key: string, label: any, disabled?: boolean, + content?: any, }; const OptionContainer = styled.div` diff --git a/src/renderer/drawers/NFTViewerDrawer/ExternalViewerButton.js b/src/renderer/drawers/NFTViewerDrawer/ExternalViewerButton.js index 569356bd8c..7fb81386f8 100644 --- a/src/renderer/drawers/NFTViewerDrawer/ExternalViewerButton.js +++ b/src/renderer/drawers/NFTViewerDrawer/ExternalViewerButton.js @@ -1,6 +1,6 @@ // @flow -import React from "react"; +import React, { useMemo, memo } from "react"; import { useTranslation } from "react-i18next"; import styled from "styled-components"; @@ -9,16 +9,12 @@ import Button from "~/renderer/components/Button"; import DropDownSelector, { DropDownItem } from "~/renderer/components/DropDownSelector"; import IconDots from "~/renderer/icons/Dots"; import IconExternal from "~/renderer/icons/ExternalLink"; -import IconOpensea from "~/renderer/icons/Opensea"; -import IconRarible from "~/renderer/icons/Rarible"; -import IconGlobe from "~/renderer/icons/Globe"; -import { openURL } from "~/renderer/linking"; +import nftLinksFactory from "~/helpers/nftLinksFactory"; +import type { NFTMetadataResponse } from "@ledgerhq/live-common/lib/types"; import type { DropDownItemType } from "~/renderer/components/DropDownSelector"; import type { ThemedComponent } from "~/renderer/styles/StyleProvider"; -import type { NFTMetadataResponse } from "@ledgerhq/live-common/lib/types"; - const Separator: ThemedComponent<{}> = styled.div` background-color: ${p => p.theme.colors.palette.divider}; height: 1px; @@ -37,68 +33,44 @@ const Item: ThemedComponent<{ display: flex; `; -type ItemType = DropDownItemType & { - icon?: React$Element<*>, - onClick?: Function, - type?: "separator", -}; - type ExternalViewerButtonProps = { links: $PropertyType<$PropertyType, "links">, contract: string, tokenId: string, + currencyId: string, }; -export const ExternalViewerButton = ({ links, contract, tokenId }: ExternalViewerButtonProps) => { +const ExternalViewerButton = ({ + links, + contract, + tokenId, + currencyId, +}: ExternalViewerButtonProps) => { const { t } = useTranslation(); - const defaultLinks = { - openSea: `https://opensea.io/assets/${contract}/${tokenId}`, - rarible: `https://rarible.com/token/${contract}:${tokenId}`, - etherscan: `https://etherscan.io/token/${contract}?a=${tokenId}`, - }; - - const items: DropDownItemType[] = [ - { - key: "opensea", - label: t("NFT.viewer.actions.open", { viewer: "Opensea.io" }), - icon: , - onClick: () => openURL(links?.opensea || defaultLinks.openSea), - }, - { - key: "rarible", - label: t("NFT.viewer.actions.open", { viewer: "Rarible" }), - icon: , - onClick: () => openURL(links?.rarible || defaultLinks.rarible), - }, - { - key: "sep2", - type: "separator", - label: "", - }, - { - key: "etherscan", - label: t("NFT.viewer.actions.open", { viewer: "Explorer" }), - icon: , - onClick: () => openURL(links?.etherscan || defaultLinks.etherscan), - }, - ]; + const items: DropDownItemType[] = useMemo(() => nftLinksFactory(currencyId, t, links), [ + currencyId, + links, + t, + ]); - const renderItem = ({ item }: { item: ItemType }) => { + const renderItem = ({ item }) => { if (item.type === "separator") { return ; } + const Icon = item.Icon ? React.createElement(item.Icon, { size: 16 }) : <>; + return ( - {item.icon ? {item.icon} : null} + {item.Icon ? {Icon} : null} {item.label} @@ -125,3 +97,5 @@ export const ExternalViewerButton = ({ links, contract, tokenId }: ExternalViewe ); }; + +export default memo(ExternalViewerButton); diff --git a/src/renderer/drawers/NFTViewerDrawer/index.js b/src/renderer/drawers/NFTViewerDrawer/index.js index 946133ec43..032d849f05 100644 --- a/src/renderer/drawers/NFTViewerDrawer/index.js +++ b/src/renderer/drawers/NFTViewerDrawer/index.js @@ -1,6 +1,6 @@ // @flow -import React, { useMemo, useCallback, useState } from "react"; +import React, { useMemo, useCallback, useState, memo } from "react"; import Box from "~/renderer/components/Box"; import styled from "styled-components"; import Text from "~/renderer/components/Text"; @@ -16,7 +16,7 @@ import { getNFTById } from "~/renderer/reducers/accounts"; import { NFTProperties } from "./NFTProperties"; import { CopiableField } from "./CopiableField"; import NftPanAndZoom from "./NftPanAndZoom"; -import { ExternalViewerButton } from "./ExternalViewerButton"; +import ExternalViewerButton from "./ExternalViewerButton"; import Skeleton from "~/renderer/screens/nft/Skeleton"; import Image from "~/renderer/screens/nft/Image"; import { useNftMetadata } from "@ledgerhq/live-common/lib/nft/NftMetadataProvider"; @@ -115,42 +115,45 @@ const HashContainer = styled.div` user-select: none; `; -function NFTAttribute({ - title, - value, - skeleton, - separatorBottom, - separatorTop, -}: { - title: string, - value: string, - skeleton?: boolean, - separatorBottom?: boolean, - separatorTop?: boolean, -}) { - if (!skeleton && !value) return null; +const NFTAttribute = memo( + ({ + title, + value, + skeleton, + separatorBottom, + separatorTop, + }: { + title: string, + value: string, + skeleton?: boolean, + separatorBottom?: boolean, + separatorTop?: boolean, + }) => { + if (!skeleton && !value) return null; - return ( - - {separatorTop ? : null} - - {title} - - - -
{value}
+ return ( + <> + {separatorTop ? : null} + + {title} -
- {separatorBottom ? : null} -
- ); -} + + +
{value}
+
+
+ {separatorBottom ? : null} + + ); + }, +); +NFTAttribute.displayName = "NFTAttribute"; type NFTViewerDrawerProps = { account: Account, @@ -160,12 +163,12 @@ type NFTViewerDrawerProps = { onRequestClose: () => void, }; -export function NFTViewerDrawer({ account, nftId, height }: NFTViewerDrawerProps) { +const NFTViewerDrawer = ({ account, nftId, height }: NFTViewerDrawerProps) => { const { t } = useTranslation(); const dispatch = useDispatch(); const nft = useSelector(state => getNFTById(state, { nftId })); - const { status, metadata } = useNftMetadata(nft.collection.contract, nft.tokenId); + const { status, metadata } = useNftMetadata(nft.contract, nft.tokenId, nft.currencyId); const show = useMemo(() => status === "loading", [status]); const name = metadata?.nftName || nft.tokenId; @@ -241,8 +244,9 @@ export function NFTViewerDrawer({ account, nftId, height }: NFTViewerDrawerProps @@ -263,9 +267,9 @@ export function NFTViewerDrawer({ account, nftId, height }: NFTViewerDrawerProps {t("NFT.viewer.attributes.tokenAddress")} - + - + @@ -291,7 +295,7 @@ export function NFTViewerDrawer({ account, nftId, height }: NFTViewerDrawerProps )} - {nft.collection.standard === "ERC1155" ? ( + {nft.standard === "ERC1155" ? (
); -} +}; + +export default memo(NFTViewerDrawer); diff --git a/src/renderer/drawers/OperationDetails/NFTOperationDetails.js b/src/renderer/drawers/OperationDetails/NFTOperationDetails.js index 3482746c00..63e72aad6b 100644 --- a/src/renderer/drawers/OperationDetails/NFTOperationDetails.js +++ b/src/renderer/drawers/OperationDetails/NFTOperationDetails.js @@ -2,6 +2,7 @@ import React, { useMemo } from "react"; import { useTranslation } from "react-i18next"; +import { decodeAccountId } from "@ledgerhq/live-common/lib/account"; import { OpDetailsSection, OpDetailsTitle, @@ -20,7 +21,8 @@ import { centerEllipsis } from "~/renderer/styles/helpers"; const NFTOperationDetails = ({ operation }: { operation: Operation }) => { const { t } = useTranslation(); - const { status, metadata } = useNftMetadata(operation.contract, operation.tokenId); + const { currencyId } = decodeAccountId(operation.accountId); + const { status, metadata } = useNftMetadata(operation.contract, operation.tokenId, currencyId); const show = useMemo(() => status === "loading", [status]); return operation.contract && operation.tokenId ? ( diff --git a/src/renderer/drawers/OperationDetails/index.js b/src/renderer/drawers/OperationDetails/index.js index 18035feb79..2d5069c9d5 100644 --- a/src/renderer/drawers/OperationDetails/index.js +++ b/src/renderer/drawers/OperationDetails/index.js @@ -133,16 +133,27 @@ const OperationD: React$ComponentType = (props: Props) => { const location = useLocation(); const mainAccount = getMainAccount(account, parentAccount); - const { extra, hash, date, senders, type, fee, recipients: _recipients } = operation; + const { + extra, + hash, + date, + senders, + type, + fee, + recipients: _recipients, + contract, + tokenId, + } = operation; const recipients = _recipients.filter(Boolean); const { name } = mainAccount; - const { status, metadata } = useNftMetadata(operation.contract, operation.tokenId); const isNftOperation = ["NFT_IN", "NFT_OUT"].includes(operation.type); - const show = useMemo(() => status === "loading", [status]); const currency = getAccountCurrency(account); const mainCurrency = getAccountCurrency(mainAccount); + const { status, metadata } = useNftMetadata(contract, tokenId, currency.id); + const show = useMemo(() => status === "loading", [status]); + const unit = getAccountUnit(account); const amount = getOperationAmountNumber(operation); diff --git a/src/renderer/families/ethereum/operationDetails.js b/src/renderer/families/ethereum/operationDetails.js index 34aab36b9d..74b4e45682 100644 --- a/src/renderer/families/ethereum/operationDetails.js +++ b/src/renderer/families/ethereum/operationDetails.js @@ -4,7 +4,8 @@ import styled from "styled-components"; import toPairs from "lodash/toPairs"; import { Trans } from "react-i18next"; import type { AccountLike, Operation } from "@ledgerhq/live-common/lib/types"; -import { useNftMetadata } from "@ledgerhq/live-common/lib/nft/NftMetadataProvider"; +import { useNftMetadata } from "@ledgerhq/live-common/lib/nft"; +import { decodeAccountId } from "@ledgerhq/live-common/lib/account"; import { centerEllipsis } from "~/renderer/styles/helpers"; import Box from "~/renderer/components/Box"; import Skeleton from "~/renderer/screens/nft/Skeleton"; @@ -57,7 +58,8 @@ const Cell: ThemedComponent<{}> = styled(Box).attrs(() => ({ `; const NFTAmountField = ({ operation }: Props) => { - const { status, metadata } = useNftMetadata(operation.contract, operation.tokenId); + const { currencyId } = decodeAccountId(operation.accountId); + const { status, metadata } = useNftMetadata(operation.contract, operation.tokenId, currencyId); const show = useMemo(() => status === "loading", [status]); return ( diff --git a/src/renderer/modals/Send/Body.js b/src/renderer/modals/Send/Body.js index 6620a70a03..fd4431e928 100644 --- a/src/renderer/modals/Send/Body.js +++ b/src/renderer/modals/Send/Body.js @@ -201,13 +201,13 @@ const Body = ({ nextNft => { setAccount(account); const bridge = getAccountBridge(account); - const standard = nextNft.collection.standard.toLowerCase(); + const standard = nextNft.standard.toLowerCase(); setTransaction( bridge.updateTransaction(transaction, { tokenIds: [nextNft.tokenId], quantities: [BigNumber(1)], - collection: nextNft.collection.contract, + collection: nextNft.contract, mode: `${standard}.transfer`, }), ); diff --git a/src/renderer/modals/Send/steps/StepAmount.js b/src/renderer/modals/Send/steps/StepAmount.js index 545f6299bf..cf40690130 100644 --- a/src/renderer/modals/Send/steps/StepAmount.js +++ b/src/renderer/modals/Send/steps/StepAmount.js @@ -64,7 +64,7 @@ const StepAmount = ({ /> ) : null} {isNFTSend && nft ? ( - nft.collection.standard === "ERC1155" ? ( + nft.standard === "ERC1155" ? ( { - const { nfts } = collection; - const { status, metadata } = useNftMetadata(collection.contract, nfts[0]?.tokenId); +}; + +// TODO Make me pretty +const CollectionName = ({ nft, fallback }: Props) => { + const { status, metadata } = useNftMetadata(nft.contract, nft.tokenId, nft.currencyId); const { tokenName } = metadata || {}; const loading = status === "loading"; @@ -23,4 +23,4 @@ const CollectionName = ({ ); }; -export default CollectionName; +export default memo(CollectionName); diff --git a/src/renderer/screens/nft/Collections/Collections.js b/src/renderer/screens/nft/Collections/Collections.js index 7f85cba397..4e8ac438c0 100644 --- a/src/renderer/screens/nft/Collections/Collections.js +++ b/src/renderer/screens/nft/Collections/Collections.js @@ -1,6 +1,6 @@ // @flow -import React, { useState, useCallback, useEffect } from "react"; +import React, { useState, useCallback, useEffect, useMemo, memo } from "react"; import { useTranslation } from "react-i18next"; import { useDispatch } from "react-redux"; import Button from "~/renderer/components/Button"; @@ -43,15 +43,30 @@ const Collections = ({ account }: Props) => { [account.id, history], ); - const collections = nftsByCollections(account.nfts); + const collections = useMemo(() => nftsByCollections(account.nfts), [account.nfts]); + const collectionsLength = Object.keys(collections).length; const onShowMore = useCallback(() => { setNumberOfVisibleCollections(numberOfVisibleCollection => - Math.min(numberOfVisibleCollection + INCREMENT, collections.length), + Math.min(numberOfVisibleCollection + INCREMENT, collectionsLength), ); - }, [collections.length]); + }, [collectionsLength]); - const visibleCollection = collections.slice(0, numberOfVisibleCollection); + const visibleCollection = useMemo( + () => + Object.entries(collections) + .slice(0, numberOfVisibleCollection) + .map(([contract, nfts]: any) => ( + onOpenCollection(contract)} + key={contract} + contract={contract} + currencyId={account.currency.id} + nfts={nfts} + /> + )), + [account.currency, collections, numberOfVisibleCollection, onOpenCollection], + ); useEffect(() => { track("View NFT Collections (Account Page)"); @@ -72,20 +87,13 @@ const Collections = ({ account }: Props) => { {account.nfts?.length ? ( - visibleCollection.map(({ contract, nfts }) => ( - onOpenCollection(contract)} - key={contract} - contract={contract} - nfts={nfts} - /> - )) + visibleCollection ) : ( )} - {collections?.length > numberOfVisibleCollection ? ( + {collectionsLength > numberOfVisibleCollection ? ( @@ -102,4 +110,4 @@ const Collections = ({ account }: Props) => { ); }; -export default Collections; +export default memo(Collections); diff --git a/src/renderer/screens/nft/Collections/Row.js b/src/renderer/screens/nft/Collections/Row.js index a09a03a3fc..92a841c22a 100644 --- a/src/renderer/screens/nft/Collections/Row.js +++ b/src/renderer/screens/nft/Collections/Row.js @@ -1,6 +1,6 @@ // @flow -import React, { useMemo } from "react"; +import React, { useMemo, memo } from "react"; import styled from "styled-components"; import Box from "~/renderer/components/Box"; import Text from "~/renderer/components/Text"; @@ -28,11 +28,12 @@ const Container: ThemedComponent<{}> = styled(Box)` type Props = { nfts: NFTWithMetadata[], contract: string, + currencyId: string, onClick: string => void, }; -const Row = ({ nfts, contract, onClick }: Props) => { - const { status, metadata } = useNftMetadata(contract, nfts[0].tokenId); +const Row = ({ nfts, contract, currencyId, onClick }: Props) => { + const { status, metadata } = useNftMetadata(contract, nfts[0].tokenId, currencyId); const { tokenName } = metadata || {}; const show = useMemo(() => status === "loading", [status]); @@ -64,4 +65,4 @@ const Row = ({ nfts, contract, onClick }: Props) => { ); }; -export default Row; +export default memo(Row); diff --git a/src/renderer/screens/nft/Gallery/Collection.js b/src/renderer/screens/nft/Gallery/Collection.js index 6c3f86da5a..c0cdd1664e 100644 --- a/src/renderer/screens/nft/Gallery/Collection.js +++ b/src/renderer/screens/nft/Gallery/Collection.js @@ -1,6 +1,6 @@ // @flow -import React, { useMemo, useCallback, useRef, useState, useEffect } from "react"; +import React, { useMemo, useCallback, useRef, useState, useEffect, memo } from "react"; import { useParams } from "react-router-dom"; import { useSelector, useDispatch } from "react-redux"; import { useTranslation } from "react-i18next"; @@ -49,12 +49,16 @@ const Collection = () => { const { id, collectionAddress } = useParams(); const account = useSelector(state => accountSelector(state, { accountId: id })); - const collection = useMemo(() => nftsByCollections(account.nfts, collectionAddress)[0], [ + const nfts = useMemo(() => nftsByCollections(account.nfts, collectionAddress) || [], [ account.nfts, collectionAddress, ]); - const { status, metadata } = useNftMetadata(collection.contract, collection.nfts[0].tokenId); + const { status, metadata } = useNftMetadata( + nfts[0]?.contract, + nfts[0]?.tokenId, + account.currency.id, + ); const show = useMemo(() => status === "loading", [status]); const onSend = useCallback(() => { @@ -66,22 +70,19 @@ const Collection = () => { // NB To be determined if this filter is good enough for what we expect. const filterOperation = op => !!op.nftOperations?.length && - !!op.nftOperations.find(nftOp => nftOp.contract === collectionAddress); + !!op.nftOperations.find(nftOp => nftOp?.contract === collectionAddress); const ref = useRef(); const isAtBottom = useOnScreen(ref); const [maxVisibleNTFs, setMaxVisibleNFTs] = useState(1); useEffect(() => { - if (isAtBottom && maxVisibleNTFs < collection.nfts.length) { + if (isAtBottom && maxVisibleNTFs < nfts.length) { setMaxVisibleNFTs(maxVisibleNTFs => maxVisibleNTFs + 5); } }, [isAtBottom]); - const slicedNfts = useMemo(() => collection.nfts.slice(0, maxVisibleNTFs), [ - collection.nfts, - maxVisibleNTFs, - ]); + const slicedNfts = useMemo(() => nfts.slice(0, maxVisibleNTFs), [nfts, maxVisibleNTFs]); return ( <> @@ -93,13 +94,13 @@ const Collection = () => { {t("NFT.gallery.collection.header.contract", { - contract: collection.contract, + contract: collectionAddress, })} - + @@ -111,8 +112,8 @@ const Collection = () => { - - {collection.nfts.length > maxVisibleNTFs && ( + + {nfts.length > maxVisibleNTFs && ( @@ -129,4 +130,4 @@ const Collection = () => { ); }; -export default Collection; +export default memo<{}>(Collection); diff --git a/src/renderer/screens/nft/Gallery/Gallery.js b/src/renderer/screens/nft/Gallery/Gallery.js index 1a9ea2c010..0dba9443d2 100644 --- a/src/renderer/screens/nft/Gallery/Gallery.js +++ b/src/renderer/screens/nft/Gallery/Gallery.js @@ -1,11 +1,10 @@ // @flow -import React, { useCallback, useRef, useState, useEffect, useMemo } from "react"; +import React, { useCallback, useRef, useState, useEffect, useMemo, memo } from "react"; import { useParams, useHistory } from "react-router-dom"; import { useSelector, useDispatch } from "react-redux"; import { useTranslation } from "react-i18next"; import { accountSelector } from "~/renderer/reducers/accounts"; -import type { ThemedComponent } from "~/renderer/styles/StyleProvider"; import { openModal } from "~/renderer/actions/modals"; import { nftsByCollections } from "@ledgerhq/live-common/lib/nft"; import styled from "styled-components"; @@ -20,6 +19,9 @@ import Button from "~/renderer/components/Button"; import Text from "~/renderer/components/Text"; import GridListToggle from "./GridListToggle"; +import type { ProtoNFT } from "@ledgerhq/live-common/lib/nft"; +import type { ThemedComponent } from "~/renderer/styles/StyleProvider"; + const SpinnerContainer: ThemedComponent<{}> = styled.div` display: flex; flex: 1; @@ -47,7 +49,10 @@ const Gallery = () => { const account = useSelector(state => accountSelector(state, { accountId: id })); const history = useHistory(); - const collections = nftsByCollections(account.nfts); + const collections: { [key: string]: ProtoNFT[] } = useMemo( + () => nftsByCollections(account.nfts), + [account.nfts], + ); const onSend = useCallback(() => { dispatch(openModal("MODAL_SEND", { account, isNFTSend: true })); @@ -64,12 +69,8 @@ const Gallery = () => { const isAtBottom = useOnScreen(ref); const [maxVisibleNFTs, setMaxVisibleNFTs] = useState(1); - const totalNFTs = useMemo(() => collections.map(c => c.nfts.length).reduce((a, b) => a + b), [ - collections, - ]); - useEffect(() => { - if (isAtBottom && maxVisibleNFTs < totalNFTs) { + if (isAtBottom && maxVisibleNFTs < account.nfts.length) { setMaxVisibleNFTs(maxVisibleNFTs => maxVisibleNFTs + 5); } }, [isAtBottom]); @@ -78,32 +79,28 @@ const Gallery = () => { const collectionsRender = []; let isLoading = false; let displayedNFTs = 0; - collections.forEach(collection => { + Object.entries(collections).forEach(([contract, nfts]: any) => { if (displayedNFTs > maxVisibleNFTs) return; collectionsRender.push( -
- onSelectCollection(collection.contract)}> +
+ onSelectCollection(contract)}> - + - +
, ); - if (displayedNFTs + collection.nfts.length > maxVisibleNFTs) { + if (displayedNFTs + nfts.length > maxVisibleNFTs) { isLoading = true; } - displayedNFTs += collection.nfts.length; + displayedNFTs += nfts.length; }); return [collectionsRender, isLoading]; - }, [collections, maxVisibleNFTs, account]); + }, [collections, maxVisibleNFTs, account, onSelectCollection]); return ( <> @@ -135,4 +132,4 @@ const Gallery = () => { ); }; -export default Gallery; +export default memo<{}>(Gallery); diff --git a/src/renderer/screens/nft/Gallery/TokensList/Item.js b/src/renderer/screens/nft/Gallery/TokensList/Item.js index 5d7848574e..85673dc69e 100644 --- a/src/renderer/screens/nft/Gallery/TokensList/Item.js +++ b/src/renderer/screens/nft/Gallery/TokensList/Item.js @@ -13,9 +13,9 @@ import { centerEllipsis } from "~/renderer/styles/helpers"; import Image from "~/renderer/screens/nft/Image"; import Skeleton from "~/renderer/screens/nft/Skeleton"; import IconDots from "~/renderer/icons/Dots"; -import { useNftMetadata } from "@ledgerhq/live-common/lib/nft/NftMetadataProvider"; +import { useNftMetadata } from "@ledgerhq/live-common/lib/nft"; import NFTContextMenu from "~/renderer/components/ContextMenu/NFTContextMenu"; -import { NFTViewerDrawer } from "~/renderer/drawers/NFTViewerDrawer"; +import NFTViewerDrawer from "~/renderer/drawers/NFTViewerDrawer"; import { setDrawer } from "~/renderer/drawers/Provider"; const Wrapper: ThemedComponent<{}> = styled(Card)` @@ -56,16 +56,14 @@ const TitleContainer: ThemedComponent<{}> = styled(Text)` type Props = { account: Account, - contract: string, - tokenId: string, id: string, mode: "grid" | "list", withContextMenu?: boolean, }; -const NftCard = ({ contract, tokenId, id, mode, account, withContextMenu = false }: Props) => { - const { status, metadata } = useNftMetadata(contract, tokenId); +const NftCard = ({ id, mode, account, withContextMenu = false }: Props) => { const nft = useSelector(state => getNFTById(state, { nftId: id })); + const { status, metadata } = useNftMetadata(nft.contract, nft.tokenId, nft.currencyId); const { nftName } = metadata || {}; const show = useMemo(() => status === "loading", [status]); const isGrid = mode === "grid"; @@ -80,7 +78,12 @@ const NftCard = ({ contract, tokenId, id, mode, account, withContextMenu = false const MaybeContext = ({ children }: any) => withContextMenu ? ( - + {children} ) : ( @@ -115,10 +118,10 @@ const NftCard = ({ contract, tokenId, id, mode, account, withContextMenu = false - {nft.collection.standard === "ERC1155" && isGrid && ( + {nft.standard === "ERC1155" && isGrid && ( {`x${nft.amount.toFixed()}`} @@ -128,12 +131,18 @@ const NftCard = ({ contract, tokenId, id, mode, account, withContextMenu = false
{!isGrid ? ( <> - {nft.collection.standard === "ERC1155" && ( + {nft.standard === "ERC1155" && ( {`x${nft.amount.toFixed()}`} )} - + @@ -145,5 +154,4 @@ const NftCard = ({ contract, tokenId, id, mode, account, withContextMenu = false ); }; -// $FlowFixMe -export default memo(NftCard); +export default memo(NftCard); diff --git a/src/renderer/screens/nft/Gallery/TokensList/TokensList.js b/src/renderer/screens/nft/Gallery/TokensList/TokensList.js index 9e4a68ff91..4883ad9c75 100644 --- a/src/renderer/screens/nft/Gallery/TokensList/TokensList.js +++ b/src/renderer/screens/nft/Gallery/TokensList/TokensList.js @@ -1,6 +1,6 @@ // @flow -import React from "react"; +import React, { memo } from "react"; import styled from "styled-components"; import type { ThemedComponent } from "~/renderer/styles/StyleProvider"; import type { Account, NFT } from "@ledgerhq/live-common/lib/types"; @@ -11,7 +11,6 @@ import Item from "./Item"; type Props = { account: Account, - collectionAddress: string, isLoading?: boolean, nfts: NFT[], }; @@ -22,23 +21,16 @@ const Container: ThemedComponent<{ mode?: "grid" | "list" }> = styled(Box)` grid-template-columns: repeat(auto-fill, minmax(235px, 1fr)); `; -const TokensList = ({ account, isLoading, nfts, collectionAddress }: Props) => { +const TokensList = ({ account, isLoading, nfts }: Props) => { const nftsViewMode = useSelector(nftsViewModeSelector); return ( {nfts.map(nft => ( - + ))} ); }; -export default TokensList; +export default memo(TokensList); diff --git a/src/renderer/screens/nft/Send/Option.js b/src/renderer/screens/nft/Send/Option.js index faef59629c..95dca02793 100644 --- a/src/renderer/screens/nft/Send/Option.js +++ b/src/renderer/screens/nft/Send/Option.js @@ -12,18 +12,14 @@ type OptionProps = { data: { tokenId: string, amount: BigNumber, - collection: { contract: string, standard: string }, + contract: string, + standard: string, + currencyId: string, }, }; -const Option = ({ - data: { - tokenId, - amount, - collection: { contract, standard }, - }, -}: OptionProps) => { - const { status, metadata } = useNftMetadata(contract, tokenId); +const Option = ({ data: { tokenId, amount, contract, standard, currencyId } }: OptionProps) => { + const { status, metadata } = useNftMetadata(contract, tokenId, currencyId); const show = useMemo(() => status === "loading", [status]); return ( @@ -55,4 +51,5 @@ const Option = ({ ); }; +// $FlowFixMe export default Option; diff --git a/src/renderer/screens/nft/Send/SelectNFT.js b/src/renderer/screens/nft/Send/SelectNFT.js index 72f67802ac..6a16eaf589 100644 --- a/src/renderer/screens/nft/Send/SelectNFT.js +++ b/src/renderer/screens/nft/Send/SelectNFT.js @@ -1,6 +1,7 @@ // @flow import React, { useMemo, useEffect, useCallback, useState } from "react"; import type { NFT } from "@ledgerhq/live-common/lib/types"; +import { nftsByCollections } from "@ledgerhq/live-common/lib/nft/helpers"; import Select from "~/renderer/components/Select"; import Option from "./Option"; @@ -19,10 +20,7 @@ const SelectNFT = ({ const getOptionValue = useCallback(item => item, []); const filteredNFTs = useMemo( - () => - maybeNFTCollection - ? nfts.filter(nft => nft.collection.contract === maybeNFTCollection) - : nfts, + () => (maybeNFTCollection ? nftsByCollections(nfts, maybeNFTCollection) : nfts), [nfts, maybeNFTCollection], ); diff --git a/src/renderer/screens/nft/Send/Summary.js b/src/renderer/screens/nft/Send/Summary.js index b07904e772..0774f8997b 100644 --- a/src/renderer/screens/nft/Send/Summary.js +++ b/src/renderer/screens/nft/Send/Summary.js @@ -1,6 +1,6 @@ // @flow -import React, { useMemo } from "react"; +import React, { useMemo, memo } from "react"; import { Trans } from "react-i18next"; import { useSelector } from "react-redux"; import { getAllNFTs } from "~/renderer/reducers/accounts"; @@ -12,10 +12,16 @@ import Skeleton from "~/renderer/screens/nft/Skeleton"; import { useNftMetadata } from "@ledgerhq/live-common/lib/nft/NftMetadataProvider"; import { centerEllipsis } from "~/renderer/styles/helpers"; -const Summary = ({ transaction }: { transaction: Transaction }) => { +type Props = { + transaction: Transaction, +}; + +const Summary = ({ transaction }: Props) => { const allNfts = useSelector(getAllNFTs); - const nft = allNfts.find(nft => nft.tokenId === transaction?.tokenIds[0]); - const { status, metadata } = useNftMetadata(nft.collection.contract, nft.tokenId); + const [tokenId] = transaction.tokenIds; + const [contract] = transaction.collections; + const nft = allNfts.find(nft => nft.tokenId === tokenId && nft.contract === contract); + const { status, metadata } = useNftMetadata(nft.contract, nft.tokenId, nft.currencyId); const { nftName } = metadata || {}; const show = useMemo(() => status === "loading", [status]); @@ -44,7 +50,7 @@ const Summary = ({ transaction }: { transaction: Transaction }) => { - {nft.collection.standard === "ERC1155" ? ( + {nft.standard === "ERC1155" ? ( @@ -60,4 +66,4 @@ const Summary = ({ transaction }: { transaction: Transaction }) => { ); }; -export default Summary; +export default memo(Summary); diff --git a/yarn.lock b/yarn.lock index 599bacd7a4..83a646c341 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2739,17 +2739,10 @@ dependencies: commander "^2.20.0" -"@ledgerhq/cryptoassets@6.27.0", "@ledgerhq/cryptoassets@^6.27.0": - version "6.27.0" - resolved "https://registry.yarnpkg.com/@ledgerhq/cryptoassets/-/cryptoassets-6.27.0.tgz#1a4efdef07858e8cb1b2dd4c1b9e110f1ed60f3f" - integrity sha512-fM1tm+xJSkbgEB73RtWeTeogcWRkpAcgZX+hBGbvq7anhbRwF2jD4EL897SRNwDuvCKFfpSMxpSUaSvEmj9Lcw== - dependencies: - invariant "2" - -"@ledgerhq/cryptoassets@^6.26.1": - version "6.27.0" - resolved "https://registry.yarnpkg.com/@ledgerhq/cryptoassets/-/cryptoassets-6.27.0.tgz#1a4efdef07858e8cb1b2dd4c1b9e110f1ed60f3f" - integrity sha512-fM1tm+xJSkbgEB73RtWeTeogcWRkpAcgZX+hBGbvq7anhbRwF2jD4EL897SRNwDuvCKFfpSMxpSUaSvEmj9Lcw== +"@ledgerhq/cryptoassets@6.28.0", "@ledgerhq/cryptoassets@^6.27.0": + version "6.28.0" + resolved "https://registry.yarnpkg.com/@ledgerhq/cryptoassets/-/cryptoassets-6.28.0.tgz#218a41c5184a176ceb3ec16dc21b37589f673c08" + integrity sha512-j3fBnjsOi2qijWO7p/PNoiEHdzjxP849pO02Q4YWW4Ms4lByv7ysmNLMwrset91We2yyVrdHsjdWY8X5JE97qQ== dependencies: invariant "2" @@ -3016,9 +3009,9 @@ bignumber.js "^9.0.1" json-rpc-2.0 "^0.2.16" -"@ledgerhq/live-common@https://github.com/LedgerHQ/ledger-live-common.git\\#sunset-libcore": - version "21.35.0" - resolved "https://github.com/LedgerHQ/ledger-live-common.git\\#8425eca243858ef01eea99874541047c0823d5cc" +"@ledgerhq/live-common@https://github.com/LedgerHQ/ledger-live-common.git#921fef7bfbd31642322c8cbfb0b746dae5d6774e": + version "22.0.0" + resolved "https://github.com/LedgerHQ/ledger-live-common.git#921fef7bfbd31642322c8cbfb0b746dae5d6774e" dependencies: "@celo/contractkit" "^1.5.2" "@celo/wallet-base" "^1.5.2" @@ -3031,12 +3024,11 @@ "@ethereumjs/common" "^2.6.2" "@ethereumjs/tx" "^3.5.0" "@ledgerhq/compressjs" "1.3.2" - "@ledgerhq/cryptoassets" "6.27.0" + "@ledgerhq/cryptoassets" "6.28.0" "@ledgerhq/devices" "6.24.1" "@ledgerhq/errors" "6.10.0" "@ledgerhq/hw-app-algorand" "6.24.1" "@ledgerhq/hw-app-btc" "6.24.1" - "@ledgerhq/hw-app-cosmos" "6.24.1" "@ledgerhq/hw-app-eth" "6.27.0" "@ledgerhq/hw-app-polkadot" "6.24.1" "@ledgerhq/hw-app-solana" "^6.27.0" From e0fc5ca4d010fc40d17af10866bcbce3809d6f2b Mon Sep 17 00:00:00 2001 From: Gabriel Restori Soares Date: Tue, 5 Apr 2022 23:34:14 +0200 Subject: [PATCH 04/31] LIVE-1569 Add "Hide NFT Collection" feature (#4868) * Update NFT send flow for new NFT model * Add LLC dependency * run ci * LIVE-1569 Add filtering for NFT collections on settings * LIVE-1569 Use currency id and contract address to index hidden collections * LIVE-1569 Add unhide for NFT Collections to the settings page * LIVE-1569 Add hide menu beside NFT Collection name * LIVE-1569 Add ban icon the context menu * LIVE-1569 Change wording for hiding NFT collections instructions * LIVE-1569 Fix linting * LIVE-1569 Rename collectionAddress variable in the redux reducer * Fix missing NFT filter * Remove hidden NFT collections from NFT send select * Update LLC dep to future v22.0.0 Co-authored-by: lambertkevin --- src/renderer/actions/settings.js | 10 ++ .../ContextMenu/NFTCollectionContextMenu.js | 48 ++++++ .../components/ContextMenu/NFTContextMenu.js | 26 ++- .../NFTViewerDrawer/ExternalViewerButton.js | 80 ++++----- src/renderer/drawers/NFTViewerDrawer/index.js | 7 +- .../hooks/useNftLinks.js} | 42 ++++- src/renderer/modals/HideNftCollection/Body.js | 55 +++++++ .../modals/HideNftCollection/Footer.js | 38 +++++ .../modals/HideNftCollection/index.js | 21 +++ .../modals/Send/steps/StepRecipient.js | 2 +- src/renderer/modals/index.js | 2 + src/renderer/reducers/settings.js | 17 ++ src/renderer/screens/nft/CollectionName.js | 42 ++++- .../screens/nft/Collections/Collections.js | 9 +- src/renderer/screens/nft/Collections/Row.js | 56 ++++--- src/renderer/screens/nft/Gallery/Gallery.js | 19 ++- .../screens/nft/Gallery/TokensList/Item.js | 13 +- src/renderer/screens/nft/Send/SelectNFT.js | 18 ++- src/renderer/screens/nft/Send/Summary.js | 2 +- .../sections/Accounts/HiddenNFTCollections.js | 153 ++++++++++++++++++ .../settings/sections/Accounts/index.js | 2 + yarn.lock | 20 ++- 22 files changed, 551 insertions(+), 131 deletions(-) create mode 100644 src/renderer/components/ContextMenu/NFTCollectionContextMenu.js rename src/{helpers/nftLinksFactory.js => renderer/hooks/useNftLinks.js} (59%) create mode 100644 src/renderer/modals/HideNftCollection/Body.js create mode 100644 src/renderer/modals/HideNftCollection/Footer.js create mode 100644 src/renderer/modals/HideNftCollection/index.js create mode 100644 src/renderer/screens/settings/sections/Accounts/HiddenNFTCollections.js diff --git a/src/renderer/actions/settings.js b/src/renderer/actions/settings.js index 11c3740362..227ae258b4 100644 --- a/src/renderer/actions/settings.js +++ b/src/renderer/actions/settings.js @@ -115,6 +115,11 @@ export const blacklistToken = (tokenId: string) => ({ payload: tokenId, }); +export const hideNftCollection = (collectionId: string) => ({ + type: "HIDE_NFT_COLLECTION", + payload: collectionId, +}); + export const swapAcceptProvider = (providerId: string) => ({ type: "ACCEPT_SWAP_PROVIDER", payload: providerId, @@ -125,6 +130,11 @@ export const showToken = (tokenId: string) => ({ payload: tokenId, }); +export const unhideNftCollection = (collectionId: string) => ({ + type: "UNHIDE_NFT_COLLECTION", + payload: collectionId, +}); + type FetchSettings = (*) => (Dispatch<*>) => void; export const fetchSettings: FetchSettings = (settings: *) => dispatch => { dispatch({ diff --git a/src/renderer/components/ContextMenu/NFTCollectionContextMenu.js b/src/renderer/components/ContextMenu/NFTCollectionContextMenu.js new file mode 100644 index 0000000000..8eb594d108 --- /dev/null +++ b/src/renderer/components/ContextMenu/NFTCollectionContextMenu.js @@ -0,0 +1,48 @@ +// @flow +import React from "react"; +import { useDispatch } from "react-redux"; +import { useTranslation } from "react-i18next"; +import IconBan from "~/renderer/icons/Ban"; +import { openModal } from "~/renderer/actions/modals"; +import ContextMenuItem from "./ContextMenuItem"; +import type { Account } from "@ledgerhq/live-common/lib/types"; + +type Props = { + account: Account, + collectionAddress: string, + collectionName?: string, + children: any, + leftClick?: boolean, +}; + +export default function NFTCollectionContextMenu({ + children, + account, + collectionAddress, + collectionName, + leftClick, +}: Props) { + const { t } = useTranslation(); + const dispatch = useDispatch(); + + const menuItems = [ + { + key: "hide", + label: t("hideNftCollection.hideCTA"), + Icon: IconBan, + callback: () => + dispatch( + openModal("MODAL_HIDE_NFT_COLLECTION", { + collectionName: collectionName ?? collectionAddress, + collectionId: `${account.id}|${collectionAddress}`, + }), + ), + }, + ]; + + return ( + + {children} + + ); +} diff --git a/src/renderer/components/ContextMenu/NFTContextMenu.js b/src/renderer/components/ContextMenu/NFTContextMenu.js index 50a52a1c18..b0c112af35 100644 --- a/src/renderer/components/ContextMenu/NFTContextMenu.js +++ b/src/renderer/components/ContextMenu/NFTContextMenu.js @@ -1,30 +1,22 @@ // @flow -import React, { useMemo, memo } from "react"; -import { useTranslation } from "react-i18next"; -import { useNftMetadata } from "@ledgerhq/live-common/lib/nft/NftMetadataProvider"; +import React, { memo } from "react"; import ContextMenuItem from "./ContextMenuItem"; -import nftLinksFactory from "~/helpers/nftLinksFactory"; +import type { Account, ProtoNFT, NFTMetadata } from "@ledgerhq/live-common/lib/types"; +import useNftLinks from "~/renderer/hooks/useNftLinks"; type Props = { - contract: string, - tokenId: string, - currencyId: string, + account: Account, + nft: ProtoNFT, + metadata: NFTMetadata, leftClick?: boolean, children: any, }; -const NFTContextMenu = ({ leftClick, children, contract, tokenId, currencyId }: Props) => { - const { t } = useTranslation(); - const { status, metadata } = useNftMetadata(contract, tokenId, currencyId); - const links = useMemo(() => nftLinksFactory(currencyId, t, metadata?.links), [ - currencyId, - metadata?.links, - t, - ]); - const menuItems = useMemo(() => (status === "loaded" ? links : []), [links, status]); +const NFTContextMenu = ({ leftClick, children, account, nft, metadata }: Props) => { + const links = useNftLinks(account, nft, metadata); return ( - + {children} ); diff --git a/src/renderer/drawers/NFTViewerDrawer/ExternalViewerButton.js b/src/renderer/drawers/NFTViewerDrawer/ExternalViewerButton.js index 7fb81386f8..fffb49d150 100644 --- a/src/renderer/drawers/NFTViewerDrawer/ExternalViewerButton.js +++ b/src/renderer/drawers/NFTViewerDrawer/ExternalViewerButton.js @@ -1,7 +1,6 @@ // @flow -import React, { useMemo, memo } from "react"; -import { useTranslation } from "react-i18next"; +import React, { memo } from "react"; import styled from "styled-components"; import Box from "~/renderer/components/Box"; @@ -9,10 +8,9 @@ import Button from "~/renderer/components/Button"; import DropDownSelector, { DropDownItem } from "~/renderer/components/DropDownSelector"; import IconDots from "~/renderer/icons/Dots"; import IconExternal from "~/renderer/icons/ExternalLink"; -import nftLinksFactory from "~/helpers/nftLinksFactory"; +import useNftLinks from "~/renderer/hooks/useNftLinks"; -import type { NFTMetadataResponse } from "@ledgerhq/live-common/lib/types"; -import type { DropDownItemType } from "~/renderer/components/DropDownSelector"; +import type { Account, ProtoNFT, NFTMetadata } from "@ledgerhq/live-common/lib/types"; import type { ThemedComponent } from "~/renderer/styles/StyleProvider"; const Separator: ThemedComponent<{}> = styled.div` @@ -33,52 +31,42 @@ const Item: ThemedComponent<{ display: flex; `; -type ExternalViewerButtonProps = { - links: $PropertyType<$PropertyType, "links">, - contract: string, - tokenId: string, - currencyId: string, -}; - -const ExternalViewerButton = ({ - links, - contract, - tokenId, - currencyId, -}: ExternalViewerButtonProps) => { - const { t } = useTranslation(); - - const items: DropDownItemType[] = useMemo(() => nftLinksFactory(currencyId, t, links), [ - currencyId, - links, - t, - ]); +const renderItem = ({ item }) => { + if (item.type === "separator") { + return ; + } - const renderItem = ({ item }) => { - if (item.type === "separator") { - return ; - } + const Icon = item.Icon ? React.createElement(item.Icon, { size: 16 }) : <>; - const Icon = item.Icon ? React.createElement(item.Icon, { size: 16 }) : <>; - - return ( - - - {item.Icon ? {Icon} : null} - {item.label} - + return ( + + + {item.Icon ? {Icon} : null} + {item.label} + + {item.type === "external" ? ( - - ); - }; + ) : null} + + ); +}; + +type ExternalViewerButtonProps = { + nft: ProtoNFT, + account: Account, + metadata: NFTMetadata, +}; + +const ExternalViewerButton = ({ nft, account, metadata }: ExternalViewerButtonProps) => { + const items = useNftLinks(account, nft, metadata); return ( { - + diff --git a/src/helpers/nftLinksFactory.js b/src/renderer/hooks/useNftLinks.js similarity index 59% rename from src/helpers/nftLinksFactory.js rename to src/renderer/hooks/useNftLinks.js index 21a9e41fc8..cba388ce73 100644 --- a/src/helpers/nftLinksFactory.js +++ b/src/renderer/hooks/useNftLinks.js @@ -1,7 +1,14 @@ +// @flow +import { useMemo } from "react"; +import { useDispatch } from "react-redux"; +import { useTranslation } from "react-i18next"; +import type { Account, ProtoNFT, NFTMetadata } from "@ledgerhq/live-common/lib/types"; +import { openModal } from "~/renderer/actions/modals"; import IconOpensea from "~/renderer/icons/Opensea"; import IconRarible from "~/renderer/icons/Rarible"; import IconGlobe from "~/renderer/icons/Globe"; import { openURL } from "~/renderer/linking"; +import IconBan from "~/renderer/icons/Ban"; const linksPerCurrency = { ethereum: (t, links) => [ @@ -36,7 +43,7 @@ const linksPerCurrency = { callback: () => openURL(links.etherscan), }, ], - polygon: (t, links) => [ + polygon: (t, links, dispatch) => [ links?.opensea && { key: "opensea", id: "opensea", @@ -70,5 +77,34 @@ const linksPerCurrency = { ], }; -export default (currencyId, t, links) => - linksPerCurrency?.[currencyId]?.(t, links).filter(x => x) || []; +export default (account: Account, nft: ProtoNFT, metadata: NFTMetadata) => { + const dispatch = useDispatch(); + const { t } = useTranslation(); + + const hideCollection = useMemo( + () => ({ + key: "hide-collection", + id: "hide-collection", + label: t("hideNftCollection.hideCTA"), + Icon: IconBan, + type: null, + callback: () => { + return dispatch( + openModal("MODAL_HIDE_NFT_COLLECTION", { + collectionName: metadata?.tokenName ?? nft.contract, + collectionId: `${account.id}|${nft.contract}`, + }), + ); + }, + }), + [account, dispatch, metadata?.tokenName, nft, t], + ); + const links = useMemo(() => { + const metadataLinks = + linksPerCurrency?.[account.currency.id]?.(t, metadata?.links).filter(x => x) || []; + + return [...metadataLinks, hideCollection]; + }, [account.currency.id, hideCollection, metadata?.links, t]); + + return links; +}; diff --git a/src/renderer/modals/HideNftCollection/Body.js b/src/renderer/modals/HideNftCollection/Body.js new file mode 100644 index 0000000000..5222612217 --- /dev/null +++ b/src/renderer/modals/HideNftCollection/Body.js @@ -0,0 +1,55 @@ +// @flow + +import Box from "~/renderer/components/Box"; +import { Trans, useTranslation } from "react-i18next"; +import Text from "~/renderer/components/Text"; +import ModalBody from "~/renderer/components/Modal/ModalBody"; +import React from "react"; +import Footer from "~/renderer/modals/HideNftCollection/Footer"; + +const Body = ({ + onClose, + collectionId, + collectionName, +}: { + onClose: () => void, + collectionId: string, + collectionName: string, +}) => { + const { t } = useTranslation(); + + return ( + ( + + + + {"This action will hide all NFTs from the "} + {/* $FlowFixMe */} + + {{ collectionName }} + + {" collection, you can show them again using "} + + {"Settings"} + + + + + )} + renderFooter={() =>