{ gameInfo.isLoading?
@@ -92,9 +44,17 @@ function Welcome() {
-
+
-
}
@@ -102,3 +62,37 @@ function Welcome() {
}
export default Welcome
+
+function computeWorldLayout(worlds) {
+
+ let elements = []
+ for (let node of worlds.nodes) {
+ elements.push({ data: { id: node } })
+ }
+ for (let edge of worlds.edges) {
+ elements.push({
+ data: {
+ id: edge[0] + " --edge-to--> " + edge[1],
+ source: edge[0],
+ target: edge[1]
+ }
+ })
+ }
+
+ const cy = cytoscape({
+ container: null,
+ elements,
+ headless: true,
+ styleEnabled: false
+ })
+
+ const layout = cy.layout({name: "klay", klay: {direction: "DOWN"}} as LayoutOptions).run()
+ let nodes = {}
+ cy.nodes().forEach((node, id) => {
+ nodes[node.id()] = node.position()
+ console.log(node.position())
+ })
+ const bounds = cy.nodes().boundingBox()
+ console.log(bounds)
+ return { nodes, bounds }
+}
diff --git a/client/src/hooks.ts b/client/src/hooks.ts
index 60a4163..afdce0a 100644
--- a/client/src/hooks.ts
+++ b/client/src/hooks.ts
@@ -1,5 +1,5 @@
import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux'
-import type { RootState, AppDispatch } from './store'
+import type { RootState, AppDispatch } from './state/store'
// Use throughout your app instead of plain `useDispatch` and `useSelector`
export const useAppDispatch: () => AppDispatch = useDispatch
diff --git a/client/src/index.tsx b/client/src/index.tsx
index 9c9790b..f936b83 100644
--- a/client/src/index.tsx
+++ b/client/src/index.tsx
@@ -3,7 +3,7 @@ import { createRoot } from 'react-dom/client';
import './index.css';
import App from './App';
import { ConnectionContext, connection } from './connection'
-import { store } from './store';
+import { store } from './state/store';
import { Provider } from 'react-redux';
import {
createHashRouter,
diff --git a/client/src/game/api.ts b/client/src/state/api.ts
similarity index 90%
rename from client/src/game/api.ts
rename to client/src/state/api.ts
index 784cfd7..5ee564a 100644
--- a/client/src/game/api.ts
+++ b/client/src/state/api.ts
@@ -4,7 +4,7 @@ import { Connection } from '../connection'
interface GameInfo {
title: null|string,
introduction: null|string,
- worlds: null|{nodes: string[], edges: string[][2]},
+ worlds: null|{nodes: string[], edges: string[][]},
authors: null|string[],
conclusion: null|string,
}
@@ -37,7 +37,7 @@ const customBaseQuery = async (
}
// Define a service using a base URL and expected endpoints
-export const gameApi = createApi({
+export const apiSlice = createApi({
reducerPath: 'gameApi',
baseQuery: customBaseQuery,
endpoints: (builder) => ({
@@ -52,4 +52,4 @@ export const gameApi = createApi({
// Export hooks for usage in functional components, which are
// auto-generated based on the defined endpoints
-export const { useGetGameInfoQuery, useLoadLevelQuery } = gameApi
+export const { useGetGameInfoQuery, useLoadLevelQuery } = apiSlice
diff --git a/client/src/state/progress.ts b/client/src/state/progress.ts
new file mode 100644
index 0000000..6e65467
--- /dev/null
+++ b/client/src/state/progress.ts
@@ -0,0 +1,30 @@
+import { createSlice } from '@reduxjs/toolkit'
+import type { PayloadAction } from '@reduxjs/toolkit'
+
+interface ProgressState {
+ code: {[world: string]: {[level: number]: string}}
+}
+
+const initialState = { code: {} } as ProgressState
+
+export const progressSlice = createSlice({
+ name: 'progress',
+ initialState,
+ reducers: {
+ codeEdited(state, action: PayloadAction<{world: string, level: number, code: string}>) {
+ if (!state.code[action.payload.world]) {
+ state.code[action.payload.world] = {}
+ }
+ state.code[action.payload.world][action.payload.level] = action.payload.code
+ },
+ }
+})
+
+export function selectCode(world: string, level: number) {
+ return (state) => {
+ if (!state.progress.code[world]) { return undefined }
+ state.progress.code[world][level];
+ }
+}
+
+export const { codeEdited } = progressSlice.actions
diff --git a/client/src/store.ts b/client/src/state/store.ts
similarity index 68%
rename from client/src/store.ts
rename to client/src/state/store.ts
index 1e6b216..927460e 100644
--- a/client/src/store.ts
+++ b/client/src/state/store.ts
@@ -1,19 +1,21 @@
import { configureStore } from '@reduxjs/toolkit';
-import { connection } from './connection'
+import { connection } from '../connection'
import thunkMiddleware from 'redux-thunk'
-import { gameApi } from './game/api'
+import { apiSlice } from './api'
+import { progressSlice } from './progress'
export const store = configureStore({
reducer: {
- [gameApi.reducerPath]: gameApi.reducer,
+ [apiSlice.reducerPath]: apiSlice.reducer,
+ [progressSlice.name]: progressSlice.reducer,
},
middleware: getDefaultMiddleware =>
getDefaultMiddleware({
thunk: {
extraArgument: { connection }
}
- }).concat(gameApi.middleware),
+ }).concat(apiSlice.middleware),
});
// Infer the `RootState` and `AppDispatch` types from the store itself