feat: Introduce account center with subscription management and contract-based payment integration.

This commit is contained in:
2569718930@qq.com
2026-03-17 13:57:44 +08:00
parent 54544e40b9
commit 90c998f06d
2 changed files with 56 additions and 7 deletions
+55 -7
View File
@@ -764,12 +764,42 @@ export function AccountCenter() {
const walletsJson = (await walletsRes.json()) as { const walletsJson = (await walletsRes.json()) as {
wallets?: BoundWallet[]; wallets?: BoundWallet[];
}; };
const wallets = Array.isArray(walletsJson.wallets) const wallets = (
? walletsJson.wallets Array.isArray(walletsJson.wallets) ? walletsJson.wallets : []
: []; )
.filter((row) => {
const status = String(row?.status || "active").toLowerCase();
const address = String(row?.address || "");
return status === "active" && address.startsWith("0x");
})
.map((row) => ({
...row,
address: String(row.address || "").toLowerCase(),
}));
setBoundWallets(wallets); setBoundWallets(wallets);
if (wallets.length && !selectedWallet) if (wallets.length) {
setSelectedWallet(wallets[0].address); const currentSelected = String(selectedWallet || "").toLowerCase();
const hasCurrent = wallets.some(
(row) => String(row.address || "").toLowerCase() === currentSelected,
);
const fallback =
wallets.find((row) => Boolean(row.is_primary))?.address ||
wallets[0].address;
if (!currentSelected || !hasCurrent) {
setSelectedWallet(fallback);
}
const currentWalletAddress = String(walletAddress || "").toLowerCase();
const hasWalletAddress = wallets.some(
(row) =>
String(row.address || "").toLowerCase() === currentWalletAddress,
);
if (!currentWalletAddress || !hasWalletAddress) {
setWalletAddress(fallback);
}
} else {
setSelectedWallet("");
setWalletAddress("");
}
} }
} catch { } catch {
// ignore // ignore
@@ -779,6 +809,7 @@ export function AccountCenter() {
buildAuthedHeaders, buildAuthedHeaders,
selectedPlanCode, selectedPlanCode,
selectedWallet, selectedWallet,
walletAddress,
]); ]);
const loadSnapshot = useCallback(async () => { const loadSnapshot = useCallback(async () => {
@@ -1302,12 +1333,19 @@ export function AccountCenter() {
data = {}; data = {};
} }
const newPrimary = String(data?.new_primary || "").toLowerCase(); const newPrimary = String(data?.new_primary || "").toLowerCase();
if (selectedWallet === target) { const selectedWalletNorm = String(selectedWallet || "").toLowerCase();
const walletAddressNorm = String(walletAddress || "").toLowerCase();
if (selectedWalletNorm === target) {
setSelectedWallet(newPrimary || ""); setSelectedWallet(newPrimary || "");
} }
if (walletAddress === target) { if (walletAddressNorm === target) {
setWalletAddress(newPrimary || ""); setWalletAddress(newPrimary || "");
} }
setBoundWallets((prev) =>
prev.filter(
(row) => String(row.address || "").toLowerCase() !== String(target),
),
);
await loadPaymentSnapshot(); await loadPaymentSnapshot();
setPaymentInfo( setPaymentInfo(
newPrimary newPrimary
@@ -1899,6 +1937,16 @@ export function AccountCenter() {
<h3 className="text-blue-400 text-sm font-bold uppercase tracking-widest mb-6 flex items-center gap-2"> <h3 className="text-blue-400 text-sm font-bold uppercase tracking-widest mb-6 flex items-center gap-2">
<Wallet size={18} /> {copy.paymentMgmt} <Wallet size={18} /> {copy.paymentMgmt}
</h3> </h3>
{paymentError ? (
<div className="mb-4 rounded-xl border border-red-400/40 bg-red-500/10 px-3 py-2 text-[11px] text-red-200">
{paymentError}
</div>
) : null}
{!paymentError && paymentInfo ? (
<div className="mb-4 rounded-xl border border-cyan-400/35 bg-cyan-500/10 px-3 py-2 text-[11px] text-cyan-200">
{paymentInfo}
</div>
) : null}
{availableTokenList.length > 0 && ( {availableTokenList.length > 0 && (
<div className="mb-5"> <div className="mb-5">
<p className="text-[11px] uppercase tracking-widest text-slate-500 mb-2"> <p className="text-[11px] uppercase tracking-widest text-slate-500 mb-2">
+1
View File
@@ -918,6 +918,7 @@ class PaymentContractCheckoutService:
"select": "chain_id,address,status,is_primary,verified_at", "select": "chain_id,address,status,is_primary,verified_at",
"user_id": f"eq.{user_id}", "user_id": f"eq.{user_id}",
"chain_id": f"eq.{self.chain_id}", "chain_id": f"eq.{self.chain_id}",
"status": "eq.active",
"order": "is_primary.desc,verified_at.desc", "order": "is_primary.desc,verified_at.desc",
}, },
allowed_status=[200], allowed_status=[200],