From 90473813fbb956f0264e02836afff7ca2abf8e7d Mon Sep 17 00:00:00 2001 From: bmodee <bjomo323@student.liu.se> Date: Tue, 20 Apr 2021 21:05:43 +0200 Subject: [PATCH 1/8] add styling and web sockets to presenter view --- client/src/pages/views/AudienceViewPage.tsx | 3 +- client/src/pages/views/JudgeViewPage.tsx | 15 +-- client/src/pages/views/PresenterViewPage.tsx | 101 +++++++++++++----- .../pages/views/components/SlideDisplay.tsx | 27 ++++- client/src/pages/views/components/Timer.tsx | 3 +- client/src/pages/views/components/styled.tsx | 7 ++ client/src/pages/views/styled.tsx | 17 ++- client/src/sockets.ts | 2 +- 8 files changed, 134 insertions(+), 41 deletions(-) diff --git a/client/src/pages/views/AudienceViewPage.tsx b/client/src/pages/views/AudienceViewPage.tsx index 45ec132d..00a821f3 100644 --- a/client/src/pages/views/AudienceViewPage.tsx +++ b/client/src/pages/views/AudienceViewPage.tsx @@ -1,7 +1,8 @@ import React from 'react' +import SlideDisplay from './components/SlideDisplay' const AudienceViewPage: React.FC = () => { - return <div>Publik</div> + return <SlideDisplay /> } export default AudienceViewPage diff --git a/client/src/pages/views/JudgeViewPage.tsx b/client/src/pages/views/JudgeViewPage.tsx index 12e866a0..5f1feffb 100644 --- a/client/src/pages/views/JudgeViewPage.tsx +++ b/client/src/pages/views/JudgeViewPage.tsx @@ -10,6 +10,7 @@ import { } from '../../actions/presentation' import { useAppDispatch, useAppSelector } from '../../hooks' import { ViewParams } from '../../interfaces/ViewParams' +import { socket_connect } from '../../sockets' import { SlideListItem } from '../presentationEditor/styled' import JudgeScoreDisplay from './components/JudgeScoreDisplay' import SlideDisplay from './components/SlideDisplay' @@ -43,17 +44,20 @@ const JudgeViewPage: React.FC = () => { const { id, code }: ViewParams = useParams() const dispatch = useAppDispatch() const [activeSlideIndex, setActiveSlideIndex] = useState<number>(0) - useEffect(() => { - dispatch(getPresentationCompetition(id)) - dispatch(getPresentationTeams(id)) - dispatch(setPresentationCode(code)) - }, []) const teams = useAppSelector((state) => state.presentation.teams) const slides = useAppSelector((state) => state.presentation.competition.slides) const handleSelectSlide = (index: number) => { setActiveSlideIndex(index) dispatch(setCurrentSlide(slides[index])) } + + useEffect(() => { + socket_connect() + dispatch(getPresentationCompetition(id)) + dispatch(getPresentationTeams(id)) + dispatch(setPresentationCode(code)) + }, []) + return ( <div> <JudgeAppBar position="fixed"> @@ -103,7 +107,6 @@ const JudgeViewPage: React.FC = () => { ))} </List> </RightDrawer> - aaa <Content leftDrawerWidth={leftDrawerWidth} rightDrawerWidth={rightDrawerWidth}> <div className={classes.toolbar} /> <SlideDisplay /> diff --git a/client/src/pages/views/PresenterViewPage.tsx b/client/src/pages/views/PresenterViewPage.tsx index 22a672ba..9a3547de 100644 --- a/client/src/pages/views/PresenterViewPage.tsx +++ b/client/src/pages/views/PresenterViewPage.tsx @@ -1,14 +1,26 @@ -import { List, ListItem, Popover } from '@material-ui/core' +import { List, ListItem, Popover, Tooltip } from '@material-ui/core' +import AssignmentIcon from '@material-ui/icons/Assignment' +import BackspaceIcon from '@material-ui/icons/Backspace' +import ChevronLeftIcon from '@material-ui/icons/ChevronLeft' import ChevronRightIcon from '@material-ui/icons/ChevronRight' +import PlayArrowIcon from '@material-ui/icons/PlayArrow' +import TimerIcon from '@material-ui/icons/Timer' import React, { useEffect } from 'react' import { useHistory, useParams } from 'react-router-dom' import { getPresentationCompetition, getPresentationTeams, setPresentationCode } from '../../actions/presentation' import { useAppDispatch, useAppSelector } from '../../hooks' import { ViewParams } from '../../interfaces/ViewParams' +import { + socketEndPresentation, + socketSetSlideNext, + socketSetSlidePrev, + socketStartPresentation, + socketStartTimer, + socket_connect, +} from '../../sockets' import SlideDisplay from './components/SlideDisplay' -import SocketTest from './components/SocketTest' import Timer from './components/Timer' -import { PresenterButton, PresenterContainer, PresenterFooter, PresenterHeader } from './styled' +import { PresenterButton, PresenterContainer, PresenterFooter, PresenterHeader, ToolBarContainer } from './styled' const PresenterViewPage: React.FC = () => { const teams = useAppSelector((state) => state.presentation.teams) @@ -16,46 +28,87 @@ const PresenterViewPage: React.FC = () => { const { id, code }: ViewParams = useParams() const history = useHistory() const dispatch = useAppDispatch() + useEffect(() => { + socket_connect() dispatch(getPresentationCompetition(id)) dispatch(getPresentationTeams(id)) dispatch(setPresentationCode(code)) }, []) + const handleOpenPopover = (event: React.MouseEvent<HTMLButtonElement>) => { setAnchorEl(event.currentTarget) } const handleClose = () => { setAnchorEl(null) } - const handleNextSlidePressed = () => { - // dispatch(setCurrentSlideNext()) - // syncSlide() - } - const handlePreviousSlidePressed = () => { - // dispatch(setCurrentSlidePrevious()) - // syncSlide() + + const endCompetition = () => { + if (confirm('Är du säker på att du vill avsluta tävlingen för alla?')) { + socketEndPresentation() + history.push('/admin') + } } return ( <PresenterContainer> <PresenterHeader> - <PresenterButton onClick={handleOpenPopover} color="primary" variant="contained"> - Visa ställning - </PresenterButton> - <PresenterButton onClick={() => history.push('/admin')} variant="contained" color="secondary"> - Avsluta tävling - </PresenterButton> + <Tooltip title="Avsluta tävling" arrow> + <PresenterButton onClick={endCompetition} variant="contained" color="secondary"> + <BackspaceIcon fontSize="large" /> + </PresenterButton> + </Tooltip> </PresenterHeader> <SlideDisplay /> <PresenterFooter> - <PresenterButton onClick={handlePreviousSlidePressed} variant="contained"> - <ChevronRightIcon fontSize="large" /> - </PresenterButton> - <SocketTest></SocketTest> - <Timer></Timer> - <PresenterButton onClick={handleNextSlidePressed} variant="contained"> - <ChevronRightIcon fontSize="large" /> - </PresenterButton> + <ToolBarContainer> + <Tooltip title="Previous Slide" arrow> + <PresenterButton onClick={socketSetSlidePrev} variant="contained"> + <ChevronLeftIcon fontSize="large" /> + </PresenterButton> + </Tooltip> + + <Tooltip title="Start Presentation" arrow> + <PresenterButton onClick={socketStartPresentation} variant="contained"> + <PlayArrowIcon fontSize="large" /> + </PresenterButton> + </Tooltip> + + {/* This creates a join button, but presenter should not others, others should join presenter + <Tooltip title="Join Presentation" arrow> + <PresenterButton onClick={socketJoinPresentation} variant="contained"> + <GroupAddIcon fontSize="large" /> + </PresenterButton> + </Tooltip> + + + This creates another end button + <Tooltip title="End Presentation" arrow> + <PresenterButton onClick={socketEndPresentation} variant="contained"> + <CancelIcon fontSize="large" /> + </PresenterButton> + </Tooltip> + */} + + <Tooltip title="Start Timer" arrow> + <PresenterButton onClick={socketStartTimer} variant="contained"> + <TimerIcon fontSize="large" /> + <Timer></Timer> + </PresenterButton> + </Tooltip> + + <Tooltip title="Scoreboard" arrow> + <PresenterButton onClick={handleOpenPopover} variant="contained"> + <AssignmentIcon fontSize="large" /> + </PresenterButton> + </Tooltip> + + <Tooltip title="Next Slide" arrow> + <PresenterButton onClick={socketSetSlideNext} variant="contained"> + <ChevronRightIcon fontSize="large" /> + </PresenterButton> + </Tooltip> + </ToolBarContainer> </PresenterFooter> <Popover open={Boolean(anchorEl)} diff --git a/client/src/pages/views/components/SlideDisplay.tsx b/client/src/pages/views/components/SlideDisplay.tsx index 1c10f9cf..ee2ee09b 100644 --- a/client/src/pages/views/components/SlideDisplay.tsx +++ b/client/src/pages/views/components/SlideDisplay.tsx @@ -1,14 +1,31 @@ import { Typography } from '@material-ui/core' -import React from 'react' -import { useAppSelector } from '../../../hooks' +import React, { useEffect } from 'react' +import { useParams } from 'react-router-dom' +import { getPresentationCompetition, getPresentationTeams, setPresentationCode } from '../../../actions/presentation' +import { useAppDispatch, useAppSelector } from '../../../hooks' +import { ViewParams } from '../../../interfaces/ViewParams' import { SlideContainer } from './styled' const SlideDisplay: React.FC = () => { + const { id, code }: ViewParams = useParams() const currentSlide = useAppSelector((state) => state.presentation.slide) + const dispatch = useAppDispatch() + useEffect(() => { + console.log('Slide ID: ' + id) + console.log('Code: ' + code) + dispatch(getPresentationCompetition(id)) + dispatch(getPresentationTeams(id)) + dispatch(setPresentationCode(code)) + }, []) + return ( - <SlideContainer> - <Typography variant="h3">{currentSlide.title}</Typography> - </SlideContainer> + <div> + <SlideContainer> + <Typography variant="h3">Slide Title: {currentSlide.title} </Typography> + <Typography variant="h3">Timer: {currentSlide.timer} </Typography> + <Typography variant="h3">Slide ID: {currentSlide.id} </Typography> + </SlideContainer> + </div> ) } diff --git a/client/src/pages/views/components/Timer.tsx b/client/src/pages/views/components/Timer.tsx index b4401a69..0cbd1fdf 100644 --- a/client/src/pages/views/components/Timer.tsx +++ b/client/src/pages/views/components/Timer.tsx @@ -38,8 +38,7 @@ const Timer: React.FC = (props: any) => { return ( <> - <div>Timer: {props.timer.value}</div> - <div>Enabled: {props.timer.enabled.toString()}</div> + <div>{props.timer.value}</div> </> ) } diff --git a/client/src/pages/views/components/styled.tsx b/client/src/pages/views/components/styled.tsx index 0034c39b..b522b20d 100644 --- a/client/src/pages/views/components/styled.tsx +++ b/client/src/pages/views/components/styled.tsx @@ -3,7 +3,14 @@ import styled from 'styled-components' export const SlideContainer = styled.div` display: flex; + flex-direction: column; + margin-left: auto; + margin-right: auto; + margin-top: 5%; justify-content: center; + background-color: grey; + width: 1280px; + height: 720px; ` export const ScoreDisplayContainer = styled.div` diff --git a/client/src/pages/views/styled.tsx b/client/src/pages/views/styled.tsx index d4a9cf9e..121a1925 100644 --- a/client/src/pages/views/styled.tsx +++ b/client/src/pages/views/styled.tsx @@ -50,8 +50,9 @@ export const PresenterFooter = styled.div` export const PresenterButton = styled(Button)` width: 100px; height: 100px; - padding-top: 16px; - padding-bottom: 16px; + margin-left: 16px; + margin-right: 16px; + margin-top: 16px; ` export const PresenterContainer = styled.div` @@ -61,6 +62,18 @@ export const PresenterContainer = styled.div` height: 100%; ` +export const ToolBarContainer = styled.div` + align-self: center; + display: flex; + flex-direction: row; + justify-content: space-between; + height: 100%; + width: auto; + margin-right: auto; + margin-left: auto; + margin-bottom: 20px; +` + interface DrawerProps { width: number } diff --git a/client/src/sockets.ts b/client/src/sockets.ts index 874bfc46..42899753 100644 --- a/client/src/sockets.ts +++ b/client/src/sockets.ts @@ -41,7 +41,7 @@ export const socketStartPresentation = () => { } export const socketJoinPresentation = () => { - socket.emit('join_presentation', { code: 'OEM1V4' }) // TODO: Send code gotten from auth/login/<code> api call + socket.emit('join_presentation', { code: 'CO0ART' }) // TODO: Send code gotten from auth/login/<code> api call } export const socketEndPresentation = () => { -- GitLab From ec7a254d15f2bc30e3a6b39ef7562b07f5fb4904 Mon Sep 17 00:00:00 2001 From: bmodee <bjomo323@student.liu.se> Date: Wed, 21 Apr 2021 09:18:15 +0200 Subject: [PATCH 2/8] add slide counter --- .../admin/competitions/AddCompetition.tsx | 27 ++++++++++++++++--- .../admin/competitions/CompetitionManager.tsx | 20 +++++++++++--- client/src/pages/views/JudgeViewPage.tsx | 3 ++- client/src/pages/views/PresenterViewPage.tsx | 22 ++++++++++++--- client/src/pages/views/styled.tsx | 6 +++++ 5 files changed, 67 insertions(+), 11 deletions(-) diff --git a/client/src/pages/admin/competitions/AddCompetition.tsx b/client/src/pages/admin/competitions/AddCompetition.tsx index 3d45e629..5cbb3583 100644 --- a/client/src/pages/admin/competitions/AddCompetition.tsx +++ b/client/src/pages/admin/competitions/AddCompetition.tsx @@ -10,10 +10,17 @@ import { City } from '../../../interfaces/ApiModels' import { AddCompetitionModel, FormModel } from '../../../interfaces/FormModels' import { AddButton, AddContent, AddForm } from '../styledComp' +/** + * Component description: + * This component handles the functionality when adding a competition to the system + * This component is a child component to CompetitionManager.tsx + */ + type formType = FormModel<AddCompetitionModel> const noCitySelected = 'Välj stad' +//Description of the form and what is required const competitionSchema: Yup.SchemaOf<formType> = Yup.object({ model: Yup.object() .shape({ @@ -31,7 +38,8 @@ const competitionSchema: Yup.SchemaOf<formType> = Yup.object({ const AddCompetition: React.FC = (props: any) => { const [anchorEl, setAnchorEl] = React.useState<HTMLButtonElement | null>(null) const cities = useAppSelector((state) => state.cities.cities) - const userCity = useAppSelector((state) => state.user.userInfo?.city) + const currentUser = useAppSelector((state) => state.user.userInfo) + const userCity = cities.find((city) => city.id === currentUser?.city.id) const [selectedCity, setSelectedCity] = React.useState<City | undefined>(userCity) const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => { setAnchorEl(event.currentTarget) @@ -44,20 +52,25 @@ const AddCompetition: React.FC = (props: any) => { const dispatch = useAppDispatch() const id = open ? 'simple-popover' : undefined const currentYear = new Date().getFullYear() + + // Handles the actual submition to the database const handleCompetitionSubmit = async (values: formType, actions: FormikHelpers<formType>) => { + // The parameters sent const params = { name: values.model.name, year: values.model.year, city_id: selectedCity?.id as number, } + await axios - .post('/competitions', params) + .post('/competitions', params) // send to database .then(() => { - actions.resetForm() + actions.resetForm() // reset the form setAnchorEl(null) - dispatch(getCompetitions()) + dispatch(getCompetitions()) // refresh competitions setSelectedCity(undefined) }) + // if the post request fails .catch(({ response }) => { console.warn(response.data) if (response.data && response.data.message) @@ -82,6 +95,12 @@ const AddCompetition: React.FC = (props: any) => { > Ny Tävling </AddButton> + + {/** + * The "pop up" menu for adding a competition + * contains 3 fields; Name, Region and Year + * + */} <Popover id={id} open={open} diff --git a/client/src/pages/admin/competitions/CompetitionManager.tsx b/client/src/pages/admin/competitions/CompetitionManager.tsx index 08a18581..d545e1d0 100644 --- a/client/src/pages/admin/competitions/CompetitionManager.tsx +++ b/client/src/pages/admin/competitions/CompetitionManager.tsx @@ -21,6 +21,13 @@ import { CompetitionFilterParams } from '../../../interfaces/FilterParams' import { FilterContainer, RemoveMenuItem, TopBar, YearFilterTextField } from '../styledComp' import AddCompetition from './AddCompetition' +/** + * Component description: + * This component shows a list of all the competitions which a user can search through + * We can also start, duplicate or delete a competition + */ + +// Use defined styling const useStyles = makeStyles((theme: Theme) => createStyles({ table: { @@ -36,10 +43,13 @@ const CompetitionManager: React.FC = (props: any) => { const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null) const [activeId, setActiveId] = React.useState<number | undefined>(undefined) const [timerHandle, setTimerHandle] = React.useState<number | undefined>(undefined) + + const loading = useAppSelector((state) => state.user.userInfo === null) const competitions = useAppSelector((state) => state.competitions.competitions) const filterParams = useAppSelector((state) => state.competitions.filterParams) const competitionTotal = useAppSelector((state) => state.competitions.total) const cities = useAppSelector((state) => state.cities.cities) + const classes = useStyles() const noFilterText = 'Alla' const dispatch = useAppDispatch() @@ -58,6 +68,7 @@ const CompetitionManager: React.FC = (props: any) => { dispatch(getCompetitions()) }, []) + // Search funtion to search for a specific string const onSearchChange = (event: React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>) => { if (timerHandle) { clearTimeout(timerHandle) @@ -68,13 +79,14 @@ const CompetitionManager: React.FC = (props: any) => { dispatch(setFilterParams({ ...filterParams, name: event.target.value })) } + // Function to remove a competition from the systems database const handleDeleteCompetition = async () => { if (activeId) { await axios .delete(`/competitions/${activeId}`) .then(() => { setAnchorEl(null) - dispatch(getCompetitions()) + dispatch(getCompetitions()) // refresh the competition list }) .catch(({ response }) => { console.warn(response.data) @@ -82,6 +94,7 @@ const CompetitionManager: React.FC = (props: any) => { } } + // Filters the competiton list with the given parameters const handleFilterChange = (newParams: CompetitionFilterParams) => { dispatch(setFilterParams(newParams)) dispatch(getCompetitions()) @@ -125,12 +138,12 @@ const CompetitionManager: React.FC = (props: any) => { label="År" name="model.year" type="number" - value={filterParams.year || new Date().getFullYear()} + value={filterParams.year || ''} onChange={(event) => handleFilterChange({ ...filterParams, year: +event.target.value })} margin="normal" /> </FilterContainer> - <AddCompetition /> + {!loading && <AddCompetition />} </TopBar> <TableContainer component={Paper}> <Table className={classes.table} aria-label="simple table"> @@ -162,6 +175,7 @@ const CompetitionManager: React.FC = (props: any) => { ))} </TableBody> </Table> + {/** We can't find any competitions at all or with a specific filter */} {(!competitions || competitions.length === 0) && ( <Typography>Inga tävlingar hittades med nuvarande filter</Typography> )} diff --git a/client/src/pages/views/JudgeViewPage.tsx b/client/src/pages/views/JudgeViewPage.tsx index 5f1feffb..60018624 100644 --- a/client/src/pages/views/JudgeViewPage.tsx +++ b/client/src/pages/views/JudgeViewPage.tsx @@ -1,4 +1,4 @@ -import { Divider, List, ListItemText } from '@material-ui/core' +import { Divider, List, ListItemText, Typography } from '@material-ui/core' import { createStyles, makeStyles, Theme } from '@material-ui/core/styles' import React, { useEffect, useState } from 'react' import { useParams } from 'react-router-dom' @@ -84,6 +84,7 @@ const JudgeViewPage: React.FC = () => { button key={slide.id} > + <Typography variant="h6">Slide ID: {slide.id} </Typography> <ListItemText primary={slide.title} /> </SlideListItem> ))} diff --git a/client/src/pages/views/PresenterViewPage.tsx b/client/src/pages/views/PresenterViewPage.tsx index 9a3547de..f538fd33 100644 --- a/client/src/pages/views/PresenterViewPage.tsx +++ b/client/src/pages/views/PresenterViewPage.tsx @@ -1,4 +1,4 @@ -import { List, ListItem, Popover, Tooltip } from '@material-ui/core' +import { List, ListItem, Popover, Tooltip, Typography } from '@material-ui/core' import AssignmentIcon from '@material-ui/icons/Assignment' import BackspaceIcon from '@material-ui/icons/Backspace' import ChevronLeftIcon from '@material-ui/icons/ChevronLeft' @@ -20,12 +20,20 @@ import { } from '../../sockets' import SlideDisplay from './components/SlideDisplay' import Timer from './components/Timer' -import { PresenterButton, PresenterContainer, PresenterFooter, PresenterHeader, ToolBarContainer } from './styled' +import { + PresenterButton, + PresenterContainer, + PresenterFooter, + PresenterHeader, + SlideCounter, + ToolBarContainer, +} from './styled' const PresenterViewPage: React.FC = () => { const teams = useAppSelector((state) => state.presentation.teams) const [anchorEl, setAnchorEl] = React.useState<HTMLButtonElement | null>(null) const { id, code }: ViewParams = useParams() + const presentation = useAppSelector((state) => state.presentation) const history = useHistory() const dispatch = useAppDispatch() @@ -58,6 +66,11 @@ const PresenterViewPage: React.FC = () => { <BackspaceIcon fontSize="large" /> </PresenterButton> </Tooltip> + <SlideCounter> + <Typography variant="h3"> + {presentation.slide.id} / {presentation.competition.slides.length} + </Typography> + </SlideCounter> </PresenterHeader> <SlideDisplay /> <PresenterFooter> @@ -74,7 +87,7 @@ const PresenterViewPage: React.FC = () => { </PresenterButton> </Tooltip> - {/* This creates a join button, but presenter should not others, others should join presenter + {/* This creates a join button, but presenter should not join others, others should join presenter <Tooltip title="Join Presentation" arrow> <PresenterButton onClick={socketJoinPresentation} variant="contained"> <GroupAddIcon fontSize="large" /> @@ -124,6 +137,9 @@ const PresenterViewPage: React.FC = () => { }} > <List> + {/** TODO: + * Fix scoreboard + */} {teams.map((team) => ( <ListItem key={team.id}>{team.name} score: 20</ListItem> ))} diff --git a/client/src/pages/views/styled.tsx b/client/src/pages/views/styled.tsx index 121a1925..1f3a61c6 100644 --- a/client/src/pages/views/styled.tsx +++ b/client/src/pages/views/styled.tsx @@ -55,6 +55,12 @@ export const PresenterButton = styled(Button)` margin-top: 16px; ` +export const SlideCounter = styled(Button)` + margin-left: 16px; + margin-right: 16px; + margin-top: 16px; +` + export const PresenterContainer = styled.div` display: flex; flex-direction: column; -- GitLab From 3ade939c1681f13c04e5819844f1361078a45596 Mon Sep 17 00:00:00 2001 From: bmodee <bjomo323@student.liu.se> Date: Wed, 21 Apr 2021 11:17:19 +0200 Subject: [PATCH 3/8] add console logs --- client/src/pages/views/PresenterViewPage.tsx | 18 +++++++++++++++--- client/src/sockets.ts | 7 +++++++ 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/client/src/pages/views/PresenterViewPage.tsx b/client/src/pages/views/PresenterViewPage.tsx index f538fd33..ff61343f 100644 --- a/client/src/pages/views/PresenterViewPage.tsx +++ b/client/src/pages/views/PresenterViewPage.tsx @@ -12,6 +12,7 @@ import { useAppDispatch, useAppSelector } from '../../hooks' import { ViewParams } from '../../interfaces/ViewParams' import { socketEndPresentation, + socketSetSlide, socketSetSlideNext, socketSetSlidePrev, socketStartPresentation, @@ -39,6 +40,7 @@ const PresenterViewPage: React.FC = () => { useEffect(() => { socket_connect() + socketSetSlide dispatch(getPresentationCompetition(id)) dispatch(getPresentationTeams(id)) dispatch(setPresentationCode(code)) @@ -51,10 +53,19 @@ const PresenterViewPage: React.FC = () => { setAnchorEl(null) } + const startCompetition = () => { + socketStartPresentation() + const haveStarted = true + console.log('You have started the competition! GLHF!') + console.log(haveStarted) + } + const endCompetition = () => { if (confirm('Är du säker på att du vill avsluta tävlingen för alla?')) { + const haveStarted = false socketEndPresentation() history.push('/admin') + window.location.reload(false) // TODO: fix this ugly hack, we "need" to refresh site to be able to run the competition correctly again } } @@ -82,12 +93,13 @@ const PresenterViewPage: React.FC = () => { </Tooltip> <Tooltip title="Start Presentation" arrow> - <PresenterButton onClick={socketStartPresentation} variant="contained"> + <PresenterButton onClick={startCompetition} variant="contained"> <PlayArrowIcon fontSize="large" /> </PresenterButton> </Tooltip> - {/* This creates a join button, but presenter should not join others, others should join presenter + {/* + // This creates a join button, but presenter should not join others, others should join presenter <Tooltip title="Join Presentation" arrow> <PresenterButton onClick={socketJoinPresentation} variant="contained"> <GroupAddIcon fontSize="large" /> @@ -95,7 +107,7 @@ const PresenterViewPage: React.FC = () => { </Tooltip> - This creates another end button + // This creates another end button, it might not be needed since we already have one <Tooltip title="End Presentation" arrow> <PresenterButton onClick={socketEndPresentation} variant="contained"> <CancelIcon fontSize="large" /> diff --git a/client/src/sockets.ts b/client/src/sockets.ts index 42899753..54f84c39 100644 --- a/client/src/sockets.ts +++ b/client/src/sockets.ts @@ -38,22 +38,27 @@ export const socket_connect = () => { export const socketStartPresentation = () => { socket.emit('start_presentation', { competition_id: store.getState().presentation.competition.id }) + console.log('START PRESENTATION') } export const socketJoinPresentation = () => { socket.emit('join_presentation', { code: 'CO0ART' }) // TODO: Send code gotten from auth/login/<code> api call + console.log('JOIN PRESENTATION') } export const socketEndPresentation = () => { socket.emit('end_presentation', { competition_id: store.getState().presentation.competition.id }) + console.log('END PRESENTATION') } export const socketSetSlideNext = () => { socketSetSlide(store.getState().presentation.slide.order + 1) // TODO: Check that this slide exists + console.log('NEXT SLIDE +1') } export const socketSetSlidePrev = () => { socketSetSlide(store.getState().presentation.slide.order - 1) // TODO: Check that this slide exists + console.log('PREVIOUS SLIDE -1') } export const socketSetSlide = (slide_order: number) => { @@ -69,6 +74,7 @@ export const socketSetSlide = (slide_order: number) => { } export const socketSetTimer = (timer: Timer) => { + console.log('SET TIMER') socket.emit('set_timer', { competition_id: store.getState().presentation.competition.id, timer: timer, @@ -76,5 +82,6 @@ export const socketSetTimer = (timer: Timer) => { } export const socketStartTimer = () => { + console.log('START TIMER') socketSetTimer({ enabled: true, value: store.getState().presentation.timer.value }) } -- GitLab From 799694f33b7a3c3ec307e98623ed63269262936f Mon Sep 17 00:00:00 2001 From: bmodee <bjomo323@student.liu.se> Date: Wed, 21 Apr 2021 13:42:27 +0200 Subject: [PATCH 4/8] add some stuff --- client/src/pages/admin/users/EditUser.tsx | 37 ++++++++++++- .../pages/admin/users/ResponsiveDialog.tsx | 53 +++++++++++++++++++ 2 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 client/src/pages/admin/users/ResponsiveDialog.tsx diff --git a/client/src/pages/admin/users/EditUser.tsx b/client/src/pages/admin/users/EditUser.tsx index a1be7c87..56d3e377 100644 --- a/client/src/pages/admin/users/EditUser.tsx +++ b/client/src/pages/admin/users/EditUser.tsx @@ -8,12 +8,19 @@ import { Popover, TextField, Theme, + useMediaQuery, } from '@material-ui/core' +import Dialog from '@material-ui/core/Dialog' +import DialogActions from '@material-ui/core/DialogActions' +import DialogContent from '@material-ui/core/DialogContent' +import DialogContentText from '@material-ui/core/DialogContentText' +import DialogTitle from '@material-ui/core/DialogTitle' +import { useTheme } from '@material-ui/core/styles' import MoreHorizIcon from '@material-ui/icons/MoreHoriz' import { Alert, AlertTitle } from '@material-ui/lab' import axios from 'axios' import { Formik, FormikHelpers } from 'formik' -import React, { useEffect } from 'react' +import { default as React, useEffect } from 'react' import * as Yup from 'yup' import { getSearchUsers } from '../../../actions/searchUser' import { useAppDispatch, useAppSelector } from '../../../hooks' @@ -62,6 +69,11 @@ type UserIdProps = { } const EditUser = ({ user }: UserIdProps) => { + // for dialog alert + const [openAlert, setOpen] = React.useState(false) + const theme = useTheme() + const fullScreen = useMediaQuery(theme.breakpoints.down('sm')) + const dispatch = useAppDispatch() const classes = useStyles() @@ -91,6 +103,7 @@ const EditUser = ({ user }: UserIdProps) => { } const handleDeleteUsers = async () => { + //setOpen(true) if (confirm('Are u sure?')) { await axios .delete(`/auth/delete/${user.id}`) @@ -281,6 +294,28 @@ const EditUser = ({ user }: UserIdProps) => { > Ta bort </Button> + <Dialog + fullScreen={fullScreen} + open={open} + onClose={handleClose} + aria-labelledby="responsive-dialog-title" + > + <DialogTitle id="responsive-dialog-title">{"Use Google's location service?"}</DialogTitle> + <DialogContent> + <DialogContentText> + Let Google help apps determine location. This means sending anonymous location data to Google, + even when no apps are running. + </DialogContentText> + </DialogContent> + <DialogActions> + <Button autoFocus onClick={handleClose} color="primary"> + Disagree + </Button> + <Button onClick={handleClose} color="primary" autoFocus> + Agree + </Button> + </DialogActions> + </Dialog> {formik.errors.error && ( <Alert severity="error"> diff --git a/client/src/pages/admin/users/ResponsiveDialog.tsx b/client/src/pages/admin/users/ResponsiveDialog.tsx new file mode 100644 index 00000000..e29a6786 --- /dev/null +++ b/client/src/pages/admin/users/ResponsiveDialog.tsx @@ -0,0 +1,53 @@ +import React from 'react'; +import Button from '@material-ui/core/Button'; +import Dialog from '@material-ui/core/Dialog'; +import DialogActions from '@material-ui/core/DialogActions'; +import DialogContent from '@material-ui/core/DialogContent'; +import DialogContentText from '@material-ui/core/DialogContentText'; +import DialogTitle from '@material-ui/core/DialogTitle'; +import useMediaQuery from '@material-ui/core/useMediaQuery'; +import { useTheme } from '@material-ui/core/styles'; + +export default function ResponsiveDialog() { + const [open, setOpen] = React.useState(false); + const theme = useTheme(); + const fullScreen = useMediaQuery(theme.breakpoints.down('sm')); + + const handleClickOpen = () => { + setOpen(true); + }; + + const handleClose = () => { + setOpen(false); + }; + + return ( + <div> + <Button variant="outlined" color="primary" onClick={handleClickOpen}> + Open responsive dialog + </Button> + <Dialog + fullScreen={fullScreen} + open={open} + onClose={handleClose} + aria-labelledby="responsive-dialog-title" + > + <DialogTitle id="responsive-dialog-title">{"Use Google's location service?"}</DialogTitle> + <DialogContent> + <DialogContentText> + Let Google help apps determine location. This means sending anonymous location data to + Google, even when no apps are running. + </DialogContentText> + </DialogContent> + <DialogActions> + <Button autoFocus onClick={handleClose} color="primary"> + Disagree + </Button> + <Button onClick={handleClose} color="primary" autoFocus> + Agree + </Button> + </DialogActions> + </Dialog> + </div> + ); +} \ No newline at end of file -- GitLab From 25004f78c9c73d1a56494d2ed80b577d32b909c2 Mon Sep 17 00:00:00 2001 From: bmodee <bjomo323@student.liu.se> Date: Wed, 21 Apr 2021 14:20:37 +0200 Subject: [PATCH 5/8] add alert dialog when removing user --- client/src/pages/admin/users/EditUser.tsx | 58 ++++++++++++----------- 1 file changed, 31 insertions(+), 27 deletions(-) diff --git a/client/src/pages/admin/users/EditUser.tsx b/client/src/pages/admin/users/EditUser.tsx index 56d3e377..032fc114 100644 --- a/client/src/pages/admin/users/EditUser.tsx +++ b/client/src/pages/admin/users/EditUser.tsx @@ -1,6 +1,11 @@ import { Button, createStyles, + Dialog, + DialogActions, + DialogContent, + DialogContentText, + DialogTitle, FormControl, InputLabel, makeStyles, @@ -9,18 +14,13 @@ import { TextField, Theme, useMediaQuery, + useTheme, } from '@material-ui/core' -import Dialog from '@material-ui/core/Dialog' -import DialogActions from '@material-ui/core/DialogActions' -import DialogContent from '@material-ui/core/DialogContent' -import DialogContentText from '@material-ui/core/DialogContentText' -import DialogTitle from '@material-ui/core/DialogTitle' -import { useTheme } from '@material-ui/core/styles' import MoreHorizIcon from '@material-ui/icons/MoreHoriz' import { Alert, AlertTitle } from '@material-ui/lab' import axios from 'axios' import { Formik, FormikHelpers } from 'formik' -import { default as React, useEffect } from 'react' +import React, { useEffect } from 'react' import * as Yup from 'yup' import { getSearchUsers } from '../../../actions/searchUser' import { useAppDispatch, useAppSelector } from '../../../hooks' @@ -71,6 +71,7 @@ type UserIdProps = { const EditUser = ({ user }: UserIdProps) => { // for dialog alert const [openAlert, setOpen] = React.useState(false) + const theme = useTheme() const fullScreen = useMediaQuery(theme.breakpoints.down('sm')) @@ -99,22 +100,25 @@ const EditUser = ({ user }: UserIdProps) => { setAnchorEl(event.currentTarget) } const handleClose = () => { + setOpen(false) setAnchorEl(null) } + const handleVerifyDelete = () => { + setOpen(true) + } + const handleDeleteUsers = async () => { - //setOpen(true) - if (confirm('Are u sure?')) { - await axios - .delete(`/auth/delete/${user.id}`) - .then(() => { - setAnchorEl(null) - dispatch(getSearchUsers()) - }) - .catch(({ response }) => { - console.warn(response.data) - }) - } + setOpen(false) + await axios + .delete(`/auth/delete/${user.id}`) + .then(() => { + setAnchorEl(null) + dispatch(getSearchUsers()) + }) + .catch(({ response }) => { + console.warn(response.data) + }) } const handleSubmit = async (values: formType, actions: FormikHelpers<formType>) => { @@ -286,7 +290,7 @@ const EditUser = ({ user }: UserIdProps) => { Ändra </Button> <Button - onClick={handleDeleteUsers} + onClick={handleVerifyDelete} className={classes.deleteButton} fullWidth variant="contained" @@ -294,25 +298,25 @@ const EditUser = ({ user }: UserIdProps) => { > Ta bort </Button> + <Dialog fullScreen={fullScreen} - open={open} + open={openAlert} onClose={handleClose} aria-labelledby="responsive-dialog-title" > - <DialogTitle id="responsive-dialog-title">{"Use Google's location service?"}</DialogTitle> + <DialogTitle id="responsive-dialog-title">{'Ta bort användare?'}</DialogTitle> <DialogContent> <DialogContentText> - Let Google help apps determine location. This means sending anonymous location data to Google, - even when no apps are running. + Är du säker på att du vill ta bort denna användaren och alla dess uppgifter från hela systemet? </DialogContentText> </DialogContent> <DialogActions> <Button autoFocus onClick={handleClose} color="primary"> - Disagree + Avbryt </Button> - <Button onClick={handleClose} color="primary" autoFocus> - Agree + <Button onClick={handleDeleteUsers} color="primary" autoFocus> + Ta bort </Button> </DialogActions> </Dialog> -- GitLab From 888acf91b533d0163915629ad3e6ae294cb164bb Mon Sep 17 00:00:00 2001 From: bmodee <bjomo323@student.liu.se> Date: Wed, 21 Apr 2021 14:37:25 +0200 Subject: [PATCH 6/8] run test --- client/src/pages/views/components/SlideDisplay.tsx | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/client/src/pages/views/components/SlideDisplay.tsx b/client/src/pages/views/components/SlideDisplay.tsx index ee2ee09b..b7e6dcbe 100644 --- a/client/src/pages/views/components/SlideDisplay.tsx +++ b/client/src/pages/views/components/SlideDisplay.tsx @@ -1,22 +1,12 @@ import { Typography } from '@material-ui/core' import React, { useEffect } from 'react' -import { useParams } from 'react-router-dom' -import { getPresentationCompetition, getPresentationTeams, setPresentationCode } from '../../../actions/presentation' import { useAppDispatch, useAppSelector } from '../../../hooks' -import { ViewParams } from '../../../interfaces/ViewParams' import { SlideContainer } from './styled' const SlideDisplay: React.FC = () => { - const { id, code }: ViewParams = useParams() const currentSlide = useAppSelector((state) => state.presentation.slide) const dispatch = useAppDispatch() - useEffect(() => { - console.log('Slide ID: ' + id) - console.log('Code: ' + code) - dispatch(getPresentationCompetition(id)) - dispatch(getPresentationTeams(id)) - dispatch(setPresentationCode(code)) - }, []) + useEffect(() => {}, []) return ( <div> -- GitLab From e7620eb988697dd336cd1385dc2a6e34b210a8b8 Mon Sep 17 00:00:00 2001 From: bmodee <bjomo323@student.liu.se> Date: Wed, 21 Apr 2021 15:37:16 +0200 Subject: [PATCH 7/8] add alert dialogs --- client/src/pages/admin/users/EditUser.tsx | 4 +- client/src/pages/views/PresenterViewPage.tsx | 60 ++++++++++++++++--- .../pages/views/components/SlideDisplay.tsx | 6 +- 3 files changed, 55 insertions(+), 15 deletions(-) diff --git a/client/src/pages/admin/users/EditUser.tsx b/client/src/pages/admin/users/EditUser.tsx index 032fc114..7db5aeec 100644 --- a/client/src/pages/admin/users/EditUser.tsx +++ b/client/src/pages/admin/users/EditUser.tsx @@ -71,7 +71,6 @@ type UserIdProps = { const EditUser = ({ user }: UserIdProps) => { // for dialog alert const [openAlert, setOpen] = React.useState(false) - const theme = useTheme() const fullScreen = useMediaQuery(theme.breakpoints.down('sm')) @@ -298,7 +297,6 @@ const EditUser = ({ user }: UserIdProps) => { > Ta bort </Button> - <Dialog fullScreen={fullScreen} open={openAlert} @@ -308,7 +306,7 @@ const EditUser = ({ user }: UserIdProps) => { <DialogTitle id="responsive-dialog-title">{'Ta bort användare?'}</DialogTitle> <DialogContent> <DialogContentText> - Är du säker på att du vill ta bort denna användaren och alla dess uppgifter från hela systemet? + Är du säker på att du vill ta bort användaren och all dess information från systemet? </DialogContentText> </DialogContent> <DialogActions> diff --git a/client/src/pages/views/PresenterViewPage.tsx b/client/src/pages/views/PresenterViewPage.tsx index ff61343f..346aabcf 100644 --- a/client/src/pages/views/PresenterViewPage.tsx +++ b/client/src/pages/views/PresenterViewPage.tsx @@ -1,4 +1,18 @@ -import { List, ListItem, Popover, Tooltip, Typography } from '@material-ui/core' +import { + Button, + Dialog, + DialogActions, + DialogContent, + DialogContentText, + DialogTitle, + List, + ListItem, + Popover, + Tooltip, + Typography, + useMediaQuery, + useTheme, +} from '@material-ui/core' import AssignmentIcon from '@material-ui/icons/Assignment' import BackspaceIcon from '@material-ui/icons/Backspace' import ChevronLeftIcon from '@material-ui/icons/ChevronLeft' @@ -31,6 +45,11 @@ import { } from './styled' const PresenterViewPage: React.FC = () => { + // for dialog alert + const [openAlert, setOpen] = React.useState(false) + const theme = useTheme() + const fullScreen = useMediaQuery(theme.breakpoints.down('sm')) + const teams = useAppSelector((state) => state.presentation.teams) const [anchorEl, setAnchorEl] = React.useState<HTMLButtonElement | null>(null) const { id, code }: ViewParams = useParams() @@ -50,9 +69,14 @@ const PresenterViewPage: React.FC = () => { setAnchorEl(event.currentTarget) } const handleClose = () => { + setOpen(false) setAnchorEl(null) } + const handleVerifyExit = () => { + setOpen(true) + } + const startCompetition = () => { socketStartPresentation() const haveStarted = true @@ -61,22 +85,42 @@ const PresenterViewPage: React.FC = () => { } const endCompetition = () => { - if (confirm('Är du säker på att du vill avsluta tävlingen för alla?')) { - const haveStarted = false - socketEndPresentation() - history.push('/admin') - window.location.reload(false) // TODO: fix this ugly hack, we "need" to refresh site to be able to run the competition correctly again - } + setOpen(false) + const haveStarted = false + socketEndPresentation() + history.push('/admin') + window.location.reload(false) // TODO: fix this ugly hack, we "need" to refresh site to be able to run the competition correctly again } return ( <PresenterContainer> <PresenterHeader> <Tooltip title="Avsluta tävling" arrow> - <PresenterButton onClick={endCompetition} variant="contained" color="secondary"> + <PresenterButton onClick={handleVerifyExit} variant="contained" color="secondary"> <BackspaceIcon fontSize="large" /> </PresenterButton> </Tooltip> + <Dialog + fullScreen={fullScreen} + open={openAlert} + onClose={handleClose} + aria-labelledby="responsive-dialog-title" + > + <DialogTitle id="responsive-dialog-title">{'Vill du avsluta tävlingen?'}</DialogTitle> + <DialogContent> + <DialogContentText> + Genom att avsluta tävlingen kommer den avslutas för alla. Du kommer gå tillbaka till startsidan. + </DialogContentText> + </DialogContent> + <DialogActions> + <Button autoFocus onClick={handleClose} color="primary"> + Avbryt + </Button> + <Button onClick={endCompetition} color="primary" autoFocus> + Avsluta tävling + </Button> + </DialogActions> + </Dialog> <SlideCounter> <Typography variant="h3"> {presentation.slide.id} / {presentation.competition.slides.length} diff --git a/client/src/pages/views/components/SlideDisplay.tsx b/client/src/pages/views/components/SlideDisplay.tsx index b7e6dcbe..7ecffac5 100644 --- a/client/src/pages/views/components/SlideDisplay.tsx +++ b/client/src/pages/views/components/SlideDisplay.tsx @@ -1,12 +1,10 @@ import { Typography } from '@material-ui/core' -import React, { useEffect } from 'react' -import { useAppDispatch, useAppSelector } from '../../../hooks' +import React from 'react' +import { useAppSelector } from '../../../hooks' import { SlideContainer } from './styled' const SlideDisplay: React.FC = () => { const currentSlide = useAppSelector((state) => state.presentation.slide) - const dispatch = useAppDispatch() - useEffect(() => {}, []) return ( <div> -- GitLab From 29271355ce5b42de7db332be3c66a430f980361e Mon Sep 17 00:00:00 2001 From: bmodee <bjomo323@student.liu.se> Date: Wed, 21 Apr 2021 16:02:26 +0200 Subject: [PATCH 8/8] fix --- client/src/pages/views/PresenterViewPage.tsx | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/client/src/pages/views/PresenterViewPage.tsx b/client/src/pages/views/PresenterViewPage.tsx index ee8a5472..346aabcf 100644 --- a/client/src/pages/views/PresenterViewPage.tsx +++ b/client/src/pages/views/PresenterViewPage.tsx @@ -1,4 +1,3 @@ -<<<<<<< HEAD import { Button, Dialog, @@ -12,7 +11,7 @@ import { Tooltip, Typography, useMediaQuery, - useTheme + useTheme, } from '@material-ui/core' import AssignmentIcon from '@material-ui/icons/Assignment' import BackspaceIcon from '@material-ui/icons/Backspace' @@ -32,7 +31,7 @@ import { socketSetSlidePrev, socketStartPresentation, socketStartTimer, - socket_connect + socket_connect, } from '../../sockets' import SlideDisplay from './components/SlideDisplay' import Timer from './components/Timer' @@ -42,11 +41,8 @@ import { PresenterFooter, PresenterHeader, SlideCounter, - ToolBarContainer + ToolBarContainer, } from './styled' -======= -import { List, ListItem, Popover, Tooltip, Typography } from '@material-ui/core' ->>>>>>> d488a39fed5732129f8adc7b12b9d77e077c8354 const PresenterViewPage: React.FC = () => { // for dialog alert -- GitLab