From 1d0e293bdbecc0bb65cf151e2b0a5fe73c7bd6f8 Mon Sep 17 00:00:00 2001
From: Manus
Date: Tue, 10 Mar 2026 02:12:35 -0400
Subject: [PATCH] =?UTF-8?q?Checkpoint:=20=E4=BF=AE=E5=A4=8D=E9=92=B1?=
=?UTF-8?q?=E5=8C=85=E8=BF=9E=E6=8E=A5Modal=E9=97=AE=E9=A2=98=EF=BC=9A1)?=
=?UTF-8?q?=20=E5=B0=86showWalletModal=E7=8A=B6=E6=80=81=E6=8F=90=E5=8D=87?=
=?UTF-8?q?=E5=88=B0Home=E4=B8=BB=E7=BB=84=E4=BB=B6=EF=BC=9B2)=20Modal?=
=?UTF-8?q?=E9=80=9A=E8=BF=87Portal=E6=B8=B2=E6=9F=93=E5=88=B0document.bod?=
=?UTF-8?q?y=EF=BC=8C=E8=84=B1=E7=A6=BB=E5=AF=BC=E8=88=AA=E6=A0=8Fbackdrop?=
=?UTF-8?q?Filter=E5=B1=82=E5=8F=A0=E4=B8=8A=E4=B8=8B=E6=96=87=EF=BC=9B3)?=
=?UTF-8?q?=20EVMPurchasePanel=E5=86=85=E5=B5=8CWalletSelector=E6=9B=BF?=
=?UTF-8?q?=E6=8D=A2=E4=B8=BA=E7=BB=9F=E4=B8=80=E7=9A=84Connect=20Wallet?=
=?UTF-8?q?=E6=8C=89=E9=92=AE=EF=BC=8C=E8=A7=A6=E5=8F=91=E5=90=8C=E4=B8=80?=
=?UTF-8?q?=E4=B8=AA=E9=A1=B6=E5=B1=82Modal=EF=BC=9B4)=20=E5=81=9C?=
=?UTF-8?q?=E6=AD=A2=E6=97=A7=E9=A2=84=E5=94=AE=E5=90=88=E7=BA=A60xc65e7a2?=
=?UTF-8?q?7...?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
client/src/pages/Home.tsx | 206 +++++++++++++++-----------------------
1 file changed, 82 insertions(+), 124 deletions(-)
diff --git a/client/src/pages/Home.tsx b/client/src/pages/Home.tsx
index b5e94fc..1ffe73e 100644
--- a/client/src/pages/Home.tsx
+++ b/client/src/pages/Home.tsx
@@ -4,6 +4,7 @@
// Colors: Amber Gold #f0b429 | Quantum Blue #00d4ff | Deep Black #0a0a0f
import { useState, useEffect, useCallback, useRef, useMemo } from "react";
+import { createPortal } from "react-dom";
import { toast } from "sonner";
import { Link } from "wouter";
import { useWallet } from "@/hooks/useWallet";
@@ -315,7 +316,7 @@ function TRC20Panel({ usdtAmount, lang, connectedAddress, onConnectWallet }: { u
}
// ─── EVM Purchase Panel ─────────────────────────────────────────────────────
-function EVMPurchasePanel({ network, lang, wallet }: { network: "BSC" | "ETH"; lang: Lang; wallet: WalletHookReturn }) {
+function EVMPurchasePanel({ network, lang, wallet, onOpenWalletModal }: { network: "BSC" | "ETH"; lang: Lang; wallet: WalletHookReturn; onOpenWalletModal?: () => void }) {
const { t } = useTranslation(lang);
const { purchaseState, buyWithUSDT, reset, calcTokens, getUsdtBalance } = usePresale(wallet, network);
const [usdtInput, setUsdtInput] = useState("100");
@@ -361,22 +362,18 @@ function EVMPurchasePanel({ network, lang, wallet }: { network: "BSC" | "ETH"; l
return (
{t("buy_connect_msg")}
-
{
- // KEY FIX: call connectWithProvider to sync wallet state immediately
- // Do NOT rely on accountsChanged event — it only fires for window.ethereum listeners
- if (rawProvider) {
- await wallet.connectWithProvider(rawProvider, addr);
- } else {
- // Manual address entry — no provider available, set address-only state
- await wallet.connectWithProvider({ request: async () => [] } as unknown as import("@/hooks/useWallet").EthProvider, addr);
- }
- toast.success(lang === "zh" ? `已连接: ${addr.slice(0, 6)}...${addr.slice(-4)}` : `Connected: ${addr.slice(0, 6)}...${addr.slice(-4)}`);
- }}
- compact
- />
+ onOpenWalletModal?.()}
+ className="w-full py-3 rounded-xl text-base font-bold transition-all hover:opacity-90 flex items-center justify-center gap-2"
+ style={{ background: "linear-gradient(135deg, rgba(240,180,41,0.9) 0%, rgba(255,215,0,0.9) 100%)", color: "#0a0a0f", fontFamily: "'Space Grotesk', sans-serif", boxShadow: "0 0 16px rgba(240,180,41,0.3)" }}
+ >
+
+
+
+
+
+ {lang === "zh" ? "连接钱包" : "Connect Wallet"}
+
{t("buy_connect_hint")}
);
@@ -809,10 +806,9 @@ function ChatSupport({ lang }: { lang: Lang }) {
// ─── Navbar Wallet Button ─────────────────────────────────────────────────────
type WalletHookReturn = ReturnType;
-function NavWalletButton({ lang, wallet }: { lang: Lang; wallet: WalletHookReturn }) {
+function NavWalletButton({ lang, wallet, showWalletModal, setShowWalletModal }: { lang: Lang; wallet: WalletHookReturn; showWalletModal: boolean; setShowWalletModal: (v: boolean) => void }) {
const { t } = useTranslation(lang);
const [showMenu, setShowMenu] = useState(false);
- const [showWalletModal, setShowWalletModal] = useState(false);
const menuRef = useRef(null);
useEffect(() => {
@@ -823,35 +819,10 @@ function NavWalletButton({ lang, wallet }: { lang: Lang; wallet: WalletHookRetur
return () => document.removeEventListener("mousedown", handleClick);
}, []);
- // Detect mobile browser
- const isMobile = typeof window !== "undefined" && /Android|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
- // Detect if running inside a wallet's in-app browser (window.ethereum is injected)
- const isInWalletBrowser = typeof window !== "undefined" && !!((window as unknown as Record).ethereum);
-
- // Handle connect button click — show wallet selector modal
- const handleConnectClick = async () => {
- // On mobile AND inside a wallet's in-app browser: try direct connect first
- // (window.ethereum is injected by the wallet app, so direct connect works)
- if (isMobile && isInWalletBrowser) {
- const result = await wallet.connect();
- if (!result.success) {
- // If direct connect failed, show modal as fallback
- setShowWalletModal(true);
- }
- return;
- }
- // On mobile browser (NOT in wallet app): show modal with DeepLink guide
- if (isMobile) {
- setShowWalletModal(true);
- return;
- }
- // On desktop: first try direct connect (works if wallet is already set up and locked)
- const result = await wallet.connect();
- if (!result.success && result.error) {
- // If direct connect failed, show the wallet selector modal for guided setup
- setShowWalletModal(true);
- toast.error(result.error, { duration: 6000 });
- }
+ // Handle connect button click — always show wallet selector modal
+ // Both desktop and mobile users see the modal to choose their wallet
+ const handleConnectClick = () => {
+ setShowWalletModal(true);
};
if (!wallet.isConnected) {
@@ -871,79 +842,6 @@ function NavWalletButton({ lang, wallet }: { lang: Lang; wallet: WalletHookRetur
{wallet.isConnecting ? t("nav_connecting") : t("nav_connect")}
- {/* Wallet Connection Modal */}
- {showWalletModal && (
- { if (e.target === e.currentTarget) setShowWalletModal(false); }}
- >
-
- {/* Close button */}
-
setShowWalletModal(false)}
- className="absolute top-4 right-4 text-white/40 hover:text-white/80 transition-colors"
- >
-
-
-
-
-
-
- {lang === "zh" ? "连接钱包" : "Connect Wallet"}
-
- {!isMobile && (
-
- {lang === "zh"
- ? "选择您的钱包进行连接,或手动输入地址"
- : "Select your wallet to connect, or enter address manually"}
-
- )}
-
- {/* MetaMask initialization guide — desktop only */}
- {!isMobile && (
-
-
- {lang === "zh"
- ? "💡 首次使用 MetaMask?请先打开 MetaMask 扩展完成初始化(创建或导入钱包),完成后点击下方「刷新」按钮重新检测。"
- : "💡 First time using MetaMask? Open the MetaMask extension and complete setup (create or import a wallet), then click Refresh below to re-detect."}
-
-
- )}
- {isMobile &&
}
-
-
{
- // KEY FIX: use connectWithProvider() to directly sync wallet state
- // This avoids calling connect() again which would use detectProvider()
- // and might fail if the wallet injects to a different window property (e.g. OKX -> window.okxwallet)
- if (rawProvider) {
- await wallet.connectWithProvider(rawProvider, addr);
- } else {
- // Fallback for manual address entry (no provider)
- const result = await wallet.connect();
- if (!result.success) {
- // Still mark as connected with address only
- await wallet.connectWithProvider({ request: async () => [] } as unknown as import("@/hooks/useWallet").EthProvider, addr);
- }
- }
- setShowWalletModal(false);
- toast.success(lang === "zh" ? `钱包已连接: ${addr.slice(0, 6)}...${addr.slice(-4)}` : `Wallet connected: ${addr.slice(0, 6)}...${addr.slice(-4)}`);
- }}
- />
-
-
- )}
>
);
}
@@ -1042,6 +940,8 @@ export default function Home() {
// 钱包状态提升到顶层,共享给NavWalletButton和EVMPurchasePanel
const wallet = useWallet();
+ // showWalletModal提升到顶层,供NavWalletButton和EVMPurchasePanel共用
+ const [showWalletModal, setShowWalletModal] = useState(false);
const networks: NetworkTab[] = ["BSC", "ETH", "TRON"];
@@ -1081,7 +981,7 @@ export default function Home() {
-
+
@@ -1266,8 +1166,8 @@ export default function Home() {
)}
- {activeNetwork === "BSC" && }
- {activeNetwork === "ETH" && }
+ {activeNetwork === "BSC" && setShowWalletModal(true)} />}
+ {activeNetwork === "ETH" && setShowWalletModal(true)} />}
{activeNetwork === "TRON" && (
@@ -1386,6 +1286,64 @@ export default function Home() {
{/* ── Chat Support Widget ── */}
+ {/* ── Global Wallet Connection Modal (Portal) ── */}
+ {showWalletModal && createPortal(
+
{ if (e.target === e.currentTarget) setShowWalletModal(false); }}
+ >
+
+
setShowWalletModal(false)}
+ className="absolute top-4 right-4 text-white/40 hover:text-white/80 transition-colors"
+ >
+
+
+
+
+
+ {lang === "zh" ? "连接钱包" : "Connect Wallet"}
+
+ {!/Android|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) && (
+ <>
+
+ {lang === "zh" ? "选择您的钱包进行连接,或手动输入地址" : "Select your wallet to connect, or enter address manually"}
+
+
+
+ {lang === "zh"
+ ? "💡 首次使用 MetaMask?请先打开 MetaMask 扩展完成初始化(创建或导入钱包),完成后点击下方「刷新」按鈕重新检测。"
+ : "💡 First time using MetaMask? Open the MetaMask extension and complete setup (create or import a wallet), then click Refresh below to re-detect."}
+
+
+ >
+ )}
+
{
+ if (rawProvider) {
+ await wallet.connectWithProvider(rawProvider, addr);
+ } else {
+ const result = await wallet.connect();
+ if (!result.success) {
+ await wallet.connectWithProvider({ request: async () => [] } as unknown as import("@/hooks/useWallet").EthProvider, addr);
+ }
+ }
+ setShowWalletModal(false);
+ toast.success(lang === "zh" ? `钱包已连接: ${addr.slice(0, 6)}...${addr.slice(-4)}` : `Wallet connected: ${addr.slice(0, 6)}...${addr.slice(-4)}`);
+ }}
+ />
+
+
+ , document.body)}
+