diff --git a/components/event.js b/components/event.js
index 4bf4ed0..d4c26bf 100644
--- a/components/event.js
+++ b/components/event.js
@@ -2,6 +2,7 @@ import { Box, Text, Flex, Avatar, Heading } from 'theme-ui'
import tt from 'tinytime'
import Link from 'next/link'
import Sparkles from './sparkles'
+import { XCircle, Users } from 'react-feather'
const past = dt => new Date(dt) < new Date()
const now = (start, end) =>
@@ -22,29 +23,66 @@ const tagStyle = {
mt: 1
}
-const Event = ({ id, slug, title, desc, leader, avatar, start, end, cal, tags }) => (
+const cancelledStyle = {
+ display: 'inline-flex',
+ alignItems: 'center',
+ gap: '4px',
+ fontSize: 0,
+ fontWeight: 'bold',
+ textTransform: 'uppercase',
+ letterSpacing: '0.05rem',
+ bg: 'red',
+ color: 'white',
+ px: 2,
+ py: '2px',
+ borderRadius: 'default',
+ mr: 1,
+ mt: 1
+}
+
+const Event = ({
+ id,
+ slug,
+ title,
+ desc,
+ leader,
+ avatar,
+ cancelled,
+ interestCount,
+ start,
+ end,
+ cal,
+ tags
+}) => (
@@ -56,6 +94,15 @@ const Event = ({ id, slug, title, desc, leader, avatar, start, end, cal, tags })
{title}
+ {cancelled && (
+
+
+ {' '}
+
+ Cancelled
+
+
+ )}
{tags?.length > 0 && (
{tags.map(tag => (
@@ -65,6 +112,7 @@ const Event = ({ id, slug, title, desc, leader, avatar, start, end, cal, tags })
))}
)}
+
)}
{leader}
+ {interestCount > 0 && (
+
+ {interestCount}
+
+ )}
- {now(start, end) && (
+ {now(start, end) && !cancelled && (
(
(
sx={{
color: 'primary',
borderRadius: 'circle',
+ cursor: 'pointer',
+ m: 2,
transition: 'box-shadow .125s ease-in-out',
':hover,:focus': {
boxShadow: '0 0 0 2px',
@@ -51,13 +61,26 @@ const Flag = () => (
const ColorSwitcher = props => {
const [mode, setMode] = useColorMode()
+ const [mounted, setMounted] = useState(false)
+ useEffect(() => {
+ setMounted(true)
+ }, [])
return (
setMode(mode === 'dark' ? 'light' : 'dark')}
title="Reverse color scheme"
+ sx={{
+ ...props.sx,
+ transition: 'transform 0.3s ease,box-shadow .125s ease-in-out'
+ }}
>
-
+ {mounted &&
+ (mode === 'dark' ? (
+
+ ) : (
+
+ ))}
)
}
@@ -65,13 +88,12 @@ const ColorSwitcher = props => {
export default () => {
const [mode] = useColorMode()
const router = useRouter()
-
const [session, setSession] = useState(null)
useEffect(() => {
fetch('/api/auth/me/')
- .then(r => r.json())
- .then(data => setSession(data))
- .catch(() => setSession({ slackId: null }))
+ .then(r => r.json())
+ .then(data => setSession(data))
+ .catch(() => setSession({ slackId: null }))
}, [])
const home = router.pathname === '/'
@@ -107,7 +129,7 @@ export default () => {
{session?.slackId ? (
-
+
{
src={`https://cachet.dunkirk.sh/users/${session.slackId}/r`}
alt="Your Slack Avatar"
size={28}
- sx={{hieght:28, width:28, borderRadius:'circle'}}
+ sx={{ height: 28, width: 28, borderRadius: 'circle' }}
/>
- ): session != null ? (
+ ) : session != null ? (
Log in
- ): null}
+ ) : null}
)
diff --git a/lib/data.js b/lib/data.js
index fb2b1c0..143c681 100644
--- a/lib/data.js
+++ b/lib/data.js
@@ -86,6 +86,10 @@ export const getEvents = async (page = 1) => {
avatar: e['Avatar'],
approved: e['Approved'] || false,
tags: e['Tags'] || [],
+ cancelled: e['Cancelled'] || false,
+ rawCancellation: e['RawCancellation'] || null,
+ interestCount:
+ typeof e['InterestCount'] === 'number' ? e['InterestCount'] : 0,
interestCount: e['InterestCount'] || 0,
leaderSlackId: e['LeaderSlackID'],
rsvpFormUrl: e['RSVPFormURL'] || null
diff --git a/pages/[slug].js b/pages/[slug].js
index bfb8b34..3494b75 100644
--- a/pages/[slug].js
+++ b/pages/[slug].js
@@ -11,7 +11,14 @@ import {
Spinner,
Text
} from 'theme-ui'
-import { Calendar, Download, Youtube } from 'react-feather'
+import {
+ Calendar,
+ Download,
+ Youtube,
+ XCircle,
+ Users,
+ AlertTriangle
+} from 'react-feather'
import { useRouter } from 'next/router'
import Head from 'next/head'
import Meta from '@hackclub/meta'
@@ -41,7 +48,11 @@ const makeICS = event => {
const location = calUrl?.searchParams.get('location') || ''
const escICS = str =>
- str.replace(/\\/g, '\\\\').replace(/;/g, '\\;').replace(/,/g, '\\,').replace(/\n/g, '\\n')
+ str
+ .replace(/\\/g, '\\\\')
+ .replace(/;/g, '\\;')
+ .replace(/,/g, '\\,')
+ .replace(/\n/g, '\\n')
const lines = [
'BEGIN:VCALENDAR',
@@ -76,6 +87,27 @@ const Page = ({ event }) => (
event.avatar
}`}
/>
+ {event.cancelled && (
+
+
+
+
+
+ This event has been cancelled
+
+
+ {event.rawCancellation && (
+
+ {event.rawCancellation}
+
+ )}
+
+
+ )}
@@ -155,8 +187,43 @@ const Page = ({ event }) => (
{tt('{h}:{mm} {a}').render(new Date(event.start))}–
{tt('{h}:{mm} {a}').render(new Date(event.end))}
+ {event.interestCount > 0 && (
+
+
+
+ {event.interestCount}{' '}
+ {event.interestCount === 1 ? 'person is' : 'people are'}{' '}
+ interested
+
+
+ )}
- {event.tags?.length > 0 && (
+ {event.cancelled && (
+
+
+ CANCELLED
+
+
+ )}
+ {event.tags?.length > 0 && !event.cancelled && (
{event.tags.map(tag => (
(
- {!past(event.start) && (
+ {!past(event.start) && !event.cancelled && (
-
)}
- { !event.ama && (
- event.rsvpFormUrl
- ?
- :
- )}
- {event.ama && (
+ {(event.youtube || event.ama) && (
(
}
py={[4, 5]}
>
- {past(event.start) || event.youtube ? (
+ {past(event.start) && event.youtube && (
<>
- {event.youtube && (
-
- )}
+
>
- ) : null}
- {!past(event.start) && (
+ )}
+
+ {!past(event.start) && event.ama && (
(
)}
+ {!past(event.start) && !event.ama && event.youtube && (
+
+
+ This event will be livestreamed on Youtube
+
+
+ )}
)}
>
@@ -410,6 +473,9 @@ export const getStaticProps = async ({ params }) => {
const { slug } = params
const events = await getEvents()
const event = find(events, { slug })
+ if (!event) {
+ return { notFound: true }
+ }
event.html = await parse(event.desc)
event.desc ??= null
return { props: { event }, revalidate: 2 }