refine tipping

This commit is contained in:
keyan 2021-09-12 11:55:38 -05:00
parent 650ad03de5
commit e4c1c2f1e1
15 changed files with 116 additions and 24 deletions

View File

@ -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)})`)

View File

@ -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 {

View File

@ -21,6 +21,7 @@ export default gql`
freePosts: Int!
freeComments: Int!
hasNewNotes: Boolean!
tipDefault: Int!
sats: Int!
msats: Int!
}

View File

@ -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'

View File

@ -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>}
/>
}

View File

@ -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>
<SubmitButton variant='secondary' className='mt-3'>{item ? 'save' : 'post'}</SubmitButton>
</ActionTooltip>
</div>
</Form>
)
}

View File

@ -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
}) {

View File

@ -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>

View File

@ -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>
<SubmitButton variant='secondary' className='mt-3'>{item ? 'save' : 'post'}</SubmitButton>
</ActionTooltip>
</div>
</Form>
)
}

View File

@ -16,6 +16,7 @@ export function MeProvider ({ children }) {
freePosts
freeComments
hasNewNotes
tipDefault
}
}`
const { data } = useQuery(query, { pollInterval: 1000 })

View File

@ -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 } })

View File

@ -8,6 +8,10 @@
cursor: pointer;
}
.noSelfTips:hover {
cursor: default !important;
}
.upvote.voted {
fill: #F6911D;
filter: drop-shadow(0 0 7px #F6911D);

View File

@ -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;

View File

@ -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")