Skip to content

Commit

Permalink
feat: add useWallets hook
Browse files Browse the repository at this point in the history
  • Loading branch information
chybisov committed Aug 6, 2024
1 parent 8d15a34 commit 5e300f7
Show file tree
Hide file tree
Showing 5 changed files with 171 additions and 151 deletions.
147 changes: 147 additions & 0 deletions packages/widget/src/hooks/useWallets.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
import { ChainType } from '@lifi/sdk';
import type { CreateConnectorFnExtended } from '@lifi/wallet-management';
import {
createCoinbaseConnector,
createMetaMaskConnector,
createWalletConnectConnector,
getWalletPriority,
isWalletInstalled,
} from '@lifi/wallet-management';
import type { Theme } from '@mui/material';
import { useMediaQuery } from '@mui/material';
import { WalletReadyState } from '@solana/wallet-adapter-base';
import type { Wallet } from '@solana/wallet-adapter-react';
import { useWallet } from '@solana/wallet-adapter-react';
import { useMemo } from 'react';
import type { Connector } from 'wagmi';
import { useConnect, useAccount as useWagmiAccount } from 'wagmi';
import { defaultCoinbaseConfig } from '../config/coinbase.js';
import { defaultMetaMaskConfig } from '../config/metaMask.js';
import { defaultWalletConnectConfig } from '../config/walletConnect.js';
import type { WidgetChains, WidgetWalletConfig } from '../types/widget.js';
import { isItemAllowed } from '../utils/item.js';

export const useWallets = (
walletConfig?: WidgetWalletConfig,
chains?: WidgetChains,
) => {
const account = useWagmiAccount();
const { connectors } = useConnect();
const { wallets: solanaWallets } = useWallet();

const isDesktopView = useMediaQuery((theme: Theme) =>
theme.breakpoints.up('sm'),
);

const wallets = useMemo(() => {
const evmConnectors: (CreateConnectorFnExtended | Connector)[] =
Array.from(connectors);
if (
!connectors.some((connector) =>
connector.id.toLowerCase().includes('walletconnect'),
)
) {
evmConnectors.unshift(
createWalletConnectConnector(
walletConfig?.walletConnect ?? defaultWalletConnectConfig,
),
);
}
if (
!connectors.some((connector) =>
connector.id.toLowerCase().includes('coinbase'),
) &&
!isWalletInstalled('coinbase')
) {
evmConnectors.unshift(
createCoinbaseConnector(
walletConfig?.coinbase ?? defaultCoinbaseConfig,
),
);
}
if (
!connectors.some((connector) =>
connector.id.toLowerCase().includes('metamask'),
) &&
!isWalletInstalled('metaMask')
) {
evmConnectors.unshift(
createMetaMaskConnector(
walletConfig?.metaMask ?? defaultMetaMaskConfig,
),
);
}
const evmInstalled = isItemAllowed(ChainType.EVM, chains?.types)
? evmConnectors.filter(
(connector) =>
isWalletInstalled(connector.id!) &&
// We should not show already connected connectors
account.connector?.id !== connector.id,
)
: [];
const evmNotDetected = isItemAllowed(ChainType.EVM, chains?.types)
? evmConnectors.filter((connector) => !isWalletInstalled(connector.id!))
: [];
const svmInstalled = isItemAllowed(ChainType.SVM, chains?.types)
? solanaWallets?.filter(
(connector) =>
connector.adapter.readyState === WalletReadyState.Installed &&
// We should not show already connected connectors
!connector.adapter.connected,
)
: [];
const svmNotDetected = isItemAllowed(ChainType.SVM, chains?.types)
? solanaWallets?.filter(
(connector) =>
connector.adapter.readyState !== WalletReadyState.Installed,
)
: [];

const installedWallets = [...evmInstalled, ...svmInstalled].sort(
walletComparator,
);

if (isDesktopView) {
const notDetectedWallets = [...evmNotDetected, ...svmNotDetected].sort(
walletComparator,
);
installedWallets.push(...notDetectedWallets);
}

return installedWallets;
}, [
account.connector?.id,
chains?.types,
connectors,
isDesktopView,
solanaWallets,
walletConfig?.coinbase,
walletConfig?.metaMask,
walletConfig?.walletConnect,
]);

return wallets;
};

