From e4c1c2f1e159a1b92b83d3fd4c3718f73983e40e Mon Sep 17 00:00:00 2001 From: keyan Date: Sun, 12 Sep 2021 11:55:38 -0500 Subject: [PATCH] refine tipping --- api/resolvers/item.js | 6 ++- api/typeDefs/item.js | 2 +- api/typeDefs/user.js | 1 + components/action-tooltip.js | 5 ++- components/adv-post-form.js | 4 +- components/discussion-form.js | 8 ++-- components/form.js | 30 ++++++++++++++ components/form.module.css | 2 +- components/item-act.js | 24 ++++++++++-- components/link-form.js | 8 ++-- components/me.js | 1 + components/upvote.js | 39 +++++++++++++++++-- components/upvote.module.css | 4 ++ .../20210911215322_tip_default/migration.sql | 5 +++ prisma/schema.prisma | 1 + 15 files changed, 116 insertions(+), 24 deletions(-) create mode 100644 prisma/migrations/20210911215322_tip_default/migration.sql diff --git a/api/resolvers/item.js b/api/resolvers/item.js index c777760e..753429fb 100644 --- a/api/resolvers/item.js +++ b/api/resolvers/item.js @@ -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)})`) diff --git a/api/typeDefs/item.js b/api/typeDefs/item.js index 8ce34d39..36646cab 100644 --- a/api/typeDefs/item.js +++ b/api/typeDefs/item.js @@ -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 { diff --git a/api/typeDefs/user.js b/api/typeDefs/user.js index 0bd83bcd..b42bb04e 100644 --- a/api/typeDefs/user.js +++ b/api/typeDefs/user.js @@ -21,6 +21,7 @@ export default gql` freePosts: Int! freeComments: Int! hasNewNotes: Boolean! + tipDefault: Int! sats: Int! msats: Int! } diff --git a/components/action-tooltip.js b/components/action-tooltip.js index 07d862aa..f79158e8 100644 --- a/components/action-tooltip.js +++ b/components/action-tooltip.js @@ -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 ( advanced} + header={
options
} body={ boost ranks posts higher temporarily depending on the amount} + hint={boost ranks posts higher temporarily based on the amount} append={sats} /> } diff --git a/components/discussion-form.js b/components/discussion-form.js index 1ca397f5..94539e7c 100644 --- a/components/discussion-form.js +++ b/components/discussion-form.js @@ -82,11 +82,9 @@ export function DiscussionForm ({ item, editThreshold }) { : null} /> {!item && } -
- - {item ? 'save' : 'post'} - -
+ + {item ? 'save' : 'post'} + ) } diff --git a/components/form.js b/components/form.js index a8132e3f..08089b77 100644 --- a/components/form.js +++ b/components/form.js @@ -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 ( +
+ + { + field.onChange(e) + handleChange && handleChange(e.target.checked) + }} + /> + +
{label}
+ {extra && +
+ {extra} +
} +
+
+ {children} +
+ ) +} + export function Form ({ initial, schema, onSubmit, children, initialError, validateImmediately, ...props }) { diff --git a/components/form.module.css b/components/form.module.css index da8afac0..95a321ef 100644 --- a/components/form.module.css +++ b/components/form.module.css @@ -9,4 +9,4 @@ .markdownInput .text { margin-top: -1px; height: auto; -} \ No newline at end of file +} diff --git a/components/item-act.js b/components/item-act.js index 7cbe5dbe..37e47906 100644 --- a/components/item-act.js +++ b/components/item-act.js @@ -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 () {
{ - 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={sats} /> +
tip
diff --git a/components/link-form.js b/components/link-form.js index 7c236806..b6d9ceba 100644 --- a/components/link-form.js +++ b/components/link-form.js @@ -109,11 +109,9 @@ export function LinkForm ({ item, editThreshold }) { /> {!item && } -
- - {item ? 'save' : 'post'} - -
+ + {item ? 'save' : 'post'} + ) } diff --git a/components/me.js b/components/me.js index ffc04a23..22336245 100644 --- a/components/me.js +++ b/components/me.js @@ -16,6 +16,7 @@ export function MeProvider ({ children }) { freePosts freeComments hasNewNotes + tipDefault } }` const { data } = useQuery(query, { pollInterval: 1000 }) diff --git a/components/upvote.js b/components/upvote.js index ce0d5ffc..66c9a9c5 100644 --- a/components/upvote.js +++ b/components/upvote.js @@ -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 + } + + return '1 sat' + } + + const noSelfTips = item?.meVote && item?.user?.id === me?.id + return ( {({ strike }) => - : '1 sat'}> + { 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 } }) diff --git a/components/upvote.module.css b/components/upvote.module.css index 2e5ebceb..edbe145b 100644 --- a/components/upvote.module.css +++ b/components/upvote.module.css @@ -8,6 +8,10 @@ cursor: pointer; } +.noSelfTips:hover { + cursor: default !important; +} + .upvote.voted { fill: #F6911D; filter: drop-shadow(0 0 7px #F6911D); diff --git a/prisma/migrations/20210911215322_tip_default/migration.sql b/prisma/migrations/20210911215322_tip_default/migration.sql new file mode 100644 index 00000000..f4397f2d --- /dev/null +++ b/prisma/migrations/20210911215322_tip_default/migration.sql @@ -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; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index eea47fcc..4aee1dfe 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -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")