2026-06-23 23:30:54 +00:00
|
|
|
<script lang="ts">
|
|
|
|
|
import * as nip19 from "nostr-tools/nip19"
|
2026-07-08 17:29:56 +00:00
|
|
|
import {removeUndefined} from "@welshman/lib"
|
2026-06-23 23:30:54 +00:00
|
|
|
import {Address} from "@welshman/util"
|
2026-07-28 16:16:26 +00:00
|
|
|
import type {PinReader} from "@welshman/domain"
|
2026-06-23 23:30:54 +00:00
|
|
|
import Badge from "@lib/components/Badge.svelte"
|
2026-07-08 17:29:56 +00:00
|
|
|
import MenuButton from "@lib/components/MenuButton.svelte"
|
2026-06-23 23:30:54 +00:00
|
|
|
import Content from "@app/components/Content.svelte"
|
|
|
|
|
import PinContentEvent from "@app/components/PinContentEvent.svelte"
|
2026-07-03 16:20:45 +00:00
|
|
|
import PinMenu from "@app/components/PinMenu.svelte"
|
2026-07-28 16:16:26 +00:00
|
|
|
import {pinToReference} from "@app/pinboards"
|
2026-06-23 23:30:54 +00:00
|
|
|
|
|
|
|
|
type Props = {
|
|
|
|
|
url: string
|
2026-07-28 16:16:26 +00:00
|
|
|
pin: PinReader
|
2026-06-23 23:30:54 +00:00
|
|
|
minimal?: boolean
|
|
|
|
|
class?: string
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-08 17:29:56 +00:00
|
|
|
const {url, pin, minimal, ...props}: Props = $props()
|
2026-06-23 23:30:54 +00:00
|
|
|
|
|
|
|
|
// Encode nostr references as nostr: entities (i tags are already urls) so
|
|
|
|
|
// Content parses them into rich embeds rather than rendering raw ids/coords.
|
2026-07-28 16:16:26 +00:00
|
|
|
const reference = $derived(pin.reference())
|
2026-06-23 23:30:54 +00:00
|
|
|
|
2026-07-28 16:16:26 +00:00
|
|
|
const content = $derived.by(() => {
|
|
|
|
|
if (reference?.type === "event") {
|
|
|
|
|
return "nostr:" + nip19.neventEncode({id: reference.id, relays: [url]})
|
|
|
|
|
}
|
2026-07-27 22:16:21 +00:00
|
|
|
|
2026-07-28 16:16:26 +00:00
|
|
|
if (reference?.type === "address") {
|
|
|
|
|
const {kind, pubkey, identifier} = Address.from(reference.address)
|
2026-06-23 23:30:54 +00:00
|
|
|
|
|
|
|
|
return "nostr:" + nip19.naddrEncode({kind, pubkey, identifier, relays: [url]})
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-28 16:16:26 +00:00
|
|
|
return pinToReference(pin)
|
2026-06-23 23:30:54 +00:00
|
|
|
})
|
|
|
|
|
</script>
|
|
|
|
|
|
2026-07-08 17:29:56 +00:00
|
|
|
<div class="{props.class} flex h-full flex-col gap-2 {minimal ? '' : 'card relative'}">
|
|
|
|
|
{#if !minimal}
|
2026-06-23 23:30:54 +00:00
|
|
|
<div class="absolute right-2 top-2 z-feature">
|
2026-07-03 16:20:45 +00:00
|
|
|
<MenuButton
|
2026-06-23 23:30:54 +00:00
|
|
|
class="button button-neutral button-sm button-square"
|
|
|
|
|
aria-label="More options"
|
2026-07-03 16:20:45 +00:00
|
|
|
iconSize={4}
|
|
|
|
|
component={PinMenu}
|
|
|
|
|
componentProps={{url, pin}} />
|
2026-06-23 23:30:54 +00:00
|
|
|
</div>
|
2026-07-08 17:29:56 +00:00
|
|
|
{/if}
|
2026-07-28 16:16:26 +00:00
|
|
|
{#if pin.title()}
|
|
|
|
|
<strong class="truncate min-w-0 pr-8">{pin.title()}</strong>
|
2026-06-23 23:30:54 +00:00
|
|
|
{/if}
|
2026-07-28 16:16:26 +00:00
|
|
|
{#if pin.content()}
|
|
|
|
|
<Content event={{content: pin.content(), tags: []}} {url} />
|
2026-07-06 18:53:12 +00:00
|
|
|
{/if}
|
2026-07-28 16:16:26 +00:00
|
|
|
{#if reference?.type === "event" || reference?.type === "address"}
|
|
|
|
|
<PinContentEvent
|
|
|
|
|
{url}
|
|
|
|
|
value={reference.type === "event" ? reference.id : reference.address}
|
|
|
|
|
relays={removeUndefined([reference.relay, url])} />
|
2026-07-27 22:16:21 +00:00
|
|
|
{:else}
|
|
|
|
|
<Content event={{content, tags: []}} {url} />
|
2026-06-23 23:30:54 +00:00
|
|
|
{/if}
|
2026-07-28 16:16:26 +00:00
|
|
|
{#if !minimal && pin.topics().length > 0}
|
2026-06-23 23:30:54 +00:00
|
|
|
<div class="mt-auto flex flex-wrap gap-1">
|
2026-07-28 16:16:26 +00:00
|
|
|
{#each pin.topics() as topic (topic)}
|
2026-06-23 23:30:54 +00:00
|
|
|
<Badge>#{topic}</Badge>
|
|
|
|
|
{/each}
|
|
|
|
|
</div>
|
|
|
|
|
{/if}
|
|
|
|
|
</div>
|