feat: withdraw button
This commit is contained in:
parent
890f5b2987
commit
e3e04bd796
874
.yarn/releases/yarn-3.6.3.cjs
vendored
874
.yarn/releases/yarn-3.6.3.cjs
vendored
File diff suppressed because one or more lines are too long
893
.yarn/releases/yarn-4.1.1.cjs
vendored
Executable file
893
.yarn/releases/yarn-4.1.1.cjs
vendored
Executable file
File diff suppressed because one or more lines are too long
@ -1 +1 @@
|
||||
yarnPath: .yarn/releases/yarn-3.6.3.cjs
|
||||
yarnPath: .yarn/releases/yarn-4.1.1.cjs
|
||||
|
12
package.json
12
package.json
@ -6,11 +6,11 @@
|
||||
"@emoji-mart/react": "^1.1.1",
|
||||
"@noble/curves": "^1.2.0",
|
||||
"@scure/base": "^1.1.3",
|
||||
"@snort/shared": "^1.0.14",
|
||||
"@snort/system": "^1.2.12",
|
||||
"@snort/system-react": "^1.2.12",
|
||||
"@snort/shared": "^1.0.15",
|
||||
"@snort/system": "^1.3.0",
|
||||
"@snort/system-react": "^1.3.0",
|
||||
"@snort/system-wasm": "^1.0.2",
|
||||
"@snort/worker-relay": "^1.0.9",
|
||||
"@snort/worker-relay": "^1.0.10",
|
||||
"@sqlite.org/sqlite-wasm": "^3.45.1-build1",
|
||||
"@szhsin/react-menu": "^4.0.2",
|
||||
"@types/webscopeio__react-textarea-autocomplete": "^4.7.2",
|
||||
@ -90,11 +90,11 @@
|
||||
"rollup-plugin-visualizer": "^5.10.0",
|
||||
"tailwindcss": "^3.3.5",
|
||||
"typescript": "^5.2.2",
|
||||
"vite": "^5.1.4",
|
||||
"vite": "^5.2.10",
|
||||
"vite-plugin-pwa": "^0.17.2",
|
||||
"vite-plugin-version-mark": "^0.0.10"
|
||||
},
|
||||
"packageManager": "yarn@3.6.3",
|
||||
"packageManager": "yarn@4.1.1",
|
||||
"prettier": {
|
||||
"printWidth": 120,
|
||||
"bracketSameLine": true,
|
||||
|
@ -77,7 +77,7 @@ export function LiveChat({
|
||||
rb.withFilter().kinds([LIVE_STREAM_CHAT, LIVE_STREAM_RAID, LIVE_STREAM_CLIP]).tag("a", [aTag]).limit(200);
|
||||
}
|
||||
},
|
||||
true,
|
||||
true
|
||||
);
|
||||
const login = useLogin();
|
||||
const started = useMemo(() => {
|
||||
|
@ -13,6 +13,7 @@ import Pill from "@/element/pill";
|
||||
import { AddForwardInputs } from "./fowards";
|
||||
import StreamKey from "./stream-key";
|
||||
import AccountTopup from "./topup";
|
||||
import AccountWithdrawl from "./withdraw";
|
||||
|
||||
export default function NostrProviderDialog({
|
||||
provider,
|
||||
@ -179,12 +180,8 @@ export default function NostrProviderDialog({
|
||||
values={{ amount: info.balance?.toLocaleString() }}
|
||||
/>
|
||||
</div>
|
||||
<AccountTopup
|
||||
provider={provider}
|
||||
onFinish={async () => {
|
||||
loadInfo();
|
||||
}}
|
||||
/>
|
||||
<AccountTopup provider={provider} onFinish={loadInfo} />
|
||||
<AccountWithdrawl provider={provider} onFinish={loadInfo} />
|
||||
</div>
|
||||
<small>
|
||||
<FormattedMessage defaultMessage="About {estimate}" id="Q3au2v" values={{ estimate: calcEstimate() }} />
|
||||
|
111
src/element/provider/nostr/withdraw.tsx
Normal file
111
src/element/provider/nostr/withdraw.tsx
Normal file
@ -0,0 +1,111 @@
|
||||
import { DefaultButton } from "@/element/buttons";
|
||||
import Modal from "@/element/modal";
|
||||
import { NostrStreamProvider } from "@/providers";
|
||||
import { LNURL } from "@snort/shared";
|
||||
import { useEffect, useState } from "react";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
|
||||
export default function AccountWithdrawl({
|
||||
provider,
|
||||
onFinish,
|
||||
}: {
|
||||
provider: NostrStreamProvider;
|
||||
onFinish: () => void;
|
||||
}) {
|
||||
const [topup, setTopup] = useState(false);
|
||||
const [addr, setAddress] = useState("");
|
||||
const [error, setError] = useState("");
|
||||
const [amount, setAmount] = useState<number>();
|
||||
const { formatMessage } = useIntl();
|
||||
|
||||
useEffect(() => {
|
||||
if (addr.startsWith("lnbc") && amount !== undefined) {
|
||||
setAmount(undefined);
|
||||
} else if (!addr.startsWith("lnbc") && amount === undefined) {
|
||||
setAmount(0);
|
||||
}
|
||||
}, [addr, amount]);
|
||||
|
||||
async function withdraw() {
|
||||
setError("");
|
||||
let invoice = addr;
|
||||
if (!invoice.startsWith("lnbc") && amount && amount > 1) {
|
||||
const invoiceError = formatMessage({
|
||||
defaultMessage: "Failed to get invoice",
|
||||
});
|
||||
try {
|
||||
const lnurl = new LNURL(addr);
|
||||
await lnurl.load();
|
||||
const rsp = await lnurl.getInvoice(amount, "Withdrawal from zap.stream");
|
||||
if (rsp.pr) {
|
||||
invoice = rsp.pr;
|
||||
} else {
|
||||
setError(rsp.reason ?? invoiceError);
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
setError(e.message);
|
||||
} else {
|
||||
setError(invoiceError);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await provider.withdraw(invoice);
|
||||
if (res.preimage) {
|
||||
setTopup(false);
|
||||
onFinish();
|
||||
}
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
setError(e.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<DefaultButton onClick={() => setTopup(true)}>
|
||||
<FormattedMessage defaultMessage="Withdraw" />
|
||||
</DefaultButton>
|
||||
{topup && (
|
||||
<Modal id="withdraw" onClose={() => setTopup(false)}>
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="text-xl">
|
||||
<FormattedMessage defaultMessage="Withdraw funds" />
|
||||
</div>
|
||||
<div className="flex flex-col gap-1">
|
||||
<small className="text-neutral-300">
|
||||
<FormattedMessage defaultMessage="Destination" />
|
||||
</small>
|
||||
<input
|
||||
type="text"
|
||||
value={addr}
|
||||
onChange={e => setAddress(e.target.value)}
|
||||
placeholder={formatMessage({
|
||||
defaultMessage: "LNURL or invoice",
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
{amount !== undefined && (
|
||||
<>
|
||||
<div className="flex flex-col gap-1">
|
||||
<small className="text-neutral-300">
|
||||
<FormattedMessage defaultMessage="Amount" />
|
||||
</small>
|
||||
<input type="number" value={amount} onChange={e => setAmount(e.target.valueAsNumber)} />
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
<DefaultButton disabled={addr.length < 3} onClick={withdraw}>
|
||||
<FormattedMessage defaultMessage="Withdraw" />
|
||||
</DefaultButton>
|
||||
{error && <b className="text-warning">{error}</b>}
|
||||
</div>
|
||||
</Modal>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
@ -15,7 +15,7 @@ export default function VideoGridSorted({ evs, showAll }: { evs: Array<TaggedNos
|
||||
(ev: NostrEvent) => {
|
||||
return tags.find(t => t.at(1) === getHost(ev));
|
||||
},
|
||||
[tags],
|
||||
[tags]
|
||||
);
|
||||
const { live, planned, ended } = useSortedStreams(evs, showAll ? 0 : undefined);
|
||||
const hashtags = getTagValues(tags, "t");
|
||||
|
@ -19,7 +19,7 @@ const LangSelector = new LangStore();
|
||||
export function useLang() {
|
||||
const store = useSyncExternalStore(
|
||||
c => LangSelector.hook(c),
|
||||
() => LangSelector.snapshot(),
|
||||
() => LangSelector.snapshot()
|
||||
);
|
||||
|
||||
return {
|
||||
|
@ -87,6 +87,10 @@ export class NostrStreamProvider implements StreamProvider {
|
||||
return rsp.pr;
|
||||
}
|
||||
|
||||
async withdraw(invoice: string) {
|
||||
return await this.#getJson<{ fee: number; preimage: string }>("POST", `withdraw?invoice=${invoice}`);
|
||||
}
|
||||
|
||||
async acceptTos(): Promise<void> {
|
||||
await this.#getJson("PATCH", "account", {
|
||||
accept_tos: true,
|
||||
|
Loading…
x
Reference in New Issue
Block a user