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