refine tipping
This commit is contained in:
parent
650ad03de5
commit
e4c1c2f1e1
|
@ -209,7 +209,7 @@ export default {
|
|||
|
||||
return await updateItem(parent, { id, data: { text } }, { me, models })
|
||||
},
|
||||
act: async (parent, { id, act, sats }, { me, models }) => {
|
||||
act: async (parent, { id, act, sats, tipDefault }, { me, models }) => {
|
||||
// need to make sure we are logged in
|
||||
if (!me) {
|
||||
throw new AuthenticationError('you must be logged in')
|
||||
|
@ -228,6 +228,10 @@ export default {
|
|||
if (item) {
|
||||
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)})`)
|
||||
|
|
|
@ -27,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): ItemActResult!
|
||||
act(id: ID!, act: ItemAct!, sats: Int, tipDefault: Boolean): ItemActResult!
|
||||
}
|
||||
|
||||
type Items {
|
||||
|
|
|
@ -21,6 +21,7 @@ export default gql`
|
|||
freePosts: Int!
|
||||
freeComments: Int!
|
||||
hasNewNotes: Boolean!
|
||||
tipDefault: Int!
|
||||
sats: Int!
|
||||
msats: Int!
|
||||
}
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
import { useFormikContext } from 'formik'
|
||||
import { OverlayTrigger, Tooltip } from 'react-bootstrap'
|
||||
|
||||
export default function ActionTooltip ({ children, notForm, overlayText }) {
|
||||
export default function ActionTooltip ({ children, notForm, disable, overlayText }) {
|
||||
// if we're in a form, we want to hide tooltip on submit
|
||||
let formik
|
||||
if (!notForm) {
|
||||
formik = useFormikContext()
|
||||
}
|
||||
if (disable) {
|
||||
return children
|
||||
}
|
||||
return (
|
||||
<OverlayTrigger
|
||||
placement='bottom'
|
||||
|
|
|
@ -15,12 +15,12 @@ export const AdvPostInitial = {
|
|||
export default function AdvPostForm () {
|
||||
return (
|
||||
<AccordianItem
|
||||
header={<div className='font-weight-bold'>advanced</div>}
|
||||
header={<div style={{ fontWeight: 'bold', fontSize: '92%' }}>options</div>}
|
||||
body={
|
||||
<Input
|
||||
label='boost'
|
||||
name='boost'
|
||||
hint={<span className='text-muted'>boost ranks posts higher temporarily depending on the amount</span>}
|
||||
hint={<span className='text-muted'>boost ranks posts higher temporarily based on the amount</span>}
|
||||
append={<InputGroup.Text className='text-monospace'>sats</InputGroup.Text>}
|
||||
/>
|
||||
}
|
||||
|
|
|
@ -82,11 +82,9 @@ export function DiscussionForm ({ item, editThreshold }) {
|
|||
: null}
|
||||
/>
|
||||
{!item && <AdvPostForm />}
|
||||
<div className='d-flex'>
|
||||
<ActionTooltip>
|
||||
<SubmitButton variant='secondary' className='mt-2 ml-auto'>{item ? 'save' : 'post'}</SubmitButton>
|
||||
</ActionTooltip>
|
||||
</div>
|
||||
<ActionTooltip>
|
||||
<SubmitButton variant='secondary' className='mt-3'>{item ? 'save' : 'post'}</SubmitButton>
|
||||
</ActionTooltip>
|
||||
</Form>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -175,6 +175,36 @@ export function Input ({ label, groupClassName, ...props }) {
|
|||
)
|
||||
}
|
||||
|
||||
export function Checkbox ({ children, label, extra, handleChange, ...props }) {
|
||||
// React treats radios and checkbox inputs differently other input types, select, and textarea.
|
||||
// Formik does this too! When you specify `type` to useField(), it will
|
||||
// return the correct bag of props for you
|
||||
const [field, { value }] = useField({ ...props, type: 'checkbox' })
|
||||
return (
|
||||
<div className={value ? styles.checkboxChecked : styles.checkboxUnchecked}>
|
||||
<BootstrapForm.Check
|
||||
custom
|
||||
id={props.id || props.name}
|
||||
>
|
||||
<BootstrapForm.Check.Input
|
||||
{...field} {...props} type='checkbox' onChange={(e) => {
|
||||
field.onChange(e)
|
||||
handleChange && handleChange(e.target.checked)
|
||||
}}
|
||||
/>
|
||||
<BootstrapForm.Check.Label className='d-flex'>
|
||||
<div className='flex-grow-1'>{label}</div>
|
||||
{extra &&
|
||||
<div className={styles.checkboxExtra}>
|
||||
{extra}
|
||||
</div>}
|
||||
</BootstrapForm.Check.Label>
|
||||
</BootstrapForm.Check>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export function Form ({
|
||||
initial, schema, onSubmit, children, initialError, validateImmediately, ...props
|
||||
}) {
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import { InputGroup, Modal } from 'react-bootstrap'
|
||||
import React, { useState, useCallback, useContext, useRef, useEffect } from 'react'
|
||||
import * as Yup from 'yup'
|
||||
import { Form, Input, SubmitButton } from './form'
|
||||
import { Checkbox, Form, Input, SubmitButton } from './form'
|
||||
import { useMe } from './me'
|
||||
|
||||
export const ItemActContext = React.createContext({
|
||||
item: null,
|
||||
|
@ -36,6 +37,7 @@ export const ActSchema = Yup.object({
|
|||
export function ItemActModal () {
|
||||
const { item, setItem } = useItemAct()
|
||||
const inputRef = useRef(null)
|
||||
const me = useMe()
|
||||
|
||||
useEffect(() => {
|
||||
inputRef.current?.focus()
|
||||
|
@ -51,11 +53,19 @@ export function ItemActModal () {
|
|||
<Modal.Body>
|
||||
<Form
|
||||
initial={{
|
||||
amount: 21
|
||||
amount: me?.tipDefault || 21,
|
||||
default: false
|
||||
}}
|
||||
schema={ActSchema}
|
||||
onSubmit={async ({ amount, submit }) => {
|
||||
await item.act({ variables: { id: item.itemId, act: submit, sats: Number(amount) } })
|
||||
onSubmit={async ({ amount, tipDefault, submit }) => {
|
||||
await item.act({
|
||||
variables: {
|
||||
id: item.itemId,
|
||||
act: submit,
|
||||
sats: Number(amount),
|
||||
tipDefault
|
||||
}
|
||||
})
|
||||
await item.strike()
|
||||
setItem(null)
|
||||
}}
|
||||
|
@ -68,6 +78,12 @@ export function ItemActModal () {
|
|||
autoFocus
|
||||
append={<InputGroup.Text className='text-monospace'>sats</InputGroup.Text>}
|
||||
/>
|
||||
<Checkbox
|
||||
label='set as default'
|
||||
name='tipDefault'
|
||||
required
|
||||
autoFocus
|
||||
/>
|
||||
<div className='d-flex'>
|
||||
<SubmitButton variant='success' className='ml-auto mt-1 px-4' value='TIP'>tip</SubmitButton>
|
||||
</div>
|
||||
|
|
|
@ -109,11 +109,9 @@ export function LinkForm ({ item, editThreshold }) {
|
|||
/>
|
||||
{!item && <AdvPostForm />}
|
||||
|
||||
<div className='d-flex'>
|
||||
<ActionTooltip>
|
||||
<SubmitButton variant='secondary' className='mt-2 ml-auto'>{item ? 'save' : 'post'}</SubmitButton>
|
||||
</ActionTooltip>
|
||||
</div>
|
||||
<ActionTooltip>
|
||||
<SubmitButton variant='secondary' className='mt-3'>{item ? 'save' : 'post'}</SubmitButton>
|
||||
</ActionTooltip>
|
||||
</Form>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ export function MeProvider ({ children }) {
|
|||
freePosts
|
||||
freeComments
|
||||
hasNewNotes
|
||||
tipDefault
|
||||
}
|
||||
}`
|
||||
const { data } = useQuery(query, { pollInterval: 1000 })
|
||||
|
|
|
@ -7,15 +7,17 @@ import { useFundError } from './fund-error'
|
|||
import ActionTooltip from './action-tooltip'
|
||||
import { useItemAct } from './item-act'
|
||||
import Window from '../svgs/window-2-fill.svg'
|
||||
import { useMe } from './me'
|
||||
|
||||
export default function UpVote ({ item, className }) {
|
||||
const [session] = useSession()
|
||||
const { setError } = useFundError()
|
||||
const { setItem } = useItemAct()
|
||||
const me = useMe()
|
||||
const [act] = useMutation(
|
||||
gql`
|
||||
mutation act($id: ID!, $act: ItemAct! $sats: Int!) {
|
||||
act(id: $id, act: $act, sats: $sats) {
|
||||
mutation act($id: ID!, $act: ItemAct! $sats: Int!, $tipDefault: Boolean) {
|
||||
act(id: $id, act: $act, sats: $sats, tipDefault: $tipDefault) {
|
||||
act,
|
||||
sats
|
||||
}
|
||||
|
@ -68,29 +70,58 @@ export default function UpVote ({ item, className }) {
|
|||
}
|
||||
)
|
||||
|
||||
const overlayText = () => {
|
||||
if (item?.meVote) {
|
||||
if (me?.tipDefault) {
|
||||
return `tip ${me.tipDefault}`
|
||||
}
|
||||
return <Window style={{ fill: '#fff' }} width={18} height={18} />
|
||||
}
|
||||
|
||||
return '1 sat'
|
||||
}
|
||||
|
||||
const noSelfTips = item?.meVote && item?.user?.id === me?.id
|
||||
|
||||
return (
|
||||
<LightningConsumer>
|
||||
{({ strike }) =>
|
||||
<ActionTooltip notForm overlayText={item?.meVote ? <Window style={{ fill: '#fff' }} /> : '1 sat'}>
|
||||
<ActionTooltip notForm disable={noSelfTips} overlayText={overlayText()}>
|
||||
<UpArrow
|
||||
width={24}
|
||||
height={24}
|
||||
className={
|
||||
`${styles.upvote}
|
||||
${className || ''}
|
||||
${noSelfTips ? styles.noSelfTips : ''}
|
||||
${item?.meVote ? styles.voted : ''}`
|
||||
}
|
||||
onClick={
|
||||
session
|
||||
? async (e) => {
|
||||
e.stopPropagation()
|
||||
if (!item) return
|
||||
|
||||
// we can't tip ourselves
|
||||
if (noSelfTips) {
|
||||
return
|
||||
}
|
||||
|
||||
if (item?.meVote) {
|
||||
if (me?.tipDefault) {
|
||||
try {
|
||||
await act({ variables: { id: item.id, act: 'TIP', sats: me.tipDefault } })
|
||||
strike()
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
}
|
||||
return
|
||||
}
|
||||
setItem({ itemId: item.id, act, strike })
|
||||
return
|
||||
}
|
||||
|
||||
strike()
|
||||
if (!item) return
|
||||
|
||||
try {
|
||||
await act({ variables: { id: item.id, act: 'VOTE', sats: 1 } })
|
||||
|
|
|
@ -8,6 +8,10 @@
|
|||
cursor: pointer;
|
||||
}
|
||||
|
||||
.noSelfTips:hover {
|
||||
cursor: default !important;
|
||||
}
|
||||
|
||||
.upvote.voted {
|
||||
fill: #F6911D;
|
||||
filter: drop-shadow(0 0 7px #F6911D);
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
-- AlterTable
|
||||
ALTER TABLE "ItemAct" ALTER COLUMN "sats" DROP DEFAULT;
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "users" ADD COLUMN "tipDefault" INTEGER NOT NULL DEFAULT 0;
|
|
@ -28,6 +28,7 @@ model User {
|
|||
freeComments Int @default(5)
|
||||
freePosts Int @default(2)
|
||||
checkedNotesAt DateTime?
|
||||
tipDefault Int @default(0)
|
||||
pubkey String? @unique
|
||||
|
||||
@@map(name: "users")
|
||||
|
|
Loading…
Reference in New Issue