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: add pre-signature dialog before connecting WalletConnect #454

Merged
merged 20 commits into from
Jan 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
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
7 changes: 7 additions & 0 deletions .changeset/honest-meals-sing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@fuel-connectors/walletconnect-connector": minor
"@fuels/connectors": minor
"@fuels/react": minor
---

Added a pre-signature dialog to the `WalletConnectConnector` to inform users about the signature purpose.
25 changes: 25 additions & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<!--
List the issues this PR closes in a bullet list format, e.g.:
- Closes #X
- Closes `FE-Z`
-->

# Summary
<!--
Please include a summary of your changes if something is not self-explanatory from the closed issues.
Many times this section is not needed as the closed issues themselves explains the reason of the PR exists. On that case just remove this section it.
-->

# Checklist

- [ ] I've added error handling for all actions/requests, and verified how this error will show on UI.
- [ ] I've reviewed all the copies changed/added in this PR (use AI if needs help)
- [ ] I've included the reference to the issues being closed (Github and/or Linear)
- [ ] I've changed the Docs to reflect my changes (project setup, run commands, etc…)
- [ ] I've put docs links where it may be helpful.
- [ ]
- [ ] I've added error handling for all actions/requests, and verified how it will show on UI (or verifying error handling was needed)
- [ ] I've reviewed all the copies changed/added in this PR, using AI if needed (or no copy changes were made)
- [ ] I've included the reference to the Github and/or Linear issues being closed (or no issues to reference)
- [ ] I've changed the Docs to reflect my changes (or no doc updates were needed)
- [ ] I've put docs links where it may be helpful (or no doc links were needed)
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ test.describe('WalletConnectConnector', () => {
// First-time connection requires a message signature (to prove ownership of the wallet)
const connect: ConnectorFunctions['connect'] = async (page) => {
await commonConnect(page);
await page.getByText('Sign', { exact: true }).click();
await metamask.confirmSignature();
};

Expand Down
7 changes: 1 addition & 6 deletions packages/react/src/providers/FuelUIProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -164,10 +164,6 @@ export function FuelUIProvider({
[handleStartConnection],
);

const setRoute = useCallback((state: Routes) => {
setDialogRoute(state);
}, []);

const isLoading = useMemo(() => {
const hasLoadedConnectors =
(fuelConfig.connectors || []).length > connectors.length;
Expand Down Expand Up @@ -224,7 +220,7 @@ export function FuelUIProvider({
// Dialog only
dialog: {
route: dialogRoute,
setRoute,
setRoute: setDialogRoute,
connector,
isOpen,
connect: handleSelectConnector,
Expand All @@ -248,7 +244,6 @@ export function FuelUIProvider({
isOpen,
handleCancel,
handleStartConnection,
setRoute,
handleSelectConnector,
handleConnect,
handleRetryConnect,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { Routes, useConnectUI } from '../../../../providers/FuelUIProvider';
import { ConnectorIcon } from '../Core/ConnectorIcon';

import { useEffect } from 'react';
import type { ConnectorEvent } from 'fuels';
import { useEffect, useMemo, useState } from 'react';
import { Spinner } from '../../../../icons/Spinner';
import { useFuel } from '../../../../providers/FuelHooksProvider';
import {
ConnectorButton,
ConnectorButtonPrimary,
Expand All @@ -17,7 +19,19 @@ type ConnectorProps = {
className?: string;
};

export interface CustomCurrentConnectorEvent extends ConnectorEvent {
metadata?: {
pendingSignature: boolean;
};
}

enum ConnectStep {
CONNECT = 'connect',
SIGN = 'sign',
}

export function Connecting({ className }: ConnectorProps) {
const { fuel } = useFuel();
const {
error,
isConnecting,
Expand All @@ -27,12 +41,51 @@ export function Connecting({ className }: ConnectorProps) {
isConnected,
} = useConnectUI();

const [connectStep, setConnectStep] = useState<ConnectStep>(
ConnectStep.CONNECT,
);

const { description, operation, cta } = useMemo(() => {
if (connectStep === ConnectStep.CONNECT) {
return {
description: `Click on the button below to connect to ${location.origin}.`,
operation: 'connection',
cta: 'Connect',
};
}

return {
description:
'Sign this message to prove you own this wallet and proceed. Canceling will disconnect you.',
operation: 'signature',
cta: 'Sign',
};
}, [connectStep]);

// Auto-close connecting
useEffect(() => {
if (isConnected && route === Routes.CONNECTING && !isConnecting) {
cancel();
}
}, [isConnected, route, isConnecting, cancel]);

// Switching to signing ownership mode
useEffect(() => {
const onCurrentConnectorChange = (e: CustomCurrentConnectorEvent) => {
if (e.metadata && 'pendingSignature' in e.metadata) {
setConnectStep(
e.metadata.pendingSignature ? ConnectStep.SIGN : ConnectStep.CONNECT,
);
}
};

fuel.on(fuel.events.currentConnector, onCurrentConnectorChange);

return () => {
fuel.off(fuel.events.currentConnector, onCurrentConnectorChange);
};
}, [fuel]);

if (!connector) return null;

return (
Expand All @@ -47,16 +100,15 @@ export function Connecting({ className }: ConnectorProps) {
</ConnectorImage>
<ConnectorContent>
<ConnectorTitle>{connector.name}</ConnectorTitle>
{error ? (
<ConnectorDescriptionError>{error.message}</ConnectorDescriptionError>
) : isConnecting ? (
{isConnecting ? (
<ConnectorDescription>
Requesting connection to <br /> {connector.name}.
Requesting {operation} to <br /> {connector.name}.
</ConnectorDescription>
) : (
<ConnectorDescription>
Click on the button below to connect to {location.origin}.
</ConnectorDescription>
<ConnectorDescription>{description}</ConnectorDescription>
)}
{error && (
<ConnectorDescriptionError>{error.message}</ConnectorDescriptionError>
)}
</ConnectorContent>
{isConnecting ? (
Expand All @@ -65,7 +117,7 @@ export function Connecting({ className }: ConnectorProps) {
</ConnectorButton>
) : (
<ConnectorButtonPrimary onClick={() => retryConnect(connector)}>
Connect
{cta}
</ConnectorButtonPrimary>
)}
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@ export const DialogContent = (props: Dialog.DialogContentProps) => {
style={dialogContentStyle}
{...props}
className="fuel-connectors-dialog-content"
// Workaround to prevent closing dialog when interacting with WalletConnect Modal
onPointerDownOutside={(e) => {
const walletConnectDialog = document.querySelector('w3m-modal');
if (walletConnectDialog?.classList.contains('open')) {
e.preventDefault();
}
}}
/>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ export function DialogFuel({
open,
onOpenChange,
}: DialogRadix.DialogProps & { theme: 'dark' | 'light' }) {
// const currentConnector = fuel.currentConnector();
// Fix hydration problem between nextjs render and frontend render
// UI was not getting updated and theme colors was set wrongly
// see more here https://nextjs.org/docs/messages/react-hydration-error
Expand Down
8 changes: 4 additions & 4 deletions packages/react/src/ui/Connect/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ import { DialogContent } from './components/Core/DialogContent';
import { DialogFuel } from './components/Core/DialogFuel';
import { ExternalDisclaimer } from './components/ExternalDisclaimer/ExternalDisclaimer';

const ConnectRoutes = ({ state }: { state: Routes }) => {
switch (state) {
const ConnectRoutes = ({ route }: { route: Routes }) => {
switch (route) {
case Routes.LIST:
return <Connectors />;
case Routes.INSTALL:
Expand All @@ -36,7 +36,7 @@ export function Connect() {
const {
theme,
cancel,
dialog: { isOpen, route: state, connector, back },
dialog: { isOpen, route, connector, back },
} = useConnectUI();

const handleOpenChange = (openState: boolean) => {
Expand All @@ -55,7 +55,7 @@ export function Connect() {
</DialogHeader>
<Divider />
<DialogMain>
<ConnectRoutes state={state} />
<ConnectRoutes route={route} />
</DialogMain>
</DialogContent>
</DialogFuel>
Expand Down
Loading
Loading