mirror of
https://github.com/coracle-social/coracle.git
synced 2024-09-19 11:43:35 +00:00
Factor out NoteZap from NoteActions
This commit is contained in:
parent
66b16c4db4
commit
fa1211a2fd
@ -11,6 +11,7 @@
|
|||||||
import LoginPubKey from "src/app/views/LoginPubKey.svelte"
|
import LoginPubKey from "src/app/views/LoginPubKey.svelte"
|
||||||
import Onboarding from "src/app/views/Onboarding.svelte"
|
import Onboarding from "src/app/views/Onboarding.svelte"
|
||||||
import NoteCreate from "src/app/views/NoteCreate.svelte"
|
import NoteCreate from "src/app/views/NoteCreate.svelte"
|
||||||
|
import NoteZap from "src/app/views/NoteZap.svelte"
|
||||||
import NoteDetail from "src/app/views/NoteDetail.svelte"
|
import NoteDetail from "src/app/views/NoteDetail.svelte"
|
||||||
import PersonFeed from "src/app/views/PersonFeed.svelte"
|
import PersonFeed from "src/app/views/PersonFeed.svelte"
|
||||||
import PersonList from "src/app/shared/PersonList.svelte"
|
import PersonList from "src/app/shared/PersonList.svelte"
|
||||||
@ -39,6 +40,8 @@
|
|||||||
{/key}
|
{/key}
|
||||||
{:else if m.type === "note/create"}
|
{:else if m.type === "note/create"}
|
||||||
<NoteCreate pubkey={m.pubkey} nevent={m.nevent} />
|
<NoteCreate pubkey={m.pubkey} nevent={m.nevent} />
|
||||||
|
{:else if m.type === "note/zap"}
|
||||||
|
<NoteZap note={m.note} />
|
||||||
{:else if m.type === "relay/add"}
|
{:else if m.type === "relay/add"}
|
||||||
<RelayAdd url={m.url} />
|
<RelayAdd url={m.url} />
|
||||||
{:else if m.type === "onboarding"}
|
{:else if m.type === "onboarding"}
|
||||||
|
@ -111,7 +111,6 @@
|
|||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
clearInterval(interval)
|
clearInterval(interval)
|
||||||
actions.cleanupZap()
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
@ -3,29 +3,22 @@
|
|||||||
import {nip19} from "nostr-tools"
|
import {nip19} from "nostr-tools"
|
||||||
import {tweened} from "svelte/motion"
|
import {tweened} from "svelte/motion"
|
||||||
import {find, reject, identity, propEq, pathEq, sum, pluck, sortBy} from "ramda"
|
import {find, reject, identity, propEq, pathEq, sum, pluck, sortBy} from "ramda"
|
||||||
import {warn} from "src/util/logger"
|
|
||||||
import {copyToClipboard} from "src/util/html"
|
import {copyToClipboard} from "src/util/html"
|
||||||
import {stringToHue, fetchJson, now, formatSats, hsl} from "src/util/misc"
|
import {stringToHue, formatSats, hsl} from "src/util/misc"
|
||||||
import {displayRelay, isLike, displayPerson, processZaps} from "src/util/nostr"
|
import {displayRelay, isLike, processZaps} from "src/util/nostr"
|
||||||
import {quantify, first} from "hurdak/lib/hurdak"
|
import {quantify, first} from "hurdak/lib/hurdak"
|
||||||
import {toast, modal} from "src/partials/state"
|
import {toast, modal} from "src/partials/state"
|
||||||
import Popover from "src/partials/Popover.svelte"
|
import Popover from "src/partials/Popover.svelte"
|
||||||
import QRCode from "src/partials/QRCode.svelte"
|
|
||||||
import Content from "src/partials/Content.svelte"
|
import Content from "src/partials/Content.svelte"
|
||||||
import Modal from "src/partials/Modal.svelte"
|
import Modal from "src/partials/Modal.svelte"
|
||||||
import Anchor from "src/partials/Anchor.svelte"
|
|
||||||
import OverflowMenu from "src/partials/OverflowMenu.svelte"
|
import OverflowMenu from "src/partials/OverflowMenu.svelte"
|
||||||
import CopyValue from "src/partials/CopyValue.svelte"
|
import CopyValue from "src/partials/CopyValue.svelte"
|
||||||
import PersonBadge from "src/app/shared/PersonBadge.svelte"
|
import PersonBadge from "src/app/shared/PersonBadge.svelte"
|
||||||
import Input from "src/partials/Input.svelte"
|
|
||||||
import Textarea from "src/partials/Textarea.svelte"
|
|
||||||
import RelayCard from "src/app/shared/RelayCard.svelte"
|
import RelayCard from "src/app/shared/RelayCard.svelte"
|
||||||
import {getEventPublishRelays} from "src/agent/relays"
|
import {getEventPublishRelays} from "src/agent/relays"
|
||||||
import {getPersonWithFallback} from "src/agent/db"
|
import {getPersonWithFallback} from "src/agent/db"
|
||||||
import network from "src/agent/network"
|
|
||||||
import pool from "src/agent/pool"
|
import pool from "src/agent/pool"
|
||||||
import user from "src/agent/user"
|
import user from "src/agent/user"
|
||||||
import keys from "src/agent/keys"
|
|
||||||
import cmd from "src/agent/cmd"
|
import cmd from "src/agent/cmd"
|
||||||
|
|
||||||
export let note
|
export let note
|
||||||
@ -66,78 +59,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
const startZap = () => {
|
const startZap = () => {
|
||||||
draftZap = {
|
modal.push({type: "note/zap", note})
|
||||||
amount: user.getSetting("defaultZap"),
|
|
||||||
message: "",
|
|
||||||
invoice: null,
|
|
||||||
loading: false,
|
|
||||||
startedAt: now(),
|
|
||||||
confirmed: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const loadZapInvoice = async () => {
|
|
||||||
draftZap.loading = true
|
|
||||||
|
|
||||||
const {zapper, lnurl} = $author
|
|
||||||
const amount = draftZap.amount * 1000
|
|
||||||
const relays = getEventPublishRelays(note)
|
|
||||||
const urls = pluck("url", relays)
|
|
||||||
const publishable = cmd.requestZap(urls, draftZap.message, note.pubkey, note.id, amount, lnurl)
|
|
||||||
const event = encodeURI(JSON.stringify(await keys.sign(publishable.event)))
|
|
||||||
const res = await fetchJson(`${zapper.callback}?amount=${amount}&nostr=${event}&lnurl=${lnurl}`)
|
|
||||||
|
|
||||||
// If they closed the dialog before fetch resolved, we're done
|
|
||||||
if (!draftZap) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!res.pr) {
|
|
||||||
throw new Error(JSON.stringify(res))
|
|
||||||
}
|
|
||||||
|
|
||||||
draftZap.invoice = res.pr
|
|
||||||
draftZap.loading = false
|
|
||||||
|
|
||||||
// Open up alby or whatever
|
|
||||||
const {webln} = window as {webln?: any}
|
|
||||||
if (webln) {
|
|
||||||
await webln.enable()
|
|
||||||
|
|
||||||
try {
|
|
||||||
webln.sendPayment(draftZap.invoice)
|
|
||||||
} catch (e) {
|
|
||||||
warn(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Listen for the zap confirmation
|
|
||||||
draftZap.sub = network.listen({
|
|
||||||
relays,
|
|
||||||
filter: {
|
|
||||||
kinds: [9735],
|
|
||||||
authors: [zapper.nostrPubkey],
|
|
||||||
"#p": [$author.pubkey],
|
|
||||||
since: draftZap.startedAt - 10,
|
|
||||||
},
|
|
||||||
onChunk: chunk => {
|
|
||||||
zap = first(chunk)
|
|
||||||
draftZap.confirmed = true
|
|
||||||
setTimeout(cleanupZap, 1000)
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export const cleanupZap = () => {
|
|
||||||
if (draftZap) {
|
|
||||||
draftZap.sub?.then(s => s.unsub())
|
|
||||||
draftZap = null
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let like, likes, allLikes, zap, zaps, allZaps
|
let like, likes, allLikes, zap, zaps, allZaps
|
||||||
let actions = []
|
let actions = []
|
||||||
let draftZap = null
|
|
||||||
let showDetails = false
|
let showDetails = false
|
||||||
|
|
||||||
$: disableActions = !$canPublish || muted
|
$: disableActions = !$canPublish || muted
|
||||||
@ -250,44 +176,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#if draftZap}
|
|
||||||
<Modal onEscape={cleanupZap}>
|
|
||||||
<Content size="lg">
|
|
||||||
<div class="text-center">
|
|
||||||
<h1 class="staatliches text-2xl">Send a zap</h1>
|
|
||||||
<p>to {displayPerson($author)}</p>
|
|
||||||
</div>
|
|
||||||
{#if draftZap.confirmed}
|
|
||||||
<div class="flex items-center justify-center gap-2 text-gray-1">
|
|
||||||
<i class="fa fa-champagne-glasses" />
|
|
||||||
<p>Success! Zap confirmed.</p>
|
|
||||||
</div>
|
|
||||||
{:else if draftZap.invoice}
|
|
||||||
<QRCode code={draftZap.invoice} />
|
|
||||||
<p class="text-center text-gray-1">
|
|
||||||
Copy or scan using a lightning wallet to pay your zap.
|
|
||||||
</p>
|
|
||||||
<div class="flex items-center justify-center gap-2 text-gray-1">
|
|
||||||
<i class="fa fa-circle-notch fa-spin" />
|
|
||||||
Waiting for confirmation...
|
|
||||||
</div>
|
|
||||||
{:else}
|
|
||||||
<Textarea bind:value={draftZap.message} placeholder="Add an optional message" />
|
|
||||||
<div class="flex items-center gap-2">
|
|
||||||
<label class="flex-grow">Custom amount:</label>
|
|
||||||
<Input bind:value={draftZap.amount}>
|
|
||||||
<i slot="before" class="fa fa-bolt" />
|
|
||||||
<span slot="after" class="-mt-1">sats</span>
|
|
||||||
</Input>
|
|
||||||
<Anchor loading={draftZap.loading} type="button-accent" on:click={loadZapInvoice}>
|
|
||||||
Zap!
|
|
||||||
</Anchor>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
</Content>
|
|
||||||
</Modal>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
{#if showDetails}
|
{#if showDetails}
|
||||||
<Modal
|
<Modal
|
||||||
onEscape={() => {
|
onEscape={() => {
|
||||||
|
118
src/app/views/NoteZap.svelte
Normal file
118
src/app/views/NoteZap.svelte
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import {onDestroy} from "svelte"
|
||||||
|
import {pluck} from "ramda"
|
||||||
|
import {warn} from "src/util/logger"
|
||||||
|
import {fetchJson, now} from "src/util/misc"
|
||||||
|
import {displayPerson} from "src/util/nostr"
|
||||||
|
import {modal} from "src/partials/state"
|
||||||
|
import QRCode from "src/partials/QRCode.svelte"
|
||||||
|
import Content from "src/partials/Content.svelte"
|
||||||
|
import Anchor from "src/partials/Anchor.svelte"
|
||||||
|
import Input from "src/partials/Input.svelte"
|
||||||
|
import Textarea from "src/partials/Textarea.svelte"
|
||||||
|
import {getEventPublishRelays} from "src/agent/relays"
|
||||||
|
import {getPersonWithFallback} from "src/agent/db"
|
||||||
|
import network from "src/agent/network"
|
||||||
|
import user from "src/agent/user"
|
||||||
|
import keys from "src/agent/keys"
|
||||||
|
import cmd from "src/agent/cmd"
|
||||||
|
|
||||||
|
export let note
|
||||||
|
|
||||||
|
let sub
|
||||||
|
let zap = {
|
||||||
|
amount: user.getSetting("defaultZap"),
|
||||||
|
message: "",
|
||||||
|
invoice: null,
|
||||||
|
loading: false,
|
||||||
|
startedAt: now(),
|
||||||
|
confirmed: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
const author = getPersonWithFallback(note.pubkey)
|
||||||
|
|
||||||
|
const loadZapInvoice = async () => {
|
||||||
|
zap.loading = true
|
||||||
|
|
||||||
|
const {zapper, lnurl} = author
|
||||||
|
const amount = zap.amount * 1000
|
||||||
|
const relays = getEventPublishRelays(note)
|
||||||
|
const urls = pluck("url", relays)
|
||||||
|
const publishable = cmd.requestZap(urls, zap.message, note.pubkey, note.id, amount, lnurl)
|
||||||
|
const event = encodeURI(JSON.stringify(await keys.sign(publishable.event)))
|
||||||
|
const res = await fetchJson(`${zapper.callback}?amount=${amount}&nostr=${event}&lnurl=${lnurl}`)
|
||||||
|
|
||||||
|
// If they closed the dialog before fetch resolved, we're done
|
||||||
|
if (!zap) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!res.pr) {
|
||||||
|
throw new Error(JSON.stringify(res))
|
||||||
|
}
|
||||||
|
|
||||||
|
zap.invoice = res.pr
|
||||||
|
zap.loading = false
|
||||||
|
|
||||||
|
// Open up alby or whatever
|
||||||
|
const {webln} = window as {webln?: any}
|
||||||
|
if (webln) {
|
||||||
|
await webln.enable()
|
||||||
|
|
||||||
|
try {
|
||||||
|
webln.sendPayment(zap.invoice)
|
||||||
|
} catch (e) {
|
||||||
|
warn(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Listen for the zap confirmation
|
||||||
|
sub = network.listen({
|
||||||
|
relays,
|
||||||
|
filter: {
|
||||||
|
kinds: [9735],
|
||||||
|
authors: [zapper.nostrPubkey],
|
||||||
|
"#p": [note.pubkey],
|
||||||
|
since: zap.startedAt - 10,
|
||||||
|
},
|
||||||
|
onChunk: chunk => {
|
||||||
|
zap.confirmed = true
|
||||||
|
setTimeout(() => modal.pop(), 1000)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
onDestroy(() => {
|
||||||
|
sub?.then(s => s.unsub())
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Content size="lg">
|
||||||
|
<div class="text-center">
|
||||||
|
<h1 class="staatliches text-2xl">Send a zap</h1>
|
||||||
|
<p>to {displayPerson(author)}</p>
|
||||||
|
</div>
|
||||||
|
{#if zap.confirmed}
|
||||||
|
<div class="flex items-center justify-center gap-2 text-gray-1">
|
||||||
|
<i class="fa fa-champagne-glasses" />
|
||||||
|
<p>Success! Zap confirmed.</p>
|
||||||
|
</div>
|
||||||
|
{:else if zap.invoice}
|
||||||
|
<QRCode code={zap.invoice} />
|
||||||
|
<p class="text-center text-gray-1">Copy or scan using a lightning wallet to pay your zap.</p>
|
||||||
|
<div class="flex items-center justify-center gap-2 text-gray-1">
|
||||||
|
<i class="fa fa-circle-notch fa-spin" />
|
||||||
|
Waiting for confirmation...
|
||||||
|
</div>
|
||||||
|
{:else}
|
||||||
|
<Textarea bind:value={zap.message} placeholder="Add an optional message" />
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<label class="flex-grow">Custom amount:</label>
|
||||||
|
<Input bind:value={zap.amount}>
|
||||||
|
<i slot="before" class="fa fa-bolt" />
|
||||||
|
<span slot="after" class="-mt-1">sats</span>
|
||||||
|
</Input>
|
||||||
|
<Anchor loading={zap.loading} type="button-accent" on:click={loadZapInvoice}>Zap!</Anchor>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</Content>
|
Loading…
Reference in New Issue
Block a user