Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Revamp connect wallet flow and design #498

Merged
merged 55 commits into from
Jul 24, 2024
Merged
Show file tree
Hide file tree
Changes from 47 commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
3926880
rename 'Connect to Wallet' to 'Connect Wallet'
Sharqiewicz Jun 10, 2024
46fe5a4
replace deprecated hero icon
Sharqiewicz Jun 10, 2024
29612bd
Divide WalletConnect button to separate components
Sharqiewicz Jun 10, 2024
9f1ff79
change prettier and tailwindcss config filenames
Sharqiewicz Jun 14, 2024
e713e5c
add prettier plugin for tailwindcss
Sharqiewicz Jun 14, 2024
b8387a8
restructure ConnectWallet file structure
Sharqiewicz Jun 14, 2024
4f23be3
implement ConnectWalletModal
Sharqiewicz Jun 14, 2024
1eea1a8
implement SearchInput
Sharqiewicz Jun 14, 2024
b0c2cf3
implement AccountCard component
Sharqiewicz Jun 14, 2024
6853c55
remove unnecessary style overrides
Sharqiewicz Jun 14, 2024
26cb523
move wallets (not included in talismn) to wallets directory
Sharqiewicz Jun 14, 2024
705cd56
remove unused code
Sharqiewicz Jun 14, 2024
59eeb6a
show proper chain icon for wallet account
Sharqiewicz Jun 14, 2024
81e17a3
create useConnectWallet hook
Sharqiewicz Jun 17, 2024
bbe2d31
Add ConnectModalContentLoading
Sharqiewicz Jun 17, 2024
2355865
add toast when user tries to connect a wallet that is already pending
Sharqiewicz Jun 17, 2024
9f4a554
Add warning to the showToast
Sharqiewicz Jun 17, 2024
332b005
restructure ConnectModal
Sharqiewicz Jun 17, 2024
ce10bc6
extract toast communicate to another ToastMessage
Sharqiewicz Jun 17, 2024
39173a7
add WalletConnect and error handling
Sharqiewicz Jun 18, 2024
731036a
add metamask wallet connect
Sharqiewicz Jun 18, 2024
6c47f56
adjust metamask snap connection
Sharqiewicz Jun 25, 2024
b64d653
unify wallet connection logic
Sharqiewicz Jun 27, 2024
5604930
fix connect metamask logic
Sharqiewicz Jun 27, 2024
53b5d3c
unify selectWallet logic
Sharqiewicz Jun 27, 2024
b1e8fb3
improve metamask account creation
Sharqiewicz Jun 27, 2024
ec494b2
refactor metamask snap connection logic
Sharqiewicz Jun 27, 2024
03d1585
fix globalstate metamask connection, add metamask account disclaimer
Sharqiewicz Jun 27, 2024
b22456c
remove unnecessary text
Sharqiewicz Jun 27, 2024
b2a57d4
fix GET TOKEN on mobile
Sharqiewicz Jun 27, 2024
feb2309
improve responsive design of buttons
Sharqiewicz Jul 1, 2024
242a57e
hide accounts if wallet is not selected, remove required from Dialog …
Sharqiewicz Jul 1, 2024
8b75a7f
refactor GlobalStateProvider, implement talisman wallets localstorage
Sharqiewicz Jul 2, 2024
ee2fa7d
always show talisman, subwallet, polkadotjs wallets
Sharqiewicz Jul 2, 2024
38a184e
redirect to wallet install page if it not installed
Sharqiewicz Jul 2, 2024
9dc483d
refactor AccountCard component
Sharqiewicz Jul 2, 2024
8b48b6a
refactor Nova Wallet connection
Sharqiewicz Jul 2, 2024
a7a2e9c
use preact/compat instead of React
Sharqiewicz Jul 2, 2024
11f42c6
show Nova only on mobile
Sharqiewicz Jul 2, 2024
204af2b
hide ChainSelector name on mobile
Sharqiewicz Jul 2, 2024
dca8605
hide jumping arrow tooltip on mobile
Sharqiewicz Jul 2, 2024
2a60174
fix ChainSelector on mobile
Sharqiewicz Jul 2, 2024
65e1998
add space in Connect Wallet button
Sharqiewicz Jul 2, 2024
2047fcd
add max width for mobile wallet account name
Sharqiewicz Jul 2, 2024
e4e8788
Merge branch 'main' into 28-revamp-connect-wallet-flow-and-design
Sharqiewicz Jul 11, 2024
8f93499
move Dialog into components directory
Sharqiewicz Jul 11, 2024
7e3a73b
Merge branch 'main' into 28-revamp-connect-wallet-flow-and-design
Sharqiewicz Jul 16, 2024
3738534
improve ConnectModalDialog design
Sharqiewicz Jul 23, 2024
2e1bfa1
fix infinite rerender loop
Sharqiewicz Jul 23, 2024
f8d8284
improve mobile ui
Sharqiewicz Jul 23, 2024
e315626
aside easier to be closed on mobile
Sharqiewicz Jul 23, 2024
edc5add
Merge branch 'main' into 28-revamp-connect-wallet-flow-and-design
Sharqiewicz Jul 23, 2024
54ea329
display account name in accounts list
Sharqiewicz Jul 24, 2024
cb66ea0
Merge branch 'main' into 28-revamp-connect-wallet-flow-and-design
Sharqiewicz Jul 24, 2024
b1fc776
Merge branch 'main' into 28-revamp-connect-wallet-flow-and-design
Sharqiewicz Jul 24, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"arrowParens": "always",
"printWidth": 120,
"tabWidth": 2,
"useTabs": false,
"semi": true,
"singleQuote": true,
"trailingComma": "all",
"bracketSpacing": true,
"proseWrap": "always",
"plugins": ["prettier-plugin-tailwindcss"],
"tailwindConfig": "./tailwind.config.js"
}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@
"postcss": "^8.4.21",
"postcss-import": "^15.1.0",
"prettier": "^3.1.1",
"prettier-plugin-tailwindcss": "^0.6.4",
"react-error-overlay": "6.0.9",
"react-table": "^7.8.0",
"sass": "^1.58.3",
Expand Down
11 changes: 0 additions & 11 deletions prettier.config.cjs

