add freebies
This commit is contained in:
parent
1621eeac80
commit
d9d426e5c3
|
@ -8,7 +8,6 @@ import {
|
|||
BOOST_MIN, ITEM_SPAM_INTERVAL, MAX_POLL_NUM_CHOICES,
|
||||
MAX_TITLE_LENGTH, ITEM_FILTER_THRESHOLD, DONT_LIKE_THIS_COST
|
||||
} from '../../lib/constants'
|
||||
import { mdHas } from '../../lib/md'
|
||||
|
||||
async function comments (me, models, id, sort) {
|
||||
let orderBy
|
||||
|
@ -85,15 +84,28 @@ export async function orderByNumerator (me, models) {
|
|||
}
|
||||
|
||||
export async function filterClause (me, models) {
|
||||
// by default don't include freebies unless they have upvotes
|
||||
let clause = ' AND (NOT "Item".freebie OR "Item"."weightedVotes" - "Item"."weightedDownVotes" > 0'
|
||||
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) {
|
||||
clause = 'AND (NOT "Item".freebie OR ("Item"."weightedVotes" - "Item"."weightedDownVotes" >= 0 AND "Item".freebie)'
|
||||
}
|
||||
|
||||
// always include if it's mine
|
||||
clause += ` OR "Item"."userId" = ${me.id})`
|
||||
} else {
|
||||
// close default freebie clause
|
||||
clause += ')'
|
||||
}
|
||||
|
||||
// if the item is above the threshold or is mine
|
||||
let clause = ` AND ("Item"."weightedVotes" - "Item"."weightedDownVotes" > -${ITEM_FILTER_THRESHOLD}`
|
||||
clause += ` AND ("Item"."weightedVotes" - "Item"."weightedDownVotes" > -${ITEM_FILTER_THRESHOLD}`
|
||||
if (me) {
|
||||
clause += ` OR "Item"."userId" = ${me.id}`
|
||||
}
|
||||
|
@ -215,7 +227,7 @@ export default {
|
|||
${SELECT}
|
||||
FROM "Item"
|
||||
WHERE "parentId" IS NULL AND "Item".created_at <= $1 AND "Item".created_at > $3
|
||||
AND "pinId" IS NULL
|
||||
AND "pinId" IS NULL AND NOT bio
|
||||
${subClause(4)}
|
||||
${await filterClause(me, models)}
|
||||
${await newTimedOrderByWeightedSats(me, models, 1)}
|
||||
|
@ -228,7 +240,7 @@ export default {
|
|||
${SELECT}
|
||||
FROM "Item"
|
||||
WHERE "parentId" IS NULL AND "Item".created_at <= $1
|
||||
AND "pinId" IS NULL
|
||||
AND "pinId" IS NULL AND NOT bio
|
||||
${subClause(3)}
|
||||
${await filterClause(me, models)}
|
||||
${await newTimedOrderByWeightedSats(me, models, 1)}
|
||||
|
@ -312,6 +324,21 @@ export default {
|
|||
items
|
||||
}
|
||||
},
|
||||
freebieItems: async (parent, { cursor }, { me, models }) => {
|
||||
const decodedCursor = decodeCursor(cursor)
|
||||
|
||||
const items = await models.$queryRaw(`
|
||||
${SELECT}
|
||||
FROM "Item"
|
||||
WHERE "Item".freebie
|
||||
ORDER BY created_at DESC
|
||||
OFFSET $1
|
||||
LIMIT ${LIMIT}`, decodedCursor.offset)
|
||||
return {
|
||||
cursor: items.length === LIMIT ? nextCursorEncoded(decodedCursor) : null,
|
||||
items
|
||||
}
|
||||
},
|
||||
moreFlatComments: async (parent, { cursor, name, sort, within }, { me, models }) => {
|
||||
const decodedCursor = decodeCursor(cursor)
|
||||
|
||||
|
@ -574,8 +601,6 @@ export default {
|
|||
}
|
||||
}
|
||||
|
||||
const hasImgLink = !!(text && mdHas(text, ['link', 'image']))
|
||||
|
||||
if (id) {
|
||||
const optionCount = await models.pollOption.count({
|
||||
where: {
|
||||
|
@ -588,8 +613,8 @@ export default {
|
|||
}
|
||||
|
||||
const [item] = await serialize(models,
|
||||
models.$queryRaw(`${SELECT} FROM update_poll($1, $2, $3, $4, $5, $6, $7) AS "Item"`,
|
||||
Number(id), title, text, Number(boost || 0), options, Number(fwdUser?.id), hasImgLink))
|
||||
models.$queryRaw(`${SELECT} FROM update_poll($1, $2, $3, $4, $5, $6) AS "Item"`,
|
||||
Number(id), title, text, Number(boost || 0), options, Number(fwdUser?.id)))
|
||||
|
||||
return item
|
||||
} else {
|
||||
|
@ -598,8 +623,8 @@ export default {
|
|||
}
|
||||
|
||||
const [item] = await serialize(models,
|
||||
models.$queryRaw(`${SELECT} FROM create_poll($1, $2, $3, $4, $5, $6, $7, $8, '${ITEM_SPAM_INTERVAL}') AS "Item"`,
|
||||
title, text, 1, Number(boost || 0), Number(me.id), options, Number(fwdUser?.id), hasImgLink))
|
||||
models.$queryRaw(`${SELECT} FROM create_poll($1, $2, $3, $4, $5, $6, $7, '${ITEM_SPAM_INTERVAL}') AS "Item"`,
|
||||
title, text, 1, Number(boost || 0), Number(me.id), options, Number(fwdUser?.id)))
|
||||
|
||||
await createMentions(item, models)
|
||||
|
||||
|
@ -981,12 +1006,10 @@ export const updateItem = async (parent, { id, data: { title, url, text, boost,
|
|||
}
|
||||
}
|
||||
|
||||
const hasImgLink = !!(text && mdHas(text, ['link', 'image']))
|
||||
|
||||
const [item] = await serialize(models,
|
||||
models.$queryRaw(
|
||||
`${SELECT} FROM update_item($1, $2, $3, $4, $5, $6, $7) AS "Item"`,
|
||||
Number(id), title, url, text, Number(boost || 0), Number(fwdUser?.id), hasImgLink))
|
||||
`${SELECT} FROM update_item($1, $2, $3, $4, $5, $6) AS "Item"`,
|
||||
Number(id), title, url, text, Number(boost || 0), Number(fwdUser?.id)))
|
||||
|
||||
await createMentions(item, models)
|
||||
|
||||
|
@ -1014,13 +1037,11 @@ const createItem = async (parent, { title, url, text, boost, forward, parentId }
|
|||
}
|
||||
}
|
||||
|
||||
const hasImgLink = !!(text && mdHas(text, ['link', 'image']))
|
||||
|
||||
const [item] = await serialize(models,
|
||||
models.$queryRaw(
|
||||
`${SELECT} FROM create_item($1, $2, $3, $4, $5, $6, $7, $8, '${ITEM_SPAM_INTERVAL}') AS "Item"`,
|
||||
`${SELECT} FROM create_item($1, $2, $3, $4, $5, $6, $7, '${ITEM_SPAM_INTERVAL}') AS "Item"`,
|
||||
title, url, text, Number(boost || 0), Number(parentId), Number(me.id),
|
||||
Number(fwdUser?.id), hasImgLink))
|
||||
Number(fwdUser?.id)))
|
||||
|
||||
await createMentions(item, models)
|
||||
|
||||
|
@ -1058,9 +1079,9 @@ export const SELECT =
|
|||
`SELECT "Item".id, "Item".created_at as "createdAt", "Item".updated_at as "updatedAt", "Item".title,
|
||||
"Item".text, "Item".url, "Item"."userId", "Item"."fwdUserId", "Item"."parentId", "Item"."pinId", "Item"."maxBid",
|
||||
"Item".company, "Item".location, "Item".remote,
|
||||
"Item"."subName", "Item".status, "Item"."uploadId", "Item"."pollCost", "Item"."paidImgLink",
|
||||
"Item"."subName", "Item".status, "Item"."uploadId", "Item"."pollCost",
|
||||
"Item".sats, "Item".ncomments, "Item"."commentSats", "Item"."lastCommentAt", "Item"."weightedVotes",
|
||||
"Item"."weightedDownVotes", ltree2text("Item"."path") AS "path"`
|
||||
"Item"."weightedDownVotes", "Item".freebie, ltree2text("Item"."path") AS "path"`
|
||||
|
||||
async function newTimedOrderByWeightedSats (me, models, num) {
|
||||
return `
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { AuthenticationError, UserInputError } from 'apollo-server-errors'
|
||||
import { decodeCursor, LIMIT, nextCursorEncoded } from '../../lib/cursor'
|
||||
import { mdHas } from '../../lib/md'
|
||||
import { createMentions, getItem, SELECT, updateItem, filterClause } from './item'
|
||||
import serialize from './serial'
|
||||
|
||||
|
@ -202,11 +201,9 @@ export default {
|
|||
if (user.bioId) {
|
||||
await updateItem(parent, { id: user.bioId, data: { text: bio, title: `@${user.name}'s bio` } }, { me, models })
|
||||
} else {
|
||||
const hasImgLink = !!(bio && mdHas(bio, ['link', 'image']))
|
||||
|
||||
const [item] = await serialize(models,
|
||||
models.$queryRaw(`${SELECT} FROM create_bio($1, $2, $3, $4) AS "Item"`,
|
||||
`@${user.name}'s bio`, bio, Number(me.id), hasImgLink))
|
||||
models.$queryRaw(`${SELECT} FROM create_bio($1, $2, $3) AS "Item"`,
|
||||
`@${user.name}'s bio`, bio, Number(me.id)))
|
||||
await createMentions(item, models)
|
||||
}
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ export default gql`
|
|||
itemRepetition(parentId: ID): Int!
|
||||
outlawedItems(cursor: String): Items
|
||||
borderlandItems(cursor: String): Items
|
||||
freebieItems(cursor: String): Items
|
||||
}
|
||||
|
||||
type ItemActResult {
|
||||
|
@ -83,6 +84,7 @@ export default gql`
|
|||
meSats: Int!
|
||||
meDontLike: Boolean!
|
||||
outlawed: Boolean!
|
||||
freebie: Boolean!
|
||||
paidImgLink: Boolean
|
||||
ncomments: Int!
|
||||
comments: [Item!]!
|
||||
|
|
|
@ -31,7 +31,8 @@ export default gql`
|
|||
setName(name: String!): Boolean
|
||||
setSettings(tipDefault: Int!, noteItemSats: Boolean!, noteEarning: Boolean!,
|
||||
noteAllDescendants: Boolean!, noteMentions: Boolean!, noteDeposits: Boolean!,
|
||||
noteInvites: Boolean!, noteJobIndicator: Boolean!, hideInvoiceDesc: Boolean!, wildWestMode: Boolean!): User
|
||||
noteInvites: Boolean!, noteJobIndicator: Boolean!, hideInvoiceDesc: Boolean!,
|
||||
wildWestMode: Boolean!, greeterMode: Boolean!): User
|
||||
setPhoto(photoId: ID!): Int!
|
||||
upsertBio(bio: String!): User!
|
||||
setWalkthrough(tipPopover: Boolean, upvotePopover: Boolean): Boolean
|
||||
|
@ -73,6 +74,7 @@ export default gql`
|
|||
noteJobIndicator: Boolean!
|
||||
hideInvoiceDesc: Boolean!
|
||||
wildWestMode: Boolean!
|
||||
greeterMode: Boolean!
|
||||
lastCheckedJobs: String
|
||||
authMethods: AuthMethods!
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@ import * as Yup from 'yup'
|
|||
import { gql, useMutation } from '@apollo/client'
|
||||
import styles from './reply.module.css'
|
||||
import TextareaAutosize from 'react-textarea-autosize'
|
||||
import { useState } from 'react'
|
||||
import { EditFeeButton } from './fee-button'
|
||||
|
||||
export const CommentSchema = Yup.object({
|
||||
|
@ -11,14 +10,11 @@ export const CommentSchema = Yup.object({
|
|||
})
|
||||
|
||||
export default function CommentEdit ({ comment, editThreshold, onSuccess, onCancel }) {
|
||||
const [hasImgLink, setHasImgLink] = useState()
|
||||
|
||||
const [updateComment] = useMutation(
|
||||
gql`
|
||||
mutation updateComment($id: ID! $text: String!) {
|
||||
updateComment(id: $id, text: $text) {
|
||||
text
|
||||
paidImgLink
|
||||
}
|
||||
}`, {
|
||||
update (cache, { data: { updateComment } }) {
|
||||
|
@ -27,9 +23,6 @@ export default function CommentEdit ({ comment, editThreshold, onSuccess, onCanc
|
|||
fields: {
|
||||
text () {
|
||||
return updateComment.text
|
||||
},
|
||||
paidImgLink () {
|
||||
return updateComment.paidImgLink
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -59,11 +52,10 @@ export default function CommentEdit ({ comment, editThreshold, onSuccess, onCanc
|
|||
as={TextareaAutosize}
|
||||
minRows={6}
|
||||
autoFocus
|
||||
setHasImgLink={setHasImgLink}
|
||||
required
|
||||
/>
|
||||
<EditFeeButton
|
||||
paidSats={comment.meSats} hadImgLink={comment.paidImgLink} hasImgLink={hasImgLink}
|
||||
paidSats={comment.meSats}
|
||||
parentId={comment.parentId} text='save' ChildButton={SubmitButton} variant='secondary'
|
||||
/>
|
||||
</Form>
|
||||
|
|
|
@ -133,8 +133,9 @@ export default function Comment ({
|
|||
<a title={item.createdAt} className='text-reset'>{timeSince(new Date(item.createdAt))}</a>
|
||||
</Link>
|
||||
{includeParent && <Parent item={item} rootText={rootText} />}
|
||||
{me && !item.meSats && !item.meDontLike && <DontLikeThis id={item.id} />}
|
||||
{item.outlawed && <Link href='/outlawed'><a>{' '}<Badge className={itemStyles.newComment} variant={null}>OUTLAWED</Badge></a></Link>}
|
||||
{me && !item.meSats && !item.meDontLike && !item.mine && <DontLikeThis id={item.id} />}
|
||||
{(item.outlawed && <Link href='/outlawed'><a>{' '}<Badge className={itemStyles.newComment} variant={null}>OUTLAWED</Badge></a></Link>) ||
|
||||
(item.freebie && !item.mine && (me?.greeterMode) && <Link href='/freebie'><a>{' '}<Badge className={itemStyles.newComment} variant={null}>FREEBIE</Badge></a></Link>)}
|
||||
{canEdit &&
|
||||
<>
|
||||
<span> \ </span>
|
||||
|
|
|
@ -6,7 +6,6 @@ import TextareaAutosize from 'react-textarea-autosize'
|
|||
import Countdown from './countdown'
|
||||
import AdvPostForm, { AdvPostInitial, AdvPostSchema } from './adv-post-form'
|
||||
import { MAX_TITLE_LENGTH } from '../lib/constants'
|
||||
import { useState } from 'react'
|
||||
import FeeButton, { EditFeeButton } from './fee-button'
|
||||
|
||||
export function DiscussionForm ({
|
||||
|
@ -16,7 +15,6 @@ export function DiscussionForm ({
|
|||
}) {
|
||||
const router = useRouter()
|
||||
const client = useApolloClient()
|
||||
const [hasImgLink, setHasImgLink] = useState()
|
||||
// const me = useMe()
|
||||
const [upsertDiscussion] = useMutation(
|
||||
gql`
|
||||
|
@ -77,17 +75,16 @@ export function DiscussionForm ({
|
|||
hint={editThreshold
|
||||
? <div className='text-muted font-weight-bold'><Countdown date={editThreshold} /></div>
|
||||
: null}
|
||||
setHasImgLink={setHasImgLink}
|
||||
/>
|
||||
{adv && <AdvPostForm edit={!!item} />}
|
||||
<div className='mt-3'>
|
||||
{item
|
||||
? <EditFeeButton
|
||||
paidSats={item.meSats} hadImgLink={item.paidImgLink} hasImgLink={hasImgLink}
|
||||
paidSats={item.meSats}
|
||||
parentId={null} text='save' ChildButton={SubmitButton} variant='secondary'
|
||||
/>
|
||||
: <FeeButton
|
||||
baseFee={1} hasImgLink={hasImgLink} parentId={null} text={buttonText}
|
||||
baseFee={1} parentId={null} text={buttonText}
|
||||
ChildButton={SubmitButton} variant='secondary'
|
||||
/>}
|
||||
</div>
|
||||
|
|
|
@ -110,8 +110,9 @@ export default function Item ({ item, rank, showFwdUser, toc, children }) {
|
|||
<Link href={`/items/${item.id}`} passHref>
|
||||
<a title={item.createdAt} className='text-reset'>{timeSince(new Date(item.createdAt))}</a>
|
||||
</Link>
|
||||
{me && !item.meSats && !item.position && !item.meDontLike && <DontLikeThis id={item.id} />}
|
||||
{item.outlawed && <Link href='/outlawed'><a>{' '}<Badge className={styles.newComment} variant={null}>OUTLAWED</Badge></a></Link>}
|
||||
{me && !item.meSats && !item.position && !item.meDontLike && !item.mine && <DontLikeThis id={item.id} />}
|
||||
{(item.outlawed && <Link href='/outlawed'><a>{' '}<Badge className={styles.newComment} variant={null}>OUTLAWED</Badge></a></Link>) ||
|
||||
(item.freebie && !item.mine && (me?.greeterMode) && <Link href='/freebie'><a>{' '}<Badge className={styles.newComment} variant={null}>FREEBIE</Badge></a></Link>)}
|
||||
{item.prior &&
|
||||
<>
|
||||
<span> \ </span>
|
||||
|
|
|
@ -23,6 +23,7 @@ a.title:visited {
|
|||
.newComment {
|
||||
color: var(--theme-grey) !important;
|
||||
background: var(--theme-clickToContextColor) !important;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.pin {
|
||||
|
|
|
@ -6,13 +6,11 @@ import Countdown from './countdown'
|
|||
import AdvPostForm, { AdvPostInitial, AdvPostSchema } from './adv-post-form'
|
||||
import { MAX_TITLE_LENGTH, MAX_POLL_CHOICE_LENGTH, MAX_POLL_NUM_CHOICES } from '../lib/constants'
|
||||
import TextareaAutosize from 'react-textarea-autosize'
|
||||
import { useState } from 'react'
|
||||
import FeeButton, { EditFeeButton } from './fee-button'
|
||||
|
||||
export function PollForm ({ item, editThreshold }) {
|
||||
const router = useRouter()
|
||||
const client = useApolloClient()
|
||||
const [hasImgLink, setHasImgLink] = useState()
|
||||
|
||||
const [upsertPoll] = useMutation(
|
||||
gql`
|
||||
|
@ -82,7 +80,6 @@ export function PollForm ({ item, editThreshold }) {
|
|||
name='text'
|
||||
as={TextareaAutosize}
|
||||
minRows={2}
|
||||
setHasImgLink={setHasImgLink}
|
||||
/>
|
||||
<VariableInput
|
||||
label='choices'
|
||||
|
@ -97,11 +94,11 @@ export function PollForm ({ item, editThreshold }) {
|
|||
<div className='mt-3'>
|
||||
{item
|
||||
? <EditFeeButton
|
||||
paidSats={item.meSats} hadImgLink={item.paidImgLink} hasImgLink={hasImgLink}
|
||||
paidSats={item.meSats}
|
||||
parentId={null} text='save' ChildButton={SubmitButton} variant='secondary'
|
||||
/>
|
||||
: <FeeButton
|
||||
baseFee={1} hasImgLink={hasImgLink} parentId={null} text='post'
|
||||
baseFee={1} parentId={null} text='post'
|
||||
ChildButton={SubmitButton} variant='secondary'
|
||||
/>}
|
||||
</div>
|
||||
|
|
|
@ -25,7 +25,6 @@ export function ReplyOnAnotherPage ({ parentId }) {
|
|||
export default function Reply ({ item, onSuccess, replyOpen }) {
|
||||
const [reply, setReply] = useState(replyOpen)
|
||||
const me = useMe()
|
||||
const [hasImgLink, setHasImgLink] = useState()
|
||||
const parentId = item.id
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -104,7 +103,6 @@ export default function Reply ({ item, onSuccess, replyOpen }) {
|
|||
}
|
||||
resetForm({ text: '' })
|
||||
setReply(replyOpen || false)
|
||||
setHasImgLink(false)
|
||||
}}
|
||||
storageKeyPrefix={'reply-' + parentId}
|
||||
>
|
||||
|
@ -114,13 +112,12 @@ export default function Reply ({ item, onSuccess, replyOpen }) {
|
|||
minRows={6}
|
||||
autoFocus={!replyOpen}
|
||||
required
|
||||
setHasImgLink={setHasImgLink}
|
||||
hint={me?.freeComments ? <span className='text-success'>{me.freeComments} free comments left</span> : null}
|
||||
/>
|
||||
{reply &&
|
||||
<div className='mt-1'>
|
||||
<FeeButton
|
||||
baseFee={1} hasImgLink={hasImgLink} parentId={parentId} text='reply'
|
||||
baseFee={1} parentId={parentId} text='reply'
|
||||
ChildButton={SubmitButton} variant='secondary' alwaysShow
|
||||
/>
|
||||
</div>}
|
||||
|
|
|
@ -16,10 +16,10 @@ export const COMMENT_FIELDS = gql`
|
|||
meSats
|
||||
meDontLike
|
||||
outlawed
|
||||
freebie
|
||||
path
|
||||
commentSats
|
||||
mine
|
||||
paidImgLink
|
||||
ncomments
|
||||
root {
|
||||
id
|
||||
|
|
|
@ -23,6 +23,7 @@ export const ITEM_FIELDS = gql`
|
|||
meSats
|
||||
meDontLike
|
||||
outlawed
|
||||
freebie
|
||||
ncomments
|
||||
commentSats
|
||||
lastCommentAt
|
||||
|
@ -38,7 +39,6 @@ export const ITEM_FIELDS = gql`
|
|||
status
|
||||
uploadId
|
||||
mine
|
||||
paidImgLink
|
||||
root {
|
||||
id
|
||||
title
|
||||
|
@ -95,6 +95,19 @@ export const BORDERLAND_ITEMS = gql`
|
|||
}
|
||||
}`
|
||||
|
||||
export const FREEBIE_ITEMS = gql`
|
||||
${ITEM_FIELDS}
|
||||
|
||||
query freebieItems($cursor: String) {
|
||||
freebieItems(cursor: $cursor) {
|
||||
cursor
|
||||
items {
|
||||
...ItemFields
|
||||
text
|
||||
}
|
||||
}
|
||||
}`
|
||||
|
||||
export const POLL_FIELDS = gql`
|
||||
fragment PollFields on Item {
|
||||
poll {
|
||||
|
|
|
@ -26,6 +26,7 @@ export const ME = gql`
|
|||
noteJobIndicator
|
||||
hideInvoiceDesc
|
||||
wildWestMode
|
||||
greeterMode
|
||||
lastCheckedJobs
|
||||
}
|
||||
}`
|
||||
|
@ -52,6 +53,7 @@ export const ME_SSR = gql`
|
|||
noteJobIndicator
|
||||
hideInvoiceDesc
|
||||
wildWestMode
|
||||
greeterMode
|
||||
lastCheckedJobs
|
||||
}
|
||||
}`
|
||||
|
@ -68,6 +70,7 @@ export const SETTINGS_FIELDS = gql`
|
|||
noteJobIndicator
|
||||
hideInvoiceDesc
|
||||
wildWestMode
|
||||
greeterMode
|
||||
authMethods {
|
||||
lightning
|
||||
email
|
||||
|
@ -89,11 +92,13 @@ gql`
|
|||
${SETTINGS_FIELDS}
|
||||
mutation setSettings($tipDefault: Int!, $noteItemSats: Boolean!, $noteEarning: Boolean!,
|
||||
$noteAllDescendants: Boolean!, $noteMentions: Boolean!, $noteDeposits: Boolean!,
|
||||
$noteInvites: Boolean!, $noteJobIndicator: Boolean!, $hideInvoiceDesc: Boolean!, $wildWestMode: Boolean!) {
|
||||
$noteInvites: Boolean!, $noteJobIndicator: Boolean!, $hideInvoiceDesc: Boolean!,
|
||||
$wildWestMode: Boolean!, $greeterMode: Boolean!) {
|
||||
setSettings(tipDefault: $tipDefault, noteItemSats: $noteItemSats,
|
||||
noteEarning: $noteEarning, noteAllDescendants: $noteAllDescendants,
|
||||
noteMentions: $noteMentions, noteDeposits: $noteDeposits, noteInvites: $noteInvites,
|
||||
noteJobIndicator: $noteJobIndicator, hideInvoiceDesc: $hideInvoiceDesc, wildWestMode: $wildWestMode) {
|
||||
noteJobIndicator: $noteJobIndicator, hideInvoiceDesc: $hideInvoiceDesc, wildWestMode: $wildWestMode,
|
||||
greeterMode: $greeterMode) {
|
||||
...SettingsFields
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,8 +23,6 @@ const BioSchema = Yup.object({
|
|||
})
|
||||
|
||||
export function BioForm ({ handleSuccess, bio }) {
|
||||
const [hasImgLink, setHasImgLink] = useState()
|
||||
|
||||
const [upsertBio] = useMutation(
|
||||
gql`
|
||||
${ITEM_FIELDS}
|
||||
|
@ -70,16 +68,15 @@ export function BioForm ({ handleSuccess, bio }) {
|
|||
name='bio'
|
||||
as={TextareaAutosize}
|
||||
minRows={6}
|
||||
setHasImgLink={setHasImgLink}
|
||||
/>
|
||||
<div className='mt-3'>
|
||||
{bio?.text
|
||||
? <EditFeeButton
|
||||
paidSats={bio?.meSats} hadImgLink={bio?.paidImgLink} hasImgLink={hasImgLink}
|
||||
paidSats={bio?.meSats}
|
||||
parentId={null} text='save' ChildButton={SubmitButton} variant='secondary'
|
||||
/>
|
||||
: <FeeButton
|
||||
baseFee={1} hasImgLink={hasImgLink} parentId={null} text='create'
|
||||
baseFee={1} parentId={null} text='create'
|
||||
ChildButton={SubmitButton} variant='secondary'
|
||||
/>}
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
import Layout from '../components/layout'
|
||||
import { ItemsSkeleton } from '../components/items'
|
||||
import { getGetServerSideProps } from '../api/ssrApollo'
|
||||
import { FREEBIE_ITEMS } from '../fragments/items'
|
||||
import { useQuery } from '@apollo/client'
|
||||
import MixedItems from '../components/items-mixed'
|
||||
|
||||
export const getServerSideProps = getGetServerSideProps(FREEBIE_ITEMS)
|
||||
|
||||
export default function Index ({ data: { freebieItems: { items, cursor } } }) {
|
||||
return (
|
||||
<Layout>
|
||||
<Items
|
||||
items={items} cursor={cursor}
|
||||
/>
|
||||
</Layout>
|
||||
)
|
||||
}
|
||||
|
||||
function Items ({ rank, items, cursor }) {
|
||||
const { data, fetchMore } = useQuery(FREEBIE_ITEMS)
|
||||
|
||||
if (!data && !items) {
|
||||
return <ItemsSkeleton rank={rank} />
|
||||
}
|
||||
|
||||
if (data) {
|
||||
({ freebieItems: { items, cursor } } = data)
|
||||
}
|
||||
|
||||
return <MixedItems items={items} cursor={cursor} rank={rank} fetchMore={fetchMore} />
|
||||
}
|
|
@ -62,7 +62,8 @@ export default function Settings ({ data: { settings } }) {
|
|||
noteInvites: settings?.noteInvites,
|
||||
noteJobIndicator: settings?.noteJobIndicator,
|
||||
hideInvoiceDesc: settings?.hideInvoiceDesc,
|
||||
wildWestMode: settings?.wildWestMode
|
||||
wildWestMode: settings?.wildWestMode,
|
||||
greeterMode: settings?.greeterMode
|
||||
}}
|
||||
schema={SettingsSchema}
|
||||
onSubmit={async ({ tipDefault, ...values }) => {
|
||||
|
@ -138,13 +139,28 @@ export default function Settings ({ data: { settings } }) {
|
|||
<div className='d-flex align-items-center'>wild west mode
|
||||
<Info>
|
||||
<ul className='font-weight-bold'>
|
||||
<li>Don't hide flagged content</li>
|
||||
<li>Don't down rank flagged content</li>
|
||||
<li>don't hide flagged content</li>
|
||||
<li>don't down rank flagged content</li>
|
||||
</ul>
|
||||
</Info>
|
||||
</div>
|
||||
}
|
||||
name='wildWestMode'
|
||||
groupClassName='mb-0'
|
||||
/>
|
||||
<Checkbox
|
||||
label={
|
||||
<div className='d-flex align-items-center'>greeter mode
|
||||
<Info>
|
||||
<ul className='font-weight-bold'>
|
||||
<li>see and screen free posts and comments</li>
|
||||
<li>help onboard users to SN and Lightning</li>
|
||||
<li>you might be subject to more spam</li>
|
||||
</ul>
|
||||
</Info>
|
||||
</div>
|
||||
}
|
||||
name='greeterMode'
|
||||
/>
|
||||
<div className='d-flex'>
|
||||
<SubmitButton variant='info' className='ml-auto mt-1 px-4'>save</SubmitButton>
|
||||
|
|
|
@ -1 +1 @@
|
|||
CREATE INDEX IF NOT EXISTS "item_gist_path_index" ON "Item" USING GIST ("path");
|
||||
CREATE INDEX "item_gist_path_index" ON "Item" USING GIST ("path" gist_ltree_ops(siglen=2024));
|
|
@ -0,0 +1,9 @@
|
|||
-- AlterTable
|
||||
ALTER TABLE "Item"
|
||||
ADD COLUMN "bio" BOOLEAN NOT NULL DEFAULT false,
|
||||
ADD COLUMN "freebie" BOOLEAN NOT NULL DEFAULT false;
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "users" ADD COLUMN "greeterMode" BOOLEAN NOT NULL DEFAULT false,
|
||||
ALTER COLUMN "freeComments" SET DEFAULT 5,
|
||||
ALTER COLUMN "freePosts" SET DEFAULT 2;
|
|
@ -0,0 +1,172 @@
|
|||
DROP FUNCTION IF EXISTS create_bio(title TEXT, text TEXT, user_id INTEGER, has_img_link BOOLEAN);
|
||||
|
||||
-- when creating bio, set bio flag so they won't appear on first page
|
||||
CREATE OR REPLACE FUNCTION create_bio(title TEXT, text TEXT, user_id INTEGER)
|
||||
RETURNS "Item"
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
DECLARE
|
||||
item "Item";
|
||||
BEGIN
|
||||
PERFORM ASSERT_SERIALIZED();
|
||||
|
||||
SELECT * INTO item FROM create_item(title, NULL, text, 0, NULL, user_id, NULL, '0');
|
||||
|
||||
UPDATE "Item" SET bio = true WHERE id = item.id;
|
||||
UPDATE users SET "bioId" = item.id WHERE id = user_id;
|
||||
|
||||
RETURN item;
|
||||
END;
|
||||
$$;
|
||||
|
||||
DROP FUNCTION IF EXISTS create_item(
|
||||
title TEXT, url TEXT, text TEXT, boost INTEGER,
|
||||
parent_id INTEGER, user_id INTEGER, fwd_user_id INTEGER,
|
||||
has_img_link BOOLEAN, spam_within INTERVAL);
|
||||
|
||||
-- when creating free item, set freebie flag so can be optionally viewed
|
||||
CREATE OR REPLACE FUNCTION create_item(
|
||||
title TEXT, url TEXT, text TEXT, boost INTEGER,
|
||||
parent_id INTEGER, user_id INTEGER, fwd_user_id INTEGER,
|
||||
spam_within INTERVAL)
|
||||
RETURNS "Item"
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
DECLARE
|
||||
user_msats INTEGER;
|
||||
cost INTEGER;
|
||||
free_posts INTEGER;
|
||||
free_comments INTEGER;
|
||||
freebie BOOLEAN;
|
||||
item "Item";
|
||||
med_votes FLOAT;
|
||||
BEGIN
|
||||
PERFORM ASSERT_SERIALIZED();
|
||||
|
||||
SELECT msats, "freePosts", "freeComments"
|
||||
INTO user_msats, free_posts, free_comments
|
||||
FROM users WHERE id = user_id;
|
||||
|
||||
cost := 1000 * POWER(10, item_spam(parent_id, user_id, spam_within));
|
||||
freebie := (cost <= 1000) AND ((parent_id IS NULL AND free_posts > 0) OR (parent_id IS NOT NULL AND free_comments > 0));
|
||||
|
||||
IF NOT freebie AND cost > user_msats THEN
|
||||
RAISE EXCEPTION 'SN_INSUFFICIENT_FUNDS';
|
||||
END IF;
|
||||
|
||||
-- get this user's median item score
|
||||
SELECT COALESCE(percentile_cont(0.5) WITHIN GROUP(ORDER BY "weightedVotes" - "weightedDownVotes"), 0) INTO med_votes FROM "Item" WHERE "userId" = user_id;
|
||||
|
||||
-- if their median votes are positive, start at 0
|
||||
-- if the median votes are negative, start their post with that many down votes
|
||||
-- basically: if their median post is bad, presume this post is too
|
||||
IF med_votes >= 0 THEN
|
||||
med_votes := 0;
|
||||
ELSE
|
||||
med_votes := ABS(med_votes);
|
||||
END IF;
|
||||
|
||||
INSERT INTO "Item" (title, url, text, "userId", "parentId", "fwdUserId", freebie, "weightedDownVotes", created_at, updated_at)
|
||||
VALUES (title, url, text, user_id, parent_id, fwd_user_id, freebie, med_votes, now_utc(), now_utc()) RETURNING * INTO item;
|
||||
|
||||
IF freebie THEN
|
||||
IF parent_id IS NULL THEN
|
||||
UPDATE users SET "freePosts" = "freePosts" - 1 WHERE id = user_id;
|
||||
ELSE
|
||||
UPDATE users SET "freeComments" = "freeComments" - 1 WHERE id = user_id;
|
||||
END IF;
|
||||
ELSE
|
||||
UPDATE users SET msats = msats - cost WHERE id = user_id;
|
||||
|
||||
INSERT INTO "ItemAct" (sats, "itemId", "userId", act, created_at, updated_at)
|
||||
VALUES (cost / 1000, item.id, user_id, 'VOTE', now_utc(), now_utc());
|
||||
END IF;
|
||||
|
||||
IF boost > 0 THEN
|
||||
PERFORM item_act(item.id, user_id, 'BOOST', boost);
|
||||
END IF;
|
||||
|
||||
RETURN item;
|
||||
END;
|
||||
$$;
|
||||
|
||||
DROP FUNCTION IF EXISTS update_item(item_id INTEGER,
|
||||
item_title TEXT, item_url TEXT, item_text TEXT, boost INTEGER,
|
||||
fwd_user_id INTEGER, has_img_link BOOLEAN);
|
||||
|
||||
CREATE OR REPLACE FUNCTION update_item(item_id INTEGER,
|
||||
item_title TEXT, item_url TEXT, item_text TEXT, boost INTEGER,
|
||||
fwd_user_id INTEGER)
|
||||
RETURNS "Item"
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
DECLARE
|
||||
user_msats INTEGER;
|
||||
item "Item";
|
||||
BEGIN
|
||||
PERFORM ASSERT_SERIALIZED();
|
||||
|
||||
UPDATE "Item" set title = item_title, url = item_url, text = item_text, "fwdUserId" = fwd_user_id
|
||||
WHERE id = item_id
|
||||
RETURNING * INTO item;
|
||||
|
||||
IF boost > 0 THEN
|
||||
PERFORM item_act(item.id, item."userId", 'BOOST', boost);
|
||||
END IF;
|
||||
|
||||
RETURN item;
|
||||
END;
|
||||
$$;
|
||||
|
||||
DROP FUNCTION IF EXISTS create_poll(
|
||||
title TEXT, text TEXT, poll_cost INTEGER, boost INTEGER, user_id INTEGER,
|
||||
options TEXT[], fwd_user_id INTEGER, has_img_link BOOLEAN, spam_within INTERVAL);
|
||||
|
||||
CREATE OR REPLACE FUNCTION create_poll(
|
||||
title TEXT, text TEXT, poll_cost INTEGER, boost INTEGER, user_id INTEGER,
|
||||
options TEXT[], fwd_user_id INTEGER, spam_within INTERVAL)
|
||||
RETURNS "Item"
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
DECLARE
|
||||
item "Item";
|
||||
option TEXT;
|
||||
BEGIN
|
||||
PERFORM ASSERT_SERIALIZED();
|
||||
|
||||
item := create_item(title, null, text, boost, null, user_id, fwd_user_id, spam_within);
|
||||
|
||||
UPDATE "Item" set "pollCost" = poll_cost where id = item.id;
|
||||
FOREACH option IN ARRAY options LOOP
|
||||
INSERT INTO "PollOption" (created_at, updated_at, "itemId", "option") values (now_utc(), now_utc(), item.id, option);
|
||||
END LOOP;
|
||||
|
||||
RETURN item;
|
||||
END;
|
||||
$$;
|
||||
|
||||
DROP FUNCTION IF EXISTS update_poll(
|
||||
id INTEGER, title TEXT, text TEXT, boost INTEGER,
|
||||
options TEXT[], fwd_user_id INTEGER, has_img_link BOOLEAN);
|
||||
|
||||
CREATE OR REPLACE FUNCTION update_poll(
|
||||
id INTEGER, title TEXT, text TEXT, boost INTEGER,
|
||||
options TEXT[], fwd_user_id INTEGER)
|
||||
RETURNS "Item"
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
DECLARE
|
||||
item "Item";
|
||||
option TEXT;
|
||||
BEGIN
|
||||
PERFORM ASSERT_SERIALIZED();
|
||||
|
||||
item := update_item(id, title, null, text, boost, fwd_user_id);
|
||||
|
||||
FOREACH option IN ARRAY options LOOP
|
||||
INSERT INTO "PollOption" (created_at, updated_at, "itemId", "option") values (now_utc(), now_utc(), item.id, option);
|
||||
END LOOP;
|
||||
|
||||
RETURN item;
|
||||
END;
|
||||
$$;
|
|
@ -32,8 +32,8 @@ model User {
|
|||
bioId Int?
|
||||
msats Int @default(0)
|
||||
stackedMsats Int @default(0)
|
||||
freeComments Int @default(0)
|
||||
freePosts Int @default(0)
|
||||
freeComments Int @default(5)
|
||||
freePosts Int @default(2)
|
||||
checkedNotesAt DateTime?
|
||||
tipDefault Int @default(10)
|
||||
pubkey String? @unique
|
||||
|
@ -61,6 +61,7 @@ model User {
|
|||
|
||||
// content settings
|
||||
wildWestMode Boolean @default(false)
|
||||
greeterMode Boolean @default(false)
|
||||
|
||||
Earn Earn[]
|
||||
Upload Upload[] @relation(name: "Uploads")
|
||||
|
@ -185,6 +186,10 @@ model Item {
|
|||
upload Upload?
|
||||
paidImgLink Boolean @default(false)
|
||||
|
||||
// is free post or bio
|
||||
freebie Boolean @default(false)
|
||||
bio Boolean @default(false)
|
||||
|
||||
// denormalized self stats
|
||||
weightedVotes Float @default(0)
|
||||
weightedDownVotes Float @default(0)
|
||||
|
|
Loading…
Reference in New Issue