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

feat(bridge-ui-v2): switch chain on wrong network #14511

Merged
merged 5 commits into from
Aug 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
<script lang="ts">
import { type Chain, type GetNetworkResult, switchNetwork } from '@wagmi/core';
import { type ComponentType, onDestroy } from 'svelte';
import { onDestroy } from 'svelte';
import { t } from 'svelte-i18n';
import { UserRejectedRequestError } from 'viem';

import { EthIcon, Icon, TaikoIcon } from '$components/Icon';
import { Icon } from '$components/Icon';
import { LoadingMask } from '$components/LoadingMask';
import { warningToast } from '$components/NotificationToast';
import { PUBLIC_L1_CHAIN_ID, PUBLIC_L2_CHAIN_ID } from '$env/static/public';
import { chains } from '$libs/chain';
import { chainToIconMap } from '$libs/util/chainToIconMap';
import { classNames } from '$libs/util/classNames';
import { uid } from '$libs/util/uid';
import { account } from '$stores/account';
Expand All @@ -29,11 +29,6 @@
'flex justify-start content-center body-bold py-2 px-[20px]',
);

let chainToIconMap: Record<string, ComponentType> = {
[PUBLIC_L1_CHAIN_ID]: EthIcon,
[PUBLIC_L2_CHAIN_ID]: TaikoIcon,
};

let switchingNetwork = false;
let buttonId = `button-${uid()}`;
let dialogId = `dialog-${uid()}`;
Expand Down Expand Up @@ -143,7 +138,7 @@
tabindex="0"
class="p-4 rounded-[10px]"
class:opacity-20={disabled}
class:hover:bg-grey-10={!disabled}
class:hover:bg-primary-content={!disabled}
class:hover:cursor-pointer={!disabled}
aria-disabled={disabled}
on:click={() => selectChain(chain)}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<script lang="ts">
import { type Chain, switchNetwork } from '@wagmi/core';
import { t } from 'svelte-i18n';
import { UserRejectedRequestError } from 'viem';

import { LoadingMask } from '$components/LoadingMask';
import { warningToast } from '$components/NotificationToast';
import { chains } from '$libs/chain';
import { chainToIconMap } from '$libs/util/chainToIconMap';
import { switchChainModal } from '$stores/modal';

let switchingNetwork = false;

function closeModal() {
$switchChainModal = false;
}

async function selectChain(chain: Chain) {
// We want to switch the wallet to the selected network.
// This will trigger the network switch in the UI also
switchingNetwork = true;

try {
await switchNetwork({ chainId: chain.id });
closeModal();
} catch (err) {
console.error(err);

if (err instanceof UserRejectedRequestError) {
warningToast($t('messages.network.rejected'));
}
} finally {
switchingNetwork = false;
}
}

function getChainKeydownHandler(chain: Chain) {
return (event: KeyboardEvent) => {
if (event.key === 'Enter') {
selectChain(chain);
}
};
}
</script>

<dialog class="modal modal-bottom md:modal-middle" class:modal-open={$switchChainModal}>
<div class="modal-box relative px-6 py-[35px] md:py-[20px] bg-primary-base-background text-primary-base-content">
{#if switchingNetwork}
<LoadingMask
class="bg-grey-0/60"
spinnerClass="border-primary-base-content"
text={$t('messages.network.switching')} />
{/if}

<h3 class="title-body-bold mb-[20px]">{$t('switch_modal.title')}</h3>
<p class="body-regular">{$t('switch_modal.description')}</p>
<ul role="menu" class="space-y-4">
{#each chains as chain (chain.id)}
<li
role="menuitem"
tabindex="0"
class="p-4 rounded-[10px] hover:bg-primary-content hover:cursor-pointer"
on:click={() => selectChain(chain)}
on:keydown={getChainKeydownHandler(chain)}>
<!-- TODO: agree on hover:bg color -->
<div class="f-row justify-between">
<div class="f-items-center space-x-4">
<i role="img" aria-label={chain.name}>
<svelte:component this={chainToIconMap[chain.id]} size={32} />
</i>
<span class="body-bold">{chain.name}</span>
</div>
<span class="body-regular">{chain.network}</span>
</div>
</li>
{/each}
</ul>
</div>
</dialog>
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as SwitchChainModal } from './SwitchChainModal.svelte';
4 changes: 4 additions & 0 deletions packages/bridge-ui-v2/src/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,10 @@
"connecting": "Connecting"
}
},
"switch_modal": {
"title": "Not on the right network",
"description": "Your current network is not supported. Please select one:"
},
"messages": {
"account": {
"required": "Please connect your wallet.",
Expand Down
10 changes: 10 additions & 0 deletions packages/bridge-ui-v2/src/libs/util/chainToIconMap.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import type { ComponentType } from 'svelte';

import { EthIcon, TaikoIcon } from '$components/Icon';
import { PUBLIC_L1_CHAIN_ID, PUBLIC_L2_CHAIN_ID } from '$env/static/public';

export const chainToIconMap: Record<string, ComponentType> = {
[PUBLIC_L1_CHAIN_ID]: EthIcon,
[PUBLIC_L2_CHAIN_ID]: TaikoIcon,
// TODO: L3
};
14 changes: 13 additions & 1 deletion packages/bridge-ui-v2/src/libs/wagmi/watcher.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { watchAccount, watchNetwork /*, watchPublicClient, watchWalletClient*/ } from '@wagmi/core';

import { isSupportedChain } from '$libs/chain';
import { getLogger } from '$libs/util/logger';
import { account } from '$stores/account';
import { switchChainModal } from '$stores/modal';
import { network } from '$stores/network';

const log = getLogger('wagmi:watcher');
Expand All @@ -17,9 +19,19 @@ export function startWatching() {
unWatchNetwork = watchNetwork((data) => {
log('Network changed', data);

const { chain } = data;

// We need to check if the chain is supported, and if not
// we present the user with a modal to switch networks.
if (chain && !isSupportedChain(BigInt(chain.id))) {
log('Unsupported chain', chain);
switchChainModal.set(true);
return;
}

// When we switch networks, we are actually selecting
// the source chain.
network.set(data.chain);
network.set(chain);
});

// Action for subscribing to account changes.
Expand Down
3 changes: 3 additions & 0 deletions packages/bridge-ui-v2/src/routes/+layout.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import { Header } from '$components/Header';
import { NotificationToast } from '$components/NotificationToast';
import { SideNavigation } from '$components/SideNavigation';
import SwitchChainModal from '$components/SwitchChainModal/SwitchChainModal.svelte';
import { startWatching, stopWatching } from '$libs/wagmi';

onMount(startWatching);
Expand All @@ -30,3 +31,5 @@
<NotificationToast />

<AccountConnectionToast />

<SwitchChainModal />
5 changes: 5 additions & 0 deletions packages/bridge-ui-v2/src/stores/modal.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { writable } from 'svelte/store';

// We make this global because we need to be able to
// open and close the modal from anywhere in the app
export const switchChainModal = writable<boolean>(false);