From 5c2aa979eab2456e2448d5278afcbe5508fc4e16 Mon Sep 17 00:00:00 2001 From: soxa <6390896+Soxasora@users.noreply.github.com> Date: Fri, 7 Feb 2025 20:38:57 +0100 Subject: [PATCH] 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 --- api/paidAction/itemCreate.js | 28 +++++++++++++++++-- api/typeDefs/sub.js | 4 ++- components/reply.js | 2 +- components/territory-form.js | 8 ++++++ components/territory-header.js | 13 +++++++-- fragments/items.js | 1 + fragments/paidAction.js | 8 +++--- fragments/subs.js | 1 + lib/validate.js | 3 ++ .../migration.sql | 2 ++ prisma/schema.prisma | 1 + 11 files changed, 60 insertions(+), 11 deletions(-) create mode 100644 prisma/migrations/20241227132729_comment_fee_control/migration.sql diff --git a/api/paidAction/itemCreate.js b/api/paidAction/itemCreate.js index 8d284657..be7a77d6 100644 --- a/api/paidAction/itemCreate.js +++ b/api/paidAction/itemCreate.js @@ -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` diff --git a/api/typeDefs/sub.js b/api/typeDefs/sub.js index 29fc8c7f..a7b00aee 100644 --- a/api/typeDefs/sub.js +++ b/api/typeDefs/sub.js @@ -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! diff --git a/components/reply.js b/components/reply.js index 0928ccdf..ec12d0a5 100644 --- a/components/reply.js +++ b/components/reply.js @@ -161,7 +161,7 @@ export default forwardRef(function Reply ({ {reply &&
options
} body={ <> + sats} + /> moderation on {new Date(sub.createdAt).toDateString()} -
- post cost - {numWithUnits(sub.baseCost)} +
+
+ post cost + {numWithUnits(sub.baseCost)} +
+ \ +
+ reply cost + {numWithUnits(sub.replyCost)} +
diff --git a/fragments/items.js b/fragments/items.js index c7e1e57a..c9c2a8da 100644 --- a/fragments/items.js +++ b/fragments/items.js @@ -35,6 +35,7 @@ export const ITEM_FIELDS = gql` meMuteSub meSubscription nsfw + replyCost } otsHash position diff --git a/fragments/paidAction.js b/fragments/paidAction.js index 7a4bd44c..5b33d1cb 100644 --- a/fragments/paidAction.js +++ b/fragments/paidAction.js @@ -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 diff --git a/fragments/subs.js b/fragments/subs.js index 8b84f8d7..1ff2c492 100644 --- a/fragments/subs.js +++ b/fragments/subs.js @@ -25,6 +25,7 @@ export const SUB_FIELDS = gql` billedLastAt billPaidUntil baseCost + replyCost userId desc status diff --git a/lib/validate.js b/lib/validate.js index 7002b179..a9cda447 100644 --- a/lib/validate.js +++ b/lib/validate.js @@ -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() diff --git a/prisma/migrations/20241227132729_comment_fee_control/migration.sql b/prisma/migrations/20241227132729_comment_fee_control/migration.sql new file mode 100644 index 00000000..241212a0 --- /dev/null +++ b/prisma/migrations/20241227132729_comment_fee_control/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "Sub" ADD COLUMN "replyCost" INTEGER NOT NULL DEFAULT 1; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 236e3762..8e0e64ea 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -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)