complete tips

This commit is contained in:
keyan 2021-09-10 16:13:52 -05:00
parent 0a20f2ea23
commit 2dd49171e2
10 changed files with 155 additions and 36 deletions

View File

@ -219,8 +219,23 @@ export default {
throw new UserInputError('sats must be positive', { argumentName: 'sats' })
}
// if we are tipping disallow self tips
if (act === 'TIP') {
const [item] = await models.$queryRaw(`
${SELECT}
FROM "Item"
WHERE id = $1 AND "userId" = $2`, Number(id), me.id)
if (item) {
throw new UserInputError('cannot tip your self')
}
}
await serialize(models, models.$queryRaw`SELECT item_act(${Number(id)}, ${me.id}, ${act}, ${Number(sats)})`)
return sats
return {
sats,
act
}
}
},
@ -260,7 +275,20 @@ export default {
return sats || 0
},
meSats: async (item, args, { me, models }) => {
tips: async (item, args, { models }) => {
const { sum: { sats } } = await models.itemAct.aggregate({
sum: {
sats: true
},
where: {
itemId: item.id,
act: 'TIP'
}
})
return sats || 0
},
meVote: async (item, args, { me, models }) => {
if (!me) return 0
const { sum: { sats } } = await models.itemAct.aggregate({
@ -276,6 +304,38 @@ export default {
return sats || 0
},
meBoost: async (item, args, { me, models }) => {
if (!me) return 0
const { sum: { sats } } = await models.itemAct.aggregate({
sum: {
sats: true
},
where: {
itemId: item.id,
userId: me.id,
act: 'BOOST'
}
})
return sats || 0
},
meTip: async (item, args, { me, models }) => {
if (!me) return 0
const { sum: { sats } } = await models.itemAct.aggregate({
sum: {
sats: true
},
where: {
itemId: item.id,
userId: me.id,
act: 'TIP'
}
})
return sats || 0
},
root: async (item, args, { models }) => {
if (!item.parentId) {
return null

View File

@ -15,6 +15,11 @@ export default gql`
TIP
}
type ItemActResult {
sats: Int!
act: ItemAct!
}
extend type Mutation {
createLink(title: String!, url: String): Item!
updateLink(id: ID!, title: String!, url: String): Item!
@ -22,7 +27,7 @@ export default gql`
updateDiscussion(id: ID!, title: String!, text: String): Item!
createComment(text: String!, parentId: ID!): Item!
updateComment(id: ID!, text: String!): Item!
act(id: ID!, act: ItemAct!, sats: Int): Int!
act(id: ID!, act: ItemAct!, sats: Int): ItemActResult!
}
type Items {
@ -48,7 +53,10 @@ export default gql`
depth: Int!
sats: Int!
boost: Int!
meSats: Int!
tips: Int!
meVote: Int!
meBoost: Int!
meTip: Int!
ncomments: Int!
comments: [Item!]!
path: String

View File

@ -1,7 +1,7 @@
import { useFormikContext } from 'formik'
import { OverlayTrigger, Tooltip } from 'react-bootstrap'
export default function ActionTooltip ({ children, notForm }) {
export default function ActionTooltip ({ children, notForm, overlayText }) {
// if we're in a form, we want to hide tooltip on submit
let formik
if (!notForm) {
@ -12,7 +12,7 @@ export default function ActionTooltip ({ children, notForm }) {
placement='bottom'
overlay={
<Tooltip>
1 sat
{overlayText || '1 sat'}
</Tooltip>
}
trigger={['hover', 'focus']}

View File

@ -63,14 +63,22 @@ export default function Comment ({ item, children, replyOpen, includeParent, roo
ref={ref} className={includeParent ? '' : `${styles.comment} ${collapse ? styles.collapsed : ''}`}
>
<div className={`${itemStyles.item} ${styles.item}`}>
<UpVote itemId={item.id} meSats={item.meSats} className={styles.upvote} />
<UpVote item={item} className={styles.upvote} />
<div className={`${itemStyles.hunk} ${styles.hunk}`}>
<div className='d-flex align-items-center'>
<div className={`${itemStyles.other} ${styles.other}`}>
<span>{item.sats} sats</span>
<span> \ </span>
<span>{item.boost} boost</span>
<span> \ </span>
{item.boost > 0 &&
<>
<span>{item.boost} boost</span>
<span> \ </span>
</>}
{item.tips > 0 &&
<>
<span>{item.tips} tipped</span>
<span> \ </span>
</>}
<Link href={`/items/${item.id}`} passHref>
<a onClick={e => e.stopPropagation()} className='text-reset'>{item.ncomments} replies</a>
</Link>

View File

@ -22,7 +22,7 @@ export default function Item ({ item, rank, children }) {
</div>)
: <div />}
<div className={styles.item}>
<UpVote itemId={item.id} meSats={item.meSats} className={styles.upvote} />
<UpVote item={item} className={styles.upvote} />
<div className={styles.hunk}>
<div className={`${styles.main} flex-wrap`}>
<Link href={`/items/${item.id}`} passHref>
@ -44,6 +44,11 @@ export default function Item ({ item, rank, children }) {
<span>{item.boost} boost</span>
<span> \ </span>
</>}
{item.tips > 0 &&
<>
<span>{item.tips} tipped</span>
<span> \ </span>
</>}
<Link href={`/items/${item.id}`} passHref>
<a className='text-reset'>{item.ncomments} comments</a>
</Link>

View File

@ -20,9 +20,17 @@ export default function Seo ({ item, user }) {
desc = desc.replace(/\s+/g, ' ')
}
} else {
desc = `@${item.user.name} stacked ${item.sats} sats ${item.url ? `posting ${item.url}` : ''}`
desc = `@${item.user.name} stacked ${(item.sats > 0 ? item.sats - 1 : 0) + item.tips} sats ${item.url ? `posting ${item.url}` : 'with this discussion'}`
}
if (item.ncomments) {
desc += ` [${item.ncomments} comments`
if (item.boost) {
desc += `, ${item.boost} boost`
}
desc += ']'
} else if (item.boost) {
desc += ` [${item.boost} boost]`
}
desc += ` [${item.ncomments} comments, ${item.boost} boost]`
}
if (user) {
desc = `@${user.name} has [${user.stacked} stacked, ${user.sats} sats, ${user.nitems} posts, ${user.ncomments} comments]`

View File

@ -6,38 +6,61 @@ import { signIn, useSession } from 'next-auth/client'
import { useFundError } from './fund-error'
import ActionTooltip from './action-tooltip'
import { useItemAct } from './item-act'
import Window from '../svgs/window-2-fill.svg'
export default function UpVote ({ itemId, meSats, className }) {
export default function UpVote ({ item, className }) {
const [session] = useSession()
const { setError } = useFundError()
const { setItem } = useItemAct()
const [act] = useMutation(
gql`
mutation act($id: ID!, $act: ItemAct! $sats: Int!) {
act(id: $id, act: $act, sats: $sats)
act(id: $id, act: $act, sats: $sats) {
act,
sats
}
}`, {
update (cache, { data: { act } }) {
update (cache, { data: { act: { act, sats } } }) {
// read in the cached object so we don't use meSats prop
// which can be stale
const item = cache.readFragment({
id: `Item:${itemId}`,
fragment: gql`
fragment actedItem on Item {
meSats
}
`
})
cache.modify({
id: `Item:${itemId}`,
id: `Item:${item.id}`,
fields: {
meSats (existingMeSats = 0) {
return existingMeSats + act
meVote (existingMeVote = 0) {
if (act === 'VOTE') {
return existingMeVote + sats
}
return existingMeVote
},
meBoost (existingMeBoost = 0) {
if (act === 'BOOST') {
return existingMeBoost + sats
}
return existingMeBoost
},
meTip (existingMeTip = 0) {
if (act === 'TIP') {
return existingMeTip + sats
}
return existingMeTip
},
sats (existingSats = 0) {
return item.meSats === 0 ? existingSats + act : existingSats
if (act === 'VOTE') {
return existingSats + sats
}
return existingSats
},
boost (existingBoost = 0) {
return item.meSats >= 1 ? existingBoost + act : existingBoost
if (act === 'BOOST') {
return existingBoost + sats
}
return existingBoost
},
tips (existingTips = 0) {
if (act === 'TIP') {
return existingTips + sats
}
return existingTips
}
}
})
@ -48,29 +71,29 @@ export default function UpVote ({ itemId, meSats, className }) {
return (
<LightningConsumer>
{({ strike }) =>
<ActionTooltip notForm>
<ActionTooltip notForm overlayText={item?.meVote ? <Window style={{ fill: '#fff' }} /> : '1 sat'}>
<UpArrow
width={24}
height={24}
className={
`${styles.upvote}
${className || ''}
${meSats ? (meSats > 1 ? styles.stimi : styles.voted) : ''}`
${item?.meVote ? styles.voted : ''}`
}
onClick={
session
? async (e) => {
e.stopPropagation()
if (meSats >= 1) {
setItem({ itemId, act, strike })
if (item?.meVote) {
setItem({ itemId: item.id, act, strike })
return
}
strike()
if (!itemId) return
if (!item) return
try {
await act({ variables: { id: itemId, act: 'VOTE', sats: 1 } })
await act({ variables: { id: item.id, act: 'VOTE', sats: 1 } })
} catch (error) {
if (error.toString().includes('insufficient funds')) {
setError(true)

View File

@ -12,7 +12,10 @@ export const COMMENT_FIELDS = gql`
}
sats
boost
meSats
tips
meVote
meBoost
meTip
ncomments
root {
id

View File

@ -13,7 +13,10 @@ export const ITEM_FIELDS = gql`
}
sats
boost
meSats
tips
meVote
meBoost
meTip
ncomments
root {
id

1
svgs/window-2-fill.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M3 3h18a1 1 0 0 1 1 1v16a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1zm17 7H4v9h16v-9zm-5-4v2h4V6h-4z"/></svg>

After

Width:  |  Height:  |  Size: 240 B