diff --git a/index.js b/index.js index 48108b9..0d54d17 100644 --- a/index.js +++ b/index.js @@ -1,16 +1,19 @@ import { h, render } from 'preact' import { useState } from 'preact/hooks' +import { ThemeProvider } from 'styled-components' import { BrowserRouter, Route, Switch } from 'react-router-dom' import Main from './app' import SeriesPage from './src/pages/SeriesPage' import { useEventApi, useEventCalendar } from './src/hooks/data' +import { useTheme } from './src/store' import { useTimeout } from './src/hooks/timerHooks' import LoaderLayout from './src/pages/LoaderLayout' import FourOhFour from './src/pages/404' import Series from './src/pages/Series' const App = () => { + const { theme } = useTheme((store) => store) const { data: calData, calLoading } = useEventCalendar() const { data: seriesDataArray, loading: eventsLoading } = useEventApi() const [minLoadTimePassed, setMinTimeUp] = useState(false) @@ -22,23 +25,26 @@ const App = () => { const seriesData = Object.values(seriesDataArray) - return calLoading || eventsLoading || !minLoadTimePassed ? ( - - ) : ( - - - - - {seriesData.length ? seriesData.map(series => ( - - - )) : null} - - - - - - ) + return ( + + {calLoading || eventsLoading || !minLoadTimePassed ? ( + + ) : ( + + + + + {seriesData.length ? seriesData.map(series => ( + + + )) : null} + + + + + + )} + ) } diff --git a/src/components/Header/index.js b/src/components/Header/index.js index 3df1d1b..1241dc6 100644 --- a/src/components/Header/index.js +++ b/src/components/Header/index.js @@ -1,66 +1,80 @@ import { h } from 'preact' import { Link as ReactLink } from 'react-router-dom' -import { RightBox, StyledRow as Row, Modal } from './styles' +import { RightBox, StyledRow as Row, Modal, PositionedCross as CrossSvg } from './styles' import { ImageLogo } from '../Logo' import { useWindowSize } from '../../hooks/dom' import Link from '../Link' import { Span } from '../Text' -import CrossSvg from '../Svg/Cross' import navigation from '../../data/navigation' import { colours, screenSizes, textSizes } from '../../assets/theme' -import { useToggle } from '../../hooks/utility' +import { useTheme, useUiStore } from '../../store' -const Navigation = ({ theme = {}, lang = 'en', headerTheme }) => navigation[lang].map(navItem => ( +const Navigation = ({ theme = {}, lang = 'en', miniHeader, toggleMobileMenu }) => navigation[lang].map(navItem => ( {navItem.label} )) -const NavigationModal = ({ theme = {}, lang = 'en', headerTheme, toggleMenuOpen, ...rest }) => ( - - - - - -
- -
-
-) +export const NavigationModal = ({ theme = {}, lang = 'en', headerTheme, ...rest }) => { + const { toggleMobileMenu } = useUiStore(store => store) -const FullHeader = ({ theme = {}, headerTheme = {}, lang = 'en', miniHeader, isMobile, toggleMenuOpen, ...rest }) => ( - + return ( + + + + + +
+ +
+
+ ) +} + +const FullHeader = ({ theme = {}, lang = 'en', miniHeader, isMobile, ...rest }) => ( + {!miniHeader ? : null} {!isMobile ? ( - + - ) : Menu} + ) : } ) +export const MobileMenuToggle = ({ miniHeader, ...props }) => { + const { toggleMobileMenu } = useUiStore(store => store) + const { theme } = useTheme(store => store) + + return ( + Menu) +} + const Header = ({ miniHeader, theme, ...rest }) => { - const headerTheme = { foreground: theme.background, background: 'transparent', } const { width: screenWidth } = useWindowSize() - const [menuOpen, toggleMenuOpen] = useToggle(false) + const { menuOpen } = useUiStore(store => store) const isMobile = screenWidth < screenSizes.lg - if (menuOpen) return - - return + return } export default Header diff --git a/src/components/Header/styles.js b/src/components/Header/styles.js index 2382ef6..6b605ce 100644 --- a/src/components/Header/styles.js +++ b/src/components/Header/styles.js @@ -7,7 +7,7 @@ import CrossSvg from '../Svg/Cross' export const StyledRow = styled(Row)` pointer-events: all; height: 100px; - background-color: ${({ theme }) => theme.background}; + background-color: transparent; color: ${({ theme }) => theme.foreground}; position: absolute; left: ${({ miniHeader }) => miniHeader ? 'initial' : '0'}; @@ -71,6 +71,7 @@ export const Modal = styled.div` ` export const PositionedCross = styled(CrossSvg)` position: fixed; - right: 1em; - top: 1em; + right: 2.5em; + top: 2em; + cursor: pointer; ` \ No newline at end of file diff --git a/src/components/Link/styles.js b/src/components/Link/styles.js index 885c46b..e1d3fbe 100644 --- a/src/components/Link/styles.js +++ b/src/components/Link/styles.js @@ -3,7 +3,7 @@ import { Link } from 'react-router-dom' import { colours } from '../../assets/theme' export const RRLink = styled(Link)` - color: ${props => { console.log({ col: props.$colour }); return props.$colour || colours.highlight }}; + color: ${props => props.$colour || colours.highlight}; text-decoration: none; ${props => props.$navLink ? ` font-family: Lunchtype24; diff --git a/src/hooks/data.js b/src/hooks/data.js index 7ae13fa..1cab5a9 100644 --- a/src/hooks/data.js +++ b/src/hooks/data.js @@ -99,7 +99,7 @@ export const useEventCalendar = () => { export const useEventApi = () => { - const [series, setSeries] = useSeriesStore(store => [store.series, store.setSeries]) + const [series, episodes, setSeries, setEpisodes] = useSeriesStore(store => [store.series, store.episodes, store.setSeries, store.setEpisodes]) const [loading, setLoading] = useState(!!series.length) @@ -112,6 +112,9 @@ export const useEventApi = () => { ) setSeries(responseData) + // setEpisodes() + + // console.log({ episodes }) setLoading(false) } diff --git a/src/layouts/InfoLayout/styles.js b/src/layouts/InfoLayout/styles.js index d8ac06c..4a6bbe1 100644 --- a/src/layouts/InfoLayout/styles.js +++ b/src/layouts/InfoLayout/styles.js @@ -98,6 +98,10 @@ export const LoaderWrapper = styled.div` export const Content = styled.div` padding-top: 80px; + + @media screen and (max-width: ${screenSizes.md}px) { + padding-top: 100px; + } img.img-logo { max-height: 80px; @@ -122,6 +126,10 @@ export const PositionedLink = styled(Link)` max-height: 80px; mix-blend-mode: exclusion; padding: 0.5em 2em 0; + + @media screen and (max-width: ${screenSizes.md}px) { + padding-left: 1em + } } ` diff --git a/src/layouts/Page/index.js b/src/layouts/Page/index.js index ce3c19f..80f51cb 100644 --- a/src/layouts/Page/index.js +++ b/src/layouts/Page/index.js @@ -1,27 +1,42 @@ import { Fragment, h } from 'preact' import PropTypes, { oneOfType, shape, string } from 'prop-types' +import { useEffect } from 'preact/hooks' import SEO from '../../components/Seo' -import Header from '../../components/Header' +import Header, { NavigationModal as MenuModal } from '../../components/Header' import { capitaliseFirstLetter } from '../../helpers/string' import { defaultTheme } from '../../assets/theme' import { ThemedBlock } from './styles' +import { useTheme, useUiStore } from '../../store' -const Page = ({ children, title = '', description, metaImg, backTo, noindex, withHeader = true, theme = defaultTheme }) => ( - - - {withHeader ?
: null} - - {children} - - -) +const Page = ({ children, title = '', description, metaImg, backTo, noindex, withHeader = true, theme = defaultTheme }) => { + const { setTheme } = useTheme(store => store) + const { mobileMenuOpen: menuOpen, toggleMenuOpen } = useUiStore(store => store) + + + useEffect(() => { + if (theme) { + setTheme(theme) + } + }, []) + + return ( + + + {withHeader ?
: null} + {menuOpen ? : null} + + {children} + + + ) +} Page.propTypes = { children: PropTypes.node.isRequired, diff --git a/src/pages/SeriesPage/index.js b/src/pages/SeriesPage/index.js index 7dbc6e3..297df1e 100644 --- a/src/pages/SeriesPage/index.js +++ b/src/pages/SeriesPage/index.js @@ -1,5 +1,6 @@ /* eslint-disable react/prop-types */ import { h, Fragment } from 'preact' +import { useEffect } from 'preact/hooks' import striptags from 'striptags' import { H1 } from '../../components/Text' @@ -18,8 +19,11 @@ import { import Page from '../../layouts/Page' import { splitArray } from '../../helpers/utils' +import { defaultTheme } from '../../assets/theme' const SeriesPage = ({ data }) => { + const theme = data.theme || defaultTheme + const credits = data.credits ? ` ## Credits ${data.credits} @@ -43,12 +47,12 @@ const SeriesPage = ({ data }) => { return ( - +

{data.title}:

{data.subtitle}

- {data.description ? {data.description} : null} + {data.description ? {data.description} : null} {data.trailer ? ( @@ -75,7 +79,7 @@ const SeriesPage = ({ data }) => { {translations.en.program}: {data.episodes.future.map(feeditem => ( { Past streams: {data.episodes.past.map(feeditem => ( null} // todo: fix this @@ -99,7 +103,7 @@ const SeriesPage = ({ data }) => { ) : null} {credits ? Credits - {credits} + {credits} : null}
diff --git a/src/store/helpers.js b/src/store/helpers.js deleted file mode 100644 index 7e4215a..0000000 --- a/src/store/helpers.js +++ /dev/null @@ -1,35 +0,0 @@ -export const getMetadataByKey = (episode, key, firstItem = true) => { - const filteredItems = episode.metadata.length ? episode.metadata.filter( - meta => meta.key.includes(key) - ) : null - - if (filteredItems) { - return firstItem ? filteredItems[0].value : filteredItems - } - - return null -} - -export const getPostByKey = (posts, key) => { - const filteredPostItems = posts.elements.length ? posts.elements.filter(post => post.title === key) : [] - return filteredPostItems.length ? filteredPostItems[0].body : null -} - -export const getResourcesByKey = (resources, key, lookup = 'title') => { - const filteredResources = resources.elements.length ? resources.elements.filter(resource => resource[lookup].includes(key)) : [] - - if (!filteredResources.length) return null - - return filteredResources -} - -export const getPeertubeIDfromUrl = (string) => string && string.includes('https://tv.undersco.re') ? string.split('/').pop() : string - -export const getEpisodeCode = (episode) => { - const metaItems = getMetadataByKey(episode, 'mz:plain:', false) - if (metaItems && metaItems.length) { - // TODO - } - - return 'TODO' -} \ No newline at end of file diff --git a/src/store/index.js b/src/store/index.js index 72ab87a..679ab32 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -1,56 +1,26 @@ import create from 'zustand' -import striptags from 'striptags' -import { isFuture, isPast } from 'date-fns' -import { slugify } from '../helpers/string' -import { getMetadataByKey, getPostByKey, getResourcesByKey, getPeertubeIDfromUrl, getEpisodeCode } from './helpers' -import { colours, defaultTheme } from '../assets/theme' +import { defaultTheme } from '../assets/theme' -export const [useSeriesStore] = create(set => ({ +export const useSeriesStore = create((set, get) => ({ series: [], - - setSeries: seriesArray => { - const allSeries = seriesArray.map(({ name, organizedEvents, posts, resources, banner, summary, members }) => { - - - const allEpisodes = organizedEvents.elements.length ? organizedEvents.elements.map(ep => ({ - title: ep.title, - beginsOn: ep.beginsOn, - endsOn: ep.endsOn, - description: ep.description, - media: ep.media, - image: ep.picture ? ep.picture.url : null, - peertubeId: getPeertubeIDfromUrl(getMetadataByKey(ep, 'peertube:url')), - code: getEpisodeCode(ep) - })) : [] - - const trailer = getResourcesByKey(resources, 'SERIES_TRAILER')?.[0]?.resourceUrl ?? null - const theme = striptags(getPostByKey(posts, 'THEME')) - const orgLinks = getResourcesByKey(resources, 'ORG_LINK:')?.map(link => link.title) - - console.log({ orgLinks }) - - - const series = { - title: name, - subtitle: striptags(summary), - description: getPostByKey(posts, 'SERIES_INFO'), - posts: posts.elements, - resources: resources.elements, - image: banner ? banner.url : '', - episodes: { - future: allEpisodes.filter(ep => isFuture(new Date(ep.endsOn))).sort((a, b) => new Date(a.beginsOn) - new Date(b.beginsOn)), - past: allEpisodes.filter(ep => isPast(new Date(ep.endsOn))).sort((a, b) => new Date(a.beginsOn) - new Date(b.beginsOn)) - }, - slug: slugify(name), - credits: getPostByKey(posts, 'SERIES_CREDITS'), - trailer, - links: getResourcesByKey(resources, 'SERIES_LINK') || [], - theme: theme ? JSON.parse(theme) : defaultTheme, - hosts: members.elements.filter(({ actor }) => actor.name !== 'streamappbot') - } - return series - }) - - set({ series: allSeries }) + episodes: [], + setSeries: series => set({ series }), + setEpisodes: () => { + if (get().series) { + set({ + episodes: get().series.map(series => series.episodes) + }) + } else set({}) } +})) + +export const [useTheme] = create(set => ({ + theme: defaultTheme, + setTheme: (theme) => set({ theme }), + setDefaultTheme: () => set({ theme: defaultTheme }) +})) + +export const [useUiStore] = create((set, get) => ({ + mobileMenuOpen: false, + toggleMobileMenu: () => set({ mobileMenuOpen: !get().mobileMenuOpen }), })) \ No newline at end of file