account for job payment status
This commit is contained in:
parent
1338c026a3
commit
bc1c45e7bf
|
@ -81,6 +81,10 @@ export default {
|
|||
return sub ? ` AND "subName" = $${num} ` : ` AND ("subName" IS NULL OR "subName" = $${3}) `
|
||||
}
|
||||
|
||||
const activeOrMine = () => {
|
||||
return me ? ` AND (status = 'ACTIVE' OR "userId" = ${me.id}) ` : ' AND status = \'ACTIVE\' '
|
||||
}
|
||||
|
||||
switch (sort) {
|
||||
case 'user':
|
||||
if (!name) {
|
||||
|
@ -97,6 +101,7 @@ export default {
|
|||
FROM "Item"
|
||||
WHERE "userId" = $1 AND "parentId" IS NULL AND created_at <= $2
|
||||
AND "pinId" IS NULL
|
||||
${activeOrMine()}
|
||||
ORDER BY created_at DESC
|
||||
OFFSET $3
|
||||
LIMIT ${LIMIT}`, user.id, decodedCursor.time, decodedCursor.offset)
|
||||
|
@ -107,6 +112,7 @@ export default {
|
|||
FROM "Item"
|
||||
WHERE "parentId" IS NULL AND created_at <= $1 AND "pinId" IS NULL
|
||||
${subClause(3)}
|
||||
${activeOrMine()}
|
||||
ORDER BY created_at DESC
|
||||
OFFSET $2
|
||||
LIMIT ${LIMIT}`, decodedCursor.time, decodedCursor.offset, sub || 'NULL')
|
||||
|
@ -140,6 +146,7 @@ export default {
|
|||
WHERE "parentId" IS NULL AND created_at <= $1
|
||||
AND "pinId" IS NULL
|
||||
${subClause(3)}
|
||||
AND status = 'ACTIVE'
|
||||
ORDER BY "maxBid" / 1000 DESC, created_at ASC
|
||||
OFFSET $2
|
||||
LIMIT ${LIMIT}`, decodedCursor.time, decodedCursor.offset, sub)
|
||||
|
@ -291,7 +298,7 @@ export default {
|
|||
comments: async (parent, { id, sort }, { models }) => {
|
||||
return comments(models, id, sort)
|
||||
},
|
||||
search: async (parent, { q: query, sub, cursor }, { models, search }) => {
|
||||
search: async (parent, { q: query, sub, cursor }, { me, models, search }) => {
|
||||
const decodedCursor = decodeCursor(cursor)
|
||||
let sitems
|
||||
|
||||
|
@ -305,8 +312,18 @@ export default {
|
|||
bool: {
|
||||
must: [
|
||||
sub
|
||||
? { term: { 'sub.name': sub } }
|
||||
? { match: { 'sub.name': sub } }
|
||||
: { bool: { must_not: { exists: { field: 'sub.name' } } } },
|
||||
me
|
||||
? {
|
||||
bool: {
|
||||
should: [
|
||||
{ match: { status: 'ACTIVE' } },
|
||||
{ match: { userId: me.id } }
|
||||
]
|
||||
}
|
||||
}
|
||||
: { match: { status: 'ACTIVE' } },
|
||||
{
|
||||
bool: {
|
||||
should: [
|
||||
|
@ -395,6 +412,7 @@ export default {
|
|||
const where = {
|
||||
where: {
|
||||
subName: sub,
|
||||
status: 'ACTIVE',
|
||||
OR: [{
|
||||
maxBid: {
|
||||
gte: bid + 1000
|
||||
|
@ -487,7 +505,7 @@ export default {
|
|||
|
||||
return await updateItem(parent, { id, data: { title, text } }, { me, models })
|
||||
},
|
||||
upsertJob: async (parent, { id, sub, title, text, url, maxBid }, { me, models }) => {
|
||||
upsertJob: async (parent, { id, sub, title, text, url, maxBid, status }, { me, models }) => {
|
||||
if (!me) {
|
||||
throw new AuthenticationError('you must be logged in to create job')
|
||||
}
|
||||
|
@ -512,6 +530,15 @@ export default {
|
|||
throw new UserInputError(`bid must be at least ${fullSub.baseCost}`, { argumentName: 'maxBid' })
|
||||
}
|
||||
|
||||
const checkSats = async () => {
|
||||
// check if the user has the funds to run for the first minute
|
||||
const minuteMsats = maxBid * 1000 / 30 / 24 / 60
|
||||
const user = models.user.findUnique({ where: { id: me.id } })
|
||||
if (user.msats < minuteMsats) {
|
||||
throw new UserInputError('insufficient funds')
|
||||
}
|
||||
}
|
||||
|
||||
const data = {
|
||||
title,
|
||||
text,
|
||||
|
@ -522,12 +549,23 @@ export default {
|
|||
}
|
||||
|
||||
if (id) {
|
||||
if (status) {
|
||||
data.status = status
|
||||
|
||||
// if the job is changing to active, we need to check they have funds
|
||||
if (status === 'ACTIVE') {
|
||||
await checkSats()
|
||||
}
|
||||
}
|
||||
|
||||
return await models.item.update({
|
||||
where: { id: Number(id) },
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// before creating job, check the sats
|
||||
await checkSats()
|
||||
return await models.item.create({
|
||||
data
|
||||
})
|
||||
|
@ -850,7 +888,7 @@ function nestComments (flat, parentId) {
|
|||
export const SELECT =
|
||||
`SELECT "Item".id, "Item".created_at as "createdAt", "Item".updated_at as "updatedAt", "Item".title,
|
||||
"Item".text, "Item".url, "Item"."userId", "Item"."parentId", "Item"."pinId", "Item"."maxBid",
|
||||
"Item"."subName", ltree2text("Item"."path") AS "path"`
|
||||
"Item"."subName", "Item".status, ltree2text("Item"."path") AS "path"`
|
||||
|
||||
const LEFT_JOIN_SATS_SELECT = 'SELECT i.id, SUM(CASE WHEN "ItemAct".act = \'VOTE\' THEN "ItemAct".sats ELSE 0 END) as sats, SUM(CASE WHEN "ItemAct".act = \'BOOST\' THEN "ItemAct".sats ELSE 0 END) as boost'
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@ export default gql`
|
|||
updateDiscussion(id: ID!, title: String!, text: String): Item!
|
||||
createComment(text: String!, parentId: ID!): Item!
|
||||
updateComment(id: ID!, text: String!): Item!
|
||||
upsertJob(id: ID, sub: ID!, title: String!, text: String!, url: String!, maxBid: Int!): Item!
|
||||
upsertJob(id: ID, sub: ID!, title: String!, text: String!, url: String!, maxBid: Int!, status: String): Item!
|
||||
act(id: ID!, sats: Int): ItemActResult!
|
||||
}
|
||||
|
||||
|
@ -67,5 +67,6 @@ export default gql`
|
|||
prior: Int
|
||||
maxBid: Int
|
||||
sub: Sub
|
||||
status: String
|
||||
}
|
||||
`
|
||||
|
|
|
@ -69,6 +69,7 @@ export function ItemJob ({ item, rank, children }) {
|
|||
edit
|
||||
</a>
|
||||
</Link>
|
||||
{item.status !== 'ACTIVE' && <span className='font-weight-bold text-danger'> {item.status}</span>}
|
||||
</>}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -24,7 +24,6 @@
|
|||
.case {
|
||||
fill: #a5a5a5;
|
||||
margin-right: .2rem;
|
||||
margin-left: .2rem;
|
||||
margin-top: .2rem;
|
||||
padding: 0 2px;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Form, Input, MarkdownInput, SubmitButton } from './form'
|
||||
import { Checkbox, Form, Input, MarkdownInput, SubmitButton } from './form'
|
||||
import TextareaAutosize from 'react-textarea-autosize'
|
||||
import { InputGroup, Modal } from 'react-bootstrap'
|
||||
import * as Yup from 'yup'
|
||||
|
@ -8,6 +8,7 @@ import AccordianItem from './accordian-item'
|
|||
import styles from '../styles/post.module.css'
|
||||
import { useLazyQuery, gql, useMutation } from '@apollo/client'
|
||||
import { useRouter } from 'next/router'
|
||||
import Link from 'next/link'
|
||||
|
||||
Yup.addMethod(Yup.string, 'or', function (schemas, msg) {
|
||||
return this.test({
|
||||
|
@ -41,8 +42,10 @@ export default function JobForm ({ item, sub }) {
|
|||
}`,
|
||||
{ fetchPolicy: 'network-only' })
|
||||
const [upsertJob] = useMutation(gql`
|
||||
mutation upsertJob($id: ID, $title: String!, $text: String!, $url: String!, $maxBid: Int!) {
|
||||
upsertJob(sub: "${sub.name}", id: $id title: $title, text: $text, url: $url, maxBid: $maxBid) {
|
||||
mutation upsertJob($id: ID, $title: String!, $text: String!,
|
||||
$url: String!, $maxBid: Int!, $status: String) {
|
||||
upsertJob(sub: "${sub.name}", id: $id title: $title, text: $text,
|
||||
url: $url, maxBid: $maxBid, status: $status) {
|
||||
id
|
||||
}
|
||||
}`
|
||||
|
@ -118,12 +121,21 @@ export default function JobForm ({ item, sub }) {
|
|||
title: item?.title || '',
|
||||
text: item?.text || '',
|
||||
url: item?.url || '',
|
||||
maxBid: item?.maxBid || sub.baseCost
|
||||
maxBid: item?.maxBid || sub.baseCost,
|
||||
stop: false,
|
||||
start: false
|
||||
}}
|
||||
schema={JobSchema}
|
||||
storageKeyPrefix={storageKeyPrefix}
|
||||
onSubmit={(async ({ maxBid, ...values }) => {
|
||||
const variables = { sub: sub.name, maxBid: Number(maxBid), ...values }
|
||||
onSubmit={(async ({ maxBid, stop, start, ...values }) => {
|
||||
let status
|
||||
if (start) {
|
||||
status = 'ACTIVE'
|
||||
} else if (stop) {
|
||||
status = 'STOPPED'
|
||||
}
|
||||
|
||||
const variables = { sub: sub.name, maxBid: Number(maxBid), status, ...values }
|
||||
if (item) {
|
||||
variables.id = item.id
|
||||
}
|
||||
|
@ -176,8 +188,57 @@ export default function JobForm ({ item, sub }) {
|
|||
hint={<span className='text-muted'>up to {pull} sats/min will be pulled from your wallet</span>}
|
||||
/>
|
||||
<div className='font-weight-bold text-muted'>This bid puts your job in position: {position}</div>
|
||||
{item && <StatusControl item={item} />}
|
||||
<SubmitButton variant='secondary' className='mt-3'>{item ? 'save' : 'post'}</SubmitButton>
|
||||
</Form>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
function StatusControl ({ item }) {
|
||||
let StatusComp
|
||||
|
||||
if (item.status === 'ACTIVE') {
|
||||
StatusComp = () => {
|
||||
return (
|
||||
<AccordianItem
|
||||
header={<div style={{ fontWeight: 'bold', fontSize: '92%' }}>I want to stop my job</div>}
|
||||
headerColor='var(--danger)'
|
||||
body={
|
||||
<Checkbox
|
||||
label={<div className='font-weight-bold text-danger'>stop my job</div>} name='stop' inline
|
||||
/>
|
||||
}
|
||||
/>
|
||||
)
|
||||
}
|
||||
} else if (item.status === 'STOPPED') {
|
||||
StatusComp = () => {
|
||||
return (
|
||||
<AccordianItem
|
||||
header={<div style={{ fontWeight: 'bold', fontSize: '92%' }}>I want to resume my job</div>}
|
||||
headerColor='var(--success)'
|
||||
body={
|
||||
<Checkbox
|
||||
label={<div className='font-weight-bold text-success'>resume my job</div>} name='start' inline
|
||||
/>
|
||||
}
|
||||
/>
|
||||
)
|
||||
}
|
||||
} else {
|
||||
StatusComp = () => {
|
||||
return (
|
||||
<div style={{ fontWeight: 'bold', color: 'var(--danger)' }}>
|
||||
you have no sats! <Link href='/wallet?type=fund' passHref><a className='text-reset text-underline'>fund your wallet</a></Link> to resume your job
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='my-2'>
|
||||
<StatusComp />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ export const ITEM_FIELDS = gql`
|
|||
name
|
||||
baseCost
|
||||
}
|
||||
status
|
||||
mine
|
||||
root {
|
||||
id
|
||||
|
|
|
@ -17,6 +17,7 @@ const ITEM_SEARCH_FIELDS = gql`
|
|||
sub {
|
||||
name
|
||||
}
|
||||
status
|
||||
maxBid
|
||||
upvotes
|
||||
sats
|
||||
|
|
Loading…
Reference in New Issue