Skip to content

Commit

Permalink
feat: Save locally added manifest into the storage on both Mobile and…
Browse files Browse the repository at this point in the history
… Desktop client (#6719)

feat: Save locally added manifest into the storage on both Mobile and Desktop client
  • Loading branch information
RamyEB committed May 10, 2024
1 parent 69dfce5 commit ca42740
Show file tree
Hide file tree
Showing 40 changed files with 425 additions and 328 deletions.
7 changes: 7 additions & 0 deletions .changeset/hot-zebras-ring.md
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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 (
<RemoteLiveAppProvider
Expand All @@ -33,11 +39,20 @@ export function PlatformAppProviderWrapper({ children }: PlatformAppProviderWrap
}}
updateFrequency={AUTO_UPDATE_DEFAULT_DELAY}
>
<LocalLiveAppProvider>
<LocalLiveAppProvider db={localLiveAppDB}>
<RampCatalogProvider provider={provider} updateFrequency={AUTO_UPDATE_DEFAULT_DELAY}>
{children}
</RampCatalogProvider>
</LocalLiveAppProvider>
</RemoteLiveAppProvider>
);
}

function useLocalLiveAppDB() {
return useDB(
"app",
DISCOVER_STORE_KEY,
INITIAL_PLATFORM_STATE,
state => state.localLiveApp || INITIAL_PLATFORM_STATE.localLiveApp,
);
}
Original file line number Diff line number Diff line change
@@ -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";
Expand All @@ -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",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -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 (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -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 = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<RecentlyUsedManifest>) {
export function MinimumCard(props: PropsCard<RecentlyUsedManifest | LiveAppManifest>) {
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 (
<Container disabled={disabled} onClick={onClick} width={300}>
Expand All @@ -27,7 +30,7 @@ export function MinimumCard(props: PropsCard<RecentlyUsedManifest>) {
<Text overflow="hidden" whiteSpace="nowrap" textOverflow="ellipsis" fontSize={14}>
{manifest.name}
</Text>
<Subtitle>{usedAt}</Subtitle>
{usedAt && <Subtitle>{usedAt}</Subtitle>}
</Flex>
</Flex>
</Container>
Expand Down
Original file line number Diff line number Diff line change
@@ -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 (
<Flex flexDirection="column" marginBottom={4}>
<SectionHeader iconLeft="Download">
{t("platform.catalog.section.locallyLoaded")}
</SectionHeader>
<Scroll>
{localLiveApps.map(manifest => (
<Flex key={manifest.id} margin={2}>
<MinimumCard
manifest={manifest}
onClick={(manifest: LiveAppManifest) => history.push(`/platform/${manifest.id}`)}
/>
</Flex>
))}
</Scroll>
</Flex>
);
}

const Scroll = styled(Flex).attrs({ overflowX: "scroll" })`
&::-webkit-scrollbar {
display: none;
}
`;
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
<Flex flexDirection="column" paddingBottom={100}>
Expand All @@ -20,6 +22,8 @@ export function Catalog() {
{t("platform.catalog.title")}
</Text>

{localLiveApps.length ? <LocalLiveAppSection localLiveApps={localLiveApps} /> : null}

{recentlyUsed.data.length ? (
<RecentlyUsed recentlyUsed={recentlyUsed} disclaimer={disclaimer} />
) : null}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -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;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand All @@ -23,7 +23,7 @@ const RunLocalAppButton = () => {
const { t } = useTranslation();
const {
addLocalManifest,
state: { liveAppByIndex },
state: localLiveApps,
removeLocalManifestById,
} = useLocalLiveAppContext();

Expand All @@ -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,
);

Expand All @@ -56,7 +56,7 @@ const RunLocalAppButton = () => {
}
});
},
[liveAppByIndex],
[localLiveApps],
);

const onBrowseLocalManifest = useCallback(() => {
Expand Down Expand Up @@ -121,7 +121,7 @@ const RunLocalAppButton = () => {
</Button>
</Flex>
</Row>
{liveAppByIndex.map((manifest: LiveAppManifest) => (
{localLiveApps.map((manifest: LiveAppManifest) => (
<Row key={manifest.id} title={manifest.name} desc={manifest.url as string}>
<ButtonContainer>
<Button small primary onClick={() => history.push(`/platform/${manifest.id}`)}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ import { counterValueCurrencySelector, languageSelector } from "~/renderer/reduc
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 { useHistory, useLocation } from "react-router-dom";
import { WebviewProps } from "~/renderer/components/Web3AppWebview/types";
import { useDebounce } from "@ledgerhq/live-common/hooks/useDebounce";
import { captureException } from "~/sentry/internal";
import { UnableToLoadSwapLiveError } from "~/renderer/screens/exchange/Swap2/Form/SwapWebView";
import { useLocalLiveAppManifest } from "@ledgerhq/live-common/wallet-api/LocalLiveAppProvider/index";

const DEFAULT_SWAP_APP_ID = "swapWeb";

Expand Down
1 change: 1 addition & 0 deletions apps/ledger-live-desktop/static/i18n/en/app.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"title": "Discover",
"section": {
"recentlyUsed": "Recently used",
"locallyLoaded": "Locally Loaded Live Apps",
"browse": "Browse"
},
"filter": {
Expand Down
19 changes: 17 additions & 2 deletions apps/ledger-live-mobile/src/PlatformAppProviderWrapper.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
import React, { ReactNode } from "react";
import VersionNumber from "react-native-version-number";
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 useEnv from "@ledgerhq/live-common/hooks/useEnv";
import { Platform } from "react-native";
import { useLocale } from "~/context/Locale";
import {
DISCOVER_STORE_KEY,
INITIAL_PLATFORM_STATE,
} from "@ledgerhq/live-common/wallet-api/constants";
import { DiscoverDB } from "@ledgerhq/live-common/wallet-api/types";
import { useDB } from "./db";

type PlatformAppProviderWrapperProps = {
children: ReactNode;
Expand All @@ -20,6 +26,7 @@ export default function PlatformAppProviderWrapper({ children }: PlatformAppProv
) as boolean;
const { locale: lang } = useLocale();
const isDebugAppEnabled = useEnv<"PLATFORM_DEBUG">("PLATFORM_DEBUG") as boolean;
const localLiveAppDB = useLocalLiveAppDB();

return (
<RemoteLiveAppProvider
Expand All @@ -32,11 +39,19 @@ export default function PlatformAppProviderWrapper({ children }: PlatformAppProv
lang,
}}
>
<LocalLiveAppProvider>
<LocalLiveAppProvider db={localLiveAppDB}>
<RampCatalogProvider updateFrequency={AUTO_UPDATE_DEFAULT_DELAY}>
{children}
</RampCatalogProvider>
</LocalLiveAppProvider>
</RemoteLiveAppProvider>
);
}

function useLocalLiveAppDB() {
return useDB<DiscoverDB, DiscoverDB["localLiveApp"]>(
DISCOVER_STORE_KEY,
INITIAL_PLATFORM_STATE,
state => state.localLiveApp || INITIAL_PLATFORM_STATE.localLiveApp,
);
}

0 comments on commit ca42740

Please sign in to comment.