forward tips from posts
This commit is contained in:
parent
822fa9113a
commit
d978ff5ea5
|
@ -611,6 +611,12 @@ export default {
|
||||||
},
|
},
|
||||||
user: async (item, args, { models }) =>
|
user: async (item, args, { models }) =>
|
||||||
await models.user.findUnique({ where: { id: item.userId } }),
|
await models.user.findUnique({ where: { id: item.userId } }),
|
||||||
|
fwdUser: async (item, args, { models }) => {
|
||||||
|
if (!item.fwdUserId) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return await models.user.findUnique({ where: { id: item.fwdUserId } })
|
||||||
|
},
|
||||||
ncomments: async (item, args, { models }) => {
|
ncomments: async (item, args, { models }) => {
|
||||||
const [{ count }] = await models.$queryRaw`
|
const [{ count }] = await models.$queryRaw`
|
||||||
SELECT count(*)
|
SELECT count(*)
|
||||||
|
@ -793,12 +799,29 @@ const createItem = async (parent, { title, url, text, boost, forward, parentId }
|
||||||
throw new UserInputError(`boost must be at least ${BOOST_MIN}`, { argumentName: 'boost' })
|
throw new UserInputError(`boost must be at least ${BOOST_MIN}`, { argumentName: 'boost' })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let fwdUser
|
||||||
|
if (forward) {
|
||||||
|
fwdUser = await models.user.findUnique({ where: { name: forward } })
|
||||||
|
if (!fwdUser) {
|
||||||
|
throw new UserInputError('forward user does not exist', { argumentName: 'forward' })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const [item] = await serialize(models,
|
const [item] = await serialize(models,
|
||||||
models.$queryRaw(`${SELECT} FROM create_item($1, $2, $3, $4, $5, $6) AS "Item"`,
|
models.$queryRaw(`${SELECT} FROM create_item($1, $2, $3, $4, $5, $6) AS "Item"`,
|
||||||
title, url, text, Number(boost || 0), Number(parentId), Number(me.id)))
|
title, url, text, Number(boost || 0), Number(parentId), Number(me.id)))
|
||||||
|
|
||||||
await createMentions(item, models)
|
await createMentions(item, models)
|
||||||
|
|
||||||
|
if (fwdUser) {
|
||||||
|
await models.item.update({
|
||||||
|
where: { id: item.id },
|
||||||
|
data: {
|
||||||
|
fwdUserId: fwdUser.id
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
item.comments = []
|
item.comments = []
|
||||||
return item
|
return item
|
||||||
}
|
}
|
||||||
|
@ -831,7 +854,7 @@ function nestComments (flat, parentId) {
|
||||||
// we have to do our own query because ltree is unsupported
|
// we have to do our own query because ltree is unsupported
|
||||||
export const SELECT =
|
export const SELECT =
|
||||||
`SELECT "Item".id, "Item".created_at as "createdAt", "Item".updated_at as "updatedAt", "Item".title,
|
`SELECT "Item".id, "Item".created_at as "createdAt", "Item".updated_at as "updatedAt", "Item".title,
|
||||||
"Item".text, "Item".url, "Item"."userId", "Item"."parentId", "Item"."pinId", "Item"."maxBid",
|
"Item".text, "Item".url, "Item"."userId", "Item"."fwdUserId", "Item"."parentId", "Item"."pinId", "Item"."maxBid",
|
||||||
"Item".company, "Item".location, "Item".remote,
|
"Item".company, "Item".location, "Item".remote,
|
||||||
"Item"."subName", "Item".status, ltree2text("Item"."path") AS "path"`
|
"Item"."subName", "Item".status, ltree2text("Item"."path") AS "path"`
|
||||||
|
|
||||||
|
|
|
@ -97,7 +97,9 @@ export default {
|
||||||
FROM "ItemAct"
|
FROM "ItemAct"
|
||||||
JOIN "Item" on "ItemAct"."itemId" = "Item".id
|
JOIN "Item" on "ItemAct"."itemId" = "Item".id
|
||||||
WHERE "ItemAct"."userId" <> $1 AND "ItemAct".act <> 'BOOST'
|
WHERE "ItemAct"."userId" <> $1 AND "ItemAct".act <> 'BOOST'
|
||||||
AND "Item"."userId" = $1 AND "ItemAct".created_at <= $2
|
AND (("Item"."userId" = $1 AND "Item"."fwdUserId" IS NULL)
|
||||||
|
OR ("Item"."fwdUserId" = $1 AND "ItemAct"."userId" <> "Item"."userId"))
|
||||||
|
AND "ItemAct".created_at <= $2
|
||||||
GROUP BY "Item".id)`)
|
GROUP BY "Item".id)`)
|
||||||
queries.push(
|
queries.push(
|
||||||
`(SELECT ('earn' || "Earn".id) as id, "Earn".id as "factId", NULL as bolt11,
|
`(SELECT ('earn' || "Earn".id) as id, "Earn".id as "factId", NULL as bolt11,
|
||||||
|
|
|
@ -52,6 +52,7 @@ export default gql`
|
||||||
root: Item
|
root: Item
|
||||||
user: User!
|
user: User!
|
||||||
userId: Int!
|
userId: Int!
|
||||||
|
fwdUser: User
|
||||||
depth: Int!
|
depth: Int!
|
||||||
mine: Boolean!
|
mine: Boolean!
|
||||||
boost: Int!
|
boost: Int!
|
||||||
|
|
|
@ -3,11 +3,23 @@ import * as Yup from 'yup'
|
||||||
import { Input } from './form'
|
import { Input } from './form'
|
||||||
import { InputGroup } from 'react-bootstrap'
|
import { InputGroup } from 'react-bootstrap'
|
||||||
import { BOOST_MIN } from '../lib/constants'
|
import { BOOST_MIN } from '../lib/constants'
|
||||||
|
import { NAME_QUERY } from '../fragments/users'
|
||||||
|
|
||||||
export const AdvPostSchema = {
|
export function AdvPostSchema (client) {
|
||||||
boost: Yup.number().typeError('must be a number')
|
return {
|
||||||
.min(BOOST_MIN, `must be at least ${BOOST_MIN}`).integer('must be whole'),
|
boost: Yup.number().typeError('must be a number')
|
||||||
forward: Yup.string().trim()
|
.min(BOOST_MIN, `must be at least ${BOOST_MIN}`).integer('must be whole'),
|
||||||
|
forward: Yup.string()
|
||||||
|
.test({
|
||||||
|
name: 'name',
|
||||||
|
test: async name => {
|
||||||
|
if (!name || !name.length) return true
|
||||||
|
const { data } = await client.query({ query: NAME_QUERY, variables: { name }, fetchPolicy: 'network-only' })
|
||||||
|
return !data.nameAvailable
|
||||||
|
},
|
||||||
|
message: 'user does not exist'
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const AdvPostInitial = {
|
export const AdvPostInitial = {
|
||||||
|
@ -30,8 +42,9 @@ export default function AdvPostForm () {
|
||||||
<Input
|
<Input
|
||||||
label='forward sats to'
|
label='forward sats to'
|
||||||
name='forward'
|
name='forward'
|
||||||
hint={<span className='text-muted'>100% of sats earned will be sent to this user</span>}
|
hint={<span className='text-muted'>100% of sats will be sent to this user</span>}
|
||||||
prepend=<InputGroup.Text>@</InputGroup.Text>
|
prepend=<InputGroup.Text>@</InputGroup.Text>
|
||||||
|
showValid
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { Form, Input, MarkdownInput, SubmitButton } from '../components/form'
|
import { Form, Input, MarkdownInput, SubmitButton } from '../components/form'
|
||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
import * as Yup from 'yup'
|
import * as Yup from 'yup'
|
||||||
import { gql, useMutation } from '@apollo/client'
|
import { gql, useApolloClient, useMutation } from '@apollo/client'
|
||||||
import ActionTooltip from '../components/action-tooltip'
|
import ActionTooltip from '../components/action-tooltip'
|
||||||
import TextareaAutosize from 'react-textarea-autosize'
|
import TextareaAutosize from 'react-textarea-autosize'
|
||||||
import Countdown from './countdown'
|
import Countdown from './countdown'
|
||||||
|
@ -18,6 +18,7 @@ export function DiscussionForm ({
|
||||||
adv, handleSubmit
|
adv, handleSubmit
|
||||||
}) {
|
}) {
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
const client = useApolloClient()
|
||||||
const [upsertDiscussion] = useMutation(
|
const [upsertDiscussion] = useMutation(
|
||||||
gql`
|
gql`
|
||||||
mutation upsertDiscussion($id: ID, $title: String!, $text: String, $boost: Int, $forward: String) {
|
mutation upsertDiscussion($id: ID, $title: String!, $text: String, $boost: Int, $forward: String) {
|
||||||
|
@ -27,6 +28,11 @@ export function DiscussionForm ({
|
||||||
}`
|
}`
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const DiscussionSchema = Yup.object({
|
||||||
|
title: Yup.string().required('required').trim(),
|
||||||
|
...AdvPostSchema(client)
|
||||||
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form
|
<Form
|
||||||
initial={{
|
initial={{
|
||||||
|
|
|
@ -173,7 +173,7 @@ function InputInner ({
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
isInvalid={meta.touched && meta.error}
|
isInvalid={meta.touched && meta.error}
|
||||||
isValid={showValid && meta.touched && !meta.error}
|
isValid={showValid && meta.initialValue !== meta.value && meta.touched && !meta.error}
|
||||||
/>
|
/>
|
||||||
{append && (
|
{append && (
|
||||||
<InputGroup.Append>
|
<InputGroup.Append>
|
||||||
|
|
|
@ -83,7 +83,7 @@ function TopLevelItem ({ item, noReply, ...props }) {
|
||||||
const ItemComponent = item.maxBid ? ItemJob : Item
|
const ItemComponent = item.maxBid ? ItemJob : Item
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ItemComponent item={item} {...props}>
|
<ItemComponent item={item} showFwdUser {...props}>
|
||||||
{item.text && <ItemText item={item} />}
|
{item.text && <ItemText item={item} />}
|
||||||
{item.url && <ItemEmbed item={item} />}
|
{item.url && <ItemEmbed item={item} />}
|
||||||
{!noReply && <Reply parentId={item.id} meComments={item.meComments} replyOpen />}
|
{!noReply && <Reply parentId={item.id} meComments={item.meComments} replyOpen />}
|
||||||
|
|
|
@ -97,7 +97,18 @@ export function ItemJob ({ item, rank, children }) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Item ({ item, rank, children }) {
|
function FwdUser ({ user }) {
|
||||||
|
return (
|
||||||
|
<div className={styles.other}>
|
||||||
|
100% of tips are forwarded to{' '}
|
||||||
|
<Link href={`/${user.name}`} passHref>
|
||||||
|
<a>@{user.name}</a>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Item ({ item, rank, showFwdUser, children }) {
|
||||||
const mine = item.mine
|
const mine = item.mine
|
||||||
const editThreshold = new Date(item.createdAt).getTime() + 10 * 60000
|
const editThreshold = new Date(item.createdAt).getTime() + 10 * 60000
|
||||||
const [canEdit, setCanEdit] =
|
const [canEdit, setCanEdit] =
|
||||||
|
@ -187,6 +198,7 @@ export default function Item ({ item, rank, children }) {
|
||||||
</Link>
|
</Link>
|
||||||
</>}
|
</>}
|
||||||
</div>
|
</div>
|
||||||
|
{showFwdUser && item.fwdUser && <FwdUser user={item.fwdUser} />}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{children && (
|
{children && (
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { Form, Input, SubmitButton } from '../components/form'
|
import { Form, Input, SubmitButton } from '../components/form'
|
||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
import * as Yup from 'yup'
|
import * as Yup from 'yup'
|
||||||
import { gql, useLazyQuery, useMutation } from '@apollo/client'
|
import { gql, useApolloClient, useLazyQuery, useMutation } from '@apollo/client'
|
||||||
import ActionTooltip from '../components/action-tooltip'
|
import ActionTooltip from '../components/action-tooltip'
|
||||||
import Countdown from './countdown'
|
import Countdown from './countdown'
|
||||||
import AdvPostForm, { AdvPostInitial, AdvPostSchema } from './adv-post-form'
|
import AdvPostForm, { AdvPostInitial, AdvPostSchema } from './adv-post-form'
|
||||||
|
@ -11,14 +11,10 @@ import AccordianItem from './accordian-item'
|
||||||
|
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
const URL = /^((https?|ftp):\/\/)?(www.)?(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i
|
const URL = /^((https?|ftp):\/\/)?(www.)?(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i
|
||||||
export const LinkSchema = Yup.object({
|
|
||||||
title: Yup.string().required('required').trim(),
|
|
||||||
url: Yup.string().matches(URL, 'invalid url').required('required'),
|
|
||||||
...AdvPostSchema
|
|
||||||
})
|
|
||||||
|
|
||||||
export function LinkForm ({ item, editThreshold }) {
|
export function LinkForm ({ item, editThreshold }) {
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
const client = useApolloClient()
|
||||||
|
|
||||||
const [getPageTitle, { data }] = useLazyQuery(gql`
|
const [getPageTitle, { data }] = useLazyQuery(gql`
|
||||||
query PageTitle($url: String!) {
|
query PageTitle($url: String!) {
|
||||||
|
@ -45,6 +41,12 @@ export function LinkForm ({ item, editThreshold }) {
|
||||||
}`
|
}`
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const LinkSchema = Yup.object({
|
||||||
|
title: Yup.string().required('required').trim(),
|
||||||
|
url: Yup.string().matches(URL, 'invalid url').required('required'),
|
||||||
|
...AdvPostSchema(client)
|
||||||
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form
|
<Form
|
||||||
initial={{
|
initial={{
|
||||||
|
|
|
@ -79,7 +79,7 @@ function Notification ({ n }) {
|
||||||
<>
|
<>
|
||||||
{n.__typename === 'Votification' &&
|
{n.__typename === 'Votification' &&
|
||||||
<small className='font-weight-bold text-success ml-2'>
|
<small className='font-weight-bold text-success ml-2'>
|
||||||
your {n.item.title ? 'post' : 'reply'} stacked {n.earnedSats} sats
|
your {n.item.title ? 'post' : 'reply'} {n.item.fwdUser ? 'forwarded' : 'stacked'} {n.earnedSats} sats{n.item.fwdUser && ` to @${n.item.fwdUser.name}`}
|
||||||
</small>}
|
</small>}
|
||||||
{n.__typename === 'Mention' &&
|
{n.__typename === 'Mention' &&
|
||||||
<small className='font-weight-bold text-info ml-2'>
|
<small className='font-weight-bold text-info ml-2'>
|
||||||
|
|
|
@ -76,6 +76,8 @@ export default function UpVote ({ item, className }) {
|
||||||
}`
|
}`
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const fwd2me = me && me?.id === item?.fwdUser?.id
|
||||||
|
|
||||||
const setVoteShow = (yes) => {
|
const setVoteShow = (yes) => {
|
||||||
if (!me) return
|
if (!me) return
|
||||||
|
|
||||||
|
@ -155,7 +157,7 @@ export default function UpVote ({ item, className }) {
|
||||||
if (!item) return
|
if (!item) return
|
||||||
|
|
||||||
// we can't tip ourselves
|
// we can't tip ourselves
|
||||||
if (item?.mine) {
|
if (item?.mine || fwd2me) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -169,7 +171,7 @@ export default function UpVote ({ item, className }) {
|
||||||
if (!item) return
|
if (!item) return
|
||||||
|
|
||||||
// we can't tip ourselves
|
// we can't tip ourselves
|
||||||
if (item?.mine) {
|
if (item?.mine || fwd2me) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -201,9 +203,9 @@ export default function UpVote ({ item, className }) {
|
||||||
: signIn
|
: signIn
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<ActionTooltip notForm disable={item?.mine} overlayText={overlayText()}>
|
<ActionTooltip notForm disable={item?.mine || fwd2me} overlayText={overlayText()}>
|
||||||
<div
|
<div
|
||||||
className={`${item?.mine ? styles.noSelfTips : ''}
|
className={`${item?.mine || fwd2me ? styles.noSelfTips : ''}
|
||||||
${styles.upvoteWrapper}`}
|
${styles.upvoteWrapper}`}
|
||||||
>
|
>
|
||||||
<UpBolt
|
<UpBolt
|
||||||
|
@ -212,7 +214,7 @@ export default function UpVote ({ item, className }) {
|
||||||
className={
|
className={
|
||||||
`${styles.upvote}
|
`${styles.upvote}
|
||||||
${className || ''}
|
${className || ''}
|
||||||
${item?.mine ? styles.noSelfTips : ''}
|
${item?.mine || fwd2me ? styles.noSelfTips : ''}
|
||||||
${item?.meSats ? styles.voted : ''}`
|
${item?.meSats ? styles.voted : ''}`
|
||||||
}
|
}
|
||||||
style={item?.meSats
|
style={item?.meSats
|
||||||
|
|
|
@ -9,20 +9,7 @@ import * as Yup from 'yup'
|
||||||
import { gql, useApolloClient, useMutation } from '@apollo/client'
|
import { gql, useApolloClient, useMutation } from '@apollo/client'
|
||||||
import styles from './user-header.module.css'
|
import styles from './user-header.module.css'
|
||||||
import { useMe } from './me'
|
import { useMe } from './me'
|
||||||
|
import { NAME_MUTATION, NAME_QUERY } from '../fragments/users'
|
||||||
const NAME_QUERY =
|
|
||||||
gql`
|
|
||||||
query nameAvailable($name: String!) {
|
|
||||||
nameAvailable(name: $name)
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
const NAME_MUTATION =
|
|
||||||
gql`
|
|
||||||
mutation setName($name: String!) {
|
|
||||||
setName(name: $name)
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
export default function UserHeader ({ user }) {
|
export default function UserHeader ({ user }) {
|
||||||
const [editting, setEditting] = useState(false)
|
const [editting, setEditting] = useState(false)
|
||||||
|
|
|
@ -12,6 +12,10 @@ export const ITEM_FIELDS = gql`
|
||||||
name
|
name
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
|
fwdUser {
|
||||||
|
name
|
||||||
|
id
|
||||||
|
}
|
||||||
sats
|
sats
|
||||||
upvotes
|
upvotes
|
||||||
boost
|
boost
|
||||||
|
|
|
@ -22,6 +22,20 @@ export const ME = gql`
|
||||||
}
|
}
|
||||||
}`
|
}`
|
||||||
|
|
||||||
|
export const NAME_QUERY =
|
||||||
|
gql`
|
||||||
|
query nameAvailable($name: String!) {
|
||||||
|
nameAvailable(name: $name)
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
export const NAME_MUTATION =
|
||||||
|
gql`
|
||||||
|
mutation setName($name: String!) {
|
||||||
|
setName(name: $name)
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
export const USER_FIELDS = gql`
|
export const USER_FIELDS = gql`
|
||||||
${ITEM_FIELDS}
|
${ITEM_FIELDS}
|
||||||
fragment UserFields on User {
|
fragment UserFields on User {
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
CREATE OR REPLACE FUNCTION item_act(item_id INTEGER, user_id INTEGER, act "ItemActType", act_sats INTEGER)
|
||||||
|
RETURNS INTEGER
|
||||||
|
LANGUAGE plpgsql
|
||||||
|
AS $$
|
||||||
|
DECLARE
|
||||||
|
user_sats INTEGER;
|
||||||
|
BEGIN
|
||||||
|
PERFORM ASSERT_SERIALIZED();
|
||||||
|
|
||||||
|
SELECT (msats / 1000) INTO user_sats FROM users WHERE id = user_id;
|
||||||
|
IF act_sats > user_sats THEN
|
||||||
|
RAISE EXCEPTION 'SN_INSUFFICIENT_FUNDS';
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
-- deduct sats from actor
|
||||||
|
UPDATE users SET msats = msats - (act_sats * 1000) WHERE id = user_id;
|
||||||
|
|
||||||
|
IF act = 'BOOST' THEN
|
||||||
|
INSERT INTO "ItemAct" (sats, "itemId", "userId", act, created_at, updated_at)
|
||||||
|
VALUES (act_sats, item_id, user_id, 'BOOST', now_utc(), now_utc());
|
||||||
|
ELSE
|
||||||
|
-- add sats to actee's balance and stacked count
|
||||||
|
UPDATE users
|
||||||
|
SET msats = msats + (act_sats * 1000), "stackedMsats" = "stackedMsats" + (act_sats * 1000)
|
||||||
|
WHERE id = (SELECT COALESCE("fwdUserId", "userId") FROM "Item" WHERE id = item_id);
|
||||||
|
|
||||||
|
-- if they have already voted, this is a tip
|
||||||
|
IF EXISTS (SELECT 1 FROM "ItemAct" WHERE "itemId" = item_id AND "userId" = user_id AND "ItemAct".act = 'VOTE') THEN
|
||||||
|
INSERT INTO "ItemAct" (sats, "itemId", "userId", act, created_at, updated_at)
|
||||||
|
VALUES (act_sats, item_id, user_id, 'TIP', now_utc(), now_utc());
|
||||||
|
ELSE
|
||||||
|
-- else this is a vote with a possible extra tip
|
||||||
|
INSERT INTO "ItemAct" (sats, "itemId", "userId", act, created_at, updated_at)
|
||||||
|
VALUES (1, item_id, user_id, 'VOTE', now_utc(), now_utc());
|
||||||
|
act_sats := act_sats - 1;
|
||||||
|
|
||||||
|
-- if we have sats left after vote, leave them as a tip
|
||||||
|
IF act_sats > 0 THEN
|
||||||
|
INSERT INTO "ItemAct" (sats, "itemId", "userId", act, created_at, updated_at)
|
||||||
|
VALUES (act_sats, item_id, user_id, 'TIP', now_utc(), now_utc());
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
RETURN 1;
|
||||||
|
END IF;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
RETURN 0;
|
||||||
|
END;
|
||||||
|
$$;
|
Loading…
Reference in New Issue