diff --git a/app/(app)/event/[naddr]/_components/AnnouncementsContainer.tsx b/app/(app)/event/[naddr]/_components/AnnouncementsContainer.tsx new file mode 100644 index 0000000..603adce --- /dev/null +++ b/app/(app)/event/[naddr]/_components/AnnouncementsContainer.tsx @@ -0,0 +1,31 @@ +import { HiSignal } from "react-icons/hi2"; +import Feed from "@/containers/Feed"; + +type AnnouncementsContainerProps = { + eventReference: string; +}; +export default function AnnouncementsContainer({ + eventReference, +}: AnnouncementsContainerProps) { + return ( +
+
+ +

Announcements

+
+
+ ( +
+

No Announcements yet

+
+ )} + /> +
+
+ ); +} diff --git a/app/(app)/event/[naddr]/_components/AttendeesContainer.tsx b/app/(app)/event/[naddr]/_components/AttendeesContainer.tsx new file mode 100644 index 0000000..bed400b --- /dev/null +++ b/app/(app)/event/[naddr]/_components/AttendeesContainer.tsx @@ -0,0 +1,23 @@ +import { HiOutlineUserGroup } from "react-icons/hi2"; + +import UserRow from "./UserRow"; +type AttendeesContainerProps = { + attendees: string[]; +}; +export default function AttendeesContainer({ + attendees, +}: AttendeesContainerProps) { + return ( +
+
+ +

Attendees

+
+ +
+ ); +} diff --git a/app/(app)/event/[naddr]/_components/Header.tsx b/app/(app)/event/[naddr]/_components/Header.tsx index 13594ef..b0f3431 100644 --- a/app/(app)/event/[naddr]/_components/Header.tsx +++ b/app/(app)/event/[naddr]/_components/Header.tsx @@ -27,7 +27,7 @@ import { formatDate } from "@/lib/utils/dates"; import SmallCalendarIcon from "@/components/EventIcons/DateIcon"; import LocationIcon from "@/components/EventIcons/LocationIcon"; -const EditEventModal = dynamic(() => import("@/components/Modals/EditEvent"), { +const RSVPButton = dynamic(() => import("./RSVPButton"), { ssr: false, }); const CreateListEvent = dynamic( @@ -48,7 +48,7 @@ export default function Header({ event }: { event: NDKEvent }) { const [hasValidPayment, setHasValidPayment] = useState(false); const { pubkey, tags } = event; const { profile } = useProfile(pubkey); - + const eventReference = event.encode(); const title = getTagValues("name", tags) ?? "Untitled"; const image = getTagValues("image", tags) ?? @@ -170,35 +170,7 @@ export default function Header({ event }: { event: NDKEvent }) { )} */} - + {/* {!isMember && (hasValidPayment ? ( @@ -240,7 +212,7 @@ export default function Header({ event }: { event: NDKEvent }) {
{!!description && ( -

+

{description}

)} diff --git a/app/(app)/event/[naddr]/_components/HostsContainer.tsx b/app/(app)/event/[naddr]/_components/HostsContainer.tsx new file mode 100644 index 0000000..45e96c6 --- /dev/null +++ b/app/(app)/event/[naddr]/_components/HostsContainer.tsx @@ -0,0 +1,21 @@ +import { HiOutlineUsers } from "react-icons/hi2"; + +import UserRow from "./UserRow"; +type HostsContainerProps = { + hosts: string[]; +}; +export default function HostsContainer({ hosts }: HostsContainerProps) { + return ( +
+
+ +

Hosts

+
+
    + {hosts.map((pubkey) => ( + + ))} +
+
+ ); +} diff --git a/app/(app)/event/[naddr]/_components/LocationContainer.tsx b/app/(app)/event/[naddr]/_components/LocationContainer.tsx new file mode 100644 index 0000000..873b7f8 --- /dev/null +++ b/app/(app)/event/[naddr]/_components/LocationContainer.tsx @@ -0,0 +1,26 @@ +import LocationBoxRaw from "@/components/LocationPreview/LocationBoxRaw"; +import { HiOutlineMapPin, HiCheckBadge, HiOutlineUsers } from "react-icons/hi2"; + +type LocationContainerProps = { + address: string; + geohash: string; +}; +export default function LocationContainer({ + address, + geohash, +}: LocationContainerProps) { + return ( +
+
+ +

Location

+
+
+ +
+
+

{address}

+
+
+ ); +} diff --git a/app/(app)/event/[naddr]/_components/RSVPButton.tsx b/app/(app)/event/[naddr]/_components/RSVPButton.tsx new file mode 100644 index 0000000..f44e9c5 --- /dev/null +++ b/app/(app)/event/[naddr]/_components/RSVPButton.tsx @@ -0,0 +1,19 @@ +import { Button } from "@/components/ui/button"; +import { useModal } from "@/app/_providers/modal/provider"; +import RSVPModal from "@/components/Modals/RSVP"; + +type RSVPButtonProps = { + eventReference: string; +}; + +export default function RSVPButton({ eventReference }: RSVPButtonProps) { + const modal = useModal(); + + return ( + + ); +} diff --git a/app/(app)/event/[naddr]/_components/UserRow.tsx b/app/(app)/event/[naddr]/_components/UserRow.tsx new file mode 100644 index 0000000..7199f5c --- /dev/null +++ b/app/(app)/event/[naddr]/_components/UserRow.tsx @@ -0,0 +1,51 @@ +import Link from "next/link"; +import { HiOutlineMapPin, HiCheckBadge, HiOutlineUsers } from "react-icons/hi2"; +import { Avatar, AvatarImage, AvatarFallback } from "@/components/ui/avatar"; +import useProfile from "@/lib/hooks/useProfile"; +import { getTwoLetters, getNameToShow } from "@/lib/utils"; +import { nip19 } from "nostr-tools"; + +export default function UserRow({ pubkey }: { pubkey: string }) { + const npub = nip19.npubEncode(pubkey); + const { profile } = useProfile(pubkey); + return ( +
  • + + + + + {getTwoLetters({ npub, profile })} + + + {profile?.displayName || profile?.name ? ( +
    +
    + + {getNameToShow({ npub, profile })} + + {!!profile?.nip05 && ( + + )} +
    +
    + {!!profile.nip05 && ( + + {profile.nip05} + + )} +
    +
    + ) : ( +
    + + {getNameToShow({ npub, profile })} + + {!!profile?.nip05 && ( + + )} +
    + )} + +
  • + ); +} diff --git a/app/(app)/event/[naddr]/page.tsx b/app/(app)/event/[naddr]/page.tsx index bc70e85..b14ec45 100644 --- a/app/(app)/event/[naddr]/page.tsx +++ b/app/(app)/event/[naddr]/page.tsx @@ -1,17 +1,23 @@ "use client"; import { useState } from "react"; import Image from "next/image"; +import Link from "next/link"; import { nip19 } from "nostr-tools"; import useEvents from "@/lib/hooks/useEvents"; import Spinner from "@/components/spinner"; import { getTagAllValues, getTagValues, + getTagsAllValues, getTagsValues, } from "@/lib/nostr/utils"; -import Feed from "@/containers/Feed"; + import Header from "./_components/Header"; import LocationPreview from "@/components/LocationPreview"; +import HostsContainer from "./_components/HostsContainer"; +import LocationContainer from "./_components/LocationContainer"; +import AnnouncementsContainer from "./_components/AnnouncementsContainer"; +import AttendeesContainer from "./_components/AttendeesContainer"; export default function EventPage({ params: { naddr }, @@ -42,35 +48,37 @@ export default function EventPage({
    ); } - const noteIds = getTagsValues("e", event.tags).filter(Boolean); - const location = getTagAllValues("location", event.tags)[0] - ? getTagAllValues("location", event.tags) - : getTagAllValues("address", event.tags); - const geohash = getTagValues("g", event.tags); + const { tags } = event; + const eventReference = event.encode(); + const noteIds = getTagsValues("e", tags).filter(Boolean); + const location = getTagAllValues("location", tags)[0] + ? getTagAllValues("location", tags) + : getTagAllValues("address", tags); + const geohash = getTagValues("g", tags); + const hosts = getTagsAllValues("p", tags) + .filter(([pubkey, relay, role]) => role === "host") + .map(([pubkey]) => pubkey) + .filter(Boolean); + const attendees = getTagsAllValues("p", tags) + .map(([pubkey]) => pubkey) + .filter(Boolean); return ( -
    +
    -
    -
    +
    +
    {!!location && !!geohash && ( - )} -
    - ( -
    -

    No Announcements yet

    -
    - )} - /> -
    + + +
    +
    +
    diff --git a/components/LocationPreview/LocationBoxRaw.tsx b/components/LocationPreview/LocationBoxRaw.tsx new file mode 100644 index 0000000..4a76591 --- /dev/null +++ b/components/LocationPreview/LocationBoxRaw.tsx @@ -0,0 +1,58 @@ +"use client"; + +import { useLoadScript, GoogleMap } from "@react-google-maps/api"; +import type { NextPage } from "next"; +import { useMemo } from "react"; +import { + Card, + CardContent, + CardFooter, + CardHeader, + CardTitle, +} from "@/components/ui/card"; +import { cn } from "@/lib/utils"; +import Geohash from "latlon-geohash"; +import { HiMapPin } from "react-icons/hi2"; + +type LocationPreviewProps = { + geohash: string; + address: string; + className?: string; +}; +export default function LocationBoxRaw({ + geohash, + address, + className, +}: LocationPreviewProps) { + const libraries = useMemo(() => ["places"], []); + const { lat, lon } = Geohash.decode(geohash); + const mapCenter = useMemo(() => ({ lat, lng: lon }), []); + + const mapOptions = useMemo( + () => ({ + disableDefaultUI: true, + clickableIcons: true, + scrollwheel: false, + }), + [], + ); + + const { isLoaded } = useLoadScript({ + googleMapsApiKey: process.env.NEXT_PUBLIC_GOOGLE_MAPS_KEY as string, + libraries: libraries as any, + }); + + if (!isLoaded) { + return
    ; + } + return ( + console.log("Map Component Loaded...")} + /> + ); +} diff --git a/components/Modals/CreateCalendarEvent.tsx b/components/Modals/CreateCalendarEvent.tsx index 675ca59..182d1fe 100644 --- a/components/Modals/CreateCalendarEvent.tsx +++ b/components/Modals/CreateCalendarEvent.tsx @@ -98,6 +98,7 @@ export default function CreateCalendarEventModal() { ["start", toUnix(convertToTimezone(startDate, timezone)).toString()], ["end", toUnix(convertToTimezone(endDate, timezone)).toString()], ["start_tzid", timezone], + ["p", currentUser.pubkey, "", "host"], ]; if (location) { diff --git a/components/Modals/RSVP.tsx b/components/Modals/RSVP.tsx new file mode 100644 index 0000000..229c25c --- /dev/null +++ b/components/Modals/RSVP.tsx @@ -0,0 +1,92 @@ +"use client"; +import { useState } from "react"; +import Template from "./Template"; +import { Button } from "@/components/ui/button"; +import { toast } from "sonner"; +import { useModal } from "@/app/_providers/modal/provider"; +import { randomId } from "@/lib/nostr"; +import { unixTimeNowInSeconds } from "@/lib/nostr/dates"; +// import { useKeys } from "@/app/_providers/keysProvider"; +import useCurrentUser from "@/lib/hooks/useCurrentUser"; +import { createEvent } from "@/lib/actions/create"; +import { useNDK } from "@/app/_providers/ndk"; + +type RSVPModalProps = { + eventReference: string; +}; + +const statusMap = { + accept: "accepted", + maybe: "tentative", + decline: "declined", +}; + +export default function RSVPModal({ eventReference }: RSVPModalProps) { + const modal = useModal(); + const { ndk } = useNDK(); + const { currentUser } = useCurrentUser(); + const [loading, setLoading] = useState({ + accept: false, + maybe: false, + decline: false, + }); + + async function handleRSVP(type: "accept" | "maybe" | "decline") { + if (!ndk || !currentUser) return; + setLoading((prev) => ({ ...prev, [type]: true })); + + try { + const random = randomId(); + const tags: string[][] = [ + ["d", random], + ["a", eventReference], + ["L", "status"], + ["l", statusMap[type], "status"], + ]; + const event = await createEvent(ndk, { + content: "", + kind: 31925, + tags, + }); + if (event) { + toast.success("Event Created!"); + modal?.hide(); + } else { + toast.error("An error occured"); + } + } catch (err) { + console.log("Err", err); + } finally { + setLoading({ accept: false, maybe: false, decline: false }); + } + } + return ( + + ); +}