From f4382ad73e8b62244189b16ac9bdbb5fbe24072d Mon Sep 17 00:00:00 2001 From: Keyan <34140557+huumn@users.noreply.github.com> Date: Wed, 2 Oct 2024 19:24:01 -0500 Subject: [PATCH] better boost hints (#1441) * better boost hints * refine --- api/resolvers/item.js | 30 ++++++-- api/typeDefs/item.js | 2 + components/adv-post-form.js | 102 ++++++++++++++++++++-------- components/adv-post-form.module.css | 7 ++ components/item-act.js | 7 +- 5 files changed, 107 insertions(+), 41 deletions(-) diff --git a/api/resolvers/item.js b/api/resolvers/item.js index 76a56c84..06ddec3c 100644 --- a/api/resolvers/item.js +++ b/api/resolvers/item.js @@ -685,11 +685,7 @@ export default { return await models.item.count({ where }) + 1 }, - boostPosition: async (parent, { id, sub, boost }, { models, me }) => { - if (boost <= 0) { - throw new GqlInputError('boost must be greater than 0') - } - + boostPosition: async (parent, { id, sub, boost = 0 }, { models, me }) => { const where = { boost: { gte: boost }, status: 'ACTIVE', @@ -701,9 +697,29 @@ export default { where.id = { not: Number(id) } } + const homeAgg = await models.item.aggregate({ + _count: { id: true }, + _max: { boost: true }, + where + }) + + let subAgg + if (sub) { + subAgg = await models.item.aggregate({ + _count: { id: true }, + _max: { boost: true }, + where: { + ...where, + subName: sub + } + }) + } + return { - home: await models.item.count({ where }) === 0, - sub: sub ? await models.item.count({ where: { ...where, subName: sub } }) === 0 : false + home: homeAgg._count.id === 0 && boost >= BOOST_MULT, + sub: subAgg?._count.id === 0 && boost >= BOOST_MULT, + homeMaxBoost: homeAgg._max.boost || 0, + subMaxBoost: subAgg?._max.boost || 0 } } }, diff --git a/api/typeDefs/item.js b/api/typeDefs/item.js index c70b84c8..fe87babd 100644 --- a/api/typeDefs/item.js +++ b/api/typeDefs/item.js @@ -16,6 +16,8 @@ export default gql` type BoostPositions { home: Boolean! sub: Boolean! + homeMaxBoost: Int! + subMaxBoost: Int! } type TitleUnshorted { diff --git a/components/adv-post-form.js b/components/adv-post-form.js index 5e688e54..5ce43d47 100644 --- a/components/adv-post-form.js +++ b/components/adv-post-form.js @@ -1,18 +1,20 @@ -import { useState, useEffect, useMemo } from 'react' +import { useState, useEffect, useMemo, useCallback } 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 { BOOST_MIN, BOOST_MULT, MAX_FORWARDS, SSR } from '@/lib/constants' import { DEFAULT_CROSSPOSTING_RELAYS } from '@/lib/nostr' import Info from './info' -import { numWithUnits } from '@/lib/format' +import { abbrNum, 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' -import { gql, useLazyQuery } from '@apollo/client' +import { gql, useQuery } from '@apollo/client' import useDebounceCallback from './use-debounce-callback' +import { Button } from 'react-bootstrap' +import classNames from 'classnames' const EMPTY_FORWARD = { nym: '', pct: '' } @@ -85,56 +87,96 @@ export function BoostInput ({ onChange, ...props }) { ) } +const BoostMaxes = ({ subName, homeMax, subMax, boost, updateBoost }) => { + return ( +
+ + {subName && + } +
+ ) +} + // act means we are adding to existing boost export function BoostItemInput ({ item, sub, act = false, ...props }) { - const [boost, setBoost] = useState(Number(item?.boost) + (act ? BOOST_MULT : 0)) + // act adds boost to existing boost + const existingBoost = act ? Number(item?.boost || 0) : 0 + const [boost, setBoost] = useState(act ? 0 : Number(item?.boost || 0)) - const [getBoostPosition, { data }] = useLazyQuery(gql` + const { data, previousData, refetch } = useQuery(gql` query BoostPosition($sub: String, $id: ID, $boost: Int) { boostPosition(sub: $sub, id: $id, boost: $boost) { home sub + homeMaxBoost + subMaxBoost } }`, - { fetchPolicy: 'cache-and-network' }) + { + variables: { sub: item?.subName || sub?.name, boost: existingBoost + boost, id: item?.id }, + fetchPolicy: 'cache-and-network', + skip: !!item?.parentId || SSR + }) - const getPositionDebounce = useDebounceCallback((...args) => getBoostPosition(...args), 1000, [getBoostPosition]) + const getPositionDebounce = useDebounceCallback((...args) => refetch(...args), 1000, [refetch]) + const updateBoost = useCallback((boost) => { + const boostToUse = Number(boost || 0) + setBoost(boostToUse) + getPositionDebounce({ sub: item?.subName || sub?.name, boost: Number(existingBoost + boostToUse), id: item?.id }) + }, [getPositionDebounce, item?.id, item?.subName, sub?.name, existingBoost]) - useEffect(() => { - if (boost >= 0 && !item?.parentId) { - getPositionDebounce({ variables: { sub: item?.subName || sub?.name, boost: Number(boost), id: item?.id } }) - } - }, [boost, item?.id, !item?.parentId, item?.subName || sub?.name]) + const dat = data || previousData const boostMessage = useMemo(() => { - if (!item?.parentId) { - if (data?.boostPosition?.home || data?.boostPosition?.sub) { + if (!item?.parentId && boost >= BOOST_MULT) { + if (dat?.boostPosition?.home || dat?.boostPosition?.sub || boost > dat?.boostPosition?.homeMaxBoost || boost > dat?.boostPosition?.subMaxBoost) { const boostPinning = [] - if (data?.boostPosition?.home) { + if (dat?.boostPosition?.home || boost > dat?.boostPosition?.homeMaxBoost) { boostPinning.push('homepage') } - if (data?.boostPosition?.sub) { + if ((item?.subName || sub?.name) && (dat?.boostPosition?.sub || boost > dat?.boostPosition?.subMaxBoost)) { boostPinning.push(`~${item?.subName || sub?.name}`) } return `pins to the top of ${boostPinning.join(' and ')}` } } - if (boost >= 0 && boost % BOOST_MULT === 0) { - return `${act ? 'brings to' : 'equivalent to'} ${numWithUnits(boost / BOOST_MULT, { unitPlural: 'zapvotes', unitSingular: 'zapvote' })}` - } return 'ranks posts higher based on the amount' - }, [boost, data?.boostPosition?.home, data?.boostPosition?.sub, item?.subName, sub?.name]) + }, [boost, dat?.boostPosition?.home, dat?.boostPosition?.sub, item?.subName, sub?.name]) return ( - {boostMessage}} - onChange={(_, e) => { - if (e.target.value >= 0) { - setBoost(Number(e.target.value) + (act ? Number(item?.boost) : 0)) - } - }} - {...props} - /> + <> + {boostMessage}} + onChange={(_, e) => { + if (e.target.value >= 0) { + updateBoost(Number(e.target.value)) + } + }} + overrideValue={boost} + {...props} + groupClassName='mb-1' + /> + {!item?.parentId && + } + ) } diff --git a/components/adv-post-form.module.css b/components/adv-post-form.module.css index 1fee284c..e4bfe363 100644 --- a/components/adv-post-form.module.css +++ b/components/adv-post-form.module.css @@ -9,4 +9,11 @@ display: flex; flex: 0 1 fit-content; height: fit-content; +} + +.boostMax small { + font-weight: 400; + margin-left: 0.25rem; + margin-right: 0.25rem; + opacity: 0.5; } \ No newline at end of file diff --git a/components/item-act.js b/components/item-act.js index 1d3b3a95..9e38fcfb 100644 --- a/components/item-act.js +++ b/components/item-act.js @@ -70,19 +70,18 @@ function BoostForm ({ step, onSubmit, children, item, oValue, inputRef, act = 'B name='amount' type='number' innerRef={inputRef} - overrideValue={oValue} sub={item.sub} step={step} required autoFocus item={item} /> - {children}
boost
+ {children} ) } @@ -147,7 +146,7 @@ export default function ItemAct ({ onClose, item, act = 'TIP', step, children, a }, [me, actor, !!wallet, act, item.id, onClose, abortSignal, strike]) return act === 'BOOST' - ? {children} + ? {children} : (
- {children}
{act === 'DONT_LIKE_THIS' ? 'downzap' : 'zap'}
+ {children} ) }