This file was deleted.

52 changes: 52 additions & 0 deletions src/GlobalStateProvider/helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { getWalletBySource, WalletAccount } from '@talismn/connect-wallets';
import { getSdkError } from '@walletconnect/utils';
import { storageService } from '../services/storage/local';
import { walletConnectService } from '../services/walletConnect';
import { initiateMetamaskInjectedAccount } from '../services/metamask';
import { chainIds } from '../config/walletConnect';
import { TenantName } from '../models/Tenant';
import { LocalStorageKeys } from '../hooks/useLocalStorage';

const initTalisman = async (dAppName: string, selected?: string) => {
const name = storageService.get(LocalStorageKeys.SELECTED_WALLET_NAME);
if (!name?.length) return;
const wallet = getWalletBySource(name);
if (!wallet) return;
await wallet.enable(dAppName);
const accounts = await wallet.getAccounts();
const selectedWallet = accounts.find((a) => a.address === selected) || accounts[0];
return selectedWallet;
};

const initWalletConnect = async (chainId: string) => {
const provider = await walletConnectService.getProvider();
if (!provider?.session) return;
return await walletConnectService.init(provider?.session, chainId);
};

const initMetamask = async (tenantName: TenantName) => {
const metamaskWalletAddress = storageService.get(LocalStorageKeys.SELECTED_WALLET_NAME);
if (metamaskWalletAddress) {
const injectedAccounts = await initiateMetamaskInjectedAccount(tenantName);
return injectedAccounts[0];
}
};

