From 0ba8deacd0c7d500250e42b5081f94693ee04e4f Mon Sep 17 00:00:00 2001 From: Benjamin Jones Date: Thu, 11 Mar 2021 21:24:44 +0100 Subject: [PATCH] More work on logic flow + info page --- app.js | 65 ++++++++++-- package.json | 2 + src/assets/fonts/Karla/Karla-Regular.ttf | Bin 0 -> 38304 bytes src/assets/hooks/calendar.js | 23 ----- src/assets/js/index.js | 68 ------------- src/assets/js/scheduling.js | 12 --- src/assets/js/video-controls.js | 15 --- src/assets/styles/fontface.scss | 8 ++ src/components/Info/helpers.js | 4 + src/components/Info/index.js | 67 ++++++------ src/components/Info/styles.js | 61 +++++++++++ src/components/InfoLayout/index.js | 31 ++++-- src/components/InfoLayout/styles.js | 49 ++++++++- src/components/Loader/index.js | 38 +++++++ .../chat.js => components/Loader/styles.js} | 0 src/components/Text/index.js | 18 ++-- src/components/Text/styles.js | 4 +- src/data/strings.js | 5 +- src/hooks/data.js | 96 ++++++++++++++++++ src/hooks/timerHooks.js | 38 +++++++ yarn.lock | 24 +++++ 21 files changed, 449 insertions(+), 179 deletions(-) create mode 100644 src/assets/fonts/Karla/Karla-Regular.ttf delete mode 100644 src/assets/hooks/calendar.js delete mode 100644 src/assets/js/index.js delete mode 100644 src/assets/js/scheduling.js delete mode 100644 src/assets/js/video-controls.js create mode 100644 src/components/Info/helpers.js create mode 100644 src/components/Loader/index.js rename src/{assets/js/chat.js => components/Loader/styles.js} (100%) create mode 100644 src/hooks/data.js create mode 100644 src/hooks/timerHooks.js diff --git a/app.js b/app.js index 6f1169f..10f95af 100644 --- a/app.js +++ b/app.js @@ -1,14 +1,13 @@ import { h } from 'preact' -import axios from 'axios' -// eslint-disable-next-line import/no-extraneous-dependencies +import { useState, useEffect } from 'preact/hooks' import 'regenerator-runtime/runtime' -import { useEffect, useState } from 'preact/hooks' +import axios from 'axios' import Video from './src/components/Video' import config from './src/data/config' import Info from './src/components/Info' -import { useFetch } from './src/assets/hooks/calendar' -import { P } from './src/components/Text' +import { useCalendar } from './src/hooks/data' +import { useTimeout } from './src/hooks/timerHooks' // const appStates = [ // 'noStream', @@ -19,13 +18,58 @@ import { P } from './src/components/Text' export default () => { const [isPlaying, setIsPlaying] = useState(false) const [videoUrl, setVideoUrl] = useState(null) - // const [feedData, setFeedData] = useState([]) + const [feedData, setFeedData] = useState([]) + const [minLoadTimePassed, setMinTimeUp] = useState(false) + const { data, loading } = useCalendar() - const { data: feedData, loading } = useFetch(`${config.calendar}`) + useTimeout(() => { + setMinTimeUp(true) + }, 1500) + + useEffect(() => { + if (data && data.length) { + data.forEach(async (calItem, index) => { + if (calItem.url) { + const id = calItem.url.val.split('/').pop() + + const { + data: { + account, + category, + channel, + embedPath, + language, + name, + state, + previewPath, + views, + }, + } = await axios.get(`https://tv.undersco.re/api/v1/videos/${id}`) + + const item = { + name, + account, + category, + channel, + description: calItem.description, + embedPath, + language, + state, + previewPath, + views, + start: calItem.start, + end: calItem.end, + id, + } + setFeedData(arr => [...arr, item]) + } + }) + } + }, [data]) return (
- {/* {false ? ( + {false ? (
) } diff --git a/package.json b/package.json index cca5985..9dd4d37 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,8 @@ "dependencies": { "@peertube/embed-api": "^0.0.4", "axios": "^0.21.1", + "date-fns": "^2.19.0", + "ical": "^0.8.0", "preact": "^10.5.12", "prop-types": "^15.7.2", "styled-components": "^5.2.1" diff --git a/src/assets/fonts/Karla/Karla-Regular.ttf b/src/assets/fonts/Karla/Karla-Regular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..a08cc9b60caf5d3792c611c8d8b5ae556f0ab41f GIT binary patch literal 38304 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

{ - const [data, setData] = useState(null) - const [loading, setLoading] = useState(true) - - async function fetchData() { - setLoading(true) - - const { data: responseData } = await axios.get(url) - console.log('url', url) - console.log('responseData', responseData) - setData(responseData) - setLoading(false) - } - - useEffect(() => { - fetchData() - }, []) - - return { loading, data } -} diff --git a/src/assets/js/index.js b/src/assets/js/index.js deleted file mode 100644 index 7bc7711..0000000 --- a/src/assets/js/index.js +++ /dev/null @@ -1,68 +0,0 @@ -import { PeerTubePlayer } from '@peertube/embed-api'; -import 'regenerator-runtime/runtime'; -import { toggleVideoPlaying } from './video-controls'; -import { getVideoData } from './scheduling'; - -const streamData = getVideoData(); - -const videoWrapper = document.getElementById('video-wrapper'); -const overlay = document.getElementById('overlay'); -const videoiFrame = document.querySelector('iframe.video-player'); - -const video = { - isPlaying: false, - overlayVisible: false, -}; - -export const setUpPlayer = async () => { - const player = new PeerTubePlayer(videoiFrame); - - await player.ready; - - onPlayerReady(player); - app(); -}; - -const app = () => { - if (streamData && streamData) { - console.log({ streamData }); - } -}; - -const onPlayerReady = (player) => { - videoWrapper.addEventListener('mousemove', showOverlay); - - videoWrapper.addEventListener('click', () => { - toggleVideoPlaying({ - player, - video, - onPlay: () => { - hideOverlay(); - }, - onPause: () => { - showOverlay(); - }, - }); - video.isPlaying = !video.isPlaying; - }); -}; - -const showOverlay = () => { - if (!video.overlayVisible) { - videoWrapper.classList.add('active'); - overlay.classList.add('active'); - - setTimeout(hideOverlay, 2000); - } - video.overlayVisible = true; -}; - -const hideOverlay = () => { - if (video.isPlaying) { - video.overlayVisible = false; - videoWrapper.classList.remove('active'); - overlay.classList.remove('active'); - } -}; - -setUpPlayer(); diff --git a/src/assets/js/scheduling.js b/src/assets/js/scheduling.js deleted file mode 100644 index 44a8fa2..0000000 --- a/src/assets/js/scheduling.js +++ /dev/null @@ -1,12 +0,0 @@ -import axios from 'axios'; -import 'regenerator-runtime/runtime'; -import config from '../../data/conf.json'; - -export const getVideoData = async () => { - console.log({ config }); - const { data } = await axios.get(`https://tv.undersco.re/api/v1/videos/${config.next_stream.peertube_id}`); - - console.log(data); - - return data; -}; diff --git a/src/assets/js/video-controls.js b/src/assets/js/video-controls.js deleted file mode 100644 index a02933e..0000000 --- a/src/assets/js/video-controls.js +++ /dev/null @@ -1,15 +0,0 @@ -export const toggleVideoPlaying = ({ player, video, onPlay, onPause }) => { - console.log('video', video); - - if (video.isPlaying) { - player.pause(); - if (typeof onPause === 'function') { - onPause(); - } - } else { - player.play(); - if (typeof onPlay === 'function') { - onPlay(); - } - } -}; diff --git a/src/assets/styles/fontface.scss b/src/assets/styles/fontface.scss index 8f5fde1..836a2b0 100644 --- a/src/assets/styles/fontface.scss +++ b/src/assets/styles/fontface.scss @@ -1,3 +1,11 @@ +/* Karla Regular */ +@font-face { + font-family: 'Karla'; + src: url('../fonts/Karla/Karla-Regular.ttf') format('truetype'); + font-weight: 400; + font-style: normal; +} + /* Karla Medium */ @font-face { font-family: 'Karla'; diff --git a/src/components/Info/helpers.js b/src/components/Info/helpers.js new file mode 100644 index 0000000..4b9b9e6 --- /dev/null +++ b/src/components/Info/helpers.js @@ -0,0 +1,4 @@ +export const sortData = data => + Object.values(data) + .filter(feedItem => feedItem.type === 'VEVENT') + .sort((a, b) => new Date(a.start) - new Date(b.start)) diff --git a/src/components/Info/index.js b/src/components/Info/index.js index 46d1ad1..60a06f2 100644 --- a/src/components/Info/index.js +++ b/src/components/Info/index.js @@ -1,42 +1,51 @@ -import { h } from 'preact' +/* eslint-disable react/prop-types */ +import { h, Fragment } from 'preact' import { useEffect, useRef, useState } from 'preact/hooks' +import { isBefore } from 'date-fns' -import { H1, H2, P } from '../Text' -import { - Wrapper, - PositionedLogo as Logo, - TaglineContainer, - Top, -} from './styles' +import { P } from '../Text' import translations from '../../data/strings' import InfoLayout from '../InfoLayout' +import { VideoCard, Title, InfoContent } from './styles' -const allowedChannels = [7, 4] // ReclaimFutures, NDC +const Info = ({ data, loading }) => { + const now = new Date() + const pastStreams = + data && data.length + ? data.filter(feeditem => isBefore(new Date(feeditem.end), now)) + : [] -const Info = ({ data }) => { - // const [feed, setFeed] = useState([]) - - // useEffect(() => { - // setFeed(sortData(data)) - // }, [data]) - - useEffect(() => { - console.log({ data }) - }, [data]) + const futureStreams = + data && data.length + ? data.filter(feeditem => isBefore(now, new Date(feeditem.start))) + : [] return ( - -

ding dong

+ + {!loading && ( + + {futureStreams.map(feeditem => ( + + ))} + {pastStreams.length ? ( + + {translations.en.pastStream}: + {pastStreams.map(feeditem => ( + + ))} + + ) : null} + + )} ) } -const sortData = data => { - // if (!data || data?.length === 0) return - // console.log('data', data) - // return data.filter(feedItem => { - // return allowedChannels.includes(feedItem.channel.id) && feedItem.isLive - // }) -} - export default Info diff --git a/src/components/Info/styles.js b/src/components/Info/styles.js index 0b403ea..ebcef2a 100644 --- a/src/components/Info/styles.js +++ b/src/components/Info/styles.js @@ -1,6 +1,12 @@ +import { format } from 'date-fns' +import { h, Fragment } from 'preact' import styled from 'styled-components' import { colours } from '../../assets/theme' +import config from '../../data/config' import Logo from '../Logo' +import translations from '../../data/strings' + +import { P, H1, H2, Span, Label } from '../Text' export const Wrapper = styled.div` height: 100vh; @@ -27,6 +33,10 @@ export const Wrapper = styled.div` export const Top = styled.div`` +export const InfoContent = styled.div` + padding-bottom: 1em; +` + export const PositionedLogo = styled(Logo)` margin-bottom: 64px; ` @@ -36,3 +46,54 @@ export const TaglineContainer = styled.div` margin-top: 32px; } ` + +export const Title = styled(H1)` + margin: 0.3em 0; +` + +const VCWrapper = styled.div` + max-width: 600px; + margin-bottom: 3em; + border-left: 7px solid ${colours.midnightDarker}; + padding-left: 1em; +` + +const VCImg = styled.img` + width: 100%; +` + +const ItemTitle = styled(H2)` + margin-bottom: 0.3em; +` + +const DateLabel = styled(Label)` + margin: 1em 0; + display: block; +` + +export const VideoCard = ({ + name, + description, + start, + end, + previewPath, + hasPassed, +}) => { + return ( + + {name} + + + {`${ + hasPassed + ? translations.en.streamDatePast + : translations.en.streamDateFuture + }`} + + {format(new Date(start), 'hh:mm dd/MM/yy')} + + +

{description}

+
+ ) +} diff --git a/src/components/InfoLayout/index.js b/src/components/InfoLayout/index.js index ab3ec95..12ccc79 100644 --- a/src/components/InfoLayout/index.js +++ b/src/components/InfoLayout/index.js @@ -1,34 +1,53 @@ import { h } from 'preact' import { useEffect, useRef, useState } from 'preact/hooks' +import { bool, string } from 'prop-types' -import { H1, H2, P } from '../Text' +import { H1, H2 } from '../Text' import { Wrapper, PositionedLogo as Logo, TaglineContainer, - Top, + Title, Content, + Fade, } from './styles' import translations from '../../data/strings' import { colours } from '../../assets/theme' +import Loader from '../Loader' +import { useTimeout } from '../../hooks/timerHooks' -const InfoLayout = ({ title, children }) => { +const InfoLayout = ({ title, children, loading }) => { return ( - + + + -

{title}

+ {loading ? ( +
+ +
+ ) : ( + {title} + )} {children}
{translations && translations.en.underscoreTagline.map(line => ( -

{line}

+

+ {line} +

))}
) } +InfoLayout.propTypes = { + title: string, + loading: bool, +} + export default InfoLayout diff --git a/src/components/InfoLayout/styles.js b/src/components/InfoLayout/styles.js index e2a385a..01b9afa 100644 --- a/src/components/InfoLayout/styles.js +++ b/src/components/InfoLayout/styles.js @@ -1,5 +1,8 @@ import styled from 'styled-components' import { colours } from '../../assets/theme' + +import { H1 } from '../Text' + import Logo from '../Logo' export const Wrapper = styled.div` @@ -41,14 +44,52 @@ export const Wrapper = styled.div` export const Top = styled.div` width: 50%; ` -export const Content = styled.div`` -export const PositionedLogo = styled(Logo)` +const gradientColour = '#F8E5E2' +const getGradient = direction => + `linear-gradient(to ${direction}, ${gradientColour}ee 0%,${gradientColour}00 100%);` + +// prettier-ignore +export const Fade = styled.div` + width: 100%; + background-color: linear; position: fixed; - top: 2em; + padding: 2em 0 1em 2em; + top: 0; + left: 0; + background: ${getGradient('bottom')}; ` +export const Title = styled(H1)` + margin: 0.5em 0; +` + +export const Content = styled.div` + /* margin-bottom: 3em; */ +` + +export const PositionedLogo = styled(Logo)`` export const TaglineContainer = styled.div` + background: ${getGradient('top')}; position: fixed; - bottom: 2em; + bottom: 0em; + padding-bottom: 0.5em; + right: 1em; + pointer-events: none; + + @media screen and (max-width: 1200px) { + width: 100vw; + right: auto; + left: 1.5em; + h1 { + font-size: 32px; + text-align: left; + } + } + + @media screen and (max-width: 800px) { + h1 { + font-size: 24px; + } + } ` diff --git a/src/components/Loader/index.js b/src/components/Loader/index.js new file mode 100644 index 0000000..b8b7ed6 --- /dev/null +++ b/src/components/Loader/index.js @@ -0,0 +1,38 @@ +import { h } from 'preact' +import { useRef, useState } from 'preact/hooks' +import { useInterval, useTimeout } from '../../hooks/timerHooks' +import { colours } from '../../assets/theme' +import { H1 } from '../Text' + +// const symbols = ['⌏', '⌎', '⌌', '⌍'] + +const Loader = ({ + active = true, + offset = 0, + animation = [':..', '.:.', '..:', '...'], +}) => { + const [text, setText] = useState('.') + const arrayPosition = useRef(offset) + const rate = 350 + + useInterval( + () => { + setText(animation[arrayPosition.current]) + + if (arrayPosition.current === animation.length - 1) { + arrayPosition.current = 0 + } else { + arrayPosition.current += 1 + } + }, + active ? rate : null + ) + + return ( +

+ {text} +

+ ) +} + +export default Loader diff --git a/src/assets/js/chat.js b/src/components/Loader/styles.js similarity index 100% rename from src/assets/js/chat.js rename to src/components/Loader/styles.js diff --git a/src/components/Text/index.js b/src/components/Text/index.js index 1d97be1..5f89fb3 100644 --- a/src/components/Text/index.js +++ b/src/components/Text/index.js @@ -25,7 +25,7 @@ const Text = ({ weight={weight} lineHeight={lineHeight} $fontFamily={fontFamily} - size={size} + $size={size} selectable={selectable} {...rest} > @@ -66,7 +66,7 @@ export const H1 = ({ children, ...rest }) => { ( ( export const P = ({ children, ...rest }) => ( @@ -102,15 +102,15 @@ export const P = ({ children, ...rest }) => ( ) export const Span = ({ children, ...rest }) => ( - + {children} ) -export const Label = ({ children, ...rest }) => ( +export const Label = ({ children, size, ...rest }) => ( { + const [data, setData] = useState(null) + const [loading, setLoading] = useState(true) + + async function fetchData() { + setLoading(true) + + const { data: responseData } = await axios.get(`${config.calendar}`) + const streamsData = Object.values(ical.parseICS(responseData)) + .filter(feedItem => feedItem.type === 'VEVENT') + .sort((a, b) => new Date(a.start) - new Date(b.start)) + setData(streamsData) + setLoading(false) + } + + useEffect(() => { + fetchData() + }, []) + + return { loading, data } +} + +// export const useCalendar = () => { +// const [data, setData] = useState(null) +// const [loading, setLoading] = useState(true) + +// async function fetchData() { +// setLoading(true) + +// const { data: responseData } = await axios.get(`${config.calendar}`) +// const streamsData = Object.values(ical.parseICS(responseData)) +// .filter(feedItem => feedItem.type === 'VEVENT') +// .sort((a, b) => new Date(a.start) - new Date(b.start)) +// setData(streamsData) +// setLoading(false) +// } + +// useEffect(() => { +// fetchData() +// }, []) + +// return { loading, data } +// } +// useEffect(() => { +// const feedPromise = +// data && +// data.map(async feedItem => { +// if (feedItem.url) { +// const id = feedItem.url.val.split('/').pop() + +// const { +// data: { +// account, +// category, +// channel, +// description, +// embedPath, +// language, +// name, +// state, +// previewPath, +// views, +// }, +// } = await axios.get(`https://tv.undersco.re/api/v1/videos/${id}`) + +// const item = { +// name, +// account, +// category, +// channel, +// description, +// embedPath, +// language, +// state, +// previewPath, +// views, +// start: feedItem.start, +// end: feedItem.end, +// } + +// console.log(item) + +// return item +// } +// return null +// }) + +// feedPromise.then(result => { +// console.log(result) +// }) +// }, [data]) diff --git a/src/hooks/timerHooks.js b/src/hooks/timerHooks.js new file mode 100644 index 0000000..4383a31 --- /dev/null +++ b/src/hooks/timerHooks.js @@ -0,0 +1,38 @@ +/* eslint-disable consistent-return */ +import { useEffect, useRef } from 'preact/hooks' + +export const useInterval = (callback, interval) => { + const savedCallback = useRef() + + useEffect(() => { + savedCallback.current = callback + }, [callback]) + + useEffect(() => { + function tick() { + savedCallback.current() + } + if (interval !== null) { + const id = setInterval(tick, interval) + return () => clearInterval(id) + } + }, [interval]) +} + +export const useTimeout = (callback, timeout) => { + const savedCallback = useRef() + + useEffect(() => { + savedCallback.current = callback + }, [callback]) + + useEffect(() => { + function tick() { + savedCallback.current() + } + if (timeout !== null) { + const id = setTimeout(tick, timeout) + return () => clearTimeout(id) + } + }, [timeout]) +} diff --git a/yarn.lock b/yarn.lock index e7fc05b..358eca8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2483,6 +2483,11 @@ data-urls@^1.1.0: whatwg-mimetype "^2.2.0" whatwg-url "^7.0.0" +date-fns@^2.19.0: + version "2.19.0" + resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.19.0.tgz#65193348635a28d5d916c43ec7ce6fbd145059e1" + integrity sha512-X3bf2iTPgCAQp9wvjOQytnf5vO5rESYRXlPIVcgSbtT5OTScPcsf9eZU+B/YIkKAtYr5WeCii58BgATrNitlWg== + deasync@^0.1.14: version "0.1.21" resolved "https://registry.yarnpkg.com/deasync/-/deasync-0.1.21.tgz#bb11eabd4466c0d8776f0d82deb8a6126460d30f" @@ -3612,6 +3617,13 @@ https-browserify@^1.0.0: resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM= +ical@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/ical/-/ical-0.8.0.tgz#aa93f021dfead58e54aaa22076a11ca07d65886b" + integrity sha512-/viUSb/RGLLnlgm0lWRlPBtVeQguQRErSPYl3ugnUaKUnzQswKqOG3M8/P1v1AB5NJwlHTuvTq1cs4mpeG2rCg== + dependencies: + rrule "2.4.1" + iconv-lite@0.4.24, iconv-lite@^0.4.17: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" @@ -4263,6 +4275,11 @@ lru-cache@^4.0.1, lru-cache@^4.1.5: pseudomap "^1.0.2" yallist "^2.1.2" +luxon@^1.3.3: + version "1.26.0" + resolved "https://registry.yarnpkg.com/luxon/-/luxon-1.26.0.tgz#d3692361fda51473948252061d0f8561df02b578" + integrity sha512-+V5QIQ5f6CDXQpWNICELwjwuHdqeJM1UenlZWx5ujcRMc9venvluCjFb4t5NYLhb6IhkbMVOxzVuOqkgMxee2A== + magic-string@^0.22.4: version "0.22.5" resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.22.5.tgz#8e9cf5afddf44385c1da5bc2a6a0dbd10b03657e" @@ -5792,6 +5809,13 @@ ripemd160@^2.0.0, ripemd160@^2.0.1: hash-base "^3.0.0" inherits "^2.0.1" +rrule@2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/rrule/-/rrule-2.4.1.tgz#1d0db4e45f2b0e92e2cca62d2f7093729ac7ec94" + integrity sha512-+NcvhETefswZq13T8nkuEnnQ6YgUeZaqMqVbp+ZiFDPCbp3AVgQIwUvNVDdMNrP05bKZG9ddDULFp0qZZYDrxg== + optionalDependencies: + luxon "^1.3.3" + run-async@^2.2.0: version "2.4.1" resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455"