feat: Introduce account center with subscription management and contract-based payment integration.
This commit is contained in:
@@ -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">
|
||||||
|
|||||||
@@ -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],
|
||||||
|
|||||||
Reference in New Issue
Block a user