You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
136 lines
4.3 KiB
136 lines
4.3 KiB
<script lang="ts"> |
|
import type { NostrEvent } from '../../types/nostr.js'; |
|
import { nostrClient } from '../../services/nostr/nostr-client.js'; |
|
import { relayManager } from '../../services/nostr/relay-manager.js'; |
|
import { stripMarkdown } from '../../services/text-utils.js'; |
|
|
|
interface Props { |
|
parentEvent?: NostrEvent; // Optional - if not provided, will load by parentEventId |
|
parentEventId?: string; // Optional - used to load parent if not provided |
|
targetId?: string; // Optional ID to scroll to (defaults to parent event ID) |
|
onParentLoaded?: (event: NostrEvent) => void; // Callback when parent is loaded |
|
} |
|
|
|
let { parentEvent: providedParentEvent, parentEventId, targetId, onParentLoaded }: Props = $props(); |
|
|
|
let loadedParentEvent = $state<NostrEvent | null>(null); |
|
let loadingParent = $state(false); |
|
|
|
// Derive the effective parent event: prefer provided, fall back to loaded |
|
let parentEvent = $derived(providedParentEvent || loadedParentEvent); |
|
|
|
// Sync provided parent event changes and load if needed |
|
$effect(() => { |
|
if (providedParentEvent) { |
|
// If provided parent event is available, use it |
|
return; |
|
} |
|
|
|
// If no provided parent event and we have an ID, try to load it |
|
if (!loadedParentEvent && parentEventId && !loadingParent) { |
|
loadParentEvent(); |
|
} |
|
}); |
|
|
|
async function loadParentEvent() { |
|
const eventId = parentEventId || parentEvent?.id; |
|
if (!eventId || loadingParent) return; |
|
|
|
loadingParent = true; |
|
try { |
|
const relays = relayManager.getFeedReadRelays(); |
|
const events = await nostrClient.fetchEvents( |
|
[{ kinds: [1], ids: [eventId] }], |
|
relays, |
|
{ useCache: true, cacheResults: true } |
|
); |
|
|
|
if (events.length > 0) { |
|
loadedParentEvent = events[0]; |
|
if (onParentLoaded) { |
|
onParentLoaded(loadedParentEvent); |
|
} |
|
// After loading, try to scroll to it |
|
setTimeout(() => scrollToParent(), 100); |
|
} |
|
} catch (error) { |
|
console.error('Error loading parent event:', error); |
|
} finally { |
|
loadingParent = false; |
|
} |
|
} |
|
|
|
function getParentPreview(): string { |
|
if (!parentEvent) { |
|
return loadingParent ? 'Loading...' : 'Parent event not found'; |
|
} |
|
// Create preview from parent (first 100 chars, plaintext with markdown stripped) |
|
const plaintext = stripMarkdown(parentEvent.content); |
|
return plaintext.slice(0, 100) + (plaintext.length > 100 ? '...' : ''); |
|
} |
|
|
|
async function scrollToParent() { |
|
const eventId = parentEvent?.id || parentEventId; |
|
if (!eventId) return; |
|
|
|
// If parent not loaded yet, load it first |
|
if (!parentEvent && parentEventId) { |
|
await loadParentEvent(); |
|
} |
|
|
|
const elementId = targetId || `event-${eventId}`; |
|
let element = document.getElementById(elementId) || document.querySelector(`[data-event-id="${eventId}"]`); |
|
|
|
// If still not found, wait a bit for DOM to update |
|
if (!element) { |
|
await new Promise(resolve => setTimeout(resolve, 200)); |
|
element = document.getElementById(elementId) || document.querySelector(`[data-event-id="${eventId}"]`); |
|
} |
|
|
|
if (element) { |
|
element.scrollIntoView({ behavior: 'smooth', block: 'center' }); |
|
element.classList.add('highlight-parent'); |
|
setTimeout(() => { |
|
element?.classList.remove('highlight-parent'); |
|
}, 2000); |
|
} |
|
} |
|
</script> |
|
|
|
<div |
|
class="reply-context mb-2 p-2 bg-fog-highlight dark:bg-fog-dark-highlight rounded text-xs text-fog-text-light dark:text-fog-dark-text-light cursor-pointer hover:opacity-80 transition-opacity" |
|
onclick={scrollToParent} |
|
role="button" |
|
tabindex="0" |
|
onkeydown={(e) => { |
|
if (e.key === 'Enter' || e.key === ' ') { |
|
e.preventDefault(); |
|
scrollToParent(); |
|
} |
|
}} |
|
> |
|
<span class="font-semibold">Replying to:</span> {getParentPreview()} |
|
{#if loadingParent} |
|
<span class="text-xs opacity-70"> (loading...)</span> |
|
{/if} |
|
</div> |
|
|
|
<style> |
|
.reply-context { |
|
border-left: 2px solid var(--fog-accent, #64748b); |
|
} |
|
|
|
:global(.dark) .reply-context { |
|
border-left-color: var(--fog-dark-accent, #64748b); |
|
} |
|
|
|
:global(.highlight-parent) { |
|
outline: 2px solid var(--fog-accent, #64748b); |
|
outline-offset: 2px; |
|
transition: outline 0.3s ease; |
|
} |
|
|
|
:global(.dark .highlight-parent) { |
|
outline-color: var(--fog-dark-accent, #64748b); |
|
} |
|
</style>
|
|
|