undo zap/downzap and improve downzap ux
This commit is contained in:
parent
7e0da18878
commit
65744364f1
|
@ -141,7 +141,7 @@ async function itemQueryWithMeta ({ me, models, query, orderBy = '' }, ...args)
|
|||
return await models.$queryRawUnsafe(`
|
||||
SELECT "Item".*, to_jsonb(users.*) || jsonb_build_object('meMute', "Mute"."mutedId" IS NOT NULL) as user,
|
||||
COALESCE("ItemAct"."meMsats", 0) as "meMsats",
|
||||
COALESCE("ItemAct"."meDontLike", false) as "meDontLike", b."itemId" IS NOT NULL AS "meBookmark",
|
||||
COALESCE("ItemAct"."meDontLikeMsats", 0) as "meDontLikeMsats", b."itemId" IS NOT NULL AS "meBookmark",
|
||||
"ThreadSubscription"."itemId" IS NOT NULL AS "meSubscription", "ItemForward"."itemId" IS NOT NULL AS "meForward"
|
||||
FROM (
|
||||
${query}
|
||||
|
@ -153,7 +153,7 @@ async function itemQueryWithMeta ({ me, models, query, orderBy = '' }, ...args)
|
|||
LEFT JOIN "ItemForward" ON "ItemForward"."itemId" = "Item".id AND "ItemForward"."userId" = ${me.id}
|
||||
LEFT JOIN LATERAL (
|
||||
SELECT "itemId", sum("ItemAct".msats) FILTER (WHERE act = 'FEE' OR act = 'TIP') AS "meMsats",
|
||||
bool_or(act = 'DONT_LIKE_THIS') AS "meDontLike"
|
||||
sum("ItemAct".msats) FILTER (WHERE act = 'DONT_LIKE_THIS') AS "meDontLikeMsats"
|
||||
FROM "ItemAct"
|
||||
WHERE "ItemAct"."userId" = ${me.id}
|
||||
AND "ItemAct"."itemId" = "Item".id
|
||||
|
@ -805,7 +805,7 @@ export default {
|
|||
{ me, models, lnd, hash, hmac }
|
||||
)
|
||||
|
||||
return true
|
||||
return sats
|
||||
}
|
||||
},
|
||||
Item: {
|
||||
|
@ -933,11 +933,16 @@ export default {
|
|||
|
||||
return (msats && msatsToSats(msats)) || 0
|
||||
},
|
||||
meDontLike: async (item, args, { me, models }) => {
|
||||
meDontLikeSats: async (item, args, { me, models }) => {
|
||||
if (!me) return false
|
||||
if (typeof item.meDontLike !== 'undefined') return item.meDontLike
|
||||
if (typeof item.meMsats !== 'undefined') {
|
||||
return msatsToSats(item.meDontLikeMsats)
|
||||
}
|
||||
|
||||
const dontLike = await models.itemAct.findFirst({
|
||||
const { _sum: { msats } } = await models.itemAct.aggregate({
|
||||
_sum: {
|
||||
msats: true
|
||||
},
|
||||
where: {
|
||||
itemId: Number(item.id),
|
||||
userId: me.id,
|
||||
|
@ -945,7 +950,7 @@ export default {
|
|||
}
|
||||
})
|
||||
|
||||
return !!dontLike
|
||||
return (msats && msatsToSats(msats)) || 0
|
||||
},
|
||||
meBookmark: async (item, args, { me, models }) => {
|
||||
if (!me) return false
|
||||
|
|
|
@ -34,7 +34,7 @@ export default gql`
|
|||
upsertPoll(id: ID, sub: String, title: String!, text: String, options: [String!]!, boost: Int, forward: [ItemForwardInput], hash: String, hmac: String): Item!
|
||||
updateNoteId(id: ID!, noteId: String!): Item!
|
||||
upsertComment(id:ID, text: String!, parentId: ID, hash: String, hmac: String): Item!
|
||||
dontLikeThis(id: ID!, sats: Int, hash: String, hmac: String): Boolean!
|
||||
dontLikeThis(id: ID!, sats: Int, hash: String, hmac: String): Int!
|
||||
act(id: ID!, sats: Int, hash: String, hmac: String): ItemActResult!
|
||||
pollVote(id: ID!, hash: String, hmac: String): ID!
|
||||
}
|
||||
|
@ -90,7 +90,7 @@ export default gql`
|
|||
lastCommentAt: Date
|
||||
upvotes: Int!
|
||||
meSats: Int!
|
||||
meDontLike: Boolean!
|
||||
meDontLikeSats: Int!
|
||||
meBookmark: Boolean!
|
||||
meSubscription: Boolean!
|
||||
meForward: Boolean
|
||||
|
|
|
@ -14,7 +14,6 @@ import { ignoreClick } from '../lib/clicks'
|
|||
import PayBounty from './pay-bounty'
|
||||
import BountyIcon from '../svgs/bounty-bag.svg'
|
||||
import ActionTooltip from './action-tooltip'
|
||||
import Flag from '../svgs/flag-fill.svg'
|
||||
import { numWithUnits } from '../lib/format'
|
||||
import Share from './share'
|
||||
import ItemInfo from './item-info'
|
||||
|
@ -22,6 +21,7 @@ import Badge from 'react-bootstrap/Badge'
|
|||
import { RootProvider, useRoot } from './root'
|
||||
import { useMe } from './me'
|
||||
import { useQuoteReply } from './use-quote-reply'
|
||||
import { DownZap } from './dont-link-this'
|
||||
|
||||
function Parent ({ item, rootText }) {
|
||||
const root = useRoot()
|
||||
|
@ -146,8 +146,8 @@ export default function Comment ({
|
|||
onTouchStart={() => ref.current.classList.add('outline-new-comment-unset')}
|
||||
>
|
||||
<div className={`${itemStyles.item} ${styles.item}`}>
|
||||
{item.meDontLike
|
||||
? <Flag width={24} height={24} className={styles.dontLike} />
|
||||
{item.meDontLikeSats > item.meSats
|
||||
? <DownZap width={24} height={24} className={styles.dontLike} id={item.id} meDontLikeSats={item.meDontLikeSats} />
|
||||
: <UpVote item={item} className={styles.upvote} pendingSats={pendingSats} setPendingSats={setPendingSats} />}
|
||||
<div className={`${itemStyles.hunk} ${styles.hunk}`}>
|
||||
<div className='d-flex align-items-center'>
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
padding: 2px;
|
||||
margin-left: 1px;
|
||||
margin-top: 9px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.text {
|
||||
|
|
|
@ -4,8 +4,23 @@ import { useShowModal } from './modal'
|
|||
import { useToast } from './toast'
|
||||
import ItemAct from './item-act'
|
||||
import AccordianItem from './accordian-item'
|
||||
import Flag from '../svgs/flag-fill.svg'
|
||||
import { useMemo } from 'react'
|
||||
import getColor from '../lib/rainbow'
|
||||
|
||||
export default function DontLikeThisDropdownItem ({ id }) {
|
||||
export function DownZap ({ id, meDontLikeSats, ...props }) {
|
||||
const style = useMemo(() => (meDontLikeSats
|
||||
? {
|
||||
fill: getColor(meDontLikeSats),
|
||||
filter: `drop-shadow(0 0 6px ${getColor(meDontLikeSats)}90)`
|
||||
}
|
||||
: undefined), [meDontLikeSats])
|
||||
return (
|
||||
<DownZapper id={id} As={({ ...oprops }) => <Flag {...props} {...oprops} style={style} />} />
|
||||
)
|
||||
}
|
||||
|
||||
function DownZapper ({ id, As, children }) {
|
||||
const toaster = useToast()
|
||||
const showModal = useShowModal()
|
||||
|
||||
|
@ -14,12 +29,12 @@ export default function DontLikeThisDropdownItem ({ id }) {
|
|||
mutation dontLikeThis($id: ID!, $sats: Int, $hash: String, $hmac: String) {
|
||||
dontLikeThis(id: $id, sats: $sats, hash: $hash, hmac: $hmac)
|
||||
}`, {
|
||||
update (cache) {
|
||||
update (cache, { data: { dontLikeThis } }) {
|
||||
cache.modify({
|
||||
id: `Item:${id}`,
|
||||
fields: {
|
||||
meDontLike () {
|
||||
return true
|
||||
meDontLikeSats (existingSats = 0) {
|
||||
return existingSats + dontLikeThis
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -28,7 +43,7 @@ export default function DontLikeThisDropdownItem ({ id }) {
|
|||
)
|
||||
|
||||
return (
|
||||
<Dropdown.Item
|
||||
<As
|
||||
onClick={async () => {
|
||||
try {
|
||||
showModal(onClose =>
|
||||
|
@ -53,7 +68,18 @@ export default function DontLikeThisDropdownItem ({ id }) {
|
|||
}
|
||||
}}
|
||||
>
|
||||
<span className='text-danger'>downzap</span>
|
||||
</Dropdown.Item>
|
||||
{children}
|
||||
</As>
|
||||
)
|
||||
}
|
||||
|
||||
export default function DontLikeThisDropdownItem ({ id }) {
|
||||
return (
|
||||
<DownZapper
|
||||
As={Dropdown.Item}
|
||||
id={id}
|
||||
>
|
||||
<span className='text-danger'>downzap</span>
|
||||
</DownZapper>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ import Hat from './hat'
|
|||
import { AD_USER_ID } from '../lib/constants'
|
||||
import ActionDropdown from './action-dropdown'
|
||||
import MuteDropdownItem from './mute'
|
||||
import { DropdownItemUpVote } from './upvote'
|
||||
|
||||
export default function ItemInfo ({
|
||||
item, pendingSats, full, commentsText = 'comments',
|
||||
|
@ -39,7 +40,7 @@ export default function ItemInfo ({
|
|||
}, [item])
|
||||
|
||||
useEffect(() => {
|
||||
if (item) setMeTotalSats(item.meSats + item.meAnonSats + pendingSats)
|
||||
if (item) setMeTotalSats((item.meSats || 0) + (item.meAnonSats || 0) + (pendingSats || 0))
|
||||
}, [item?.meSats, item?.meAnonSats, pendingSats])
|
||||
|
||||
return (
|
||||
|
@ -52,7 +53,9 @@ export default function ItemInfo ({
|
|||
unitPlural: 'stackers'
|
||||
})} ${item.mine
|
||||
? `\\ ${numWithUnits(item.meSats, { abbreviate: false })} to post`
|
||||
: `(${numWithUnits(meTotalSats, { abbreviate: false })} from me)`} `}
|
||||
: `(${numWithUnits(meTotalSats, { abbreviate: false })}${item.meDontLikeSats
|
||||
? ` & ${numWithUnits(item.meDontLikeSats, { abbreviate: false, unitSingular: 'downsat', unitPlural: 'downsats' })}`
|
||||
: ''} from me)`} `}
|
||||
>
|
||||
{numWithUnits(item.sats + pendingSats)}
|
||||
</span>
|
||||
|
@ -143,8 +146,11 @@ export default function ItemInfo ({
|
|||
<Link href={`/items/${item.id}/ots`} className='text-reset dropdown-item'>
|
||||
opentimestamp
|
||||
</Link>}
|
||||
{me && !item.meSats && !item.position &&
|
||||
!item.mine && !item.deletedAt && <DontLikeThisDropdownItem id={item.id} />}
|
||||
{me && !item.position &&
|
||||
!item.mine && !item.deletedAt &&
|
||||
(item.meDontLikeSats > meTotalSats
|
||||
? <DropdownItemUpVote item={item} />
|
||||
: <DontLikeThisDropdownItem id={item.id} />)}
|
||||
{me && item?.noteId && (
|
||||
<Dropdown.Item onClick={() => window.open(`https://nostr.com/${item.noteId}`, '_blank', 'noopener')}>
|
||||
nostr note
|
||||
|
|
|
@ -8,7 +8,6 @@ import reactStringReplace from 'react-string-replace'
|
|||
import PollIcon from '../svgs/bar-chart-horizontal-fill.svg'
|
||||
import BountyIcon from '../svgs/bounty-bag.svg'
|
||||
import ActionTooltip from './action-tooltip'
|
||||
import Flag from '../svgs/flag-fill.svg'
|
||||
import ImageIcon from '../svgs/image-fill.svg'
|
||||
import { numWithUnits } from '../lib/format'
|
||||
import ItemInfo from './item-info'
|
||||
|
@ -17,6 +16,7 @@ import { commentsViewedAt } from '../lib/new-comments'
|
|||
import { useRouter } from 'next/router'
|
||||
import { Badge } from 'react-bootstrap'
|
||||
import AdIcon from '../svgs/advertisement-fill.svg'
|
||||
import { DownZap } from './dont-link-this'
|
||||
|
||||
export function SearchTitle ({ title }) {
|
||||
return reactStringReplace(title, /\*\*\*([^*]+)\*\*\*/g, (match, i) => {
|
||||
|
@ -43,8 +43,8 @@ export default function Item ({ item, rank, belowTitle, right, full, children, s
|
|||
<div className={`${styles.item} ${siblingComments ? 'pt-3' : ''}`}>
|
||||
{item.position
|
||||
? <Pin width={24} height={24} className={styles.pin} />
|
||||
: item.meDontLike
|
||||
? <Flag width={24} height={24} className={styles.dontLike} />
|
||||
: item.meDontLikeSats > item.meSats
|
||||
? <DownZap width={24} height={24} className={styles.dontLike} id={item.id} meDontLikeSats={item.meDontLikeSats} />
|
||||
: Number(item.user?.id) === AD_USER_ID
|
||||
? <AdIcon width={24} height={24} className={styles.ad} />
|
||||
: <UpVote item={item} className={styles.upvote} pendingSats={pendingSats} setPendingSats={setPendingSats} />}
|
||||
|
|
|
@ -65,6 +65,7 @@ a.title:visited {
|
|||
margin-right: .35rem;
|
||||
margin-left: -.2rem;
|
||||
flex-shrink: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.case {
|
||||
|
|
|
@ -4,7 +4,7 @@ import { gql, useMutation } from '@apollo/client'
|
|||
import ActionTooltip from './action-tooltip'
|
||||
import ItemAct from './item-act'
|
||||
import { useMe } from './me'
|
||||
import Rainbow from '../lib/rainbow'
|
||||
import getColor from '../lib/rainbow'
|
||||
import { useCallback, useMemo, useRef, useState } from 'react'
|
||||
import LongPressable from 'react-longpressable'
|
||||
import Overlay from 'react-bootstrap/Overlay'
|
||||
|
@ -15,17 +15,7 @@ import { numWithUnits } from '../lib/format'
|
|||
import { payOrLoginError, useInvoiceModal } from './invoice'
|
||||
import useDebounceCallback from './use-debounce-callback'
|
||||
import { useToast } from './toast'
|
||||
|
||||
const getColor = (meSats) => {
|
||||
if (!meSats || meSats <= 10) {
|
||||
return 'var(--bs-secondary)'
|
||||
}
|
||||
|
||||
const idx = Math.min(
|
||||
Math.floor((Math.log(meSats) / Math.log(10000)) * (Rainbow.length - 1)),
|
||||
Rainbow.length - 1)
|
||||
return Rainbow[idx]
|
||||
}
|
||||
import { Dropdown } from 'react-bootstrap'
|
||||
|
||||
const UpvotePopover = ({ target, show, handleClose }) => {
|
||||
const me = useMe()
|
||||
|
@ -66,6 +56,72 @@ const TipPopover = ({ target, show, handleClose }) => (
|
|||
</Overlay>
|
||||
)
|
||||
|
||||
function useAct ({ item, setVoteShow = () => {}, setTipShow = () => {} }) {
|
||||
const me = useMe()
|
||||
return useMutation(
|
||||
gql`
|
||||
mutation act($id: ID!, $sats: Int!, $hash: String, $hmac: String) {
|
||||
act(id: $id, sats: $sats, hash: $hash, hmac: $hmac) {
|
||||
sats
|
||||
}
|
||||
}`, {
|
||||
update (cache, { data: { act: { sats } } }) {
|
||||
cache.modify({
|
||||
id: `Item:${item.id}`,
|
||||
fields: {
|
||||
sats (existingSats = 0) {
|
||||
return existingSats + sats
|
||||
},
|
||||
meSats: me
|
||||
? (existingSats = 0) => {
|
||||
if (sats <= me.privates?.sats) {
|
||||
if (existingSats === 0) {
|
||||
setVoteShow(true)
|
||||
} else {
|
||||
setTipShow(true)
|
||||
}
|
||||
}
|
||||
|
||||
return existingSats + sats
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
})
|
||||
|
||||
// update all ancestors
|
||||
item.path.split('.').forEach(id => {
|
||||
if (Number(id) === Number(item.id)) return
|
||||
cache.modify({
|
||||
id: `Item:${id}`,
|
||||
fields: {
|
||||
commentSats (existingCommentSats = 0) {
|
||||
return existingCommentSats + sats
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
export function DropdownItemUpVote ({ item }) {
|
||||
const showModal = useShowModal()
|
||||
|
||||
const [act] = useAct({ item })
|
||||
|
||||
return (
|
||||
<Dropdown.Item
|
||||
onClick={async () => {
|
||||
showModal(onClose =>
|
||||
<ItemAct onClose={onClose} itemId={item.id} act={act} />)
|
||||
}}
|
||||
>
|
||||
<span className='text-success'>zap</span>
|
||||
</Dropdown.Item>
|
||||
)
|
||||
}
|
||||
|
||||
export default function UpVote ({ item, className, pendingSats, setPendingSats }) {
|
||||
const showModal = useShowModal()
|
||||
const [voteShow, _setVoteShow] = useState(false)
|
||||
|
@ -110,51 +166,8 @@ export default function UpVote ({ item, className, pendingSats, setPendingSats }
|
|||
}
|
||||
}, [me, tipShow, setWalkthrough])
|
||||
|
||||
const [act] = useMutation(
|
||||
gql`
|
||||
mutation act($id: ID!, $sats: Int!, $hash: String, $hmac: String) {
|
||||
act(id: $id, sats: $sats, hash: $hash, hmac: $hmac) {
|
||||
sats
|
||||
}
|
||||
}`, {
|
||||
update (cache, { data: { act: { sats } } }) {
|
||||
cache.modify({
|
||||
id: `Item:${item.id}`,
|
||||
fields: {
|
||||
sats (existingSats = 0) {
|
||||
return existingSats + sats
|
||||
},
|
||||
meSats: me
|
||||
? (existingSats = 0) => {
|
||||
if (sats <= me.privates?.sats) {
|
||||
if (existingSats === 0) {
|
||||
setVoteShow(true)
|
||||
} else {
|
||||
setTipShow(true)
|
||||
}
|
||||
}
|
||||
const [act] = useAct({ item, setVoteShow, setTipShow })
|
||||
|
||||
return existingSats + sats
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
})
|
||||
|
||||
// update all ancestors
|
||||
item.path.split('.').forEach(id => {
|
||||
if (Number(id) === Number(item.id)) return
|
||||
cache.modify({
|
||||
id: `Item:${id}`,
|
||||
fields: {
|
||||
commentSats (existingCommentSats = 0) {
|
||||
return existingCommentSats + sats
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
)
|
||||
const showInvoiceModal = useInvoiceModal(
|
||||
async ({ hash, hmac }, { variables }) => {
|
||||
await act({ variables: { ...variables, hash, hmac } })
|
||||
|
|
|
@ -21,7 +21,7 @@ export const COMMENT_FIELDS = gql`
|
|||
freedFreebie
|
||||
boost
|
||||
meSats
|
||||
meDontLike
|
||||
meDontLikeSats
|
||||
meBookmark
|
||||
meSubscription
|
||||
outlawed
|
||||
|
|
|
@ -28,7 +28,7 @@ export const ITEM_FIELDS = gql`
|
|||
path
|
||||
upvotes
|
||||
meSats
|
||||
meDontLike
|
||||
meDontLikeSats
|
||||
meBookmark
|
||||
meSubscription
|
||||
meForward
|
||||
|
|
|
@ -1,4 +1,15 @@
|
|||
export default [
|
||||
export default function getColor (meSats) {
|
||||
if (!meSats || meSats <= 10) {
|
||||
return 'var(--bs-secondary)'
|
||||
}
|
||||
|
||||
const idx = Math.min(
|
||||
Math.floor((Math.log(meSats) / Math.log(10000)) * (Rainbow.length - 1)),
|
||||
Rainbow.length - 1)
|
||||
return Rainbow[idx]
|
||||
}
|
||||
|
||||
const Rainbow = [
|
||||
'#f6911d',
|
||||
'#f6921e',
|
||||
'#f6931f',
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
CREATE OR REPLACE FUNCTION item_comments_zaprank_with_me(_item_id int, _global_seed int, _me_id int, _level int, _where text, _order_by text)
|
||||
RETURNS jsonb
|
||||
LANGUAGE plpgsql VOLATILE PARALLEL SAFE AS
|
||||
$$
|
||||
DECLARE
|
||||
result jsonb;
|
||||
BEGIN
|
||||
IF _level < 1 THEN
|
||||
RETURN '[]'::jsonb;
|
||||
END IF;
|
||||
|
||||
EXECUTE 'CREATE TEMP TABLE IF NOT EXISTS t_item ON COMMIT DROP AS'
|
||||
|| ' SELECT "Item".*, "Item".created_at at time zone ''UTC'' AS "createdAt", "Item".updated_at at time zone ''UTC'' AS "updatedAt", '
|
||||
|| ' to_jsonb(users.*) || jsonb_build_object(''meMute'', "Mute"."mutedId" IS NOT NULL) AS user, '
|
||||
|| ' COALESCE("ItemAct"."meMsats", 0) AS "meMsats", COALESCE("ItemAct"."meDontLikeMsats", 0) AS "meDontLikeMsats", '
|
||||
|| ' "Bookmark"."itemId" IS NOT NULL AS "meBookmark", "ThreadSubscription"."itemId" IS NOT NULL AS "meSubscription", '
|
||||
|| ' GREATEST(g.tf_hot_score, l.tf_hot_score) AS personal_hot_score, GREATEST(g.tf_top_score, l.tf_top_score) AS personal_top_score '
|
||||
|| ' FROM "Item" '
|
||||
|| ' JOIN users ON users.id = "Item"."userId" '
|
||||
|| ' LEFT JOIN "Mute" ON "Mute"."muterId" = $5 AND "Mute"."mutedId" = "Item"."userId"'
|
||||
|| ' LEFT JOIN "Bookmark" ON "Bookmark"."userId" = $5 AND "Bookmark"."itemId" = "Item".id '
|
||||
|| ' LEFT JOIN "ThreadSubscription" ON "ThreadSubscription"."userId" = $5 AND "ThreadSubscription"."itemId" = "Item".id '
|
||||
|| ' LEFT JOIN LATERAL ( '
|
||||
|| ' SELECT "itemId", sum("ItemAct".msats) FILTER (WHERE act = ''FEE'' OR act = ''TIP'') AS "meMsats", '
|
||||
|| ' sum("ItemAct".msats) FILTER (WHERE act = ''DONT_LIKE_THIS'') AS "meDontLikeMsats" '
|
||||
|| ' FROM "ItemAct" '
|
||||
|| ' WHERE "ItemAct"."userId" = $5 '
|
||||
|| ' AND "ItemAct"."itemId" = "Item".id '
|
||||
|| ' GROUP BY "ItemAct"."itemId" '
|
||||
|| ' ) "ItemAct" ON true '
|
||||
|| ' LEFT JOIN zap_rank_personal_view g ON g."viewerId" = $6 AND g.id = "Item".id '
|
||||
|| ' LEFT JOIN zap_rank_personal_view l ON l."viewerId" = $5 AND l.id = g.id '
|
||||
|| ' WHERE "Item".path <@ (SELECT path FROM "Item" WHERE id = $1) ' || _where || ' '
|
||||
USING _item_id, _level, _where, _order_by, _me_id, _global_seed;
|
||||
|
||||
EXECUTE ''
|
||||
|| 'SELECT COALESCE(jsonb_agg(sub), ''[]''::jsonb) AS comments '
|
||||
|| 'FROM ( '
|
||||
|| ' SELECT "Item".*, item_comments_zaprank_with_me("Item".id, $6, $5, $2 - 1, $3, $4) AS comments '
|
||||
|| ' FROM t_item "Item" '
|
||||
|| ' WHERE "Item"."parentId" = $1 '
|
||||
|| _order_by
|
||||
|| ' ) sub'
|
||||
INTO result USING _item_id, _level, _where, _order_by, _me_id, _global_seed;
|
||||
|
||||
RETURN result;
|
||||
END
|
||||
$$;
|
||||
|
||||
CREATE OR REPLACE FUNCTION item_comments(_item_id int, _level int, _where text, _order_by text)
|
||||
RETURNS jsonb
|
||||
LANGUAGE plpgsql VOLATILE PARALLEL SAFE AS
|
||||
$$
|
||||
DECLARE
|
||||
result jsonb;
|
||||
BEGIN
|
||||
IF _level < 1 THEN
|
||||
RETURN '[]'::jsonb;
|
||||
END IF;
|
||||
|
||||
EXECUTE 'CREATE TEMP TABLE IF NOT EXISTS t_item ON COMMIT DROP AS'
|
||||
|| ' SELECT "Item".*, "Item".created_at at time zone ''UTC'' AS "createdAt", "Item".updated_at at time zone ''UTC'' AS "updatedAt", '
|
||||
|| ' to_jsonb(users.*) as user '
|
||||
|| ' FROM "Item" '
|
||||
|| ' JOIN users ON users.id = "Item"."userId" '
|
||||
|| ' WHERE "Item".path <@ (SELECT path FROM "Item" WHERE id = $1) ' || _where
|
||||
USING _item_id, _level, _where, _order_by;
|
||||
|
||||
EXECUTE ''
|
||||
|| 'SELECT COALESCE(jsonb_agg(sub), ''[]''::jsonb) AS comments '
|
||||
|| 'FROM ( '
|
||||
|| ' SELECT "Item".*, item_comments("Item".id, $2 - 1, $3, $4) AS comments '
|
||||
|| ' FROM t_item "Item"'
|
||||
|| ' WHERE "Item"."parentId" = $1 '
|
||||
|| _order_by
|
||||
|| ' ) sub'
|
||||
INTO result USING _item_id, _level, _where, _order_by;
|
||||
RETURN result;
|
||||
END
|
||||
$$;
|
Loading…
Reference in New Issue