Feature: Implement NIP-68 (Picture-first feeds) (#26)

* update global feed to use kind 20 and kind 20 cards (NIP-68 Picture-first feeds)

* update profile quick view feed to use kind 20 and kind 20 cards (NIP-68 Picture-first feeds)

* refactor: move getImageUrl function to utils for better modularity

* show kind 20 notes on note subpage

* also show comments on kind 20 events

* update follower feed and follower quick view feed to use kind 20 and kind 20 cards (NIP-68 Picture-first feeds)

* update profile feed to use kind 20 and kind 20 cards (NIP-68 Picture-first feeds)

* update search feed to use kind 20 and kind 20 cards (NIP-68 Picture-first feeds)

* update tag feed to use kind 20 and kind 20 cards (NIP-68 Picture-first feeds)

* add relay.damus.io

* update KIND20Card and QuickViewKind20NoteCard to use Next.js Image component and extract dimensions from event metadata
This commit is contained in:
mroxso
2025-01-18 02:03:12 +01:00
committed by GitHub
parent 11400d553b
commit 2c9bae18f0
27 changed files with 326 additions and 88 deletions

View File

@@ -18,6 +18,7 @@ const DashboardPage: React.FC= ({ }) => {
const relayUrls = [
"wss://relay.nostr.band",
"wss://relay.damus.io",
];
return (

View File

@@ -29,6 +29,7 @@ export default function FeedPage() {
const relayUrls = [
"wss://relay.nostr.band",
"wss://relay.damus.io",
];
return (

View File

@@ -7,6 +7,7 @@ export default function Home() {
const relayUrls = [
"wss://relay.nostr.band",
"wss://relay.damus.io",
];
return (

View File

@@ -17,6 +17,7 @@ export default function NotePage() {
const relayUrls = [
"wss://relay.nostr.band",
"wss://relay.damus.io",
];
return (

View File

@@ -20,6 +20,7 @@ const NotificationsPage: React.FC= ({ }) => {
const relayUrls = [
"wss://relay.nostr.band",
"wss://relay.damus.io",
];
return (

View File

@@ -8,6 +8,7 @@ export default function OnboardingCreateProfile() {
const relayUrls = [
"wss://relay.nostr.band",
"wss://relay.damus.io",
];
return (

View File

@@ -9,6 +9,7 @@ export default function OnboardingHome() {
const relayUrls = [
"wss://relay.nostr.band",
"wss://relay.damus.io",
];
return (

View File

@@ -9,7 +9,8 @@ import { NostrProvider } from "nostr-react";
export default function Home() {
const relayUrls = [
"wss://relay.nostr.band",
// "wss://relay.nostr.band",
"wss://relay.damus.io",
];
return (

View File

@@ -24,6 +24,7 @@ export default function ProfilePage() {
const relayUrls = [
"wss://relay.nostr.band",
"wss://relay.damus.io",
];
return (

View File

@@ -20,6 +20,7 @@ export default function ProfileSettingsPage() {
const relayUrls = [
"wss://relay.nostr.band",
"wss://relay.damus.io",
];
return (

View File

@@ -6,6 +6,7 @@ import { NostrProvider } from "nostr-react";
export default function ReelPage() {
const relayUrls = [
"wss://relay.nostr.band",
"wss://relay.damus.io",
];
return (

View File

@@ -34,6 +34,7 @@ export default function SearchPage() {
const relayUrls = [
"wss://relay.nostr.band",
"wss://relay.damus.io",
];
return (

View File

@@ -10,6 +10,7 @@ export default function SearchMainPage() {
const relayUrls = [
"wss://relay.nostr.band",
"wss://relay.damus.io",
];
return (

View File

@@ -23,6 +23,7 @@ export default function Home() {
const relayUrls = [
"wss://relay.nostr.band",
"wss://relay.damus.io",
];
return (

View File

@@ -26,6 +26,7 @@ export default function UploadPage() {
const relayUrls = [
"wss://relay.nostr.band",
"wss://relay.damus.io",
];
return (

View File

@@ -14,7 +14,7 @@ const CommentsCompontent: React.FC<CommentsCompontentProps> = ({ pubkey, event }
const { events } = useNostrEvents({
filter: {
kinds: [1],
// kinds: [1],
'#e': [event.id],
},
});

View File

@@ -1,6 +1,8 @@
import { useRef } from "react";
import { useNostrEvents, dateToUnix } from "nostr-react";
import NoteCard from './NoteCard';
import KIND20Card from "./KIND20Card";
import { getImageUrl } from "@/utils/utils";
interface FollowerFeedProps {
pubkey: string;
@@ -25,26 +27,17 @@ const FollowerFeed: React.FC<FollowerFeedProps> = ({ pubkey }) => {
// since: dateToUnix(now.current), // all new events from now
// since: 0,
limit: 1000,
kinds: [1],
kinds: [20],
authors: followingPubkeys,
},
});
// const filteredEvents = events.filter((event) => event.content.includes(".jpg"));
// filter events with regex that checks for png, jpg, or gif
let filteredEvents = events.filter((event) => event.content.match(/https?:\/\/.*\.(?:png|jpg|gif|mp4|webm|jpeg)/g)?.[0]);
// now filter all events with a tag[0] == t and tag[1] == nsfw
filteredEvents = filteredEvents.filter((event) => event.tags.map((tag) => tag[0] == "t" && tag[1] == "nsfw"));
// filter out all replies
filteredEvents = filteredEvents.filter((event) => !event.tags.some((tag) => { return tag[0] == 'e' }));
return (
<>
{filteredEvents.map((event) => (
{events.map((event) => (
// <p key={event.id}>{event.pubkey} posted: {event.content}</p>
<div key={event.id} className="py-6">
<NoteCard key={event.id} pubkey={event.pubkey} text={event.content} eventId={event.id} tags={event.tags} event={event} showViewNoteCardButton={true} />
<KIND20Card key={event.id} pubkey={event.pubkey} text={event.content} image={getImageUrl(event.tags)} eventId={event.id} tags={event.tags} event={event} showViewNoteCardButton={true} />
</div>
))}
</>

View File

@@ -2,6 +2,8 @@ import { useRef } from "react";
import { useNostrEvents, dateToUnix } from "nostr-react";
import { Skeleton } from "@/components/ui/skeleton";
import QuickViewNoteCard from "./QuickViewNoteCard";
import QuickViewKind20NoteCard from "./QuickViewKind20NoteCard";
import { getImageUrl } from "@/utils/utils";
interface FollowerQuickViewFeedProps {
pubkey: string;
@@ -26,19 +28,15 @@ const FollowerQuickViewFeed: React.FC<FollowerQuickViewFeedProps> = ({ pubkey })
// since: dateToUnix(now.current), // all new events from now
// since: 0,
limit: 1000,
kinds: [1],
kinds: [20],
authors: followingPubkeys,
},
});
let filteredEvents = events.filter((event) => event.content.match(/https?:\/\/.*\.(?:png|jpg|gif|jpeg)/g)?.[0]);
// filter out all replies (tag[0] == e)
filteredEvents = filteredEvents.filter((event) => !event.tags.some((tag) => { return tag[0] == 'e' }));
return (
<>
<div className="grid grid-cols-3 gap-2">
{filteredEvents.length === 0 ? (
{events.length === 0 ? (
<>
<div>
<Skeleton className="h-[125px] rounded-xl" />
@@ -62,8 +60,8 @@ const FollowerQuickViewFeed: React.FC<FollowerQuickViewFeedProps> = ({ pubkey })
</div>
</div>
</>
) : (filteredEvents.map((event) => (
<QuickViewNoteCard key={event.id} pubkey={event.pubkey} text={event.content} event={event} tags={event.tags} eventId={event.id} linkToNote={true} />
) : (events.map((event) => (
<QuickViewKind20NoteCard key={event.id} pubkey={event.pubkey} text={event.content} image={getImageUrl(event.tags)} event={event} tags={event.tags} eventId={event.id} linkToNote={true} />
)))}
</div>
</>

View File

@@ -1,40 +1,38 @@
import { useRef } from "react";
import { useNostrEvents, dateToUnix } from "nostr-react";
import NoteCard from './NoteCard';
import { useNostrEvents } from "nostr-react";
import KIND20Card from "./KIND20Card";
import { getImageUrl } from "@/utils/utils";
const GlobalFeed: React.FC = () => {
const now = useRef(new Date()); // Make sure current time isn't re-rendered
const { events } = useNostrEvents({
filter: {
// since: dateToUnix(now.current), // all new events from now
// since: 0,
limit: 100,
kinds: [1],
kinds: [20],
},
});
// const filteredEvents = events.filter((event) => event.content.includes(".jpg"));
// filter events with regex that checks for png, jpg, or gif
let filteredEvents = events.filter((event) => event.content.match(/https?:\/\/.*\.(?:png|jpg|gif|jpeg)/g)?.[0]);
// now filter all events with a tag[0] == t and tag[1] == nsfw
// filteredEvents = filteredEvents.filter((event) => event.tags.map((tag) => tag[0] == "t" && tag[1] == "nsfw"));
filteredEvents = filteredEvents.filter((event) => !event.tags.some((tag) => { return tag[0] == 't' && tag[1] == 'nsfw'}));
// filter out all replies
filteredEvents = filteredEvents.filter((event) => !event.tags.some((tag) => { return tag[0] == 'e' }));
return (
<>
<h2>Global Feed</h2>
{filteredEvents.map((event) => (
// <p key={event.id}>{event.pubkey} posted: {event.content}</p>
<div key={event.id} className="py-6">
<NoteCard key={event.id} pubkey={event.pubkey} text={event.content} eventId={event.id} tags={event.tags} event={event} showViewNoteCardButton={true} />
</div>
))}
{events.map((event) => {
const imageUrl = getImageUrl(event.tags);
return (
<div key={event.id} className="py-6">
<KIND20Card
key={event.id}
pubkey={event.pubkey}
text={event.content}
image={imageUrl}
eventId={event.id}
tags={event.tags}
event={event}
showViewNoteCardButton={true}
/>
</div>
);
})}
</>
);
}
export default GlobalFeed;
export default GlobalFeed;

View File

@@ -0,0 +1,124 @@
import React from 'react';
import { useProfile } from "nostr-react";
import {
nip19,
} from "nostr-tools";
import {
Card,
CardContent,
CardFooter,
CardHeader,
CardTitle,
} from "@/components/ui/card"
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip"
import {
Carousel,
CarouselContent,
CarouselItem,
CarouselNext,
CarouselPrevious,
} from "@/components/ui/carousel"
import ReactionButton from '@/components/ReactionButton';
import { Avatar, AvatarImage } from '@/components/ui/avatar';
import ViewRawButton from '@/components/ViewRawButton';
import ViewNoteButton from './ViewNoteButton';
import Link from 'next/link';
import ViewCopyButton from './ViewCopyButton';
import { Event as NostrEvent } from "nostr-tools";
import ZapButton from './ZapButton';
import Image from 'next/image';
import { extractDimensions } from '@/utils/utils';
interface KIND20CardProps {
pubkey: string;
text: string;
image: string;
eventId: string;
tags: string[][];
event: NostrEvent;
showViewNoteCardButton: boolean;
}
const KIND20Card: React.FC<KIND20CardProps> = ({ pubkey, text, image, eventId, tags, event, showViewNoteCardButton }) => {
const { data: userData } = useProfile({
pubkey,
});
const title = userData?.username || userData?.display_name || userData?.name || userData?.npub || nip19.npubEncode(pubkey);
text = text.replaceAll('\n', ' ');
const createdAt = new Date(event.created_at * 1000);
const hrefProfile = `/profile/${nip19.npubEncode(pubkey)}`;
const profileImageSrc = userData?.picture || "https://robohash.org/" + pubkey;
const { width, height } = extractDimensions(event);
return (
<>
<Card>
<CardHeader>
<CardTitle>
<Link href={hrefProfile} style={{ textDecoration: 'none' }}>
<TooltipProvider>
<Tooltip>
<TooltipTrigger>
<div style={{ display: 'flex', alignItems: 'center' }}>
<Avatar>
<AvatarImage src={profileImageSrc} />
</Avatar>
<span className='break-all' style={{ marginLeft: '10px' }}>{title}</span>
</div>
</TooltipTrigger>
<TooltipContent>
<p>{title}</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
</Link>
</CardTitle>
</CardHeader>
<CardContent>
<div className='py-4'>
<div className='w-full h-full px-10'>
{image && (
<Image
src={image || "/placeholder.svg"}
alt={text}
width={width}
height={height}
className='rounded lg:rounded-lg'
style={{ maxWidth: '100%', maxHeight: '66vh', objectFit: 'contain', margin: 'auto' }}
/>
)}
</div>
<br />
<div className='break-word overflow-hidden'>
{text}
</div>
</div>
<hr />
<div className='py-4 space-x-4 flex justify-between items-start'>
<div className='flex space-x-4'>
<ReactionButton event={event} />
<ZapButton event={event} />
{showViewNoteCardButton && <ViewNoteButton event={event} />}
</div>
<div className='flex space-x-2'>
<ViewCopyButton event={event} />
<ViewRawButton event={event} />
</div>
</div>
</CardContent>
<CardFooter>
<small className="text-muted">{createdAt.toLocaleString()}</small>
</CardFooter>
</Card>
</>
);
}
export default KIND20Card;

View File

@@ -2,6 +2,8 @@ import { useRef } from "react";
import { useNostrEvents } from "nostr-react";
import NoteCard from '@/components/NoteCard';
import CommentsCompontent from "@/components/CommentsCompontent";
import KIND20Card from "./KIND20Card";
import { getImageUrl } from "@/utils/utils";
interface NotePageComponentProps {
id: string;
@@ -12,23 +14,45 @@ const NotePageComponent: React.FC<NotePageComponentProps> = ({ id }) => {
const { events } = useNostrEvents({
filter: {
// since: dateToUnix(now.current), // all new events from now
// since: 0,
ids: [id],
limit: 1,
kinds: [1],
},
});
// filter out all events that also have another e tag with another id
const filteredEvents = events.filter((event) => { return event.tags.filter((tag) => { return tag[0] === '#e' && tag[1] !== id }).length === 0 });
const filteredEvents = events.filter((event) => {
return event.tags.filter((tag) => {
return tag[0] === '#e' && tag[1] !== id;
}).length === 0;
});
return (
<>
{events.map((event) => (
// <p key={event.id}>{event.pubkey} posted: {event.content}</p>
{filteredEvents.map((event) => (
<div key={event.id} className="py-6">
<NoteCard key={event.id} pubkey={event.pubkey} text={event.content} eventId={event.id} tags={event.tags} event={event} showViewNoteCardButton={false} />
{event.kind === 1 && (
<NoteCard
key={event.id}
pubkey={event.pubkey}
text={event.content}
eventId={event.id}
tags={event.tags}
event={event}
showViewNoteCardButton={false}
/>
)}
{event.kind === 20 && (
<KIND20Card
key={event.id}
pubkey={event.pubkey}
text={event.content}
image={getImageUrl(event.tags)}
eventId={event.id}
tags={event.tags}
event={event}
showViewNoteCardButton={false}
/>
)}
<div className="py-6 px-6">
<CommentsCompontent pubkey={event.pubkey} event={event} />
</div>

View File

@@ -2,6 +2,8 @@ import { useRef } from "react";
import { useNostrEvents, dateToUnix } from "nostr-react";
import NoteCard from '@/components/NoteCard';
import { Skeleton } from "@/components/ui/skeleton";
import KIND20Card from "./KIND20Card";
import { getImageUrl } from "@/utils/utils";
interface ProfileFeedProps {
pubkey: string;
@@ -16,19 +18,15 @@ const ProfileFeed: React.FC<ProfileFeedProps> = ({ pubkey }) => {
authors: [pubkey],
// since: 0,
// limit: 10,
kinds: [1],
kinds: [20],
},
});
let filteredEvents = events.filter((event) => event.content.match(/https?:\/\/.*\.(?:png|jpg|gif|mp4|webm|mov|jpeg)/g)?.[0]);
// filter out all replies (tag[0] == e)
filteredEvents = filteredEvents.filter((event) => !event.tags.some((tag) => { return tag[0] == 'e' }));
return (
<>
{/* <h2>Profile Feed</h2> */}
{filteredEvents.length === 0 ? (
{events.length === 0 ? (
<div className="flex flex-col space-y-3">
<Skeleton className="h-[125px] rounded-xl" />
<div className="space-y-2">
@@ -36,11 +34,11 @@ const ProfileFeed: React.FC<ProfileFeedProps> = ({ pubkey }) => {
<Skeleton className="h-4 w-[200px]" />
</div>
</div>
) : (filteredEvents.map((event) => (
) : (events.map((event) => (
// <p key={event.id}>{event.pubkey} posted: {event.content}</p>
// <ProfileNoteCard key={event.id} pubkey={event.pubkey} text={event.content} event={event} tags={event.tags} />
<div key={event.id} className="py-6">
<NoteCard key={event.id} pubkey={event.pubkey} text={event.content} event={event} tags={event.tags} eventId={event.id} showViewNoteCardButton={true}/>
<KIND20Card key={event.id} pubkey={event.pubkey} text={event.content} image={getImageUrl(event.tags)} event={event} tags={event.tags} eventId={event.id} showViewNoteCardButton={true}/>
</div>
)))}
</>

View File

@@ -1,8 +1,9 @@
import { useRef, useState } from "react";
import { useNostrEvents, dateToUnix } from "nostr-react";
import { useNostrEvents } from "nostr-react";
import { Skeleton } from "@/components/ui/skeleton";
import QuickViewNoteCard from "./QuickViewNoteCard";
import { Button } from "@/components/ui/button";
import QuickViewKind20NoteCard from "./QuickViewKind20NoteCard";
import { getImageUrl } from "@/utils/utils";
interface ProfileQuickViewFeedProps {
pubkey: string;
@@ -12,18 +13,14 @@ const ProfileQuickViewFeed: React.FC<ProfileQuickViewFeedProps> = ({ pubkey }) =
const now = useRef(new Date()); // Make sure current time isn't re-rendered
const [limit, setLimit] = useState(100);
const { isLoading ,events } = useNostrEvents({
const { isLoading, events } = useNostrEvents({
filter: {
authors: [pubkey],
limit: limit,
kinds: [1],
kinds: [20],
},
});
let filteredEvents = events.filter((event) => event.content.match(/https?:\/\/.*\.(?:png|jpg|gif|mp4|webm|mov|jpeg)/g)?.[0]);
// filter out all replies (tag[0] == e)
filteredEvents = filteredEvents.filter((event) => !event.tags.some((tag) => { return tag[0] == 'e' }));
const loadMore = () => {
setLimit(limit => limit + 50);
}
@@ -31,7 +28,7 @@ const ProfileQuickViewFeed: React.FC<ProfileQuickViewFeedProps> = ({ pubkey }) =
return (
<>
<div className="grid grid-cols-3 gap-2">
{filteredEvents.length === 0 && isLoading ? (
{events.length === 0 && isLoading ? (
<>
<div>
<Skeleton className="h-[125px] rounded-xl" />
@@ -45,9 +42,12 @@ const ProfileQuickViewFeed: React.FC<ProfileQuickViewFeedProps> = ({ pubkey }) =
</>
) : (
<>
{filteredEvents.map((event) => (
<QuickViewNoteCard key={event.id} pubkey={event.pubkey} text={event.content} event={event} tags={event.tags} eventId={event.id} linkToNote={true} />
))}
{events.map((event) => {
const imageUrl = getImageUrl(event.tags);
return (
<QuickViewKind20NoteCard key={event.id} pubkey={event.pubkey} text={event.content} image={imageUrl} event={event} tags={event.tags} eventId={event.id} linkToNote={true} />
);
})}
</>
)}
</div>

View File

@@ -0,0 +1,65 @@
import React from 'react';
import { useProfile } from "nostr-react";
import {
nip19,
} from "nostr-tools";
import {
Card,
SmallCardContent,
} from "@/components/ui/card"
import Link from 'next/link';
import Image from 'next/image';
import { extractDimensions } from '@/utils/utils';
interface QuickViewKind20NoteCardProps {
pubkey: string;
text: string;
image: string;
eventId: string;
tags: string[][];
event: any;
linkToNote: boolean;
}
const QuickViewKind20NoteCard: React.FC<QuickViewKind20NoteCardProps> = ({ pubkey, text, image, eventId, tags, event, linkToNote }) => {
text = text.replaceAll('\n', ' ');
const encodedNoteId = nip19.noteEncode(event.id)
const { width, height } = extractDimensions(event);
const card = (
<Card>
<SmallCardContent>
<div>
<div className='d-flex justify-content-center align-items-center'>
<div style={{ position: 'relative' }}>
<Image
src={image || "/placeholder.svg"}
alt={text}
width={width}
height={height}
className='rounded lg:rounded-lg'
style={{ maxWidth: '100%', maxHeight: '75vh', objectFit: 'contain', margin: 'auto' }}
/>
</div>
</div>
</div>
</SmallCardContent>
</Card>
);
return (
<>
{linkToNote ? (
<Link href={`/note/${encodedNoteId}`}>
{card}
</Link>
) : (
card
)}
</>
);
}
export default QuickViewKind20NoteCard;

View File

@@ -1,6 +1,8 @@
import { useRef } from "react";
import { useNostrEvents, dateToUnix } from "nostr-react";
import NoteCard from './NoteCard';
import KIND20Card from "./KIND20Card";
import { getImageUrl } from "@/utils/utils";
interface TagFeedProps {
tag: string;
@@ -14,27 +16,18 @@ const TagFeed: React.FC<TagFeedProps> = ({tag}) => {
// since: dateToUnix(now.current), // all new events from now
// since: 0,
// limit: 100,
kinds: [1],
kinds: [20],
"#t": [tag],
},
});
// const filteredEvents = events.filter((event) => event.content.includes(".jpg"));
// filter events with regex that checks for png, jpg, or gif
let filteredEvents = events.filter((event) => event.content.match(/https?:\/\/.*\.(?:png|jpg|gif|mp4|webm|jpeg)/g)?.[0]);
// now filter all events with a tag[0] == t and tag[1] == nsfw
filteredEvents = filteredEvents.filter((event) => event.tags.map((tag) => tag[0] == "t" && tag[1] == "nsfw"));
// filter out all replies
filteredEvents = filteredEvents.filter((event) => !event.tags.some((tag) => { return tag[0] == 'e' }));
return (
<>
<h2>Tag Feed for {tag}</h2>
{filteredEvents.map((event) => (
{events.map((event) => (
// <p key={event.id}>{event.pubkey} posted: {event.content}</p>
<div key={event.id} className="py-6">
<NoteCard key={event.id} pubkey={event.pubkey} text={event.content} eventId={event.id} tags={event.tags} event={event} showViewNoteCardButton={true} />
<KIND20Card key={event.id} pubkey={event.pubkey} text={event.content} image={getImageUrl(event.tags)} eventId={event.id} tags={event.tags} event={event} showViewNoteCardButton={true} />
</div>
))}
</>

View File

@@ -30,6 +30,8 @@ import Link from 'next/link';
import { Event as NostrEvent } from "nostr-tools";
import ProfileInfoCard from '../ProfileInfoCard';
import NoteCard from '../NoteCard';
import KIND20Card from '../KIND20Card';
import { getImageUrl } from '@/utils/utils';
interface SearchNotesBoxProps {
searchTag: string;
@@ -38,7 +40,7 @@ interface SearchNotesBoxProps {
const SearchNotesBox: React.FC<SearchNotesBoxProps> = ({ searchTag }) => {
const { events: notes } = useNostrEvents({
filter: {
kinds: [1],
kinds: [1, 20],
search: searchTag,
limit: 10,
},
@@ -53,7 +55,11 @@ const SearchNotesBox: React.FC<SearchNotesBoxProps> = ({ searchTag }) => {
<CardContent>
<div className="grid grid-cols-1 gap-6">
{notes.map((event: NostrEvent) => (
<NoteCard event={event} eventId={event.id} pubkey={event.pubkey} showViewNoteCardButton={true} tags={event.tags} text={event.content} key={event.id} />
event.kind === 1 ? (
<NoteCard event={event} eventId={event.id} pubkey={event.pubkey} showViewNoteCardButton={true} tags={event.tags} text={event.content} key={event.id} />
) : event.kind === 20 ? (
<KIND20Card key={event.id} pubkey={event.pubkey} text={event.content} image={getImageUrl(event.tags)} event={event} tags={event.tags} eventId={event.id} showViewNoteCardButton={true}/>
) : null
))}
</div>
</CardContent>

24
lumina/utils/utils.ts Normal file
View File

@@ -0,0 +1,24 @@
import { Event as NostrEvent } from "nostr-tools";
export function getImageUrl(tags: string[][]): string {
const imetaTag = tags.find(tag => tag[0] === 'imeta');
if (imetaTag) {
const urlItem = imetaTag.find(item => item.startsWith('url '));
if (urlItem) {
return urlItem.split(' ')[1];
}
}
return '';
}
export function extractDimensions(event: NostrEvent): { width: number; height: number } {
const imetaTag = event.tags.find(tag => tag[0] === 'imeta');
if (imetaTag) {
const dimInfo = imetaTag.find(item => item.startsWith('dim '));
if (dimInfo) {
const [width, height] = dimInfo.split(' ')[1].split('x').map(Number);
return { width, height };
}
}
return { width: 500, height: 300 }; // Default dimensions if not found
}