From 052e72c3df56c85f20f127ff01c91f929b91233b Mon Sep 17 00:00:00 2001 From: Luke Berndt Date: Tue, 17 Jan 2023 22:23:28 -0500 Subject: [PATCH 1/8] add some basic drag and drop --- frontend/src/Call/CallInfo.js | 169 ++++++++++++++++----------- frontend/src/Call/CallItem.js | 19 ++- frontend/src/Call/GroupModal.js | 3 +- frontend/src/Call/PlaylistBuilder.js | 49 ++++++++ 4 files changed, 166 insertions(+), 74 deletions(-) create mode 100644 frontend/src/Call/PlaylistBuilder.js diff --git a/frontend/src/Call/CallInfo.js b/frontend/src/Call/CallInfo.js index 71ae3c6..7ba5b48 100644 --- a/frontend/src/Call/CallInfo.js +++ b/frontend/src/Call/CallInfo.js @@ -1,4 +1,4 @@ -import React from "react"; +import React, { useState } from "react"; import { Header, Divider, @@ -6,81 +6,108 @@ import { Segment, Statistic, Icon, - Menu + Menu, + Tab } from "semantic-ui-react"; - +import PlaylistBuilder from "./PlaylistBuilder" // ---------------------------------------------------- function CallInfo(props) { - let srcList = ""; - let callLength = "-"; - let callFreq = "-"; - let callDate = "-"; - let callTime = "-"; - let talkgroupNum = "-"; - let callDownload = ""; - let callTweet = ""; - if (props.call) { - const currentCall = props.call; - var time = new Date(currentCall.time); - callTime = time.toLocaleTimeString(); - callDate = time.toLocaleDateString(); - if (currentCall.freq) { - var freq = currentCall.freq / 1000000; - callFreq = Math.round(freq * 1000) / 1000; - } - srcList = currentCall.srcList.map((source, index) => {source.src}[{source.pos}]); - callLength = currentCall.len; - talkgroupNum = currentCall.talkgroupNum; - callDownload = currentCall.url; - callTweet = "https://twitter.com/intent/tweet?url="+encodeURIComponent(document.location.origin+props.link)+"&via="+encodeURIComponent("OpenMHz"); + + + let srcList = ""; + let callLength = "-"; + let callFreq = "-"; + let callDate = "-"; + let callTime = "-"; + let talkgroupNum = "-"; + let callDownload = ""; + let callTweet = ""; + if (props.call) { + const currentCall = props.call; + var time = new Date(currentCall.time); + callTime = time.toLocaleTimeString(); + callDate = time.toLocaleDateString(); + if (currentCall.freq) { + var freq = currentCall.freq / 1000000; + callFreq = Math.round(freq * 1000) / 1000; } - return ( -
- -
{props.header}
- - {srcList} - - - - Seconds - {callLength} - - - Talkgroup - {talkgroupNum} - - - - - - - {callTime} - - - - - - {callDate} - - - - - - {callFreq} MHz - - - -
- - Tweet - Download - Link - -
- ); + srcList = currentCall.srcList.map((source, index) => {source.src}[{source.pos}]); + callLength = currentCall.len; + talkgroupNum = currentCall.talkgroupNum; + callDownload = currentCall.url; + callTweet = "https://twitter.com/intent/tweet?url=" + encodeURIComponent(document.location.origin + props.link) + "&via=" + encodeURIComponent("OpenMHz"); } + const [activeTab, setActiveTab] = useState(0); + const handleTabChange = (e, data) => setActiveTab(data.activeIndex); - export default CallInfo; + + + const panes = [ + { + menuItem: 'Call Info', render: () => { + return ( +
+ + + {srcList} + + + + Seconds + {callLength} + + + Talkgroup + {talkgroupNum} + + + + + + + {callTime} + + + + + + {callDate} + + + + + + {callFreq} MHz + + + + + + + Tweet + Download + Link + +
+ ) + } + }, + { + menuItem: 'Playlist', render: () => { + return ( + + ) + } + } + ] + return ( +
+ + +
+ ); +} + +export default CallInfo; diff --git a/frontend/src/Call/CallItem.js b/frontend/src/Call/CallItem.js index dcfd011..9b3c3fb 100644 --- a/frontend/src/Call/CallItem.js +++ b/frontend/src/Call/CallItem.js @@ -34,6 +34,23 @@ const CallItem = (props) => { var rowSelected = {}; let starButton; var starClickable = {}; + + const onDragStart = (event) => { + // It receives a DragEvent + // which inherits properties from + // MouseEvent and Event + // so we can access the element + // through event.currentTarget + + // Later, we'll save + // in a hook variable + // the item being dragged + const callId = event.currentTarget.dataset.callid; + event.dataTransfer.setData("call-id", callId); + console.log("Dragging: " + callId); + + } + if (!starClicked) { starClickable = { link: true }; } @@ -68,7 +85,7 @@ const CallItem = (props) => { talkgroup = props.talkgroups[call.talkgroupNum].description; } return ( - props.onClick({ call: call }, e)} {...rowSelected}> + props.onClick({ call: call }, e)} {...rowSelected} onDragStart={onDragStart} data-callid={call._id}> {call.len} {talkgroup} {time.toLocaleTimeString()} diff --git a/frontend/src/Call/GroupModal.js b/frontend/src/Call/GroupModal.js index 59ebcbe..22c23d2 100644 --- a/frontend/src/Call/GroupModal.js +++ b/frontend/src/Call/GroupModal.js @@ -1,4 +1,4 @@ -import React, { Component, useState } from "react"; +import React, { useState } from "react"; import { useGetGroupsQuery } from '../features/api/apiSlice' import { setGroupFilter } from "../features/callPlayer/callPlayerSlice"; import { useSelector, useDispatch } from 'react-redux' @@ -12,7 +12,6 @@ import { import "./FilterModal.css"; function GroupModal (props) { - const shortName = useSelector((state) => state.callPlayer.shortName); const { data, isError, isLoading, isSuccess,error } = useGetGroupsQuery(props.shortName); const [open, setOpen] = useState(false); const [selectedGroup, setSelectedGroup] = useState(false); diff --git a/frontend/src/Call/PlaylistBuilder.js b/frontend/src/Call/PlaylistBuilder.js new file mode 100644 index 0000000..1ca25cb --- /dev/null +++ b/frontend/src/Call/PlaylistBuilder.js @@ -0,0 +1,49 @@ +import { useSelector, useDispatch } from 'react-redux' + +import { + Header, + Divider, + List, + Segment, + Statistic, + Icon, + Menu, + Tab + } from "semantic-ui-react"; + + function PlaylistBuilder(props) { + + const { loading: callsLoading, data: callsData } = useSelector((state) => state.calls); + + const onDragOver = (event) => { + // It also receives a DragEvent. + // Later, we'll read the position + // of the item from event.currentTarget + // and store the updated list state + + // We need to prevent the default behavior + // of this event, in order for the onDrop + // event to fire. + // It may sound weird, but the default is + // to cancel out the drop. + + event.preventDefault(); + } + + const onDrop = (event) => { + // Here, we will: + // - update the rendered list + // - and reset the DnD state + var callId = event.dataTransfer.getData("call-id"); + console.log("Dropping: " + callId); + console.log("Found: " + callsData.entities[callId].freq); + } + + return ( + +
Placeholder
+
+ ) + } + + export default PlaylistBuilder; \ No newline at end of file From 5025d37c3c1c9cca511fe5bf8c7d87c5eb205a51 Mon Sep 17 00:00:00 2001 From: Luke Berndt Date: Wed, 18 Jan 2023 21:50:14 -0500 Subject: [PATCH 2/8] rebased --- .DS_Store | Bin 6148 -> 6148 bytes frontend/src/Call/CallPlayer.js | 2 - frontend/src/Call/components/CallInfo.js | 113 +++++++++++------- .../Call/{ => components}/PlaylistBuilder.js | 0 4 files changed, 68 insertions(+), 47 deletions(-) rename frontend/src/Call/{ => components}/PlaylistBuilder.js (100%) diff --git a/.DS_Store b/.DS_Store index b4b7e59d95290f6827abaa1fbf4422f9ad3d9431..fb40b1e48ea73f7cfd0c9a9336e77d99a9916ed7 100644 GIT binary patch delta 63 zcmZoMXfc=|&e%RNQEZ}~A|vy}0Ba!8qsf_;QkarWZ3K|a+rBy0Sf?Z_Y-FT delta 59 zcmV-B0L1@ diff --git a/frontend/src/Call/components/CallInfo.js b/frontend/src/Call/components/CallInfo.js index b6440f7..a566028 100644 --- a/frontend/src/Call/components/CallInfo.js +++ b/frontend/src/Call/components/CallInfo.js @@ -1,4 +1,4 @@ -import React from "react"; +import React, { useState } from "react"; import { useCallLink } from "./CallLinks"; import { useGetGroupsQuery, useGetTalkgroupsQuery } from '../../features/api/apiSlice' import { Link, useLocation, useParams, useNavigate } from 'react-router-dom'; @@ -9,9 +9,10 @@ import { Segment, Statistic, Icon, - Menu + Menu, + Tab } from "semantic-ui-react"; - +import PlaylistBuilder from "./PlaylistBuilder" // ---------------------------------------------------- function CallInfo(props) { const { shortName } = useParams(); @@ -44,51 +45,73 @@ function CallInfo(props) { talkgroupNum = currentCall.talkgroupNum; } - const {callLink,callDownload,callTweet} = useCallLink(props.call) + const { callLink, callDownload, callTweet } = useCallLink(props.call) + const [activeTab, setActiveTab] = useState(0); + const handleTabChange = (e, data) => setActiveTab(data.activeIndex); + + const panes = [ + { + menuItem: 'Call Info', render: () => { + return ( +
+ +
{header}
+ + {srcList} + + + + Seconds + {callLength} + + + Talkgroup + {talkgroupNum} + + + + + + + {callTime} + + + + + + {callDate} + + + + + + {callFreq} MHz + + + +
+ + Tweet + Download + Link + +
+ ) + } + }, + { + menuItem: 'Playlist', render: () => { + return ( + + ) + } + } + ] + return (
- -
{header}
- - {srcList} - - - - Seconds - {callLength} - - - Talkgroup - {talkgroupNum} - - - - - - - {callTime} - - - - - - {callDate} - - - - - - {callFreq} MHz - - - -
- - Tweet - Download - Link - +
); } diff --git a/frontend/src/Call/PlaylistBuilder.js b/frontend/src/Call/components/PlaylistBuilder.js similarity index 100% rename from frontend/src/Call/PlaylistBuilder.js rename to frontend/src/Call/components/PlaylistBuilder.js From 395bb0bc110704e4413618e3f051553020fecbb5 Mon Sep 17 00:00:00 2001 From: Luke Berndt Date: Thu, 19 Jan 2023 20:40:11 -0500 Subject: [PATCH 3/8] have it adding calls better --- .../src/Call/components/PlaylistBuilder.js | 95 +++++++++++-------- 1 file changed, 54 insertions(+), 41 deletions(-) diff --git a/frontend/src/Call/components/PlaylistBuilder.js b/frontend/src/Call/components/PlaylistBuilder.js index 1ca25cb..bdccc9d 100644 --- a/frontend/src/Call/components/PlaylistBuilder.js +++ b/frontend/src/Call/components/PlaylistBuilder.js @@ -1,49 +1,62 @@ import { useSelector, useDispatch } from 'react-redux' - +import React, { useState } from "react"; import { - Header, - Divider, - List, - Segment, - Statistic, - Icon, - Menu, - Tab - } from "semantic-ui-react"; + Header, + Divider, + List, + ListItem, + Segment, + Statistic, + Icon, + Menu, + Tab +} from "semantic-ui-react"; - function PlaylistBuilder(props) { +function PlaylistBuilder(props) { - const { loading: callsLoading, data: callsData } = useSelector((state) => state.calls); + const { loading: callsLoading, data: callsData } = useSelector((state) => state.calls); + const [playlist, setPlaylist] = useState([]); - const onDragOver = (event) => { - // It also receives a DragEvent. - // Later, we'll read the position - // of the item from event.currentTarget - // and store the updated list state - - // We need to prevent the default behavior - // of this event, in order for the onDrop - // event to fire. - // It may sound weird, but the default is - // to cancel out the drop. - - event.preventDefault(); - } - - const onDrop = (event) => { - // Here, we will: - // - update the rendered list - // - and reset the DnD state - var callId = event.dataTransfer.getData("call-id"); - console.log("Dropping: " + callId); - console.log("Found: " + callsData.entities[callId].freq); - } + const onDragOver = (event) => { + // It also receives a DragEvent. + // Later, we'll read the position + // of the item from event.currentTarget + // and store the updated list state - return ( - -
Placeholder
-
- ) + // We need to prevent the default behavior + // of this event, in order for the onDrop + // event to fire. + // It may sound weird, but the default is + // to cancel out the drop. + + event.preventDefault(); } - export default PlaylistBuilder; \ No newline at end of file + const onDrop = (event) => { + // Here, we will: + // - update the rendered list + // - and reset the DnD state + var callId = event.dataTransfer.getData("call-id"); + console.log("Dropping: " + callId); + console.log("Found: " + callsData.entities[callId].freq); + setPlaylist([...playlist, callId]); + } + let listItems = ""; + + listItems = playlist.map((callId, index) => { + const call = callsData.entities[callId]; + return(
  • {call.time}
  • ) + }) + + + return ( + +
      + {listItems} +
    • test
    • +
    +
    + ) +} + +export default PlaylistBuilder; \ No newline at end of file From 033c3dcf5d88be3da873ef00eb6d9f43b5cdad3a Mon Sep 17 00:00:00 2001 From: Luke Berndt Date: Sat, 21 Jan 2023 10:29:23 -0500 Subject: [PATCH 4/8] Sending network traffic! --- .../src/Call/components/PlaylistBuilder.js | 142 ++++++++++++++++-- frontend/src/features/api/apiSlice.js | 9 +- 2 files changed, 135 insertions(+), 16 deletions(-) diff --git a/frontend/src/Call/components/PlaylistBuilder.js b/frontend/src/Call/components/PlaylistBuilder.js index bdccc9d..4c0b411 100644 --- a/frontend/src/Call/components/PlaylistBuilder.js +++ b/frontend/src/Call/components/PlaylistBuilder.js @@ -1,4 +1,5 @@ import { useSelector, useDispatch } from 'react-redux' +import { useParams } from 'react-router-dom'; import React, { useState } from "react"; import { Header, @@ -9,13 +10,45 @@ import { Statistic, Icon, Menu, - Tab + Tab, + Table, + Modal, + Button, + Image, + Form } from "semantic-ui-react"; +import { useAddNewEventMutation, useGetTalkgroupsQuery } from '../../features/api/apiSlice' function PlaylistBuilder(props) { - + const { shortName } = useParams(); + const { data: talkgroupsData, isSuccess: isTalkgroupsSuccess } = useGetTalkgroupsQuery(shortName); + const [addNewEvent, { isLoading }] = useAddNewEventMutation() const { loading: callsLoading, data: callsData } = useSelector((state) => state.calls); const [playlist, setPlaylist] = useState([]); + const [open, setOpen] = React.useState(false); + const [title, setTitle] = useState("") + const [description, setDescription] = useState("") + const handleTitleChange = (e, { name, value }) => setTitle(value); + const handleDescriptionChange = (e, { name, value }) => setDescription(value); + + + const compareCalls = (a, b) => { + const aTimestamp = new Date(a.time).getTime() + const bTimestamp = new Date(b.time).getTime() + if (aTimestamp > bTimestamp) { + return -1 + } else { + return 1 + } + } + const addCall = (call) => { + const found = playlist.find(c => c._id == call._id); + if (!found) { + playlist.push(call); + setPlaylist([...playlist.sort(compareCalls)]) + } + + } const onDragOver = (event) => { // It also receives a DragEvent. @@ -32,30 +65,109 @@ function PlaylistBuilder(props) { event.preventDefault(); } + const onDrop = (event) => { // Here, we will: // - update the rendered list // - and reset the DnD state var callId = event.dataTransfer.getData("call-id"); console.log("Dropping: " + callId); - console.log("Found: " + callsData.entities[callId].freq); - setPlaylist([...playlist, callId]); + addCall(callsData.entities[callId]) } let listItems = ""; - listItems = playlist.map((callId, index) => { - const call = callsData.entities[callId]; - return(
  • {call.time}
  • ) - }) - + listItems = playlist.map((call, index) => { + let tgAlpha = ""; + if (talkgroupsData) { + const talkgroup = talkgroupsData.talkgroups[call.talkgroupNum] + tgAlpha = talkgroup.alpha + } + const time = new Date(call.time); + const callTime = time.toLocaleTimeString(); + const callDate = time.toLocaleDateString(); + return ( + {call.len} + {tgAlpha} + {callTime} + + ) + }) + + const submitPlaylist = () => { + setOpen(true) + } + + const handleSubmit = () => { + console.log(title) + setOpen(false) + const callIds = playlist.map( (call) => call._id) + const event = { + title, + description, + callIds + } + addNewEvent(event) + setTitle("") + setDescription("") + setPlaylist([]) + } return ( - -
      - {listItems} -
    • test
    • -
    -
    + <> + setOpen(false)} + onOpen={() => setOpen(true)} + open={open} + > + Submit an Event + + + +
    Event
    +
    + + + + + +
    +
    + + +