export const initSelectedWallet = async (dAppName: TenantName, tenantName: TenantName, storageAddress: string) => {
const appName = dAppName || TenantName.Amplitude;
return (await initTalisman(appName, storageAddress)) ||
(await initWalletConnect(chainIds[tenantName])) ||
(await initMetamask(tenantName));
}

export const handleWalletConnectDisconnect = async (walletAccount: WalletAccount | undefined) => {
if (walletAccount?.wallet?.extensionName === 'WalletConnect') {
const topic = walletConnectService.session?.topic;
if (topic) {
await walletConnectService.provider?.client.disconnect({
topic,
reason: getSdkError('USER_DISCONNECTED'),
});
}
}
}
83 changes: 20 additions & 63 deletions src/GlobalStateProvider.tsx → src/GlobalStateProvider/index.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
import { getWalletBySource, WalletAccount } from '@talismn/connect-wallets';
import { getSdkError } from '@walletconnect/utils';
import { ComponentChildren, createContext } from 'preact';
import { useCallback, useContext, useEffect, useMemo, useRef, useState } from 'preact/compat';
import { useLocation } from 'react-router-dom';
import { config } from './config';
import { chainIds } from './config/walletConnect';
import { storageKeys } from './constants/localStorage';
import { useLocalStorage } from './hooks/useLocalStorage';
import { TenantName } from './models/Tenant';
import { ThemeName } from './models/Theme';
import { initiateMetamaskInjectedAccount, WALLET_SOURCE_METAMASK } from './services/metamask/metamask';
import { storageService } from './services/storage/local';
import { walletConnectService } from './services/walletConnect';
import { WalletAccount } from '@talismn/connect-wallets';
import { config } from '../config';
import { storageKeys } from '../constants/localStorage';
import { LocalStorageKeys, useLocalStorage } from '../hooks/useLocalStorage';
import { TenantName } from '../models/Tenant';
import { ThemeName } from '../models/Theme';
import { storageService } from '../services/storage/local';
import { handleWalletConnectDisconnect, initSelectedWallet } from './helpers';

const SECONDS_IN_A_DAY = 86400;
const EXPIRATION_PERIOD = 2 * SECONDS_IN_A_DAY; // 2 days
Expand All @@ -29,31 +26,6 @@ export interface GlobalState {
export const defaultTenant = TenantName.Pendulum;
const GlobalStateContext = createContext<GlobalState | undefined>(undefined);

const initTalisman = async (dAppName: string, selected?: string) => {
const name = storageService.get('@talisman-connect/selected-wallet-name');
if (!name?.length) return;
const wallet = getWalletBySource(name);
if (!wallet) return;
await wallet.enable(dAppName);
const accounts = await wallet.getAccounts();
const selectedWallet = accounts.find((a) => a.address === selected) || accounts[0];
return selectedWallet;
};
const initWalletConnect = async (chainId: string) => {
const provider = await walletConnectService.getProvider();
//const pairings = provider.client.pairing.getAll({ active: true });
if (!provider?.session) return;
return await walletConnectService.init(provider?.session, chainId);
};

const initMetamaskWallet = async (tenantName: TenantName) => {
const metamaskWalletAddress = storageService.get(`metamask-snap-account`);
if (metamaskWalletAddress) {
return await initiateMetamaskInjectedAccount(tenantName);
}
return;
};

const GlobalStateProvider = ({ children }: { children: ComponentChildren }) => {
const tenantRef = useRef<string>();
const [walletAccount, setWallet] = useState<WalletAccount | undefined>(undefined);
Expand All @@ -71,6 +43,7 @@ const GlobalStateProvider = ({ children }: { children: ComponentChildren }) => {
[tenantName],
);

// Get currently selected wallet account from local storage
const {
state: storageAddress,
set,
Expand All @@ -80,34 +53,22 @@ const GlobalStateProvider = ({ children }: { children: ComponentChildren }) => {
expire: EXPIRATION_PERIOD,
});

const handleWalletConnectDisconnect = useCallback(async () => {
if (walletAccount?.wallet?.extensionName === 'WalletConnect') {
const topic = walletConnectService.session?.topic;
if (topic) {
await walletConnectService.provider?.client.disconnect({
topic,
reason: getSdkError('USER_DISCONNECTED'),
});
}
}
}, [walletAccount]);

const clearLocalStorageWallets = () => {
storageService.remove(LocalStorageKeys.SELECTED_WALLET_NAME);
}

const removeWalletAccount = useCallback(async () => {
await handleWalletConnectDisconnect();
await handleWalletConnectDisconnect(walletAccount);
clear();
// remove talisman
storageService.remove('@talisman-connect/selected-wallet-name');
storageService.remove(`metamask-snap-account`);
clearLocalStorageWallets()
setWallet(undefined);
}, [clear, handleWalletConnectDisconnect]);
}, [clear, walletAccount]);

