Show two levels of comments under each home network note
This commit is contained in:
parent
27fa25c35c
commit
ff3461a020
4 changed files with 83 additions and 18 deletions
|
|
@ -21,18 +21,19 @@
|
|||
node: CommentNode
|
||||
root: TrustedEvent
|
||||
replyTo?: TrustedEvent
|
||||
setReplyTo: (comment?: TrustedEvent) => void
|
||||
setReplyTo?: (comment?: TrustedEvent) => void
|
||||
url?: string
|
||||
context: FeedContext
|
||||
maxDepth?: number
|
||||
}
|
||||
|
||||
const {node, root, replyTo, setReplyTo, url, context}: Props = $props()
|
||||
const {node, root, replyTo, setReplyTo, url, context, maxDepth = Infinity}: Props = $props()
|
||||
|
||||
const composing = $derived(replyTo?.id === node.comment.id)
|
||||
|
||||
const reply = () => setReplyTo(node.comment)
|
||||
const reply = () => setReplyTo?.(node.comment)
|
||||
|
||||
const clearReplyTo = () => setReplyTo(undefined)
|
||||
const clearReplyTo = () => setReplyTo?.(undefined)
|
||||
|
||||
const openProfile = () => pushModal(ProfileDetail, {pubkey: node.comment.pubkey, url})
|
||||
|
||||
|
|
@ -70,11 +71,13 @@
|
|||
</div>
|
||||
<Content showEntire event={node.comment} {url} />
|
||||
{#if url}
|
||||
<div class="mt-1 flex flex-wrap items-center justify-between gap-2">
|
||||
<Button class="button button-neutral button-xs" onclick={reply}>
|
||||
<Icon icon={Reply} size={4} />
|
||||
Reply
|
||||
</Button>
|
||||
<div class="mt-1 flex flex-wrap items-center justify-end gap-2">
|
||||
{#if setReplyTo}
|
||||
<Button class="button button-neutral button-xs mr-auto" onclick={reply}>
|
||||
<Icon icon={Reply} size={4} />
|
||||
Reply
|
||||
</Button>
|
||||
{/if}
|
||||
<CommentActions event={node.comment} {url} {context} />
|
||||
</div>
|
||||
{/if}
|
||||
|
|
@ -91,14 +94,21 @@
|
|||
onSubmit={clearReplyTo} />
|
||||
</div>
|
||||
{/if}
|
||||
{#if node.children.length > 0}
|
||||
{#if node.children.length > 0 && maxDepth > 1}
|
||||
<!-- The thread line runs under the avatar's center and indents replies to line up with
|
||||
this comment's text column; it brightens while the subtree is hovered. -->
|
||||
<div
|
||||
data-component="CommentReplies"
|
||||
class="border-line-less hover:border-line ml-4 flex flex-col border-l pl-7 transition-colors">
|
||||
{#each node.children as child (child.comment.id)}
|
||||
<CommentTree node={child} {root} {replyTo} {setReplyTo} {url} {context} />
|
||||
<CommentTree
|
||||
node={child}
|
||||
{root}
|
||||
{replyTo}
|
||||
{setReplyTo}
|
||||
{url}
|
||||
{context}
|
||||
maxDepth={maxDepth - 1} />
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
import Masonry from "@lib/components/Masonry.svelte"
|
||||
import Spinner from "@lib/components/Spinner.svelte"
|
||||
import HomeSection from "@app/components/HomeSection.svelte"
|
||||
import NoteItem from "@app/components/NoteItem.svelte"
|
||||
import HomeNetworkItem from "@app/components/HomeNetworkItem.svelte"
|
||||
import {followLists, relayLists, router, user} from "@app/core"
|
||||
import {isFeedLoading, makeFeed, makeFeedContext, makeScrollLoader} from "@app/feeds"
|
||||
|
||||
|
|
@ -20,7 +20,10 @@
|
|||
// `syncFollowNetwork` reads their lists from.
|
||||
const RELAY_LIMIT = 8
|
||||
|
||||
const context = makeFeedContext({relays: $relayLists.readUrls($user.pubkey).get()})
|
||||
const context = makeFeedContext({
|
||||
relays: $relayLists.readUrls($user.pubkey).get(),
|
||||
withReplies: true,
|
||||
})
|
||||
|
||||
const followList = $derived($followLists.one($user.pubkey))
|
||||
const follows = $derived($followList?.pubkeys())
|
||||
|
|
@ -91,7 +94,7 @@
|
|||
{:else}
|
||||
<Masonry items={notes} getKey={event => event.id} columnWidth={80} maxColumns={2} gap={3}>
|
||||
{#snippet child(event)}
|
||||
<NoteItem {event} {context} />
|
||||
<HomeNetworkItem {event} {context} />
|
||||
{/snippet}
|
||||
</Masonry>
|
||||
{#if loading}
|
||||
|
|
|
|||
41
src/app/components/HomeNetworkItem.svelte
Normal file
41
src/app/components/HomeNetworkItem.svelte
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
<script lang="ts">
|
||||
import {sortBy} from "@welshman/lib"
|
||||
import {COMMENT, NOTE} from "@welshman/util"
|
||||
import type {TrustedEvent} from "@welshman/util"
|
||||
import NoteItem from "@app/components/NoteItem.svelte"
|
||||
import CommentTree from "@app/components/CommentTree.svelte"
|
||||
import type {FeedContext} from "@app/feeds"
|
||||
import {buildCommentTree} from "@app/social"
|
||||
|
||||
type Props = {
|
||||
event: TrustedEvent
|
||||
context: FeedContext
|
||||
}
|
||||
|
||||
const {event, context}: Props = $props()
|
||||
|
||||
const related = context.related(event)
|
||||
|
||||
// buildCommentTree adopts a comment whose parent never loaded, and reads oldest first so
|
||||
// that it adopts a parent before its own children.
|
||||
const nodes = $derived(
|
||||
buildCommentTree(
|
||||
event,
|
||||
sortBy(
|
||||
e => e.created_at,
|
||||
$related.filter(e => e.kind === COMMENT || e.kind === NOTE),
|
||||
),
|
||||
),
|
||||
)
|
||||
</script>
|
||||
|
||||
<div class="cv card card-interactive flex flex-col gap-3">
|
||||
<NoteItem {event} {context} class="" />
|
||||
{#if nodes.length > 0}
|
||||
<div class="border-line-less flex flex-col border-t">
|
||||
{#each nodes as node (node.comment.id)}
|
||||
<CommentTree {node} root={event} {context} maxDepth={2} />
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
|
@ -5,6 +5,7 @@ import {
|
|||
COMMENT,
|
||||
DELETE,
|
||||
EVENT_TIME,
|
||||
NOTE,
|
||||
addressTags,
|
||||
compareEventsAsc,
|
||||
getAddress,
|
||||
|
|
@ -58,7 +59,17 @@ const getTargets = ({kind, tags}: TrustedEvent) =>
|
|||
const getKeys = (event: TrustedEvent) =>
|
||||
isReplaceableKind(event.kind) ? [event.id, getAddress(event)] : [event.id]
|
||||
|
||||
export const makeFeedContext = ({relays}: {relays: string[] | Promise<string[]>}) => {
|
||||
export const makeFeedContext = ({
|
||||
relays,
|
||||
withReplies = false,
|
||||
}: {
|
||||
relays: string[] | Promise<string[]>
|
||||
withReplies?: boolean
|
||||
}) => {
|
||||
// Every feed loads NIP-22 comments. Kind 1 notes reply to each other with `e` tags instead,
|
||||
// so a feed that renders those replies has to ask for them as well.
|
||||
const contextKinds = withReplies ? [...EVENT_CONTEXT_KINDS, NOTE] : EVENT_CONTEXT_KINDS
|
||||
const requestKinds = withReplies ? [...REACTION_KINDS, NOTE] : REACTION_KINDS
|
||||
const {repository} = app.get()
|
||||
const controller = new AbortController()
|
||||
const targets = new Set<string>()
|
||||
|
|
@ -134,7 +145,7 @@ export const makeFeedContext = ({relays}: {relays: string[] | Promise<string[]>}
|
|||
// What's already local — an earlier page, our own optimistic reactions — never comes
|
||||
// through the update listener, so file it before asking the network for the rest
|
||||
for (const event of repository.query([
|
||||
...getReplyFilters(events, {kinds: EVENT_CONTEXT_KINDS}),
|
||||
...getReplyFilters(events, {kinds: contextKinds}),
|
||||
...getCommentFiltersForRoot(events),
|
||||
])) {
|
||||
addEvent(event, touched)
|
||||
|
|
@ -148,7 +159,7 @@ export const makeFeedContext = ({relays}: {relays: string[] | Promise<string[]>}
|
|||
relays: urls,
|
||||
signal: controller.signal,
|
||||
filters: [
|
||||
...getReplyFilters(events, {kinds: REACTION_KINDS}),
|
||||
...getReplyFilters(events, {kinds: requestKinds}),
|
||||
...getCommentFiltersForRoot(events),
|
||||
],
|
||||
})
|
||||
|
|
@ -170,7 +181,7 @@ export const makeFeedContext = ({relays}: {relays: string[] | Promise<string[]>}
|
|||
const touched = new Set<string>()
|
||||
|
||||
for (const event of added) {
|
||||
if (EVENT_CONTEXT_KINDS.includes(event.kind)) {
|
||||
if (contextKinds.includes(event.kind)) {
|
||||
addEvent(event, touched)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue