raise boost minimum to 25k, enhance editing boost, JIT fund editing costs
This commit is contained in:
		
							parent
							
								
									374cc26224
								
							
						
					
					
						commit
						370e3c1c48
					
				@ -12,7 +12,7 @@ import {
 | 
			
		||||
import { msatsToSats, numWithUnits } from '../../lib/format'
 | 
			
		||||
import { parse } from 'tldts'
 | 
			
		||||
import uu from 'url-unshort'
 | 
			
		||||
import { amountSchema, bountySchema, commentSchema, discussionSchema, jobSchema, linkSchema, pollSchema, ssValidate } from '../../lib/validate'
 | 
			
		||||
import { advSchema, amountSchema, bountySchema, commentSchema, discussionSchema, jobSchema, linkSchema, pollSchema, ssValidate } from '../../lib/validate'
 | 
			
		||||
import { sendUserNotification } from '../webPush'
 | 
			
		||||
import { proxyImages } from './imgproxy'
 | 
			
		||||
import { defaultCommentSort } from '../../lib/item'
 | 
			
		||||
@ -624,34 +624,34 @@ export default {
 | 
			
		||||
      return await models.item.update({ where: { id: Number(id) }, data })
 | 
			
		||||
    },
 | 
			
		||||
    upsertLink: async (parent, { id, hash, hmac, ...item }, { me, models, lnd }) => {
 | 
			
		||||
      await ssValidate(linkSchema, item, models, me)
 | 
			
		||||
      await ssValidate(linkSchema, item, { models, me })
 | 
			
		||||
 | 
			
		||||
      if (id) {
 | 
			
		||||
        return await updateItem(parent, { id, ...item }, { me, models })
 | 
			
		||||
        return await updateItem(parent, { id, ...item }, { me, models, lnd, hash, hmac })
 | 
			
		||||
      } else {
 | 
			
		||||
        return await createItem(parent, item, { me, models, lnd, hash, hmac })
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    upsertDiscussion: async (parent, { id, hash, hmac, ...item }, { me, models, lnd }) => {
 | 
			
		||||
      await ssValidate(discussionSchema, item, models, me)
 | 
			
		||||
      await ssValidate(discussionSchema, item, { models, me })
 | 
			
		||||
 | 
			
		||||
      if (id) {
 | 
			
		||||
        return await updateItem(parent, { id, ...item }, { me, models })
 | 
			
		||||
        return await updateItem(parent, { id, ...item }, { me, models, lnd, hash, hmac })
 | 
			
		||||
      } else {
 | 
			
		||||
        return await createItem(parent, item, { me, models, lnd, hash, hmac })
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    upsertBounty: async (parent, { id, hash, hmac, ...item }, { me, models, lnd }) => {
 | 
			
		||||
      await ssValidate(bountySchema, item, models, me)
 | 
			
		||||
      await ssValidate(bountySchema, item, { models, me })
 | 
			
		||||
 | 
			
		||||
      if (id) {
 | 
			
		||||
        return await updateItem(parent, { id, ...item }, { me, models })
 | 
			
		||||
        return await updateItem(parent, { id, ...item }, { me, models, lnd, hash, hmac })
 | 
			
		||||
      } else {
 | 
			
		||||
        return await createItem(parent, item, { me, models, lnd, hash, hmac })
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    upsertPoll: async (parent, { id, hash, hmac, ...item }, { me, models, lnd }) => {
 | 
			
		||||
      const optionCount = id
 | 
			
		||||
      const numExistingChoices = id
 | 
			
		||||
        ? await models.pollOption.count({
 | 
			
		||||
          where: {
 | 
			
		||||
            itemId: Number(id)
 | 
			
		||||
@ -659,10 +659,10 @@ export default {
 | 
			
		||||
        })
 | 
			
		||||
        : 0
 | 
			
		||||
 | 
			
		||||
      await ssValidate(pollSchema, item, models, me, optionCount)
 | 
			
		||||
      await ssValidate(pollSchema, item, { models, me, numExistingChoices })
 | 
			
		||||
 | 
			
		||||
      if (id) {
 | 
			
		||||
        return await updateItem(parent, { id, ...item }, { me, models })
 | 
			
		||||
        return await updateItem(parent, { id, ...item }, { me, models, lnd, hash, hmac })
 | 
			
		||||
      } else {
 | 
			
		||||
        item.pollCost = item.pollCost || POLL_COST
 | 
			
		||||
        return await createItem(parent, item, { me, models, lnd, hash, hmac })
 | 
			
		||||
@ -674,7 +674,7 @@ export default {
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      item.location = item.location?.toLowerCase() === 'remote' ? undefined : item.location
 | 
			
		||||
      await ssValidate(jobSchema, item, models)
 | 
			
		||||
      await ssValidate(jobSchema, item, { models })
 | 
			
		||||
      if (item.logo !== undefined) {
 | 
			
		||||
        item.uploadId = item.logo
 | 
			
		||||
        delete item.logo
 | 
			
		||||
@ -1119,13 +1119,15 @@ export const createMentions = async (item, models) => {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const updateItem = async (parent, { sub: subName, forward, options, ...item }, { me, models }) => {
 | 
			
		||||
export const updateItem = async (parent, { sub: subName, forward, options, ...item }, { me, models, lnd, hash, hmac }) => {
 | 
			
		||||
  // update iff this item belongs to me
 | 
			
		||||
  const old = await models.item.findUnique({ where: { id: Number(item.id) } })
 | 
			
		||||
  if (Number(old.userId) !== Number(me?.id)) {
 | 
			
		||||
    throw new GraphQLError('item does not belong to you', { extensions: { code: 'FORBIDDEN' } })
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  await ssValidate(advSchema, { boost: item.boost }, { models, me, existingBoost: old.boost })
 | 
			
		||||
 | 
			
		||||
  // if it's not the FAQ, not their bio, and older than 10 minutes
 | 
			
		||||
  const user = await models.user.findUnique({ where: { id: me.id } })
 | 
			
		||||
  if (![349, 76894, 78763, 81862].includes(old.id) && user.bioId !== old.id &&
 | 
			
		||||
@ -1141,18 +1143,43 @@ export const updateItem = async (parent, { sub: subName, forward, options, ...it
 | 
			
		||||
    item.url = removeTracking(item.url)
 | 
			
		||||
    item.url = await proxyImages(item.url)
 | 
			
		||||
  }
 | 
			
		||||
  // only update item with the boost delta ... this is a bit of hack given the way
 | 
			
		||||
  // boost used to work
 | 
			
		||||
  if (item.boost > 0 && old.boost > 0) {
 | 
			
		||||
    // only update the boost if it is higher than the old boost
 | 
			
		||||
    if (item.boost > old.boost) {
 | 
			
		||||
      item.boost = item.boost - old.boost
 | 
			
		||||
    } else {
 | 
			
		||||
      delete item.boost
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  item = { subName, userId: me.id, ...item }
 | 
			
		||||
  const fwdUsers = await getForwardUsers(models, forward)
 | 
			
		||||
 | 
			
		||||
  const [rItem] = await serialize(models,
 | 
			
		||||
  let invoice
 | 
			
		||||
  if (hash) {
 | 
			
		||||
    invoice = await checkInvoice(models, hash, hmac)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const trx = [
 | 
			
		||||
    models.$queryRawUnsafe(`${SELECT} FROM update_item($1::JSONB, $2::JSONB, $3::JSONB) AS "Item"`,
 | 
			
		||||
      JSON.stringify(item), JSON.stringify(fwdUsers), JSON.stringify(options)))
 | 
			
		||||
      JSON.stringify(item), JSON.stringify(fwdUsers), JSON.stringify(options))
 | 
			
		||||
  ]
 | 
			
		||||
  if (invoice) {
 | 
			
		||||
    trx.unshift(models.$queryRaw`UPDATE users SET msats = msats + ${invoice.msatsReceived} WHERE id = ${invoice.user.id}`)
 | 
			
		||||
    trx.push(models.invoice.update({ where: { hash: invoice.hash }, data: { confirmedAt: new Date() } }))
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const query = await serialize(models, ...trx)
 | 
			
		||||
  const rItem = trx.length > 1 ? query[1][0] : query[0]
 | 
			
		||||
 | 
			
		||||
  if (invoice?.isHeld) await settleHodlInvoice({ secret: invoice.preimage, lnd })
 | 
			
		||||
 | 
			
		||||
  await createMentions(rItem, models)
 | 
			
		||||
 | 
			
		||||
  item.comments = []
 | 
			
		||||
  return item
 | 
			
		||||
  rItem.comments = []
 | 
			
		||||
  return rItem
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const createItem = async (parent, { forward, options, ...item }, { me, models, lnd, hash, hmac }) => {
 | 
			
		||||
 | 
			
		||||
@ -484,7 +484,7 @@ export default {
 | 
			
		||||
        throw new GraphQLError('you must be logged in', { extensions: { code: 'UNAUTHENTICATED' } })
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      await ssValidate(userSchema, data, models)
 | 
			
		||||
      await ssValidate(userSchema, data, { models })
 | 
			
		||||
 | 
			
		||||
      try {
 | 
			
		||||
        await models.user.update({ where: { id: me.id }, data })
 | 
			
		||||
 | 
			
		||||
@ -1,21 +1,21 @@
 | 
			
		||||
import AccordianItem from './accordian-item'
 | 
			
		||||
import { Input, InputUserSuggest, VariableInput } from './form'
 | 
			
		||||
import InputGroup from 'react-bootstrap/InputGroup'
 | 
			
		||||
import { BOOST_MIN, MAX_FORWARDS } from '../lib/constants'
 | 
			
		||||
import { BOOST_MIN, BOOST_MULT, MAX_FORWARDS } from '../lib/constants'
 | 
			
		||||
import Info from './info'
 | 
			
		||||
import { numWithUnits } from '../lib/format'
 | 
			
		||||
import styles from './adv-post-form.module.css'
 | 
			
		||||
 | 
			
		||||
const EMPTY_FORWARD = { nym: '', pct: '' }
 | 
			
		||||
 | 
			
		||||
export function AdvPostInitial ({ forward }) {
 | 
			
		||||
export function AdvPostInitial ({ forward, boost }) {
 | 
			
		||||
  return {
 | 
			
		||||
    boost: '',
 | 
			
		||||
    boost: boost || '',
 | 
			
		||||
    forward: forward?.length ? forward : [EMPTY_FORWARD]
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default function AdvPostForm ({ edit }) {
 | 
			
		||||
export default function AdvPostForm () {
 | 
			
		||||
  return (
 | 
			
		||||
    <AccordianItem
 | 
			
		||||
      header={<div style={{ fontWeight: 'bold', fontSize: '92%' }}>options</div>}
 | 
			
		||||
@ -23,17 +23,17 @@ export default function AdvPostForm ({ edit }) {
 | 
			
		||||
        <>
 | 
			
		||||
          <Input
 | 
			
		||||
            label={
 | 
			
		||||
              <div className='d-flex align-items-center'>{edit ? 'add boost' : 'boost'}
 | 
			
		||||
              <div className='d-flex align-items-center'>boost
 | 
			
		||||
                <Info>
 | 
			
		||||
                  <ol className='fw-bold'>
 | 
			
		||||
                    <li>Boost ranks posts higher temporarily based on the amount</li>
 | 
			
		||||
                    <li>The minimum boost is {numWithUnits(BOOST_MIN, { abbreviate: false })}</li>
 | 
			
		||||
                    <li>Each {numWithUnits(BOOST_MIN, { abbreviate: false })} of boost is equivalent to one trusted upvote
 | 
			
		||||
                    <li>Each {numWithUnits(BOOST_MULT, { abbreviate: false })} of boost is equivalent to one trusted upvote
 | 
			
		||||
                      <ul>
 | 
			
		||||
                        <li>e.g. {numWithUnits(BOOST_MIN * 2, { abbreviate: false })} is like 2 votes</li>
 | 
			
		||||
                        <li>e.g. {numWithUnits(BOOST_MULT * 5, { abbreviate: false })} is like 5 votes</li>
 | 
			
		||||
                      </ul>
 | 
			
		||||
                    </li>
 | 
			
		||||
                    <li>The decay of boost "votes" increases at 2x the rate of organic votes
 | 
			
		||||
                    <li>The decay of boost "votes" increases at 1.25x the rate of organic votes
 | 
			
		||||
                      <ul>
 | 
			
		||||
                        <li>i.e. boost votes fall out of ranking faster</li>
 | 
			
		||||
                      </ul>
 | 
			
		||||
 | 
			
		||||
@ -27,7 +27,7 @@ export function BountyForm ({
 | 
			
		||||
  const router = useRouter()
 | 
			
		||||
  const client = useApolloClient()
 | 
			
		||||
  const me = useMe()
 | 
			
		||||
  const schema = bountySchema(client, me)
 | 
			
		||||
  const schema = bountySchema({ client, me, existingBoost: item?.boost })
 | 
			
		||||
  const [upsertBounty] = useMutation(
 | 
			
		||||
    gql`
 | 
			
		||||
      mutation upsertBounty(
 | 
			
		||||
@ -89,7 +89,7 @@ export function BountyForm ({
 | 
			
		||||
        title: item?.title || '',
 | 
			
		||||
        text: item?.text || '',
 | 
			
		||||
        bounty: item?.bounty || 1000,
 | 
			
		||||
        ...AdvPostInitial({ forward: normalizeForwards(item?.forwards) }),
 | 
			
		||||
        ...AdvPostInitial({ forward: normalizeForwards(item?.forwards), boost: item?.boost }),
 | 
			
		||||
        ...SubSelectInitial({ sub: item?.subName || sub?.name })
 | 
			
		||||
      }}
 | 
			
		||||
      schema={schema}
 | 
			
		||||
 | 
			
		||||
@ -25,7 +25,7 @@ export function DiscussionForm ({
 | 
			
		||||
  const router = useRouter()
 | 
			
		||||
  const client = useApolloClient()
 | 
			
		||||
  const me = useMe()
 | 
			
		||||
  const schema = discussionSchema(client, me)
 | 
			
		||||
  const schema = discussionSchema({ client, me, existingBoost: item?.boost })
 | 
			
		||||
  // if Web Share Target API was used
 | 
			
		||||
  const shareTitle = router.query.title
 | 
			
		||||
 | 
			
		||||
@ -81,7 +81,7 @@ export function DiscussionForm ({
 | 
			
		||||
      initial={{
 | 
			
		||||
        title: item?.title || shareTitle || '',
 | 
			
		||||
        text: item?.text || '',
 | 
			
		||||
        ...AdvPostInitial({ forward: normalizeForwards(item?.forwards) }),
 | 
			
		||||
        ...AdvPostInitial({ forward: normalizeForwards(item?.forwards), boost: item?.boost }),
 | 
			
		||||
        ...SubSelectInitial({ sub: item?.subName || sub?.name })
 | 
			
		||||
      }}
 | 
			
		||||
      schema={schema}
 | 
			
		||||
 | 
			
		||||
@ -137,7 +137,7 @@ function EditReceipt ({ cost, paidSats, addImgLink, boost, parentId }) {
 | 
			
		||||
 | 
			
		||||
export function EditFeeButton ({ paidSats, hadImgLink, hasImgLink, ChildButton, variant, text, alwaysShow, parentId }) {
 | 
			
		||||
  const formik = useFormikContext()
 | 
			
		||||
  const boost = formik?.values?.boost || 0
 | 
			
		||||
  const boost = (formik?.values?.boost || 0) - (formik?.initialValues?.boost || 0)
 | 
			
		||||
  const addImgLink = hasImgLink && !hadImgLink
 | 
			
		||||
  const cost = (addImgLink ? paidSats * 9 : 0) + Number(boost)
 | 
			
		||||
 | 
			
		||||
@ -148,7 +148,7 @@ export function EditFeeButton ({ paidSats, hadImgLink, hasImgLink, ChildButton,
 | 
			
		||||
  const show = alwaysShow || !formik?.isSubmitting
 | 
			
		||||
  return (
 | 
			
		||||
    <div className='d-flex align-items-center'>
 | 
			
		||||
      <ActionTooltip overlayText={numWithUnits(cost, { abbreviate: false })}>
 | 
			
		||||
      <ActionTooltip overlayText={numWithUnits(cost >= 0 ? cost : 0, { abbreviate: false })}>
 | 
			
		||||
        <ChildButton variant={variant}>{text}{cost > 0 && show && <small> {numWithUnits(cost, { abbreviate: false })}</small>}</ChildButton>
 | 
			
		||||
      </ActionTooltip>
 | 
			
		||||
      {cost > 0 && show &&
 | 
			
		||||
 | 
			
		||||
@ -22,7 +22,7 @@ export function LinkForm ({ item, sub, editThreshold, children }) {
 | 
			
		||||
  const router = useRouter()
 | 
			
		||||
  const client = useApolloClient()
 | 
			
		||||
  const me = useMe()
 | 
			
		||||
  const schema = linkSchema(client, me)
 | 
			
		||||
  const schema = linkSchema({ client, me, existingBoost: item?.boost })
 | 
			
		||||
  // if Web Share Target API was used
 | 
			
		||||
  const shareUrl = router.query.url
 | 
			
		||||
  const shareTitle = router.query.title
 | 
			
		||||
@ -123,7 +123,7 @@ export function LinkForm ({ item, sub, editThreshold, children }) {
 | 
			
		||||
      initial={{
 | 
			
		||||
        title: item?.title || shareTitle || '',
 | 
			
		||||
        url: item?.url || shareUrl || '',
 | 
			
		||||
        ...AdvPostInitial({ forward: normalizeForwards(item?.forwards) }),
 | 
			
		||||
        ...AdvPostInitial({ forward: normalizeForwards(item?.forwards), boost: item?.boost }),
 | 
			
		||||
        ...SubSelectInitial({ sub: item?.subName || sub?.name })
 | 
			
		||||
      }}
 | 
			
		||||
      schema={schema}
 | 
			
		||||
 | 
			
		||||
@ -18,7 +18,7 @@ export function PollForm ({ item, sub, editThreshold, children }) {
 | 
			
		||||
  const router = useRouter()
 | 
			
		||||
  const client = useApolloClient()
 | 
			
		||||
  const me = useMe()
 | 
			
		||||
  const schema = pollSchema(client, me)
 | 
			
		||||
  const schema = pollSchema({ client, me, existingBoost: item?.boost })
 | 
			
		||||
 | 
			
		||||
  const [upsertPoll] = useMutation(
 | 
			
		||||
    gql`
 | 
			
		||||
@ -65,7 +65,7 @@ export function PollForm ({ item, sub, editThreshold, children }) {
 | 
			
		||||
        title: item?.title || '',
 | 
			
		||||
        text: item?.text || '',
 | 
			
		||||
        options: initialOptions || ['', ''],
 | 
			
		||||
        ...AdvPostInitial({ forward: normalizeForwards(item?.forwards) }),
 | 
			
		||||
        ...AdvPostInitial({ forward: normalizeForwards(item?.forwards), boost: item?.boost }),
 | 
			
		||||
        ...SubSelectInitial({ sub: item?.subName || sub?.name })
 | 
			
		||||
      }}
 | 
			
		||||
      schema={schema}
 | 
			
		||||
 | 
			
		||||
@ -105,7 +105,7 @@ function NymEdit ({ user, setEditting }) {
 | 
			
		||||
    }
 | 
			
		||||
  })
 | 
			
		||||
  const client = useApolloClient()
 | 
			
		||||
  const schema = userSchema(client)
 | 
			
		||||
  const schema = userSchema({ client })
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <Form
 | 
			
		||||
 | 
			
		||||
@ -4,7 +4,8 @@ export const SUBS = ['bitcoin', 'nostr', 'tech', 'meta', 'jobs']
 | 
			
		||||
export const SUBS_NO_JOBS = SUBS.filter(s => s !== 'jobs')
 | 
			
		||||
 | 
			
		||||
export const NOFOLLOW_LIMIT = 100
 | 
			
		||||
export const BOOST_MIN = 5000
 | 
			
		||||
export const BOOST_MULT = 5000
 | 
			
		||||
export const BOOST_MIN = BOOST_MULT * 5
 | 
			
		||||
export const UPLOAD_SIZE_MAX = 2 * 1024 * 1024
 | 
			
		||||
export const IMAGE_PIXELS_MAX = 35000000
 | 
			
		||||
export const UPLOAD_TYPES_ALLOW = [
 | 
			
		||||
 | 
			
		||||
@ -1,14 +1,14 @@
 | 
			
		||||
import { string, ValidationError, number, object, array, addMethod, boolean } from 'yup'
 | 
			
		||||
import { BOOST_MIN, MAX_POLL_CHOICE_LENGTH, MAX_TITLE_LENGTH, MAX_POLL_NUM_CHOICES, MIN_POLL_NUM_CHOICES, SUBS_NO_JOBS, MAX_FORWARDS } from './constants'
 | 
			
		||||
import { BOOST_MIN, MAX_POLL_CHOICE_LENGTH, MAX_TITLE_LENGTH, MAX_POLL_NUM_CHOICES, MIN_POLL_NUM_CHOICES, SUBS_NO_JOBS, MAX_FORWARDS, BOOST_MULT } from './constants'
 | 
			
		||||
import { NAME_QUERY } from '../fragments/users'
 | 
			
		||||
import { URL_REGEXP, WS_REGEXP } from './url'
 | 
			
		||||
import { SUPPORTED_CURRENCIES } from './currency'
 | 
			
		||||
import { NOSTR_MAX_RELAY_NUM, NOSTR_PUBKEY_BECH32, NOSTR_PUBKEY_HEX } from './nostr'
 | 
			
		||||
 | 
			
		||||
export async function ssValidate (schema, data, ...args) {
 | 
			
		||||
export async function ssValidate (schema, data, args) {
 | 
			
		||||
  try {
 | 
			
		||||
    if (typeof schema === 'function') {
 | 
			
		||||
      await schema(...args).validate(data)
 | 
			
		||||
      await schema(args).validate(data)
 | 
			
		||||
    } else {
 | 
			
		||||
      await schema.validate(data)
 | 
			
		||||
    }
 | 
			
		||||
@ -43,28 +43,29 @@ const titleValidator = string().required('required').trim().max(
 | 
			
		||||
 | 
			
		||||
const intValidator = number().typeError('must be a number').integer('must be whole')
 | 
			
		||||
 | 
			
		||||
async function usernameExists (client, name) {
 | 
			
		||||
  if (!client) {
 | 
			
		||||
async function usernameExists (name, { client, models }) {
 | 
			
		||||
  if (!client && !models) {
 | 
			
		||||
    throw new Error('cannot check for user')
 | 
			
		||||
  }
 | 
			
		||||
  // apollo client
 | 
			
		||||
  if (client.query) {
 | 
			
		||||
  if (client) {
 | 
			
		||||
    const { data } = await client.query({ query: NAME_QUERY, variables: { name } })
 | 
			
		||||
    return !data.nameAvailable
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // prisma client
 | 
			
		||||
  const user = await client.user.findUnique({ where: { name } })
 | 
			
		||||
  const user = await models.user.findUnique({ where: { name } })
 | 
			
		||||
  return !!user
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function advPostSchemaMembers (client, me) {
 | 
			
		||||
export function advPostSchemaMembers ({ me, existingBoost = 0, ...args }) {
 | 
			
		||||
  const boostMin = existingBoost || BOOST_MIN
 | 
			
		||||
  return {
 | 
			
		||||
    boost: intValidator
 | 
			
		||||
      .min(BOOST_MIN, `must be blank or at least ${BOOST_MIN}`).test({
 | 
			
		||||
      .min(boostMin, `must be ${existingBoost ? '' : 'blank or '}at least ${boostMin}`).test({
 | 
			
		||||
        name: 'boost',
 | 
			
		||||
        test: async boost => !boost || boost % BOOST_MIN === 0,
 | 
			
		||||
        message: `must be divisble be ${BOOST_MIN}`
 | 
			
		||||
        test: async boost => (!existingBoost && !boost) || boost % BOOST_MULT === 0,
 | 
			
		||||
        message: `must be divisble be ${BOOST_MULT}`
 | 
			
		||||
      }),
 | 
			
		||||
    forward: array()
 | 
			
		||||
      .max(MAX_FORWARDS, `you can only configure ${MAX_FORWARDS} forward recipients`)
 | 
			
		||||
@ -74,7 +75,7 @@ export function advPostSchemaMembers (client, me) {
 | 
			
		||||
            name: 'nym',
 | 
			
		||||
            test: async name => {
 | 
			
		||||
              if (!name || !name.length) return false
 | 
			
		||||
              return await usernameExists(client, name)
 | 
			
		||||
              return await usernameExists(name, args)
 | 
			
		||||
            },
 | 
			
		||||
            message: 'stacker does not exist'
 | 
			
		||||
          })
 | 
			
		||||
@ -101,41 +102,47 @@ export function advPostSchemaMembers (client, me) {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function subSelectSchemaMembers (client) {
 | 
			
		||||
export function subSelectSchemaMembers () {
 | 
			
		||||
  return {
 | 
			
		||||
    sub: string().required('required').oneOf(SUBS_NO_JOBS, 'required')
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
// for testing advPostSchemaMembers in isolation
 | 
			
		||||
export function advSchema (args) {
 | 
			
		||||
  return object({
 | 
			
		||||
    ...advPostSchemaMembers(args)
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function bountySchema (client, me) {
 | 
			
		||||
export function bountySchema (args) {
 | 
			
		||||
  return object({
 | 
			
		||||
    title: titleValidator,
 | 
			
		||||
    bounty: intValidator
 | 
			
		||||
      .min(1000, 'must be at least 1000')
 | 
			
		||||
      .max(1000000, 'must be at most 1m'),
 | 
			
		||||
    ...advPostSchemaMembers(client, me),
 | 
			
		||||
    ...advPostSchemaMembers(args),
 | 
			
		||||
    ...subSelectSchemaMembers()
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function discussionSchema (client, me) {
 | 
			
		||||
export function discussionSchema (args) {
 | 
			
		||||
  return object({
 | 
			
		||||
    title: titleValidator,
 | 
			
		||||
    ...advPostSchemaMembers(client, me),
 | 
			
		||||
    ...advPostSchemaMembers(args),
 | 
			
		||||
    ...subSelectSchemaMembers()
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function linkSchema (client, me) {
 | 
			
		||||
export function linkSchema (args) {
 | 
			
		||||
  return object({
 | 
			
		||||
    title: titleValidator,
 | 
			
		||||
    url: string().matches(URL_REGEXP, 'invalid url').required('required'),
 | 
			
		||||
    ...advPostSchemaMembers(client, me),
 | 
			
		||||
    ...advPostSchemaMembers(args),
 | 
			
		||||
    ...subSelectSchemaMembers()
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function pollSchema (client, me, numExistingChoices = 0) {
 | 
			
		||||
export function pollSchema ({ numExistingChoices = 0, ...args }) {
 | 
			
		||||
  return object({
 | 
			
		||||
    title: titleValidator,
 | 
			
		||||
    options: array().of(
 | 
			
		||||
@ -151,12 +158,12 @@ export function pollSchema (client, me, numExistingChoices = 0) {
 | 
			
		||||
      message: `at least ${MIN_POLL_NUM_CHOICES} choices required`,
 | 
			
		||||
      test: arr => arr.length >= MIN_POLL_NUM_CHOICES - numExistingChoices
 | 
			
		||||
    }),
 | 
			
		||||
    ...advPostSchemaMembers(client, me),
 | 
			
		||||
    ...advPostSchemaMembers(args),
 | 
			
		||||
    ...subSelectSchemaMembers()
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function userSchema (client) {
 | 
			
		||||
export function userSchema (args) {
 | 
			
		||||
  return object({
 | 
			
		||||
    name: string()
 | 
			
		||||
      .required('required')
 | 
			
		||||
@ -166,7 +173,7 @@ export function userSchema (client) {
 | 
			
		||||
        name: 'name',
 | 
			
		||||
        test: async name => {
 | 
			
		||||
          if (!name || !name.length) return false
 | 
			
		||||
          return !(await usernameExists(client, name))
 | 
			
		||||
          return !(await usernameExists(name, args))
 | 
			
		||||
        },
 | 
			
		||||
        message: 'taken'
 | 
			
		||||
      })
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user