feat: comment fee control (#1768)
* feat: comment fee control * update typeDefs for unarchiving territories * review: move functions to top level; consider saloon items * ux: cleaner post/reply cost section * hotfix: handle salon replies * bios don't have subs + simplify root query * move reply cost to accordian --------- Co-authored-by: Keyan <34140557+huumn@users.noreply.github.com> Co-authored-by: k00b <k00b@stacker.news>
This commit is contained in:
		
							parent
							
								
									ac321be3cd
								
							
						
					
					
						commit
						5c2aa979ea
					
				@ -13,9 +13,33 @@ export const paymentMethods = [
 | 
			
		||||
  PAID_ACTION_PAYMENT_METHODS.PESSIMISTIC
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
export const DEFAULT_ITEM_COST = 1000n
 | 
			
		||||
 | 
			
		||||
export async function getBaseCost ({ models, bio, parentId, subName }) {
 | 
			
		||||
  if (bio) return DEFAULT_ITEM_COST
 | 
			
		||||
 | 
			
		||||
  if (parentId) {
 | 
			
		||||
    // the subname is stored in the root item of the thread
 | 
			
		||||
    const parent = await models.item.findFirst({
 | 
			
		||||
      where: { id: Number(parentId) },
 | 
			
		||||
      include: {
 | 
			
		||||
        root: { include: { sub: true } },
 | 
			
		||||
        sub: true
 | 
			
		||||
      }
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    const root = parent.root ?? parent
 | 
			
		||||
 | 
			
		||||
    if (!root.sub) return DEFAULT_ITEM_COST
 | 
			
		||||
    return satsToMsats(root.sub.replyCost)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const sub = await models.sub.findUnique({ where: { name: subName } })
 | 
			
		||||
  return satsToMsats(sub.baseCost)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export async function getCost ({ subName, parentId, uploadIds, boost = 0, bio }, { models, me }) {
 | 
			
		||||
  const sub = (parentId || bio) ? null : await models.sub.findUnique({ where: { name: subName } })
 | 
			
		||||
  const baseCost = sub ? satsToMsats(sub.baseCost) : 1000n
 | 
			
		||||
  const baseCost = await getBaseCost({ models, bio, parentId, subName })
 | 
			
		||||
 | 
			
		||||
  // cost = baseCost * 10^num_items_in_10m * 100 (anon) or 1 (user) + upload fees + boost
 | 
			
		||||
  const [{ cost }] = await models.$queryRaw`
 | 
			
		||||
 | 
			
		||||
@ -16,6 +16,7 @@ export default gql`
 | 
			
		||||
 | 
			
		||||
  extend type Mutation {
 | 
			
		||||
    upsertSub(oldName: String, name: String!, desc: String, baseCost: Int!,
 | 
			
		||||
      replyCost: Int!,
 | 
			
		||||
      postTypes: [String!]!,
 | 
			
		||||
      billingType: String!, billingAutoRenew: Boolean!,
 | 
			
		||||
      moderated: Boolean!, nsfw: Boolean!): SubPaidAction!
 | 
			
		||||
@ -24,7 +25,7 @@ export default gql`
 | 
			
		||||
    toggleSubSubscription(name: String!): Boolean!
 | 
			
		||||
    transferTerritory(subName: String!, userName: String!): Sub
 | 
			
		||||
    unarchiveTerritory(name: String!, desc: String, baseCost: Int!,
 | 
			
		||||
      postTypes: [String!]!,
 | 
			
		||||
      replyCost: Int!, postTypes: [String!]!,
 | 
			
		||||
      billingType: String!, billingAutoRenew: Boolean!,
 | 
			
		||||
      moderated: Boolean!, nsfw: Boolean!): SubPaidAction!
 | 
			
		||||
  }
 | 
			
		||||
@ -45,6 +46,7 @@ export default gql`
 | 
			
		||||
    billedLastAt: Date!
 | 
			
		||||
    billPaidUntil: Date
 | 
			
		||||
    baseCost: Int!
 | 
			
		||||
    replyCost: Int!
 | 
			
		||||
    status: String!
 | 
			
		||||
    moderated: Boolean!
 | 
			
		||||
    moderatedCount: Int!
 | 
			
		||||
 | 
			
		||||
@ -161,7 +161,7 @@ export default forwardRef(function Reply ({
 | 
			
		||||
      {reply &&
 | 
			
		||||
        <div className={styles.reply}>
 | 
			
		||||
          <FeeButtonProvider
 | 
			
		||||
            baseLineItems={postCommentBaseLineItems({ baseCost: 1, comment: true, me: !!me })}
 | 
			
		||||
            baseLineItems={postCommentBaseLineItems({ baseCost: sub?.replyCost ?? 1, comment: true, me: !!me })}
 | 
			
		||||
            useRemoteLineItems={postCommentUseRemoteLineItems({ parentId: item.id, me: !!me })}
 | 
			
		||||
          >
 | 
			
		||||
            <Form
 | 
			
		||||
 | 
			
		||||
@ -91,6 +91,7 @@ export default function TerritoryForm ({ sub }) {
 | 
			
		||||
          name: sub?.name || '',
 | 
			
		||||
          desc: sub?.desc || '',
 | 
			
		||||
          baseCost: sub?.baseCost || 10,
 | 
			
		||||
          replyCost: sub?.replyCost || 1,
 | 
			
		||||
          postTypes: sub?.postTypes || POST_TYPES,
 | 
			
		||||
          billingType: sub?.billingType || 'MONTHLY',
 | 
			
		||||
          billingAutoRenew: sub?.billingAutoRenew || false,
 | 
			
		||||
@ -234,6 +235,13 @@ export default function TerritoryForm ({ sub }) {
 | 
			
		||||
          header={<div style={{ fontWeight: 'bold', fontSize: '92%' }}>options</div>}
 | 
			
		||||
          body={
 | 
			
		||||
            <>
 | 
			
		||||
              <Input
 | 
			
		||||
                label='reply cost'
 | 
			
		||||
                name='replyCost'
 | 
			
		||||
                type='number'
 | 
			
		||||
                required
 | 
			
		||||
                append={<InputGroup.Text className='text-monospace'>sats</InputGroup.Text>}
 | 
			
		||||
              />
 | 
			
		||||
              <BootstrapForm.Label>moderation</BootstrapForm.Label>
 | 
			
		||||
              <Checkbox
 | 
			
		||||
                inline
 | 
			
		||||
 | 
			
		||||
@ -57,9 +57,16 @@ export function TerritoryInfo ({ sub }) {
 | 
			
		||||
          <span> on </span>
 | 
			
		||||
          <span className='fw-bold'>{new Date(sub.createdAt).toDateString()}</span>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div className='text-muted'>
 | 
			
		||||
          <span>post cost </span>
 | 
			
		||||
          <span className='fw-bold'>{numWithUnits(sub.baseCost)}</span>
 | 
			
		||||
        <div className='d-flex'>
 | 
			
		||||
          <div className='text-muted'>
 | 
			
		||||
            <span>post cost </span>
 | 
			
		||||
            <span className='fw-bold'>{numWithUnits(sub.baseCost)}</span>
 | 
			
		||||
          </div>
 | 
			
		||||
          <span className='px-1'> \ </span>
 | 
			
		||||
          <div className='text-muted'>
 | 
			
		||||
            <span>reply cost </span>
 | 
			
		||||
            <span className='fw-bold'>{numWithUnits(sub.replyCost)}</span>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
        <TerritoryBillingLine sub={sub} />
 | 
			
		||||
      </CardFooter>
 | 
			
		||||
 | 
			
		||||
@ -35,6 +35,7 @@ export const ITEM_FIELDS = gql`
 | 
			
		||||
      meMuteSub
 | 
			
		||||
      meSubscription
 | 
			
		||||
      nsfw
 | 
			
		||||
      replyCost
 | 
			
		||||
    }
 | 
			
		||||
    otsHash
 | 
			
		||||
    position
 | 
			
		||||
 | 
			
		||||
@ -264,10 +264,10 @@ export const UPDATE_COMMENT = gql`
 | 
			
		||||
export const UPSERT_SUB = gql`
 | 
			
		||||
  ${PAID_ACTION}
 | 
			
		||||
  mutation upsertSub($oldName: String, $name: String!, $desc: String, $baseCost: Int!,
 | 
			
		||||
    $postTypes: [String!]!, $billingType: String!,
 | 
			
		||||
    $replyCost: Int!, $postTypes: [String!]!, $billingType: String!,
 | 
			
		||||
    $billingAutoRenew: Boolean!, $moderated: Boolean!, $nsfw: Boolean!) {
 | 
			
		||||
      upsertSub(oldName: $oldName, name: $name, desc: $desc, baseCost: $baseCost,
 | 
			
		||||
        postTypes: $postTypes, billingType: $billingType,
 | 
			
		||||
        replyCost: $replyCost, postTypes: $postTypes, billingType: $billingType,
 | 
			
		||||
        billingAutoRenew: $billingAutoRenew, moderated: $moderated, nsfw: $nsfw) {
 | 
			
		||||
      result {
 | 
			
		||||
        name
 | 
			
		||||
@ -279,10 +279,10 @@ export const UPSERT_SUB = gql`
 | 
			
		||||
export const UNARCHIVE_TERRITORY = gql`
 | 
			
		||||
  ${PAID_ACTION}
 | 
			
		||||
  mutation unarchiveTerritory($name: String!, $desc: String, $baseCost: Int!,
 | 
			
		||||
    $postTypes: [String!]!, $billingType: String!,
 | 
			
		||||
    $replyCost: Int!, $postTypes: [String!]!, $billingType: String!,
 | 
			
		||||
    $billingAutoRenew: Boolean!, $moderated: Boolean!, $nsfw: Boolean!) {
 | 
			
		||||
      unarchiveTerritory(name: $name, desc: $desc, baseCost: $baseCost,
 | 
			
		||||
        postTypes: $postTypes, billingType: $billingType,
 | 
			
		||||
        replyCost: $replyCost, postTypes: $postTypes, billingType: $billingType,
 | 
			
		||||
        billingAutoRenew: $billingAutoRenew, moderated: $moderated, nsfw: $nsfw) {
 | 
			
		||||
      result {
 | 
			
		||||
        name
 | 
			
		||||
 | 
			
		||||
@ -25,6 +25,7 @@ export const SUB_FIELDS = gql`
 | 
			
		||||
    billedLastAt
 | 
			
		||||
    billPaidUntil
 | 
			
		||||
    baseCost
 | 
			
		||||
    replyCost
 | 
			
		||||
    userId
 | 
			
		||||
    desc
 | 
			
		||||
    status
 | 
			
		||||
 | 
			
		||||
@ -317,6 +317,9 @@ export function territorySchema (args) {
 | 
			
		||||
    baseCost: intValidator
 | 
			
		||||
      .min(1, 'must be at least 1')
 | 
			
		||||
      .max(100000, 'must be at most 100k'),
 | 
			
		||||
    replyCost: intValidator
 | 
			
		||||
      .min(1, 'must be at least 1')
 | 
			
		||||
      .max(100000, 'must be at most 100k'),
 | 
			
		||||
    postTypes: array().of(string().oneOf(POST_TYPES)).min(1, 'must support at least one post type'),
 | 
			
		||||
    billingType: string().required('required').oneOf(TERRITORY_BILLING_TYPES, 'required'),
 | 
			
		||||
    nsfw: boolean()
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,2 @@
 | 
			
		||||
-- AlterTable
 | 
			
		||||
ALTER TABLE "Sub" ADD COLUMN     "replyCost" INTEGER NOT NULL DEFAULT 1;
 | 
			
		||||
@ -737,6 +737,7 @@ model Sub {
 | 
			
		||||
  rankingType      RankingType
 | 
			
		||||
  allowFreebies    Boolean     @default(true)
 | 
			
		||||
  baseCost         Int         @default(1)
 | 
			
		||||
  replyCost        Int         @default(1)
 | 
			
		||||
  rewardsPct       Int         @default(50)
 | 
			
		||||
  desc             String?
 | 
			
		||||
  status           Status      @default(ACTIVE)
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user