Skip to content

Commit

Permalink
feat(bridge-ui-v2): switch chain on wrong network (#14511)
Browse files Browse the repository at this point in the history
  • Loading branch information
jscriptcoder authored Aug 16, 2023
1 parent 3407f82 commit 89b8a86
Show file tree
Hide file tree
Showing 8 changed files with 119 additions and 10 deletions.
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);

1 comment on commit 89b8a86

@vercel
Copy link

@vercel vercel bot commented on 89b8a86 Aug 16, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

bridge-ui-v2 – ./packages/bridge-ui-v2

taiko-mono-bridge-ui-v2.vercel.app
bridge-ui-v2-git-main-taikoxyz.vercel.app
bridge-ui-v2-taikoxyz.vercel.app

Please sign in to comment.