From b4b62fde8a7f9e625876fbff8c6cd20dfe10b0f5 Mon Sep 17 00:00:00 2001
From: Josef Olsson <josol381@student.liu.se>
Date: Tue, 20 Apr 2021 10:45:28 +0200
Subject: [PATCH 01/13] Upload file from presentation editor

---
 .vscode/settings.json                         |  1 +
 .../components/SlideSettings.tsx              | 23 +++++++++++--------
 2 files changed, 14 insertions(+), 10 deletions(-)

diff --git a/.vscode/settings.json b/.vscode/settings.json
index 99228c86..549bb40e 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -48,4 +48,5 @@
     "search.exclude": {
         "**/env": true
     },
+    "python.pythonPath": "server\\env\\Scripts\\python.exe",
 }
\ No newline at end of file
diff --git a/client/src/pages/presentationEditor/components/SlideSettings.tsx b/client/src/pages/presentationEditor/components/SlideSettings.tsx
index c61d478e..fcaa028a 100644
--- a/client/src/pages/presentationEditor/components/SlideSettings.tsx
+++ b/client/src/pages/presentationEditor/components/SlideSettings.tsx
@@ -131,19 +131,22 @@ const SlideSettings: React.FC = () => {
       .catch(console.log)
   }
 
-  const handleFileSelected = (e: React.ChangeEvent<HTMLInputElement>): void => {
+  const uploadFile = async (formData: FormData) => {
+    await axios
+      .post(`/media/images`, formData)
+      .then(() => {
+        dispatch(getEditorCompetition(id))
+      })
+      .catch(console.log)
+  }
+
+  const handleFileSelected = async (e: React.ChangeEvent<HTMLInputElement>) => {
     if (e.target.files !== null && e.target.files[0]) {
       const files = Array.from(e.target.files)
       const file = files[0]
-      const reader = new FileReader()
-      reader.readAsDataURL(file)
-      reader.onload = function () {
-        console.log(reader.result)
-        // TODO: Send image to back-end (remove console.log)
-      }
-      reader.onerror = function (error) {
-        console.log('Error: ', error)
-      }
+      const formData = new FormData()
+      formData.append('image', file)
+      await uploadFile(formData)
     }
   }
 
-- 
GitLab


From b0173a80896287507317247c80cc5727bd195a85 Mon Sep 17 00:00:00 2001
From: Sebastian Karlsson <sebka991@student.liu.se>
Date: Wed, 21 Apr 2021 14:38:21 +0200
Subject: [PATCH 02/13] Add thumbnail

---
 .../components/SlideSettings.tsx              | 75 +++++++++++++------
 .../components/TextComponentDisplay.tsx       |  2 +-
 2 files changed, 54 insertions(+), 23 deletions(-)

diff --git a/client/src/pages/presentationEditor/components/SlideSettings.tsx b/client/src/pages/presentationEditor/components/SlideSettings.tsx
index fcaa028a..11eea562 100644
--- a/client/src/pages/presentationEditor/components/SlideSettings.tsx
+++ b/client/src/pages/presentationEditor/components/SlideSettings.tsx
@@ -16,7 +16,7 @@ import { createStyles, makeStyles, Theme, withStyles } from '@material-ui/core/s
 import CloseIcon from '@material-ui/icons/Close'
 import MoreHorizOutlinedIcon from '@material-ui/icons/MoreHorizOutlined'
 import axios from 'axios'
-import React, { useState } from 'react'
+import React from 'react'
 import { useParams } from 'react-router-dom'
 import { getEditorCompetition } from '../../../actions/editor'
 import { useAppDispatch, useAppSelector } from '../../../hooks'
@@ -101,14 +101,26 @@ const SlideSettings: React.FC = () => {
         ?.components.filter((component) => component.type_id === 1) as TextComponent[]
   )
 
-  const pictureList = [
-    { id: 'picture1', name: 'Picture1.jpeg' },
-    { id: 'picture2', name: 'Picture2.jpeg' },
-  ]
-  const handleClosePictureClick = (id: string) => {
-    setPictures(pictures.filter((item) => item.id !== id)) //Will not be done like this when api is used
-  }
-  const [pictures, setPictures] = useState(pictureList)
+  /*const pictures = useAppSelector(
+    (state) =>
+      state.editor.competition.slides
+        .find((slide) => slide.id === state.editor.activeSlideId)
+        ?.components.filter((component) => component.type_id === 2) as ImageComponent[]
+  )*/
+
+  //const [pictures, setPictures] = useState(pictureList)
+  //pictures.map((picture) => competition.slides[0].components.push(picture))
+
+  //const handleClosePictureClick = (id: number) => {
+  //  setPictures(pictures.filter((item) => item.id !== id)) //Will not be done like this when api is used
+  //Ta bort från servern
+  //Ta bort Media
+  //Ta bort ImageComponent
+  //}
+
+  const pictures = [{ id: 'picture1', name: 'Wallgren.png' }]
+
+  //const [pictures, setPictures] = useState(pictureList)
 
   const updateSlideType = async (event: React.ChangeEvent<{ value: unknown }>) => {
     await axios
@@ -140,6 +152,15 @@ const SlideSettings: React.FC = () => {
       .catch(console.log)
   }
 
+  /*const createImageComponent = async () => {
+    await axios
+      .post(`/competitions/`, formData)
+      .then(() => {
+        dispatch(getEditorCompetition(id))
+      })
+      .catch(console.log)
+  }*/
+
   const handleFileSelected = async (e: React.ChangeEvent<HTMLInputElement>) => {
     if (e.target.files !== null && e.target.files[0]) {
       const files = Array.from(e.target.files)
@@ -147,6 +168,11 @@ const SlideSettings: React.FC = () => {
       const formData = new FormData()
       formData.append('image', file)
       await uploadFile(formData)
+
+      //createImageComponent()
+      // Skapa en ImageComponent
+      // Lägg in filnamnet i data hos komponenten
+      //
     }
   }
 
@@ -154,6 +180,9 @@ const SlideSettings: React.FC = () => {
     console.log('Add text component')
     // TODO: post the new text]
     // setTexts([...texts, { id: 'newText', name: 'New Text' }])
+    console.log(texts)
+    console.log('--------')
+    console.log(pictures)
   }
 
   const GreenCheckbox = withStyles({
@@ -254,19 +283,21 @@ const SlideSettings: React.FC = () => {
         <ListItem divider>
           <ListItemText className={classes.textCenter} primary="Bilder" />
         </ListItem>
-        {pictures.map((picture) => (
-          <div key={picture.id}>
-            <ListItem divider button>
-              <img
-                id="temp source, todo: add image source to elements of pictureList"
-                src="https://i1.wp.com/stickoutmedia.se/wp-content/uploads/2021/01/placeholder-3.png?ssl=1"
-                className={classes.importedImage}
-              />
-              <ListItemText className={classes.textCenter} primary={picture.name} />
-              <CloseIcon onClick={() => handleClosePictureClick(picture.id)} />
-            </ListItem>
-          </div>
-        ))}
+        {pictures &&
+          pictures.map((picture) => (
+            <div key={picture.id}>
+              <ListItem divider button>
+                <img
+                  id="temp source, todo: add image source to elements of pictureList"
+                  src={`http://localhost:5000/static/images/${picture.name}`}
+                  //{picture.data.media_id}
+                  className={classes.importedImage}
+                />
+                <ListItemText className={classes.textCenter} primary={picture.name} />
+                <CloseIcon /*onClick={() => handleClosePictureClick(picture.id)}*/ />
+              </ListItem>
+            </div>
+          ))}
         <ListItem className={classes.center} button>
           <HiddenInput accept="image/*" id="contained-button-file" multiple type="file" onChange={handleFileSelected} />
 
diff --git a/client/src/pages/presentationEditor/components/TextComponentDisplay.tsx b/client/src/pages/presentationEditor/components/TextComponentDisplay.tsx
index 4fb34650..c84e8aee 100644
--- a/client/src/pages/presentationEditor/components/TextComponentDisplay.tsx
+++ b/client/src/pages/presentationEditor/components/TextComponentDisplay.tsx
@@ -15,7 +15,7 @@ const TextComponentDisplay = ({ component }: ImageComponentProps) => {
   const [currentSize, setCurrentSize] = useState<Size>({ w: component.w, h: component.h })
   const competitionId = useAppSelector((state) => state.editor.competition.id)
   const slideId = useAppSelector((state) => state.editor.activeSlideId)
-  if (component.id === 1) console.log(component)
+  //if (component.id === 1) console.log(component)
   const handleEditorChange = (e: any) => {
     console.log('Content was updated:', e.target.getContent())
     axios.put(`/competitions/${competitionId}/slides/${slideId}/components/${component.id}`, {
-- 
GitLab


From 2878298926fa149c363bd44396ebabd49b8dafd9 Mon Sep 17 00:00:00 2001
From: Josef Olsson <josol381@student.liu.se>
Date: Wed, 21 Apr 2021 15:12:42 +0000
Subject: [PATCH 03/13] Update __init__.py

---
 server/app/database/__init__.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/server/app/database/__init__.py b/server/app/database/__init__.py
index 7deab335..784c006b 100644
--- a/server/app/database/__init__.py
+++ b/server/app/database/__init__.py
@@ -45,7 +45,7 @@ class Dictionary(TypeDecorator):
 
     def process_bind_param(self, value, dialect):
         if value is not None:
-            value = json.dumps(value).replace("'", '"')
+            value = json.dumps(value)
 
         return value
 
-- 
GitLab


From 8e175e1fa224e27e2dd7d682244c816f9dd170ee Mon Sep 17 00:00:00 2001
From: Sebastian Karlsson <sebka991@student.liu.se>
Date: Wed, 21 Apr 2021 17:15:43 +0200
Subject: [PATCH 04/13] Start adding media state (probably in a bad way)

---
 client/src/actions/types.ts            |  4 +++
 client/src/interfaces/ApiRichModels.ts |  3 +-
 client/src/reducers/allReducers.ts     |  2 ++
 client/src/reducers/mediaReducer.ts    | 39 ++++++++++++++++++++++++++
 4 files changed, 47 insertions(+), 1 deletion(-)
 create mode 100644 client/src/reducers/mediaReducer.ts

diff --git a/client/src/actions/types.ts b/client/src/actions/types.ts
index a417f6af..e500f2c8 100644
--- a/client/src/actions/types.ts
+++ b/client/src/actions/types.ts
@@ -29,4 +29,8 @@ export default {
   SET_CITIES_TOTAL: 'SET_CITIES_TOTAL',
   SET_CITIES_COUNT: 'SET_CITIES_COUNT',
   SET_TYPES: 'SET_TYPES',
+  SET_MEDIA_ID: 'SET_MEDIA_ID',
+  SET_MEDIA_FILENAME: 'SET_MEDIA_ID',
+  SET_MEDIA_TYPE_ID: 'SET_MEDIA_TYPE_ID',
+  SET_MEDIA_USER_ID: 'SET_MEDIA_USER_ID',
 }
diff --git a/client/src/interfaces/ApiRichModels.ts b/client/src/interfaces/ApiRichModels.ts
index 99ee4ec2..c0d2de7c 100644
--- a/client/src/interfaces/ApiRichModels.ts
+++ b/client/src/interfaces/ApiRichModels.ts
@@ -1,4 +1,4 @@
-import { Component, QuestionAlternative, QuestionAnswer, QuestionType } from './ApiModels'
+import { Component, Media, QuestionAlternative, QuestionAnswer, QuestionType } from './ApiModels'
 
 export interface RichCompetition {
   name: string
@@ -17,6 +17,7 @@ export interface RichSlide {
   competition_id: number
   components: Component[]
   questions: RichQuestion[]
+  medias: Media[]
 }
 
 export interface RichTeam {
diff --git a/client/src/reducers/allReducers.ts b/client/src/reducers/allReducers.ts
index 2aee4697..2a25bad7 100644
--- a/client/src/reducers/allReducers.ts
+++ b/client/src/reducers/allReducers.ts
@@ -4,6 +4,7 @@ import { combineReducers } from 'redux'
 import citiesReducer from './citiesReducer'
 import competitionsReducer from './competitionsReducer'
 import editorReducer from './editorReducer'
+import mediaReducer from './mediaReducer'
 import presentationReducer from './presentationReducer'
 import rolesReducer from './rolesReducer'
 import searchUserReducer from './searchUserReducer'
@@ -22,5 +23,6 @@ const allReducers = combineReducers({
   roles: rolesReducer,
   searchUsers: searchUserReducer,
   types: typesReducer,
+  media: mediaReducer,
 })
 export default allReducers
diff --git a/client/src/reducers/mediaReducer.ts b/client/src/reducers/mediaReducer.ts
new file mode 100644
index 00000000..ad5f3b46
--- /dev/null
+++ b/client/src/reducers/mediaReducer.ts
@@ -0,0 +1,39 @@
+import { AnyAction } from 'redux'
+import Types from '../actions/types'
+
+interface MediaState {
+  id: number
+  filename: string
+  mediatype_id: number
+  user_id: number
+}
+const initialState: MediaState = {
+  id: 0,
+  filename: '',
+  mediatype_id: 1,
+  user_id: 0,
+}
+
+export default function (state = initialState, action: AnyAction) {
+  switch (action.type) {
+    case Types.SET_MEDIA_ID:
+      return { ...state, id: action.payload as number }
+    case Types.SET_MEDIA_FILENAME:
+      return {
+        ...state,
+        filename: action.payload as string,
+      }
+    case Types.SET_MEDIA_TYPE_ID:
+      return {
+        ...state,
+        mediatype_id: action.payload as number,
+      }
+    case Types.SET_MEDIA_USER_ID:
+      return {
+        ...state,
+        user_id: action.payload as number,
+      }
+    default:
+      return state
+  }
+}
-- 
GitLab


From c09a5322b2aee63eebe4fbd5abc27c99f25f719a Mon Sep 17 00:00:00 2001
From: Sebastian Karlsson <sebka991@student.liu.se>
Date: Thu, 22 Apr 2021 16:18:10 +0200
Subject: [PATCH 05/13] Add image component to database

---
 .../components/SlideSettings.tsx              | 33 ++++++++++++-------
 1 file changed, 21 insertions(+), 12 deletions(-)

diff --git a/client/src/pages/presentationEditor/components/SlideSettings.tsx b/client/src/pages/presentationEditor/components/SlideSettings.tsx
index 11eea562..2c5ede66 100644
--- a/client/src/pages/presentationEditor/components/SlideSettings.tsx
+++ b/client/src/pages/presentationEditor/components/SlideSettings.tsx
@@ -144,22 +144,32 @@ const SlideSettings: React.FC = () => {
   }
 
   const uploadFile = async (formData: FormData) => {
-    await axios
+    return await axios
       .post(`/media/images`, formData)
-      .then(() => {
-        dispatch(getEditorCompetition(id))
-      })
+      .then((response) => response.data.id as number)
       .catch(console.log)
+    // Fånga upp media_id från detta. Hur?
   }
 
-  /*const createImageComponent = async () => {
+  const createImageComponent = async (media_id: number) => {
+    const imageData = {
+      x: 0,
+      y: 0,
+      w: 400,
+      h: 400,
+      data: {
+        media_id: media_id,
+      },
+      type_id: 2,
+    }
+
     await axios
-      .post(`/competitions/`, formData)
+      .post(`/competitions/${id}/slides/${currentSlide?.order}/components`, imageData)
       .then(() => {
         dispatch(getEditorCompetition(id))
       })
       .catch(console.log)
-  }*/
+  }
 
   const handleFileSelected = async (e: React.ChangeEvent<HTMLInputElement>) => {
     if (e.target.files !== null && e.target.files[0]) {
@@ -167,12 +177,11 @@ const SlideSettings: React.FC = () => {
       const file = files[0]
       const formData = new FormData()
       formData.append('image', file)
-      await uploadFile(formData)
+      const response = await uploadFile(formData)
 
-      //createImageComponent()
-      // Skapa en ImageComponent
-      // Lägg in filnamnet i data hos komponenten
-      //
+      if (response) {
+        const newComponent = createImageComponent(response)
+      }
     }
   }
 
-- 
GitLab


From edbafad02179b4b23dba4b9e0fb42a124dd0a558 Mon Sep 17 00:00:00 2001
From: Sebastian Karlsson <sebka991@student.liu.se>
Date: Thu, 22 Apr 2021 17:34:07 +0200
Subject: [PATCH 06/13] Add ImageComponents to state

---
 .../components/SlideSettings.tsx              | 20 +++++++++++--------
 1 file changed, 12 insertions(+), 8 deletions(-)

diff --git a/client/src/pages/presentationEditor/components/SlideSettings.tsx b/client/src/pages/presentationEditor/components/SlideSettings.tsx
index 2c5ede66..cbf2a8aa 100644
--- a/client/src/pages/presentationEditor/components/SlideSettings.tsx
+++ b/client/src/pages/presentationEditor/components/SlideSettings.tsx
@@ -20,7 +20,7 @@ import React from 'react'
 import { useParams } from 'react-router-dom'
 import { getEditorCompetition } from '../../../actions/editor'
 import { useAppDispatch, useAppSelector } from '../../../hooks'
-import { TextComponent } from '../../../interfaces/ApiModels'
+import { ImageComponent, TextComponent } from '../../../interfaces/ApiModels'
 import { HiddenInput } from './styled'
 
 const useStyles = makeStyles((theme: Theme) =>
@@ -101,12 +101,12 @@ const SlideSettings: React.FC = () => {
         ?.components.filter((component) => component.type_id === 1) as TextComponent[]
   )
 
-  /*const pictures = useAppSelector(
+  const pictures = useAppSelector(
     (state) =>
       state.editor.competition.slides
         .find((slide) => slide.id === state.editor.activeSlideId)
         ?.components.filter((component) => component.type_id === 2) as ImageComponent[]
-  )*/
+  )
 
   //const [pictures, setPictures] = useState(pictureList)
   //pictures.map((picture) => competition.slides[0].components.push(picture))
@@ -118,7 +118,7 @@ const SlideSettings: React.FC = () => {
   //Ta bort ImageComponent
   //}
 
-  const pictures = [{ id: 'picture1', name: 'Wallgren.png' }]
+  //const pictures = [{ id: 'picture1', name: 'Wallgren.png' }]
 
   //const [pictures, setPictures] = useState(pictureList)
 
@@ -144,11 +144,15 @@ const SlideSettings: React.FC = () => {
   }
 
   const uploadFile = async (formData: FormData) => {
+    // Uploads the file to the server and creates a Media object in database
+    // Returns media id
     return await axios
       .post(`/media/images`, formData)
-      .then((response) => response.data.id as number)
+      .then((response) => {
+        dispatch(getEditorCompetition(id))
+        return response.data.id as number
+      })
       .catch(console.log)
-    // Fånga upp media_id från detta. Hur?
   }
 
   const createImageComponent = async (media_id: number) => {
@@ -298,11 +302,11 @@ const SlideSettings: React.FC = () => {
               <ListItem divider button>
                 <img
                   id="temp source, todo: add image source to elements of pictureList"
-                  src={`http://localhost:5000/static/images/${picture.name}`}
+                  //src={`http://localhost:5000/static/images/${picture.data.media_id}`}
                   //{picture.data.media_id}
                   className={classes.importedImage}
                 />
-                <ListItemText className={classes.textCenter} primary={picture.name} />
+                <ListItemText className={classes.textCenter} /*primary={picture.name}*/ />
                 <CloseIcon /*onClick={() => handleClosePictureClick(picture.id)}*/ />
               </ListItem>
             </div>
-- 
GitLab


From 1027a2f94cfb23311d83928670c86115a8cf8c1d Mon Sep 17 00:00:00 2001
From: Josef Olsson <josol381@student.liu.se>
Date: Fri, 23 Apr 2021 09:33:35 +0200
Subject: [PATCH 07/13] Add images to slide in interface

---
 client/src/enum/ComponentTypes.ts             |  2 +-
 client/src/interfaces/ApiModels.ts            |  1 +
 .../components/ImageComponentDisplay.tsx      | 43 +++++--------------
 .../components/RndComponent.tsx               |  9 +++-
 .../components/SlideSettings.tsx              |  9 ++--
 5 files changed, 25 insertions(+), 39 deletions(-)

diff --git a/client/src/enum/ComponentTypes.ts b/client/src/enum/ComponentTypes.ts
index 8acb2fd9..8567e1c8 100644
--- a/client/src/enum/ComponentTypes.ts
+++ b/client/src/enum/ComponentTypes.ts
@@ -1,5 +1,5 @@
 export enum ComponentTypes {
   Text = 1,
-  Checkbox,
   Image,
+  Checkbox,
 }
diff --git a/client/src/interfaces/ApiModels.ts b/client/src/interfaces/ApiModels.ts
index 650814f1..347fdbfa 100644
--- a/client/src/interfaces/ApiModels.ts
+++ b/client/src/interfaces/ApiModels.ts
@@ -84,6 +84,7 @@ export interface Component {
 export interface ImageComponent extends Component {
   data: {
     media_id: number
+    filename: string
   }
 }
 
diff --git a/client/src/pages/presentationEditor/components/ImageComponentDisplay.tsx b/client/src/pages/presentationEditor/components/ImageComponentDisplay.tsx
index cffba9e5..7886a9b1 100644
--- a/client/src/pages/presentationEditor/components/ImageComponentDisplay.tsx
+++ b/client/src/pages/presentationEditor/components/ImageComponentDisplay.tsx
@@ -1,43 +1,20 @@
-import React, { useState } from 'react'
-import { Rnd } from 'react-rnd'
+import React from 'react'
 import { ImageComponent } from '../../../interfaces/ApiModels'
-import { Position, Size } from '../../../interfaces/Components'
 
 type ImageComponentProps = {
   component: ImageComponent
+  width: number
+  height: number
 }
 
-const ImageComponentDisplay = ({ component }: ImageComponentProps) => {
-  const [currentPos, setCurrentPos] = useState<Position>({ x: component.x, y: component.y })
-  const [currentSize, setCurrentSize] = useState<Size>({ w: component.w, h: component.h })
+const ImageComponentDisplay = ({ component, width, height }: ImageComponentProps) => {
   return (
-    <Rnd
-      minWidth={50}
-      minHeight={50}
-      bounds="parent"
-      onDragStop={(e, d) => {
-        setCurrentPos({ x: d.x, y: d.y })
-      }}
-      size={{ width: currentSize.w, height: currentSize.h }}
-      position={{ x: currentPos.x, y: currentPos.y }}
-      onResize={(e, direction, ref, delta, position) => {
-        setCurrentSize({
-          w: ref.offsetWidth,
-          h: ref.offsetHeight,
-        })
-        setCurrentPos(position)
-      }}
-      onResizeStop={() => {
-        console.log('Skicka data till server')
-      }}
-    >
-      <img
-        src="https://365psd.com/images/previews/c61/cartoon-cow-52394.png"
-        height={currentSize.h}
-        width={currentSize.w}
-        draggable={false}
-      />
-    </Rnd>
+    <img
+      src={`http://localhost:5000/static/images/${component.data.filename}`}
+      height={height}
+      width={width}
+      draggable={false}
+    />
   )
 }
 
diff --git a/client/src/pages/presentationEditor/components/RndComponent.tsx b/client/src/pages/presentationEditor/components/RndComponent.tsx
index 890e5160..11875755 100644
--- a/client/src/pages/presentationEditor/components/RndComponent.tsx
+++ b/client/src/pages/presentationEditor/components/RndComponent.tsx
@@ -44,7 +44,14 @@ const RndComponent = ({ component }: ImageComponentProps) => {
           />
         )
       case ComponentTypes.Image:
-        return <ImageComponentDisplay key={component.id} component={component as ImageComponent} />
+        return (
+          <ImageComponentDisplay
+            key={component.id}
+            component={component as ImageComponent}
+            width={currentSize.w}
+            height={currentSize.h}
+          />
+        )
       default:
         break
     }
diff --git a/client/src/pages/presentationEditor/components/SlideSettings.tsx b/client/src/pages/presentationEditor/components/SlideSettings.tsx
index b48c7f6a..f105914c 100644
--- a/client/src/pages/presentationEditor/components/SlideSettings.tsx
+++ b/client/src/pages/presentationEditor/components/SlideSettings.tsx
@@ -22,7 +22,7 @@ import { green, grey } from '@material-ui/core/colors'
 import { createStyles, makeStyles, Theme, withStyles } from '@material-ui/core/styles'
 import CloseIcon from '@material-ui/icons/Close'
 import axios from 'axios'
-import { ImageComponent, TextComponent, QuestionAlternative } from '../../../interfaces/ApiModels'
+import { ImageComponent, TextComponent, QuestionAlternative, Media } from '../../../interfaces/ApiModels'
 import React, { useEffect, useState } from 'react'
 import { useParams } from 'react-router-dom'
 import { getEditorCompetition } from '../../../actions/editor'
@@ -238,19 +238,20 @@ const SlideSettings: React.FC = () => {
       .post(`/media/images`, formData)
       .then((response) => {
         dispatch(getEditorCompetition(id))
-        return response.data.id as number
+        return response.data as Media
       })
       .catch(console.log)
   }
 
-  const createImageComponent = async (media_id: number) => {
+  const createImageComponent = async (media: Media) => {
     const imageData = {
       x: 0,
       y: 0,
       w: 400,
       h: 400,
       data: {
-        media_id: media_id,
+        media_id: media.id,
+        filename: media.filename,
       },
       type_id: 2,
     }
-- 
GitLab


From 6b77ba90124125354ae1395a17051c308b5a3279 Mon Sep 17 00:00:00 2001
From: Josef Olsson <josol381@student.liu.se>
Date: Fri, 23 Apr 2021 15:52:24 +0200
Subject: [PATCH 08/13] Delete images from editor

---
 .../components/SlideSettings.tsx              | 44 ++++++++++---------
 1 file changed, 24 insertions(+), 20 deletions(-)

diff --git a/client/src/pages/presentationEditor/components/SlideSettings.tsx b/client/src/pages/presentationEditor/components/SlideSettings.tsx
index f105914c..33f96d4c 100644
--- a/client/src/pages/presentationEditor/components/SlideSettings.tsx
+++ b/client/src/pages/presentationEditor/components/SlideSettings.tsx
@@ -117,26 +117,32 @@ const SlideSettings: React.FC = () => {
         ?.components.filter((component) => component.type_id === 1) as TextComponent[]
   )
 
-  const pictures = useAppSelector(
+  const images = useAppSelector(
     (state) =>
       state.editor.competition.slides
         .find((slide) => slide.id === state.editor.activeSlideId)
         ?.components.filter((component) => component.type_id === 2) as ImageComponent[]
   )
 
-  //const [pictures, setPictures] = useState(pictureList)
-  //pictures.map((picture) => competition.slides[0].components.push(picture))
+  const handleCloseimageClick = async (image: ImageComponent) => {
+    await axios
+      .delete(`/media/images/${image.data.media_id}`)
+      .then(() => {
+        dispatch(getEditorCompetition(id))
+      })
+      .catch(console.log)
 
-  //const handleClosePictureClick = (id: number) => {
-  //  setPictures(pictures.filter((item) => item.id !== id)) //Will not be done like this when api is used
-  //Ta bort från servern
-  //Ta bort Media
-  //Ta bort ImageComponent
-  //}
+    await axios
+      .delete(`/competitions/${id}/slides/${activeSlide?.id}/components/${image.id}`)
+      .then(() => {
+        dispatch(getEditorCompetition(id))
+      })
+      .catch(console.log)
+  }
 
-  //const pictures = [{ id: 'picture1', name: 'Wallgren.png' }]
+  //const images = [{ id: 'image1', name: 'Wallgren.png' }]
 
-  //const [pictures, setPictures] = useState(pictureList)
+  //const [images, setimages] = useState(imageList)
 
   const updateSlideType = async () => {
     closeSlideTypeDialog()
@@ -451,18 +457,16 @@ const SlideSettings: React.FC = () => {
         <ListItem divider>
           <ListItemText className={classes.textCenter} primary="Bilder" />
         </ListItem>
-        {pictures &&
-          pictures.map((picture) => (
-            <div key={picture.id}>
+        {images &&
+          images.map((image) => (
+            <div key={image.id}>
               <ListItem divider button>
                 <img
-                  id="temp source, todo: add image source to elements of pictureList"
-                  //src={`http://localhost:5000/static/images/${picture.data.media_id}`}
-                  //{picture.data.media_id}
+                  src={`http://localhost:5000/static/images/thumbnail_${image.data.filename}`}
                   className={classes.importedImage}
                 />
-                <ListItemText className={classes.textCenter} /*primary={picture.name}*/ />
-                <CloseIcon /*onClick={() => handleClosePictureClick(picture.id)}*/ />
+                <ListItemText className={classes.textCenter} primary={image.data.filename} />
+                <CloseIcon onClick={() => handleCloseimageClick(image)} />
               </ListItem>
             </div>
           ))}
@@ -477,7 +481,7 @@ const SlideSettings: React.FC = () => {
 
       <ListItem button>
         <img
-          id="temp source, todo: add image source to elements of pictureList"
+          id="temp source, todo: add image source to elements of imageList"
           src="https://i1.wp.com/stickoutmedia.se/wp-content/uploads/2021/01/placeholder-3.png?ssl=1"
           className={classes.importedImage}
         />
-- 
GitLab


From 7bbcb4465379deef002d495bcf328c1cb39fc1a6 Mon Sep 17 00:00:00 2001
From: Josef Olsson <josol381@student.liu.se>
Date: Fri, 23 Apr 2021 16:54:35 +0200
Subject: [PATCH 09/13] Autosize added images

---
 .../components/SlideSettings.tsx              |  6 ------
 server/app/database/controller/add.py         | 21 ++++++++++++++++++-
 2 files changed, 20 insertions(+), 7 deletions(-)

diff --git a/client/src/pages/presentationEditor/components/SlideSettings.tsx b/client/src/pages/presentationEditor/components/SlideSettings.tsx
index 33f96d4c..1d8aad5f 100644
--- a/client/src/pages/presentationEditor/components/SlideSettings.tsx
+++ b/client/src/pages/presentationEditor/components/SlideSettings.tsx
@@ -140,10 +140,6 @@ const SlideSettings: React.FC = () => {
       .catch(console.log)
   }
 
-  //const images = [{ id: 'image1', name: 'Wallgren.png' }]
-
-  //const [images, setimages] = useState(imageList)
-
   const updateSlideType = async () => {
     closeSlideTypeDialog()
     if (activeSlide) {
@@ -253,8 +249,6 @@ const SlideSettings: React.FC = () => {
     const imageData = {
       x: 0,
       y: 0,
-      w: 400,
-      h: 400,
       data: {
         media_id: media.id,
         filename: media.filename,
diff --git a/server/app/database/controller/add.py b/server/app/database/controller/add.py
index 1dbcf442..778f28ac 100644
--- a/server/app/database/controller/add.py
+++ b/server/app/database/controller/add.py
@@ -2,10 +2,15 @@
 This file contains functionality to add data to the database.
 """
 
+import os
+from PIL import Image
+from flask.globals import current_app
+from sqlalchemy.orm import relation
 from sqlalchemy.orm.session import sessionmaker
+from app.apis.media import PHOTO_PATH
 import app.core.http_codes as codes
 from app.core import db
-from app.database.controller import utils
+from app.database.controller import get, search, utils
 from app.database.models import (
     Blacklist,
     City,
@@ -98,6 +103,20 @@ def component(type_id, slide_id, data, x=0, y=0, w=0, h=0):
     provided size and data .
     """
 
+    if type_id == 2:  # 2 is image
+        item_image = get.one(Media, data["media_id"])
+        filename = item_image.filename
+        path = os.path.join(PHOTO_PATH, filename)
+        with Image.open(path) as im:
+            h = im.height
+            w = im.width
+
+    largest = max(w, h)
+    if largest > 600:
+        ratio = 600 / largest
+        w *= ratio
+        h *= ratio
+
     return db_add(Component(slide_id, type_id, data, x, y, w, h))
 
 
-- 
GitLab


From 306ba16fa45c23d7b603e107163ff3092caa4227 Mon Sep 17 00:00:00 2001
From: Josef Olsson <josol381@student.liu.se>
Date: Fri, 23 Apr 2021 17:12:15 +0200
Subject: [PATCH 10/13] Fix populate

---
 server/populate.py | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/server/populate.py b/server/populate.py
index 9ca3c95e..69ca46b4 100644
--- a/server/populate.py
+++ b/server/populate.py
@@ -1,6 +1,5 @@
 import random
 
-import app.database.controller as dbc
 from app import create_app, db
 from app.database.models import City, QuestionType, Role
 
@@ -100,6 +99,8 @@ if __name__ == "__main__":
     app, _ = create_app("configmodule.DevelopmentConfig")
 
     with app.app_context():
+        import app.database.controller as dbc  # Media needs app_context to work
+
         db.drop_all()
         db.create_all()
         _add_items()
-- 
GitLab


From e0085de3e397e3425b1570ce482476d495d2fa35 Mon Sep 17 00:00:00 2001
From: Josef Olsson <josol381@student.liu.se>
Date: Fri, 23 Apr 2021 17:30:30 +0200
Subject: [PATCH 11/13] Fix front-end tests

---
 .../components/CompetitionSettings.test.tsx            | 10 ----------
 .../components/ImageComponentDisplay.test.tsx          |  8 +++++++-
 .../components/SettingsPanel.test.tsx                  |  3 ++-
 3 files changed, 9 insertions(+), 12 deletions(-)
 delete mode 100644 client/src/pages/presentationEditor/components/CompetitionSettings.test.tsx

diff --git a/client/src/pages/presentationEditor/components/CompetitionSettings.test.tsx b/client/src/pages/presentationEditor/components/CompetitionSettings.test.tsx
deleted file mode 100644
index a655a30f..00000000
--- a/client/src/pages/presentationEditor/components/CompetitionSettings.test.tsx
+++ /dev/null
@@ -1,10 +0,0 @@
-import { render } from '@testing-library/react'
-import React from 'react'
-import { BrowserRouter } from 'react-router-dom'
-import ImageComponentDisplay from './ImageComponentDisplay'
-
-it('renders image component display', () => {
-  render(
-  <ImageComponentDisplay component={{ id: 0, x: 0, y: 0, w: 0, h: 0, type: 0, media_id: 0 }} />
-  )
-})
diff --git a/client/src/pages/presentationEditor/components/ImageComponentDisplay.test.tsx b/client/src/pages/presentationEditor/components/ImageComponentDisplay.test.tsx
index b726023b..9e78f8e1 100644
--- a/client/src/pages/presentationEditor/components/ImageComponentDisplay.test.tsx
+++ b/client/src/pages/presentationEditor/components/ImageComponentDisplay.test.tsx
@@ -3,5 +3,11 @@ import React from 'react'
 import ImageComponentDisplay from './ImageComponentDisplay'
 
 it('renders competition settings', () => {
-  render(<ImageComponentDisplay component={{ id: 0, x: 0, y: 0, w: 0, h: 0, media_id: 0, type: 2 }} />)
+  render(
+    <ImageComponentDisplay
+      component={{ id: 0, x: 0, y: 0, w: 0, h: 0, data: { media_id: 0, filename: '' }, type_id: 2 }}
+      width={0}
+      height={0}
+    />
+  )
 })
diff --git a/client/src/pages/presentationEditor/components/SettingsPanel.test.tsx b/client/src/pages/presentationEditor/components/SettingsPanel.test.tsx
index a7a71e25..a17569ec 100644
--- a/client/src/pages/presentationEditor/components/SettingsPanel.test.tsx
+++ b/client/src/pages/presentationEditor/components/SettingsPanel.test.tsx
@@ -6,6 +6,7 @@ import { BrowserRouter } from 'react-router-dom'
 import store from '../../../store'
 import CompetitionSettings from './CompetitionSettings'
 import SettingsPanel from './SettingsPanel'
+import SlideSettings from './SlideSettings'
 
 it('renders settings panel', () => {
   render(
@@ -28,5 +29,5 @@ it('renders slide settings tab', () => {
   const tabs = wrapper.find('.MuiTabs-flexContainer')
   expect(wrapper.find(CompetitionSettings).length).toEqual(1)
   tabs.children().at(1).simulate('click')
-  expect(wrapper.text().includes('2')).toBe(true) //TODO: check that SlideSettings exists
+  expect(wrapper.find(SlideSettings).length).toEqual(1)
 })
-- 
GitLab


From 90b977403ee9479377753dfe0b96321b0a5d579e Mon Sep 17 00:00:00 2001
From: Josef Olsson <josol381@student.liu.se>
Date: Fri, 23 Apr 2021 17:30:51 +0200
Subject: [PATCH 12/13] Fix back-end tests

---
 server/app/database/controller/add.py | 11 ++++++-----
 server/populate.py                    |  2 +-
 2 files changed, 7 insertions(+), 6 deletions(-)

diff --git a/server/app/database/controller/add.py b/server/app/database/controller/add.py
index 778f28ac..216160cd 100644
--- a/server/app/database/controller/add.py
+++ b/server/app/database/controller/add.py
@@ -3,11 +3,7 @@ This file contains functionality to add data to the database.
 """
 
 import os
-from PIL import Image
-from flask.globals import current_app
-from sqlalchemy.orm import relation
-from sqlalchemy.orm.session import sessionmaker
-from app.apis.media import PHOTO_PATH
+
 import app.core.http_codes as codes
 from app.core import db
 from app.database.controller import get, search, utils
@@ -30,8 +26,12 @@ from app.database.models import (
     User,
     ViewType,
 )
+from flask.globals import current_app
 from flask_restx import abort
+from PIL import Image
 from sqlalchemy import exc
+from sqlalchemy.orm import relation
+from sqlalchemy.orm.session import sessionmaker
 
 
 def db_add(item):
@@ -102,6 +102,7 @@ def component(type_id, slide_id, data, x=0, y=0, w=0, h=0):
     Adds a component to the slide at the specified coordinates with the
     provided size and data .
     """
+    from app.apis.media import PHOTO_PATH
 
     if type_id == 2:  # 2 is image
         item_image = get.one(Media, data["media_id"])
diff --git a/server/populate.py b/server/populate.py
index 69ca46b4..703e96c2 100644
--- a/server/populate.py
+++ b/server/populate.py
@@ -1,5 +1,6 @@
 import random
 
+import app.database.controller as dbc
 from app import create_app, db
 from app.database.models import City, QuestionType, Role
 
@@ -99,7 +100,6 @@ if __name__ == "__main__":
     app, _ = create_app("configmodule.DevelopmentConfig")
 
     with app.app_context():
-        import app.database.controller as dbc  # Media needs app_context to work
 
         db.drop_all()
         db.create_all()
-- 
GitLab


From 6f53a9b5cca60e706f89a999d396fb4cb216a118 Mon Sep 17 00:00:00 2001
From: Josef Olsson <josol381@student.liu.se>
Date: Fri, 23 Apr 2021 17:42:26 +0200
Subject: [PATCH 13/13] Add api to paths

---
 client/src/pages/admin/AdminPage.test.tsx          |  2 +-
 .../admin/competitions/CompetitionManager.test.tsx |  2 +-
 .../components/SlideSettings.tsx                   | 14 +++++++-------
 3 files changed, 9 insertions(+), 9 deletions(-)

diff --git a/client/src/pages/admin/AdminPage.test.tsx b/client/src/pages/admin/AdminPage.test.tsx
index ab694cee..445373a0 100644
--- a/client/src/pages/admin/AdminPage.test.tsx
+++ b/client/src/pages/admin/AdminPage.test.tsx
@@ -40,7 +40,7 @@ it('renders admin view', () => {
     },
   }
   ;(mockedAxios.get as jest.Mock).mockImplementation((path: string, params?: any) => {
-    if (path === '/misc/cities') return Promise.resolve(cityRes)
+    if (path === '/api/misc/cities') return Promise.resolve(cityRes)
     else return Promise.resolve(rolesRes)
   })
   render(
diff --git a/client/src/pages/admin/competitions/CompetitionManager.test.tsx b/client/src/pages/admin/competitions/CompetitionManager.test.tsx
index f47d8045..7af04abb 100644
--- a/client/src/pages/admin/competitions/CompetitionManager.test.tsx
+++ b/client/src/pages/admin/competitions/CompetitionManager.test.tsx
@@ -47,7 +47,7 @@ it('renders competition manager', () => {
   }
 
   ;(mockedAxios.get as jest.Mock).mockImplementation((path: string, params?: any) => {
-    if (path === '/competitions/search') return Promise.resolve(compRes)
+    if (path === '/api/competitions/search') return Promise.resolve(compRes)
     else return Promise.resolve(cityRes)
   })
   render(
diff --git a/client/src/pages/presentationEditor/components/SlideSettings.tsx b/client/src/pages/presentationEditor/components/SlideSettings.tsx
index e06f1ce3..ffa95333 100644
--- a/client/src/pages/presentationEditor/components/SlideSettings.tsx
+++ b/client/src/pages/presentationEditor/components/SlideSettings.tsx
@@ -101,7 +101,7 @@ const SlideSettings: React.FC = () => {
     if (activeSlide && activeSlide.questions[0]) {
       await axios
         .delete(
-          `/competitions/${id}/slides/${activeSlideId}/questions/${activeSlide?.questions[0].id}/alternatives/${alternative_id}`
+          `/api/competitions/${id}/slides/${activeSlideId}/questions/${activeSlide?.questions[0].id}/alternatives/${alternative_id}`
         )
         .then(() => {
           dispatch(getEditorCompetition(id))
@@ -126,14 +126,14 @@ const SlideSettings: React.FC = () => {
 
   const handleCloseimageClick = async (image: ImageComponent) => {
     await axios
-      .delete(`/media/images/${image.data.media_id}`)
+      .delete(`/api/media/images/${image.data.media_id}`)
       .then(() => {
         dispatch(getEditorCompetition(id))
       })
       .catch(console.log)
 
     await axios
-      .delete(`/competitions/${id}/slides/${activeSlide?.id}/components/${image.id}`)
+      .delete(`/api/competitions/${id}/slides/${activeSlide?.id}/components/${image.id}`)
       .then(() => {
         dispatch(getEditorCompetition(id))
       })
@@ -195,7 +195,7 @@ const SlideSettings: React.FC = () => {
       console.log('newValue: ' + newValue)
       await axios
         .put(
-          `/competitions/${id}/slides/${activeSlide?.id}/questions/${activeSlide?.questions[0].id}/alternatives/${alternative.id}`,
+          `/api/competitions/${id}/slides/${activeSlide?.id}/questions/${activeSlide?.questions[0].id}/alternatives/${alternative.id}`,
           { value: newValue }
         )
         .then(() => {
@@ -209,7 +209,7 @@ const SlideSettings: React.FC = () => {
     if (activeSlide && activeSlide.questions[0]) {
       await axios
         .put(
-          `/competitions/${id}/slides/${activeSlide?.id}/questions/${activeSlide?.questions[0].id}/alternatives/${alternative_id}`,
+          `/api/competitions/${id}/slides/${activeSlide?.id}/questions/${activeSlide?.questions[0].id}/alternatives/${alternative_id}`,
           { text: newText }
         )
         .then(() => {
@@ -240,7 +240,7 @@ const SlideSettings: React.FC = () => {
     // Uploads the file to the server and creates a Media object in database
     // Returns media id
     return await axios
-      .post(`/media/images`, formData)
+      .post(`/api/media/images`, formData)
       .then((response) => {
         dispatch(getEditorCompetition(id))
         return response.data as Media
@@ -260,7 +260,7 @@ const SlideSettings: React.FC = () => {
     }
 
     await axios
-      .post(`/competitions/${id}/slides/${activeSlide?.id}/components`, imageData)
+      .post(`/api/competitions/${id}/slides/${activeSlide?.id}/components`, imageData)
       .then(() => {
         dispatch(getEditorCompetition(id))
       })
-- 
GitLab