import * as React from 'react'; import './App.scss'; import { MainMenu } from './Components/MainMenu/MainMenu'; import { ContainerModel, IContainerModel } from './Interfaces/ContainerModel'; import Editor, { IEditorState } from './Editor'; import { Configuration } from './Interfaces/Configuration'; import { Revive } from './utils/saveload'; export interface IHistoryState { MainContainer: IContainerModel | null SelectedContainer: IContainerModel | null SelectedContainerId: string TypeCounters: Record } // App will never have props // eslint-disable-next-line @typescript-eslint/no-empty-interface interface IAppProps { } interface IAppState { configuration: Configuration history: IHistoryState[] historyCurrentStep: number isLoaded: boolean } export class App extends React.Component { public state: IAppState; constructor(props: IAppProps) { super(props); this.state = { configuration: { AvailableContainers: [], AvailableSymbols: [], MainContainer: { Type: 'EmptyContainer', Width: 3000, Height: 200, Style: {} } }, history: [], historyCurrentStep: 0, isLoaded: false }; } componentDidMount(): void { const queryString = window.location.search; const urlParams = new URLSearchParams(queryString); const state = urlParams.get('state'); if (state === null) { return; } fetch(state) .then( async(response) => await response.json(), (error) => { throw new Error(error); } ) .then((data: IEditorState) => { this.LoadState(data); }, (error) => { throw new Error(error); }); } public NewEditor(): void { // Fetch the configuration from the API fetchConfiguration() .then((configuration: Configuration) => { // Set the main container from the given properties of the API const MainContainer = new ContainerModel( null, { id: 'main', parentId: 'null', x: 0, y: 0, width: configuration.MainContainer.Width, height: configuration.MainContainer.Height, fillOpacity: 0, stroke: 'black' } ); // Save the configuration and the new MainContainer // and default the selected container to it this.setState({ configuration, history: [ { MainContainer, SelectedContainer: MainContainer, TypeCounters: {} } ], historyCurrentStep: 0, isLoaded: true }); }, (error) => { // TODO: Implement an alert component console.warn('[NewEditor] Could not fetch resource from API. Returning default.', error); const MainContainer = new ContainerModel( null, { id: 'main', parentId: 'null', x: 0, y: 0, width: DEFAULT_CONFIG.MainContainer.Width, height: DEFAULT_CONFIG.MainContainer.Height, fillOpacity: DEFAULT_CONFIG.MainContainer.Style.fillOpacity, stroke: DEFAULT_CONFIG.MainContainer.Style.stroke, } ); // Save the configuration and the new MainContainer // and default the selected container to it this.setState({ configuration: DEFAULT_CONFIG, history: [ { MainContainer, SelectedContainer: MainContainer, TypeCounters: {} } ], historyCurrentStep: 0, isLoaded: true }); }); } public LoadEditor(files: FileList | null): void { if (files === null) { return; } const file = files[0]; const reader = new FileReader(); reader.addEventListener('load', () => { const result = reader.result as string; const editorState: IEditorState = JSON.parse(result); this.LoadState(editorState); }); reader.readAsText(file); } private LoadState(editorState: IEditorState): void { Revive(editorState); this.setState({ configuration: editorState.configuration, history: editorState.history, historyCurrentStep: editorState.historyCurrentStep, isLoaded: true }); } public render(): JSX.Element { if (this.state.isLoaded) { return (
); } else { return (
this.NewEditor()} loadEditor={(files: FileList | null) => this.LoadEditor(files)} />
); } } } /** * Fetch the configuration from the API * @returns {Configation} The model of the configuration for the application */ export async function fetchConfiguration(): Promise { const url = `${import.meta.env.VITE_API_URL}`; // The test library cannot use the Fetch API // @ts-expect-error // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions if (window.fetch) { return await fetch(url, { method: 'POST' }) .then(async(response) => await response.json() ) as Configuration; } return await new Promise((resolve) => { const xhr = new XMLHttpRequest(); xhr.open('POST', url, true); xhr.onreadystatechange = function() { // Call a function when the state changes. if (xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) { resolve(JSON.parse(this.responseText)); } }; xhr.send(); }); } const DEFAULT_CONFIG: Configuration = { AvailableContainers: [ { Type: 'Container', Width: 75, Height: 100, Style: { fillOpacity: 0, stroke: 'green' } } ], AvailableSymbols: [], MainContainer: { Type: 'Container', Width: 2000, Height: 100, Style: { fillOpacity: 0, stroke: 'black' } } }