From 8d9a8cf8680c7745dec802d611b4e4c7e09c8396 Mon Sep 17 00:00:00 2001 From: Benji Grant Date: Sun, 18 Jun 2023 13:55:44 +1000 Subject: [PATCH] Wrap event page parts in suspense --- frontend/src/app/[id]/EventAvailabilities.tsx | 48 ++++++++-------- frontend/src/app/[id]/page.module.scss | 9 +++ frontend/src/app/[id]/page.tsx | 57 ++++++++++++------- .../AvailabilityEditor/AvailabilityEditor.tsx | 6 +- .../AvailabilityViewer/AvailabilityViewer.tsx | 4 +- .../CalendarField/components/Month/Month.tsx | 4 +- .../components/Weekdays/Weekdays.tsx | 2 +- frontend/src/components/Copyable/Copyable.tsx | 9 ++- .../components/EventInfo/EventInfo.tsx | 2 +- .../src/components/Error/Error.module.scss | 6 ++ frontend/src/components/Footer/Footer.tsx | 2 +- frontend/src/components/Legend/Legend.tsx | 2 +- .../src/components/Login/Login.module.scss | 5 +- frontend/src/components/Login/Login.tsx | 4 +- frontend/src/components/Settings/Settings.tsx | 2 +- frontend/src/components/Video/Video.tsx | 4 +- frontend/src/utils/calculateAvailability.ts | 4 +- frontend/src/utils/calculateTable.ts | 2 +- frontend/src/utils/convertTimesToDates.ts | 2 +- 19 files changed, 103 insertions(+), 71 deletions(-) diff --git a/frontend/src/app/[id]/EventAvailabilities.tsx b/frontend/src/app/[id]/EventAvailabilities.tsx index 992f48e..ddf6dfd 100644 --- a/frontend/src/app/[id]/EventAvailabilities.tsx +++ b/frontend/src/app/[id]/EventAvailabilities.tsx @@ -1,6 +1,6 @@ 'use client' -import { useEffect, useMemo, useState } from 'react' +import { useEffect, useMemo, useRef, useState } from 'react' import { Trans } from 'react-i18next/TransWithoutContext' import AvailabilityEditor from '/src/components/AvailabilityEditor/AvailabilityEditor' @@ -20,17 +20,16 @@ import { calculateTable, expandTimes, makeClass } from '/src/utils' import styles from './page.module.scss' interface EventAvailabilitiesProps { - event: EventResponse - people: PersonResponse[] + event?: EventResponse } -const EventAvailabilities = ({ event, ...data }: EventAvailabilitiesProps) => { +const EventAvailabilities = ({ event }: EventAvailabilitiesProps) => { const { t, i18n } = useTranslation('event') const timeFormat = useStore(useSettingsStore, state => state.timeFormat) ?? '12h' - const [people, setPeople] = useState(data.people) - const expandedTimes = useMemo(() => expandTimes(event.times), [event.times]) + const [people, setPeople] = useState([]) + const expandedTimes = useMemo(() => expandTimes(event?.times ?? []), [event?.times]) const [user, setUser] = useState() const [password, setPassword] = useState() @@ -39,40 +38,40 @@ const EventAvailabilities = ({ event, ...data }: EventAvailabilitiesProps) => { const [timezone, setTimezone] = useState(Intl.DateTimeFormat().resolvedOptions().timeZone) // Web worker for calculating the heatmap table - const tableWorker = useMemo(() => (typeof window !== undefined && window.Worker) ? new Worker(new URL('/src/workers/calculateTable', import.meta.url)) : undefined, []) + const tableWorker = useRef() // Calculate table (using a web worker if available) const [table, setTable] = useState>() useEffect(() => { + if (!tableWorker.current) { + tableWorker.current = window.Worker ? new Worker(new URL('/src/workers/calculateTable', import.meta.url)) : undefined + } const args = { times: expandedTimes, locale: i18n.language, timeFormat, timezone } - if (tableWorker) { - tableWorker.postMessage(args) + if (tableWorker.current) { + tableWorker.current.onmessage = (e: MessageEvent>) => setTable(e.data) + tableWorker.current.postMessage(args) setTable(undefined) } else { setTable(calculateTable(args)) } - }, [tableWorker, expandedTimes, i18n.language, timeFormat, timezone]) - - useEffect(() => { - if (tableWorker) { - tableWorker.onmessage = (e: MessageEvent>) => setTable(e.data) - } - }, [tableWorker]) + }, [expandedTimes, i18n.language, timeFormat, timezone]) // Add this event to recents const addRecent = useRecentsStore(state => state.addRecent) useEffect(() => { - addRecent({ - id: event.id, - name: event.name, - created_at: event.created_at, - }) + if (event) { + addRecent({ + id: event.id, + name: event.name, + created_at: event.created_at, + }) + } }, [addRecent]) // Refetch availabilities useEffect(() => { - if (tab === 'group') { + if (tab === 'group' && event) { getPeople(event.id) .then(setPeople) .catch(console.warn) @@ -82,7 +81,7 @@ const EventAvailabilities = ({ event, ...data }: EventAvailabilitiesProps) => { return <>
- { + { setUser(u) setPassword(p) setTab(u ? 'you' : 'group') @@ -148,7 +147,7 @@ const EventAvailabilities = ({ event, ...data }: EventAvailabilitiesProps) => { document.dispatchEvent(new CustomEvent('focusName')) } }} - title={user ? '' : t('tabs.you_tooltip')} + title={user ? '' : t('tabs.you_tooltip')} >{t('tabs.you')} - {times[0].length === 13 && + {times[0]?.length === 13 &&
('greyed_times')} + title={t('greyed_times')} /> const isSelected = ( @@ -162,7 +162,7 @@ const AvailabilityEditor = ({ times, timezone, value = [], onChange, table }: Av })}
:
} - ) ?? } + ) ?? }
diff --git a/frontend/src/components/AvailabilityViewer/AvailabilityViewer.tsx b/frontend/src/components/AvailabilityViewer/AvailabilityViewer.tsx index eee8bad..05f31e1 100644 --- a/frontend/src/components/AvailabilityViewer/AvailabilityViewer.tsx +++ b/frontend/src/components/AvailabilityViewer/AvailabilityViewer.tsx @@ -70,7 +70,7 @@ const AvailabilityViewer = ({ times, people, table }: AvailabilityViewerProps) = if (!cell) return
('greyed_times')} + title={t('greyed_times')} /> let peopleHere = availabilities.find(a => a.date === cell.serialized)?.people ?? [] @@ -106,7 +106,7 @@ const AvailabilityViewer = ({ times, people, table }: AvailabilityViewerProps) = })}
:
} - ) ?? , [ + ) ?? , [ availabilities, table?.columns, highlight, diff --git a/frontend/src/components/CalendarField/components/Month/Month.tsx b/frontend/src/components/CalendarField/components/Month/Month.tsx index e6ceeef..ed570d8 100644 --- a/frontend/src/components/CalendarField/components/Month/Month.tsx +++ b/frontend/src/components/CalendarField/components/Month/Month.tsx @@ -55,13 +55,13 @@ const Month = ({ value, onChange }: MonthProps) => { return <> {useMemo(() =>
} diff --git a/frontend/src/components/Error/Error.module.scss b/frontend/src/components/Error/Error.module.scss index 0a9e38f..5057cea 100644 --- a/frontend/src/components/Error/Error.module.scss +++ b/frontend/src/components/Error/Error.module.scss @@ -39,4 +39,10 @@ justify-content: center; margin-left: 16px; padding: 0; + border-radius: .2em; + + &:focus-visible { + outline: 2px solid currentColor; + outline-offset: 2px; + } } diff --git a/frontend/src/components/Footer/Footer.tsx b/frontend/src/components/Footer/Footer.tsx index 2a668af..7f7d690 100644 --- a/frontend/src/components/Footer/Footer.tsx +++ b/frontend/src/components/Footer/Footer.tsx @@ -23,7 +23,7 @@ const Footer = async ({ isSmall }: FooterProps) => { {t('donate.info')} { width="560" height="315" src="https://www.youtube.com/embed/yXGd4VXZzcY?modestbranding=1&rel=0&autoplay=1" - title={t('video.title')} + title={t('video.title')} frameBorder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowFullScreen @@ -34,7 +34,7 @@ const Video = () => { setIsPlaying(true) }} > - {t<string('video.button')} /> + {t('video.button')} {t('video.button')} ) diff --git a/frontend/src/utils/calculateAvailability.ts b/frontend/src/utils/calculateAvailability.ts index 93b4b01..67286f3 100644 --- a/frontend/src/utils/calculateAvailability.ts +++ b/frontend/src/utils/calculateAvailability.ts @@ -23,8 +23,8 @@ interface AvailabilityInfo { * group availability for each date passed in. */ export const calculateAvailability = (dates: string[], people: Person[]): AvailabilityInfo => { - let min = Infinity - let max = -Infinity + let min = 0 + let max = people.length const availabilities: Availability[] = dates.map(date => { const names = people.flatMap(p => p.availability.some(d => d === date) ? [p.name] : []) diff --git a/frontend/src/utils/calculateTable.ts b/frontend/src/utils/calculateTable.ts index b2358c4..500734c 100644 --- a/frontend/src/utils/calculateTable.ts +++ b/frontend/src/utils/calculateTable.ts @@ -26,7 +26,7 @@ export const calculateTable = ({ const columns = calculateColumns(dates) // Is specific dates or just days of the week - const isSpecificDates = times[0].length === 13 + const isSpecificDates = times[0]?.length === 13 return { rows: rows.map(row => row && row.minute === 0 ? { diff --git a/frontend/src/utils/convertTimesToDates.ts b/frontend/src/utils/convertTimesToDates.ts index a0064ff..62dc505 100644 --- a/frontend/src/utils/convertTimesToDates.ts +++ b/frontend/src/utils/convertTimesToDates.ts @@ -6,7 +6,7 @@ import { Temporal } from '@js-temporal/polyfill' * @param timezone The target timezone */ export const convertTimesToDates = (times: string[], timezone: string): Temporal.ZonedDateTime[] => { - const isSpecificDates = times[0].length === 13 + const isSpecificDates = times[0]?.length === 13 return times.map(time => isSpecificDates ? parseSpecificDate(time).withTimeZone(timezone)