import React, { useState, useEffect, useRef, useMemo } from "react";
import { Link, useNavigate } from 'react-router-dom'
import SystemCard from "../System/SystemCard";
import SupportModal from "../Common/SupportModal";
import "./Main.css";
import { createMedia } from "@artsy/fresnel";
import {
Button,
ButtonGroup,
ButtonContent,
Container,
Divider,
Grid,
Header,
Icon,
Menu,
Segment,
Sidebar,
Loader,
Statistic,
Transition,
} from 'semantic-ui-react'
import { AreaClosed, Line, Bar } from '@visx/shape';
import { curveMonotoneX } from '@visx/curve';
import { GridRows, GridColumns } from '@visx/grid';
import { scaleTime, scaleLinear } from '@visx/scale';
import { withTooltip, Tooltip, TooltipWithBounds, defaultStyles } from '@visx/tooltip';
import { useTooltip, useTooltipInPortal } from '@visx/tooltip';
import { localPoint } from '@visx/event';
import { LinearGradient } from '@visx/gradient';
import { max, extent, bisector } from 'd3-array';
import { timeFormat } from 'd3-time-format';
import { AxisLeft } from '@visx/axis';
import { useDispatch } from 'react-redux'
import { useGetStatsQuery, useGetSystemsQuery, useGetSiteStatsQuery } from "../features/api/apiSlice";
/* Responsive component was removed from Semantic UI. This is discussed here: https://github.com/Semantic-Org/Semantic-UI-React/pull/4008 */
const AppMedia = createMedia({
breakpoints: {
mobile: 320,
tablet: 768,
computer: 992,
largeScreen: 1200,
widescreen: 1920
}
});
const mediaStyles = AppMedia.createMediaStyle();
const { Media } = AppMedia;
/* eslint-disable react/no-multi-comp */
/* Heads up! HomepageHeading uses inline styling, however it's not the best practice. Use CSS or styled components for
* such things.
*/
const HomepageHeading = ({ mobile }) => (
)
/*
HomepageHeading.propTypes = {
mobile: PropTypes.bool,
}
*/
/* Heads up!
* Neither Semantic UI nor Semantic UI React offer a responsive navbar, however, it can be implemented easily.
* It can be more complicated, but you can create really flexible markup.
*/
/*
General flow:
- componentDidMount(): it will call fetchSystem() this will perform an HTTP request if Systems does not exist, and then add System to Props
*/
const DesktopContainer = (props) => {
const [fixed, setFixed] = useState(false);
const hideFixedMenu = () => setFixed(false)
const showFixedMenu = () => setFixed(true)
const { children } = props
return (
{process.env.REACT_APP_SITE_NAME}
Listen
Events
About
{children}
)
}
/*
DesktopContainer.propTypes = {
children: PropTypes.node,
}
*/
const MobileContainer = (props) => {
const [sidebarOpened, setSidebarOpened] = useState(false);
const handlePusherClick = () => {
setSidebarOpened(!sidebarOpened)
}
const handleToggle = () => setSidebarOpened(!sidebarOpened)
const { children } = props
return (
{process.env.REACT_APP_SITE_NAME}
{children}
)
}
/*
MobileContainer.propTypes = {
children: PropTypes.node,
}
*/
const ResponsiveContainer = ({ children }) => (
{children}
{children}
)
const BetterSiteStatsChart = ({siteStats}) => {
//const props = {data};
const background = '#9f0000'; //'#3b6978';
const background2 = '#9f0000'; //'#204051';
const accentColor = '#edffea';
const accentColorDark = '#ff7543'; //#75daad';
const tooltipStyles = {
...defaultStyles,
background,
border: '1px solid white',
color: 'white',
};
const formatDate = timeFormat("%b %d, %H:%MM");
// accessors
const getDate = (d) => {
return d.x;
}
const getCallActivity = (d) => {
return parseInt(d.y);
}
const {
tooltipData,
tooltipLeft,
tooltipTop,
tooltipOpen,
showTooltip,
hideTooltip,
} = useTooltip();
const width = 500;
const height = 250;
const { containerRef, TooltipInPortal } = useTooltipInPortal({
// use TooltipWithBounds
detectBounds: true,
// when tooltip containers are scrolled, this will correctly update the Tooltip position
scroll: true,
})
const bisectDate = bisector((d) => d.x).left;
const getMouseData = (event) => {
const coords = localPoint(event.target.ownerSVGElement, event);
const { x } = coords;
const x0 = dateScale.invert(x);
const index = bisectDate(calls, x0, 1);
const d0 = calls[index - 1];
const d1 = calls[index];
let d = d0;
if (d1 && getDate(d1)) {
d = x0.valueOf() - getDate(d0).valueOf() > getDate(d1).valueOf() - x0.valueOf() ? d1 : d0;
}
return { data: d, coords }
}
const handleMouseOver = (event, datum) => {
const { coords, data } = getMouseData(event);
showTooltip({
tooltipLeft: coords.x,
tooltipTop: callActivityScale(getCallActivity(data)),
tooltipData: data
});
};
// bounds
const margin = { top: 10, right: 0, bottom: 0, left: 25 };
const innerWidth = width - margin.left - margin.right;
const innerHeight = height - margin.top - margin.bottom;
const calls = useMemo(
() => {
let callTotals = [];
const now = new Date();
var MS_PER_MINUTE = 60000;
for (let j = 0; j < siteStats.length; j++) {
let spotsBack = siteStats.length - j;
let time = new Date(now - spotsBack * 15 * MS_PER_MINUTE);
callTotals.push({ y: siteStats[j], x: time });
}
callTotals.sort((a,b) => a.x-b.x);
return callTotals
}, [siteStats]
);
const dateScale = useMemo(
() =>
scaleTime({
range: [margin.left, innerWidth + margin.left],
domain: extent(calls, getDate),
}),
[innerWidth, margin.left, calls],
);
const callActivityScale = useMemo(
() => {
return scaleLinear({
range: [innerHeight + margin.top, margin.top],
domain: [0, (max(calls, getCallActivity) || 0)], // + innerHeight / 3],
nice: true,
})
},
[margin.top, innerHeight, calls],
);
return (
// Set `ref={containerRef}` on the element corresponding to the coordinate system that
// `left/top` (passed to `TooltipInPortal`) are relative to.
dateScale(getDate(d)) ?? 0}
y={(d) => callActivityScale(getCallActivity(d)) ?? 0}
yScale={callActivityScale}
strokeWidth={1}
stroke="url(#area-gradient)"
fill="url(#area-gradient)"
curve={curveMonotoneX}
/>
{tooltipData && (
)}
{tooltipOpen && (
{`${getCallActivity(tooltipData)}`}
{formatDate(getDate(tooltipData))}
)}
)
};
const SiteStatsContainer = () => {
const { data: siteStats, isSuccess: siteStatsSuccess } = useGetSiteStatsQuery();
if (!siteStatsSuccess) {
return Loading Site Stats ;
}
return (
Site Activity
Calls per Minute
);
};
// ----------------------------------------------------
const Main = (props) => {
const [visible, setVisible] = useState(true);
const [currentSystem, setCurrentSystem] = useState(0);
const navigate = useNavigate();
const { data: systems, isSuccess } = useGetSystemsQuery(); //= selectAllSystems();
const { data: siteStats, isSuccess: siteStatsSuccess } = useGetSiteStatsQuery();
function useInterval(callback, delay) {
const savedCallback = useRef();
// Remember the latest callback.
useEffect(() => {
savedCallback.current = callback;
}, [callback]);
// Set up the interval.
useEffect(() => {
function tick() {
savedCallback.current();
}
if (delay !== null) {
let id = setInterval(tick, delay);
return () => clearInterval(id);
}
}, [delay]);
}
const advanceSystem = () => {
if (isSuccess) {
let nextSystem = currentSystem + 1;
if (nextSystem > systems.systems.length) {
nextSystem = 0;
}
setCurrentSystem(nextSystem);
setVisible(!visible);
}
//this.setState({ visible: !this.state.visible, currentSystem });
}
useInterval(() => { advanceSystem() }, 3000)
//https://stackoverflow.com/questions/36559661/how-can-i-dispatch-from-child-components-in-react-redux
//https://stackoverflow.com/questions/42597602/react-onclick-pass-event-with-parameter
let system = false;
if (isSuccess) {
system = systems.systems[currentSystem];
}
return (
<>
{system && (
navigate("/system/" + system.shortName)} />
)}
navigate("/system/" + system.shortName)}>
Listen
Donate
Thank You
} />
Events Podcast
iOS App
{siteStats?.activeSystems || 0}
Active Systems
{siteStats?.totalClients || 0}
People Listening
Every call, on every talkgroup is recorded
Missed a call? Not a problem, everything is archived!
Don't just scan, hear it all!
Most large cities use trunked radio systems to get the most use out of their assigned radio spectrum. With trunked systems, the transmission are constantly hopping to different frequencies. Using a cheap Software Defined Radio (SDR), it is possible capture all of the transmission on a system.
Instead of scanning to a single frequency, SDR capturea wide swathes of spectrum, covering all of the frequencies a system could use.
Get Involved
Want to record your local radio system?
Join us! Record a local radio system and share it with the world. If you have a spare computer, it is easy to get started. With a $25 SDR or two, you can capture an entire system.
It does take a little bit of work to get things setup, but we are here to help and answer questions.
Learn More
>);
}
export default Main;