Solution to reflow vs completeness problem: mark replies that were lazily loaded with isContext, and only reflow on detail pages where we want to see everything anyway, while on feeds we show everything we have up front (which, with parent retrieval, often gives us some nice threads), and hide lazily loaded context

This commit is contained in:
Jonathan Staab 2023-02-22 11:19:35 -06:00
parent bb7df4cd68
commit 8b06c6eb5d
4 changed files with 26 additions and 17 deletions

View File

@ -1,5 +1,5 @@
import type {MyEvent} from 'src/util/types'
import {partition, uniq, uniqBy, prop, propEq, reject, groupBy, pluck} from 'ramda'
import {assoc, partition, uniq, uniqBy, prop, propEq, reject, groupBy, pluck} from 'ramda'
import {personKinds, findReplyId} from 'src/util/nostr'
import {log} from 'src/util/logger'
import {chunk} from 'hurdak/lib/hurdak'
@ -187,15 +187,24 @@ const streamContext = ({notes, onChunk, depth = 0}) =>
)
const applyContext = (notes, context) => {
const [replies, reactions] = partition(propEq('kind', 1), context)
const [replies, reactions] = partition(
propEq('kind', 1),
context.map(assoc('isContext', true))
)
const repliesByParentId = groupBy(findReplyId, replies)
const reactionsByParentId = groupBy(findReplyId, reactions)
const annotate = ({replies = [], reactions = [], ...note}) => ({
...note,
replies: uniqBy(prop('id'), replies.concat(repliesByParentId[note.id] || [])).map(annotate),
reactions: uniqBy(prop('id'), reactions.concat(reactionsByParentId[note.id] || [])),
})
const annotate = ({replies = [], reactions = [], ...note}) => {
const combinedReplies = replies.concat(repliesByParentId[note.id] || [])
const combinedReactions = reactions.concat(reactionsByParentId[note.id] || [])
return {
...note,
replies: uniqBy(prop('id'), combinedReplies).map(annotate),
reactions: uniqBy(prop('id'), combinedReactions),
}
}
return notes.map(annotate)
}

View File

@ -27,6 +27,7 @@
export let depth = 0
export let anchorId = null
export let showParent = true
export let showContext = false
export let invertColors = false
const getDefaultReplyMentions = () =>
@ -35,6 +36,7 @@
let reply = null
let replyMentions = getDefaultReplyMentions()
let replyContainer = null
let visibleNotes = []
const {profile} = user
const links = extractUrls(note.content)
@ -60,6 +62,7 @@
$: $likesCount = likes.length
$: $flagsCount = flags.length
$: $repliesCount = note.replies.length
$: visibleNotes = note.replies.filter(r => showContext ? true : !r.isContext)
const onClick = e => {
const target = e.target as HTMLElement
@ -314,16 +317,16 @@
</div>
{/if}
{#if note.replies.length > 0 && depth > 0}
{#if visibleNotes.length > 0 && depth > 0}
<div class="ml-8 note-children" bind:this={childrenContainer}>
{#if !showEntire && note.replies.length > 3}
{#if !showEntire && note.replies.length > visibleNotes.length}
<button class="ml-5 py-2 text-light cursor-pointer" on:click={onClick}>
<i class="fa fa-up-down text-sm pr-2" />
Show {quantify(note.replies.length - 3, 'other reply', 'more replies')}
Show {quantify(note.replies.length - visibleNotes.length, 'other reply', 'more replies')}
</button>
{/if}
{#each note.replies.slice(showEntire ? 0 : -3) as r (r.id)}
<svelte:self showParent={false} note={r} depth={depth - 1} {invertColors} {anchorId} />
{#each visibleNotes as r (r.id)}
<svelte:self showParent={false} note={r} depth={depth - 1} {invertColors} {anchorId} {showContext} />
{/each}
</div>
{/if}

View File

@ -18,7 +18,6 @@
let notes = []
let notesBuffer = []
let showReplies = false
const since = now()
const maxNotes = 100
@ -49,8 +48,6 @@
onChunk: context => {
notes = network.applyContext(notes, context)
},
}).then(() => {
showReplies = true
})
// Show replies grouped by parent whenever possible
@ -111,7 +108,7 @@
<div>
{#each notes as note (note.id)}
<Note depth={showReplies ? 2 : 0} {note} />
<Note depth={2} {note} />
{/each}
</div>

View File

@ -55,7 +55,7 @@
</div>
{:else if note.pubkey}
<div in:fly={{y: 20}}>
<Note invertColors depth={6} anchorId={note.id} note={asDisplayEvent(note)} />
<Note showContext invertColors depth={6} anchorId={note.id} note={asDisplayEvent(note)} />
</div>
{/if}