last min fixes

This commit is contained in:
Benjamin Jones 2021-05-23 17:21:15 +02:00
parent 9852003f55
commit 28fae929b2
13 changed files with 151 additions and 48 deletions

18
app.js
View File

@ -1,6 +1,7 @@
import { h } from 'preact' import { h } from 'preact'
import { useState, useEffect } from 'preact/hooks' import { useState, useEffect } from 'preact/hooks'
import { isWithinInterval, subHours } from 'date-fns' import { isWithinInterval, subHours } from 'date-fns'
import { zonedTimeToUtc, utcToZonedTime, format } from 'date-fns-tz'
import { addHours } from 'date-fns/esm' import { addHours } from 'date-fns/esm'
import Video from './src/components/Video' import Video from './src/components/Video'
@ -23,18 +24,27 @@ export default () => {
useEffect(() => { useEffect(() => {
if (data && data.length) { if (data && data.length) {
data.forEach((stream, index) => { data.forEach((stream, index) => {
const utcStartDate = zonedTimeToUtc(
new Date(stream.start),
'Europe/Berlin'
)
const utcEndDate = zonedTimeToUtc(new Date(stream.end), 'Europe/Berlin')
const { timeZone } = Intl.DateTimeFormat().resolvedOptions()
const zonedStartDate = utcToZonedTime(utcStartDate, timeZone)
const zonedEndDate = utcToZonedTime(utcEndDate, timeZone)
if ( if (
isWithinInterval(new Date(), { isWithinInterval(new Date(), {
start: subHours(stream.start, 1), start: subHours(zonedStartDate, 1),
end: addHours(stream.end, 1), end: addHours(zonedEndDate, 1),
}) })
) { ) {
setCurrentVideo(stream) setCurrentVideo(stream)
} }
if ( if (
isWithinInterval(new Date(), { isWithinInterval(new Date(), {
start: stream.start, start: zonedStartDate,
end: stream.end, end: zonedEndDate,
}) })
) { ) {
setStreamIsLive(true) setStreamIsLive(true)

View File

@ -37,7 +37,7 @@
/> />
<meta <meta
name="twitter:description" name="twitter:description"
content="The Para-Real: Finding the Future in Unexpected Places - A livestream series about subcultures building livelihoods in spite of platform exploitation." content="The NEW DESIGN CONGRESS x RECLAIMFUTURES present a livestream series about subcultures building livelihoods in spite of platform exploitation."
/> />
<meta name="twitter:image" content="static/meta.png" /> <meta name="twitter:image" content="static/meta.png" />
@ -47,7 +47,7 @@
/> />
<meta <meta
property="og:description" property="og:description"
content="The Para-Real: Finding the Future in Unexpected Places - A livestream series about subcultures building livelihoods in spite of platform exploitation." content="The NEW DESIGN CONGRESS x RECLAIMFUTURES present a livestream series about subcultures building livelihoods in spite of platform exploitation"
/> />
<meta property="og:image" content="static/meta.png" /> <meta property="og:image" content="static/meta.png" />
<meta property="og:url" content="https://stream.undersco.re/" /> <meta property="og:url" content="https://stream.undersco.re/" />

View File

@ -13,11 +13,11 @@ import {
} from './styles' } from './styles'
import { colours } from '../../assets/theme' import { colours } from '../../assets/theme'
const Chat = ({}) => { const Chat = ({ overlayActive }) => {
const { width, height } = useWindowDimensions() const { width, height } = useWindowDimensions()
const [chatIsOpen, toggleChatOpen] = useToggle(true) const [chatIsOpen, toggleChatOpen] = useToggle(true)
return chatIsOpen ? ( return chatIsOpen ? (
<ChatWrapper> <ChatWrapper chatIsOpen={chatIsOpen} overlayActive={overlayActive}>
<ChatFrame> <ChatFrame>
<ChatHeader chatIsOpen> <ChatHeader chatIsOpen>
<Label weight="400" size={24}> <Label weight="400" size={24}>
@ -26,7 +26,7 @@ const Chat = ({}) => {
<CloseBox colour={colours.white} size={18} onClick={toggleChatOpen} /> <CloseBox colour={colours.white} size={18} onClick={toggleChatOpen} />
</ChatHeader> </ChatHeader>
<iframe <iframe
src="https://titanembeds.com/embed/803918964082212905?css=215&defaultchannel=817134294199566356&lang=en_EN" src="https://titanembeds.com/embed/709318870909059082?css=215&defaultchannel=826751398757793842&lang=en_EN"
height={(height / 4) * 3} height={(height / 4) * 3}
width="350" width="350"
frameBorder="0" frameBorder="0"
@ -38,7 +38,7 @@ const Chat = ({}) => {
) : ( ) : (
<ChatHeader chatIsOpen={false} onClick={toggleChatOpen}> <ChatHeader chatIsOpen={false} onClick={toggleChatOpen}>
<Label weight="400" size={24}> <Label weight="400" size={24}>
Chat CHAT
</Label> </Label>
<OpenIcon colour={colours.white} size={16} /> <OpenIcon colour={colours.white} size={16} />
</ChatHeader> </ChatHeader>

View File

@ -15,16 +15,20 @@ export const ChatWrapper = styled.div`
bottom: -5px; bottom: -5px;
right: 0; right: 0;
backdrop-filter: blur(20px); backdrop-filter: blur(20px);
background-color: ${colours.midnight}40; background-color: ${colours.midnight}60;
border-radius: ${ui.borderRadius}px; border-radius: ${ui.borderRadius}px;
opacity: ${props => (!props.chatIsOpen && !props.overlayActive ? 0 : 1)};
` `
export const ChatHeader = styled.div` export const ChatHeader = styled.div`
position: absolute; position: absolute;
bottom: ${props => (props.chatIsOpen ? 'initial' : 0)}; bottom: ${props => (props.chatIsOpen ? 'initial' : '0')};
top: ${props => (props.chatIsOpen ? '4px' : 'initial')}; top: ${props => (props.chatIsOpen ? '0px' : 'initial')};
border-radius: ${ui.borderRadius}px 0 0 0; border-radius: ${props =>
props.chatIsOpen ? `${ui.borderRadius}px 0 0 0` : '0'};
z-index: 2; z-index: 2;
background-color: ${props => (props.chatIsOpen ? '#00000036' : '#ffffffba')};
backdrop-filter: blur(2px);
height: 32px; height: 32px;
box-sizing: border-box; box-sizing: border-box;
@ -34,17 +38,23 @@ export const ChatHeader = styled.div`
justify-content: space-between; justify-content: space-between;
padding: 0px 0px 3px 0px; padding: 0px 0px 3px 0px;
right: 0; right: ${props => (props.chatIsOpen ? '0' : '32px')};
box-sizing: content-box; box-sizing: content-box;
border: ${props => border: ${props =>
props.chatIsOpen ? 'none' : `1px solid ${colours.white}`}; props.chatIsOpen ? 'none' : `1px solid ${colours.midnightDarker}`};
border-bottom: ${props => border-bottom: ${props =>
props.chatIsOpen ? `1px solid ${colours.white}75` : 'none'}; props.chatIsOpen ? `1px solid ${colours.white}75` : 'none'};
border-right: none; /* border-right: none; */
label { label {
margin-left: 12px; margin-left: 12px;
margin-right: ${props => (props.chatIsOpen ? '0' : '12px')}; margin-right: ${props => (props.chatIsOpen ? '0' : '12px')};
color: ${props =>
props.chatIsOpen ? colours.white : colours.midnightDarker};
}
svg path {
fill: ${props =>
props.chatIsOpen ? colours.white : colours.midnightDarker};
} }
` `

View File

@ -58,7 +58,9 @@ const Info = ({ data, loading, infoActive, setInfoActive, currentVideo }) => {
url={config.seriesTrailer} url={config.seriesTrailer}
/> />
)} )}
{infoActive && <CrossSvg onClick={() => setInfoActive(false)} />} {infoActive && (
<CrossSvg size="64" onClick={() => setInfoActive(false)} />
)}
{!loading && ( {!loading && (
<Fragment> <Fragment>
<InfoContent> <InfoContent>
@ -92,14 +94,14 @@ const Info = ({ data, loading, infoActive, setInfoActive, currentVideo }) => {
</Fragment> </Fragment>
)} )}
{futureStreams.length && ( {futureStreams.length ? (
<Fragment> <Fragment>
<Title>{translations.en.nextStream}:</Title> <Title>{translations.en.nextStream}:</Title>
{futureStreams.map(feeditem => ( {futureStreams.map(feeditem => (
<VideoCard key={feeditem.start} {...feeditem} /> <VideoCard key={feeditem.start} {...feeditem} />
))} ))}
</Fragment> </Fragment>
)} ) : null}
{pastStreams.length ? ( {pastStreams.length ? (
<Fragment> <Fragment>

View File

@ -130,10 +130,11 @@ export const PositionedCross = styled(CrossSvg)`
position: fixed; position: fixed;
right: 2.5em; right: 2.5em;
top: 2em; top: 2em;
width: 24px; /* width: 32px; */
height: 24px; /* height: 32px; */
cursor: pointer; cursor: pointer;
stroke: ${colours.midnightDarker}; stroke: ${colours.midnightDarker};
z-index: 5;
&:hover { &:hover {
opacity: 0.5; opacity: 0.5;

View File

@ -18,7 +18,7 @@ const Logo = ({ colour = colours.offwhite, ...rest }) => (
) )
const LogoSvg = styled.svg` const LogoSvg = styled.svg`
height: 60px; height: ${props => (props.$size === 'lg' ? '60px' : '32px')};
` `
Logo.propTypes = { Logo.propTypes = {
@ -31,6 +31,7 @@ export const NdcLogo = ({ colour = colours.offwhite, ...rest }) => (
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
fill="none" fill="none"
viewBox="0 0 552 140" viewBox="0 0 552 140"
$size="lg"
{...rest} {...rest}
> >
<path <path
@ -45,6 +46,7 @@ export const RFLogo = ({ colour = colours.offwhite, ...rest }) => (
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
fill="none" fill="none"
viewBox="0 0 299 140" viewBox="0 0 299 140"
$size="lg"
{...rest} {...rest}
> >
<path <path

View File

@ -91,11 +91,11 @@ export const H2 = ({ children, ...rest }) => (
</Text> </Text>
) )
export const P = ({ children, ...rest }) => ( export const P = ({ children, size, ...rest }) => (
<Text <Text
tag="p" tag="p"
weight="400" weight="400"
$size="15" $size={size || '15'}
lineHeight="21px" lineHeight="21px"
fontFamily="Karla" fontFamily="Karla"
{...rest} {...rest}

View File

@ -14,11 +14,13 @@ import { PeerTubePlayer } from '@peertube/embed-api'
import Chat from '../Chat' import Chat from '../Chat'
import Overlay from '../VideoOverlay' import Overlay from '../VideoOverlay'
import { VideoWrapper, Iframe } from './styles' import { VideoWrapper, Iframe, PlayButton } from './styles'
import config from '../../data/config' import config from '../../data/config'
import { useToggle } from '../../hooks/utility'
const Video = ({ video, org, setInfoActive }) => { const Video = ({ video, org, setInfoActive }) => {
const [isPlaying, setPlaying] = useState(false) const [isPlaying, setPlaying] = useState(false)
const [isFullscreen, toggleIsFullscreen] = useToggle(false)
const videoiFrame = useRef(null) const videoiFrame = useRef(null)
const overlayTimeout = useRef(null) const overlayTimeout = useRef(null)
const [videoReady, setVideoReady] = useState(false) const [videoReady, setVideoReady] = useState(false)
@ -73,6 +75,7 @@ const Video = ({ video, org, setInfoActive }) => {
} }
const toggleFullscreen = () => { const toggleFullscreen = () => {
toggleIsFullscreen()
if (!document.fullscreenElement) { if (!document.fullscreenElement) {
document.documentElement.requestFullscreen() document.documentElement.requestFullscreen()
} else if (document.exitFullscreen) { } else if (document.exitFullscreen) {
@ -120,7 +123,10 @@ const Video = ({ video, org, setInfoActive }) => {
active={overlayActive || !isPlaying} active={overlayActive || !isPlaying}
title={video.title} title={video.title}
setInfoActive={setInfoActive} setInfoActive={setInfoActive}
onClickFullscreen={toggleFullscreen}
isFullscreen={isFullscreen}
/> />
{!isPlaying && <PlayButton />}
<Iframe <Iframe
sandbox="allow-same-origin allow-scripts allow-popups" sandbox="allow-same-origin allow-scripts allow-popups"
src={`${config.peertube_root}${video.embedPath}?api=1&controls=false&vq=hd1080`} src={`${config.peertube_root}${video.embedPath}?api=1&controls=false&vq=hd1080`}
@ -129,7 +135,7 @@ const Video = ({ video, org, setInfoActive }) => {
allow="autoplay" allow="autoplay"
ref={videoiFrame} ref={videoiFrame}
/> />
<Chat /> <Chat overlayActive={overlayActive} />
</VideoWrapper> </VideoWrapper>
) )
} }

View File

@ -1,4 +1,9 @@
import { h } from 'preact'
import styled from 'styled-components' import styled from 'styled-components'
import { Label } from '../Text'
import translations from '../../data/strings'
import Button from '../Button'
import { colours } from '../../assets/theme'
export const VideoWrapper = styled.div` export const VideoWrapper = styled.div`
width: 100vw; width: 100vw;
@ -10,12 +15,16 @@ export const VideoWrapper = styled.div`
left: 0; left: 0;
right: 0; right: 0;
cursor: ${props => (props.$active ? 'pointer' : 'none')}; cursor: ${props => (props.$active ? 'pointer' : 'none')};
display: flex;
justify-content: center;
align-items: center;
` `
export const Iframe = styled.iframe` export const Iframe = styled.iframe`
z-index: -1; z-index: -1;
width: 100vw; width: 100vw;
height: 100vh; height: 100vh;
pointer-events: none; pointer-events: none;
position: absolute;
` `
export const Overlay = styled.div` export const Overlay = styled.div`
z-index: 1; z-index: 1;
@ -23,3 +32,36 @@ export const Overlay = styled.div`
height: 100vh; height: 100vh;
width: 100vw; width: 100vw;
` `
const ButtonWrapper = styled.div`
div {
padding: 1em 2em;
background-color: #ffffffba;
display: flex;
flex-direction: row;
align-items: center;
border: 1px solid ${colours.midnight};
}
label {
color: ${colours.midnightDarker};
margin-left: 8px;
font-size: 20px;
}
:hover div {
background-color: #ffffff;
}
`
export const PlayButton = props => (
<ButtonWrapper {...props}>
<div>
<Label>{translations.en.joinStream}</Label>
</div>
</ButtonWrapper>
)
export const ActionButton = styled(Button)`
font-size: 18px;
`

View File

@ -1,28 +1,54 @@
import { Fragment, h } from 'preact' import { Fragment, h } from 'preact'
import { bool, string } from 'prop-types' import { bool, string } from 'prop-types'
import styled from 'styled-components'
import Logo from '../Logo' import Logo from '../Logo'
import { H2, P } from '../Text' import { H2, P } from '../Text'
import { InfoButton, OverlayWrapper, TopLeft } from './styles' import { InfoButton, OverlayWrapper, TopLeft } from './styles'
const VideoOverlay = ({ active, title, org, setInfoActive, onClick }) => ( const StyledP = styled(P)`
&:first-of-type {
border-top-left-radius: 5px;
border-top-right-radius: 5px;
}
&:last-of-type {
border-bottom-left-radius: 5px;
border-bottom-right-radius: 5px;
}
`
const renderTitles = titles =>
titles.split('\\n').map(title => (
<StyledP key={title} size={18}>
{title}
</StyledP>
))
const VideoOverlay = ({
active,
title,
org,
setInfoActive,
onClick,
onClickFullscreen,
isFullscreen,
}) => (
// const displayTitle = `${title}${org ? ` — ${org}` : ''}` // const displayTitle = `${title}${org ? ` — ${org}` : ''}`
<Fragment> <Fragment>
<OverlayWrapper onClick={onClick}> <OverlayWrapper onClick={onClick}>
<TopLeft $active={active}> <TopLeft $active={active}>
<Logo active={active} /> {/* <Logo active={active} /> */}
<P {title ? renderTitles(title) : null}
size={18}
css={`
margin-top: 16px;
`}
>
{title}
</P>
</TopLeft> </TopLeft>
</OverlayWrapper> </OverlayWrapper>
<InfoButton $active={active} onClick={() => setInfoActive(true)} /> <InfoButton $active={active} onClick={() => setInfoActive(true)}>
INFO
</InfoButton>
<InfoButton $active={active} onClick={onClickFullscreen} postition="bl">
{isFullscreen ? 'EXIT FULLSCREEN' : 'FULLSCREEN'}
</InfoButton>
</Fragment> </Fragment>
) )

View File

@ -1,6 +1,7 @@
import styled, { css } from 'styled-components' import styled, { css } from 'styled-components'
import { colours } from '../../assets/theme' import { colours } from '../../assets/theme'
import burb from '../../assets/img/IconSM.png' import burb from '../../assets/img/IconSM.png'
import Button from '../Button'
export const OverlayWrapper = styled.div` export const OverlayWrapper = styled.div`
z-index: 2; z-index: 2;
@ -28,23 +29,25 @@ export const TopLeft = styled.div`
padding: 4px 8px; padding: 4px 8px;
display: inline-block; display: inline-block;
margin-right: 45%; margin-right: 45%;
border-radius: 5px;
} }
` `
export const InfoButton = styled.img.attrs(() => ({ export const InfoButton = styled(Button)`
src: burb,
}))`
opacity: 0.001; opacity: 0.001;
transform: translateY(-20%); transform: translateY(
${props => (props.postition === 'bl' ? '20%' : '-20%')}
);
transition: all 0.2s ease-in-out; transition: all 0.2s ease-in-out;
transition-delay: 0.2s; transition-delay: 0.2s;
position: fixed; position: fixed;
right: 2em; right: ${props => (props.postition === 'bl' ? 'initial' : '32px')};
top: 2em; top: ${props => (props.postition === 'bl' ? 'initial' : '32px')};
width: 45px; bottom: ${props => (props.postition === 'bl' ? '0' : 'initial')};
height: 45px; left: ${props => (props.postition === 'bl' ? '32px' : 'initial')};
z-index: 100; z-index: 100;
width: auto;
background-color: #ffffffba;
${props => ${props =>
props.$active && props.$active &&
css` css`
@ -53,6 +56,6 @@ export const InfoButton = styled.img.attrs(() => ({
`}; `};
&:hover { &:hover {
filter: invert(1); opacity: 0.7;
} }
` `

View File

@ -10,5 +10,6 @@ export default {
subEvent: 'Add to calendar', subEvent: 'Add to calendar',
watchEpisode: 'Watch Episode', watchEpisode: 'Watch Episode',
watchTrailer: 'Watch the trailer', watchTrailer: 'Watch the trailer',
joinStream: 'Click to join stream',
}, },
} }