0 ? '' : 'invisible'}`}>
diff --git a/components/item-info.js b/components/item-info.js
index bbfc0588..db84b473 100644
--- a/components/item-info.js
+++ b/components/item-info.js
@@ -165,6 +165,8 @@ export default function ItemInfo ({
nostr note
)}
+ {item && item.mine && !item.noteId && !item.isJob && !item.parentId &&
+
}
{me && !item.position &&
!item.mine && !item.deletedAt &&
(item.meDontLikeSats > meTotalSats
@@ -185,8 +187,6 @@ export default function ItemInfo ({
>}
- {item?.mine && !item?.noteId &&
-
}
{item.mine && !item.position && !item.deletedAt && !item.bio &&
<>
diff --git a/components/link-form.js b/components/link-form.js
index e2eed965..3858ee57 100644
--- a/components/link-form.js
+++ b/components/link-form.js
@@ -13,6 +13,7 @@ import { normalizeForwards, toastDeleteScheduled } from '../lib/form'
import { useToast } from './toast'
import { SubSelectInitial } from './sub-select'
import { MAX_TITLE_LENGTH } from '../lib/constants'
+import useCrossposter from './use-crossposter'
import { useMe } from './me'
import { ItemButtonBar } from './post'
@@ -26,6 +27,8 @@ export function LinkForm ({ item, sub, editThreshold, children }) {
const shareUrl = router.query.url
const shareTitle = router.query.title
+ const crossposter = useCrossposter()
+
const [getPageTitleAndUnshorted, { data }] = useLazyQuery(gql`
query PageTitleAndUnshorted($url: String!) {
pageTitleAndUnshorted(url: $url) {
@@ -78,7 +81,7 @@ export function LinkForm ({ item, sub, editThreshold, children }) {
)
const onSubmit = useCallback(
- async ({ boost, title, ...values }) => {
+ async ({ boost, crosspost, title, ...values }) => {
const { data, error } = await upsertLink({
variables: {
sub: item?.subName || sub?.name,
@@ -92,6 +95,13 @@ export function LinkForm ({ item, sub, editThreshold, children }) {
if (error) {
throw new Error({ message: error.toString() })
}
+
+ const linkId = data?.upsertLink?.id
+
+ if (crosspost && linkId) {
+ await crossposter(linkId)
+ }
+
if (item) {
await router.push(`/items/${item.id}`)
} else {
@@ -125,6 +135,7 @@ export function LinkForm ({ item, sub, editThreshold, children }) {
title: item?.title || shareTitle || '',
url: item?.url || shareUrl || '',
text: item?.text || '',
+ crosspost: item ? !!item.noteId : me?.privates?.nostrCrossposting,
...AdvPostInitial({ forward: normalizeForwards(item?.forwards), boost: item?.boost }),
...SubSelectInitial({ sub: item?.subName || sub?.name })
}}
@@ -183,7 +194,7 @@ export function LinkForm ({ item, sub, editThreshold, children }) {
}
}}
/>
-
+
{
+ async ({ boost, title, options, crosspost, ...values }) => {
const optionsFiltered = options.slice(initialOptions?.length).filter(word => word.trim().length > 0)
const { data, error } = await upsertPoll({
variables: {
@@ -49,6 +52,13 @@ export function PollForm ({ item, sub, editThreshold, children }) {
if (error) {
throw new Error({ message: error.toString() })
}
+
+ const pollId = data?.upsertPoll?.id
+
+ if (crosspost && pollId) {
+ await crossposter(pollId)
+ }
+
if (item) {
await router.push(`/items/${item.id}`)
} else {
@@ -67,6 +77,7 @@ export function PollForm ({ item, sub, editThreshold, children }) {
title: item?.title || '',
text: item?.text || '',
options: initialOptions || ['', ''],
+ crosspost: item ? !!item.noteId : me?.privates?.nostrCrossposting,
pollExpiresAt: item ? item.pollExpiresAt : datePivot(new Date(), { hours: 25 }),
...AdvPostInitial({ forward: normalizeForwards(item?.forwards), boost: item?.boost }),
...SubSelectInitial({ sub: item?.subName || sub?.name })
@@ -100,7 +111,7 @@ export function PollForm ({ item, sub, editThreshold, children }) {
: null}
maxLength={MAX_POLL_CHOICE_LENGTH}
/>
-
+
{
+ try {
+ await crossposter(item.id)
+ } catch (e) {
+ console.error(e)
+ toaster.danger('Crosspost failed')
+ }
+ }
return (
- {
- try {
- const pubkey = await callWithTimeout(() => window.nostr.getPublicKey(), 5000)
- if (!pubkey) {
- throw new Error('not available')
- }
- } catch (e) {
- toaster.danger(`Nostr extension error: ${e.message}`)
- return
- }
- try {
- if (item?.id) {
- const crosspostResult = await crossposter({ ...item })
- const noteId = crosspostResult?.noteId
- if (noteId) {
- await updateNoteId({
- variables: {
- id: item.id,
- noteId
- }
- })
- }
- toaster.success('Crosspost successful')
- } else {
- toaster.warning('Item ID not available')
- }
- } catch (e) {
- console.error(e)
- toaster.danger('Crosspost failed')
- }
- }}
- >
+
crosspost to nostr
)
diff --git a/components/use-crossposter.js b/components/use-crossposter.js
index 552bf50d..80ad3dd6 100644
--- a/components/use-crossposter.js
+++ b/components/use-crossposter.js
@@ -1,9 +1,10 @@
import { useCallback } from 'react'
import { useToast } from './toast'
import { Button } from 'react-bootstrap'
-import { DEFAULT_CROSSPOSTING_RELAYS, crosspost } from '../lib/nostr'
-import { useQuery } from '@apollo/client'
+import { DEFAULT_CROSSPOSTING_RELAYS, crosspost, callWithTimeout } from '../lib/nostr'
+import { gql, useMutation, useQuery, useLazyQuery } from '@apollo/client'
import { SETTINGS } from '../fragments/users'
+import { ITEM_FULL_FIELDS, POLL_FIELDS } from '../fragments/items'
async function discussionToEvent (item) {
const createdAt = Math.floor(Date.now() / 1000)
@@ -20,14 +21,89 @@ async function discussionToEvent (item) {
}
}
+async function linkToEvent (item) {
+ const createdAt = Math.floor(Date.now() / 1000)
+
+ return {
+ created_at: createdAt,
+ kind: 1,
+ content: `${item.title} \n ${item.url}`,
+ tags: []
+ }
+}
+
+async function pollToEvent (item) {
+ const createdAt = Math.floor(Date.now() / 1000)
+
+ const expiresAt = createdAt + 86400
+
+ return {
+ created_at: createdAt,
+ kind: 1,
+ content: item.text,
+ tags: [
+ ['poll', 'single', expiresAt.toString(), item.title, ...item.poll.options.map(op => op?.option.toString())]
+ ]
+ }
+}
+
+async function bountyToEvent (item) {
+ const createdAt = Math.floor(Date.now() / 1000)
+
+ return {
+ created_at: createdAt,
+ kind: 30402,
+ content: item.text,
+ tags: [
+ ['d', item.id.toString()],
+ ['title', item.title],
+ ['location', `https://stacker.news/items/${item.id}`],
+ ['price', item.bounty.toString(), 'SATS'],
+ ['t', 'bounty'],
+ ['published_at', createdAt.toString()]
+ ]
+ }
+}
+
export default function useCrossposter () {
- const toast = useToast()
+ const toaster = useToast()
const { data } = useQuery(SETTINGS)
- const relays = [...DEFAULT_CROSSPOSTING_RELAYS, ...(data?.settings?.nostrRelays || [])]
+ const userRelays = data?.settings?.privates?.nostrRelays || []
+ const relays = [...DEFAULT_CROSSPOSTING_RELAYS, ...userRelays]
+
+ const [fetchItem] = useLazyQuery(
+ gql`
+ ${ITEM_FULL_FIELDS}
+ ${POLL_FIELDS}
+ query Item($id: ID!) {
+ item(id: $id) {
+ ...ItemFullFields
+ ...PollFields
+ }
+ }`, {
+ fetchPolicy: 'no-cache'
+ }
+ )
+
+ const [updateNoteId] = useMutation(
+ gql`
+ mutation updateNoteId($id: ID!, $noteId: String!) {
+ updateNoteId(id: $id, noteId: $noteId) {
+ id
+ noteId
+ }
+ }`
+ )
const relayError = (failedRelays) => {
return new Promise(resolve => {
- const removeToast = toast.danger(
+ const handleSkip = () => {
+ resolve('skip')
+
+ removeToast()
+ }
+
+ const removeToast = toaster.danger(
<>
Crossposting failed for {failedRelays.join(', ')}