diff --git a/app/(landing)/page.tsx b/app/(landing)/page.tsx index c45fa26..2a43fc6 100644 --- a/app/(landing)/page.tsx +++ b/app/(landing)/page.tsx @@ -51,7 +51,7 @@ export default function LandingPage() { }} /> -
+
diff --git a/bun.lockb b/bun.lockb index e4f644c..3917105 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/components/KindCard/3745.tsx b/components/KindCard/3745.tsx index 5f40704..a4d20a0 100644 --- a/components/KindCard/3745.tsx +++ b/components/KindCard/3745.tsx @@ -9,7 +9,7 @@ import { useNDK } from "@/app/_providers/ndk"; import { RiArrowRightLine, RiLockLine } from "react-icons/ri"; import { HiOutlineLockOpen } from "react-icons/hi"; import { decryptMessage } from "@/lib/nostr"; -import { NDKUser } from "@nostr-dev-kit/ndk"; +import { NDKFilter, NDKUser } from "@nostr-dev-kit/ndk"; import { log } from "@/lib/utils"; import { EventSchema } from "@/types"; import KindCard from "@/components/KindCard"; @@ -24,9 +24,10 @@ import { import useCurrentUser from "@/lib/hooks/useCurrentUser"; import { unlockEvent } from "@/lib/actions/create"; import { type KindCardProps } from "./"; +import { getTagValues } from "@/lib/nostr/utils"; export default function Kind3745(props: KindCardProps) { - const { pubkey, content, id } = props; + const { pubkey, content, id, tags } = props; const { currentUser } = useCurrentUser(); const [error, setError] = useState(""); const [passphrase, setPassphrase] = useState(""); @@ -44,11 +45,16 @@ export default function Kind3745(props: KindCardProps) { log("func", `handleFetchEvent()`); setFetchingEvent(true); try { - const directMessageEvent = await ndk.fetchEvent({ + const delegate = getTagValues("delegate", tags); + const filter: NDKFilter = { kinds: [4], ["#e"]: [id], ["#p"]: [currentUser.pubkey], - }); + }; + if (delegate) { + filter.authors = [delegate]; + } + const directMessageEvent = await ndk.fetchEvent(filter); if (directMessageEvent) { log("info", "direct msg decryption"); if (!signer) return; diff --git a/components/ui/calendar.tsx b/components/ui/calendar.tsx new file mode 100644 index 0000000..6c08e3a --- /dev/null +++ b/components/ui/calendar.tsx @@ -0,0 +1,71 @@ +"use client" + +import * as React from "react" +import { ChevronLeftIcon, ChevronRightIcon } from "@radix-ui/react-icons" +import { DayPicker } from "react-day-picker" + +import { cn } from "@/lib/utils" +import { buttonVariants } from "@/components/ui/button" + +export type CalendarProps = React.ComponentProps + +function Calendar({ + className, + classNames, + showOutsideDays = true, + ...props +}: CalendarProps) { + return ( + .day-range-end)]:rounded-r-md [&:has(>.day-range-start)]:rounded-l-md first:[&:has([aria-selected])]:rounded-l-md last:[&:has([aria-selected])]:rounded-r-md" + : "[&:has([aria-selected])]:rounded-md" + ), + day: cn( + buttonVariants({ variant: "ghost" }), + "h-8 w-8 p-0 font-normal aria-selected:opacity-100" + ), + day_range_start: "day-range-start", + day_range_end: "day-range-end", + day_selected: + "bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground", + day_today: "bg-accent text-accent-foreground", + day_outside: "text-muted-foreground opacity-50", + day_disabled: "text-muted-foreground opacity-50", + day_range_middle: + "aria-selected:bg-accent aria-selected:text-accent-foreground", + day_hidden: "invisible", + ...classNames, + }} + components={{ + IconLeft: ({ ...props }) => , + IconRight: ({ ...props }) => , + }} + {...props} + /> + ) +} +Calendar.displayName = "Calendar" + +export { Calendar } diff --git a/lib/actions/create.ts b/lib/actions/create.ts index 1facef3..215c921 100644 --- a/lib/actions/create.ts +++ b/lib/actions/create.ts @@ -309,3 +309,91 @@ export async function follow( const newContacts = await createEvent(ndk, newEvent); return newContacts; } +export async function createCalendarEvent( + ndk: NDK, + event: { + content: string; + kind: number; + tags: string[][]; + }, + isPrivate?: boolean, + list?: NDKList, + delegateSigner?: NDKPrivateKeySigner, +) { + log("func", "createEventHandler"); + const pubkey = await window.nostr?.getPublicKey(); + if (!pubkey || !window.nostr) { + throw new Error("No public key provided!"); + } + const eventToPublish = new NDKEvent(ndk, { + ...event, + tags: [...event.tags, ["client", "flockstr"]], + pubkey, + created_at: unixTimeNowInSeconds(), + } as NostrEvent); + + await eventToPublish.sign(); + + let publishedEvent: NDKEvent | null = null; + // Check if is private event + if (isPrivate) { + log("info", "isPrivate"); + const rawEventString = JSON.stringify(eventToPublish.rawEvent()); + const passphrase = generateRandomString(); + const encryptedRawEventString = await encryptMessage( + rawEventString, + passphrase, + ); + const newEvent = new NDKEvent(ndk, { + content: encryptedRawEventString, + kind: 3745, + tags: [ + ["kind", event.kind.toString()], + ["client", "flockstr"], + ], + pubkey, + } as NostrEvent); + await newEvent.sign(); + await newEvent.publish(); + + const messenger = delegateSigner ?? ndk.signer!; + const user = await messenger.user(); + log("info", "Signer", user.toString()); + + if (list) { + // Send DMs to subscribers + const subscribers = getTagsValues("p", list.tags); + for (const subscriber of subscribers) { + const messageEvent = new NDKEvent(ndk, { + content: passphrase, + kind: 4, + tags: [ + ["p", subscriber], + ["e", newEvent.id], + ["client", "flockstr"], + ], + pubkey: user.pubkey, + } as NostrEvent); + await messageEvent.encrypt( + new NDKUser({ hexpubkey: subscriber }), + messenger, + ); + await messageEvent.sign(messenger); + await messageEvent.publish(); + } + } + publishedEvent = newEvent; + } else { + await eventToPublish.publish(); + publishedEvent = eventToPublish; + } + if (list) { + const tag = publishedEvent.tagReference(); + if (!tag) return; + // Add event to list + await list.addItem(tag, undefined, false); + await list.sign(); + await list.publish(); + } + return true; +} diff --git a/package.json b/package.json index 7d2cda3..00c341c 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,7 @@ "cmdk": "^0.2.0", "crypto": "^1.0.1", "crypto-js": "^4.1.1", + "date-fns": "^2.30.0", "dayjs": "^1.11.10", "dexie": "^3.2.4", "dexie-react-hooks": "^1.1.6", @@ -49,6 +50,7 @@ "nostr-tools": "^1.16.0", "ramda": "^0.29.1", "react": "^18", + "react-day-picker": "^8.9.1", "react-dom": "^18", "react-hook-form": "^7.47.0", "react-icons": "^4.11.0",