started program guide list

This commit is contained in:
sunda 2021-10-22 15:44:28 +02:00
parent be6aa96e1b
commit 36fd61d3ac
14 changed files with 243 additions and 26 deletions

View File

@ -5,35 +5,38 @@ import { BrowserRouter, Route, Switch } from 'react-router-dom'
import Main from './app' import Main from './app'
import SeriesPage from './src/pages/SeriesPage' import SeriesPage from './src/pages/SeriesPage'
import { useEventApi, useEventCalendar } from './src/hooks/data' import { useEventApi } from './src/hooks/data'
import { useTheme } from './src/store' import { useTheme } from './src/store'
import { useTimeout } from './src/hooks/timerHooks' import { useTimeout } from './src/hooks/timerHooks'
import LoaderLayout from './src/pages/LoaderLayout' import LoaderLayout from './src/pages/LoaderLayout'
import FourOhFour from './src/pages/404' import FourOhFour from './src/pages/404'
import Series from './src/pages/Series' import Series from './src/pages/Series'
import Program from './src/pages/Program'
const App = () => { const App = () => {
const { theme } = useTheme((store) => store) const { theme } = useTheme((store) => store)
const { data: calData, calLoading } = useEventCalendar() const { data, loading: eventsLoading } = useEventApi()
const { data: seriesDataArray, loading: eventsLoading } = useEventApi()
const [minLoadTimePassed, setMinTimeUp] = useState(false) const [minLoadTimePassed, setMinTimeUp] = useState(false)
useTimeout(() => { useTimeout(() => {
setMinTimeUp(true) setMinTimeUp(true)
}, 1500) }, 1500)
// console.log({ episodes: data.episodes, series: data.series })
const seriesData = data.series ? Object.values(data.series) : []
const seriesData = Object.values(seriesDataArray)
return ( return (
<ThemeProvider theme={theme}> <ThemeProvider theme={theme}>
{calLoading || eventsLoading || !minLoadTimePassed ? ( {!seriesData.length || eventsLoading || !minLoadTimePassed ? (
<LoaderLayout /> <LoaderLayout />
) : ( ) : (
<BrowserRouter> <BrowserRouter>
<Switch> <Switch>
<Route exact path="/" component={Main} /> <Route exact path="/" component={Main} />
<Route exact path="/series" component={Series} /> <Route exact path="/series" component={Series} />
<Route exact path="/program" component={Program} />
{seriesData.length ? seriesData.map(series => ( {seriesData.length ? seriesData.map(series => (
<Route exact path={`/series/${series.slug}`}> <Route exact path={`/series/${series.slug}`}>
<SeriesPage data={series} /> <SeriesPage data={series} />

View File

@ -0,0 +1,43 @@
import { h } from 'preact'
import { format } from 'date-fns'
import Link from '../Link'
import { H2, H3, Label } from '../Text'
import strings from '../../data/strings'
import { andList } from '../../helpers/string'
import { colours } from '../../assets/theme'
import { Img, Left, Right, Title, Row, Column, StyledButton as Button } from './styles'
import { useEventApi } from '../../hooks/data'
const EpisodeCard = ({ image, title, seriesId, beginsOn, id, ...rest }) => {
const { data: { series: allSeries } } = useEventApi()
const series = seriesId ? allSeries.filter(({ id }) => id === seriesId)[0] : {}
const hosts = series.hosts ? series.hosts.map(host => host.actor.name) : null
const startTime = format(new Date(beginsOn), 'ha')
return (
<Row align="stretch" {...rest}>
<Left >
<Link to={`/series/${series.slug}#${id}`}><Img src={image} /></Link>
<Column justify="space-between" flex={1}>
<Title size={24} colour={colours.rose} weight="500">{title}</Title>
<div>
<H3 weight="500">From: <Link textProps={{
weight: '700'
}} to={`/series/${series.slug}`}>{`${series.title}: ${series.subtitle}`}</Link></H3>
{hosts ? <Label size={16} colour={colours.rose}> {andList(hosts)}</Label> : null}
</div>
<Button>Play now</Button>
</Column>
</Left>
<Right>
<Label size={24} colour={colours.rose} weight="600">{startTime}</Label>
</Right>
</Row>
)
}
export default EpisodeCard

View File

@ -0,0 +1,39 @@
import styled from 'styled-components'
import { colours } from '../../assets/theme'
import { Label, H2 } from '../Text'
import { Row as FlexRow, Column as FlexColumn } from '../Flex'
import Button from '../Button'
export const Row = styled(FlexRow)`
`
export const Column = styled(FlexColumn)`
height: 100%;
max-width: 50%;
`
export const Left = styled(FlexRow)`
`
export const Title = styled(H2)`
/* max-width: 50% */
`
export const Right = styled.div`
`
export const StyledButton = styled(Button)`
width: max-content;
padding: 0.3em 2em;
`
export const Img = styled.div`
background: url(${({ src }) => src});
width: 380px;
height: 215px;
background-size: cover;
position: relative;
background-position: center;
margin-right: 1em;
`

View File

@ -1,3 +1,4 @@
import { bool, number, oneOf } from 'prop-types'
import styled from 'styled-components' import styled from 'styled-components'
export const Row = styled.div` export const Row = styled.div`
@ -5,6 +6,9 @@ export const Row = styled.div`
flex-direction: ${props => (props.reverse ? 'row-reverse' : 'row')}; flex-direction: ${props => (props.reverse ? 'row-reverse' : 'row')};
justify-content: ${props => props.justify || 'flex-start'}; justify-content: ${props => props.justify || 'flex-start'};
align-items: ${props => props.align || 'flex-start'}; align-items: ${props => props.align || 'flex-start'};
${props => props.flex ? `
flex: ${props.flex};
` : ''}
` `
export const Column = styled.div` export const Column = styled.div`
@ -12,4 +16,16 @@ export const Column = styled.div`
flex-direction: ${props => (props.reverse ? 'column-reverse' : 'column')}; flex-direction: ${props => (props.reverse ? 'column-reverse' : 'column')};
justify-content: ${props => props.justify || 'flex-start'}; justify-content: ${props => props.justify || 'flex-start'};
align-items: ${props => props.align || 'flex-start'}; align-items: ${props => props.align || 'flex-start'};
${props => props.flex ? `
flex: ${props.flex};
` : ''}
` `
const propTypes = {
reverse: bool,
justify: oneOf(['flex-start', 'flex-end', 'center', 'space-around', 'space-between']),
align: oneOf(['flex-start', 'flex-end', 'center', 'stretch']),
flex: number
}
Row.propTypes = propTypes
Column.propTypes = propTypes

View File

@ -5,14 +5,14 @@ import { useRouteMatch } from 'react-router-dom'
import { A } from '../Text' import { A } from '../Text'
import { RRLink } from './styles' import { RRLink } from './styles'
const Link = ({ children, to, activeOnlyWhenExact = true, href, textProps: { colour, size } = {}, navLink, ...rest }) => { const Link = ({ children, to, activeOnlyWhenExact = true, href, textProps: { colour, size, weight } = {}, navLink, ...rest }) => {
const match = useRouteMatch({ const match = useRouteMatch({
path: to, path: to,
exact: activeOnlyWhenExact exact: activeOnlyWhenExact
}) })
return href ? <A href={href} colour={colour} size={size} fontFamily={navLink ? 'Lunchtype24' : null} {...rest}>{children}</A> : ( return href ? <A href={href} colour={colour} size={size} fontFamily={navLink ? 'Lunchtype24' : null} {...rest}>{children}</A> : (
<A as="span" colour={colour} size={size} {...rest}> <A as="span" colour={colour} size={size} weight={weight} {...rest}>
<RRLink to={to} $navLink={navLink} $colour={colour} $selected={match && navLink}>{children}</RRLink> <RRLink to={to} $navLink={navLink} $colour={colour} $selected={match && navLink}>{children}</RRLink>
</A> </A>
) )

View File

@ -91,10 +91,10 @@ export const H2 = ({ children, size, ...rest }) => (
</Text> </Text>
) )
export const H3 = ({ children, size, ...rest }) => ( export const H3 = ({ children, size, weight = '700', ...rest }) => (
<Text <Text
tag="h3" tag="h3"
weight="700" weight={weight}
$size={size || '21'} $size={size || '21'}
lineHeight="1" lineHeight="1"
fontFamily="Lunchtype24" fontFamily="Lunchtype24"

View File

@ -20,5 +20,7 @@ export default {
lastStream: 'Last stream', lastStream: 'Last stream',
nextStream: 'Next stream', nextStream: 'Next stream',
episodes: 'episodes', episodes: 'episodes',
today: 'today',
tomorrow: 'tomorrow'
}, },
} }

View File

@ -99,23 +99,19 @@ export const useEventCalendar = () => {
export const useEventApi = () => { export const useEventApi = () => {
const [series, episodes, setSeries, setEpisodes] = useSeriesStore(store => [store.series, store.episodes, store.setSeries, store.setEpisodes]) const [data, setData] = useSeriesStore(store => [store.series, store.setSeries])
const [loading, setLoading] = useState(!!series.length) const [loading, setLoading] = useState(!!data.length)
async function fetchData() { async function fetchData() {
if (!series.length) { if (!data.length) {
setLoading(true) setLoading(true)
const { data: responseData } = await axios.get( const { data: responseData } = await axios.get(
`${config.EVENTS_API_URL}/events` `${config.EVENTS_API_URL}/events`
) )
setSeries(responseData)
// setEpisodes()
// console.log({ episodes })
setData(responseData)
setLoading(false) setLoading(false)
} }
} }
@ -124,5 +120,5 @@ export const useEventApi = () => {
fetchData() fetchData()
}, []) }, [])
return { loading, data: series } return { loading, data }
} }

View File

@ -0,0 +1,35 @@
import { isBefore, format, isToday, isTomorrow } from 'date-fns'
import { capitaliseFirstLetter } from '../../helpers/string'
import strings from '../../data/strings'
export const getScheduleFromData = data => data.filter(item => {
const { endsOn } = item
const today = format(new Date(), 'yyyy/M/d')
const broadcastEnd = new Date(endsOn)
return !isBefore(broadcastEnd, new Date(today))
})
.reduce((obj, item) => {
const newObject = obj
const { beginsOn } = item
const startDay = format(new Date(beginsOn), 'yyyy-MM-dd')
if (newObject[startDay]) {
newObject[startDay] = [...newObject[startDay], item]
} else {
newObject[startDay] = [item]
}
return newObject
}, {})
export const formatDay = (dateTime, lang = 'en') => {
let day = ''
const date = new Date(dateTime)
console.log({ date })
if (isToday(date)) day = strings[lang].today
if (isTomorrow(date)) day = strings[lang].tomorrow
else day = format(date, 'cccc MMM d')
return capitaliseFirstLetter(day)
}

View File

@ -0,0 +1,42 @@
/* eslint-disable react/prop-types */
import { isWithinInterval } from 'date-fns'
import { h } from 'preact'
import { H1, H2 } from '../../components/Text'
import strings from '../../data/strings'
import { useEventApi } from '../../hooks/data'
import { Content, ScheduleList, Day } from './styles'
import Page from '../../layouts/Page'
import { formatDay, getScheduleFromData } from './helpers'
import EpisodeCard from '../../components/EpisodeCard'
import { colours } from '../../assets/theme'
const Program = () => {
const { data } = useEventApi()
const episodes = getScheduleFromData(data.episodes)
console.log({ episodes })
return (
<Page title={strings.en.program}>
<Content>
<ScheduleList>
{Object.keys(episodes || {}).sort((a, b) => new Date(a) - new Date(b)).map(day => (
<Day>
<H1 colour={colours.rose}>{formatDay(day)}</H1>
{episodes[day].map(episode => (
<EpisodeCard {...episode} />
))}
</Day>
))}
</ScheduleList>
{/* <H1>Program</H1> */}
</Content>
</Page>
)
}
export default Program

View File

@ -0,0 +1,42 @@
import styled from 'styled-components'
import { screenSizes } from '../../assets/theme'
import { Row } from '../../components/Flex'
export const SeriesGrid = styled.div`
margin-left: 32px;
`
export const ScheduleList = styled.ul`
display: flex;
flex-direction: column;
`
export const Day = styled.div`
margin: 0 0 6em 0;
h1 {
margin-bottom: 0.5em;
}
`
export const Content = styled.div`
width: 80vw;
max-width: ${screenSizes.lg}px;
margin: 0 auto;
padding: 64px 0;
overflow-y: scroll;
::-webkit-scrollbar {
display: none;
}
`
/* ${mediaQuery.lessThan('lg')`
max-height: calc(100vh - 200px);
`}
${mediaQuery.lessThan('md')`
max-width: 600px;
padding: 8px 0;
margin: 0 32px;
`}; */

View File

@ -11,12 +11,11 @@ import SeriesCard from '../../components/SeriesCard'
import { colours } from '../../assets/theme' import { colours } from '../../assets/theme'
const Series = () => { const Series = () => {
const { data: seriesDataArray } = useEventApi() const { data } = useEventApi()
const pastSeries = [] const pastSeries = []
const currentSeries = seriesDataArray.filter(series => { const currentSeries = data.series ? data.series.filter(series => {
// const seriesInTheLastMonth = series.episodes.past.filter(episode => { // const seriesInTheLastMonth = series.episodes.past.filter(episode => {
// }) // })
if (series.episodes.future.length) { if (series.episodes.future.length) {
return true return true
@ -24,7 +23,7 @@ const Series = () => {
pastSeries.push(series) pastSeries.push(series)
return false return false
}) }) : []
return ( return (

View File

@ -13,6 +13,7 @@ import CrossSvg from '../../components/Svg/Cross'
import { H1, H2, Span, Label } from '../../components/Text' import { H1, H2, Span, Label } from '../../components/Text'
import Link from '../../components/Link' import Link from '../../components/Link'
import Button from '../../components/Button' import Button from '../../components/Button'
import { slugify } from '../../helpers/string'
export const TrailerContainer = styled.div` export const TrailerContainer = styled.div`
height: 22em; height: 22em;
@ -140,8 +141,6 @@ const renderTitles = titles =>
export const EpisodeCard = ({ export const EpisodeCard = ({
title, title,
image, image,
description, description,
beginsOn, beginsOn,
hasPassed, hasPassed,
@ -149,6 +148,7 @@ export const EpisodeCard = ({
onClickButton, onClickButton,
tzShort, tzShort,
theme, theme,
id
}) => { }) => {
const startDate = new Date(beginsOn) const startDate = new Date(beginsOn)
const utcDate = zonedTimeToUtc(startDate, 'Europe/Berlin') const utcDate = zonedTimeToUtc(startDate, 'Europe/Berlin')
@ -156,7 +156,7 @@ export const EpisodeCard = ({
const { timeZone } = Intl.DateTimeFormat().resolvedOptions() const { timeZone } = Intl.DateTimeFormat().resolvedOptions()
const zonedDate = utcToZonedTime(utcDate, timeZone) const zonedDate = utcToZonedTime(utcDate, timeZone)
return ( return (
<VCWrapper> <VCWrapper id={id}>
<DateLabel size={textSizes.lg} colour={theme.foreground}> <DateLabel size={textSizes.lg} colour={theme.foreground}>
{`${hasPassed ? translations.en.streamDatePast : ''}`} {`${hasPassed ? translations.en.streamDatePast : ''}`}
<Span bold colour={theme.foreground}> <Span bold colour={theme.foreground}>

View File

@ -2,7 +2,7 @@ import create from 'zustand'
import { defaultTheme } from '../assets/theme' import { defaultTheme } from '../assets/theme'
export const useSeriesStore = create((set, get) => ({ export const useSeriesStore = create((set, get) => ({
series: [], series: {},
episodes: [], episodes: [],
setSeries: series => set({ series }), setSeries: series => set({ series }),
setEpisodes: () => { setEpisodes: () => {