Skip to content

Commit 6326e45

Browse files
committed
update
1 parent a0835f7 commit 6326e45

File tree

6 files changed

+233
-13
lines changed

6 files changed

+233
-13
lines changed

.changeset/little-beds-dress.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"thirdweb": patch
3+
---
4+
5+
Add onTimeout callback to useAutoConnect

packages/thirdweb/src/react/core/hooks/connection/types.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,4 +113,9 @@ export type AutoConnectProps = {
113113
* Optional chain to autoconnect to
114114
*/
115115
chain?: Chain;
116+
117+
/**
118+
* Callback to be called when the connection is timeout-ed
119+
*/
120+
onTimeout?: () => void;
116121
};

packages/thirdweb/src/react/core/hooks/wallets/useAutoConnect.ts

Lines changed: 42 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
"use client";
22

33
import { useQuery } from "@tanstack/react-query";
4+
import type { Chain } from "../../../../chains/types.js";
5+
import type { ThirdwebClient } from "../../../../client/client.js";
46
import type { AsyncStorage } from "../../../../utils/storage/AsyncStorage.js";
57
import { isEcosystemWallet } from "../../../../wallets/ecosystem/is-ecosystem-wallet.js";
68
import { ClientScopedStorage } from "../../../../wallets/in-app/core/authentication/client-scoped-storage.js";
9+
import type { AuthStoredTokenWithCookieReturnType } from "../../../../wallets/in-app/core/authentication/types.js";
710
import { getUrlToken } from "../../../../wallets/in-app/web/lib/get-url-token.js";
811
import type { Wallet } from "../../../../wallets/interfaces/wallet.js";
912
import {
@@ -24,6 +27,7 @@ export function useAutoConnectCore(
2427
props: AutoConnectProps & { wallets: Wallet[] },
2528
createWalletFn: (id: WalletId) => Wallet,
2629
getInstalledWallets?: () => Wallet[],
30+
onTimeout?: () => void,
2731
) {
2832
const manager = useConnectionManagerCtx("useAutoConnect");
2933
const setConnectionStatus = useSetActiveWalletConnectionStatus();
@@ -83,14 +87,6 @@ export function useAutoConnectCore(
8387
const lastConnectedChain =
8488
(await getLastConnectedChain(storage)) || props.chain;
8589

86-
async function handleWalletConnection(wallet: Wallet) {
87-
return wallet.autoConnect({
88-
client: props.client,
89-
chain: lastConnectedChain ?? undefined,
90-
authResult,
91-
});
92-
}
93-
9490
const availableWallets = [...wallets, ...(getInstalledWallets?.() ?? [])];
9591
const activeWallet =
9692
lastActiveWalletId &&
@@ -100,9 +96,22 @@ export function useAutoConnectCore(
10096
if (activeWallet) {
10197
try {
10298
setConnectionStatus("connecting"); // only set connecting status if we are connecting the last active EOA
103-
await timeoutPromise(handleWalletConnection(activeWallet), {
104-
ms: timeout,
105-
message: `AutoConnect timeout: ${timeout}ms limit exceeded.`,
99+
await timeoutPromise(
100+
handleWalletConnection({
101+
wallet: activeWallet,
102+
client: props.client,
103+
lastConnectedChain,
104+
authResult,
105+
}),
106+
{
107+
ms: timeout,
108+
message: `AutoConnect timeout: ${timeout}ms limit exceeded.`,
109+
},
110+
).catch((err) => {
111+
console.warn(err.message);
112+
if (onTimeout) {
113+
onTimeout();
114+
}
106115
});
107116

108117
// connected wallet could be activeWallet or smart wallet
@@ -138,7 +147,12 @@ export function useAutoConnectCore(
138147

139148
for (const wallet of otherWallets) {
140149
try {
141-
await handleWalletConnection(wallet);
150+
await handleWalletConnection({
151+
wallet,
152+
client: props.client,
153+
lastConnectedChain,
154+
authResult,
155+
});
142156
manager.addConnectedWallet(wallet);
143157
} catch {
144158
// no-op
@@ -158,3 +172,19 @@ export function useAutoConnectCore(
158172

159173
return query;
160174
}
175+
176+
/**
177+
* @internal
178+
*/
179+
export async function handleWalletConnection(props: {
180+
wallet: Wallet;
181+
client: ThirdwebClient;
182+
authResult: AuthStoredTokenWithCookieReturnType | undefined;
183+
lastConnectedChain: Chain | undefined;
184+
}) {
185+
return props.wallet.autoConnect({
186+
client: props.client,
187+
chain: props.lastConnectedChain,
188+
authResult: props.authResult,
189+
});
190+
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import { renderHook, waitFor } from "@testing-library/react";
2+
import type { ReactNode } from "react";
3+
import { describe, expect, it } from "vitest";
4+
import { MockStorage } from "~test/mocks/storage.js";
5+
import { TEST_CLIENT } from "~test/test-clients.js";
6+
import { TEST_ACCOUNT_A } from "~test/test-wallets.js";
7+
import { createWalletAdapter } from "../../../../adapters/wallet-adapter.js";
8+
import { ethereum } from "../../../../chains/chain-definitions/ethereum.js";
9+
import { isAddress } from "../../../../utils/address.js";
10+
import { createConnectionManager } from "../../../../wallets/manager/index.js";
11+
import type { WalletId } from "../../../../wallets/wallet-types.js";
12+
import { ThirdwebProvider } from "../../../web/providers/thirdweb-provider.js";
13+
import { ConnectionManagerCtx } from "../../providers/connection-manager.js";
14+
import {
15+
handleWalletConnection,
16+
useAutoConnectCore,
17+
} from "./useAutoConnect.js";
18+
19+
const wallet = createWalletAdapter({
20+
adaptedAccount: TEST_ACCOUNT_A,
21+
client: TEST_CLIENT,
22+
chain: ethereum,
23+
onDisconnect: () => {},
24+
switchChain: () => {},
25+
});
26+
27+
describe("useAutoConnectCore", () => {
28+
const mockStorage = new MockStorage();
29+
const manager = createConnectionManager(mockStorage);
30+
31+
// Create a wrapper component with the mocked context
32+
const wrapper = ({ children }: { children: ReactNode }) => {
33+
return (
34+
<ThirdwebProvider>
35+
<ConnectionManagerCtx.Provider value={manager}>
36+
{children}
37+
</ConnectionManagerCtx.Provider>
38+
</ThirdwebProvider>
39+
);
40+
};
41+
42+
it("should return a useQuery result", async () => {
43+
const { result } = renderHook(
44+
() =>
45+
useAutoConnectCore(
46+
mockStorage,
47+
{
48+
wallets: [wallet],
49+
client: TEST_CLIENT,
50+
},
51+
(id: WalletId) =>
52+
createWalletAdapter({
53+
adaptedAccount: TEST_ACCOUNT_A,
54+
client: TEST_CLIENT,
55+
chain: ethereum,
56+
onDisconnect: () => {
57+
console.warn(id);
58+
},
59+
switchChain: () => {},
60+
}),
61+
),
62+
{ wrapper },
63+
);
64+
expect("data" in result.current).toBeTruthy();
65+
await waitFor(() => {
66+
expect(typeof result.current.data).toBe("boolean");
67+
});
68+
});
69+
});
70+
71+
describe("handleWalletConnection", () => {
72+
it("should return the correct result", async () => {
73+
const result = await handleWalletConnection({
74+
client: TEST_CLIENT,
75+
lastConnectedChain: ethereum,
76+
authResult: undefined,
77+
wallet,
78+
});
79+
80+
expect("address" in result).toBe(true);
81+
expect(isAddress(result.address)).toBe(true);
82+
expect("sendTransaction" in result).toBe(true);
83+
expect(typeof result.sendTransaction).toBe("function");
84+
expect("signMessage" in result).toBe(true);
85+
expect("signTypedData" in result).toBe(true);
86+
expect("signTransaction" in result).toBe(true);
87+
});
88+
});
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import { afterEach, beforeEach, describe, expect, it } from "vitest";
2+
import { getUrlToken } from "./get-url-token.js";
3+
4+
describe("getUrlToken", () => {
5+
let originalLocation: Location;
6+
7+
beforeEach(() => {
8+
originalLocation = window.location;
9+
10+
Object.defineProperty(window, "location", {
11+
value: {
12+
...originalLocation,
13+
search: "",
14+
},
15+
writable: true,
16+
});
17+
});
18+
19+
afterEach(() => {
20+
// Restore the original location object after each test
21+
Object.defineProperty(window, "location", {
22+
value: originalLocation,
23+
writable: true,
24+
});
25+
});
26+
27+
it("should return an empty object if not in web context", () => {
28+
const originalWindow = window;
29+
// biome-ignore lint/suspicious/noExplicitAny: Test
30+
(global as any).window = undefined;
31+
32+
const result = getUrlToken();
33+
// biome-ignore lint/suspicious/noExplicitAny: Test
34+
(global as any).window = originalWindow;
35+
36+
expect(result).toEqual({});
37+
});
38+
39+
it("should return an empty object if no parameters are present", () => {
40+
const result = getUrlToken();
41+
expect(result).toEqual({});
42+
});
43+
44+
it("should parse walletId and authResult correctly", () => {
45+
window.location.search =
46+
"?walletId=123&authResult=%7B%22token%22%3A%22abc%22%7D";
47+
48+
const result = getUrlToken();
49+
50+
expect(result).toEqual({
51+
walletId: "123",
52+
authResult: { token: "abc" },
53+
authProvider: null,
54+
authCookie: null,
55+
});
56+
});
57+
58+
it("should handle authCookie and update URL correctly", () => {
59+
window.location.search = "?walletId=123&authCookie=myCookie";
60+
61+
const result = getUrlToken();
62+
63+
expect(result).toEqual({
64+
walletId: "123",
65+
authResult: undefined,
66+
authProvider: null,
67+
authCookie: "myCookie",
68+
});
69+
70+
// Check if URL has been updated correctly
71+
expect(window.location.search).toBe("?walletId=123&authCookie=myCookie");
72+
});
73+
74+
it("should handle all parameters correctly", () => {
75+
window.location.search =
76+
"?walletId=123&authResult=%7B%22token%22%3A%22xyz%22%7D&authProvider=provider1&authCookie=myCookie";
77+
78+
const result = getUrlToken();
79+
80+
expect(result).toEqual({
81+
walletId: "123",
82+
authResult: { token: "xyz" },
83+
authProvider: "provider1",
84+
authCookie: "myCookie",
85+
});
86+
87+
// Check if URL has been updated correctly
88+
expect(window.location.search).toBe(
89+
"?walletId=123&authResult=%7B%22token%22%3A%22xyz%22%7D&authProvider=provider1&authCookie=myCookie",
90+
);
91+
});
92+
});

packages/thirdweb/test/vitest.config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ export default defineConfig({
3131
],
3232
include: ["src/**"],
3333
},
34-
environmentMatchGlobs: [["src/react/**/*.test.tsx", "happy-dom"]],
34+
environmentMatchGlobs: [["src/**/*.test.tsx", "happy-dom"]],
3535
environment: "node",
3636
include: ["src/**/*.test.{ts,tsx}"],
3737
setupFiles: [join(__dirname, "./reactSetup.ts")],

0 commit comments

Comments
 (0)