diff --git a/client/src/components/PrivacyPolicy.tsx b/client/src/components/PrivacyPolicy.tsx new file mode 100644 index 0000000..774cc36 --- /dev/null +++ b/client/src/components/PrivacyPolicy.tsx @@ -0,0 +1,52 @@ +import { faShield } from '@fortawesome/free-solid-svg-icons'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import * as React from 'react' + +const PrivacyPolicy: React.FC = () => { + const [open, setOpen] = React.useState(false); + const handleOpen = () => setOpen(true); + const handleClose = () => setOpen(false); + + return ( + +
+ +

legal

+

notes

+
+ {open? +
+
+
+
+

Privacy Policy & Impressum

+ +

Our server collects metadata (such as IP address, browser, operating system) + and the data that the user enters into the editor. The data is used to + compute the Lean output and display it to the user. The information will be stored + as long as the user stays on our website and will be deleted immediately afterwards. + We keep logs to improve our software, but the contained data is anonymized.

+ +

We do not use cookies, but your game progress is stored in the browser + as site data. Your game progress is not saved on the server; if you delete + your browser storage, it is completely gone. +

+ +

Our server is located in Germany.

+ +

Contact information:
+ Jon Eugster
+ Mathematisches Institut der Heinrich-Heine-Universität Düsseldorf
+ Universitätsstr. 1
+ 40225 Düsseldorf
+ Germany
+ jon.eugster@hhu.de + +

+
+
: null} + + ) +} + +export default PrivacyPolicy diff --git a/client/src/components/Welcome.tsx b/client/src/components/Welcome.tsx index 2ee5de7..a69d37f 100644 --- a/client/src/components/Welcome.tsx +++ b/client/src/components/Welcome.tsx @@ -5,7 +5,9 @@ import cytoscape, { LayoutOptions } from 'cytoscape' import klay from 'cytoscape-klay'; import { useNavigate } from 'react-router-dom'; import { useSelector } from 'react-redux'; +import Split from 'react-split' +import PrivacyPolicy from './PrivacyPolicy'; cytoscape.use( klay ); @@ -16,14 +18,23 @@ import Markdown from './Markdown'; import { selectCompleted } from '../state/progress'; import { GameIdContext } from '../App'; +const N = 24 // max number of levels per world +const R = 800 // radius of a world +const r = 110 // radius of a level +const s = 100 // global scale +const padding = 2000 // padding of the graphic (on a different scale) function LevelIcon({ worldId, levelId, position }) { const gameId = React.useContext(GameIdContext) const completed = useSelector(selectCompleted(gameId, worldId,levelId)) + + const x = s * position.x + Math.sin(levelId * 2 * Math.PI / N) * (R + 1.2*r + 2*Math.floor((levelId - 1)/N)) + const y = s * position.y - Math.cos(levelId * 2 * Math.PI / N) * (R + 1.2*r + 2*Math.floor((levelId - 1)/N)) + // TODO: relative positioning? return ( - + ) } @@ -42,56 +53,66 @@ function Welcome() { } }, [gameInfo.data?.title]) - const padding = 20 - const svgElements = [] if (gameInfo.data) { for (let i in gameInfo.data.worlds.edges) { const edge = gameInfo.data.worlds.edges[i] svgElements.push( - + ) } + for (let id in nodes) { let position: cytoscape.Position = nodes[id].position - svgElements.push( - - - {nodes[id].data.title ? nodes[id].data.title : id} - - ) - for (let i = 1; i <= gameInfo.data.worldSize[id]; i++) { svgElements.push( ) } + + svgElements.push( + + + +
+

+ {nodes[id].data.title ? nodes[id].data.title : id} +

