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