Skip to content

Commit adec6be

Browse files
[SDK] Fix showQrModal option not respected on desktop web (#7711)
1 parent c80eed3 commit adec6be

File tree

12 files changed

+509
-59
lines changed

12 files changed

+509
-59
lines changed

.changeset/social-dogs-bet.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+
Fix showQrModal option not respected on desktop web

apps/dashboard/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@
5757
"posthog-js": "1.256.1",
5858
"posthog-node": "^5.4.0",
5959
"prettier": "3.6.2",
60-
"qrcode": "^1.5.3",
60+
"qrcode": "1.5.3",
6161
"react": "19.1.0",
6262
"react-children-utilities": "^2.10.0",
6363
"react-day-picker": "^8.10.1",
@@ -99,7 +99,7 @@
9999
"@types/node": "22.14.1",
100100
"@types/papaparse": "^5.3.16",
101101
"@types/pluralize": "^0.0.33",
102-
"@types/qrcode": "^1.5.5",
102+
"@types/qrcode": "1.5.5",
103103
"@types/react": "19.1.8",
104104
"@types/react-dom": "19.1.6",
105105
"@types/react-table": "^7.7.20",

apps/playground-web/src/app/wallets/sign-in/headless/page.tsx

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,13 +71,13 @@ function BuildCustomUISection() {
7171
<CodeExample
7272
code={`\
7373
import { useConnect } from "thirdweb/react";
74-
import { createWallet } from "thirdweb/wallets";
74+
import { createWallet, injectedProvider } from "thirdweb/wallets";
7575
import { shortenAddress } from "thirdweb/utils";
7676
7777
function App() {
7878
const account = useActiveAccount();
7979
const wallet = useActiveWallet();
80-
const { connect, isConnecting, error } = useConnect();
80+
const { connect, isConnecting, error, cancelConnection } = useConnect();
8181
const { disconnect } = useDisconnect();
8282
8383
if (account) {
@@ -99,7 +99,17 @@ function App() {
9999
connect(async () => {
100100
// 500+ wallets supported with id autocomplete
101101
const wallet = createWallet("io.metamask");
102-
await wallet.connect({ client });
102+
const isInstalled = !!injectedProvider("io.metamask");
103+
await wallet.connect({ client, ...(isInstalled ? {} : {
104+
walletConnect: {
105+
// if not installed, show qr modal
106+
showQrModal: true,
107+
onCancel: () => {
108+
cancelConnection();
109+
}
110+
}
111+
})
112+
});
103113
return wallet;
104114
})
105115
}

apps/playground-web/src/components/sign-in/hooks.tsx

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {
66
useDisconnect,
77
} from "thirdweb/react";
88
import { shortenAddress } from "thirdweb/utils";
9-
import { createWallet } from "thirdweb/wallets";
9+
import { createWallet, injectedProvider } from "thirdweb/wallets";
1010
import { THIRDWEB_CLIENT } from "../../lib/client";
1111
import { Button } from "../ui/button";
1212

@@ -21,6 +21,17 @@ export function HooksPreview() {
2121
const adminWallet = createWallet("io.metamask");
2222
await adminWallet.connect({
2323
client: THIRDWEB_CLIENT,
24+
...(injectedProvider("io.metamask")
25+
? {}
26+
: {
27+
walletConnect: {
28+
showQrModal: true,
29+
onCancel: () => {
30+
console.log("onCancel");
31+
connectMutation.cancelConnection();
32+
},
33+
},
34+
}),
2435
});
2536
return adminWallet;
2637
});

apps/playground-web/src/components/sign-in/modal.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@ export function ModalPreview({ enableAuth }: { enableAuth?: boolean }) {
5454
auth: enableAuth ? playgroundAuth : undefined,
5555
client: THIRDWEB_CLIENT,
5656
});
57-
console.log("connected", wallet);
5857
return wallet;
5958
};
6059

apps/portal/src/components/Document/AuthMethodsTabs.tsx

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
55
import type React from "react";
6-
import { useState } from "react";
6+
import { useMemo, useState } from "react";
77
import { getSocialIcon } from "thirdweb/wallets/in-app";
88
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
99
import {
@@ -819,7 +819,26 @@ const getUnrealSnippet = (authMethod: AuthMethod): string => {
819819

820820
function AuthMethodsTabsContent() {
821821
const [selectedAuth, setSelectedAuth] = useState<AuthMethod>("email");
822-
const platforms = getPlatformsForAuth(selectedAuth);
822+
const [selectedPlatform, setSelectedPlatform] = useState<Platform | null>(
823+
null,
824+
);
825+
const platforms = useMemo(
826+
() => getPlatformsForAuth(selectedAuth),
827+
[selectedAuth],
828+
);
829+
830+
// Reset platform selection when platforms change
831+
const currentPlatform = useMemo(() => {
832+
const defaultPlatform = platforms[0]?.id || "typescript";
833+
// If currently selected platform is not available in new platforms, reset to first available
834+
if (
835+
!selectedPlatform ||
836+
!platforms.find((p) => p.id === selectedPlatform)
837+
) {
838+
return defaultPlatform;
839+
}
840+
return selectedPlatform;
841+
}, [platforms, selectedPlatform]);
823842

824843
return (
825844
<div className="space-y-6">
@@ -858,7 +877,10 @@ function AuthMethodsTabsContent() {
858877
<h3 className="text-lg font-semibold mb-4 text-foreground">
859878
2. Select Platform
860879
</h3>
861-
<Tabs defaultValue={platforms[0]?.id || "typescript"}>
880+
<Tabs
881+
value={currentPlatform}
882+
onValueChange={(value) => setSelectedPlatform(value as Platform)}
883+
>
862884
<TabsList>
863885
{platforms.map((platform) => {
864886
const IconComponent = platform.icon;

packages/thirdweb/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
"ora": "8.2.0",
3737
"ox": "0.7.0",
3838
"prompts": "2.4.2",
39+
"qrcode": "1.5.3",
3940
"toml": "3.0.0",
4041
"uqr": "0.1.2",
4142
"viem": "2.28.1",
@@ -61,6 +62,7 @@
6162
"@testing-library/user-event": "^14.6.1",
6263
"@types/cross-spawn": "^6.0.6",
6364
"@types/prompts": "2.4.9",
65+
"@types/qrcode": "1.5.5",
6466
"@types/react": "19.1.8",
6567
"@viem/anvil": "0.0.10",
6668
"@vitejs/plugin-react": "^4.6.0",

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

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,5 +79,15 @@ export function useConnect(options?: ConnectManagerOptions) {
7979
[connect, options, setConnectionStatus],
8080
);
8181

82-
return { connect: handleConnection, error, isConnecting } as const;
82+
const cancelConnection = useCallback(() => {
83+
setIsConnecting(false);
84+
setConnectionStatus("disconnected");
85+
}, [setConnectionStatus]);
86+
87+
return {
88+
connect: handleConnection,
89+
error,
90+
isConnecting,
91+
cancelConnection,
92+
} as const;
8393
}

packages/thirdweb/src/wallets/create-wallet.ts

Lines changed: 85 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { inAppWallet } from "./in-app/web/in-app.js";
1717
import { getInjectedProvider } from "./injected/index.js";
1818
import type { Account, Wallet } from "./interfaces/wallet.js";
1919
import { smartWallet } from "./smart/smart-wallet.js";
20+
import type { QROverlay } from "./wallet-connect/qr-overlay.js";
2021
import type { WCConnectOptions } from "./wallet-connect/types.js";
2122
import { createWalletEmitter } from "./wallet-emitter.js";
2223
import type {
@@ -299,30 +300,90 @@ export function createWallet<const ID extends WalletId>(
299300
"./wallet-connect/controller.js"
300301
);
301302

302-
const [
303-
connectedAccount,
304-
connectedChain,
305-
doDisconnect,
306-
doSwitchChain,
307-
] = await connectWC(
308-
wcOptions,
309-
emitter,
310-
wallet.id as WCSupportedWalletIds | "walletConnect",
311-
webLocalStorage,
312-
sessionHandler,
313-
);
314-
// set the states
315-
account = connectedAccount;
316-
chain = connectedChain;
317-
handleDisconnect = doDisconnect;
318-
handleSwitchChain = doSwitchChain;
319-
trackConnect({
320-
chainId: chain.id,
321-
client: wcOptions.client,
322-
walletAddress: account.address,
323-
walletType: id,
324-
});
325-
return account;
303+
let qrOverlay: QROverlay | undefined;
304+
305+
try {
306+
const [
307+
connectedAccount,
308+
connectedChain,
309+
doDisconnect,
310+
doSwitchChain,
311+
] = await connectWC(
312+
{
313+
...wcOptions,
314+
walletConnect: {
315+
...wcOptions.walletConnect,
316+
onDisplayUri: wcOptions.walletConnect?.showQrModal
317+
? async (uri) => {
318+
// Check if we're in a browser environment
319+
if (
320+
typeof window !== "undefined" &&
321+
typeof document !== "undefined"
322+
) {
323+
try {
324+
const { createQROverlay } = await import(
325+
"./wallet-connect/qr-overlay.js"
326+
);
327+
328+
// Clean up any existing overlay
329+
if (qrOverlay) {
330+
qrOverlay.destroy();
331+
}
332+
333+
// Create new QR overlay
334+
qrOverlay = createQROverlay(uri, {
335+
theme:
336+
wcOptions.walletConnect?.qrModalOptions
337+
?.themeMode ?? "dark",
338+
qrSize: 280,
339+
showCloseButton: true,
340+
onCancel: () => {
341+
wcOptions.walletConnect?.onCancel?.();
342+
},
343+
});
344+
} catch (error) {
345+
console.error(
346+
"Failed to create QR overlay:",
347+
error,
348+
);
349+
}
350+
}
351+
}
352+
: undefined,
353+
},
354+
},
355+
emitter,
356+
wallet.id as WCSupportedWalletIds | "walletConnect",
357+
webLocalStorage,
358+
sessionHandler,
359+
);
360+
361+
// Clean up QR overlay on successful connection
362+
if (qrOverlay) {
363+
qrOverlay.destroy();
364+
qrOverlay = undefined;
365+
}
366+
367+
// set the states
368+
account = connectedAccount;
369+
chain = connectedChain;
370+
handleDisconnect = doDisconnect;
371+
handleSwitchChain = doSwitchChain;
372+
trackConnect({
373+
chainId: chain.id,
374+
client: wcOptions.client,
375+
walletAddress: account.address,
376+
walletType: id,
377+
});
378+
return account;
379+
} catch (error) {
380+
// Clean up QR overlay on connection error
381+
if (qrOverlay) {
382+
qrOverlay.destroy();
383+
qrOverlay = undefined;
384+
}
385+
throw error;
386+
}
326387
}
327388

328389
if (id === "walletConnect") {

0 commit comments

Comments
 (0)