+
+
+ + ) } } - return
+ return
{ gameInfo.isLoading? - + + + : -
- - + +
+ {gameInfo.data?.introduction} - - - - {svgElements} - - -
+
+
+ + + {svgElements} + + +
+ } - +
} @@ -119,9 +140,8 @@ function computeWorldLayout(worlds) { headless: true, styleEnabled: false }) -// TODO: Jon play around with graph layout - const layout = cy.layout({name: "klay", klay: {direction: "DOWN"}} as LayoutOptions).run() + const layout = cy.layout({name: "klay", klay: {direction: "DOWN", nodePlacement: "LINEAR_SEGMENTS"}} as LayoutOptions).run() let nodes = {} cy.nodes().forEach((node, id) => { nodes[node.id()] = { diff --git a/client/src/components/welcome.css b/client/src/components/welcome.css index 849f441..8e457a2 100644 --- a/client/src/components/welcome.css +++ b/client/src/components/welcome.css @@ -1,12 +1,191 @@ -svg .world-circle { +/* svg .world-circle { fill: var(--clr-primary) +} */ + +.welcome { + height: 100%; + flex: 1; + min-height: 0; + display: flex; +} + +.app-content { + height: 100% +} + +.welcome .column { + height: 100%; + overflow: auto; +} + +.welcome-text { + padding: 20px; +} + +h1 { + font-size: 2em; + margin: .67em 0; +} + +h2 { + font-size: 1.5em; +} + +h3 { + font-size: 1.3em; +} + +h4 { + font-size: 1.1em; + font-style: italic; +} + +h5, h6 { + font-size: 1em; + font-style: italic; +} + +/***************/ +/* SVG Graphic */ +/***************/ + +svg .world-title-wrapper { + overflow: auto; +} + +svg .world-title-wrapper div { + width: 100%; + height: 100%; +} + +svg .world-title-wrapper div { + display: flex; + align-items:center; + justify-content:center; + overflow: visible; + } -svg .world-name { - fill: white; - font-size: 2px; +svg .world-title { font-weight: 500; - text-anchor: middle; - dominant-baseline: middle; + color: white; + margin: 0; + padding: 0; +} + +/******************/ +/* Privacy Button */ +/******************/ + +.privacy { + width: 40px; + height: 40px; + font-size: 25px; + border-radius: 20px; + position: absolute; + right: 10px; + bottom: 10px; + display: flex; + align-items:center; + justify-content:center; + color: #aaa; + background-color: #eee; + cursor: pointer; +} + +.privacy p { + position: absolute; + color: #888; + bottom: 1.5px; + font-size: 6px; +} + +.privacy .p1 { + transform: rotate(50deg); + left: 1.5px; +} + +.privacy .p2 { + transform: rotate(-50deg); + right: 1.5px; +} + +/*****************/ +/* Privacy Popup */ +/*****************/ + +.modal-wrapper { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + padding: 0; +} + +.modal-backdrop { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + background: rgba(0, 0, 0, 0.25); + z-index: 2; +} + +.modal h2 { + text-align: center; +} + +.modal { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + min-width: 50%; + max-width: 60ch; + background: #fff; + z-index: 3; + padding: 2em; + border-radius: 1em; + text-align: left; + color: var(--vscode-breadcrumb-foreground); +} + +.modal input[type="text"] { + width: 100%; +} + +.modal .form-error { + color: #a00; + font-weight: bold; +} + +.modal input[type="submit"] { + border: none; + color: var(--vscode-button-foreground); + background: var(--vscode-button-background); + cursor: pointer; + padding: .5em 1em; + border-radius: .2em; + display: block; + margin: 1em auto; +} + +.modal-close { + float: right; + scale: 2; + color: var(--vscode-breadcrumb-foreground); + cursor: pointer; +} + +.modal-close:hover { + float: right; + scale: 2; + color: var(--vscode-breadcrumb-focusForeground); +} + +.modal table { + width: 100%; } diff --git a/package-lock.json b/package-lock.json index 26c2ec9..2e6d55a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,6 +18,7 @@ "@types/cytoscape": "^3.19.9", "@types/react-router-dom": "^5.3.3", "cytoscape": "^3.23.0", + "cytoscape-elk": "^2.1.0", "cytoscape-klay": "^3.1.4", "debounce": "^1.2.1", "express": "^4.18.2", @@ -34,6 +35,7 @@ "remark-gfm": "^3.0.1", "remark-math": "^5.1.1", "vscode-ws-jsonrpc": "^2.0.1", + "web-worker": "^1.2.0", "ws": "^8.11.0" }, "devDependencies": { @@ -4180,6 +4182,17 @@ "node": ">=0.10" } }, + "node_modules/cytoscape-elk": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cytoscape-elk/-/cytoscape-elk-2.1.0.tgz", + "integrity": "sha512-stkKoUTNOqpyP5eMuqatK0EYir2NWGTH+XlY0rxFj0t0HiQPGI4AuSuTPaGbNM1WhVfb0tWJ5TQQ0R0qshACLw==", + "dependencies": { + "elkjs": "^0.8.1" + }, + "peerDependencies": { + "cytoscape": "^3.2.0" + } + }, "node_modules/cytoscape-klay": { "version": "3.1.4", "resolved": "https://registry.npmjs.org/cytoscape-klay/-/cytoscape-klay-3.1.4.tgz", @@ -4435,6 +4448,11 @@ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.286.tgz", "integrity": "sha512-Vp3CVhmYpgf4iXNKAucoQUDcCrBQX3XLBtwgFqP9BUXuucgvAV9zWp1kYU7LL9j4++s9O+12cb3wMtN4SJy6UQ==" }, + "node_modules/elkjs": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/elkjs/-/elkjs-0.8.2.tgz", + "integrity": "sha512-L6uRgvZTH+4OF5NE/MBbzQx/WYpru1xCBE9respNj6qznEewGUIfhzmm7horWWxbNO2M0WckQypGctR8lH79xQ==" + }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -9869,6 +9887,11 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/web-worker": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/web-worker/-/web-worker-1.2.0.tgz", + "integrity": "sha512-PgF341avzqyx60neE9DD+XS26MMNMoUQRz9NOZwW32nPQrF6p77f1htcnjBSEV8BGMKZ16choqUG4hyI0Hx7mA==" + }, "node_modules/webpack": { "version": "5.75.0", "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.75.0.tgz", diff --git a/package.json b/package.json index fc3bf0a..67cab6b 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "@types/cytoscape": "^3.19.9", "@types/react-router-dom": "^5.3.3", "cytoscape": "^3.23.0", + "cytoscape-elk": "^2.1.0", "cytoscape-klay": "^3.1.4", "debounce": "^1.2.1", "express": "^4.18.2", @@ -30,6 +31,7 @@ "remark-gfm": "^3.0.1", "remark-math": "^5.1.1", "vscode-ws-jsonrpc": "^2.0.1", + "web-worker": "^1.2.0", "ws": "^8.11.0" }, "devDependencies": {