Add feed context to improve performance of ReactionSummary
This commit is contained in:
parent
7d436c482c
commit
0dab2cc779
42 changed files with 468 additions and 153 deletions
|
|
@ -7,6 +7,7 @@
|
|||
import RoomName from "@app/components/RoomName.svelte"
|
||||
import {publishReaction, retractReaction} from "@app/reactions"
|
||||
import ReactionSummary from "@app/components/ReactionSummary.svelte"
|
||||
import type {FeedContext} from "@app/feeds"
|
||||
import ThunkStatusOrDeleted from "@app/components/ThunkStatusOrDeleted.svelte"
|
||||
import EventActivity from "@app/components/EventActivity.svelte"
|
||||
import EventActions from "@app/components/EventActions.svelte"
|
||||
|
|
@ -18,9 +19,10 @@
|
|||
event: TrustedEvent
|
||||
showRoom?: boolean
|
||||
showActivity?: boolean
|
||||
context: FeedContext
|
||||
}
|
||||
|
||||
const {url, event, showRoom, showActivity}: Props = $props()
|
||||
const {url, event, showRoom, showActivity, context}: Props = $props()
|
||||
|
||||
const article = $derived(reader(Article)(event))
|
||||
const h = $derived(article.room())
|
||||
|
|
@ -46,7 +48,13 @@
|
|||
{/each}
|
||||
</div>
|
||||
<ThunkStatusOrDeleted {event}>
|
||||
<ReactionSummary {url} {event} {deleteReaction} {createReaction} reactionClass="tip-left" />
|
||||
<ReactionSummary
|
||||
{url}
|
||||
{event}
|
||||
{context}
|
||||
{deleteReaction}
|
||||
{createReaction}
|
||||
reactionClass="tip-left" />
|
||||
{#if showActivity}
|
||||
<EventActivity {url} {path} {event} />
|
||||
{/if}
|
||||
|
|
|
|||
|
|
@ -7,15 +7,17 @@
|
|||
import Content from "@app/components/Content.svelte"
|
||||
import ProfileLink from "@app/components/ProfileLink.svelte"
|
||||
import ArticleActions from "@app/components/ArticleActions.svelte"
|
||||
import type {FeedContext} from "@app/feeds"
|
||||
import {reader} from "@app/core"
|
||||
import {makeArticlePath} from "@app/routes"
|
||||
|
||||
type Props = {
|
||||
url: string
|
||||
event: TrustedEvent
|
||||
context: FeedContext
|
||||
}
|
||||
|
||||
const {url, event}: Props = $props()
|
||||
const {url, event, context}: Props = $props()
|
||||
|
||||
const article = $derived(reader(Article)(event))
|
||||
const title = $derived(article.title())
|
||||
|
|
@ -52,7 +54,7 @@
|
|||
Written by
|
||||
<ProfileLink pubkey={event.pubkey} {url} />
|
||||
</span>
|
||||
<ArticleActions showRoom showActivity {url} {event} />
|
||||
<ArticleActions showRoom showActivity {url} {event} {context} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
import RoomName from "@app/components/RoomName.svelte"
|
||||
import {publishReaction, retractReaction} from "@app/reactions"
|
||||
import ReactionSummary from "@app/components/ReactionSummary.svelte"
|
||||
import type {FeedContext} from "@app/feeds"
|
||||
import ThunkStatusOrDeleted from "@app/components/ThunkStatusOrDeleted.svelte"
|
||||
import EventActivity from "@app/components/EventActivity.svelte"
|
||||
import EventActions from "@app/components/EventActions.svelte"
|
||||
|
|
@ -21,9 +22,10 @@
|
|||
event: TrustedEvent
|
||||
showRoom?: boolean
|
||||
showActivity?: boolean
|
||||
context: FeedContext
|
||||
}
|
||||
|
||||
const {url, event, showRoom, showActivity}: Props = $props()
|
||||
const {url, event, showRoom, showActivity, context}: Props = $props()
|
||||
|
||||
const h = $derived(tagValue(tagSpec("h"), event.tags))
|
||||
const path = $derived(makeCalendarPath(url, getAddress(event)))
|
||||
|
|
@ -42,7 +44,13 @@
|
|||
</Link>
|
||||
{/if}
|
||||
<ThunkStatusOrDeleted {event}>
|
||||
<ReactionSummary {url} {event} {deleteReaction} {createReaction} reactionClass="tip tip-left" />
|
||||
<ReactionSummary
|
||||
{url}
|
||||
{event}
|
||||
{context}
|
||||
{deleteReaction}
|
||||
{createReaction}
|
||||
reactionClass="tip tip-left" />
|
||||
{#if showActivity}
|
||||
<EventActivity {url} {path} {event} />
|
||||
{/if}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
import {getAddress, tagValue, tagSpec} from "@welshman/util"
|
||||
import Link from "@lib/components/Link.svelte"
|
||||
import CalendarEventActions from "@app/components/CalendarEventActions.svelte"
|
||||
import type {FeedContext} from "@app/feeds"
|
||||
import CalendarEventHeader from "@app/components/CalendarEventHeader.svelte"
|
||||
import ProfileLink from "@app/components/ProfileLink.svelte"
|
||||
import RoomLink from "@app/components/RoomLink.svelte"
|
||||
|
|
@ -11,9 +12,10 @@
|
|||
type Props = {
|
||||
url: string
|
||||
event: TrustedEvent
|
||||
context: FeedContext
|
||||
}
|
||||
|
||||
const {url, event}: Props = $props()
|
||||
const {url, event, context}: Props = $props()
|
||||
|
||||
const h = $derived(tagValue(tagSpec("h"), event.tags))
|
||||
</script>
|
||||
|
|
@ -29,6 +31,6 @@
|
|||
in <RoomLink {url} {h} />
|
||||
{/if}
|
||||
</span>
|
||||
<CalendarEventActions showActivity {url} {event} />
|
||||
<CalendarEventActions showActivity {url} {event} {context} />
|
||||
</div>
|
||||
</Link>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<script lang="ts">
|
||||
import type {Snippet} from "svelte"
|
||||
import {onMount} from "svelte"
|
||||
import {onDestroy, onMount} from "svelte"
|
||||
import {goto} from "$app/navigation"
|
||||
import {
|
||||
ago,
|
||||
|
|
@ -19,7 +19,7 @@
|
|||
} from "@welshman/lib"
|
||||
import type {Maybe} from "@welshman/lib"
|
||||
import type {TrustedEvent, EventTemplate, EventContent} from "@welshman/util"
|
||||
import {makeEvent, DIRECT_MESSAGE, DIRECT_MESSAGE_FILE} from "@welshman/util"
|
||||
import {makeEvent, userInbox, DIRECT_MESSAGE, DIRECT_MESSAGE_FILE} from "@welshman/util"
|
||||
import {parse, isLink} from "@welshman/content"
|
||||
import {MessagingRelayLists, Thunks} from "@welshman/app"
|
||||
import Danger from "@assets/icons/danger-triangle.svg?dataurl"
|
||||
|
|
@ -41,9 +41,10 @@
|
|||
import ChatComposeEdit from "@app/components/ChatComposeEdit.svelte"
|
||||
import ChatComposeParent from "@app/components/ChatComposeParent.svelte"
|
||||
import ThunkToast from "@app/components/ThunkToast.svelte"
|
||||
import {app, deletes, user, wraps} from "@app/core"
|
||||
import {app, deletes, router, user, wraps} from "@app/core"
|
||||
import {userSettingsValues} from "@app/settings"
|
||||
import {deriveChat, makeChatId} from "@app/chats"
|
||||
import {makeFeedContext} from "@app/feeds"
|
||||
import {pushModal} from "@app/modal"
|
||||
import {DraftKey, type Draft} from "@app/drafts"
|
||||
import {prependParent} from "@app/rooms"
|
||||
|
|
@ -58,6 +59,9 @@
|
|||
const {pubkeys, info}: Props = $props()
|
||||
|
||||
const chatId = makeChatId(pubkeys)
|
||||
const context = makeFeedContext({relays: $router.resolver.relays([userInbox()])})
|
||||
|
||||
onDestroy(context.cleanup)
|
||||
const chat = deriveChat(chatId)
|
||||
const draftKey = new DraftKey<Draft>(`dm:${chatId}`)
|
||||
const others = remove($user.pubkey, pubkeys)
|
||||
|
|
@ -313,6 +317,7 @@
|
|||
event={$state.snapshot(value as TrustedEvent)}
|
||||
{pubkeys}
|
||||
{showPubkey}
|
||||
{context}
|
||||
{replyTo}
|
||||
canEdit={canEditEvent}
|
||||
onEdit={onEditEvent} />
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
import {publishWrappedReaction, retractWrappedReaction} from "@app/reactions"
|
||||
import Content from "@app/components/Content.svelte"
|
||||
import ReactionSummary from "@app/components/ReactionSummary.svelte"
|
||||
import type {FeedContext} from "@app/feeds"
|
||||
import ThunkFailure from "@app/components/ThunkFailure.svelte"
|
||||
import ProfileDetail from "@app/components/ProfileDetail.svelte"
|
||||
import ChatMessageMenu from "@app/components/ChatMessageMenu.svelte"
|
||||
|
|
@ -29,9 +30,10 @@
|
|||
onEdit?: (event: TrustedEvent) => void
|
||||
pubkeys: string[]
|
||||
showPubkey?: boolean
|
||||
context: FeedContext
|
||||
}
|
||||
|
||||
const {event, replyTo, canEdit, onEdit, pubkeys, showPubkey = false}: Props = $props()
|
||||
const {event, replyTo, canEdit, onEdit, pubkeys, showPubkey = false, context}: Props = $props()
|
||||
|
||||
const isOwn = event.pubkey === $user.pubkey
|
||||
const profileDisplay = $profiles.display(event.pubkey).$
|
||||
|
|
@ -117,7 +119,7 @@
|
|||
</div>
|
||||
</TapTarget>
|
||||
<div class="flex gap-2 z-feature -mt-4 ml-4">
|
||||
<ReactionSummary {event} {deleteReaction} {createReaction} noTooltip />
|
||||
<ReactionSummary {event} {context} {deleteReaction} {createReaction} noTooltip />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
import RoomName from "@app/components/RoomName.svelte"
|
||||
import {publishReaction, retractReaction} from "@app/reactions"
|
||||
import ReactionSummary from "@app/components/ReactionSummary.svelte"
|
||||
import type {FeedContext} from "@app/feeds"
|
||||
import ClassifiedStatus from "@app/components/ClassifiedStatus.svelte"
|
||||
import ThunkStatusOrDeleted from "@app/components/ThunkStatusOrDeleted.svelte"
|
||||
import EventActivity from "@app/components/EventActivity.svelte"
|
||||
|
|
@ -25,9 +26,10 @@
|
|||
event: TrustedEvent
|
||||
showRoom?: boolean
|
||||
showActivity?: boolean
|
||||
context: FeedContext
|
||||
}
|
||||
|
||||
const {url, event, showRoom, showActivity}: Props = $props()
|
||||
const {url, event, showRoom, showActivity, context}: Props = $props()
|
||||
|
||||
// Editing a listing hands this a new version of the event, so every value read off it has to be
|
||||
// recomputed rather than captured when the component was created.
|
||||
|
|
@ -60,7 +62,13 @@
|
|||
{#snippet status()}
|
||||
<ClassifiedStatus {event} />
|
||||
{/snippet}
|
||||
<ReactionSummary {url} {event} {deleteReaction} {createReaction} reactionClass="tip-left" />
|
||||
<ReactionSummary
|
||||
{url}
|
||||
{event}
|
||||
{context}
|
||||
{deleteReaction}
|
||||
{createReaction}
|
||||
reactionClass="tip-left" />
|
||||
{#if showActivity}
|
||||
<EventActivity {url} {path} {event} />
|
||||
{/if}
|
||||
|
|
|
|||
|
|
@ -10,15 +10,17 @@
|
|||
import Content from "@app/components/Content.svelte"
|
||||
import ProfileLink from "@app/components/ProfileLink.svelte"
|
||||
import ClassifiedActions from "@app/components/ClassifiedActions.svelte"
|
||||
import type {FeedContext} from "@app/feeds"
|
||||
import RoomLink from "@app/components/RoomLink.svelte"
|
||||
import {makeClassifiedPath} from "@app/routes"
|
||||
|
||||
type Props = {
|
||||
url: string
|
||||
event: TrustedEvent
|
||||
context: FeedContext
|
||||
}
|
||||
|
||||
const {url, event}: Props = $props()
|
||||
const {url, event, context}: Props = $props()
|
||||
|
||||
const classified = $derived(reader(Classified)(event))
|
||||
const title = $derived(classified.title())
|
||||
|
|
@ -59,6 +61,6 @@
|
|||
in <RoomLink {url} {h} />
|
||||
{/if}
|
||||
</span>
|
||||
<ClassifiedActions showActivity {url} {event} />
|
||||
<ClassifiedActions showActivity {url} {event} {context} />
|
||||
</div>
|
||||
</Link>
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
import type {TrustedEvent, EventContent} from "@welshman/util"
|
||||
import {tagSpec, tagValue} from "@welshman/util"
|
||||
import ReactionSummary from "@app/components/ReactionSummary.svelte"
|
||||
import type {FeedContext} from "@app/feeds"
|
||||
import {publishReaction, retractReaction} from "@app/reactions"
|
||||
import ThunkStatusOrDeleted from "@app/components/ThunkStatusOrDeleted.svelte"
|
||||
import EventActivity from "@app/components/EventActivity.svelte"
|
||||
|
|
@ -13,9 +14,10 @@
|
|||
event: TrustedEvent
|
||||
segment?: string
|
||||
showActivity?: boolean
|
||||
context: FeedContext
|
||||
}
|
||||
|
||||
const {url, event, segment, showActivity = false}: Props = $props()
|
||||
const {url, event, segment, showActivity = false, context}: Props = $props()
|
||||
|
||||
const h = tagValue(tagSpec("h"), event.tags)
|
||||
|
||||
|
|
@ -29,7 +31,13 @@
|
|||
<div class="flex flex-wrap items-center justify-between gap-2">
|
||||
<div class="flex grow flex-wrap justify-end gap-2">
|
||||
<ThunkStatusOrDeleted {event}>
|
||||
<ReactionSummary {url} {event} {deleteReaction} {createReaction} reactionClass="tip-left" />
|
||||
<ReactionSummary
|
||||
{url}
|
||||
{event}
|
||||
{context}
|
||||
{deleteReaction}
|
||||
{createReaction}
|
||||
reactionClass="tip-left" />
|
||||
{#if showActivity && path}
|
||||
<EventActivity {url} {path} {event} />
|
||||
{/if}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
import NoteCard from "@app/components/NoteCard.svelte"
|
||||
import NoteContent from "@app/components/NoteContent.svelte"
|
||||
import CommentActions from "@app/components/CommentActions.svelte"
|
||||
import type {FeedContext} from "@app/feeds"
|
||||
import type {CommentNode} from "@app/social"
|
||||
|
||||
type Props = {
|
||||
|
|
@ -16,9 +17,10 @@
|
|||
replyTo?: TrustedEvent
|
||||
setReplyTo: (comment?: TrustedEvent) => void
|
||||
url?: string
|
||||
context: FeedContext
|
||||
}
|
||||
|
||||
const {node, root, replyTo, setReplyTo, url}: Props = $props()
|
||||
const {node, root, replyTo, setReplyTo, url, context}: Props = $props()
|
||||
|
||||
const composing = $derived(replyTo?.id === node.comment.id)
|
||||
|
||||
|
|
@ -37,7 +39,7 @@
|
|||
<Icon icon={Reply} />
|
||||
Reply
|
||||
</Button>
|
||||
<CommentActions event={node.comment} {url} />
|
||||
<CommentActions event={node.comment} {url} {context} />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
|
@ -55,7 +57,7 @@
|
|||
{#if node.children.length > 0}
|
||||
<div class="flex flex-col gap-3 border-l border-solid border-line ml-4 pl-4">
|
||||
{#each node.children as child (child.comment.id)}
|
||||
<CommentTree node={child} {root} {replyTo} {setReplyTo} {url} />
|
||||
<CommentTree node={child} {root} {replyTo} {setReplyTo} {url} {context} />
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
import Icon from "@lib/components/Icon.svelte"
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
import CommentTree from "@app/components/CommentTree.svelte"
|
||||
import type {FeedContext} from "@app/feeds"
|
||||
import CommentCompose from "@app/components/CommentCompose.svelte"
|
||||
import {network} from "@app/core"
|
||||
import {deriveEventsById} from "@app/repository"
|
||||
|
|
@ -16,9 +17,10 @@
|
|||
type Props = {
|
||||
event: TrustedEvent
|
||||
url?: string
|
||||
context: FeedContext
|
||||
}
|
||||
|
||||
const {event, url}: Props = $props()
|
||||
const {event, url, context}: Props = $props()
|
||||
|
||||
const relays = removeUndefined([url])
|
||||
const filters = getCommentFiltersForRoot([event])
|
||||
|
|
@ -49,7 +51,7 @@
|
|||
|
||||
<div class="flex flex-col gap-3">
|
||||
{#each nodes as node (node.comment.id)}
|
||||
<CommentTree {node} root={event} {replyTo} {setReplyTo} {url} />
|
||||
<CommentTree {node} root={event} {replyTo} {setReplyTo} {url} {context} />
|
||||
{/each}
|
||||
</div>
|
||||
{#if url}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
import Link from "@lib/components/Link.svelte"
|
||||
import {reader} from "@app/core"
|
||||
import ReactionSummary from "@app/components/ReactionSummary.svelte"
|
||||
import type {FeedContext} from "@app/feeds"
|
||||
import {publishReaction, retractReaction} from "@app/reactions"
|
||||
import ThunkStatusOrDeleted from "@app/components/ThunkStatusOrDeleted.svelte"
|
||||
import EventActivity from "@app/components/EventActivity.svelte"
|
||||
|
|
@ -16,9 +17,10 @@
|
|||
event: TrustedEvent
|
||||
showRoom?: boolean
|
||||
showActivity?: boolean
|
||||
context: FeedContext
|
||||
}
|
||||
|
||||
const {url, event, showRoom, showActivity}: Props = $props()
|
||||
const {url, event, showRoom, showActivity, context}: Props = $props()
|
||||
|
||||
const path = makeGoalPath(url, event.id)
|
||||
const goal = reader(ZapGoal)(event)
|
||||
|
|
@ -37,7 +39,13 @@
|
|||
</Link>
|
||||
{/if}
|
||||
<ThunkStatusOrDeleted {event}>
|
||||
<ReactionSummary {url} {event} {deleteReaction} {createReaction} reactionClass="tip-left" />
|
||||
<ReactionSummary
|
||||
{url}
|
||||
{event}
|
||||
{context}
|
||||
{deleteReaction}
|
||||
{createReaction}
|
||||
reactionClass="tip-left" />
|
||||
{#if showActivity}
|
||||
<EventActivity {url} {path} {event} />
|
||||
{/if}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
import Content from "@app/components/Content.svelte"
|
||||
import ProfileLink from "@app/components/ProfileLink.svelte"
|
||||
import GoalActions from "@app/components/GoalActions.svelte"
|
||||
import type {FeedContext} from "@app/feeds"
|
||||
import GoalSummary from "@app/components/GoalSummary.svelte"
|
||||
import RoomLink from "@app/components/RoomLink.svelte"
|
||||
import {makeGoalPath} from "@app/routes"
|
||||
|
|
@ -13,9 +14,10 @@
|
|||
type Props = {
|
||||
url: string
|
||||
event: TrustedEvent
|
||||
context: FeedContext
|
||||
}
|
||||
|
||||
const {url, event}: Props = $props()
|
||||
const {url, event, context}: Props = $props()
|
||||
|
||||
const goal = reader(ZapGoal)(event)
|
||||
|
||||
|
|
@ -42,6 +44,6 @@
|
|||
in <RoomLink {url} {h} />
|
||||
{/if}
|
||||
</span>
|
||||
<GoalActions showActivity {url} {event} />
|
||||
<GoalActions showActivity {url} {event} {context} />
|
||||
</div>
|
||||
</Link>
|
||||
|
|
|
|||
|
|
@ -10,15 +10,17 @@
|
|||
import {publishReaction, retractReaction} from "@app/reactions"
|
||||
import NoteCard from "@app/components/NoteCard.svelte"
|
||||
import ReactionSummary from "@app/components/ReactionSummary.svelte"
|
||||
import type {FeedContext} from "@app/feeds"
|
||||
import {router} from "@app/core"
|
||||
|
||||
type Props = {
|
||||
event: TrustedEvent
|
||||
children?: Snippet
|
||||
context: FeedContext
|
||||
url?: string
|
||||
}
|
||||
|
||||
const {url, event, children}: Props = $props()
|
||||
const {url, event, children, context}: Props = $props()
|
||||
|
||||
const getRelays = () => (url ? [url] : $router.resolver.relays([seen(event)]))
|
||||
|
||||
|
|
@ -34,7 +36,13 @@
|
|||
<NoteCard {event} {url} class="cv card card-interactive">
|
||||
<NoteContent {event} expandMode="inline" />
|
||||
<div class="flex w-full justify-between gap-2">
|
||||
<ReactionSummary {url} {event} {deleteReaction} {createReaction} reactionClass="tip-right">
|
||||
<ReactionSummary
|
||||
{url}
|
||||
{event}
|
||||
{context}
|
||||
{deleteReaction}
|
||||
{createReaction}
|
||||
reactionClass="tip-right">
|
||||
<EmojiButton {onEmoji} class="button button-neutral button-xs h-[26px] rounded-2xl">
|
||||
<Icon icon={SmileCircle} size={4} />
|
||||
</EmojiButton>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
<script lang="ts">
|
||||
import {onDestroy} from "svelte"
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
import Spinner from "@lib/components/Spinner.svelte"
|
||||
import NoteItem from "@app/components/NoteItem.svelte"
|
||||
import {makeFeedContext} from "@app/feeds"
|
||||
import {deriveEvent} from "@app/repository"
|
||||
import {goToEvent} from "@app/routes"
|
||||
|
||||
|
|
@ -14,11 +16,14 @@
|
|||
const {url, value, relays}: Props = $props()
|
||||
|
||||
const event = deriveEvent(value, relays)
|
||||
const context = makeFeedContext({relays})
|
||||
|
||||
onDestroy(context.cleanup)
|
||||
</script>
|
||||
|
||||
{#if $event}
|
||||
<Button onclick={() => goToEvent($event)}>
|
||||
<NoteItem {url} event={$event} />
|
||||
<NoteItem {url} {context} event={$event} />
|
||||
</Button>
|
||||
{:else}
|
||||
<p class="flex justify-center py-8">
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
import RoomName from "@app/components/RoomName.svelte"
|
||||
import {publishReaction, retractReaction} from "@app/reactions"
|
||||
import ReactionSummary from "@app/components/ReactionSummary.svelte"
|
||||
import type {FeedContext} from "@app/feeds"
|
||||
import ThunkStatusOrDeleted from "@app/components/ThunkStatusOrDeleted.svelte"
|
||||
import EventActivity from "@app/components/EventActivity.svelte"
|
||||
import EventActions from "@app/components/EventActions.svelte"
|
||||
|
|
@ -15,9 +16,10 @@
|
|||
event: TrustedEvent
|
||||
showRoom?: boolean
|
||||
showActivity?: boolean
|
||||
context: FeedContext
|
||||
}
|
||||
|
||||
const {url, event, showRoom, showActivity}: Props = $props()
|
||||
const {url, event, showRoom, showActivity, context}: Props = $props()
|
||||
|
||||
const h = tagValue(tagSpec("h"), event.tags)
|
||||
const path = makePollPath(url, event.id)
|
||||
|
|
@ -34,7 +36,13 @@
|
|||
</Link>
|
||||
{/if}
|
||||
<ThunkStatusOrDeleted {event}>
|
||||
<ReactionSummary {url} {event} {deleteReaction} {createReaction} reactionClass="tip-left" />
|
||||
<ReactionSummary
|
||||
{url}
|
||||
{event}
|
||||
{context}
|
||||
{deleteReaction}
|
||||
{createReaction}
|
||||
reactionClass="tip-left" />
|
||||
{#if showActivity}
|
||||
<EventActivity {url} {path} {event} />
|
||||
{/if}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
import Link from "@lib/components/Link.svelte"
|
||||
import NoteContent from "@app/components/NoteContent.svelte"
|
||||
import PollActions from "@app/components/PollActions.svelte"
|
||||
import type {FeedContext} from "@app/feeds"
|
||||
import RoomLink from "@app/components/RoomLink.svelte"
|
||||
import ProfileLink from "@app/components/ProfileLink.svelte"
|
||||
import {makePollPath} from "@app/routes"
|
||||
|
|
@ -11,9 +12,10 @@
|
|||
type Props = {
|
||||
url: string
|
||||
event: TrustedEvent
|
||||
context: FeedContext
|
||||
}
|
||||
|
||||
const {url, event}: Props = $props()
|
||||
const {url, event, context}: Props = $props()
|
||||
|
||||
const h = tagValue(tagSpec("h"), event.tags)
|
||||
</script>
|
||||
|
|
@ -29,6 +31,6 @@
|
|||
in <RoomLink {url} {h} />
|
||||
{/if}
|
||||
</span>
|
||||
<PollActions showActivity {url} {event} />
|
||||
<PollActions showActivity {url} {event} {context} />
|
||||
</div>
|
||||
</Link>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<script lang="ts">
|
||||
import {onMount} from "svelte"
|
||||
import {onDestroy, onMount} from "svelte"
|
||||
import {sortBy, uniqBy} from "@welshman/lib"
|
||||
import {feedFromFilter, makeIntersectionFeed, makeRelayFeed} from "@welshman/feeds"
|
||||
import {NOTE} from "@welshman/util"
|
||||
|
|
@ -11,6 +11,7 @@
|
|||
import Spinner from "@lib/components/Spinner.svelte"
|
||||
import NoteItem from "@app/components/NoteItem.svelte"
|
||||
import {app} from "@app/core"
|
||||
import {makeFeedContext} from "@app/feeds"
|
||||
|
||||
type Props = {
|
||||
url: string
|
||||
|
|
@ -21,6 +22,10 @@
|
|||
|
||||
let {url, pubkey, events = $bindable([]), hideLoading = false}: Props = $props()
|
||||
|
||||
const context = makeFeedContext({relays: [url]})
|
||||
|
||||
onDestroy(context.cleanup)
|
||||
|
||||
const ctrl = $app.use(Feeds).makeFeedController({
|
||||
useWindowing: true,
|
||||
feed: makeIntersectionFeed(
|
||||
|
|
@ -30,6 +35,7 @@
|
|||
onEvent: (event: TrustedEvent) => {
|
||||
if (getReplyTags(event.tags).replies.length === 0) {
|
||||
buffer.push(event)
|
||||
context.add(event)
|
||||
}
|
||||
},
|
||||
})
|
||||
|
|
@ -64,7 +70,7 @@
|
|||
<div class="flex flex-col gap-2">
|
||||
{#each events as event (event.id)}
|
||||
<div in:fly>
|
||||
<NoteItem {url} {event} />
|
||||
<NoteItem {url} {event} {context} />
|
||||
</div>
|
||||
{/each}
|
||||
{#if !hideLoading}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
<script lang="ts">
|
||||
import {onDestroy} from "svelte"
|
||||
import type {Snippet} from "svelte"
|
||||
import {NOTE} from "@welshman/util"
|
||||
import Spinner from "@lib/components/Spinner.svelte"
|
||||
import NoteItem from "@app/components/NoteItem.svelte"
|
||||
import {network} from "@app/core"
|
||||
import {makeFeedContext} from "@app/feeds"
|
||||
|
||||
interface Props {
|
||||
url: string
|
||||
|
|
@ -14,6 +16,10 @@
|
|||
|
||||
const {url, pubkey, limit = 1, fallback}: Props = $props()
|
||||
|
||||
const context = makeFeedContext({relays: [url]})
|
||||
|
||||
onDestroy(context.cleanup)
|
||||
|
||||
const events = $network.load({
|
||||
relays: [url],
|
||||
filters: [{authors: [pubkey], kinds: [NOTE], limit}],
|
||||
|
|
@ -28,7 +34,7 @@
|
|||
</p>
|
||||
{:then events}
|
||||
{#each events as event (event.id)}
|
||||
<NoteItem {url} {event} />
|
||||
<NoteItem {url} {event} {context} />
|
||||
{:else}
|
||||
<div class="min-h-6">
|
||||
{@render fallback?.()}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<script lang="ts">
|
||||
import {onMount} from "svelte"
|
||||
import {onDestroy, onMount} from "svelte"
|
||||
import {derived, writable} from "svelte/store"
|
||||
import type {Writable} from "svelte/store"
|
||||
import {sortBy, uniqBy, now} from "@welshman/lib"
|
||||
|
|
@ -11,7 +11,7 @@
|
|||
import Spinner from "@lib/components/Spinner.svelte"
|
||||
import NoteItem from "@app/components/NoteItem.svelte"
|
||||
import {app, network, router} from "@app/core"
|
||||
import {makeFeed} from "@app/feeds"
|
||||
import {makeFeed, makeFeedContext} from "@app/feeds"
|
||||
|
||||
type Props = {
|
||||
pubkey: string
|
||||
|
|
@ -20,6 +20,10 @@
|
|||
const {pubkey}: Props = $props()
|
||||
|
||||
const relays = $router.resolver.relays([outbox(pubkey)])
|
||||
const context = makeFeedContext({relays})
|
||||
|
||||
onDestroy(context.cleanup)
|
||||
|
||||
const pinnedIds = derived($app.use(PinLists).one(pubkey), $pinList => $pinList?.ids() ?? [])
|
||||
|
||||
$effect(() => {
|
||||
|
|
@ -58,6 +62,7 @@
|
|||
relays: $relays,
|
||||
element: element!,
|
||||
filters: [{kinds: [NOTE], authors: [pubkey]}],
|
||||
onEvent: context.add,
|
||||
onBackwardExhausted: () => {
|
||||
exhausted = true
|
||||
},
|
||||
|
|
@ -74,7 +79,7 @@
|
|||
<div class="flex flex-col gap-4" bind:this={element}>
|
||||
{#each feedEvents as event (event.id)}
|
||||
<div in:fly>
|
||||
<NoteItem {event} />
|
||||
<NoteItem {event} {context} />
|
||||
</div>
|
||||
{:else}
|
||||
{#if exhausted}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,10 @@
|
|||
<script lang="ts">
|
||||
import cx from "classnames"
|
||||
import {onMount} from "svelte"
|
||||
import {derived} from "svelte/store"
|
||||
import type {Snippet} from "svelte"
|
||||
import {
|
||||
batch,
|
||||
call,
|
||||
displayList,
|
||||
filter,
|
||||
groupBy,
|
||||
map,
|
||||
removeUndefined,
|
||||
|
|
@ -15,17 +13,7 @@
|
|||
uniq,
|
||||
uniqBy,
|
||||
} from "@welshman/lib"
|
||||
import {
|
||||
REPORT,
|
||||
REACTION,
|
||||
ZAP_RECEIPT,
|
||||
getReplyFilters,
|
||||
fromMsats,
|
||||
matchTag,
|
||||
tagSpec,
|
||||
userInbox,
|
||||
DELETE,
|
||||
} from "@welshman/util"
|
||||
import {REPORT, REACTION, ZAP_RECEIPT, fromMsats, matchTag, tagSpec} from "@welshman/util"
|
||||
import type {TrustedEvent, EventContent} from "@welshman/util"
|
||||
import {getEmojis} from "@welshman/domain"
|
||||
import type {Zap} from "@welshman/domain"
|
||||
|
|
@ -34,13 +22,12 @@
|
|||
import Danger from "@assets/icons/danger-triangle.svg?dataurl"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
import {deriveEvents} from "@app/repository"
|
||||
import Reaction from "@app/components/Reaction.svelte"
|
||||
import ReportDetails from "@app/components/ReportDetails.svelte"
|
||||
import ProfileList from "@app/components/ProfileList.svelte"
|
||||
import ZapModal from "@app/components/Zap.svelte"
|
||||
import {REACTION_KINDS} from "@app/content"
|
||||
import {app, network, router, user} from "@app/core"
|
||||
import {app, user} from "@app/core"
|
||||
import type {FeedContext} from "@app/feeds"
|
||||
import {deriveUserIsSpaceAdmin} from "@app/management"
|
||||
import {pushModal} from "@app/modal"
|
||||
import {deriveDisplaysByPubkey} from "@app/social"
|
||||
|
|
@ -53,6 +40,7 @@
|
|||
reactionClass?: string
|
||||
noTooltip?: boolean
|
||||
innerEvent?: TrustedEvent
|
||||
context: FeedContext
|
||||
children?: Snippet
|
||||
}
|
||||
|
||||
|
|
@ -64,16 +52,19 @@
|
|||
reactionClass = "",
|
||||
noTooltip = false,
|
||||
innerEvent = undefined,
|
||||
context,
|
||||
children,
|
||||
}: Props = $props()
|
||||
|
||||
const eventIds = innerEvent ? [event.id, innerEvent.id] : [event.id]
|
||||
const ownRelated = context.related(event)
|
||||
|
||||
const reports = deriveEvents([{kinds: [REPORT], "#e": [event.id]}])
|
||||
const related = innerEvent
|
||||
? derived([ownRelated, context.related(innerEvent)], ([$own, $inner]) => [...$own, ...$inner])
|
||||
: ownRelated
|
||||
|
||||
const reactions = deriveEvents([{kinds: [REACTION], "#e": eventIds}])
|
||||
|
||||
const receipts = deriveEvents([{kinds: [ZAP_RECEIPT], "#e": eventIds}])
|
||||
const reports = derived(ownRelated, $ownRelated => filter(spec({kind: REPORT}), $ownRelated))
|
||||
const reactions = derived(related, $related => filter(spec({kind: REACTION}), $related))
|
||||
const receipts = derived(related, $related => filter(spec({kind: ZAP_RECEIPT}), $related))
|
||||
|
||||
// A receipt can be a zap of either the event or the one it wraps
|
||||
const zaps = derived<typeof receipts, Zap[]>(
|
||||
|
|
@ -155,32 +146,6 @@
|
|||
)
|
||||
|
||||
const groupedZaps = $derived(groupBy(e => getReactionKey(e.request), $zaps.values()))
|
||||
|
||||
onMount(() => {
|
||||
const controller = new AbortController()
|
||||
|
||||
call(async () => {
|
||||
const relays = url ? [url] : await $router.resolver.relays([userInbox()])
|
||||
|
||||
if (relays.length > 0) {
|
||||
$network.load({
|
||||
relays,
|
||||
signal: controller.signal,
|
||||
filters: getReplyFilters([event], {kinds: REACTION_KINDS}),
|
||||
onEvent: batch(300, (events: TrustedEvent[]) => {
|
||||
$network.load({
|
||||
relays,
|
||||
filters: getReplyFilters(events, {kinds: [DELETE]}),
|
||||
})
|
||||
}),
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
return () => {
|
||||
controller.abort()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
{#if $reactions.length > 0 || $zaps.length || $reports.length > 0 || children}
|
||||
|
|
|
|||
|
|
@ -8,30 +8,32 @@
|
|||
import CalendarEventItem from "@app/components/CalendarEventItem.svelte"
|
||||
import PollItem from "@app/components/PollItem.svelte"
|
||||
import RecentConversation from "@app/components/RecentConversation.svelte"
|
||||
import type {FeedContext} from "@app/feeds"
|
||||
import type {RecentActivityItem} from "@app/recent"
|
||||
|
||||
type Props = {
|
||||
url: string
|
||||
item: RecentActivityItem
|
||||
context: FeedContext
|
||||
}
|
||||
|
||||
const {url, item}: Props = $props()
|
||||
const {url, item, context}: Props = $props()
|
||||
</script>
|
||||
|
||||
{#if item.type === "message"}
|
||||
<RecentConversation {url} event={item.event} count={item.count} />
|
||||
{:else if item.event.kind === THREAD}
|
||||
<ThreadItem {url} event={item.event} />
|
||||
<ThreadItem {url} {context} event={item.event} />
|
||||
{:else if item.event.kind === CLASSIFIED}
|
||||
<ClassifiedItem {url} event={item.event} />
|
||||
<ClassifiedItem {url} {context} event={item.event} />
|
||||
{:else if item.event.kind === LONG_FORM}
|
||||
<ArticleItem {url} event={item.event} />
|
||||
<ArticleItem {url} {context} event={item.event} />
|
||||
{:else if item.event.kind === ZAP_GOAL}
|
||||
<GoalItem {url} event={item.event} />
|
||||
<GoalItem {url} {context} event={item.event} />
|
||||
{:else if item.event.kind === EVENT_TIME}
|
||||
<CalendarEventItem {url} event={item.event} />
|
||||
<CalendarEventItem {url} {context} event={item.event} />
|
||||
{:else if item.event.kind === POLL}
|
||||
<PollItem {url} event={item.event} />
|
||||
<PollItem {url} {context} event={item.event} />
|
||||
{:else}
|
||||
<NoteItem {url} event={item.event} />
|
||||
<NoteItem {url} {context} event={item.event} />
|
||||
{/if}
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@
|
|||
prependParent,
|
||||
} from "@app/rooms"
|
||||
import {userSettingsValues} from "@app/settings"
|
||||
import {makeFeed} from "@app/feeds"
|
||||
import {makeFeed, makeFeedContext} from "@app/feeds"
|
||||
import {checked, deferredRoomPath, setChecked} from "@app/notifications"
|
||||
import {makeRoomPath} from "@app/routes"
|
||||
import {pendingShare, type Share} from "@app/share"
|
||||
|
|
@ -54,6 +54,10 @@
|
|||
|
||||
const {url, h}: Props = $props()
|
||||
|
||||
const context = makeFeedContext({relays: [url]})
|
||||
|
||||
onDestroy(context.cleanup)
|
||||
|
||||
const room = h ? $rooms.forRoom(url, h) : readable(undefined)
|
||||
const addMemberKind = h ? ROOM_ADD_MEMBER : RELAY_ADD_MEMBER
|
||||
const isVoiceRoom = $derived($room && getRoomType($room) === RoomType.Voice)
|
||||
|
|
@ -427,6 +431,7 @@
|
|||
filters: [
|
||||
h ? {kinds: [MESSAGE, addMemberKind], "#h": [h]} : {kinds: [MESSAGE, addMemberKind]},
|
||||
],
|
||||
onEvent: context.add,
|
||||
onBackwardExhausted: () => {
|
||||
loadingBackward = false
|
||||
},
|
||||
|
|
@ -575,6 +580,7 @@
|
|||
{event}
|
||||
{replyTo}
|
||||
{showPubkey}
|
||||
{context}
|
||||
canEdit={canEditEvent}
|
||||
onEdit={onEditEvent} />
|
||||
{/if}
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@
|
|||
import {profiles, thunks, user} from "@app/core"
|
||||
import {colors} from "@app/theme"
|
||||
import {ENABLE_ZAPS} from "@app/env"
|
||||
import type {FeedContext} from "@app/feeds"
|
||||
import {deriveEvent, deriveEventsForUrl} from "@app/repository"
|
||||
import {makeContentPath} from "@app/routes"
|
||||
import {pushModal} from "@app/modal"
|
||||
|
|
@ -50,11 +51,20 @@
|
|||
event: TrustedEvent
|
||||
replyTo?: (event: TrustedEvent) => void
|
||||
showPubkey?: boolean
|
||||
context: FeedContext
|
||||
canEdit: (event: TrustedEvent) => boolean
|
||||
onEdit: (event: TrustedEvent) => void
|
||||
}
|
||||
|
||||
const {url, event, replyTo = undefined, showPubkey = false, canEdit, onEdit}: Props = $props()
|
||||
const {
|
||||
url,
|
||||
event,
|
||||
replyTo = undefined,
|
||||
showPubkey = false,
|
||||
context,
|
||||
canEdit,
|
||||
onEdit,
|
||||
}: Props = $props()
|
||||
|
||||
const h = tagValue(tagSpec("h"), event.tags)
|
||||
const today = formatTimestampAsDate(now())
|
||||
|
|
@ -139,6 +149,7 @@
|
|||
<ReactionSummary
|
||||
{url}
|
||||
{event}
|
||||
{context}
|
||||
{deleteReaction}
|
||||
{createReaction}
|
||||
reactionClass="tip-right"
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
<script lang="ts">
|
||||
import {onDestroy} from "svelte"
|
||||
import History from "@assets/icons/history.svg?dataurl"
|
||||
import AltArrowRight from "@assets/icons/alt-arrow-right.svg?dataurl"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import Link from "@lib/components/Link.svelte"
|
||||
import RecentItem from "@app/components/RecentItem.svelte"
|
||||
import {makeFeedContext} from "@app/feeds"
|
||||
import {deriveRecentActivity} from "@app/recent"
|
||||
import {makeSpacePath} from "@app/routes"
|
||||
|
||||
|
|
@ -12,9 +14,12 @@
|
|||
}
|
||||
|
||||
const {url}: Props = $props()
|
||||
const context = makeFeedContext({relays: [url]})
|
||||
|
||||
const recentActivity = deriveRecentActivity(url)
|
||||
const recentPath = makeSpacePath(url, "recent")
|
||||
|
||||
onDestroy(context.cleanup)
|
||||
</script>
|
||||
|
||||
<div class="card flex flex-col gap-3">
|
||||
|
|
@ -27,7 +32,7 @@
|
|||
{:else}
|
||||
<div class="flex flex-col gap-2">
|
||||
{#each $recentActivity.slice(0, 3) as item (item.event.id)}
|
||||
<RecentItem {url} {item} />
|
||||
<RecentItem {url} {item} {context} />
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
import RoomName from "@app/components/RoomName.svelte"
|
||||
import {publishReaction, retractReaction} from "@app/reactions"
|
||||
import ReactionSummary from "@app/components/ReactionSummary.svelte"
|
||||
import type {FeedContext} from "@app/feeds"
|
||||
import ThunkStatusOrDeleted from "@app/components/ThunkStatusOrDeleted.svelte"
|
||||
import EventActivity from "@app/components/EventActivity.svelte"
|
||||
import EventActions from "@app/components/EventActions.svelte"
|
||||
|
|
@ -15,9 +16,10 @@
|
|||
event: TrustedEvent
|
||||
showRoom?: boolean
|
||||
showActivity?: boolean
|
||||
context: FeedContext
|
||||
}
|
||||
|
||||
const {url, event, showRoom, showActivity}: Props = $props()
|
||||
const {url, event, showRoom, showActivity, context}: Props = $props()
|
||||
|
||||
const h = tagValue(tagSpec("h"), event.tags)
|
||||
const path = makeThreadPath(url, event.id)
|
||||
|
|
@ -33,7 +35,13 @@
|
|||
</Link>
|
||||
{/if}
|
||||
<ThunkStatusOrDeleted {event}>
|
||||
<ReactionSummary {url} {event} {deleteReaction} {createReaction} reactionClass="tip-left" />
|
||||
<ReactionSummary
|
||||
{url}
|
||||
{event}
|
||||
{context}
|
||||
{deleteReaction}
|
||||
{createReaction}
|
||||
reactionClass="tip-left" />
|
||||
{#if showActivity}
|
||||
<EventActivity {url} {path} {event} />
|
||||
{/if}
|
||||
|
|
|
|||
|
|
@ -6,15 +6,17 @@
|
|||
import Content from "@app/components/Content.svelte"
|
||||
import ProfileLink from "@app/components/ProfileLink.svelte"
|
||||
import ThreadActions from "@app/components/ThreadActions.svelte"
|
||||
import type {FeedContext} from "@app/feeds"
|
||||
import RoomLink from "@app/components/RoomLink.svelte"
|
||||
import {makeThreadPath} from "@app/routes"
|
||||
|
||||
type Props = {
|
||||
url: string
|
||||
event: TrustedEvent
|
||||
context: FeedContext
|
||||
}
|
||||
|
||||
const {url, event}: Props = $props()
|
||||
const {url, event, context}: Props = $props()
|
||||
|
||||
const title = tagValue(tagSpec("title"), event.tags)
|
||||
const h = tagValue(tagSpec("h"), event.tags)
|
||||
|
|
@ -44,6 +46,6 @@
|
|||
in <RoomLink {url} {h} />
|
||||
{/if}
|
||||
</span>
|
||||
<ThreadActions showActivity {url} {event} />
|
||||
<ThreadActions showActivity {url} {event} {context} />
|
||||
</div>
|
||||
</Link>
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
import NoteContent from "@app/components/NoteContent.svelte"
|
||||
import Content from "@app/components/Content.svelte"
|
||||
import CommentActions from "@app/components/CommentActions.svelte"
|
||||
import type {FeedContext} from "@app/feeds"
|
||||
import ThreadActions from "@app/components/ThreadActions.svelte"
|
||||
import {handles, profiles} from "@app/core"
|
||||
import {makeEventPermalink} from "@app/routes"
|
||||
|
|
@ -23,9 +24,10 @@
|
|||
event: TrustedEvent
|
||||
threadPubkey: string
|
||||
onReply: (event: TrustedEvent) => void
|
||||
context: FeedContext
|
||||
}
|
||||
|
||||
const {url, event, threadPubkey, onReply}: Props = $props()
|
||||
const {url, event, threadPubkey, onReply, context}: Props = $props()
|
||||
|
||||
const profileDisplay = $profiles.display(event.pubkey, [url]).$
|
||||
const handle = $handles.forPubkey(event.pubkey).$
|
||||
|
|
@ -90,9 +92,9 @@
|
|||
Reply
|
||||
</Button>
|
||||
{#if isComment}
|
||||
<CommentActions segment="threads" {event} {url} />
|
||||
<CommentActions segment="threads" {event} {url} {context} />
|
||||
{:else}
|
||||
<ThreadActions {event} {url} />
|
||||
<ThreadActions {event} {url} {context} />
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -36,6 +36,8 @@ export const makeDeleteFilter = (kinds: number[], extra: Filter = {}) => ({
|
|||
|
||||
export const REPOST_KINDS = [REPOST, GENERIC_REPOST]
|
||||
|
||||
export const REACTION_DISPLAY_KINDS = [REPORT, REACTION, ZAP_RECEIPT]
|
||||
|
||||
export const REACTION_KINDS = [REPORT, DELETE, REACTION]
|
||||
|
||||
if (ENABLE_ZAPS) {
|
||||
|
|
|
|||
173
src/app/feeds.ts
173
src/app/feeds.ts
|
|
@ -1,18 +1,179 @@
|
|||
import {writable} from "svelte/store"
|
||||
import {readable, writable} from "svelte/store"
|
||||
import type {Readable} from "svelte/store"
|
||||
import {batch, between, call, int, now, on, sortBy, uniqBy, MONTH, YEAR} from "@welshman/lib"
|
||||
import {EVENT_TIME, getAddress, matchFilters, tagSpec, tagValue} from "@welshman/util"
|
||||
import {
|
||||
DELETE,
|
||||
EVENT_TIME,
|
||||
getAddress,
|
||||
getReplyFilters,
|
||||
hexTags,
|
||||
matchFilters,
|
||||
tagSpec,
|
||||
tagValue,
|
||||
tagValues,
|
||||
} from "@welshman/util"
|
||||
import type {Filter, TrustedEvent} from "@welshman/util"
|
||||
import {mergeRepositoryUpdates} from "@welshman/net"
|
||||
import type {RepositoryUpdate} from "@welshman/net"
|
||||
import {createScroller} from "@lib/html"
|
||||
import {daysBetween} from "@lib/util"
|
||||
import {REACTION_DISPLAY_KINDS, REACTION_KINDS} from "@app/content"
|
||||
import {app, network} from "@app/core"
|
||||
import {getEventsForUrl} from "@app/repository"
|
||||
|
||||
const noEvents: TrustedEvent[] = []
|
||||
|
||||
export const makeFeedContext = ({relays}: {relays: string[] | Promise<string[]>}) => {
|
||||
const {repository} = app.get()
|
||||
const controller = new AbortController()
|
||||
const targets = new Set<string>()
|
||||
const eventsByTarget = new Map<string, TrustedEvent[]>()
|
||||
const targetsByEventId = new Map<string, string[]>()
|
||||
const subscribersByTarget = new Map<string, Set<(events: TrustedEvent[]) => void>>()
|
||||
|
||||
const addEvent = (event: TrustedEvent, touched: Set<string>) => {
|
||||
// An event seen before its target was tracked stays unfiled, so that adding the target
|
||||
// later can pick it up out of the repository
|
||||
if (!targetsByEventId.has(event.id)) {
|
||||
const eventTargets = tagValues(hexTags("e"), event.tags).filter(target => targets.has(target))
|
||||
|
||||
if (eventTargets.length > 0) {
|
||||
targetsByEventId.set(event.id, eventTargets)
|
||||
|
||||
for (const target of eventTargets) {
|
||||
eventsByTarget.set(target, [...(eventsByTarget.get(target) || []), event])
|
||||
touched.add(target)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const removeEvent = (id: string, touched: Set<string>) => {
|
||||
const eventTargets = targetsByEventId.get(id)
|
||||
|
||||
if (eventTargets) {
|
||||
targetsByEventId.delete(id)
|
||||
|
||||
for (const target of eventTargets) {
|
||||
const events = eventsByTarget.get(target)!.filter(event => event.id !== id)
|
||||
|
||||
if (events.length > 0) {
|
||||
eventsByTarget.set(target, events)
|
||||
} else {
|
||||
eventsByTarget.delete(target)
|
||||
}
|
||||
|
||||
touched.add(target)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const notify = (touched: Set<string>) => {
|
||||
for (const target of touched) {
|
||||
for (const subscriber of subscribersByTarget.get(target) || []) {
|
||||
subscriber(eventsByTarget.get(target) || noEvents)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const loadContext = batch(100, async (events: TrustedEvent[]) => {
|
||||
const touched = new Set<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: REACTION_DISPLAY_KINDS}),
|
||||
)) {
|
||||
addEvent(event, touched)
|
||||
}
|
||||
|
||||
notify(touched)
|
||||
|
||||
const urls = await relays
|
||||
|
||||
const context = await network.get().load({
|
||||
relays: urls,
|
||||
signal: controller.signal,
|
||||
filters: getReplyFilters(events, {kinds: REACTION_KINDS}),
|
||||
})
|
||||
|
||||
if (context.length > 0) {
|
||||
network.get().load({
|
||||
relays: urls,
|
||||
signal: controller.signal,
|
||||
filters: getReplyFilters(context, {kinds: [DELETE]}),
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
const unsubscribe = on(
|
||||
repository,
|
||||
"update",
|
||||
batch(150, (updates: RepositoryUpdate[]) => {
|
||||
const {added, removed} = mergeRepositoryUpdates(updates)
|
||||
const touched = new Set<string>()
|
||||
|
||||
for (const event of added) {
|
||||
if (REACTION_DISPLAY_KINDS.includes(event.kind)) {
|
||||
addEvent(event, touched)
|
||||
}
|
||||
}
|
||||
|
||||
for (const id of removed) {
|
||||
removeEvent(id, touched)
|
||||
}
|
||||
|
||||
notify(touched)
|
||||
}),
|
||||
)
|
||||
|
||||
// Track an event so its context gets requested with the rest of the batch
|
||||
const add = (event: TrustedEvent) => {
|
||||
if (!targets.has(event.id)) {
|
||||
targets.add(event.id)
|
||||
loadContext(event)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
add,
|
||||
related: (event: TrustedEvent): Readable<TrustedEvent[]> => {
|
||||
add(event)
|
||||
|
||||
return readable(eventsByTarget.get(event.id) || noEvents, set => {
|
||||
let subscribers = subscribersByTarget.get(event.id)
|
||||
|
||||
if (!subscribers) {
|
||||
subscribers = new Set()
|
||||
subscribersByTarget.set(event.id, subscribers)
|
||||
}
|
||||
|
||||
subscribers.add(set)
|
||||
set(eventsByTarget.get(event.id) || noEvents)
|
||||
|
||||
return () => {
|
||||
subscribers.delete(set)
|
||||
|
||||
if (subscribers.size === 0) {
|
||||
subscribersByTarget.delete(event.id)
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
cleanup: () => {
|
||||
controller.abort()
|
||||
unsubscribe()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export type FeedContext = ReturnType<typeof makeFeedContext>
|
||||
|
||||
export const makeFeed = ({
|
||||
relays,
|
||||
filters,
|
||||
element,
|
||||
onEvent,
|
||||
onBackwardExhausted,
|
||||
onForwardExhausted,
|
||||
at = now(),
|
||||
|
|
@ -20,6 +181,7 @@ export const makeFeed = ({
|
|||
relays: string[]
|
||||
filters: Filter[]
|
||||
element: HTMLElement
|
||||
onEvent?: (event: TrustedEvent) => void
|
||||
onBackwardExhausted?: () => void
|
||||
onForwardExhausted?: () => void
|
||||
at?: number
|
||||
|
|
@ -64,6 +226,10 @@ export const makeFeed = ({
|
|||
if (visible.length > 0) {
|
||||
visible.sort((a, b) => a.created_at - b.created_at)
|
||||
|
||||
for (const event of visible) {
|
||||
onEvent?.(event)
|
||||
}
|
||||
|
||||
events.update($events => {
|
||||
const merged: TrustedEvent[] = []
|
||||
let i = 0
|
||||
|
|
@ -223,11 +389,13 @@ export const makeCalendarFeed = ({
|
|||
relays,
|
||||
filters,
|
||||
element,
|
||||
onEvent,
|
||||
onExhausted,
|
||||
}: {
|
||||
relays: string[]
|
||||
filters: Filter[]
|
||||
element: HTMLElement
|
||||
onEvent?: (event: TrustedEvent) => void
|
||||
onExhausted?: () => void
|
||||
}) => {
|
||||
const interval = int(5, MONTH)
|
||||
|
|
@ -260,6 +428,7 @@ export const makeCalendarFeed = ({
|
|||
|
||||
for (const event of valid) {
|
||||
seen.add(event.id)
|
||||
onEvent?.(event)
|
||||
}
|
||||
|
||||
valid.sort((a, b) => getStart(a) - getStart(b))
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<script lang="ts">
|
||||
import {onMount} from "svelte"
|
||||
import {onDestroy, onMount} from "svelte"
|
||||
import {readable} from "svelte/store"
|
||||
import type {Readable} from "svelte/store"
|
||||
import {page} from "$app/stores"
|
||||
|
|
@ -21,10 +21,13 @@
|
|||
import {reader} from "@app/core"
|
||||
import {decodeRelay} from "@app/relays"
|
||||
import {makeCommentFilter} from "@app/content"
|
||||
import {makeFeed} from "@app/feeds"
|
||||
import {makeFeed, makeFeedContext} from "@app/feeds"
|
||||
import {pushModal} from "@app/modal"
|
||||
|
||||
const url = decodeRelay($page.params.relay!)
|
||||
const context = makeFeedContext({relays: [url]})
|
||||
|
||||
onDestroy(context.cleanup)
|
||||
|
||||
let loading = $state(true)
|
||||
let author: string | undefined = $state()
|
||||
|
|
@ -60,6 +63,7 @@
|
|||
const feed = makeFeed({
|
||||
relays: [url],
|
||||
element: element!,
|
||||
onEvent: context.add,
|
||||
filters: [{kinds: [LONG_FORM]}, makeCommentFilter([LONG_FORM])],
|
||||
onBackwardExhausted: () => {
|
||||
loading = false
|
||||
|
|
@ -68,9 +72,7 @@
|
|||
|
||||
events = feed.events
|
||||
|
||||
return () => {
|
||||
feed.cleanup()
|
||||
}
|
||||
return () => feed.cleanup()
|
||||
})
|
||||
</script>
|
||||
|
||||
|
|
@ -93,7 +95,7 @@
|
|||
<div class="flex min-w-0 grow flex-col gap-2 sm:gap-4 p-2 sm:p-4 lg:pr-0">
|
||||
{#each filtered as event (getAddress(event))}
|
||||
<div in:fly>
|
||||
<ArticleItem {url} {event} />
|
||||
<ArticleItem {url} {event} {context} />
|
||||
</div>
|
||||
{/each}
|
||||
<p class="flex h-10 items-center justify-center py-20">
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<script lang="ts">
|
||||
import {onDestroy} from "svelte"
|
||||
import {derived} from "svelte/store"
|
||||
import {page} from "$app/stores"
|
||||
import {formatTimestamp, sleep} from "@welshman/lib"
|
||||
|
|
@ -13,14 +14,18 @@
|
|||
import EventComments from "@app/components/EventComments.svelte"
|
||||
import {reader} from "@app/core"
|
||||
import {deriveEvent} from "@app/repository"
|
||||
import {makeFeedContext} from "@app/feeds"
|
||||
import {decodeRelay} from "@app/relays"
|
||||
|
||||
const {relay, address} = $page.params as MakeNonOptional<typeof $page.params>
|
||||
const url = decodeRelay(relay)
|
||||
const context = makeFeedContext({relays: [url]})
|
||||
const event = deriveEvent(address, [url])
|
||||
const article = derived(event, $event => ($event ? reader(Article)($event) : undefined))
|
||||
|
||||
const back = () => history.back()
|
||||
|
||||
onDestroy(context.cleanup)
|
||||
</script>
|
||||
|
||||
<SpaceBar {back}>
|
||||
|
|
@ -46,10 +51,10 @@
|
|||
<p class="text-lg opacity-75">{$article.summary()}</p>
|
||||
{/if}
|
||||
<ContentMarkdown event={$event} {url} />
|
||||
<ArticleActions showRoom event={$event} {url} />
|
||||
<ArticleActions showRoom event={$event} {url} {context} />
|
||||
</div>
|
||||
</NoteCard>
|
||||
<EventComments event={$event} {url} />
|
||||
<EventComments event={$event} {url} {context} />
|
||||
{:else}
|
||||
<div class="flex justify-center py-20">
|
||||
{#await sleep(5000)}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<script lang="ts">
|
||||
import {onMount} from "svelte"
|
||||
import {onDestroy, onMount} from "svelte"
|
||||
import type {Readable} from "svelte/store"
|
||||
import {readable} from "svelte/store"
|
||||
import {page} from "$app/stores"
|
||||
|
|
@ -20,15 +20,18 @@
|
|||
import {pushModal} from "@app/modal"
|
||||
import {decodeRelay} from "@app/relays"
|
||||
import {makeCommentFilter} from "@app/content"
|
||||
import {makeCalendarFeed} from "@app/feeds"
|
||||
import {makeCalendarFeed, makeFeedContext} from "@app/feeds"
|
||||
|
||||
const url = decodeRelay($page.params.relay!)
|
||||
const context = makeFeedContext({relays: [url]})
|
||||
|
||||
const makeEvent = () => pushModal(CalendarEventCreate, {url})
|
||||
|
||||
const getStart = (event: TrustedEvent) => parseInt(tagValue(tagSpec("start"), event.tags) || "")
|
||||
|
||||
let element: HTMLElement | undefined = $state()
|
||||
onDestroy(context.cleanup)
|
||||
|
||||
let loading = $state(true)
|
||||
let events: Readable<TrustedEvent[]> = $state(readable([]))
|
||||
|
||||
|
|
@ -94,6 +97,7 @@
|
|||
const feed = makeCalendarFeed({
|
||||
relays: [url],
|
||||
element: element!,
|
||||
onEvent: context.add,
|
||||
filters: [{kinds: [EVENT_TIME]}, makeCommentFilter([EVENT_TIME])],
|
||||
onExhausted: () => {
|
||||
loading = false
|
||||
|
|
@ -102,9 +106,7 @@
|
|||
|
||||
events = feed.events
|
||||
|
||||
return () => {
|
||||
feed.cleanup()
|
||||
}
|
||||
return () => feed.cleanup()
|
||||
})
|
||||
</script>
|
||||
|
||||
|
|
@ -136,7 +138,7 @@
|
|||
{#if dateDisplay}
|
||||
<Divider>{dateDisplay}</Divider>
|
||||
{/if}
|
||||
<CalendarEventItem {url} {event} />
|
||||
<CalendarEventItem {url} {event} {context} />
|
||||
</div>
|
||||
{/each}
|
||||
{#if loading}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<script lang="ts">
|
||||
import {onDestroy} from "svelte"
|
||||
import {derived} from "svelte/store"
|
||||
import {page} from "$app/stores"
|
||||
import {sortBy, sleep} from "@welshman/lib"
|
||||
|
|
@ -23,10 +24,12 @@
|
|||
import EventReply from "@app/components/EventReply.svelte"
|
||||
import {network, reader} from "@app/core"
|
||||
import {deriveEvent, deriveEvents} from "@app/repository"
|
||||
import {makeFeedContext} from "@app/feeds"
|
||||
import {decodeRelay} from "@app/relays"
|
||||
|
||||
const {relay, address} = $page.params as MakeNonOptional<typeof $page.params>
|
||||
const url = decodeRelay(relay)
|
||||
const context = makeFeedContext({relays: [url]})
|
||||
const event = deriveEvent(address, [url])
|
||||
const timeEvent = derived(event, $event => ($event ? reader(TimeEvent)($event) : undefined))
|
||||
const filters = $derived($event ? getCommentFiltersForRoot([$event]) : [])
|
||||
|
|
@ -46,6 +49,8 @@
|
|||
showAll = true
|
||||
}
|
||||
|
||||
onDestroy(context.cleanup)
|
||||
|
||||
let showAll = $state(false)
|
||||
let showReply = $state(false)
|
||||
|
||||
|
|
@ -81,7 +86,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="flex w-full flex-col justify-end sm:flex-row">
|
||||
<CalendarEventActions showRoom {url} event={$event} />
|
||||
<CalendarEventActions showRoom {url} event={$event} {context} />
|
||||
</div>
|
||||
</div>
|
||||
{#if !showAll && $replies.length > 4}
|
||||
|
|
@ -96,7 +101,7 @@
|
|||
<NoteCard event={reply} {url} class="card z-feature w-full">
|
||||
<div class="flex flex-col gap-3 ml-12">
|
||||
<NoteContent showEntire event={reply} {url} />
|
||||
<CommentActions segment="calendar" event={reply} {url} />
|
||||
<CommentActions segment="calendar" event={reply} {url} {context} />
|
||||
</div>
|
||||
</NoteCard>
|
||||
{/each}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<script lang="ts">
|
||||
import {onMount} from "svelte"
|
||||
import {onDestroy, onMount} from "svelte"
|
||||
import {readable} from "svelte/store"
|
||||
import type {Readable} from "svelte/store"
|
||||
import {page} from "$app/stores"
|
||||
|
|
@ -18,10 +18,13 @@
|
|||
import ClassifiedCreate from "@app/components/ClassifiedCreate.svelte"
|
||||
import {decodeRelay} from "@app/relays"
|
||||
import {makeCommentFilter} from "@app/content"
|
||||
import {makeFeed} from "@app/feeds"
|
||||
import {makeFeed, makeFeedContext} from "@app/feeds"
|
||||
import {pushModal} from "@app/modal"
|
||||
|
||||
const url = decodeRelay($page.params.relay!)
|
||||
const context = makeFeedContext({relays: [url]})
|
||||
|
||||
onDestroy(context.cleanup)
|
||||
|
||||
let loading = $state(true)
|
||||
let element: HTMLElement | undefined = $state()
|
||||
|
|
@ -48,6 +51,7 @@
|
|||
const feed = makeFeed({
|
||||
relays: [url],
|
||||
element: element!,
|
||||
onEvent: context.add,
|
||||
filters: [{kinds: [CLASSIFIED]}, makeCommentFilter([CLASSIFIED])],
|
||||
onBackwardExhausted: () => {
|
||||
loading = false
|
||||
|
|
@ -56,9 +60,7 @@
|
|||
|
||||
events = feed.events
|
||||
|
||||
return () => {
|
||||
feed.cleanup()
|
||||
}
|
||||
return () => feed.cleanup()
|
||||
})
|
||||
</script>
|
||||
|
||||
|
|
@ -80,7 +82,7 @@
|
|||
<PageContent bind:element class="flex flex-col gap-2 p-2 sm:gap-4 sm:p-4">
|
||||
{#each items as event (event.id)}
|
||||
<div in:fly>
|
||||
<ClassifiedItem {url} event={$state.snapshot(event)} />
|
||||
<ClassifiedItem {url} {context} event={$state.snapshot(event)} />
|
||||
</div>
|
||||
{/each}
|
||||
<p class="flex h-10 items-center justify-center py-20">
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<script lang="ts">
|
||||
import {onDestroy} from "svelte"
|
||||
import {derived} from "svelte/store"
|
||||
import {page} from "$app/stores"
|
||||
import {sleep} from "@welshman/lib"
|
||||
|
|
@ -20,10 +21,12 @@
|
|||
import EventReply from "@app/components/EventReply.svelte"
|
||||
import {network, reader} from "@app/core"
|
||||
import {deriveEvent, deriveEventsById} from "@app/repository"
|
||||
import {makeFeedContext} from "@app/feeds"
|
||||
import {decodeRelay} from "@app/relays"
|
||||
|
||||
const {relay, address} = $page.params as MakeNonOptional<typeof $page.params>
|
||||
const url = decodeRelay(relay)
|
||||
const context = makeFeedContext({relays: [url]})
|
||||
const event = deriveEvent(address, [url])
|
||||
const classified = derived(event, $event => ($event ? reader(Classified)($event) : undefined))
|
||||
const filters = $derived($event ? getCommentFiltersForRoot([$event]) : [])
|
||||
|
|
@ -43,6 +46,8 @@
|
|||
showAll = true
|
||||
}
|
||||
|
||||
onDestroy(context.cleanup)
|
||||
|
||||
let showAll = $state(false)
|
||||
let showReply = $state(false)
|
||||
|
||||
|
|
@ -69,7 +74,7 @@
|
|||
<NoteCard event={$event} {url} class="card z-feature w-full">
|
||||
<div class="flex flex-col gap-3 ml-12">
|
||||
<NoteContent showEntire event={$event} {url} />
|
||||
<ClassifiedActions showRoom event={$event} {url} />
|
||||
<ClassifiedActions showRoom event={$event} {url} {context} />
|
||||
</div>
|
||||
</NoteCard>
|
||||
{#if !showAll && $replies.length > 4}
|
||||
|
|
@ -84,7 +89,7 @@
|
|||
<NoteCard event={reply} {url} class="card z-feature w-full">
|
||||
<div class="flex flex-col gap-3 ml-12">
|
||||
<NoteContent showEntire event={reply} {url} />
|
||||
<CommentActions segment="classifieds" event={reply} {url} />
|
||||
<CommentActions segment="classifieds" event={reply} {url} {context} />
|
||||
</div>
|
||||
</NoteCard>
|
||||
{/each}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<script lang="ts">
|
||||
import {onMount} from "svelte"
|
||||
import {onDestroy, onMount} from "svelte"
|
||||
import {readable} from "svelte/store"
|
||||
import type {Readable} from "svelte/store"
|
||||
import {page} from "$app/stores"
|
||||
|
|
@ -18,10 +18,13 @@
|
|||
import GoalCreate from "@app/components/GoalCreate.svelte"
|
||||
import {decodeRelay} from "@app/relays"
|
||||
import {makeCommentFilter} from "@app/content"
|
||||
import {makeFeed} from "@app/feeds"
|
||||
import {makeFeed, makeFeedContext} from "@app/feeds"
|
||||
import {pushModal} from "@app/modal"
|
||||
|
||||
const url = decodeRelay($page.params.relay!)
|
||||
const context = makeFeedContext({relays: [url]})
|
||||
|
||||
onDestroy(context.cleanup)
|
||||
|
||||
let loading = $state(true)
|
||||
let element: HTMLElement | undefined = $state()
|
||||
|
|
@ -48,6 +51,7 @@
|
|||
const feed = makeFeed({
|
||||
relays: [url],
|
||||
element: element!,
|
||||
onEvent: context.add,
|
||||
filters: [{kinds: [ZAP_GOAL]}, makeCommentFilter([ZAP_GOAL])],
|
||||
onBackwardExhausted: () => {
|
||||
loading = false
|
||||
|
|
@ -56,9 +60,7 @@
|
|||
|
||||
events = feed.events
|
||||
|
||||
return () => {
|
||||
feed.cleanup()
|
||||
}
|
||||
return () => feed.cleanup()
|
||||
})
|
||||
</script>
|
||||
|
||||
|
|
@ -80,7 +82,7 @@
|
|||
<PageContent bind:element class="flex flex-col gap-2 p-2 sm:gap-4 sm:p-4">
|
||||
{#each items as event (event.id)}
|
||||
<div in:fly>
|
||||
<GoalItem {url} event={$state.snapshot(event)} />
|
||||
<GoalItem {url} {context} event={$state.snapshot(event)} />
|
||||
</div>
|
||||
{/each}
|
||||
<p class="flex h-10 items-center justify-center py-20">
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<script lang="ts">
|
||||
import {onDestroy} from "svelte"
|
||||
import {derived} from "svelte/store"
|
||||
import {page} from "$app/stores"
|
||||
import {sleep} from "@welshman/lib"
|
||||
|
|
@ -20,10 +21,12 @@
|
|||
import EventReply from "@app/components/EventReply.svelte"
|
||||
import {network, reader} from "@app/core"
|
||||
import {deriveEvent, deriveEventsById} from "@app/repository"
|
||||
import {makeFeedContext} from "@app/feeds"
|
||||
import {decodeRelay} from "@app/relays"
|
||||
|
||||
const {relay, id} = $page.params as MakeNonOptional<typeof $page.params>
|
||||
const url = decodeRelay(relay)
|
||||
const context = makeFeedContext({relays: [url]})
|
||||
const event = deriveEvent(id, [url])
|
||||
const filters = $derived($event ? getCommentFiltersForRoot([$event]) : [])
|
||||
const replies = $derived(deriveEventsAsc(deriveEventsById(filters)))
|
||||
|
|
@ -44,6 +47,8 @@
|
|||
showAll = true
|
||||
}
|
||||
|
||||
onDestroy(context.cleanup)
|
||||
|
||||
let showAll = $state(false)
|
||||
let showReply = $state(false)
|
||||
|
||||
|
|
@ -70,7 +75,7 @@
|
|||
<NoteCard event={$event} {url} class="card z-feature w-full">
|
||||
<div class="flex flex-col gap-3 ml-12">
|
||||
<NoteContent showEntire event={{...$event, content: summary}} {url} />
|
||||
<GoalActions showRoom event={$event} {url} />
|
||||
<GoalActions showRoom event={$event} {url} {context} />
|
||||
</div>
|
||||
</NoteCard>
|
||||
{#if !showAll && $replies.length > 4}
|
||||
|
|
@ -85,7 +90,7 @@
|
|||
<NoteCard event={reply} {url} class="card z-feature w-full">
|
||||
<div class="flex flex-col gap-3 ml-12">
|
||||
<NoteContent showEntire event={reply} {url} />
|
||||
<CommentActions segment="goals" event={reply} {url} />
|
||||
<CommentActions segment="goals" event={reply} {url} {context} />
|
||||
</div>
|
||||
</NoteCard>
|
||||
{/each}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<script lang="ts">
|
||||
import {onMount} from "svelte"
|
||||
import {onDestroy, onMount} from "svelte"
|
||||
import {readable} from "svelte/store"
|
||||
import type {Readable} from "svelte/store"
|
||||
import {page} from "$app/stores"
|
||||
|
|
@ -18,10 +18,13 @@
|
|||
import PollCreate from "@app/components/PollCreate.svelte"
|
||||
import {decodeRelay} from "@app/relays"
|
||||
import {makeCommentFilter} from "@app/content"
|
||||
import {makeFeed} from "@app/feeds"
|
||||
import {makeFeed, makeFeedContext} from "@app/feeds"
|
||||
import {pushModal} from "@app/modal"
|
||||
|
||||
const url = decodeRelay($page.params.relay!)
|
||||
const context = makeFeedContext({relays: [url]})
|
||||
|
||||
onDestroy(context.cleanup)
|
||||
|
||||
let loading = $state(true)
|
||||
let element: HTMLElement | undefined = $state()
|
||||
|
|
@ -48,6 +51,7 @@
|
|||
const feed = makeFeed({
|
||||
relays: [url],
|
||||
element: element!,
|
||||
onEvent: context.add,
|
||||
filters: [{kinds: [POLL]}, makeCommentFilter([POLL])],
|
||||
onBackwardExhausted: () => {
|
||||
loading = false
|
||||
|
|
@ -56,9 +60,7 @@
|
|||
|
||||
events = feed.events
|
||||
|
||||
return () => {
|
||||
feed.cleanup()
|
||||
}
|
||||
return () => feed.cleanup()
|
||||
})
|
||||
</script>
|
||||
|
||||
|
|
@ -80,7 +82,7 @@
|
|||
<PageContent bind:element class="flex flex-col gap-2 p-2 sm:gap-4 sm:p-4">
|
||||
{#each items as event (event.id)}
|
||||
<div in:fly>
|
||||
<PollItem {url} event={$state.snapshot(event)} />
|
||||
<PollItem {url} {context} event={$state.snapshot(event)} />
|
||||
</div>
|
||||
{/each}
|
||||
<p class="flex h-10 items-center justify-center py-20">
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<script lang="ts">
|
||||
import {onMount} from "svelte"
|
||||
import {onDestroy, onMount} from "svelte"
|
||||
import {page} from "$app/stores"
|
||||
import {sleep} from "@welshman/lib"
|
||||
import type {MakeNonOptional} from "@welshman/lib"
|
||||
|
|
@ -17,10 +17,12 @@
|
|||
import EventReply from "@app/components/EventReply.svelte"
|
||||
import {network} from "@app/core"
|
||||
import {deriveEvent, deriveEventsById} from "@app/repository"
|
||||
import {makeFeedContext} from "@app/feeds"
|
||||
import {decodeRelay} from "@app/relays"
|
||||
|
||||
const {relay, id} = $page.params as MakeNonOptional<typeof $page.params>
|
||||
const url = decodeRelay(relay)
|
||||
const context = makeFeedContext({relays: [url]})
|
||||
const event = deriveEvent(id, [url])
|
||||
const filters = $derived($event ? getCommentFiltersForRoot([$event]) : [])
|
||||
const comments = $derived(deriveEventsAsc(deriveEventsById(filters)))
|
||||
|
|
@ -39,6 +41,8 @@
|
|||
showAll = true
|
||||
}
|
||||
|
||||
onDestroy(context.cleanup)
|
||||
|
||||
let showAll = $state(false)
|
||||
let showReply = $state(false)
|
||||
|
||||
|
|
@ -82,7 +86,7 @@
|
|||
<NoteCard event={$event} {url} class="card z-feature w-full">
|
||||
<div class="flex flex-col gap-3 ml-12 flex flex-col gap-3">
|
||||
<NoteContent showEntire event={$event} {url} />
|
||||
<CommentActions segment="polls" showActivity {url} event={$event} />
|
||||
<CommentActions segment="polls" showActivity {url} event={$event} {context} />
|
||||
</div>
|
||||
</NoteCard>
|
||||
{#if !showAll && $comments.length > 4}
|
||||
|
|
@ -97,7 +101,7 @@
|
|||
<NoteCard event={reply} {url} class="card z-feature w-full">
|
||||
<div class="flex flex-col gap-3 ml-12">
|
||||
<NoteContent showEntire event={reply} {url} />
|
||||
<CommentActions segment="polls" event={reply} {url} />
|
||||
<CommentActions segment="polls" event={reply} {url} {context} />
|
||||
</div>
|
||||
</NoteCard>
|
||||
{/each}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<script lang="ts">
|
||||
import {onMount} from "svelte"
|
||||
import {onDestroy, onMount} from "svelte"
|
||||
import {page} from "$app/stores"
|
||||
import History from "@assets/icons/history.svg?dataurl"
|
||||
import Magnifier from "@assets/icons/magnifier.svg?dataurl"
|
||||
|
|
@ -9,12 +9,16 @@
|
|||
import PageContent from "@lib/components/PageContent.svelte"
|
||||
import SpaceBar from "@app/components/SpaceBar.svelte"
|
||||
import RecentItem from "@app/components/RecentItem.svelte"
|
||||
import {makeFeedContext} from "@app/feeds"
|
||||
import SpaceSearch from "@app/components/SpaceSearch.svelte"
|
||||
import {decodeRelay} from "@app/relays"
|
||||
import {deriveRecentActivity} from "@app/recent"
|
||||
import {pushModal} from "@app/modal"
|
||||
|
||||
const url = decodeRelay($page.params.relay!)
|
||||
const context = makeFeedContext({relays: [url]})
|
||||
|
||||
onDestroy(context.cleanup)
|
||||
|
||||
const recentActivity = deriveRecentActivity(url)
|
||||
|
||||
|
|
@ -57,7 +61,7 @@
|
|||
<p class="flex flex-col items-center py-20 text-center">No recent activity found!</p>
|
||||
{:else}
|
||||
{#each $recentActivity.slice(0, limit) as item (item.event.id)}
|
||||
<RecentItem {url} {item} />
|
||||
<RecentItem {url} {item} {context} />
|
||||
{/each}
|
||||
{/if}
|
||||
</PageContent>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<script lang="ts">
|
||||
import {onDestroy} from "svelte"
|
||||
import * as nip19 from "nostr-tools/nip19"
|
||||
import {page} from "$app/stores"
|
||||
import {goto} from "$app/navigation"
|
||||
|
|
@ -20,6 +21,7 @@
|
|||
import RoomName from "@app/components/RoomName.svelte"
|
||||
import {deriveEvent, deriveEventsById} from "@app/repository"
|
||||
import {network} from "@app/core"
|
||||
import {makeFeedContext} from "@app/feeds"
|
||||
import {decodeRelay} from "@app/relays"
|
||||
import {makeSpacePath, scrollToEvent} from "@app/routes"
|
||||
|
||||
|
|
@ -28,6 +30,8 @@
|
|||
const {relay, id} = $page.params as MakeNonOptional<typeof $page.params>
|
||||
const url = decodeRelay(relay)
|
||||
const event = deriveEvent(id, [url])
|
||||
// Rows register themselves with `related`, so the whole page's reactions load in one batch
|
||||
const context = makeFeedContext({relays: [url]})
|
||||
const filters = $derived($event ? getCommentFiltersForRoot([$event]) : [])
|
||||
const replies = $derived(deriveEventsAsc(deriveEventsById(filters)))
|
||||
|
||||
|
|
@ -96,6 +100,8 @@
|
|||
}
|
||||
}
|
||||
|
||||
onDestroy(context.cleanup)
|
||||
|
||||
let showReply = $state(false)
|
||||
let replyTo: TrustedEvent | undefined = $state()
|
||||
|
||||
|
|
@ -172,7 +178,7 @@
|
|||
{#if $event}
|
||||
<div class="bg-surface border-y" style="border-color: var(--line)">
|
||||
{#each pagePosts as post (post.id)}
|
||||
<ThreadPost {url} event={post} threadPubkey={$event.pubkey} onReply={openReply} />
|
||||
<ThreadPost {url} {context} event={post} threadPubkey={$event.pubkey} onReply={openReply} />
|
||||
{/each}
|
||||
</div>
|
||||
{#if pageCount > 1}
|
||||
|
|
|
|||
Loading…
Reference in a new issue