export const walletComparator = (
a: CreateConnectorFnExtended | Connector | Wallet,
b: CreateConnectorFnExtended | Connector | Wallet,
) => {
let aId = (a as Connector).id || (a as Wallet).adapter?.name;
let bId = (b as Connector).id || (b as Wallet).adapter?.name;

const priorityA = getWalletPriority(aId);
const priorityB = getWalletPriority(bId);

if (priorityA !== priorityB) {
return priorityA - priorityB;
}

if (aId < bId) {
return -1;
}
if (aId > bId) {
return 1;
}
return 0;
};
1 change: 1 addition & 0 deletions packages/widget/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export * from './components/Skeleton/WidgetSkeleton.js';
export * from './config/version.js';
export { useAccount } from './hooks/useAccount.js';
export { useAvailableChains } from './hooks/useAvailableChains.js';
export { useWallets, walletComparator } from './hooks/useWallets.js';
export { useWidgetEvents, widgetEvents } from './hooks/useWidgetEvents.js';
export * from './stores/form/types.js';
export { useFieldActions } from './stores/form/useFieldActions.js';
Expand Down
115 changes: 4 additions & 111 deletions packages/widget/src/pages/SelectWalletPage/SelectWalletPage.tsx
Original file line number Diff line number Diff line change
@@ -1,56 +1,34 @@
import { ChainType } from '@lifi/sdk';
import type { CreateConnectorFnExtended } from '@lifi/wallet-management';
import {
createCoinbaseConnector,
createMetaMaskConnector,
createWalletConnectConnector,
isWalletInstalled,
} from '@lifi/wallet-management';
import type { Theme } from '@mui/material';
import {
Button,
DialogActions,
DialogContent,
DialogContentText,
List,
useMediaQuery,
} from '@mui/material';
import { WalletReadyState } from '@solana/wallet-adapter-base';
import type { Wallet } from '@solana/wallet-adapter-react';
import { useWallet } from '@solana/wallet-adapter-react';
import { useCallback, useMemo, useState } from 'react';
import { useCallback, useState } from 'react';
import { useTranslation } from 'react-i18next';
import type { Connector } from 'wagmi';
import { useConnect, useAccount as useWagmiAccount } from 'wagmi';
import { useAccount as useWagmiAccount } from 'wagmi';
import { Dialog } from '../../components/Dialog.js';
import { PageContainer } from '../../components/PageContainer.js';
import { defaultCoinbaseConfig } from '../../config/coinbase.js';
import { defaultMetaMaskConfig } from '../../config/metaMask.js';
import { defaultWalletConnectConfig } from '../../config/walletConnect.js';
import { useHeader } from '../../hooks/useHeader.js';
import { useWallets } from '../../hooks/useWallets.js';
import { useWidgetConfig } from '../../providers/WidgetProvider/WidgetProvider.js';
import { isItemAllowed } from '../../utils/item.js';
import { EVMListItemButton } from './EVMListItemButton.js';
import { SVMListItemButton } from './SVMListItemButton.js';
import { walletComparator } from './utils.js';

