mirror of
https://github.com/owncast/owncast.git
synced 2024-11-22 12:49:37 +03:00
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:
parent
3941bc1a70
commit
3bd45d09f3
4 changed files with 53 additions and 9 deletions
|
@ -22,3 +22,17 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.fadeIn {
|
||||||
|
animation: fadein 2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fadein {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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 />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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>
|
||||||
|
|
Loading…
Reference in a new issue