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}) `
|
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) {
|
switch (sort) {
|
||||||
case 'user':
|
case 'user':
|
||||||
if (!name) {
|
if (!name) {
|
||||||
|
@ -97,6 +101,7 @@ export default {
|
||||||
FROM "Item"
|
FROM "Item"
|
||||||
WHERE "userId" = $1 AND "parentId" IS NULL AND created_at <= $2
|
WHERE "userId" = $1 AND "parentId" IS NULL AND created_at <= $2
|
||||||
AND "pinId" IS NULL
|
AND "pinId" IS NULL
|
||||||
|
${activeOrMine()}
|
||||||
ORDER BY created_at DESC
|
ORDER BY created_at DESC
|
||||||
OFFSET $3
|
OFFSET $3
|
||||||
LIMIT ${LIMIT}`, user.id, decodedCursor.time, decodedCursor.offset)
|
LIMIT ${LIMIT}`, user.id, decodedCursor.time, decodedCursor.offset)
|
||||||
|
@ -107,6 +112,7 @@ export default {
|
||||||
FROM "Item"
|
FROM "Item"
|
||||||
WHERE "parentId" IS NULL AND created_at <= $1 AND "pinId" IS NULL
|
WHERE "parentId" IS NULL AND created_at <= $1 AND "pinId" IS NULL
|
||||||
${subClause(3)}
|
${subClause(3)}
|
||||||
|
${activeOrMine()}
|
||||||
ORDER BY created_at DESC
|
ORDER BY created_at DESC
|
||||||
OFFSET $2
|
OFFSET $2
|
||||||
LIMIT ${LIMIT}`, decodedCursor.time, decodedCursor.offset, sub || 'NULL')
|
LIMIT ${LIMIT}`, decodedCursor.time, decodedCursor.offset, sub || 'NULL')
|
||||||
|
@ -140,6 +146,7 @@ export default {
|
||||||
WHERE "parentId" IS NULL AND created_at <= $1
|
WHERE "parentId" IS NULL AND created_at <= $1
|
||||||
AND "pinId" IS NULL
|
AND "pinId" IS NULL
|
||||||
${subClause(3)}
|
${subClause(3)}
|
||||||
|
AND status = 'ACTIVE'
|
||||||
ORDER BY "maxBid" / 1000 DESC, created_at ASC
|
ORDER BY "maxBid" / 1000 DESC, created_at ASC
|
||||||
OFFSET $2
|
OFFSET $2
|
||||||
LIMIT ${LIMIT}`, decodedCursor.time, decodedCursor.offset, sub)
|
LIMIT ${LIMIT}`, decodedCursor.time, decodedCursor.offset, sub)
|
||||||
|
@ -291,7 +298,7 @@ export default {
|
||||||
comments: async (parent, { id, sort }, { models }) => {
|
comments: async (parent, { id, sort }, { models }) => {
|
||||||
return comments(models, id, sort)
|
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)
|
const decodedCursor = decodeCursor(cursor)
|
||||||
let sitems
|
let sitems
|
||||||
|
|
||||||
|
@ -305,8 +312,18 @@ export default {
|
||||||
bool: {
|
bool: {
|
||||||
must: [
|
must: [
|
||||||
sub
|
sub
|
||||||
? { term: { 'sub.name': sub } }
|
? { match: { 'sub.name': sub } }
|
||||||
: { bool: { must_not: { exists: { field: 'sub.name' } } } },
|
: { bool: { must_not: { exists: { field: 'sub.name' } } } },
|
||||||
|
me
|
||||||
|
? {
|
||||||
|
bool: {
|
||||||
|
should: [
|
||||||
|
{ match: { status: 'ACTIVE' } },
|
||||||
|
{ match: { userId: me.id } }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
: { match: { status: 'ACTIVE' } },
|
||||||
{
|
{
|
||||||
bool: {
|
bool: {
|
||||||
should: [
|
should: [
|
||||||
|
@ -395,6 +412,7 @@ export default {
|
||||||
const where = {
|
const where = {
|
||||||
where: {
|
where: {
|
||||||
subName: sub,
|
subName: sub,
|
||||||
|
status: 'ACTIVE',
|
||||||
OR: [{
|
OR: [{
|
||||||
maxBid: {
|
maxBid: {
|
||||||
gte: bid + 1000
|
gte: bid + 1000
|
||||||
|
@ -487,7 +505,7 @@ export default {
|
||||||
|
|
||||||
return await updateItem(parent, { id, data: { title, text } }, { me, models })
|
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) {
|
if (!me) {
|
||||||
throw new AuthenticationError('you must be logged in to create job')
|
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' })
|
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 = {
|
const data = {
|
||||||
title,
|
title,
|
||||||
text,
|
text,
|
||||||
|
@ -522,12 +549,23 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (id) {
|
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({
|
return await models.item.update({
|
||||||
where: { id: Number(id) },
|
where: { id: Number(id) },
|
||||||
data
|
data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// before creating job, check the sats
|
||||||
|
await checkSats()
|
||||||
return await models.item.create({
|
return await models.item.create({
|
||||||
data
|
data
|
||||||
})
|
})
|
||||||
|
@ -850,7 +888,7 @@ function nestComments (flat, parentId) {
|
||||||
export const SELECT =
|
export const SELECT =
|
||||||
`SELECT "Item".id, "Item".created_at as "createdAt", "Item".updated_at as "updatedAt", "Item".title,
|
`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".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'
|
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!
|
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!
|
||||||
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!
|
act(id: ID!, sats: Int): ItemActResult!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,5 +67,6 @@ export default gql`
|
||||||
prior: Int
|
prior: Int
|
||||||
maxBid: Int
|
maxBid: Int
|
||||||
sub: Sub
|
sub: Sub
|
||||||
|
status: String
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
|
@ -69,6 +69,7 @@ export function ItemJob ({ item, rank, children }) {
|
||||||
edit
|
edit
|
||||||
</a>
|
</a>
|
||||||
</Link>
|
</Link>
|
||||||
|
{item.status !== 'ACTIVE' && <span className='font-weight-bold text-danger'> {item.status}</span>}
|
||||||
</>}
|
</>}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -24,7 +24,6 @@
|
||||||
.case {
|
.case {
|
||||||
fill: #a5a5a5;
|
fill: #a5a5a5;
|
||||||
margin-right: .2rem;
|
margin-right: .2rem;
|
||||||
margin-left: .2rem;
|
|
||||||
margin-top: .2rem;
|
margin-top: .2rem;
|
||||||
padding: 0 2px;
|
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 TextareaAutosize from 'react-textarea-autosize'
|
||||||
import { InputGroup, Modal } from 'react-bootstrap'
|
import { InputGroup, Modal } from 'react-bootstrap'
|
||||||
import * as Yup from 'yup'
|
import * as Yup from 'yup'
|
||||||
|
@ -8,6 +8,7 @@ import AccordianItem from './accordian-item'
|
||||||
import styles from '../styles/post.module.css'
|
import styles from '../styles/post.module.css'
|
||||||
import { useLazyQuery, gql, useMutation } from '@apollo/client'
|
import { useLazyQuery, gql, useMutation } from '@apollo/client'
|
||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
|
import Link from 'next/link'
|
||||||
|
|
||||||
Yup.addMethod(Yup.string, 'or', function (schemas, msg) {
|
Yup.addMethod(Yup.string, 'or', function (schemas, msg) {
|
||||||
return this.test({
|
return this.test({
|
||||||
|
@ -41,8 +42,10 @@ export default function JobForm ({ item, sub }) {
|
||||||
}`,
|
}`,
|
||||||
{ fetchPolicy: 'network-only' })
|
{ fetchPolicy: 'network-only' })
|
||||||
const [upsertJob] = useMutation(gql`
|
const [upsertJob] = useMutation(gql`
|
||||||
mutation upsertJob($id: ID, $title: String!, $text: String!, $url: String!, $maxBid: Int!) {
|
mutation upsertJob($id: ID, $title: String!, $text: String!,
|
||||||
upsertJob(sub: "${sub.name}", id: $id title: $title, text: $text, url: $url, maxBid: $maxBid) {
|
$url: String!, $maxBid: Int!, $status: String) {
|
||||||
|
upsertJob(sub: "${sub.name}", id: $id title: $title, text: $text,
|
||||||
|
url: $url, maxBid: $maxBid, status: $status) {
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
}`
|
}`
|
||||||
|
@ -118,12 +121,21 @@ export default function JobForm ({ item, sub }) {
|
||||||
title: item?.title || '',
|
title: item?.title || '',
|
||||||
text: item?.text || '',
|
text: item?.text || '',
|
||||||
url: item?.url || '',
|
url: item?.url || '',
|
||||||
maxBid: item?.maxBid || sub.baseCost
|
maxBid: item?.maxBid || sub.baseCost,
|
||||||
|
stop: false,
|
||||||
|
start: false
|
||||||
}}
|
}}
|
||||||
schema={JobSchema}
|
schema={JobSchema}
|
||||||
storageKeyPrefix={storageKeyPrefix}
|
storageKeyPrefix={storageKeyPrefix}
|
||||||
onSubmit={(async ({ maxBid, ...values }) => {
|
onSubmit={(async ({ maxBid, stop, start, ...values }) => {
|
||||||
const variables = { sub: sub.name, maxBid: Number(maxBid), ...values }
|
let status
|
||||||
|
if (start) {
|
||||||
|
status = 'ACTIVE'
|
||||||
|
} else if (stop) {
|
||||||
|
status = 'STOPPED'
|
||||||
|
}
|
||||||
|
|
||||||
|
const variables = { sub: sub.name, maxBid: Number(maxBid), status, ...values }
|
||||||
if (item) {
|
if (item) {
|
||||||
variables.id = item.id
|
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>}
|
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>
|
<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>
|
<SubmitButton variant='secondary' className='mt-3'>{item ? 'save' : 'post'}</SubmitButton>
|
||||||
</Form>
|
</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
|
name
|
||||||
baseCost
|
baseCost
|
||||||
}
|
}
|
||||||
|
status
|
||||||
mine
|
mine
|
||||||
root {
|
root {
|
||||||
id
|
id
|
||||||
|
|
|
@ -17,6 +17,7 @@ const ITEM_SEARCH_FIELDS = gql`
|
||||||
sub {
|
sub {
|
||||||
name
|
name
|
||||||
}
|
}
|
||||||
|
status
|
||||||
maxBid
|
maxBid
|
||||||
upvotes
|
upvotes
|
||||||
sats
|
sats
|
||||||
|
|
Loading…
Reference in New Issue