From 54d738aefac9fa06317329e745ccf8e77fee7902 Mon Sep 17 00:00:00 2001 From: Jon Staab Date: Tue, 21 Nov 2023 08:52:28 -0800 Subject: [PATCH] Add cross-posting --- .env | 1 + src/app/shared/Note.svelte | 8 ++ src/app/shared/NoteActions.svelte | 154 +++++++++++++++++++++--------- src/main.js | 6 +- src/util/nostr.ts | 7 ++ 5 files changed, 131 insertions(+), 45 deletions(-) diff --git a/.env b/.env index f57b1a5e..7604dfca 100644 --- a/.env +++ b/.env @@ -4,6 +4,7 @@ VITE_DEFAULT_RELAYS=wss://purplepag.es,wss://relay.damus.io,wss://relay.nostr.ba VITE_DEFAULT_FOLLOWS=1739d937dc8c0c7370aa27585938c119e25c41f6c441a5d34c6d38503e3136ef,fa984bd7dbb282f07e16e7ae87b26a2a7b9b90b7246a44771f0cf5ae58018f52,cc8d072efdcc676fcbac14f6cd6825edc3576e55eb786a2a975ee034a6a026cb,d91191e30e00444b942c0e82cad470b32af171764c2275bee0bd99377efd4075,3335d373e6c1b5bc669b4b1220c08728ea8ce622e5a7cfeeb4c0001d91ded1de,0b118e40d6f3dfabb17f21a94a647701f140d8b063a9e84fe6e483644edc09cb,b83a28b7e4e5d20bd960c5faeb6625f95529166b8bdb045d42634a2f35919450,958b754a1d3de5b5eca0fe31d2d555f451325f8498a83da1997b7fcd5c39e88c,b9e76546ba06456ed301d9e52bc49fa48e70a6bf2282be7a1ae72947612023dc,b708f7392f588406212c3882e7b3bc0d9b08d62f95fa170d099127ece2770e5e,5c508c34f58866ec7341aaf10cc1af52e9232bb9f859c8103ca5ecf2aa93bf78,baf27a4cc4da49913e7fdecc951fd3b971c9279959af62b02b761a043c33384c,2edbcea694d164629854a52583458fd6d965b161e3c48b57d3aff01940558884,0fecf65daa26faf3f668e8143325a4c199a040b6345ed40a08614d7dd85b1823,1bc70a0148b3f316da33fe3c89f23e3e71ac4ff998027ec712b905cd24f6a411,f783ba3b12b91e375aba6594015b90bd95f7e132b03cc8c4c52ce0a7c36aab52,3f770d65d3a764a9c5cb503ae123e62ec7598ad035d836e2a810f3877a745b24,82341f882b6eabcd2ba7f1ef90aad961cf074af15b9ef44a09f9d2a8fbfbe6a2,3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d,ee11a5dff40c19a555f41fe42b48f00e618c91225622ae37b6c2bb67b76c4e49,97c70a44366a6535c145b333f973ea86dfdc2d7a99da618c40c64705ad98e322,a1fc5dfd7ffcf563c89155b466751b580d115e136e2f8c90e8913385bbedb1cf,84dee6e676e5bb67b4ad4e042cf70cbd8681155db535942fcc6a0533858a7240 VITE_IMGPROXY_URL=https://imgproxy.coracle.social VITE_DUFFLEPUD_URL=https://dufflepud.onrender.com +VITE_ENABLE_GROUPS=true VITE_ENABLE_ZAPS=true VITE_FORCE_RELAYS= VITE_LOGO_URL= diff --git a/src/app/shared/Note.svelte b/src/app/shared/Note.svelte index c4613190..1c05b235 100644 --- a/src/app/shared/Note.svelte +++ b/src/app/shared/Note.svelte @@ -154,6 +154,9 @@ zapper ) + // Split out reposts + $: reposts = children.filter(e => [6, 16].includes(e.kind)) + onMount(async () => { const zapAddress = Tags.from(note).getValue("zap") @@ -191,6 +194,11 @@ kinds.push(9735) } + if ($env.ENABLE_GROUPS && !event.wrap) { + kinds.push(6) + kinds.push(16) + } + load({ relays: mergeHints([relays, getReplyHints(event)]), filters: getReplyFilters([event], {kinds}), diff --git a/src/app/shared/NoteActions.svelte b/src/app/shared/NoteActions.svelte index 5ad39ec8..122bacf0 100644 --- a/src/app/shared/NoteActions.svelte +++ b/src/app/shared/NoteActions.svelte @@ -3,12 +3,20 @@ import {nip19} from "nostr-tools" import {toNostrURI, createEvent} from "paravel" import {tweened} from "svelte/motion" - import {identity, sum, pluck, sortBy} from "ramda" + import {identity, sum, uniqBy, prop, pluck, sortBy} from "ramda" import {formatSats} from "src/util/misc" - import {LOCAL_RELAY_URL, getGroupAddress, asNostrEvent, getIdOrAddress} from "src/util/nostr" + import { + LOCAL_RELAY_URL, + getGroupAddress, + getIdOrAddressTag, + asNostrEvent, + getIdOrAddress, + } from "src/util/nostr" import {quantify} from "hurdak" import {toast} from "src/partials/state" import Popover from "src/partials/Popover.svelte" + import Card from "src/partials/Card.svelte" + import Heading from "src/partials/Heading.svelte" import ColorDot from "src/partials/ColorDot.svelte" import Content from "src/partials/Content.svelte" import Modal from "src/partials/Modal.svelte" @@ -16,15 +24,18 @@ import CopyValue from "src/partials/CopyValue.svelte" import PersonBadge from "src/app/shared/PersonBadge.svelte" import RelayCard from "src/app/shared/RelayCard.svelte" + import GroupSummary from "src/app/shared/GroupSummary.svelte" import {router} from "src/app/router" import type {Event} from "src/engine" import { env, mute, unmute, + groups, canSign, session, Publisher, + mention, signer, deriveGroupAccess, publishToZeroOrMoreGroups, @@ -44,11 +55,10 @@ export let showMuted export let showEntire export let removeFromContext - export let replies - export let likes - export let zaps + export let replies, likes, zaps export let zapper + const address = getGroupAddress(note) const relays = getEventHints(note) const nevent = nip19.neventEncode({id: note.id, relays}) const muted = isEventMuted.derived($isEventMuted => $isEventMuted(note, true)) @@ -59,6 +69,10 @@ //const report = () => router.at("notes").of(note.id, {relays: getEventHints(note)}).at('report').qp({pubkey: note.pubkey}).open() + const setView = v => { + view = v + } + const label = () => router.at("notes").of(note.id, {relays}).at("label").open() const quote = () => router.at("notes/create").cx({quote: note, relays}).open() @@ -87,6 +101,24 @@ removeFromContext(e) } + const crossPost = async address => { + const relays = getPublishHints(note) + const tags = [getIdOrAddressTag(note, relays[0]), mention(note.pubkey)] + const content = JSON.stringify(asNostrEvent(note)) + const shouldWrap = groups.key(address).get()?.access === "closed" + + let template + if (note.kind === 1) { + template = createEvent(6, {content, tags}) + } else { + template = createEvent(16, {content, tags: [...tags, ["k", note.kind]]}) + } + + publishToZeroOrMoreGroups([address], template, {relays, shouldWrap}) + + setView(null) + } + const startZap = () => router .at("people") @@ -110,11 +142,24 @@ .cx({relays: [url]}) .open() - let like, allLikes, zap - let showDetails = false + const groupOptions = session.derived($session => { + const options = [] + + for (const addr of Object.keys($session.groups || {})) { + const group = groups.key(addr).get() + const access = deriveGroupAccess(addr).get() + + if (group && access === "granted" && addr !== address) { + options.push(group) + } + } + + return uniqBy(prop("address"), options) + }) + + let view let actions = [] - $: address = getGroupAddress(note) $: disableActions = !$canSign || ($muted && !showMuted) || @@ -140,6 +185,7 @@ actions = [] actions.push({label: "Quote", icon: "quote-left", onClick: quote}) + actions.push({label: "Cross-post", icon: "shuffle", onClick: () => setView('cross-post')}) actions.push({label: "Tag", icon: "tag", onClick: label}) //actions.push({label: "Report", icon: "triangle-exclamation", onClick: report}) @@ -156,9 +202,7 @@ actions.push({ label: "Details", icon: "info", - onClick: () => { - showDetails = true - }, + onClick: () => setView("info"), }) } @@ -243,43 +287,67 @@ -{#if showDetails} - { - showDetails = false - }}> +{#if view} + setView(null)}> - {#if zaps.length > 0} -

