diff --git a/web/components/layouts/Main/Main.module.scss b/web/components/layouts/Main/Main.module.scss index 43111fb4c..4a0825d57 100644 --- a/web/components/layouts/Main/Main.module.scss +++ b/web/components/layouts/Main/Main.module.scss @@ -2,37 +2,15 @@ .layout { // this margin is for fixed header - margin-top: var(--header-height); + padding-top: var(--header-height); background-color: var(--theme-color-main-background); - @include screen(tablet) { - position: absolute; - top: 0; - left: 0; - bottom: 0; - right: 0; - // this one is for fixed footer - margin-bottom: 30px; - } + min-height: 100vh; + position: relative; - @include screen(mobile) { - margin-bottom: 0px; - - footer { - display: none; - } - } -} - -.fadeIn { - animation: fadein 0.5s; -} - -@keyframes fadein { - from { - opacity: 0; - } - - to { - opacity: 1; - } + // add some spacing between the last row of content and the footer + :global(.ant-row) { + &:last-of-type { + margin-bottom: 5em; + } + } } diff --git a/web/components/layouts/Main/Main.tsx b/web/components/layouts/Main/Main.tsx index 905d941bb..20d2fb079 100644 --- a/web/components/layouts/Main/Main.tsx +++ b/web/components/layouts/Main/Main.tsx @@ -3,7 +3,7 @@ /* eslint-disable react/no-unescaped-entities */ import { useRecoilValue } from 'recoil'; import Head from 'next/head'; -import { FC, useEffect, useRef, useState } from 'react'; +import { FC, useEffect, useRef } from 'react'; import { Layout } from 'antd'; import dynamic from 'next/dynamic'; import Script from 'next/script'; @@ -32,6 +32,7 @@ import { PushNotificationServiceWorker } from '../../workers/PushNotificationSer import { AppStateOptions } from '../../stores/application-state'; import { Noscript } from '../../ui/Noscript/Noscript'; import { ServerStatus } from '../../../interfaces/server-status.model'; +import { DYNAMIC_PADDING_VALUE } from '../../../utils/constants'; // Lazy loaded components @@ -46,7 +47,6 @@ const FatalErrorStateModal = dynamic( ); export const Main: FC = () => { - const [displayFooter, setDisplayFooter] = useState(false); const clientConfig = useRecoilValue<ClientConfig>(clientConfigStateAtom); const clientStatus = useRecoilValue<ServerStatus>(serverStatusState); const { name } = clientConfig; @@ -58,42 +58,16 @@ export const Main: FC = () => { const layoutRef = useRef<HTMLDivElement>(null); const { chatDisabled } = clientConfig; const { videoAvailable } = appState; - const { online, streamTitle, versionNumber: version } = clientStatus; + const { online, streamTitle } = clientStatus; // accounts for sidebar width when online in desktop const showChat = online && !chatDisabled && isChatVisible; - const dynamicFooterPadding = showChat && !isMobile ? '340px' : '20px'; + const dynamicFooterPadding = showChat && !isMobile ? DYNAMIC_PADDING_VALUE : ''; useEffect(() => { setupNoLinkReferrer(layoutRef.current); }, []); - const handleScroll = () => { - const documentHeight = document.body.scrollHeight; - const currentScroll = window.scrollY + window.innerHeight; - - // When the user is [modifier]px from the bottom, fire the event. - const modifier = 10; - if (currentScroll + modifier > documentHeight) { - if (!displayFooter) { - setDisplayFooter(true); - } - } else { - // eslint-disable-next-line no-lonely-if - if (displayFooter) { - setDisplayFooter(false); - } - } - }; - - useEffect(() => { - window.addEventListener('scroll', handleScroll); - - return () => { - window.removeEventListener('scroll', handleScroll); - }; - }, [displayFooter]); - const isProduction = process.env.NODE_ENV === 'production'; const headerText = online ? streamTitle || name : name; @@ -196,12 +170,8 @@ export const Main: FC = () => { {fatalError && ( <FatalErrorStateModal title={fatalError.title} message={fatalError.message} /> )} - <div - style={displayFooter ? { display: 'flex' } : { display: 'none' }} - className={styles.fadeIn} - > - <Footer version={version} dynamicPadding={dynamicFooterPadding} /> - </div> + + {(!isMobile || !online) && <Footer dynamicPaddingValue={dynamicFooterPadding} />} </Layout> <Noscript /> </> diff --git a/web/components/ui/Content/Content.module.scss b/web/components/ui/Content/Content.module.scss index 326b51360..d4f1645df 100644 --- a/web/components/ui/Content/Content.module.scss +++ b/web/components/ui/Content/Content.module.scss @@ -4,22 +4,23 @@ padding: var(--content-padding); } -.lowerSectionMobile { +.lowerSectionMobileTabbed { display: flex; flex-direction: column; flex-grow: 1; flex-shrink: 0; - position: absolute; + position: relative; bottom: 0; width: 100%; - @include screen(tablet) { + @include screen(tablet) { + top: 0; position: relative; - } - @include screen(mobile) { - //sets the position of tabbed content for online mode - top: 280px; + &.online { + position: absolute; + top: calc(var(--player-container-height) + var(--status-bar-height) + var(--header-height)); + } } :global(.ant-tabs-nav) { @@ -29,15 +30,7 @@ } } -.online { - @include screen(tablet) { - //sets the position of tabbed content for online mode - position: absolute; - top: 430px; - } -} - -.mobileNoTabs { +.lowerSectionMobileNoTabs { padding-top: 20px; } diff --git a/web/components/ui/Content/MobileContent.tsx b/web/components/ui/Content/MobileContent.tsx index 487ab27aa..81c7b4bc3 100644 --- a/web/components/ui/Content/MobileContent.tsx +++ b/web/components/ui/Content/MobileContent.tsx @@ -137,10 +137,13 @@ export const MobileContent: FC<MobileContentProps> = ({ <ComponentErrorFallback error={error} resetErrorBoundary={resetErrorBoundary} /> )} > - <div className={classNames([styles.lowerSectionMobile, online && styles.online])}> - {items.length > 1 && <Tabs defaultActiveKey="0" items={items} />} - </div> - <div className={styles.mobileNoTabs}>{items.length <= 1 && aboutTabContent}</div> + {items.length > 1 ? ( + <div className={classNames([styles.lowerSectionMobileTabbed, online && styles.online])}> + <Tabs defaultActiveKey="0" items={items} /> + </div> + ) : ( + <div className={styles.lowerSectionMobileNoTabs}>{aboutTabContent}</div> + )} </ErrorBoundary> ); }; diff --git a/web/components/ui/Footer/Footer.module.scss b/web/components/ui/Footer/Footer.module.scss index c0a11fd0a..bfa960c31 100644 --- a/web/components/ui/Footer/Footer.module.scss +++ b/web/components/ui/Footer/Footer.module.scss @@ -1,33 +1,28 @@ @import '../../../styles/mixins.scss'; .footer { + + position: absolute; + bottom: 0; + display: flex; align-items: center; flex-wrap: wrap; - height: var(--footer-height); justify-content: space-between; flex-direction: row; background-color: var(--theme-color-background-header); color: var(--theme-color-components-text-on-dark); font-family: var(--theme-text-body-font-family); - padding: 0.6rem 1rem; + padding: 0.6rem var(--footer-padding-x); font-size: 0.75rem; font-weight: 400; border-top: 1px solid rgba(214, 211, 211, 0.5); width: 100%; - position: fixed; - bottom: 0; + @include screen(tablet) { font-size: 10px; - position: fixed; - bottom: 0; - } - - @include screen(mobile) { - position: fixed; - bottom: 0; } a { diff --git a/web/components/ui/Footer/Footer.stories.tsx b/web/components/ui/Footer/Footer.stories.tsx index 69ee6cd6b..40eb48975 100644 --- a/web/components/ui/Footer/Footer.stories.tsx +++ b/web/components/ui/Footer/Footer.stories.tsx @@ -1,5 +1,6 @@ import React from 'react'; import { ComponentStory, ComponentMeta } from '@storybook/react'; +import { RecoilRoot } from 'recoil'; import { Footer } from './Footer'; export default { @@ -8,7 +9,11 @@ export default { parameters: {}, } as ComponentMeta<typeof Footer>; -const Template: ComponentStory<typeof Footer> = args => <Footer {...args} />; +const Template: ComponentStory<typeof Footer> = args => ( + <RecoilRoot> + <Footer {...args} /> + </RecoilRoot> +); // eslint-disable-next-line @typescript-eslint/no-unused-vars export const Example = Template.bind({}); diff --git a/web/components/ui/Footer/Footer.tsx b/web/components/ui/Footer/Footer.tsx index b819d7699..eb95f025c 100644 --- a/web/components/ui/Footer/Footer.tsx +++ b/web/components/ui/Footer/Footer.tsx @@ -1,27 +1,36 @@ import { FC } from 'react'; +import { useRecoilValue } from 'recoil'; import styles from './Footer.module.scss'; +import { ServerStatus } from '../../../interfaces/server-status.model'; +import { serverStatusState } from '../../stores/ClientConfigStore'; export type FooterProps = { - version: string; - dynamicPadding: string; + dynamicPaddingValue?: string; }; -export const Footer: FC<FooterProps> = ({ version, dynamicPadding }) => ( - <footer className={styles.footer} id="footer" style={{ paddingRight: dynamicPadding }}> - <span> - Powered by <a href="https://owncast.online">Owncast v{version}</a> - </span> - <span className={styles.links}> - <a href="https://owncast.online/docs" target="_blank" rel="noreferrer"> - Documentation - </a> - <a href="https://owncast.online/help" target="_blank" rel="noreferrer"> - Contribute - </a> - <a href="https://github.com/owncast/owncast" target="_blank" rel="noreferrer"> - Source - </a> - </span> - </footer> -); +export const Footer: FC<FooterProps> = ({ dynamicPaddingValue }) => { + const clientStatus = useRecoilValue<ServerStatus>(serverStatusState); + const { versionNumber } = clientStatus; + const dynamicPaddingStyle = dynamicPaddingValue + ? { paddingRight: `calc(${dynamicPaddingValue} + var(--footer-padding-x)` } + : null; + return ( + <footer className={styles.footer} id="footer" style={dynamicPaddingStyle}> + <span> + Powered by <a href="https://owncast.online">Owncast v{versionNumber}</a> + </span> + <span className={styles.links}> + <a href="https://owncast.online/docs" target="_blank" rel="noreferrer"> + Documentation + </a> + <a href="https://owncast.online/help" target="_blank" rel="noreferrer"> + Contribute + </a> + <a href="https://github.com/owncast/owncast" target="_blank" rel="noreferrer"> + Source + </a> + </span> + </footer> + ); +}; export default Footer; diff --git a/web/components/ui/Statusbar/Statusbar.module.scss b/web/components/ui/Statusbar/Statusbar.module.scss index abdc6ea8b..22f54a1db 100644 --- a/web/components/ui/Statusbar/Statusbar.module.scss +++ b/web/components/ui/Statusbar/Statusbar.module.scss @@ -2,7 +2,7 @@ display: flex; align-items: center; justify-content: space-between; - height: 2rem; + height: var(--status-bar-height); width: 100%; padding: var(--content-padding); color: var(--theme-color-components-video-status-bar-foreground); diff --git a/web/components/video/OwncastPlayer/OwncastPlayer.module.scss b/web/components/video/OwncastPlayer/OwncastPlayer.module.scss index a49218157..1f507d41f 100644 --- a/web/components/video/OwncastPlayer/OwncastPlayer.module.scss +++ b/web/components/video/OwncastPlayer/OwncastPlayer.module.scss @@ -4,12 +4,12 @@ display: grid; width: 100%; justify-items: center; - height: 75vh; + height: var(--player-container-height); aspect-ratio: 16 / 9; @media (max-width: 1200px) { height: 100%; - max-height: 75vh; + max-height: var(--player-container-height); } @include screen(desktop) { @@ -19,14 +19,8 @@ //set height of player for tablet @include screen(tablet) { - height: 400px; - max-height: 400px; - } - - //set height of player for mobile - @include screen(mobile) { - height: 250px; - max-height: 250px; + height: var(--player-container-height); + max-height: var(--player-container-height); } .player, diff --git a/web/styles/globals.scss b/web/styles/globals.scss index 68857174f..4e8b1c814 100644 --- a/web/styles/globals.scss +++ b/web/styles/globals.scss @@ -10,7 +10,6 @@ --content-padding: 0.95rem; --module-spacing: 12px; // margin size between lines of stuff, if needed --header-height: 70px; // needed for making main content scrollable; - --footer-height: 2.5rem; // needed for making main content scrollable; --content-height: calc(100vh - var(--header-height)); --replacement-bar-height: 46px; // needed for making main content scrollable on mobile; @@ -19,10 +18,19 @@ --chat-notification-icon-padding: 6px; --chat-message-padding: 10px; --chat-text-highlight-border-radius: 3px; + --chat-col-width: 320px; + + --player-container-height: 75vh; + --status-bar-height: 2rem; + --footer-padding-x: 1rem; @include screen(tablet) { --header-height: 3.85rem; + --player-container-height: 400px; } + @include screen(mobile) { + --player-container-height: 250px; + } } ::selection { diff --git a/web/utils/constants.js b/web/utils/constants.js index 451552d91..b2642ffcd 100644 --- a/web/utils/constants.js +++ b/web/utils/constants.js @@ -74,3 +74,5 @@ export const HAS_DISPLAYED_NOTIFICATION_MODAL_KEY = 'HAS_DISPLAYED_NOTIFICATION_ export const USER_VISIT_COUNT_KEY = 'USER_VISIT_COUNT'; export const USER_DISMISSED_ANNOYING_NOTIFICATION_POPUP_KEY = 'USER_DISMISSED_ANNOYING_NOTIFICATION_POPUP_KEY'; + +export const DYNAMIC_PADDING_VALUE = '320px';