diff --git a/package.json b/package.json index 2708cbf69..43b6e3521 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,6 @@ "mime-types": "^2.1.35", "node-html-parser": "^6.1.5", "oidc-client": "^1.11.5", - "parse-link-header": "^2.0.0", "plotly.js": "^3.3.1", "prismjs": "^1.29.0", "prompts": "2.4.0", @@ -65,7 +64,6 @@ "react-container-query": "^0.13.0", "react-cookie": "^4.1.1", "react-dom": "^18.1.0", - "react-dropzone": "^12.0.4", "react-i18next": "^12.0.0", "react-modal": "^3.14.4", "react-redux": "^7.2.5", diff --git a/public/translations/en-US.json b/public/translations/en-US.json index 0f5a937ab..38e23bfab 100644 --- a/public/translations/en-US.json +++ b/public/translations/en-US.json @@ -297,19 +297,6 @@ "loading": "Loading", "failed": "Load failed" }, - "imageUploadButton": { - "uploadImage": "Upload image", - "uploadAnImage": "Upload an image", - "info": "Drag and drop images here, or click to select images from file", - "cancel": "Cancel", - "save": "Save", - "or": "or", - "errors": { - "error": "Error", - "imageNameNotUnique": "Image names must be unique.", - "invalidImageExtension": "Image names must end in {{extensions}}." - } - }, "newInputPanelButton": { "buttonText": "Add another panel" }, diff --git a/public/translations/en.json b/public/translations/en.json index e1fe50737..ce4781aee 100644 --- a/public/translations/en.json +++ b/public/translations/en.json @@ -298,19 +298,6 @@ "loading": "Loading", "failed": "Load failed" }, - "imageUploadButton": { - "uploadImage": "Upload image", - "uploadAnImage": "Upload an image", - "info": "Drag and drop images here, or click to select images from file", - "cancel": "Cancel", - "save": "Save", - "or": "or", - "errors": { - "error": "Error", - "imageNameNotUnique": "Image names must be unique.", - "invalidImageExtension": "Image names must end in {{extensions}}." - } - }, "newInputPanelButton": { "buttonText": "Add another panel" }, diff --git a/public/translations/es-LA.json b/public/translations/es-LA.json index 841566a1c..ff0c60fdf 100644 --- a/public/translations/es-LA.json +++ b/public/translations/es-LA.json @@ -297,19 +297,6 @@ "loading": "Cargando", "failed": "Error al cargar" }, - "imageUploadButton": { - "uploadImage": "Subir imagen", - "uploadAnImage": "Subir una imagen", - "info": "Arrastra y suelta imágenes aquí o haz clic para seleccionar imágenes de un archivo", - "cancel": "Cancelar", - "save": "Guardar", - "or": "o", - "errors": { - "error": "Error", - "imageNameNotUnique": "Los nombres de imagen deben ser únicos.", - "invalidImageExtension": "Los nombres de imagen deben terminar en {{extensions}}." - } - }, "newInputPanelButton": { "buttonText": "Agregar otro panel" }, diff --git a/public/translations/fr-FR.json b/public/translations/fr-FR.json index 5640c25cf..8ba6688ae 100644 --- a/public/translations/fr-FR.json +++ b/public/translations/fr-FR.json @@ -297,19 +297,6 @@ "loading": "Chargement en cours", "failed": "Échec du chargement" }, - "imageUploadButton": { - "uploadImage": "Téléverser une image", - "uploadAnImage": "Téléverser une image", - "info": "Faire glisser et déposer les images ici, ou cliquer pour sélectionner des images à partir du fichier", - "cancel": "Annuler", - "save": "Enregistrer", - "or": "ou", - "errors": { - "error": "Erreur", - "imageNameNotUnique": "Le nom des images doit être unique.", - "invalidImageExtension": "Le nom des images doit se terminer par {{extensions}}." - } - }, "newInputPanelButton": { "buttonText": "Ajouter une autre panneau" }, diff --git a/public/translations/xx-XX.json b/public/translations/xx-XX.json index a3013850a..ce1dbf426 100644 --- a/public/translations/xx-XX.json +++ b/public/translations/xx-XX.json @@ -296,18 +296,6 @@ "loading": "Wird geladen", "failed": "Laden fehlgeschlagen" }, - "imageUploadButton": { - "uploadImage": "Bild hochladen", - "uploadAnImage": "Laden Sie ein Bild hoch", - "info": "Ziehen Sie Bilder hierher, oder klicken Sie, um Bilder aus der Datei auszuwählen", - "cancel": "Stornieren", - "save": "Speichern", - "errors": { - "error": "Fehler", - "imageNameNotUnique": "Bildnamen müssen eindeutig sein.", - "invalidImageExtension": "Bildnamen müssen enden auf {{extensions}}." - } - }, "newInputPanelButton": { "buttonText": "Fügen Sie ein weiteres Panel hinzu" }, diff --git a/src/assets/stylesheets/ImageUploadButton.scss b/src/assets/stylesheets/ImageUploadButton.scss deleted file mode 100644 index 7d26850cf..000000000 --- a/src/assets/stylesheets/ImageUploadButton.scss +++ /dev/null @@ -1,16 +0,0 @@ -@use './rpf_design_system/font-weight' as *; - -.dropzone-area { - border: 1px dashed black; - border-radius: 10px; - padding: 20px; - text-align: center; -} - -.dropzone-info { - font-weight: $font-weight-bold; -} - -.modal-footer { - text-align: inline-end; -} diff --git a/src/components/Editor/ImageUploadButton/ImageUploadButton.jsx b/src/components/Editor/ImageUploadButton/ImageUploadButton.jsx deleted file mode 100644 index 49d571338..000000000 --- a/src/components/Editor/ImageUploadButton/ImageUploadButton.jsx +++ /dev/null @@ -1,165 +0,0 @@ -import "../../../assets/stylesheets/ImageUploadButton.scss"; - -import { useCallback, useState } from "react"; -import { useDispatch, useSelector } from "react-redux"; -import Dropzone from "react-dropzone"; -import Modal from "react-modal"; - -import { updateImages, setNameError } from "../../../redux/EditorSlice"; -import Button from "../../Button/Button"; -import NameErrorMessage from "../ErrorMessage/NameErrorMessage"; -import store from "../../../app/store"; -import ApiCallHandler from "../../../utils/apiCallHandler"; -import { useTranslation } from "react-i18next"; -import { allowedExtensionsString } from "../../../utils/allowedExtensionsString"; - -const allowedExtensions = { - python: ["jpg", "jpeg", "png", "gif"], -}; - -const ImageUploadButton = ({ reactAppApiEndpoint }) => { - const [modalIsOpen, setIsOpen] = useState(false); - const [files, setFiles] = useState([]); - const dispatch = useDispatch(); - const projectType = useSelector((state) => state.editor.project.project_type); - const projectIdentifier = useSelector( - (state) => state.editor.project.identifier, - ); - const projectImages = useSelector((state) => state.editor.project.image_list); - const imageNames = projectImages.map((image) => `${image.filename}`); - const user = useSelector((state) => state.auth.user); - const { t } = useTranslation(); - - const closeModal = () => { - setFiles([]); - setIsOpen(false); - }; - const showModal = () => { - dispatch(setNameError("")); - setIsOpen(true); - }; - const saveImages = async () => { - const { uploadImages } = ApiCallHandler({ - reactAppApiEndpoint, - }); - - files.every((file) => { - const fileName = file.name; - const extension = fileName.split(".").slice(1).join(".").toLowerCase(); - if ( - imageNames.includes(fileName) || - files.filter((file) => file.name === fileName).length > 1 - ) { - dispatch( - setNameError(t("imageUploadButton.errors.imageNameNotUnique")), - ); - return false; - } else if (isValidFileName(fileName, files)) { - return true; - } else if (!allowedExtensions[projectType].includes(extension)) { - dispatch( - setNameError( - t("errors.invalidImageExtension", { - extensions: allowedExtensionsString( - projectType, - t, - allowedExtensions, - ), - }), - ), - ); - return false; - } else { - dispatch(setNameError("imageUploadButton.error")); - return false; - } - }); - if (store.getState().editor.nameError === "") { - const response = await uploadImages( - projectIdentifier, - user.access_token, - files, - ); - dispatch(updateImages(response.data.image_list)); - closeModal(); - } - }; - - const isValidFileName = (fileName, files) => { - const extension = fileName.split(".").slice(1).join(".").toLowerCase(); - if ( - allowedExtensions[projectType].includes(extension) && - !imageNames.includes(fileName) && - files.filter((file) => file.name === fileName).length === 1 - ) { - return true; - } else { - return false; - } - }; - - const customStyles = { - content: { - top: "50%", - left: "50%", - right: "auto", - bottom: "auto", - marginRight: "-50%", - transform: "translate(-50%, -50%)", - }, - overlay: { - zIndex: 1000, - }, - }; - - return ( - <> - - - - {t("imageUploadButton.uploadAnImage")} - - - { - setFiles((prev) => [...prev, ...acceptedFiles]); - }, [])} - > - {({ getRootProps, getInputProps }) => ( - - - - {t("imageUploadButton.info")} - {files.map((file, i) => ( - {file.name} - ))} - - - )} - - - - - - - > - ); -}; - -export default ImageUploadButton; diff --git a/src/components/Editor/ImageUploadButton/ImageUploadButton.test.js b/src/components/Editor/ImageUploadButton/ImageUploadButton.test.js deleted file mode 100644 index c03735f19..000000000 --- a/src/components/Editor/ImageUploadButton/ImageUploadButton.test.js +++ /dev/null @@ -1,63 +0,0 @@ -import React from "react"; -import { fireEvent, render } from "@testing-library/react"; -import { Provider } from "react-redux"; -import configureStore from "redux-mock-store"; - -import ImageUploadButton from "./ImageUploadButton"; - -describe("When user logged in and owns project", () => { - let store; - let button; - let queryByText; - - beforeEach(() => { - const middlewares = []; - const mockStore = configureStore(middlewares); - const initialState = { - editor: { - project: { - components: [ - { - name: "main", - extension: "py", - }, - ], - image_list: [], - project_type: "python", - user_id: "b48e70e2-d9ed-4a59-aee5-fc7cf09dbfaf", - }, - nameError: "", - }, - auth: { - user: { - profile: { - user: "b48e70e2-d9ed-4a59-aee5-fc7cf09dbfaf", - }, - }, - }, - }; - store = mockStore(initialState); - ({ queryByText } = render( - - - - - , - )); - button = queryByText("imageUploadButton.uploadImage"); - }); - - test("Modal opens when Image Upload button clicked", () => { - fireEvent.click(button); - const dropzone = queryByText("imageUploadButton.info"); - expect(dropzone).not.toBeNull(); - }); - - test("Modal closes when cancel button clicked", () => { - fireEvent.click(button); - const cancelButton = queryByText("imageUploadButton.cancel"); - fireEvent.click(cancelButton); - const dropzone = queryByText("imageUploadButton.info"); - expect(dropzone).toBeNull(); - }); -}); diff --git a/src/redux/EditorSlice.js b/src/redux/EditorSlice.js index c53751b09..fddffcd2b 100644 --- a/src/redux/EditorSlice.js +++ b/src/redux/EditorSlice.js @@ -1,5 +1,4 @@ import { createAsyncThunk, createSlice } from "@reduxjs/toolkit"; -import parseLinkHeader from "parse-link-header"; import { loadProjectPending, loadProjectFulfilled, @@ -78,21 +77,6 @@ export const syncProject = (actionName) => }, ); -export const loadProjectList = createAsyncThunk( - `editor/loadProjectList`, - async ({ reactAppApiEndpoint, page, accessToken }) => { - const { readProjectList } = ApiCallHandler({ - reactAppApiEndpoint, - }); - const response = await readProjectList(page, accessToken); - return { - projects: response.data, - page, - links: parseLinkHeader(response.headers.link), - }; - }, -); - export const editorInitialState = { project: {}, cascadeUpdate: false, @@ -183,12 +167,6 @@ export const EditorSlice = createSlice({ state.focussedFileIndices[action.payload.panelIndex] = action.payload.fileIndex; }, - updateImages: (state, action) => { - if (!state.project.image_list) { - state.project.image_list = []; - } - state.project.image_list = action.payload; - }, setWebComponent: (state, action) => { state.webComponent = action.payload; }, @@ -470,7 +448,6 @@ export const { triggerDraw, triggerSave, updateComponentName, - updateImages, updateProjectComponent, updateProjectName, showBetaModal, diff --git a/src/utils/apiCallHandler.js b/src/utils/apiCallHandler.js index ead3394db..5b1dab933 100644 --- a/src/utils/apiCallHandler.js +++ b/src/utils/apiCallHandler.js @@ -50,10 +50,6 @@ const ApiCallHandler = ({ reactAppApiEndpoint }) => { ); }; - const getImage = async (url) => { - return await get(url, headers()); - }; - const loadRemix = async (projectIdentifier, accessToken) => { return await get( `${host}/api/projects/${projectIdentifier}/remix`, @@ -85,27 +81,6 @@ const ApiCallHandler = ({ reactAppApiEndpoint }) => { ); }; - const readProjectList = async (page, accessToken) => { - return await get(`${host}/api/projects`, { - params: { page }, - ...headers(accessToken), - }); - }; - - const uploadImages = async (projectIdentifier, accessToken, images) => { - var formData = new FormData(); - - images.forEach((image) => { - formData.append("images[]", image, image.name); - }); - - return await post( - `${host}/api/projects/${projectIdentifier}/images`, - formData, - { ...headers(accessToken), "Content-Type": "multipart/form-data" }, - ); - }; - const createError = async ( projectIdentifier, userId, @@ -130,13 +105,10 @@ const ApiCallHandler = ({ reactAppApiEndpoint }) => { put, createOrUpdateProject, deleteProject, - getImage, loadRemix, createRemix, readProject, loadAssets, - readProjectList, - uploadImages, createError, }; }; diff --git a/src/utils/apiCallHandler.test.js b/src/utils/apiCallHandler.test.js index f05b2992d..aff8cf0c7 100644 --- a/src/utils/apiCallHandler.test.js +++ b/src/utils/apiCallHandler.test.js @@ -11,13 +11,10 @@ const authHeaders = { }; const { - getImage, createOrUpdateProject, readProject, loadAssets, createRemix, - uploadImages, - readProjectList, createError, } = ApiCallHandler({ reactAppApiEndpoint: host }); @@ -168,46 +165,6 @@ describe("Testing project API calls", () => { authHeaders, ); }); - - test("Upload image", async () => { - const projectIdentifier = "my-amazing-project"; - const image = new File(["(⌐□_□)"], "image1.png", { type: "image/png" }); - axios.post.mockImplementationOnce(() => - Promise.resolve({ status: 200, url: "google.drive.com/image1.png" }), - ); - - var formData = new FormData(); - formData.append("images[]", image, image.name); - - await uploadImages(projectIdentifier, accessToken, [image]); - expect(axios.post).toHaveBeenCalledWith( - `${host}/api/projects/${projectIdentifier}/images`, - formData, - { ...authHeaders, "Content-Type": "multipart/form-data" }, - ); - }); - - test("Get image", async () => { - const image = new File(["(⌐□_□)"], "image1.png", { type: "image/png" }); - const imageUrl = "google.drive.com/image1.png"; - - axios.get.mockImplementationOnce(() => Promise.resolve({ image: image })); - - await getImage(imageUrl); - expect(axios.get).toHaveBeenCalledWith(imageUrl, defaultHeaders); - }); -}); - -describe("Index page API calls", () => { - test("Loading project list", async () => { - axios.get.mockImplementationOnce(() => Promise.resolve(200)); - const page = 3; - await readProjectList(page, accessToken); - expect(axios.get).toHaveBeenCalledWith(`${host}/api/projects`, { - ...authHeaders, - params: { page }, - }); - }); }); describe("Testing project errors API calls", () => { diff --git a/yarn.lock b/yarn.lock index be619290e..01cc20083 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3954,7 +3954,6 @@ __metadata: node-html-parser: "npm:^6.1.5" oidc-client: "npm:^1.11.5" optimize-css-assets-webpack-plugin: "npm:5.0.4" - parse-link-header: "npm:^2.0.0" path-browserify: "npm:^1.0.1" plotly.js: "npm:^3.3.1" pnp-webpack-plugin: "npm:1.6.4" @@ -3977,7 +3976,6 @@ __metadata: react-cookie: "npm:^4.1.1" react-dev-utils: "npm:^11.0.3" react-dom: "npm:^18.1.0" - react-dropzone: "npm:^12.0.4" react-i18next: "npm:^12.0.0" react-modal: "npm:^3.14.4" react-redux: "npm:^7.2.5" @@ -6999,13 +6997,6 @@ __metadata: languageName: node linkType: hard -"attr-accept@npm:^2.2.2": - version: 2.2.4 - resolution: "attr-accept@npm:2.2.4" - checksum: 10/2839a5740c8bf8ea30f84d4da850b8a31d766be787fbaf45efb94b5a2292781abfa93d274f585c6c0e64dad0e9c0b5c53ae9849c7272f0cf1f2d643a75cc166e - languageName: node - linkType: hard - "audio-context@npm:^1.0.1": version: 1.0.3 resolution: "audio-context@npm:1.0.3" @@ -12114,15 +12105,6 @@ __metadata: languageName: node linkType: hard -"file-selector@npm:^0.5.0": - version: 0.5.0 - resolution: "file-selector@npm:0.5.0" - dependencies: - tslib: "npm:^2.0.3" - checksum: 10/bbbfc5d9d1602e4bc376d67b62f44576301a8c7ae91dd6d57647b31ea497a37cef4b8a367def31f8a4a4ec6e96db886c2f509a816f8b395c3b6020f1a3dbdb1d - languageName: node - linkType: hard - "file-type@npm:^11.1.0": version: 11.1.0 resolution: "file-type@npm:11.1.0" @@ -18428,15 +18410,6 @@ __metadata: languageName: node linkType: hard -"parse-link-header@npm:^2.0.0": - version: 2.0.0 - resolution: "parse-link-header@npm:2.0.0" - dependencies: - xtend: "npm:~4.0.1" - checksum: 10/36d0d44433ec3ee80c001fe54dd29268924f06352ed82a29e4060dbd8bb28a330cf7ea7adf4815f73e0684baaf36440ac010d599094eb24130c07fc87424450c - languageName: node - linkType: hard - "parse-rect@npm:^1.2.0": version: 1.2.0 resolution: "parse-rect@npm:1.2.0" @@ -20364,19 +20337,6 @@ __metadata: languageName: node linkType: hard -"react-dropzone@npm:^12.0.4": - version: 12.1.0 - resolution: "react-dropzone@npm:12.1.0" - dependencies: - attr-accept: "npm:^2.2.2" - file-selector: "npm:^0.5.0" - prop-types: "npm:^15.8.1" - peerDependencies: - react: ">= 16.8" - checksum: 10/cafea162568d6ac5ec5100dec93a994ace8e4870a81aef7eb356ca621394c5bb7c4f536f275ba1d1bc320092fd546f2c58b89d3f1b2b0a7e3dcc0a76839a502b - languageName: node - linkType: hard - "react-error-overlay@npm:^6.0.9": version: 6.0.11 resolution: "react-error-overlay@npm:6.0.11"
{t("imageUploadButton.info")}
{file.name}