delete
This commit is contained in:
parent
ed153b5199
commit
10ff3fa1c3
|
@ -159,7 +159,7 @@ export default {
|
|||
${SELECT}
|
||||
FROM "Item"
|
||||
WHERE "parentId" IS NULL AND "Item".created_at <= $1
|
||||
AND "pinId" IS NULL
|
||||
AND "pinId" IS NULL AND "deletedAt" IS NULL
|
||||
${topClause(when)}
|
||||
${await filterClause(me, models)}
|
||||
${await topOrderClause(sort, me, models)}
|
||||
|
@ -176,7 +176,7 @@ export default {
|
|||
${SELECT}
|
||||
FROM "Item"
|
||||
WHERE "parentId" IS NOT NULL
|
||||
AND "Item".created_at <= $1
|
||||
AND "Item".created_at <= $1 AND "deletedAt" IS NULL
|
||||
${topClause(when)}
|
||||
${await filterClause(me, models)}
|
||||
${await topOrderClause(sort, me, models)}
|
||||
|
@ -239,7 +239,7 @@ export default {
|
|||
${SELECT}
|
||||
FROM "Item"
|
||||
WHERE "parentId" IS NULL AND "Item".created_at <= $1
|
||||
AND "pinId" IS NULL
|
||||
AND "pinId" IS NULL AND "deletedAt" IS NULL
|
||||
${topClause(within)}
|
||||
${await filterClause(me, models)}
|
||||
${await topOrderByWeightedSats(me, models)}
|
||||
|
@ -288,7 +288,7 @@ export default {
|
|||
${SELECT}
|
||||
FROM "Item"
|
||||
WHERE "parentId" IS NULL AND "Item".created_at <= $1 AND "Item".created_at > $3
|
||||
AND "pinId" IS NULL AND NOT bio
|
||||
AND "pinId" IS NULL AND NOT bio AND "deletedAt" IS NULL
|
||||
${subClause(4)}
|
||||
${await filterClause(me, models)}
|
||||
${await newTimedOrderByWeightedSats(me, models, 1)}
|
||||
|
@ -301,7 +301,7 @@ export default {
|
|||
${SELECT}
|
||||
FROM "Item"
|
||||
WHERE "parentId" IS NULL AND "Item".created_at <= $1
|
||||
AND "pinId" IS NULL AND NOT bio
|
||||
AND "pinId" IS NULL AND NOT bio AND "deletedAt" IS NULL
|
||||
${subClause(3)}
|
||||
${await filterClause(me, models)}
|
||||
${await newTimedOrderByWeightedSats(me, models, 1)}
|
||||
|
@ -438,7 +438,7 @@ export default {
|
|||
comments = await models.$queryRaw(`
|
||||
${SELECT}
|
||||
FROM "Item"
|
||||
WHERE "parentId" IS NOT NULL
|
||||
WHERE "parentId" IS NOT NULL AND "deletedAt" IS NULL
|
||||
AND "Item".created_at <= $1
|
||||
${topClause(within)}
|
||||
${await filterClause(me, models)}
|
||||
|
@ -548,6 +548,28 @@ export default {
|
|||
},
|
||||
|
||||
Mutation: {
|
||||
deleteItem: async (parent, { id }, { me, models }) => {
|
||||
const old = await models.item.findUnique({ where: { id: Number(id) } })
|
||||
if (Number(old.userId) !== Number(me?.id)) {
|
||||
throw new AuthenticationError('item does not belong to you')
|
||||
}
|
||||
|
||||
const data = { deletedAt: new Date() }
|
||||
if (old.text) {
|
||||
data.text = '*deleted by author*'
|
||||
}
|
||||
if (old.title) {
|
||||
data.title = 'deleted by author'
|
||||
}
|
||||
if (old.url) {
|
||||
data.url = null
|
||||
}
|
||||
if (old.pollCost) {
|
||||
data.pollCost = null
|
||||
}
|
||||
|
||||
return await models.item.update({ where: { id: Number(id) }, data })
|
||||
},
|
||||
upsertLink: async (parent, args, { me, models }) => {
|
||||
const { id, ...data } = args
|
||||
data.url = ensureProtocol(data.url)
|
||||
|
@ -1040,7 +1062,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"."fwdUserId", "Item"."parentId", "Item"."pinId", "Item"."maxBid",
|
||||
"Item".company, "Item".location, "Item".remote,
|
||||
"Item".company, "Item".location, "Item".remote, "Item"."deletedAt",
|
||||
"Item"."subName", "Item".status, "Item"."uploadId", "Item"."pollCost",
|
||||
"Item".msats, "Item".ncomments, "Item"."commentMsats", "Item"."lastCommentAt", "Item"."weightedVotes",
|
||||
"Item"."weightedDownVotes", "Item".freebie, ltree2text("Item"."path") AS "path"`
|
||||
|
|
|
@ -31,6 +31,7 @@ export default gql`
|
|||
}
|
||||
|
||||
extend type Mutation {
|
||||
deleteItem(id: ID): Item
|
||||
upsertLink(id: ID, title: String!, url: String!, boost: Int, forward: String): Item!
|
||||
upsertDiscussion(id: ID, title: String!, text: String, boost: Int, forward: String): Item!
|
||||
upsertJob(id: ID, sub: ID!, title: String!, company: String!, location: String, remote: Boolean,
|
||||
|
@ -71,6 +72,7 @@ export default gql`
|
|||
id: ID!
|
||||
createdAt: String!
|
||||
updatedAt: String!
|
||||
deletedAt: String
|
||||
title: String
|
||||
searchTitle: String
|
||||
url: String
|
||||
|
|
|
@ -4,6 +4,8 @@ import { gql, useMutation } from '@apollo/client'
|
|||
import styles from './reply.module.css'
|
||||
import TextareaAutosize from 'react-textarea-autosize'
|
||||
import { EditFeeButton } from './fee-button'
|
||||
import { Button } from 'react-bootstrap'
|
||||
import Delete from './delete'
|
||||
|
||||
export const CommentSchema = Yup.object({
|
||||
text: Yup.string().required('required').trim()
|
||||
|
@ -54,10 +56,15 @@ export default function CommentEdit ({ comment, editThreshold, onSuccess, onCanc
|
|||
autoFocus
|
||||
required
|
||||
/>
|
||||
<EditFeeButton
|
||||
paidSats={comment.meSats}
|
||||
parentId={comment.parentId} text='save' ChildButton={SubmitButton} variant='secondary'
|
||||
/>
|
||||
<div className='d-flex justify-content-between'>
|
||||
<Delete itemId={comment.id} onDelete={onSuccess}>
|
||||
<Button variant='grey-medium'>delete</Button>
|
||||
</Delete>
|
||||
<EditFeeButton
|
||||
paidSats={comment.meSats}
|
||||
parentId={comment.parentId} text='save' ChildButton={SubmitButton} variant='secondary'
|
||||
/>
|
||||
</div>
|
||||
</Form>
|
||||
</div>
|
||||
)
|
||||
|
|
|
@ -19,6 +19,7 @@ import Flag from '../svgs/flag-fill.svg'
|
|||
import { Badge } from 'react-bootstrap'
|
||||
import { abbrNum } from '../lib/format'
|
||||
import Share from './share'
|
||||
import { DeleteDropdown } from './delete'
|
||||
|
||||
function Parent ({ item, rootText }) {
|
||||
const ParentFrag = () => (
|
||||
|
@ -138,7 +139,7 @@ export default function Comment ({
|
|||
{me && !item.meSats && !item.meDontLike && !item.mine && <DontLikeThis id={item.id} />}
|
||||
{(item.outlawed && <Link href='/outlawed'><a>{' '}<Badge className={itemStyles.newComment} variant={null}>OUTLAWED</Badge></a></Link>) ||
|
||||
(item.freebie && !item.mine && (me?.greeterMode) && <Link href='/freebie'><a>{' '}<Badge className={itemStyles.newComment} variant={null}>FREEBIE</Badge></a></Link>)}
|
||||
{canEdit &&
|
||||
{canEdit && !item.deletedAt &&
|
||||
<>
|
||||
<span> \ </span>
|
||||
<div
|
||||
|
@ -156,6 +157,7 @@ export default function Comment ({
|
|||
/>
|
||||
</div>
|
||||
</>}
|
||||
{mine && !canEdit && !item.deletedAt && <DeleteDropdown itemId={item.id} />}
|
||||
</div>
|
||||
{!includeParent && (collapse
|
||||
? <Eye
|
||||
|
|
|
@ -0,0 +1,103 @@
|
|||
import { useMutation } from '@apollo/client'
|
||||
import { gql } from 'apollo-server-micro'
|
||||
import { useState } from 'react'
|
||||
import { Alert, Button, Dropdown } from 'react-bootstrap'
|
||||
import { useShowModal } from './modal'
|
||||
import MoreIcon from '../svgs/more-fill.svg'
|
||||
|
||||
export default function Delete ({ itemId, children, onDelete }) {
|
||||
const showModal = useShowModal()
|
||||
|
||||
const [deleteItem] = useMutation(
|
||||
gql`
|
||||
mutation deleteItem($id: ID!) {
|
||||
deleteItem(id: $id) {
|
||||
text
|
||||
title
|
||||
url
|
||||
pollCost
|
||||
deletedAt
|
||||
}
|
||||
}`, {
|
||||
update (cache, { data: { deleteItem } }) {
|
||||
console.log(deleteItem)
|
||||
cache.modify({
|
||||
id: `Item:${itemId}`,
|
||||
fields: {
|
||||
text: () => deleteItem.text,
|
||||
title: () => deleteItem.title,
|
||||
url: () => deleteItem.url,
|
||||
pollCost: () => deleteItem.pollCost,
|
||||
deletedAt: () => deleteItem.deletedAt
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
)
|
||||
return (
|
||||
<span
|
||||
className='pointer' onClick={() => {
|
||||
showModal(onClose => {
|
||||
return (
|
||||
<DeleteConfirm
|
||||
onConfirm={async () => {
|
||||
const { error } = await deleteItem({ variables: { id: itemId } })
|
||||
if (error) {
|
||||
throw new Error({ message: error.toString() })
|
||||
}
|
||||
if (onDelete) {
|
||||
onDelete()
|
||||
}
|
||||
onClose()
|
||||
}}
|
||||
/>
|
||||
)
|
||||
})
|
||||
}}
|
||||
>{children}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
function DeleteConfirm ({ onConfirm }) {
|
||||
const [error, setError] = useState()
|
||||
|
||||
return (
|
||||
<>
|
||||
{error && <Alert variant='danger' onClose={() => setError(undefined)} dismissible>{error}</Alert>}
|
||||
<p className='font-weight-bolder'>Are you sure? This is a gone forever kind of delete.</p>
|
||||
<div className='d-flex justify-content-end'>
|
||||
<Button
|
||||
variant='danger' onClick={async () => {
|
||||
try {
|
||||
await onConfirm()
|
||||
} catch (e) {
|
||||
setError(e.message || e)
|
||||
}
|
||||
}}
|
||||
>delete
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export function DeleteDropdown (props) {
|
||||
return (
|
||||
<Dropdown className='pointer' as='span'>
|
||||
<Dropdown.Toggle variant='success' id='dropdown-basic' as='a'>
|
||||
<MoreIcon className='fill-grey ml-1' height={16} width={16} />
|
||||
</Dropdown.Toggle>
|
||||
|
||||
<Dropdown.Menu>
|
||||
<Delete {...props}>
|
||||
<Dropdown.Item
|
||||
className='text-center'
|
||||
>
|
||||
delete
|
||||
</Dropdown.Item>
|
||||
</Delete>
|
||||
</Dropdown.Menu>
|
||||
</Dropdown>
|
||||
)
|
||||
}
|
|
@ -10,6 +10,8 @@ import FeeButton, { EditFeeButton } from './fee-button'
|
|||
import { ITEM_FIELDS } from '../fragments/items'
|
||||
import AccordianItem from './accordian-item'
|
||||
import Item from './item'
|
||||
import Delete from './delete'
|
||||
import { Button } from 'react-bootstrap'
|
||||
|
||||
export function DiscussionForm ({
|
||||
item, editThreshold, titleLabel = 'title',
|
||||
|
@ -103,27 +105,34 @@ export function DiscussionForm ({
|
|||
{adv && <AdvPostForm edit={!!item} />}
|
||||
<div className='mt-3'>
|
||||
{item
|
||||
? <EditFeeButton
|
||||
paidSats={item.meSats}
|
||||
parentId={null} text='save' ChildButton={SubmitButton} variant='secondary'
|
||||
/>
|
||||
? (
|
||||
<div className='d-flex justify-content-between'>
|
||||
<Delete itemId={item.id} onDelete={() => router.push(`/items/${item.id}`)}>
|
||||
<Button variant='grey-medium'>delete</Button>
|
||||
</Delete>
|
||||
<EditFeeButton
|
||||
paidSats={item.meSats}
|
||||
parentId={null} text='save' ChildButton={SubmitButton} variant='secondary'
|
||||
/>
|
||||
</div>)
|
||||
: <FeeButton
|
||||
baseFee={1} parentId={null} text={buttonText}
|
||||
ChildButton={SubmitButton} variant='secondary'
|
||||
/>}
|
||||
</div>
|
||||
<div className={`mt-3 ${related.length > 0 ? '' : 'invisible'}`}>
|
||||
<AccordianItem
|
||||
header={<div style={{ fontWeight: 'bold', fontSize: '92%' }}>similar</div>}
|
||||
body={
|
||||
<div>
|
||||
{related.map((item, i) => (
|
||||
<Item item={item} key={item.id} />
|
||||
))}
|
||||
</div>
|
||||
{!item &&
|
||||
<div className={`mt-3 ${related.length > 0 ? '' : 'invisible'}`}>
|
||||
<AccordianItem
|
||||
header={<div style={{ fontWeight: 'bold', fontSize: '92%' }}>similar</div>}
|
||||
body={
|
||||
<div>
|
||||
{related.map((item, i) => (
|
||||
<Item item={item} key={item.id} />
|
||||
))}
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
/>
|
||||
</div>}
|
||||
</Form>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ import DontLikeThis from './dont-link-this'
|
|||
import Flag from '../svgs/flag-fill.svg'
|
||||
import Share from './share'
|
||||
import { abbrNum } from '../lib/format'
|
||||
import { DeleteDropdown } from './delete'
|
||||
|
||||
export function SearchTitle ({ title }) {
|
||||
return reactStringReplace(title, /:high\[([^\]]+)\]/g, (match, i) => {
|
||||
|
@ -123,7 +124,7 @@ export default function Item ({ item, rank, showFwdUser, toc, children }) {
|
|||
</Link>
|
||||
</>}
|
||||
</span>
|
||||
{canEdit &&
|
||||
{canEdit && !item.deletedAt &&
|
||||
<>
|
||||
<span> \ </span>
|
||||
<Link href={`/items/${item.id}/edit`} passHref>
|
||||
|
@ -139,6 +140,8 @@ export default function Item ({ item, rank, showFwdUser, toc, children }) {
|
|||
</a>
|
||||
</Link>
|
||||
</>}
|
||||
{mine && !canEdit && !item.position && !item.deletedAt &&
|
||||
<DeleteDropdown itemId={item.id} />}
|
||||
</div>
|
||||
{showFwdUser && item.fwdUser && <FwdUser user={item.fwdUser} />}
|
||||
</div>
|
||||
|
|
|
@ -10,6 +10,8 @@ import AccordianItem from './accordian-item'
|
|||
import { MAX_TITLE_LENGTH } from '../lib/constants'
|
||||
import { URL_REGEXP } from '../lib/url'
|
||||
import FeeButton, { EditFeeButton } from './fee-button'
|
||||
import Delete from './delete'
|
||||
import { Button } from 'react-bootstrap'
|
||||
|
||||
export function LinkForm ({ item, editThreshold }) {
|
||||
const router = useRouter()
|
||||
|
@ -138,42 +140,51 @@ export function LinkForm ({ item, editThreshold }) {
|
|||
<AdvPostForm edit={!!item} />
|
||||
<div className='mt-3'>
|
||||
{item
|
||||
? <EditFeeButton
|
||||
paidSats={item.meSats}
|
||||
parentId={null} text='save' ChildButton={SubmitButton} variant='secondary'
|
||||
/>
|
||||
? (
|
||||
<div className='d-flex justify-content-between'>
|
||||
<Delete itemId={item.id} onDelete={() => router.push(`/items/${item.id}`)}>
|
||||
<Button variant='grey-medium'>delete</Button>
|
||||
</Delete>
|
||||
<EditFeeButton
|
||||
paidSats={item.meSats}
|
||||
parentId={null} text='save' ChildButton={SubmitButton} variant='secondary'
|
||||
/>
|
||||
</div>)
|
||||
: <FeeButton
|
||||
baseFee={1} parentId={null} text='post'
|
||||
ChildButton={SubmitButton} variant='secondary'
|
||||
/>}
|
||||
</div>
|
||||
{dupesData?.dupes?.length > 0 &&
|
||||
<div className='mt-3'>
|
||||
<AccordianItem
|
||||
show
|
||||
headerColor='#c03221'
|
||||
header={<div style={{ fontWeight: 'bold', fontSize: '92%' }}>dupes</div>}
|
||||
body={
|
||||
<div>
|
||||
{dupesData.dupes.map((item, i) => (
|
||||
<Item item={item} key={item.id} />
|
||||
))}
|
||||
</div>
|
||||
{!item &&
|
||||
<>
|
||||
{dupesData?.dupes?.length > 0 &&
|
||||
<div className='mt-3'>
|
||||
<AccordianItem
|
||||
show
|
||||
headerColor='#c03221'
|
||||
header={<div style={{ fontWeight: 'bold', fontSize: '92%' }}>dupes</div>}
|
||||
body={
|
||||
<div>
|
||||
{dupesData.dupes.map((item, i) => (
|
||||
<Item item={item} key={item.id} />
|
||||
))}
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
</div>}
|
||||
<div className={`mt-3 ${related.length > 0 ? '' : 'invisible'}`}>
|
||||
<AccordianItem
|
||||
header={<div style={{ fontWeight: 'bold', fontSize: '92%' }}>similar</div>}
|
||||
body={
|
||||
<div>
|
||||
{related.map((item, i) => (
|
||||
<Item item={item} key={item.id} />
|
||||
))}
|
||||
</div>
|
||||
/>
|
||||
</div>}
|
||||
<div className={`mt-3 ${related.length > 0 ? '' : 'invisible'}`}>
|
||||
<AccordianItem
|
||||
header={<div style={{ fontWeight: 'bold', fontSize: '92%' }}>similar</div>}
|
||||
body={
|
||||
<div>
|
||||
{related.map((item, i) => (
|
||||
<Item item={item} key={item.id} />
|
||||
))}
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
/>
|
||||
</div>
|
||||
</>}
|
||||
</Form>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -83,7 +83,7 @@ function Notification ({ n }) {
|
|||
{n.sources.tips > 0 && <span>{(n.sources.comments > 0 || n.sources.posts > 0) && ' \\ '}{n.sources.tips} sats for tipping top content early</span>}
|
||||
</div>}
|
||||
<div className='pb-1' style={{ lineHeight: '140%' }}>
|
||||
SN distributes the sats it earns back to its best users daily. These sats come from <Link href='/~jobs' passHref><a>jobs</a></Link>, boosts, posting fees, and donations. You can see the daily rewards pool and make a donation <Link href='/rewards' passHref><a>here</a></Link>.
|
||||
SN distributes the sats it earns back to its best users daily. These sats come from <Link href='/~jobs' passHref><a>jobs</a></Link>, boosts, posting fees, and donations. You can see the daily rewards pool and make a donation <Link href='/rewards' passHref><a>here</a></Link>.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -7,6 +7,8 @@ import AdvPostForm, { AdvPostInitial, AdvPostSchema } from './adv-post-form'
|
|||
import { MAX_TITLE_LENGTH, MAX_POLL_CHOICE_LENGTH, MAX_POLL_NUM_CHOICES } from '../lib/constants'
|
||||
import TextareaAutosize from 'react-textarea-autosize'
|
||||
import FeeButton, { EditFeeButton } from './fee-button'
|
||||
import Delete from './delete'
|
||||
import { Button } from 'react-bootstrap'
|
||||
|
||||
export function PollForm ({ item, editThreshold }) {
|
||||
const router = useRouter()
|
||||
|
@ -94,10 +96,16 @@ export function PollForm ({ item, editThreshold }) {
|
|||
<AdvPostForm edit={!!item} />
|
||||
<div className='mt-3'>
|
||||
{item
|
||||
? <EditFeeButton
|
||||
paidSats={item.meSats}
|
||||
parentId={null} text='save' ChildButton={SubmitButton} variant='secondary'
|
||||
/>
|
||||
? (
|
||||
<div className='d-flex justify-content-between'>
|
||||
<Delete itemId={item.id} onDelete={() => router.push(`/items/${item.id}`)}>
|
||||
<Button variant='grey-medium'>delete</Button>
|
||||
</Delete>
|
||||
<EditFeeButton
|
||||
paidSats={item.meSats}
|
||||
parentId={null} text='save' ChildButton={SubmitButton} variant='secondary'
|
||||
/>
|
||||
</div>)
|
||||
: <FeeButton
|
||||
baseFee={1} parentId={null} text='post'
|
||||
ChildButton={SubmitButton} variant='secondary'
|
||||
|
|
|
@ -171,6 +171,8 @@ export default function UpVote ({ item, className }) {
|
|||
return `${sats} sat${sats > 1 ? 's' : ''}`
|
||||
}
|
||||
|
||||
const disabled = item?.mine || fwd2me || item?.deletedAt
|
||||
|
||||
const color = getColor(item?.meSats)
|
||||
return (
|
||||
<LightningConsumer>
|
||||
|
@ -182,7 +184,7 @@ export default function UpVote ({ item, className }) {
|
|||
if (!item) return
|
||||
|
||||
// we can't tip ourselves
|
||||
if (item?.mine || fwd2me) {
|
||||
if (disabled) {
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -197,7 +199,7 @@ export default function UpVote ({ item, className }) {
|
|||
if (!item) return
|
||||
|
||||
// we can't tip ourselves
|
||||
if (item?.mine || fwd2me) {
|
||||
if (disabled) {
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -234,9 +236,9 @@ export default function UpVote ({ item, className }) {
|
|||
})
|
||||
}
|
||||
>
|
||||
<ActionTooltip notForm disable={item?.mine || fwd2me} overlayText={overlayText()}>
|
||||
<ActionTooltip notForm disable={disabled} overlayText={overlayText()}>
|
||||
<div
|
||||
className={`${item?.mine || fwd2me ? styles.noSelfTips : ''}
|
||||
className={`${disabled ? styles.noSelfTips : ''}
|
||||
${styles.upvoteWrapper}`}
|
||||
>
|
||||
<UpBolt
|
||||
|
@ -245,7 +247,7 @@ export default function UpVote ({ item, className }) {
|
|||
className={
|
||||
`${styles.upvote}
|
||||
${className || ''}
|
||||
${item?.mine || fwd2me ? styles.noSelfTips : ''}
|
||||
${disabled ? styles.noSelfTips : ''}
|
||||
${item?.meSats ? styles.voted : ''}`
|
||||
}
|
||||
style={item?.meSats
|
||||
|
|
|
@ -5,6 +5,7 @@ export const COMMENT_FIELDS = gql`
|
|||
id
|
||||
parentId
|
||||
createdAt
|
||||
deletedAt
|
||||
text
|
||||
user {
|
||||
name
|
||||
|
|
|
@ -6,6 +6,7 @@ export const ITEM_FIELDS = gql`
|
|||
id
|
||||
parentId
|
||||
createdAt
|
||||
deletedAt
|
||||
title
|
||||
url
|
||||
user {
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
export function ignoreClick (e) {
|
||||
return e.target.onclick || // the target has a click handler
|
||||
// the target has an interactive parent
|
||||
e.target.matches(':where(.upvoteParent, form, textarea, button, a, input) :scope') ||
|
||||
e.target.matches(':where(.upvoteParent, .pointer, form, textarea, button, a, input) :scope') ||
|
||||
// the target is an interactive element
|
||||
['TEXTAREA', 'BUTTON', 'A', 'INPUT', 'FORM'].includes(e.target.tagName.toUpperCase()) ||
|
||||
// the target is an interactive element
|
||||
e.target.class === 'upvoteParent'
|
||||
e.target.class === 'upvoteParent' || e.target.class === 'pointer'
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ import { useRouter } from 'next/router'
|
|||
import Item from '../components/item'
|
||||
import Comment from '../components/comment'
|
||||
import React from 'react'
|
||||
import ItemJob from '../components/item-job'
|
||||
|
||||
export const getServerSideProps = getGetServerSideProps(WALLET_HISTORY)
|
||||
|
||||
|
@ -92,7 +93,7 @@ function Detail ({ fact }) {
|
|||
return (
|
||||
<>
|
||||
<div className={satusClass(fact.status)}>
|
||||
SN distributes the sats it earns back to its best users daily. These sats come from <Link href='/~jobs' passHref><a>jobs</a></Link>, boosts, posting fees, and donations. You can see the daily rewards pool and make a donation <Link href='/rewards' passHref><a>here</a></Link>.
|
||||
SN distributes the sats it earns back to its best users daily. These sats come from <Link href='/~jobs' passHref><a>jobs</a></Link>, boosts, posting fees, and donations. You can see the daily rewards pool and make a donation <Link href='/rewards' passHref><a>here</a></Link>.
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
|
@ -128,6 +129,9 @@ function Detail ({ fact }) {
|
|||
}
|
||||
|
||||
if (fact.item.title) {
|
||||
if (fact.item.isJob) {
|
||||
return <ItemJob className={styles.itemWrapper} item={fact.item} />
|
||||
}
|
||||
return <div className={styles.itemWrapper}><Item item={fact.item} /></div>
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
-- AlterTable
|
||||
ALTER TABLE "Item" ADD COLUMN "deletedAt" TIMESTAMP(3);
|
|
@ -210,6 +210,7 @@ model Item {
|
|||
id Int @id @default(autoincrement())
|
||||
createdAt DateTime @default(now()) @map(name: "created_at")
|
||||
updatedAt DateTime @default(now()) @updatedAt @map(name: "updated_at")
|
||||
deletedAt DateTime?
|
||||
title String?
|
||||
text String?
|
||||
url String?
|
||||
|
|
|
@ -53,7 +53,7 @@ function earn ({ models }) {
|
|||
FROM
|
||||
"Item"
|
||||
WHERE created_at >= now_utc() - interval '36 hours'
|
||||
AND "weightedVotes" > 0
|
||||
AND "weightedVotes" > 0 AND "deletedAt" IS NULL AND NOT bio
|
||||
) x
|
||||
WHERE x.percentile <= ${TOP_PERCENTILE}
|
||||
),
|
||||
|
|
Loading…
Reference in New Issue