120 lines
3.8 KiB
TypeScript
120 lines
3.8 KiB
TypeScript
import { BrowserRouter, Link, Outlet, Route, Routes, useLocation, useNavigate, useParams } from 'react-router-dom';
|
|
import { VoxelEditorPage } from './components/voxelEditor/VoxelEditorPage';
|
|
import { useEffect, useLayoutEffect, useRef } from 'react';
|
|
import { reaction } from 'mobx';
|
|
import { state } from './state';
|
|
import './App.scss';
|
|
import { Panels } from './components/Panels';
|
|
import { LayeredThreeView } from './components/layers/LayeredThreeView';
|
|
|
|
function StateToUrlSync() {
|
|
const navigate = useNavigate();
|
|
const { pathname } = useLocation();
|
|
const pathnameRef = useRef(pathname);
|
|
useLayoutEffect(() => { pathnameRef.current = pathname; });
|
|
|
|
useEffect(() => reaction(
|
|
() => ({
|
|
isGame: !!state.game,
|
|
selectedObjectId: state.worldEditor.selection?.type === 'object' && state.worldEditor.selection.id,
|
|
selectedObjectTypeId: state.worldEditor.selection?.type === 'objectType' && state.worldEditor.selection.id,
|
|
}),
|
|
({ isGame, selectedObjectId, selectedObjectTypeId }) => {
|
|
let target: string;
|
|
if (isGame)
|
|
target = '/game';
|
|
else if (selectedObjectId)
|
|
target = `/editor/object/${selectedObjectId}`;
|
|
else if (selectedObjectTypeId)
|
|
target = `/editor/objectType/${selectedObjectTypeId}`;
|
|
else
|
|
target = '/editor';
|
|
|
|
const current = pathnameRef.current;
|
|
if (current === target || current.startsWith(target + '/'))
|
|
return;
|
|
navigate(target);
|
|
},
|
|
), [navigate]);
|
|
return null;
|
|
}
|
|
|
|
function EditorRoute() {
|
|
useEffect(() => {
|
|
if (state.game)
|
|
state.stopGame();
|
|
state.worldEditor.resetSelection();
|
|
}, []);
|
|
return null;
|
|
}
|
|
|
|
function EditorObjectRoute() {
|
|
const { id } = useParams<{ id: string }>();
|
|
useEffect(() => {
|
|
if (!id)
|
|
return;
|
|
if (state.game)
|
|
state.stopGame();
|
|
const editMode = state.worldEditor.selection?.type === 'object' && state.worldEditor.selection.id === id
|
|
? state.worldEditor.selection.editMode ?? 'translate'
|
|
: 'translate';
|
|
state.worldEditor.setSelectedObject({ id, editMode });
|
|
}, [id]);
|
|
return null;
|
|
}
|
|
|
|
function EditorObjectTypeRoute() {
|
|
const { id } = useParams<{ id: string }>();
|
|
useEffect(() => {
|
|
if (!id)
|
|
return;
|
|
if (state.game)
|
|
state.stopGame();
|
|
const editMode = state.worldEditor.selection?.type === 'objectType' && state.worldEditor.selection.id === id
|
|
? state.worldEditor.selection.editMode ?? 'scripts'
|
|
: 'scripts';
|
|
state.worldEditor.setSelectedObjectType({ id, editMode });
|
|
}, [id]);
|
|
return null;
|
|
}
|
|
|
|
function GameRoute() {
|
|
useEffect(() => {
|
|
if (!state.game)
|
|
state.startGame();
|
|
}, []);
|
|
return null;
|
|
}
|
|
|
|
function HomePage() {
|
|
return <Link to="/editor">Open Editor</Link>;
|
|
}
|
|
|
|
function EditorLayout() {
|
|
return (
|
|
<div className="editor-layout">
|
|
<Outlet />
|
|
<LayeredThreeView />
|
|
<Panels />
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export const App = function () {
|
|
return (
|
|
<BrowserRouter>
|
|
<StateToUrlSync />
|
|
<Routes>
|
|
<Route path="/" element={<HomePage />} />
|
|
<Route path="/editor/objectType/:id/voxels" element={<VoxelEditorPage />} />
|
|
<Route element={<EditorLayout />}>
|
|
<Route path="/editor" element={<EditorRoute />} />
|
|
<Route path="/editor/object/:id" element={<EditorObjectRoute />} />
|
|
<Route path="/editor/objectType/:id" element={<EditorObjectTypeRoute />} />
|
|
<Route path="/game" element={<GameRoute />} />
|
|
</Route>
|
|
</Routes>
|
|
</BrowserRouter>
|
|
);
|
|
};
|