diff --git a/docs/assets/files.png b/docs/assets/files.png new file mode 100644 index 0000000..85b582a Binary files /dev/null and b/docs/assets/files.png differ diff --git a/src/main.tsx b/src/main.tsx index 008d7fc..cb7e5d9 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -6,6 +6,8 @@ import { handleErrors } from './modules/errors/helpers/handleErrors' import { initKeyBindings } from './modules/keybindings/helpers/initialiseKeyBindings' import { initBaseTemplates } from './modules/saving/helpers/initBaseTemplates' +console.clear() + handleErrors() initKeyBindings() initBaseTemplates() diff --git a/src/modules/core/components/Canvas.tsx b/src/modules/core/components/Canvas.tsx index 8092163..e575fb4 100644 --- a/src/modules/core/components/Canvas.tsx +++ b/src/modules/core/components/Canvas.tsx @@ -1,11 +1,9 @@ import React, { Component, createRef, Ref, RefObject } from 'react' import FluidCanvas from './FluidCanvas' import loop from 'mainloop.js' -import { Gate } from '../../simulation/classes/Gate' import { SimulationRenderer } from '../../simulationRenderer/classes/SimulationRenderer' import { renderSimulation } from '../../simulationRenderer/helpers/renderSimulation' import { updateSimulation } from '../../simulationRenderer/helpers/updateSimulation' -import { addGate } from '../../simulation/helpers/addGate' import { rendererSubject } from '../subjects/rendererSubject' class Canvas extends Component { @@ -18,8 +16,6 @@ class Canvas extends Component { rendererSubject.next(this.renderer) - addGate(this.renderer.simulation, 'not') - loop.setDraw(() => { if (this.renderingContext) { renderSimulation(this.renderingContext, this.renderer) diff --git a/src/modules/core/components/CreateSimulationButton.tsx b/src/modules/core/components/CreateSimulationButton.tsx new file mode 100644 index 0000000..7ce8441 --- /dev/null +++ b/src/modules/core/components/CreateSimulationButton.tsx @@ -0,0 +1,28 @@ +import React from 'react' +import ListItem from '@material-ui/core/ListItem' +import ListItemIcon from '@material-ui/core/ListItemIcon' +import ListItemText from '@material-ui/core/ListItemText' +import Icon from '@material-ui/core/Icon' +import { handleCreating } from '../../create-simulation/helpers/handleCreating' +import { useTranslation } from '../../internalisation/helpers/useLanguage' + +/** + * The component for the 'Create simulation' button from the top of the sidebar. + * + * The only way i found to apply a different color to the ListItem button was + * by using !important in the scss. + */ +const CreateSimulationButton = () => { + const translation = useTranslation() + + return ( + + + note_add + + {translation.sidebar.createSimulation} + + ) +} + +export default CreateSimulationButton diff --git a/src/modules/core/components/LogicGates.tsx b/src/modules/core/components/LogicGates.tsx new file mode 100644 index 0000000..dbf5f0f --- /dev/null +++ b/src/modules/core/components/LogicGates.tsx @@ -0,0 +1,93 @@ +import React, { useState } from 'react' +import ListItem from '@material-ui/core/ListItem' +import ListItemIcon from '@material-ui/core/ListItemIcon' +import ListItemText from '@material-ui/core/ListItemText' +import Menu from '@material-ui/core/Menu' +import MenuItem from '@material-ui/core/MenuItem' +import Icon from '@material-ui/core/Icon' +import Typography from '@material-ui/core/Typography' +import { BehaviorSubject } from 'rxjs' +import { useObservable } from 'rxjs-hooks' +import { switchTo } from '../../saving/helpers/switchTo' +import { SimulationError } from '../../errors/classes/SimulationError' +import { templateStore } from '../../saving/stores/templateStore' +import { useTranslation } from '../../internalisation/helpers/useLanguage' + +/** + * Subject to make React update the dom when new gates are stored + */ +const allGatesSubject = new BehaviorSubject([]) + +/** + * Triggers a dom update by pushing a new value to the + * useObservable hook inside the React component. + * + * It also has the side effect of sorting the template names. + */ +const updateTemplateList = () => { + allGatesSubject.next(templateStore.ls().sort()) +} + +/** + * Component wich contains the sidebar 'Open simulation' button + * + * @throws SimulationError if the data about a simulation cant be found in localStorage + */ +const LogicGates = () => { + const [anchorEl, setAnchorEl] = useState(null) + const simulations = useObservable(() => allGatesSubject, []) + + const translation = useTranslation() + + const handleClose = () => { + setAnchorEl(null) + } + + return ( + <> + { + updateTemplateList() + setAnchorEl(event.currentTarget) + }} + > + + memory + + {translation.sidebar.logicGates} + + + + {simulations.map((simulationName, index) => { + const simulationData = templateStore.get(simulationName) + + if (!simulationData) { + throw new SimulationError( + `Cannot get data for simulation ${simulationName}` + ) + } + + return ( + { + switchTo(simulationName) + handleClose() + }} + > + {simulationName} + + ) + })} + + + ) +} + +export default LogicGates diff --git a/src/modules/core/components/OpenSimulation.tsx b/src/modules/core/components/OpenSimulation.tsx index 905bef7..6cbe577 100644 --- a/src/modules/core/components/OpenSimulation.tsx +++ b/src/modules/core/components/OpenSimulation.tsx @@ -5,24 +5,48 @@ import ListItemText from '@material-ui/core/ListItemText' import Menu from '@material-ui/core/Menu' import MenuItem from '@material-ui/core/MenuItem' import Icon from '@material-ui/core/Icon' +import Typography from '@material-ui/core/Typography' import { saveStore } from '../../saving/stores/saveStore' import { BehaviorSubject } from 'rxjs' import { useObservable } from 'rxjs-hooks' -import { rendererSubject } from '../subjects/rendererSubject' -import { currentStore } from '../../saving/stores/currentStore' +import { switchTo } from '../../saving/helpers/switchTo' +import { SimulationError } from '../../errors/classes/SimulationError' +import { icons } from '../constants' +import { useTranslation } from '../../internalisation/helpers/useLanguage' +/** + * Returns a list with the names of all saved simulations + */ const allSimulations = () => { return saveStore.ls() } + +/** + * Subject to make React update the dom when new simulations are stored + */ const allSimulationSubject = new BehaviorSubject([]) + +/** + * Triggers a dom update by pushing a new value to the + * useObservable hook inside the React component. + * + * It also has the side effect of sorting the simulation names. + */ const updateSimulationList = () => { - allSimulationSubject.next(allSimulations()) + allSimulationSubject.next(allSimulations().sort()) } +/** + * Component wich contains the sidebar 'Open simulation' button + * + * @throws SimulationError if the data about a simulation cant be found in localStorage + */ const OpenSimulation = () => { const [anchorEl, setAnchorEl] = useState(null) const simulations = useObservable(() => allSimulationSubject, []) + const translation = useTranslation() + const handleClose = () => { setAnchorEl(null) } @@ -39,7 +63,9 @@ const OpenSimulation = () => { folder_open - Open simulation + + {translation.sidebar.openSimulation} + { open={Boolean(anchorEl)} onClose={handleClose} > - {simulations.map((simulation, index) => ( - { - if (rendererSubject.value) { - const renderer = rendererSubject.value + {simulations.map((simulationName, index) => { + const simulationData = saveStore.get(simulationName) - currentStore.set(simulation) - renderer.reloadSave() - } + if (!simulationData) { + throw new SimulationError( + `Cannot get data for simulation ${simulationName}` + ) + } - handleClose() - }} - > - {simulation} - - ))} + return ( + { + switchTo(simulationName) + handleClose() + }} + > + + + { + icons.simulationMode[ + simulationData.simulation.mode + ] + } + + + {simulationName} + + ) + })} ) diff --git a/src/modules/core/components/Sidebar.tsx b/src/modules/core/components/Sidebar.tsx index 9f8232c..32c356c 100644 --- a/src/modules/core/components/Sidebar.tsx +++ b/src/modules/core/components/Sidebar.tsx @@ -1,35 +1,51 @@ -import React, { useState } from 'react' +import React from 'react' import Drawer from '@material-ui/core/Drawer' import List from '@material-ui/core/List' -import ListItem from '@material-ui/core/ListItem' -import ListItemIcon from '@material-ui/core/ListItemIcon' -import ListItemText from '@material-ui/core/ListItemText' -import Icon from '@material-ui/core/Icon' -import { handleCreating } from '../../create-simulation/helpers/handleCreating' -import { makeStyles, Theme, createStyles } from '@material-ui/core/styles' import OpenSimulation from './OpenSimulation' +import CreateSimulationButton from './CreateSimulationButton' +import LogicGates from './LogicGates' +import { makeStyles, createStyles } from '@material-ui/core/styles' +/** + * The width of the sidebar + */ +const sidebarWidth = 240 -const drawerWidth = 240 -const useStyles = makeStyles((theme: Theme) => +/** + * The z-index of the sidebar. + */ +const sidebarZIndex = 5 + +/** + * The styles for the sidebar component + */ +const useStyles = makeStyles( createStyles({ + // This class is applied on the sidebar container root: { display: 'flex', - zIndex: 5 + zIndex: sidebarZIndex }, + + // This is the class of the actual sidebar drawer: { - width: drawerWidth, - flexShrink: 0, - zIndex: 5 + width: sidebarWidth, + zIndex: sidebarZIndex, + flexShrink: 0 }, + + // This is the class for the surface of the sidebar drawerPaper: { - padding: '4px', - width: drawerWidth, background: `#111111`, - zIndex: 5 + padding: '4px', + width: sidebarWidth, + zIndex: sidebarZIndex } }) ) +/** + * The sidebar component + */ const Sidebar = () => { const classes = useStyles() @@ -44,18 +60,10 @@ const Sidebar = () => { paper: classes.drawerPaper }} > - - - - note_add - - Create simulation - + + + diff --git a/src/modules/core/constants.ts b/src/modules/core/constants.ts index c7c9eb1..5174c13 100644 --- a/src/modules/core/constants.ts +++ b/src/modules/core/constants.ts @@ -1,5 +1,6 @@ import { createMuiTheme } from '@material-ui/core/styles' import { red, deepPurple } from '@material-ui/core/colors' +import { simulationMode } from '../saving/types/SimulationSave' export const theme = createMuiTheme({ palette: { @@ -8,3 +9,20 @@ export const theme = createMuiTheme({ secondary: red } }) + +/** + * Used to get intellisense from visual studio code + */ +export interface IconInterface { + simulationMode: Record +} + +/** + * Stores the names of icons reuseed truogh the app + */ +export const icons: IconInterface = { + simulationMode: { + project: 'gamepad', + ic: 'memory' + } +} diff --git a/src/modules/create-simulation/components/CreateSimulation.tsx b/src/modules/create-simulation/components/CreateSimulation.tsx index 74b4e3f..72784ba 100644 --- a/src/modules/create-simulation/components/CreateSimulation.tsx +++ b/src/modules/create-simulation/components/CreateSimulation.tsx @@ -4,21 +4,19 @@ import { useObservable } from 'rxjs-hooks' import { CreateSimulationStore } from '../stores/CreateSimulationStore' import { simulationMode } from '../../saving/types/SimulationSave' import Icon from '@material-ui/core/Icon' +import { useTranslation } from '../../internalisation/helpers/useLanguage' export interface CreateSimulationOption { mode: simulationMode icon: string - name: string } export const createSimulationOptions: CreateSimulationOption[] = [ { - name: 'project', mode: 'project', icon: 'gamepad' }, { - name: 'integrated circuit', icon: 'memory', mode: 'ic' } @@ -26,6 +24,7 @@ export const createSimulationOptions: CreateSimulationOption[] = [ const CreateSimulation = () => { const open = useObservable(() => CreateSimulationStore.data.open, false) + const translation = useTranslation() const closeModal = () => { CreateSimulationStore.actions.next('quit') @@ -38,7 +37,7 @@ const CreateSimulation = () => { onClick={closeModal} >
- What kind of simulation do you want to create? + {translation.createSimulation.mode.question}
@@ -57,7 +56,11 @@ const CreateSimulation = () => { {option.icon}
- {option.name} + { + translation.createSimulation.mode.options[ + option.mode + ] + }
))} diff --git a/src/modules/create-simulation/helpers/handleCreating.ts b/src/modules/create-simulation/helpers/handleCreating.ts index 0c9cb92..7fa4a3f 100644 --- a/src/modules/create-simulation/helpers/handleCreating.ts +++ b/src/modules/create-simulation/helpers/handleCreating.ts @@ -1,5 +1,6 @@ import { CreateSimulationStore } from '../stores/CreateSimulationStore' import { initSimulation } from '../../saving/helpers/initSimulation' +import { switchTo } from '../../saving/helpers/switchTo' export const handleCreating = async () => { const options = await CreateSimulationStore.create() @@ -8,5 +9,7 @@ export const handleCreating = async () => { const simulation = initSimulation(options.name, options.mode) + switchTo(options.name) + return simulation } diff --git a/src/modules/create-simulation/stores/CreateSimulationStore.ts b/src/modules/create-simulation/stores/CreateSimulationStore.ts index c4546fe..93016a1 100644 --- a/src/modules/create-simulation/stores/CreateSimulationStore.ts +++ b/src/modules/create-simulation/stores/CreateSimulationStore.ts @@ -2,6 +2,7 @@ import { BehaviorSubject, Subject } from 'rxjs' import { take } from 'rxjs/operators' import { simulationMode } from '../../saving/types/SimulationSave' import { InputStore } from '../../input/stores/InputStore' +import { CurrentLanguage } from '../../internalisation/stores/currentLanguage' export type CreateSimulationStoreAction = 'quit' | 'submit' @@ -13,6 +14,8 @@ export const CreateSimulationStore = { .pipe(take(1)) .toPromise() + const translation = CurrentLanguage.getTranslation() + CreateSimulationStore.close() if (action === 'quit') { @@ -20,7 +23,7 @@ export const CreateSimulationStore = { } const name = await InputStore.get( - 'What do you want your simulation to be called?' + translation.createSimulation.name.question ) if (!name) { diff --git a/src/modules/internalisation/constants.ts b/src/modules/internalisation/constants.ts new file mode 100644 index 0000000..c7f743a --- /dev/null +++ b/src/modules/internalisation/constants.ts @@ -0,0 +1,12 @@ +import { supportedLanguages } from './types/supportedLanguages' +import { Translation } from './types/TranslationInterface' +import { EnglishTranslation } from './translations/english' +import { RomanianTranslation } from './translations/romanian' + +/** + * Object with all translations + */ +export const translations: Record = { + english: EnglishTranslation, + ['română']: RomanianTranslation +} diff --git a/src/modules/internalisation/helpers/useLanguage.ts b/src/modules/internalisation/helpers/useLanguage.ts new file mode 100644 index 0000000..2439bd1 --- /dev/null +++ b/src/modules/internalisation/helpers/useLanguage.ts @@ -0,0 +1,16 @@ +import { useObservable } from 'rxjs-hooks' +import currentLanguageSubject from '../subjects/currentLanguageSubject' +import { translations } from '../constants' + +/** + * Hook to get the current translation + */ +export const useTranslation = () => { + const currentLanguage = useObservable( + () => currentLanguageSubject, + 'english' + ) + const translation = translations[currentLanguage] + + return translation +} diff --git a/src/modules/internalisation/stores/currentLanguage.ts b/src/modules/internalisation/stores/currentLanguage.ts new file mode 100644 index 0000000..c2cada7 --- /dev/null +++ b/src/modules/internalisation/stores/currentLanguage.ts @@ -0,0 +1,53 @@ +import { supportedLanguages } from '../types/supportedLanguages' +import { LocalStore } from '../../storage/classes/LocalStore' +import currentLanguageSubject from './../subjects/currentLanguageSubject' +import { SimulationError } from '../../errors/classes/SimulationError' +import { translations } from '../constants' + +/** + * Local store containing the current selected language + */ +export const CurrentLanguageLocalStore = new LocalStore( + 'language' +) + +// This makes sure the language isnt undefined +if (!CurrentLanguageLocalStore.get()) { + CurrentLanguageLocalStore.set('english') +} + +currentLanguageSubject.next(CurrentLanguageLocalStore.get() || 'english') + +/** + * The preffered interface for interacting with CurrentLanguageLocalStore + */ +const CurrentLanguage = { + set(name: supportedLanguages) { + CurrentLanguageLocalStore.set(name) + currentLanguageSubject.next(name) + }, + + /** + * Used to get the current selected store + * + * @throws SimulationError if the language cannot be found + */ + get() { + const language = CurrentLanguageLocalStore.get() + + if (!language) { + throw new SimulationError(`Current language not found`) + } + + return language + }, + + /** + * Helper to get the current translation outside React components + */ + getTranslation() { + return translations[CurrentLanguage.get()] + } +} + +export { CurrentLanguage } diff --git a/src/modules/internalisation/subjects/currentLanguageSubject.ts b/src/modules/internalisation/subjects/currentLanguageSubject.ts new file mode 100644 index 0000000..219fde6 --- /dev/null +++ b/src/modules/internalisation/subjects/currentLanguageSubject.ts @@ -0,0 +1,7 @@ +import { BehaviorSubject } from 'rxjs' +import { supportedLanguages } from '../types/supportedLanguages' + +/** + * Subject with the current language + */ +export default new BehaviorSubject('english') diff --git a/src/modules/internalisation/translations/english.ts b/src/modules/internalisation/translations/english.ts new file mode 100644 index 0000000..1c784fd --- /dev/null +++ b/src/modules/internalisation/translations/english.ts @@ -0,0 +1,31 @@ +import { Translation } from '../types/TranslationInterface' + +/** + * The enaglish translation + */ +export const EnglishTranslation: Translation = { + language: 'english', + sidebar: { + createSimulation: 'Create simulation', + logicGates: 'Logic gates', + openSimulation: 'Open simulations' + }, + createSimulation: { + mode: { + question: 'What kind of simulation do you want to create?', + options: { + ic: 'Integrated circuit', + project: 'Project' + } + }, + name: { + question: 'What do you want your simulation to be called?' + } + }, + messages: { + createdSimulation: name => `Succesfully created simulation '${name}'`, + switchedToSimulation: name => + `Succesfully switched to simulation '${name}'`, + savedSimulation: name => `Succesfully saved simulation '${name}'` + } +} diff --git a/src/modules/internalisation/translations/romanian.ts b/src/modules/internalisation/translations/romanian.ts new file mode 100644 index 0000000..333eca4 --- /dev/null +++ b/src/modules/internalisation/translations/romanian.ts @@ -0,0 +1,32 @@ +import { Translation } from '../types/TranslationInterface' + +/** + * The romanian translation + */ +export const RomanianTranslation: Translation = { + language: 'română', + sidebar: { + createSimulation: 'Creează o simulație', + openSimulation: 'Deschide o simulație', + logicGates: 'Porți logice' + }, + createSimulation: { + mode: { + question: 'Ce fel de simulație vrei să creiezi?', + options: { + ic: 'Circuit integrat', + project: 'Proiect' + } + }, + name: { + question: 'Cum vrei să numești simulația?' + } + }, + messages: { + createdSimulation: name => + `Simulația '${name}' a fost creiată cu succes`, + switchedToSimulation: name => + `Simulația '${name}' a fost deschisă cu succes`, + savedSimulation: name => `Simulația '${name}' a fost salvată cu succes` + } +} diff --git a/src/modules/internalisation/types/TranslationInterface.ts b/src/modules/internalisation/types/TranslationInterface.ts new file mode 100644 index 0000000..4995db1 --- /dev/null +++ b/src/modules/internalisation/types/TranslationInterface.ts @@ -0,0 +1,31 @@ +import { supportedLanguages } from './supportedLanguages' +import { simulationMode } from '../../saving/types/SimulationSave' + +export type SentenceFactory = (...names: T) => string +export type NameSentence = SentenceFactory<[string]> + +/** + * The interface all translations need to follow + */ +export interface Translation { + language: supportedLanguages + sidebar: { + createSimulation: string + openSimulation: string + logicGates: string + } + createSimulation: { + mode: { + question: string + options: Record + } + name: { + question: string + } + } + messages: { + createdSimulation: NameSentence + switchedToSimulation: NameSentence + savedSimulation: NameSentence + } +} diff --git a/src/modules/internalisation/types/supportedLanguages.ts b/src/modules/internalisation/types/supportedLanguages.ts new file mode 100644 index 0000000..fadf838 --- /dev/null +++ b/src/modules/internalisation/types/supportedLanguages.ts @@ -0,0 +1,4 @@ +/** + * Type containing the names of all supported languages + */ +export type supportedLanguages = 'română' | 'english' diff --git a/src/modules/modals/styles/mixins/modal-container.scss b/src/modules/modals/styles/mixins/modal-container.scss index bc54c99..e2b0cb7 100644 --- a/src/modules/modals/styles/mixins/modal-container.scss +++ b/src/modules/modals/styles/mixins/modal-container.scss @@ -13,5 +13,5 @@ z-index: $modal-index; color: white; background-color: $modal-bg-color; - font-family: 'Righteous'; + font-family: 'Righteous', cursive; } diff --git a/src/modules/saving/helpers/cloneState.ts b/src/modules/saving/helpers/cloneState.ts index d4692bb..94e9de8 100644 --- a/src/modules/saving/helpers/cloneState.ts +++ b/src/modules/saving/helpers/cloneState.ts @@ -1 +1,6 @@ +/** + * Very basic JSON based object cloning + * + * @param state The object to clone + */ export const cloneState = (state: T): T => JSON.parse(JSON.stringify(state)) diff --git a/src/modules/saving/helpers/dumpSimulation.ts b/src/modules/saving/helpers/dumpSimulation.ts new file mode 100644 index 0000000..4100086 --- /dev/null +++ b/src/modules/saving/helpers/dumpSimulation.ts @@ -0,0 +1,16 @@ +import { SimulationRenderer } from '../../simulationRenderer/classes/SimulationRenderer' + +/** + * Does the cleanup for switching to another simulation + * + * @param renderer The renderer to clean up + */ +export const dumpSimulation = (renderer: SimulationRenderer) => { + renderer.simulation.dispose() + renderer.lastMousePosition = [0, 0] + renderer.selectedGate = null + renderer.selectedPins = { + end: null, + start: null + } +} diff --git a/src/modules/saving/helpers/fromState.ts b/src/modules/saving/helpers/fromState.ts index 81d1837..02bd080 100644 --- a/src/modules/saving/helpers/fromState.ts +++ b/src/modules/saving/helpers/fromState.ts @@ -1,8 +1,6 @@ -import { SimulationRenderer } from '../../simulationRenderer/classes/SimulationRenderer' import { Gate, PinWrapper } from '../../simulation/classes/Gate' import { TransformState, - RendererState, CameraState, SimulationState } from '../types/SimulationSave' @@ -12,6 +10,10 @@ import { Simulation } from '../../simulation/classes/Simulation' import { Wire } from '../../simulation/classes/Wire' import { templateStore } from '../stores/templateStore' +/** + * Contains methods for transforming saved state into the respective class instances + */ + export const fromTransformState = (state: TransformState): Transform => { return new Transform(state.position, state.scale, state.rotation) } @@ -25,7 +27,7 @@ export const fromCameraState = (state: CameraState): Camera => { } export const fromSimulationState = (state: SimulationState): Simulation => { - const simulation = new Simulation(state.mode) + const simulation = new Simulation(state.mode, state.name) for (const gateState of state.gates) { const gate = new Gate( diff --git a/src/modules/saving/helpers/getState.ts b/src/modules/saving/helpers/getState.ts index 4c516c1..1cc1892 100644 --- a/src/modules/saving/helpers/getState.ts +++ b/src/modules/saving/helpers/getState.ts @@ -14,6 +14,10 @@ import { Camera } from '../../simulationRenderer/classes/Camera' import { Simulation } from '../../simulation/classes/Simulation' import { Wire } from '../../simulation/classes/Wire' +/** + * Methods for gettings the savable state from class instances + */ + export const getTransformState = (transform: Transform): TransformState => { return { position: transform.position, diff --git a/src/modules/saving/helpers/initBaseTemplates.ts b/src/modules/saving/helpers/initBaseTemplates.ts index f961702..2a2badc 100644 --- a/src/modules/saving/helpers/initBaseTemplates.ts +++ b/src/modules/saving/helpers/initBaseTemplates.ts @@ -1,10 +1,20 @@ import { baseTemplates } from '../constants' import { templateStore } from '../stores/templateStore' +import { SimulationError } from '../../errors/classes/SimulationError' +/** + * Stores the base logic gate templates into localStorage + * + * @throws SimulationError if something is wrong with the template + */ export const initBaseTemplates = () => { for (const template of baseTemplates) { if (template.metadata && template.metadata.name) { templateStore.set(template.metadata.name, template) + } else { + throw new SimulationError( + `Template ${JSON.stringify(template)} cannot be stored.` + ) } } } diff --git a/src/modules/saving/helpers/initSimulation.ts b/src/modules/saving/helpers/initSimulation.ts index cd3c783..c423f97 100644 --- a/src/modules/saving/helpers/initSimulation.ts +++ b/src/modules/saving/helpers/initSimulation.ts @@ -4,9 +4,20 @@ import { cloneState } from './cloneState' import { saveStore } from '../stores/saveStore' import { toast } from 'react-toastify' import { createToastArguments } from '../../toasts/helpers/createToastArguments' +import { CurrentLanguage } from '../../internalisation/stores/currentLanguage' +/** + * Inits a simulation by: + * 1) first initialising the place in localstorage + * where the simulation will be saved + * 2) notifying the used about that + * + * @param name - the name of the simulation + * @param mode - the mode of the simulation + */ export const initSimulation = (name: string, mode: simulationMode) => { const state = cloneState(baseSave) + const translation = CurrentLanguage.getTranslation() state.simulation.name = name state.simulation.mode = mode @@ -15,7 +26,7 @@ export const initSimulation = (name: string, mode: simulationMode) => { toast.success( ...createToastArguments( - `Successfully created simulation ${name}`, + translation.messages.createdSimulation(name), 'check' ) ) diff --git a/src/modules/saving/helpers/save.ts b/src/modules/saving/helpers/save.ts index 90f5e78..3ba0821 100644 --- a/src/modules/saving/helpers/save.ts +++ b/src/modules/saving/helpers/save.ts @@ -5,16 +5,32 @@ import { getRendererState } from './getState' import { saveStore } from '../stores/saveStore' import { toast } from 'react-toastify' import { createToastArguments } from '../../toasts/helpers/createToastArguments' +import { CurrentLanguage } from '../../internalisation/stores/currentLanguage' +/** + * Saves the state from a renderer in localStorage, + * then notifies the user about it + * + * @throws SimulationError if the simulation name + * cannot found in the currentStore + * + * @param renderer - the renderer to saev the state of + */ export const save = (renderer: SimulationRenderer) => { const current = currentStore.get() if (current) { const state = getRendererState(renderer) + const translation = CurrentLanguage.getTranslation() saveStore.set(current, state) - toast(...createToastArguments(`Succesfully saved ${current}`, 'save')) + toast( + ...createToastArguments( + translation.messages.savedSimulation(current), + 'save' + ) + ) } else { throw new SimulationError( 'Cannot save without knowing the name of the active simulation' diff --git a/src/modules/saving/helpers/switchTo.ts b/src/modules/saving/helpers/switchTo.ts new file mode 100644 index 0000000..827de37 --- /dev/null +++ b/src/modules/saving/helpers/switchTo.ts @@ -0,0 +1,42 @@ +import { currentStore } from '../stores/currentStore' +import { rendererSubject } from '../../core/subjects/rendererSubject' +import { SimulationError } from '../../errors/classes/SimulationError' +import { toast } from 'react-toastify' +import { createToastArguments } from '../../toasts/helpers/createToastArguments' +import { dumpSimulation } from './dumpSimulation' +import { CurrentLanguage } from '../../internalisation/stores/currentLanguage' + +/** + * Used to switch to a simulation + * + * @throws SimulationError if theres no renderer stored in the rendererSubject + * + * @param simulationName The name of the simulation to switch to + * + * @example + * switchTo() + * switchTo('test') + * + */ +export const switchTo = (simulationName: string = 'default') => { + if (rendererSubject.value) { + const renderer = rendererSubject.value + const translation = CurrentLanguage.getTranslation() + + dumpSimulation(renderer) + + currentStore.set(simulationName) + renderer.reloadSave() + + toast( + ...createToastArguments( + translation.messages.switchedToSimulation(simulationName), + 'arrow_right_alt' + ) + ) + } else { + throw new SimulationError( + `Renderer not found while trying to switch to simulation '${simulationName}'` + ) + } +} diff --git a/src/modules/saving/stores/currentStore.ts b/src/modules/saving/stores/currentStore.ts index 6f7920c..745ea65 100644 --- a/src/modules/saving/stores/currentStore.ts +++ b/src/modules/saving/stores/currentStore.ts @@ -1,8 +1,12 @@ import { LocalStore } from '../../storage/classes/LocalStore' import { defaultSimulationName } from '../constants' +/** + * Stores the name of the current simulation + */ const currentStore = new LocalStore('currentSave') +// This makes sure the store isnt empty if (!currentStore.get()) { currentStore.set(defaultSimulationName) } diff --git a/src/modules/saving/stores/saveStore.ts b/src/modules/saving/stores/saveStore.ts index 1656424..dff300e 100644 --- a/src/modules/saving/stores/saveStore.ts +++ b/src/modules/saving/stores/saveStore.ts @@ -1,4 +1,7 @@ import { LocalStore } from '../../storage/classes/LocalStore' import { RendererState } from '../types/SimulationSave' +/** + * This store is used to save all simulations. + */ export const saveStore = new LocalStore('saves') diff --git a/src/modules/saving/stores/templateStore.ts b/src/modules/saving/stores/templateStore.ts index 74dcf60..5897051 100644 --- a/src/modules/saving/stores/templateStore.ts +++ b/src/modules/saving/stores/templateStore.ts @@ -1,6 +1,9 @@ import { LocalStore } from '../../storage/classes/LocalStore' import { GateTemplate } from '../../simulation/types/GateTemplate' +/** + * This store is used to save all logic gate templates + */ export const templateStore = new LocalStore>( 'templates' ) diff --git a/src/modules/simulationRenderer/classes/SimulationRenderer.ts b/src/modules/simulationRenderer/classes/SimulationRenderer.ts index 49defd5..0acadab 100644 --- a/src/modules/simulationRenderer/classes/SimulationRenderer.ts +++ b/src/modules/simulationRenderer/classes/SimulationRenderer.ts @@ -27,6 +27,7 @@ import { wireConnectedToGate } from '../helpers/wireConnectedToGate' import { updateMouse, handleScroll } from '../helpers/scaleCanvas' import { RefObject } from 'react' import { Singleton } from '@eix-js/utils' +import { dumpSimulation } from '../../saving/helpers/dumpSimulation' export class SimulationRenderer { public mouseDownOutput = new Subject() @@ -200,6 +201,8 @@ export class SimulationRenderer { this.lastMousePosition = this.camera.toWordPostition(event.position) }) + dumpSimulation(this) + this.reloadSave() this.initKeyBindings() } @@ -222,7 +225,6 @@ export class SimulationRenderer { if (!save) return if (!(save.simulation || save.camera)) return - this.simulation.dispose() this.simulation = fromSimulationState(save.simulation) this.camera = fromCameraState(save.camera) } catch (e) { diff --git a/src/modules/toasts/components/ToastContent.scss b/src/modules/toasts/components/ToastContent.scss index 2acd320..ec3bd7d 100644 --- a/src/modules/toasts/components/ToastContent.scss +++ b/src/modules/toasts/components/ToastContent.scss @@ -11,4 +11,5 @@ .toast-content-container > #toast-content-icon { font-size: 2em; + margin: 10px; }