refine tipping experience, removing notion of upvote from UX
This commit is contained in:
parent
b6b5cea1f5
commit
5d49ecc536
|
@ -362,7 +362,7 @@ export default {
|
||||||
|
|
||||||
return await updateItem(parent, { id, data: { text } }, { me, models })
|
return await updateItem(parent, { id, data: { text } }, { me, models })
|
||||||
},
|
},
|
||||||
act: async (parent, { id, act, sats, tipDefault }, { me, models }) => {
|
act: async (parent, { id, sats }, { me, models }) => {
|
||||||
// need to make sure we are logged in
|
// need to make sure we are logged in
|
||||||
if (!me) {
|
if (!me) {
|
||||||
throw new AuthenticationError('you must be logged in')
|
throw new AuthenticationError('you must be logged in')
|
||||||
|
@ -372,26 +372,20 @@ export default {
|
||||||
throw new UserInputError('sats must be positive', { argumentName: 'sats' })
|
throw new UserInputError('sats must be positive', { argumentName: 'sats' })
|
||||||
}
|
}
|
||||||
|
|
||||||
// if we are tipping disallow self tips
|
// disallow self tips
|
||||||
if (act === 'TIP') {
|
const [item] = await models.$queryRaw(`
|
||||||
const [item] = await models.$queryRaw(`
|
${SELECT}
|
||||||
${SELECT}
|
FROM "Item"
|
||||||
FROM "Item"
|
WHERE id = $1 AND "userId" = $2`, Number(id), me.id)
|
||||||
WHERE id = $1 AND "userId" = $2`, Number(id), me.id)
|
if (item) {
|
||||||
if (item) {
|
throw new UserInputError('cannot tip your self')
|
||||||
throw new UserInputError('cannot tip your self')
|
|
||||||
}
|
|
||||||
// if tipDefault, set on user
|
|
||||||
if (tipDefault) {
|
|
||||||
await models.user.update({ where: { id: me.id }, data: { tipDefault: sats } })
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await serialize(models, models.$queryRaw`SELECT item_act(${Number(id)}, ${me.id}, ${act}, ${Number(sats)})`)
|
const [{ item_act: vote }] = await serialize(models, models.$queryRaw`SELECT item_act(${Number(id)}, ${me.id}, 'TIP', ${Number(sats)})`)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
sats,
|
vote,
|
||||||
act
|
sats
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -448,6 +442,27 @@ export default {
|
||||||
},
|
},
|
||||||
where: {
|
where: {
|
||||||
itemId: item.id,
|
itemId: item.id,
|
||||||
|
userId: {
|
||||||
|
not: item.userId
|
||||||
|
},
|
||||||
|
act: {
|
||||||
|
not: 'BOOST'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return sats || 0
|
||||||
|
},
|
||||||
|
upvotes: async (item, args, { models }) => {
|
||||||
|
const { sum: { sats } } = await models.itemAct.aggregate({
|
||||||
|
sum: {
|
||||||
|
sats: true
|
||||||
|
},
|
||||||
|
where: {
|
||||||
|
itemId: item.id,
|
||||||
|
userId: {
|
||||||
|
not: item.userId
|
||||||
|
},
|
||||||
act: 'VOTE'
|
act: 'VOTE'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -467,35 +482,6 @@ export default {
|
||||||
|
|
||||||
return sats || 0
|
return sats || 0
|
||||||
},
|
},
|
||||||
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({
|
|
||||||
sum: {
|
|
||||||
sats: true
|
|
||||||
},
|
|
||||||
where: {
|
|
||||||
itemId: item.id,
|
|
||||||
userId: me.id,
|
|
||||||
act: 'VOTE'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return sats || 0
|
|
||||||
},
|
|
||||||
meSats: async (item, args, { me, models }) => {
|
meSats: async (item, args, { me, models }) => {
|
||||||
if (!me) return 0
|
if (!me) return 0
|
||||||
|
|
||||||
|
@ -522,22 +508,6 @@ export default {
|
||||||
mine: async (item, args, { me, models }) => {
|
mine: async (item, args, { me, models }) => {
|
||||||
return me?.id === item.userId
|
return me?.id === item.userId
|
||||||
},
|
},
|
||||||
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 }) => {
|
root: async (item, args, { models }) => {
|
||||||
if (!item.parentId) {
|
if (!item.parentId) {
|
||||||
return null
|
return null
|
||||||
|
|
|
@ -10,15 +10,9 @@ export default gql`
|
||||||
dupes(url: String!): [Item!]
|
dupes(url: String!): [Item!]
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ItemAct {
|
|
||||||
VOTE
|
|
||||||
BOOST
|
|
||||||
TIP
|
|
||||||
}
|
|
||||||
|
|
||||||
type ItemActResult {
|
type ItemActResult {
|
||||||
|
vote: Int!
|
||||||
sats: Int!
|
sats: Int!
|
||||||
act: ItemAct!
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extend type Mutation {
|
extend type Mutation {
|
||||||
|
@ -28,7 +22,7 @@ export default gql`
|
||||||
updateDiscussion(id: ID!, title: String!, text: String): Item!
|
updateDiscussion(id: ID!, title: String!, text: String): Item!
|
||||||
createComment(text: String!, parentId: ID!): Item!
|
createComment(text: String!, parentId: ID!): Item!
|
||||||
updateComment(id: ID!, text: String!): Item!
|
updateComment(id: ID!, text: String!): Item!
|
||||||
act(id: ID!, act: ItemAct!, sats: Int, tipDefault: Boolean): ItemActResult!
|
act(id: ID!, sats: Int): ItemActResult!
|
||||||
}
|
}
|
||||||
|
|
||||||
type Items {
|
type Items {
|
||||||
|
@ -53,13 +47,11 @@ export default gql`
|
||||||
root: Item
|
root: Item
|
||||||
user: User!
|
user: User!
|
||||||
depth: Int!
|
depth: Int!
|
||||||
sats: Int!
|
|
||||||
boost: Int!
|
|
||||||
tips: Int!
|
|
||||||
mine: Boolean!
|
mine: Boolean!
|
||||||
meVote: Int!
|
boost: Int!
|
||||||
|
sats: Int!
|
||||||
|
upvotes: Int!
|
||||||
meSats: Int!
|
meSats: Int!
|
||||||
meTip: Int!
|
|
||||||
ncomments: Int!
|
ncomments: Int!
|
||||||
comments: [Item!]!
|
comments: [Item!]!
|
||||||
path: String
|
path: String
|
||||||
|
|
|
@ -79,7 +79,7 @@ export default function Comment ({
|
||||||
<div className={`${itemStyles.hunk} ${styles.hunk}`}>
|
<div className={`${itemStyles.hunk} ${styles.hunk}`}>
|
||||||
<div className='d-flex align-items-center'>
|
<div className='d-flex align-items-center'>
|
||||||
<div className={`${itemStyles.other} ${styles.other}`}>
|
<div className={`${itemStyles.other} ${styles.other}`}>
|
||||||
<span title={`${item.sats} upvotes \\ ${item.tips} tipped${item.meSats > 0 ? ` (${item.meSats} from me)` : ''}`}>{item.sats + item.tips} sats</span>
|
<span title={`from ${item.upvotes} users (${item.meSats} from me)`}>{item.sats} sats</span>
|
||||||
<span> \ </span>
|
<span> \ </span>
|
||||||
{item.boost > 0 &&
|
{item.boost > 0 &&
|
||||||
<>
|
<>
|
||||||
|
|
|
@ -57,13 +57,11 @@ export function ItemActModal () {
|
||||||
default: false
|
default: false
|
||||||
}}
|
}}
|
||||||
schema={ActSchema}
|
schema={ActSchema}
|
||||||
onSubmit={async ({ amount, tipDefault, submit }) => {
|
onSubmit={async ({ amount }) => {
|
||||||
await item.act({
|
await item.act({
|
||||||
variables: {
|
variables: {
|
||||||
id: item.itemId,
|
id: item.itemId,
|
||||||
act: submit,
|
sats: Number(amount)
|
||||||
sats: Number(amount),
|
|
||||||
tipDefault
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
await item.strike()
|
await item.strike()
|
||||||
|
|
|
@ -50,7 +50,7 @@ export default function Item ({ item, rank, children }) {
|
||||||
<div className={`${styles.other}`}>
|
<div className={`${styles.other}`}>
|
||||||
{!item.position &&
|
{!item.position &&
|
||||||
<>
|
<>
|
||||||
<span title={`${item.sats} upvotes \\ ${item.tips} tipped${item.meSats > 0 ? ` (${item.meSats} from me)` : ''}`}>{item.sats + item.tips} sats</span>
|
<span title={`from ${item.upvotes} users (${item.meSats} sats from me)`}>{item.sats} sats</span>
|
||||||
<span> \ </span>
|
<span> \ </span>
|
||||||
</>}
|
</>}
|
||||||
{item.boost > 0 &&
|
{item.boost > 0 &&
|
||||||
|
|
|
@ -20,7 +20,7 @@ export default function Seo ({ item, user }) {
|
||||||
desc = desc.replace(/\s+/g, ' ')
|
desc = desc.replace(/\s+/g, ' ')
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
desc = `@${item.user.name} stacked ${(item.sats > 0 ? item.sats - 1 : 0) + item.tips} sats ${item.url ? `posting ${item.url}` : 'with this discussion'}`
|
desc = `@${item.user.name} stacked ${item.sats} sats ${item.url ? `posting ${item.url}` : 'with this discussion'}`
|
||||||
}
|
}
|
||||||
if (item.ncomments) {
|
if (item.ncomments) {
|
||||||
desc += ` [${item.ncomments} comments`
|
desc += ` [${item.ncomments} comments`
|
||||||
|
|
|
@ -104,60 +104,29 @@ export default function UpVote ({ item, className }) {
|
||||||
|
|
||||||
const [act] = useMutation(
|
const [act] = useMutation(
|
||||||
gql`
|
gql`
|
||||||
mutation act($id: ID!, $act: ItemAct! $sats: Int!, $tipDefault: Boolean) {
|
mutation act($id: ID!, $sats: Int!) {
|
||||||
act(id: $id, act: $act, sats: $sats, tipDefault: $tipDefault) {
|
act(id: $id, sats: $sats) {
|
||||||
act,
|
vote,
|
||||||
sats
|
sats
|
||||||
}
|
}
|
||||||
}`, {
|
}`, {
|
||||||
update (cache, { data: { act: { act, sats } } }) {
|
update (cache, { data: { act: { vote, sats } } }) {
|
||||||
// read in the cached object so we don't use meSats prop
|
|
||||||
// which can be stale
|
|
||||||
if (act === 'VOTE') {
|
|
||||||
setVoteShow(true)
|
|
||||||
}
|
|
||||||
if (act === 'TIP') {
|
|
||||||
setTipShow(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
cache.modify({
|
cache.modify({
|
||||||
id: `Item:${item.id}`,
|
id: `Item:${item.id}`,
|
||||||
fields: {
|
fields: {
|
||||||
meVote (existingMeVote = 0) {
|
|
||||||
if (act === 'VOTE') {
|
|
||||||
return existingMeVote + sats
|
|
||||||
}
|
|
||||||
return existingMeVote
|
|
||||||
},
|
|
||||||
meTip (existingMeTip = 0) {
|
|
||||||
if (act === 'TIP') {
|
|
||||||
return existingMeTip + sats
|
|
||||||
}
|
|
||||||
return existingMeTip
|
|
||||||
},
|
|
||||||
sats (existingSats = 0) {
|
sats (existingSats = 0) {
|
||||||
if (act === 'VOTE') {
|
return existingSats + sats
|
||||||
return existingSats + sats
|
|
||||||
}
|
|
||||||
return existingSats
|
|
||||||
},
|
},
|
||||||
meSats (existingSats = 0) {
|
meSats (existingSats = 0) {
|
||||||
if (act === 'VOTE' || act === 'TIP') {
|
if (existingSats === 0) {
|
||||||
return existingSats + sats
|
setVoteShow(true)
|
||||||
|
} else {
|
||||||
|
setTipShow(true)
|
||||||
}
|
}
|
||||||
return existingSats
|
return existingSats + sats
|
||||||
},
|
},
|
||||||
boost (existingBoost = 0) {
|
upvotes (existingUpvotes = 0) {
|
||||||
if (act === 'BOOST') {
|
return existingUpvotes + vote
|
||||||
return existingBoost + sats
|
|
||||||
}
|
|
||||||
return existingBoost
|
|
||||||
},
|
|
||||||
tips (existingTips = 0) {
|
|
||||||
if (act === 'TIP') {
|
|
||||||
return existingTips + sats
|
|
||||||
}
|
|
||||||
return existingTips
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -166,15 +135,12 @@ export default function UpVote ({ item, className }) {
|
||||||
)
|
)
|
||||||
|
|
||||||
const overlayText = () => {
|
const overlayText = () => {
|
||||||
if (item?.meVote) {
|
if (me?.tipDefault) {
|
||||||
if (me?.tipDefault) {
|
return `${me.tipDefault} sat${me.tipDefault > 1 ? 's' : ''}`
|
||||||
return `${me.tipDefault} sat${me.tipDefault > 1 ? 's' : ''}`
|
|
||||||
}
|
|
||||||
return '1 sat'
|
|
||||||
}
|
}
|
||||||
|
return '1 sat'
|
||||||
}
|
}
|
||||||
|
|
||||||
const noSelfTips = item?.meVote && item?.mine
|
|
||||||
const color = getColor(item?.meSats)
|
const color = getColor(item?.meSats)
|
||||||
return (
|
return (
|
||||||
<LightningConsumer>
|
<LightningConsumer>
|
||||||
|
@ -186,7 +152,7 @@ export default function UpVote ({ item, className }) {
|
||||||
if (!item || voteLock) return
|
if (!item || voteLock) return
|
||||||
|
|
||||||
// we can't tip ourselves
|
// we can't tip ourselves
|
||||||
if (noSelfTips) {
|
if (item?.mine) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -200,26 +166,19 @@ export default function UpVote ({ item, className }) {
|
||||||
if (!item || voteLock) return
|
if (!item || voteLock) return
|
||||||
|
|
||||||
// we can't tip ourselves
|
// we can't tip ourselves
|
||||||
if (noSelfTips) {
|
if (item?.mine) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item?.meVote) {
|
if (item?.meSats) {
|
||||||
setVoteShow(false)
|
setVoteShow(false)
|
||||||
try {
|
|
||||||
strike()
|
|
||||||
await act({ variables: { id: item.id, act: 'TIP', sats: me.tipDefault || 1 } })
|
|
||||||
} catch (e) {
|
|
||||||
console.log(e)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
strike()
|
strike()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
setVoteLock(true)
|
setVoteLock(true)
|
||||||
await act({ variables: { id: item.id, act: 'VOTE', sats: 1 } })
|
await act({ variables: { id: item.id, sats: me.tipDefault || 1 } })
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error.toString().includes('insufficient funds')) {
|
if (error.toString().includes('insufficient funds')) {
|
||||||
setError(true)
|
setError(true)
|
||||||
|
@ -233,9 +192,9 @@ export default function UpVote ({ item, className }) {
|
||||||
: signIn
|
: signIn
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<ActionTooltip notForm disable={noSelfTips} overlayText={overlayText()}>
|
<ActionTooltip notForm disable={item?.mine} overlayText={overlayText()}>
|
||||||
<div
|
<div
|
||||||
className={`${noSelfTips ? styles.noSelfTips : ''}
|
className={`${item?.mine ? styles.noSelfTips : ''}
|
||||||
${styles.upvoteWrapper}`}
|
${styles.upvoteWrapper}`}
|
||||||
>
|
>
|
||||||
<UpBolt
|
<UpBolt
|
||||||
|
@ -244,7 +203,7 @@ export default function UpVote ({ item, className }) {
|
||||||
className={
|
className={
|
||||||
`${styles.upvote}
|
`${styles.upvote}
|
||||||
${className || ''}
|
${className || ''}
|
||||||
${noSelfTips ? styles.noSelfTips : ''}
|
${item?.mine ? styles.noSelfTips : ''}
|
||||||
${item?.meSats ? styles.voted : ''}`
|
${item?.meSats ? styles.voted : ''}`
|
||||||
}
|
}
|
||||||
style={item?.meSats
|
style={item?.meSats
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
padding-right: .2rem;
|
padding-right: .2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.noSelfTips .upvote.voted {
|
.noSelfTips {
|
||||||
fill: transparent !important;
|
fill: transparent !important;
|
||||||
filter: none !important;
|
filter: none !important;
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,11 +11,9 @@ export const COMMENT_FIELDS = gql`
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
sats
|
sats
|
||||||
|
upvotes
|
||||||
boost
|
boost
|
||||||
tips
|
|
||||||
meVote
|
|
||||||
meSats
|
meSats
|
||||||
meTip
|
|
||||||
mine
|
mine
|
||||||
ncomments
|
ncomments
|
||||||
root {
|
root {
|
||||||
|
|
|
@ -13,11 +13,9 @@ export const ITEM_FIELDS = gql`
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
sats
|
sats
|
||||||
|
upvotes
|
||||||
boost
|
boost
|
||||||
tips
|
|
||||||
meVote
|
|
||||||
meSats
|
meSats
|
||||||
meTip
|
|
||||||
ncomments
|
ncomments
|
||||||
mine
|
mine
|
||||||
root {
|
root {
|
||||||
|
|
|
@ -37,9 +37,11 @@ BEGIN
|
||||||
INSERT INTO "ItemAct" (sats, "itemId", "userId", act, created_at, updated_at)
|
INSERT INTO "ItemAct" (sats, "itemId", "userId", act, created_at, updated_at)
|
||||||
VALUES (act_sats, item_id, user_id, 'TIP', now_utc(), now_utc());
|
VALUES (act_sats, item_id, user_id, 'TIP', now_utc(), now_utc());
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
|
RETURN 1;
|
||||||
END IF;
|
END IF;
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
RETURN act_sats;
|
RETURN 0;
|
||||||
END;
|
END;
|
||||||
$$;
|
$$;
|
Loading…
Reference in New Issue