stacker.news/components/use-crossposter.js
Austin Kelsay b3aee502a0
Crosspost discussion items to nostr (#522)
* Crossposting discussion function, crossposting setting migration

* Passing in id, adding relays to test

* Adding checkbox setting for crossposting enabled

* Adding paramaterized event fields to crosspostDiscussion, successfully crossposting discussions

* Cleaning up for rebase

* Removing nostrRelays

* Retry crosspost toast

* Adding nostrCrossposting to settings, fixing migration

* Full flow is working with error surfacing, retries, and skips for a retry

* Updates to error handling/retries for crossposting, fixing settings for crossposting

* Allowing recursive retries for crossposting to specific relays

* Fixing / syncing crossposting settings, cleaning up and seperating out nostr functions

* Cleaning up

* Running linter

* make nostr crossposter a hook

---------

Co-authored-by: Austin <austin@pop-os.localdomain>
Co-authored-by: plebdev <plebdev@plebdevs-MacBook-Pro.local>
Co-authored-by: Keyan <34140557+huumn@users.noreply.github.com>
Co-authored-by: keyan <keyan.kousha+huumn@gmail.com>
2023-10-04 13:47:09 -05:00

81 lines
2.2 KiB
JavaScript

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 { SETTINGS } from '../fragments/users'
async function discussionToEvent (item) {
const pubkey = await window.nostr.getPublicKey()
const createdAt = Math.floor(Date.now() / 1000)
return {
created_at: createdAt,
kind: 30023,
content: item.text,
tags: [
['d', `https://stacker.news/items/${item.id}`],
['a', `30023:${pubkey}:https://stacker.news/items/${item.id}`, 'wss://relay.nostr.band'],
['title', item.title],
['published_at', createdAt.toString()]
]
}
}
export default function useCrossposter () {
const toast = useToast()
const { data } = useQuery(SETTINGS)
const relays = [...DEFAULT_CROSSPOSTING_RELAYS, ...(data?.settings?.nostRelays || [])]
const relayError = (failedRelays) => {
return new Promise(resolve => {
const { removeToast } = toast.danger(
<>
Crossposting failed for {failedRelays.join(', ')} <br />
<Button
variant='link' onClick={() => {
resolve('retry')
setTimeout(() => {
removeToast()
}, 1000)
}}
>Retry
</Button>
{' | '}
<Button
variant='link' onClick={() => resolve('skip')}
>Skip
</Button>
</>,
() => resolve('skip') // will skip if user closes the toast
)
})
}
return useCallback(async item => {
let failedRelays
let allSuccessful = false
do {
// XXX we only use discussions right now
const event = await discussionToEvent(item)
const result = await crosspost(event, failedRelays || relays)
failedRelays = result.failedRelays.map(relayObj => relayObj.relay)
if (failedRelays.length > 0) {
const userAction = await relayError(failedRelays)
if (userAction === 'skip') {
toast.success('Skipped failed relays.')
break
}
} else {
allSuccessful = true
}
} while (failedRelays.length > 0)
return { allSuccessful }
}, [relays, toast])
}