import { useEffect, useContext, createContext, useState, useCallback, useMemo } from 'react' import Table from 'react-bootstrap/Table' import ActionTooltip from './action-tooltip' import Info from './info' import styles from './fee-button.module.css' import { gql, useQuery } from '@apollo/client' import { ANON_FEE_MULTIPLIER, FAST_POLL_INTERVAL, SSR } from '@/lib/constants' import { numWithUnits } from '@/lib/format' import { useMe } from './me' import AnonIcon from '@/svgs/spy-fill.svg' import { useShowModal } from './modal' import Link from 'next/link' import { SubmitButton } from './form' const FeeButtonContext = createContext() export function postCommentBaseLineItems ({ baseCost = 1, comment = false, me }) { const anonCharge = me ? {} : { anonCharge: { term: `x ${ANON_FEE_MULTIPLIER}`, label: 'anon mult', op: '*', modifier: (cost) => cost * ANON_FEE_MULTIPLIER } } return { baseCost: { term: baseCost, label: `${comment ? 'comment' : 'post'} cost`, op: '_', modifier: (cost) => cost + baseCost, allowFreebies: comment }, ...anonCharge } } export function postCommentUseRemoteLineItems ({ parentId } = {}) { const query = parentId ? gql`{ itemRepetition(parentId: "${parentId}") }` : gql`{ itemRepetition }` return function useRemoteLineItems () { const [line, setLine] = useState({}) const { data } = useQuery(query, SSR ? {} : { pollInterval: FAST_POLL_INTERVAL, nextFetchPolicy: 'cache-and-network' }) useEffect(() => { const repetition = data?.itemRepetition if (!repetition) return setLine({}) setLine({ itemRepetition: { term: <>x 10{repetition}, label: <>{repetition} {parentId ? 'repeat or self replies' : 'posts'} in 10m, op: '*', modifier: (cost) => cost * Math.pow(10, repetition) } }) }, [data?.itemRepetition]) return line } } function sortHelper (a, b) { if (a.op === '_') { return -1 } else if (b.op === '_') { return 1 } else if (a.op === '*' || a.op === '/') { if (b.op === '*' || b.op === '/') { return 0 } // a is higher precedence return -1 } else { if (b.op === '*' || b.op === '/') { // b is higher precedence return 1 } // postive first if (a.op === '+' && b.op === '-') { return -1 } if (a.op === '-' && b.op === '+') { return 1 } // both are + or - return 0 } } export function FeeButtonProvider ({ baseLineItems = {}, useRemoteLineItems = () => null, children }) { const [lineItems, setLineItems] = useState({}) const [disabled, setDisabled] = useState(false) const { me } = useMe() const remoteLineItems = useRemoteLineItems() const mergeLineItems = useCallback((newLineItems) => { setLineItems(lineItems => ({ ...lineItems, ...newLineItems })) }, [setLineItems]) const value = useMemo(() => { const lines = { ...baseLineItems, ...lineItems, ...remoteLineItems } const total = Object.values(lines).sort(sortHelper).reduce((acc, { modifier }) => modifier(acc), 0) // freebies: there's only a base cost and we don't have enough sats const free = total === lines.baseCost?.modifier(0) && lines.baseCost?.allowFreebies && me?.privates?.sats < total && !me?.privates?.disableFreebies return { lines, merge: mergeLineItems, total, disabled, setDisabled, free } }, [me?.privates?.sats, me?.privates?.disableFreebies, baseLineItems, lineItems, remoteLineItems, mergeLineItems, disabled, setDisabled]) return ( {children} ) } export function useFeeButton () { const context = useContext(FeeButtonContext) return context } function FreebieDialog () { return ( <>
you don't have enough sats, so this one is on us
) } export default function FeeButton ({ ChildButton = SubmitButton, variant, text, disabled }) { const { me } = useMe() const { lines, total, disabled: ctxDisabled, free } = useFeeButton() const feeText = free ? 'free' : total > 1 ? numWithUnits(total, { abbreviate: false, format: true }) : undefined disabled ||= ctxDisabled return (
{text} {!me && } {(free && ) || (total > 1 && )}
) } function Receipt ({ lines, total }) { return ( {Object.entries(lines).sort(([, a], [, b]) => sortHelper(a, b)).map(([key, { term, label, omit }]) => ( !omit && ))}
{term} {label}
{numWithUnits(total, { abbreviate: false, format: true })} total fee
) } function AnonInfo () { const showModal = useShowModal() return ( showModal(onClose =>
You are posting without an account
  1. You'll pay by invoice
  2. Your content will be content-joined (get it?!) under the @anon account
  3. Any sats your content earns will go toward rewards
  4. We won't be able to notify you when you receive replies
btw if you don't need to be anonymous, posting is cheaper with an account
) } /> ) }