fixed up the other windows

This commit is contained in:
Luke Berndt
2023-01-16 21:16:13 -05:00
parent 14906865a1
commit 6f50a7b9c8
11 changed files with 141 additions and 295 deletions

View File

@@ -3,45 +3,42 @@ import { Link } from 'react-router-dom'
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() {
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;
export default AboutComponent;

View File

@@ -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)

View File

@@ -5,7 +5,7 @@ import MediaPlayer from "./MediaPlayer";
import FilterModal from "./FilterModal";
import GroupModal from "./GroupModal";
import SupportModal from "./SupportModal";
import CalendarModal from "./CalendarModalContainer";
import CalendarModal from "./CalendarModal";
import CallInfo from "./CallInfo";
import ListCalls from "./ListCalls";
import { useSelector, useDispatch } from 'react-redux'
@@ -50,9 +50,7 @@ function CallPlayer(props) {
const [autoPlay, setAutoPlay] = useState(true);
const [addCallScroll, setAddCallScroll] = useState(false);
const [currentCall, setCurrentCall] = useState(false);
const [prevScrollHeight, setPrevScrollHeight] = useState(0);
const [urlOptions, setUrlOptions] = useState(false);
const [isPlaying, setIsPlaying] = useState(false);
const [sidebarOpened, setSidebarOpened] = useState(false);
@@ -155,13 +153,10 @@ function CallPlayer(props) {
case 'calls':
const ref = positionRef.current;
pageYOffset.current = positionRef.current.clientHeight;
setPrevScrollHeight(positionRef.current.clientHeight);
console.log("Prior to setAddCallScroll, addCallScroll was: " + addCallScroll);
setAddCallScroll(true);
if (shouldPlayAddCallRef.current) {
setCurrentCall(message);
}
console.log("Prior to addCall, addCallScroll was: " + addCallScroll);
dispatch(addCall(message));
console.log("Got: " + message._id);
@@ -349,7 +344,7 @@ function CallPlayer(props) {
useLayoutEffect( () => {
const scrollAmount = parseInt(positionRef.current.clientHeight) - parseInt(pageYOffset.current);
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);
}
}, [callsData])

View File

@@ -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))

View File

@@ -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)

View File

@@ -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)

View File

