clone of repo on github
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.
 
 
 
 

146 lines
4.3 KiB

<script lang="ts">
import type { NDKEvent } from "@nostr-dev-kit/ndk";
import { scale } from "svelte/transition";
import { Card } from "flowbite-svelte";
import { userBadge } from "$lib/snippets/UserSnippets.svelte";
import Interactions from "$components/util/Interactions.svelte";
import { quintOut } from "svelte/easing";
import CardActions from "$components/util/CardActions.svelte";
import { getMatchingTags } from "$lib/utils/nostrUtils";
import LazyImage from "$components/util/LazyImage.svelte";
import { generateDarkPastelColor } from "$lib/utils/image_utils";
import { getNdkContext } from "$lib/ndk";
import { deleteEvent } from "$lib/services/deletion";
const {
rootId,
event,
onBlogUpdate,
active = true,
} = $props<{
rootId: string;
event: NDKEvent;
onBlogUpdate?: any;
active: boolean;
}>();
const ndk = getNdkContext();
/**
* Handle deletion of this blog article
*/
async function handleDelete() {
const confirmed = confirm(
"Are you sure you want to delete this article? This action will publish a deletion request to all relays."
);
if (!confirmed) return;
try {
await deleteEvent({
eventAddress: event.tagAddress(),
eventKind: event.kind,
reason: "User deleted article",
onSuccess: (deletionEventId) => {
console.log("[BlogHeader] Deletion event published:", deletionEventId);
// Call onBlogUpdate if provided to refresh the list
if (onBlogUpdate) {
onBlogUpdate();
}
},
onError: (error) => {
console.error("[BlogHeader] Deletion failed:", error);
alert(`Failed to delete article: ${error}`);
},
}, ndk);
} catch (error) {
console.error("[BlogHeader] Deletion error:", error);
}
}
let title: string = $derived(event.getMatchingTags("title")[0]?.[1]);
let author: string = $derived(
getMatchingTags(event, "author")[0]?.[1] ?? "unknown",
);
let image: string = $derived(event.getMatchingTags("image")[0]?.[1] ?? null);
let authorPubkey: string = $derived(
event.getMatchingTags("p")[0]?.[1] ?? null,
);
let hashtags: string = $derived(event.getMatchingTags("t") ?? null);
function publishedAt() {
const date = event.created_at ? new Date(event.created_at * 1000) : "";
if (date !== "") {
const formattedDate = new Intl.DateTimeFormat("en-US", {
year: "numeric",
month: "short",
day: "2-digit",
}).format(date);
return formattedDate ?? "";
}
return "";
}
function showBlog() {
onBlogUpdate?.(rootId);
}
</script>
{#if title != null}
<Card
class="ArticleBox card-leather w-full grid max-w-xl {active
? 'active'
: ''}"
>
<div class="space-y-4 relative">
<div class="flex flex-row justify-between my-2">
<div class="flex flex-col">
{@render userBadge(authorPubkey, author, ndk)}
<span class="text-gray-700 dark:text-gray-300">{publishedAt()}</span>
</div>
</div>
<div
class="ArticleBoxImage flex justify-center items-center p-2 h-40 -mt-2"
in:scale={{ start: 0.8, duration: 500, delay: 100, easing: quintOut }}
>
{#if image}
<LazyImage
src={image}
alt={title || "Publication image"}
eventId={event.id}
className="rounded w-full h-full object-cover"
/>
{:else}
<div
class="rounded w-full h-full"
style="background-color: {generateDarkPastelColor(event.id)};"
>
</div>
{/if}
</div>
<div class="flex flex-col space-y-4">
<button onclick={() => showBlog()} class="text-left">
<h2 class="text-lg font-bold line-clamp-2" {title}>{title}</h2>
</button>
{#if hashtags}
<div class="tags">
{#each hashtags as tag}
<span class="mr-2">#{tag}</span>
{/each}
</div>
{/if}
</div>
{#if active}
<Interactions {rootId} {event} />
{/if}
<!-- Position CardActions at bottom-right -->
<div class="absolute bottom-2 right-2">
<CardActions {event} onDelete={handleDelete} />
</div>
</div>
</Card>
{/if}