Zapped By

-
- {#each zaps as zap} -
- - {formatSats(zap.invoiceAmount / 1000)} sats + {#if view === "info"} + {#if zaps.length > 0} +

Zapped By

+
+ {#each zaps as zap} +
+ + {formatSats(zap.invoiceAmount / 1000)} sats +
+ {/each} +
+ {/if} + {#if likes.length > 0} +

Liked By

+
+ {#each likes as like} + + {/each} +
+ {/if} + {#if note.seen_on.length > 0} +

Relays

+

This note was found on {quantify(note.seen_on.length, "relay")} below.

+
+ {#each note.seen_on as url} + + {/each} +
+ {/if} +

Details

+ + + + {:else if view === "cross-post"} +
+ Cross-post +
+
Select where you'd like to post to:
+
+ {#if address} + crossPost()}> +
+ +
+

Global

+

Post to your main feed.

+
+
+ {/if} + {#each $groupOptions as g (g.address)} + crossPost(g.address)}> + + {/each}
{/if} - {#if likes.length > 0} -

Liked By

-
- {#each likes as like} - - {/each} -
- {/if} -

Relays

-

This note was found on {quantify(note.seen_on.length, "relay")} below.

-
- {#each note.seen_on as url} - - {/each} -
-

Details

- - - {/if} diff --git a/src/main.js b/src/main.js index 9e72520f..d90297cc 100644 --- a/src/main.js +++ b/src/main.js @@ -46,8 +46,7 @@ const MULTIPLEXTR_URL = import.meta.env.VITE_MULTIPLEXTR_URL const FORCE_RELAYS = fromCsv(import.meta.env.VITE_FORCE_RELAYS) -const DVM_RELAYS = - FORCE_RELAYS.length > 0 ? FORCE_RELAYS : fromCsv(import.meta.env.VITE_DVM_RELAYS) +const DVM_RELAYS = FORCE_RELAYS.length > 0 ? FORCE_RELAYS : fromCsv(import.meta.env.VITE_DVM_RELAYS) const SEARCH_RELAYS = FORCE_RELAYS.length > 0 ? FORCE_RELAYS : ["wss://relay.nostr.band", "wss://nostr.wine"] @@ -59,6 +58,8 @@ const DEFAULT_FOLLOWS = fromCsv(import.meta.env.VITE_DEFAULT_FOLLOWS) const ENABLE_ZAPS = JSON.parse(import.meta.env.VITE_ENABLE_ZAPS) +const ENABLE_GROUPS = JSON.parse(import.meta.env.VITE_ENABLE_GROUPS) + // Prep our env env.set({ DEFAULT_FOLLOWS, @@ -70,6 +71,7 @@ env.set({ SEARCH_RELAYS, DEFAULT_RELAYS, ENABLE_ZAPS, + ENABLE_GROUPS, }) // Throw some hardcoded defaults in there diff --git a/src/util/nostr.ts b/src/util/nostr.ts index 86d29689..bca260ec 100644 --- a/src/util/nostr.ts +++ b/src/util/nostr.ts @@ -62,6 +62,13 @@ export const getIdOrAddress = e => { return e.id } +export const getIdOrAddressTag = (e, hint) => { + const value = getIdOrAddress(e) + const type = value.includes(":") ? "a" : "e" + + return [type, value, hint] +} + export const getGroupAddress = e => Tags.from(e) .type("a")