adding events
This commit is contained in:
parent
f952f4d456
commit
6b13cfee0a
84
app/(app)/app/_sections/UpcomingEvents.tsx
Normal file
84
app/(app)/app/_sections/UpcomingEvents.tsx
Normal file
@ -0,0 +1,84 @@
|
||||
"use client";
|
||||
import {
|
||||
Section,
|
||||
SectionHeader,
|
||||
SectionTitle,
|
||||
SectionContent,
|
||||
} from "@/containers/PageSection";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { RiArrowRightLine } from "react-icons/ri";
|
||||
import CalendarEventCard, {
|
||||
CardLoading,
|
||||
} from "@/components/Cards/CalendarEvent";
|
||||
import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area";
|
||||
import Link from "next/link";
|
||||
import useEvents from "@/lib/hooks/useEvents";
|
||||
import { Event } from "nostr-tools";
|
||||
import KindLoading from "@/components/KindCard/loading";
|
||||
import { nip19 } from "nostr-tools";
|
||||
import { getTagValues, getTagsValues } from "@/lib/nostr/utils";
|
||||
import { type NDKKind } from "@nostr-dev-kit/ndk";
|
||||
import { uniqBy } from "ramda";
|
||||
|
||||
export default function UpcomingEventsSection() {
|
||||
const { events } = useEvents({
|
||||
filter: {
|
||||
kinds: [31923 as NDKKind],
|
||||
limit: 10,
|
||||
},
|
||||
});
|
||||
|
||||
console.log("UpcomingEventsSection", events);
|
||||
|
||||
const processedEvents = uniqBy(
|
||||
(e) => getTagValues("name", e.tags),
|
||||
events,
|
||||
).sort((a, b) => {
|
||||
const aImage = getTagValues("image", a.tags);
|
||||
const bImage = getTagValues("image", b.tags);
|
||||
if (aImage && bImage) {
|
||||
return 0;
|
||||
}
|
||||
if (bImage) return 1;
|
||||
return -1;
|
||||
});
|
||||
|
||||
return (
|
||||
<Section className="max-sm:-mx-5">
|
||||
<SectionHeader>
|
||||
<div className="center gap-x-2 max-sm:px-5">
|
||||
<SectionTitle>Upcoming Events</SectionTitle>
|
||||
</div>
|
||||
<Button variant={"ghost"}>
|
||||
View all <RiArrowRightLine className="ml-1 h-4 w-4" />
|
||||
</Button>
|
||||
</SectionHeader>
|
||||
<SectionContent className="relative">
|
||||
<ScrollArea>
|
||||
<div className="flex space-x-2 pb-4 max-sm:px-5">
|
||||
{processedEvents?.length > 3 ? (
|
||||
processedEvents.slice(0, 6).map((e, idx) => {
|
||||
return (
|
||||
<Link key={e.id} href={`/event/${e.encode()}`}>
|
||||
<CalendarEventCard
|
||||
event={e.rawEvent()}
|
||||
className="min-w-[250px] max-w-[350px]"
|
||||
/>
|
||||
</Link>
|
||||
);
|
||||
})
|
||||
) : (
|
||||
<>
|
||||
<CardLoading className="min-w-[250px] max-w-[350px]" />
|
||||
<CardLoading className="min-w-[250px] max-w-[350px]" />
|
||||
<CardLoading className="min-w-[250px] max-w-[350px]" />
|
||||
<CardLoading className="min-w-[250px] max-w-[350px]" />
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<ScrollBar orientation="horizontal" />
|
||||
</ScrollArea>
|
||||
</SectionContent>
|
||||
</Section>
|
||||
);
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
import dynamic from "next/dynamic";
|
||||
import ExploreCreators from "./_sections/ExploreCreators";
|
||||
import UpcomingEvents from "./_sections/UpcomingEvents";
|
||||
import LongFormContentSection from "./_sections/LongFormContent";
|
||||
import BecomeACreator from "./_sections/BecomeACreator";
|
||||
|
||||
@ -23,6 +24,7 @@ export default function Page() {
|
||||
return (
|
||||
<div className="relative space-y-6 px-5 pt-5 sm:pt-7">
|
||||
<ExploreCreators />
|
||||
<UpcomingEvents />
|
||||
<LongFormContentSection />
|
||||
<BecomeACreator />
|
||||
<LiveStreamingSection />
|
||||
|
@ -46,14 +46,10 @@ export default function Header({ event }: { event: NDKEvent }) {
|
||||
const { ndk } = useNDK();
|
||||
const [checkingPayment, setCheckingPayment] = useState(false);
|
||||
const [hasValidPayment, setHasValidPayment] = useState(false);
|
||||
const [syncingUsers, setSyncingUsers] = useState(false);
|
||||
const { pubkey, tags } = event;
|
||||
const { profile } = useProfile(pubkey);
|
||||
console.log("EVENT", tags);
|
||||
|
||||
const noteIds = getTagsValues("e", tags).filter(Boolean);
|
||||
const title = getTagValues("name", tags) ?? "Untitled";
|
||||
console.log("tite", tags);
|
||||
const image =
|
||||
getTagValues("image", tags) ??
|
||||
getTagValues("picture", tags) ??
|
||||
@ -110,19 +106,7 @@ export default function Header({ event }: { event: NDKEvent }) {
|
||||
setCheckingPayment(false);
|
||||
}
|
||||
}
|
||||
async function handleSyncUsers() {
|
||||
if (!event || !ndk) return;
|
||||
setSyncingUsers(true);
|
||||
try {
|
||||
console.log("handleSyncUsers");
|
||||
await updateListUsersFromZaps(ndk, event.tagId(), rawEvent);
|
||||
toast.success("Users Synced!");
|
||||
} catch (err) {
|
||||
console.log("error syncing users", err);
|
||||
} finally {
|
||||
setSyncingUsers(false);
|
||||
}
|
||||
}
|
||||
|
||||
async function handleSendZap() {
|
||||
try {
|
||||
const result = await sendZap(
|
||||
@ -163,7 +147,7 @@ export default function Header({ event }: { event: NDKEvent }) {
|
||||
</div>
|
||||
<div className="space-y-1 p-3 @sm:px-3.5 @sm:pb-2 @sm:pt-5">
|
||||
<div className="flex items-start justify-between gap-x-1.5 @lg:gap-x-2.5">
|
||||
<div className="space-y-1 @sm:space-y-2">
|
||||
<div className="shrink-0 space-y-1 @sm:space-y-2">
|
||||
<h2 className="font-condensed text-2xl font-semibold sm:text-3xl lg:text-4xl">
|
||||
{title}
|
||||
</h2>
|
||||
@ -175,14 +159,7 @@ export default function Header({ event }: { event: NDKEvent }) {
|
||||
{!!currentUser && currentUser.pubkey === pubkey && (
|
||||
<>
|
||||
<Button onClick={() => modal?.show(<CreateListEvent />)}>
|
||||
Add Event
|
||||
</Button>
|
||||
<Button
|
||||
variant={"outline"}
|
||||
loading={syncingUsers}
|
||||
onClick={() => void handleSyncUsers()}
|
||||
>
|
||||
Sync users
|
||||
Invite Users
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
@ -233,30 +210,30 @@ export default function Header({ event }: { event: NDKEvent }) {
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex pt-1 @md:pt-2">
|
||||
<div className="flex flex-col gap-x-6 gap-y-3 pt-1 @md:pt-2 @xl:flex-row">
|
||||
<div className="flex-1">
|
||||
{!!description && (
|
||||
<p className="line-clamp-3 text-sm text-muted-foreground md:text-sm">
|
||||
<p className="line-clamp-3 text-sm text-muted-foreground @md:text-sm">
|
||||
{description}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex flex-1 justify-end">
|
||||
<div className="flex flex-1 @xl:justify-end">
|
||||
<div className="flex flex-col gap-3 pr-3">
|
||||
{!!startDate && (
|
||||
<div className="flex flex-1 items-center gap-3">
|
||||
<SmallCalendarIcon date={startDate} />
|
||||
<div className="">
|
||||
<p className="text-bold text-base">
|
||||
<p className="text-bold text-sm @xl:text-base">
|
||||
{formatDate(startDate, "dddd, MMMM Do")}
|
||||
</p>
|
||||
{!!endDate ? (
|
||||
<p className="text-sm text-muted-foreground">{`${formatDate(
|
||||
<p className="text-xs text-muted-foreground @xl:text-sm">{`${formatDate(
|
||||
startDate,
|
||||
"h:mm a",
|
||||
)} to ${formatDate(endDate, "h:mm a")}`}</p>
|
||||
) : (
|
||||
<p className="text-xs text-muted-foreground">{`${formatDate(
|
||||
<p className="text-xs text-muted-foreground @xl:text-sm">{`${formatDate(
|
||||
startDate,
|
||||
"h:mm a",
|
||||
)}`}</p>
|
||||
@ -270,8 +247,10 @@ export default function Header({ event }: { event: NDKEvent }) {
|
||||
<div className="">
|
||||
{location.length > 2 ? (
|
||||
<>
|
||||
<p className="text-bold text-base">{location[1]}</p>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
<p className="text-bold text-sm @xl:text-base">
|
||||
{location[1]}
|
||||
</p>
|
||||
<p className="text-xs text-muted-foreground @xl:text-sm">
|
||||
{location[2]}
|
||||
</p>
|
||||
</>
|
||||
|
159
components/Cards/CalendarEvent/index.tsx
Normal file
159
components/Cards/CalendarEvent/index.tsx
Normal file
@ -0,0 +1,159 @@
|
||||
"use client";
|
||||
import Image from "next/image";
|
||||
import { cn, formatNumber } from "@/lib/utils";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { RxClock, RxCalendar } from "react-icons/rx";
|
||||
import { HiOutlineUsers } from "react-icons/hi";
|
||||
import { AspectRatio } from "@/components/ui/aspect-ratio";
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
import { formatDate } from "@/lib/utils/dates";
|
||||
import { NostrEvent } from "@nostr-dev-kit/ndk";
|
||||
import {
|
||||
getTagAllValues,
|
||||
getTagValues,
|
||||
getTagsValues,
|
||||
} from "@/lib/nostr/utils";
|
||||
import useProfile from "@/lib/hooks/useProfile";
|
||||
import SmallProfileLine from "@/components/ProfileContainers/SmallProfileLine";
|
||||
|
||||
type CalendarEventCardProps = {
|
||||
event: NostrEvent;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
export default function CalendarEventCard({
|
||||
className,
|
||||
event,
|
||||
}: CalendarEventCardProps) {
|
||||
const { pubkey, tags } = event;
|
||||
const { profile } = useProfile(pubkey);
|
||||
|
||||
const title = getTagValues("name", tags) || "Untitled";
|
||||
console.log("tite", tags);
|
||||
const image =
|
||||
getTagValues("image", tags) ??
|
||||
getTagValues("picture", tags) ??
|
||||
getTagValues("banner", tags) ??
|
||||
profile?.banner;
|
||||
|
||||
const description = event.content;
|
||||
const startDate = getTagValues("start", tags)
|
||||
? new Date(parseInt(getTagValues("start", tags) as string) * 1000)
|
||||
: null;
|
||||
const endDate = getTagValues("end", tags)
|
||||
? new Date(parseInt(getTagValues("end", tags) as string) * 1000)
|
||||
: null;
|
||||
const getLocation = () => {
|
||||
let temp = getTagAllValues("location", tags);
|
||||
if (temp[0]) {
|
||||
return temp;
|
||||
}
|
||||
return getTagAllValues("address", tags);
|
||||
};
|
||||
const location = getLocation();
|
||||
|
||||
const users = getTagsValues("p", tags);
|
||||
const hashtags = getTagsValues("t", tags);
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"group flex h-full flex-col rounded-[16px] p-2 hover:bg-muted",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
<div className="relative overflow-hidden rounded-md">
|
||||
<AspectRatio ratio={16 / 9} className="bg-muted">
|
||||
<Image
|
||||
src={image ?? ""}
|
||||
alt={title}
|
||||
width={250}
|
||||
height={150}
|
||||
unoptimized
|
||||
className={cn(
|
||||
"h-auto w-auto object-cover transition-all group-hover:scale-105",
|
||||
"aspect-video",
|
||||
)}
|
||||
/>
|
||||
</AspectRatio>
|
||||
</div>
|
||||
<div className="mt-3 flex-1 space-y-2 text-base">
|
||||
<h3 className="line-clamp-2 font-semibold leading-5">{title}</h3>
|
||||
<div className="flex flex-col items-start gap-y-1">
|
||||
<div className="flex flex-col items-start gap-x-3 gap-y-1">
|
||||
{!!startDate && (
|
||||
<>
|
||||
{startDate.getDay() === endDate?.getDay() ? (
|
||||
<div className="center shrink-0 gap-x-1 text-xs text-muted-foreground">
|
||||
<RxCalendar className="h-4 w-4 text-primary" />
|
||||
<span>{formatDate(startDate, "ddd, MMM D")}</span>
|
||||
</div>
|
||||
) : (
|
||||
<div className="center shrink-0 gap-x-1 text-xs text-muted-foreground">
|
||||
<RxCalendar className="h-4 w-4 text-primary" />
|
||||
<span>{formatDate(startDate, "ddd, MMM D")}</span>
|
||||
{!!endDate && (
|
||||
<>
|
||||
{" "}
|
||||
<span>-</span>{" "}
|
||||
<span>{formatDate(endDate, "MMM D")}</span>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="center shrink-0 gap-x-1 text-xs text-muted-foreground">
|
||||
<RxClock className="h-4 w-4 text-primary" />
|
||||
<span>{formatDate(startDate, "h:mm a")}</span>
|
||||
{!!endDate && (
|
||||
<>
|
||||
{" "}
|
||||
<span>-</span>{" "}
|
||||
<span>{formatDate(endDate, "h:mm a")}</span>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{!!users.length && (
|
||||
<div className="center gap-x-1 text-xs text-muted-foreground">
|
||||
<HiOutlineUsers className="h-4 w-4 text-primary" />
|
||||
<span>{formatNumber(users.length)}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-1 flex">
|
||||
<SmallProfileLine pubkey={pubkey} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
export function CardLoading({ className }: { className: string }) {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"group pointer-events-none flex flex-col space-y-3 rounded-[16px] p-2",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
<div className="overflow-hidden rounded-md">
|
||||
<AspectRatio ratio={16 / 9} className="bg-muted"></AspectRatio>
|
||||
</div>
|
||||
<div className="flex-1 space-y-2 text-base">
|
||||
<Skeleton className="mb-2 h-4 w-1/3 bg-muted" />
|
||||
<div className="flex flex-col items-start">
|
||||
<div className="center gap-x-1 text-xs text-muted-foreground">
|
||||
<Skeleton className="h-3 w-3 bg-muted" />
|
||||
<Skeleton className="h-3 w-[50px] bg-muted" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="-mt-1 flex flex-wrap gap-2 overflow-x-hidden">
|
||||
<Skeleton className="h-2 w-[50px] bg-muted" />
|
||||
<Skeleton className="h-2 w-[40px] bg-muted" />
|
||||
<Skeleton className="h-2 w-[30px] bg-muted" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
43
components/ProfileContainers/SmallProfileLine.tsx
Normal file
43
components/ProfileContainers/SmallProfileLine.tsx
Normal file
@ -0,0 +1,43 @@
|
||||
import Link from "next/link";
|
||||
import { Avatar, AvatarImage, AvatarFallback } from "@/components/ui/avatar";
|
||||
import useProfile from "@/lib/hooks/useProfile";
|
||||
import { nip19 } from "nostr-tools";
|
||||
import { getTwoLetters, getNameToShow } from "@/lib/utils";
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
import { HiMiniChevronRight, HiCheckBadge } from "react-icons/hi2";
|
||||
|
||||
type ProfileInfoProps = {
|
||||
pubkey: string;
|
||||
};
|
||||
export default function ProfileInfo({ pubkey }: ProfileInfoProps) {
|
||||
const { profile } = useProfile(pubkey);
|
||||
const npub = nip19.npubEncode(pubkey);
|
||||
return (
|
||||
<Link
|
||||
href={`/${npub}`}
|
||||
className="center group gap-x-2 rounded-sm rounded-r-full bg-background/50 pr-1 text-muted-foreground hover:shadow"
|
||||
>
|
||||
<Avatar className="center h-[16px] w-[16px] overflow-hidden rounded-[.25rem] bg-muted">
|
||||
<AvatarImage src={profile?.image} alt={profile?.displayName} />
|
||||
<AvatarFallback className="text-[8px]">
|
||||
{getTwoLetters({ npub, profile })}
|
||||
</AvatarFallback>
|
||||
</Avatar>
|
||||
<div className="flex items-center gap-1">
|
||||
<span className="text-[12px]">{getNameToShow({ npub, profile })}</span>
|
||||
{!!profile?.nip05 && <HiCheckBadge className="h-3 w-3 text-primary" />}
|
||||
</div>
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
export function LoadingProfileInfo() {
|
||||
return (
|
||||
<div className="center group gap-x-1">
|
||||
<Avatar className="center h-[16px] w-[16px] overflow-hidden rounded-[.25rem] bg-muted @sm:h-[18px] @sm:w-[18px]"></Avatar>
|
||||
<div className="space-y-1">
|
||||
<Skeleton className="h-2 w-[70px] bg-muted" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user