@@ -1,4 +1,4 @@
import React, { Component } from "react";
import React, { Component, useState, useEffect, useRef } from "react";
import { Link } from 'react-router-dom'
import SystemCard from "../System/SystemCard";
import "./Main.css";
@@ -17,6 +17,9 @@ import {
} 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 */
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
*/
class DesktopContainer extends Component {
state = {}
const DesktopContainer = (props) => {
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 (
<Media greaterThanOrEqual="tablet">
@@ -128,26 +130,24 @@ class DesktopContainer extends Component {
</Media>
)
}
}
/*
DesktopContainer.propTypes = {
children: PropTypes.node,
}
*/
class MobileContainer extends Component {
state = {}
const MobileContainer = (props) => {
handlePusherClick = () => {
const { sidebarOpened } = this.state
const [sidebarOpened, setSidebarOpened] = useState(false);
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 { sidebarOpened } = this.state
const { children } = props
return (
<Media lessThan="tablet">
@@ -162,7 +162,7 @@ class MobileContainer extends Component {
<Sidebar.Pusher
dimmed={sidebarOpened}
onClick={this.handlePusherClick}
onClick={handlePusherClick}
style={{ minHeight: '100vh' }}
>
<div className='relative'>
@@ -181,7 +181,7 @@ class MobileContainer extends Component {
<Container>
<Menu inverted secondary size='large'>
<Menu.Item onClick={this.handleToggle}>
<Menu.Item onClick={handleToggle}>
<Icon name='sidebar' />
</Menu.Item>
<Menu.Item header>{process.env.REACT_APP_SITE_NAME}</Menu.Item>
@@ -196,7 +196,6 @@ class MobileContainer extends Component {
</Media>
)
}
}
/*
MobileContainer.propTypes = {
children: PropTypes.node,
@@ -210,41 +209,62 @@ const ResponsiveContainer = ({ children }) => (
)
// ----------------------------------------------------
class Main extends Component {
constructor(props) {
super(props);
this.advanceSystem = this.advanceSystem.bind(this);
this.state = { duration: 500, visible: true, currentSystem: 0}
}
const Main = (props) => {
handleChange = (e, { name, value }) => this.setState({ [name]: value })
handleVisibility = () => this.setState({ visible: !this.state.visible })
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);
}
const [visible, setVisible] = useState(true);
const [duration, setDuration] = useState(500);
const [currentSystem, setCurrentSystem] = useState(0);
const dispatch = useDispatch();
const {data:systems, isSuccess} = useGetSystemsQuery(); //= selectAllSystems();
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/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 (
<>
<style>{mediaStyles}</style>
@@ -254,9 +274,9 @@ render() {
<Grid columns='equal' stackable textAlign='center' style={{ height: '350px', marginRight: '0px' }}>
<Grid.Row textAlign='center'>
<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>)}
</Grid.Column>
@@ -351,6 +371,5 @@ render() {
</>);
}
}
export default Main;

View File

@@ -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)

View File

@@ -10,22 +10,15 @@ import {
Divider
} from "semantic-ui-react";
import { selectAllSystems, selectActiveSystems, useGetSystemsQuery } from "../features/api/apiSlice";
// ----------------------------------------------------
class ListSystems extends Component {
state = {
requestMessage: "",
}
const ListSystems = (props) => {
componentDidMount() {
this.props.fetchSystems();
}
const {data:systems, isSuccess} = useGetSystemsQuery();
//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 (
<div>
<Menu fixed="top">
@@ -35,7 +28,8 @@ class ListSystems extends Component {
<Container >
<Divider horizontal style={{paddingTop:"5em", paddingBottom:"2em"}}><Header as="h1">Radio Systems<Icon name='rss' /></Header></Divider>
<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)}/>
// <SystemCard system={system} key={system.shortName} onClick={(e) => this.props.changeUrl("/system/" + system.shortName)}/>
)}
@@ -44,6 +38,6 @@ class ListSystems extends Component {
</div>
);
}
}
export default ListSystems;

View File

@@ -1,13 +1,13 @@
// Import the RTK Query methods from the React-specific entry point
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
import { createSelector} from '@reduxjs/toolkit'
// Define our single API slice object
export const apiSlice = createApi({
// The cache reducer expects to be added at `state.api` (already default - this is optional)
reducerPath: 'api',
// All of our requests will have URLs starting with '/fakeApi'
baseQuery: fetchBaseQuery({ baseUrl: process.env.REACT_APP_BACKEND_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'
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 const { useGetGroupsQuery, useGetSystemsQuery, useGetTalkgroupsQuery, useGetCallsQuery } = apiSlice
export const { useGetGroupsQuery, useGetSystemsQuery, useGetTalkgroupsQuery } = apiSlice

View File

@@ -11,10 +11,10 @@ import 'semantic-ui-css/semantic.min.css'
//import withTracker from './withTracker';
// Main
import MainContainer from "./Main/MainContainer"
import Main from "./Main/Main"
// System
import ListSystemsContainer from "./System/ListSystemsContainer"
import ListSystems from "./System/ListSystems"
// Call
import CallPlayer from "./Call/CallPlayer"
@@ -30,8 +30,8 @@ root.render(
<Provider store={store}>
<BrowserRouter>
<Routes>
<Route exact path="/" element={<MainContainer/>} />
<Route exact path="/systems" element={<ListSystemsContainer/>} />
<Route exact path="/" element={<Main/>} />
<Route exact path="/systems" element={<ListSystems/>} />
<Route exact path="/system/:shortName" element={<CallPlayer/>} />
<Route exact path="/about" element={<AboutComponent/>} />