blob: 9483c9914e71e07d215b03377a19f75f0a31eb22 [file]
/**
* Utility functions for events page
*/
import type {Event, EventType} from '@site/src/data/events';
/**
* Format event date for display
*/
export function formatEventDate(event: Event): string {
const start = new Date(event.date);
const options: Intl.DateTimeFormatOptions = {
year: 'numeric',
month: 'long',
day: 'numeric',
};
return start.toLocaleDateString('en-US', options);
}
/**
* Format event date range for display
*/
export function formatEventDateRange(startDate: string, endDate?: string): string {
const start = new Date(startDate);
if (!endDate || startDate === endDate) {
return formatEventDate({date: startDate} as Event);
}
const end = new Date(endDate);
const options: Intl.DateTimeFormatOptions = {
year: 'numeric',
month: 'long',
day: 'numeric',
};
return `${start.toLocaleDateString('en-US', options)} - ${end.toLocaleDateString('en-US', options)}`;
}
/**
* Group events by year
*/
export function groupEventsByYear(events: Event[]): Record<string, Event[]> {
return events.reduce((acc, event) => {
const year = new Date(event.date).getFullYear().toString();
if (!acc[year]) {
acc[year] = [];
}
acc[year].push(event);
return acc;
}, {} as Record<string, Event[]>);
}
/**
* Filter events by type and year
*/
export function filterEvents(
events: Event[],
type: EventType | null,
year: string | null
): Event[] {
return events.filter((event) => {
if (type && event.type !== type) return false;
if (year && new Date(event.date).getFullYear().toString() !== year) return false;
return true;
});
}
/**
* Sort events by date
*/
export function sortEventsByDate(events: Event[], ascending = false): Event[] {
return [...events].sort((a, b) => {
const dateA = new Date(a.date).getTime();
const dateB = new Date(b.date).getTime();
return ascending ? dateA - dateB : dateB - dateA;
});
}
/**
* Get upcoming events (date >= today)
*/
export function getUpcomingEvents(events: Event[]): Event[] {
const now = new Date();
now.setHours(0, 0, 0, 0); // Reset to start of day
return events.filter((event) => new Date(event.date) >= now);
}
/**
* Get past events (date < today)
*/
export function getPastEvents(events: Event[]): Event[] {
const now = new Date();
now.setHours(0, 0, 0, 0); // Reset to start of day
return events.filter((event) => new Date(event.date) < now);
}
/**
* Get available years from events
*/
export function getAvailableYears(events: Event[]): string[] {
const years = new Set<string>();
events.forEach((event) => {
years.add(new Date(event.date).getFullYear().toString());
});
return Array.from(years).sort((a, b) => parseInt(b) - parseInt(a));
}
/**
* Get available event types from events
*/
export function getAvailableTypes(events: Event[]): EventType[] {
const types = new Set<EventType>();
events.forEach((event) => {
types.add(event.type);
});
return Array.from(types).sort();
}
/**
* Extract YouTube video ID from URL
*/
export function extractYouTubeVideoId(url: string): string | null {
const patterns = [
/(?:youtube\.com\/watch\?v=|youtu\.be\/)([^&\n?#]+)/,
/youtube\.com\/embed\/([^&\n?#]+)/,
/youtube\.com\/v\/([^&\n?#]+)/,
];
for (const pattern of patterns) {
const match = url.match(pattern);
if (match) {
return match[1];
}
}
return null;
}
/**
* Get video thumbnail URL for display
* Returns a local placeholder image to comply with ASF Content Security Policy
* which prohibits loading external resources without user interaction
*/
export function getYouTubeThumbnail(videoUrl: string): string | null {
const videoId = extractYouTubeVideoId(videoUrl);
if (!videoId) return null;
return '/img/events/content/meetup.png';
}
/**
* Check if a link is external
*/
export function isExternalLink(url: string): boolean {
try {
const urlObj = new URL(url);
return urlObj.hostname !== 'ignite.apache.org' && urlObj.hostname !== 'localhost';
} catch {
// If URL parsing fails, assume it's a relative/internal link
return false;
}
}