import { useState, useEffect } from 'react' import AccordianItem from './accordian-item' import { Input, InputUserSuggest, VariableInput, Checkbox } from './form' import InputGroup from 'react-bootstrap/InputGroup' import { BOOST_MIN, BOOST_MULT, MAX_FORWARDS } from '@/lib/constants' import { DEFAULT_CROSSPOSTING_RELAYS } from '@/lib/nostr' import Info from './info' import { numWithUnits } from '@/lib/format' import styles from './adv-post-form.module.css' import { useMe } from './me' import { useFeeButton } from './fee-button' import { useRouter } from 'next/router' import { useFormikContext } from 'formik' const EMPTY_FORWARD = { nym: '', pct: '' } export function AdvPostInitial ({ forward, boost }) { return { boost: boost || '', forward: forward?.length ? forward : [EMPTY_FORWARD] } } const FormStatus = { DIRTY: 'dirty', ERROR: 'error' } export default function AdvPostForm ({ children, item, storageKeyPrefix }) { const { me } = useMe() const { merge } = useFeeButton() const router = useRouter() const [itemType, setItemType] = useState() const formik = useFormikContext() const [show, setShow] = useState(false) useEffect(() => { const isDirty = formik?.values.forward?.[0].nym !== '' || formik?.values.forward?.[0].pct !== '' || formik?.values.boost !== '' || (router.query?.type === 'link' && formik?.values.text !== '') // if the adv post form is dirty on first render, show the accordian if (isDirty) { setShow(FormStatus.DIRTY) } // HACK ... TODO: we should generically handle this kind of local storage stuff // in the form component, overriding the initial values if (storageKeyPrefix) { for (let i = 0; i < MAX_FORWARDS; i++) { ['nym', 'pct'].forEach(key => { const value = window.localStorage.getItem(`${storageKeyPrefix}-forward[${i}].${key}`) if (value) { formik?.setFieldValue(`forward[${i}].${key}`, value) } }) } } }, [formik?.values, storageKeyPrefix]) useEffect(() => { // force show the accordian if there is an error and the form is submitting const hasError = !!formik?.errors?.boost || formik?.errors?.forward?.length > 0 // if it's open we don't want to collapse on submit setShow(show => hasError && formik?.isSubmitting ? FormStatus.ERROR : show) }, [formik?.isSubmitting]) useEffect(() => { const determineItemType = () => { if (router && router.query.type) { return router.query.type } else if (item) { const typeMap = { url: 'link', bounty: 'bounty', pollCost: 'poll' } for (const [key, type] of Object.entries(typeMap)) { if (item[key]) { return type } } return 'discussion' } } const type = determineItemType() setItemType(type) }, [item, router]) function renderCrosspostDetails (itemType) { switch (itemType) { case 'discussion': return
  • crosspost this discussion as a NIP-23 event
  • case 'link': return
  • crosspost this link as a NIP-01 event
  • case 'bounty': return
  • crosspost this bounty as a NIP-99 event
  • case 'poll': return
  • crosspost this poll as a NIP-41 event
  • default: return null } } return ( options} show={show} body={ <> {children} boost
    1. Boost ranks posts higher temporarily based on the amount
    2. The minimum boost is {numWithUnits(BOOST_MIN, { abbreviate: false })}
    3. Each {numWithUnits(BOOST_MULT, { abbreviate: false })} of boost is equivalent to one trusted upvote
      • e.g. {numWithUnits(BOOST_MULT * 5, { abbreviate: false })} is like 5 votes
    4. The decay of boost "votes" increases at 1.25x the rate of organic votes
      • i.e. boost votes fall out of ranking faster
    5. 100% of sats from boost are given back to top stackers as rewards
    } name='boost' onChange={(_, e) => merge({ boost: { term: `+ ${e.target.value}`, label: 'boost', modifier: cost => cost + Number(e.target.value) } })} hint={ranks posts higher temporarily based on the amount} append={sats} /> Forward sats to up to 5 other stackers. Any remaining sats go to you.} > {({ index, placeholder }) => { return (
    @} showValid groupClassName={`${styles.name} me-3 mb-0`} /> %} groupClassName={`${styles.percent} mb-0`} />
    ) }}
    {me && itemType && crosspost to nostr
      {renderCrosspostDetails(itemType)}
    • requires NIP-07 extension for signing
    • we use your NIP-05 relays if set
    • we use these relays by default:
      • {DEFAULT_CROSSPOSTING_RELAYS.map((relay, i) => (
      • {relay}
      • ))}
    } name='crosspost' />} } /> ) }