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
|
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 }) {
|
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 = await getBaseCost({ models, bio, parentId, subName })
|
||||||
const baseCost = sub ? satsToMsats(sub.baseCost) : 1000n
|
|
||||||
|
|
||||||
// cost = baseCost * 10^num_items_in_10m * 100 (anon) or 1 (user) + upload fees + boost
|
// cost = baseCost * 10^num_items_in_10m * 100 (anon) or 1 (user) + upload fees + boost
|
||||||
const [{ cost }] = await models.$queryRaw`
|
const [{ cost }] = await models.$queryRaw`
|
||||||
|
@ -16,6 +16,7 @@ export default gql`
|
|||||||
|
|
||||||
extend type Mutation {
|
extend type Mutation {
|
||||||
upsertSub(oldName: String, name: String!, desc: String, baseCost: Int!,
|
upsertSub(oldName: String, name: String!, desc: String, baseCost: Int!,
|
||||||
|
replyCost: Int!,
|
||||||
postTypes: [String!]!,
|
postTypes: [String!]!,
|
||||||
billingType: String!, billingAutoRenew: Boolean!,
|
billingType: String!, billingAutoRenew: Boolean!,
|
||||||
moderated: Boolean!, nsfw: Boolean!): SubPaidAction!
|
moderated: Boolean!, nsfw: Boolean!): SubPaidAction!
|
||||||
@ -24,7 +25,7 @@ export default gql`
|
|||||||
toggleSubSubscription(name: String!): Boolean!
|
toggleSubSubscription(name: String!): Boolean!
|
||||||
transferTerritory(subName: String!, userName: String!): Sub
|
transferTerritory(subName: String!, userName: String!): Sub
|
||||||
unarchiveTerritory(name: String!, desc: String, baseCost: Int!,
|
unarchiveTerritory(name: String!, desc: String, baseCost: Int!,
|
||||||
postTypes: [String!]!,
|
replyCost: Int!, postTypes: [String!]!,
|
||||||
billingType: String!, billingAutoRenew: Boolean!,
|
billingType: String!, billingAutoRenew: Boolean!,
|
||||||
moderated: Boolean!, nsfw: Boolean!): SubPaidAction!
|
moderated: Boolean!, nsfw: Boolean!): SubPaidAction!
|
||||||
}
|
}
|
||||||
@ -45,6 +46,7 @@ export default gql`
|
|||||||
billedLastAt: Date!
|
billedLastAt: Date!
|
||||||
billPaidUntil: Date
|
billPaidUntil: Date
|
||||||
baseCost: Int!
|
baseCost: Int!
|
||||||
|
replyCost: Int!
|
||||||
status: String!
|
status: String!
|
||||||
moderated: Boolean!
|
moderated: Boolean!
|
||||||
moderatedCount: Int!
|
moderatedCount: Int!
|
||||||
|
@ -161,7 +161,7 @@ export default forwardRef(function Reply ({
|
|||||||
{reply &&
|
{reply &&
|
||||||
<div className={styles.reply}>
|
<div className={styles.reply}>
|
||||||
<FeeButtonProvider
|
<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 })}
|
useRemoteLineItems={postCommentUseRemoteLineItems({ parentId: item.id, me: !!me })}
|
||||||
>
|
>
|
||||||
<Form
|
<Form
|
||||||
|
@ -91,6 +91,7 @@ export default function TerritoryForm ({ sub }) {
|
|||||||
name: sub?.name || '',
|
name: sub?.name || '',
|
||||||
desc: sub?.desc || '',
|
desc: sub?.desc || '',
|
||||||
baseCost: sub?.baseCost || 10,
|
baseCost: sub?.baseCost || 10,
|
||||||
|
replyCost: sub?.replyCost || 1,
|
||||||
postTypes: sub?.postTypes || POST_TYPES,
|
postTypes: sub?.postTypes || POST_TYPES,
|
||||||
billingType: sub?.billingType || 'MONTHLY',
|
billingType: sub?.billingType || 'MONTHLY',
|
||||||
billingAutoRenew: sub?.billingAutoRenew || false,
|
billingAutoRenew: sub?.billingAutoRenew || false,
|
||||||
@ -234,6 +235,13 @@ export default function TerritoryForm ({ sub }) {
|
|||||||
header={<div style={{ fontWeight: 'bold', fontSize: '92%' }}>options</div>}
|
header={<div style={{ fontWeight: 'bold', fontSize: '92%' }}>options</div>}
|
||||||
body={
|
body={
|
||||||
<>
|
<>
|
||||||
|
<Input
|
||||||
|
label='reply cost'
|
||||||
|
name='replyCost'
|
||||||
|
type='number'
|
||||||
|
required
|
||||||
|
append={<InputGroup.Text className='text-monospace'>sats</InputGroup.Text>}
|
||||||
|
/>
|
||||||
<BootstrapForm.Label>moderation</BootstrapForm.Label>
|
<BootstrapForm.Label>moderation</BootstrapForm.Label>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
inline
|
inline
|
||||||
|
@ -57,9 +57,16 @@ export function TerritoryInfo ({ sub }) {
|
|||||||
<span> on </span>
|
<span> on </span>
|
||||||
<span className='fw-bold'>{new Date(sub.createdAt).toDateString()}</span>
|
<span className='fw-bold'>{new Date(sub.createdAt).toDateString()}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className='text-muted'>
|
<div className='d-flex'>
|
||||||
<span>post cost </span>
|
<div className='text-muted'>
|
||||||
<span className='fw-bold'>{numWithUnits(sub.baseCost)}</span>
|
<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>
|
</div>
|
||||||
<TerritoryBillingLine sub={sub} />
|
<TerritoryBillingLine sub={sub} />
|
||||||
</CardFooter>
|
</CardFooter>
|
||||||
|
@ -35,6 +35,7 @@ export const ITEM_FIELDS = gql`
|
|||||||
meMuteSub
|
meMuteSub
|
||||||
meSubscription
|
meSubscription
|
||||||
nsfw
|
nsfw
|
||||||
|
replyCost
|
||||||
}
|
}
|
||||||
otsHash
|
otsHash
|
||||||
position
|
position
|
||||||
|
@ -264,10 +264,10 @@ export const UPDATE_COMMENT = gql`
|
|||||||
export const UPSERT_SUB = gql`
|
export const UPSERT_SUB = gql`
|
||||||
${PAID_ACTION}
|
${PAID_ACTION}
|
||||||
mutation upsertSub($oldName: String, $name: String!, $desc: String, $baseCost: Int!,
|
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!) {
|
$billingAutoRenew: Boolean!, $moderated: Boolean!, $nsfw: Boolean!) {
|
||||||
upsertSub(oldName: $oldName, name: $name, desc: $desc, baseCost: $baseCost,
|
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) {
|
billingAutoRenew: $billingAutoRenew, moderated: $moderated, nsfw: $nsfw) {
|
||||||
result {
|
result {
|
||||||
name
|
name
|
||||||
@ -279,10 +279,10 @@ export const UPSERT_SUB = gql`
|
|||||||
export const UNARCHIVE_TERRITORY = gql`
|
export const UNARCHIVE_TERRITORY = gql`
|
||||||
${PAID_ACTION}
|
${PAID_ACTION}
|
||||||
mutation unarchiveTerritory($name: String!, $desc: String, $baseCost: Int!,
|
mutation unarchiveTerritory($name: String!, $desc: String, $baseCost: Int!,
|
||||||
$postTypes: [String!]!, $billingType: String!,
|
$replyCost: Int!, $postTypes: [String!]!, $billingType: String!,
|
||||||
$billingAutoRenew: Boolean!, $moderated: Boolean!, $nsfw: Boolean!) {
|
$billingAutoRenew: Boolean!, $moderated: Boolean!, $nsfw: Boolean!) {
|
||||||
unarchiveTerritory(name: $name, desc: $desc, baseCost: $baseCost,
|
unarchiveTerritory(name: $name, desc: $desc, baseCost: $baseCost,
|
||||||
postTypes: $postTypes, billingType: $billingType,
|
replyCost: $replyCost, postTypes: $postTypes, billingType: $billingType,
|
||||||
billingAutoRenew: $billingAutoRenew, moderated: $moderated, nsfw: $nsfw) {
|
billingAutoRenew: $billingAutoRenew, moderated: $moderated, nsfw: $nsfw) {
|
||||||
result {
|
result {
|
||||||
name
|
name
|
||||||
|
@ -25,6 +25,7 @@ export const SUB_FIELDS = gql`
|
|||||||
billedLastAt
|
billedLastAt
|
||||||
billPaidUntil
|
billPaidUntil
|
||||||
baseCost
|
baseCost
|
||||||
|
replyCost
|
||||||
userId
|
userId
|
||||||
desc
|
desc
|
||||||
status
|
status
|
||||||
|
@ -317,6 +317,9 @@ export function territorySchema (args) {
|
|||||||
baseCost: intValidator
|
baseCost: intValidator
|
||||||
.min(1, 'must be at least 1')
|
.min(1, 'must be at least 1')
|
||||||
.max(100000, 'must be at most 100k'),
|
.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'),
|
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'),
|
billingType: string().required('required').oneOf(TERRITORY_BILLING_TYPES, 'required'),
|
||||||
nsfw: boolean()
|
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
|
rankingType RankingType
|
||||||
allowFreebies Boolean @default(true)
|
allowFreebies Boolean @default(true)
|
||||||
baseCost Int @default(1)
|
baseCost Int @default(1)
|
||||||
|
replyCost Int @default(1)
|
||||||
rewardsPct Int @default(50)
|
rewardsPct Int @default(50)
|
||||||
desc String?
|
desc String?
|
||||||
status Status @default(ACTIVE)
|
status Status @default(ACTIVE)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user