diff --git a/unlock-app/src/components/content/event/EventDetails.tsx b/unlock-app/src/components/content/event/EventDetails.tsx index 82655b6f536..c42e9335816 100644 --- a/unlock-app/src/components/content/event/EventDetails.tsx +++ b/unlock-app/src/components/content/event/EventDetails.tsx @@ -9,7 +9,7 @@ import { Icon, minifyAddress, } from '@unlock-protocol/ui' -import { getEventDate, getEventEndDate, getEventUrl } from './utils' +import { getEventUrl } from './utils' import { useEventOrganizer } from '~/hooks/useEventOrganizer' import { useEventOrganizers } from '~/hooks/useEventOrganizers' import dayjs from 'dayjs' @@ -22,6 +22,7 @@ import { useEventVerifiers } from '~/hooks/useEventVerifiers' import { EventDefaultLayout } from './Layout/EventDefaultLayout' import { EventBannerlessLayout } from './Layout/EventBannerlessLayout' import { EventsLayout } from './Layout/constants' +import { formatEventDates } from '~/utils/formatEventDates' interface EventDetailsProps { event: Event @@ -44,7 +45,7 @@ export const EventDetails = ({ }: EventDetailsProps) => { const router = useRouter() - // Check if the user is one of the lock manager + // Check if the user is one of the lock managers const { data: isOrganizer } = useEventOrganizer({ checkoutConfig, }) @@ -54,9 +55,7 @@ export const EventDetails = ({ { initialData: eventProp } ) - const eventUrl = getEventUrl({ - event, - }) + const eventUrl = getEventUrl({ event }) const { data: organizers } = useEventOrganizers({ checkoutConfig, @@ -75,58 +74,22 @@ export const EventDetails = ({ ) } - const eventDate = getEventDate(event.ticket) // Full date + time of event - const eventEndDate = getEventEndDate(event.ticket) + // Format the event dates and get boolean flags + const { + startDate, + startTime, + endDate, + endTime, + eventDate, + eventEndDate, + hasDate, + hasLocation, + } = formatEventDates(event.ticket, language()) + const hasPassed = eventEndDate ? dayjs().isAfter(eventEndDate) : dayjs().isAfter(eventDate) - const isSameDay = dayjs(eventDate).isSame(eventEndDate, 'day') - - const startDate = eventDate - ? eventDate.toLocaleDateString(undefined, { - timeZone: event.ticket.event_timezone, - weekday: 'long', - year: 'numeric', - month: 'short', - day: 'numeric', - }) - : null - - const startTime = - eventDate && event.ticket.event_start_time - ? eventDate.toLocaleTimeString(language(), { - timeZone: event.ticket.event_timezone, - hour: '2-digit', - minute: '2-digit', - timeZoneName: 'short', - }) - : undefined - - const endDate = - eventEndDate && eventEndDate && !isSameDay - ? eventEndDate.toLocaleDateString(undefined, { - timeZone: event.ticket.event_timezone, - weekday: 'long', - year: 'numeric', - month: 'short', - day: 'numeric', - }) - : null - - const endTime = - eventDate && event.ticket.event_end_time && eventEndDate && isSameDay - ? eventEndDate.toLocaleTimeString(language(), { - timeZone: event.ticket.event_timezone, - hour: '2-digit', - minute: '2-digit', - timeZoneName: 'short', - }) - : null - - const hasLocation = (event.ticket.event_address || '')?.length > 0 - const hasDate = startDate || startTime || endDate || endTime - const coverImage = event.ticket.event_cover_image const layoutProps = { diff --git a/unlock-app/src/components/content/event/Layout/EventBannerlessLayout.tsx b/unlock-app/src/components/content/event/Layout/EventBannerlessLayout.tsx index 3726fc401fb..91aa7320661 100644 --- a/unlock-app/src/components/content/event/Layout/EventBannerlessLayout.tsx +++ b/unlock-app/src/components/content/event/Layout/EventBannerlessLayout.tsx @@ -1,4 +1,3 @@ -import React from 'react' import { Event, PaywallConfigType } from '@unlock-protocol/core' import AddToCalendarButton from '../AddToCalendarButton' import TweetItButton from '../TweetItButton' @@ -21,7 +20,7 @@ type EventBannerlessProps = { config: PaywallConfigType } hasLocation: boolean - hasDate: string | null + hasDate: boolean organizers: string[] | undefined startDate: string | null endDate: string | null @@ -51,7 +50,7 @@ export const EventBannerlessLayout = ({
{event.title}
diff --git a/unlock-app/src/components/content/event/Layout/EventDefaultLayout.tsx b/unlock-app/src/components/content/event/Layout/EventDefaultLayout.tsx index c0f0897fb56..a859518ec9c 100644 --- a/unlock-app/src/components/content/event/Layout/EventDefaultLayout.tsx +++ b/unlock-app/src/components/content/event/Layout/EventDefaultLayout.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react' +import { useState } from 'react' import { CoverImageDrawer } from '../CoverImageDrawer' import { Event, PaywallConfigType } from '@unlock-protocol/core' import AddToCalendarButton from '../AddToCalendarButton' @@ -22,7 +22,7 @@ type EventDefaultLayoutProps = { config: PaywallConfigType } hasLocation: boolean - hasDate: string | null + hasDate: boolean coverImage: string refetch: () => void organizers: string[] | undefined @@ -75,7 +75,7 @@ export const EventDefaultLayout = ({
{event.title}
diff --git a/unlock-app/src/components/content/events-collection/EventDetailDrawer.tsx b/unlock-app/src/components/content/events-collection/EventDetailDrawer.tsx index 1b6582cf1fd..09bc4d1a0c2 100644 --- a/unlock-app/src/components/content/events-collection/EventDetailDrawer.tsx +++ b/unlock-app/src/components/content/events-collection/EventDetailDrawer.tsx @@ -1,5 +1,4 @@ import React from 'react' -import { Event } from './EventsCollectionDetailContent' import ReactMarkdown from 'react-markdown' import { Button, Drawer, Placeholder } from '@unlock-protocol/ui' import { AiOutlineCalendar as CalendarIcon } from 'react-icons/ai' @@ -9,21 +8,24 @@ import { useEventOrganizers } from '~/hooks/useEventOrganizers' import Hosts from '../event/Hosts' import { EventDetail } from '../event/EventDetail' import { EventLocation } from '../event/EventLocation' -import dayjs from 'dayjs' import AddToCalendarButton from '../event/AddToCalendarButton' import { FaExternalLinkAlt } from 'react-icons/fa' import Link from 'next/link' import PastEvent from '../event/Layout/PastEvent' import RemoveFromCollectionButton from './RemoveFromCollectionButton' -import { getEventAttributes } from '~/utils/eventCollections' import { useEventOrganizer } from '~/hooks/useEventOrganizer' import { TbSettings } from 'react-icons/tb' +import { useEvent } from '~/hooks/useEvent' +import { toFormData } from '~/components/interface/locks/metadata/utils' +import { Event } from '@unlock-protocol/core' +import { getEventUrl } from '../event/utils' +import { formatEventDates } from '~/utils/formatEventDates' interface EventDetailDrawerProps { collectionSlug: string | undefined isOpen: boolean setIsOpen: (isOpen: boolean) => void - event: Event | null + selectedEvent: any isManager: boolean } @@ -31,13 +33,26 @@ export const EventDetailDrawer: React.FC = ({ collectionSlug, isOpen, setIsOpen, - event, + selectedEvent, isManager, }) => { const { data: checkoutConfig, isPending: isCheckoutConfigPending } = useCheckoutConfig({ - id: event?.checkoutConfigId, + id: selectedEvent?.checkoutConfigId, }) + const { data: eventDetails } = useEvent({ + slug: selectedEvent?.slug, + }) + + // transform the event details into an event object + const event = toFormData({ + ...eventDetails!, + slug: selectedEvent?.slug, + }) as Event + + const eventUrl = getEventUrl({ + event, + }) const { data: isEventOrganizer } = useEventOrganizer({ checkoutConfig: checkoutConfig!, @@ -49,46 +64,18 @@ export const EventDetailDrawer: React.FC = ({ if (!event) return null - const { name, data } = event - const { image, description } = data + const { name, image, description } = event + // Format the event dates and get boolean flags const { - startDate: eventStartDate, - startTime: eventStartTime, - endDate: eventEndDate, + startDate, + startTime, + endDate, endTime, - timezone, - address, - } = getEventAttributes(event) - - const parsedEvent = { - ...event.data, - slug: event.slug!, - ticket: { - event_start_date: eventStartDate, - event_end_date: eventEndDate, - event_start_time: eventStartTime, - event_end_time: endTime, - event_timezone: timezone, - event_address: address, - }, - } - - const eventDate = dayjs.tz(`${eventStartDate} ${eventStartTime}`, timezone) - const eventEndDateObj = dayjs.tz(`${eventEndDate} ${endTime}`, timezone) - - const hasPassed = dayjs().isAfter(eventEndDateObj) - - const isSameDay = eventDate.isSame(eventEndDateObj, 'day') - - const startDate = eventDate.format('dddd, MMMM D, YYYY') - - const endDate = !isSameDay - ? eventEndDateObj.format('dddd, MMMM D, YYYY') - : null - - const hasLocation = address?.length > 0 - const hasDate = startDate || endDate + hasDate, + hasLocation, + hasPassed, + } = formatEventDates(event.ticket) // close drawer when the event is removed const handleEventRemove = () => { @@ -131,11 +118,8 @@ export const EventDetailDrawer: React.FC = ({ />
- - + + @@ -155,19 +139,22 @@ export const EventDetailDrawer: React.FC = ({ {/* Date */} {hasDate && ( -
- {startDate && endDate && ( - {dayjs(startDate).format('dddd D MMM YYYY')} +
+ {startDate} + {startTime && {startTime}} + {endDate && ( + <> + to + {endDate} + )} + {endTime && {endTime}}
)} {/* Location */} - {hasLocation && } + {hasLocation && } {isCheckoutConfigPending ? ( @@ -181,8 +168,7 @@ export const EventDetailDrawer: React.FC = ({ /> ) : ( )} diff --git a/unlock-app/src/components/content/events-collection/EventsCollectionDetailContent.tsx b/unlock-app/src/components/content/events-collection/EventsCollectionDetailContent.tsx index 21024b35d2d..54f2caee393 100644 --- a/unlock-app/src/components/content/events-collection/EventsCollectionDetailContent.tsx +++ b/unlock-app/src/components/content/events-collection/EventsCollectionDetailContent.tsx @@ -17,7 +17,6 @@ import { import { FaGithub, FaGlobe, FaXTwitter, FaYoutube } from 'react-icons/fa6' import { SiFarcaster as FarcasterIcon } from 'react-icons/si' import AddEventsToCollectionDrawer from './AddEventsToCollectionDawer' -import { EventDetailDrawer } from './EventDetailDrawer' import { Metadata } from '@unlock-protocol/core' import CopyUrlButton from '../event/CopyUrlButton' import TweetItButton from '../event/TweetItButton' @@ -28,6 +27,7 @@ import utc from 'dayjs/plugin/utc' import timezone from 'dayjs/plugin/timezone' import { useConnectModal } from '~/hooks/useConnectModal' import { useAuthenticate } from '~/hooks/useAuthenticate' +import { EventDetailDrawer } from './EventDetailDrawer' dayjs.extend(utc) dayjs.extend(timezone) @@ -353,7 +353,7 @@ export default function EventsCollectionDetailContent({ collectionSlug={eventCollection?.slug} isOpen={isEventDetailDrawerOpen} setIsOpen={setIsEventDetailDrawerOpen} - event={selectedEvent} + selectedEvent={selectedEvent} isManager={isManager} /> )} diff --git a/unlock-app/src/utils/formatEventDates.ts b/unlock-app/src/utils/formatEventDates.ts new file mode 100644 index 00000000000..5f20b02e4df --- /dev/null +++ b/unlock-app/src/utils/formatEventDates.ts @@ -0,0 +1,106 @@ +import dayjs from 'dayjs' +import { + getEventDate, + getEventEndDate, +} from '../components/content/event/utils' + +export interface FormattedEventDates { + eventDate: Date | null + eventEndDate: Date | null + isSameDay: boolean + startDate: string | null + startTime?: string + endDate: string | null + endTime: string | null + hasDate: boolean + hasLocation: boolean + hasPassed: boolean +} + +/** + * Formats event date and time details based on the provided event ticket. + * Also returns boolean flags for date, location, and whether the event has passed. + * @param ticket - The event ticket object containing date/time information. + * @param locale - Optional locale; defaults to navigator.language or 'en-US' if not available. + * @returns An object with formatted date/time strings and boolean flags. + */ +export const formatEventDates = ( + ticket: any, + locale?: string +): FormattedEventDates => { + const eventDate = getEventDate(ticket) + const eventEndDate = getEventEndDate(ticket) + const isSameDay = + eventDate && eventEndDate + ? dayjs(eventDate).isSame(eventEndDate, 'day') + : false + + let startDate: string | null = null + let startTime: string | undefined = undefined + let endDate: string | null = null + let endTime: string | null = null + + const userLocale = + locale || (typeof navigator !== 'undefined' ? navigator.language : 'en-US') + + if (eventDate) { + startDate = eventDate.toLocaleDateString(undefined, { + timeZone: ticket.event_timezone, + weekday: 'long', + year: 'numeric', + month: 'short', + day: 'numeric', + }) + if (ticket.event_start_time) { + startTime = eventDate.toLocaleTimeString(userLocale, { + timeZone: ticket.event_timezone, + hour: '2-digit', + minute: '2-digit', + timeZoneName: 'short', + }) + } + } + + if (eventEndDate) { + if (!isSameDay) { + endDate = eventEndDate.toLocaleDateString(undefined, { + timeZone: ticket.event_timezone, + weekday: 'long', + year: 'numeric', + month: 'short', + day: 'numeric', + }) + } else if (ticket.event_end_time) { + endTime = eventEndDate.toLocaleTimeString(userLocale, { + timeZone: ticket.event_timezone, + hour: '2-digit', + minute: '2-digit', + timeZoneName: 'short', + }) + } + } + + const hasDate = Boolean(startDate || startTime || endDate || endTime) + const hasLocation = Boolean( + ticket.event_address && ticket.event_address.length > 0 + ) + + const hasPassed = eventEndDate + ? dayjs().isAfter(eventEndDate) + : eventDate + ? dayjs().isAfter(eventDate) + : false + + return { + eventDate, + eventEndDate, + isSameDay, + startDate, + startTime, + endDate, + endTime, + hasDate, + hasLocation, + hasPassed, + } +}