diff --git a/package-lock.json b/package-lock.json
index 330d1b38f..9cb3da7c6 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -32,6 +32,7 @@
"date-fns": "^2.30.0",
"glob": "^10.3.10",
"keycloak-js": "^22.0.4",
+ "keycloak-react-web": "^0.1.19",
"ky": "^1.1.0",
"material-ui-popup-state": "^5.0.9",
"notistack": "^3.0.1",
@@ -14281,6 +14282,23 @@
"js-sha256": "^0.9.0"
}
},
+ "node_modules/keycloak-react-web": {
+ "version": "0.1.19",
+ "resolved": "https://registry.npmjs.org/keycloak-react-web/-/keycloak-react-web-0.1.19.tgz",
+ "integrity": "sha512-ECpJ75rajWPB77zjUcigzpCofi6ynqACKNDyhzx7fUI17JlsCsoRGGGDrDGPlVh+sMvurQNcmZHGhSNWnzCZAw==",
+ "dependencies": {
+ "keycloak-js": "^19.0.2"
+ }
+ },
+ "node_modules/keycloak-react-web/node_modules/keycloak-js": {
+ "version": "19.0.3",
+ "resolved": "https://registry.npmjs.org/keycloak-js/-/keycloak-js-19.0.3.tgz",
+ "integrity": "sha512-mzCBxrzfl+vB551Q7MB+T9+40IHU4i0a6g1eTatzeEGrQMis5m/BqvPC3kxTsI+/LxHbB9XYQE3u9SlWKDHQCw==",
+ "dependencies": {
+ "base64-js": "^1.5.1",
+ "js-sha256": "^0.9.0"
+ }
+ },
"node_modules/kind-of": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
@@ -32645,6 +32663,25 @@
"js-sha256": "^0.9.0"
}
},
+ "keycloak-react-web": {
+ "version": "0.1.19",
+ "resolved": "https://registry.npmjs.org/keycloak-react-web/-/keycloak-react-web-0.1.19.tgz",
+ "integrity": "sha512-ECpJ75rajWPB77zjUcigzpCofi6ynqACKNDyhzx7fUI17JlsCsoRGGGDrDGPlVh+sMvurQNcmZHGhSNWnzCZAw==",
+ "requires": {
+ "keycloak-js": "^19.0.2"
+ },
+ "dependencies": {
+ "keycloak-js": {
+ "version": "19.0.3",
+ "resolved": "https://registry.npmjs.org/keycloak-js/-/keycloak-js-19.0.3.tgz",
+ "integrity": "sha512-mzCBxrzfl+vB551Q7MB+T9+40IHU4i0a6g1eTatzeEGrQMis5m/BqvPC3kxTsI+/LxHbB9XYQE3u9SlWKDHQCw==",
+ "requires": {
+ "base64-js": "^1.5.1",
+ "js-sha256": "^0.9.0"
+ }
+ }
+ }
+ },
"kind-of": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
diff --git a/package.json b/package.json
index 5659410f6..be8e0f148 100644
--- a/package.json
+++ b/package.json
@@ -27,6 +27,7 @@
"date-fns": "^2.30.0",
"glob": "^10.3.10",
"keycloak-js": "^22.0.4",
+ "keycloak-react-web": "^0.1.19",
"ky": "^1.1.0",
"material-ui-popup-state": "^5.0.9",
"notistack": "^3.0.1",
diff --git a/src/App.tsx b/src/App.tsx
index cd85d7948..6d77afa1c 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -9,6 +9,7 @@ import { ConfigProvider } from './config/ConfigService';
import { PythonConverterService } from './PythonConverter/PythonConverterService';
import { Auth } from './services/AuthService';
import { DialogProvider } from './services/DialogService';
+import { KeycloakAuth } from './services/KeycloakAuthService';
import { Loader } from './services/LoaderService';
import { ShSimulation } from './services/ShSimulatorService';
import { Store } from './services/StoreService';
@@ -84,11 +85,12 @@ function App() {
,
,
,
+ ,
+ ,
+ ,
,
,
,
- ,
- ,
]}>
diff --git a/src/ThreeEditor/components/Dialog/ClearHistoryDialog.tsx b/src/ThreeEditor/components/Dialog/ClearHistoryDialog.tsx
index 6603bafb5..e5e594ee4 100644
--- a/src/ThreeEditor/components/Dialog/ClearHistoryDialog.tsx
+++ b/src/ThreeEditor/components/Dialog/ClearHistoryDialog.tsx
@@ -1,11 +1,12 @@
import { Button } from '@mui/material';
-import { useStore } from '../../../services/StoreService';
+import { StoreContext } from '../../../services/StoreService';
import { ConcreteDialogProps, CustomDialog } from './CustomDialog';
-export function ClearHistoryDialog({ onClose }: ConcreteDialogProps) {
- const { yaptideEditor } = useStore();
-
+export function ClearHistoryDialog({
+ onClose,
+ yaptideEditor
+}: ConcreteDialogProps>>) {
return (
theme.palette.grey[500]
+ color: ({ palette }: Theme) => palette.grey[500]
}}>
diff --git a/src/ThreeEditor/components/Dialog/EditProjectInfoDialog.tsx b/src/ThreeEditor/components/Dialog/EditProjectInfoDialog.tsx
index d4c13e4cd..8e5a2b9ca 100644
--- a/src/ThreeEditor/components/Dialog/EditProjectInfoDialog.tsx
+++ b/src/ThreeEditor/components/Dialog/EditProjectInfoDialog.tsx
@@ -1,11 +1,13 @@
import { Box, Button, TextField } from '@mui/material';
import { useState } from 'react';
-import { useStore } from '../../../services/StoreService';
+import { StoreContext } from '../../../services/StoreService';
import { ConcreteDialogProps, CustomDialog } from './CustomDialog';
-export function EditProjectInfoDialog({ onClose }: ConcreteDialogProps) {
- const { yaptideEditor } = useStore();
+export function EditProjectInfoDialog({
+ onClose,
+ yaptideEditor
+}: ConcreteDialogProps>>) {
const [title, setTitle] = useState(yaptideEditor?.config.getKey('project/title'));
const [description, setDescription] = useState(
yaptideEditor?.config.getKey('project/description')
diff --git a/src/ThreeEditor/components/Dialog/LoadFileDialog.tsx b/src/ThreeEditor/components/Dialog/LoadFileDialog.tsx
index 4c8ae48d0..9ae869a82 100644
--- a/src/ThreeEditor/components/Dialog/LoadFileDialog.tsx
+++ b/src/ThreeEditor/components/Dialog/LoadFileDialog.tsx
@@ -1,20 +1,21 @@
import { Button } from '@mui/material';
import Typography from '@mui/material/Typography';
-import { useStore } from '../../../services/StoreService';
+import { StoreContext } from '../../../services/StoreService';
import { EditorJson } from '../../js/EditorJson';
import { ConcreteDialogProps, CustomDialog } from './CustomDialog';
export function LoadFileDialog({
onClose,
validVersion = true,
- data
-}: ConcreteDialogProps<{
- validVersion: boolean;
- data: EditorJson;
-}>) {
- const { yaptideEditor } = useStore();
-
+ data,
+ yaptideEditor
+}: ConcreteDialogProps<
+ {
+ validVersion: boolean;
+ data: EditorJson;
+ } & Required>
+>) {
return (
>>) {
return (
) {
+ return (
+
+
+
+ );
+}
diff --git a/src/ThreeEditor/components/Dialog/RunSimulationDialog.tsx b/src/ThreeEditor/components/Dialog/RunSimulationDialog.tsx
index 6ecb84c16..bfb86c323 100644
--- a/src/ThreeEditor/components/Dialog/RunSimulationDialog.tsx
+++ b/src/ThreeEditor/components/Dialog/RunSimulationDialog.tsx
@@ -2,8 +2,8 @@ import { Card, CardContent, Fade, Modal } from '@mui/material';
import { enqueueSnackbar } from 'notistack';
import { useState } from 'react';
-import { useShSimulation } from '../../../services/ShSimulatorService';
-import { useStore } from '../../../services/StoreService';
+import { RestSimulationContext } from '../../../services/ShSimulatorService';
+import { StoreContext } from '../../../services/StoreService';
import { SimulatorType } from '../../../types/RequestTypes';
import { SimulationInputFiles } from '../../../types/ResponseTypes';
import {
@@ -19,15 +19,19 @@ export function RunSimulationDialog({
inputFiles = {},
simulator,
onClose,
- onSubmit = () => {}
-}: ConcreteDialogProps<{
- onSubmit?: (jobId: string) => void;
- inputFiles?: Record;
- simulator: SimulatorType;
-}>) {
- const { yaptideEditor } = useStore();
+ onSubmit = () => {},
+ yaptideEditor,
+ postJobDirect,
+ postJobBatch
+}: ConcreteDialogProps<
+ {
+ onSubmit?: (jobId: string) => void;
+ inputFiles?: Record;
+ simulator: SimulatorType;
+ } & Required> &
+ Pick
+>) {
const [controller] = useState(new AbortController());
- const { postJobDirect, postJobBatch } = useShSimulation();
const sendSimulationRequest = (
postJobFn: typeof postJobDirect,
runType: SimulationRunType,
@@ -46,16 +50,22 @@ export function RunSimulationDialog({
runType === 'batch'
? {
...batchOptions,
- arrayOptions: batchOptions.arrayOptions?.reduce((acc, curr) => {
- acc[curr.optionKey] = curr.optionValue;
+ arrayOptions: batchOptions.arrayOptions?.reduce(
+ (acc, curr) => {
+ acc[curr.optionKey] = curr.optionValue;
- return acc;
- }, {} as Record),
- collectOptions: batchOptions.collectOptions?.reduce((acc, curr) => {
- acc[curr.optionKey] = curr.optionValue;
+ return acc;
+ },
+ {} as Record
+ ),
+ collectOptions: batchOptions.collectOptions?.reduce(
+ (acc, curr) => {
+ acc[curr.optionKey] = curr.optionValue;
- return acc;
- }, {} as Record)
+ return acc;
+ },
+ {} as Record
+ )
}
: undefined;
diff --git a/src/ThreeEditor/components/Dialog/SaveFileDialog.tsx b/src/ThreeEditor/components/Dialog/SaveFileDialog.tsx
index ebd6413dd..e1ed63d08 100644
--- a/src/ThreeEditor/components/Dialog/SaveFileDialog.tsx
+++ b/src/ThreeEditor/components/Dialog/SaveFileDialog.tsx
@@ -2,7 +2,7 @@ import { Button, Checkbox, FormControlLabel, TextField } from '@mui/material';
import { ChangeEvent, useCallback, useEffect, useState } from 'react';
import { FullSimulationData } from '../../../services/ShSimulatorService';
-import { useStore } from '../../../services/StoreService';
+import { StoreContext } from '../../../services/StoreService';
import { saveString } from '../../../util/File';
import { ConcreteDialogProps, CustomDialog } from './CustomDialog';
@@ -25,13 +25,15 @@ export function SaveFileDialog({
onClose,
name: defaultName = 'editor',
results: providedResults,
- disableCheckbox = false
-}: ConcreteDialogProps<{
- name?: string;
- results?: FullSimulationData;
- disableCheckbox?: boolean;
-}>) {
- const { yaptideEditor } = useStore();
+ disableCheckbox = false,
+ yaptideEditor
+}: ConcreteDialogProps<
+ {
+ name?: string;
+ results?: FullSimulationData;
+ disableCheckbox?: boolean;
+ } & Required>
+>) {
const results: FullSimulationData | undefined = providedResults ?? yaptideEditor?.getResults();
const [keepResults, setKeepResults] = useState(false);
diff --git a/src/ThreeEditor/components/Editor/EditorAppBar/EditorAppBar.tsx b/src/ThreeEditor/components/Editor/EditorAppBar/EditorAppBar.tsx
index 41e2c1295..99c492607 100644
--- a/src/ThreeEditor/components/Editor/EditorAppBar/EditorAppBar.tsx
+++ b/src/ThreeEditor/components/Editor/EditorAppBar/EditorAppBar.tsx
@@ -12,6 +12,7 @@ import { useCallback, useEffect, useMemo, useState } from 'react';
import { useDialog } from '../../../../services/DialogService';
import { useLoader } from '../../../../services/LoaderService';
+import { useStore } from '../../../../services/StoreService';
import { YaptideEditor } from '../../../js/YaptideEditor';
import { EditorTitleBar } from './components/EditorTitlebar';
import { EditorToolbar } from './components/EditorToolbar';
@@ -30,11 +31,12 @@ type AppBarOptions = {
function EditorAppBar({ editor }: AppBarProps) {
const { loadFromJson, loadFromFiles, loadFromUrl, loadFromJsonString } = useLoader();
- const [openTheOpenFileDialog] = useDialog('openFile');
- const [openTheSaveFileDialog] = useDialog('saveFile');
- const [openTheNewProjectDialog] = useDialog('newProject');
+ const { open: openTheOpenFileDialog } = useDialog('openFile');
+ const { open: openTheSaveFileDialog } = useDialog('saveFile');
+ const { open: openTheNewProjectDialog } = useDialog('newProject');
const [canUndo, setCanUndo] = useState((editor?.history.undos.length ?? 0) > 0);
const [canRedo, setCanRedo] = useState((editor?.history.redos.length ?? 0) > 0);
+ const { yaptideEditor } = useStore();
const updateHistoryButtons = useCallback(() => {
setCanUndo((editor?.history.undos.length ?? 0) > 0);
@@ -71,7 +73,7 @@ function EditorAppBar({ editor }: AppBarProps) {
label: 'New',
icon: ,
disabled: false,
- onClick: () => openTheNewProjectDialog()
+ onClick: () => yaptideEditor && openTheNewProjectDialog({ yaptideEditor })
},
{
label: 'Open',
@@ -95,7 +97,7 @@ function EditorAppBar({ editor }: AppBarProps) {
label: 'Save as',
icon: ,
disabled: false,
- onClick: () => openTheSaveFileDialog()
+ onClick: () => yaptideEditor && openTheSaveFileDialog({ yaptideEditor })
},
{
label: 'Redo (ctrl+y)',
@@ -113,6 +115,7 @@ function EditorAppBar({ editor }: AppBarProps) {
[
canUndo,
canRedo,
+ yaptideEditor,
openTheNewProjectDialog,
openTheOpenFileDialog,
loadFromFiles,
diff --git a/src/ThreeEditor/components/Editor/EditorAppBar/components/EditorTitlebar.tsx b/src/ThreeEditor/components/Editor/EditorAppBar/components/EditorTitlebar.tsx
index aaeef64a9..4dbc2d526 100644
--- a/src/ThreeEditor/components/Editor/EditorAppBar/components/EditorTitlebar.tsx
+++ b/src/ThreeEditor/components/Editor/EditorAppBar/components/EditorTitlebar.tsx
@@ -15,7 +15,7 @@ import { useDialog } from '../../../../../services/DialogService';
import { useStore } from '../../../../../services/StoreService';
export function EditorTitleBar() {
- const [open, , isOpen] = useDialog('editProject');
+ const { open: openEditProjectDialog, isOpen } = useDialog('editProject');
const { yaptideEditor } = useStore();
const [saving, setSaving] = useState(false);
const [editMode, setEditMode] = useState(false);
@@ -60,7 +60,11 @@ export function EditorTitleBar() {
key='edit project description'
onClick={() => {
popupState.close();
- open();
+
+ if (yaptideEditor)
+ openEditProjectDialog({
+ yaptideEditor
+ });
}}>
Edit Project Description
diff --git a/src/ThreeEditor/components/Editor/EditorMenu/EditorMenu.tsx b/src/ThreeEditor/components/Editor/EditorMenu/EditorMenu.tsx
index 54a1a4177..b9ec10cf0 100644
--- a/src/ThreeEditor/components/Editor/EditorMenu/EditorMenu.tsx
+++ b/src/ThreeEditor/components/Editor/EditorMenu/EditorMenu.tsx
@@ -3,6 +3,7 @@ import { MouseEvent, useCallback, useEffect, useState } from 'react';
import { Object3D } from 'three';
import { useDialog } from '../../../../services/DialogService';
+import { useStore } from '../../../../services/StoreService';
import { useSignal } from '../../../../util/hooks/signals';
import { toggleFullscreen } from '../../../../util/toggleFullscreen';
import {
@@ -104,8 +105,9 @@ function MenuPosition({ label, idx, openIdx, setOpenIdx, options }: MenuPosition
}
export function EditorMenu({ editor }: EditorMenuProps) {
- const [open] = useDialog('clearHistory');
+ const { open: openClearHistory } = useDialog('clearHistory');
const [openIdx, setOpenIdx] = useState(-1);
+ const { yaptideEditor } = useStore();
const [, setSelectedObject] = useState(editor?.selected);
const handleObjectUpdate = useCallback((o: Object3D) => {
@@ -188,7 +190,7 @@ export function EditorMenu({ editor }: EditorMenuProps) {
[
{
label: 'Clear history',
- onClick: () => open(),
+ onClick: () => yaptideEditor && openClearHistory({ yaptideEditor }),
disabled:
editor?.history.undos.length === 0 &&
editor?.history.redos.length === 0
diff --git a/src/ThreeEditor/components/ZoneManagerPanel/BooleanAlgebra/OperationToggle.tsx b/src/ThreeEditor/components/ZoneManagerPanel/BooleanAlgebra/OperationToggle.tsx
index 05244c9b5..0adaa6ca5 100644
--- a/src/ThreeEditor/components/ZoneManagerPanel/BooleanAlgebra/OperationToggle.tsx
+++ b/src/ThreeEditor/components/ZoneManagerPanel/BooleanAlgebra/OperationToggle.tsx
@@ -6,6 +6,7 @@ import {
Chip,
IconButton,
SvgIconProps,
+ Theme,
ToggleButton,
ToggleButtonGroup,
ToggleButtonGroupProps,
@@ -169,10 +170,8 @@ function OperationInput({
'height': 55,
'width': 55,
'margin': '0 auto',
- 'color': theme =>
- theme.palette.mode === 'dark'
- ? 'text.primary'
- : 'secondary.main',
+ 'color': ({ palette }: Theme) =>
+ palette.mode === 'dark' ? 'text.primary' : 'secondary.main',
'&:hover': {
color: 'error.main'
}
@@ -217,8 +216,8 @@ function OperationInput({
'padding': '4px 0',
'minWidth': '56px',
'flexDirection': 'column',
- '&.Mui-selected': theme =>
- theme.palette.mode === 'dark'
+ '&.Mui-selected': ({ palette }: Theme) =>
+ palette.mode === 'dark'
? {}
: {
backgroundColor: 'primary.main',
diff --git a/src/ThreeEditor/components/ZoneManagerPanel/ZoneManagerPanel.tsx b/src/ThreeEditor/components/ZoneManagerPanel/ZoneManagerPanel.tsx
index dbc91717b..1322e9a8c 100644
--- a/src/ThreeEditor/components/ZoneManagerPanel/ZoneManagerPanel.tsx
+++ b/src/ThreeEditor/components/ZoneManagerPanel/ZoneManagerPanel.tsx
@@ -8,6 +8,7 @@ import {
SelectChangeEvent,
Tab,
Tabs,
+ Theme,
Tooltip
} from '@mui/material';
import {
@@ -379,8 +380,8 @@ function ZoneManagerPanel(props: BooleanZoneManagerPanelProps) {
'width': '24px',
'height': '24px',
'borderRadius': 0,
- 'color': theme =>
- theme.palette.mode === 'dark'
+ 'color': ({ palette }: Theme) =>
+ palette.mode === 'dark'
? 'text.primary'
: 'secondary.main',
'&:hover': {
@@ -398,7 +399,7 @@ function ZoneManagerPanel(props: BooleanZoneManagerPanelProps) {
ref={backdropRef}
sx={{
color: '#fff',
- zIndex: theme => theme.zIndex.drawer + 1,
+ zIndex: ({ zIndex }: Theme) => zIndex.drawer + 1,
position: 'absolute',
left: 0,
bottom: 0,
diff --git a/src/ThreeEditor/js/Storage.js b/src/ThreeEditor/js/Storage.js
index 22485481f..2af75efdb 100644
--- a/src/ThreeEditor/js/Storage.js
+++ b/src/ThreeEditor/js/Storage.js
@@ -20,6 +20,21 @@ function Storage() {
let database;
+ const deleteDatabase = function () {
+ const req = indexedDB.deleteDatabase(name);
+ req.onsuccess = function () {
+ console.log('Deleted database successfully');
+ };
+
+ req.onerror = function () {
+ console.log('Could not delete database');
+ };
+
+ req.onblocked = function () {
+ console.log('Could not delete database due to the operation being blocked');
+ };
+ };
+
return {
init: function (callback) {
const request = indexedDB.open(name, version);
@@ -39,27 +54,42 @@ function Storage() {
request.onerror = function (event) {
console.error('IndexedDB', event);
+ // delete database
+ deleteDatabase();
};
},
get: function (callback) {
- const transaction = database.transaction(['states'], 'readwrite');
- const objectStore = transaction.objectStore('states');
- const request = objectStore.get(0);
- request.onsuccess = function (event) {
- callback(event.target.result);
- };
+ try {
+ const transaction = database.transaction(['states'], 'readwrite');
+ const objectStore = transaction.objectStore('states');
+ const request = objectStore.get(0);
+ request.onsuccess = function (event) {
+ callback(event.target.result);
+ };
+ } catch (error) {
+ console.error(error);
+ deleteDatabase();
+ }
},
set: function (data) {
- const start = performance.now();
-
- const transaction = database.transaction(['states'], 'readwrite');
- const objectStore = transaction.objectStore('states');
- const request = objectStore.put(data, 0);
- request.onsuccess = function () {
- devLog('Saved state to IndexedDB.', `${(performance.now() - start).toFixed(2)}ms`);
- };
+ try {
+ const start = performance.now();
+
+ const transaction = database.transaction(['states'], 'readwrite');
+ const objectStore = transaction.objectStore('states');
+ const request = objectStore.put(data, 0);
+ request.onsuccess = function () {
+ devLog(
+ 'Saved state to IndexedDB.',
+ `${(performance.now() - start).toFixed(2)}ms`
+ );
+ };
+ } catch (error) {
+ console.error(error);
+ deleteDatabase();
+ }
},
clear: function () {
diff --git a/src/WrapperApp/components/InputEditor/InputFilesEditor.tsx b/src/WrapperApp/components/InputEditor/InputFilesEditor.tsx
index e2ef43e65..ce35c7f07 100644
--- a/src/WrapperApp/components/InputEditor/InputFilesEditor.tsx
+++ b/src/WrapperApp/components/InputEditor/InputFilesEditor.tsx
@@ -5,6 +5,7 @@ import CodeEditor from '@uiw/react-textarea-code-editor';
import { useConfig } from '../../../config/ConfigService';
import { useAuth } from '../../../services/AuthService';
import { useDialog } from '../../../services/DialogService';
+import { useShSimulation } from '../../../services/ShSimulatorService';
import { useStore } from '../../../services/StoreService';
import { SimulatorType } from '../../../types/RequestTypes';
import {
@@ -26,8 +27,9 @@ interface InputFilesEditorProps {
}
export function InputFilesEditor(props: InputFilesEditorProps) {
- const [open] = useDialog('runSimulation');
- const { setTrackedId } = useStore();
+ const { open: openRunSimulationDialog } = useDialog('runSimulation');
+ const { postJobDirect, postJobBatch } = useShSimulation();
+ const { yaptideEditor, setTrackedId } = useStore();
const { demoMode } = useConfig();
const { isAuthorized } = useAuth();
const inputFiles = props.inputFiles ?? _defaultShInputFiles;
@@ -85,12 +87,16 @@ export function InputFilesEditor(props: InputFilesEditorProps) {
variant='contained'
disabled={demoMode || !isAuthorized}
onClick={() =>
- open({
+ yaptideEditor &&
+ openRunSimulationDialog({
inputFiles: Object.fromEntries(
Object.entries(inputFiles).filter(([, data]) => data.length > 0)
),
simulator: props.simulator,
- onSubmit: setTrackedId
+ onSubmit: setTrackedId,
+ postJobDirect,
+ postJobBatch,
+ yaptideEditor
})
}>
Run with these input files
diff --git a/src/WrapperApp/components/Panels/LoginPanel.tsx b/src/WrapperApp/components/Panels/LoginPanel.tsx
index 4451304cf..d246660dd 100644
--- a/src/WrapperApp/components/Panels/LoginPanel.tsx
+++ b/src/WrapperApp/components/Panels/LoginPanel.tsx
@@ -3,10 +3,12 @@ import { ChangeEvent, useCallback, useEffect, useState } from 'react';
import { useConfig } from '../../../config/ConfigService';
import { useAuth } from '../../../services/AuthService';
+import { useKeycloakAuth } from '../../../services/KeycloakAuthService';
export default function LoginPanel() {
const { altAuth } = useConfig();
- const { login, tokenLogin } = useAuth();
+ const { login } = useAuth();
+ const { keycloak, initialized } = useKeycloakAuth();
const theme = useTheme();
const [username, setUsername] = useState('');
@@ -34,6 +36,10 @@ export default function LoginPanel() {
return () => document.removeEventListener('keydown', handleEnter);
}, [handleEnter]);
+ const keycloakLogin = useCallback(() => {
+ if (initialized && !keycloak.authenticated) keycloak.login();
+ }, [initialized, keycloak]);
+
return (
+ onClick={keycloakLogin}>
Connect with PLGrid
>
diff --git a/src/WrapperApp/components/Results/ResultsPanel.tsx b/src/WrapperApp/components/Results/ResultsPanel.tsx
index 1ef1df045..d4a6aaacd 100644
--- a/src/WrapperApp/components/Results/ResultsPanel.tsx
+++ b/src/WrapperApp/components/Results/ResultsPanel.tsx
@@ -9,7 +9,7 @@ import {
Tabs,
Typography
} from '@mui/material';
-import { SyntheticEvent, useEffect, useState } from 'react';
+import { ChangeEvent, SyntheticEvent, useEffect, useState } from 'react';
import { Estimator, generateGraphs, isPage0d, Page, Page0D } from '../../../JsRoot/GraphData';
import { useDialog } from '../../../services/DialogService';
@@ -25,8 +25,8 @@ export interface EstimatorResults extends Estimator {
}
function ResultsPanel() {
- const [open] = useDialog('saveFile');
- const { resultsSimulationData: simulation } = useStore();
+ const { open: openSaveFileDialog } = useDialog('saveFile');
+ const { yaptideEditor, resultsSimulationData: simulation } = useStore();
const [tabsValue, setTabsValue] = useState(0);
const [estimatorsResults, setEstimatorsResults] = useState([]);
@@ -42,11 +42,13 @@ function ResultsPanel() {
};
const onClickSaveToFile = () => {
- open({
- name: `${titleToKebabCase(simulation?.title ?? 'simulation')}-result.json`,
- results: simulation,
- disableCheckbox: true
- });
+ if (yaptideEditor)
+ openSaveFileDialog({
+ name: `${titleToKebabCase(simulation?.title ?? 'simulation')}-result.json`,
+ results: simulation,
+ disableCheckbox: true,
+ yaptideEditor
+ });
};
const parseEstimators = (estimators: Estimator[]) => {
@@ -107,7 +109,9 @@ function ResultsPanel() {
control={
setGroupQuantities(e.target.checked)}
+ onChange={(e: ChangeEvent) =>
+ setGroupQuantities(e.target.checked)
+ }
disabled={!resultsGeneratedFromProjectFile}
/>
}
diff --git a/src/WrapperApp/components/Simulation/RunSimulationForm.tsx b/src/WrapperApp/components/Simulation/RunSimulationForm.tsx
index 32cc852ca..36893c796 100644
--- a/src/WrapperApp/components/Simulation/RunSimulationForm.tsx
+++ b/src/WrapperApp/components/Simulation/RunSimulationForm.tsx
@@ -244,8 +244,9 @@ export function RunSimulationForm({
size='small'
label='Simulation software'
defaultValue={forwardedSimulator}
- onChange={evn => setSelectedSimulator(evn.target.value as SimulatorType)}
- >
+ onChange={evn =>
+ setSelectedSimulator(evn.target.value as SimulatorType)
+ }>
diff --git a/src/WrapperApp/components/Simulation/SimulationCard.tsx b/src/WrapperApp/components/Simulation/SimulationCard.tsx
index da567e69d..6ec257822 100644
--- a/src/WrapperApp/components/Simulation/SimulationCard.tsx
+++ b/src/WrapperApp/components/Simulation/SimulationCard.tsx
@@ -73,11 +73,11 @@ export default function SimulationCard({
showInputFiles,
...other
}: SimulationCardProps) {
- const { resultsSimulationData } = useStore();
+ const { yaptideEditor, resultsSimulationData } = useStore();
const { loadFromJson } = useLoader();
const { getJobLogs, getJobInputs, getFullSimulationData } = useShSimulation();
const { enqueueSnackbar } = useSnackbar();
- const [open] = useDialog('saveFile');
+ const { open: openSaveFileDialog } = useDialog('saveFile');
const [disableLoadJson, setDisableLoadJson] = useState(false);
@@ -149,11 +149,13 @@ export default function SimulationCard({
const onClickSaveToFile = () => {
getFullSimulationData(simulationStatus)
.then((simulation: FullSimulationData | undefined) => {
- open({
- name: `${titleToKebabCase(simulation?.title ?? 'simulation')}-result.json`,
- results: simulation,
- disableCheckbox: true
- });
+ if (yaptideEditor)
+ openSaveFileDialog({
+ name: `${titleToKebabCase(simulation?.title ?? 'simulation')}-result.json`,
+ results: simulation,
+ disableCheckbox: true,
+ yaptideEditor
+ });
})
.catch(() => {
enqueueSnackbar('Could not load simulation data', { variant: 'error' });
@@ -299,8 +301,8 @@ export default function SimulationCard({
component={Paper}
sx={{
'& .MuiTableRow-root': {
- backgroundColor: theme =>
- theme.palette.mode === 'dark'
+ backgroundColor: ({ palette }: Theme) =>
+ palette.mode === 'dark'
? 'rgba(255, 255, 255, 0.05)'
: 'rgba(0, 0, 0, 0.05)'
}
@@ -427,7 +429,9 @@ export default function SimulationCard({
color='info'
size='small'
onClick={onClickSaveToFile}
- disabled={!Boolean(simulationStatus.jobState === StatusState.COMPLETED)}>
+ disabled={
+ !(simulationStatus.jobState === StatusState.COMPLETED && yaptideEditor)
+ }>
Save to file
diff --git a/src/WrapperApp/components/Simulation/SimulationCardGrid.tsx b/src/WrapperApp/components/Simulation/SimulationCardGrid.tsx
index b2206b93d..eb1f02cd3 100644
--- a/src/WrapperApp/components/Simulation/SimulationCardGrid.tsx
+++ b/src/WrapperApp/components/Simulation/SimulationCardGrid.tsx
@@ -9,11 +9,13 @@ import {
CircularProgress,
Grid,
GridProps,
+ Theme,
Typography
} from '@mui/material';
import { FC, useState } from 'react';
import { useDialog } from '../../../services/DialogService';
+import { useShSimulation } from '../../../services/ShSimulatorService';
import { useStore } from '../../../services/StoreService';
import { SimulatorType } from '../../../types/RequestTypes';
import { JobStatusData, SimulationInputFiles } from '../../../types/ResponseTypes';
@@ -66,6 +68,10 @@ const stylesByLayout: Record {
+ return layout in stylesByLayout;
+};
+
export function SimulationCardGrid({
simulations,
layout,
@@ -79,7 +85,7 @@ export function SimulationCardGrid({
let gridContainerProps: GridProps = { container: true };
let gridItemProps: GridProps = { item: true };
- if (layout in stylesByLayout) {
+ if (validGriLayout(layout)) {
gridContainerProps = {
...gridContainerProps,
...stylesByLayout[layout].gridContainerProps
@@ -119,20 +125,20 @@ export function SimulationCardGrid({
) : (
palette.text.disabled}
+ color={({ palette }: Theme) => palette.text.disabled}
sx={{
textAlign: 'center',
width: '100%',
- p: ({ spacing }) => spacing(8, 4),
+ p: ({ spacing }: Theme) => spacing(8, 4),
display: 'flex',
alignItems: 'center',
justifyContent: 'center'
}}>
spacing(0, 2),
- pb: ({ spacing }) => spacing(0.5),
- fontSize: ({ spacing }) => spacing(4)
+ m: ({ spacing }: Theme) => spacing(0, 2),
+ pb: ({ spacing }: Theme) => spacing(0.5),
+ fontSize: ({ spacing }: Theme) => spacing(4)
}}
/>
No simulations found
@@ -141,7 +147,7 @@ export function SimulationCardGrid({
) : (
spacing(4)
+ p: ({ spacing }: Theme) => spacing(4)
}}
/>
)}
@@ -173,13 +179,13 @@ export function PaginatedSimulationCardGrid({
isAccordion={isAccordion}
simulations={simulations}
layout={layout}
- header={accordion =>
+ header={(accordion: SimulationAccordionProps) =>
SimulationBackendHeader({
title,
subtitle,
accordion,
sx: {
- mb: ({ spacing }) => spacing(0)
+ mb: ({ spacing }: Theme) => spacing(0)
},
children,
...pageData
@@ -190,8 +196,8 @@ export function PaginatedSimulationCardGrid({
...pageData,
stickTo: 'bottom',
sx: {
- mt: ({ spacing }) => spacing(0),
- zIndex: ({ zIndex }) => zIndex.appBar
+ mt: ({ spacing }: Theme) => spacing(0),
+ zIndex: ({ zIndex }: Theme) => zIndex.appBar
}
})
}
@@ -210,7 +216,9 @@ export function PaginatedSimulationsFromBackend({
...other
}: SimulationsFromBackendProps) {
const { setTrackedId } = useStore();
- const [open] = useDialog('runSimulation');
+ const { open: openRunSimulationDialog } = useDialog('runSimulation');
+ const { yaptideEditor } = useStore();
+ const { postJobDirect, postJobBatch } = useShSimulation();
return (
@@ -222,18 +230,22 @@ export function PaginatedSimulationsFromBackend({
variant='contained'
color='info'
startIcon={}
- disabled={!isBackendAlive}
+ disabled={!(isBackendAlive && yaptideEditor)}
onClick={() =>
- open({
+ yaptideEditor &&
+ openRunSimulationDialog({
onSubmit: setTrackedId,
- simulator: SimulatorType.SHIELDHIT
+ simulator: SimulatorType.SHIELDHIT,
+ yaptideEditor,
+ postJobDirect,
+ postJobBatch
})
}>
Run new simulation
spacing(2)
+ p: ({ spacing }: Theme) => spacing(2)
}}
isBackendAlive={isBackendAlive}
/>
@@ -271,16 +283,16 @@ export function AccordionCardGrid({
'&:before': {
display: 'none'
},
- 'p': ({ spacing }) => spacing(0)
+ 'p': ({ spacing }: Theme) => spacing(0)
}}>
spacing(0),
- m: ({ spacing }) => spacing(0),
+ p: ({ spacing }: Theme) => spacing(0),
+ m: ({ spacing }: Theme) => spacing(0),
position: 'sticky',
- inset: ({ spacing }) => spacing(0, 0, 0, 0),
- zIndex: ({ zIndex }) => zIndex.appBar + 1,
- mb: ({ spacing }) => (expanded ? spacing(7) : spacing(0))
+ inset: ({ spacing }: Theme) => spacing(0, 0, 0, 0),
+ zIndex: ({ zIndex }: Theme) => zIndex.appBar + 1,
+ mb: ({ spacing }: Theme) => (expanded ? spacing(7) : spacing(0))
}}>
{header &&
header(
@@ -291,7 +303,7 @@ export function AccordionCardGrid({
(expanded ? spacing(-14) : spacing(0)),
+ mt: ({ spacing }: Theme) => (expanded ? spacing(-14) : spacing(0)),
p: 0,
...sx
}}>
@@ -322,12 +334,12 @@ export function DemoCardGrid({
isAccordion={isAccordion}
simulations={simulations}
layout={layout}
- header={accordion =>
+ header={(accordion: SimulationAccordionProps) =>
SimulationLabelBar({
title: title,
accordion,
sx: {
- mb: ({ spacing }) => spacing(accordion.expanded ? 4 : -2)
+ mb: ({ spacing }: Theme) => spacing(accordion.expanded ? 4 : -2)
}
})
}
diff --git a/src/services/AuthService.tsx b/src/services/AuthService.tsx
index 7051a6819..e6e0f9355 100644
--- a/src/services/AuthService.tsx
+++ b/src/services/AuthService.tsx
@@ -1,5 +1,4 @@
-import { Backdrop, CircularProgress, Typography } from '@mui/material';
-import Keycloak from 'keycloak-js';
+import { Backdrop, CircularProgress, Theme, Typography } from '@mui/material';
import ky, { HTTPError } from 'ky';
import { KyInstance } from 'ky/distribution/types/ky';
import { useSnackbar } from 'notistack';
@@ -16,8 +15,9 @@ import {
import { hasFields } from '../util/customGuards';
import useIntervalAsync from '../util/hooks/useIntervalAsync';
import { snakeToCamelCase } from '../util/Notation/Notation';
+import { useDialog } from './DialogService';
import { createGenericContext, GenericContextProviderProps } from './GenericContext';
-import { keycloakConfig } from './keycloakConfig';
+import { useKeycloakAuth } from './KeycloakAuthService';
type AuthUser = Pick;
@@ -67,14 +67,12 @@ export interface AuthContext {
isAuthorized: boolean;
isServerReachable: boolean;
login: (...args: RequestAuthLogin) => void;
- tokenLogin: () => void;
logout: (...args: RequestAuthLogout) => void;
refresh: (...args: RequestAuthRefresh) => void;
authKy: KyInstance;
}
const [useAuth, AuthContextProvider] = createGenericContext();
-const keycloak = new Keycloak(keycloakConfig);
const ignored_messages = ['No token provided'];
const Auth = ({ children }: GenericContextProviderProps) => {
@@ -82,9 +80,13 @@ const Auth = ({ children }: GenericContextProviderProps) => {
const [user, setUser] = useState(load(StorageKey.USER, isAuthUser));
const [reachInterval, setReachInterval] = useState();
const [refreshInterval, setRefreshInterval] = useState(180000); // 3 minutes in ms default interval for refresh token
+ const { keycloak, initialized } = useKeycloakAuth();
const [keyCloakInterval, setKeyCloakInterval] = useState();
const [isServerReachable, setIsServerReachable] = useState(null);
const { enqueueSnackbar } = useSnackbar();
+ const { open: openRejectKeycloakDialog } = useDialog('rejectKeycloak');
+
+ const isAuthorized = useMemo(() => user !== null || demoMode, [demoMode, user]);
useEffect(() => {
setReachInterval(isServerReachable ? 180000 : undefined);
@@ -168,67 +170,82 @@ const Auth = ({ children }: GenericContextProviderProps) => {
}, [reachServer]);
useEffect(() => {
- if (user !== null && user.source !== 'keycloak' && isServerReachable)
+ if (!demoMode && user?.source !== 'keycloak' && isServerReachable)
setRefreshInterval(prev => (prev === undefined ? 3000 : prev));
// 3 seconds in ms default interval for refresh when logged in with username and password
- else if (!isServerReachable || !user?.source) setRefreshInterval(undefined);
- }, [isServerReachable, user]);
+ else setRefreshInterval(undefined);
+ }, [demoMode, isServerReachable, user]);
const tokenVerification = useCallback(() => {
- if (!keycloak.authenticated) return Promise.reject();
+ if (!initialized || !keycloak.authenticated) return;
const username = keycloak.tokenParsed?.preferred_username;
- return kyRef
- .post(`auth/keycloak`, {
- headers: {
- Authorization: `Bearer ${keycloak.token}`
- },
- json: {
- username
- }
- })
- .then(() => {
- setUser({
- username,
- source: 'keycloak'
- });
- })
- .catch((_: HTTPError) => {
- setUser(null);
- setRefreshInterval(undefined);
+ /**
+ * TODO: Check if user is authorized to use this application (e.g. by checking if user is in a certain group)
+ */
+ const validUser = true;
+
+ if (!validUser)
+ openRejectKeycloakDialog({
+ reason: 'You are not authorized to use this application.',
+ keycloakAuth: { keycloak, initialized }
});
- }, [kyRef]);
+ else if (initialized)
+ kyRef
+ .post(`auth/keycloak`, {
+ headers: {
+ Authorization: `Bearer ${keycloak.token}`
+ },
+ json: {
+ username
+ }
+ })
+ .json()
+ .then(({ accessExp }) => {
+ setUser(prev =>
+ prev?.username === username
+ ? prev
+ : {
+ username,
+ source: 'keycloak'
+ }
+ );
+ setRefreshInterval(getRefreshDelay(accessExp));
+ })
+ .catch((err: HTTPError) => {
+ setUser(null);
+ setRefreshInterval(undefined);
+ openRejectKeycloakDialog({
+ reason:
+ err.response?.status === 403
+ ? 'You are not authorized to use this application.'
+ : err.message,
+ keycloakAuth: { keycloak, initialized }
+ });
+ });
+ }, [initialized, keycloak, kyRef, openRejectKeycloakDialog]);
useEffect(() => {
- keycloak
- .init({
- pkceMethod: 'S256',
- checkLoginIframe: false
- })
- .then(auth => {
- console.log('after init', auth);
-
- if (auth) {
- setKeyCloakInterval(
- keycloak.tokenParsed?.exp !== undefined
- ? getRefreshDelay(keycloak.tokenParsed.exp * 1000)
- : undefined
- );
- tokenVerification();
- }
- });
- }, [tokenVerification]);
+ if (initialized && keycloak.authenticated)
+ setKeyCloakInterval(
+ keycloak.tokenParsed?.exp !== undefined
+ ? getRefreshDelay(keycloak.tokenParsed.exp * 1000)
+ : undefined
+ );
+ tokenVerification();
+ }, [initialized, keycloak, tokenVerification]);
const logout = useCallback(() => {
setUser(null);
setRefreshInterval(undefined);
+ setKeyCloakInterval(undefined);
- if (user?.source === 'keycloak') keycloak.logout();
+ if (initialized && keycloak.authenticated) keycloak.logout();
kyRef
.delete(`auth/logout`)
.json()
.catch((_: HTTPError) => {});
- }, [kyRef, user?.source]);
+ }, [initialized, keycloak, kyRef]);
const login = useCallback(
(...[username, password]: RequestAuthLogin) => {
@@ -252,6 +269,8 @@ const Auth = ({ children }: GenericContextProviderProps) => {
);
const tokenRefresh = useCallback(() => {
+ if (!initialized) return Promise.resolve();
+
return keycloak
.updateToken(300) // 5 minutes in seconds minimum remaining lifetime for token before refresh is allowed
.then(refreshed => {
@@ -262,14 +281,15 @@ const Auth = ({ children }: GenericContextProviderProps) => {
);
})
.catch(reason => {});
- }, [enqueueSnackbar]);
+ }, [enqueueSnackbar, keycloak, initialized]);
- useIntervalAsync(tokenRefresh, keycloak.authenticated ? keyCloakInterval : undefined);
-
- const isAuthorized = useMemo(() => user !== null || demoMode, [demoMode, user]);
+ useIntervalAsync(
+ tokenRefresh,
+ initialized && keycloak.authenticated ? keyCloakInterval : undefined
+ );
const refresh = useCallback(async () => {
- if (user?.source === 'keycloak' && isAuthorized) return tokenVerification();
+ if (user?.source === 'keycloak' && isAuthorized) return await tokenVerification();
if (demoMode || !isServerReachable) {
setRefreshInterval(undefined);
@@ -285,7 +305,7 @@ const Auth = ({ children }: GenericContextProviderProps) => {
return setRefreshInterval(getRefreshDelay(accessExp));
} catch (_) {}
- }, [demoMode, isAuthorized, isServerReachable, kyIntervalRef, tokenVerification, user?.source]);
+ }, [demoMode, isAuthorized, isServerReachable, kyIntervalRef, tokenVerification, user]);
const authKy = useMemo(() => kyRef, [kyRef]);
@@ -302,13 +322,17 @@ const Auth = ({ children }: GenericContextProviderProps) => {
isAuthorized,
isServerReachable: Boolean(isServerReachable),
login,
- tokenLogin: keycloak.login,
logout,
authKy,
refresh
}}>
{children}
-
+ zIndex.drawer + 1,
+ color: '#fff'
+ }}>
Waiting for verification...
diff --git a/src/services/DialogService.tsx b/src/services/DialogService.tsx
index ccbae1d5d..387449122 100644
--- a/src/services/DialogService.tsx
+++ b/src/services/DialogService.tsx
@@ -5,6 +5,7 @@ import { EditProjectInfoDialog } from '../ThreeEditor/components/Dialog/EditProj
import { LoadFileDialog } from '../ThreeEditor/components/Dialog/LoadFileDialog';
import { NewProjectDialog } from '../ThreeEditor/components/Dialog/NewProjectDialog';
import { OpenFileDialog } from '../ThreeEditor/components/Dialog/OpenFileDialog';
+import { RejectKeycloakUserDialog } from '../ThreeEditor/components/Dialog/RejectKeycloakUserDialog';
import { RunSimulationDialog } from '../ThreeEditor/components/Dialog/RunSimulationDialog';
import { SaveFileDialog } from '../ThreeEditor/components/Dialog/SaveFileDialog';
import {
@@ -35,7 +36,8 @@ export type DialogComponentTypeMap = EntriesToObj<
ValidComponentTypes<['openFile', typeof OpenFileDialog]>,
ValidComponentTypes<['runSimulation', typeof RunSimulationDialog]>,
ValidComponentTypes<['saveFile', typeof SaveFileDialog]>,
- ValidComponentTypes<['editProject', typeof EditProjectInfoDialog]>
+ ValidComponentTypes<['editProject', typeof EditProjectInfoDialog]>,
+ ValidComponentTypes<['rejectKeycloak', typeof RejectKeycloakUserDialog]>
]
>;
@@ -43,17 +45,21 @@ const [useDialogContext, DialogContextProvider] = createGenericContext(
name: T
-): readonly [(props: RestDialogPropsType[T]) => void, () => void, boolean];
+): {
+ open: (props: RestDialogPropsType[T]) => void;
+ close: () => void;
+ isOpen: boolean;
+};
function useDialog(
name: T
-): readonly [(props?: RestDialogPropsType[T]) => void, () => void, boolean];
+): { open: (props?: RestDialogPropsType[T]) => void; close: () => void; isOpen: boolean };
function useDialog(
name: T
-): readonly [
- ((props?: RestDialogPropsType[T]) => void) | ((props: RestDialogPropsType[T]) => void),
- () => void,
- boolean
-] {
+): {
+ open: ((props?: RestDialogPropsType[T]) => void) | ((props: RestDialogPropsType[T]) => void);
+ close: () => void;
+ isOpen: boolean;
+} {
const { openDialog, closeDialog, getIsOpen } = useDialogContext();
const open = useCallback(
(props: RestDialogPropsType[T] = {} as RestDialogPropsType[T]) => {
@@ -67,7 +73,7 @@ function useDialog(
}, [name, closeDialog]);
const isOpen = useMemo(() => getIsOpen(name), [name, getIsOpen]);
- return [open, close, isOpen] as const;
+ return { open, close, isOpen };
}
const DialogProvider = ({ children }: GenericContextProviderProps) => {
@@ -104,7 +110,8 @@ const DialogProvider = ({ children }: GenericContextProviderProps) => {
openFile: OpenFileDialog,
runSimulation: RunSimulationDialog,
saveFile: SaveFileDialog,
- editProject: EditProjectInfoDialog
+ editProject: EditProjectInfoDialog,
+ rejectKeycloak: RejectKeycloakUserDialog
}),
[]
);
diff --git a/src/services/GenericContext.ts b/src/services/GenericContext.ts
index 4190277a0..7821375ce 100644
--- a/src/services/GenericContext.ts
+++ b/src/services/GenericContext.ts
@@ -21,3 +21,25 @@ export const createGenericContext = () => {
return [useGenericContext, genericContext.Provider] as const;
};
+
+export const createSubstituteContext = (useSubstituted: () => T) => {
+ // Create a context with a generic parameter or undefined
+ const genericContext = createContext(undefined);
+
+ // Check if the value provided to the context is defined or call the substitute
+ const useSubstituteOrGenericContext = () => {
+ const contextIsDefined = useContext(genericContext);
+
+ try {
+ return useSubstituted();
+ } catch (e) {
+ if (!contextIsDefined) {
+ throw new Error('useSubstituteOrGenericContext must be used within a Provider');
+ }
+
+ return contextIsDefined;
+ }
+ };
+
+ return [useSubstituteOrGenericContext, genericContext.Provider] as const;
+};
diff --git a/src/services/KeycloakAuthService.tsx b/src/services/KeycloakAuthService.tsx
new file mode 100644
index 000000000..7e7da738d
--- /dev/null
+++ b/src/services/KeycloakAuthService.tsx
@@ -0,0 +1,61 @@
+import Keycloak, { KeycloakInitOptions } from 'keycloak-js';
+import { KeycloakProvider, useKeycloak } from 'keycloak-react-web';
+import { ReactNode, useCallback } from 'react';
+
+import { useConfig } from '../config/ConfigService';
+import { createSubstituteContext, GenericContextProviderProps } from './GenericContext';
+
+const keycloakParams = {
+ url: `${process.env.REACT_APP_KEYCLOAK_BASE_URL ?? 'https://localhost:8080'}/auth/`,
+ realm: `${process.env.REACT_APP_KEYCLOAK_REALM ?? ''}`,
+ clientId: `${process.env.REACT_APP_KEYCLOAK_CLIENT_ID ?? ''}`
+};
+
+const authInstance = new Keycloak(keycloakParams) as any;
+
+const initOptions = {
+ pkceMethod: 'S256',
+ onLoad: 'check-sso',
+ checkLoginIframe: false,
+ enableLogging: false
+} as const satisfies KeycloakInitOptions;
+
+export type KeycloakAuthContext =
+ | {
+ initialized: false;
+ keycloak?: Keycloak;
+ }
+ | {
+ initialized: true;
+ keycloak: Keycloak;
+ };
+
+const [useKeycloakAuth, KeycloakAuthContextProvider] =
+ createSubstituteContext(useKeycloak);
+
+const KeycloakAuth = ({ children }: GenericContextProviderProps) => {
+ const { altAuth } = useConfig();
+ const proxyContextProvider = useCallback(
+ (children: ReactNode) => (
+
+ {children}
+
+ ),
+ []
+ );
+
+ return altAuth ? (
+
+ {proxyContextProvider(children)}
+
+ ) : (
+ proxyContextProvider(children)
+ );
+};
+
+export { KeycloakAuth, useKeycloakAuth };
diff --git a/src/services/LoaderService.tsx b/src/services/LoaderService.tsx
index cc9658717..cfcf17b1d 100644
--- a/src/services/LoaderService.tsx
+++ b/src/services/LoaderService.tsx
@@ -43,7 +43,7 @@ const readFile = (file: File) => {
};
const Loader = ({ children }: GenericContextProviderProps) => {
- const [open] = useDialog('loadFile');
+ const { open: openLoadFileDialog } = useDialog('loadFile');
const { yaptideEditor, setResultsSimulationData, setLocalResultsSimulationData } = useStore();
const [, setUrlInPath] = useState();
@@ -53,10 +53,12 @@ const Loader = ({ children }: GenericContextProviderProps) => {
switch (type) {
case 'Editor':
- open({
- data: json,
- validVersion: json.metadata.version === JSON_VERSION
- });
+ if (yaptideEditor)
+ openLoadFileDialog({
+ data: json,
+ validVersion: json.metadata.version === JSON_VERSION,
+ yaptideEditor
+ });
break;
default:
@@ -65,7 +67,7 @@ const Loader = ({ children }: GenericContextProviderProps) => {
break;
}
},
- [open]
+ [openLoadFileDialog, yaptideEditor]
);
const loadData = useCallback(
diff --git a/src/services/keycloakConfig.ts b/src/services/keycloakConfig.ts
deleted file mode 100644
index 75f2b30f8..000000000
--- a/src/services/keycloakConfig.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-export const keycloakConfig = {
- url: 'https://sso.pre.plgrid.pl/auth/',
- realm: 'PLGrid',
- clientId: 'yaptide-staging',
- enableLogging: true
-};