const setWalletAccount = useCallback(
(wallet: WalletAccount | undefined) => {
set(wallet?.address);
setWallet(wallet);
if (wallet?.source === WALLET_SOURCE_METAMASK) {
storageService.set(`metamask-snap-account`, wallet.address);
}
(newWalletAccount: WalletAccount | undefined) => {
set(newWalletAccount?.address);
setWallet(newWalletAccount);
},
[set],
);
Expand All @@ -122,11 +83,7 @@ const GlobalStateProvider = ({ children }: { children: ComponentChildren }) => {
// skip if tenant already initialized
if (tenantRef.current === tenantName || accountAddress) return;
tenantRef.current = tenantName;
const appName = dAppName || TenantName.Amplitude;
const selectedWallet =
(await initTalisman(appName, storageAddress)) ||
(await initWalletConnect(chainIds[tenantName])) ||
(await initMetamaskWallet(tenantName));
const selectedWallet = await initSelectedWallet(dAppName, tenantName, storageAddress)
if (selectedWallet) setWallet(selectedWallet);
};
run();
Expand Down
4 changes: 2 additions & 2 deletions src/TermsAndConditions.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { PropsWithChildren, useState } from 'preact/compat';
import { Button, Checkbox, Link, Modal } from 'react-daisyui';
import { useLocalStorage } from './hooks/useLocalStorage';
import { useLocalStorage, LocalStorageKeys } from './hooks/useLocalStorage';

const TermsAndConditions = (_props: PropsWithChildren) => {
const { state, set } = useLocalStorage<string | undefined>({ key: 'termsAndConditions' });
const { state, set } = useLocalStorage<string | undefined>({ key: LocalStorageKeys.TERMS_AND_CONDITIONS });
const [checked, setChecked] = useState<boolean>(false);

const acceptTerms = () => {
Expand Down
2 changes: 1 addition & 1 deletion src/assets/CloseIcon.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { FC } from 'preact/compat';
import { FC } from "preact/compat";

interface Props {
className?: string;
Expand Down
2 changes: 1 addition & 1 deletion src/assets/spacewalk.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { HTMLAttributes } from 'preact/compat';
const SpacewalkIcon = (props: HTMLAttributes<SVGSVGElement>) => (
<svg width="32" height="32" {...props} viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="m16,5.03c-6.06,0-10.97,4.91-10.97,10.97s4.91,10.97,10.97,10.97,10.97-4.91,10.97-10.97-4.91-10.97-10.97-10.97Zm0,19.8c-4.87,0-8.83-3.96-8.83-8.83s3.96-8.83,8.83-8.83,8.83,3.96,8.83,8.83-3.96,8.83-8.83,8.83Z" />
<path d="m16.99,9.37c-2.31,0-4.52,1.75-5.83,4.42-1.7,3.46-1.2,7.17,1.74,8.59.68.33,1.38.48,2.06.48,2.31,0,4.51-1.76,5.82-4.43,1.7-3.46,1.39-7.08-1.74-8.59-.68-.33-1.36-.48-2.04-.48h0Zm0,1.76c.42,0,.83.1,1.23.3,1.07.52,1.56,1.42,1.67,2.45l-4.59-2.23c.55-.33,1.12-.51,1.68-.51h0Zm-4.87,5.59c.08-.5.22-1.01.42-1.53l6.23,3.03c-.28.48-.6.91-.94,1.28l-5.71-2.78Zm1.04-2.81c.28-.48.6-.91.94-1.28l5.73,2.79c-.09.51-.25,1.03-.44,1.52l-6.23-3.03Zm.54,6.8c-.87-.42-1.4-1.17-1.59-2.22-.01-.07-.02-.14-.03-.2l4.54,2.21c-.55.33-1.12.51-1.67.51-.42,0-.84-.1-1.25-.3Z" />{' '}
<path d="m16.99,9.37c-2.31,0-4.52,1.75-5.83,4.42-1.7,3.46-1.2,7.17,1.74,8.59.68.33,1.38.48,2.06.48,2.31,0,4.51-1.76,5.82-4.43,1.7-3.46,1.39-7.08-1.74-8.59-.68-.33-1.36-.48-2.04-.48h0Zm0,1.76c.42,0,.83.1,1.23.3,1.07.52,1.56,1.42,1.67,2.45l-4.59-2.23c.55-.33,1.12-.51,1.68-.51h0Zm-4.87,5.59c.08-.5.22-1.01.42-1.53l6.23,3.03c-.28.48-.6.91-.94,1.28l-5.71-2.78Zm1.04-2.81c.28-.48.6-.91.94-1.28l5.73,2.79c-.09.51-.25,1.03-.44,1.52l-6.23-3.03Zm.54,6.8c-.87-.42-1.4-1.17-1.59-2.22-.01-.07-.02-.14-.03-.2l4.54,2.21c-.55.33-1.12.51-1.67.51-.42,0-.84-.1-1.25-.3Z" />
</svg>
);

Expand Down
25 changes: 25 additions & 0 deletions src/components/AccountCard/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { WalletAccount } from '@talismn/connect-wallets';
import { trimAddress } from '../../helpers/addressFormatter';
import { useGlobalState } from '../../GlobalStateProvider';
import ChainLogo from '../../assets/ChainLogo';

interface AccountProps {
account: WalletAccount;
}

export const AccountCard = ({ account }: AccountProps) => {
const { setWalletAccount } = useGlobalState();

return (
<li className="w-full">
<button
aria-label={`Select ${account.address}`}
className="flex w-full cursor-pointer items-center rounded border-l-2 border-transparent p-1.5 hover:border-primary hover:bg-base-100"
onClick={() => setWalletAccount(account)}
>
<ChainLogo className="w-8 h-8" />
<p className="ml-2.5">{trimAddress(account.address)}</p>
</button>
</li>
);
};
38 changes: 19 additions & 19 deletions src/components/ChainSelector.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ChevronDownIcon } from '@heroicons/react/20/solid';
import { Button, Dropdown } from 'react-daisyui';
import { isDesktop } from 'react-device-detect';
import AmplitudeLogo from '../assets/AmplitudeLogo';
import PendulumLogo from '../assets/PendulumLogo';
import { toTitle } from '../helpers/string';
Expand All @@ -11,34 +11,34 @@ const options = [TenantName.Pendulum, TenantName.Amplitude, TenantName.Foucoco];
const ChainSelector = (): JSX.Element => {
const { switchChain, currentTenant } = useSwitchChain();
return (
<Dropdown vertical="bottom" end className="w-30">
<Button
size="sm"
color="ghost"
className="text-sm border-base-300 bg-base-200 min-h-[2.1rem] h-auto px-2 sm:px-3"
<details className="w-30 dropdown">
<summary
className="text-sm border-base-300 bg-base-200 min-h-[2.1rem] h-auto px-2 sm:px-3 btn btn-sm btn-ghost"
title={currentTenant}
>
{currentTenant === TenantName.Pendulum ? (
<PendulumLogo className="w-4 h-5 mr-1" />
) : (
<AmplitudeLogo className="w-4 h-4 mr-1 " />
)}
<span className="text-sm mr-1 sm:mr-2">{currentTenant ? toTitle(currentTenant) : ''}</span>
{isDesktop ? <span className="mr-1 text-sm sm:mr-2">{currentTenant ? toTitle(currentTenant) : ''}</span> : <></>}
<ChevronDownIcon className="w-4 h-4" stroke-width="2" />
</Button>
<Dropdown.Menu className="w-30 mt-1.5 p-1 text-sm border-base-300 border bg-base-200 rounded-xl shadow-none">
</summary>
<ul className="text-sm border-base-300 border bg-base-200 rounded-xl shadow-none menu dropdown-content z-[1]">
{options.map((option, i) => (
<Dropdown.Item key={i} onClick={() => switchChain(option)}>
{option === TenantName.Pendulum ? (
<PendulumLogo light={currentTenant !== TenantName.Pendulum} className="w-5 h-6 mr-1" />
) : (
<AmplitudeLogo className="w-5 h-5 mr-1" />
)}
<span className="text-sm mr-3">{toTitle(option)}</span>
</Dropdown.Item>
<li key={i} onClick={() => switchChain(option)}>
<a>
{option === TenantName.Pendulum ? (
<PendulumLogo light={currentTenant !== TenantName.Pendulum} className="w-5 h-6 mr-1" />
) : (
<AmplitudeLogo className="w-5 h-5 mr-1" />
)}
<span className="mr-3 text-sm">{toTitle(option)}</span>
</a>
</li>
))}
</Dropdown.Menu>
</Dropdown>
</ul>
</details>
);
};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import { Modal } from 'react-daisyui';
import { FC, createPortal, useCallback, useEffect, useRef, useState } from 'preact/compat';

import { CloseButton } from '../../../components/CloseButton';
import { CloseButton } from '../CloseButton';

interface DialogProps {
visible: boolean;
onClose: () => void;
headerText?: string;
content: JSX.Element;
actions: JSX.Element;
actions?: JSX.Element;
form?: {
onSubmit: (event?: Event) => void | Promise<void>;
className?: string;
Expand Down
15 changes: 12 additions & 3 deletions src/components/GetToken/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { useAccountBalance } from '../../shared/useAccountBalance';
import useSwitchChain from '../../hooks/useSwitchChain';
import { useNodeInfoState } from '../../NodeInfoProvider';
import { TenantName } from '../../models/Tenant';
import { isDesktop } from 'react-device-detect';

const tenantColors = {
[TenantName.Pendulum]: {
Expand Down Expand Up @@ -78,20 +79,28 @@ export const GetToken = () => {

const isBalanceZero = Number(total) === 0;

const showCurrentToken = getTokenIcon(currentTenant)
return (
<section className="flex items-center">
{isBalanceZero && (
{(isBalanceZero && isDesktop) ? (
<>
<InsufficientFundsTooltip tenantName={currentTenant} />
<JumpingArrow tenantName={currentTenant} />
</>
)}
) : <></>
}

{tokenSymbol ? (
<NavLink to={link}>
<Button size="sm" className={`text-sm px-2 sm:px-3 ${getTenantColors(currentTenant).button}`} type="button">
{getTokenIcon(currentTenant)}
<div className="hidden md:flex">
{showCurrentToken}
<p className="text-neutral">GET {tokenSymbol}</p>
</div>
<div className="flex md:hidden">
<p className="mr-2 text-neutral">GET</p>
{showCurrentToken}
</div>
</Button>
</NavLink>
) : (
Expand Down
Loading
Loading