Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Retroactive event calendar #70

Merged
merged 3 commits into from
May 3, 2024
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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'
@@ -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(`https://calypso.datasektionen.se/api/event/span`);
url.searchParams.set('startDate', iso(startDate));
url.searchParams.set('endDate', iso(endDate));
return url.href;
}

export const Calypso = ({ type, search, children, ttl, 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
@@ -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.
@@ -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;

@@ -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),
@@ -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 = [];
@@ -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" }}>
@@ -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}`}
@@ -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;
15 changes: 11 additions & 4 deletions src/components/Frontpage/index.js
Original file line number Diff line number Diff line change
@@ -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)
@@ -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'}>
@@ -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>

15 changes: 11 additions & 4 deletions src/components/News/index.js
Original file line number Diff line number Diff line change
@@ -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
@@ -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>

@@ -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)