Skip to content

Commit e797011

Browse files
committed
fix: don't show unpaid comments; cleanup: compact cache merge/dedupe, queue comments via state
1 parent 907c71d commit e797011

File tree

2 files changed

+38
-51
lines changed

2 files changed

+38
-51
lines changed

api/resolvers/item.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -742,8 +742,11 @@ export default {
742742
${SELECT}
743743
FROM "Item"
744744
-- comments can be nested, so we need to get all comments that are descendants of the root
745-
WHERE "Item".path <@ (SELECT path FROM "Item" WHERE id = $1)
746-
AND "Item"."created_at" > $2
745+
${whereClause(
746+
'"Item".path <@ (SELECT path FROM "Item" WHERE id = $1)',
747+
activeOrMine(me),
748+
'"Item"."created_at" > $2'
749+
)}
747750
ORDER BY "Item"."created_at" ASC`
748751
}, Number(rootId), after)
749752

components/use-live-comments.js

Lines changed: 33 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -2,30 +2,42 @@ import { useQuery, useApolloClient } from '@apollo/client'
22
import { SSR } from '../lib/constants'
33
import { GET_NEW_COMMENTS, COMMENT_WITH_NEW } from '../fragments/comments'
44
import { ITEM_FULL } from '../fragments/items'
5-
import { useCallback, useEffect, useState, useRef } from 'react'
5+
import { useCallback, useEffect, useState } from 'react'
66
import styles from './comment.module.css'
77

88
const POLL_INTERVAL = 1000 * 10 // 10 seconds
99

10+
// the item query is used to update the item's newComments field
1011
function itemUpdateQuery (client, id, sort, fn) {
1112
client.cache.updateQuery({
1213
query: ITEM_FULL,
1314
variables: sort === 'top' ? { id } : { id, sort }
14-
}, (data) => fn(data))
15+
}, (data) => {
16+
if (!data) return data
17+
return { item: fn(data.item) }
18+
})
1519
}
1620

1721
function commentUpdateFragment (client, id, fn) {
1822
client.cache.updateFragment({
1923
id: `Item:${id}`,
2024
fragment: COMMENT_WITH_NEW,
2125
fragmentName: 'CommentWithNew'
22-
}, (data) => fn(data))
26+
}, (data) => {
27+
if (!data) return data
28+
return { ...data, ...fn(data) }
29+
})
30+
}
31+
32+
function dedupeComments (existing = [], incoming = []) {
33+
const existingIds = new Set(existing.map(c => c.id))
34+
return [...incoming.filter(c => !existingIds.has(c.id)), ...existing]
2335
}
2436

2537
export default function useLiveComments (rootId, after, sort) {
2638
const client = useApolloClient()
2739
const [latest, setLatest] = useState(after)
28-
const queuedCommentsRef = useRef([])
40+
const [queue, setQueue] = useState([])
2941

3042
const { data } = useQuery(GET_NEW_COMMENTS, SSR
3143
? {}
@@ -39,11 +51,11 @@ export default function useLiveComments (rootId, after, sort) {
3951

4052
// live comments can be orphans if the parent comment is not in the cache
4153
// queue them up and retry later, when the parent decides they want the children.
42-
const allComments = [...queuedCommentsRef.current, ...data.newComments.comments]
54+
const allComments = [...queue, ...data.newComments.comments]
4355
const { queuedComments } = cacheNewComments(client, rootId, allComments, sort)
4456

45-
// keep the queued comments in the ref for the next poll
46-
queuedCommentsRef.current = queuedComments
57+
// keep the queued comments for the next poll
58+
setQueue(queuedComments)
4759

4860
// update latest timestamp to the latest comment created at
4961
setLatest(prevLatest => getLatestCommentCreatedAt(data.newComments.comments, prevLatest))
@@ -61,10 +73,7 @@ function cacheNewComments (client, rootId, newComments, sort) {
6173
// if the comment is a top level comment, update the item
6274
if (topLevel) {
6375
console.log('topLevel', topLevel)
64-
itemUpdateQuery(client, rootId, sort, (data) => {
65-
if (!data) return data
66-
return { item: mergeNewComment(data?.item, newComment) }
67-
})
76+
itemUpdateQuery(client, rootId, sort, (data) => mergeNewComment(data, newComment))
6877
} else {
6978
// check if parent exists in cache before attempting update
7079
const parentExists = client.cache.readFragment({
@@ -76,10 +85,7 @@ function cacheNewComments (client, rootId, newComments, sort) {
7685
if (parentExists) {
7786
// if the comment is a reply, update the parent comment
7887
console.log('reply', parentId)
79-
commentUpdateFragment(client, parentId, (data) => {
80-
if (!data) return data
81-
return mergeNewComment(data, newComment)
82-
})
88+
commentUpdateFragment(client, parentId, (data) => mergeNewComment(data, newComment))
8389
} else {
8490
// parent not in cache, queue for retry
8591
queuedComments.push(newComment)
@@ -93,6 +99,7 @@ function cacheNewComments (client, rootId, newComments, sort) {
9399
// merge new comment into item's newComments
94100
// if the new comment is already in item's newComments or existing comments, do nothing
95101
function mergeNewComment (item, newComment) {
102+
console.log('mergeNewComment', item, newComment)
96103
const existingNewComments = item.newComments || []
97104
const existingComments = item.comments?.comments || []
98105

@@ -119,47 +126,24 @@ export function ShowNewComments ({ newComments = [], itemId, topLevel = false, s
119126
const client = useApolloClient()
120127

121128
const showNewComments = useCallback(() => {
129+
const payload = (data) => {
130+
if (!data) return data
131+
return {
132+
...data,
133+
comments: { ...data.comments, comments: dedupeComments(data.comments.comments, newComments) },
134+
newComments: []
135+
}
136+
}
137+
122138
if (topLevel) {
123139
console.log('topLevel', topLevel)
124-
itemUpdateQuery(client, itemId, sort, (data) => {
125-
console.log('data', data)
126-
if (!data) return data
127-
const { item } = data
128-
129-
return {
130-
item: {
131-
...item,
132-
comments: injectComments(item.comments, newComments),
133-
newComments: []
134-
}
135-
}
136-
})
140+
itemUpdateQuery(client, itemId, sort, payload)
137141
} else {
138142
console.log('reply', itemId)
139-
commentUpdateFragment(client, itemId, (data) => {
140-
console.log('data', data)
141-
if (!data) return data
142-
143-
return {
144-
...data,
145-
comments: injectComments(data.comments, newComments),
146-
newComments: []
147-
}
148-
})
143+
commentUpdateFragment(client, itemId, payload)
149144
}
150145
}, [client, itemId, newComments, topLevel, sort])
151146

152-
// inject new comments into existing comments
153-
// if the new comment is already in existing comments, do nothing
154-
const injectComments = (existingComments = [], newComments = []) => {
155-
const existingIds = new Set(existingComments.comments.map(c => c.id))
156-
const filteredNew = newComments.filter(c => !existingIds.has(c.id))
157-
return {
158-
...existingComments,
159-
comments: [...filteredNew, ...(existingComments.comments || [])]
160-
}
161-
}
162-
163147
return (
164148
<div
165149
onClick={showNewComments}

0 commit comments

Comments
 (0)