Skip to content

Commit

Permalink
Merge pull request #5072 from LedgerHQ/support/extract-json
Browse files Browse the repository at this point in the history
Improve Startup LL Desktop
  • Loading branch information
KVNLS authored Oct 24, 2023
2 parents 54a648a + 77435da commit ff13a91
Show file tree
Hide file tree
Showing 40 changed files with 201 additions and 104 deletions.
5 changes: 5 additions & 0 deletions .changeset/empty-ligers-unite.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"ledger-live-desktop": minor
---

Improve startup time with lazy loading + remove json files from renderer bundle
139 changes: 90 additions & 49 deletions apps/ledger-live-desktop/src/renderer/Default.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,13 @@
import React, { useEffect } from "react";
import React, { useEffect, lazy, Suspense } from "react";
import styled from "styled-components";
import { ipcRenderer } from "electron";
import { Redirect, Route, Switch, useHistory } from "react-router-dom";
import { useSelector } from "react-redux";
import { FeatureToggle } from "@ledgerhq/live-common/featureFlags/index";
import TrackAppStart from "~/renderer/components/TrackAppStart";
import { PlatformCatalog, LiveApp } from "~/renderer/screens/platform";
import { BridgeSyncProvider } from "~/renderer/bridge/BridgeSyncContext";
import { SyncNewAccounts } from "~/renderer/bridge/SyncNewAccounts";
import Dashboard from "~/renderer/screens/dashboard";
import Settings from "~/renderer/screens/settings";
import Accounts from "~/renderer/screens/accounts";
import Card from "~/renderer/screens/card";
import Manager from "~/renderer/screens/manager";
import Exchange from "~/renderer/screens/exchange";
import Earn from "./screens/earn";
import SwapWeb from "./screens/swapWeb";
import Swap2 from "~/renderer/screens/exchange/Swap2";
import USBTroubleshooting from "~/renderer/screens/USBTroubleshooting";
import Account from "~/renderer/screens/account";
import Asset from "~/renderer/screens/asset";
import { PlatformCatalog, LiveApp } from "~/renderer/screens/platform";
import NFTGallery from "~/renderer/screens/nft/Gallery";
import NFTCollection from "~/renderer/screens/nft/Gallery/Collection";
import Box from "~/renderer/components/Box/Box";
import { useListenToHidDevices } from "./hooks/useListenToHidDevices";
import ExportLogsButton from "~/renderer/components/ExportLogsButton";
Expand Down Expand Up @@ -52,16 +38,7 @@ import Drawer from "~/renderer/drawers/Drawer";
import UpdateBanner from "~/renderer/components/Updater/Banner";
import FirmwareUpdateBanner from "~/renderer/components/FirmwareUpdateBanner";
import VaultSignerBanner from "~/renderer/components/VaultSignerBanner";
import RecoverRestore from "~/renderer/components/RecoverRestore";
import Onboarding from "~/renderer/components/Onboarding";
import PostOnboardingScreen from "~/renderer/components/PostOnboardingScreen";
import { hasCompletedOnboardingSelector } from "~/renderer/reducers/settings";
import Market from "~/renderer/screens/market";
import MarketCoinScreen from "~/renderer/screens/market/MarketCoinScreen";
import Learn from "~/renderer/screens/learn";
import WelcomeScreenSettings from "~/renderer/screens/settings/WelcomeScreenSettings";
import SyncOnboarding from "./components/SyncOnboarding";
import RecoverPlayer from "~/renderer/screens/recover/Player";
import { updateIdentify } from "./analytics/segment";
import { useDiscoverDB } from "./screens/platform/v2/hooks";
import { useFeature } from "@ledgerhq/live-common/featureFlags/index";
Expand All @@ -70,9 +47,64 @@ import {
useFetchCurrencyAll,
useFetchCurrencyFrom,
} from "@ledgerhq/live-common/exchange/swap/hooks/index";
import { Flex, InfiniteLoader } from "@ledgerhq/react-ui";
import useAccountsWithFundsListener from "@ledgerhq/live-common/hooks/useAccountsWithFundsListener";
import { accountsSelector } from "./reducers/accounts";

const Dashboard = lazy(() => import("~/renderer/screens/dashboard"));
const Settings = lazy(() => import("~/renderer/screens/settings"));
const Accounts = lazy(() => import("~/renderer/screens/accounts"));
const Card = lazy(() => import("~/renderer/screens/card"));
const Manager = lazy(() => import("~/renderer/screens/manager"));
const Exchange = lazy(() => import("~/renderer/screens/exchange"));
const Earn = lazy(() => import("~/renderer/screens/earn"));
const SwapWeb = lazy(() => import("~/renderer/screens/swapWeb"));
const Swap2 = lazy(() => import("~/renderer/screens/exchange/Swap2"));

