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

<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>