From ca4274009ae050b30695b7b505241e4530ecbd55 Mon Sep 17 00:00:00 2001 From: Ramy EL BEHEDY <32883529+RamyEB@users.noreply.github.com> Date: Fri, 10 May 2024 14:49:38 +0200 Subject: [PATCH] feat: Save locally added manifest into the storage on both Mobile and Desktop client (#6719) feat: Save locally added manifest into the storage on both Mobile and Desktop client --- .changeset/hot-zebras-ring.md | 7 + .../components/PlatformAppProviderWrapper.tsx | 19 +- .../StakeFlowModal/component/ProviderItem.tsx | 2 +- .../modals/CreateLocalManifest/index.tsx | 2 +- .../src/renderer/screens/earn/index.tsx | 2 +- .../screens/exchange/Swap2/Form/index.tsx | 2 +- .../src/renderer/screens/exchange/index.tsx | 2 +- .../src/renderer/screens/platform/LiveApp.tsx | 2 +- .../platform/v2/Catalog/Card/Minimum.tsx | 13 +- .../v2/Catalog/LocalLiveAppSection.tsx | 37 +++ .../screens/platform/v2/Catalog/index.tsx | 10 +- .../src/renderer/screens/platform/v2/hooks.ts | 10 +- .../src/renderer/screens/recover/Player.tsx | 2 +- .../sections/Developer/RunLocalAppButton.tsx | 10 +- .../src/renderer/screens/swapWeb/index.tsx | 2 +- .../static/i18n/en/app.json | 1 + .../src/PlatformAppProviderWrapper.tsx | 19 +- .../EvmStakingDrawerProvider.tsx | 8 +- .../src/families/tron/Votes/index.tsx | 8 +- .../src/locales/en/common.json | 1 + .../src/screens/PTX/BuyAndSell/index.tsx | 2 +- .../src/screens/PTX/Earn/index.tsx | 2 +- .../src/screens/Platform/Catalog/AppCard.tsx | 2 + .../src/screens/Platform/LiveApp.tsx | 2 +- .../Platform/v2/Catalog/LocalLiveApp.tsx | 52 +++++ .../src/screens/Platform/v2/Catalog/index.tsx | 9 +- .../src/screens/Platform/v2/hooks.ts | 12 +- .../src/screens/Protect/Player.tsx | 2 +- .../Settings/Developer/CustomManifest.tsx | 212 ++++++++++-------- libs/ledger-live-common/.unimportedrc.json | 4 +- .../src/platform/hooks/useManifest.test.ts | 70 ------ .../src/platform/hooks/useManifest.ts | 15 -- .../providers/LocalLiveAppProvider/index.tsx | 83 ------- .../providers/LocalLiveAppProvider/types.ts | 10 - .../providers/RemoteLiveAppProvider/types.ts | 4 - .../wallet-api/LocalLiveAppProvider/index.tsx | 40 ++++ .../wallet-api/LocalLiveAppProvider/types.ts | 14 ++ .../src/wallet-api/constants.ts | 6 +- .../src/wallet-api/react.ts | 52 +++++ .../src/wallet-api/types.ts | 1 + 40 files changed, 425 insertions(+), 328 deletions(-) create mode 100644 .changeset/hot-zebras-ring.md create mode 100644 apps/ledger-live-desktop/src/renderer/screens/platform/v2/Catalog/LocalLiveAppSection.tsx create mode 100644 apps/ledger-live-mobile/src/screens/Platform/v2/Catalog/LocalLiveApp.tsx delete mode 100644 libs/ledger-live-common/src/platform/hooks/useManifest.test.ts delete mode 100644 libs/ledger-live-common/src/platform/hooks/useManifest.ts delete mode 100644 libs/ledger-live-common/src/platform/providers/LocalLiveAppProvider/index.tsx delete mode 100644 libs/ledger-live-common/src/platform/providers/LocalLiveAppProvider/types.ts create mode 100644 libs/ledger-live-common/src/wallet-api/LocalLiveAppProvider/index.tsx create mode 100644 libs/ledger-live-common/src/wallet-api/LocalLiveAppProvider/types.ts diff --git a/.changeset/hot-zebras-ring.md b/.changeset/hot-zebras-ring.md new file mode 100644 index 000000000000..ab6a0d6c7479 --- /dev/null +++ b/.changeset/hot-zebras-ring.md @@ -0,0 +1,7 @@ +--- +"ledger-live-desktop": patch +"live-mobile": patch +"@ledgerhq/live-common": patch +--- + +Save locally added manifest into the storage on both Mobile and Desktop client diff --git a/apps/ledger-live-desktop/src/renderer/components/PlatformAppProviderWrapper.tsx b/apps/ledger-live-desktop/src/renderer/components/PlatformAppProviderWrapper.tsx index 6bf9f3f63648..589e295657fa 100644 --- a/apps/ledger-live-desktop/src/renderer/components/PlatformAppProviderWrapper.tsx +++ b/apps/ledger-live-desktop/src/renderer/components/PlatformAppProviderWrapper.tsx @@ -7,8 +7,13 @@ import { } from "~/renderer/reducers/settings"; import { useSelector } from "react-redux"; import { RemoteLiveAppProvider } from "@ledgerhq/live-common/platform/providers/RemoteLiveAppProvider/index"; -import { LocalLiveAppProvider } from "@ledgerhq/live-common/platform/providers/LocalLiveAppProvider/index"; +import { LocalLiveAppProvider } from "@ledgerhq/live-common/wallet-api/LocalLiveAppProvider/index"; import { RampCatalogProvider } from "@ledgerhq/live-common/platform/providers/RampCatalogProvider/index"; +import { useDB } from "../storage"; +import { + DISCOVER_STORE_KEY, + INITIAL_PLATFORM_STATE, +} from "@ledgerhq/live-common/wallet-api/constants"; type PlatformAppProviderWrapperProps = { children: ReactNode; @@ -21,6 +26,7 @@ export function PlatformAppProviderWrapper({ children }: PlatformAppProviderWrap const allowExperimentalApps = useSelector(allowExperimentalAppsSelector); const provider = useSelector(catalogProviderSelector); const locale = useSelector(languageSelector); + const localLiveAppDB = useLocalLiveAppDB(); return ( - + {children} @@ -41,3 +47,12 @@ export function PlatformAppProviderWrapper({ children }: PlatformAppProviderWrap ); } + +function useLocalLiveAppDB() { + return useDB( + "app", + DISCOVER_STORE_KEY, + INITIAL_PLATFORM_STATE, + state => state.localLiveApp || INITIAL_PLATFORM_STATE.localLiveApp, + ); +} diff --git a/apps/ledger-live-desktop/src/renderer/families/evm/StakeFlowModal/component/ProviderItem.tsx b/apps/ledger-live-desktop/src/renderer/families/evm/StakeFlowModal/component/ProviderItem.tsx index c3e80d1a2173..edc723a8548f 100644 --- a/apps/ledger-live-desktop/src/renderer/families/evm/StakeFlowModal/component/ProviderItem.tsx +++ b/apps/ledger-live-desktop/src/renderer/families/evm/StakeFlowModal/component/ProviderItem.tsx @@ -1,4 +1,3 @@ -import { useLocalLiveAppManifest } from "@ledgerhq/live-common/platform/providers/LocalLiveAppProvider/index"; import { useRemoteLiveAppManifest } from "@ledgerhq/live-common/platform/providers/RemoteLiveAppProvider/index"; import { Flex, Icon, Tag as TagCore, Text } from "@ledgerhq/react-ui"; import React, { useCallback, useEffect, useMemo } from "react"; @@ -7,6 +6,7 @@ import styled, { DefaultTheme, StyledComponent } from "styled-components"; import { StakeOnClickProps } from "../EthStakingModalBody"; import { StakingIcon } from "../StakingIcon"; import { ListProvider } from "../types"; +import { useLocalLiveAppManifest } from "@ledgerhq/live-common/wallet-api/LocalLiveAppProvider/index"; export const Container: StyledComponent< "div", diff --git a/apps/ledger-live-desktop/src/renderer/modals/CreateLocalManifest/index.tsx b/apps/ledger-live-desktop/src/renderer/modals/CreateLocalManifest/index.tsx index bf34f37bfb4f..ce7881299040 100644 --- a/apps/ledger-live-desktop/src/renderer/modals/CreateLocalManifest/index.tsx +++ b/apps/ledger-live-desktop/src/renderer/modals/CreateLocalManifest/index.tsx @@ -15,7 +15,6 @@ import { LiveAppManifestSchemaType, } from "@ledgerhq/live-common/platform/types"; import Text from "~/renderer/components/Text"; -import { useLocalLiveAppContext } from "@ledgerhq/live-common/platform/providers/LocalLiveAppProvider/index"; import Switch from "~/renderer/components/Switch"; import FormLiveAppInput from "./FormLiveAppInput"; import NestedFormCategory from "./NestedFormCategory"; @@ -32,6 +31,7 @@ import { Separator } from "~/renderer/components/Onboarding/Screens/SelectUseCas import { DEFAULT_FORM, DEFAULT_VALUES } from "./defaultValues"; import { objectKeysType } from "@ledgerhq/live-common/helpers"; +import { useLocalLiveAppContext } from "@ledgerhq/live-common/wallet-api/LocalLiveAppProvider/index"; function createLocalManifest() { return ( diff --git a/apps/ledger-live-desktop/src/renderer/screens/earn/index.tsx b/apps/ledger-live-desktop/src/renderer/screens/earn/index.tsx index fde361c55229..a9156306ac21 100644 --- a/apps/ledger-live-desktop/src/renderer/screens/earn/index.tsx +++ b/apps/ledger-live-desktop/src/renderer/screens/earn/index.tsx @@ -9,9 +9,9 @@ import { import { useRemoteLiveAppManifest } from "@ledgerhq/live-common/platform/providers/RemoteLiveAppProvider/index"; import WebPlatformPlayer from "~/renderer/components/WebPlatformPlayer"; import useTheme from "~/renderer/hooks/useTheme"; -import { useLocalLiveAppManifest } from "@ledgerhq/live-common/platform/providers/LocalLiveAppProvider/index"; import { useDeepLinkListener } from "~/renderer/screens/earn/useDeepLinkListener"; import { useDiscreetMode } from "~/renderer/components/Discreet"; +import { useLocalLiveAppManifest } from "@ledgerhq/live-common/wallet-api/LocalLiveAppProvider/index"; const DEFAULT_EARN_APP_ID = "earn"; diff --git a/apps/ledger-live-desktop/src/renderer/screens/exchange/Swap2/Form/index.tsx b/apps/ledger-live-desktop/src/renderer/screens/exchange/Swap2/Form/index.tsx index f7e7370938c4..f8e011595edc 100644 --- a/apps/ledger-live-desktop/src/renderer/screens/exchange/Swap2/Form/index.tsx +++ b/apps/ledger-live-desktop/src/renderer/screens/exchange/Swap2/Form/index.tsx @@ -35,9 +35,9 @@ import SwapWebView, { SwapWebProps, useSwapLiveAppManifestID } from "./SwapWebVi import { SwapMigrationUI } from "./Migrations/SwapMigrationUI"; import { useSwapLiveAppHook } from "~/renderer/hooks/swap-migrations/useSwapLiveAppHook"; import SwapFormSummary from "./FormSummary"; -import { useLocalLiveAppManifest } from "@ledgerhq/live-common/platform/providers/LocalLiveAppProvider/index"; import { useRemoteLiveAppManifest } from "@ledgerhq/live-common/platform/providers/RemoteLiveAppProvider/index"; import { languageSelector } from "~/renderer/reducers/settings"; +import { useLocalLiveAppManifest } from "@ledgerhq/live-common/wallet-api/LocalLiveAppProvider/index"; import { walletSelector } from "~/renderer/reducers/wallet"; const DAPP_PROVIDERS = ["paraswap", "oneinch", "moonpay"]; diff --git a/apps/ledger-live-desktop/src/renderer/screens/exchange/index.tsx b/apps/ledger-live-desktop/src/renderer/screens/exchange/index.tsx index 82b9b568c588..6191f55558de 100644 --- a/apps/ledger-live-desktop/src/renderer/screens/exchange/index.tsx +++ b/apps/ledger-live-desktop/src/renderer/screens/exchange/index.tsx @@ -8,7 +8,6 @@ import { languageSelector } from "~/renderer/reducers/settings"; import { accountsSelector } from "~/renderer/reducers/accounts"; import { useRemoteLiveAppManifest } from "@ledgerhq/live-common/platform/providers/RemoteLiveAppProvider/index"; import useTheme from "~/renderer/hooks/useTheme"; -import { useLocalLiveAppManifest } from "@ledgerhq/live-common/platform/providers/LocalLiveAppProvider/index"; import WebPTXPlayer from "~/renderer/components/WebPTXPlayer"; import { getParentAccount, isTokenAccount } from "@ledgerhq/live-common/account/index"; import { LiveAppManifest, Loadable } from "@ledgerhq/live-common/platform/types"; @@ -20,6 +19,7 @@ import { } from "@ledgerhq/live-common/wallet-api/constants"; import { useInternalAppIds } from "@ledgerhq/live-common/hooks/useInternalAppIds"; import { useFeature } from "@ledgerhq/live-common/featureFlags/index"; +import { useLocalLiveAppManifest } from "@ledgerhq/live-common/wallet-api/LocalLiveAppProvider/index"; import { walletSelector } from "~/renderer/reducers/wallet"; export type DProps = { diff --git a/apps/ledger-live-desktop/src/renderer/screens/platform/LiveApp.tsx b/apps/ledger-live-desktop/src/renderer/screens/platform/LiveApp.tsx index f53f68f62faf..7ab5bfe641cc 100644 --- a/apps/ledger-live-desktop/src/renderer/screens/platform/LiveApp.tsx +++ b/apps/ledger-live-desktop/src/renderer/screens/platform/LiveApp.tsx @@ -6,7 +6,7 @@ import WebPlatformPlayer from "~/renderer/components/WebPlatformPlayer"; import { languageSelector } from "~/renderer/reducers/settings"; import { useSelector } from "react-redux"; import { useRemoteLiveAppManifest } from "@ledgerhq/live-common/platform/providers/RemoteLiveAppProvider/index"; -import { useLocalLiveAppManifest } from "@ledgerhq/live-common/platform/providers/LocalLiveAppProvider/index"; +import { useLocalLiveAppManifest } from "@ledgerhq/live-common/wallet-api/LocalLiveAppProvider/index"; type Props = { match: { diff --git a/apps/ledger-live-desktop/src/renderer/screens/platform/v2/Catalog/Card/Minimum.tsx b/apps/ledger-live-desktop/src/renderer/screens/platform/v2/Catalog/Card/Minimum.tsx index 721e9760e68c..3da77bb2e917 100644 --- a/apps/ledger-live-desktop/src/renderer/screens/platform/v2/Catalog/Card/Minimum.tsx +++ b/apps/ledger-live-desktop/src/renderer/screens/platform/v2/Catalog/Card/Minimum.tsx @@ -7,16 +7,19 @@ import { Container, Subtitle } from "./Layout"; import { useSelector } from "react-redux"; import { languageSelector } from "~/renderer/reducers/settings"; import { RecentlyUsedManifest } from "@ledgerhq/live-common/wallet-api/react"; +import { LiveAppManifest } from "@ledgerhq/live-common/platform/types"; -export function MinimumCard(props: PropsCard) { +export function MinimumCard(props: PropsCard) { const { disabled, onClick } = useCard(props); const { manifest } = props; const lang = useSelector(languageSelector); const usedAt = useMemo(() => { - const rtf = new Intl.RelativeTimeFormat(lang); - return rtf.format(-manifest.usedAt.diff, manifest.usedAt.unit); - }, [lang, manifest.usedAt.diff, manifest.usedAt.unit]); + if ("usedAt" in manifest) { + const rtf = new Intl.RelativeTimeFormat(lang); + return rtf.format(-manifest.usedAt.diff, manifest.usedAt.unit); + } else return; + }, [lang, manifest]); return ( @@ -27,7 +30,7 @@ export function MinimumCard(props: PropsCard) { {manifest.name} - {usedAt} + {usedAt && {usedAt}} diff --git a/apps/ledger-live-desktop/src/renderer/screens/platform/v2/Catalog/LocalLiveAppSection.tsx b/apps/ledger-live-desktop/src/renderer/screens/platform/v2/Catalog/LocalLiveAppSection.tsx new file mode 100644 index 000000000000..1f90ecc477f7 --- /dev/null +++ b/apps/ledger-live-desktop/src/renderer/screens/platform/v2/Catalog/LocalLiveAppSection.tsx @@ -0,0 +1,37 @@ +import React from "react"; +import { Flex } from "@ledgerhq/react-ui"; +import { MinimumCard } from "./Card"; +import styled from "styled-components"; +import { LiveAppManifest } from "@ledgerhq/live-common/platform/types"; +import { useHistory } from "react-router"; +import { SectionHeader } from "./SectionHeader"; +import { useTranslation } from "react-i18next"; + +export function LocalLiveAppSection({ localLiveApps }: { localLiveApps: LiveAppManifest[] }) { + const history = useHistory(); + const { t } = useTranslation(); + + return ( + + + {t("platform.catalog.section.locallyLoaded")} + + + {localLiveApps.map(manifest => ( + + history.push(`/platform/${manifest.id}`)} + /> + + ))} + + + ); +} + +const Scroll = styled(Flex).attrs({ overflowX: "scroll" })` + &::-webkit-scrollbar { + display: none; + } +`; diff --git a/apps/ledger-live-desktop/src/renderer/screens/platform/v2/Catalog/index.tsx b/apps/ledger-live-desktop/src/renderer/screens/platform/v2/Catalog/index.tsx index c1a8d4cd18d8..073b426ca46d 100644 --- a/apps/ledger-live-desktop/src/renderer/screens/platform/v2/Catalog/index.tsx +++ b/apps/ledger-live-desktop/src/renderer/screens/platform/v2/Catalog/index.tsx @@ -4,13 +4,15 @@ import { Text, Flex } from "@ledgerhq/react-ui"; import { RecentlyUsed } from "./RecentlyUsed"; import { Browse } from "./Browse"; import { useTranslation } from "react-i18next"; -import { useCatalog, useDiscoverDB } from "../hooks"; +import { useCatalog, useRecentlyUsedDB } from "../hooks"; +import { LocalLiveAppSection } from "./LocalLiveAppSection"; export function Catalog() { - const discoverDB = useDiscoverDB(); + const recentlyUsedDB = useRecentlyUsedDB(); const { t } = useTranslation(); - const { categories, recentlyUsed, disclaimer, search } = useCatalog(discoverDB); + const { categories, recentlyUsed, disclaimer, search, localLiveApps } = + useCatalog(recentlyUsedDB); return ( @@ -20,6 +22,8 @@ export function Catalog() { {t("platform.catalog.title")} + {localLiveApps.length ? : null} + {recentlyUsed.data.length ? ( ) : null} diff --git a/apps/ledger-live-desktop/src/renderer/screens/platform/v2/hooks.ts b/apps/ledger-live-desktop/src/renderer/screens/platform/v2/hooks.ts index 9afb043f8c2b..a9cbb17043b5 100644 --- a/apps/ledger-live-desktop/src/renderer/screens/platform/v2/hooks.ts +++ b/apps/ledger-live-desktop/src/renderer/screens/platform/v2/hooks.ts @@ -21,12 +21,14 @@ import { useCallback, useMemo } from "react"; import { useHistory } from "react-router"; import { closePlatformAppDrawer, openPlatformAppDisclaimerDrawer } from "~/renderer/actions/UI"; import { useManifests } from "@ledgerhq/live-common/platform/providers/RemoteLiveAppProvider/index"; +import { useLocalLiveAppContext } from "@ledgerhq/live-common/wallet-api/LocalLiveAppProvider/index"; -export function useCatalog(db: RecentlyUsedDB) { +export function useCatalog(recentlyUsedDB: RecentlyUsedDB) { const completeManifests = useManifests({ visibility: ["complete"] }); const combinedManifests = useManifests({ visibility: ["searchable", "complete"] }); const categories = useCategories(completeManifests); - const recentlyUsed = useRecentlyUsed(combinedManifests, db); + const recentlyUsed = useRecentlyUsed(combinedManifests, recentlyUsedDB); + const { state: localLiveApps } = useLocalLiveAppContext(); const search = useSearch({ list: combinedManifests, @@ -50,11 +52,11 @@ export function useCatalog(db: RecentlyUsedDB) { recentlyUsed, disclaimer, search, + localLiveApps, }; } -// TODO: rename to useRecentlyUsedDB -export function useDiscoverDB() { +export function useRecentlyUsedDB() { return useDB("app", DISCOVER_STORE_KEY, INITIAL_PLATFORM_STATE, state => state.recentlyUsed); } diff --git a/apps/ledger-live-desktop/src/renderer/screens/recover/Player.tsx b/apps/ledger-live-desktop/src/renderer/screens/recover/Player.tsx index ac9a97e5e7db..81c452c2e698 100644 --- a/apps/ledger-live-desktop/src/renderer/screens/recover/Player.tsx +++ b/apps/ledger-live-desktop/src/renderer/screens/recover/Player.tsx @@ -2,7 +2,6 @@ import React, { useCallback, useEffect, useMemo } from "react"; import { useSelector } from "react-redux"; import { RouteComponentProps, useHistory } from "react-router-dom"; import { useRemoteLiveAppManifest } from "@ledgerhq/live-common/platform/providers/RemoteLiveAppProvider/index"; -import { useLocalLiveAppManifest } from "@ledgerhq/live-common/platform/providers/LocalLiveAppProvider/index"; import { useOnboardingStatePolling } from "@ledgerhq/live-common/onboarding/hooks/useOnboardingStatePolling"; import { OnboardingStep } from "@ledgerhq/live-common/hw/extractOnboardingState"; import { counterValueCurrencySelector, languageSelector } from "~/renderer/reducers/settings"; @@ -12,6 +11,7 @@ import { getCurrentDevice } from "~/renderer/reducers/devices"; import styled from "styled-components"; import { useFeature } from "@ledgerhq/live-common/featureFlags/index"; import { StaticContext } from "react-router"; +import { useLocalLiveAppManifest } from "@ledgerhq/live-common/wallet-api/LocalLiveAppProvider/index"; const pollingPeriodMs = 1000; diff --git a/apps/ledger-live-desktop/src/renderer/screens/settings/sections/Developer/RunLocalAppButton.tsx b/apps/ledger-live-desktop/src/renderer/screens/settings/sections/Developer/RunLocalAppButton.tsx index aa6d2213e0a1..1bebb7639d89 100644 --- a/apps/ledger-live-desktop/src/renderer/screens/settings/sections/Developer/RunLocalAppButton.tsx +++ b/apps/ledger-live-desktop/src/renderer/screens/settings/sections/Developer/RunLocalAppButton.tsx @@ -4,13 +4,13 @@ import React, { useCallback } from "react"; import Button from "~/renderer/components/Button"; import { useTranslation } from "react-i18next"; import { readFile, writeFile } from "fs"; -import { useLocalLiveAppContext } from "@ledgerhq/live-common/platform/providers/LocalLiveAppProvider/index"; import { SettingsSectionRow as Row } from "../../SettingsSection"; import { useHistory } from "react-router-dom"; import styled from "styled-components"; import { Flex } from "@ledgerhq/react-ui"; import { useDispatch } from "react-redux"; import { openModal } from "~/renderer/actions/modals"; +import { useLocalLiveAppContext } from "@ledgerhq/live-common/wallet-api/LocalLiveAppProvider/index"; import { LiveAppManifest } from "@ledgerhq/live-common/platform/types"; const ButtonContainer = styled.div` @@ -23,7 +23,7 @@ const RunLocalAppButton = () => { const { t } = useTranslation(); const { addLocalManifest, - state: { liveAppByIndex }, + state: localLiveApps, removeLocalManifestById, } = useLocalLiveAppContext(); @@ -41,7 +41,7 @@ const RunLocalAppButton = () => { }) .then(function (response) { if (!response.canceled && response.filePath) { - const exportedManifest = liveAppByIndex.find( + const exportedManifest = localLiveApps.find( (manifest: LiveAppManifest) => manifest.id === id, ); @@ -56,7 +56,7 @@ const RunLocalAppButton = () => { } }); }, - [liveAppByIndex], + [localLiveApps], ); const onBrowseLocalManifest = useCallback(() => { @@ -121,7 +121,7 @@ const RunLocalAppButton = () => { - {liveAppByIndex.map((manifest: LiveAppManifest) => ( + {localLiveApps.map((manifest: LiveAppManifest) => (