flow: universes
This commit is contained in:
parent
40a05a7ea4
commit
b29b9d7a05
|
@ -28,6 +28,7 @@
|
|||
"@avocado/sound": "^1.0.0",
|
||||
"@avocado/timing": "^2.0.0",
|
||||
"@avocado/topdown": "^2.0.0",
|
||||
"@humus/universe": "^1.0.0",
|
||||
"@inlet/react-pixi": "^6.0.7",
|
||||
"@latus/core": "2.0.0",
|
||||
"@latus/db": "2.0.0",
|
||||
|
|
|
@ -63,9 +63,7 @@ const Humus = ({history}) => {
|
|||
<Route path="/login">
|
||||
{isLoggedIn ? <Redirect to="/title" /> : Login}
|
||||
</Route>
|
||||
<Route path="/universe">
|
||||
<Universe />
|
||||
</Route>
|
||||
<Route path={['/universe/:uuid', '/universe']} component={Universe} />
|
||||
{isNative ? <Route path="/title"><Title /></Route> : <Redirect to="/universe" />}
|
||||
</Switch>
|
||||
</Ui>
|
||||
|
|
|
@ -8,3 +8,19 @@ body {
|
|||
position: relative;
|
||||
width: 100vw;
|
||||
}
|
||||
|
||||
.back {
|
||||
color: white;
|
||||
font-family: var(--thick-title-font-family);
|
||||
font-size: 1.5em;
|
||||
position: absolute;
|
||||
left: 3em;
|
||||
text-decoration: none;
|
||||
top: 3em;
|
||||
-webkit-text-stroke: 0.25px black;
|
||||
}
|
||||
|
||||
.back__arrow {
|
||||
letter-spacing: -0.25em;
|
||||
margin-right: 0.25em;
|
||||
}
|
||||
|
|
|
@ -1,13 +1,30 @@
|
|||
import './index.scss';
|
||||
|
||||
import React from 'react';
|
||||
import {
|
||||
Link,
|
||||
} from 'react-router-dom';
|
||||
|
||||
import Universes from '../universes';
|
||||
|
||||
const Local = () => (
|
||||
<div className="local">
|
||||
<Link
|
||||
className="back"
|
||||
to="/title/main"
|
||||
>
|
||||
<span className="back__arrow"><-</span>
|
||||
{' '}
|
||||
Back
|
||||
</Link>
|
||||
<h2 className="local__title">Local universes</h2>
|
||||
<Universes />
|
||||
<Link
|
||||
className="local__create"
|
||||
to="/title/local/create"
|
||||
>
|
||||
+ Create a new universe
|
||||
</Link>
|
||||
<Universes isLocal />
|
||||
</div>
|
||||
);
|
||||
|
||||
|
|
|
@ -1,10 +1,23 @@
|
|||
.local__title {
|
||||
font-family: var(--thick-title-font-family);
|
||||
font-size: 2em;
|
||||
margin-left: 2em;
|
||||
font-size: 4em;
|
||||
margin-left: 1em;
|
||||
margin-top: 2em;
|
||||
}
|
||||
|
||||
.local__create {
|
||||
color: white;
|
||||
display: inline-block;
|
||||
font-family: var(--title-font-family);
|
||||
font-size: 1.5em;
|
||||
margin-top: 1em;
|
||||
margin-left: 2.75em;
|
||||
padding: 0.5em;
|
||||
padding-left: 1em;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.local > .universes {
|
||||
margin: 4em;
|
||||
margin-top: 2em;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
.title-main__title {
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
font-family: var(--scribbles-font-family);
|
||||
font-size: 12em;
|
||||
left: 25%;
|
||||
|
@ -30,8 +31,8 @@
|
|||
.title-main__button {
|
||||
display: block;
|
||||
font-size: 3em;
|
||||
padding: 0.25em;
|
||||
padding: 0.5em 1.5em;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,13 +1,30 @@
|
|||
import './index.scss';
|
||||
|
||||
import React from 'react';
|
||||
import {
|
||||
Link,
|
||||
} from 'react-router-dom';
|
||||
|
||||
import Universes from '../universes';
|
||||
|
||||
const Servers = () => (
|
||||
<div className="servers">
|
||||
<h2 className="servers__title">Servers</h2>
|
||||
<Universes />
|
||||
<Link
|
||||
className="back"
|
||||
to="/title/main"
|
||||
>
|
||||
<span className="back__arrow"><-</span>
|
||||
{' '}
|
||||
Back
|
||||
</Link>
|
||||
<h2 className="servers__title">Remote server universes</h2>
|
||||
<Link
|
||||
className="servers__connect"
|
||||
to="/title/servers/connect"
|
||||
>
|
||||
+ Connect to a new universe
|
||||
</Link>
|
||||
<Universes isLocal={false} />
|
||||
</div>
|
||||
);
|
||||
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
.servers__title {
|
||||
font-family: var(--thick-title-font-family);
|
||||
font-size: 4em;
|
||||
margin-left: 1em;
|
||||
margin-top: 2em;
|
||||
}
|
||||
|
||||
.servers__connect {
|
||||
color: white;
|
||||
display: inline-block;
|
||||
font-family: var(--title-font-family);
|
||||
font-size: 1.5em;
|
||||
margin-top: 1em;
|
||||
margin-left: 2.75em;
|
||||
padding: 0.5em;
|
||||
padding-left: 1em;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.servers > .universes {
|
||||
margin: 4em;
|
||||
margin-top: 2em;
|
||||
}
|
|
@ -1,29 +1,26 @@
|
|||
import './index.scss';
|
||||
|
||||
import {universesByLocalitySelector} from '@humus/universe';
|
||||
import {useSelector} from '@latus/redux';
|
||||
import {hot} from 'react-hot-loader';
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
|
||||
import Universe from './universe';
|
||||
|
||||
const universes = [
|
||||
{
|
||||
isOnline: true,
|
||||
title: "cha0s's universe",
|
||||
uuid: '2543c30e-3749-4c08-a0b2-c50137a39c4a',
|
||||
},
|
||||
{
|
||||
isOnline: false,
|
||||
title: 'random universe',
|
||||
uuid: 'c41ddaac-89c2-46a4-b3e5-1d634a1a7c36',
|
||||
},
|
||||
];
|
||||
const Universes = ({isLocal}) => {
|
||||
const universes = useSelector((state) => universesByLocalitySelector(state, isLocal));
|
||||
return (
|
||||
<div className="universes">
|
||||
{Object.entries(universes).map(([uuid, universe]) => (
|
||||
<Universe key={uuid} universe={{...universe, uuid}} />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const Universes = () => (
|
||||
<div className="universes">
|
||||
{universes.map((universe) => (
|
||||
<Universe key={universe.uuid} universe={universe} />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
Universes.propTypes = {
|
||||
isLocal: PropTypes.bool.isRequired,
|
||||
};
|
||||
|
||||
export default hot(module)(Universes);
|
||||
|
|
|
@ -6,15 +6,39 @@ import {
|
|||
Link,
|
||||
} from 'react-router-dom';
|
||||
|
||||
const Universe = ({universe: {isOnline, title, uuid}}) => (
|
||||
<Link className="universes-universe" to={`/universe/${uuid}`}>
|
||||
<h3>{title}</h3>
|
||||
<span>{isOnline ? '✔️' : '❌'}</span>
|
||||
</Link>
|
||||
);
|
||||
const Universe = ({
|
||||
universe: {
|
||||
address,
|
||||
isOnline,
|
||||
title,
|
||||
uuid,
|
||||
},
|
||||
}) => {
|
||||
const [host, port] = address.split(':');
|
||||
return (
|
||||
<Link className="button universes-universe" to={`/universe/${uuid}`}>
|
||||
<h3>{title}</h3>
|
||||
{address && (
|
||||
<span className="universes-universe__address">
|
||||
[
|
||||
<span className="universes-universe__host">{host}</span>
|
||||
{port && (
|
||||
<span className="universes-universe__port">
|
||||
:
|
||||
{port}
|
||||
</span>
|
||||
)}
|
||||
]
|
||||
</span>
|
||||
)}
|
||||
<span>{isOnline ? '✔️' : '❌'}</span>
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
|
||||
Universe.propTypes = {
|
||||
universe: PropTypes.shape({
|
||||
address: PropTypes.string,
|
||||
isOnline: PropTypes.bool,
|
||||
title: PropTypes.string,
|
||||
uuid: PropTypes.string,
|
||||
|
|
|
@ -1,13 +1,27 @@
|
|||
@import 'scss/colors.scss';
|
||||
|
||||
.universes-universe {
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
align-items: baseline;
|
||||
color: lighten($color-active, 20%);
|
||||
display: flex;
|
||||
font-family: var(--thick-title-font-family);
|
||||
justify-content: space-between;
|
||||
margin-bottom: 1em;
|
||||
padding: 2em;
|
||||
text-decoration: none;
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(0, 0, 0, 0.75);
|
||||
}
|
||||
}
|
||||
|
||||
.universes-universe__address {
|
||||
color: #fff;
|
||||
font-family: monospace;
|
||||
font-size: 1.5em;
|
||||
margin-right: auto;
|
||||
margin-left: 0.5em;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.universes-universe__port {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
|
|
@ -1,33 +1,54 @@
|
|||
import './index.scss';
|
||||
|
||||
import {universesByLocalitySelector, universesSelector} from '@humus/universe';
|
||||
import {useSelector} from '@latus/redux';
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import {
|
||||
Link,
|
||||
Redirect,
|
||||
} from 'react-router-dom';
|
||||
|
||||
import useIsNative from 'hooks/use-is-native';
|
||||
|
||||
const Universe = () => (
|
||||
<div className="universe">
|
||||
<div className="universe__muted">
|
||||
{useIsNative && (
|
||||
<Link className="universe__back" to="/title">Back to title</Link>
|
||||
)}
|
||||
<h2 className="universe__title">{'{UNIVERSE_NAME}'}</h2>
|
||||
<button className="universe__play" type="button">Play</button>
|
||||
<p className="universe__attribution">
|
||||
Art by
|
||||
{' '}
|
||||
<a
|
||||
href="https://www.artstation.com/simonkono"
|
||||
rel="noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
Simon Kono
|
||||
</a>
|
||||
</p>
|
||||
const Universe = ({match: {params: {uuid}}}) => {
|
||||
const isNative = useIsNative();
|
||||
const universes = useSelector(universesSelector);
|
||||
const remoteUniverses = useSelector((state) => universesByLocalitySelector(state, false));
|
||||
const uuids = Object.keys(remoteUniverses);
|
||||
if (!uuid && uuids.length === 0) {
|
||||
return <Redirect to="/" />;
|
||||
}
|
||||
const universe = uuid ? universes[uuid] : universes[uuids.pop()];
|
||||
if (!universe) {
|
||||
return <Redirect to="/" />;
|
||||
}
|
||||
return (
|
||||
<div className="universe">
|
||||
<div className="universe__muted">
|
||||
{isNative && (
|
||||
<Link
|
||||
className="back"
|
||||
to={universe.address ? '/title/servers' : '/title/local'}
|
||||
>
|
||||
<span className="back__arrow"><-</span>
|
||||
{' '}
|
||||
Back
|
||||
</Link>
|
||||
)}
|
||||
<h2 className="universe__title">{universe.title}</h2>
|
||||
<button className="universe__play" type="button">Play</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
);
|
||||
};
|
||||
|
||||
Universe.propTypes = {
|
||||
match: PropTypes.shape({
|
||||
params: PropTypes.shape({
|
||||
uuid: PropTypes.string,
|
||||
}),
|
||||
}).isRequired,
|
||||
};
|
||||
|
||||
export default Universe;
|
||||
|
|
|
@ -6,32 +6,16 @@
|
|||
}
|
||||
|
||||
.universe__muted {
|
||||
background-color: rgba(0, 0, 0, 0.25);
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.universe__back {
|
||||
color: white;
|
||||
font-size: 1.5em;
|
||||
position: absolute;
|
||||
left: 3em;
|
||||
top: 1em;
|
||||
-webkit-text-stroke: 0.25px black;
|
||||
}
|
||||
|
||||
.universe__attribution {
|
||||
position: absolute;
|
||||
bottom: 1em;
|
||||
left: 1em;
|
||||
}
|
||||
|
||||
.universe__title {
|
||||
font-family: var(--thick--title-font-family);
|
||||
font-family: var(--thick-title-font-family);
|
||||
font-size: 4em;
|
||||
margin: 1em;
|
||||
-webkit-text-stroke: 1.5px black;
|
||||
margin: 2em 1em;
|
||||
}
|
||||
|
||||
.universe__play {
|
||||
|
|
|
@ -17,7 +17,7 @@ const Index = () => (
|
|||
export default {
|
||||
hooks: {
|
||||
'@latus/react/components': () => Index,
|
||||
'@latus/redux/client/slices': () => ({
|
||||
'@latus/redux/slices': () => ({
|
||||
router: connectRouter(history),
|
||||
}),
|
||||
'@latus/redux/store': (options) => {
|
||||
|
|
|
@ -150,10 +150,15 @@ fieldset {
|
|||
}
|
||||
|
||||
button, .button {
|
||||
background: #222222;
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
background-color: rgba(0, 0, 0, 0.65);
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
color: #ffffff;
|
||||
font-size: 100%;
|
||||
text-decoration: none;
|
||||
transition: background-color 0.2s;
|
||||
&:hover {
|
||||
background-color: rgba(0, 0, 0, 0.85);
|
||||
}
|
||||
}
|
||||
|
||||
button, .button, input[type="checkbox"], input[type="checkbox"] + label {
|
||||
|
|
|
@ -1086,6 +1086,14 @@
|
|||
object-assign "^4.1.1"
|
||||
scheduler "^0.20.1"
|
||||
|
||||
"@humus/universe@^1.0.0":
|
||||
version "1.0.0"
|
||||
resolved "https://npm.i12e.cha0s.io/@humus%2funiverse/-/universe-1.0.0.tgz#7072c22ea3dbaf94dc1aae4f374fa334f2387d05"
|
||||
integrity sha512-a8axM6QtzS2V0fcA2dh5FUrGVToMYYz+JAmkPA/N+4yLRChVpTVyFIWu6qwrsy1rPbEaMuvo7ZT844zeytQ0aw==
|
||||
dependencies:
|
||||
"@latus/redux" "^2.0.0"
|
||||
debug "4.3.1"
|
||||
|
||||
"@inlet/react-pixi@^6.0.7":
|
||||
version "6.0.7"
|
||||
resolved "https://npm.i12e.cha0s.io/@inlet%2freact-pixi/-/react-pixi-6.0.7.tgz#0b05a85893213b78962c1cbe9c5016f3c183f032"
|
||||
|
@ -1172,8 +1180,8 @@
|
|||
|
||||
"@latus/redux@^2.0.0":
|
||||
version "2.0.0"
|
||||
resolved "https://npm.i12e.cha0s.io/@latus%2fredux/-/redux-2.0.0.tgz#e74c4040fd4958e77c3f9a4a0c77a4538bf6fbeb"
|
||||
integrity sha512-npKxsLEQdDNg6aAANGInuZeBjOZdQsKySezv+MEjOqVgo7yurwDogXNRKPGb9mqjzaH4D4ugcCDAGBLLY5CvFg==
|
||||
resolved "https://npm.i12e.cha0s.io/@latus%2fredux/-/redux-2.0.0.tgz#10fee1fd1073ef12fff02294ee09c5362d2cc8b7"
|
||||
integrity sha512-4DfJbZAfOOR7QV0Y9lcJ8MZhBWc+kYG5ngH6fWAhzhG41XIcOE/H3E6K3J6xX5Y/RCr9/qcuSSDwE3ReEkYT/Q==
|
||||
dependencies:
|
||||
"@latus/core" "2.0.0"
|
||||
"@reduxjs/toolkit" "^1.5.0"
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"name": "@latus/package",
|
||||
"name": "@humus/package",
|
||||
"version": "1.0.0",
|
||||
"main": "index.js",
|
||||
"author": "cha0s",
|
||||
|
|
1
packages/universe/.eslintrc.js
Normal file
1
packages/universe/.eslintrc.js
Normal file
|
@ -0,0 +1 @@
|
|||
module.exports = require('../../config/.eslintrc');
|
6
packages/universe/.gitignore
vendored
Normal file
6
packages/universe/.gitignore
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
**/*.js
|
||||
**/*.map
|
||||
!/.*
|
||||
!/webpack.config.js
|
||||
!src/**/*.js
|
||||
!/test/**/*.js
|
1
packages/universe/.neutrinorc.js
Normal file
1
packages/universe/.neutrinorc.js
Normal file
|
@ -0,0 +1 @@
|
|||
module.exports = require('../../config/.neutrinorc');
|
44
packages/universe/package.json
Normal file
44
packages/universe/package.json
Normal file
|
@ -0,0 +1,44 @@
|
|||
{
|
||||
"name": "@humus/universe",
|
||||
"version": "1.0.0",
|
||||
"main": "index.js",
|
||||
"author": "cha0s",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"build": "NODE_PATH=./node_modules webpack --mode production",
|
||||
"clean": "rm -rf yarn.lock node_modules && yarn",
|
||||
"dev": "NODE_PATH=./node_modules webpack --mode development",
|
||||
"forcepub": "npm unpublish --force $(node -e 'const {name, version} = require(`./package.json`); process.stdout.write(`${name}@${version}`)') && npm publish",
|
||||
"link": "node -e \"Object.keys(require('./package.json').dependencies).filter((m) => 0 === m.indexOf('@latus/')).forEach((m) => require('child_process').spawn('yarn', ['link', m]));\"",
|
||||
"lint": "NODE_PATH=./node_modules eslint --format codeframe --ext mjs,js .",
|
||||
"test": "NODE_PATH=./node_modules mocha --config ../../config/.mocharc.js",
|
||||
"unlink": "node -e \"Object.keys(require('./package.json').dependencies).filter((m) => 0 === m.indexOf('@latus/')).forEach((m) => require('child_process').spawn('yarn', ['unlink', m]));\" && yarn install --force",
|
||||
"watch": "NODE_PATH=./node_modules webpack --watch --mode development"
|
||||
},
|
||||
"files": [
|
||||
"index.js",
|
||||
"index.js.map",
|
||||
"test.js",
|
||||
"test.js.map"
|
||||
],
|
||||
"dependencies": {
|
||||
"@latus/redux": "^2.0.0",
|
||||
"debug": "4.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@neutrinojs/airbnb-base": "^9.4.0",
|
||||
"@neutrinojs/banner": "^9.4.0",
|
||||
"@neutrinojs/copy": "9.4.0",
|
||||
"@neutrinojs/mocha": "^9.4.0",
|
||||
"@neutrinojs/react": "^9.4.0",
|
||||
"chai": "4.2.0",
|
||||
"eslint": "^7",
|
||||
"eslint-import-resolver-webpack": "0.13.0",
|
||||
"glob": "7.1.6",
|
||||
"mocha": "^8",
|
||||
"neutrino": "^9.4.0",
|
||||
"source-map-support": "0.5.19",
|
||||
"webpack": "^4",
|
||||
"webpack-cli": "^3"
|
||||
}
|
||||
}
|
11
packages/universe/src/index.js
Normal file
11
packages/universe/src/index.js
Normal file
|
@ -0,0 +1,11 @@
|
|||
import {universes} from './state';
|
||||
|
||||
export * from './state';
|
||||
|
||||
export default {
|
||||
hooks: {
|
||||
'@latus/redux/slices': () => ({
|
||||
universes,
|
||||
}),
|
||||
},
|
||||
};
|
2
packages/universe/src/state/index.js
Normal file
2
packages/universe/src/state/index.js
Normal file
|
@ -0,0 +1,2 @@
|
|||
export * from './universes';
|
||||
export {default as universes} from './universes';
|
46
packages/universe/src/state/universes.js
Normal file
46
packages/universe/src/state/universes.js
Normal file
|
@ -0,0 +1,46 @@
|
|||
import {
|
||||
createSelector,
|
||||
createSlice,
|
||||
} from '@latus/redux';
|
||||
|
||||
export const universesSelector = (state) => state.universes;
|
||||
|
||||
export const universesByLocalitySelector = createSelector(
|
||||
[universesSelector, (_, isLocal) => isLocal],
|
||||
(universes, isLocal_) => Object.fromEntries(
|
||||
Object.entries(universes).filter(([, {address}]) => !address === isLocal_),
|
||||
),
|
||||
);
|
||||
export const universeSelector = createSelector(
|
||||
[universesSelector, (_, uuid) => uuid],
|
||||
(universes, uuid) => uuid && universes[uuid],
|
||||
);
|
||||
|
||||
const slice = createSlice({
|
||||
name: 'humus/universes',
|
||||
initialState: {
|
||||
'2543c30e-3749-4c08-a0b2-c50137a39c4a': {
|
||||
address: '',
|
||||
isOnline: true,
|
||||
title: "cha0s's universe",
|
||||
},
|
||||
'c41ddaac-89c2-46a4-b3e5-1d634a1a7c36': {
|
||||
address: 'humus.cha0s.io:32340',
|
||||
isOnline: false,
|
||||
title: 'random universe',
|
||||
},
|
||||
},
|
||||
/* eslint-disable no-param-reassign */
|
||||
extraReducers: {
|
||||
},
|
||||
reducers: {
|
||||
},
|
||||
/* eslint-enable no-param-reassign */
|
||||
});
|
||||
|
||||
slice.reducer.subscription = slice.reducer;
|
||||
|
||||
// export const {
|
||||
// } = slice.actions;
|
||||
|
||||
export default slice.reducer;
|
9
packages/universe/test/exists.js
Normal file
9
packages/universe/test/exists.js
Normal file
|
@ -0,0 +1,9 @@
|
|||
import {expect} from 'chai';
|
||||
|
||||
const {name} = require('../package.json');
|
||||
|
||||
describe(name, () => {
|
||||
it('exists', () => {
|
||||
expect(true).to.be.true;
|
||||
})
|
||||
});
|
3
packages/universe/webpack.config.js
Normal file
3
packages/universe/webpack.config.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
const neutrino = require('neutrino');
|
||||
|
||||
module.exports = neutrino(require(`${__dirname}/.neutrinorc`)).webpack();
|
6300
packages/universe/yarn.lock
Normal file
6300
packages/universe/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user