diff --git a/.changeset/hot-zebras-ring.md b/.changeset/hot-zebras-ring.md new file mode 100644 index 00000000000..ab6a0d6c747 --- /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 6bf9f3f6364..589e295657f 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 c3e80d1a217..edc723a8548 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 bf34f37bfb4..ce788129904 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 fde361c5522..a9156306ac2 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 f7e7370938c..f8e011595ed 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 82b9b568c58..6191f55558d 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 f53f68f62fa..7ab5bfe641c 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 721e9760e68..3da77bb2e91 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 00000000000..1f90ecc477f --- /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 c1a8d4cd18d..073b426ca46 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 9afb043f8c2..a9cbb17043b 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 ac9a97e5e7d..81c452c2e69 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 aa6d2213e0a..1bebb7639d8 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) => (