Compare commits
10 Commits
3fa54dea99
...
b2dfaa18d1
Author | SHA1 | Date | |
---|---|---|---|
![]() |
b2dfaa18d1 | ||
![]() |
173d5e2bf3 | ||
![]() |
d820d33af0 | ||
![]() |
ff6ebb349e | ||
![]() |
866628501c | ||
![]() |
fe66367c1b | ||
![]() |
6719fef9e9 | ||
![]() |
c0ac236a5f | ||
![]() |
401d7cdcfe | ||
![]() |
753210b6b2 |
@ -1,18 +1,31 @@
|
|||||||
import type { Metadata, ResolvingMetadata } from "next";
|
import type { Metadata, ResolvingMetadata } from "next";
|
||||||
import { get } from "@/lib/server-actions/events/cache";
|
import { get } from "@/lib/server-actions/events/cache";
|
||||||
|
import { getEvent } from "@/lib/server-actions/meta/event";
|
||||||
|
import { nip19 } from "nostr-tools";
|
||||||
|
import { getTagValues } from "@/lib/nostr/utils";
|
||||||
|
|
||||||
export async function generateMetadata(
|
export async function generateMetadata(
|
||||||
{ params }: { params: { naddr: string } },
|
{ params }: { params: { naddr: string } },
|
||||||
parent: ResolvingMetadata,
|
parent: ResolvingMetadata,
|
||||||
): Promise<Metadata> {
|
): Promise<Metadata> {
|
||||||
|
const previousImages = (await parent).openGraph?.images || [];
|
||||||
|
|
||||||
// read route params
|
// read route params
|
||||||
const identifier = params.naddr;
|
const identifier = params.naddr;
|
||||||
|
const { data, type } = nip19.decode(identifier);
|
||||||
|
if (type !== "naddr") {
|
||||||
|
return {
|
||||||
|
title: "Flockstr Calendar",
|
||||||
|
openGraph: {
|
||||||
|
images: previousImages,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// fetch data
|
// fetch data
|
||||||
const event = await get(identifier);
|
const event = await getEvent(data.kind, data.pubkey, data.identifier);
|
||||||
|
|
||||||
// optionally access and extend (rather than replace) parent metadata
|
// optionally access and extend (rather than replace) parent metadata
|
||||||
const previousImages = (await parent).openGraph?.images || [];
|
|
||||||
if (!event) {
|
if (!event) {
|
||||||
return {
|
return {
|
||||||
title: "Flockstr Calendar",
|
title: "Flockstr Calendar",
|
||||||
@ -21,21 +34,24 @@ export async function generateMetadata(
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
const title = `${event.name} | Flockstr`;
|
|
||||||
const images = event.image
|
const title = `${getTagValues("name", event.tags as string[][])} | Flockstr`;
|
||||||
? [event.image, ...previousImages]
|
const images =
|
||||||
: previousImages;
|
getTagValues("image", event.tags as string[][]) ??
|
||||||
|
getTagValues("banner", event.tags as string[][]) ??
|
||||||
|
"";
|
||||||
|
|
||||||
return {
|
return {
|
||||||
title: title,
|
title: title,
|
||||||
description: event.description,
|
description: event.content,
|
||||||
openGraph: {
|
openGraph: {
|
||||||
title: title,
|
title: title,
|
||||||
description: event.description,
|
description: event.content,
|
||||||
images: images,
|
images: images,
|
||||||
},
|
},
|
||||||
twitter: {
|
twitter: {
|
||||||
title: title,
|
title: title,
|
||||||
description: event.description,
|
description: event.content,
|
||||||
images: images,
|
images: images,
|
||||||
card: "summary_large_image",
|
card: "summary_large_image",
|
||||||
},
|
},
|
||||||
|
@ -45,7 +45,7 @@ export default function Header({ event }: { event: NDKEvent }) {
|
|||||||
const { pubkey, tags } = event;
|
const { pubkey, tags } = event;
|
||||||
const { profile } = useProfile(pubkey);
|
const { profile } = useProfile(pubkey);
|
||||||
const eventReference = event.tagId();
|
const eventReference = event.tagId();
|
||||||
const title = getTagValues("name", tags) ?? "Untitled";
|
const title = getTagValues("title", tags) ?? "Untitled";
|
||||||
const image =
|
const image =
|
||||||
getTagValues("image", tags) ??
|
getTagValues("image", tags) ??
|
||||||
getTagValues("picture", tags) ??
|
getTagValues("picture", tags) ??
|
||||||
|
@ -29,7 +29,7 @@ export default function RSVPButton({ event }: RSVPButtonProps) {
|
|||||||
const { currentUser } = useCurrentUser();
|
const { currentUser } = useCurrentUser();
|
||||||
const { ndk } = useNDK();
|
const { ndk } = useNDK();
|
||||||
const eventReference = event.tagId();
|
const eventReference = event.tagId();
|
||||||
const name = getTagValues("name", event.tags);
|
const name = getTagValues("title", event.tags);
|
||||||
const tickets = getTagValues("tickets", event.tags);
|
const tickets = getTagValues("tickets", event.tags);
|
||||||
const price = getTagAllValues("price", event.tags);
|
const price = getTagAllValues("price", event.tags);
|
||||||
const priceInBTC = parseFloat(getTagValues("price", event.tags) ?? "0");
|
const priceInBTC = parseFloat(getTagValues("price", event.tags) ?? "0");
|
||||||
@ -88,13 +88,14 @@ export default function RSVPButton({ event }: RSVPButtonProps) {
|
|||||||
|
|
||||||
if (!tickets) {
|
if (!tickets) {
|
||||||
if (rsvpEvent) {
|
if (rsvpEvent) {
|
||||||
const rsvpResponse = getTagValues("l", rsvpEvent.tags);
|
const rsvpResponse = getTagValues("status", rsvpEvent.tags);
|
||||||
|
const rsvpResponseDeprecated = getTagValues("l", rsvpEvent.tags);
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Button disabled>
|
<Button disabled>
|
||||||
{rsvpResponse === "accepted"
|
{rsvpResponse === "accepted" || rsvpResponseDeprecated === "accepted"
|
||||||
? "Going"
|
? "Going"
|
||||||
: rsvpResponse === "tentative"
|
: rsvpResponse === "tentative" || rsvpResponseDeprecated === "tentative"
|
||||||
? "Tentative"
|
? "Tentative"
|
||||||
: "Not Going"}
|
: "Not Going"}
|
||||||
</Button>
|
</Button>
|
||||||
|
@ -1,18 +1,29 @@
|
|||||||
import type { Metadata, ResolvingMetadata } from "next";
|
import type { Metadata, ResolvingMetadata } from "next";
|
||||||
import { get } from "@/lib/server-actions/events/cache";
|
import { getEvent } from "@/lib/server-actions/meta/event";
|
||||||
|
import { nip19 } from "nostr-tools";
|
||||||
|
import { getTagValues } from "@/lib/nostr/utils";
|
||||||
|
|
||||||
export async function generateMetadata(
|
export async function generateMetadata(
|
||||||
{ params }: { params: { naddr: string } },
|
{ params }: { params: { naddr: string } },
|
||||||
parent: ResolvingMetadata,
|
parent: ResolvingMetadata,
|
||||||
): Promise<Metadata> {
|
): Promise<Metadata> {
|
||||||
|
const previousImages = (await parent).openGraph?.images || [];
|
||||||
// read route params
|
// read route params
|
||||||
const identifier = params.naddr;
|
const identifier = params.naddr;
|
||||||
|
const { data, type } = nip19.decode(identifier);
|
||||||
|
if (type !== "naddr") {
|
||||||
|
return {
|
||||||
|
title: "Flockstr Event",
|
||||||
|
openGraph: {
|
||||||
|
images: previousImages,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// fetch data
|
// fetch data
|
||||||
const event = await get(identifier);
|
const event = await getEvent(data.kind, data.pubkey, data.identifier);
|
||||||
|
|
||||||
// optionally access and extend (rather than replace) parent metadata
|
// optionally access and extend (rather than replace) parent metadata
|
||||||
const previousImages = (await parent).openGraph?.images || [];
|
|
||||||
if (!event) {
|
if (!event) {
|
||||||
return {
|
return {
|
||||||
title: "Flockstr Event",
|
title: "Flockstr Event",
|
||||||
@ -21,22 +32,25 @@ export async function generateMetadata(
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
const title = `${event.name} | Flockstr`;
|
|
||||||
const images = event.image
|
const title = `${getTagValues("title", event.tags as string[][])} | Flockstr`;
|
||||||
? [event.image, ...previousImages]
|
const images =
|
||||||
: previousImages;
|
getTagValues("image", event.tags as string[][]) ??
|
||||||
|
getTagValues("banner", event.tags as string[][]) ??
|
||||||
|
"";
|
||||||
|
|
||||||
return {
|
return {
|
||||||
title: title,
|
title: title,
|
||||||
description: event.description,
|
description: event.content,
|
||||||
openGraph: {
|
openGraph: {
|
||||||
title: title,
|
title: title,
|
||||||
description: event.description,
|
description: event.content,
|
||||||
images: images,
|
images: [images],
|
||||||
},
|
},
|
||||||
twitter: {
|
twitter: {
|
||||||
title: title,
|
title: title,
|
||||||
description: event.description,
|
description: event.content,
|
||||||
images: images,
|
images: [images],
|
||||||
card: "summary_large_image",
|
card: "summary_large_image",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -44,7 +44,7 @@ export default function EventPage({
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (event) {
|
if (event) {
|
||||||
const { tags, content } = event;
|
const { tags, content } = event;
|
||||||
const name = getTagValues("name", tags) ?? "Untitled";
|
const name = getTagValues("title", tags) ?? "Untitled";
|
||||||
const image =
|
const image =
|
||||||
getTagValues("image", tags) ??
|
getTagValues("image", tags) ??
|
||||||
getTagValues("picture", tags) ??
|
getTagValues("picture", tags) ??
|
||||||
|
@ -57,7 +57,7 @@ export default function CreateCalendarEventModal() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!name) {
|
if (!name) {
|
||||||
setError("Please add a title");
|
setError("Please add a name");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
|
@ -120,7 +120,7 @@ export default function CreateCalendarEventModal({
|
|||||||
const random = randomId();
|
const random = randomId();
|
||||||
const tags: string[][] = [
|
const tags: string[][] = [
|
||||||
["d", random],
|
["d", random],
|
||||||
["name", title],
|
["title", title],
|
||||||
["description", description],
|
["description", description],
|
||||||
["start", toUnix(convertToTimezone(startDate, timezone)).toString()],
|
["start", toUnix(convertToTimezone(startDate, timezone)).toString()],
|
||||||
["end", toUnix(convertToTimezone(endDate, timezone)).toString()],
|
["end", toUnix(convertToTimezone(endDate, timezone)).toString()],
|
||||||
|
@ -41,8 +41,7 @@ export default function RSVPModal({ eventReference }: RSVPModalProps) {
|
|||||||
const tags: string[][] = [
|
const tags: string[][] = [
|
||||||
["d", random],
|
["d", random],
|
||||||
["a", eventReference],
|
["a", eventReference],
|
||||||
["L", "status"],
|
["status", statusMap[type]],
|
||||||
["l", statusMap[type], "status"],
|
|
||||||
];
|
];
|
||||||
const event = await createEvent(ndk, {
|
const event = await createEvent(ndk, {
|
||||||
content: "",
|
content: "",
|
||||||
|
@ -44,8 +44,8 @@ export default function AvatarStack({
|
|||||||
}
|
}
|
||||||
|
|
||||||
function User({ pubkey, className }: { pubkey: string; className: string }) {
|
function User({ pubkey, className }: { pubkey: string; className: string }) {
|
||||||
const { profile } = useProfile(pubkey);
|
const { profile, npub } = useProfile(pubkey);
|
||||||
const npub = nip19.npubEncode(pubkey);
|
// const npub = profilenip19.npubEncode(pubkey);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Avatar
|
<Avatar
|
||||||
|
11
db/client.ts
Normal file
11
db/client.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import { PrismaClient } from "@prisma/client";
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
var prisma: PrismaClient | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const prisma = global.prisma || new PrismaClient();
|
||||||
|
|
||||||
|
if (process.env.NODE_ENV === "development") global.prisma = prisma;
|
||||||
|
|
||||||
|
export default prisma;
|
@ -7,7 +7,7 @@ import { type NDKUserProfile } from "@nostr-dev-kit/ndk";
|
|||||||
|
|
||||||
export default function useProfile(key: string) {
|
export default function useProfile(key: string) {
|
||||||
const { ndk, getProfile } = useNDK();
|
const { ndk, getProfile } = useNDK();
|
||||||
|
const npub = key.length === 64 ? nip19.npubEncode(key) : key;
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!ndk) return;
|
if (!ndk) return;
|
||||||
if (NOSTR_BECH32_REGEXP.test(key)) {
|
if (NOSTR_BECH32_REGEXP.test(key)) {
|
||||||
@ -20,5 +20,5 @@ export default function useProfile(key: string) {
|
|||||||
};
|
};
|
||||||
}, [key, ndk]);
|
}, [key, ndk]);
|
||||||
|
|
||||||
return { profile: getProfile(key) };
|
return { profile: getProfile(key), npub: npub };
|
||||||
}
|
}
|
||||||
|
12
lib/server-actions/meta/event.ts
Normal file
12
lib/server-actions/meta/event.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
"use server";
|
||||||
|
import prisma from "@/db/client";
|
||||||
|
|
||||||
|
export async function getEvent(kind: number, pubkey: string, d: string) {
|
||||||
|
return await prisma.nostrEvent.findFirst({
|
||||||
|
where: {
|
||||||
|
kind,
|
||||||
|
pubkey,
|
||||||
|
d,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
@ -4,7 +4,7 @@
|
|||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev",
|
"dev": "next dev",
|
||||||
"build": "next build",
|
"build": "prisma generate && next build",
|
||||||
"start": "next start",
|
"start": "next start",
|
||||||
"lint": "next lint"
|
"lint": "next lint"
|
||||||
},
|
},
|
||||||
@ -17,6 +17,7 @@
|
|||||||
"@nostr-dev-kit/ndk": "^2.0.0",
|
"@nostr-dev-kit/ndk": "^2.0.0",
|
||||||
"@nostr-dev-kit/ndk-cache-dexie": "^2.0.3",
|
"@nostr-dev-kit/ndk-cache-dexie": "^2.0.3",
|
||||||
"@nostr-dev-kit/ndk-react": "^0.1.1",
|
"@nostr-dev-kit/ndk-react": "^0.1.1",
|
||||||
|
"@prisma/client": "^5.7.1",
|
||||||
"@radix-ui/react-aspect-ratio": "^1.0.3",
|
"@radix-ui/react-aspect-ratio": "^1.0.3",
|
||||||
"@radix-ui/react-avatar": "^1.0.4",
|
"@radix-ui/react-avatar": "^1.0.4",
|
||||||
"@radix-ui/react-dialog": "^1.0.5",
|
"@radix-ui/react-dialog": "^1.0.5",
|
||||||
@ -88,6 +89,7 @@
|
|||||||
"postcss": "^8",
|
"postcss": "^8",
|
||||||
"prettier": "^3.0.3",
|
"prettier": "^3.0.3",
|
||||||
"prettier-plugin-tailwindcss": "^0.5.6",
|
"prettier-plugin-tailwindcss": "^0.5.6",
|
||||||
|
"prisma": "^5.7.1",
|
||||||
"tailwindcss": "^3",
|
"tailwindcss": "^3",
|
||||||
"typescript": "^5",
|
"typescript": "^5",
|
||||||
"webln": "^0.3.2"
|
"webln": "^0.3.2"
|
||||||
|
33
prisma/schema.prisma
Normal file
33
prisma/schema.prisma
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
// This is your Prisma schema file,
|
||||||
|
// learn more about it in the docs: https://pris.ly/d/prisma-schema
|
||||||
|
|
||||||
|
// datasource db {
|
||||||
|
// provider = "postgresql"
|
||||||
|
// url = env("POSTGRES_PRISMA_URL") // uses connection pooling
|
||||||
|
// directUrl = env("POSTGRES_URL_NON_POOLING") // uses a direct connection
|
||||||
|
// }
|
||||||
|
|
||||||
|
// generator client {
|
||||||
|
// provider = "prisma-client-js"
|
||||||
|
// }
|
||||||
|
datasource db {
|
||||||
|
provider = "mysql"
|
||||||
|
url = env("DATABASE_URL")
|
||||||
|
relationMode = "prisma"
|
||||||
|
}
|
||||||
|
|
||||||
|
generator client {
|
||||||
|
provider = "prisma-client-js"
|
||||||
|
}
|
||||||
|
|
||||||
|
model NostrEvent {
|
||||||
|
key Int @id @default(autoincrement())
|
||||||
|
id String
|
||||||
|
kind Int
|
||||||
|
content String @db.Text
|
||||||
|
created_at Int
|
||||||
|
pubkey String
|
||||||
|
tags Json @default("[]")
|
||||||
|
sig String
|
||||||
|
d String?
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user