@theoo/react 0.4.2 → 0.4.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/components.d.ts.map +1 -1
- package/dist/components.js +6 -6
- package/dist/components.js.map +1 -1
- package/dist/connect.d.ts.map +1 -1
- package/dist/connect.js +1 -3
- package/dist/connect.js.map +1 -1
- package/dist/hooks-entry.d.ts +1 -1
- package/dist/hooks-entry.d.ts.map +1 -1
- package/dist/hooks-entry.js +1 -1
- package/dist/hooks-entry.js.map +1 -1
- package/dist/hooks.d.ts +70 -5
- package/dist/hooks.d.ts.map +1 -1
- package/dist/hooks.js +158 -0
- package/dist/hooks.js.map +1 -1
- package/dist/money.d.ts +3 -3
- package/dist/money.d.ts.map +1 -1
- package/dist/money.js +143 -110
- package/dist/money.js.map +1 -1
- package/package.json +2 -2
package/dist/money.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { useEffect, useMemo, useState } from "react";
|
|
2
|
+
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
3
3
|
import { formatUnits, parseUnits } from "@theoo/sdk";
|
|
4
4
|
import { useTheoo } from "./context.js";
|
|
5
5
|
import { styles } from "./components.js";
|
|
@@ -27,13 +27,13 @@ function find(balances, wanted) {
|
|
|
27
27
|
return balances.find((b) => b.token.symbol.toLowerCase() === w || b.token.address.toLowerCase() === w);
|
|
28
28
|
}
|
|
29
29
|
const STATE_WORDS = {
|
|
30
|
-
awaiting_signature: "
|
|
31
|
-
processing: "
|
|
30
|
+
awaiting_signature: "Awaiting confirmation",
|
|
31
|
+
processing: "Processing",
|
|
32
32
|
completed: "Done",
|
|
33
33
|
failed: "Failed",
|
|
34
34
|
expired: "Expired",
|
|
35
35
|
open: "Open",
|
|
36
|
-
awaiting_delivery: "
|
|
36
|
+
awaiting_delivery: "Pending",
|
|
37
37
|
delivered: "Delivered",
|
|
38
38
|
disputed: "Disputed",
|
|
39
39
|
refunded: "Refunded",
|
|
@@ -47,10 +47,10 @@ const STATE_WORDS = {
|
|
|
47
47
|
* the passkey signs is one batch that approves the exact amount, swaps, and
|
|
48
48
|
* resets the approval to zero. Nothing is left behind for anyone to spend.
|
|
49
49
|
*/
|
|
50
|
-
export function TheooMoney({ chain, flows = ["swap"], defaultSell, defaultBuy, onDone, explorerTx, className
|
|
50
|
+
export function TheooMoney({ chain, flows = ["swap"], defaultSell, defaultBuy, onDone, explorerTx, className }) {
|
|
51
51
|
const [tab, setTab] = useState(flows[0] ?? "swap");
|
|
52
52
|
const names = { swap: "Swap", espees: "Buy ESPEES", sell: "Sell ESPEES", buy: "Add money", cashout: "Cash out" };
|
|
53
|
-
return (_jsxs("div", { className: className, style: card, "data-theoo-money": "", children: [flows.length > 1 ? (_jsx("div", { role: "tablist", style: { display: "flex", gap: 6, overflowX: "auto", scrollbarWidth: "none", margin: "0 -4px", padding: "0 4px" }, children: flows.map((f) => (_jsx("button", { type: "button", role: "tab", "aria-selected": tab === f, onClick: () => setTab(f), style: { ...(tab === f ? s.button : s.secondary), padding: "6px 12px", fontSize: 13.5, whiteSpace: "nowrap", flex: "none", width: "auto" }, children: names[f] }, f))) })) : null, tab === "espees" ? (_jsx(EspeesPanel, { chain: chain, onDone: onDone, explorerTx: explorerTx })) : tab === "sell" ? (_jsx(SellPanel, { chain: chain })) : tab === "buy" ? (_jsx(
|
|
53
|
+
return (_jsxs("div", { className: className, style: card, "data-theoo-money": "", children: [flows.length > 1 ? (_jsx("div", { role: "tablist", style: { display: "flex", gap: 6, overflowX: "auto", scrollbarWidth: "none", margin: "0 -4px", padding: "0 4px" }, children: flows.map((f) => (_jsx("button", { type: "button", role: "tab", "aria-selected": tab === f, onClick: () => setTab(f), style: { ...(tab === f ? s.button : s.secondary), padding: "6px 12px", fontSize: 13.5, whiteSpace: "nowrap", flex: "none", width: "auto" }, children: names[f] }, f))) })) : null, tab === "espees" ? (_jsx(EspeesPanel, { chain: chain, onDone: onDone, explorerTx: explorerTx })) : tab === "sell" ? (_jsx(SellPanel, { chain: chain })) : tab === "buy" ? (_jsx(BachsPanel, { mode: "buy" })) : tab === "cashout" ? (_jsx(BachsPanel, { mode: "cashout" })) : (_jsx(SwapPanel, { chain: chain, defaultSell: defaultSell, defaultBuy: defaultBuy, onDone: onDone, explorerTx: explorerTx }))] }));
|
|
54
54
|
}
|
|
55
55
|
function SwapPanel({ chain, defaultSell, defaultBuy, onDone, explorerTx }) {
|
|
56
56
|
const { balances, error: balanceError, reload } = useBalances(chain);
|
|
@@ -82,17 +82,15 @@ function SwapPanel({ chain, defaultSell, defaultBuy, onDone, explorerTx }) {
|
|
|
82
82
|
try {
|
|
83
83
|
const units = parseUnits(amount, sell.token.decimals);
|
|
84
84
|
if (BigInt(units) === 0n)
|
|
85
|
-
return { problem: "Enter an amount
|
|
85
|
+
return { problem: "Enter an amount." };
|
|
86
86
|
if (BigInt(units) > BigInt(sell.amount))
|
|
87
|
-
return { problem:
|
|
87
|
+
return { problem: "Not enough balance." };
|
|
88
88
|
return { units };
|
|
89
89
|
}
|
|
90
90
|
catch (e) {
|
|
91
91
|
const tooPrecise = e instanceof Error && /decimal places/.test(e.message);
|
|
92
92
|
return {
|
|
93
|
-
problem: tooPrecise
|
|
94
|
-
? `${sell.token.symbol} has at most ${sell.token.decimals} decimal places.`
|
|
95
|
-
: "Enter an amount like 12.5.",
|
|
93
|
+
problem: tooPrecise ? "Too many decimals." : "Invalid amount.",
|
|
96
94
|
};
|
|
97
95
|
}
|
|
98
96
|
}, [amount, sell]);
|
|
@@ -123,17 +121,23 @@ function SwapPanel({ chain, defaultSell, defaultBuy, onDone, explorerTx }) {
|
|
|
123
121
|
const q = swap.quote;
|
|
124
122
|
const done = swap.status;
|
|
125
123
|
const txLink = done?.txHash && explorerTx ? explorerTx(done.txHash, done.chain) : null;
|
|
126
|
-
return (_jsxs("div", { style: { display: "grid", gap: 12 }, children: [
|
|
124
|
+
return (_jsxs("div", { style: { display: "grid", gap: 12 }, children: [_jsx("div", { style: { display: "flex", flexWrap: "wrap", justifyContent: "space-between", alignItems: "baseline", gap: "4px 10px" }, children: _jsx("strong", { style: { fontSize: 15.5, letterSpacing: "-0.01em", whiteSpace: "nowrap" }, children: "Swap" }) }), balanceError ? (_jsx("div", { style: s.error, role: "alert", children: balanceError.message })) : null, !balances && !balanceError ? _jsx("p", { style: s.muted, children: "Loading\u2026" }) : null, balances && list.length < 2 ? _jsx("p", { style: s.muted, children: "Not available yet." }) : null, balances && list.length >= 2 ? (_jsxs(_Fragment, { children: [_jsxs("div", { style: { display: "grid", gap: 6 }, children: [_jsxs("div", { style: { display: "flex", justifyContent: "space-between" }, children: [_jsx("label", { htmlFor: "theoo-money-amount", style: label, children: "You pay" }), sell ? (_jsxs("span", { style: s.tiny, children: ["Balance ", sell.display, " ", sell.token.symbol, " ", _jsx("button", { type: "button", style: link, onClick: () => setAmount(formatUnits(sell.amount, sell.token.decimals, sell.token.decimals)), children: "Max" })] })) : null] }), _jsxs("div", { style: row, children: [_jsx("input", { id: "theoo-money-amount", inputMode: "decimal", autoComplete: "off", placeholder: "0.00", value: amount, onChange: (e) => setAmount(e.target.value), style: { ...s.input, fontFamily: t.mono, fontSize: 17 }, "aria-invalid": parsed.problem ? true : undefined }), _jsx("select", { id: "theoo-money-sell", "aria-label": "Token to pay with", value: sellAddr, onChange: (e) => setSellAddr(e.target.value), style: select, children: list.map((b) => (_jsx("option", { value: b.token.address, disabled: b.token.address === buyAddr, children: b.token.symbol }, b.token.address))) })] }), parsed.problem ? _jsx("p", { style: { ...s.tiny, color: t.alert }, children: parsed.problem }) : null] }), _jsx("div", { style: { display: "flex", justifyContent: "center" }, children: _jsx("button", { type: "button", onClick: flip, "aria-label": "Swap direction", style: { ...s.secondary, padding: "4px 12px", fontSize: 15 }, children: "\u21C5" }) }), _jsxs("div", { style: { display: "grid", gap: 6 }, children: [_jsx("label", { htmlFor: "theoo-money-buy", style: label, children: "You get" }), _jsxs("div", { style: row, children: [_jsx("div", { style: { ...s.input, fontFamily: t.mono, fontSize: 17, color: q ? t.ink : t.ink3 }, "aria-live": "polite", children: q ? q.buy.display : "—" }), _jsx("select", { id: "theoo-money-buy", value: buyAddr, onChange: (e) => setBuyAddr(e.target.value), style: select, children: list.map((b) => (_jsx("option", { value: b.token.address, disabled: b.token.address === sellAddr, children: b.token.symbol }, b.token.address))) })] })] }), q && !swap.expired ? (_jsxs("div", { style: { display: "grid", gap: 6, padding: 12, borderRadius: t.radius, background: t.accentSoft, fontSize: 13.5, lineHeight: 1.5 }, "data-theoo-quote": "", children: [_jsx("span", { children: q.summary }), q.warning ? _jsx("span", { style: { color: t.alert }, children: q.warning }) : null, _jsxs("span", { style: s.tiny, children: [swap.secondsLeft, "s"] })] })) : null, q && swap.expired ? _jsx("p", { style: s.muted, children: "Quote expired." }) : null, swap.error ? (_jsx("div", { style: s.error, role: "alert", children: swap.error.message })) : null, !q || swap.expired ? (_jsx("button", { type: "button", style: { ...s.button, opacity: !parsed.units || swap.busy ? 0.6 : 1 }, disabled: !parsed.units || swap.busy || !sell || !buy, onClick: () => sell && buy && parsed.units && swap.ask({ sellToken: sell.token.address, buyToken: buy.token.address, sellAmount: parsed.units }), children: swap.busy ? "Getting a quote…" : q ? "Refresh quote" : "Get a quote" })) : (_jsxs("div", { style: { display: "grid", gridTemplateColumns: "1fr auto", gap: 8 }, children: [_jsx("button", { type: "button", style: { ...s.button, opacity: swap.busy ? 0.6 : 1 }, disabled: swap.busy, onClick: () => void swap.confirm(), children: swap.busy ? "Confirming…" : "Confirm" }), _jsx("button", { type: "button", style: s.secondary, disabled: swap.busy, onClick: swap.reset, children: "Cancel" })] })), done ? (_jsxs("p", { style: { ...s.muted, color: done.state === "failed" ? t.alert : t.ink2 }, role: "status", children: [done.state === "included" ? "Swapped. " : done.state === "failed" ? "Swap failed. " : "Processing. ", txLink ? (_jsx("a", { href: txLink, target: "_blank", rel: "noopener noreferrer", style: { color: t.accent }, children: "View transaction" })) : null] })) : null] })) : null, orders && orders.length ? _jsx(Recent, { orders: orders, explorerTx: explorerTx }) : null] }));
|
|
127
125
|
}
|
|
126
|
+
/**
|
|
127
|
+
* A buy, in a few words. Behind them: the payment sits in an escrow on chain,
|
|
128
|
+
* not with the desk. The desk sends ESPEES by hand; the buyer confirms it
|
|
129
|
+
* arrived or disputes it, and a dispute goes to the arbiter. If the desk
|
|
130
|
+
* delivers nothing by the deadline, the escrow refunds the buyer on its own.
|
|
131
|
+
*/
|
|
128
132
|
const ESPEES_WORDS = {
|
|
129
|
-
awaiting_signature: "
|
|
130
|
-
awaiting_delivery: "Paid
|
|
131
|
-
delivered: "
|
|
132
|
-
disputed: "
|
|
133
|
-
completed: "Done
|
|
134
|
-
refunded: "Refunded
|
|
135
|
-
failed: "
|
|
136
|
-
expired: "
|
|
133
|
+
awaiting_signature: "Awaiting confirmation.",
|
|
134
|
+
awaiting_delivery: "Paid. Your ESPEES is on its way.",
|
|
135
|
+
delivered: "Sent. Did it arrive?",
|
|
136
|
+
disputed: "Under review.",
|
|
137
|
+
completed: "Done.",
|
|
138
|
+
refunded: "Refunded.",
|
|
139
|
+
failed: "Failed. Nothing was charged.",
|
|
140
|
+
expired: "Expired. Nothing was charged.",
|
|
137
141
|
};
|
|
138
142
|
/**
|
|
139
143
|
* ESPEES from the desk. The payment goes into an escrow on chain, the desk
|
|
@@ -160,13 +164,13 @@ function EspeesPanel({ chain, onDone, explorerTx }) {
|
|
|
160
164
|
try {
|
|
161
165
|
const units = parseUnits(amount, chosen.token.decimals);
|
|
162
166
|
if (BigInt(units) === 0n)
|
|
163
|
-
return { problem: "Enter an amount
|
|
167
|
+
return { problem: "Enter an amount." };
|
|
164
168
|
if (held && BigInt(units) > BigInt(held.amount))
|
|
165
|
-
return { problem:
|
|
169
|
+
return { problem: "Not enough balance." };
|
|
166
170
|
return { units };
|
|
167
171
|
}
|
|
168
172
|
catch {
|
|
169
|
-
return { problem: "
|
|
173
|
+
return { problem: "Invalid amount." };
|
|
170
174
|
}
|
|
171
175
|
}, [amount, chosen, held]);
|
|
172
176
|
const estimate = chosen && parsed.units
|
|
@@ -203,25 +207,32 @@ function EspeesPanel({ chain, onDone, explorerTx }) {
|
|
|
203
207
|
await reloadOrders();
|
|
204
208
|
};
|
|
205
209
|
if (!e.info)
|
|
206
|
-
return _jsx("p", { style: s.muted, children: "Loading
|
|
210
|
+
return _jsx("p", { style: s.muted, children: "Loading\u2026" });
|
|
207
211
|
if (!e.info.open)
|
|
208
|
-
return _jsx("p", { style: s.muted, children: "
|
|
212
|
+
return _jsx("p", { style: s.muted, children: "Not available right now." });
|
|
209
213
|
const q = e.quote;
|
|
210
214
|
const minutes = Math.round(e.info.deliverWithinSecs / 60);
|
|
211
|
-
return (_jsxs("div", { style: { display: "grid", gap: 12 }, "data-theoo-espees": "", children: [_jsxs("div", { style: { display: "flex", flexWrap: "wrap", justifyContent: "space-between", alignItems: "baseline", gap: "4px 10px" }, children: [_jsx("strong", { style: { fontSize: 15.5, letterSpacing: "-0.01em", whiteSpace: "nowrap" }, children: "Buy ESPEES" }), _jsxs("span", { style: s.tiny, children: ["
|
|
215
|
+
return (_jsxs("div", { style: { display: "grid", gap: 12 }, "data-theoo-espees": "", children: [_jsxs("div", { style: { display: "flex", flexWrap: "wrap", justifyContent: "space-between", alignItems: "baseline", gap: "4px 10px" }, children: [_jsx("strong", { style: { fontSize: 15.5, letterSpacing: "-0.01em", whiteSpace: "nowrap" }, children: "Buy ESPEES" }), _jsxs("span", { style: s.tiny, children: ["Arrives within ", minutes, " min"] })] }), _jsxs("div", { style: { display: "grid", gap: 6 }, children: [_jsxs("div", { style: { display: "flex", justifyContent: "space-between" }, children: [_jsx("label", { htmlFor: "theoo-espees-amount", style: label, children: "You pay" }), held && chosen ? (_jsxs("span", { style: s.tiny, children: ["Balance ", held.display, " ", chosen.token.symbol] })) : null] }), _jsxs("div", { style: row, children: [_jsx("input", { id: "theoo-espees-amount", inputMode: "decimal", autoComplete: "off", placeholder: "0.00", value: amount, onChange: (ev) => setAmount(ev.target.value), style: { ...s.input, fontFamily: t.mono, fontSize: 17 }, "aria-invalid": parsed.problem ? true : undefined }), _jsx("select", { "aria-label": "Pay ESPEES with", value: token, onChange: (ev) => setToken(ev.target.value), style: select, children: tokens.map((d) => (_jsx("option", { value: d.token.address, children: d.token.symbol }, d.token.address))) })] }), parsed.problem ? _jsx("p", { style: { ...s.tiny, color: t.alert }, children: parsed.problem }) : null, chosen ? (_jsxs("p", { style: s.tiny, children: [estimate ? `You get about ${estimate} ESPEES · ` : "", chosen.price, " ESPEES per ", chosen.token.symbol] })) : null] }), _jsxs("div", { style: { display: "grid", gap: 6 }, children: [_jsx("label", { htmlFor: "theoo-espees-address", style: label, children: "Espees address" }), _jsx("input", { id: "theoo-espees-address", autoComplete: "off", value: address, onChange: (ev) => setAddress(ev.target.value), style: s.input }), _jsx("label", { htmlFor: "theoo-espees-again", style: label, children: "Confirm address" }), _jsx("input", { id: "theoo-espees-again", autoComplete: "off", value: again, onChange: (ev) => setAgain(ev.target.value), style: s.input, "aria-invalid": mismatch ? true : undefined }), mismatch ? _jsx("p", { style: { ...s.tiny, color: t.alert }, children: "The two addresses don't match." }) : null] }), q && !e.expired ? (_jsxs("div", { style: { display: "grid", gap: 6, padding: 12, borderRadius: t.radius, background: t.accentSoft, fontSize: 13.5, lineHeight: 1.5 }, "data-theoo-quote": "", children: [_jsx("span", { children: q.summary }), _jsxs("span", { style: s.tiny, children: [e.secondsLeft, "s"] })] })) : null, q && e.expired ? _jsx("p", { style: s.muted, children: "Price expired." }) : null, e.error ? (_jsx("div", { style: s.error, role: "alert", children: e.error.message })) : null, !q || e.expired ? (_jsx("button", { type: "button", style: { ...s.button, opacity: !parsed.units || e.busy ? 0.6 : 1 }, disabled: !parsed.units || e.busy || !chosen || !address.trim() || address.trim() !== again.trim(), onClick: () => chosen &&
|
|
212
216
|
parsed.units &&
|
|
213
|
-
e.ask({ payToken: chosen.token.address, payAmount: parsed.units, espeesAddress: address, espeesAddressConfirm: again }), children: e.busy ? "
|
|
217
|
+
e.ask({ payToken: chosen.token.address, payAmount: parsed.units, espeesAddress: address, espeesAddressConfirm: again }), children: e.busy ? "Getting a price…" : q ? "Refresh price" : "Get a price" })) : (_jsxs("div", { style: { display: "grid", gridTemplateColumns: "1fr auto", gap: 8 }, children: [_jsx("button", { type: "button", style: { ...s.button, opacity: e.busy ? 0.6 : 1 }, disabled: e.busy, onClick: () => void e.confirm(), children: e.busy ? "Paying…" : "Pay" }), _jsx("button", { type: "button", style: s.secondary, disabled: e.busy, onClick: e.reset, children: "Cancel" })] })), latest ? (_jsxs("div", { style: { display: "grid", gap: 8, borderTop: `1px solid ${t.line}`, paddingTop: 10 }, "data-espees-order": latest.state, children: [_jsxs("span", { style: label, children: ["Latest: ", latest.pay ? `${latest.pay.display} ${latest.pay.symbol}` : "", " \u2192 ", latest.get ? `${latest.get.display} ESPEES` : ""] }), _jsx("span", { style: { fontSize: 13.5 }, children: ESPEES_WORDS[latest.state] ?? latest.state }), latest.state === "delivered" ? (_jsxs("div", { style: { display: "flex", gap: 8 }, children: [_jsx("button", { type: "button", style: s.button, disabled: e.busy, onClick: () => void answer(e.release), children: "It arrived" }), _jsx("button", { type: "button", style: s.secondary, disabled: e.busy, onClick: () => void answer(e.dispute), children: "Report a problem" })] })) : null, latest.txHash && explorerTx && latest.chain ? (_jsx("a", { href: explorerTx(latest.txHash, latest.chain) ?? undefined, target: "_blank", rel: "noopener noreferrer", style: { color: t.accent, fontSize: 12.5 }, children: "View transaction" })) : null] })) : null] }));
|
|
214
218
|
}
|
|
219
|
+
/**
|
|
220
|
+
* A sale, in a few words. Behind them: the desk first locks the payout in an
|
|
221
|
+
* escrow on chain, naming this account; only then is the seller told where
|
|
222
|
+
* to send. If the seller sends nothing in time the desk takes its payout
|
|
223
|
+
* back; if the desk says nothing arrived, the arbiter decides. A desk that
|
|
224
|
+
* never locks a payout lets the order lapse, and nothing moves.
|
|
225
|
+
*/
|
|
215
226
|
const SELL_WORDS = {
|
|
216
|
-
awaiting_desk: "
|
|
217
|
-
awaiting_espees: "
|
|
218
|
-
espees_sent: "
|
|
219
|
-
disputed: "
|
|
220
|
-
completed: "Paid.
|
|
221
|
-
expired: "
|
|
222
|
-
rejected: "The
|
|
223
|
-
cancelled: "
|
|
224
|
-
lapsed: "
|
|
227
|
+
awaiting_desk: "Pending. Don't send yet.",
|
|
228
|
+
awaiting_espees: "Ready. Send the ESPEES now.",
|
|
229
|
+
espees_sent: "Confirming receipt.",
|
|
230
|
+
disputed: "Under review.",
|
|
231
|
+
completed: "Paid.",
|
|
232
|
+
expired: "Expired.",
|
|
233
|
+
rejected: "Closed. The ESPEES didn't arrive.",
|
|
234
|
+
cancelled: "Cancelled.",
|
|
235
|
+
lapsed: "Expired.",
|
|
225
236
|
};
|
|
226
237
|
const when = (secs) => (secs ? new Date(secs * 1000).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }) : "");
|
|
227
238
|
/**
|
|
@@ -249,11 +260,11 @@ function SellPanel({ chain }) {
|
|
|
249
260
|
try {
|
|
250
261
|
const units = parseUnits(amount, 18);
|
|
251
262
|
if (BigInt(units) === 0n)
|
|
252
|
-
return { problem: "Enter an amount
|
|
263
|
+
return { problem: "Enter an amount." };
|
|
253
264
|
return { units };
|
|
254
265
|
}
|
|
255
266
|
catch {
|
|
256
|
-
return { problem: "
|
|
267
|
+
return { problem: "Invalid amount." };
|
|
257
268
|
}
|
|
258
269
|
}, [amount]);
|
|
259
270
|
const estimate = chosen && parsed.units ? formatUnits((BigInt(parsed.units) * 10n ** BigInt(chosen.token.decimals)) / BigInt(chosen.priceE18), chosen.token.decimals, 6) : null;
|
|
@@ -264,86 +275,88 @@ function SellPanel({ chain }) {
|
|
|
264
275
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
265
276
|
}, [token, amount, from, again]);
|
|
266
277
|
if (!e.info)
|
|
267
|
-
return _jsx("p", { style: s.muted, children: "Loading
|
|
278
|
+
return _jsx("p", { style: s.muted, children: "Loading\u2026" });
|
|
268
279
|
const o = e.order;
|
|
269
280
|
const q = e.quote;
|
|
270
281
|
const error = e.error ? (_jsx("div", { style: s.error, role: "alert", children: e.error.message })) : null;
|
|
271
282
|
// A sale this session has seen: what the seller does next, or how it ended,
|
|
272
283
|
// until they choose to sell more.
|
|
273
284
|
if (o) {
|
|
274
|
-
return (_jsxs("div", { style: { display: "grid", gap: 10 }, "data-theoo-sell": o.state, children: [_jsxs("strong", { style: { fontSize: 15.5, letterSpacing: "-0.01em" }, children: ["Selling ", o.espees.display, " ESPEES for ", o.receive.display, " ", o.receive.symbol] }), _jsx("span", { style: { fontSize: 13.5, lineHeight: 1.5 }, children: SELL_WORDS[o.state] ?? o.state }), o.state === "awaiting_espees" && o.sendTo ? (_jsxs("div", { style: { display: "grid", gap: 8, padding: 12, borderRadius: t.radius, background: t.accentSoft, fontSize: 13.5, lineHeight: 1.6 }, children: [_jsxs("span", { children: ["Send ", _jsxs("strong", { children: [o.espees.display, " ESPEES"] }), " from ", _jsx("strong", { children: o.espeesFrom }), " to ", _jsx("strong", { "data-sell-send-to": "", children: o.sendTo }), ", with
|
|
285
|
+
return (_jsxs("div", { style: { display: "grid", gap: 10 }, "data-theoo-sell": o.state, children: [_jsxs("strong", { style: { fontSize: 15.5, letterSpacing: "-0.01em" }, children: ["Selling ", o.espees.display, " ESPEES for ", o.receive.display, " ", o.receive.symbol] }), _jsx("span", { style: { fontSize: 13.5, lineHeight: 1.5 }, children: SELL_WORDS[o.state] ?? o.state }), o.state === "awaiting_espees" && o.sendTo ? (_jsxs("div", { style: { display: "grid", gap: 8, padding: 12, borderRadius: t.radius, background: t.accentSoft, fontSize: 13.5, lineHeight: 1.6 }, children: [_jsxs("span", { children: ["Send ", _jsxs("strong", { children: [o.espees.display, " ESPEES"] }), " from ", _jsx("strong", { children: o.espeesFrom }), " to ", _jsx("strong", { "data-sell-send-to": "", children: o.sendTo }), " ", "with note ", _jsx("strong", { style: { fontFamily: t.mono }, "data-sell-memo": "", children: o.memo }), o.deadline ? ` by ${when(o.deadline)}` : "", "."] }), _jsx("label", { htmlFor: "theoo-sell-reference", style: label, children: "Transaction reference" }), _jsx("input", { id: "theoo-sell-reference", autoComplete: "off", value: reference, onChange: (ev) => setReference(ev.target.value), style: s.input }), _jsx("button", { type: "button", style: { ...s.button, opacity: e.busy || reference.trim().length < 3 ? 0.6 : 1 }, disabled: e.busy || reference.trim().length < 3, onClick: () => void e.sent(reference.trim()), children: e.busy ? "Confirming…" : "I've sent it" })] })) : null, o.state === "espees_sent" && o.disputeUntil ? (_jsxs("span", { style: s.tiny, children: ["Payout by ", when(o.disputeUntil)] })) : null, o.state === "awaiting_desk" ? (_jsx("button", { type: "button", style: s.secondary, disabled: e.busy, onClick: () => void e.cancel(), children: "Cancel" })) : null, !e.live ? (_jsx("button", { type: "button", style: s.secondary, onClick: e.dismiss, children: "Sell more" })) : null, error] }));
|
|
275
286
|
}
|
|
276
287
|
if (!sell?.open)
|
|
277
|
-
return _jsx("p", { style: s.muted, children: "
|
|
278
|
-
return (_jsxs("div", { style: { display: "grid", gap: 12 }, "data-theoo-sell": "", children: [
|
|
288
|
+
return _jsx("p", { style: s.muted, children: "Not available right now." });
|
|
289
|
+
return (_jsxs("div", { style: { display: "grid", gap: 12 }, "data-theoo-sell": "", children: [_jsx("div", { style: { display: "flex", flexWrap: "wrap", justifyContent: "space-between", alignItems: "baseline", gap: "4px 10px" }, children: _jsx("strong", { style: { fontSize: 15.5, letterSpacing: "-0.01em", whiteSpace: "nowrap" }, children: "Sell ESPEES" }) }), _jsxs("div", { style: { display: "grid", gap: 6 }, children: [_jsx("label", { htmlFor: "theoo-sell-amount", style: label, children: "You sell" }), _jsxs("div", { style: row, children: [_jsx("input", { id: "theoo-sell-amount", inputMode: "decimal", autoComplete: "off", placeholder: "0.00", value: amount, onChange: (ev) => setAmount(ev.target.value), style: { ...s.input, fontFamily: t.mono, fontSize: 17 }, "aria-invalid": parsed.problem ? true : undefined }), _jsx("select", { "aria-label": "Be paid in", value: token, onChange: (ev) => setToken(ev.target.value), style: select, children: tokens.map((d) => (_jsx("option", { value: d.token.address, children: d.token.symbol }, d.token.address))) })] }), parsed.problem ? _jsx("p", { style: { ...s.tiny, color: t.alert }, children: parsed.problem }) : null, chosen ? (_jsxs("p", { style: s.tiny, children: [estimate ? `You get about ${estimate} ${chosen.token.symbol} · ` : "", chosen.price, " ESPEES per ", chosen.token.symbol] })) : null] }), _jsxs("div", { style: { display: "grid", gap: 6 }, children: [_jsx("label", { htmlFor: "theoo-sell-from", style: label, children: "Espees address" }), _jsx("input", { id: "theoo-sell-from", autoComplete: "off", value: from, onChange: (ev) => setFrom(ev.target.value), style: s.input }), _jsx("label", { htmlFor: "theoo-sell-again", style: label, children: "Confirm address" }), _jsx("input", { id: "theoo-sell-again", autoComplete: "off", value: again, onChange: (ev) => setAgain(ev.target.value), style: s.input, "aria-invalid": mismatch ? true : undefined }), mismatch ? _jsx("p", { style: { ...s.tiny, color: t.alert }, children: "The two addresses don't match." }) : null] }), q && !e.expired ? (_jsxs("div", { style: { display: "grid", gap: 6, padding: 12, borderRadius: t.radius, background: t.accentSoft, fontSize: 13.5, lineHeight: 1.5 }, "data-theoo-quote": "", children: [_jsx("span", { children: q.summary }), _jsxs("span", { style: s.tiny, children: [e.secondsLeft, "s"] })] })) : null, q && e.expired ? _jsx("p", { style: s.muted, children: "Price expired." }) : null, error, !q || e.expired ? (_jsx("button", { type: "button", style: { ...s.button, opacity: !parsed.units || e.busy ? 0.6 : 1 }, disabled: !parsed.units || e.busy || !chosen || !from.trim() || from.trim() !== again.trim(), onClick: () => chosen && parsed.units && e.ask({ receiveToken: chosen.token.address, espeesAmount: parsed.units, espeesFrom: from, espeesFromConfirm: again }), children: e.busy ? "Getting a price…" : q ? "Refresh price" : "Get a price" })) : (_jsxs("div", { style: { display: "grid", gridTemplateColumns: "1fr auto", gap: 8 }, children: [_jsx("button", { type: "button", style: { ...s.button, opacity: e.busy ? 0.6 : 1 }, disabled: e.busy, onClick: () => void e.place(), children: e.busy ? "Placing…" : "Place order" }), _jsx("button", { type: "button", style: s.secondary, disabled: e.busy, onClick: e.reset, children: "Cancel" })] }))] }));
|
|
279
290
|
}
|
|
280
291
|
/** The newest order of a kind, with its state in words. */
|
|
281
292
|
function Latest({ order, words }) {
|
|
282
293
|
if (!order)
|
|
283
294
|
return null;
|
|
284
|
-
return (_jsxs("div", { style: { display: "grid", gap: 4, borderTop: `1px solid ${t.line}`, paddingTop: 10 }, "data-money-latest": order.state, children: [_jsx("span", { style: label, children: "
|
|
295
|
+
return (_jsxs("div", { style: { display: "grid", gap: 4, borderTop: `1px solid ${t.line}`, paddingTop: 10 }, "data-money-latest": order.state, children: [_jsx("span", { style: label, children: "Latest" }), _jsx("span", { style: { fontSize: 13.5 }, children: words[order.state] ?? STATE_WORDS[order.state] ?? order.state })] }));
|
|
285
296
|
}
|
|
286
|
-
|
|
287
|
-
|
|
297
|
+
const CHAIN_NAMES = { 1: "Ethereum", 56: "BNB Chain", 97: "BNB testnet", 8453: "Base", 84532: "Base Sepolia", 11155111: "Sepolia" };
|
|
298
|
+
const chainLabel = (c) => CHAIN_NAMES[c] ?? `chain ${c}`;
|
|
299
|
+
// `received`: the naira (or dollars) reached the user's Bachs balance.
|
|
300
|
+
// `processing`: Bachs is paying it on. `awaiting_payout`: Bachs refused the
|
|
301
|
+
// payout for now (a limit, a balance); the settler tries again.
|
|
302
|
+
const BACHS_WORDS = {
|
|
303
|
+
received: "Received",
|
|
304
|
+
processing: "Processing",
|
|
305
|
+
awaiting_payout: "Pending",
|
|
306
|
+
completed: "Completed",
|
|
307
|
+
failed: "Failed",
|
|
308
|
+
};
|
|
309
|
+
/**
|
|
310
|
+
* Naira in and out through the user's own Bachs account. Bachs does the KYC
|
|
311
|
+
* on its own page and holds the naira in the user's own balance there; Theoo
|
|
312
|
+
* only passes it on, to this user's own account or their own bank.
|
|
313
|
+
*
|
|
314
|
+
* None of that is said on screen, Bachs's name included: the user sees an
|
|
315
|
+
* identity check, then an account number. Bachs's fees (1% of a deposit, at
|
|
316
|
+
* most ₦300, and 1% with a minimum of 1 to pay it on as a stablecoin) show
|
|
317
|
+
* up only as "Fees apply"; the project's own terms carry the detail.
|
|
318
|
+
*/
|
|
319
|
+
function BachsPanel({ mode }) {
|
|
288
320
|
const theoo = useTheoo();
|
|
289
|
-
const {
|
|
290
|
-
const
|
|
291
|
-
const stables = (balances?.balances ?? []).filter((b) => b.token.stable);
|
|
292
|
-
const [token, setToken] = useState("");
|
|
293
|
-
const [fiat, setFiat] = useState("ngn");
|
|
294
|
-
const [amount, setAmount] = useState("");
|
|
295
|
-
const [busy, setBusy] = useState(false);
|
|
321
|
+
const { orders, reload: reloadOrders } = useMoneyOrders(10);
|
|
322
|
+
const [st, setSt] = useState(null);
|
|
296
323
|
const [error, setError] = useState(null);
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
const
|
|
302
|
-
|
|
303
|
-
|
|
324
|
+
const [busy, setBusy] = useState(false);
|
|
325
|
+
const [email, setEmail] = useState("");
|
|
326
|
+
const [first, setFirst] = useState("");
|
|
327
|
+
const [last, setLast] = useState("");
|
|
328
|
+
const [choice, setChoice] = useState("");
|
|
329
|
+
const [banks, setBanks] = useState([]);
|
|
330
|
+
const [bankCode, setBankCode] = useState("");
|
|
331
|
+
const [accountNumber, setAccountNumber] = useState("");
|
|
332
|
+
const [changing, setChanging] = useState(false);
|
|
333
|
+
const load = useCallback(async () => {
|
|
304
334
|
try {
|
|
305
|
-
|
|
306
|
-
window.open(start.url, "_blank", "noopener");
|
|
307
|
-
await reload();
|
|
335
|
+
setSt(await theoo.me.money.bachs.status());
|
|
308
336
|
}
|
|
309
337
|
catch (e) {
|
|
310
338
|
setError(e instanceof Error ? e.message : String(e));
|
|
311
339
|
}
|
|
312
|
-
finally {
|
|
313
|
-
setBusy(false);
|
|
314
|
-
}
|
|
315
|
-
};
|
|
316
|
-
return (_jsxs("div", { style: { display: "grid", gap: 12 }, "data-theoo-buy": "", children: [_jsx("strong", { style: { fontSize: 15.5, letterSpacing: "-0.01em", whiteSpace: "nowrap" }, children: "Add money" }), _jsxs("div", { style: row, children: [_jsx("input", { "aria-label": "Amount to pay", inputMode: "decimal", placeholder: "20000", value: amount, onChange: (e) => setAmount(e.target.value), style: { ...s.input, fontFamily: t.mono, fontSize: 17 } }), _jsxs("select", { "aria-label": "Pay in", value: fiat, onChange: (e) => setFiat(e.target.value), style: select, children: [_jsx("option", { value: "ngn", children: "NGN" }), _jsx("option", { value: "usd", children: "USD" })] })] }), _jsx("select", { "aria-label": "Token to receive", value: token, onChange: (e) => setToken(e.target.value), style: { ...select, width: "100%" }, children: stables.map((b) => (_jsxs("option", { value: b.token.address, children: ["Receive ", b.token.symbol] }, b.token.address))) }), _jsx("p", { style: s.tiny, children: "MoonPay checks your identity and takes the payment, by card or bank transfer. The tokens arrive in this account: its address is fixed in the link, so nobody can change it." }), error ? (_jsx("div", { style: s.error, role: "alert", children: error })) : null, _jsx("button", { type: "button", style: { ...s.button, opacity: busy || !token ? 0.6 : 1 }, disabled: busy || !token, onClick: () => void go(), children: busy ? "Opening MoonPay…" : "Continue to MoonPay" }), _jsx(Latest, { order: (orders ?? []).find((o) => o.kind === "onramp"), words: { awaiting_payment: "Waiting for your payment on MoonPay.", processing: "Paid. MoonPay is sending your tokens.", completed: "Arrived in your account.", failed: "MoonPay couldn't complete it; nothing was taken from this account." } })] }));
|
|
317
|
-
}
|
|
318
|
-
/** Cashing out to a Nigerian bank account through Yellow Card. */
|
|
319
|
-
function CashoutPanel({ chain, sender, onDone }) {
|
|
320
|
-
const theoo = useTheoo();
|
|
321
|
-
const { balances, reload } = useBalances(chain);
|
|
322
|
-
const { orders, reload: reloadOrders } = useMoneyOrders(10);
|
|
323
|
-
const [banks, setBanks] = useState(null);
|
|
324
|
-
const [bank, setBank] = useState("");
|
|
325
|
-
const [number, setNumber] = useState("");
|
|
326
|
-
const [name, setName] = useState(null);
|
|
327
|
-
const [confirmed, setConfirmed] = useState(false);
|
|
328
|
-
const [token, setToken] = useState("");
|
|
329
|
-
const [amount, setAmount] = useState("");
|
|
330
|
-
const [quote, setQuote] = useState(null);
|
|
331
|
-
const [busy, setBusy] = useState(false);
|
|
332
|
-
const [error, setError] = useState(null);
|
|
333
|
-
const stables = (balances?.balances ?? []).filter((b) => b.token.stable);
|
|
334
|
-
const chosen = stables.find((b) => b.token.address === token);
|
|
335
|
-
useEffect(() => {
|
|
336
|
-
theoo.money.banks().then(setBanks, (e) => setError(e instanceof Error ? e.message : String(e)));
|
|
337
340
|
}, [theoo]);
|
|
338
341
|
useEffect(() => {
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
+
void load();
|
|
343
|
+
}, [load]);
|
|
344
|
+
// While Bachs is still deciding, or money is moving, keep it current.
|
|
345
|
+
const waiting = !!st?.linked && (mode === "buy" ? !st.depositsActive : !st.payoutsActive);
|
|
346
|
+
const moving = (orders ?? []).some((o) => (o.kind === "onramp" || o.kind === "offramp") && ["received", "processing", "awaiting_payout"].includes(o.state));
|
|
342
347
|
useEffect(() => {
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
348
|
+
if (!waiting && !moving)
|
|
349
|
+
return;
|
|
350
|
+
const id = setInterval(() => {
|
|
351
|
+
void load();
|
|
352
|
+
void reloadOrders();
|
|
353
|
+
}, 8000);
|
|
354
|
+
return () => clearInterval(id);
|
|
355
|
+
}, [waiting, moving, load, reloadOrders]);
|
|
356
|
+
useEffect(() => {
|
|
357
|
+
if (mode === "cashout" && st?.payoutsActive && banks.length === 0)
|
|
358
|
+
theoo.money.bachsBanks().then(setBanks, () => undefined);
|
|
359
|
+
}, [mode, st?.payoutsActive, banks.length, theoo]);
|
|
347
360
|
const act = async (f) => {
|
|
348
361
|
setBusy(true);
|
|
349
362
|
setError(null);
|
|
@@ -357,25 +370,45 @@ function CashoutPanel({ chain, sender, onDone }) {
|
|
|
357
370
|
setBusy(false);
|
|
358
371
|
}
|
|
359
372
|
};
|
|
360
|
-
|
|
361
|
-
|
|
373
|
+
const verify = () => act(async () => {
|
|
374
|
+
const { url } = await theoo.me.money.bachs.onboard({ email, firstName: first || undefined, lastName: last || undefined, returnUrl: window.location.href.split("#")[0] });
|
|
375
|
+
window.open(url, "_blank", "noopener");
|
|
376
|
+
await load();
|
|
377
|
+
});
|
|
378
|
+
const errorBox = error ? (_jsx("div", { style: s.error, role: "alert", children: error })) : null;
|
|
379
|
+
if (!st)
|
|
380
|
+
return _jsx("p", { style: s.muted, children: "Loading\u2026" });
|
|
381
|
+
// Either the server has no Bachs key, or this project hasn't turned the flow on.
|
|
382
|
+
if (!st.available || (mode === "buy" ? !st.buyEnabled : !st.cashoutEnabled))
|
|
383
|
+
return _jsx("p", { style: s.muted, children: "Not available yet." });
|
|
384
|
+
const head = _jsx("strong", { style: { fontSize: 15.5, letterSpacing: "-0.01em" }, children: mode === "buy" ? "Add money" : "Cash out" });
|
|
385
|
+
if (!st.linked) {
|
|
386
|
+
return (
|
|
387
|
+
// Bachs opens an account in the user's name and checks their ID and BVN on its own page.
|
|
388
|
+
_jsxs("div", { style: { display: "grid", gap: 10 }, "data-theoo-bachs": "unlinked", children: [head, _jsx("label", { htmlFor: "theoo-bachs-email", style: label, children: "Email" }), _jsx("input", { id: "theoo-bachs-email", type: "email", autoComplete: "email", value: email, onChange: (e) => setEmail(e.target.value), style: s.input }), _jsxs("div", { style: row, children: [_jsx("input", { "aria-label": "First name", placeholder: "First name", autoComplete: "given-name", value: first, onChange: (e) => setFirst(e.target.value), style: s.input }), _jsx("input", { "aria-label": "Last name", placeholder: "Last name", autoComplete: "family-name", value: last, onChange: (e) => setLast(e.target.value), style: s.input })] }), errorBox, _jsx("button", { type: "button", style: { ...s.button, opacity: busy || !email.includes("@") ? 0.6 : 1 }, disabled: busy || !email.includes("@"), onClick: () => void verify(), children: busy ? "Opening…" : "Verify identity" })] }));
|
|
362
389
|
}
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
units = chosen && amount.trim() ? parseUnits(amount, chosen.token.decimals) : null;
|
|
390
|
+
if (waiting) {
|
|
391
|
+
return (_jsxs("div", { style: { display: "grid", gap: 10 }, "data-theoo-bachs": "verifying", children: [head, _jsx("p", { style: s.muted, children: "Verification in progress." }), errorBox, _jsx("button", { type: "button", style: s.secondary, disabled: busy, onClick: () => void verify(), children: "Continue verification" })] }));
|
|
366
392
|
}
|
|
367
|
-
|
|
368
|
-
|
|
393
|
+
const mine = (orders ?? []).filter((o) => o.kind === (mode === "buy" ? "onramp" : "offramp")).slice(0, 4);
|
|
394
|
+
const history = mine.length ? (_jsx("div", { style: { display: "grid", gap: 6, borderTop: `1px solid ${t.line}`, paddingTop: 10 }, children: mine.map((o) => (_jsxs("div", { style: { display: "flex", justifyContent: "space-between", gap: 8, fontSize: 13 }, "data-bachs-order": o.state, children: [_jsxs("span", { children: [o.pay ? `${o.pay.display} ${o.pay.symbol}` : "", o.get ? ` → ${o.get.display} ${o.get.symbol}` : ""] }), _jsx("span", { style: s.tiny, children: BACHS_WORDS[o.state] ?? o.state })] }, o.id))) })) : null;
|
|
395
|
+
if (mode === "buy") {
|
|
396
|
+
if (!st.receive || changing) {
|
|
397
|
+
const options = st.receiveOptions;
|
|
398
|
+
const selected = choice || (options[0] ? `${options[0].chain}:${options[0].asset}` : "");
|
|
399
|
+
return (_jsxs("div", { style: { display: "grid", gap: 10 }, "data-theoo-bachs": "choose", children: [head, _jsx("label", { htmlFor: "theoo-bachs-receive", style: label, children: "Receive as" }), options.length === 0 ? (_jsx("p", { style: s.muted, children: "Not available yet." })) : (_jsx("select", { id: "theoo-bachs-receive", value: selected, onChange: (e) => setChoice(e.target.value), style: { ...select, width: "100%" }, children: options.map((o) => (_jsxs("option", { value: `${o.chain}:${o.asset}`, children: [o.asset, " on ", chainLabel(o.chain)] }, `${o.chain}:${o.asset}`))) })), errorBox, _jsx("button", { type: "button", style: { ...s.button, opacity: busy || !selected ? 0.6 : 1 }, disabled: busy || !selected, onClick: () => void act(async () => {
|
|
400
|
+
const [c, a] = selected.split(":");
|
|
401
|
+
setSt(await theoo.me.money.bachs.setReceive(Number(c), a));
|
|
402
|
+
setChanging(false);
|
|
403
|
+
}), children: busy ? "Saving…" : "Continue" })] }));
|
|
404
|
+
}
|
|
405
|
+
return (_jsxs("div", { style: { display: "grid", gap: 10 }, "data-theoo-bachs": "ready", children: [head, st.nairaAccount ? (_jsxs("div", { style: { display: "grid", gap: 4, padding: 12, borderRadius: t.radius, background: t.accentSoft }, children: [_jsx("span", { style: label, children: "Send naira to" }), _jsx("strong", { style: { fontFamily: t.mono, fontSize: 20, letterSpacing: "0.04em" }, "data-bachs-account-number": "", children: st.nairaAccount.accountNumber }), _jsx("span", { style: { fontSize: 13.5 }, children: st.nairaAccount.bankName })] })) : (_jsx("p", { style: s.muted, children: "Setting up your account number\u2026" })), _jsxs("p", { style: s.tiny, children: ["Arrives as ", st.receive.asset, " on ", chainLabel(st.receive.chain), ". Fees apply.", " ", _jsx("button", { type: "button", style: link, onClick: () => setChanging(true), children: "Change" })] }), errorBox, history] }));
|
|
369
406
|
}
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
onDone?.(status);
|
|
376
|
-
await reloadOrders();
|
|
377
|
-
[0, 2000, 5000].forEach((ms) => setTimeout(() => void reload(), ms));
|
|
378
|
-
}), children: busy ? "Sending…" : "Send with passkey" })), _jsx(Latest, { order: (orders ?? []).find((o) => o.kind === "offramp"), words: { awaiting_signature: "Waiting for your passkey.", processing: "Sent. Yellow Card is paying the bank.", completed: "Paid into the bank account.", refunded: "The payout failed; your tokens came back to this account.", failed: "The transfer failed on chain; nothing was taken." } })] }));
|
|
407
|
+
// Cashing out.
|
|
408
|
+
return (_jsxs("div", { style: { display: "grid", gap: 10 }, "data-theoo-bachs": "cashout", children: [head, st.bank && !changing ? (_jsxs("div", { style: { display: "grid", gap: 4, padding: 12, borderRadius: t.radius, background: t.accentSoft }, children: [_jsx("span", { style: label, children: "Paid to" }), _jsx("strong", { style: { fontSize: 15 }, "data-bachs-account-name": "", children: st.bank.accountName }), _jsxs("span", { style: { fontSize: 13.5 }, children: [st.bank.bankName, " \u2022\u2022", st.bank.last4] }), _jsx("button", { type: "button", style: { ...link, justifySelf: "start" }, onClick: () => setChanging(true), children: "Change" })] })) : (_jsxs(_Fragment, { children: [_jsx("label", { htmlFor: "theoo-bachs-bank", style: label, children: "Your bank" }), _jsxs("select", { id: "theoo-bachs-bank", value: bankCode, onChange: (e) => setBankCode(e.target.value), style: { ...select, width: "100%" }, children: [_jsx("option", { value: "", children: "Choose your bank" }), banks.map((b) => (_jsx("option", { value: b.code, children: b.name }, b.code)))] }), _jsx("label", { htmlFor: "theoo-bachs-account", style: label, children: "Account number" }), _jsx("input", { id: "theoo-bachs-account", inputMode: "numeric", autoComplete: "off", maxLength: 10, value: accountNumber, onChange: (e) => setAccountNumber(e.target.value.replace(/\D/g, "")), style: { ...s.input, fontFamily: t.mono } }), _jsx("p", { style: s.tiny, children: "Must be in your name." }), _jsx("button", { type: "button", style: { ...s.button, opacity: busy || !bankCode || accountNumber.length !== 10 ? 0.6 : 1 }, disabled: busy || !bankCode || accountNumber.length !== 10, onClick: () => void act(async () => {
|
|
409
|
+
setSt(await theoo.me.money.bachs.setBank(bankCode, accountNumber));
|
|
410
|
+
setChanging(false);
|
|
411
|
+
}), children: busy ? "Checking…" : "Save" })] })), !st.cashoutPayInReady ? _jsx("p", { style: s.muted, children: "Coming soon." }) : null, errorBox, history] }));
|
|
379
412
|
}
|
|
380
413
|
function Recent({ orders, explorerTx }) {
|
|
381
414
|
return (_jsxs("div", { style: { display: "grid", gap: 6, borderTop: `1px solid ${t.line}`, paddingTop: 10 }, children: [_jsx("span", { style: label, children: "Recent" }), orders.map((o) => {
|