soxa f0e3516cf0
Refactor live comments and comment injection (#2462)
* Fix duplicate comment on pessimistic creation

- comment creation checks for comment's ID existence in cache
- invoice.confirmedAt included in useCanEdit deps for anons live comments

* switch to some as sets are not worth it

* only check for duplicates if a pessimistic payment method has been used

* default to empty array

* add comment about side-effects

* record ownership of an item to avoid injecting it via live comments

* trigger check only if the incoming comment is ours, cleanup

* correct conditions, correct comments, light cleanup

* fix: add defensive condition to ownership recorder, better name

* refactor: unified comment injection logic with deduplication, useCommentsView hook; revert sessionStorage-based fix

* adjust live comments naming around the codebase

* listen for hmac presence for anon edits

* always return the injected comment createdAt to bump live comments

* refactor: improve live comments hook readability

- latest comment createdAt persistence helper
- preserveScroll returns the returning value of the callback
- compact conditional logic
- refresh code comments
- refresh naming
- group constants
- reorder imports

* flat comment injection, fetch flat comments instead of the entire subtree that would've been deduplicated anyway, cleanup

* always align new comment fragment to the comments query structure

* generic useCommentsView hook

* update comment counts if live injecting into fragments without comments field

* fix: pass parentId, if a comment has a top level parent it always has the comments field

* fix: update CommentsViewAt only if we actually injected a comment into cache

* correct injectComment result usage

* pass markViewedAt to further centralize side effects, remove live from Item server typedefs

* fix: don't update counts for ancestors that are already up to date, update commentsViewedAt per batch not per comment

* port: fix coalesce, useCommentsView hook and outline changes

* update hmac field in cache on paid invoice, hmac as useCanEdit effect dependency

* comments and light cleanup, update useCommentsView

* efficient hasComments logic for live comments, establish a gql fragment

* fix: typo on topLevel evaluation

* limit extra evaluations to live comments scenarios

* update comments

* support live comments ncomments increments for anon view tracking
2025-09-07 16:04:34 -05:00

184 lines
4.6 KiB
JavaScript

import { gql } from 'graphql-tag'
import { LIMIT } from '@/lib/cursor'
export default gql`
extend type Query {
items(sub: String, sort: String, type: String, cursor: String, name: String, when: String, from: String, to: String, by: String, limit: Limit! = ${LIMIT}): Items
item(id: ID!): Item
pageTitleAndUnshorted(url: String!): TitleUnshorted
dupes(url: String!): [Item!]
related(cursor: String, title: String, id: ID, minMatch: String, limit: Limit! = ${LIMIT}): Items
search(q: String, sub: String, cursor: String, what: String, sort: String, when: String, from: String, to: String): Items
auctionPosition(sub: String, id: ID, boost: Int): Int!
boostPosition(sub: String, id: ID, boost: Int): BoostPositions!
itemRepetition(parentId: ID): Int!
newComments(itemId: ID, after: Date): Comments!
}
type BoostPositions {
home: Boolean!
sub: Boolean!
homeMaxBoost: Int!
subMaxBoost: Int!
}
type TitleUnshorted {
title: String
unshorted: String
}
type ItemActResult {
id: ID!
sats: Int!
path: String
act: String!
}
type ItemAct {
id: ID!
act: String!
invoice: Invoice
}
extend type Mutation {
bookmarkItem(id: ID): Item
pinItem(id: ID): Item
subscribeItem(id: ID): Item
deleteItem(id: ID): Item
upsertLink(
id: ID, sub: String, title: String!, url: String!, text: String, boost: Int, forward: [ItemForwardInput],
hash: String, hmac: String): ItemPaidAction!
upsertDiscussion(
id: ID, sub: String, title: String!, text: String, boost: Int, forward: [ItemForwardInput],
hash: String, hmac: String): ItemPaidAction!
upsertBounty(
id: ID, sub: String, title: String!, text: String, bounty: Int, boost: Int, forward: [ItemForwardInput],
hash: String, hmac: String): ItemPaidAction!
upsertJob(
id: ID, sub: String!, title: String!, company: String!, location: String, remote: Boolean,
text: String!, url: String!, boost: Int, status: String, logo: Int): ItemPaidAction!
upsertPoll(
id: ID, sub: String, title: String!, text: String, options: [String!]!, boost: Int, forward: [ItemForwardInput], pollExpiresAt: Date,
randPollOptions: Boolean, hash: String, hmac: String): ItemPaidAction!
updateNoteId(id: ID!, noteId: String!): Item!
upsertComment(id: ID, text: String!, parentId: ID, boost: Int, hash: String, hmac: String): ItemPaidAction!
act(id: ID!, sats: Int, act: String, hasSendWallet: Boolean): ItemActPaidAction!
pollVote(id: ID!): PollVotePaidAction!
toggleOutlaw(id: ID!): Item!
updateCommentsViewAt(id: ID!, meCommentsViewedAt: Date!): Date
}
type PollVoteResult {
id: ID!
}
type PollOption {
id: ID,
option: String!
count: Int!
}
type Poll {
meVoted: Boolean!
meInvoiceId: Int
meInvoiceActionState: InvoiceActionState
count: Int!
options: [PollOption!]!
randPollOptions: Boolean
}
type Items {
cursor: String
items: [Item!]!
pins: [Item!]
ad: Item
}
type Comments {
cursor: String
comments: [Item!]!
}
enum InvoiceActionState {
PENDING
PENDING_HELD
HELD
PAID
FAILED
}
type Item {
id: ID!
createdAt: Date!
updatedAt: Date!
invoicePaidAt: Date
deletedAt: Date
deleteScheduledAt: Date
reminderScheduledAt: Date
title: String
searchTitle: String
url: String
searchText: String
text: String
parentId: Int
parent: Item
root: Item
user: User!
userId: Int!
depth: Int
mine: Boolean!
boost: Int!
bounty: Int
bountyPaidTo: [Int]
noteId: String
sats: Int!
credits: Int!
commentSats: Int!
commentCredits: Int!
lastCommentAt: Date
upvotes: Int!
meSats: Int!
meCredits: Int!
meDontLikeSats: Int!
meBookmark: Boolean!
meSubscription: Boolean!
meForward: Boolean
outlawed: Boolean!
freebie: Boolean!
freedFreebie: Boolean!
bio: Boolean!
paidImgLink: Boolean
ncomments: Int!
nDirectComments: Int!
comments(sort: String, cursor: String): Comments!
path: String
position: Int
prior: Int
isJob: Boolean!
pollCost: Int
poll: Poll
pollExpiresAt: Date
company: String
location: String
remote: Boolean
sub: Sub
subName: String
status: String!
uploadId: Int
otsHash: String
parentOtsHash: String
forwards: [ItemForward]
imgproxyUrls: JSONObject
rel: String
apiKey: Boolean
invoice: Invoice
cost: Int!
meCommentsViewedAt: Date
}
input ItemForwardInput {
nym: String!
pct: Int!
}
`