feat: floating fading footer (#3008)

Co-authored-by: thisProjects <wibbet@wobbet.com>
Co-authored-by: Gabe Kangas <gabek@real-ity.com>
This commit is contained in:
Nathan 2023-05-12 22:52:54 +01:00 committed by GitHub
parent 3941bc1a70
commit 3bd45d09f3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 53 additions and 9 deletions

View file

@ -22,3 +22,17 @@
} }
} }
} }
.fadeIn {
animation: fadein 2s;
}
@keyframes fadein {
from {
opacity: 0;
}
to {
opacity: 1;
}
}

View file

@ -3,7 +3,7 @@
/* eslint-disable react/no-unescaped-entities */ /* eslint-disable react/no-unescaped-entities */
import { useRecoilValue } from 'recoil'; import { useRecoilValue } from 'recoil';
import Head from 'next/head'; import Head from 'next/head';
import { FC, useEffect, useRef } from 'react'; import { FC, useEffect, useRef, useState } from 'react';
import { Layout } from 'antd'; import { Layout } from 'antd';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import Script from 'next/script'; import Script from 'next/script';
@ -46,6 +46,7 @@ const FatalErrorStateModal = dynamic(
); );
export const Main: FC = () => { export const Main: FC = () => {
const [displayFooter, setDisplayFooter] = useState(false);
const clientConfig = useRecoilValue<ClientConfig>(clientConfigStateAtom); const clientConfig = useRecoilValue<ClientConfig>(clientConfigStateAtom);
const clientStatus = useRecoilValue<ServerStatus>(serverStatusState); const clientStatus = useRecoilValue<ServerStatus>(serverStatusState);
const { name } = clientConfig; const { name } = clientConfig;
@ -54,7 +55,6 @@ export const Main: FC = () => {
const appState = useRecoilValue<AppStateOptions>(appStateAtom); const appState = useRecoilValue<AppStateOptions>(appStateAtom);
const isMobile = useRecoilValue<boolean | undefined>(isMobileAtom); const isMobile = useRecoilValue<boolean | undefined>(isMobileAtom);
const isChatVisible = useRecoilValue<boolean>(isChatVisibleSelector); const isChatVisible = useRecoilValue<boolean>(isChatVisibleSelector);
const layoutRef = useRef<HTMLDivElement>(null); const layoutRef = useRef<HTMLDivElement>(null);
const { chatDisabled } = clientConfig; const { chatDisabled } = clientConfig;
const { videoAvailable } = appState; const { videoAvailable } = appState;
@ -62,12 +62,38 @@ export const Main: FC = () => {
// accounts for sidebar width when online in desktop // accounts for sidebar width when online in desktop
const showChat = online && !chatDisabled && isChatVisible; const showChat = online && !chatDisabled && isChatVisible;
const dynamicPadding = showChat && !isMobile ? '320px' : '0px'; const dynamicPadding = showChat && !isMobile ? '340px' : '0px';
useEffect(() => { useEffect(() => {
setupNoLinkReferrer(layoutRef.current); 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 isProduction = process.env.NODE_ENV === 'production';
const headerText = online ? streamTitle || name : name; const headerText = online ? streamTitle || name : name;
@ -159,7 +185,6 @@ export const Main: FC = () => {
<TitleNotifier name={name} /> <TitleNotifier name={name} />
<Theme /> <Theme />
<Script strategy="afterInteractive" src="/customjavascript" /> <Script strategy="afterInteractive" src="/customjavascript" />
<Layout ref={layoutRef} className={styles.layout}> <Layout ref={layoutRef} className={styles.layout}>
<Header <Header
name={headerText} name={headerText}
@ -171,11 +196,13 @@ export const Main: FC = () => {
{fatalError && ( {fatalError && (
<FatalErrorStateModal title={fatalError.title} message={fatalError.message} /> <FatalErrorStateModal title={fatalError.title} message={fatalError.message} />
)} )}
<div style={{ paddingRight: dynamicPadding }}> <div
<Footer version={version} /> style={displayFooter ? { display: 'flex' } : { display: 'none' }}
className={styles.fadeIn}
>
<Footer version={version} dynamicPadding={dynamicPadding} />
</div> </div>
</Layout> </Layout>
<Noscript /> <Noscript />
</> </>
); );

View file

@ -16,6 +16,8 @@
font-weight: 400; font-weight: 400;
border-top: 1px solid rgba(214, 211, 211, 0.5); border-top: 1px solid rgba(214, 211, 211, 0.5);
width: 100%; width: 100%;
position: fixed;
bottom: 0;
@include screen(tablet) { @include screen(tablet) {
font-size: 10px; font-size: 10px;

View file

@ -3,10 +3,11 @@ import styles from './Footer.module.scss';
export type FooterProps = { export type FooterProps = {
version: string; version: string;
dynamicPadding: string;
}; };
export const Footer: FC<FooterProps> = ({ version }) => ( export const Footer: FC<FooterProps> = ({ version, dynamicPadding }) => (
<footer className={styles.footer} id="footer"> <footer className={styles.footer} id="footer" style={{ paddingRight: dynamicPadding }}>
<span> <span>
Powered by <a href="https://owncast.online">Owncast v{version}</a> Powered by <a href="https://owncast.online">Owncast v{version}</a>
</span> </span>