Merge pull request #77 from CPSC319-Winter-term-2/live-participant-update-bug-fix

status update fixed and warning message added
This commit is contained in:
estebanm123 2022-04-01 19:54:56 -07:00 committed by GitHub
commit 9683a55cd5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 372 additions and 339 deletions

7
package-lock.json generated
View File

@ -4815,6 +4815,7 @@
"dependencies": {
"anymatch": "~3.1.2",
"braces": "~3.0.2",
"fsevents": "~2.3.2",
"glob-parent": "~5.1.2",
"is-binary-path": "~2.1.0",
"is-glob": "~4.0.1",
@ -8032,6 +8033,12 @@
"dev": true,
"dependencies": {
"imagemin": "^7.0.1",
"imagemin-gifsicle": "^7.0.0",
"imagemin-mozjpeg": "^9.0.0",
"imagemin-optipng": "^8.0.0",
"imagemin-pngquant": "^9.0.2",
"imagemin-svgo": "^9.0.0",
"imagemin-webp": "^7.0.0",
"loader-utils": "^2.0.0",
"object-assign": "^4.1.1",
"schema-utils": "^2.7.1"

View File

@ -9,6 +9,7 @@ import CalendarPage from "./components/calendar/CalendarPage";
import { ThemeProvider } from "@emotion/react";
import ProtectedRoute from "./ProtectedRoute";
import MeetingDetails from "./components/meeting-details/MeetingDetails";
import MeetingLink from "./components/meeting-link/MeetingLink";
import Theme from "./Theme";
@ -29,6 +30,7 @@ const App: React.FC = () => {
</Routes>
</Router>
<MeetingDetails />
<MeetingLink />
</ThemeProvider>
);
};

View File

@ -1,14 +1,5 @@
import {
Box,
Button,
Dialog,
DialogContent,
DialogContentText,
DialogTitle,
IconButton,
Typography,
} from "@mui/material";
import React, { useState } from "react";
import { Box, Button, IconButton, Typography } from "@mui/material";
import React from "react";
import PhoneIcon from "@mui/icons-material/Phone";
import AddIcon from "@mui/icons-material/Add";
import UserLite from "../../../../api-bodies/UserLite";
@ -28,6 +19,7 @@ import { MeetingStatus } from "../../../../utils";
import { selectManager, selectMe } from "../../../../redux/slices/usersSlice";
import axios from "../../../../api/axios";
import NewMeeting from "../../../../api-bodies/NewMeeting";
import { open } from "../../../../redux/slices/meetingUrlSlice";
interface Props {
contactInfo: UserLite;
@ -35,7 +27,6 @@ interface Props {
const UpperBody: React.FC<Props> = ({ contactInfo }) => {
const dispatch = useAppDispatch();
const [dialog, setDialog] = useState({ open: false, url: "" });
const userStatus: UserStatus = useAppSelector((state) =>
selectUserStatus(state, contactInfo.uuid)
);
@ -53,18 +44,21 @@ const UpperBody: React.FC<Props> = ({ contactInfo }) => {
: MeetingStatus.NOT_IN_MEETING;
const startCall = async () => {
const start = new Date().toISOString();
let newStart = start.split(".")[0];
newStart += start.includes("Z") ? "Z" : "";
const newMeeting: NewMeeting = {
startTime: "2022-03-30T23:40:00Z",
startTime: newStart,
duration: 30,
topic: `Meeting with ${contactInfo.name}`,
registrantIds: [contactInfo.uuid],
registrantIds: [contactInfo.uuid, me],
};
try {
const response = await axios.post(
`/users/${me}/meetings`,
JSON.stringify(newMeeting)
);
setDialog({ open: true, url: response.data.joinUrl });
dispatch(open(response.data.joinUrl));
} catch (e) {
console.log(e);
}
@ -92,7 +86,9 @@ const UpperBody: React.FC<Props> = ({ contactInfo }) => {
mx: 4,
}}
>
<Typography sx={{ mt: 2 }} variant="h3">{contactInfo.name}</Typography>
<Typography sx={{ mt: 2 }} variant="h3">
{contactInfo.name}
</Typography>
{!favoritesUuids.includes(contactInfo.uuid) ? (
<Button
variant="contained"
@ -151,17 +147,6 @@ const UpperBody: React.FC<Props> = ({ contactInfo }) => {
) : null}
</Box>
</Box>
<Dialog
open={dialog.open}
onClose={() => {
setDialog({ open: false, url: "" });
}}
>
<DialogTitle>Join</DialogTitle>
<DialogContent>
<DialogContentText>{dialog.url}</DialogContentText>
</DialogContent>
</Dialog>
</Box>
);
};

View File

@ -4,7 +4,6 @@ import {
Dialog,
DialogActions,
DialogContent,
DialogContentText,
DialogTitle,
FormControlLabel,
FormGroup,
@ -12,7 +11,7 @@ import {
Typography,
} from "@mui/material";
import React, { useState } from "react";
import { useAppSelector } from "../../redux/hooks";
import { useAppSelector, useAppDispatch } from "../../redux/hooks";
import { selectFavorites } from "../../redux/slices/favoritesSlice";
import { selectTeam, selectUsers } from "../../redux/slices/usersSlice";
import GroupSelect from "../sidebar/GroupSelect";
@ -20,6 +19,7 @@ import axios from "../../api/axios";
import useAuth from "../../hooks/useAuth";
import DetailedMeeting from "../../api-bodies/DetailedMeeting";
import NewMeeting from "../../api-bodies/NewMeeting";
import { open as openMeetingUrl } from "../../redux/slices/meetingUrlSlice";
interface Props {
open: boolean;
@ -30,11 +30,9 @@ const CallFavouritesDialog: React.FC<Props> = ({
open,
handleClose,
}: Props) => {
const [group, setGroup] = useState<string>("Favorites");
const [inputText, setInputText] = useState<string>("");
const [checkedUuids, setCheckedUuids] = useState<string[]>([]);
const [urlDialog, seturlDialog] = useState({ open: false, url: "" });
const handleCheck = (uuid: string, checked: boolean) => {
if (checked) {
@ -44,6 +42,7 @@ const CallFavouritesDialog: React.FC<Props> = ({
}
};
const dispatch = useAppDispatch();
const handleGroupChange = () => {
setCheckedUuids([]);
};
@ -59,23 +58,25 @@ const CallFavouritesDialog: React.FC<Props> = ({
const auth = useAuth();
const handleCall = async(e: React.SyntheticEvent) => {
const handleCall = async () => {
const start = new Date().toISOString();
let newStart = start.split('.')[0];
let newStart = start.split(".")[0];
newStart += start.includes("Z") ? "Z" : "";
const newMeeting: NewMeeting ={
const newMeeting: NewMeeting = {
startTime: newStart,
duration: 60,
topic: "Quick Call to Group",
registrantIds: checkedUuids
registrantIds: checkedUuids,
};
const response = await axios.post(
`/users/${auth?.uuid}/meetings`,
JSON.stringify(newMeeting)
);
const meeting: DetailedMeeting = response?.data;
console.log("create meeting response: " + meeting.startTime + ":" + meeting.duration);
seturlDialog({ open: true, url: meeting.joinUrl });
console.log(
"create meeting response: " + meeting.startTime + ":" + meeting.duration
);
dispatch(openMeetingUrl(response.data.joinUrl));
handleClose();
};
@ -134,17 +135,6 @@ const CallFavouritesDialog: React.FC<Props> = ({
</Button>
</DialogActions>
</Dialog>
<Dialog
open={urlDialog.open}
onClose={() => {
seturlDialog({ open: false, url: "" });
}}
>
<DialogTitle>Join</DialogTitle>
<DialogContent>
<DialogContentText>{urlDialog.url}</DialogContentText>
</DialogContent>
</Dialog>
</>
);
};

View File

@ -9,7 +9,6 @@ import { selectUsers } from "../../redux/slices/usersSlice";
import { formatTimeFromDate } from "../../utils";
import UserLite from "../../api-bodies/UserLite";
const EmptyMeetingsList: React.FC = () => {
return (
<Box
@ -25,9 +24,7 @@ const EmptyMeetingsList: React.FC = () => {
{/* <Box>
</Box> */}
<Box>
<Typography>
No Meetings in Progress
</Typography>
<Typography>No Meetings in Progress</Typography>
</Box>
<Box>
<Typography>
@ -36,7 +33,7 @@ const EmptyMeetingsList: React.FC = () => {
</Box>
</Box>
);
}
};
const MeetingsList: React.FC = () => {
const meetings = useAppSelector(selectMeetings);
@ -68,73 +65,55 @@ const MeetingsList: React.FC = () => {
let i = 1;
return (
<>
<List style={{ maxHeight: "100%", overflow: "auto" }}>
{meetings.map((meeting) => {
const meetingMembers: UserLite[] = [];
participants.forEach((userLite) => {
<List style={{ maxHeight: "100%", overflow: "auto" }}>
{meetings.map((meeting) => {
const meetingMembers: UserLite[] = [];
participants.forEach((userLite) => {
if (
userLite != undefined &&
meeting.liveParticipantIds.includes(userLite.uuid)
) {
meetingMembers.push(userLite);
}
});
const startDate = new Date(meeting.startTime);
const startDatemil = startDate.getTime();
const endDatemil = startDatemil + meeting.duration * 60000;
const endDate = new Date(endDatemil);
const currentDatemil = currentDate.getTime();
// console.log(meetingMembersString);
if (
userLite != undefined &&
meeting.liveParticipantIds.includes(userLite.uuid)
(currentDatemil >= startDatemil && currentDatemil <= endDatemil) ||
meeting.liveParticipantIds.length > 0
) {
meetingMembers.push(userLite);
}
});
const startDate = new Date(meeting.startTime);
const startDatemil = startDate.getTime();
const endDatemil = startDatemil + meeting.duration * 60000;
const endDate = new Date(endDatemil);
const currentDatemil = currentDate.getTime();
const meetingMembersString = () => {
if (meetingMembers.length > 3) {
const lastMeetingClass = meetings.length == i ? " lastMeeting" : "";
i += 1;
return (
"Participants: " +
meetingMembers[0].name +
", " +
meetingMembers[1].name +
", " +
meetingMembers[2].name +
", and " +
(meetingMembers.length - 3).toString() +
" more..."
);
} else {
return (
"Participants: " +
meetingMembers.map((userLite) => " " + userLite.name).toString()
<Meeting
meeting={meeting}
meetingClass={"meeting-" + (i - 1) + lastMeetingClass}
meetingName={meeting.topic}
meetingTime={
formatTimeFromDate(startDate) +
" - " +
formatTimeFromDate(endDate)
}
meetingMembers={meetingMembers
.map((userLite) => " " + userLite.name + " ")
.toString()}
/>
);
}
};
// console.log(meetingMembersString);
if ((currentDatemil >= startDatemil && currentDatemil <= endDatemil) || meeting.liveParticipantIds.length > 0) {
const lastMeetingClass = meetings.length == i ? " lastMeeting" : "";
i += 1;
return (
<Meeting
meeting={meeting}
meetingClass={"meeting-" + (i - 1) + lastMeetingClass}
meetingName={meeting.topic}
meetingTime={
formatTimeFromDate(startDate) +
" - " +
formatTimeFromDate(endDate)
}
meetingMembers={meetingMembers
.map((userLite) => " " + userLite.name + " ")
.toString()}
/>
);
}
})}
</List>
{i === 1 ? <EmptyMeetingsList /> : <></>}
})}
</List>
{i === 1 ? <EmptyMeetingsList /> : <></>}
</>
);
}
};
const MeetingsPanel: React.FC = () => {
return (
<div className="meetings-panel">
<div className="row panel-label">

View File

@ -1,197 +1,194 @@
import {
Button,
Checkbox,
Dialog,
DialogActions,
DialogContent,
DialogContentText,
DialogTitle,
FormControlLabel,
FormGroup,
TextField,
Typography,
} from "@mui/material";
import LocalizationProvider from '@mui/lab/LocalizationProvider';
import AdapterDateFns from '@mui/lab/AdapterDateFns';
import DateTimePicker from '@mui/lab/DateTimePicker';
import React, { useState } from "react";
import { useAppSelector } from "../../redux/hooks";
import { selectFavorites } from "../../redux/slices/favoritesSlice";
import { selectTeam, selectUsers } from "../../redux/slices/usersSlice";
import GroupSelect from "../sidebar/GroupSelect";
import axios from "../../api/axios";
import useAuth from "../../hooks/useAuth";
import DetailedMeeting from "../../api-bodies/DetailedMeeting";
import NewMeeting from "../../api-bodies/NewMeeting";
interface Props {
open: boolean;
handleClose: () => void;
}
const CallScheduledMeetingDialog: React.FC<Props> = ({
open,
handleClose,
}: Props) => {
const [meetingStartDate, setMeetingStartDate] = React.useState<Date | null>(new Date());
const [meetingTopic, setMeetingTopic] = useState<string>("");
const [meetingDuration, setMeetingDuration] = useState<number>(60);
const [group, setGroup] = useState<string>("Favorites");
const [inputText, setInputText] = useState<string>("");
const [checkedUuids, setCheckedUuids] = useState<string[]>([]);
const [urlDialog, seturlDialog] = useState({ open: false, url: "" });
const handleCheck = (uuid: string, checked: boolean) => {
if (checked) {
setCheckedUuids(checkedUuids.concat([uuid]));
} else {
setCheckedUuids(checkedUuids.filter((id) => id != uuid));
}
};
const handleGroupChange = () => {
setCheckedUuids([]);
};
Button,
Checkbox,
Dialog,
DialogActions,
DialogContent,
DialogTitle,
FormControlLabel,
FormGroup,
TextField,
Typography,
} from "@mui/material";
import LocalizationProvider from "@mui/lab/LocalizationProvider";
import AdapterDateFns from "@mui/lab/AdapterDateFns";
import DateTimePicker from "@mui/lab/DateTimePicker";
import React, { useState } from "react";
import { useAppSelector, useAppDispatch } from "../../redux/hooks";
import { selectFavorites } from "../../redux/slices/favoritesSlice";
import { selectTeam, selectUsers } from "../../redux/slices/usersSlice";
import GroupSelect from "../sidebar/GroupSelect";
import axios from "../../api/axios";
import useAuth from "../../hooks/useAuth";
import DetailedMeeting from "../../api-bodies/DetailedMeeting";
import NewMeeting from "../../api-bodies/NewMeeting";
import { open as openMeetingUrl } from "../../redux/slices/meetingUrlSlice";
const handleDateChange = (startDate: Date | null, keyboardInputValue?: string | undefined) => {
setMeetingStartDate(startDate);
};
const favoritesUuids = useAppSelector(selectFavorites);
const teamUuids = useAppSelector(selectTeam);
const groupMembersUuids: string[] =
group === "Favorites" ? favoritesUuids : teamUuids;
const groupMembers = useAppSelector((state) =>
selectUsers(state, groupMembersUuids)
);
const auth = useAuth();
const handleCall = async(e: React.SyntheticEvent) => {
const start = meetingStartDate ? meetingStartDate.toISOString() : new Date().toISOString();
let newStart = start.split('.')[0];
newStart += start.includes("Z") ? "Z" : "";
const newMeeting: NewMeeting ={
startTime: newStart,
duration: meetingDuration,
topic: meetingTopic,
registrantIds: checkedUuids
};
const response = await axios.post(
`/users/${auth?.uuid}/meetings`,
JSON.stringify(newMeeting)
);
const meeting: DetailedMeeting = response?.data;
console.log("create meeting response: " + meeting.startTime + ":" + meeting.duration);
seturlDialog({ open: true, url: meeting.joinUrl });
handleClose();
};
return (
<>
<Dialog onClose={handleClose} open={open} fullWidth maxWidth="sm">
<DialogTitle>Schedule A Meeting</DialogTitle>
<DialogContent dividers>
<LocalizationProvider dateAdapter={AdapterDateFns}>
<DateTimePicker
label="Meeting Start Date&Time"
value={meetingStartDate}
onChange={handleDateChange}
renderInput={(params) => <TextField {...params} />}
/>
</LocalizationProvider>
<TextField
className="meetingDuration-input"
id="outlined-number"
label="Duration in Minutes"
variant="outlined"
value={meetingDuration}
type="number"
onChange={(event) => {
setMeetingDuration(parseInt(event.target.value));
}}
/>
</DialogContent>
<DialogContent dividers>
<TextField
fullWidth
className="meetingTopic-input"
id="outlined-basic"
label="Meeting Topic"
variant="outlined"
value={meetingTopic}
onChange={(event) => {
setMeetingTopic(event.target.value);
}}
/>
</DialogContent>
<DialogContent>
<GroupSelect
group={group}
setGroup={setGroup}
onGroupChange={handleGroupChange}
/>
</DialogContent>
<DialogContent sx={{ height: "40vh" }} dividers>
<TextField
label="Search"
variant="outlined"
placeholder="Person"
fullWidth
sx={{ pb: 1 }}
onChange={(e) => {
setInputText(e.target.value);
}}
/>
<FormGroup>
{groupMembers
.filter((member) =>
member.name.toLowerCase().includes(inputText.toLowerCase())
)
.map((member) => (
<FormControlLabel
key={member.uuid}
label={member.name}
sx={{ pl: 1 }}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
onChange={(e: any) => {
handleCheck(member.uuid, e.target.checked);
}}
control={
<Checkbox
color="success"
checked={checkedUuids.includes(member.uuid)}
/>
}
/>
))}
</FormGroup>
</DialogContent>
<DialogActions>
<Button color="success" onClick={handleCall}>
<Typography variant="button" color="black">
Call
</Typography>
</Button>
</DialogActions>
</Dialog>
<Dialog
open={urlDialog.open}
onClose={() => {
seturlDialog({ open: false, url: "" });
}}
>
<DialogTitle>Join</DialogTitle>
<DialogContent>
<DialogContentText>{urlDialog.url}</DialogContentText>
</DialogContent>
</Dialog>
</>
);
interface Props {
open: boolean;
handleClose: () => void;
}
const CallScheduledMeetingDialog: React.FC<Props> = ({
open,
handleClose,
}: Props) => {
const [meetingStartDate, setMeetingStartDate] = React.useState<Date | null>(
new Date()
);
const [meetingTopic, setMeetingTopic] = useState<string>("");
const [meetingDuration, setMeetingDuration] = useState<number>(60);
const [group, setGroup] = useState<string>("Favorites");
const [inputText, setInputText] = useState<string>("");
const [checkedUuids, setCheckedUuids] = useState<string[]>([]);
const dispatch = useAppDispatch();
const handleCheck = (uuid: string, checked: boolean) => {
if (checked) {
setCheckedUuids(checkedUuids.concat([uuid]));
} else {
setCheckedUuids(checkedUuids.filter((id) => id != uuid));
}
};
export default CallScheduledMeetingDialog;
const handleGroupChange = () => {
setCheckedUuids([]);
};
const handleDateChange = (
startDate: Date | null,
/* eslint-disable @typescript-eslint/no-unused-vars */
keyboardInputValue?: string | undefined
) => {
setMeetingStartDate(startDate);
};
const favoritesUuids = useAppSelector(selectFavorites);
const teamUuids = useAppSelector(selectTeam);
const groupMembersUuids: string[] =
group === "Favorites" ? favoritesUuids : teamUuids;
const groupMembers = useAppSelector((state) =>
selectUsers(state, groupMembersUuids)
);
const auth = useAuth();
const handleCall = async () => {
const start = meetingStartDate
? meetingStartDate.toISOString()
: new Date().toISOString();
let newStart = start.split(".")[0];
newStart += start.includes("Z") ? "Z" : "";
const newMeeting: NewMeeting = {
startTime: newStart,
duration: meetingDuration,
topic: meetingTopic,
registrantIds: checkedUuids,
};
const response = await axios.post(
`/users/${auth?.uuid}/meetings`,
JSON.stringify(newMeeting)
);
const meeting: DetailedMeeting = response?.data;
console.log(
"create meeting response: " + meeting.startTime + ":" + meeting.duration
);
dispatch(openMeetingUrl(response.data.joinUrl));
handleClose();
};
return (
<>
<Dialog onClose={handleClose} open={open} fullWidth maxWidth="sm">
<DialogTitle>Schedule A Meeting</DialogTitle>
<DialogContent dividers>
<LocalizationProvider dateAdapter={AdapterDateFns}>
<DateTimePicker
label="Meeting Start Date&Time"
value={meetingStartDate}
onChange={handleDateChange}
renderInput={(params) => <TextField {...params} />}
/>
</LocalizationProvider>
<TextField
className="meetingDuration-input"
id="outlined-number"
label="Duration in Minutes"
variant="outlined"
value={meetingDuration}
type="number"
onChange={(event) => {
setMeetingDuration(parseInt(event.target.value));
}}
/>
</DialogContent>
<DialogContent dividers>
<TextField
fullWidth
className="meetingTopic-input"
id="outlined-basic"
label="Meeting Topic"
variant="outlined"
value={meetingTopic}
onChange={(event) => {
setMeetingTopic(event.target.value);
}}
/>
</DialogContent>
<DialogContent>
<GroupSelect
group={group}
setGroup={setGroup}
onGroupChange={handleGroupChange}
/>
</DialogContent>
<DialogContent sx={{ height: "40vh" }} dividers>
<TextField
label="Search"
variant="outlined"
placeholder="Person"
fullWidth
sx={{ pb: 1 }}
onChange={(e) => {
setInputText(e.target.value);
}}
/>
<FormGroup>
{groupMembers
.filter((member) =>
member.name.toLowerCase().includes(inputText.toLowerCase())
)
.map((member) => (
<FormControlLabel
key={member.uuid}
label={member.name}
sx={{ pl: 1 }}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
onChange={(e: any) => {
handleCheck(member.uuid, e.target.checked);
}}
control={
<Checkbox
color="success"
checked={checkedUuids.includes(member.uuid)}
/>
}
/>
))}
</FormGroup>
</DialogContent>
<DialogActions>
<Button color="success" onClick={handleCall}>
<Typography variant="button" color="black">
Call
</Typography>
</Button>
</DialogActions>
</Dialog>
</>
);
};
export default CallScheduledMeetingDialog;

View File

@ -1,19 +1,10 @@
import {
AppBar,
Button,
Dialog,
DialogContent,
DialogContentText,
DialogTitle,
IconButton,
Toolbar,
Typography,
} from "@mui/material";
import React, { useState } from "react";
import { AppBar, Button, IconButton, Toolbar, Typography } from "@mui/material";
import React from "react";
import CloseIcon from "@mui/icons-material/Close";
import { useAppDispatch } from "../../../redux/hooks";
import { close } from "../../../redux/slices/meetingDetailsOpenSlice";
import DetailedMeeting from "../../../api-bodies/DetailedMeeting";
import { open as openMeetingUrl } from "../../../redux/slices/meetingUrlSlice";
interface Props {
meeting: DetailedMeeting;
@ -21,7 +12,6 @@ interface Props {
const Header: React.FC<Props> = ({ meeting }) => {
const dispatch = useAppDispatch();
const [dialog, setDialog] = useState({ open: false, url: "" });
return (
<AppBar sx={{ position: "relative" }}>
@ -33,7 +23,7 @@ const Header: React.FC<Props> = ({ meeting }) => {
variant="contained"
color="secondary"
onClick={() => {
setDialog({ open: true, url: meeting.joinUrl });
dispatch(openMeetingUrl(meeting.joinUrl));
}}
>
Join
@ -48,17 +38,6 @@ const Header: React.FC<Props> = ({ meeting }) => {
<CloseIcon />
</IconButton>
</Toolbar>
<Dialog
open={dialog.open}
onClose={() => {
setDialog({ open: false, url: "" });
}}
>
<DialogTitle>Join</DialogTitle>
<DialogContent>
<DialogContentText>{dialog.url}</DialogContentText>
</DialogContent>
</Dialog>
</AppBar>
);
};

View File

@ -0,0 +1,30 @@
import {
Dialog,
DialogContent,
DialogContentText,
DialogTitle,
} from "@mui/material";
import React from "react";
import { useAppSelector, useAppDispatch } from "../../redux/hooks";
import { close, selectMeetingUrl } from "../../redux/slices/meetingUrlSlice";
const warningText =
"For real-time user status updates to function correctly, please ensure you are logged into the Zoom desktop client with your Digital Walkaround Zoom account and that you are not logged into the Zoom.us website in this browser with a Zoom account different from your Digital Walkaround one";
const MeetingLink: React.FC = () => {
const meetingUrl = useAppSelector(selectMeetingUrl);
const dispatch = useAppDispatch();
return (
<Dialog open={meetingUrl.open} onClose={() => dispatch(close())}>
<DialogTitle>Join</DialogTitle>
<DialogContent>
<DialogContentText sx={{ marginBottom: 2 }}>
{"Link: " + meetingUrl.url}
</DialogContentText>
<DialogContentText>{warningText}</DialogContentText>
</DialogContent>
</Dialog>
);
};
export default MeetingLink;

View File

@ -15,6 +15,7 @@ import { useNavigate } from "react-router-dom";
import { selectMe } from "../../redux/slices/usersSlice";
import NewMeeting from "../../api-bodies/NewMeeting";
import axios from "../../api/axios";
import { open as openMeetingUrl } from "../../redux/slices/meetingUrlSlice";
interface Props {
user: UserLite;
@ -38,7 +39,10 @@ const SettingsButton: React.FC<Props> = ({ user, status }: Props) => {
const handleClose = () => {
setAnchorEl(null);
};
const handleSnackbarClose = (event?: React.SyntheticEvent | Event, reason?: string) => {
const handleSnackbarClose = (
event?: React.SyntheticEvent | Event,
reason?: string
) => {
if (reason === "clickaway") {
return;
}
@ -46,18 +50,21 @@ const SettingsButton: React.FC<Props> = ({ user, status }: Props) => {
};
const startCall = async () => {
const start = new Date().toISOString();
let newStart = start.split(".")[0];
newStart += start.includes("Z") ? "Z" : "";
const newMeeting: NewMeeting = {
startTime: "2022-03-30T23:40:00Z",
startTime: newStart,
duration: 30,
topic: `Meeting with ${user.name}`,
registrantIds: [user.uuid],
registrantIds: [user.uuid, me],
};
try {
const response = await axios.post(
`/users/${me}/meetings`,
JSON.stringify(newMeeting)
);
window.open(response.data.joinUrl, "_blank")?.focus();
dispatch(openMeetingUrl(response.data.joinUrl));
} catch (e) {
console.log(e);
}
@ -100,7 +107,6 @@ const SettingsButton: React.FC<Props> = ({ user, status }: Props) => {
>
Add to Favorites
</MenuItem>
)}
{status.inMeeting ? (
<MenuItem
@ -129,10 +135,17 @@ const SettingsButton: React.FC<Props> = ({ user, status }: Props) => {
Create meeting
</MenuItem>
</Menu>
<Snackbar
<Snackbar
anchorOrigin={{ vertical: "bottom", horizontal: "right" }}
open={snackbarOpen} autoHideDuration={3000} onClose={handleSnackbarClose}>
<Alert onClose={handleSnackbarClose} severity="success" sx={{ width: "100%" }}>
open={snackbarOpen}
autoHideDuration={3000}
onClose={handleSnackbarClose}
>
<Alert
onClose={handleSnackbarClose}
severity="success"
sx={{ width: "100%" }}
>
User successfully added to favourites list!
</Alert>
</Snackbar>

View File

@ -19,7 +19,6 @@ export const meetingDetailsOpenSlice = createSlice({
open: (state, action) => {
state.open = true;
state.meeting = action.payload;
console.log(action.payload);
},
close: (state) => {
state.open = false;

View File

@ -0,0 +1,31 @@
import { createSlice } from "@reduxjs/toolkit";
import { RootState } from "../store";
interface MeetingUrlState {
open: boolean;
url: string;
}
const initialState: MeetingUrlState = {
open: false,
url: "",
};
export const meetingUrlSlice = createSlice({
name: "meetingUrl",
initialState,
reducers: {
open: (state, action) => {
state.open = true;
state.url = action.payload;
},
close: (state) => {
state.open = false;
state.url = "";
},
},
});
export const { open, close } = meetingUrlSlice.actions;
export const selectMeetingUrl = (state: RootState) => state.meetingUrl;
export default meetingUrlSlice.reducer;

View File

@ -31,9 +31,28 @@ export const meetingsAndUserStatusSlice = createSlice({
state.isConnected = true;
},
meetingCreated: (state, action) => {
console.log(action.payload);
state.meetings.push(action.payload);
},
userStatusChanged: (state, action) => {
const meetingIndex = state.meetings.findIndex(
(meeting) => meeting.meetingId === action.payload.meetingId
);
const liveParticipantIndex = state.meetings[
meetingIndex
].liveParticipantIds.indexOf(action.payload.userId);
if (action.payload.inMeeting) {
if (liveParticipantIndex === -1)
state.meetings[meetingIndex].liveParticipantIds.push(
action.payload.userId
);
} else {
if (liveParticipantIndex !== -1)
state.meetings[meetingIndex].liveParticipantIds.splice(
liveParticipantIndex,
1
);
}
state.userStatuses[action.payload.userId] = action.payload;
},
},

View File

@ -3,6 +3,7 @@ import meetingDetailsOpenReducer from "./slices/meetingDetailsOpenSlice";
import favoritesReducer from "./slices/favoritesSlice";
import meetingsAndUserStatusReducer from "./slices/meetingsAndUserStatusSlice";
import usersReducer from "./slices/usersSlice";
import meetingUrlReducer from "./slices/meetingUrlSlice";
import socketMiddleware from "./middleware/socketMiddleware";
export const store = configureStore({
@ -11,6 +12,7 @@ export const store = configureStore({
favorites: favoritesReducer,
meetingsAndUserStatuses: meetingsAndUserStatusReducer,
users: usersReducer,
meetingUrl: meetingUrlReducer,
},
middleware: (getDefaultMiddleware) => {
return getDefaultMiddleware().concat([socketMiddleware]);