From 21736a462e02b90050ce11d9fdc06c7ade89d164 Mon Sep 17 00:00:00 2001 From: Albin Henriksson <albhe428@student.liu.se> Date: Tue, 27 Apr 2021 10:45:27 +0000 Subject: [PATCH] Resolve "Show components in active competition" --- client/src/actions/presentation.test.ts | 13 +--- client/src/actions/presentation.ts | 15 ----- client/src/actions/types.ts | 1 - client/src/interfaces/ApiModels.ts | 8 +-- .../PresentationEditorPage.tsx | 8 ++- .../components/RndComponent.tsx | 31 ++------- .../components/SlideDisplay.tsx | 67 +++++++++++++++++++ .../components/SlideEditor.tsx | 45 ------------- .../components/TextComponentDisplay.tsx | 19 ++++++ .../components/TextComponentEdit.tsx | 12 +++- .../presentationEditor/components/styled.tsx | 4 +- client/src/pages/views/AudienceViewPage.tsx | 10 ++- client/src/pages/views/JudgeViewPage.test.tsx | 2 +- client/src/pages/views/JudgeViewPage.tsx | 47 +++++++------ .../pages/views/ParticipantViewPage.test.tsx | 7 ++ .../src/pages/views/ParticipantViewPage.tsx | 17 ++++- client/src/pages/views/PresenterViewPage.tsx | 22 +++--- .../src/pages/views/ViewSelectPage.test.tsx | 9 +++ client/src/pages/views/ViewSelectPage.tsx | 30 ++++++--- .../views/components/JudgeScoreDisplay.tsx | 2 +- .../components/PresentationComponent.tsx | 50 ++++++++++++++ .../views/components/SlideDisplay.test.tsx | 13 ---- .../pages/views/components/SlideDisplay.tsx | 20 ------ client/src/pages/views/components/Timer.tsx | 6 +- client/src/pages/views/styled.tsx | 46 +++++++++++-- .../src/reducers/presentationReducer.test.ts | 48 +++---------- client/src/reducers/presentationReducer.ts | 7 -- client/src/sockets.ts | 10 +-- server/app/apis/auth.py | 2 +- server/app/apis/codes.py | 4 +- server/app/apis/competitions.py | 1 - server/app/apis/components.py | 7 +- server/app/core/parsers.py | 10 +-- server/app/core/schemas.py | 8 ++- server/app/core/sockets.py | 6 +- server/configmodule.py | 2 +- 36 files changed, 336 insertions(+), 273 deletions(-) create mode 100644 client/src/pages/presentationEditor/components/SlideDisplay.tsx delete mode 100644 client/src/pages/presentationEditor/components/SlideEditor.tsx create mode 100644 client/src/pages/presentationEditor/components/TextComponentDisplay.tsx create mode 100644 client/src/pages/views/components/PresentationComponent.tsx delete mode 100644 client/src/pages/views/components/SlideDisplay.test.tsx delete mode 100644 client/src/pages/views/components/SlideDisplay.tsx diff --git a/client/src/actions/presentation.test.ts b/client/src/actions/presentation.test.ts index 53ea4847..59d4c967 100644 --- a/client/src/actions/presentation.test.ts +++ b/client/src/actions/presentation.test.ts @@ -2,10 +2,9 @@ import mockedAxios from 'axios' import expect from 'expect' // You can use any testing library import configureMockStore from 'redux-mock-store' import thunk from 'redux-thunk' -import { Slide } from '../interfaces/Slide' +import { Slide } from '../interfaces/ApiModels' import { getPresentationCompetition, - getPresentationTeams, setCurrentSlide, setCurrentSlideNext, setCurrentSlidePrevious, @@ -26,16 +25,6 @@ it('dispatches no actions when failing to get competitions', async () => { expect(console.log).toHaveBeenCalled() }) -it('dispatches no actions when failing to get teams', async () => { - console.log = jest.fn() - ;(mockedAxios.get as jest.Mock).mockImplementation((path: string, params?: any) => { - return Promise.reject(new Error('getting teams failed')) - }) - const store = mockStore({ competitions: { filterParams: [] } }) - await getPresentationTeams('0')(store.dispatch) - expect(store.getActions()).toEqual([]) - expect(console.log).toHaveBeenCalled() -}) it('dispatches correct actions when setting slide', () => { const testSlide: Slide = { competition_id: 0, id: 5, order: 5, timer: 20, title: '' } const expectedActions = [{ type: Types.SET_PRESENTATION_SLIDE, payload: testSlide }] diff --git a/client/src/actions/presentation.ts b/client/src/actions/presentation.ts index 9e7881d8..223b802d 100644 --- a/client/src/actions/presentation.ts +++ b/client/src/actions/presentation.ts @@ -23,21 +23,6 @@ export const getPresentationCompetition = (id: string) => async (dispatch: AppDi }) } -// Get all teams from current presentation competition -export const getPresentationTeams = (id: string) => async (dispatch: AppDispatch) => { - await axios - .get(`/api/competitions/${id}/teams`) - .then((res) => { - dispatch({ - type: Types.SET_PRESENTATION_TEAMS, - payload: res.data.items, - }) - }) - .catch((err) => { - console.log(err) - }) -} - export const setCurrentSlide = (slide: Slide) => (dispatch: AppDispatch) => { dispatch({ type: Types.SET_PRESENTATION_SLIDE, payload: slide }) } diff --git a/client/src/actions/types.ts b/client/src/actions/types.ts index 2e529a88..445a8d86 100644 --- a/client/src/actions/types.ts +++ b/client/src/actions/types.ts @@ -29,7 +29,6 @@ export default { SET_PRESENTATION_SLIDE_PREVIOUS: 'SET_PRESENTATION_SLIDE_PREVIOUS', SET_PRESENTATION_SLIDE_NEXT: 'SET_PRESENTATION_SLIDE_NEXT', SET_PRESENTATION_SLIDE_BY_ORDER: 'SET_PRESENTATION_SLIDE_BY_ORDER', - SET_PRESENTATION_TEAMS: 'SET_PRESENTATION_TEAMS', SET_PRESENTATION_CODE: 'SET_PRESENTATION_CODE', SET_PRESENTATION_TIMER: 'SET_PRESENTATION_TIMER', SET_CITIES: 'SET_CITIES', diff --git a/client/src/interfaces/ApiModels.ts b/client/src/interfaces/ApiModels.ts index 73fe6db6..b3cede4d 100644 --- a/client/src/interfaces/ApiModels.ts +++ b/client/src/interfaces/ApiModels.ts @@ -93,11 +93,5 @@ export interface TextComponent extends Component { } export interface QuestionAlternativeComponent extends Component { - data: { - question_id: number - text: string - value: number - question_alternative_id: number - font: string - } + question_id: number } diff --git a/client/src/pages/presentationEditor/PresentationEditorPage.tsx b/client/src/pages/presentationEditor/PresentationEditorPage.tsx index da18cd52..5ce395e2 100644 --- a/client/src/pages/presentationEditor/PresentationEditorPage.tsx +++ b/client/src/pages/presentationEditor/PresentationEditorPage.tsx @@ -19,9 +19,9 @@ import { getTypes } from '../../actions/typesAction' import { useAppDispatch, useAppSelector } from '../../hooks' import { RichSlide } from '../../interfaces/ApiRichModels' import { RemoveMenuItem } from '../admin/styledComp' -import { Content } from '../views/styled' +import { Content, InnerContent } from '../views/styled' import SettingsPanel from './components/SettingsPanel' -import SlideEditor from './components/SlideEditor' +import SlideDisplay from './components/SlideDisplay' import { CenteredSpinnerContainer, HomeIcon, @@ -249,7 +249,9 @@ const PresentationEditorPage: React.FC = () => { </Drawer> <Content leftDrawerWidth={leftDrawerWidth} rightDrawerWidth={rightDrawerWidth}> - <SlideEditor /> + <InnerContent> + <SlideDisplay editor /> + </InnerContent> </Content> <Menu keepMounted diff --git a/client/src/pages/presentationEditor/components/RndComponent.tsx b/client/src/pages/presentationEditor/components/RndComponent.tsx index c7b5933a..e1cbc0f2 100644 --- a/client/src/pages/presentationEditor/components/RndComponent.tsx +++ b/client/src/pages/presentationEditor/components/RndComponent.tsx @@ -10,16 +10,16 @@ import CheckboxComponent from './CheckboxComponent' import ImageComponentDisplay from './ImageComponentDisplay' import { HoverContainer } from './styled' import FormatAlignCenterIcon from '@material-ui/icons/FormatAlignCenter' +import TextComponentDisplay from './TextComponentDisplay' -type ImageComponentProps = { +type RndComponentProps = { component: Component width: number height: number + scale: number } -const RndComponent = ({ component, width, height }: ImageComponentProps) => { - //Makes scale close to 1, 800 height is approxemately for a 1920 by 1080 monitor - const scale = height / 800 +const RndComponent = ({ component, width, height, scale }: RndComponentProps) => { const [hover, setHover] = useState(false) const [currentPos, setCurrentPos] = useState<Position>({ x: component.x, y: component.y }) const [currentSize, setCurrentSize] = useState<Size>({ w: component.w, h: component.h }) @@ -68,35 +68,18 @@ const RndComponent = ({ component, width, height }: ImageComponentProps) => { const renderInnerComponent = () => { switch (component.type_id) { case ComponentTypes.Text: - return ( - <HoverContainer - hover={hover} - dangerouslySetInnerHTML={{ - __html: `<div style="font-size: ${Math.round(24 * scale)}px;">${(component as TextComponent).text}</div>`, - }} - /> - ) - case ComponentTypes.Image: return ( <HoverContainer hover={hover}> - <img - key={component.id} - src={`/static/images/${(component as ImageComponent).filename}`} - height={currentSize.h * scale} - width={currentSize.w * scale} - draggable={false} - /> + <TextComponentDisplay component={component as TextComponent} scale={scale} /> </HoverContainer> ) case ComponentTypes.Image: return ( <HoverContainer hover={hover}> - <img - key={component.id} - src={`/static/images/${(component as ImageComponent).filename}`} + <ImageComponentDisplay height={currentSize.h * scale} width={currentSize.w * scale} - draggable={false} + component={component as ImageComponent} /> </HoverContainer> ) diff --git a/client/src/pages/presentationEditor/components/SlideDisplay.tsx b/client/src/pages/presentationEditor/components/SlideDisplay.tsx new file mode 100644 index 00000000..6ddb38ef --- /dev/null +++ b/client/src/pages/presentationEditor/components/SlideDisplay.tsx @@ -0,0 +1,67 @@ +import { Button, Typography } from '@material-ui/core' +import React, { useEffect, useLayoutEffect, useRef, useState } from 'react' +import { getTypes } from '../../../actions/typesAction' +import { useAppDispatch, useAppSelector } from '../../../hooks' +import PresentationComponent from '../../views/components/PresentationComponent' +import RndComponent from './RndComponent' +import { SlideEditorContainer, SlideEditorContainerRatio, SlideEditorPaper } from './styled' + +type SlideDisplayProps = { + //Prop to distinguish between editor and active competition + editor?: boolean | undefined +} + +const SlideDisplay = ({ editor }: SlideDisplayProps) => { + const components = useAppSelector((state) => { + if (editor) + return state.editor.competition.slides.find((slide) => slide.id === state.editor.activeSlideId)?.components + return state.presentation.competition.slides.find((slide) => slide.id === state.presentation.slide.id)?.components + }) + const dispatch = useAppDispatch() + const editorPaperRef = useRef<HTMLDivElement>(null) + const [width, setWidth] = useState(0) + const [height, setHeight] = useState(0) + //Makes scale close to 1, 800 height is approxemately for a 1920 by 1080 monitor + const scale = height / 800 + useEffect(() => { + dispatch(getTypes()) + }, []) + + useLayoutEffect(() => { + const updateScale = () => { + if (editorPaperRef.current) { + setWidth(editorPaperRef.current.clientWidth) + setHeight(editorPaperRef.current.clientHeight) + } + } + window.addEventListener('resize', updateScale) + updateScale() + return () => window.removeEventListener('resize', updateScale) + }, []) + return ( + <SlideEditorContainer> + <SlideEditorContainerRatio> + <SlideEditorPaper ref={editorPaperRef}> + {components && + components.map((component) => { + if (editor) + return ( + <RndComponent height={height} width={width} key={component.id} component={component} scale={scale} /> + ) + return ( + <PresentationComponent + height={height} + width={width} + key={component.id} + component={component} + scale={scale} + /> + ) + })} + </SlideEditorPaper> + </SlideEditorContainerRatio> + </SlideEditorContainer> + ) +} + +export default SlideDisplay diff --git a/client/src/pages/presentationEditor/components/SlideEditor.tsx b/client/src/pages/presentationEditor/components/SlideEditor.tsx deleted file mode 100644 index b385bb20..00000000 --- a/client/src/pages/presentationEditor/components/SlideEditor.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import React, { useEffect, useLayoutEffect, useRef, useState } from 'react' -import { getTypes } from '../../../actions/typesAction' -import { useAppDispatch, useAppSelector } from '../../../hooks' -import RndComponent from './RndComponent' -import { SlideEditorContainer, SlideEditorContainerRatio, SlideEditorPaper } from './styled' - -const SlideEditor: React.FC = () => { - const components = useAppSelector( - (state) => - state.editor.competition.slides.find((slide) => slide && slide.id === state.editor.activeSlideId)?.components - ) - const dispatch = useAppDispatch() - const editorPaperRef = useRef<HTMLDivElement>(null) - const [width, setWidth] = useState(0) - const [height, setHeight] = useState(0) - useEffect(() => { - dispatch(getTypes()) - }, []) - - useLayoutEffect(() => { - const updateScale = () => { - if (editorPaperRef.current) { - setWidth(editorPaperRef.current.clientWidth) - setHeight(editorPaperRef.current.clientHeight) - } - } - window.addEventListener('resize', updateScale) - updateScale() - return () => window.removeEventListener('resize', updateScale) - }, []) - return ( - <SlideEditorContainer> - <SlideEditorContainerRatio> - <SlideEditorPaper ref={editorPaperRef}> - {components && - components.map((component) => ( - <RndComponent height={height} width={width} key={component.id} component={component} /> - ))} - </SlideEditorPaper> - </SlideEditorContainerRatio> - </SlideEditorContainer> - ) -} - -export default SlideEditor diff --git a/client/src/pages/presentationEditor/components/TextComponentDisplay.tsx b/client/src/pages/presentationEditor/components/TextComponentDisplay.tsx new file mode 100644 index 00000000..b2e11200 --- /dev/null +++ b/client/src/pages/presentationEditor/components/TextComponentDisplay.tsx @@ -0,0 +1,19 @@ +import React from 'react' +import { ImageComponent, TextComponent } from '../../../interfaces/ApiModels' + +type TextComponentDisplayProps = { + component: TextComponent + scale: number +} + +const ImageComponentDisplay = ({ component, scale }: TextComponentDisplayProps) => { + return ( + <div + dangerouslySetInnerHTML={{ + __html: `<div style="font-size: ${Math.round(24 * scale)}px;">${component.text}</div>`, + }} + /> + ) +} + +export default ImageComponentDisplay diff --git a/client/src/pages/presentationEditor/components/TextComponentEdit.tsx b/client/src/pages/presentationEditor/components/TextComponentEdit.tsx index f50e547a..4aede1b5 100644 --- a/client/src/pages/presentationEditor/components/TextComponentEdit.tsx +++ b/client/src/pages/presentationEditor/components/TextComponentEdit.tsx @@ -38,7 +38,7 @@ const TextComponentEdit = ({ component }: ImageComponentProps) => { window.setTimeout(async () => { console.log('Content was updated on server. id: ', component.id) await axios.put(`/api/competitions/${competitionId}/slides/${activeSlideId}/components/${component.id}`, { - data: { ...component, text: a }, + text: a, }) dispatch(getEditorCompetition(id)) }, 250) @@ -57,8 +57,16 @@ const TextComponentEdit = ({ component }: ImageComponentProps) => { init={{ height: '300px', menubar: false, + font_formats: + ' Andale Mono=andale mono,times; Arial=arial,helvetica,sans-serif;\ + Arial Black=arial black,avant garde; Book Antiqua=book antiqua,palatino; Calibri=calibri;\ + Comic Sans MS=comic sans ms,sans-serif; Courier New=courier new,courier;\ + Georgia=georgia,palatino; Helvetica=helvetica; Impact=impact,chicago; Symbol=symbol;\ + Tahoma=tahoma,arial,helvetica,sans-serif; Terminal=terminal,monaco;\ + Times New Roman=times new roman,times; Trebuchet MS=trebuchet ms,geneva;\ + Verdana=verdana,geneva; Webdings=webdings; Wingdings=wingdings,zapf dingbats', fontsize_formats: '8pt 9pt 10pt 11pt 12pt 14pt 18pt 24pt 30pt 36pt 48pt 60pt 72pt 96pt 120pt 144pt', - content_style: 'body {font-size: 24pt;}', + content_style: 'body {font-size: 24pt; font-family: Calibri;}', plugins: [ 'advlist autolink lists link image charmap print preview anchor', 'searchreplace visualblocks code fullscreen', diff --git a/client/src/pages/presentationEditor/components/styled.tsx b/client/src/pages/presentationEditor/components/styled.tsx index 2a3959c7..f178642c 100644 --- a/client/src/pages/presentationEditor/components/styled.tsx +++ b/client/src/pages/presentationEditor/components/styled.tsx @@ -18,17 +18,15 @@ export const SettingsTab = styled(Tab)` ` export const SlideEditorContainer = styled.div` + overflow: hidden; height: 100%; display: flex; align-items: center; justify-content: center; - background-color: rgba(0, 0, 0, 0.08); ` export const SlideEditorContainerRatio = styled.div` - padding-top: 56.25%; width: 100%; - height: 0; overflow: hidden; padding-top: 56.25%; position: relative; diff --git a/client/src/pages/views/AudienceViewPage.tsx b/client/src/pages/views/AudienceViewPage.tsx index 00a821f3..8d58a364 100644 --- a/client/src/pages/views/AudienceViewPage.tsx +++ b/client/src/pages/views/AudienceViewPage.tsx @@ -1,7 +1,15 @@ import React from 'react' -import SlideDisplay from './components/SlideDisplay' +import SlideDisplay from '../presentationEditor/components/SlideDisplay' +import PresentationComponent from './components/PresentationComponent' +import mockedAxios from 'axios' const AudienceViewPage: React.FC = () => { + const res = { + data: {}, + } + ;(mockedAxios.get as jest.Mock).mockImplementation(() => { + return Promise.resolve(res) + }) return <SlideDisplay /> } diff --git a/client/src/pages/views/JudgeViewPage.test.tsx b/client/src/pages/views/JudgeViewPage.test.tsx index 537dae4c..29de4d12 100644 --- a/client/src/pages/views/JudgeViewPage.test.tsx +++ b/client/src/pages/views/JudgeViewPage.test.tsx @@ -36,7 +36,7 @@ it('renders judge view page', () => { render( <BrowserRouter> <Provider store={store}> - <JudgeViewPage /> + <JudgeViewPage code={''} competitionId={0} /> </Provider> </BrowserRouter> ) diff --git a/client/src/pages/views/JudgeViewPage.tsx b/client/src/pages/views/JudgeViewPage.tsx index 66450f3a..0d16daac 100644 --- a/client/src/pages/views/JudgeViewPage.tsx +++ b/client/src/pages/views/JudgeViewPage.tsx @@ -2,21 +2,17 @@ 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' -import { - getPresentationCompetition, - getPresentationTeams, - setCurrentSlide, - setPresentationCode, -} from '../../actions/presentation' +import { getPresentationCompetition, setCurrentSlide, setPresentationCode } 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' +import PresentationComponent from './components/PresentationComponent' import { useHistory } from 'react-router-dom' import { Content, + InnerContent, JudgeAnswersLabel, JudgeAppBar, JudgeQuestionsLabel, @@ -24,9 +20,10 @@ import { LeftDrawer, RightDrawer, } from './styled' +import SlideDisplay from '../presentationEditor/components/SlideDisplay' const leftDrawerWidth = 150 -const rightDrawerWidth = 390 +const rightDrawerWidth = 700 const useStyles = makeStyles((theme: Theme) => createStyles({ @@ -39,14 +36,18 @@ const useStyles = makeStyles((theme: Theme) => toolbar: theme.mixins.toolbar, }) ) +type JudgeViewPageProps = { + //Prop to distinguish between editor and active competition + competitionId: number + code: string +} -const JudgeViewPage: React.FC = () => { +const JudgeViewPage = ({ competitionId, code }: JudgeViewPageProps) => { const classes = useStyles() const history = useHistory() - const { id, code }: ViewParams = useParams() const dispatch = useAppDispatch() const [activeSlideIndex, setActiveSlideIndex] = useState<number>(0) - const teams = useAppSelector((state) => state.presentation.teams) + const teams = useAppSelector((state) => state.presentation.competition.teams) const slides = useAppSelector((state) => state.presentation.competition.slides) const handleSelectSlide = (index: number) => { setActiveSlideIndex(index) @@ -54,15 +55,14 @@ const JudgeViewPage: React.FC = () => { } useEffect(() => { socket_connect() - dispatch(getPresentationCompetition(id)) - dispatch(getPresentationTeams(id)) + dispatch(getPresentationCompetition(competitionId.toString())) dispatch(setPresentationCode(code)) //hides the url so people can't sneak peak history.push('judge') }, []) return ( - <div> + <div style={{ height: '100%' }}> <JudgeAppBar position="fixed"> <JudgeToolbar> <JudgeQuestionsLabel variant="h5">Frågor</JudgeQuestionsLabel> @@ -103,17 +103,20 @@ const JudgeViewPage: React.FC = () => { > <div className={classes.toolbar} /> <List> - {teams.map((answer, index) => ( - <div key={answer.name}> - <JudgeScoreDisplay teamIndex={index} /> - <Divider /> - </div> - ))} + {teams && + teams.map((answer, index) => ( + <div key={answer.name}> + <JudgeScoreDisplay teamIndex={index} /> + <Divider /> + </div> + ))} </List> </RightDrawer> + <div style={{ height: 64 }} /> <Content leftDrawerWidth={leftDrawerWidth} rightDrawerWidth={rightDrawerWidth}> - <div className={classes.toolbar} /> - <SlideDisplay /> + <InnerContent> + <SlideDisplay /> + </InnerContent> </Content> </div> ) diff --git a/client/src/pages/views/ParticipantViewPage.test.tsx b/client/src/pages/views/ParticipantViewPage.test.tsx index c0950b3c..e25ab6b9 100644 --- a/client/src/pages/views/ParticipantViewPage.test.tsx +++ b/client/src/pages/views/ParticipantViewPage.test.tsx @@ -4,8 +4,15 @@ import { Provider } from 'react-redux' import { BrowserRouter } from 'react-router-dom' import store from '../../store' import ParticipantViewPage from './ParticipantViewPage' +import mockedAxios from 'axios' it('renders participant view page', () => { + const res = { + data: {}, + } + ;(mockedAxios.get as jest.Mock).mockImplementation(() => { + return Promise.resolve(res) + }) render( <BrowserRouter> <Provider store={store}> diff --git a/client/src/pages/views/ParticipantViewPage.tsx b/client/src/pages/views/ParticipantViewPage.tsx index f531ad76..ffee1ee1 100644 --- a/client/src/pages/views/ParticipantViewPage.tsx +++ b/client/src/pages/views/ParticipantViewPage.tsx @@ -1,14 +1,27 @@ import React, { useEffect } from 'react' -import SlideDisplay from './components/SlideDisplay' +import PresentationComponent from './components/PresentationComponent' import { useHistory } from 'react-router-dom' +import SlideDisplay from '../presentationEditor/components/SlideDisplay' +import { ParticipantContainer } from './styled' +import { socketJoinPresentation, socket_connect } from '../../sockets' +import { useAppSelector } from '../../hooks' const ParticipantViewPage: React.FC = () => { const history = useHistory() + const code = useAppSelector((state) => state.presentation.code) useEffect(() => { //hides the url so people can't sneak peak history.push('participant') + if (code && code !== '') { + socket_connect() + socketJoinPresentation() + } }, []) - return <SlideDisplay /> + return ( + <ParticipantContainer> + <SlideDisplay /> + </ParticipantContainer> + ) } export default ParticipantViewPage diff --git a/client/src/pages/views/PresenterViewPage.tsx b/client/src/pages/views/PresenterViewPage.tsx index 1abeee92..f221f5a9 100644 --- a/client/src/pages/views/PresenterViewPage.tsx +++ b/client/src/pages/views/PresenterViewPage.tsx @@ -20,7 +20,7 @@ import ChevronRightIcon from '@material-ui/icons/ChevronRight' 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 { getPresentationCompetition, setPresentationCode } from '../../actions/presentation' import { useAppDispatch, useAppSelector } from '../../hooks' import { ViewParams } from '../../interfaces/ViewParams' import { @@ -32,13 +32,16 @@ import { socketStartTimer, socket_connect, } from '../../sockets' -import SlideDisplay from './components/SlideDisplay' +import SlideDisplay from '../presentationEditor/components/SlideDisplay' +import PresentationComponent from './components/PresentationComponent' import Timer from './components/Timer' import { PresenterButton, PresenterContainer, + PresenterContent, PresenterFooter, PresenterHeader, + PresenterInnerContent, SlideCounter, ToolBarContainer, } from './styled' @@ -52,7 +55,7 @@ const PresenterViewPage: React.FC = () => { const [openAlert, setOpen] = React.useState(false) const theme = useTheme() const fullScreen = useMediaQuery(theme.breakpoints.down('sm')) - const teams = useAppSelector((state) => state.presentation.teams) + const teams = useAppSelector((state) => state.presentation.competition.teams) const [anchorEl, setAnchorEl] = React.useState<HTMLButtonElement | null>(null) const { id, code }: ViewParams = useParams() const presentation = useAppSelector((state) => state.presentation) @@ -61,7 +64,6 @@ const PresenterViewPage: React.FC = () => { useEffect(() => { dispatch(getPresentationCompetition(id)) - dispatch(getPresentationTeams(id)) dispatch(setPresentationCode(code)) socket_connect() socketSetSlide // Behövs denna? @@ -132,7 +134,13 @@ const PresenterViewPage: React.FC = () => { </Typography> </SlideCounter> </PresenterHeader> - <SlideDisplay /> + <div style={{ height: 0, paddingTop: 120 }} /> + <PresenterContent> + <PresenterInnerContent> + <SlideDisplay /> + </PresenterInnerContent> + </PresenterContent> + <div style={{ height: 0, paddingTop: 140 }} /> <PresenterFooter> <ToolBarContainer> <Tooltip title="Previous Slide" arrow> @@ -203,9 +211,7 @@ const PresenterViewPage: React.FC = () => { {/** TODO: * Fix scoreboard */} - {teams.map((team) => ( - <ListItem key={team.id}>{team.name} score: 20</ListItem> - ))} + {teams && teams.map((team) => <ListItem key={team.id}>{team.name} score: 20</ListItem>)} </List> </Popover> </PresenterContainer> diff --git a/client/src/pages/views/ViewSelectPage.test.tsx b/client/src/pages/views/ViewSelectPage.test.tsx index 83b71db0..1843a186 100644 --- a/client/src/pages/views/ViewSelectPage.test.tsx +++ b/client/src/pages/views/ViewSelectPage.test.tsx @@ -12,9 +12,18 @@ it('renders view select page', async () => { const res = { data: {}, } + const compRes = { + data: { + id: 2, + slides: [{ id: 4 }], + }, + } ;(mockedAxios.post as jest.Mock).mockImplementation(() => { return Promise.resolve(res) }) + ;(mockedAxios.get as jest.Mock).mockImplementation(() => { + return Promise.resolve(compRes) + }) render( <BrowserRouter> <Provider store={store}> diff --git a/client/src/pages/views/ViewSelectPage.tsx b/client/src/pages/views/ViewSelectPage.tsx index 3c3599ed..686ce4f1 100644 --- a/client/src/pages/views/ViewSelectPage.tsx +++ b/client/src/pages/views/ViewSelectPage.tsx @@ -9,12 +9,14 @@ import axios from 'axios' import PresenterViewPage from './PresenterViewPage' import JudgeViewPage from './JudgeViewPage' import AudienceViewPage from './AudienceViewPage' -import { useAppSelector } from '../../hooks' +import { useAppDispatch, useAppSelector } from '../../hooks' +import { getPresentationCompetition, setPresentationCode } from '../../actions/presentation' interface ViewSelectParams { code: string } const ViewSelectPage: React.FC = () => { + const dispatch = useAppDispatch() const [loading, setLoading] = useState(true) const [error, setError] = useState(false) const [viewTypeId, setViewTypeId] = useState(undefined) @@ -29,7 +31,7 @@ const ViewSelectPage: React.FC = () => { case 'Team': return <ParticipantViewPage /> case 'Judge': - return <JudgeViewPage /> + return <JudgeViewPage code={code} competitionId={competitionId} /> case 'Audience': return <AudienceViewPage /> default: @@ -43,8 +45,10 @@ const ViewSelectPage: React.FC = () => { .post('/api/auth/login/code', { code }) .then((response) => { setLoading(false) - setViewTypeId(response.data[0].view_type_id) - setCompetitionId(response.data[0].competition_id) + setViewTypeId(response.data.view_type_id) + setCompetitionId(response.data.competition_id) + dispatch(getPresentationCompetition(response.data.competition_id)) + dispatch(setPresentationCode(code)) }) .catch(() => { setLoading(false) @@ -53,13 +57,17 @@ const ViewSelectPage: React.FC = () => { }, []) return ( - <ViewSelectContainer> - <ViewSelectButtonGroup> - {loading && <CircularProgress />} - {!loading && renderView(viewTypeId)} - {error && <Typography>Något gick fel, dubbelkolla koden och försök igen</Typography>} - </ViewSelectButtonGroup> - </ViewSelectContainer> + <> + {!loading && renderView(viewTypeId)} + {(loading || error) && ( + <ViewSelectContainer> + <ViewSelectButtonGroup> + {loading && <CircularProgress />} + {error && <Typography>Något gick fel, dubbelkolla koden och försök igen</Typography>} + </ViewSelectButtonGroup> + </ViewSelectContainer> + )} + </> ) } diff --git a/client/src/pages/views/components/JudgeScoreDisplay.tsx b/client/src/pages/views/components/JudgeScoreDisplay.tsx index ae4d8ab3..6308e39b 100644 --- a/client/src/pages/views/components/JudgeScoreDisplay.tsx +++ b/client/src/pages/views/components/JudgeScoreDisplay.tsx @@ -9,7 +9,7 @@ type ScoreDisplayProps = { const questionMaxScore = 5 const JudgeScoreDisplay = ({ teamIndex }: ScoreDisplayProps) => { - const currentTeam = useAppSelector((state) => state.presentation.teams[teamIndex]) + const currentTeam = useAppSelector((state) => state.presentation.competition.teams[teamIndex]) return ( <ScoreDisplayContainer> <ScoreDisplayHeader> diff --git a/client/src/pages/views/components/PresentationComponent.tsx b/client/src/pages/views/components/PresentationComponent.tsx new file mode 100644 index 00000000..cb95576f --- /dev/null +++ b/client/src/pages/views/components/PresentationComponent.tsx @@ -0,0 +1,50 @@ +import { Typography } from '@material-ui/core' +import React from 'react' +import { Rnd } from 'react-rnd' +import { ComponentTypes } from '../../../enum/ComponentTypes' +import { useAppSelector } from '../../../hooks' +import { Component, ImageComponent, TextComponent } from '../../../interfaces/ApiModels' +import ImageComponentDisplay from '../../presentationEditor/components/ImageComponentDisplay' +import TextComponentDisplay from '../../presentationEditor/components/TextComponentDisplay' +import { SlideContainer } from './styled' + +type PresentationComponentProps = { + component: Component + width: number + height: number + scale: number +} + +const PresentationComponent = ({ component, width, height, scale }: PresentationComponentProps) => { + const renderInnerComponent = () => { + switch (component.type_id) { + case ComponentTypes.Text: + return <TextComponentDisplay component={component as TextComponent} scale={scale} /> + case ComponentTypes.Image: + return ( + <ImageComponentDisplay + height={component.h * scale} + width={component.w * scale} + component={component as ImageComponent} + /> + ) + default: + break + } + } + return ( + <Rnd + minWidth={75 * scale} + minHeight={75 * scale} + disableDragging={true} + bounds="parent" + //Multiply by scale to show components correctly for current screen size + size={{ width: component.w * scale, height: component.h * scale }} + position={{ x: component.x * scale, y: component.y * scale }} + > + {renderInnerComponent()} + </Rnd> + ) +} + +export default PresentationComponent diff --git a/client/src/pages/views/components/SlideDisplay.test.tsx b/client/src/pages/views/components/SlideDisplay.test.tsx deleted file mode 100644 index 1a661d33..00000000 --- a/client/src/pages/views/components/SlideDisplay.test.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import { render } from '@testing-library/react' -import React from 'react' -import { Provider } from 'react-redux' -import store from '../../../store' -import SlideDisplay from './SlideDisplay' - -it('renders slide display', () => { - render( - <Provider store={store}> - <SlideDisplay /> - </Provider> - ) -}) diff --git a/client/src/pages/views/components/SlideDisplay.tsx b/client/src/pages/views/components/SlideDisplay.tsx deleted file mode 100644 index 7ecffac5..00000000 --- a/client/src/pages/views/components/SlideDisplay.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import { Typography } from '@material-ui/core' -import React from 'react' -import { useAppSelector } from '../../../hooks' -import { SlideContainer } from './styled' - -const SlideDisplay: React.FC = () => { - const currentSlide = useAppSelector((state) => state.presentation.slide) - - return ( - <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> - ) -} - -export default SlideDisplay diff --git a/client/src/pages/views/components/Timer.tsx b/client/src/pages/views/components/Timer.tsx index 0cbd1fdf..8d3b9f70 100644 --- a/client/src/pages/views/components/Timer.tsx +++ b/client/src/pages/views/components/Timer.tsx @@ -36,11 +36,7 @@ const Timer: React.FC = (props: any) => { } }, [props.timer.enabled]) - return ( - <> - <div>{props.timer.value}</div> - </> - ) + return <div>{props.timer.value}</div> } export default connect(mapStateToProps, mapDispatchToProps)(Timer) diff --git a/client/src/pages/views/styled.tsx b/client/src/pages/views/styled.tsx index 1f3a61c6..17d09581 100644 --- a/client/src/pages/views/styled.tsx +++ b/client/src/pages/views/styled.tsx @@ -15,7 +15,7 @@ export const JudgeQuestionsLabel = styled(Typography)` ` export const JudgeAnswersLabel = styled(Typography)` - margin-right: 160px; + margin-right: 304px; ` export const ViewSelectContainer = styled.div` @@ -38,13 +38,18 @@ export const ViewSelectButtonGroup = styled.div` export const PresenterHeader = styled.div` display: flex; justify-content: space-between; - position: fixed; + height: 120px; width: 100%; + position: absolute; ` export const PresenterFooter = styled.div` display: flex; justify-content: space-between; + height: 140px; + position: absolute; + bottom: 0; + width: 100%; ` export const PresenterButton = styled(Button)` @@ -65,6 +70,7 @@ export const PresenterContainer = styled.div` display: flex; flex-direction: column; justify-content: space-between; + align-items: center; height: 100%; ` @@ -103,8 +109,40 @@ interface ContentProps { } export const Content = styled.div<ContentProps>` + width: 100%; + height: 100%; + max-width: calc(100% - ${(props) => (props ? props.leftDrawerWidth + props.rightDrawerWidth : 0)}px); + max-height: calc(100% - 64px); margin-left: ${(props) => (props ? props.leftDrawerWidth : 0)}px; margin-right: ${(props) => (props ? props.rightDrawerWidth : 0)}px; - width: calc(100% - ${(props) => (props ? props.leftDrawerWidth + props.rightDrawerWidth : 0)}px); - height: calc(100% - 64px); + display: flex; + justify-content: center; + background-color: rgba(0, 0, 0, 0.08); +` + +export const InnerContent = styled.div` + width: 100%; + /* Makes sure width is not bigger than where a 16:9 display can fit + without overlapping with header */ + max-width: calc(((100vh - 64px) / 9) * 16); +` + +export const PresenterContent = styled.div` + height: 100%; + width: 100%; + display: flex; + justify-content: center; + background-color: rgba(0, 0, 0, 0.08); +` + +export const PresenterInnerContent = styled.div` + height: 100%; + width: 100%; + /* Makes sure width is not bigger than where a 16:9 display can fit + without overlapping with header and footer */ + max-width: calc(((100vh - 260px) / 9) * 16); +` + +export const ParticipantContainer = styled.div` + max-width: calc((100vh / 9) * 16); ` diff --git a/client/src/reducers/presentationReducer.test.ts b/client/src/reducers/presentationReducer.test.ts index a155eab5..ee08e068 100644 --- a/client/src/reducers/presentationReducer.test.ts +++ b/client/src/reducers/presentationReducer.test.ts @@ -1,6 +1,6 @@ import Types from '../actions/types' import { RichSlide } from '../interfaces/ApiRichModels' -import { Slide } from '../interfaces/Slide' +import { Slide } from '../interfaces/ApiModels' import presentationReducer from './presentationReducer' const initialState = { @@ -19,7 +19,6 @@ const initialState = { timer: 0, title: '', }, - teams: [], code: '', timer: { enabled: false, @@ -41,7 +40,6 @@ it('should handle SET_PRESENTATION_COMPETITION', () => { }, slides: [{ id: 20 }], year: 1999, - teams: [], } expect( presentationReducer(initialState, { @@ -51,32 +49,6 @@ it('should handle SET_PRESENTATION_COMPETITION', () => { ).toEqual({ competition: testCompetition, slide: testCompetition.slides[0], - teams: initialState.teams, - code: initialState.code, - timer: initialState.timer, - }) -}) - -it('should handle SET_PRESENTATION_TEAMS', () => { - const testTeams = [ - { - name: 'testTeamName1', - id: 3, - }, - { - name: 'testTeamName2', - id: 5, - }, - ] - expect( - presentationReducer(initialState, { - type: Types.SET_PRESENTATION_TEAMS, - payload: testTeams, - }) - ).toEqual({ - competition: initialState.competition, - slide: initialState.slide, - teams: testTeams, code: initialState.code, timer: initialState.timer, }) @@ -100,7 +72,6 @@ it('should handle SET_PRESENTATION_SLIDE', () => { ).toEqual({ competition: initialState.competition, slide: testSlide, - teams: initialState.teams, code: initialState.code, timer: initialState.timer, }) @@ -115,8 +86,8 @@ describe('should handle SET_PRESENTATION_SLIDE_PREVIOUS', () => { { competition_id: 0, order: 0 }, { competition_id: 0, order: 1 }, ] as RichSlide[], + teams: [], }, - teams: initialState.teams, slide: { competition_id: 0, order: 1 } as Slide, code: initialState.code, timer: initialState.timer, @@ -128,7 +99,7 @@ describe('should handle SET_PRESENTATION_SLIDE_PREVIOUS', () => { ).toEqual({ competition: testPresentationState.competition, slide: testPresentationState.competition.slides[0], - teams: testPresentationState.teams, + code: initialState.code, timer: initialState.timer, }) @@ -141,8 +112,8 @@ describe('should handle SET_PRESENTATION_SLIDE_PREVIOUS', () => { { competition_id: 0, order: 0 }, { competition_id: 0, order: 1 }, ] as RichSlide[], + teams: [], }, - teams: initialState.teams, slide: { competition_id: 0, order: 0 } as Slide, code: initialState.code, timer: initialState.timer, @@ -154,7 +125,6 @@ describe('should handle SET_PRESENTATION_SLIDE_PREVIOUS', () => { ).toEqual({ competition: testPresentationState.competition, slide: testPresentationState.competition.slides[0], - teams: testPresentationState.teams, code: initialState.code, timer: initialState.timer, }) @@ -170,9 +140,11 @@ describe('should handle SET_PRESENTATION_SLIDE_NEXT', () => { { competition_id: 0, order: 0 }, { competition_id: 0, order: 1 }, ] as RichSlide[], + teams: [], }, - teams: initialState.teams, slide: { competition_id: 0, order: 0 } as Slide, + code: initialState.code, + timer: initialState.timer, } expect( presentationReducer(testPresentationState, { @@ -181,7 +153,8 @@ describe('should handle SET_PRESENTATION_SLIDE_NEXT', () => { ).toEqual({ competition: testPresentationState.competition, slide: testPresentationState.competition.slides[1], - teams: testPresentationState.teams, + code: initialState.code, + timer: initialState.timer, }) }) it('by not changing slide if there is no next one', () => { @@ -192,8 +165,8 @@ describe('should handle SET_PRESENTATION_SLIDE_NEXT', () => { { competition_id: 0, order: 0 }, { competition_id: 0, order: 1 }, ] as RichSlide[], + teams: [], }, - teams: initialState.teams, slide: { competition_id: 0, order: 1 } as Slide, } expect( @@ -203,7 +176,6 @@ describe('should handle SET_PRESENTATION_SLIDE_NEXT', () => { ).toEqual({ competition: testPresentationState.competition, slide: testPresentationState.competition.slides[1], - teams: testPresentationState.teams, }) }) }) diff --git a/client/src/reducers/presentationReducer.ts b/client/src/reducers/presentationReducer.ts index 5ba3ac5b..0b16c023 100644 --- a/client/src/reducers/presentationReducer.ts +++ b/client/src/reducers/presentationReducer.ts @@ -7,7 +7,6 @@ import { RichCompetition } from './../interfaces/ApiRichModels' interface PresentationState { competition: RichCompetition slide: Slide - teams: Team[] code: string timer: Timer } @@ -28,7 +27,6 @@ const initialState: PresentationState = { timer: 0, title: '', }, - teams: [], code: '', timer: { enabled: false, @@ -44,11 +42,6 @@ export default function (state = initialState, action: AnyAction) { slide: action.payload.slides[0] as Slide, competition: action.payload as RichCompetition, } - case Types.SET_PRESENTATION_TEAMS: - return { - ...state, - teams: action.payload as Team[], - } case Types.SET_PRESENTATION_CODE: return { ...state, diff --git a/client/src/sockets.ts b/client/src/sockets.ts index 54f84c39..4392021d 100644 --- a/client/src/sockets.ts +++ b/client/src/sockets.ts @@ -38,32 +38,26 @@ 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') + socket.emit('join_presentation', { code: store.getState().presentation.code }) // TODO: Send code gotten from auth/login/<code> api call } 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) => { if (slide_order < 0 || store.getState().presentation.competition.slides.length <= slide_order) { - console.log('CANT CHANGE TO NON EXISTENT SLIDE') return } @@ -74,7 +68,6 @@ 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, @@ -82,6 +75,5 @@ export const socketSetTimer = (timer: Timer) => { } export const socketStartTimer = () => { - console.log('START TIMER') socketSetTimer({ enabled: true, value: store.getState().presentation.timer.value }) } diff --git a/server/app/apis/auth.py b/server/app/apis/auth.py index 87d7f1d1..e043f1b1 100644 --- a/server/app/apis/auth.py +++ b/server/app/apis/auth.py @@ -80,7 +80,7 @@ class AuthLoginCode(Resource): api.abort(codes.BAD_REQUEST, "Invalid code") item_code = dbc.get.code_by_code(code) - return item_response(CodeDTO.schema.dump(item_code)), codes.OK + return item_response(CodeDTO.schema.dump(item_code)) @api.route("/logout") diff --git a/server/app/apis/codes.py b/server/app/apis/codes.py index 2a2eea5c..7d58aef0 100644 --- a/server/app/apis/codes.py +++ b/server/app/apis/codes.py @@ -18,7 +18,7 @@ class CodesList(Resource): @check_jwt(editor=True) def get(self, competition_id): items = dbc.get.code_list(competition_id) - return list_response(list_schema.dump(items), len(items)), codes.OK + return list_response(list_schema.dump(items), len(items)) @api.route("/<code_id>") @@ -29,4 +29,4 @@ class CodesById(Resource): item = dbc.get.one(Code, code_id) item.code = dbc.utils.generate_unique_code() dbc.utils.commit_and_refresh(item) - return item_response(schema.dump(item)), codes.OK + return item_response(schema.dump(item)) diff --git a/server/app/apis/competitions.py b/server/app/apis/competitions.py index 4a6bf79d..6421ba34 100644 --- a/server/app/apis/competitions.py +++ b/server/app/apis/competitions.py @@ -32,7 +32,6 @@ class CompetitionsList(Resource): @api.route("/<competition_id>") @api.param("competition_id") class Competitions(Resource): - @check_jwt(editor=True) def get(self, competition_id): item = dbc.get.competition(competition_id) diff --git a/server/app/apis/components.py b/server/app/apis/components.py index 23d25025..806e65e9 100644 --- a/server/app/apis/components.py +++ b/server/app/apis/components.py @@ -2,7 +2,7 @@ import app.core.http_codes as codes import app.database.controller as dbc from app.apis import check_jwt, item_response, list_response from app.core.dto import ComponentDTO -from app.core.parsers import component_create_parser, component_parser +from app.core.parsers import component_create_parser, component_edit_parser, component_parser from app.database.models import Competition, Component from flask.globals import request from flask_jwt_extended import jwt_required @@ -23,9 +23,10 @@ class ComponentByID(Resource): @check_jwt(editor=True) def put(self, competition_id, slide_id, component_id): - args = component_parser.parse_args() + args = component_edit_parser.parse_args(strict=True) item = dbc.get.component(competition_id, slide_id, component_id) - item = dbc.edit.default(item, **args) + args_without_none = {key: value for key, value in args.items() if value is not None} + item = dbc.edit.default(item, **args_without_none) return item_response(schema.dump(item)) @check_jwt(editor=True) diff --git a/server/app/core/parsers.py b/server/app/core/parsers.py index 1e9813e9..3420e99e 100644 --- a/server/app/core/parsers.py +++ b/server/app/core/parsers.py @@ -97,13 +97,13 @@ component_parser.add_argument("x", type=str, default=None, location="json") component_parser.add_argument("y", type=int, default=None, location="json") component_parser.add_argument("w", type=int, default=None, location="json") component_parser.add_argument("h", type=int, default=None, location="json") -# component_parser.add_argument("data", type=dict, default=None, location="json") -component_create_parser = component_parser.copy() -# component_create_parser.replace_argument("data", type=dict, required=True, location="json") +component_edit_parser = component_parser.copy() +component_edit_parser.add_argument("text", type=str, location="json") +component_edit_parser.add_argument("media_id", type=str, location="json") + +component_create_parser = component_edit_parser.copy() component_create_parser.add_argument("type_id", type=int, required=True, location="json") -component_create_parser.add_argument("text", type=str, required=False, location="json") -component_create_parser.add_argument("media_id", type=str, required=False, location="json") login_code_parser = reqparse.RequestParser() login_code_parser.add_argument("code", type=str, location="json") diff --git a/server/app/core/schemas.py b/server/app/core/schemas.py index 4e440fc4..1704b651 100644 --- a/server/app/core/schemas.py +++ b/server/app/core/schemas.py @@ -1,5 +1,5 @@ from marshmallow.decorators import pre_load -from marshmallow.decorators import pre_dump +from marshmallow.decorators import pre_dump, post_dump import app.database.models as models from app.core import ma from marshmallow_sqlalchemy import fields @@ -156,6 +156,12 @@ class ComponentSchema(BaseSchema): class Meta(BaseSchema.Meta): model = models.Component + @post_dump + def handle_filename(self, data, *args, **kwargs): + if data["filename"] == "": + del data["filename"] + return data + id = ma.auto_field() x = ma.auto_field() y = ma.auto_field() diff --git a/server/app/core/sockets.py b/server/app/core/sockets.py index 04099ff2..00b2ddf5 100644 --- a/server/app/core/sockets.py +++ b/server/app/core/sockets.py @@ -93,11 +93,7 @@ def join_presentation(data): logger.error(f"Client '{request.sid}' failed to join presentation with code '{code}', no such code exists") return - competition_id = ( - item_code.pointer - if item_code.view_type_id != team_view_id - else db.session.query(Team).filter(Team.id == item_code.pointer).one().competition_id - ) + competition_id = item_code.competition_id if competition_id not in presentations: logger.error(f"Client '{request.sid}' failed to join presentation '{competition_id}', no such presentation exists") diff --git a/server/configmodule.py b/server/configmodule.py index 8c07211e..9e78490d 100644 --- a/server/configmodule.py +++ b/server/configmodule.py @@ -20,7 +20,7 @@ class Config: class DevelopmentConfig(Config): - DEBUG = True + DEBUG = False SQLALCHEMY_ECHO = False # HOST = "localhost" # PORT = 5432 -- GitLab