mirror of
https://github.com/aljazceru/rabbit.git
synced 2025-12-17 05:54:19 +01:00
feat: show check mark when zapped
This commit is contained in:
@@ -12,6 +12,8 @@ import {
|
|||||||
|
|
||||||
import { createMutation } from '@tanstack/solid-query';
|
import { createMutation } from '@tanstack/solid-query';
|
||||||
import Bolt from 'heroicons/24/outline/bolt.svg';
|
import Bolt from 'heroicons/24/outline/bolt.svg';
|
||||||
|
import Check from 'heroicons/24/solid/check.svg';
|
||||||
|
import * as Kind from 'nostr-tools/kinds';
|
||||||
import { type Event as NostrEvent } from 'nostr-tools/pure';
|
import { type Event as NostrEvent } from 'nostr-tools/pure';
|
||||||
import qrcode from 'qrcode';
|
import qrcode from 'qrcode';
|
||||||
import { requestProvider, type WebLNProvider } from 'webln';
|
import { requestProvider, type WebLNProvider } from 'webln';
|
||||||
@@ -23,15 +25,18 @@ import useConfig from '@/core/useConfig';
|
|||||||
import { useTranslation } from '@/i18n/useTranslation';
|
import { useTranslation } from '@/i18n/useTranslation';
|
||||||
import createZapRequest from '@/nostr/builder/createZapRequest';
|
import createZapRequest from '@/nostr/builder/createZapRequest';
|
||||||
import { genericEvent } from '@/nostr/event';
|
import { genericEvent } from '@/nostr/event';
|
||||||
|
import ZapReceipt from '@/nostr/event/ZapReceipt';
|
||||||
import { signEvent } from '@/nostr/useCommands';
|
import { signEvent } from '@/nostr/useCommands';
|
||||||
import useLnurlEndpoint from '@/nostr/useLnurlEndpoint';
|
import useLnurlEndpoint from '@/nostr/useLnurlEndpoint';
|
||||||
import useProfile from '@/nostr/useProfile';
|
import useProfile from '@/nostr/useProfile';
|
||||||
import usePubkey from '@/nostr/usePubkey';
|
import usePubkey from '@/nostr/usePubkey';
|
||||||
|
import useSubscription from '@/nostr/useSubscription';
|
||||||
import fetchLnurlCallback, { type FetchLnurlCallbackParams } from '@/nostr/zap/fetchLnurlCallback';
|
import fetchLnurlCallback, { type FetchLnurlCallbackParams } from '@/nostr/zap/fetchLnurlCallback';
|
||||||
import lud06ToLnurlPayUrl from '@/nostr/zap/lud06ToLnurlPayUrl';
|
import lud06ToLnurlPayUrl from '@/nostr/zap/lud06ToLnurlPayUrl';
|
||||||
import lud16ToLnurlPayUrl from '@/nostr/zap/lud16ToLnurlPayUrl';
|
import lud16ToLnurlPayUrl from '@/nostr/zap/lud16ToLnurlPayUrl';
|
||||||
import verifyInvoice from '@/nostr/zap/verifyInvoice';
|
import verifyInvoice from '@/nostr/zap/verifyInvoice';
|
||||||
import ensureNonNull from '@/utils/ensureNonNull';
|
import ensureNonNull from '@/utils/ensureNonNull';
|
||||||
|
import epoch from '@/utils/epoch';
|
||||||
|
|
||||||
export type ZapRequestModalProps = {
|
export type ZapRequestModalProps = {
|
||||||
event: NostrEvent;
|
event: NostrEvent;
|
||||||
@@ -90,12 +95,34 @@ const QRCodeDisplay: Component<{ text: string }> = (props) => {
|
|||||||
return <canvas width="256" height="256" ref={canvasRef} />;
|
return <canvas width="256" height="256" ref={canvasRef} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
const InvoiceDisplay: Component<{ invoice: string }> = (props) => {
|
const InvoiceDisplay: Component<{ invoice: string; event: NostrEvent; nostrPubkey?: string }> = (
|
||||||
|
props,
|
||||||
|
) => {
|
||||||
const i18n = useTranslation();
|
const i18n = useTranslation();
|
||||||
|
const { config } = useConfig();
|
||||||
const webln = useWebLN();
|
const webln = useWebLN();
|
||||||
|
|
||||||
const lightingInvoice = () => `lightning:${props.invoice}`;
|
const lightingInvoice = () => `lightning:${props.invoice}`;
|
||||||
|
|
||||||
|
const { events } = useSubscription(() =>
|
||||||
|
ensureNonNull([props.nostrPubkey] as const)(([nostrPubkey]) => ({
|
||||||
|
relayUrls: config().relayUrls,
|
||||||
|
filters: [
|
||||||
|
{
|
||||||
|
kinds: [Kind.Zap],
|
||||||
|
authors: nostrPubkey != null ? [nostrPubkey] : undefined,
|
||||||
|
'#p': [props.event.pubkey],
|
||||||
|
'#e': [props.event.id],
|
||||||
|
since: epoch(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
continuous: true,
|
||||||
|
})),
|
||||||
|
);
|
||||||
|
|
||||||
|
const zapped = () =>
|
||||||
|
events().find((ev) => new ZapReceipt(ev).bolt11().paymentRequest === props.invoice);
|
||||||
|
|
||||||
const handleClickWebLN = () => {
|
const handleClickWebLN = () => {
|
||||||
const provider = webln.provider();
|
const provider = webln.provider();
|
||||||
if (provider == null) return;
|
if (provider == null) return;
|
||||||
@@ -112,26 +139,38 @@ const InvoiceDisplay: Component<{ invoice: string }> = (props) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class="flex flex-col items-center gap-2">
|
<Show
|
||||||
<div>
|
when={zapped()}
|
||||||
<QRCodeDisplay text={lightingInvoice()} />
|
fallback={
|
||||||
</div>
|
<div class="flex flex-col items-center gap-4 py-8">
|
||||||
<a
|
<span class="inline-block h-28 w-28 rounded-full border-4 border-primary p-4 text-primary">
|
||||||
class="inline-block rounded bg-primary p-4 font-bold text-primary-fg hover:bg-primary-hover"
|
<Check />
|
||||||
href={lightingInvoice()}
|
</span>
|
||||||
>
|
<div class="text-secondary text-xl">{i18n()('zap.completed')}</div>
|
||||||
{i18n()('zap.sendViaWallet')}
|
</div>
|
||||||
</a>
|
}
|
||||||
<Show when={webln.status() === 'available'}>
|
>
|
||||||
<button
|
<div class="flex flex-col items-center gap-2">
|
||||||
type="button"
|
<div>
|
||||||
|
<QRCodeDisplay text={lightingInvoice()} />
|
||||||
|
</div>
|
||||||
|
<a
|
||||||
class="inline-block rounded bg-primary p-4 font-bold text-primary-fg hover:bg-primary-hover"
|
class="inline-block rounded bg-primary p-4 font-bold text-primary-fg hover:bg-primary-hover"
|
||||||
onClick={handleClickWebLN}
|
href={lightingInvoice()}
|
||||||
>
|
>
|
||||||
{i18n()('zap.sendViaWebLN')}
|
{i18n()('zap.sendViaWallet')}
|
||||||
</button>
|
</a>
|
||||||
</Show>
|
<Show when={webln.status() === 'available'}>
|
||||||
</div>
|
<button
|
||||||
|
type="button"
|
||||||
|
class="inline-block rounded bg-primary p-4 font-bold text-primary-fg hover:bg-primary-hover"
|
||||||
|
onClick={handleClickWebLN}
|
||||||
|
>
|
||||||
|
{i18n()('zap.sendViaWebLN')}
|
||||||
|
</button>
|
||||||
|
</Show>
|
||||||
|
</div>
|
||||||
|
</Show>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -250,7 +289,13 @@ const ZapDialog: Component<ZapDialogProps> = (props) => {
|
|||||||
{i18n()('zap.fetchingLnUrlInvoiceError')}: {getInvoiceMutation?.error?.message}
|
{i18n()('zap.fetchingLnUrlInvoiceError')}: {getInvoiceMutation?.error?.message}
|
||||||
</Match>
|
</Match>
|
||||||
<Match when={getInvoiceMutation.isSuccess && getInvoiceMutation.data} keyed>
|
<Match when={getInvoiceMutation.isSuccess && getInvoiceMutation.data} keyed>
|
||||||
{(invoice) => <InvoiceDisplay invoice={invoice} />}
|
{(invoice) => (
|
||||||
|
<InvoiceDisplay
|
||||||
|
invoice={invoice}
|
||||||
|
event={props.event}
|
||||||
|
nostrPubkey={endpoint()?.nostrPubkey}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</Match>
|
</Match>
|
||||||
<Match when={query.isSuccess}>
|
<Match when={query.isSuccess}>
|
||||||
<div class="flex flex-col items-center">
|
<div class="flex flex-col items-center">
|
||||||
|
|||||||
@@ -138,6 +138,7 @@ export default {
|
|||||||
comment: 'Comment (optional)',
|
comment: 'Comment (optional)',
|
||||||
sendViaWallet: 'Send via wallet',
|
sendViaWallet: 'Send via wallet',
|
||||||
sendViaWebLN: 'Send via extension',
|
sendViaWebLN: 'Send via extension',
|
||||||
|
completed: 'Completed',
|
||||||
},
|
},
|
||||||
config: {
|
config: {
|
||||||
config: 'Settings',
|
config: 'Settings',
|
||||||
|
|||||||
@@ -134,6 +134,7 @@ export default {
|
|||||||
comment: 'コメント (任意)',
|
comment: 'コメント (任意)',
|
||||||
sendViaWallet: 'ウォレットで送る',
|
sendViaWallet: 'ウォレットで送る',
|
||||||
sendViaWebLN: '拡張機能で送る',
|
sendViaWebLN: '拡張機能で送る',
|
||||||
|
completed: '完了',
|
||||||
},
|
},
|
||||||
config: {
|
config: {
|
||||||
config: '設定',
|
config: '設定',
|
||||||
|
|||||||
Reference in New Issue
Block a user