mirror of
https://github.com/owncast/owncast.git
synced 2024-11-21 12:18:02 +03:00
a bit of refactor, use context for overall broacast status; move files around for routing
This commit is contained in:
parent
2b278c45e1
commit
a062856726
15 changed files with 406 additions and 79 deletions
BIN
web/favicon.ico
Normal file
BIN
web/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.9 KiB |
|
@ -10,6 +10,7 @@
|
|||
"dependencies": {
|
||||
"@ant-design/icons": "^4.2.2",
|
||||
"antd": "^4.6.6",
|
||||
"classnames": "^2.2.6",
|
||||
"d3-scale": "^3.2.3",
|
||||
"d3-time-format": "^3.0.0",
|
||||
"next": "9.5.3",
|
||||
|
|
|
@ -1,15 +1,21 @@
|
|||
// import 'antd/dist/antd.css';
|
||||
// import '../styles/globals.scss'
|
||||
|
||||
import 'antd/dist/antd.dark.css';
|
||||
import 'antd/dist/antd.compact.css';
|
||||
|
||||
import "../styles/globals.scss";
|
||||
|
||||
import { AppProps } from 'next/app'
|
||||
import { AppProps } from 'next/app';
|
||||
import BroadcastStatusProvider from './utils/broadcast-status-context';
|
||||
import MainLayout from './components/main-layout';
|
||||
|
||||
|
||||
function App({ Component, pageProps }: AppProps) {
|
||||
return <Component {...pageProps} />
|
||||
return (
|
||||
<BroadcastStatusProvider>
|
||||
<MainLayout>
|
||||
<Component {...pageProps} />
|
||||
</MainLayout>
|
||||
</BroadcastStatusProvider>
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
export default App
|
||||
export default App;
|
18
web/pages/broadcast-info.tsx
Normal file
18
web/pages/broadcast-info.tsx
Normal file
|
@ -0,0 +1,18 @@
|
|||
import React, { useContext } from 'react';
|
||||
import { BroadcastStatusContext } from './utils/broadcast-status-context';
|
||||
|
||||
|
||||
export default function BroadcastInfo() {
|
||||
const context = useContext(BroadcastStatusContext);
|
||||
const { broadcaster } = context || {};
|
||||
const { remoteAddr, time, streamDetails } = broadcaster || {};
|
||||
|
||||
return (
|
||||
<div style={{border: '1px solid green', width: '100%'}}>
|
||||
<h2>Broadcast Info</h2>
|
||||
<p>Remote Address: {remoteAddr}</p>
|
||||
<p>Time: {(new Date(time)).toLocaleTimeString()}</p>
|
||||
<p>Stream Details: {JSON.stringify(streamDetails)}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
|
||||
|
||||
export default function BroadcastInfo(props) {
|
||||
const { remoteAddr, streamDetails, time } = props;
|
||||
|
||||
return (
|
||||
<div style={{border: '1px solid green', width: '100%'}}>
|
||||
<h2>Broadcast Info</h2>
|
||||
<p>Remote Address: {remoteAddr}</p>
|
||||
<p>Time: {(new Date(time)).toLocaleTimeString()}</p>
|
||||
<p>Stream Details: {JSON.stringify(streamDetails)}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
85
web/pages/components/logo.tsx
Normal file
85
web/pages/components/logo.tsx
Normal file
|
@ -0,0 +1,85 @@
|
|||
import React from 'react';
|
||||
import adminStyles from '../../styles/styles.module.css';
|
||||
|
||||
export default function Logo() {
|
||||
return (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 95.68623352050781 104.46271514892578" className={adminStyles.logoSVG}>
|
||||
<g transform="matrix(1 0 0 1 -37.08803939819336 -18.940391540527344)">
|
||||
<g>
|
||||
<g>
|
||||
<g>
|
||||
<g transform="matrix(1.0445680396949917 0 0 1.0445679172996596 36.34559138380523 18.877718021903796)">
|
||||
<g transform="matrix(1 0 0 1 0 0)">
|
||||
|
||||
<defs>
|
||||
<linearGradient x1="0" y1="0" x2="0" y2="1" id="gradient120" gradientTransform="rotate(-90 .5 .5)">
|
||||
<stop offset="0" stopColor="#1f2022" stopOpacity="1"/>
|
||||
<stop offset="1" stopColor="#635e69" stopOpacity="1"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<path fill="url(#gradient120)" d="M91.5 75.35Q93.05 71.15 91.65 67.7 90.35 64.5 86.65 62.3 83.2 60.3 78.3 59.4 73.85 58.6 68.6 58.7 63.55 58.85 58.8 59.8 54.25 60.75 50.8 62.2 47.4 63.65 45.5 65.35 43.6 67.15 43.5 69.05 43.35 71.3 45.8 73.9 48.05 76.3 52.1 78.6 56.15 80.9 61.05 82.55 66.3 84.3 71.4 84.8 74.7 85.1 77.55 84.9 80.65 84.6 83.3 83.6 86.15 82.5 88.15 80.55 90.4 78.4 91.5 75.35M70.6 67.5Q72.3 68.4 73.1 69.7 73.9 71.15 73.45 73 73.1 74.3 72.3 75.25 71.55 76.1 70.3 76.6 69.25 77.05 67.75 77.25 66.3 77.4 64.85 77.3 62.3 77.15 59.25 76.3 56.6 75.5 54.15 74.3 51.9 73.2 50.45 72 49.05 70.75 49.1 69.8 49.2 69 50.25 68.25 51.3 67.55 53.15 67 55 66.4 57.25 66.1 59.8 65.8 62.1 65.8 64.65 65.85 66.7 66.2 68.9 66.65 70.6 67.5Z"/>
|
||||
</g>
|
||||
<g transform="matrix(1 0 0 1 0 0)">
|
||||
<defs>
|
||||
<linearGradient x1="0" y1="0" x2="0" y2="1" id="gradient121" gradientTransform="rotate(-180 .5 .5)">
|
||||
<stop offset="0" stopColor="#2087e2" stopOpacity="1"/>
|
||||
<stop offset="1" stopColor="#b63fff" stopOpacity="1"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<path fill="url(#gradient121)" d="M66.6 15.05Q66.4 9.65 63.9 6.05 61.25 2.1 56.1 0.65 54.95 0.3 53.65 0.15 52.5 0 51.3 0.1 50.2 0.1 49.1 0.35 48.15 0.55 47 1 43.3 2.45 40.3 6.1 37.5 9.4 35.5 14.3 33.75 18.45 32.7 23.4 31.7 28.05 31.35 32.85 31.05 37.2 31.3 41.2 31.6 45.15 32.4 48.35 34 54.9 37.3 56.4 37.6 56.55 37.9 56.65L39.2 56.85Q39.45 56.85 39.95 56.8 42.05 56.6 44.7 55.05 47.25 53.5 50.05 50.8 53.05 47.9 55.85 44.05 58.8 40.05 61.1 35.6 63.8 30.35 65.25 25.3 66.75 19.75 66.6 15.05M47.55 23.15Q48.05 23.25 48.4 23.4 52.45 24.8 52.55 29.85 52.6 34 50 39.4 47.85 43.9 44.85 47.3 42.05 50.5 40.15 50.7L39.9 50.75 39.45 50.7 39.2 50.6Q37.8 49.95 37.25 46.35 36.7 42.7 37.3 38 37.95 32.75 39.75 28.8 41.9 24.1 45.05 23.25 45.6 23.1 45.85 23.1 46.25 23.05 46.65 23.05 47.05 23.05 47.55 23.15Z"/>
|
||||
</g>
|
||||
<g transform="matrix(1 0 0 1 0 0)">
|
||||
<defs>
|
||||
<linearGradient x1="0" y1="0" x2="0" y2="1" id="gradient122" gradientTransform="rotate(-90 .5 .5)">
|
||||
<stop offset="0" stopColor="#100f0f" stopOpacity="1"/>
|
||||
<stop offset="1" stopColor="#49261F" stopOpacity="1"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<path fill="url(#gradient122)" d="M2.7 33.6Q2.1 34.4 1.7 35.35 1.25 36.5 1.05 37.7 0 42.6 2.2 47.2 4 51 8 54.35 11.55 57.3 16 59.15 20.5 61 23.85 60.85 24.5 60.85 25.25 60.7 26 60.55 26.5 60.3 27 60.05 27.45 59.65 27.9 59.25 28.15 58.75 29.35 56.45 27.5 51.65 25.6 47 21.75 42.1 17.75 37 13.4 34.05 8.7 30.9 5.45 31.7 4.65 31.9 3.95 32.4 3.25 32.85 2.7 33.6M10.1 43.55Q10.35 43.1 10.6 42.85 10.85 42.6 11.2 42.4 11.6 42.25 11.9 42.2 13.5 41.9 15.95 43.6 18.15 45.05 20.35 47.7 22.35 50.1 23.55 52.4 24.7 54.75 24.25 55.7 24.15 55.9 24 56 23.85 56.2 23.65 56.25 23.55 56.35 23.25 56.4L22.7 56.5Q21.1 56.6 18.55 55.6 16.05 54.6 13.85 52.95 11.5 51.2 10.35 49.15 9.05 46.8 9.75 44.45 9.9 43.95 10.1 43.55Z"/>
|
||||
</g>
|
||||
<g transform="matrix(1 0 0 1 0 0)">
|
||||
<defs>
|
||||
<linearGradient x1="0" y1="0" x2="0" y2="1" id="gradient123" gradientTransform="rotate(-180 .5 .5)">
|
||||
<stop offset="0" stopColor="#222020" stopOpacity="1"/>
|
||||
<stop offset="1" stopColor="#49261F" stopOpacity="1"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<path fill="url(#gradient123)" d="M34.95 74.2L34.75 74.2Q33.2 74.15 31.9 75.25 30.7 76.3 29.85 78.25 29.1 80 28.8 82.2 28.5 84.4 28.7 86.65 29.1 91.4 31.5 94.7 34.3 98.5 39.3 99.7L39.4 99.7 39.7 99.8 39.85 99.8Q45.3 100.85 47.15 97.75 48 96.3 48 94.05 47.95 91.9 47.2 89.35 46.45 86.75 45.1 84.15 43.75 81.5 42.05 79.35 40.25 77.1 38.45 75.75 36.55 74.35 34.95 74.2M33.55 80.4Q34.35 78.2 35.6 78.3L35.65 78.3Q36.9 78.45 38.6 80.9 40.3 83.35 41.15 86.05 42.1 89 41.55 90.75 40.9 92.6 38.35 92.25L38.3 92.25 38.25 92.2 38.1 92.2Q35.6 91.7 34.25 89.6 33.1 87.7 32.95 85 32.8 82.35 33.55 80.4Z"/>
|
||||
</g>
|
||||
<g transform="matrix(0.9999999999999999 0 0 1 0 5.684341886080802e-14)">
|
||||
<defs>
|
||||
<linearGradient x1="0" y1="0" x2="0" y2="1" id="gradient124" gradientTransform="rotate(-180 .5 .5)"> <stop offset="0" stopColor="#1e1c1c" stopOpacity="1"/>
|
||||
<stop offset="1" stopColor="#49261F" stopOpacity="1"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<path fill="url(#gradient124)" d="M22.7 69.65Q22.25 69.3 21.6 69.05 20.95 68.8 20.25 68.7 19.6 68.55 18.85 68.5 16.7 68.45 14.65 69.15 12.65 69.8 11.4 71.1 10.15 72.5 10.2 74.2 10.25 76.05 11.95 78.2 12.4 78.75 13.05 79.4 13.55 79.9 14.2 80.3 14.7 80.6 15.3 80.85 16 81.1 16.4 81.1 18.2 81.35 19.9 80.35 21.55 79.4 22.75 77.65 24 75.85 24.3 73.95 24.6 71.85 23.55 70.5 23.15 70 22.7 69.65M21.7 71.7Q22.15 72.3 21.9 73.3 21.7 74.25 21 75.25 20.3 76.2 19.4 76.75 18.45 77.35 17.55 77.25L17 77.15Q16.7 77.05 16.45 76.85 16.25 76.75 15.9 76.45 15.7 76.25 15.4 75.9 14.5 74.75 14.7 73.8 14.8 72.95 15.75 72.3 16.6 71.7 17.8 71.4 19 71.1 20.1 71.15L20.65 71.2 21.1 71.3Q21.3 71.4 21.45 71.5L21.7 71.7Z"/>
|
||||
</g>
|
||||
<g transform="matrix(1 0 0 1 0 0)">
|
||||
<defs>
|
||||
<linearGradient x1="0" y1="0" x2="0" y2="1" id="gradient125" gradientTransform="rotate(-360 .5 .5)">
|
||||
<stop offset="0" stopColor="#FFFFFF" stopOpacity="0.5"/>
|
||||
<stop offset="1" stopColor="#FFFFFF" stopOpacity="0.2"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<path fill="url(#gradient125)" d="M52.6 19.25Q59.6 19.25 66.2 20.95 66.7 17.8 66.6 15.05 66.4 9.65 63.9 6.05 61.25 2.1 56.1 0.65 54.95 0.3 53.65 0.15 52.5 0 51.3 0.1 50.2 0.1 49.1 0.35 48.15 0.55 47 1 43.3 2.45 40.3 6.1 37.5 9.4 35.5 14.3 33.85 18.3 32.8 22.85 42.25 19.25 52.6 19.25Z"/>
|
||||
</g>
|
||||
<g transform="matrix(1 0 0 1 0 0)">
|
||||
<defs>
|
||||
<linearGradient x1="0" y1="0" x2="0" y2="1" id="gradient126" gradientTransform="rotate(-360 .5 .5)">
|
||||
<stop offset="0" stopColor="#FFFFFF" stopOpacity="0.5"/>
|
||||
<stop offset="1" stopColor="#FFFFFF" stopOpacity="0.2"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<path fill="url(#gradient126)" d="M1.05 37.7Q0 42.6 2.2 47.2 2.95 48.8 4.05 50.25 7.55 41.65 14.4 34.75 14 34.45 13.4 34.05 8.7 30.9 5.45 31.7 4.65 31.9 3.95 32.4 3.25 32.85 2.7 33.6 2.1 34.4 1.7 35.35 1.25 36.5 1.05 37.7Z"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g transform="matrix(1.219512230276127 0 0 1.2195122143630526 32.82519274395008 88.56945194723018)">
|
||||
<path fill="#000000" fillOpacity="1" d=""/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
}
|
119
web/pages/components/main-layout.tsx
Normal file
119
web/pages/components/main-layout.tsx
Normal file
|
@ -0,0 +1,119 @@
|
|||
import React, { useContext } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/router';
|
||||
import { Layout, Menu } from 'antd';
|
||||
import {
|
||||
SettingOutlined,
|
||||
HomeOutlined,
|
||||
LineChartOutlined,
|
||||
CloseCircleOutlined,
|
||||
PlayCircleFilled,
|
||||
} from '@ant-design/icons';
|
||||
import classNames from 'classNames';
|
||||
|
||||
|
||||
import OwncastLogo from './logo';
|
||||
import { BroadcastStatusContext } from '../utils/broadcast-status-context';
|
||||
|
||||
import adminStyles from '../../styles/styles.module.css';
|
||||
|
||||
export default function MainLayout(props) {
|
||||
const { children } = props;
|
||||
|
||||
const context = useContext(BroadcastStatusContext);
|
||||
const { broadcastActive } = context || {};
|
||||
|
||||
const router = useRouter();
|
||||
const { route } = router || {};
|
||||
|
||||
const { Header, Footer, Content, Sider } = Layout;
|
||||
const { SubMenu } = Menu;
|
||||
|
||||
const statusMessage = broadcastActive ?
|
||||
'Online' : 'Offline';
|
||||
|
||||
const appClass = classNames({
|
||||
'owncast-layout': true,
|
||||
[adminStyles.online]: broadcastActive,
|
||||
})
|
||||
return (
|
||||
<Layout className={appClass}>
|
||||
<Sider
|
||||
width={240}
|
||||
style={{
|
||||
overflow: 'auto',
|
||||
height: '100vh',
|
||||
}}
|
||||
>
|
||||
<Menu
|
||||
theme="dark"
|
||||
defaultSelectedKeys={[route.substring(1)]}
|
||||
defaultOpenKeys={['current-stream-menu', 'utilities-menu']}
|
||||
mode="inline"
|
||||
>
|
||||
<h1 className={adminStyles.owncastTitleContainer}>
|
||||
<span className={adminStyles.logoContainer}>
|
||||
<OwncastLogo />
|
||||
</span>
|
||||
<span className={adminStyles.owncastTitle}>Owncast Admin</span>
|
||||
</h1>
|
||||
<Menu.Item key="home" icon={<HomeOutlined />}>
|
||||
<Link href="/index2">Home</Link>
|
||||
</Menu.Item>
|
||||
|
||||
<SubMenu key="current-stream-menu" icon={<LineChartOutlined />} title="Stream Details">
|
||||
<Menu.Item key="hardware-info">
|
||||
<Link href="/hardware-info">Hardware</Link>
|
||||
</Menu.Item>
|
||||
<Menu.Item key="broadcast-info">
|
||||
<Link href="/broadcast-info">Broadcaster Info</Link>
|
||||
</Menu.Item>
|
||||
<Menu.Item key="viewer-info">
|
||||
<Link href="/viewer-info">Viewers</Link>
|
||||
</Menu.Item>
|
||||
<Menu.Item key="connected-clients">
|
||||
<Link href="/connected-clients">Connected Clients</Link>
|
||||
</Menu.Item>
|
||||
{ broadcastActive ? (
|
||||
<Menu.Item key="disconnect-stream" icon={<CloseCircleOutlined />}>
|
||||
<Link href="/disconnect-stream">Disconnect Stream...</Link>
|
||||
</Menu.Item>
|
||||
) : null}
|
||||
</SubMenu>
|
||||
|
||||
<SubMenu key="utilities-menu" icon={<SettingOutlined />} title="Utilities">
|
||||
<Menu.Item key="update-server-config">
|
||||
<Link href="/update-server-config">Update Server Configuration</Link>
|
||||
</Menu.Item>
|
||||
<Menu.Item key="update-stream-key">
|
||||
<Link href="/update-stream-key">Change Stream Key</Link>
|
||||
</Menu.Item>
|
||||
</SubMenu>
|
||||
</Menu>
|
||||
</Sider>
|
||||
|
||||
<Layout>
|
||||
<Header className={adminStyles.header}>
|
||||
<div className={adminStyles.statusIndicatorContainer}>
|
||||
<span className={adminStyles.statusIcon}>
|
||||
<PlayCircleFilled />
|
||||
</span>
|
||||
<span className={adminStyles.statusLabel}>
|
||||
{statusMessage}
|
||||
</span>
|
||||
</div>
|
||||
</Header>
|
||||
<Content className={adminStyles.contentMain}>
|
||||
{children}
|
||||
</Content>
|
||||
|
||||
<Footer style={{ textAlign: 'center' }}><a href="https://owncast.online/">About Owncast</a></Footer>
|
||||
</Layout>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
|
||||
MainLayout.propTypes = {
|
||||
children: PropTypes.element.isRequired,
|
||||
};
|
|
@ -1,7 +1,7 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import { Table } from 'antd';
|
||||
|
||||
import { CONNECTED_CLIENTS, fetchData, FETCH_INTERVAL } from '../utils/apis';
|
||||
import { CONNECTED_CLIENTS, fetchData, FETCH_INTERVAL } from './utils/apis';
|
||||
|
||||
/*
|
||||
geo data looks like this
|
||||
|
@ -71,7 +71,6 @@ export default function HardwareInfo() {
|
|||
},
|
||||
];
|
||||
|
||||
console.log({clients})
|
||||
return (
|
||||
<div>
|
||||
<h2>Connected Clients</h2>
|
|
@ -1,5 +1,5 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import { HARDWARE_STATS, fetchData, FETCH_INTERVAL } from '../utils/apis';
|
||||
import { HARDWARE_STATS, fetchData, FETCH_INTERVAL } from './utils/apis';
|
||||
|
||||
export default function HardwareInfo() {
|
||||
const [hardwareStatus, setHardwareStatus] = useState({});
|
|
@ -1,12 +0,0 @@
|
|||
import React from 'react';
|
||||
|
||||
import adminStyles from './components/styles.module.css';
|
||||
|
||||
|
||||
export default function HomeView(props) {
|
||||
return (
|
||||
<div>
|
||||
< pick something
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -1,43 +1,10 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import { BROADCASTER, fetchData, FETCH_INTERVAL } from './utils/apis';
|
||||
import MainLayout from './components/main-layout';
|
||||
import Home from './home';
|
||||
import React from 'react';
|
||||
|
||||
export default function Admin() {
|
||||
const [broadcasterStatus, setBroadcasterStatus] = useState({});
|
||||
const [count, setCount] = useState(0);
|
||||
|
||||
const getBroadcastStatus = async () => {
|
||||
try {
|
||||
const result = await fetchData(BROADCASTER);
|
||||
const broadcastActive = !!result.broadcaster;
|
||||
|
||||
console.log("====",{count, result})
|
||||
|
||||
setBroadcasterStatus({ ...result, broadcastActive });
|
||||
setCount(c => c + 1);
|
||||
|
||||
} catch (error) {
|
||||
setBroadcasterStatus({ ...broadcasterStatus, message: error.message });
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
let getStatusIntervalId = null;
|
||||
|
||||
getBroadcastStatus();
|
||||
getStatusIntervalId = setInterval(getBroadcastStatus, FETCH_INTERVAL);
|
||||
|
||||
// returned function will be called on component unmount
|
||||
return () => {
|
||||
clearInterval(getStatusIntervalId);
|
||||
}
|
||||
}, [])
|
||||
|
||||
|
||||
export default function AdminHome() {
|
||||
return (
|
||||
<MainLayout {...broadcasterStatus} >
|
||||
<Home />
|
||||
</MainLayout>
|
||||
<div>
|
||||
< pick something<br />
|
||||
Home view. pretty pictures. Rainbows. Kittens.
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
40
web/pages/update-server-config.tsx
Normal file
40
web/pages/update-server-config.tsx
Normal file
|
@ -0,0 +1,40 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import { SERVER_CONFIG, fetchData, FETCH_INTERVAL } from './utils/apis';
|
||||
|
||||
export default function ServerConfig() {
|
||||
const [clients, setClients] = useState({});
|
||||
|
||||
const getInfo = async () => {
|
||||
try {
|
||||
const result = await fetchData(SERVER_CONFIG);
|
||||
console.log("viewers result", result)
|
||||
|
||||
setClients({ ...result });
|
||||
|
||||
} catch (error) {
|
||||
setClients({ ...clients, message: error.message });
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
let getStatusIntervalId = null;
|
||||
|
||||
getInfo();
|
||||
getStatusIntervalId = setInterval(getInfo, FETCH_INTERVAL);
|
||||
|
||||
// returned function will be called on component unmount
|
||||
return () => {
|
||||
clearInterval(getStatusIntervalId);
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h2>Server Config</h2>
|
||||
<p>Display this data all pretty, most things will be editable in the future, not now.</p>
|
||||
<div style={{border: '1px solid pink', height: '300px', width: '100%', overflow:'auto'}}>
|
||||
{JSON.stringify(clients)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
51
web/pages/utils/broadcast-status-context.tsx
Normal file
51
web/pages/utils/broadcast-status-context.tsx
Normal file
|
@ -0,0 +1,51 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { BROADCASTER, fetchData, FETCH_INTERVAL } from './apis';
|
||||
|
||||
const initialState = {
|
||||
broadcastActive: false,
|
||||
message: '',
|
||||
broadcaster: null,
|
||||
};
|
||||
|
||||
export const BroadcastStatusContext = React.createContext(initialState);
|
||||
|
||||
const BroadcastStatusProvider = ({ children }) => {
|
||||
const [broadcasterStatus, setBroadcasterStatus] = useState(initialState);
|
||||
|
||||
const getBroadcastStatus = async () => {
|
||||
try {
|
||||
const result = await fetchData(BROADCASTER);
|
||||
const broadcastActive = !!result.broadcaster;
|
||||
setBroadcasterStatus({ ...result, broadcastActive });
|
||||
|
||||
} catch (error) {
|
||||
setBroadcasterStatus({ ...broadcasterStatus, message: error.message });
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
let getStatusIntervalId = null;
|
||||
|
||||
getBroadcastStatus();
|
||||
getStatusIntervalId = setInterval(getBroadcastStatus, FETCH_INTERVAL);
|
||||
|
||||
// returned function will be called on component unmount
|
||||
return () => {
|
||||
clearInterval(getStatusIntervalId);
|
||||
}
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<BroadcastStatusContext.Provider value={broadcasterStatus}>
|
||||
{children}
|
||||
</BroadcastStatusContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
BroadcastStatusProvider.propTypes = {
|
||||
children: PropTypes.element.isRequired,
|
||||
};
|
||||
|
||||
export default BroadcastStatusProvider;
|
|
@ -1,7 +1,8 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import {timeFormat} from 'd3-time-format';
|
||||
import { LineChart, XAxis, YAxis, Line, Tooltip } from 'recharts';
|
||||
import { VIEWERS_OVER_TIME, fetchData } from '../utils/apis';
|
||||
|
||||
import { VIEWERS_OVER_TIME, fetchData } from './utils/apis';
|
||||
|
||||
const FETCH_INTERVAL = 5 * 60 * 1000; // 5 mins
|
||||
|
||||
|
@ -65,8 +66,6 @@ export default function ViewersOverTime() {
|
|||
/>
|
||||
</LineChart>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
);
|
||||
}
|
69
web/styles/styles.module.css
Normal file
69
web/styles/styles.module.css
Normal file
|
@ -0,0 +1,69 @@
|
|||
|
||||
.logoSVG {
|
||||
height: 2rem;
|
||||
width: 2rem;
|
||||
}
|
||||
|
||||
.owncastTitleContainer {
|
||||
padding: 1rem;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
}
|
||||
.logoContainer {
|
||||
background-color: #fff;
|
||||
padding: .35rem;
|
||||
border-radius: 9999px;
|
||||
}
|
||||
.owncastTitle {
|
||||
display: inline-block;
|
||||
margin-left: 1rem;
|
||||
color: rgba(203,213,224, 1);
|
||||
font-size: 1.15rem;
|
||||
font-weight: 200;
|
||||
text-transform: uppercase;
|
||||
line-height: normal;
|
||||
letter-spacing: .05em;
|
||||
}
|
||||
|
||||
.contentMain {
|
||||
padding: 3em;
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.statusIndicatorContainer {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
}
|
||||
.statusIcon {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
.statusIcon svg {
|
||||
fill: #ccc;
|
||||
}
|
||||
.statusLabel {
|
||||
color: #fff;
|
||||
text-transform: uppercase;
|
||||
font-size: .75rem;
|
||||
display: inline-block;
|
||||
margin-left: .5rem;
|
||||
color: #ccc;
|
||||
}
|
||||
.online .statusIcon svg {
|
||||
fill: #52c41a;
|
||||
}
|
||||
.online .statusLabel {
|
||||
color: #52c41a;
|
||||
}
|
||||
|
||||
/* //844-227-3943 */
|
Loading…
Reference in a new issue