Skip to content

Commit

Permalink
Retroactive event calendar (#70)
Browse files Browse the repository at this point in the history
* Retroactive event calendar
  • Loading branch information
Alvinn8 authored May 3, 2024
1 parent 30ee469 commit c5ba93d
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 24 deletions.
56 changes: 50 additions & 6 deletions src/components/Calypso.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from 'react'
import React, { useState } from 'react'
import fetch from 'cross-fetch'

import { DataLoader } from './DataLoader'
Expand All @@ -13,13 +13,57 @@ const calypsoFetcher = url =>
console.error('Calypso error', err)
})

export const Calypso = ({ type, search, children, ttl }) =>
<DataLoader
cacheKey={`${RAZZLE_CALYPSO_URL}/${type || 'list'}${search || ''}`}
function iso(date) {
let str = date.toISOString();
if (str.endsWith('Z')) {
return str.slice(0, -1);
}
return str;
}

/**
* Get the Calypso API URL to fetch from. If events are being fetched and a time
* span is set, events in that time span will be fetched. Otherwise, all future
* events will be returned.
*
* @param {'list' | 'event'} type The type of post to fetch.
* @param {string} search Search part of the URL.
* @param {[Date, Date] | null} timeSpan The time span, or null for all future events.
*/
function getCalypsoUrl(type, search, timeSpan) {
if (!timeSpan || type !== 'event') {
return `${RAZZLE_CALYPSO_URL}/${type || 'list'}${search || ''}`;
}
const [startDate, endDate] = timeSpan;
const url = new URL(`${RAZZLE_CALYPSO_URL}/event/span`);
url.searchParams.set('startDate', iso(startDate));
url.searchParams.set('endDate', iso(endDate));
return url.href;
}

export const Calypso = ({ type, search, children, defaultTimeSpan }) => {
const [timeSpan, setTimeSpan] = useState(defaultTimeSpan);
const cacheKey = getCalypsoUrl(type, search, timeSpan);

return <DataLoader
cacheKey={cacheKey}
fetcher={calypsoFetcher}
ttl={CALYPSO_CACHE_TTL}
>
{({ data, loading, time }) => children(data) }
</DataLoader>
{({ data, loading, time }) => {
if (type === 'event') {
return children({
content: data,
loading,
time,
onUpdateTimeSpan(span) {
setTimeSpan(span);
}
});
}
return children(data);
}}
</DataLoader>;
}

export default Calypso
55 changes: 45 additions & 10 deletions src/components/EventCalendar/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,19 @@ function getDatesOfWeek(date) {
return datesOfWeek;
}

/**
* Get the start and end dates for a week.
*
* @param {Date} date A date inside the week.
*/
export function getWeekTimeSpan(date) {
const monday = getMondayOfWeek(date);
monday.setHours(0, 0, 0, 0);
const sunday = addDays(monday, 6);
sunday.setHours(23, 59, 59, 999);
return [monday, sunday];
}

