Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/login panel #225

Merged
merged 16 commits into from
Nov 1, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
5 changes: 4 additions & 1 deletion src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
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() {
const theme = createTheme();
return (
<StyledEngineProvider injectFirst>
<ThemeProvider theme={theme}>
<WrapperApp></WrapperApp>
<Store>
<WrapperApp></WrapperApp>
</Store>
</ThemeProvider>
</StyledEngineProvider>
);
Expand Down
18 changes: 10 additions & 8 deletions src/ThreeEditor/ThreeEditor.tsx
Original file line number Diff line number Diff line change
@@ -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<Editor>();

useEffect(() => {
if (containerEl.current) {

const { editor } = initEditor(containerEl.current);
setEditor(editor);

props.onEditorInitialized?.call(null, editor);

}
return () => {

}
}, [containerEl]);
}, [containerEl, props.onEditorInitialized]);


return (
Expand All @@ -30,6 +32,6 @@ function ThreeEditor() {
flexGrow: 1
}} />
);
}
}, () => true)

export default ThreeEditor;
4 changes: 2 additions & 2 deletions src/ThreeEditor/js/commands/RemoveZoneCommand.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 );

}
Expand Down
4 changes: 3 additions & 1 deletion src/ThreeEditor/util/CSG/CSGManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(){
Expand Down
2 changes: 1 addition & 1 deletion src/ThreeEditor/util/CSG/CSGZone.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
47 changes: 31 additions & 16 deletions src/WrapperApp/WrapperApp.tsx
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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<UserData | null>(null);

const handleChange = (event: SyntheticEvent, newValue: number) => {
setValue(newValue);
newValue === 5 && setCurrentUser(null);
setTabsValue(newValue);
};

const demoMode = process.env.REACT_APP_TARGET === 'demo';

return (
<Box sx={{ width: '100%', height: '100vh', display: 'flex', flexDirection: 'column' }}>
<Box sx={{ borderBottom: 1, borderColor: 'divider' }}>
<Tabs value={value} onChange={handleChange} >
<Tabs value={tabsValue} onChange={handleChange} >
<Tab label="Editor" />
<Tab label="Run" disabled={demoMode} />
<Tab label="Run" disabled={DEMO_MODE} />
<Tab label="Results" disabled />
<Tab label="Projects" disabled />
<Tab label="Logout" disabled />
<Tab label="About" />
<Tab sx={{ marginLeft: 'auto' }} label={currentUser ? "Logout" : "Login"} />
</Tabs>
</Box>
<TabPanel value={value} index={0} >
<ThreeEditor></ThreeEditor>
<TabPanel value={tabsValue} index={0} >
<ThreeEditor onEditorInitialized={(editor) => editorRef.current = editor} />
</TabPanel>
{demoMode &&
<TabPanel value={value} index={1}>
Run Simulation
{DEMO_MODE ||
<TabPanel value={tabsValue} index={1}>
<SimulationPanel />
</TabPanel>
}
<TabPanel value={tabsValue} index={5} >
<LoginPanel
handleLogin={(data) => {
setCurrentUser({ uuid: "", login: data.email })
setTabsValue(0);
}}
/>
</TabPanel>
</Box>
);
}
Expand Down
60 changes: 60 additions & 0 deletions src/WrapperApp/components/LoginPanel.tsx
Original file line number Diff line number Diff line change
@@ -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<HTMLInputElement>) => {
setEmail(event.target.value);
};
const [password, setPassword] = React.useState('');
const handlePasswordChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setPassword(event.target.value);
};
return (
<Box sx={{
margin: "0 auto",
display: "flex",
alignItems: "center",
padding: "5rem"
}}>
<Box component="form"
sx={{
margin: "0 auto",
backgroundColor: "rgba(255,255,255,.6)",
display: "flex",
flexDirection: "column",
gap: "1.5rem",
padding: "5rem"
}}
>
<TextField
id="loginField"
label="Email adress"
variant="outlined"
value={email}
onChange={handleEmailChange}
/>
<TextField
id="passwordField"
label="Password"
variant="outlined"
type="password"
value={password}
onChange={handlePasswordChange}
/>
<Button
variant="outlined"
onClick={()=>{
setPassword('')
setEmail('')
props.handleLogin({email,password})
}}
>Login</Button>
</Box>
</Box>
)
}
63 changes: 63 additions & 0 deletions src/WrapperApp/components/SimulationPanel.tsx
Original file line number Diff line number Diff line change
@@ -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<boolean>(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 (
<Box sx={{
margin: "0 auto",
width: "min(960px, 100%)",
padding: "5rem",
display: "flex",
flexDirection: "column",
gap: "1.5rem",
}}>
<LinearProgress
variant={
isInProgress ? 'indeterminate' : 'determinate'
}
value={0}
/>
<Button
sx={{
width: "min(300px, 100%)",
margin: "0 auto",
}}
onClick={sendRequest}

>
{isInProgress ? 'Stop' : 'Start'}
</Button>
</Box>
);
}
34 changes: 34 additions & 0 deletions src/services/StoreService.tsx
Original file line number Diff line number Diff line change
@@ -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<Editor | undefined>
}

const [useStore, StoreContextProvider] = createGenericContext<IStore>();

const Store = (props: StoreProps) => {
const editorRef = useRef<Editor>();

const value: IStore = {
editorRef
}

return (
<StoreContextProvider value={value}>
{props.children}
</StoreContextProvider>
)
};

export {
useStore,
Store
}
3 changes: 3 additions & 0 deletions src/util/Config.ts
Original file line number Diff line number Diff line change
@@ -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';
17 changes: 17 additions & 0 deletions src/util/GenericContext.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import React from "react";

export const createGenericContext = <T extends unknown>() => {
// Create a context with a generic parameter or undefined
const genericContext = React.createContext<T | undefined>(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;
};
4 changes: 4 additions & 0 deletions src/util/user/UserData.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export default interface UserData{
uuid: string,
login: string,
}