replace greeter mode with investment filter (#1291)
* replace greeter mode with investment filter * change name to satsFilter * drop freebie column --------- Co-authored-by: k00b <k00b@stacker.news>
This commit is contained in:
parent
e897a2d1dc
commit
c5f043c625
@ -1,7 +1,7 @@
|
||||
import { ANON_ITEM_SPAM_INTERVAL, ITEM_SPAM_INTERVAL, USER_ID } from '@/lib/constants'
|
||||
import { notifyItemMention, notifyItemParents, notifyMention, notifyTerritorySubscribers, notifyUserSubscribers } from '@/lib/webPush'
|
||||
import { getItemMentions, getMentions, performBotBehavior } from './lib/item'
|
||||
import { satsToMsats } from '@/lib/format'
|
||||
import { msatsToSats, satsToMsats } from '@/lib/format'
|
||||
|
||||
export const anonable = true
|
||||
export const supportsPessimism = true
|
||||
@ -51,8 +51,7 @@ export async function perform (args, context) {
|
||||
itemActs.push({
|
||||
msats: cost - boostMsats, act: 'FEE', userId: data.userId, ...invoiceData
|
||||
})
|
||||
} else {
|
||||
data.freebie = true
|
||||
data.cost = msatsToSats(cost - boostMsats)
|
||||
}
|
||||
|
||||
const mentions = await getMentions(args, context)
|
||||
|
@ -30,12 +30,12 @@ function commentsOrderByClause (me, models, sort) {
|
||||
return `ORDER BY COALESCE(
|
||||
personal_hot_score,
|
||||
${orderByNumerator(models, 0)}/POWER(GREATEST(3, EXTRACT(EPOCH FROM (now_utc() - "Item".created_at))/3600), 1.3)) DESC NULLS LAST,
|
||||
"Item".msats DESC, ("Item".freebie IS FALSE) DESC, "Item".id DESC`
|
||||
"Item".msats DESC, ("Item".cost > 0) DESC, "Item".id DESC`
|
||||
} else {
|
||||
if (sort === 'top') {
|
||||
return `ORDER BY ${orderByNumerator(models, 0)} DESC NULLS LAST, "Item".msats DESC, ("Item".freebie IS FALSE) DESC, "Item".id DESC`
|
||||
return `ORDER BY ${orderByNumerator(models, 0)} DESC NULLS LAST, "Item".msats DESC, ("Item".cost > 0) DESC, "Item".id DESC`
|
||||
} else {
|
||||
return `ORDER BY ${orderByNumerator(models, 0)}/POWER(GREATEST(3, EXTRACT(EPOCH FROM (now_utc() - "Item".created_at))/3600), 1.3) DESC NULLS LAST, "Item".msats DESC, ("Item".freebie IS FALSE) DESC, "Item".id DESC`
|
||||
return `ORDER BY ${orderByNumerator(models, 0)}/POWER(GREATEST(3, EXTRACT(EPOCH FROM (now_utc() - "Item".created_at))/3600), 1.3) DESC NULLS LAST, "Item".msats DESC, ("Item".cost > 0) DESC, "Item".id DESC`
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -225,22 +225,16 @@ export async function filterClause (me, models, type) {
|
||||
|
||||
// handle freebies
|
||||
// by default don't include freebies unless they have upvotes
|
||||
let freebieClauses = ['NOT "Item".freebie', '"Item"."weightedVotes" - "Item"."weightedDownVotes" > 0']
|
||||
let investmentClause = '("Item".cost + "Item".boost + ("Item".msats / 1000)) >= 10'
|
||||
if (me) {
|
||||
const user = await models.user.findUnique({ where: { id: me.id } })
|
||||
// wild west mode has everything
|
||||
if (user.wildWestMode) {
|
||||
return ''
|
||||
}
|
||||
// greeter mode includes freebies if feebies haven't been flagged
|
||||
if (user.greeterMode) {
|
||||
freebieClauses = ['NOT "Item".freebie', '"Item"."weightedVotes" - "Item"."weightedDownVotes" >= 0']
|
||||
}
|
||||
|
||||
// always include if it's mine
|
||||
freebieClauses.push(`"Item"."userId" = ${me.id}`)
|
||||
investmentClause = `(("Item".cost + "Item".boost + ("Item".msats / 1000)) >= ${user.satsFilter} OR "Item"."userId" = ${me.id})`
|
||||
|
||||
if (user.wildWestMode) {
|
||||
return investmentClause
|
||||
}
|
||||
}
|
||||
const freebieClause = '(' + freebieClauses.join(' OR ') + ')'
|
||||
|
||||
// handle outlawed
|
||||
// if the item is above the threshold or is mine
|
||||
@ -250,7 +244,7 @@ export async function filterClause (me, models, type) {
|
||||
}
|
||||
const outlawClause = '(' + outlawClauses.join(' OR ') + ')'
|
||||
|
||||
return [freebieClause, outlawClause]
|
||||
return [investmentClause, outlawClause]
|
||||
}
|
||||
|
||||
function typeClause (type) {
|
||||
@ -268,7 +262,7 @@ function typeClause (type) {
|
||||
case 'comments':
|
||||
return '"Item"."parentId" IS NOT NULL'
|
||||
case 'freebies':
|
||||
return '"Item".freebie'
|
||||
return '"Item".cost = 0'
|
||||
case 'outlawed':
|
||||
return `"Item"."weightedVotes" - "Item"."weightedDownVotes" <= -${ITEM_FILTER_THRESHOLD} OR "Item".outlawed`
|
||||
case 'borderland':
|
||||
@ -470,10 +464,10 @@ export default {
|
||||
'"Item".bio = false',
|
||||
activeOrMine(me),
|
||||
await filterClause(me, models, type))}
|
||||
ORDER BY ${orderByNumerator(models, 0)}/POWER(GREATEST(3, EXTRACT(EPOCH FROM (now_utc() - "Item".created_at))/3600), 1.3) DESC NULLS LAST, "Item".msats DESC, ("Item".freebie IS FALSE) DESC, "Item".id DESC
|
||||
ORDER BY ${orderByNumerator(models, 0)}/POWER(GREATEST(3, EXTRACT(EPOCH FROM (now_utc() - "Item".created_at))/3600), 1.3) DESC NULLS LAST, "Item".msats DESC, ("Item".cost > 0) DESC, "Item".id DESC
|
||||
OFFSET $1
|
||||
LIMIT $2`,
|
||||
orderBy: `ORDER BY ${orderByNumerator(models, 0)}/POWER(GREATEST(3, EXTRACT(EPOCH FROM (now_utc() - "Item".created_at))/3600), 1.3) DESC NULLS LAST, "Item".msats DESC, ("Item".freebie IS FALSE) DESC, "Item".id DESC`
|
||||
orderBy: `ORDER BY ${orderByNumerator(models, 0)}/POWER(GREATEST(3, EXTRACT(EPOCH FROM (now_utc() - "Item".created_at))/3600), 1.3) DESC NULLS LAST, "Item".msats DESC, ("Item".cost > 0) DESC, "Item".id DESC`
|
||||
}, decodedCursor.offset, limit, ...subArr)
|
||||
}
|
||||
|
||||
@ -1054,6 +1048,9 @@ export default {
|
||||
freedFreebie: async (item) => {
|
||||
return item.weightedVotes - item.weightedDownVotes > 0
|
||||
},
|
||||
freebie: async (item) => {
|
||||
return item.cost === 0
|
||||
},
|
||||
meSats: async (item, args, { me, models }) => {
|
||||
if (!me) return 0
|
||||
if (typeof item.meMsats !== 'undefined') {
|
||||
@ -1255,7 +1252,7 @@ export const updateItem = async (parent, { sub: subName, forward, ...item }, { m
|
||||
const differentSub = subName && old.subName !== subName
|
||||
if (differentSub) {
|
||||
const sub = await models.sub.findUnique({ where: { name: subName } })
|
||||
if (old.freebie) {
|
||||
if (old.cost === 0) {
|
||||
if (!sub.allowFreebies) {
|
||||
throw new GraphQLError(`~${subName} does not allow freebies`, { extensions: { code: 'BAD_INPUT' } })
|
||||
}
|
||||
|
@ -71,7 +71,7 @@ export default gql`
|
||||
diagnostics: Boolean!
|
||||
noReferralLinks: Boolean!
|
||||
fiatCurrency: String!
|
||||
greeterMode: Boolean!
|
||||
satsFilter: Int!
|
||||
hideBookmarks: Boolean!
|
||||
hideCowboyHat: Boolean!
|
||||
hideGithub: Boolean!
|
||||
@ -140,7 +140,7 @@ export default gql`
|
||||
diagnostics: Boolean!
|
||||
noReferralLinks: Boolean!
|
||||
fiatCurrency: String!
|
||||
greeterMode: Boolean!
|
||||
satsFilter: Int!
|
||||
hideBookmarks: Boolean!
|
||||
hideCowboyHat: Boolean!
|
||||
hideGithub: Boolean!
|
||||
@ -192,7 +192,7 @@ export default gql`
|
||||
twitterId: String
|
||||
nostrAuthPubkey: String
|
||||
}
|
||||
|
||||
|
||||
type NameValue {
|
||||
name: String!
|
||||
value: Float!
|
||||
|
@ -97,7 +97,7 @@ export default function Comment ({
|
||||
}) {
|
||||
const [edit, setEdit] = useState()
|
||||
const me = useMe()
|
||||
const isHiddenFreebie = !me?.privates?.wildWestMode && !me?.privates?.greeterMode && !item.mine && item.freebie && !item.freedFreebie
|
||||
const isHiddenFreebie = me?.privates?.satsFilter !== 0 && !item.mine && item.freebie && !item.freedFreebie
|
||||
const [collapse, setCollapse] = useState(
|
||||
(isHiddenFreebie || item?.user?.meMute || (item?.outlawed && !me?.privates?.wildWestMode)) && !includeParent
|
||||
? 'yep'
|
||||
|
@ -15,7 +15,7 @@ export const ME = gql`
|
||||
diagnostics
|
||||
noReferralLinks
|
||||
fiatCurrency
|
||||
greeterMode
|
||||
satsFilter
|
||||
hideCowboyHat
|
||||
hideFromTopUsers
|
||||
hideGithub
|
||||
@ -104,7 +104,7 @@ export const SETTINGS_FIELDS = gql`
|
||||
nostrCrossposting
|
||||
nostrRelays
|
||||
wildWestMode
|
||||
greeterMode
|
||||
satsFilter
|
||||
nsfwMode
|
||||
authMethods {
|
||||
lightning
|
||||
|
@ -579,6 +579,7 @@ export const settingsSchema = object().shape({
|
||||
diagnostics: boolean(),
|
||||
noReferralLinks: boolean(),
|
||||
hideIsContributor: boolean(),
|
||||
satsFilter: intValidator.required('required').min(0, 'must be at least 0').max(1000, 'must be at most 1000'),
|
||||
zapUndos: intValidator.nullable().min(0, 'must be greater or equal to 0')
|
||||
// exclude from cyclic analysis. see https://github.com/jquense/yup/issues/720
|
||||
}, [['tipRandomMax', 'tipRandomMin']])
|
||||
|
@ -140,7 +140,7 @@ export default function Settings ({ ssrData }) {
|
||||
hideTwitter: settings?.hideTwitter,
|
||||
imgproxyOnly: settings?.imgproxyOnly,
|
||||
wildWestMode: settings?.wildWestMode,
|
||||
greeterMode: settings?.greeterMode,
|
||||
satsFilter: settings?.satsFilter,
|
||||
nsfwMode: settings?.nsfwMode,
|
||||
nostrPubkey: settings?.nostrPubkey ? bech32encode(settings.nostrPubkey) : '',
|
||||
nostrCrossposting: settings?.nostrCrossposting,
|
||||
@ -152,7 +152,11 @@ export default function Settings ({ ssrData }) {
|
||||
noReferralLinks: settings?.noReferralLinks
|
||||
}}
|
||||
schema={settingsSchema}
|
||||
onSubmit={async ({ tipDefault, tipRandom, tipRandomMin, tipRandomMax, withdrawMaxFeeDefault, zapUndos, zapUndosEnabled, nostrPubkey, nostrRelays, ...values }) => {
|
||||
onSubmit={async ({
|
||||
tipDefault, tipRandom, tipRandomMin, tipRandomMax, withdrawMaxFeeDefault,
|
||||
zapUndos, zapUndosEnabled, nostrPubkey, nostrRelays, satsFilter,
|
||||
...values
|
||||
}) => {
|
||||
if (nostrPubkey.length === 0) {
|
||||
nostrPubkey = null
|
||||
} else {
|
||||
@ -172,6 +176,7 @@ export default function Settings ({ ssrData }) {
|
||||
tipRandomMin: tipRandom ? Number(tipRandomMin) : null,
|
||||
tipRandomMax: tipRandom ? Number(tipRandomMax) : null,
|
||||
withdrawMaxFeeDefault: Number(withdrawMaxFeeDefault),
|
||||
satsFilter: Number(satsFilter),
|
||||
zapUndos: zapUndosEnabled ? Number(zapUndos) : null,
|
||||
nostrPubkey,
|
||||
nostrRelays: nostrRelaysFiltered,
|
||||
@ -467,7 +472,27 @@ export default function Settings ({ ssrData }) {
|
||||
label={<>don't create referral links on copy</>}
|
||||
name='noReferralLinks'
|
||||
/>
|
||||
<div className='form-label'>content</div>
|
||||
<h4>content</h4>
|
||||
<Input
|
||||
label={
|
||||
<div className='d-flex align-items-center'>filter by sats
|
||||
<Info>
|
||||
<ul className='fw-bold'>
|
||||
<li>hide the post if the sum of these is less than your setting:</li>
|
||||
<ul>
|
||||
<li>posting cost</li>
|
||||
<li>total sats from zaps</li>
|
||||
<li>boost</li>
|
||||
</ul>
|
||||
<li>set to zero to be a greeter, with the tradeoff of seeing more spam</li>
|
||||
</ul>
|
||||
</Info>
|
||||
</div>
|
||||
}
|
||||
name='satsFilter'
|
||||
required
|
||||
append={<InputGroup.Text className='text-monospace'>sats</InputGroup.Text>}
|
||||
/>
|
||||
<Checkbox
|
||||
label={
|
||||
<div className='d-flex align-items-center'>wild west mode
|
||||
@ -482,21 +507,6 @@ export default function Settings ({ ssrData }) {
|
||||
name='wildWestMode'
|
||||
groupClassName='mb-0'
|
||||
/>
|
||||
<Checkbox
|
||||
label={
|
||||
<div className='d-flex align-items-center'>greeter mode
|
||||
<Info>
|
||||
<ul className='fw-bold'>
|
||||
<li>see and screen free posts and comments</li>
|
||||
<li>help onboard new stackers to SN and Lightning</li>
|
||||
<li>you might be subject to more spam</li>
|
||||
</ul>
|
||||
</Info>
|
||||
</div>
|
||||
}
|
||||
name='greeterMode'
|
||||
groupClassName='mb-0'
|
||||
/>
|
||||
<Checkbox
|
||||
label={
|
||||
<div className='d-flex align-items-center'>nsfw mode
|
||||
|
16
prisma/migrations/20240808234214_sats_filter/migration.sql
Normal file
16
prisma/migrations/20240808234214_sats_filter/migration.sql
Normal file
@ -0,0 +1,16 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "Item" ADD COLUMN "cost" INTEGER NOT NULL DEFAULT 0;
|
||||
|
||||
-- use existing "ItemAct".act = FEE AND "Item"."userId" = "ItemAct"."userId" to calculate the cost for existing "Item"s
|
||||
UPDATE "Item" SET "cost" = "ItemAct"."msats" / 1000
|
||||
FROM "ItemAct"
|
||||
WHERE "Item"."id" = "ItemAct"."itemId" AND "ItemAct"."act" = 'FEE' AND "Item"."userId" = "ItemAct"."userId";
|
||||
|
||||
ALTER TABLE "users" ADD COLUMN "satsFilter" INTEGER NOT NULL DEFAULT 10;
|
||||
|
||||
UPDATE "users" SET "satsFilter" = 0 WHERE "greeterMode";
|
||||
|
||||
ALTER TABLE "users" DROP COLUMN "greeterMode";
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "Item_cost_idx" ON "Item"("cost");
|
@ -54,7 +54,7 @@ model User {
|
||||
upvoteTrust Float @default(0)
|
||||
hideInvoiceDesc Boolean @default(false)
|
||||
wildWestMode Boolean @default(false)
|
||||
greeterMode Boolean @default(false)
|
||||
satsFilter Int @default(10)
|
||||
nsfwMode Boolean @default(false)
|
||||
fiatCurrency String @default("USD")
|
||||
withdrawMaxFeeDefault Int @default(10)
|
||||
@ -425,6 +425,7 @@ model Item {
|
||||
lastZapAt DateTime?
|
||||
ncomments Int @default(0)
|
||||
msats BigInt @default(0)
|
||||
cost Int @default(0)
|
||||
weightedDownVotes Float @default(0)
|
||||
bio Boolean @default(false)
|
||||
freebie Boolean @default(false)
|
||||
@ -489,6 +490,7 @@ model Item {
|
||||
@@index([weightedVotes], map: "Item.weightedVotes_index")
|
||||
@@index([invoiceId])
|
||||
@@index([invoiceActionState])
|
||||
@@index([cost])
|
||||
}
|
||||
|
||||
// we use this to denormalize a user's aggregated interactions (zaps) with an item
|
||||
|
Loading…
x
Reference in New Issue
Block a user