export const SelectWalletPage = () => {
const { t } = useTranslation();
const { chains, walletConfig } = useWidgetConfig();
const account = useWagmiAccount();
const { connectors } = useConnect();
const [walletIdentity, setWalletIdentity] = useState<{
show: boolean;
connector?: Connector;
}>({ show: false });
const { wallets: solanaWallets } = useWallet();

useHeader(t(`header.selectWallet`));

const isDesktopView = useMediaQuery((theme: Theme) =>
theme.breakpoints.up('sm'),
);

const closeDialog = () => {
setWalletIdentity((state) => ({
...state,
Expand All @@ -65,92 +43,7 @@ export const SelectWalletPage = () => {
});
}, []);

const wallets = useMemo(() => {
const evmConnectors: (CreateConnectorFnExtended | Connector)[] =
Array.from(connectors);
if (
!connectors.some((connector) =>
connector.id.toLowerCase().includes('walletconnect'),
)
) {
evmConnectors.unshift(
createWalletConnectConnector(
walletConfig?.walletConnect ?? defaultWalletConnectConfig,
),
);
}
if (
!connectors.some((connector) =>
connector.id.toLowerCase().includes('coinbase'),
) &&
!isWalletInstalled('coinbase')
) {
evmConnectors.unshift(
createCoinbaseConnector(
walletConfig?.coinbase ?? defaultCoinbaseConfig,
),
);
}
if (
!connectors.some((connector) =>
connector.id.toLowerCase().includes('metamask'),
) &&
!isWalletInstalled('metaMask')
) {
evmConnectors.unshift(
createMetaMaskConnector(
walletConfig?.metaMask ?? defaultMetaMaskConfig,
),
);
}
const evmInstalled = isItemAllowed(ChainType.EVM, chains?.types)
? evmConnectors.filter(
(connector) =>
isWalletInstalled(connector.id!) &&
// We should not show already connected connectors
account.connector?.id !== connector.id,
)
: [];
const evmNotDetected = isItemAllowed(ChainType.EVM, chains?.types)
? evmConnectors.filter((connector) => !isWalletInstalled(connector.id!))
: [];
const svmInstalled = isItemAllowed(ChainType.SVM, chains?.types)
? solanaWallets?.filter(
(connector) =>
connector.adapter.readyState === WalletReadyState.Installed &&
// We should not show already connected connectors
!connector.adapter.connected,
)
: [];
const svmNotDetected = isItemAllowed(ChainType.SVM, chains?.types)
? solanaWallets?.filter(
(connector) =>
connector.adapter.readyState !== WalletReadyState.Installed,
)
: [];

const installedWallets = [...evmInstalled, ...svmInstalled].sort(
walletComparator,
);

if (isDesktopView) {
const notDetectedWallets = [...evmNotDetected, ...svmNotDetected].sort(
walletComparator,
);
installedWallets.push(...notDetectedWallets);
}

return installedWallets;
}, [
account.connector?.id,
chains?.types,
connectors,
isDesktopView,
solanaWallets,
walletConfig?.coinbase,
walletConfig?.metaMask,
walletConfig?.walletConnect,
]);
const wallets = useWallets(walletConfig, chains);

return (
<PageContainer disableGutters>
Expand Down
27 changes: 0 additions & 27 deletions packages/widget/src/pages/SelectWalletPage/utils.ts

This file was deleted.

32 changes: 19 additions & 13 deletions packages/widget/src/types/widget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,22 @@ export interface AllowDeny<T> {
deny?: T[];
}

export type WidgetChains = {
from?: AllowDeny<number>;
to?: AllowDeny<number>;
types?: AllowDeny<ChainType>;
} & AllowDeny<number>;

export type WidgetTokens = {
featured?: StaticToken[];
include?: Token[];
popular?: StaticToken[];
} & AllowDeny<BaseToken>;

export type WidgetLanguages = {
default?: LanguageKey;
} & AllowDeny<LanguageKey>;

export interface WidgetConfig {
fromChain?: number;
toChain?: number;
Expand Down Expand Up @@ -179,19 +195,9 @@ export interface WidgetConfig {

bridges?: AllowDeny<string>;
exchanges?: AllowDeny<string>;
chains?: {
from?: AllowDeny<number>;
to?: AllowDeny<number>;
types?: AllowDeny<ChainType>;
} & AllowDeny<number>;
tokens?: {
featured?: StaticToken[];
include?: Token[];
popular?: StaticToken[];
} & AllowDeny<BaseToken>;
languages?: {
default?: LanguageKey;
} & AllowDeny<LanguageKey>;
chains?: WidgetChains;
tokens?: WidgetTokens;
languages?: WidgetLanguages;
languageResources?: LanguageResources;
}

Expand Down

0 comments on commit 5e300f7

Please sign in to comment.