Skip to content

Commit

Permalink
add optional passkey configs and callbacks to connect hooks, componen…
Browse files Browse the repository at this point in the history
…ts and utils (#106)

* core: add connect with passkey param to override sign in type

* expose `hasStoredPasskey` to connect modal component and hooks

* expose more passkey configs to react components and hooks

* add connect success and error callbacks

* add changeset
  • Loading branch information
alecananian authored Aug 27, 2024
1 parent 0137720 commit b75e4c8
Show file tree
Hide file tree
Showing 8 changed files with 98 additions and 34 deletions.
5 changes: 5 additions & 0 deletions .changeset/shaggy-emus-smell.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@treasure-dev/tdk-core": patch
---

Added connect with passkey param to override sign in type
5 changes: 5 additions & 0 deletions .changeset/short-oranges-stare.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@treasure-dev/tdk-react": patch
---

Added connect success and error callbacks
10 changes: 9 additions & 1 deletion examples/connect-react/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,15 @@ export const App = () => {
<h1 className="font-semibold text-2xl text-ruby">
TDK React - Connect Example
</h1>
<ConnectButton supportedChainIds={[421614, 42161]} />
<ConnectButton
supportedChainIds={[421614, 42161]}
onConnected={(method, wallet) => {
console.log("Connect successful:", { method, wallet });
}}
onConnectError={(method, err) => {
console.log("Connect failed:", { method, err });
}}
/>
</header>
<main className="space-y-6">
{user ? (
Expand Down
1 change: 0 additions & 1 deletion packages/core/src/connect/login.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
//
import { describe, expect, it } from "vitest";
import { isSocialConnectMethod } from "./login";

Expand Down
4 changes: 3 additions & 1 deletion packages/core/src/connect/login.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ type ConnectWalletConfig = {
| {
method: "passkey";
passkeyName?: string;
hasStoredPasskey?: boolean;
}
);

Expand Down Expand Up @@ -105,7 +106,8 @@ export const connectWallet = async (params: ConnectWalletConfig) => {

// Connect with passkey
if (params.method === "passkey") {
const hasPasskey = await hasStoredPasskey(client);
const hasPasskey =
params.hasStoredPasskey ?? (await hasStoredPasskey(client));
await wallet.connect({
client,
chain,
Expand Down
87 changes: 56 additions & 31 deletions packages/react/src/components/connect/ConnectModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { Trans, useTranslation } from "react-i18next";
import { useTreasure } from "../../contexts/treasure";
import { getLocaleId } from "../../i18n";
import { cn } from "../../utils/classnames";
import { getErrorMessage } from "../../utils/error";
import { Dialog, DialogContent, DialogTitle } from "../ui/Dialog";
import {
type Options as ConnectMethodSelectionOptions,
Expand All @@ -28,6 +29,11 @@ import { ConnectVerifyCodeView } from "./ConnectVerifyCodeView";
export type Options = ConnectMethodSelectionOptions & {
authMode?: "popup" | "redirect";
redirectUrl?: string;
passkeyDomain?: string;
passkeyName?: string;
hasStoredPasskey?: boolean;
onConnected?: (method: ConnectMethod, wallet: Wallet) => void;
onConnectError?: (method: ConnectMethod, err: unknown) => void;
};

export type Props = Options & {
Expand All @@ -47,7 +53,12 @@ export const ConnectModal = ({
size = "lg",
authMode,
redirectUrl,
passkeyDomain,
passkeyName,
hasStoredPasskey,
onOpenChange,
onConnected,
onConnectError,
...methodSelectionProps
}: Props) => {
const { t } = useTranslation();
Expand All @@ -74,23 +85,17 @@ export const ConnectModal = ({
error: isLoading ? undefined : curr.error,
}));

const setError = (error: string, resetEmail = false) =>
const setError = (error: string, resetEmail = false) => {
// Skip displaying error if user manually closed login window (message comes from Thirdweb)
if (error === "User closed login window") {
return;
}

setState((curr) => ({
email: resetEmail ? "" : curr.email,
isLoading: false,
error,
}));

const handleLogin = async (wallet: Wallet) => {
try {
await logIn(wallet);
// Login was successful, close the connect modal
onOpenChange(false);
} catch (err) {
console.error("Error logging in wallet:", err);
setError((err as Error).message, true);
logOut();
}
};

const handleConnectEmail = async (verificationCode: string) => {
Expand All @@ -99,26 +104,31 @@ export const ConnectModal = ({

let wallet: Wallet | undefined | null;
try {
wallet = await connect(() =>
connectWallet({
client,
chainId: chain.id,
method: "email",
email,
verificationCode,
}),
);
const inAppWallet = await connectWallet({
client,
chainId: chain.id,
method: "email",
email,
verificationCode,
});
wallet = await connect(inAppWallet);
} catch (err) {
console.error("Error connecting wallet with email:", err);
setError((err as Error).message);
setError(getErrorMessage(err));
onConnectError?.("email", err);
}

if (wallet) {
try {
await handleLogin(wallet);
await logIn(wallet);
// Login was successful, close the connect modal
onConnected?.("email", wallet);
onOpenChange(false);
} catch (err) {
console.error("Error logging in wallet with email:", err);
setError((err as Error).message);
setError(getErrorMessage(err), true);
onConnectError?.("email", err);
logOut();
}
} else {
setIsLoading(false);
Expand All @@ -139,7 +149,8 @@ export const ConnectModal = ({
setState({ email: nextEmail, isLoading: false, error: undefined });
} catch (err) {
console.error("Error sending email verification code:", err);
setError((err as Error).message);
setError(getErrorMessage(err));
onConnectError?.("email", err);
}

return;
Expand Down Expand Up @@ -172,7 +183,8 @@ export const ConnectModal = ({
// Error can be undefined if user closed the connect modal
if (err) {
console.error("Error connecting Web3 wallet:", err);
setError((err as Error).message);
setError(getErrorMessage(err));
onConnectError?.(method, err);
}
}
} else if (
Expand All @@ -191,7 +203,8 @@ export const ConnectModal = ({
});
} catch (err) {
console.error("Error connecting in-app wallet with redirect:", err);
setError((err as Error).message);
setError(getErrorMessage(err));
onConnectError?.(method, err);
}
} else {
// Handle connecting with social / passkey
Expand All @@ -202,20 +215,31 @@ export const ConnectModal = ({
method,
authMode: "popup",
redirectUrl,
...(method === "passkey" && {
passkeyDomain,
passkeyName,
hasStoredPasskey,
}),
});
wallet = await connect(inAppWallet);
} catch (err) {
console.error("Error connecting in-app wallet:", err);
setError((err as Error).message);
setError(getErrorMessage(err));
onConnectError?.(method, err);
}
}

if (wallet) {
try {
await handleLogin(wallet);
await logIn(wallet);
// Login was successful, close the connect modal
onConnected?.(method, wallet);
onOpenChange(false);
} catch (err) {
console.error("Error logging in wallet:", err);
setError((err as Error).message);
setError(getErrorMessage(err));
onConnectError?.(method, err);
logOut();
}
} else {
setIsLoading(false);
Expand All @@ -227,7 +251,8 @@ export const ConnectModal = ({
await sendEmailVerificationCode({ client, email });
} catch (err) {
console.error("Error resending email verification code:", err);
setError((err as Error).message);
setError(getErrorMessage(err));
onConnectError?.("email", err);
}
};

Expand Down
18 changes: 18 additions & 0 deletions packages/react/src/utils/error.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { describe, expect, it } from "vitest";

import { getErrorMessage } from "./error";

class MockCustomError extends Error {
constructor(message) {
super(message);
this.name = "MockCustomError";
}
}

describe("error utils", () => {
it("should get error message", () => {
expect(getErrorMessage(new Error("test"))).toBe("test");
expect(getErrorMessage("test")).toBe("test");
expect(getErrorMessage(new MockCustomError("test"))).toBe("test");
});
});
2 changes: 2 additions & 0 deletions packages/react/src/utils/error.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export const getErrorMessage = (err: unknown) =>
err instanceof Error ? err.message : String(err);

0 comments on commit b75e4c8

Please sign in to comment.