/**
* Given an array of event objects, return an array widgetWeekGroups
* of WidgetWeekGroup objects.
Expand Down Expand Up @@ -241,10 +254,10 @@ function getWidgetsFromEvents(events) {
/**
* Event calendar in the calendar section.
*
* @param {{events: Event[]; location: any; lang: any;}} props
* @param {{events: Event[]; location: any; lang: any; onUpdateTimeSpan: ([Date, Date]) => void}} props
* @returns {JSX.Element}
*/
export default function EventCalendar({ events, location, lang }) {
export default function EventCalendar({ events, location, lang, onUpdateTimeSpan }) {
const [columnWidth, setColumnWidth] = useState(56);
var widthUpdateTimer = null;

Expand Down Expand Up @@ -272,22 +285,32 @@ export default function EventCalendar({ events, location, lang }) {
dates: getDatesOfWeek(today),
widgetIndex: 0,
});
const [hasCalculatedWeek, setHasCalculatedWeek] = useState(false);
const [selectedEventIndex, setSelectedEventIndex] = useState(-1);

var widgetWeekGroups = getWidgetsFromEvents(events);
const [widgetWeekGroups, setWidgetWeekGroups] = useState(() => getWidgetsFromEvents(events));
const monthsOfDates = weekState.dates.map(date => date.getMonth());

// When the calendar is first loaded, calculate the week to display.
useEffect(() => {
widgetWeekGroups = getWidgetsFromEvents(events);
const newWidgetWeekGroups = getWidgetsFromEvents(events);
setWidgetWeekGroups(newWidgetWeekGroups);

setSelectedEventIndex(-1);
if (events.length === 0) return;
// Do not re-calculate the week if the user manually changes the viewed week.
if (hasCalculatedWeek) return;

const timedelta = getMondayOfWeek(today) - getMondayOfWeek(events[0].eventStartTime);
const currentWeek = Math.round(timedelta / (1000 * 60 * 60 * 27 * 7));
var widgetIndex = -1;
for (var i = 0; i < widgetWeekGroups.length; i++) {
if (widgetWeekGroups[i].week > currentWeek) {
for (var i = 0; i < newWidgetWeekGroups.length; i++) {
if (newWidgetWeekGroups[i].week > currentWeek) {
break;
}
widgetIndex = i;
}
setHasCalculatedWeek(true);
setWeekState({
week: currentWeek,
dates: getDatesOfWeek(today),
Expand All @@ -306,25 +329,35 @@ export default function EventCalendar({ events, location, lang }) {
];
}

function updateTimeSpan(date) {
if (!onUpdateTimeSpan) return;
const span = getWeekTimeSpan(date);
onUpdateTimeSpan(span);
}

function goBack() {
const week = weekState.week;
const wi = weekState.widgetIndex;
const dates = weekState.dates.map(date => addDays(date, -7));
setWeekState({
week: week - 1,
dates: weekState.dates.map(date => addDays(date, -7)),
dates,
widgetIndex: (wi > 0 && widgetWeekGroups[wi - 1].week === week - 1) ? wi - 1 : wi,
});
updateTimeSpan(dates[0]);
}

function goForward() {
const week = weekState.week;
const wi = weekState.widgetIndex;
const end = widgetWeekGroups.length - 1;
const dates = weekState.dates.map(date => addDays(date, 7));
setWeekState({
week: weekState.week + 1,
dates: weekState.dates.map(date => addDays(date, 7)),
dates,
widgetIndex: (wi < end && widgetWeekGroups[wi + 1].week === week + 1) ? wi + 1 : wi,
});
updateTimeSpan(dates[0]);
}

const yearHeader = [];
Expand Down Expand Up @@ -393,7 +426,7 @@ export default function EventCalendar({ events, location, lang }) {
</table>
<div className={cx("eventDisplay")}>
{
selectedEventIndex !== -1
selectedEventIndex !== -1 && events[selectedEventIndex]
? <NewsItem item={events[selectedEventIndex]} location={location} lang={lang}/>
: <div style={{ height: "100%", width: "100%", padding: "10%", display: "flex", alignItems: "center", backgroundColor: "#eeeeee" }}>
<p style={{ fontSize: "24px", textAlign: "center" }}>
Expand All @@ -407,7 +440,6 @@ export default function EventCalendar({ events, location, lang }) {
{weekState.widgetIndex >= 0
&& widgetWeekGroups
&& widgetWeekGroups.length > 0
&& widgetWeekGroups[weekState.widgetIndex].week === weekState.week
&& widgetWeekGroups[weekState.widgetIndex].widgets.map((eventWidget, ei) => (
<div
key={`event-${ei}`}
Expand All @@ -424,6 +456,9 @@ export default function EventCalendar({ events, location, lang }) {
if (block.endMinute <= 8*H) {
return null;
}
if (!events[eventWidget.eventIndex]) {
return null;
}

const blockStartMinute = Math.max(8*H, block.startMinute);
const blockDuration = block.endMinute - blockStartMinute;
Expand Down
15 changes: 11 additions & 4 deletions src/components/Frontpage/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { Translate, English, Swedish } from '../Translate'

import styles from './Frontpage.module.css'
import skold from './skold.svg'
import EventCalendar from '../EventCalendar/index.jsx';
import EventCalendar, { getWeekTimeSpan } from '../EventCalendar/index.jsx';
import './FixMe.css'

const cx = classNames.bind(styles)
Expand Down Expand Up @@ -127,7 +127,7 @@ const Frontpage = ({ location, lang }) =>
{/* Events section */}
<Calypso type='event'>
{/* Given content from Calypso, populate the section with events information */}
{content => (
{({content}) => (
<div className={cx('news')}>
{/* Title */}
<Link to={lang === 'en' ? '/en/news?itemType=EVENT' : '/nyheter?itemType=EVENT'}>
Expand Down Expand Up @@ -255,9 +255,16 @@ const Frontpage = ({ location, lang }) =>
</h2>

{/* Calendar */}
<Calypso type='event'>
<Calypso type='event' defaultTimeSpan={getWeekTimeSpan(new Date())}>
{/* Given content from Calypso, populate the section with events information */}
{content => <EventCalendar events={content} location={location} lang={lang} />}
{({content, loading, onUpdateTimeSpan}) =>
<EventCalendar
events={loading ? [] : content}
location={location}
lang={lang}
onUpdateTimeSpan={onUpdateTimeSpan}
/>
}
</Calypso>
</div>

Expand Down
15 changes: 11 additions & 4 deletions src/components/News/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import styles from './News.module.css'
import Calypso from '../Calypso'
import { Translate, English, Swedish } from '../Translate'
import NewsItem from './NewsItem'
import EventCalendar from '../EventCalendar'
import EventCalendar, { getWeekTimeSpan } from '../EventCalendar'

if(global && !global.URLSearchParams) {
global.URLSearchParams = require('url').URLSearchParams
Expand Down Expand Up @@ -73,9 +73,16 @@ export const News = ({ location, lang, match }) => {
</h2>

{/* Calendar */}
<Calypso type='event'>
<Calypso type='event' defaultTimeSpan={getWeekTimeSpan(new Date())}>
{/* Given content from Calypso, populate the section with events information */}
{content => <EventCalendar events={content} location={location} lang={lang} />}
{({content, loading, onUpdateTimeSpan}) =>
<EventCalendar
events={loading ? [] : content}
location={location}
lang={lang}
onUpdateTimeSpan={onUpdateTimeSpan}
/>
}
</Calypso>
</div>

Expand Down Expand Up @@ -174,7 +181,7 @@ export const News = ({ location, lang, match }) => {
</Translate>
</h2>
<Calypso type='event'>
{content =>
{({content}) =>
(content && content.length)
? content
.filter((_, i) => i < 5)
Expand Down

0 comments on commit c5ba93d

Please sign in to comment.