const Market = lazy(() => import("~/renderer/screens/market"));
const MarketCoinScreen = lazy(() => import("~/renderer/screens/market/MarketCoinScreen"));
const Learn = lazy(() => import("~/renderer/screens/learn"));
const WelcomeScreenSettings = lazy(
() => import("~/renderer/screens/settings/WelcomeScreenSettings"),
);
const SyncOnboarding = lazy(() => import("./components/SyncOnboarding"));
const RecoverPlayer = lazy(() => import("~/renderer/screens/recover/Player"));

const NFTGallery = lazy(() => import("~/renderer/screens/nft/Gallery"));
const NFTCollection = lazy(() => import("~/renderer/screens/nft/Gallery/Collection"));
const RecoverRestore = lazy(() => import("~/renderer/components/RecoverRestore"));
const Onboarding = lazy(() => import("~/renderer/components/Onboarding"));
const PostOnboardingScreen = lazy(() => import("~/renderer/components/PostOnboardingScreen"));
const USBTroubleshooting = lazy(() => import("~/renderer/screens/USBTroubleshooting"));
const Asset = lazy(() => import("~/renderer/screens/asset"));
const Account = lazy(() => import("~/renderer/screens/account"));

const LoaderWrapper = styled.div`
padding: 24px;
align-self: center;
display: flex;
align-items: center;
justify-content: center;
margin: auto;
`;

const Fallback = () => (
<LoaderWrapper>
<Flex alignItems="center" justifyContent="center" borderRadius={9999} size={60} mb={5}>
<InfiniteLoader size={58} />
</Flex>
</LoaderWrapper>
);

// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
// eslint-disable-next-line react/display-name
const withSuspense = Component => props => (
<Suspense fallback={<Fallback />}>
<Component {...props} />
</Suspense>
);

