From 057f4c4d794a30918c172d15f808fdce5810996b Mon Sep 17 00:00:00 2001 From: Jakub Niechaj Date: Mon, 1 Nov 2021 17:24:29 +0100 Subject: [PATCH] Feature/login panel (#225) * build(deps): bump @types/node from 16.11.4 to 16.11.6 Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 16.11.4 to 16.11.6. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) --- updated-dependencies: - dependency-name: "@types/node" dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * Fix RemoveZoneCommand * build(deps): bump @mui/material from 5.0.5 to 5.0.6 Bumps [@mui/material](https://github.com/mui-org/material-ui/tree/HEAD/packages/mui-material) from 5.0.5 to 5.0.6. - [Release notes](https://github.com/mui-org/material-ui/releases) - [Changelog](https://github.com/mui-org/material-ui/blob/master/CHANGELOG.md) - [Commits](https://github.com/mui-org/material-ui/commits/v5.0.6/packages/mui-material) --- updated-dependencies: - dependency-name: "@mui/material" dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * build(deps-dev): bump @types/react from 17.0.32 to 17.0.33 Bumps [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react) from 17.0.32 to 17.0.33. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react) --- updated-dependencies: - dependency-name: "@types/react" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * Add simple login panel * Add start simulation ui in SimulationPanel * add mocked api request (#204) * update post req (#204) * update comment * update url * update urls and cors (#204) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: ostatni5 <26521377+ostatni5@users.noreply.github.com> --- package-lock.json | 5 ++ package.json | 1 + src/App.tsx | 5 +- src/ThreeEditor/ThreeEditor.tsx | 18 +++--- .../js/commands/RemoveZoneCommand.js | 4 +- src/ThreeEditor/util/CSG/CSGManager.ts | 4 +- src/ThreeEditor/util/CSG/CSGZone.ts | 2 +- src/WrapperApp/WrapperApp.tsx | 47 +++++++++----- src/WrapperApp/components/LoginPanel.tsx | 60 ++++++++++++++++++ src/WrapperApp/components/SimulationPanel.tsx | 63 +++++++++++++++++++ src/services/StoreService.tsx | 34 ++++++++++ src/util/Config.ts | 3 + src/util/GenericContext.ts | 17 +++++ src/util/user/UserData.tsx | 4 ++ 14 files changed, 238 insertions(+), 29 deletions(-) create mode 100644 src/WrapperApp/components/LoginPanel.tsx create mode 100644 src/WrapperApp/components/SimulationPanel.tsx create mode 100644 src/services/StoreService.tsx create mode 100644 src/util/Config.ts create mode 100644 src/util/GenericContext.ts create mode 100644 src/util/user/UserData.tsx diff --git a/package-lock.json b/package-lock.json index 265ef7e6f..1d069aa31 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9182,6 +9182,11 @@ "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.4.tgz", "integrity": "sha512-ZRbnvdg/NxqzC7L9Uyqzf4psi1OM4Cuc+sJAkQPjO6XkQIJTNbfK2Rsmbw8fx1p2mkZdp2FZYo2+LwXYY/uwIA==" }, + "ky": { + "version": "0.28.6", + "resolved": "https://registry.npmjs.org/ky/-/ky-0.28.6.tgz", + "integrity": "sha512-EjxET5qSsaLUj1BSFtxPjEtRgF5JOhdroPwMNJFH/VvzruWQFBmh6W7GtqjBR56UZw4dBFTKLvx9nDxxnFXc7w==" + }, "language-subtag-registry": { "version": "0.3.21", "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.21.tgz", diff --git a/package.json b/package.json index 720f0bbc8..1242facf5 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "@types/signals": "^1.0.1", "@types/throttle-debounce": "^2.1.0", "comlink": "^4.3.1", + "ky": "^0.28.6", "react": "^17.0.2", "react-async-script": "^1.2.0", "react-dom": "^17.0.2", diff --git a/src/App.tsx b/src/App.tsx index e2765255a..e23f439f1 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,6 +1,7 @@ import { createTheme } from '@mui/material'; import { ThemeProvider, StyledEngineProvider } from '@mui/material/styles'; import './App.css'; +import { Store } from './services/StoreService'; import WrapperApp from './WrapperApp/WrapperApp'; function App() { @@ -8,7 +9,9 @@ function App() { return ( - + + + ); diff --git a/src/ThreeEditor/ThreeEditor.tsx b/src/ThreeEditor/ThreeEditor.tsx index d1ae283f7..9418b002e 100644 --- a/src/ThreeEditor/ThreeEditor.tsx +++ b/src/ThreeEditor/ThreeEditor.tsx @@ -1,26 +1,28 @@ -import React, { useEffect, useRef, useState } from 'react'; +import React, { useEffect, useRef } from 'react'; import './css/main.css'; import { Editor } from './js/Editor'; import { initEditor } from './main'; +interface ThreeEditorProps { + onEditorInitialized?: (editor: Editor) => void +} -function ThreeEditor() { +// React.memo(component, ()=>true) to prevent re-rendering and re-initializing editor +const ThreeEditor = React.memo(function ThreeEditorComponent(props: ThreeEditorProps) { const containerEl = useRef(null); - - const [, setEditor] = useState(); - useEffect(() => { if (containerEl.current) { const { editor } = initEditor(containerEl.current); - setEditor(editor); + + props.onEditorInitialized?.call(null, editor); } return () => { } - }, [containerEl]); + }, [containerEl, props.onEditorInitialized]); return ( @@ -30,6 +32,6 @@ function ThreeEditor() { flexGrow: 1 }} /> ); -} +}, () => true) export default ThreeEditor; diff --git a/src/ThreeEditor/js/commands/RemoveZoneCommand.js b/src/ThreeEditor/js/commands/RemoveZoneCommand.js index 1961c259a..fefa0295e 100644 --- a/src/ThreeEditor/js/commands/RemoveZoneCommand.js +++ b/src/ThreeEditor/js/commands/RemoveZoneCommand.js @@ -22,14 +22,14 @@ class RemoveZoneCommand extends Command { execute() { - this.editor.zonesManager.remove( this.zone ); + this.editor.zonesManager.removeZone( this.zone ); this.editor.deselect(); } undo() { - this.editor.zonesManager.add( this.zone ); + this.editor.zonesManager.addZone( this.zone ); this.editor.select( this.zone ); } diff --git a/src/ThreeEditor/util/CSG/CSGManager.ts b/src/ThreeEditor/util/CSG/CSGManager.ts index fc0fb0a94..1b17ec361 100644 --- a/src/ThreeEditor/util/CSG/CSGManager.ts +++ b/src/ThreeEditor/util/CSG/CSGManager.ts @@ -16,7 +16,9 @@ interface CSGManagerJSON { boundingZone: BoundingZoneJSON } -export class CSGZonesContainer extends THREE.Group{ +export class CSGZonesContainer extends THREE.Group implements ISimulationObject { + notRemovable = true; + notMoveable = true; children: CSGZone[]; readonly isCSGZonesContainer: true = true; constructor(){ diff --git a/src/ThreeEditor/util/CSG/CSGZone.ts b/src/ThreeEditor/util/CSG/CSGZone.ts index 9f7baf697..1eeafb757 100644 --- a/src/ThreeEditor/util/CSG/CSGZone.ts +++ b/src/ThreeEditor/util/CSG/CSGZone.ts @@ -245,7 +245,7 @@ export class CSGZone extends THREE.Mesh { union.map((operation) => CSGOperation.fromJSON(editor, operation)) ); - let subscribedObjectsUuid = undefined; + let subscribedObjectsUuid; if(Array.isArray(data.subscribedObjectsUuid)) subscribedObjectsUuid = new Set(data.subscribedObjectsUuid) diff --git a/src/WrapperApp/WrapperApp.tsx b/src/WrapperApp/WrapperApp.tsx index 37ed6bf5b..707219b93 100644 --- a/src/WrapperApp/WrapperApp.tsx +++ b/src/WrapperApp/WrapperApp.tsx @@ -1,11 +1,15 @@ -import * as React from 'react'; -import Tabs from '@mui/material/Tabs'; -import Tab from '@mui/material/Tab'; +import React, { SyntheticEvent, useState } from 'react'; +import { css } from '@emotion/css'; import Box from '@mui/material/Box'; -import { SyntheticEvent, useState } from 'react'; +import LoginPanel from './components/LoginPanel'; +import Tab from '@mui/material/Tab'; +import Tabs from '@mui/material/Tabs'; import ThreeEditor from '../ThreeEditor/ThreeEditor'; +import UserData from '../util/user/UserData'; +import SimulationPanel from './components/SimulationPanel'; +import { useStore } from '../services/StoreService'; +import { DEMO_MODE } from '../util/Config'; -import { css } from '@emotion/css'; interface TabPanelProps { children?: React.ReactNode; @@ -34,34 +38,45 @@ function TabPanel(props: TabPanelProps) { function WrapperApp() { - const [value, setValue] = useState(0); + const { editorRef } = useStore(); + + const [tabsValue, setTabsValue] = useState(0); + const [currentUser, setCurrentUser] = useState(null); const handleChange = (event: SyntheticEvent, newValue: number) => { - setValue(newValue); + newValue === 5 && setCurrentUser(null); + setTabsValue(newValue); }; - const demoMode = process.env.REACT_APP_TARGET === 'demo'; return ( - + - + - + - - + + editorRef.current = editor} /> - {demoMode && - - Run Simulation + {DEMO_MODE || + + } + + { + setCurrentUser({ uuid: "", login: data.email }) + setTabsValue(0); + }} + /> + ); } diff --git a/src/WrapperApp/components/LoginPanel.tsx b/src/WrapperApp/components/LoginPanel.tsx new file mode 100644 index 000000000..d6cfa450b --- /dev/null +++ b/src/WrapperApp/components/LoginPanel.tsx @@ -0,0 +1,60 @@ +import { Box, Button, TextField } from "@mui/material"; +import React from "react"; + +interface LoginPanelProps { + handleLogin: (data:{email:string,password:string}) => void; +} + +export default function LoginPanel(props: LoginPanelProps) { + const [email, setEmail] = React.useState(''); + const handleEmailChange = (event: React.ChangeEvent) => { + setEmail(event.target.value); + }; + const [password, setPassword] = React.useState(''); + const handlePasswordChange = (event: React.ChangeEvent) => { + setPassword(event.target.value); + }; + return ( + + + + + + + + ) +} \ No newline at end of file diff --git a/src/WrapperApp/components/SimulationPanel.tsx b/src/WrapperApp/components/SimulationPanel.tsx new file mode 100644 index 000000000..fed7f538d --- /dev/null +++ b/src/WrapperApp/components/SimulationPanel.tsx @@ -0,0 +1,63 @@ +import { Box, Button, LinearProgress } from '@mui/material'; +import ky from 'ky'; + +import React, { useState } from "react"; +import { useStore } from '../../services/StoreService'; +import { BACKEND_URL, CORS } from '../../util/Config'; + +interface SimulationPanelProps { + onError?: (error: unknown) => void; + onSuccess?: (result: unknown) => void; +} + +export default function SimulationPanel(props: SimulationPanelProps) { + const { editorRef } = useStore(); + const [isInProgress, setInProgress] = useState(false); + + const sendRequest = () => { + setInProgress(true); + ky.post(`${BACKEND_URL}/sh/demo`, { + mode: CORS ? 'cors' : 'no-cors', + json: editorRef.current?.toJSON() + }) + .json() + .then((response) => { + alert(response); + props.onSuccess?.call(null, response); + }) + .catch((error) => { + alert(error); + props.onError?.call(null, error); + }).finally(() => { + setInProgress(false); + }); + }; + + return ( + + + + + ); +} \ No newline at end of file diff --git a/src/services/StoreService.tsx b/src/services/StoreService.tsx new file mode 100644 index 000000000..d545021c6 --- /dev/null +++ b/src/services/StoreService.tsx @@ -0,0 +1,34 @@ +import { ReactNode, useRef } from "react"; +import { Editor } from "../ThreeEditor/js/Editor"; +import { createGenericContext } from "../util/GenericContext"; + + + +export interface StoreProps { + children: ReactNode +} + +export interface IStore { + editorRef: React.MutableRefObject +} + +const [useStore, StoreContextProvider] = createGenericContext(); + +const Store = (props: StoreProps) => { + const editorRef = useRef(); + + const value: IStore = { + editorRef + } + + return ( + + {props.children} + + ) +}; + +export { + useStore, + Store +} diff --git a/src/util/Config.ts b/src/util/Config.ts new file mode 100644 index 000000000..2a90ccb72 --- /dev/null +++ b/src/util/Config.ts @@ -0,0 +1,3 @@ +export const BACKEND_URL = process.env.REACT_APP_BACKEND_URL ?? 'http://localhost:5000'; +export const CORS = false; +export const DEMO_MODE = process.env.REACT_APP_TARGET === 'demo'; \ No newline at end of file diff --git a/src/util/GenericContext.ts b/src/util/GenericContext.ts new file mode 100644 index 000000000..08bba3102 --- /dev/null +++ b/src/util/GenericContext.ts @@ -0,0 +1,17 @@ +import React from "react"; + +export const createGenericContext = () => { + // Create a context with a generic parameter or undefined + const genericContext = React.createContext(undefined); + + // Check if the value provided to the context is defined or throw an error + const useGenericContext = () => { + const contextIsDefined = React.useContext(genericContext); + if (!contextIsDefined) { + throw new Error("useGenericContext must be used within a Provider"); + } + return contextIsDefined; + }; + + return [useGenericContext, genericContext.Provider] as const; +}; \ No newline at end of file diff --git a/src/util/user/UserData.tsx b/src/util/user/UserData.tsx new file mode 100644 index 000000000..e35035ff5 --- /dev/null +++ b/src/util/user/UserData.tsx @@ -0,0 +1,4 @@ +export default interface UserData{ + uuid: string, + login: string, +} \ No newline at end of file