diff --git a/src/components/EmojiReactions.tsx b/src/components/EmojiReactions.tsx index a45bbd8..769785e 100644 --- a/src/components/EmojiReactions.tsx +++ b/src/components/EmojiReactions.tsx @@ -61,7 +61,7 @@ export function EmojiReactions({ }} className="text-[var(--theme-text-muted)] hover:text-[var(--theme-text-primary)] transition-colors cursor-pointer inline-flex items-center" > - + {showPicker && ( diff --git a/src/components/EventCard.tsx b/src/components/EventCard.tsx index 1e4c2e9..f4738b7 100644 --- a/src/components/EventCard.tsx +++ b/src/components/EventCard.tsx @@ -42,6 +42,8 @@ interface EventCardProps { onRsvp?: () => void; /** Compact mode for map popups — smaller text, no impression tracking */ compact?: boolean; + /** When true, RSVP + link buttons render above the flyer inside the card (star button is external) */ + buttonsAboveFlyer?: boolean; } function FriendsGoingModal({ @@ -144,6 +146,7 @@ export const EventCard = memo(function EventCard({ rsvpStatus, onRsvp, compact, + buttonsAboveFlyer, }: EventCardProps) { const [showFriendsModal, setShowFriendsModal] = useState(false); const [showCheckedInModal, setShowCheckedInModal] = useState(false); @@ -222,7 +225,7 @@ export const EventCard = memo(function EventCard({ : `${event.startTime}${event.endTime ? ` - ${event.endTime}` : ''}`; return ( -
{/* Left column: action buttons + cover image */} -
-
- {onItineraryToggle && ( - - )} - {onRsvp && event.link && ( -
- -
- )} - {event.link && ( - - )} + {buttonsAboveFlyer ? ( +
+ {/* Flyer image only */} + {event.link && }
- {event.link && } -
+ ) : ( +
+
+ {onItineraryToggle && ( + + )} + {onRsvp && event.link && ( +
+ +
+ )} + {event.link && ( + + )} +
+ {event.link && } +
+ )} {/* Right: event details */}
@@ -323,8 +333,12 @@ export const EventCard = memo(function EventCard({ 'bg-green-400' }`} title={liveUrgency === 'red' ? 'Ending soon' : liveUrgency === 'yellow' ? 'Less than 1hr left' : 'Live now'} /> )} - - {event.date} · {timeDisplay} + + {event.date} · {timeDisplay} + + {event.isAllDay ? 'All Day' : `${event.startTime}${event.endTime ? `-${event.endTime}` : ''}`} + {event.address && · {shortenAddress(event.address)}} +

{(checkInCount ?? 0) > 0 && ( @@ -351,11 +365,11 @@ export const EventCard = memo(function EventCard({ )}
- {/* Address */} + {/* Address — hidden on mobile (shown inline with time) */} {event.address && ( + className="hidden sm:flex w-full text-[var(--theme-text-muted)] hover:text-[var(--theme-text-secondary)] text-sm mt-1 items-start gap-1 overflow-hidden transition-colors min-w-0"> {shortenAddress(event.address)} {userLocation && event.lat && event.lng && ( @@ -370,7 +384,7 @@ export const EventCard = memo(function EventCard({ )} {/* Tags */} -
+
{event.tags.map((tag) => ( ))} @@ -381,8 +395,41 @@ export const EventCard = memo(function EventCard({

{event.note}

)} - {/* Bottom row: friends + reactions + checked-in indicator */} -
+ {/* Bottom strip: action buttons + reactions */} +
+ {buttonsAboveFlyer && onItineraryToggle && ( + + )} + {buttonsAboveFlyer && onRsvp && event.link && ( + + )} + {buttonsAboveFlyer && event.link && ( + + )} + {onToggleReaction && ( + + )} {friendsGoing && friendsGoing.length > 0 && ( )} - {onToggleReaction && ( - - )} - {/* Comments disabled until social verification is in place */} - {/* */} {checkedInFriends && checkedInFriends.length > 0 && (
); }); + +/* ------------------------------------------------------------------ */ +/* EventCardActions — external star button for buttonsAboveFlyer mode */ +/* ------------------------------------------------------------------ */ + +export function EventCardActions({ event, isInItinerary, onItineraryToggle }: { + event: ETHDenverEvent; + isInItinerary: boolean; + onItineraryToggle: (eventId: string) => void; +}) { + return ( + + ); +} diff --git a/src/components/EventPopup.tsx b/src/components/EventPopup.tsx index 43fa3ec..cf906ac 100644 --- a/src/components/EventPopup.tsx +++ b/src/components/EventPopup.tsx @@ -95,6 +95,7 @@ export const EventPopup = memo(function EventPopup({ rsvpStatus={rsvpStatus} onRsvp={onRsvp} compact + buttonsAboveFlyer />
@@ -155,6 +156,7 @@ export function MultiEventPopup({ onToggleReaction={onToggleReaction} commentCount={commentCounts?.get(event.id)} compact + buttonsAboveFlyer />
))} diff --git a/src/components/ListView.tsx b/src/components/ListView.tsx index 8bcbcac..306ce88 100644 --- a/src/components/ListView.tsx +++ b/src/components/ListView.tsx @@ -436,7 +436,7 @@ export const ListView = memo(function ListView({
) : item.kind === 'ad' ? ( -
+
) : ( -
+
setLightboxEventIndex(virtualRow.index)} rsvpStatus={getRsvpStatus?.(item.event.id)} onRsvp={item.event.link ? () => onRsvp?.(item.event.id, item.event.link!, item.event.name) : undefined} + buttonsAboveFlyer />
)} diff --git a/src/components/OGImage.tsx b/src/components/OGImage.tsx index 1512f54..f81ee96 100644 --- a/src/components/OGImage.tsx +++ b/src/components/OGImage.tsx @@ -16,11 +16,13 @@ interface OGImageProps { isInItinerary?: boolean; onItineraryToggle?: (eventId: string) => void; friendsGoing?: FriendInfo[]; + /** Optional className to override the default width classes on the thumbnail container */ + className?: string; } export const imageCache = new Map(); -export function OGImage({ url, eventId, rsvpUrl, onOpenLightbox, isInItinerary, onItineraryToggle, friendsGoing }: OGImageProps) { +export function OGImage({ url, eventId, rsvpUrl, onOpenLightbox, isInItinerary, onItineraryToggle, friendsGoing, className }: OGImageProps) { const [imageUrl, setImageUrl] = useState( imageCache.get(url) ?? null ); @@ -86,7 +88,7 @@ export function OGImage({ url, eventId, rsvpUrl, onOpenLightbox, isInItinerary, <>
{ e.stopPropagation(); if (!imageUrl) return; @@ -104,7 +106,7 @@ export function OGImage({ url, eventId, rsvpUrl, onOpenLightbox, isInItinerary, setError(true)} /> @@ -131,7 +133,7 @@ export function OGImage({ url, eventId, rsvpUrl, onOpenLightbox, isInItinerary,
{eventId && onItineraryToggle && ( @@ -253,11 +255,13 @@ export function FlyerLightbox({ imageUrl, rsvpUrl, onClose, onPrev, onNext, even />
{eventId && onItineraryToggle && ( - +
+ +
)} {rsvpUrl && ( onOpenLightbox() : undefined} + buttonsAboveFlyer />