// in order to test sentry integration, we need the ability to test it out.
const LetThisCrashForCrashTest = () => {
throw new Error("CrashTestRendering");
Expand Down Expand Up @@ -197,28 +229,34 @@ export default function Default() {
path="/onboarding"
render={() => (
<>
<Onboarding />
<Suspense fallback={<Fallback />}>
<Onboarding />
</Suspense>
<Drawer />
</>
)}
/>
<Route path="/sync-onboarding" component={SyncOnboarding} />
<Route path="/sync-onboarding" render={withSuspense(SyncOnboarding)} />
<Route
path="/post-onboarding"
render={() => (
<>
<PostOnboardingScreen />
<Suspense fallback={<Fallback />}>
<PostOnboardingScreen />
</Suspense>
<Drawer />
</>
)}
/>
<Route path="/recover-restore" component={RecoverRestore} />
<Route path="/recover-restore" render={withSuspense(RecoverRestore)} />

<Route path="/USBTroubleshooting">
<USBTroubleshooting onboarding={!hasCompletedOnboarding} />
<Suspense fallback={<Fallback />}>
<USBTroubleshooting onboarding={!hasCompletedOnboarding} />
</Suspense>
</Route>
{!hasCompletedOnboarding ? (
<Route path="/settings" component={WelcomeScreenSettings} />
<Route path="/settings" render={withSuspense(WelcomeScreenSettings)} />
) : (
<Route>
<Switch>
Expand All @@ -240,7 +278,7 @@ export default function Default() {
>
<FeatureToggle featureId="protectServicesDesktop">
<Switch>
<Route path="/recover/:appId" component={RecoverPlayer} />
<Route path="/recover/:appId" render={withSuspense(RecoverPlayer)} />
</Switch>
</FeatureToggle>
<MainSideBar />
Expand All @@ -251,38 +289,41 @@ export default function Default() {
<VaultSignerBanner />
</TopBannerContainer>
<Switch>
<Route path="/" exact component={Dashboard} />
<Route path="/settings" component={Settings} />
<Route path="/accounts" component={Accounts} />
<Route path="/card" component={Card} />
<Route path="/" exact render={withSuspense(Dashboard)} />
<Route path="/settings" render={withSuspense(Settings)} />
<Route path="/accounts" render={withSuspense(Accounts)} />
<Route path="/card" render={withSuspense(Card)} />
<Redirect from="/manager/reload" to="/manager" />
<Route path="/manager" component={Manager} />
<Route path="/manager" render={withSuspense(Manager)} />
<Route
path="/platform"
component={() => <PlatformCatalog db={discoverDB} />}
exact
/>
<Route path="/platform/:appId?" component={LiveApp} />
<Route path="/earn" component={Earn} />
<Route exact path="/exchange/:appId?" component={Exchange} />
<Route path="/earn" render={withSuspense(Earn)} />
<Route exact path="/exchange/:appId?" render={withSuspense(Exchange)} />
<Route
exact
path="/account/:id/nft-collection"
component={NFTGallery}
render={withSuspense(NFTGallery)}
/>
<Route path="/swap-web" component={SwapWeb} />
<Route path="/swap-web" render={withSuspense(SwapWeb)} />
<Route
path="/account/:id/nft-collection/:collectionAddress?"
component={NFTCollection}
render={withSuspense(NFTCollection)}
/>
<Route path="/account/:parentId/:id" render={withSuspense(Account)} />
<Route path="/account/:id" render={withSuspense(Account)} />
<Route path="/asset/:assetId+" render={withSuspense(Asset)} />
<Route path="/swap" render={withSuspense(Swap2)} />
<Route
path="/market/:currencyId"
render={withSuspense(MarketCoinScreen)}
/>
<Route path="/account/:parentId/:id" component={Account} />
<Route path="/account/:id" component={Account} />
<Route path="/asset/:assetId+" component={Asset} />
<Route path="/swap" component={Swap2} />
<Route path="/market/:currencyId" component={MarketCoinScreen} />
<Route path="/market" component={Market} />
<Route path="/market" render={withSuspense(Market)} />
<FeatureToggle featureId="learn">
<Route path="/learn" component={Learn} />
<Route path="/learn" render={withSuspense(Learn)} />
</FeatureToggle>
</Switch>
</Page>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ export const useListenToHidDevices = () => {
const timeoutSyncDevices = setTimeout(syncDevices, 1000);

return () => {
clearTimeout(timeoutSyncDevices);
sub.unsubscribe();
clearTimeout?.(timeoutSyncDevices);
sub?.unsubscribe?.();
};
}, [dispatch]);

Expand Down
12 changes: 7 additions & 5 deletions apps/ledger-live-desktop/src/renderer/init.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ import { checkLibs } from "@ledgerhq/live-common/sanityChecks";
import { importPostOnboardingState } from "@ledgerhq/live-common/postOnboarding/actions";
import i18n from "i18next";
import { webFrame, ipcRenderer } from "electron";
import { createRoot } from "react-dom/client";
// We can't use new createRoot for now. We have issues we react-redux 7.x and lazy load of components
// https://github.com/reduxjs/react-redux/issues/1977
// eslint-disable-next-line react/no-deprecated
import { render } from "react-dom";
import moment from "moment";
import each from "lodash/each";
import { reload, getKey, loadLSS } from "~/renderer/storage";
Expand Down Expand Up @@ -47,7 +50,7 @@ import { Device } from "@ledgerhq/live-common/hw/actions/types";
import { listCachedCurrencyIds } from "./bridge/cache";
import { LogEntry } from "winston";

const domNode = document.getElementById("react-root");
const rootNode = document.getElementById("react-root");
const TAB_KEY = 9;

async function init() {
Expand Down Expand Up @@ -246,9 +249,8 @@ async function init() {
};
}
function r(Comp: JSX.Element) {
if (domNode) {
const rootNode = createRoot(domNode);
rootNode.render(Comp);
if (rootNode) {
render(Comp, rootNode);
}
}
init()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ type RenderActionParams = {
disabled?: boolean;
tooltip?: string;
accountActionsTestId?: string;
contrastText: string;
currency: TokenCurrency | CryptoCurrency;
};

const ButtonSettings = styled(Tabbable).attrs<{ disabled?: boolean }>(() => ({
Expand Down Expand Up @@ -94,6 +96,36 @@ type Props = {
openModal: Function;
} & OwnProps;

const ActionItem = ({
label,
onClick,
event,
eventProperties,
icon,
disabled,
tooltip,
accountActionsTestId,
contrastText,
currency,
}: RenderActionParams) => {
const Icon = icon;
const Action = (
<ActionDefault
disabled={disabled}
onClick={onClick}
event={event}
eventProperties={eventProperties}
iconComponent={Icon && <Icon size={14} overrideColor={contrastText} currency={currency} />}
labelComponent={label}
accountActionsTestId={accountActionsTestId}
/>
);
if (tooltip) {
return <Tooltip content={tooltip}>{Action}</Tooltip>;
}
return Action;
};

const AccountHeaderSettingsButtonComponent = ({ account, parentAccount, openModal, t }: Props) => {
const mainAccount = getMainAccount(account, parentAccount);
const currency = getAccountCurrency(account);
Expand Down Expand Up @@ -246,37 +278,11 @@ const AccountHeaderActions = ({ account, parentAccount, openModal }: Props) => {
});
}, [openModal, parentAccount, account, buttonSharedTrackingFields]);

const renderAction = ({
label,
onClick,
event,
eventProperties,
icon,
disabled,
tooltip,
accountActionsTestId,
}: RenderActionParams) => {
const Icon = icon;
const Action = (
<ActionDefault
disabled={disabled}
onClick={onClick}
event={event}
eventProperties={eventProperties}
iconComponent={Icon && <Icon size={14} overrideColor={contrastText} currency={currency} />}
labelComponent={label}
accountActionsTestId={accountActionsTestId}
/>
);
if (tooltip) {
return <Tooltip content={tooltip}>{Action}</Tooltip>;
}
return Action;
};

const manageActions: RenderActionParams[] = [
...manageList.map(item => ({
...item,
contrastText,
currency,
eventProperties: {
...buttonSharedTrackingFields,
...item.eventProperties,
Expand All @@ -287,7 +293,9 @@ const AccountHeaderActions = ({ account, parentAccount, openModal }: Props) => {
const buyHeader = <BuyActionDefault onClick={() => onBuySell("buy")} />;
const sellHeader = <SellActionDefault onClick={() => onBuySell("sell")} />;
const swapHeader = <SwapActionDefault onClick={onSwap} />;
const manageActionsHeader = manageActions.map(item => renderAction(item));
const manageActionsHeader = manageActions.map(item => (
<ActionItem {...item} key={item.accountActionsTestId} />
));

const NonEmptyAccountHeader = (
<FadeInButtonsContainer data-test-id="account-buttons-group" show={showButtons}>
Expand Down
4 changes: 3 additions & 1 deletion apps/ledger-live-desktop/tests/models/AccountPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,9 @@ export class AccountPage {
}

async scrollToOperations() {
await this.page.locator("id=operation-list").scrollIntoViewIfNeeded();
const operationList = this.page.locator("id=operation-list");
await operationList.waitFor();
await operationList.scrollIntoViewIfNeeded({ timeout: 1000 });
}

async startCosmosStakingFlow() {
Expand Down
1 change: 1 addition & 0 deletions apps/ledger-live-desktop/tests/models/AddAccountModal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export class AddAccountModal extends Modal {
}

async getFirstAccountName() {
await this.page.waitForTimeout(500);
const firstAccountName = await this.accountsList.locator("input").first().inputValue();
return firstAccountName;
}
Expand Down
4 changes: 4 additions & 0 deletions apps/ledger-live-desktop/tests/models/LiveAppWebview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,10 @@ export class LiveAppWebview {
return waitFor(() => this.textIsPresent(textToCheck));
}

async waitForLoaded() {
return this.page.waitForLoadState("domcontentloaded");
}

async textIsPresent(textToCheck: string) {
const result: boolean = await this.page.evaluate(textToCheck => {

Check failure on line 120 in apps/ledger-live-desktop/tests/models/LiveAppWebview.ts

View workflow job for this annotation

GitHub Actions / Desktop E2E (macOS X)

services/ethereumStaking.spec.ts:67:5 › Ethereum staking flows via portfolio

3) services/ethereumStaking.spec.ts:67:5 › Ethereum staking flows via portfolio, asset page and market page @smoke Error: page.evaluate: Target closed at ../models/LiveAppWebview.ts:120 118 | 119 | async textIsPresent(textToCheck: string) { > 120 | const result: boolean = await this.page.evaluate(textToCheck => { | ^ 121 | const webview = document.querySelector("webview"); 122 | return (webview as WebviewTag) 123 | .executeJavaScript( at LiveAppWebview.textIsPresent (/Users/admin/actions-runner/_work/ledger-live/ledger-live/apps/ledger-live-desktop/tests/models/LiveAppWebview.ts:120:45) at predicate (/Users/admin/actions-runner/_work/ledger-live/ledger-live/apps/ledger-live-desktop/tests/models/LiveAppWebview.ts:112:31) at Timeout._onTimeout (/Users/admin/actions-runner/_work/ledger-live/ledger-live/apps/ledger-live-desktop/tests/utils/waitFor.ts:14:31)
const webview = document.querySelector("webview");
Expand Down
Loading

0 comments on commit ff13a91

Please sign in to comment.