Skip to content

Commit

Permalink
Merge pull request #199 from lidofinance/feature/si-1594-ledger-hid-c…
Browse files Browse the repository at this point in the history
…onnection-via-low-speed-internet

Feature/si 1594 ledger hid connection via low speed internet
  • Loading branch information
manneredboor authored Nov 28, 2024
2 parents 8c4e92a + b33a228 commit f3d431f
Show file tree
Hide file tree
Showing 12 changed files with 192 additions and 87 deletions.
6 changes: 6 additions & 0 deletions packages/connect-wallet-modal/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# @reef-knot/connect-wallet-modal

## 7.0.1

### Patch Changes

- Ledger HID connection on slow network fixed

## 7.0.0

### Major Changes
Expand Down
2 changes: 1 addition & 1 deletion packages/connect-wallet-modal/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@reef-knot/connect-wallet-modal",
"version": "7.0.0",
"version": "7.0.1",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"exports": {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,38 +1,36 @@
import React from 'react';
import {
Loader,
Stack,
StackItem,
Text,
useBreakpoint,
} from '@lidofinance/lido-ui';
import { LedgerImageDefault } from './icons/LedgerImageDefault';
import { LedgerImageDefaultMobile } from './icons/LedgerImageDefaultMobile';
import { LedgerScreenContainerStyled } from './styles';
import React, { FC } from 'react';
import { Loader } from '@lidofinance/lido-ui';
import { useLedgerContext } from './hooks';
import { LedgerModalScreen } from './LedgerModalScreen';
import { LedgerImageDefaultAdaptive } from './icons/LedgerImageDefaultAdaptive';
import { LedgerScreenLoadingContainer } from './styles';

export const LedgerConnectionScreen = () => {
type LedgerConnectionScreenProps = {
showConnectButton?: boolean;
onClickConnect?: () => void;
};

export const LedgerConnectionScreen: FC<LedgerConnectionScreenProps> = ({
showConnectButton,
onClickConnect,
}) => {
const { isLoadingLedgerLibs } = useLedgerContext();

const message = isLoadingLedgerLibs ? (
<LedgerScreenLoadingContainer>
<Loader size="medium" color="secondary" />
<div>Loading connector...</div>
</LedgerScreenLoadingContainer>
) : (
'Please connect your Ledger and launch Ethereum app on your device'
);

return (
<LedgerScreenContainerStyled>
<Stack direction="column" spacing="xl" align="center">
<StackItem>
{useBreakpoint('md') ? (
<LedgerImageDefaultMobile />
) : (
<LedgerImageDefault />
)}
</StackItem>
<StackItem>
{isLoadingLedgerLibs ? (
<Loader size="medium" color="secondary" />
) : (
<Text color="secondary" size="xs">
Please connect your Ledger and launch Ethereum app on your device
</Text>
)}
</StackItem>
</Stack>
</LedgerScreenContainerStyled>
<LedgerModalScreen
icon={<LedgerImageDefaultAdaptive />}
message={message}
action={showConnectButton ? 'Connect' : undefined}
onClickAction={showConnectButton ? onClickConnect : undefined}
/>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ import type Transport from '@ledgerhq/hw-transport';
import { helpers } from '@reef-knot/web3-react';
import { getTransport, isHIDSupported } from './helpers';

const USER_ACTIVATION_TIMEOUT = 3000;

type EthConstructorType = new (
...args: ConstructorParameters<typeof Eth>
) => Eth;

export interface LedgerContextProps {
isActive: boolean;
children?: ReactNode;
Expand All @@ -23,6 +29,7 @@ export type LedgerContextValue = {
ledgerAppEth: MutableRefObject<Eth | null>;
isTransportConnected: boolean;
isLoadingLedgerLibs: boolean;
isUserActivationRequired: boolean;
error: Error | null;
setError: (e: Error | null) => void;
connectTransport: () => Promise<void>;
Expand All @@ -40,8 +47,10 @@ export const LedgerContextProvider = ({
// isTransportConnecting flag helps with react v18 strict mode in the dev mode,
// which re-runs effects extra time, which breaks ledger connection process
const isTransportConnecting = useRef(false);
const ledgerLibEth = useRef<EthConstructorType | null>(null);
const ledgerAppEth = useRef<Eth | null>(null);
const [error, setError] = useState<Error | null>(null);
const [isUserActivationRequired, setUserActivationRequired] = useState(false);
const [isTransportConnected, setIsTransportConnected] = useState(false);
const [isLoadingLedgerLibs, setIsLoadingLedgerLibs] = useState(false);
const [activeAccountsRequestsCounter, setActiveAccountsRequestsCounter] =
Expand All @@ -52,6 +61,7 @@ export const LedgerContextProvider = ({
await transport?.current?.close();
transport.current = null;
ledgerAppEth.current = null;
setUserActivationRequired(false);
if (!goingToReconnect) {
setIsTransportConnected(false);
}
Expand All @@ -60,9 +70,18 @@ export const LedgerContextProvider = ({
}
}, []);

const loadLedgerLibs = useCallback(async () => {
if (ledgerLibEth.current) return;
setIsLoadingLedgerLibs(true);
const { default: Eth } = await import('@ledgerhq/hw-app-eth');
ledgerLibEth.current = Eth;
setIsLoadingLedgerLibs(false);
}, []);

const connectTransport = useCallback(async () => {
if (isTransportConnecting.current || transport.current) return;
setError(null);
setUserActivationRequired(false);
isTransportConnecting.current = true;

if (!isHIDSupported()) {
Expand All @@ -75,12 +94,17 @@ export const LedgerContextProvider = ({
}

try {
setIsLoadingLedgerLibs(true);
const { default: Eth } = await import('@ledgerhq/hw-app-eth');
setIsLoadingLedgerLibs(false);
const userActivationTime = Date.now();
await loadLedgerLibs();
const timePassedAfterUserActivation = Date.now() - userActivationTime;

if (timePassedAfterUserActivation > USER_ACTIVATION_TIMEOUT) {
setUserActivationRequired(true);
return;
}

transport.current = await getTransport();
ledgerAppEth.current = new Eth(transport.current);
ledgerAppEth.current = new ledgerLibEth.current!(transport.current);

Check warning on line 107 in packages/connect-wallet-modal/src/components/Ledger/LedgerContext.tsx

View workflow job for this annotation

GitHub Actions / release

Forbidden non-null assertion
await ledgerAppEth.current.getAppConfiguration();
setIsTransportConnected(true);
} catch (e: any) {
Expand Down Expand Up @@ -110,6 +134,7 @@ export const LedgerContextProvider = ({
ledgerAppEth,
isTransportConnected,
isLoadingLedgerLibs,
isUserActivationRequired,
error,
setError,
connectTransport,
Expand All @@ -121,6 +146,7 @@ export const LedgerContextProvider = ({
[
isTransportConnected,
isLoadingLedgerLibs,
isUserActivationRequired,
error,
connectTransport,
disconnectTransport,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,50 +1,21 @@
import React, { FC } from 'react';
import {
Text,
Button,
Stack,
StackItem,
useBreakpoint,
} from '@lidofinance/lido-ui';
import styled from '@reef-knot/ui-react/styled-wrapper';
import { LedgerScreenContainerStyled } from './styles';
import { LedgerImageError } from './icons/LedgerImageError';
import { LedgerImageErrorMobile } from './icons/LedgerImageErrorMobile';
import { LedgerModalScreen } from './LedgerModalScreen';
import { LedgerImageErrorAdaptive } from './icons/LedgerImageErrorAdaptive';

const HeadingStyled = styled(Text)`
padding-bottom: 4px;
`;
type LedgerErrorScreenProps = {
message: React.ReactNode;
onClickRetry: () => void;
};

export const LedgerErrorScreen: FC<{ message: string; retry: () => void }> = ({
export const LedgerErrorScreen: FC<LedgerErrorScreenProps> = ({
message,
retry,
onClickRetry,
}) => (
<LedgerScreenContainerStyled>
<Stack direction="column" spacing="xl" align="stretch">
<StackItem>
<Stack direction="column" spacing="xl" align="center">
<StackItem>
{useBreakpoint('md') ? (
<LedgerImageErrorMobile />
) : (
<LedgerImageError />
)}
</StackItem>
<StackItem>
<HeadingStyled color="default" size="sm" strong>
Something went wrong
</HeadingStyled>
<Text color="secondary" size="xs">
{message}
</Text>
</StackItem>
</Stack>
</StackItem>
<StackItem>
<Button variant="ghost" fullwidth onClick={retry}>
Retry
</Button>
</StackItem>
</Stack>
</LedgerScreenContainerStyled>
<LedgerModalScreen
icon={<LedgerImageErrorAdaptive />}
heading="Something went wrong"
message={message}
action="Retry"
onClickAction={onClickRetry}
/>
);
Original file line number Diff line number Diff line change
Expand Up @@ -40,21 +40,34 @@ export const LedgerModal = ({
};

export const LedgerScreen = ({ onCloseSuccess }: LedgerModalProps) => {
const { error, reconnectTransport, isTransportConnected } =
useLedgerContext();
const {
error,
reconnectTransport,
isTransportConnected,
isUserActivationRequired,
} = useLedgerContext();

const handleClickRetry = useCallback(() => {
void reconnectTransport();
}, [reconnectTransport]);

return (
<LedgerModalInnerContainer>
{error && (
<LedgerErrorScreen
message={error.message}
retry={() => void reconnectTransport()}
onClickRetry={handleClickRetry}
/>
)}
{!error && isTransportConnected && (
<LedgerAccountScreen onConnectSuccess={onCloseSuccess} />
)}
{!error && !isTransportConnected && <LedgerConnectionScreen />}
{!error && !isTransportConnected && (
<LedgerConnectionScreen
showConnectButton={isUserActivationRequired}
onClickConnect={handleClickRetry}
/>
)}
</LedgerModalInnerContainer>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import React, { FC } from 'react';
import { Text, Button, Stack, StackItem } from '@lidofinance/lido-ui';
import styled from '@reef-knot/ui-react/styled-wrapper';
import { LedgerScreenContainerStyled } from './styles';

const HeadingStyled = styled(Text)`
padding-bottom: 4px;
`;

type LedgerActionScreenProps = {
icon: React.ReactNode;
heading?: React.ReactNode;
message?: React.ReactNode;
action?: React.ReactNode;
onClickAction?: () => void;
};

export const LedgerModalScreen: FC<LedgerActionScreenProps> = ({
icon,
heading,
message,
action,
onClickAction,
}) => (
<LedgerScreenContainerStyled>
<Stack direction="column" spacing="xl" align="stretch">
<StackItem>
<Stack direction="column" spacing="xl" align="center">
<StackItem>{icon}</StackItem>
<StackItem>
{heading && (
<HeadingStyled color="default" size="sm" strong>
{heading}
</HeadingStyled>
)}
{message && (
<Text color="secondary" size="xs">
{message}
</Text>
)}
</StackItem>
</Stack>
</StackItem>
{action && (
<StackItem>
<Button variant="ghost" fullwidth onClick={onClickAction}>
{action}
</Button>
</StackItem>
)}
</Stack>
</LedgerScreenContainerStyled>
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import React from 'react';
import { useBreakpoint } from '@lidofinance/lido-ui';
import { LedgerImageDefault } from './LedgerImageDefault';
import { LedgerImageDefaultMobile } from './LedgerImageDefaultMobile';

export const LedgerImageDefaultAdaptive = () => {
return useBreakpoint('md') ? (
<LedgerImageDefaultMobile />
) : (
<LedgerImageDefault />
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import React from 'react';
import { useBreakpoint } from '@lidofinance/lido-ui';
import { LedgerImageError } from './LedgerImageError';
import { LedgerImageErrorMobile } from './LedgerImageErrorMobile';

export const LedgerImageErrorAdaptive = () => {
return useBreakpoint('md') ? (
<LedgerImageErrorMobile />
) : (
<LedgerImageError />
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,10 @@ export const LedgerScreenContainerStyled = styled.div`
margin: 0 auto;
padding: 12px 12px 40px;
`;

export const LedgerScreenLoadingContainer = styled.div`
display: flex;
align-items: center;
justify-content: center;
gap: 10px;
`;
7 changes: 7 additions & 0 deletions packages/reef-knot/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# reef-knot

## 7.0.1

### Patch Changes

- Updated dependencies
- @reef-knot/connect-wallet-modal@7.0.1

## 7.0.0

### Major Changes
Expand Down
Loading

0 comments on commit f3d431f

Please sign in to comment.