mirror of
https://github.com/openmhz/trunk-server.git
synced 2026-11-14 01:37:48 -05:00
fixed up the other windows
This commit is contained in:
@@ -3,45 +3,42 @@ import { Link } from 'react-router-dom'
|
|||||||
import { Container, Header, Divider, Icon, Menu, Button } from "semantic-ui-react";
|
import { Container, Header, Divider, Icon, Menu, Button } from "semantic-ui-react";
|
||||||
|
|
||||||
|
|
||||||
class AboutComponent extends Component {
|
const AboutComponent = () => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Menu fixed="top">
|
||||||
|
<Link to="/"><Menu.Item link><Icon name='arrow left' /> Home</Menu.Item></Link>
|
||||||
|
<Link to="/systems"><Menu.Item link>Listen</Menu.Item></Link>
|
||||||
|
</Menu>
|
||||||
|
<Container text>
|
||||||
|
<Divider horizontal style={{ paddingTop: "4em", paddingBottom: "2em" }}><Header as="h1">About<Icon name='compass' /></Header></Divider>
|
||||||
|
|
||||||
|
<p>I wrote <a href="https://github.com/robotastic/trunk-recorder">Trunk Recorder</a> because I was curious
|
||||||
|
about what my local fire station was up to and I put together the original version of OpenMHz because I
|
||||||
|
figured other people might want to listen to the recordings too.</p>
|
||||||
|
<p>The latest version of this site makes it easy for other people running Trunk Recorder to share their recrodings.
|
||||||
|
I am hoping that making it easier to listen to what our local fire, police and EMS have to go through everyday will
|
||||||
|
lead to greater appreciation for all the work they do that goes largely unseen. I am personally amazed by the number
|
||||||
|
of medical runs the DC's Fire and EMS department has to cover every day, and the shocking number of them that are ODs.</p>
|
||||||
|
|
||||||
|
<p>Each system should have a 30 day archive. This is to help make sure things don't get overloaded and make sure I don't get a crazy bill
|
||||||
|
at the end of the month.</p>
|
||||||
|
<p> - Luke <a href="mailto:luke@robotastic.com?Subject=OpenMHz" target="_top">luke@robotastic.com</a> </p>
|
||||||
|
|
||||||
|
<Divider horizontal style={{ paddingTop: "4em", paddingBottom: "2em" }}><Header as="h2">Open Source</Header></Divider>
|
||||||
|
<p>Have changes you would like to suggest? Want to run your own server for your community? The source code for OpenMHz has been released:</p>
|
||||||
|
<p><a href="https://github.com/openmhz/trunk-server"><Icon name='github' />Trunk Server</a></p>
|
||||||
|
<Divider horizontal style={{ paddingTop: "4em", paddingBottom: "2em" }}><Header as="h2">Support</Header></Divider>
|
||||||
|
<p>If OpenMHz brings you joy, think about becoming a supporter! It will cover hosting costs and help keep me focused on development.</p>
|
||||||
|
|
||||||
|
<p><a href="https://github.com/sponsors/robotastic"><Button>
|
||||||
|
Support
|
||||||
|
</Button></a></p>
|
||||||
|
|
||||||
|
</Container>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
render() {
|
export default AboutComponent;
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<Menu fixed="top">
|
|
||||||
<Link to="/"><Menu.Item link><Icon name='arrow left' /> Home</Menu.Item></Link>
|
|
||||||
<Link to="/systems"><Menu.Item link>Listen</Menu.Item></Link>
|
|
||||||
</Menu>
|
|
||||||
<Container text>
|
|
||||||
<Divider horizontal style={{ paddingTop: "4em", paddingBottom: "2em" }}><Header as="h1">About<Icon name='compass' /></Header></Divider>
|
|
||||||
|
|
||||||
<p>I wrote <a href="https://github.com/robotastic/trunk-recorder">Trunk Recorder</a> because I was curious
|
|
||||||
about what my local fire station was up to and I put together the original version of OpenMHz because I
|
|
||||||
figured other people might want to listen to the recordings too.</p>
|
|
||||||
<p>The latest version of this site makes it easy for other people running Trunk Recorder to share their recrodings.
|
|
||||||
I am hoping that making it easier to listen to what our local fire, police and EMS have to go through everyday will
|
|
||||||
lead to greater appreciation for all the work they do that goes largely unseen. I am personally amazed by the number
|
|
||||||
of medical runs the DC's Fire and EMS department has to cover every day, and the shocking number of them that are ODs.</p>
|
|
||||||
|
|
||||||
<p>Each system should have a 30 day archive. This is to help make sure things don't get overloaded and make sure I don't get a crazy bill
|
|
||||||
at the end of the month.</p>
|
|
||||||
<p> - Luke <a href="mailto:luke@robotastic.com?Subject=OpenMHz" target="_top">luke@robotastic.com</a> </p>
|
|
||||||
|
|
||||||
<Divider horizontal style={{ paddingTop: "4em", paddingBottom: "2em" }}><Header as="h2">Open Source</Header></Divider>
|
|
||||||
<p>Have changes you would like to suggest? Want to run your own server for your community? The source code for OpenMHz has been released:</p>
|
|
||||||
<p><a href="https://github.com/openmhz/trunk-server"><Icon name='github' />Trunk Server</a></p>
|
|
||||||
<Divider horizontal style={{ paddingTop: "4em", paddingBottom: "2em" }}><Header as="h2">Support</Header></Divider>
|
|
||||||
<p>If OpenMHz brings you joy, think about becoming a supporter! It will cover hosting costs and help keep me focused on development.</p>
|
|
||||||
|
|
||||||
<p><a href="https://github.com/sponsors/robotastic"><Button>
|
|
||||||
Support
|
|
||||||
</Button></a></p>
|
|
||||||
|
|
||||||
</Container>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default AboutComponent;
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
import { connect } from "react-redux"
|
|
||||||
import * as callActionCreators from "./call-actions"
|
|
||||||
import { bindActionCreators } from 'redux'
|
|
||||||
import CalendarModal from "./CalendarModal"
|
|
||||||
|
|
||||||
function mapStateToProps(state, props) {
|
|
||||||
return {
|
|
||||||
groups: state.group.items[props.shortName],
|
|
||||||
talkgroups: state.talkgroup.items[props.shortName],
|
|
||||||
talkgroupsIsWaiting: state.talkgroup.isWaiting,
|
|
||||||
groupsIsWaiting: state.group.isWaiting,
|
|
||||||
shortName: props.shortName,
|
|
||||||
activeTab: state.call.filterType,
|
|
||||||
selectedTalkgroups: state.call.filterTalkgroups
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function mapDispatchToProps(dispatch) {
|
|
||||||
return {
|
|
||||||
callActions: bindActionCreators(callActionCreators, dispatch)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export default connect(
|
|
||||||
mapStateToProps,
|
|
||||||
mapDispatchToProps
|
|
||||||
)(CalendarModal)
|
|
||||||
@@ -5,7 +5,7 @@ import MediaPlayer from "./MediaPlayer";
|
|||||||
import FilterModal from "./FilterModal";
|
import FilterModal from "./FilterModal";
|
||||||
import GroupModal from "./GroupModal";
|
import GroupModal from "./GroupModal";
|
||||||
import SupportModal from "./SupportModal";
|
import SupportModal from "./SupportModal";
|
||||||
import CalendarModal from "./CalendarModalContainer";
|
import CalendarModal from "./CalendarModal";
|
||||||
import CallInfo from "./CallInfo";
|
import CallInfo from "./CallInfo";
|
||||||
import ListCalls from "./ListCalls";
|
import ListCalls from "./ListCalls";
|
||||||
import { useSelector, useDispatch } from 'react-redux'
|
import { useSelector, useDispatch } from 'react-redux'
|
||||||
@@ -50,9 +50,7 @@ function CallPlayer(props) {
|
|||||||
|
|
||||||
|
|
||||||
const [autoPlay, setAutoPlay] = useState(true);
|
const [autoPlay, setAutoPlay] = useState(true);
|
||||||
const [addCallScroll, setAddCallScroll] = useState(false);
|
|
||||||
const [currentCall, setCurrentCall] = useState(false);
|
const [currentCall, setCurrentCall] = useState(false);
|
||||||
const [prevScrollHeight, setPrevScrollHeight] = useState(0);
|
|
||||||
const [urlOptions, setUrlOptions] = useState(false);
|
const [urlOptions, setUrlOptions] = useState(false);
|
||||||
const [isPlaying, setIsPlaying] = useState(false);
|
const [isPlaying, setIsPlaying] = useState(false);
|
||||||
const [sidebarOpened, setSidebarOpened] = useState(false);
|
const [sidebarOpened, setSidebarOpened] = useState(false);
|
||||||
@@ -155,13 +153,10 @@ function CallPlayer(props) {
|
|||||||
case 'calls':
|
case 'calls':
|
||||||
const ref = positionRef.current;
|
const ref = positionRef.current;
|
||||||
pageYOffset.current = positionRef.current.clientHeight;
|
pageYOffset.current = positionRef.current.clientHeight;
|
||||||
setPrevScrollHeight(positionRef.current.clientHeight);
|
|
||||||
console.log("Prior to setAddCallScroll, addCallScroll was: " + addCallScroll);
|
|
||||||
setAddCallScroll(true);
|
|
||||||
if (shouldPlayAddCallRef.current) {
|
if (shouldPlayAddCallRef.current) {
|
||||||
setCurrentCall(message);
|
setCurrentCall(message);
|
||||||
}
|
}
|
||||||
console.log("Prior to addCall, addCallScroll was: " + addCallScroll);
|
|
||||||
dispatch(addCall(message));
|
dispatch(addCall(message));
|
||||||
|
|
||||||
console.log("Got: " + message._id);
|
console.log("Got: " + message._id);
|
||||||
@@ -349,7 +344,7 @@ function CallPlayer(props) {
|
|||||||
useLayoutEffect( () => {
|
useLayoutEffect( () => {
|
||||||
const scrollAmount = parseInt(positionRef.current.clientHeight) - parseInt(pageYOffset.current);
|
const scrollAmount = parseInt(positionRef.current.clientHeight) - parseInt(pageYOffset.current);
|
||||||
if (scrollAmount > 0) {
|
if (scrollAmount > 0) {
|
||||||
console.log("useLayoutEffect for callsData - state prevScrollHeight: " + prevScrollHeight + " ref: " + pageYOffset.current + " current: " + positionRef.current.clientHeight + " Scroll Amount: " + scrollAmount)
|
console.log("useLayoutEffect for callsData - ref: " + pageYOffset.current + " current: " + positionRef.current.clientHeight + " Scroll Amount: " + scrollAmount)
|
||||||
window.scrollBy(0, scrollAmount);
|
window.scrollBy(0, scrollAmount);
|
||||||
}
|
}
|
||||||
}, [callsData])
|
}, [callsData])
|
||||||
|
|||||||
@@ -1,69 +0,0 @@
|
|||||||
import { connect } from "react-redux"
|
|
||||||
import * as callActionCreators from "./call-actions"
|
|
||||||
import * as talkgroupActionCreators from "../Talkgroups/talkgroup-actions"
|
|
||||||
import * as groupActionCreators from "../Group/group-actions"
|
|
||||||
import * as systemActionCreators from "../System/system-actions"
|
|
||||||
import { bindActionCreators } from 'redux'
|
|
||||||
import CallPlayer from "./CallPlayer"
|
|
||||||
import {
|
|
||||||
useLocation,
|
|
||||||
useNavigate,
|
|
||||||
useParams
|
|
||||||
} from "react-router-dom";
|
|
||||||
|
|
||||||
// glorious solution here: https://stackoverflow.com/questions/71097375/how-can-i-use-withrouter-in-react-router-v6-for-passing-props
|
|
||||||
function withRouter(Component) {
|
|
||||||
function ComponentWithRouterProp(props) {
|
|
||||||
let location = useLocation();
|
|
||||||
let navigate = useNavigate();
|
|
||||||
let params = useParams();
|
|
||||||
return (
|
|
||||||
<Component
|
|
||||||
{...props}
|
|
||||||
router={{ location, navigate, params }}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ComponentWithRouterProp;
|
|
||||||
}
|
|
||||||
|
|
||||||
function mapStateToProps(state, props) {
|
|
||||||
const shortName = props.router.params.shortName;
|
|
||||||
return {
|
|
||||||
callsById: state.call.byId,
|
|
||||||
callsAllIds: state.call.allIds,
|
|
||||||
oldestCallTime: state.call.oldestCallTime,
|
|
||||||
newestCallTime: state.call.newestCallTime,
|
|
||||||
live: state.call.live,
|
|
||||||
filterDate: state.call.filterDate,
|
|
||||||
filterType: state.call.filterType,
|
|
||||||
filterTalkgroups: state.call.filterTalkgroups,
|
|
||||||
filterGroupId: state.call.filterGroupId,
|
|
||||||
filterStarred: state.call.filterStarred,
|
|
||||||
system: state.system.items.find((item) => item.shortName === shortName),
|
|
||||||
groups: state.group.items[shortName],
|
|
||||||
talkgroups: state.talkgroup.items[shortName],
|
|
||||||
callsIsWaiting: state.call.isWaiting,
|
|
||||||
systemIsWaiting: state.system.isWaiting,
|
|
||||||
talkgroupsIsWaiting: state.talkgroup.isWaiting,
|
|
||||||
groupsIsWaiting: state.group.isWaiting,
|
|
||||||
location: props.router.location,
|
|
||||||
shortName: shortName
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function mapDispatchToProps(dispatch) {
|
|
||||||
return {
|
|
||||||
groupActions: bindActionCreators(groupActionCreators, dispatch),
|
|
||||||
systemActions: bindActionCreators(systemActionCreators, dispatch),
|
|
||||||
callActions: bindActionCreators(callActionCreators, dispatch),
|
|
||||||
talkgroupActions: bindActionCreators(talkgroupActionCreators, dispatch)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export default withRouter(connect(
|
|
||||||
mapStateToProps,
|
|
||||||
mapDispatchToProps
|
|
||||||
)(CallPlayer))
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
import { connect } from "react-redux"
|
|
||||||
import * as talkgroupActionCreators from "../Talkgroups/talkgroup-actions"
|
|
||||||
import * as groupActionCreators from "../Group/group-actions"
|
|
||||||
import * as callActionCreators from "./call-actions"
|
|
||||||
import { bindActionCreators } from 'redux'
|
|
||||||
import FilterModal from "./FilterModal"
|
|
||||||
|
|
||||||
function mapStateToProps(state, props) {
|
|
||||||
return {
|
|
||||||
groups: state.group.items[props.shortName],
|
|
||||||
talkgroups: state.talkgroup.items[props.shortName],
|
|
||||||
talkgroupsIsWaiting: state.talkgroup.isWaiting,
|
|
||||||
groupsIsWaiting: state.group.isWaiting,
|
|
||||||
shortName: props.shortName,
|
|
||||||
activeTab: state.call.filterType,
|
|
||||||
selectedTalkgroups: state.call.filterTalkgroups,
|
|
||||||
selectedGroup: state.call.filterGroupId,
|
|
||||||
filterStarred: state.call.filterStarred
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function mapDispatchToProps(dispatch) {
|
|
||||||
return {
|
|
||||||
groupActions: bindActionCreators(groupActionCreators, dispatch),
|
|
||||||
talkgroupActions: bindActionCreators(talkgroupActionCreators, dispatch),
|
|
||||||
callActions: bindActionCreators(callActionCreators, dispatch)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export default connect(
|
|
||||||
mapStateToProps,
|
|
||||||
mapDispatchToProps
|
|
||||||
)(FilterModal)
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
import { connect } from "react-redux"
|
|
||||||
import * as groupActionCreators from "../Group/group-actions"
|
|
||||||
import * as callActionCreators from "./call-actions"
|
|
||||||
import { bindActionCreators } from 'redux'
|
|
||||||
import GroupModal from "./GroupModal"
|
|
||||||
|
|
||||||
function mapStateToProps(state, props) {
|
|
||||||
return {
|
|
||||||
groups: state.group.items[props.shortName],
|
|
||||||
groupsIsWaiting: state.group.isWaiting,
|
|
||||||
shortName: props.shortName,
|
|
||||||
selectedGroup: state.call.filterGroupId
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function mapDispatchToProps(dispatch) {
|
|
||||||
return {
|
|
||||||
groupActions: bindActionCreators(groupActionCreators, dispatch),
|
|
||||||
callActions: bindActionCreators(callActionCreators, dispatch)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export default connect(
|
|
||||||
mapStateToProps,
|
|
||||||
mapDispatchToProps
|
|
||||||
)(GroupModal)
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { Component } from "react";
|
import React, { Component, useState, useEffect, useRef } from "react";
|
||||||
import { Link } from 'react-router-dom'
|
import { Link } from 'react-router-dom'
|
||||||
import SystemCard from "../System/SystemCard";
|
import SystemCard from "../System/SystemCard";
|
||||||
import "./Main.css";
|
import "./Main.css";
|
||||||
@@ -17,6 +17,9 @@ import {
|
|||||||
|
|
||||||
} from 'semantic-ui-react'
|
} from 'semantic-ui-react'
|
||||||
|
|
||||||
|
import { useSelector, useDispatch } from 'react-redux'
|
||||||
|
import { selectAllSystems, selectActiveSystems, useGetSystemsQuery } 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 */
|
/* Responsive component was removed from Semantic UI. This is discussed here: https://github.com/Semantic-Org/Semantic-UI-React/pull/4008 */
|
||||||
|
|
||||||
const AppMedia = createMedia({
|
const AppMedia = createMedia({
|
||||||
@@ -80,15 +83,14 @@ General flow:
|
|||||||
- componentDidMount(): it will call fetchSystem() this will perform an HTTP request if Systems does not exist, and then add System to Props
|
- componentDidMount(): it will call fetchSystem() this will perform an HTTP request if Systems does not exist, and then add System to Props
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class DesktopContainer extends Component {
|
const DesktopContainer = (props) => {
|
||||||
state = {}
|
const [fixed, setFixed] = useState(false);
|
||||||
|
const hideFixedMenu = () => setFixed(false)
|
||||||
|
const showFixedMenu = () => setFixed(true)
|
||||||
|
|
||||||
hideFixedMenu = () => this.setState({ fixed: false })
|
|
||||||
showFixedMenu = () => this.setState({ fixed: true })
|
const { children } = props
|
||||||
|
|
||||||
render() {
|
|
||||||
const { children } = this.props
|
|
||||||
const { fixed } = this.state
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Media greaterThanOrEqual="tablet">
|
<Media greaterThanOrEqual="tablet">
|
||||||
@@ -128,26 +130,24 @@ class DesktopContainer extends Component {
|
|||||||
</Media>
|
</Media>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
/*
|
/*
|
||||||
DesktopContainer.propTypes = {
|
DesktopContainer.propTypes = {
|
||||||
children: PropTypes.node,
|
children: PropTypes.node,
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
class MobileContainer extends Component {
|
const MobileContainer = (props) => {
|
||||||
state = {}
|
|
||||||
|
|
||||||
handlePusherClick = () => {
|
const [sidebarOpened, setSidebarOpened] = useState(false);
|
||||||
const { sidebarOpened } = this.state
|
|
||||||
|
|
||||||
if (sidebarOpened) this.setState({ sidebarOpened: false })
|
const handlePusherClick = () => {
|
||||||
|
setSidebarOpened(!sidebarOpened)
|
||||||
}
|
}
|
||||||
|
|
||||||
handleToggle = () => this.setState({ sidebarOpened: !this.state.sidebarOpened })
|
const handleToggle = () => setSidebarOpened(!sidebarOpened)
|
||||||
|
|
||||||
render() {
|
|
||||||
const { children } = this.props
|
const { children } = props
|
||||||
const { sidebarOpened } = this.state
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Media lessThan="tablet">
|
<Media lessThan="tablet">
|
||||||
@@ -162,7 +162,7 @@ class MobileContainer extends Component {
|
|||||||
|
|
||||||
<Sidebar.Pusher
|
<Sidebar.Pusher
|
||||||
dimmed={sidebarOpened}
|
dimmed={sidebarOpened}
|
||||||
onClick={this.handlePusherClick}
|
onClick={handlePusherClick}
|
||||||
style={{ minHeight: '100vh' }}
|
style={{ minHeight: '100vh' }}
|
||||||
>
|
>
|
||||||
<div className='relative'>
|
<div className='relative'>
|
||||||
@@ -181,7 +181,7 @@ class MobileContainer extends Component {
|
|||||||
<Container>
|
<Container>
|
||||||
|
|
||||||
<Menu inverted secondary size='large'>
|
<Menu inverted secondary size='large'>
|
||||||
<Menu.Item onClick={this.handleToggle}>
|
<Menu.Item onClick={handleToggle}>
|
||||||
<Icon name='sidebar' />
|
<Icon name='sidebar' />
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
<Menu.Item header>{process.env.REACT_APP_SITE_NAME}</Menu.Item>
|
<Menu.Item header>{process.env.REACT_APP_SITE_NAME}</Menu.Item>
|
||||||
@@ -196,7 +196,6 @@ class MobileContainer extends Component {
|
|||||||
</Media>
|
</Media>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
/*
|
/*
|
||||||
MobileContainer.propTypes = {
|
MobileContainer.propTypes = {
|
||||||
children: PropTypes.node,
|
children: PropTypes.node,
|
||||||
@@ -210,41 +209,62 @@ const ResponsiveContainer = ({ children }) => (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// ----------------------------------------------------
|
// ----------------------------------------------------
|
||||||
class Main extends Component {
|
const Main = (props) => {
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
this.advanceSystem = this.advanceSystem.bind(this);
|
|
||||||
this.state = { duration: 500, visible: true, currentSystem: 0}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
const [visible, setVisible] = useState(true);
|
||||||
|
const [duration, setDuration] = useState(500);
|
||||||
handleChange = (e, { name, value }) => this.setState({ [name]: value })
|
const [currentSystem, setCurrentSystem] = useState(0);
|
||||||
|
const dispatch = useDispatch();
|
||||||
handleVisibility = () => this.setState({ visible: !this.state.visible })
|
const {data:systems, isSuccess} = useGetSystemsQuery(); //= selectAllSystems();
|
||||||
|
|
||||||
advanceSystem() {
|
|
||||||
var currentSystem = this.state.currentSystem + 1;
|
|
||||||
if (currentSystem > this.props.system.items.length) {
|
|
||||||
currentSystem = 0;
|
|
||||||
}
|
|
||||||
this.setState({ visible: !this.state.visible, currentSystem });
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
this.props.fetchSystems();
|
|
||||||
this.interval = setInterval(() => this.advanceSystem(), 3000);
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount() {
|
|
||||||
clearInterval(this.interval);
|
|
||||||
|
|
||||||
|
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 handleVisibility = () => setVisible(!visible);
|
||||||
|
|
||||||
|
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/36559661/how-can-i-dispatch-from-child-components-in-react-redux
|
||||||
//https://stackoverflow.com/questions/42597602/react-onclick-pass-event-with-parameter
|
//https://stackoverflow.com/questions/42597602/react-onclick-pass-event-with-parameter
|
||||||
render() {
|
|
||||||
|
|
||||||
const system = this.props.system.items[this.state.currentSystem];
|
let system = false;
|
||||||
|
if (isSuccess) {
|
||||||
|
system = systems.systems[currentSystem];
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<style>{mediaStyles}</style>
|
<style>{mediaStyles}</style>
|
||||||
@@ -254,9 +274,9 @@ render() {
|
|||||||
<Grid columns='equal' stackable textAlign='center' style={{ height: '350px', marginRight: '0px' }}>
|
<Grid columns='equal' stackable textAlign='center' style={{ height: '350px', marginRight: '0px' }}>
|
||||||
<Grid.Row textAlign='center'>
|
<Grid.Row textAlign='center'>
|
||||||
<Grid.Column style={{ paddingBottom: '4em', paddingTop: '2em', maxWidth: 450 }}>
|
<Grid.Column style={{ paddingBottom: '4em', paddingTop: '2em', maxWidth: 450 }}>
|
||||||
{ system && (<Transition visible={this.state.visible} animation='pulse' duration={500}>
|
{ system && (<Transition visible={visible} animation='pulse' duration={500}>
|
||||||
|
|
||||||
<SystemCard keepShort={true} system={system} key={system.shortName} onClick={(e) => this.props.changeUrl("/system/" + system.shortName)}/>
|
<SystemCard keepShort={true} system={system} key={system.shortName} onClick={(e) => props.changeUrl("/system/" + system.shortName)}/>
|
||||||
|
|
||||||
</Transition>)}
|
</Transition>)}
|
||||||
</Grid.Column>
|
</Grid.Column>
|
||||||
@@ -351,6 +371,5 @@ render() {
|
|||||||
</>);
|
</>);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Main;
|
export default Main;
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
import { connect } from "react-redux"
|
|
||||||
import * as systemActions from "../System/system-actions"
|
|
||||||
import Main from "./Main"
|
|
||||||
|
|
||||||
function mapStateToProps(state) {
|
|
||||||
return {
|
|
||||||
system: state.system
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export default connect(
|
|
||||||
mapStateToProps,
|
|
||||||
systemActions
|
|
||||||
)(Main)
|
|
||||||
@@ -10,22 +10,15 @@ import {
|
|||||||
Divider
|
Divider
|
||||||
} from "semantic-ui-react";
|
} from "semantic-ui-react";
|
||||||
|
|
||||||
|
import { selectAllSystems, selectActiveSystems, useGetSystemsQuery } from "../features/api/apiSlice";
|
||||||
|
|
||||||
// ----------------------------------------------------
|
// ----------------------------------------------------
|
||||||
class ListSystems extends Component {
|
|
||||||
|
|
||||||
state = {
|
const ListSystems = (props) => {
|
||||||
requestMessage: "",
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
const {data:systems, isSuccess} = useGetSystemsQuery();
|
||||||
this.props.fetchSystems();
|
|
||||||
}
|
|
||||||
|
|
||||||
//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
|
|
||||||
render() {
|
|
||||||
const systems = this.props.system.items;
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Menu fixed="top">
|
<Menu fixed="top">
|
||||||
@@ -35,7 +28,8 @@ class ListSystems extends Component {
|
|||||||
<Container >
|
<Container >
|
||||||
<Divider horizontal style={{paddingTop:"5em", paddingBottom:"2em"}}><Header as="h1">Radio Systems<Icon name='rss' /></Header></Divider>
|
<Divider horizontal style={{paddingTop:"5em", paddingBottom:"2em"}}><Header as="h1">Radio Systems<Icon name='rss' /></Header></Divider>
|
||||||
<Card.Group itemsPerRow={4} stackable={true}>
|
<Card.Group itemsPerRow={4} stackable={true}>
|
||||||
{systems.map((system) =>
|
|
||||||
|
{isSuccess&&systems.systems.map((system) =>
|
||||||
system.active&&<SystemCard system={system} key={system.shortName} onClick={(e) => this.props.changeUrl("/system/" + system.shortName)}/>
|
system.active&&<SystemCard system={system} key={system.shortName} onClick={(e) => this.props.changeUrl("/system/" + system.shortName)}/>
|
||||||
// <SystemCard system={system} key={system.shortName} onClick={(e) => this.props.changeUrl("/system/" + system.shortName)}/>
|
// <SystemCard system={system} key={system.shortName} onClick={(e) => this.props.changeUrl("/system/" + system.shortName)}/>
|
||||||
)}
|
)}
|
||||||
@@ -44,6 +38,6 @@ class ListSystems extends Component {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default ListSystems;
|
export default ListSystems;
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
// Import the RTK Query methods from the React-specific entry point
|
// Import the RTK Query methods from the React-specific entry point
|
||||||
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
|
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
|
||||||
|
import { createSelector} from '@reduxjs/toolkit'
|
||||||
|
|
||||||
|
|
||||||
// Define our single API slice object
|
// Define our single API slice object
|
||||||
export const apiSlice = createApi({
|
export const apiSlice = createApi({
|
||||||
// The cache reducer expects to be added at `state.api` (already default - this is optional)
|
// The cache reducer expects to be added at `state.api` (already default - this is optional)
|
||||||
reducerPath: 'api',
|
reducerPath: 'api',
|
||||||
|
|
||||||
// All of our requests will have URLs starting with '/fakeApi'
|
// All of our requests will have URLs starting with '/fakeApi'
|
||||||
baseQuery: fetchBaseQuery({ baseUrl: process.env.REACT_APP_BACKEND_SERVER }),
|
baseQuery: fetchBaseQuery({ baseUrl: process.env.REACT_APP_BACKEND_SERVER }),
|
||||||
// The "endpoints" represent operations and requests for this server
|
// The "endpoints" represent operations and requests for this server
|
||||||
@@ -25,9 +25,24 @@ export const apiSlice = createApi({
|
|||||||
// The URL for the request is '/fakeApi/posts'
|
// The URL for the request is '/fakeApi/posts'
|
||||||
query: (shortName) => ({ url: `/${shortName}/talkgroups` })
|
query: (shortName) => ({ url: `/${shortName}/talkgroups` })
|
||||||
}),
|
}),
|
||||||
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
export const selectSystemsResult = apiSlice.endpoints.getSystems.select();
|
||||||
|
|
||||||
|
const emptySystems = []
|
||||||
|
|
||||||
|
export const selectAllSystems = createSelector(
|
||||||
|
selectSystemsResult,
|
||||||
|
systemsResult => systemsResult?.data ?? emptySystems
|
||||||
|
)
|
||||||
|
|
||||||
|
export const selectActiveSystems = createSelector(
|
||||||
|
selectAllSystems,
|
||||||
|
(state, userId) => userId,
|
||||||
|
(systems, userId) => systems.find(system => system.active === true)
|
||||||
|
)
|
||||||
|
|
||||||
// Export the auto-generated hook for the `getPosts` query endpoint
|
// Export the auto-generated hook for the `getPosts` query endpoint
|
||||||
export const { useGetGroupsQuery, useGetSystemsQuery, useGetTalkgroupsQuery, useGetCallsQuery } = apiSlice
|
export const { useGetGroupsQuery, useGetSystemsQuery, useGetTalkgroupsQuery } = apiSlice
|
||||||
|
|||||||
@@ -11,10 +11,10 @@ import 'semantic-ui-css/semantic.min.css'
|
|||||||
//import withTracker from './withTracker';
|
//import withTracker from './withTracker';
|
||||||
|
|
||||||
// Main
|
// Main
|
||||||
import MainContainer from "./Main/MainContainer"
|
import Main from "./Main/Main"
|
||||||
|
|
||||||
// System
|
// System
|
||||||
import ListSystemsContainer from "./System/ListSystemsContainer"
|
import ListSystems from "./System/ListSystems"
|
||||||
|
|
||||||
// Call
|
// Call
|
||||||
import CallPlayer from "./Call/CallPlayer"
|
import CallPlayer from "./Call/CallPlayer"
|
||||||
@@ -30,8 +30,8 @@ root.render(
|
|||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
<BrowserRouter>
|
<BrowserRouter>
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route exact path="/" element={<MainContainer/>} />
|
<Route exact path="/" element={<Main/>} />
|
||||||
<Route exact path="/systems" element={<ListSystemsContainer/>} />
|
<Route exact path="/systems" element={<ListSystems/>} />
|
||||||
<Route exact path="/system/:shortName" element={<CallPlayer/>} />
|
<Route exact path="/system/:shortName" element={<CallPlayer/>} />
|
||||||
<Route exact path="/about" element={<AboutComponent/>} />
|
<Route exact path="/about" element={<AboutComponent/>} />
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user