mirror of
https://gitlab.com/soapbox-pub/mkstack.git
synced 2025-08-27 13:09:22 +00:00
cleanup + icons
This commit is contained in:
parent
ec1a9777de
commit
55bf545646
@ -1,5 +1,5 @@
|
||||
import { useState, useEffect, useRef } from 'react';
|
||||
import { Zap, Copy } from 'lucide-react';
|
||||
import { Zap, Copy, Sparkle, Sparkles, Star, Rocket } from 'lucide-react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
Dialog,
|
||||
@ -18,6 +18,7 @@ import { useAuthor } from '@/hooks/useAuthor';
|
||||
import { useToast } from '@/hooks/useToast';
|
||||
import { useZaps } from '@/hooks/useZaps';
|
||||
import type { WebLNProvider } from 'webln';
|
||||
import { requestProvider } from 'webln';
|
||||
import QRCode from 'qrcode';
|
||||
import type { Event } from 'nostr-tools';
|
||||
|
||||
@ -27,11 +28,17 @@ interface ZapDialogProps {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const presetAmounts = [1, 50, 100, 250, 1000];
|
||||
const presetAmounts = [
|
||||
{ amount: 1, icon: Sparkle },
|
||||
{ amount: 50, icon: Sparkles },
|
||||
{ amount: 100, icon: Zap },
|
||||
{ amount: 250, icon: Star },
|
||||
{ amount: 1000, icon: Rocket },
|
||||
];
|
||||
|
||||
export function ZapDialog({ target, children, className }: ZapDialogProps) {
|
||||
const [open, setOpen] = useState(false);
|
||||
const [webln, _setWebln] = useState<WebLNProvider | null>(null);
|
||||
const [webln, setWebln] = useState<WebLNProvider | null>(null);
|
||||
const { user } = useCurrentUser();
|
||||
const { data: author } = useAuthor(target.pubkey);
|
||||
const { toast } = useToast();
|
||||
@ -47,6 +54,23 @@ export function ZapDialog({ target, children, className }: ZapDialogProps) {
|
||||
}
|
||||
}, [target]);
|
||||
|
||||
// Detect WebLN when dialog opens
|
||||
useEffect(() => {
|
||||
const detectWebLN = async () => {
|
||||
if (open && !webln) {
|
||||
try {
|
||||
const provider = await requestProvider();
|
||||
setWebln(provider);
|
||||
} catch (error) {
|
||||
console.warn('WebLN requestProvider failed:', error);
|
||||
setWebln(null);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
detectWebLN();
|
||||
}, [open, webln]);
|
||||
|
||||
useEffect(() => {
|
||||
if (invoice && qrCodeRef.current) {
|
||||
QRCode.toCanvas(qrCodeRef.current, invoice, { width: 256 });
|
||||
@ -81,8 +105,8 @@ export function ZapDialog({ target, children, className }: ZapDialogProps) {
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={setOpen}>
|
||||
<DialogTrigger asChild>
|
||||
<Button size="sm" className={className}>
|
||||
<Zap className={`h-4 w-4 ${children ? 'mr-2' : ''}`} />
|
||||
<Button variant="ghost" size="sm" className={`text-muted-foreground hover:text-yellow-600 ${className || ''}`}>
|
||||
<Zap className={`h-4 w-4 ${children ? 'mr-1' : ''}`} />
|
||||
{children}
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
@ -123,12 +147,13 @@ export function ZapDialog({ target, children, className }: ZapDialogProps) {
|
||||
}}
|
||||
className="grid grid-cols-5 gap-2"
|
||||
>
|
||||
{presetAmounts.map((presetAmount) => (
|
||||
{presetAmounts.map(({ amount: presetAmount, icon: Icon }) => (
|
||||
<ToggleGroupItem
|
||||
key={presetAmount}
|
||||
value={String(presetAmount)}
|
||||
className="flex flex-col h-auto"
|
||||
>
|
||||
<Icon className="h-5 w-5 mb-1.5" />
|
||||
{presetAmount}
|
||||
</ToggleGroupItem>
|
||||
))}
|
||||
|
@ -1,11 +1,12 @@
|
||||
import { useState } from 'react';
|
||||
import { useNostrPublish } from '@/hooks/useNostrPublish';
|
||||
import { useCurrentUser } from '@/hooks/useCurrentUser';
|
||||
import { useAuthor } from '@/hooks/useAuthor';
|
||||
import { useAppContext } from '@/hooks/useAppContext';
|
||||
import { useToast } from '@/hooks/useToast';
|
||||
import { nip57, nip19, Event } from 'nostr-tools';
|
||||
import { nip57, nip19 } from 'nostr-tools';
|
||||
import type { Event } from 'nostr-tools';
|
||||
import type { WebLNProvider } from 'webln';
|
||||
import { LNURL } from '@nostrify/nostrify/ln';
|
||||
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { useNostr } from '@nostrify/react';
|
||||
@ -16,7 +17,6 @@ export function useZaps(target: Event, webln: WebLNProvider | null, onZapSuccess
|
||||
const { toast } = useToast();
|
||||
const { user } = useCurrentUser();
|
||||
const { config } = useAppContext();
|
||||
const { mutate: publishEvent } = useNostrPublish();
|
||||
const author = useAuthor(target?.pubkey);
|
||||
const [isZapping, setIsZapping] = useState(false);
|
||||
const [invoice, setInvoice] = useState<string | null>(null);
|
||||
@ -86,18 +86,8 @@ export function useZaps(target: Event, webln: WebLNProvider | null, onZapSuccess
|
||||
}
|
||||
|
||||
try {
|
||||
const lud16 = author.data?.metadata?.lud16;
|
||||
if (!lud16) {
|
||||
toast({
|
||||
title: 'Lightning address not found',
|
||||
description: 'The author does not have a lightning address configured.',
|
||||
variant: 'destructive',
|
||||
});
|
||||
setIsZapping(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!author.data) {
|
||||
if (!author.data || !author.data?.metadata) {
|
||||
toast({
|
||||
title: 'Author not found',
|
||||
description: 'Could not find the author of this item.',
|
||||
@ -107,67 +97,50 @@ export function useZaps(target: Event, webln: WebLNProvider | null, onZapSuccess
|
||||
return;
|
||||
}
|
||||
|
||||
const zapEndpoint = await nip57.getZapEndpoint(author.data.event as Event);
|
||||
if (!zapEndpoint) {
|
||||
const { lud06, lud16 } = author.data.metadata;
|
||||
if (!lud16 && !lud06) {
|
||||
toast({
|
||||
title: 'Zap endpoint not found',
|
||||
description: 'Could not find a zap endpoint for the author.',
|
||||
title: 'Lightning address not found',
|
||||
description: 'The author does not have a lightning address (lud16 or lud06) configured.',
|
||||
variant: 'destructive',
|
||||
});
|
||||
setIsZapping(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const lnurl = lud06 ? LNURL.fromString(lud06) : LNURL.fromLightningAddress(lud16!);
|
||||
const zapAmount = amount * 1000; // convert to millisats
|
||||
const relays = [config.relayUrl];
|
||||
const zapRequest = nip57.makeZapRequest({
|
||||
const zapRequest = await user.signer.signEvent(nip57.makeZapRequest({
|
||||
profile: target.pubkey,
|
||||
event: target.id,
|
||||
event: target,
|
||||
amount: zapAmount,
|
||||
relays,
|
||||
relays: [config.relayUrl],
|
||||
comment: comment,
|
||||
}));
|
||||
|
||||
const { pr: newInvoice } = await lnurl.getInvoice({
|
||||
amount: zapAmount,
|
||||
nostr: zapRequest,
|
||||
});
|
||||
|
||||
if (naddr) {
|
||||
const decoded = nip19.decode(naddr).data as nip19.AddressPointer;
|
||||
zapRequest.tags.push(["a", `${decoded.kind}:${decoded.pubkey}:${decoded.identifier}`]);
|
||||
zapRequest.tags = zapRequest.tags.filter(t => t[0] !== 'e');
|
||||
if (webln) {
|
||||
await webln.sendPayment(newInvoice);
|
||||
toast({
|
||||
title: 'Zap successful!',
|
||||
description: `You sent ${amount} sats to the author.`,
|
||||
});
|
||||
onZapSuccess?.();
|
||||
} else {
|
||||
setInvoice(newInvoice);
|
||||
}
|
||||
|
||||
publishEvent(zapRequest, {
|
||||
onSuccess: async (event) => {
|
||||
try {
|
||||
const res = await fetch(`${zapEndpoint}?amount=${zapAmount}&nostr=${encodeURI(JSON.stringify(event))}`);
|
||||
const { pr: newInvoice } = await res.json();
|
||||
|
||||
if (webln) {
|
||||
await webln.sendPayment(newInvoice);
|
||||
toast({
|
||||
title: 'Zap successful!',
|
||||
description: `You sent ${amount} sats to the author.`,
|
||||
});
|
||||
onZapSuccess?.();
|
||||
} else {
|
||||
setInvoice(newInvoice);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Zap error:', err);
|
||||
toast({
|
||||
title: 'Zap failed',
|
||||
description: (err as Error).message,
|
||||
variant: 'destructive',
|
||||
});
|
||||
} finally {
|
||||
setIsZapping(false);
|
||||
}
|
||||
},
|
||||
});
|
||||
} catch (err) {
|
||||
console.error('Zap error:', err);
|
||||
toast({
|
||||
title: 'Zap failed',
|
||||
description: (err as Error).message,
|
||||
variant: 'destructive',
|
||||
});
|
||||
} finally {
|
||||
setIsZapping(false);
|
||||
}
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user