split stylesheet from js, style selection and fix all lsint errors

This commit is contained in:
HF 2020-05-10 10:43:43 +02:00
parent 358b5a3ce0
commit fb167337bb
76 changed files with 2910 additions and 1090 deletions

View File

@ -145,6 +145,10 @@ If you want to add a new canvas, be sure that you additionally create `public/lo
The default configuration values can be seen in `src/core/config.js` and for the canvases in `src/core/constats.js` The default configuration values can be seen in `src/core/config.js` and for the canvases in `src/core/constats.js`
#### Styles
To add more css styles, create a new css file in `src/styles` based on `src/styles/default.css` and add it to the FILES array in `tools/minifyCss.js`
### Running ### Running
1. Make sure that mysql and redis are running 1. Make sure that mysql and redis are running
@ -206,7 +210,7 @@ After=network.target mysql.service redis.service
### Development ### Development
Run `npm run lint:src` to check for code errors and warnings or `npm run lint -- ./your/file.js` to check a single file. Run `npm run lint:src` to check for code errors and warnings or `npm run lint -- ./your/file.js` to check a single file.
We have enough warnings already, just don't produce too many additional ones. Please do not produce too many additional warnings.
You can use `npm run babel-node ./your/script.js` to execute a script with local babel. You can use `npm run babel-node ./your/script.js` to execute a script with local babel.

View File

@ -1,25 +1,22 @@
#!/bin/bash #!/bin/bash
# This hook builds pixelplanet after a push, and deploys it # This hook builds pixelplanet after a push, and deploys it
# If it is the production branch, it will deploy it on the life system, and other branch will get deployed to the dev-canvas (a second canvas that is running on the server) # If it is the production branch, it will deploy it on the life system, and other branch will get deployed to the dev-canvas (a second canvas that is running on the server)
# canvases.json, proxies.json and ecosystem.yml are already in the terget directories
# Update messages will get sent via the Webhooks to Discord # Update messages will get sent via the Webhooks to Discord
# #
# To set up a server to use this, you have to go through the building steps manually first. # To set up a server to use this, you have to go through the building steps manually first.
# This hook just builds the canvas, it does not install new yarn/npm packages if needed. So this has to be done manually first # This hook just builds the canvas, it does not install new packages if needed. So this has to be done manually first
# Also keep in mind that running a dev-canvas and a life canvas independently together on one server needs two redis installations.
# tl;dr: Don't just copy that script, try to know how that setup works first
# #
#discord webhook for dev canvas #discord webhook for dev canvas
WEBHOOK='https://discordapp.com/api/webhooks/' WEBHOOK='https://discordapp.com/api/webhooks/5440815510.....'
#discord webhook for production canvas #discord webhook for production canvas
PWEBHOOK='https://discordapp.com/api/webhooks/' PWEBHOOK='https://discordapp.com/api/webhooks/54413213......'
#folder for building the canvas (the git repository will get checkout there and the canvas will get buil thtere) #folder for building the canvas (the git repository will get checkout there and the canvas will get buil thtere)
BUILDDIR="pixelplanet-build" BUILDDIR="/home/pixelpla/pixelplanet-build"
#folder for dev canvas #folder for dev canvas
DEVFOLDER="pixelplanet-dev" DEVFOLDER="/home/pixelpla/pixelplanet-dev"
#folder for production canvas #folder for production canvas
PFOLDER="pixelplanet" PFOLDER="/home/pixelpla/pixelplanet"
#proxies.json path
PROXYFILE="/proxies.json"
while read oldrev newrev refname while read oldrev newrev refname
do do
@ -33,35 +30,40 @@ do
COMMITS=`echo "$COMMITS" | sed ':a;N;$!ba;s/\n/\\\n/g'` COMMITS=`echo "$COMMITS" | sed ':a;N;$!ba;s/\n/\\\n/g'`
echo "---BUILDING pixelplanet---" echo "---BUILDING pixelplanet---"
cd "$BUILDDIR" cd "$BUILDDIR"
cp "$PROXYFILE" ./src/ npm run build
yarn run build --release
echo "---RESTARTING CANVAS---" echo "---RESTARTING CANVAS---"
cp -r build/* "${PFOLDER}/" cp -r build/*.js "${PFOLDER}/"
cp -r build/public "${PFOLDER}/"
cp -r build/package.json "${PFOLDER}/"
cp -r build/assets.json "${PFOLDER}/"
cp -r build/styleassets.json "${PFOLDER}/"
mkdir -p "${PFOLDER}/log"
#cp ecosystem-production.yml "${PFOLDER}/ecosystem.yml" #cp ecosystem-production.yml "${PFOLDER}/ecosystem.yml"
cd "$PFOLDER" cd "$PFOLDER"
pm2 stop web pm2 stop web
pm2 start ecosystem.yml pm2 start ecosystem.yml
#make backup
tar -cvJf /backup/pixelplanet-src/pixelplanet-src-`date +%Y%m%d`.tar.xz --exclude=node_modules --exclude=.git -C "${BUILDDIR}/.." "pixelplanet-build"
#send update message to discord
curl -H "Content-Type: application/json" --data-binary '{ "username": "PixelPlanet Server", "avatar_url": "https://pixelplanet.fun/favicon.ico", "content": "...Done", "embeds": [{"title": "New Commits", "url": "https://pixelplanet.fun", "description": "'"$COMMITS"'", "color": 15258703}] }' "$PWEBHOOK" curl -H "Content-Type: application/json" --data-binary '{ "username": "PixelPlanet Server", "avatar_url": "https://pixelplanet.fun/favicon.ico", "content": "...Done", "embeds": [{"title": "New Commits", "url": "https://pixelplanet.fun", "description": "'"$COMMITS"'", "color": 15258703}] }' "$PWEBHOOK"
else else
echo "---UPDATING REPO ON DEV SERVER---" echo "---UPDATING REPO ON DEV SERVER---"
pm2 stop web-dev pm2 stop web-dev
GIT_WORK_TREE="$BUILDDIR" GIT_DIR="${BUILDDIR}/.git" git fetch --all GIT_WORK_TREE="$BUILDDIR" GIT_DIR="${BUILDDIR}/.git" git fetch --all
GIT_WORK_TREE="$BUILDDIR" GIT_DIR="${BUILDDIR}/.git" git reset --hard "origin/$branch" GIT_WORK_TREE="$BUILDDIR" GIT_DIR="${BUILDDIR}/.git" git reset --hard "origin/$branch"
curl -H "Content-Type: application/json" --data-binary '{ "username": "PixelPlanet Server", "avatar_url": "https://pixelplanet.fun/favicon.ico", "content": "Restarting pixelplanet development canvas for update..." }' "$WEBHOOK" #curl -H "Content-Type: application/json" --data-binary '{ "username": "PixelPlanet Server", "avatar_url": "https://pixelplanet.fun/favicon.ico", "content": "Restarting pixelplanet development canvas for update..." }' "$WEBHOOK"
COMMITS=`git log --pretty=format:'- %s%b' $newrev ^$oldrev` COMMITS=`git log --pretty=format:'- %s%b' $newrev ^$oldrev`
COMMITS=`echo "$COMMITS" | sed ':a;N;$!ba;s/\n/\\\n/g'` COMMITS=`echo "$COMMITS" | sed ':a;N;$!ba;s/\n/\\\n/g'`
echo "---BUILDING pixelplanet---" echo "---BUILDING pixelplanet---"
cd "$BUILDDIR" cd "$BUILDDIR"
cp "$PROXYFILE" ./src/ nice -n 19 npm run build
nice -n 19 yarn run build --release
echo "---RESTARTING CANVAS---" echo "---RESTARTING CANVAS---"
cp -r build/* "${DEVFOLDER}/" cp -r build/*.js "${DEVFOLDER}/"
cp -r build/public "${DEVFOLDER}/"
cp -r build/package.json "${DEVFOLDER}/"
cp -r build/assets.json "${DEVFOLDER}/"
cp -r build/styleassets.json "${DEVFOLDER}/"
mkdir -p "${PFOLDER}/log"
#cp ecosystem-dev.yml "${DEVFOLDER}/ecosystem.yml" #cp ecosystem-dev.yml "${DEVFOLDER}/ecosystem.yml"
cd "$DEVFOLDER" cd "$DEVFOLDER"
pm2 start ecosystem.yml pm2 start ecosystem.yml
curl -H "Content-Type: application/json" --data-binary '{ "username": "PixelPlanet Server", "avatar_url": "https://pixelplanet.fun/favicon.ico", "content": "...Done\nhttp://dev.pixelplanet.fun is now on branch '"$branch"'", "embeds": [{"title": "New Commits", "url": "https://pixelplanet.fun", "description": "'"$COMMITS"'", "color": 15258703}] }' "$WEBHOOK" #curl -H "Content-Type: application/json" --data-binary '{ "username": "PixelPlanet Server", "avatar_url": "https://pixelplanet.fun/favicon.ico", "content": "...Done\nhttp://dev.pixelplanet.fun is now on branch '"$branch"'", "embeds": [{"title": "New Commits", "url": "https://pixelplanet.fun", "description": "'"$COMMITS"'", "color": 15258703}] }' "$WEBHOOK"
fi fi
done done

17
package-lock.json generated
View File

@ -4210,6 +4210,23 @@
} }
} }
}, },
"clean-css": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.3.tgz",
"integrity": "sha512-VcMWDN54ZN/DS+g58HYL5/n4Zrqe8vHJpGA8KdgUXFU4fuP/aHNw8eld9SyEIyabIMJX/0RaY/fplOo5hYLSFA==",
"dev": true,
"requires": {
"source-map": "~0.6.0"
},
"dependencies": {
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true
}
}
},
"cli-boxes": { "cli-boxes": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-1.0.0.tgz", "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-1.0.0.tgz",

View File

@ -112,6 +112,7 @@
"@babel/preset-flow": "^7.9.0", "@babel/preset-flow": "^7.9.0",
"@babel/preset-react": "^7.9.4", "@babel/preset-react": "^7.9.4",
"@babel/preset-typescript": "^7.9.0", "@babel/preset-typescript": "^7.9.0",
"clean-css": "^4.2.3",
"assets-webpack-plugin": "^3.9.12", "assets-webpack-plugin": "^3.9.12",
"babel-eslint": "^10.1.0", "babel-eslint": "^10.1.0",
"babel-loader": "^8.1.0", "babel-loader": "^8.1.0",

View File

@ -89,6 +89,13 @@ export function toggleOpenPalette(): Action {
}; };
} }
export function selectStyle(style: string): Action {
return {
type: 'SELECT_STYLE',
style,
};
}
export function toggleOpenMenu(): Action { export function toggleOpenMenu(): Action {
return { return {
type: 'TOGGLE_OPEN_MENU', type: 'TOGGLE_OPEN_MENU',
@ -581,11 +588,10 @@ export function initTimer(): ThunkAction {
}; };
} }
export function showModal(modalType: string, modalProps: Object = {}): Action { export function showModal(modalType: string): Action {
return { return {
type: 'SHOW_MODAL', type: 'SHOW_MODAL',
modalType, modalType,
modalProps,
}; };
} }

View File

@ -25,6 +25,7 @@ export type Action =
| { type: 'TOGGLE_LIGHT_GRID' } | { type: 'TOGGLE_LIGHT_GRID' }
| { type: 'TOGGLE_OPEN_MENU' } | { type: 'TOGGLE_OPEN_MENU' }
| { type: 'TOGGLE_HISTORICAL_VIEW' } | { type: 'TOGGLE_HISTORICAL_VIEW' }
| { type: 'SELECT_STYLE', style: string }
| { type: 'SET_NOTIFICATION', notification: string } | { type: 'SET_NOTIFICATION', notification: string }
| { type: 'UNSET_NOTIFICATION' } | { type: 'UNSET_NOTIFICATION' }
| { type: 'SET_PLACE_ALLOWED', placeAllowed: boolean } | { type: 'SET_PLACE_ALLOWED', placeAllowed: boolean }
@ -78,15 +79,10 @@ export type Action =
| { type: 'SET_MINECRAFT_NAME', minecraftname: string } | { type: 'SET_MINECRAFT_NAME', minecraftname: string }
| { type: 'SET_MAILREG', mailreg: boolean } | { type: 'SET_MAILREG', mailreg: boolean }
| { type: 'REM_FROM_MESSAGES', message: string } | { type: 'REM_FROM_MESSAGES', message: string }
| { type: 'SHOW_MODAL', modalType: string, modalProps: obj } | { type: 'SHOW_MODAL', modalType: string }
| { type: 'HIDE_MODAL' } | { type: 'HIDE_MODAL' }
| { type: 'RELOAD_URL' } | { type: 'RELOAD_URL' }
| { type: 'SET_HISTORICAL_TIME', date: string, time: string } | { type: 'SET_HISTORICAL_TIME', date: string, time: string }
| { type: 'ON_VIEW_FINISH_CHANGE' }; | { type: 'ON_VIEW_FINISH_CHANGE' };
export type PromiseAction = Promise<Action>; export type PromiseAction = Promise<Action>;
export type Dispatch = (action: Action
| ThunkAction
| PromiseAction
| Array<Action>) => any;
export type GetState = () => State; export type GetState = () => State;
export type ThunkAction = (dispatch: Dispatch, getState: GetState) => any;

View File

@ -1,11 +1,8 @@
/* @flow */ /* @flow */
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import fetch from 'isomorphic-fetch'; // TODO put in the beggining with webpack! import fetch from 'isomorphic-fetch'; // TODO put in the beggining with webpack!
import './components/font.css'; import './styles/font.css';
// import initAds, { requestAds } from './ui/ads'; // import initAds, { requestAds } from './ui/ads';
import onKeyPress from './controls/keypress'; import onKeyPress from './controls/keypress';
@ -24,7 +21,7 @@ import {
import store from './ui/store'; import store from './ui/store';
import App from './components/App'; import renderApp from './components/App';
import { initRenderer, getRenderer } from './ui/renderer'; import { initRenderer, getRenderer } from './ui/renderer';
import ProtocolClient from './socket/ProtocolClient'; import ProtocolClient from './socket/ProtocolClient';
@ -97,12 +94,7 @@ function init() {
init(); init();
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
ReactDOM.render( renderApp(document.getElementById('app'));
<Provider store={store}>
<App />
</Provider>,
document.getElementById('app'),
);
document.addEventListener('keydown', onKeyPress, false); document.addEventListener('keydown', onKeyPress, false);

View File

@ -4,8 +4,12 @@
*/ */
import React from 'react'; import React from 'react';
import { Provider } from 'react-redux';
import ReactDOM from 'react-dom';
import { IconContext } from 'react-icons'; import { IconContext } from 'react-icons';
import store from '../ui/store';
import CoordinatesBox from './CoordinatesBox'; import CoordinatesBox from './CoordinatesBox';
import CanvasSwitchButton from './CanvasSwitchButton'; import CanvasSwitchButton from './CanvasSwitchButton';
import OnlineBox from './OnlineBox'; import OnlineBox from './OnlineBox';
@ -17,12 +21,8 @@ import ReCaptcha from './ReCaptcha';
import ExpandMenuButton from './ExpandMenuButton'; import ExpandMenuButton from './ExpandMenuButton';
import ModalRoot from './ModalRoot'; import ModalRoot from './ModalRoot';
import baseCss from './base.tcss';
const App = () => ( const App = () => (
<div> <div>
{/* eslint-disable-next-line react/no-danger */}
<style dangerouslySetInnerHTML={{ __html: baseCss }} />
<div id="outstreamContainer" /> <div id="outstreamContainer" />
<ReCaptcha /> <ReCaptcha />
<IconContext.Provider value={{ style: { verticalAlign: 'middle' } }}> <IconContext.Provider value={{ style: { verticalAlign: 'middle' } }}>
@ -39,4 +39,13 @@ const App = () => (
</div> </div>
); );
export default App; function renderApp(domParent) {
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
domParent,
);
}
export default renderApp;

View File

@ -10,38 +10,6 @@ import { THREE_CANVAS_HEIGHT } from '../core/constants';
import { selectCanvas } from '../actions'; import { selectCanvas } from '../actions';
const textStyle = {
color: 'hsla(218, 5%, 47%, .6)',
fontSize: 14,
fontWeight: 500,
padding: 0,
display: 'inline-block',
verticalAlign: 'middle',
marginTop: 3,
marginBottom: 3,
width: '75%',
};
const infoStyle = {
color: '#4f545c',
fontSize: 15,
fontWeight: 500,
position: 'relative',
textAlign: 'inherit',
float: 'none',
margin: 0,
padding: 0,
};
const titleStyle = {
color: '#4f545c',
overflow: 'hidden',
wordWrap: 'break-word',
lineHeight: '26px',
fontSize: 16,
fontWeight: 'bold',
};
const buttonStyle = { const buttonStyle = {
marginTop: 8, marginTop: 8,
marginBottom: 8, marginBottom: 8,
@ -71,29 +39,29 @@ const CanvasItem = ({ canvasId, canvas, changeCanvas }) => (
alt="preview" alt="preview"
src={`/preview${canvasId}.png`} src={`/preview${canvasId}.png`}
/> />
<p style={textStyle}> <p className="modalcvtext">
<span style={titleStyle}>{canvas.title}</span><br /> <span className="modaltitle">{canvas.title}</span><br />
<span style={infoStyle}>{canvas.desc}</span><br /> <span className="modalinfo">{canvas.desc}</span><br />
Cooldown: Cooldown:
&nbsp; &nbsp;
<span style={infoStyle}> <span className="modalinfo">
{(canvas.bcd !== canvas.pcd) {(canvas.bcd !== canvas.pcd)
? <span> {canvas.bcd / 1000}s / {canvas.pcd / 1000}s</span> ? <span> {canvas.bcd / 1000}s / {canvas.pcd / 1000}s</span>
: <span> {canvas.bcd / 1000}s</span>} : <span> {canvas.bcd / 1000}s</span>}
</span><br /> </span><br />
Stacking till Stacking till
<span style={infoStyle}> {canvas.cds / 1000}s</span><br /> <span className="modalinfo"> {canvas.cds / 1000}s</span><br />
Ranked: Ranked:
&nbsp; &nbsp;
<span style={infoStyle}>{(canvas.ranked) ? 'Yes' : 'No'}</span><br /> <span className="modalinfo">{(canvas.ranked) ? 'Yes' : 'No'}</span><br />
{(canvas.req !== -1) ? <span>Requirements:<br /></span> : null} {(canvas.req !== -1) ? <span>Requirements:<br /></span> : null}
<span style={infoStyle}> <span className="modalinfo">
{(canvas.req !== -1) ? <span>User Account </span> : null} {(canvas.req !== -1) ? <span>User Account </span> : null}
{(canvas.req > 0) ? <span> and {canvas.req} Pixels set</span> : null} {(canvas.req > 0) ? <span> and {canvas.req} Pixels set</span> : null}
</span> </span>
{(canvas.req !== -1) ? <br /> : null} {(canvas.req !== -1) ? <br /> : null}
Dimensions: Dimensions:
<span style={infoStyle}> {canvas.size} x {canvas.size} <span className="modalinfo"> {canvas.size} x {canvas.size}
{(canvas.v) {(canvas.v)
? <span> x {THREE_CANVAS_HEIGHT} Voxels</span> ? <span> x {THREE_CANVAS_HEIGHT} Voxels</span>
: <span> Pixels</span>} : <span> Pixels</span>}

View File

@ -15,19 +15,6 @@ import CanvasItem from './CanvasItem';
import type { State } from '../reducers'; import type { State } from '../reducers';
const textStyle = {
color: 'hsla(218, 5%, 47%, .6)',
fontSize: 14,
fontWeight: 500,
position: 'relative',
textAlign: 'inherit',
float: 'none',
margin: 0,
padding: 0,
lineHeight: 'normal',
};
const CanvasSelectModal = ({ canvases }) => ( const CanvasSelectModal = ({ canvases }) => (
<Modal title="Canvas Selection"> <Modal title="Canvas Selection">
<p style={{ <p style={{
@ -37,7 +24,7 @@ const CanvasSelectModal = ({ canvases }) => (
paddingTop: 20, paddingTop: 20,
}} }}
> >
<p style={textStyle}> <p className="modaltext">
Select the canvas you want to use. Select the canvas you want to use.
Every canvas is unique and has different palettes, Every canvas is unique and has different palettes,
cooldown and requirements. cooldown and requirements.

View File

@ -9,24 +9,19 @@ import { FaGlobe } from 'react-icons/fa';
import { showCanvasSelectionModal } from '../actions'; import { showCanvasSelectionModal } from '../actions';
import type { State } from '../reducers';
const CanvasSwitchButton = ({ open }) => ( const CanvasSwitchButton = ({ open }) => (
<div <div
id="canvasbutton" id="canvasbutton"
className="actionbuttons" className="actionbuttons"
onClick={open} onClick={open}
role="button"
tabIndex={-1}
> >
<FaGlobe /> <FaGlobe />
</div> </div>
); );
function mapStateToProps(state: State) {
const { canvasId } = state.canvas;
return { canvasId };
}
function mapDispatchToProps(dispatch) { function mapDispatchToProps(dispatch) {
return { return {
open() { open() {

View File

@ -5,7 +5,7 @@
import React from 'react'; import React from 'react';
import { import {
validateName, validateEMail, validatePassword, parseAPIresponse, validateEMail, validatePassword, parseAPIresponse,
} from '../utils/validation'; } from '../utils/validation';
function validate(email, password) { function validate(email, password) {
@ -19,7 +19,7 @@ function validate(email, password) {
return errors; return errors;
} }
async function submit_mailchange(email, password) { async function submitMailchange(email, password) {
const body = JSON.stringify({ const body = JSON.stringify({
email, email,
password, password,
@ -63,7 +63,7 @@ class ChangeMail extends React.Component {
if (errors.length > 0) return; if (errors.length > 0) return;
this.setState({ submitting: true }); this.setState({ submitting: true });
const { errors: resperrors } = await submit_mailchange(email, password); const { errors: resperrors } = await submitMailchange(email, password);
if (resperrors) { if (resperrors) {
this.setState({ this.setState({
errors: resperrors, errors: resperrors,
@ -77,15 +77,24 @@ class ChangeMail extends React.Component {
} }
render() { render() {
if (this.state.success) { const { success } = this.state;
const { done } = this.props;
if (success) {
return ( return (
<div className="inarea"> <div className="inarea">
<p className="modalmessage">Changed Mail successfully. We sent you a verification mail, please verify your new mail adress.</p> <p
<button type="button" onClick={this.props.done}>Close</button> className="modalmessage"
>
Changed Mail successfully.
We sent you a verification mail, please verify your new mail adress.
</p>
<button type="button" onClick={done}>Close</button>
</div> </div>
); );
} }
const { errors } = this.state; const {
errors, password, email, submitting,
} = this.state;
return ( return (
<div className="inarea"> <div className="inarea">
<form onSubmit={this.handleSubmit}> <form onSubmit={this.handleSubmit}>
@ -93,21 +102,23 @@ class ChangeMail extends React.Component {
<p key={error} className="errormessage">Error: {error}</p> <p key={error} className="errormessage">Error: {error}</p>
))} ))}
<input <input
value={this.state.password} value={password}
onChange={(evt) => this.setState({ password: evt.target.value })} onChange={(evt) => this.setState({ password: evt.target.value })}
type="password" type="password"
placeholder="Password" placeholder="Password"
/> />
<br /> <br />
<input <input
value={this.state.email} value={email}
onChange={(evt) => this.setState({ email: evt.target.value })} onChange={(evt) => this.setState({ email: evt.target.value })}
type="text" type="text"
placeholder="New Mail" placeholder="New Mail"
/> />
<br /> <br />
<button type="submit">{(this.state.submitting) ? '...' : 'Save'}</button> <button type="submit">
<button type="button" onClick={this.props.done}>Cancel</button> {(submitting) ? '...' : 'Save'}
</button>
<button type="button" onClick={done}>Cancel</button>
</form> </form>
</div> </div>
); );

View File

@ -16,7 +16,7 @@ function validate(name) {
return errors; return errors;
} }
async function submit_namechange(name) { async function submitNamechange(name) {
const body = JSON.stringify({ const body = JSON.stringify({
name, name,
}); });
@ -57,7 +57,7 @@ class ChangeName extends React.Component {
if (errors.length > 0) return; if (errors.length > 0) return;
this.setState({ submitting: true }); this.setState({ submitting: true });
const { errors: resperrors } = await submit_namechange(name); const { errors: resperrors } = await submitNamechange(name);
if (resperrors) { if (resperrors) {
this.setState({ this.setState({
errors: resperrors, errors: resperrors,
@ -65,12 +65,14 @@ class ChangeName extends React.Component {
}); });
return; return;
} }
this.props.set_name(name); const { setName, done } = this.props;
this.props.done(); setName(name);
done();
} }
render() { render() {
const { errors } = this.state; const { errors, name, submitting } = this.state;
const { done } = this.props;
return ( return (
<div className="inarea"> <div className="inarea">
<form onSubmit={this.handleSubmit}> <form onSubmit={this.handleSubmit}>
@ -78,14 +80,16 @@ class ChangeName extends React.Component {
<p key={error} className="errormessage">Error: {error}</p> <p key={error} className="errormessage">Error: {error}</p>
))} ))}
<input <input
value={this.state.name} value={name}
onChange={(evt) => this.setState({ name: evt.target.value })} onChange={(evt) => this.setState({ name: evt.target.value })}
type="text" type="text"
placeholder="New Username" placeholder="New Username"
/> />
<br /> <br />
<button type="submit">{(this.state.submitting) ? '...' : 'Save'}</button> <button type="submit">
<button type="button" onClick={this.props.done}>Cancel</button> {(submitting) ? '...' : 'Save'}
</button>
<button type="button" onClick={done}>Cancel</button>
</form> </form>
</div> </div>
); );

View File

@ -6,27 +6,27 @@
import React from 'react'; import React from 'react';
import { validatePassword, parseAPIresponse } from '../utils/validation'; import { validatePassword, parseAPIresponse } from '../utils/validation';
function validate(mailreg, password, new_password, confirm_password) { function validate(mailreg, password, newPassword, confirmPassword) {
const errors = []; const errors = [];
if (mailreg) { if (mailreg) {
const oldpasserror = validatePassword(password); const oldpasserror = validatePassword(password);
if (oldpasserror) errors.push(oldpasserror); if (oldpasserror) errors.push(oldpasserror);
} }
if (new_password != confirm_password) { if (newPassword !== confirmPassword) {
errors.push('Passwords do not match.'); errors.push('Passwords do not match.');
return errors; return errors;
} }
const passerror = validatePassword(new_password); const passerror = validatePassword(newPassword);
if (passerror) errors.push(passerror); if (passerror) errors.push(passerror);
return errors; return errors;
} }
async function submit_passwordchange(new_password, password) { async function submitPasswordChange(newPassword, password) {
const body = JSON.stringify({ const body = JSON.stringify({
password, password,
new_password, newPassword,
}); });
const response = await fetch('./api/auth/change_passwd', { const response = await fetch('./api/auth/change_passwd', {
method: 'POST', method: 'POST',
@ -46,8 +46,8 @@ class ChangePassword extends React.Component {
super(); super();
this.state = { this.state = {
password: '', password: '',
new_password: '', newPassword: '',
confirm_password: '', confirmPassword: '',
success: false, success: false,
submitting: false, submitting: false,
@ -61,17 +61,26 @@ class ChangePassword extends React.Component {
e.preventDefault(); e.preventDefault();
const { const {
password, new_password, confirm_password, submitting, password, newPassword, confirmPassword, submitting,
} = this.state; } = this.state;
if (submitting) return; if (submitting) return;
const errors = validate(this.props.mailreg, password, new_password, confirm_password); const { mailreg } = this.props;
const errors = validate(
mailreg,
password,
newPassword,
confirmPassword,
);
this.setState({ errors }); this.setState({ errors });
if (errors.length > 0) return; if (errors.length > 0) return;
this.setState({ submitting: true }); this.setState({ submitting: true });
const { errors: resperrors } = await submit_passwordchange(new_password, password); const { errors: resperrors } = await submitPasswordChange(
newPassword,
password,
);
if (resperrors) { if (resperrors) {
this.setState({ this.setState({
errors: resperrors, errors: resperrors,
@ -85,25 +94,34 @@ class ChangePassword extends React.Component {
} }
render() { render() {
if (this.state.success) { const { success } = this.state;
if (success) {
const { done } = this.props;
return ( return (
<div className="inarea"> <div className="inarea">
<p className="modalmessage">Changed Password successfully.</p> <p className="modalmessage">Changed Password successfully.</p>
<button type="button" onClick={this.props.done}>Close</button> <button type="button" onClick={done}>Close</button>
</div> </div>
); );
} }
const { errors } = this.state; const {
errors,
password,
newPassword,
confirmPassword,
submitting,
} = this.state;
const { cancel, mailreg } = this.props;
return ( return (
<div className="inarea"> <div className="inarea">
<form onSubmit={this.handleSubmit}> <form onSubmit={this.handleSubmit}>
{errors.map((error) => ( {errors.map((error) => (
<p key={error} className="errormessage">Error: {error}</p> <p key={error} className="errormessage">Error: {error}</p>
))} ))}
{(this.props.mailreg) {(mailreg)
&& ( && (
<input <input
value={this.state.password} value={password}
onChange={(evt) => this.setState({ password: evt.target.value })} onChange={(evt) => this.setState({ password: evt.target.value })}
type="password" type="password"
placeholder="Old Password" placeholder="Old Password"
@ -111,21 +129,25 @@ class ChangePassword extends React.Component {
)} )}
<br /> <br />
<input <input
value={this.state.new_password} value={newPassword}
onChange={(evt) => this.setState({ new_password: evt.target.value })} onChange={(evt) => this.setState({ newPassword: evt.target.value })}
type="password" type="password"
placeholder="New Password" placeholder="New Password"
/> />
<br /> <br />
<input <input
value={this.state.confirm_password} value={confirmPassword}
onChange={(evt) => this.setState({ confirm_password: evt.target.value })} onChange={(evt) => this.setState({
confirmPassword: evt.target.value,
})}
type="password" type="password"
placeholder="Confirm New Password" placeholder="Confirm New Password"
/> />
<br /> <br />
<button type="submit">{(this.state.submitting) ? '...' : 'Save'}</button> <button type="submit">
<button type="button" onClick={this.props.cancel}>Cancel</button> {(submitting) ? '...' : 'Save'}
</button>
<button type="button" onClick={cancel}>Cancel</button>
</form> </form>
</div> </div>
); );

View File

@ -9,23 +9,10 @@ import Modal from './Modal';
import Chat from './Chat'; import Chat from './Chat';
const textStyle = {
color: 'hsla(218, 5%, 47%, .6)',
fontSize: 14,
fontWeight: 500,
position: 'relative',
textAlign: 'inherit',
float: 'none',
margin: 0,
padding: 0,
lineHeight: 'normal',
};
const ChatModal = () => ( const ChatModal = () => (
<Modal title="Chat"> <Modal title="Chat">
<p style={{ textAlign: 'center' }}> <p style={{ textAlign: 'center' }}>
<p style={textStyle}>Chat with other people here</p> <p className="modaltext">Chat with other people here</p>
</p> </p>
<div className="inarea" style={{ height: '65%' }}> <div className="inarea" style={{ height: '65%' }}>
<Chat /> <Chat />

View File

@ -12,28 +12,6 @@ import type { State } from '../reducers';
import printGIMPPalette from '../core/exportGPL'; import printGIMPPalette from '../core/exportGPL';
import { copyCanvasToClipboard } from '../utils/clipboard'; import { copyCanvasToClipboard } from '../utils/clipboard';
const titleStyle = {
color: '#4f545c',
marginLeft: 0,
marginRight: 10,
overflow: 'hidden',
wordWrap: 'break-word',
lineHeight: '24px',
fontSize: 16,
fontWeight: 500,
marginTop: 4,
marginBottom: 0,
};
const textStyle = {
color: 'hsla(218, 5%, 47%, .6)',
fontSize: 14,
fontWeight: 500,
position: 'relative',
textAlign: 'inherit',
float: 'none',
lineHeight: 'normal',
};
function downloadOutput() { function downloadOutput() {
const output = document.getElementById('imgoutput'); const output = document.getElementById('imgoutput');
@ -284,7 +262,7 @@ function Converter({
return ( return (
<p style={{ textAlign: 'center' }}> <p style={{ textAlign: 'center' }}>
<p style={textStyle}>Choose Canvas:&nbsp; <p className="modalcotext">Choose Canvas:&nbsp;
<select <select
onChange={(e) => { onChange={(e) => {
const sel = e.target; const sel = e.target;
@ -307,8 +285,8 @@ function Converter({
} }
</select> </select>
</p> </p>
<h3 style={titleStyle}>Palette Download</h3> <h3 className="modaltitle">Palette Download</h3>
<p style={textStyle}> <p className="modalcotext">
Palette for <a href="https://www.gimp.org">GIMP</a>:&nbsp; Palette for <a href="https://www.gimp.org">GIMP</a>:&nbsp;
<button <button
type="button" type="button"
@ -329,8 +307,8 @@ function Converter({
<p>Credit for the Palette of the Moon goes to <p>Credit for the Palette of the Moon goes to
<a href="https://twitter.com/starhousedev">starhouse</a>.</p> <a href="https://twitter.com/starhousedev">starhouse</a>.</p>
</p> </p>
<h3 style={titleStyle}>Image Converter</h3> <h3 className="modaltitle">Image Converter</h3>
<p style={textStyle}>Convert an image to canvas colors</p> <p className="modalcotext">Convert an image to canvas colors</p>
<input <input
type="file" type="file"
id="imgfile" id="imgfile"
@ -341,7 +319,7 @@ function Converter({
readFile(file, selectFile, setScaleData); readFile(file, selectFile, setScaleData);
}} }}
/> />
<p style={textStyle}>Choose Strategy:&nbsp; <p className="modalcotext">Choose Strategy:&nbsp;
<select <select
onChange={(e) => { onChange={(e) => {
const sel = e.target; const sel = e.target;
@ -368,7 +346,7 @@ function Converter({
} }
</select> </select>
</p> </p>
<p style={textStyle}>Choose Color Mode:&nbsp; <p className="modalcotext">Choose Color Mode:&nbsp;
<select <select
onChange={(e) => { onChange={(e) => {
const sel = e.target; const sel = e.target;
@ -395,7 +373,7 @@ function Converter({
} }
</select> </select>
</p> </p>
<p style={{ ...textStyle, fontHeight: 16 }}> <p style={{ fontHeight: 16 }} className="modalcotext">
<input <input
type="checkbox" type="checkbox"
checked={gridEnabled} checked={gridEnabled}
@ -418,7 +396,7 @@ function Converter({
display: 'inline-block', display: 'inline-block',
}} }}
> >
<p style={{ ...textStyle, fontHeight: 16 }}> <p style={{ fontHeight: 16 }} className="modalcotext">
<input <input
type="checkbox" type="checkbox"
checked={gridLight} checked={gridLight}
@ -431,7 +409,7 @@ function Converter({
/> />
Light Grid Light Grid
</p> </p>
<span style={textStyle}>Offset X:&nbsp; <span className="modalcotext">Offset X:&nbsp;
<input <input
type="number" type="number"
step="1" step="1"
@ -445,9 +423,9 @@ function Converter({
offsetX: e.target.value, offsetX: e.target.value,
}); });
}} }}
/>% />&nbsp;
</span> </span>
<span style={textStyle}>Offset Y:&nbsp; <span className="modalcotext">Offset Y:&nbsp;
<input <input
type="number" type="number"
step="1" step="1"
@ -461,12 +439,12 @@ function Converter({
offsetY: e.target.value, offsetY: e.target.value,
}); });
}} }}
/>% />
</span> </span>
</div> </div>
) )
: null} : null}
<p style={{ ...textStyle, fontHeight: 16 }}> <p style={{ fontHeight: 16 }} className="modalcotext">
<input <input
type="checkbox" type="checkbox"
checked={scalingEnabled} checked={scalingEnabled}
@ -489,7 +467,7 @@ function Converter({
display: 'inline-block', display: 'inline-block',
}} }}
> >
<span style={textStyle}>Width:&nbsp; <span className="modalcotext">Width:&nbsp;
<input <input
type="number" type="number"
step="1" step="1"
@ -504,6 +482,7 @@ function Converter({
if (selectedScaleKeepRatio && selectedFile) { if (selectedScaleKeepRatio && selectedFile) {
const ratio = selectedFile.width / selectedFile.height; const ratio = selectedFile.width / selectedFile.height;
const newHeight = Math.round(newWidth / ratio); const newHeight = Math.round(newWidth / ratio);
if (newHeight <= 0) return;
setScaleData({ setScaleData({
...scaleData, ...scaleData,
width: newWidth, width: newWidth,
@ -516,9 +495,9 @@ function Converter({
width: newWidth, width: newWidth,
}); });
}} }}
/>% />&nbsp;
</span> </span>
<span style={textStyle}>Height:&nbsp; <span className="modalcotext">Height:&nbsp;
<input <input
type="number" type="number"
step="1" step="1"
@ -533,6 +512,7 @@ function Converter({
if (selectedScaleKeepRatio && selectedFile) { if (selectedScaleKeepRatio && selectedFile) {
const ratio = selectedFile.width / selectedFile.height; const ratio = selectedFile.width / selectedFile.height;
const nuWidth = Math.round(ratio * nuHeight); const nuWidth = Math.round(ratio * nuHeight);
if (nuWidth <= 0) return;
setScaleData({ setScaleData({
...scaleData, ...scaleData,
width: nuWidth, width: nuWidth,
@ -545,9 +525,9 @@ function Converter({
height: nuHeight, height: nuHeight,
}); });
}} }}
/>% />
</span> </span>
<p style={{ ...textStyle, fontHeight: 16 }}> <p style={{ fontHeight: 16 }} className="modalcotext">
<input <input
type="checkbox" type="checkbox"
checked={selectedScaleKeepRatio} checked={selectedScaleKeepRatio}
@ -557,7 +537,7 @@ function Converter({
/> />
Keep Ratio Keep Ratio
</p> </p>
<p style={{ ...textStyle, fontHeight: 16 }}> <p style={{ fontHeight: 16 }} className="modalcotext">
<input <input
type="checkbox" type="checkbox"
checked={scalingAA} checked={scalingAA}

View File

@ -50,7 +50,7 @@
d="m 169.92806,224.8757 c 12.96191,0 174.83965,2.84395 174.83965,2.84395 1.031,45.06702 -0.18501,97.37311 -0.18501,144.3008 H 169.92691 c 8.5e-4,-48.64059 8.5e-4,-96.25484 8.5e-4,-147.14475 z m 443.64407,451.61505 c 0,0 -81.1844,-0.18967 -85.99494,-0.0279 -0.0349,0.0116 -0.0698,0.0302 -0.10705,0.0279 -0.19899,-0.0105 -0.14779,-0.021 0.10705,-0.0279 3.75627,-1.25326 -0.69004,-78.95949 -0.69004,-78.95949 -30.07804,0 -157.04976,0.33745 -181.70519,-0.35492 v 77.17678 c 0,0 -60.2026,0.13615 -86.31027,0.13615 V 450.98926 h 88.10464 V 377.78518 H 523.3194 v 69.88884 h 90.25273 z m 90.54596,-304.1119 H 527.47014 V 224.80121 h 176.64795 z" d="m 169.92806,224.8757 c 12.96191,0 174.83965,2.84395 174.83965,2.84395 1.031,45.06702 -0.18501,97.37311 -0.18501,144.3008 H 169.92691 c 8.5e-4,-48.64059 8.5e-4,-96.25484 8.5e-4,-147.14475 z m 443.64407,451.61505 c 0,0 -81.1844,-0.18967 -85.99494,-0.0279 -0.0349,0.0116 -0.0698,0.0302 -0.10705,0.0279 -0.19899,-0.0105 -0.14779,-0.021 0.10705,-0.0279 3.75627,-1.25326 -0.69004,-78.95949 -0.69004,-78.95949 -30.07804,0 -157.04976,0.33745 -181.70519,-0.35492 v 77.17678 c 0,0 -60.2026,0.13615 -86.31027,0.13615 V 450.98926 h 88.10464 V 377.78518 H 523.3194 v 69.88884 h 90.25273 z m 90.54596,-304.1119 H 527.47014 V 224.80121 h 176.64795 z"
id="path4" id="path4"
inkscape:connector-curvature="0" inkscape:connector-curvature="0"
style="clip-rule:evenodd;fill:#1a1a1a;fill-rule:evenodd;stroke-width:1.16365039" style="clip-rule:evenodd;fill:currentColor;fill-rule:evenodd;stroke-width:1.16365039"
sodipodi:nodetypes="ccccccccccccccccccccccccc" /> sodipodi:nodetypes="ccccccccccccccccccccccccc" />
</g> </g>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@ -15,7 +15,7 @@ function validate(password) {
return errors; return errors;
} }
async function submit_delete_account(password) { async function submitDeleteAccount(password) {
const body = JSON.stringify({ const body = JSON.stringify({
password, password,
}); });
@ -56,7 +56,7 @@ class DeleteAccount extends React.Component {
if (errors.length > 0) return; if (errors.length > 0) return;
this.setState({ submitting: true }); this.setState({ submitting: true });
const { errors: resperrors } = await submit_delete_account(password); const { errors: resperrors } = await submitDeleteAccount(password);
if (resperrors) { if (resperrors) {
this.setState({ this.setState({
errors: resperrors, errors: resperrors,
@ -64,11 +64,13 @@ class DeleteAccount extends React.Component {
}); });
return; return;
} }
this.props.set_name(null); const { setName } = this.props;
setName(null);
} }
render() { render() {
const { errors } = this.state; const { errors, password, submitting } = this.state;
const { done } = this.props;
return ( return (
<div className="inarea" style={{ backgroundColor: '#ff6666' }}> <div className="inarea" style={{ backgroundColor: '#ff6666' }}>
<form onSubmit={this.handleSubmit}> <form onSubmit={this.handleSubmit}>
@ -76,14 +78,16 @@ class DeleteAccount extends React.Component {
<p key={error} className="errormessage">Error: {error}</p> <p key={error} className="errormessage">Error: {error}</p>
))} ))}
<input <input
value={this.state.password} value={password}
onChange={(evt) => this.setState({ password: evt.target.value })} onChange={(evt) => this.setState({ password: evt.target.value })}
type="password" type="password"
placeholder="Password" placeholder="Password"
/> />
<br /> <br />
<button type="submit">{(this.state.submitting) ? '...' : 'Yes, Delete My Account!'}</button> <button type="submit">
<button type="button" onClick={this.props.done}>Cancel</button> {(submitting) ? '...' : 'Yes, Delete My Account!'}
</button>
<button type="button" onClick={done}>Cancel</button>
</form> </form>
</div> </div>
); );

View File

@ -1,5 +1,7 @@
/* /*
* espand menu / show other menu buttons * espand menu / show other menu buttons
*
* @flow
*/ */
import React from 'react'; import React from 'react';
@ -9,7 +11,13 @@ import { MdExpandMore, MdExpandLess } from 'react-icons/md';
import { toggleOpenMenu } from '../actions'; import { toggleOpenMenu } from '../actions';
const ExpandMenuButton = ({ menuOpen, expand }) => ( const ExpandMenuButton = ({ menuOpen, expand }) => (
<div id="menubutton" className="actionbuttons" onClick={expand}> <div
id="menubutton"
className="actionbuttons"
role="button"
tabIndex={-1}
onClick={expand}
>
{(menuOpen) ? <MdExpandLess /> : <MdExpandMore /> } {(menuOpen) ? <MdExpandLess /> : <MdExpandMore /> }
</div> </div>
); );

View File

@ -11,22 +11,10 @@ import Modal from './Modal';
import { showUserAreaModal } from '../actions'; import { showUserAreaModal } from '../actions';
import NewPasswordForm from './NewPasswordForm'; import NewPasswordForm from './NewPasswordForm';
const textStyle = {
color: 'hsla(218, 5%, 47%, .6)',
fontSize: 14,
fontWeight: 500,
position: 'relative',
textAlign: 'inherit',
float: 'none',
margin: 0,
paddingLeft: '5%',
lineHeight: 'normal',
};
const ForgotPasswordModal = ({ login }) => ( const ForgotPasswordModal = ({ login }) => (
<Modal title="Restore my Password"> <Modal title="Restore my Password">
<p style={{ paddingLeft: '5%', paddingRight: '5%' }}> <p style={{ paddingLeft: '5%', paddingRight: '5%' }}>
<p style={textStyle}> <p className="modaltext">
Enter your mail adress and we will send you a new password: Enter your mail adress and we will send you a new password:
</p><br /> </p><br />
<p style={{ textAlign: 'center' }}> <p style={{ textAlign: 'center' }}>

View File

@ -8,29 +8,40 @@ import React from 'react';
import ReactDOM from 'react-dom/server'; import ReactDOM from 'react-dom/server';
import Html from './Html'; import Html from './Html';
/* this will be set by webpack */
// eslint-disable-next-line import/no-unresolved
import assets from './assets.json'; import assets from './assets.json';
import { ASSET_SERVER } from '../core/config'; import { ASSET_SERVER } from '../core/config';
import globeCss from './globe.tcss'; import globeCss from '../styles/globe.css';
const Globe = () => ( const Globe = () => (
<div> <div>
<style dangerouslySetInnerHTML={{ __html: globeCss }} />
<div id="webgl" /> <div id="webgl" />
<div id="coorbox">(0, 0)</div> <div id="coorbox">(0, 0)</div>
<div id="info">Double click on globe to go back.</div> <div id="info">Double click on globe to go back.</div>
<div id="loading">Loading...</div> <div id="loading">Loading...</div>
</div> </div>
); );
const styles = [{
id: 'globe',
cssText: globeCss,
}];
const data = { const title = 'PixelPlanet.fun 3DGlobe';
title: 'PixelPlanet.fun 3DGlobe', const description = '3D globe of our canvas';
description: '3D globe of our canvas', const scripts = [
scripts: [ ASSET_SERVER + assets.globe.js,
ASSET_SERVER + assets.globe.js, ];
], const body = <Globe />;
body: <Globe />, const globeHtml = `<!doctype html>${ReactDOM.renderToStaticMarkup(
}; <Html
const globeHtml = `<!doctype html>${ReactDOM.renderToStaticMarkup(<Html {...data} />)}`; title={title}
description={description}
scripts={scripts}
body={body}
styles={styles}
/>,
)}`;
export default globeHtml; export default globeHtml;

View File

@ -15,6 +15,7 @@ import type { State } from '../reducers';
*/ */
function globe(canvasId, canvasIdent, canvasSize, view) { function globe(canvasId, canvasIdent, canvasSize, view) {
const [x, y] = view.map(Math.round); const [x, y] = view.map(Math.round);
// eslint-disable-next-line max-len
window.location.href = `globe#${canvasIdent},${canvasId},${canvasSize},${x},${y}`; window.location.href = `globe#${canvasIdent},${canvasId},${canvasSize},${x},${y}`;
} }
@ -22,12 +23,17 @@ function globe(canvasId, canvasIdent, canvasSize, view) {
const GlobeButton = ({ const GlobeButton = ({
canvasId, canvasIdent, canvasSize, view, canvasId, canvasIdent, canvasSize, view,
}) => ( }) => (
<div id="globebutton" className="actionbuttons" onClick={() => globe(canvasId, canvasIdent, canvasSize, view)}> <div
role="button"
tabIndex={-1}
id="globebutton"
className="actionbuttons"
onClick={() => globe(canvasId, canvasIdent, canvasSize, view)}
>
<Md3DRotation /> <Md3DRotation />
</div> </div>
); );
// TODO optimize
function mapStateToProps(state: State) { function mapStateToProps(state: State) {
const { const {
canvasId, canvasIdent, canvasSize, view, canvasId, canvasIdent, canvasSize, view,

View File

@ -11,16 +11,16 @@ import { toggleGrid } from '../actions';
const GridButton = ({ onToggleGrid }) => ( const GridButton = ({ onToggleGrid }) => (
<div className="actionbuttons" onClick={onToggleGrid}> <div
role="button"
tabIndex={-1}
className="actionbuttons"
onClick={onToggleGrid}
>
<FaTh /> <FaTh />
</div> </div>
); );
// TODO simplify...
function mapStateToProps(state: State) {
return {};
}
function mapDispatchToProps(dispatch) { function mapDispatchToProps(dispatch) {
return { return {
open() { open() {
@ -29,4 +29,4 @@ function mapDispatchToProps(dispatch) {
}; };
} }
export default connect(mapStateToProps, mapDispatchToProps)(GridButton); export default connect(null, mapDispatchToProps)(GridButton);

View File

@ -11,7 +11,13 @@ import { showHelpModal } from '../actions';
const HelpButton = ({ open }) => ( const HelpButton = ({ open }) => (
<div id="helpbutton" className="actionbuttons" onClick={open}> <div
id="helpbutton"
className="actionbuttons"
onClick={open}
role="button"
tabIndex={-1}
>
<FaQuestion /> <FaQuestion />
</div> </div>
); );

View File

@ -13,75 +13,49 @@ import React from 'react';
import Modal from './Modal'; import Modal from './Modal';
const titleStyle = {
color: '#4f545c',
marginLeft: 0,
marginRight: 10,
overflow: 'hidden',
wordWrap: 'break-word',
lineHeight: '24px',
fontSize: 16,
fontWeight: 500,
// marginTop: 0,
marginBottom: 0,
};
const textStyle = {
color: 'hsla(218, 5%, 47%, .6)',
fontSize: 14,
fontWeight: 500,
position: 'relative',
textAlign: 'inherit',
float: 'none',
margin: 0,
padding: 0,
lineHeight: 'normal',
};
const HelpModal = () => ( const HelpModal = () => (
<Modal title="Welcome to PixelPlanet.fun"> <Modal title="Welcome to PixelPlanet.fun">
<p style={{ textAlign: 'center', paddingLeft: '5%', paddingRight: '5%' }}> <p style={{ textAlign: 'center', paddingLeft: '5%', paddingRight: '5%' }}>
<p style={textStyle}>Place color pixels on a large canvas with other players online! <p className="modaltext">Place color pixels on a large canvas with other players online!
Our main canvas is a huge worldmap, you can place wherever you like, but you will have to wait a specific Our main canvas is a huge worldmap, you can place wherever you like, but you will have to wait a specific
Cooldown between pixels. You can check out the cooldown and requiremnts on the Canvas Selection menu (globe button on top). Cooldown between pixels. You can check out the cooldown and requiremnts on the Canvas Selection menu (globe button on top).
Some canvases have a different cooldown for replacing a user-set pixels than placing on a unset pixel. i.e. 4s/7s means 4s on fresh Some canvases have a different cooldown for replacing a user-set pixels than placing on a unset pixel. i.e. 4s/7s means 4s on fresh
pixels and 7s on already set pixels. pixels and 7s on already set pixels.
Higher zoomlevels take some time to update, the 3D globe gets updated at least once per day. Higher zoomlevels take some time to update, the 3D globe gets updated at least once per day.
Have fun!</p> Have fun!</p>
<p>Discord: <a href="./discord" target="_blank">pixelplanet.fun/discord</a></p> <p>Discord: <a href="./discord" target="_blank" rel="noopener noreferrer">pixelplanet.fun/discord</a></p>
<p>Source on <a href="https://github.com/pixelplanetdev/pixelplanet" target="_blank">github</a></p> <p>Source on <a href="https://github.com/pixelplanetdev/pixelplanet" target="_blank" rel="noopener noreferrer">github</a></p>
<p>Reddit: <a href="https://www.reddit.com/r/PixelPlanetFun/" target="_blank">r/PixelPlanetFun</a></p> <p>Reddit: <a href="https://www.reddit.com/r/PixelPlanetFun/" target="_blank" rel="noopener noreferrer">r/PixelPlanetFun</a></p>
<p style={titleStyle}>Map Data</p> <p className="modaltitle">Map Data</p>
<p style={textStyle}>The bare map data that we use, together with converted OpenStreetMap tiles for orientation, <p className="modaltext">The bare map data that we use, together with converted OpenStreetMap tiles for orientation,
can be downloaded from mega.nz here: <a href="https://mega.nz/#!JpkBwAbJ!EnSLlZmKv3kEBE0HDhakTgAZZycD3ELjduajJxPGaXo">pixelplanetmap.zip</a> (422MB)</p> can be downloaded from mega.nz here: <a href="https://mega.nz/#!JpkBwAbJ!EnSLlZmKv3kEBE0HDhakTgAZZycD3ELjduajJxPGaXo">pixelplanetmap.zip</a> (422MB)</p>
<p style={titleStyle}>Detected as Proxy?</p> <p className="modaltitle">Detected as Proxy?</p>
<p style={textStyle}>If you got detected as proxy, but you are none, please send us an e-mail with <a href="https://www.whatismyip.com/">your IP</a> to <a href="mailto:pixelplanetdev@gmail.com">pixelplanetdev@gmail.com</a>. Do not post your IP anywhere else. We are sorry for the inconvenience.</p> <p className="modaltext">If you got detected as proxy, but you are none, please send us an e-mail with <a href="https://www.whatismyip.com/">your IP</a> to <a href="mailto:pixelplanetdev@gmail.com">pixelplanetdev@gmail.com</a>. Do not post your IP anywhere else. We are sorry for the inconvenience.</p>
<h3 style={titleStyle}>2D Controls</h3> <h3 className="modaltitle">2D Controls</h3>
<p style={textStyle}>Click a color in palette to select</p> <p className="modaltext">Click a color in palette to select</p>
<p style={textStyle}>Press <kbd>G</kbd> to toggle grid</p> <p className="modaltext">Press <kbd>G</kbd> to toggle grid</p>
<p style={textStyle}>Press <kbd>X</kbd> to toggle showing of pixel activity</p> <p className="modaltext">Press <kbd>X</kbd> to toggle showing of pixel activity</p>
<p style={textStyle}>Press <kbd>R</kbd> to copy coordinates</p> <p className="modaltext">Press <kbd>R</kbd> to copy coordinates</p>
<p style={textStyle}>Press <kbd>Q</kbd> or <kbd>E</kbd> to zoom</p> <p className="modaltext">Press <kbd>Q</kbd> or <kbd>E</kbd> to zoom</p>
<p style={textStyle}>Press <kbd>W</kbd>,<kbd>A</kbd>,<kbd>S</kbd>, <kbd>D</kbd> to move</p> <p className="modaltext">Press <kbd>W</kbd>,<kbd>A</kbd>,<kbd>S</kbd>, <kbd>D</kbd> to move</p>
<p style={textStyle}>Press <kbd></kbd>,<kbd></kbd>,<kbd></kbd>, <kbd></kbd> to move</p> <p className="modaltext">Press <kbd></kbd>,<kbd></kbd>,<kbd></kbd>, <kbd></kbd> to move</p>
<p style={textStyle}>Drag mouse to move</p> <p className="modaltext">Drag mouse to move</p>
<p style={textStyle}>Scroll mouse wheel to zoom</p> <p className="modaltext">Scroll mouse wheel to zoom</p>
<p style={textStyle}>Click middle mouse button to current hovering color</p> <p className="modaltext">Click middle mouse button to current hovering color</p>
<p style={textStyle}>Pinch to zoom (on touch devices)</p> <p className="modaltext">Pinch to zoom (on touch devices)</p>
<p style={textStyle}>Pan to move (on touch devices)</p> <p className="modaltext">Pan to move (on touch devices)</p>
<p style={textStyle}>Click or tap to place a pixel</p> <p className="modaltext">Click or tap to place a pixel</p>
<h3 style={titleStyle}>3D Controls</h3> <h3 className="modaltitle">3D Controls</h3>
<p style={textStyle}>Press <kbd>W</kbd>,<kbd>A</kbd>,<kbd>S</kbd>, <kbd>D</kbd> to move</p> <p className="modaltext">Press <kbd>W</kbd>,<kbd>A</kbd>,<kbd>S</kbd>, <kbd>D</kbd> to move</p>
<p style={textStyle}>Press <kbd></kbd>,<kbd></kbd>,<kbd></kbd>, <kbd></kbd> to move</p> <p className="modaltext">Press <kbd></kbd>,<kbd></kbd>,<kbd></kbd>, <kbd></kbd> to move</p>
<p style={textStyle}>Scroll mouse wheel to zoom</p> <p className="modaltext">Scroll mouse wheel to zoom</p>
<p style={textStyle}>Left click and drag mouse to rotate</p> <p className="modaltext">Left click and drag mouse to rotate</p>
<p style={textStyle}>Middle click and drag mouse to zoom</p> <p className="modaltext">Middle click and drag mouse to zoom</p>
<p style={textStyle}>Right click and drag mouse to pan</p> <p className="modaltext">Right click and drag mouse to pan</p>
<p style={textStyle}>Left Click or tap to place a pixel</p> <p className="modaltext">Left Click or tap to place a pixel</p>
<p style={textStyle}>Right Click of double tap to remove a pixel</p> <p className="modaltext">Right Click of double tap to remove a pixel</p>
<p>Partners: <a href="https://www.crazygames.com/c/io" target="_blank">crazygames.com</a></p> <p>Partners: <a href="https://www.crazygames.com/c/io" target="_blank" rel="noopener noreferrer">crazygames.com</a></p>
<p style={textStyle}> <p className="modaltext">
<small>This site is protected by reCAPTCHA and the Google <small>This site is protected by reCAPTCHA and the Google
<a href="https://policies.google.com/privacy">Privacy Policy</a> and <a href="https://policies.google.com/privacy">Privacy Policy</a> and
<a href="https://policies.google.com/terms">Terms of Service</a> apply. <a href="https://policies.google.com/terms">Terms of Service</a> apply.

View File

@ -8,82 +8,81 @@
* LICENSE.txt file in the root directory of this source tree. * LICENSE.txt file in the root directory of this source tree.
*/ */
/* eslint-disable max-len */
import React from 'react'; import React from 'react';
import { analytics, RECAPTCHA_SITEKEY } from '../core/config'; import { analytics, RECAPTCHA_SITEKEY } from '../core/config';
class Html extends React.Component { const Html = ({
static defaultProps = { title,
styles: [], description,
scripts: [], body,
}; // array of css stylesheet urls
css,
// array of script urls
scripts,
// style as string
styles,
// code as string
code,
// if recaptcha should get loaded
useRecaptcha,
}) => (
<html className="no-js" lang="en">
<head>
<meta charSet="utf-8" />
<meta httpEquiv="x-ua-compatible" content="ie=edge" />
<title>{title}</title>
<meta name="description" content={description} />
<link rel="icon" href="/favicon.ico" type="image/x-icon" />
<meta
name="viewport"
content="user-scalable=no, width=device-width, initial-scale=1.0, maximum-scale=1.0"
/>
<link rel="apple-touch-icon" href="apple-touch-icon.png" />
{styles && styles.map((style) => (
<style
key={style.id}
id={style.id}
// eslint-disable-next-line react/no-danger
dangerouslySetInnerHTML={{ __html: style.cssText }}
/>
))}
{RECAPTCHA_SITEKEY && useRecaptcha
// eslint-disable-next-line react/no-danger
&& <script dangerouslySetInnerHTML={{ __html: `window.sitekey="${RECAPTCHA_SITEKEY}"` }} />}
{RECAPTCHA_SITEKEY && useRecaptcha && <script src="https://www.google.com/recaptcha/api.js" async defer />}
{code && (
<script
// eslint-disable-next-line react/no-danger
dangerouslySetInnerHTML={{ __html: code }}
/>
)}
{css && css.map((stylesheet) => (
<link rel="stylesheet" type="text/css" id={stylesheet.id} href={stylesheet.uri} />
))}
</head>
<body>
<div id="app">
{body}
</div>
{scripts && scripts.map((script) => <script key={script} src={script} />)}
{analytics.google.trackingId
&& (
<script
// eslint-disable-next-line react/no-danger
dangerouslySetInnerHTML={{
__html:
'window.ga=function(){ga.q.push(arguments)};ga.q=[];ga.l=+new Date;'
+ `ga('create','${analytics.google.trackingId}','auto');ga('send','pageview')`,
}}
/>
)}
{analytics.google.trackingId
&& <script src="https://www.google-analytics.com/analytics.js" async defer />}
</body>
</html>
);
props: {
title: string,
description: string,
styles: Array<{
id: string,
cssText: string,
}>,
scripts: Array<string>,
body: string,
code: string,
useRecaptcha: boolean,
};
render() {
const {
title, description, styles, scripts, body, code, useRecaptcha,
} = this.props;
return (
<html className="no-js" lang="en">
<head>
<meta charSet="utf-8" />
<meta httpEquiv="x-ua-compatible" content="ie=edge" />
<title>{title}</title>
<meta name="description" content={description} />
<link rel="icon" href="/favicon.ico" type="image/x-icon" />
<meta
name="viewport"
content="user-scalable=no, width=device-width, initial-scale=1.0, maximum-scale=1.0"
/>
<link rel="apple-touch-icon" href="apple-touch-icon.png" />
{styles.map((style) => (
<style
key={style.id}
id={style.id}
// eslint-disable-next-line react/no-danger
dangerouslySetInnerHTML={{ __html: style.cssText }}
/>
))}
{RECAPTCHA_SITEKEY && useRecaptcha && <script dangerouslySetInnerHTML={{ __html: `window.sitekey="${RECAPTCHA_SITEKEY}"` }} />}
{RECAPTCHA_SITEKEY && useRecaptcha && <script src="https://www.google.com/recaptcha/api.js" async defer />}
</head>
<body>
<div id="app">
{body}
</div>
<script
// eslint-disable-next-line react/no-danger
dangerouslySetInnerHTML={{ __html: code }}
/>
{scripts.map((script) => <script key={script} src={script} />)}
{analytics.google.trackingId
&& (
<script
// eslint-disable-next-line react/no-danger
dangerouslySetInnerHTML={{
__html:
'window.ga=function(){ga.q.push(arguments)};ga.q=[];ga.l=+new Date;'
+ `ga('create','${analytics.google.trackingId}','auto');ga('send','pageview')`,
}}
/>
)}
{analytics.google.trackingId
&& <script src="https://www.google-analytics.com/analytics.js" async defer />}
</body>
</html>
);
}
}
export default Html; export default Html;

View File

@ -11,7 +11,13 @@ import { showUserAreaModal } from '../actions';
const LogInButton = ({ open }) => ( const LogInButton = ({ open }) => (
<div id="loginbutton" className="actionbuttons" onClick={open}> <div
id="loginbutton"
className="actionbuttons"
onClick={open}
role="button"
tabIndex={-1}
>
<MdPerson /> <MdPerson />
</div> </div>
); );

View File

@ -9,22 +9,29 @@ import React from 'react';
import ReactDOM from 'react-dom/server'; import ReactDOM from 'react-dom/server';
import Html from './Html'; import Html from './Html';
/* this one is set by webpack */
// eslint-disable-next-line import/no-unresolved
import assets from './assets.json'; import assets from './assets.json';
// eslint-disable-next-line import/no-unresolved
import styleassets from './styleassets.json';
import { ASSET_SERVER, BACKUP_URL } from '../core/config'; import { ASSET_SERVER, BACKUP_URL } from '../core/config';
const data = { // eslint-disable-next-line max-len
title: 'PixelPlanet.fun', let code = `window.assetserver="${ASSET_SERVER}";window.availableStyles=JSON.parse('${JSON.stringify(styleassets)}');`;
description: 'Place color pixels on an map styled canvas ' if (BACKUP_URL) {
+ 'with other players online', code += `window.backupurl="${BACKUP_URL}";`;
// styles: [ }
// { id: 'css', cssText: baseCss }, const scripts = [
// ], ASSET_SERVER + assets.vendor.js,
scripts: [ ASSET_SERVER + assets.client.js,
ASSET_SERVER + assets.vendor.js, ];
ASSET_SERVER + assets.client.js, const css = [
], {
useRecaptcha: true, id: 'globcss',
}; uri: styleassets.default,
},
];
/* /*
* generates string with html of main page * generates string with html of main page
@ -35,12 +42,18 @@ const data = {
*/ */
function generateMainPage(countryCoords: Cell): string { function generateMainPage(countryCoords: Cell): string {
const [x, y] = countryCoords; const [x, y] = countryCoords;
let code = `window.coordx=${x};window.coordy=${y};window.assetserver="${ASSET_SERVER}";`; // eslint-disable-next-line
if (BACKUP_URL) { const html = ReactDOM.renderToStaticMarkup(
code += `window.backupurl="${BACKUP_URL}";`; <Html
} title="PixelPlanet.fun"
const htmldata = { ...data, code }; // eslint-disable-next-line max-len
const html = ReactDOM.renderToStaticMarkup(<Html {...htmldata} />); description="Place color pixels on an map styled canvas with other players online"
scripts={scripts}
css={css}
code={`${code}window.coordx=${x};window.coordy=${y};`}
useRecaptcha
/>,
);
return `<!doctype html>${html}`; return `<!doctype html>${html}`;
} }

View File

@ -6,14 +6,13 @@ import ToggleButton from 'react-toggle-button';
import { MdCheck, MdClose } from 'react-icons/md'; import { MdCheck, MdClose } from 'react-icons/md';
const MdToggleButton = ({ value, onToggle, ...props }) => ( const MdToggleButton = ({ value, onToggle }) => (
<ToggleButton <ToggleButton
inactiveLabel={<MdClose />} inactiveLabel={<MdClose />}
activeLabel={<MdCheck />} activeLabel={<MdCheck />}
thumbAnimateRange={[-10, 36]} thumbAnimateRange={[-10, 36]}
value={value} value={value}
onToggle={onToggle} onToggle={onToggle}
{...props}
/> />
); );

View File

@ -9,11 +9,14 @@ import Creeper from './Creeper.svg';
import { showMinecraftModal } from '../actions'; import { showMinecraftModal } from '../actions';
import type { State } from '../reducers';
const MinecraftButton = ({ open }) => ( const MinecraftButton = ({ open }) => (
<div id="minecraftbutton" className="actionbuttons" onClick={open}> <div
id="minecraftbutton"
className="actionbuttons"
onClick={open}
role="button"
tabIndex={-1}
>
<Creeper /> <Creeper />
</div> </div>
); );

View File

@ -9,13 +9,13 @@ import { MdNearMe } from 'react-icons/md';
import type { State } from '../reducers'; import type { State } from '../reducers';
async function submit_minecraft_tp(view) { async function submitMinecraftTp(view) {
const [x, y] = view.map(Math.round); const [x, y] = view.map(Math.round);
const body = JSON.stringify({ const body = JSON.stringify({
x, x,
y, y,
}); });
const response = await fetch('./api/mctp', { await fetch('./api/mctp', {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-type': 'application/json', 'Content-type': 'application/json',
@ -27,7 +27,13 @@ async function submit_minecraft_tp(view) {
const MinecraftTPButton = ({ view }) => ( const MinecraftTPButton = ({ view }) => (
<div id="minecrafttpbutton" className="actionbuttons" onClick={() => submit_minecraft_tp(view)}> <div
id="minecrafttpbutton"
className="actionbuttons"
role="button"
tabIndex={-1}
onClick={() => submitMinecraftTp(view)}
>
<MdNearMe /> <MdNearMe />
</div> </div>
); );

View File

@ -54,179 +54,171 @@ function cancelMovement() {
renderer.controls.moveDown = false; renderer.controls.moveDown = false;
} }
class Mobile3DControls extends React.Component { const Mobile3DControls = () => (
constructor() { <div>
super(); <div
} className="actionbuttons"
role="button"
render() { tabIndex={0}
return ( style={{
<div> ...btnStyle,
<div // left: 46,
className="actionbuttons" left: 57,
role="button" // bottom: 128,
tabIndex={0} bottom: 139,
style={{ }}
...btnStyle, onMouseDown={() => {
// left: 46, move('FORWARD', true);
left: 57, }}
// bottom: 128, onMouseUp={() => {
bottom: 139, move('FORWARD', false);
}} }}
onMouseDown={() => { onTouchStart={() => {
move('FORWARD', true); move('FORWARD', true);
}} }}
onMouseUp={() => { onTouchEnd={() => {
move('FORWARD', false); move('FORWARD', false);
}} }}
onTouchStart={() => { onTouchCancel={cancelMovement}
move('FORWARD', true); onMouseLeave={cancelMovement}
}} >
onTouchEnd={() => {
move('FORWARD', false); </div>
}} <div
onTouchCancel={cancelMovement} className="actionbuttons"
onMouseLeave={cancelMovement} role="button"
> tabIndex={0}
style={{
</div> ...btnStyle,
<div // left: 46,
className="actionbuttons" left: 57,
role="button" bottom: 98,
tabIndex={0} }}
style={{ onMouseDown={() => {
...btnStyle, move('BACKWARD', true);
// left: 46, }}
left: 57, onMouseUp={() => {
bottom: 98, move('BACKWARD', false);
}} }}
onMouseDown={() => { onTouchStart={() => {
move('BACKWARD', true); move('BACKWARD', true);
}} }}
onMouseUp={() => { onTouchEnd={() => {
move('BACKWARD', false); move('BACKWARD', false);
}} }}
onTouchStart={() => { onTouchCancel={cancelMovement}
move('BACKWARD', true); onMouseLeave={cancelMovement}
}} >
onTouchEnd={() => {
move('BACKWARD', false); </div>
}} <div
onTouchCancel={cancelMovement} className="actionbuttons"
onMouseLeave={cancelMovement} role="button"
> tabIndex={0}
style={{
</div> ...btnStyle,
<div left: 16,
className="actionbuttons" bottom: 98,
role="button" }}
tabIndex={0} onMouseDown={() => {
style={{ move('LEFT', true);
...btnStyle, }}
left: 16, onMouseUp={() => {
bottom: 98, move('LEFT', false);
}} }}
onMouseDown={() => { onTouchStart={() => {
move('LEFT', true); move('LEFT', true);
}} }}
onMouseUp={() => { onTouchEnd={() => {
move('LEFT', false); move('LEFT', false);
}} }}
onTouchStart={() => { onTouchCancel={cancelMovement}
move('LEFT', true); onMouseLeave={cancelMovement}
}} >
onTouchEnd={() => {
move('LEFT', false); </div>
}} <div
onTouchCancel={cancelMovement} className="actionbuttons"
onMouseLeave={cancelMovement} role="button"
> tabIndex={0}
style={{
</div> ...btnStyle,
<div // left: 76,
className="actionbuttons" left: 98,
role="button" bottom: 98,
tabIndex={0} }}
style={{ onMouseDown={() => {
...btnStyle, move('RIGHT', true);
// left: 76, }}
left: 98, onMouseUp={() => {
bottom: 98, move('RIGHT', false);
}} }}
onMouseDown={() => { onTouchStart={() => {
move('RIGHT', true); move('RIGHT', true);
}} }}
onMouseUp={() => { onTouchEnd={() => {
move('RIGHT', false); move('RIGHT', false);
}} }}
onTouchStart={() => { onTouchCancel={cancelMovement}
move('RIGHT', true); onMouseLeave={cancelMovement}
}} >
onTouchEnd={() => {
move('RIGHT', false); </div>
}} <div
onTouchCancel={cancelMovement} className="actionbuttons"
onMouseLeave={cancelMovement} role="button"
> tabIndex={0}
style={{
</div> ...btnStyle,
<div // left: 76,
className="actionbuttons" left: 16,
role="button" bottom: 139,
tabIndex={0} }}
style={{ onMouseDown={() => {
...btnStyle, move('UP', true);
// left: 76, }}
left: 16, onMouseUp={() => {
bottom: 139, move('UP', false);
}} }}
onMouseDown={() => { onTouchStart={() => {
move('UP', true); move('UP', true);
}} }}
onMouseUp={() => { onTouchEnd={() => {
move('UP', false); move('UP', false);
}} }}
onTouchStart={() => { onTouchCancel={cancelMovement}
move('UP', true); onMouseLeave={cancelMovement}
}} >
onTouchEnd={() => {
move('UP', false); </div>
}} <div
onTouchCancel={cancelMovement} className="actionbuttons"
onMouseLeave={cancelMovement} role="button"
> tabIndex={0}
style={{
</div> ...btnStyle,
<div // left: 76,
className="actionbuttons" left: 98,
role="button" bottom: 139,
tabIndex={0} }}
style={{ onMouseDown={() => {
...btnStyle, move('DOWN', true);
// left: 76, }}
left: 98, onMouseUp={() => {
bottom: 139, move('DOWN', false);
}} }}
onMouseDown={() => { onTouchStart={() => {
move('DOWN', true); move('DOWN', true);
}} }}
onMouseUp={() => { onTouchEnd={() => {
move('DOWN', false); move('DOWN', false);
}} }}
onTouchStart={() => { onTouchCancel={cancelMovement}
move('DOWN', true); onMouseLeave={cancelMovement}
}} >
onTouchEnd={() => {
move('DOWN', false); </div>
}} </div>
onTouchCancel={cancelMovement} );
onMouseLeave={cancelMovement}
>
</div>
</div>
);
}
}
export default Mobile3DControls; export default Mobile3DControls;

View File

@ -13,26 +13,6 @@ import {
} from '../actions'; } from '../actions';
const closeStyles = {
position: 'fixed',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
flex: '0 0 36px',
borderWidth: 2,
borderStyle: 'solid',
borderRadius: '50%',
width: 36,
height: 36,
cursor: 'pointer',
backgroundColor: '#f6f6f7',
borderColor: '#dcddde',
top: 30,
right: 40,
};
// TODO appear with animation
function MyModal({ close, title, children }) { function MyModal({ close, title, children }) {
return ( return (
<Modal <Modal
@ -45,8 +25,8 @@ function MyModal({ close, title, children }) {
> >
<h2 style={{ paddingLeft: '5%' }}>{title}</h2> <h2 style={{ paddingLeft: '5%' }}>{title}</h2>
<div <div
style={closeStyles}
onClick={close} onClick={close}
className="ModalClose"
role="button" role="button"
label="close" label="close"
tabIndex={-1} tabIndex={-1}

View File

@ -30,13 +30,13 @@ const MODAL_COMPONENTS = {
/* other modals */ /* other modals */
}; };
const ModalRoot = ({ modalType, modalProps }) => { const ModalRoot = ({ modalType }) => {
if (!modalType) { if (!modalType) {
return null; return null;
} }
const SpecificModal = MODAL_COMPONENTS[modalType]; const SpecificModal = MODAL_COMPONENTS[modalType];
return <SpecificModal {...modalProps} />; return <SpecificModal />;
}; };
export default connect( export default connect(

View File

@ -73,17 +73,19 @@ class NewPasswordForm extends React.Component {
} }
render() { render() {
const { errors } = this.state; const { success } = this.state;
if (this.state.success) { const { back } = this.props;
if (success) {
return ( return (
<div> <div>
<p className="modalmessage"> <p className="modalmessage">
Sent you a mail with instructions to reset your password. Sent you a mail with instructions to reset your password.
</p> </p>
<button type="button" onClick={this.props.back}>Back</button> <button type="button" onClick={back}>Back</button>
</div> </div>
); );
} }
const { errors, email, submitting } = this.state;
return ( return (
<form onSubmit={this.handleSubmit}> <form onSubmit={this.handleSubmit}>
{errors.map((error) => ( {errors.map((error) => (
@ -91,16 +93,16 @@ class NewPasswordForm extends React.Component {
))} ))}
<input <input
style={inputStyles} style={inputStyles}
value={this.state.email} value={email}
onChange={(evt) => this.setState({ email: evt.target.value })} onChange={(evt) => this.setState({ email: evt.target.value })}
type="text" type="text"
placeholder="Email" placeholder="Email"
/> />
<br /> <br />
<button type="submit"> <button type="submit">
{(this.state.submitting) ? '...' : 'Submit'} {(submitting) ? '...' : 'Submit'}
</button> </button>
<button type="button" onClick={this.props.back}>Cancel</button> <button type="button" onClick={back}>Cancel</button>
</form> </form>
); );
} }

View File

@ -8,49 +8,46 @@ import React from 'react';
import TotalRankings from './TotalRankings'; import TotalRankings from './TotalRankings';
import DailyRankings from './DailyRankings'; import DailyRankings from './DailyRankings';
const textStyle = {
color: 'hsla(218, 5%, 47%, .6)',
fontSize: 14,
fontWeight: 500,
position: 'relative',
textAlign: 'inherit',
float: 'none',
margin: 0,
padding: 0,
lineHeight: 'normal',
};
class Rankings extends React.Component { class Rankings extends React.Component {
constructor() { constructor() {
super(); super();
this.state = { this.state = {
order_daily: false, orderDaily: false,
}; };
} }
render() { render() {
const { orderDaily } = this.state;
return ( return (
<div> <div>
<p> <p>
<span <span
className={(!this.state.order_daily) ? 'modallinkselected' : 'modallink'} role="button"
onClick={() => { this.setState({ order_daily: false }); }} tabIndex={-1}
className={
(!orderDaily) ? 'modallinkselected' : 'modallink'
}
onClick={() => {
this.setState({ orderDaily: false });
}}
>Total</span> | >Total</span> |
<span <span
className={(this.state.order_daily) ? 'modallinkselected' : 'modallink'} role="button"
onClick={() => { this.setState({ order_daily: true }); }} tabIndex={-1}
className={
(orderDaily) ? 'modallinkselected' : 'modallink'
}
onClick={() => { this.setState({ orderDaily: true }); }}
>Daily</span> >Daily</span>
</p> </p>
{(this.state.order_daily) ? <DailyRankings /> : <TotalRankings />} {(orderDaily) ? <DailyRankings /> : <TotalRankings />}
<p style={textStyle}>Ranking updates every 5 min. Daily rankings get reset at midnight UTC.</p> <p className="modaltext">
Ranking updates every 5 min. Daily rankings get reset at midnight UTC.
</p>
</div> </div>
); );
} }
} }
function mapStateToProps(state: State) {
const { totalRanking } = state.user;
return { totalRanking };
}
export default Rankings; export default Rankings;

View File

@ -12,8 +12,6 @@ import { requestPlacePixel } from '../actions';
function onCaptcha(token: string) { function onCaptcha(token: string) {
console.log('token', token);
const { canvasId, coordinates, color } = window.pixel; const { canvasId, coordinates, color } = window.pixel;
store.dispatch(requestPlacePixel(canvasId, coordinates, color, token)); store.dispatch(requestPlacePixel(canvasId, coordinates, color, token));

View File

@ -8,32 +8,21 @@ import { connect } from 'react-redux';
import Modal from './Modal'; import Modal from './Modal';
import { showUserAreaModal, receiveMe } from '../actions'; import { showUserAreaModal } from '../actions';
// import { send_registration } from '../ui/register'; // import { send_registration } from '../ui/register';
import SignUpForm from './SignUpForm'; import SignUpForm from './SignUpForm';
const textStyle = { const RegisterModal = ({ login }) => (
color: 'hsla(218, 5%, 47%, .6)',
fontSize: 14,
fontWeight: 500,
position: 'relative',
textAlign: 'inherit',
float: 'none',
margin: 0,
paddingLeft: '5%',
lineHeight: 'normal',
};
const RegisterModal = ({ login, doMe }) => (
<Modal title="Register New Account"> <Modal title="Register New Account">
<p style={textStyle}>Register new account here</p><br /> <p style={{ paddingLeft: '5%', paddingRight: '5%' }}>
<p style={{ textAlign: 'center' }}> <p className="modaltext">Register new account here</p><br />
<SignUpForm userarea={login} me={doMe} /> <p style={{ textAlign: 'center' }}>
<button type="button" onClick={login}>Cancel</button> <SignUpForm back={login} />
<p>Also join our Discord:&nbsp; <p>Also join our Discord:&nbsp;
<a href="./discord" target="_blank">pixelplanet.fun/discord</a> <a href="./discord" target="_blank">pixelplanet.fun/discord</a>
</p>
</p> </p>
</p> </p>
</Modal> </Modal>
@ -44,9 +33,6 @@ function mapDispatchToProps(dispatch) {
login() { login() {
dispatch(showUserAreaModal()); dispatch(showUserAreaModal());
}, },
doMe(me) {
dispatch(receiveMe(me));
},
}; };
} }

View File

@ -9,11 +9,15 @@ import { FaCog } from 'react-icons/fa';
import { showSettingsModal } from '../actions'; import { showSettingsModal } from '../actions';
import type { State } from '../reducers';
const SettingsButton = ({ open }) => ( const SettingsButton = ({ open }) => (
<div id="settingsbutton" className="actionbuttons" onClick={open}> <div
id="settingsbutton"
className="actionbuttons"
onClick={open}
role="button"
tabIndex={-1}
>
<FaCog /> <FaCog />
</div> </div>
); );

View File

@ -18,6 +18,7 @@ import {
togglePotatoMode, togglePotatoMode,
toggleLightGrid, toggleLightGrid,
toggleHistoricalView, toggleHistoricalView,
selectStyle,
} from '../actions'; } from '../actions';
import type { State } from '../reducers'; import type { State } from '../reducers';
@ -40,16 +41,6 @@ const itemStyles = {
const titleStyles = { const titleStyles = {
flex: '1 1 auto', flex: '1 1 auto',
marginLeft: 0,
marginRight: 10,
color: '#4f545c',
overflow: 'hidden',
wordWrap: 'break-word',
lineHeight: '24px',
fontSize: 16,
fontWeight: 500,
marginTop: 0,
marginBottom: 0,
}; };
const rowStyles = { const rowStyles = {
@ -57,35 +48,50 @@ const rowStyles = {
flexDirection: 'row', flexDirection: 'row',
}; };
const descriptionStyle = { const SettingsItemSelect = ({
boxSizing: 'border-box', title, description, values, selected, onSelect,
flex: '1 1 auto', }) => (
color: 'hsla(218, 5%, 47%, .6)', <div style={itemStyles}>
fontSize: 14, <div style={rowStyles}>
lineHeight: '20px', <h3 style={titleStyles} className="modaltitle">{title}</h3>
fontWeight: 500, <select
marginTop: 4, onChange={(e) => {
}; const sel = e.target;
onSelect(sel.options[sel.selectedIndex].value);
const dividerStyles = { }}
boxSizing: 'border-box', >
marginTop: 20, {
height: 1, values.map((value) => (
width: '100%', <option
backgroundColor: 'hsla(216, 4%, 74%, .3)', selected={value === selected}
}; value={value}
>
{value}
</option>
))
}
</select>
</div>
{description && <div className="modaldesc">{description} </div>}
<div className="modaldivider" />
</div>
);
const SettingsItem = ({ const SettingsItem = ({
title, description, keyBind, value, onToggle, title, description, keyBind, value, onToggle,
}) => ( }) => (
<div style={itemStyles}> <div style={itemStyles}>
<div style={rowStyles}> <div style={rowStyles}>
<h3 style={titleStyles}>{title} {keyBind && <kbd>{keyBind}</kbd>}</h3> <h3
style={titleStyles}
className="modaltitle"
>
{title} {keyBind && <kbd>{keyBind}</kbd>}
</h3>
<MdToggleButtonHover value={value} onToggle={onToggle} /> <MdToggleButtonHover value={value} onToggle={onToggle} />
</div> </div>
{description && <div style={descriptionStyle}>{description} </div>} {description && <div className="modaldesc">{description} </div>}
<div style={dividerStyles} /> <div className="modaldivider" />
</div> </div>
); );
@ -99,6 +105,7 @@ function SettingsModal({
onMute, onMute,
autoZoomIn, autoZoomIn,
compactPalette, compactPalette,
selectedStyle,
onToggleGrid, onToggleGrid,
onTogglePixelNotify, onTogglePixelNotify,
onToggleAutoZoomIn, onToggleAutoZoomIn,
@ -107,6 +114,7 @@ function SettingsModal({
onTogglePotatoMode, onTogglePotatoMode,
onToggleLightGrid, onToggleLightGrid,
onToggleHistoricalView, onToggleHistoricalView,
onSelectStyle,
chatNotify, chatNotify,
}) { }) {
return ( return (
@ -175,6 +183,15 @@ function SettingsModal({
onToggle={onToggleHistoricalView} onToggle={onToggleHistoricalView}
/> />
) : null } ) : null }
{(typeof window.availableStyles !== 'undefined') && (
<SettingsItemSelect
title="Styles"
description="How pixelplanet should look like."
values={Object.keys(window.availableStyles)}
selected={selectedStyle}
onSelect={onSelectStyle}
/>
)}
</p> </p>
</Modal> </Modal>
); );
@ -189,6 +206,7 @@ function mapStateToProps(state: State) {
compactPalette, compactPalette,
isPotato, isPotato,
isLightGrid, isLightGrid,
style: selectedStyle,
} = state.gui; } = state.gui;
const isMuted = mute; const isMuted = mute;
const { const {
@ -206,6 +224,7 @@ function mapStateToProps(state: State) {
isPotato, isPotato,
isLightGrid, isLightGrid,
isHistoricalView, isHistoricalView,
selectedStyle,
}; };
} }
@ -238,6 +257,9 @@ function mapDispatchToProps(dispatch) {
onToggleHistoricalView() { onToggleHistoricalView() {
dispatch(toggleHistoricalView()); dispatch(toggleHistoricalView());
}, },
onSelectStyle(style) {
dispatch(selectStyle(style));
},
}; };
} }

View File

@ -4,10 +4,14 @@
*/ */
import React from 'react'; import React from 'react';
import { connect } from 'react-redux';
import { import {
validateEMail, validateName, validatePassword, parseAPIresponse, validateEMail, validateName, validatePassword, parseAPIresponse,
} from '../utils/validation'; } from '../utils/validation';
import { showUserAreaModal, receiveMe } from '../actions';
function validate(name, email, password, confirmPassword) { function validate(name, email, password, confirmPassword) {
const errors = []; const errors = [];
const mailerror = validateEMail(email); const mailerror = validateEMail(email);
@ -90,12 +94,23 @@ class SignUpForm extends React.Component {
}); });
return; return;
} }
this.props.me(me); const { doMe, userarea } = this.props;
this.props.userarea(); doMe(me);
userarea();
} }
render() { render() {
const { errors } = this.state; const {
errors,
name,
email,
password,
confirmPassword,
submitting,
} = this.state;
const {
back,
} = this.props;
return ( return (
<form onSubmit={this.handleSubmit}> <form onSubmit={this.handleSubmit}>
{errors.map((error) => ( {errors.map((error) => (
@ -103,28 +118,28 @@ class SignUpForm extends React.Component {
))} ))}
<input <input
style={inputStyles} style={inputStyles}
value={this.state.name} value={name}
onChange={(evt) => this.setState({ name: evt.target.value })} onChange={(evt) => this.setState({ name: evt.target.value })}
type="text" type="text"
placeholder="Name" placeholder="Name"
/><br /> /><br />
<input <input
style={inputStyles} style={inputStyles}
value={this.state.email} value={email}
onChange={(evt) => this.setState({ email: evt.target.value })} onChange={(evt) => this.setState({ email: evt.target.value })}
type="text" type="text"
placeholder="Email" placeholder="Email"
/><br /> /><br />
<input <input
style={inputStyles} style={inputStyles}
value={this.state.password} value={password}
onChange={(evt) => this.setState({ password: evt.target.value })} onChange={(evt) => this.setState({ password: evt.target.value })}
type="password" type="password"
placeholder="Password" placeholder="Password"
/><br /> /><br />
<input <input
style={inputStyles} style={inputStyles}
value={this.state.confirmPassword} value={confirmPassword}
onChange={(evt) => this.setState({ onChange={(evt) => this.setState({
confirmPassword: evt.target.value, confirmPassword: evt.target.value,
})} })}
@ -132,11 +147,29 @@ class SignUpForm extends React.Component {
placeholder="Confirm Password" placeholder="Confirm Password"
/><br /> /><br />
<button type="submit"> <button type="submit">
{(this.state.submitting) ? '...' : 'Submit'} {(submitting) ? '...' : 'Submit'}
</button>
<button
type="button"
onClick={back}
>
Cancel
</button> </button>
</form> </form>
); );
} }
} }
export default SignUpForm;
function mapDispatchToProps(dispatch) {
return {
doMe(me) {
dispatch(receiveMe(me));
},
userarea() {
dispatch(showUserAreaModal());
},
};
}
export default connect(null, mapDispatchToProps)(SignUpForm);

View File

@ -1,13 +1,6 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import PropTypes from 'prop-types';
class Tab extends Component { class Tab extends Component {
static propTypes = {
activeTab: PropTypes.string.isRequired,
label: PropTypes.string.isRequired,
onClick: PropTypes.func.isRequired,
};
onClick = () => { onClick = () => {
const { label, onClick } = this.props; const { label, onClick } = this.props;
onClick(label); onClick(label);
@ -30,6 +23,7 @@ class Tab extends Component {
return ( return (
<li <li
role="presentation"
className={className} className={className}
onClick={onClick} onClick={onClick}
> >

View File

@ -1,18 +1,14 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import PropTypes from 'prop-types';
import Tab from './Tab'; import Tab from './Tab';
class Tabs extends Component { class Tabs extends Component {
static propTypes = {
children: PropTypes.instanceOf(Array).isRequired,
}
constructor(props) { constructor(props) {
super(props); super(props);
const { children } = this.props;
this.state = { this.state = {
activeTab: this.props.children[0].props.label, activeTab: children[0].props.label,
}; };
} }

View File

@ -16,21 +16,10 @@ import DeleteAccount from './DeleteAccount';
import { numberToString } from '../core/utils'; import { numberToString } from '../core/utils';
const textStyle = {
color: 'hsla(218, 5%, 47%, .6)',
fontSize: 14,
fontWeight: 500,
position: 'relative',
textAlign: 'inherit',
float: 'none',
margin: 0,
padding: 0,
lineHeight: 'normal',
};
const Stat = ({ text, value, rank }) => ( const Stat = ({ text, value, rank }) => (
<p> <p>
<span className="stattext">{(rank) ? `${text}: #` : `${text}: `}</span> <span className="stattext">{(rank) ? `${text}: #` : `${text}: `}</span>
&nbsp;
<span className="statvalue">{numberToString(value)}</span> <span className="statvalue">{numberToString(value)}</span>
</p> </p>
); );
@ -40,95 +29,131 @@ class UserArea extends React.Component {
super(); super();
this.state = { this.state = {
// that should be an ENUM tbh // that should be an ENUM tbh
change_name_extended: false, changeNameExtended: false,
change_mail_extended: false, changeMailExtended: false,
change_passwd_extended: false, changePasswdExtended: false,
delete_account_extended: false, deleteAccountExtended: false,
}; };
} }
render() { render() {
const {
stats, name, logout, mailreg, setMailreg, setName,
} = this.props;
const {
changeNameExtended,
changeMailExtended,
changePasswdExtended,
deleteAccountExtended,
} = this.state;
return ( return (
<p style={{ textAlign: 'center' }}> <p style={{ textAlign: 'center' }}>
<UserMessages /> <UserMessages />
<Stat text="Todays Placed Pixels" value={this.props.stats.dailyTotalPixels} /> <Stat
<Stat text="Daily Rank" value={this.props.stats.dailyRanking} rank /> text="Todays Placed Pixels"
<Stat text="Placed Pixels" value={this.props.stats.totalPixels} /> value={stats.dailyTotalPixels}
<Stat text="Total Rank" value={this.props.stats.ranking} rank /> />
<p style={textStyle}> <Stat
<p>Your name is: {this.props.name}</p>( text="Daily Rank"
value={stats.dailyRanking}
rank
/>
<Stat
text="Placed Pixels"
value={stats.totalPixels}
/>
<Stat
text="Total Rank"
value={stats.ranking}
rank
/>
<p className="modaltext">
<p>Your name is: {name}</p>(
<span <span
role="button"
tabIndex={-1}
className="modallink" className="modallink"
onClick={this.props.logout} onClick={logout}
> Log out</span> | > Log out</span> |
<span <span
role="button"
tabIndex={-1}
className="modallink" className="modallink"
onClick={(evt) => this.setState({ onClick={() => this.setState({
change_name_extended: true, changeNameExtended: true,
change_mail_extended: false, changeMailExtended: false,
change_passwd_extended: false, changePasswdExtended: false,
delete_account_extended: false, deleteAccountExtended: false,
})} })}
> Change Username</span> | > Change Username</span> |
{(this.props.mailreg) {(mailreg)
&& ( && (
<span> <span>
<span <span
role="button"
tabIndex={-1}
className="modallink" className="modallink"
onClick={(evt) => this.setState({ onClick={() => this.setState({
change_name_extended: false, changeNameExtended: false,
change_mail_extended: true, changeMailExtended: true,
change_passwd_extended: false, changePasswdExtended: false,
delete_account_extended: false, deleteAccountExtended: false,
})} })}
> Change Mail</span> | > Change Mail</span> |
</span> </span>
)} )}
<span <span
role="button"
tabIndex={-1}
className="modallink" className="modallink"
onClick={(evt) => this.setState({ onClick={() => this.setState({
change_name_extended: false, changeNameExtended: false,
change_mail_extended: false, changeMailExtended: false,
change_passwd_extended: true, changePasswdExtended: true,
delete_account_extended: false, deleteAccountExtended: false,
})} })}
> Change Password</span> | > Change Password</span> |
<span <span
role="button"
tabIndex={-1}
className="modallink" className="modallink"
onClick={(evt) => this.setState({ onClick={() => this.setState({
change_name_extended: false, changeNameExtended: false,
change_mail_extended: false, changeMailExtended: false,
change_passwd_extended: false, changePasswdExtended: false,
delete_account_extended: true, deleteAccountExtended: true,
})} })}
> Delete Account</span> ) > Delete Account</span> )
</p> </p>
{(this.state.change_passwd_extended) {(changePasswdExtended)
&& ( && (
<ChangePassword <ChangePassword
mailreg={this.props.mailreg} mailreg={mailreg}
done={() => { this.props.set_mailreg(true); this.setState({ change_passwd_extended: false }); }} done={() => {
cancel={() => { this.setState({ change_passwd_extended: false }); }} setMailreg(true);
this.setState({ changePasswdExtended: false });
}}
cancel={() => { this.setState({ changePasswdExtended: false }); }}
/> />
)} )}
{(this.state.change_name_extended) {(changeNameExtended)
&& ( && (
<ChangeName <ChangeName
set_name={this.props.set_name} setName={setName}
done={() => { this.setState({ change_name_extended: false }); }} done={() => { this.setState({ changeNameExtended: false }); }}
/> />
)} )}
{(this.state.change_mail_extended) {(changeMailExtended)
&& ( && (
<ChangeMail <ChangeMail
done={() => { this.setState({ change_mail_extended: false }); }} done={() => { this.setState({ changeMailExtended: false }); }}
/> />
)} )}
{(this.state.delete_account_extended) {(deleteAccountExtended)
&& ( && (
<DeleteAccount <DeleteAccount
set_name={this.props.set_name} setName={setName}
done={() => { this.setState({ delete_account_extended: false }); }} done={() => { this.setState({ deleteAccountExtended: false }); }}
/> />
)} )}
</p> </p>

View File

@ -26,26 +26,15 @@ const logoStyle = {
marginRight: 5, marginRight: 5,
}; };
const textStyle = { const LogInArea = ({ register, forgotPassword, me }) => (
color: 'hsla(218, 5%, 47%, .6)',
fontSize: 14,
fontWeight: 500,
position: 'relative',
textAlign: 'inherit',
float: 'none',
margin: 0,
padding: 0,
lineHeight: 'normal',
};
const LogInArea = ({ register, forgot_password, me }) => (
<p style={{ textAlign: 'center' }}> <p style={{ textAlign: 'center' }}>
<p style={textStyle}>Login to access more features and stats.</p><br /> <p className="modaltext">Login to access more features and stats.</p><br />
<h2>Login with Mail:</h2> <h2>Login with Mail:</h2>
<LogInForm me={me} /> <LogInForm me={me} />
<p <p
className="modallink" className="modallink"
onClick={forgot_password} onClick={forgotPassword}
role="presentation"
> >
I forgot my Password.</p> I forgot my Password.</p>
<h2>or login with:</h2> <h2>or login with:</h2>
@ -95,7 +84,7 @@ const LogInArea = ({ register, forgot_password, me }) => (
); );
const UserAreaModal = ({ const UserAreaModal = ({
name, register, forgot_password, doMe, logout, setUserName, setUserMailreg, name, register, forgotPassword, doMe, logout, setUserName, setUserMailreg,
}) => ( }) => (
<Modal title="User Area"> <Modal title="User Area">
<p style={{ textAlign: 'center' }}> <p style={{ textAlign: 'center' }}>
@ -103,7 +92,7 @@ const UserAreaModal = ({
? ( ? (
<LogInArea <LogInArea
register={register} register={register}
forgot_password={forgot_password} forgotPassword={forgotPassword}
me={doMe} me={doMe}
/> />
) )
@ -112,8 +101,8 @@ const UserAreaModal = ({
<div label="Profile"> <div label="Profile">
<UserArea <UserArea
logout={logout} logout={logout}
set_name={setUserName} setName={setUserName}
set_mailreg={setUserMailreg} setMailreg={setUserMailreg}
/> />
</div> </div>
<div label="Ranking"> <div label="Ranking">
@ -138,7 +127,7 @@ function mapDispatchToProps(dispatch) {
register() { register() {
dispatch(showRegisterModal()); dispatch(showRegisterModal());
}, },
forgot_password() { forgotPassword() {
dispatch(showForgotPasswordModal()); dispatch(showForgotPasswordModal());
}, },
doMe(me) { doMe(me) {

View File

@ -13,20 +13,21 @@ class UserMessages extends React.Component {
constructor() { constructor() {
super(); super();
this.state = { this.state = {
resent_verify: false, resentVerify: false,
sent_link: false, sentLink: false,
verify_answer: null, verifyAnswer: null,
link_answer: null, linkAnswer: null,
}; };
this.submit_resend_verify = this.submit_resend_verify.bind(this); this.submitResendVerify = this.submitResendVerify.bind(this);
this.submit_mc_link = this.submit_mc_link.bind(this); this.submitMcLink = this.submitMcLink.bind(this);
} }
async submit_resend_verify() { async submitResendVerify() {
if (this.state.resent_verify) return; const { resentVerify } = this.state;
if (resentVerify) return;
this.setState({ this.setState({
resent_verify: true, resentVerify: true,
}); });
const response = await fetch('./api/auth/resend_verify', { const response = await fetch('./api/auth/resend_verify', {
@ -34,16 +35,19 @@ class UserMessages extends React.Component {
}); });
const { errors } = await parseAPIresponse(response); const { errors } = await parseAPIresponse(response);
const verify_answer = (errors) ? errors[0] : 'A new verification mail is getting sent to you.'; const verifyAnswer = (errors)
? errors[0]
: 'A new verification mail is getting sent to you.';
this.setState({ this.setState({
verify_answer, verifyAnswer,
}); });
} }
async submit_mc_link(accepted) { async submitMcLink(accepted) {
if (this.state.sent_link) return; const { sentLink } = this.state;
if (sentLink) return;
this.setState({ this.setState({
sent_link: true, sentLink: true,
}); });
const body = JSON.stringify({ accepted }); const body = JSON.stringify({ accepted });
const rep = await fetch('./api/auth/mclink', { const rep = await fetch('./api/auth/mclink', {
@ -56,43 +60,94 @@ class UserMessages extends React.Component {
const { errors } = parseAPIresponse(rep); const { errors } = parseAPIresponse(rep);
if (errors) { if (errors) {
this.setState({ this.setState({
link_answer: errors[0], linkAnswer: errors[0],
}); });
return; return;
} }
const { setMCName, remFromUserMessages } = this.props;
if (!accepted) { if (!accepted) {
this.props.setMCName(null); setMCName(null);
} }
this.props.rem_from_messages('not_mc_verified'); remFromUserMessages('not_mc_verified');
this.setState({ this.setState({
link_answer: (accepted) ? 'You successfully linked your mc account.' : 'You denied.', linkAnswer: (accepted)
? 'You successfully linked your mc account.'
: 'You denied.',
}); });
} }
render() { render() {
if (!this.props.messages) return null; const { messages: messagesr } = this.props;
if (!messagesr) return null;
// state variable is not allowed to be changed, make copy // state variable is not allowed to be changed, make copy
const messages = [...this.props.messages]; const messages = [...messagesr];
const { verifyAnswer, linkAnswer } = this.state;
const { minecraftname } = this.props;
return ( return (
<div style={{ paddingLeft: '5%', paddingRight: '5%' }}> <div style={{ paddingLeft: '5%', paddingRight: '5%' }}>
{(messages.includes('not_verified') && messages.splice(messages.indexOf('not_verified'), 1)) {(messages.includes('not_verified')
&& messages.splice(messages.indexOf('not_verified'), 1))
? ( ? (
<p className="usermessages"> <p className="usermessages">
Please verify your mail address or your account could get deleted after a few days.&nbsp; Please verify your mail address&nbsp;
{(this.state.verify_answer) or your account could get deleted after a few days.&nbsp;
? <span className="modallink">{this.state.verify_answer}</span> {(verifyAnswer)
: <span className="modallink" onClick={this.submit_resend_verify}>Click here to request a new verification mail.</span>} ? (
<span
className="modallink"
>
{verifyAnswer}
</span>
)
: (
<span
role="button"
tabIndex={-1}
className="modallink"
onClick={this.submitResendVerify}
>
Click here to request a new verification mail.
</span>
)}
</p> </p>
) : null} ) : null}
{(messages.includes('not_mc_verified') && messages.splice(messages.indexOf('not_mc_verified'), 1)) {(messages.includes('not_mc_verified')
&& messages.splice(messages.indexOf('not_mc_verified'), 1))
? ( ? (
<p className="usermessages">You requested to link your mc account {this.props.minecraftname}. <p className="usermessages">
{(this.state.link_answer) You requested to link your mc account {minecraftname}.
? <span className="modallink">{this.state.link_answer}</span> &nbsp;
{(linkAnswer)
? (
<span
className="modallink"
>
{linkAnswer}
</span>
)
: ( : (
<span> <span>
<span className="modallink" onClick={() => { this.submit_mc_link(true); }}>Accept</span> or <span className="modallink" onClick={() => { this.submit_mc_link(false); }}>Deny</span>. <span
role="button"
tabIndex={-1}
className="modallink"
onClick={() => {
this.submitMcLink(true);
}}
>
Accept
</span>&nbsp;or&nbsp;
<span
role="button"
tabIndex={-1}
className="modallink"
onClick={() => {
this.submitMcLink(false);
}}
>
Deny
</span>.
</span> </span>
)} )}
</p> </p>
@ -110,7 +165,7 @@ function mapDispatchToProps(dispatch) {
setMCName(minecraftname) { setMCName(minecraftname) {
dispatch(setMinecraftName(minecraftname)); dispatch(setMinecraftName(minecraftname));
}, },
rem_from_messages(message) { remFromUserMessages(message) {
dispatch(remFromMessages(message)); dispatch(remFromMessages(message));
}, },
}; };

View File

@ -948,8 +948,8 @@ class VoxelPainterControls extends EventDispatcher {
*/ */
// clamp to boundaries // clamp to boundaries
const state = scope.store.getState(); const reduxState = scope.store.getState();
const { canvasSize } = state.canvas; const { canvasSize } = reduxState.canvas;
const bound = canvasSize / 2; const bound = canvasSize / 2;
scope.target.clamp({ scope.target.clamp({
x: -bound, x: -bound,

View File

@ -4,6 +4,9 @@
* *
*/ */
// Tile creation is allowed to be slow
/* eslint-disable no-await-in-loop */
import RedisCanvas from '../data/models/RedisCanvas'; import RedisCanvas from '../data/models/RedisCanvas';
import logger from './logger'; import logger from './logger';
import { getChunkOfPixel } from './utils'; import { getChunkOfPixel } from './utils';

View File

@ -4,6 +4,9 @@
* @flow * @flow
* */ * */
// Tile creation is allowed to be slow
/* eslint-disable no-await-in-loop */
import sharp from 'sharp'; import sharp from 'sharp';
import fs from 'fs'; import fs from 'fs';
@ -14,7 +17,8 @@ import { getMaxTiledZoom } from './utils';
import { TILE_SIZE, TILE_ZOOM_LEVEL } from './constants'; import { TILE_SIZE, TILE_ZOOM_LEVEL } from './constants';
/* /*
* Deletes a subtile from a tile (paints it in color 0), if we wouldn't do it, it would be black * Deletes a subtile from a tile (paints it in color 0),
* if we wouldn't do it, it would be black
* @param palette Palette to use * @param palette Palette to use
* @param subtilesInTile how many subtiles are in a tile (per dimension) * @param subtilesInTile how many subtiles are in a tile (per dimension)
* @param cell subtile to delete [dx, dy] * @param cell subtile to delete [dx, dy]
@ -32,8 +36,11 @@ function deleteSubtilefromTile(
let channelOffset = (offset + row * TILE_SIZE * subtilesInTile) * 3; let channelOffset = (offset + row * TILE_SIZE * subtilesInTile) * 3;
const max = channelOffset + TILE_SIZE * 3; const max = channelOffset + TILE_SIZE * 3;
while (channelOffset < max) { while (channelOffset < max) {
// eslint-disable-next-line prefer-destructuring
buffer[channelOffset++] = palette.rgb[0]; buffer[channelOffset++] = palette.rgb[0];
// eslint-disable-next-line prefer-destructuring
buffer[channelOffset++] = palette.rgb[1]; buffer[channelOffset++] = palette.rgb[1];
// eslint-disable-next-line prefer-destructuring
buffer[channelOffset++] = palette.rgb[2]; buffer[channelOffset++] = palette.rgb[2];
} }
} }
@ -52,7 +59,7 @@ function addRGBSubtiletoTile(
buffer: Uint8Array, buffer: Uint8Array,
) { ) {
const [dx, dy] = cell; const [dx, dy] = cell;
const chunkOffset = (dx + dy * subtilesInTile * TILE_SIZE) * TILE_SIZE; // offset in pixels const chunkOffset = (dx + dy * subtilesInTile * TILE_SIZE) * TILE_SIZE;
let pos: number = 0; let pos: number = 0;
for (let row = 0; row < TILE_SIZE; row += 1) { for (let row = 0; row < TILE_SIZE; row += 1) {
let channelOffset = (chunkOffset + row * TILE_SIZE * subtilesInTile) * 3; let channelOffset = (chunkOffset + row * TILE_SIZE * subtilesInTile) * 3;
@ -80,7 +87,7 @@ function addIndexedSubtiletoTile(
buffer: Uint8Array, buffer: Uint8Array,
) { ) {
const [dx, dy] = cell; const [dx, dy] = cell;
const chunkOffset = (dx + dy * subtilesInTile * TILE_SIZE) * TILE_SIZE; // offset in pixels const chunkOffset = (dx + dy * subtilesInTile * TILE_SIZE) * TILE_SIZE;
let pos: number = 0; let pos: number = 0;
let clr: number; let clr: number;
for (let row = 0; row < TILE_SIZE; row += 1) { for (let row = 0; row < TILE_SIZE; row += 1) {
@ -194,6 +201,7 @@ export async function createZoomedTile(
const na = []; const na = [];
for (let dy = 0; dy < TILE_ZOOM_LEVEL; dy += 1) { for (let dy = 0; dy < TILE_ZOOM_LEVEL; dy += 1) {
for (let dx = 0; dx < TILE_ZOOM_LEVEL; dx += 1) { for (let dx = 0; dx < TILE_ZOOM_LEVEL; dx += 1) {
// eslint-disable-next-line max-len
const chunkfile = `${canvasTileFolder}/${z + 1}/${x * TILE_ZOOM_LEVEL + dx}/${y * TILE_ZOOM_LEVEL + dy}.png`; const chunkfile = `${canvasTileFolder}/${z + 1}/${x * TILE_ZOOM_LEVEL + dx}/${y * TILE_ZOOM_LEVEL + dy}.png`;
if (!fs.existsSync(chunkfile)) { if (!fs.existsSync(chunkfile)) {
na.push([dx, dy]); na.push([dx, dy]);
@ -244,8 +252,11 @@ export async function createEmptyTile(
let i = 0; let i = 0;
const max = TILE_SIZE * TILE_SIZE * 3; const max = TILE_SIZE * TILE_SIZE * 3;
while (i < max) { while (i < max) {
// eslint-disable-next-line prefer-destructuring
tileRGBBuffer[i++] = palette.rgb[0]; tileRGBBuffer[i++] = palette.rgb[0];
// eslint-disable-next-line prefer-destructuring
tileRGBBuffer[i++] = palette.rgb[1]; tileRGBBuffer[i++] = palette.rgb[1];
// eslint-disable-next-line prefer-destructuring
tileRGBBuffer[i++] = palette.rgb[2]; tileRGBBuffer[i++] = palette.rgb[2];
} }
const filename = `${canvasTileFolder}/emptytile.png`; const filename = `${canvasTileFolder}/emptytile.png`;
@ -416,6 +427,7 @@ export async function initializeTiles(
} }
} }
logger.info( logger.info(
// eslint-disable-next-line max-len
`Tiling: Created ${cnts} / ${cnt} tiles for zoom ${zoom} for canvas${canvasId}`, `Tiling: Created ${cnts} / ${cnt} tiles for zoom ${zoom} for canvas${canvasId}`,
); );
} }
@ -429,6 +441,7 @@ export async function initializeTiles(
); );
//-- //--
logger.info( logger.info(
// eslint-disable-next-line max-len
`Tiling: Elapsed Time: ${Math.round((Date.now() - startTime) / 1000)} for canvas${canvasId}`, `Tiling: Elapsed Time: ${Math.round((Date.now() - startTime) / 1000)} for canvas${canvasId}`,
); );
} }

View File

@ -18,8 +18,11 @@ const logger = proxyLogger;
* @param ip IP to check * @param ip IP to check
* @return true if proxy, false if not * @return true if proxy, false if not
*/ */
// eslint-disable-next-line no-unused-vars
async function getIPIntel(ip: string): Promise<boolean> { async function getIPIntel(ip: string): Promise<boolean> {
// eslint-disable-next-line max-len
const email = `${Math.random().toString(36).substring(8)}-${Math.random().toString(36).substring(4)}@gmail.com`; const email = `${Math.random().toString(36).substring(8)}-${Math.random().toString(36).substring(4)}@gmail.com`;
// eslint-disable-next-line max-len
const url = `http://check.getipintel.net/check.php?ip=${ip}&contact=${email}&flags=m`; const url = `http://check.getipintel.net/check.php?ip=${ip}&contact=${email}&flags=m`;
logger.info(`PROXYCHECK fetching getipintel ${url}`); logger.info(`PROXYCHECK fetching getipintel ${url}`);
const response = await fetch(url, { const response = await fetch(url, {
@ -27,6 +30,7 @@ async function getIPIntel(ip: string): Promise<boolean> {
Accept: '*/*', Accept: '*/*',
'Accept-Language': 'de,en-US;q=0.7,en;q=0.3', 'Accept-Language': 'de,en-US;q=0.7,en;q=0.3',
Referer: 'http://check.getipintel.net/', Referer: 'http://check.getipintel.net/',
// eslint-disable-next-line max-len
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36',
}, },
}); });
@ -54,6 +58,7 @@ async function getProxyCheck(ip: string): Promise<boolean> {
logger.info('PROXYCHECK fetching proxycheck %s', url); logger.info('PROXYCHECK fetching proxycheck %s', url);
const response = await fetch(url, { const response = await fetch(url, {
headers: { headers: {
// eslint-disable-next-line max-len
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36',
}, },
}); });
@ -63,7 +68,7 @@ async function getProxyCheck(ip: string): Promise<boolean> {
} }
const data = await response.json(); const data = await response.json();
logger.info('PROXYCHECK proxycheck is proxy?', data); logger.info('PROXYCHECK proxycheck is proxy?', data);
return data.status == 'ok' && data[ip].proxy === 'yes'; return data.status === 'ok' && data[ip].proxy === 'yes';
} }
/* /*
@ -75,11 +80,13 @@ async function getProxyCheck(ip: string): Promise<boolean> {
*/ */
async function getShroomey(ip: string): Promise<boolean> { async function getShroomey(ip: string): Promise<boolean> {
logger.info('PROXYCHECK fetching shroomey %s', ip); logger.info('PROXYCHECK fetching shroomey %s', ip);
// eslint-disable-next-line max-len
const response = await fetch(`http://www.shroomery.org/ythan/proxycheck.php?ip=${ip}`, { const response = await fetch(`http://www.shroomery.org/ythan/proxycheck.php?ip=${ip}`, {
headers: { headers: {
Accept: '*/*', Accept: '*/*',
'Accept-Language': 'es-ES,es;q=0.8,en;q=0.6', 'Accept-Language': 'es-ES,es;q=0.8,en;q=0.6',
Referer: 'http://www.shroomery.org/', Referer: 'http://www.shroomery.org/',
// eslint-disable-next-line max-len
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36',
}, },
}); });
@ -89,21 +96,6 @@ async function getShroomey(ip: string): Promise<boolean> {
return body === 'Y'; return body === 'Y';
} }
/*
* check shroomey, and if positive there, check
* getipintel
* @param ip IP to check
* @return true if proxy, false if not
*/
async function getCombined(ip: string): Promise<boolean> {
if (ip.indexOf(':') == -1) {
const shroom = await getShroomey(ip);
if (!shroom) return false;
}
const ipintel = await getIPIntel(ip);
return ipintel;
}
/* /*
* check MYSQL Blacklist table * check MYSQL Blacklist table
* @param ip IP to check * @param ip IP to check
@ -150,7 +142,14 @@ async function dummy(): Promise<boolean> {
async function withoutCache(f, ip) { async function withoutCache(f, ip) {
if (!ip) return true; if (!ip) return true;
const ipKey = getIPv6Subnet(ip); const ipKey = getIPv6Subnet(ip);
return !(await isWhitelisted(ipKey)) && (await isBlacklisted(ipKey) || await f(ip)); if (await isWhitelisted(ipKey)) {
return false;
}
if (await isBlacklisted(ipKey)) {
return true;
}
const result = f(ip);
return result;
} }
/* /*
@ -170,7 +169,12 @@ async function withCache(f, ip) {
const cache = await redis.getAsync(key); const cache = await redis.getAsync(key);
if (cache) { if (cache) {
const str = cache.toString('utf8'); const str = cache.toString('utf8');
logger.debug('PROXYCHECK fetch isproxy from cache %s %s %s %s %s', key, cache, typeof cache, str, typeof str); logger.debug('PROXYCHECK fetch isproxy from cache %s %s %s %s %s',
key,
cache,
typeof cache,
str,
typeof str);
return str === 'y'; return str === 'y';
} }
logger.debug('PROXYCHECK fetch isproxy not from cache %s', key); logger.debug('PROXYCHECK fetch isproxy not from cache %s', key);
@ -178,7 +182,7 @@ async function withCache(f, ip) {
// else make asynchronous ipcheck and assume no proxy in the meantime // else make asynchronous ipcheck and assume no proxy in the meantime
// use lock to just check three at a time // use lock to just check three at a time
// do not check ip that currently gets checked // do not check ip that currently gets checked
if (checking.indexOf(ipKey) == -1 && lock > 0) { if (checking.indexOf(ipKey) === -1 && lock > 0) {
lock -= 1; lock -= 1;
checking.push(ipKey); checking.push(ipKey);
withoutCache(f, ip) withoutCache(f, ip)
@ -199,16 +203,16 @@ async function withCache(f, ip) {
return false; return false;
} }
export async function cheapDetector(ip: string): Promise<boolean> { export function cheapDetector(ip: string): Promise<boolean> {
return (await withCache(getProxyCheck, ip)); return withCache(getProxyCheck, ip);
} }
export async function strongDetector(ip: string): Promise<boolean> { export function strongDetector(ip: string): Promise<boolean> {
return (await withCache(getShroomey, ip)); return withCache(getShroomey, ip);
} }
export async function blacklistDetector(ip: string): Promise<boolean> { export function blacklistDetector(ip: string): Promise<boolean> {
return (await withCache(dummy, ip)); return withCache(dummy, ip);
} }
// export default cheapDetector; // export default cheapDetector;

View File

@ -15,7 +15,7 @@ class Minecraft {
this.online = {}; this.online = {};
} }
async report_login(minecraftid, minecraftname) { async reportLogin(minecraftid, minecraftname) {
const user = new User(); const user = new User();
user.minecraftname = minecraftname; user.minecraftname = minecraftname;
const reguser = await RegUser.findOne({ where: { minecraftid } }); const reguser = await RegUser.findOne({ where: { minecraftid } });
@ -35,26 +35,27 @@ class Minecraft {
} }
*/ */
report_logout(minecraftid) { reportLogout(minecraftid) {
delete this.online[minecraftid]; delete this.online[minecraftid];
} }
report_userlist(list) { reportUserlist(list) {
this.online = {}; this.online = {};
list.forEach((user) => { list.forEach((user) => {
const [minecraftid, minecraftname] = user; const [minecraftid, minecraftname] = user;
this.report_login(minecraftid, minecraftname); this.reportLogin(minecraftid, minecraftname);
}); });
} }
async linkacc(minecraftid, minecraftname, name) { static async linkacc(minecraftid, minecraftname, name) {
try { try {
const finduser = await RegUser.findOne({ where: { minecraftid } }); const finduser = await RegUser.findOne({ where: { minecraftid } });
if (finduser) { if (finduser) {
if (finduser.name == name) { if (finduser.name === name) {
if (finduser.mcVerified) { if (finduser.mcVerified) {
return 'You are already verified'; return 'You are already verified';
} }
// eslint-disable-next-line max-len
return 'You already got a verification message in the pixelplanet UserArea. Please refresh the page if you do not see it.'; return 'You already got a verification message in the pixelplanet UserArea. Please refresh the page if you do not see it.';
} }
return `You already linked to other account ${finduser.name}.`; return `You already linked to other account ${finduser.name}.`;
@ -62,6 +63,7 @@ class Minecraft {
const reguser = await RegUser.findOne({ where: { name } }); const reguser = await RegUser.findOne({ where: { name } });
if (reguser) { if (reguser) {
if (reguser.minecraftid) { if (reguser.minecraftid) {
// eslint-disable-next-line max-len
return `This pixelplanet account is already linked to ${reguser.minecraftname}`; return `This pixelplanet account is already linked to ${reguser.minecraftname}`;
} }
reguser.update({ minecraftname, minecraftid }); reguser.update({ minecraftname, minecraftid });
@ -95,8 +97,11 @@ class Minecraft {
minecraftname2User(minecraftname: string): User { minecraftname2User(minecraftname: string): User {
const searchstring = minecraftname; const searchstring = minecraftname;
for (const [minecraftid, user] of Object.entries(this.online)) { const onlineIds = Object.keys(this.online);
if (user.minecraftname == searchstring) { return user; } for (let i = 0; i < onlineIds.length; i += 1) {
const id = onlineIds[i];
const user = this.online[id];
if (user.minecraftname === searchstring) { return user; }
} }
const user = new User(); const user = new User();

View File

@ -62,7 +62,7 @@ async function exportVox(
return zc - 1; return zc - 1;
} }
const zcMax = zc + VOX_OBJECT_CSIZE / 2 + 1; const zcMax = zc + VOX_OBJECT_CSIZE / 2 + 1;
return (zcMin <= posMax) ? zcMax : zc; return (zcMax <= posMax) ? zcMax : zc;
}); });
// Size Chunk // Size Chunk
@ -79,7 +79,7 @@ async function exportVox(
// 4 bytes numVoxels // 4 bytes numVoxels
// 4 bytes (x, y, z, clr) for every voxel // 4 bytes (x, y, z, clr) for every voxel
let numVoxels = 0; let numVoxels = 0;
for (let j = yxcMin; j <= ycMax; j += 1) { for (let j = ycMin; j <= ycMax; j += 1) {
for (let i = xcMin; i <= xcMax; i += 1) { for (let i = xcMin; i <= xcMax; i += 1) {
const key = `${i}:${j}`; const key = `${i}:${j}`;
const { buffer } = chunks.get(key); const { buffer } = chunks.get(key);

View File

@ -18,6 +18,7 @@ export type GUIState = {
paletteOpen: boolean, paletteOpen: boolean,
menuOpen: boolean, menuOpen: boolean,
chatChannel: number, chatChannel: number,
style: string,
}; };
const initialState: GUIState = { const initialState: GUIState = {
@ -33,6 +34,7 @@ const initialState: GUIState = {
paletteOpen: true, paletteOpen: true,
menuOpen: false, menuOpen: false,
chatChannel: 0, chatChannel: 0,
style: 'default',
}; };
@ -97,6 +99,14 @@ export default function gui(
}; };
} }
case 'SELECT_STYLE': {
const { style } = action;
return {
...state,
style,
};
}
case 'SET_CHAT_CHANNEL': { case 'SET_CHAT_CHANNEL': {
return { return {
...state, ...state,

View File

@ -8,13 +8,11 @@ import type { Action } from '../actions/types';
export type ModalState = { export type ModalState = {
modalType: ?string, modalType: ?string,
modalProps: object,
chatOpen: boolean, chatOpen: boolean,
}; };
const initialState: ModalState = { const initialState: ModalState = {
modalType: null, modalType: null,
modalProps: {},
chatOpen: false, chatOpen: false,
}; };
@ -27,12 +25,11 @@ export default function modal(
// clear hover when placing a pixel // clear hover when placing a pixel
// fixes a bug with iPad // fixes a bug with iPad
case 'SHOW_MODAL': { case 'SHOW_MODAL': {
const { modalType, modalProps } = action; const { modalType } = action;
const chatOpen = (modalType === 'CHAT') ? false : state.chatOpen; const chatOpen = (modalType === 'CHAT') ? false : state.chatOpen;
return { return {
...state, ...state,
modalType, modalType,
modalProps,
chatOpen, chatOpen,
}; };
} }
@ -42,7 +39,6 @@ export default function modal(
return { return {
...state, ...state,
modalType: null, modalType: null,
modalProps: {},
}; };
case 'TOGGLE_CHAT_BOX': { case 'TOGGLE_CHAT_BOX': {

View File

@ -19,7 +19,7 @@ function validate(newPassword) {
} }
export default async (req: Request, res: Response) => { export default async (req: Request, res: Response) => {
const { new_password: newPassword, password } = req.body; const { newPassword, password } = req.body;
const errors = validate(newPassword); const errors = validate(newPassword);
if (errors.length > 0) { if (errors.length > 0) {
res.status(400); res.status(400);

View File

@ -45,23 +45,35 @@ router.use(bodyParser.urlencoded({ extended: true }));
* Check for POST parameters, * Check for POST parameters,
* if invalid password is given, ignore it and go to next * if invalid password is given, ignore it and go to next
*/ */
router.post('/', async (req: Request, res: Response, next) => { router.post('/', async (req: Request, res: Response) => {
const { pass, passconf, code } = req.body; const { pass, passconf, code } = req.body;
if (!pass || !passconf || !code) { if (!pass || !passconf || !code) {
const html = getPasswordResetHtml(null, null, 'You sent an empty password or invalid data :('); const html = getPasswordResetHtml(
null,
null,
'You sent an empty password or invalid data :(',
);
res.status(400).send(html); res.status(400).send(html);
return; return;
} }
const email = mailProvider.checkCode(code); const email = mailProvider.checkCode(code);
if (!email) { if (!email) {
const html = getPasswordResetHtml(null, null, "This password-reset link isn't valid anymore :("); const html = getPasswordResetHtml(
null,
null,
"This password-reset link isn't valid anymore :(",
);
res.status(401).send(html); res.status(401).send(html);
return; return;
} }
if (pass != passconf) { if (pass !== passconf) {
const html = getPasswordResetHtml(null, null, 'Your passwords do not match :('); const html = getPasswordResetHtml(
null,
null,
'Your passwords do not match :(',
);
res.status(400).send(html); res.status(400).send(html);
return; return;
} }
@ -69,15 +81,24 @@ router.post('/', async (req: Request, res: Response, next) => {
// set password // set password
const reguser = await RegUser.findOne({ where: { email } }); const reguser = await RegUser.findOne({ where: { email } });
if (!reguser) { if (!reguser) {
// eslint-disable-next-line max-len
logger.error(`${email} from PasswordReset page does not exist in database`); logger.error(`${email} from PasswordReset page does not exist in database`);
const html = getPasswordResetHtml(null, null, "User doesn't exist in our database :("); const html = getPasswordResetHtml(
null,
null,
"User doesn't exist in our database :(",
);
res.status(400).send(html); res.status(400).send(html);
return; return;
} }
await reguser.update({ password: pass }); await reguser.update({ password: pass });
logger.info(`Changed password of ${email} via passowrd reset form`); logger.info(`Changed password of ${email} via passowrd reset form`);
const html = getPasswordResetHtml(null, null, 'Passowrd successfully changed.'); const html = getPasswordResetHtml(
null,
null,
'Passowrd successfully changed.',
);
res.status(200).send(html); res.status(200).send(html);
}); });
@ -85,17 +106,26 @@ router.post('/', async (req: Request, res: Response, next) => {
/* /*
* Check GET parameters for action to execute * Check GET parameters for action to execute
*/ */
router.get('/', async (req: Request, res: Response, next) => { router.get('/', async (req: Request, res: Response) => {
const { token } = req.query; const { token } = req.query;
if (!token) { if (!token) {
const html = getPasswordResetHtml(null, null, 'Invalid url :( Please check your mail again.'); const html = getPasswordResetHtml(
null,
null,
'Invalid url :( Please check your mail again.',
);
res.status(400).send(html); res.status(400).send(html);
return; return;
} }
const email = mailProvider.checkCode(token); const email = mailProvider.checkCode(token);
if (!email) { if (!email) {
const html = getPasswordResetHtml(null, null, 'This passwort reset link is wrong or already expired, please request a new one (Note: you can use those links just once)'); const html = getPasswordResetHtml(
null,
null,
// eslint-disable-next-line max-len
'This passwort reset link is wrong or already expired, please request a new one (Note: you can use those links just once)',
);
res.status(401).send(html); res.status(401).send(html);
return; return;
} }

View File

@ -214,7 +214,7 @@ class APISocketServer extends WebSocketEvents {
logger.info(`APISocket message ${message}`); logger.info(`APISocket message ${message}`);
if (command === 'login') { if (command === 'login') {
const [minecraftid, minecraftname, ip] = packet; const [minecraftid, minecraftname, ip] = packet;
const user = await this.mc.report_login(minecraftid, minecraftname); const user = await this.mc.reportLogin(minecraftid, minecraftname);
// get userinfo // get userinfo
user.ip = ip; user.ip = ip;
const wait = await user.getWait(0); const wait = await user.getWait(0);
@ -234,12 +234,12 @@ class APISocketServer extends WebSocketEvents {
logger.error('Got invalid minecraft userlist on APISocketServer'); logger.error('Got invalid minecraft userlist on APISocketServer');
return; return;
} }
this.mc.report_userlist(userlist); this.mc.reportUserlist(userlist);
return; return;
} }
if (command === 'logout') { if (command === 'logout') {
const [minecraftid] = packet; const [minecraftid] = packet;
this.mc.report_logout(minecraftid); this.mc.reportLogout(minecraftid);
return; return;
} }
if (command === 'mcchat') { if (command === 'mcchat') {
@ -260,7 +260,7 @@ class APISocketServer extends WebSocketEvents {
} }
if (command === 'linkacc') { if (command === 'linkacc') {
const [minecraftid, minecraftname, name] = packet; const [minecraftid, minecraftname, name] = packet;
const ret = await this.mc.linkacc(minecraftid, minecraftname, name); const ret = await Minecraft.linkacc(minecraftid, minecraftname, name);
if (!ret) { if (!ret) {
webSockets.notifyChangedMe(name); webSockets.notifyChangedMe(name);
} }

View File

@ -3,6 +3,9 @@
* @flow * @flow
*/ */
// allow the websocket to be noisy on the console
/* eslint-disable no-console */
import EventEmitter from 'events'; import EventEmitter from 'events';
import CoolDownPacket from './packets/CoolDownPacket'; import CoolDownPacket from './packets/CoolDownPacket';
@ -44,9 +47,9 @@ class ProtocolClient extends EventEmitter {
console.log('WebSocket already open, not starting'); console.log('WebSocket already open, not starting');
} }
this.timeConnected = Date.now(); this.timeConnected = Date.now();
const protocol = location.protocol === 'https:' ? 'wss:' : 'ws:'; const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
const url = `${protocol}//${location.hostname}${ const url = `${protocol}//${window.location.hostname}${
location.port ? `:${location.port}` : '' window.location.port ? `:${window.location.port}` : ''
}/ws`; }/ws`;
this.ws = new WebSocket(url); this.ws = new WebSocket(url);
this.ws.binaryType = 'arraybuffer'; this.ws.binaryType = 'arraybuffer';

View File

@ -9,6 +9,7 @@ import audio from './audio';
import swal from './sweetAlert'; import swal from './sweetAlert';
import protocolClientHook from './protocolClientHook'; import protocolClientHook from './protocolClientHook';
import rendererHook from './rendererHook'; import rendererHook from './rendererHook';
import styleHook from './styleHook';
// import ads from './ads'; // import ads from './ads';
// import analytics from './analytics'; // import analytics from './analytics';
import array from './array'; import array from './array';
@ -40,6 +41,7 @@ const store = createStore(
title, title,
protocolClientHook, protocolClientHook,
rendererHook, rendererHook,
styleHook,
// ads, // ads,
// analytics, // analytics,
logger, logger,
@ -49,7 +51,9 @@ const store = createStore(
export default function configureStore(onComplete: ?() => void) { export default function configureStore(onComplete: ?() => void) {
persistStore(store, null, onComplete); persistStore(store, null, () => {
onComplete(store);
});
if (isDebuggingInChrome) { if (isDebuggingInChrome) {
window.store = store; window.store = store;
} }

22
src/store/styleHook.js Normal file
View File

@ -0,0 +1,22 @@
/*
* @flow
*/
import setStyle from '../ui/setStyle';
export default () => (next) => (action) => {
switch (action.type) {
case 'SELECT_STYLE': {
const {
style,
} = action;
setStyle(style);
break;
}
default:
// nothing
}
return next(action);
};

View File

@ -3,7 +3,6 @@
*/ */
import swal from 'sweetalert2'; import swal from 'sweetalert2';
import 'sweetalert2/src/sweetalert2.scss';
export default () => (next) => (action) => { export default () => (next) => (action) => {
switch (action.type) { switch (action.type) {

484
src/styles/dark-round.css Normal file
View File

@ -0,0 +1,484 @@
body {
margin: 0;
font-family: 'Montserrat', sans-serif;
font-size: 16px;
border: none;
user-select: none;
background: white;
}
html, body {
position: fixed;
top: 0;
left: 0;
bottom: 0;
right: 0;
overflow: hidden;
}
/**
* https://github.com/benweet/stackedit/issues/212
*/
kbd {
padding: 0.1em 0.6em;
border: 1px solid #ccc;
font-size: 11px;
font-family: Arial,Helvetica,sans-serif;
background-color: #f7f7f7;
color: #333;
-moz-box-shadow: 0 1px 0px rgba(0, 0, 0, 0.2),0 0 0 2px #ffffff inset;
-webkit-box-shadow: 0 1px 0px rgba(0, 0, 0, 0.2),0 0 0 2px #ffffff inset;
box-shadow: 0 1px 0px rgba(0, 0, 0, 0.2),0 0 0 2px #ffffff inset;
-moz-border-radius: 3px;
-webkit-border-radius: 3px;
display: inline-block;
margin: 0 0.1em;
text-shadow: 0 1px 0 #fff;
line-height: 1.4;
white-space: nowrap;
}
a:link {
color: #91ffe0;
}
a:visited {
color: #b5d06d;
}
a:hover {
color: #d9f68a;
}
.modallink {
text-decoration: none;
color: #91ffe0;
cursor: pointer;
}
.modallink:hover){
font-weight: bold;
color: #d9f68a;
}
.inarea {
border-style: solid;
border-color: #d5d5d5;
padding: 4px;
margin-top: 4px;
border-width: 1px;
}
.tab-list {
border-bottom: 1px solid #ccc;
padding-left: 0;
}
.tab-list-item {
display: inline-block;
list-style: none;
margin-bottom: -1px;
padding: 0.5rem 0.75rem;
}
.tab-list-active {
background-color: #7b7b7b;
border: solid #ccc;
border-width: 1px 1px 0 1px;
}
table {
font-family: arial, sans-serif;
border-collapse: collapse;
width: 100%;
}
td, th {
border: 1px solid #dddddd;
text-align: left;
padding: 8px;
}
tr:nth-child(even) {
background-color: #505050;
}
.chatbox {
position: absolute;
background-color: rgba(59, 59, 59, 0.8);
width: 350px;
height: 200px;
bottom: 16px;
right: 98px;
border: solid black;
border-width: thin;
}
.actionbuttons, .coorbox, .onlinebox, .cooldownbox, .palettebox {
position: absolute;
background-color: rgba(59, 59, 59, 0.8);
color: #f4f4f4;
text-align: center;
vertical-align: middle;
line-height: 36px;
height: 36px;
width: auto;
padding: 0 16px;
border: solid black;
border-radius: 21px;
border-width: thin;
}
.coorbox {
left: 16px;
cursor: pointer;
bottom: 16px;
}
.onlinebox {
left: 16px;
bottom: 57px;
white-space: nowrap;
}
#menubutton {
left: 16px;
top: 16px;
}
#menu > div {
z-index: 1;
background-color: #15374fd1;
}
#helpbutton {
left: 16px;
top: 221px;
}
#minecraftbutton {
left: 16px;
top: 180px;
}
#settingsbutton {
left: 16px;
top: 57px;
}
#loginbutton {
left: 16px;
top: 98px;
}
#downloadbutton {
left: 16px;
top: 139px;
}
#globebutton {
left: 98px;
top: 16px;
}
#voxelbutton {
left: 180px;
top: 16px;
}
#canvasbutton {
left: 57px;
top: 16px;
}
#minecrafttpbutton {
top: 16px;
right: 16px;
}
#palselbutton {
bottom: 16px;
right: 16px;
}
#chatbutton {
bottom: 16px;
right: 57px;
}
#historyselect {
position: absolute;
top: 16px;
left: 0px;
right: 0px;
height: 70px;
width: 140px;
background-color: rgba(226, 226, 226, 0.80);
color: black;
font-size: 14px;
line-height: 26px;
text-align: center;
border: solid black;
border-width: thin;
padding: 0 24px;
margin-left: auto;
margin-right: auto;
z-index: 2;
}
.hsar {
padding: 0;
}
.Modal {
position: absolute;
top: 50%;
left: 50%;
right: auto;
bottom: auto;
border: 1px solid rgb(204, 204, 204);
background: #444242 none repeat scroll 0 0;;
color: #f4f4f4;
overflow-y: auto;
border-radius: 4px;
outline: currentcolor none medium;
transform: translate(-50%, -50%);
height: 80%;
max-height: 900px;
width: 70%;
transition: all 0.5s ease 0s;
border-radius: 21px;
}
.modaltext, .modalcotext {
color: #f4f4f4;
font-size: 14px;
font-weight: 500;
line-height: normal;
position: relative;
text-align: inherit;
float: none;
}
.modaltext {
margin: 0;
padding: 0;
}
.modaltitle {
color: #d7e5fb;
font-weight: 500;
font-size: 16px;
line-height: 24px;
margin-left: 0;
margin-right: 10px;
overflow: hidden;
word-wrap: break-word;
margin-bottom: 0;
}
.modaldesc {
box-sizing: border-box;
flex: 1 1 auto;
color: hsla(220, 100%, 95.3%, 0.6);
font-size: 14px;
line-height: 20px;
font-weight: 500;
margin-top: 4px;
}
.modaldivider {
box-sizing: border-box;
margin-top: 20px;
height: 1px;
width: 100%;
background-color: hsla(216, 4%, 74%, .3);
}
.modalinfo {
color: #ddd;
font-size: 15px;
font-weight: 500;
position: relative;
text-align: inherit;
float: none;
margin: 0;
padding: 0;
}
.modalcvtext {
color: hsla(220, 100%, 95.3%, 0.6);
font-size: 14px;
font-weight: 500;
padding: 0;
display: inline-block;
vertical-align: middle;
margin-top: 3px;
margin-bottom: 3px;
width: 75%;
}
.ModalClose {
position: fixed;
display: flex;
justify-content: center;
align-items: center;
flex: 0 0 36px;
border-width: 2px;
border-style: solid;
border-radius: 50%;
width: 36px;
height: 36px;
cursor: pointer;
background-color: #55555d;
border-color: #dcddde;
top: 30px;
right: 40px;
}
@media (max-width: 604px) {
.Modal {
position: fixed;
top: 0px;
left: 0px;
right: 0px;
bottom: 0px;
height: 95%;
width: 90%;
transform: none;
max-width: none;
max-height: none;
padding: 5%;
}
}
.Overlay {
position: fixed;
top: 0px;
left: 0px;
right: 0px;
bottom: 0px;
background-color: rgba(255, 255, 255, 0.75);
z-index: 4;
}
.chatbox div .chatarea {
height: 174px;
}
.chatarea {
padding: 3px 3px 0px 3px;
margin: 0px;
overflow-x: hidden;
overflow-y: scroll;
height: 95%;
}
.chatinput {
height: 22px;
white-space: nowrap;
}
.chatname {
color: #4B0000;
font-size: 13px;
user-select: all;
}
.chatmsg {
color: white;
font-size: 13px;
user-select: text;
margin: 0;
}
.usermessages {
font-size: 14px;
font-weight: 500;
line-height: normal;
}
.stattext {
font-size: 18px;
}
.statvalue {
font-size: 18px;
font-weight: bold;
color: #ecc9ff;
}
.pressed {
box-shadow:0 0 3px 2px rgba(0,0,0,.6);
}
.notifyboxvis, .notifyboxhid {
background-color: rgba(226, 226, 226, 0.80);
position: absolute;
top: 57px;
left: 0px;
right: 0px;
height: 30px;
width: 20px;
color: black;
font-size: 14px;
line-height: 30px;
text-align: center;
vertical-align: middle;
border: solid black;
border-width: thin;
padding: 0 24px;
margin-left: auto;
margin-right: auto;
z-index: 2;
}
.notifyboxvis {
visibility: visible;
opacity: 1;
transition: visibility 0s, opacity 0.5s linear;
}
.notifyboxhid {
visibility: hidden;
opacity: 0;
transition: visibility 0.5s, opacity 0.5s linear;
}
.cooldownbox {
top: 16px;
width: 48px;
left: 0px;
right: 0px;
margin-left: auto;
margin-right: auto;
z-index: 2;
}
.actionbuttons {
vertical-align: text-bottom;
cursor: pointer;
width: 36px;
padding: 0;
}
.actionbuttons:hover, .coorbox:hover, #menu > div:hover {
background-color: #363637;
}
.palettebox {
z-index: 1;
bottom: 59px;
padding: 3px;
position: fixed;
right: 16px;
margin-left: auto;
margin-right: auto;
border-radius: 0;
}
#colors .selected,
#colors span:hover {
z-index: 2 !important;
outline: rgb(234, 234, 234) solid 1px;
box-shadow: rgba(0, 0, 0, 0.80) 0px 0px 5px 2px;
-ms-transform: scale(1.10,1.10); /* IE 9 */
-webkit-transform: scale(1.10,1.10); /* Safari */
transform: scale(1.10,1.10); /* Standard syntax */
}
#outstreamContainer {
position: fixed;
display: none;
width: 100%;
height: 100%;
top: 0px;
left: 0px;
right: 0px;
bottom: 0px;
background-color: black;
z-index: 9000;
}
.grecaptcha-badge {
visibility: hidden;
}

481
src/styles/dark.css Normal file
View File

@ -0,0 +1,481 @@
body {
margin: 0;
font-family: 'Montserrat', sans-serif;
font-size: 16px;
border: none;
user-select: none;
background: white;
}
html, body {
position: fixed;
top: 0;
left: 0;
bottom: 0;
right: 0;
overflow: hidden;
}
/**
* https://github.com/benweet/stackedit/issues/212
*/
kbd {
padding: 0.1em 0.6em;
border: 1px solid #ccc;
font-size: 11px;
font-family: Arial,Helvetica,sans-serif;
background-color: #f7f7f7;
color: #333;
-moz-box-shadow: 0 1px 0px rgba(0, 0, 0, 0.2),0 0 0 2px #ffffff inset;
-webkit-box-shadow: 0 1px 0px rgba(0, 0, 0, 0.2),0 0 0 2px #ffffff inset;
box-shadow: 0 1px 0px rgba(0, 0, 0, 0.2),0 0 0 2px #ffffff inset;
-moz-border-radius: 3px;
-webkit-border-radius: 3px;
display: inline-block;
margin: 0 0.1em;
text-shadow: 0 1px 0 #fff;
line-height: 1.4;
white-space: nowrap;
}
a:link {
color: #91ffe0;
}
a:visited {
color: #b5d06d;
}
a:hover {
color: #d9f68a;
}
.modallink {
text-decoration: none;
color: #91ffe0;
cursor: pointer;
}
.modallink:hover){
font-weight: bold;
color: #d9f68a;
}
.inarea {
border-style: solid;
border-color: #d5d5d5;
padding: 4px;
margin-top: 4px;
border-width: 1px;
}
.tab-list {
border-bottom: 1px solid #ccc;
padding-left: 0;
}
.tab-list-item {
display: inline-block;
list-style: none;
margin-bottom: -1px;
padding: 0.5rem 0.75rem;
}
.tab-list-active {
background-color: #7b7b7b;
border: solid #ccc;
border-width: 1px 1px 0 1px;
}
table {
font-family: arial, sans-serif;
border-collapse: collapse;
width: 100%;
}
td, th {
border: 1px solid #dddddd;
text-align: left;
padding: 8px;
}
tr:nth-child(even) {
background-color: #505050;
}
.chatbox {
position: absolute;
background-color: rgba(59, 59, 59, 0.8);
width: 350px;
height: 200px;
bottom: 16px;
right: 98px;
border: solid black;
border-width: thin;
}
.actionbuttons, .coorbox, .onlinebox, .cooldownbox, .palettebox {
position: absolute;
background-color: rgba(59, 59, 59, 0.8);
color: #f4f4f4;
text-align: center;
vertical-align: middle;
line-height: 36px;
height: 36px;
width: auto;
padding: 0 16px;
border: solid black;
border-width: thin;
}
.coorbox {
left: 16px;
cursor: pointer;
bottom: 16px;
}
.onlinebox {
left: 16px;
bottom: 57px;
white-space: nowrap;
}
#menubutton {
left: 16px;
top: 16px;
}
#menu > div {
z-index: 1;
background-color: #15374fd1;
}
#helpbutton {
left: 16px;
top: 221px;
}
#minecraftbutton {
left: 16px;
top: 180px;
}
#settingsbutton {
left: 16px;
top: 57px;
}
#loginbutton {
left: 16px;
top: 98px;
}
#downloadbutton {
left: 16px;
top: 139px;
}
#globebutton {
left: 98px;
top: 16px;
}
#voxelbutton {
left: 180px;
top: 16px;
}
#canvasbutton {
left: 57px;
top: 16px;
}
#minecrafttpbutton {
top: 16px;
right: 16px;
}
#palselbutton {
bottom: 16px;
right: 16px;
}
#chatbutton {
bottom: 16px;
right: 57px;
}
#historyselect {
position: absolute;
top: 16px;
left: 0px;
right: 0px;
height: 70px;
width: 140px;
background-color: rgba(226, 226, 226, 0.80);
color: black;
font-size: 14px;
line-height: 26px;
text-align: center;
border: solid black;
border-width: thin;
padding: 0 24px;
margin-left: auto;
margin-right: auto;
z-index: 2;
}
.hsar {
padding: 0;
}
.Modal {
position: absolute;
top: 50%;
left: 50%;
right: auto;
bottom: auto;
border: 1px solid rgb(204, 204, 204);
background: #444242 none repeat scroll 0 0;;
color: #f4f4f4;
overflow-y: auto;
border-radius: 4px;
outline: currentcolor none medium;
transform: translate(-50%, -50%);
height: 80%;
max-height: 900px;
width: 70%;
transition: all 0.5s ease 0s;
}
.modaltext, .modalcotext {
color: #f4f4f4;
font-size: 14px;
font-weight: 500;
line-height: normal;
position: relative;
text-align: inherit;
float: none;
}
.modaltext {
margin: 0;
padding: 0;
}
.modaltitle {
color: #d7e5fb;
font-weight: 500;
font-size: 16px;
line-height: 24px;
margin-left: 0;
margin-right: 10px;
overflow: hidden;
word-wrap: break-word;
margin-bottom: 0;
}
.modaldesc {
box-sizing: border-box;
flex: 1 1 auto;
color: hsla(220, 100%, 95.3%, 0.6);
font-size: 14px;
line-height: 20px;
font-weight: 500;
margin-top: 4px;
}
.modaldivider {
box-sizing: border-box;
margin-top: 20px;
height: 1px;
width: 100%;
background-color: hsla(216, 4%, 74%, .3);
}
.modalinfo {
color: #ddd;
font-size: 15px;
font-weight: 500;
position: relative;
text-align: inherit;
float: none;
margin: 0;
padding: 0;
}
.modalcvtext {
color: hsla(220, 100%, 95.3%, 0.6);
font-size: 14px;
font-weight: 500;
padding: 0;
display: inline-block;
vertical-align: middle;
margin-top: 3px;
margin-bottom: 3px;
width: 75%;
}
.ModalClose {
position: fixed;
display: flex;
justify-content: center;
align-items: center;
flex: 0 0 36px;
border-width: 2px;
border-style: solid;
border-radius: 50%;
width: 36px;
height: 36px;
cursor: pointer;
background-color: #55555d;
border-color: #dcddde;
top: 30px;
right: 40px;
}
@media (max-width: 604px) {
.Modal {
position: fixed;
top: 0px;
left: 0px;
right: 0px;
bottom: 0px;
height: 95%;
width: 90%;
transform: none;
max-width: none;
max-height: none;
padding: 5%;
}
}
.Overlay {
position: fixed;
top: 0px;
left: 0px;
right: 0px;
bottom: 0px;
background-color: rgba(255, 255, 255, 0.75);
z-index: 4;
}
.chatbox div .chatarea {
height: 174px;
}
.chatarea {
padding: 3px 3px 0px 3px;
margin: 0px;
overflow-x: hidden;
overflow-y: scroll;
height: 95%;
}
.chatinput {
height: 22px;
white-space: nowrap;
}
.chatname {
color: #4B0000;
font-size: 13px;
user-select: all;
}
.chatmsg {
color: white;
font-size: 13px;
user-select: text;
margin: 0;
}
.usermessages {
font-size: 14px;
font-weight: 500;
line-height: normal;
}
.stattext {
font-size: 18px;
}
.statvalue {
font-size: 18px;
font-weight: bold;
color: #ecc9ff;
}
.pressed {
box-shadow:0 0 3px 2px rgba(0,0,0,.6);
}
.notifyboxvis, .notifyboxhid {
background-color: rgba(226, 226, 226, 0.80);
position: absolute;
top: 57px;
left: 0px;
right: 0px;
height: 30px;
width: 20px;
color: black;
font-size: 14px;
line-height: 30px;
text-align: center;
vertical-align: middle;
border: solid black;
border-width: thin;
padding: 0 24px;
margin-left: auto;
margin-right: auto;
z-index: 2;
}
.notifyboxvis {
visibility: visible;
opacity: 1;
transition: visibility 0s, opacity 0.5s linear;
}
.notifyboxhid {
visibility: hidden;
opacity: 0;
transition: visibility 0.5s, opacity 0.5s linear;
}
.cooldownbox {
top: 16px;
width: 48px;
left: 0px;
right: 0px;
margin-left: auto;
margin-right: auto;
z-index: 2;
}
.actionbuttons {
vertical-align: text-bottom;
cursor: pointer;
width: 36px;
padding: 0;
}
.actionbuttons:hover, .coorbox:hover, #menu > div:hover {
background-color: #363637;
}
.palettebox {
z-index: 1;
bottom: 59px;
padding: 3px;
position: fixed;
right: 16px;
margin-left: auto;
margin-right: auto;
}
#colors .selected,
#colors span:hover {
z-index: 2 !important;
outline: rgb(234, 234, 234) solid 1px;
box-shadow: rgba(0, 0, 0, 0.80) 0px 0px 5px 2px;
-ms-transform: scale(1.10,1.10); /* IE 9 */
-webkit-transform: scale(1.10,1.10); /* Safari */
transform: scale(1.10,1.10); /* Standard syntax */
}
#outstreamContainer {
position: fixed;
display: none;
width: 100%;
height: 100%;
top: 0px;
left: 0px;
right: 0px;
bottom: 0px;
background-color: black;
z-index: 9000;
}
.grecaptcha-badge {
visibility: hidden;
}

View File

@ -38,17 +38,17 @@ kbd {
white-space: nowrap; white-space: nowrap;
} }
:global(.modallink) { .modallink {
text-decoration: none; text-decoration: none;
color: #428bca; color: #428bca;
cursor: pointer; cursor: pointer;
} }
:global(.modallink:hover){ .modallink:hover){
font-weight: bold; font-weight: bold;
color: #226baa; color: #226baa;
} }
:global(.inarea) { .inarea {
border-style: solid; border-style: solid;
border-color: #d5d5d5; border-color: #d5d5d5;
padding: 4px; padding: 4px;
@ -56,41 +56,41 @@ kbd {
border-width: 1px; border-width: 1px;
} }
:global(.tab-list) { .tab-list {
border-bottom: 1px solid #ccc; border-bottom: 1px solid #ccc;
padding-left: 0; padding-left: 0;
} }
:global(.tab-list-item) { .tab-list-item {
display: inline-block; display: inline-block;
list-style: none; list-style: none;
margin-bottom: -1px; margin-bottom: -1px;
padding: 0.5rem 0.75rem; padding: 0.5rem 0.75rem;
} }
:global(.tab-list-active) { .tab-list-active {
background-color: white; background-color: white;
border: solid #ccc; border: solid #ccc;
border-width: 1px 1px 0 1px; border-width: 1px 1px 0 1px;
} }
:global(table) { table {
font-family: arial, sans-serif; font-family: arial, sans-serif;
border-collapse: collapse; border-collapse: collapse;
width: 100%; width: 100%;
} }
:global(td, th) { td, th {
border: 1px solid #dddddd; border: 1px solid #dddddd;
text-align: left; text-align: left;
padding: 8px; padding: 8px;
} }
:global(tr:nth-child(even)) { tr:nth-child(even) {
background-color: #dddddd; background-color: #dddddd;
} }
:global(.chatbox) { .chatbox {
position: absolute; position: absolute;
background-color: rgba(226, 226, 226, 0.92); background-color: rgba(226, 226, 226, 0.92);
width: 350px; width: 350px;
@ -101,7 +101,7 @@ kbd {
border-width: thin; border-width: thin;
} }
:global(.actionbuttons), :global(.coorbox), :global(.onlinebox), :global(.cooldownbox), :global(.palettebox) { .actionbuttons, .coorbox, .onlinebox, .cooldownbox, .palettebox {
position: absolute; position: absolute;
background-color: rgba(226, 226, 226, 0.80); background-color: rgba(226, 226, 226, 0.80);
color: black; color: black;
@ -114,114 +114,202 @@ kbd {
border: solid black; border: solid black;
border-width: thin; border-width: thin;
} }
:global(.coorbox) { .coorbox {
left: 16px; left: 16px;
cursor: pointer; cursor: pointer;
bottom: 16px; bottom: 16px;
} }
:global(.onlinebox) { .onlinebox {
left: 16px; left: 16px;
bottom: 57px; bottom: 57px;
white-space: nowrap; white-space: nowrap;
} }
:global(#menubutton) { #menubutton {
left: 16px; left: 16px;
top: 16px; top: 16px;
} }
:global(#menu > div) { #menu > div {
z-index: 1; z-index: 1;
background-color: rgb(213, 238, 255); background-color: rgb(213, 238, 255);
} }
:global(#helpbutton) { #helpbutton {
left: 16px; left: 16px;
top: 221px; top: 221px;
} }
:global(#minecraftbutton) { #minecraftbutton {
left: 16px; left: 16px;
top: 180px; top: 180px;
} }
:global(#settingsbutton) { #settingsbutton {
left: 16px; left: 16px;
top: 57px; top: 57px;
} }
:global(#loginbutton) { #loginbutton {
left: 16px; left: 16px;
top: 98px; top: 98px;
} }
:global(#downloadbutton) { #downloadbutton {
left: 16px; left: 16px;
top: 139px; top: 139px;
} }
:global(#globebutton) { #globebutton {
left: 98px; left: 98px;
top: 16px; top: 16px;
} }
:global(#voxelbutton) { #voxelbutton {
left: 180px; left: 180px;
top: 16px; top: 16px;
} }
:global(#canvasbutton) { #canvasbutton {
left: 57px; left: 57px;
top: 16px; top: 16px;
} }
:global(#minecrafttpbutton) { #minecrafttpbutton {
top: 16px; top: 16px;
right: 16px; right: 16px;
} }
:global(#palselbutton) { #palselbutton {
bottom: 16px; bottom: 16px;
right: 16px; right: 16px;
} }
:global(#chatbutton) { #chatbutton {
bottom: 16px; bottom: 16px;
right: 57px; right: 57px;
} }
:global(#historyselect) { #historyselect {
position: absolute; position: absolute;
top: 16px; top: 16px;
left: 0px; left: 0px;
right: 0px; right: 0px;
height: 70px; height: 70px;
width: 140px; width: 140px;
background-color: rgba(226, 226, 226, 0.80); background-color: rgba(226, 226, 226, 0.80);
color: black; color: black;
font-size: 14px; font-size: 14px;
line-height: 26px; line-height: 26px;
text-align: center; text-align: center;
border: solid black; border: solid black;
border-width: thin; border-width: thin;
padding: 0 24px; padding: 0 24px;
margin-left: auto; margin-left: auto;
margin-right: auto; margin-right: auto;
z-index: 2; z-index: 2;
} }
:global(.hsar) {
.hsar {
padding: 0; padding: 0;
} }
:global(.Modal) { .Modal {
position: absolute; position: absolute;
top: 50%; top: 50%;
left: 50%; left: 50%;
right: auto; right: auto;
bottom: auto; bottom: auto;
border: 1px solid rgb(204, 204, 204); border: 1px solid rgb(204, 204, 204);
background: rgb(255, 255, 255) none repeat scroll 0% 0%; background: rgb(255, 255, 255) none repeat scroll 0% 0%;
overflow-y: auto; overflow-y: auto;
border-radius: 4px; border-radius: 4px;
outline: currentcolor none medium; outline: currentcolor none medium;
transform: translate(-50%, -50%); transform: translate(-50%, -50%);
height: 80%; height: 80%;
max-height: 900px; max-height: 900px;
width: 70%; width: 70%;
transition: all 0.5s ease 0s; transition: all 0.5s ease 0s;
} }
.modaltext, .modalcotext {
color: hsla(218, 5%, 47%, .6);
font-size: 14px;
font-weight: 500;
line-height: normal;
position: relative;
text-align: inherit;
float: none;
}
.modaltext {
margin: 0;
padding: 0;
}
.modaltitle {
color: #4f545c;
font-weight: 500;
font-size: 16px;
line-height: 24px;
margin-left: 0;
margin-right: 10px;
overflow: hidden;
word-wrap: break-word;
margin-bottom: 0;
}
.modaldesc {
box-sizing: border-box;
flex: 1 1 auto;
color: hsla(218, 5%, 47%, .6);
font-size: 14px;
line-height: 20px;
font-weight: 500;
margin-top: 4px;
}
.modaldivider {
box-sizing: border-box;
margin-top: 20px;
height: 1px;
width: 100%;
background-color: hsla(216, 4%, 74%, .3);
}
.modalinfo {
color: #4f545c;
font-size: 15px;
font-weight: 500;
position: relative;
text-align: inherit;
float: none;
margin: 0;
padding: 0;
}
.modalcvtext {
color: hsla(218, 5%, 47%, .6);
font-size: 14px;
font-weight: 500;
padding: 0;
display: inline-block;
vertical-align: middle;
margin-top: 3px;
margin-bottom: 3px;
width: 75%;
}
.ModalClose {
position: fixed;
display: flex;
justify-content: center;
align-items: center;
flex: 0 0 36px;
border-width: 2px;
border-style: solid;
border-radius: 50%;
width: 36px;
height: 36px;
cursor: pointer;
background-color: #f6f6f7;
border-color: #dcddde;
top: 30px;
right: 40px;
}
@media (max-width: 604px) { @media (max-width: 604px) {
:global(.Modal) { .Modal {
position: fixed; position: fixed;
top: 0px; top: 0px;
left: 0px; left: 0px;
@ -235,7 +323,7 @@ kbd {
padding: 5%; padding: 5%;
} }
} }
:global(.Overlay) { .Overlay {
position: fixed; position: fixed;
top: 0px; top: 0px;
left: 0px; left: 0px;
@ -245,54 +333,51 @@ kbd {
z-index: 4; z-index: 4;
} }
:global(.chatbox div .chatarea ) { .chatbox div .chatarea {
height: 174px; height: 174px;
} }
:global(.chatarea) { .chatarea {
padding: 3px 3px 0px 3px; padding: 3px 3px 0px 3px;
margin: 0px; margin: 0px;
overflow-x: hidden; overflow-x: hidden;
overflow-y: scroll; overflow-y: scroll;
height: 95%; height: 95%;
} }
:global(.Modal div chatarea) { .chatinput {
max-height: 600px;
}
:global(.chatinput) {
height: 22px; height: 22px;
white-space: nowrap; white-space: nowrap;
} }
:global(.chatname) { .chatname {
color: #4B0000; color: #4B0000;
font-size: 13px; font-size: 13px;
user-select: all; user-select: all;
} }
:global(.chatmsg) { .chatmsg {
color: #030303; color: #030303;
font-size: 13px; font-size: 13px;
user-select: text; user-select: text;
margin: 0; margin: 0;
} }
:global(.usermessages) { .usermessages {
font-size: 14px; font-size: 14px;
font-weight: 500; font-weight: 500;
line-height: normal; line-height: normal;
} }
:global(.stattext) { .stattext {
font-size: 18px; font-size: 18px;
} }
:global(.statvalue) { .statvalue {
font-size: 18px; font-size: 18px;
font-weight: bold; font-weight: bold;
color: #2d0045; color: #2d0045;
} }
:global(.pressed) { .pressed {
box-shadow:0 0 3px 2px rgba(0,0,0,.6); box-shadow:0 0 3px 2px rgba(0,0,0,.6);
} }
:global(.notifyboxvis), :global(.notifyboxhid) { .notifyboxvis, .notifyboxhid {
background-color: rgba(226, 226, 226, 0.80); background-color: rgba(226, 226, 226, 0.80);
position: absolute; position: absolute;
top: 57px; top: 57px;
@ -313,18 +398,18 @@ kbd {
z-index: 2; z-index: 2;
} }
:global(.notifyboxvis) { .notifyboxvis {
visibility: visible; visibility: visible;
opacity: 1; opacity: 1;
transition: visibility 0s, opacity 0.5s linear; transition: visibility 0s, opacity 0.5s linear;
} }
:global(.notifyboxhid) { .notifyboxhid {
visibility: hidden; visibility: hidden;
opacity: 0; opacity: 0;
transition: visibility 0.5s, opacity 0.5s linear; transition: visibility 0.5s, opacity 0.5s linear;
} }
:global(.cooldownbox) { .cooldownbox {
top: 16px; top: 16px;
width: 48px; width: 48px;
left: 0px; left: 0px;
@ -334,18 +419,18 @@ kbd {
z-index: 2; z-index: 2;
} }
:global(.actionbuttons) { .actionbuttons {
vertical-align: text-bottom; vertical-align: text-bottom;
cursor: pointer; cursor: pointer;
width: 36px; width: 36px;
padding: 0; padding: 0;
} }
:global(.actionbuttons:hover) { .actionbuttons:hover, .coorbox:hover, #menu > div:hover {
background-color: #d2d2d2cc; background-color: #d2d2d2cc;
} }
:global(.palettebox) { .palettebox {
z-index: 1; z-index: 1;
bottom: 59px; bottom: 59px;
padding: 3px; padding: 3px;
@ -355,8 +440,8 @@ kbd {
margin-right: auto; margin-right: auto;
} }
:global(#colors) :global(.selected), #colors .selected,
:global(#colors) span:hover { #colors span:hover {
z-index: 2 !important; z-index: 2 !important;
outline: rgb(234, 234, 234) solid 1px; outline: rgb(234, 234, 234) solid 1px;
box-shadow: rgba(0, 0, 0, 0.80) 0px 0px 5px 2px; box-shadow: rgba(0, 0, 0, 0.80) 0px 0px 5px 2px;
@ -365,7 +450,7 @@ kbd {
transform: scale(1.10,1.10); /* Standard syntax */ transform: scale(1.10,1.10); /* Standard syntax */
} }
:global(#outstreamContainer) { #outstreamContainer {
position: fixed; position: fixed;
display: none; display: none;
width: 100%; width: 100%;
@ -378,6 +463,6 @@ kbd {
z-index: 9000; z-index: 9000;
} }
:global(.grecaptcha-badge) { .grecaptcha-badge {
visibility: hidden; visibility: hidden;
} }

View File

@ -6,13 +6,13 @@ body {
font-family: Montserrat,sans-serif; font-family: Montserrat,sans-serif;
} }
:global(.tm) { .tm {
position: absolute; position: absolute;
top: 10px; top: 10px;
right: 10px; right: 10px;
} }
:global(.webgl-error) { .webgl-error {
font: 15px/30px monospace; font: 15px/30px monospace;
text-align: center; text-align: center;
color: #fff; color: #fff;
@ -20,12 +20,12 @@ body {
} }
:global(.webgl-error) a { .webgl-error a {
color: #fff; color: #fff;
} }
:global(#coorbox) { #coorbox {
position: absolute; position: absolute;
background-color: hsla(0,0%,89%,.8); background-color: hsla(0,0%,89%,.8);
color: #000; color: #000;
@ -42,7 +42,7 @@ body {
} }
:global(#info) { #info {
font-size: 13px; font-size: 13px;
position: absolute; position: absolute;
background-color: hsla(0,0%,89%,.8); background-color: hsla(0,0%,89%,.8);
@ -59,7 +59,7 @@ body {
top: 16px top: 16px
} }
:global(#loading) { #loading {
font-size: 16px; font-size: 16px;
font-weight: bold; font-weight: bold;
position: absolute; position: absolute;

471
src/styles/light-round.css Normal file
View File

@ -0,0 +1,471 @@
body {
margin: 0;
font-family: 'Montserrat', sans-serif;
font-size: 16px;
border: none;
user-select: none;
background: white;
}
html, body {
position: fixed;
top: 0;
left: 0;
bottom: 0;
right: 0;
overflow: hidden;
}
/**
* https://github.com/benweet/stackedit/issues/212
*/
kbd {
padding: 0.1em 0.6em;
border: 1px solid #ccc;
font-size: 11px;
font-family: Arial,Helvetica,sans-serif;
background-color: #f7f7f7;
color: #333;
-moz-box-shadow: 0 1px 0px rgba(0, 0, 0, 0.2),0 0 0 2px #ffffff inset;
-webkit-box-shadow: 0 1px 0px rgba(0, 0, 0, 0.2),0 0 0 2px #ffffff inset;
box-shadow: 0 1px 0px rgba(0, 0, 0, 0.2),0 0 0 2px #ffffff inset;
-moz-border-radius: 3px;
-webkit-border-radius: 3px;
display: inline-block;
margin: 0 0.1em;
text-shadow: 0 1px 0 #fff;
line-height: 1.4;
white-space: nowrap;
}
.modallink {
text-decoration: none;
color: #428bca;
cursor: pointer;
}
.modallink:hover){
font-weight: bold;
color: #226baa;
}
.inarea {
border-style: solid;
border-color: #d5d5d5;
padding: 4px;
margin-top: 4px;
border-width: 1px;
}
.tab-list {
border-bottom: 1px solid #ccc;
padding-left: 0;
}
.tab-list-item {
display: inline-block;
list-style: none;
margin-bottom: -1px;
padding: 0.5rem 0.75rem;
}
.tab-list-active {
background-color: white;
border: solid #ccc;
border-width: 1px 1px 0 1px;
}
table {
font-family: arial, sans-serif;
border-collapse: collapse;
width: 100%;
}
td, th {
border: 1px solid #dddddd;
text-align: left;
padding: 8px;
}
tr:nth-child(even) {
background-color: #dddddd;
}
.chatbox {
position: absolute;
background-color: rgba(226, 226, 226, 0.92);
width: 350px;
height: 200px;
bottom: 16px;
right: 98px;
border: solid black;
border-width: thin;
}
.actionbuttons, .coorbox, .onlinebox, .cooldownbox, .palettebox {
position: absolute;
background-color: rgba(226, 226, 226, 0.80);
color: black;
text-align: center;
vertical-align: middle;
line-height: 36px;
height: 36px;
width: auto;
padding: 0 16px;
border: solid black;
border-radius: 21px;
border-width: thin;
}
.coorbox {
left: 16px;
cursor: pointer;
bottom: 16px;
}
.onlinebox {
left: 16px;
bottom: 57px;
white-space: nowrap;
}
#menubutton {
left: 16px;
top: 16px;
}
#menu > div {
z-index: 1;
background-color: rgb(213, 238, 255);
}
#helpbutton {
left: 16px;
top: 221px;
}
#minecraftbutton {
left: 16px;
top: 180px;
}
#settingsbutton {
left: 16px;
top: 57px;
}
#loginbutton {
left: 16px;
top: 98px;
}
#downloadbutton {
left: 16px;
top: 139px;
}
#globebutton {
left: 98px;
top: 16px;
}
#voxelbutton {
left: 180px;
top: 16px;
}
#canvasbutton {
left: 57px;
top: 16px;
}
#minecrafttpbutton {
top: 16px;
right: 16px;
}
#palselbutton {
bottom: 16px;
right: 16px;
}
#chatbutton {
bottom: 16px;
right: 57px;
}
#historyselect {
position: absolute;
top: 16px;
left: 0px;
right: 0px;
height: 70px;
width: 140px;
background-color: rgba(226, 226, 226, 0.80);
color: black;
font-size: 14px;
line-height: 26px;
text-align: center;
border: solid black;
border-width: thin;
padding: 0 24px;
margin-left: auto;
margin-right: auto;
z-index: 2;
}
.hsar {
padding: 0;
}
.Modal {
position: absolute;
top: 50%;
left: 50%;
right: auto;
bottom: auto;
border: 1px solid rgb(204, 204, 204);
background: rgb(255, 255, 255) none repeat scroll 0% 0%;
overflow-y: auto;
border-radius: 4px;
outline: currentcolor none medium;
transform: translate(-50%, -50%);
height: 80%;
max-height: 900px;
width: 70%;
transition: all 0.5s ease 0s;
border-radius: 21px;
}
.modaltext, .modalcotext {
color: hsla(218, 5%, 47%, .6);
font-size: 14px;
font-weight: 500;
line-height: normal;
position: relative;
text-align: inherit;
float: none;
}
.modaltext {
margin: 0;
padding: 0;
}
.modaltitle {
color: #4f545c;
font-weight: 500;
font-size: 16px;
line-height: 24px;
margin-left: 0;
margin-right: 10px;
overflow: hidden;
word-wrap: break-word;
margin-bottom: 0;
}
.modaldesc {
box-sizing: border-box;
flex: 1 1 auto;
color: hsla(218, 5%, 47%, .6);
font-size: 14px;
line-height: 20px;
font-weight: 500;
margin-top: 4px;
}
.modaldivider {
box-sizing: border-box;
margin-top: 20px;
height: 1px;
width: 100%;
background-color: hsla(216, 4%, 74%, .3);
}
.modalinfo {
color: #4f545c;
font-size: 15px;
font-weight: 500;
position: relative;
text-align: inherit;
float: none;
margin: 0;
padding: 0;
}
.modalcvtext {
color: hsla(218, 5%, 47%, .6);
font-size: 14px;
font-weight: 500;
padding: 0;
display: inline-block;
vertical-align: middle;
margin-top: 3px;
margin-bottom: 3px;
width: 75%;
}
.ModalClose {
position: fixed;
display: flex;
justify-content: center;
align-items: center;
flex: 0 0 36px;
border-width: 2px;
border-style: solid;
border-radius: 50%;
width: 36px;
height: 36px;
cursor: pointer;
background-color: #f6f6f7;
border-color: #dcddde;
top: 30px;
right: 40px;
}
@media (max-width: 604px) {
.Modal {
position: fixed;
top: 0px;
left: 0px;
right: 0px;
bottom: 0px;
height: 95%;
width: 90%;
transform: none;
max-width: none;
max-height: none;
padding: 5%;
}
}
.Overlay {
position: fixed;
top: 0px;
left: 0px;
right: 0px;
bottom: 0px;
background-color: rgba(255, 255, 255, 0.75);
z-index: 4;
}
.chatbox div .chatarea {
height: 174px;
}
.chatarea {
padding: 3px 3px 0px 3px;
margin: 0px;
overflow-x: hidden;
overflow-y: scroll;
height: 95%;
}
.chatinput {
height: 22px;
white-space: nowrap;
}
.chatname {
color: #4B0000;
font-size: 13px;
user-select: all;
}
.chatmsg {
color: #030303;
font-size: 13px;
user-select: text;
margin: 0;
}
.usermessages {
font-size: 14px;
font-weight: 500;
line-height: normal;
}
.stattext {
font-size: 18px;
}
.statvalue {
font-size: 18px;
font-weight: bold;
color: #2d0045;
}
.pressed {
box-shadow:0 0 3px 2px rgba(0,0,0,.6);
}
.notifyboxvis, .notifyboxhid {
background-color: rgba(226, 226, 226, 0.80);
position: absolute;
top: 57px;
left: 0px;
right: 0px;
height: 30px;
width: 20px;
color: black;
font-size: 14px;
line-height: 30px;
text-align: center;
vertical-align: middle;
border: solid black;
border-width: thin;
padding: 0 24px;
margin-left: auto;
margin-right: auto;
z-index: 2;
}
.notifyboxvis {
visibility: visible;
opacity: 1;
transition: visibility 0s, opacity 0.5s linear;
}
.notifyboxhid {
visibility: hidden;
opacity: 0;
transition: visibility 0.5s, opacity 0.5s linear;
}
.cooldownbox {
top: 16px;
width: 48px;
left: 0px;
right: 0px;
margin-left: auto;
margin-right: auto;
z-index: 2;
}
.actionbuttons {
vertical-align: text-bottom;
cursor: pointer;
width: 36px;
padding: 0;
}
.actionbuttons:hover, .coorbox:hover, #menu > div:hover {
background-color: #d2d2d2cc;
}
.palettebox {
z-index: 1;
bottom: 59px;
padding: 3px;
position: fixed;
right: 16px;
margin-left: auto;
margin-right: auto;
border-radius: 0;
}
#colors .selected,
#colors span:hover {
z-index: 2 !important;
outline: rgb(234, 234, 234) solid 1px;
box-shadow: rgba(0, 0, 0, 0.80) 0px 0px 5px 2px;
-ms-transform: scale(1.10,1.10); /* IE 9 */
-webkit-transform: scale(1.10,1.10); /* Safari */
transform: scale(1.10,1.10); /* Standard syntax */
}
#outstreamContainer {
position: fixed;
display: none;
width: 100%;
height: 100%;
top: 0px;
left: 0px;
right: 0px;
bottom: 0px;
background-color: black;
z-index: 9000;
}
.grecaptcha-badge {
visibility: hidden;
}

View File

@ -159,7 +159,9 @@ class Renderer {
canvasSize, canvasSize,
) { ) {
pixelNotify.updateScale(viewscale); pixelNotify.updateScale(viewscale);
let tiledScale = (viewscale > 0.5) ? 0 : Math.round(Math.log2(viewscale) / 2); let tiledScale = (viewscale > 0.5)
? 0
: Math.round(Math.log2(viewscale) / 2);
tiledScale = 4 ** tiledScale; tiledScale = 4 ** tiledScale;
const tiledZoom = canvasMaxTiledZoom + Math.log2(tiledScale) / 2; const tiledZoom = canvasMaxTiledZoom + Math.log2(tiledScale) / 2;
const relScale = viewscale / tiledScale; const relScale = viewscale / tiledScale;
@ -174,7 +176,11 @@ class Renderer {
updateView(view, canvasSize) { updateView(view, canvasSize) {
const [x, y] = view; const [x, y] = view;
let [cx, cy] = this.centerChunk; let [cx, cy] = this.centerChunk;
const [curcx, curcy] = getTileOfPixel(this.tiledScale, [x, y], canvasSize); const [curcx, curcy] = getTileOfPixel(
this.tiledScale,
[x, y],
canvasSize,
);
if (cx !== curcx || cy !== curcy) { if (cx !== curcx || cy !== curcy) {
cx = curcx; cx = curcx;
cy = curcy; cy = curcy;
@ -233,8 +239,12 @@ class Renderer {
return false; return false;
} }
const { width, height } = this.viewport; const { width, height } = this.viewport;
const CHUNK_RENDER_RADIUS_X = Math.ceil(width / TILE_SIZE / 2 / this.relScale); const CHUNK_RENDER_RADIUS_X = Math.ceil(
const CHUNK_RENDER_RADIUS_Y = Math.ceil(height / TILE_SIZE / 2 / this.relScale); width / TILE_SIZE / 2 / this.relScale,
);
const CHUNK_RENDER_RADIUS_Y = Math.ceil(
height / TILE_SIZE / 2 / this.relScale,
);
const [xc, yc] = this.centerChunk; const [xc, yc] = this.centerChunk;
if (Math.abs(cx - xc) if (Math.abs(cx - xc)
<= CHUNK_RENDER_RADIUS_X && Math.abs(cy - yc) <= CHUNK_RENDER_RADIUS_X && Math.abs(cy - yc)
@ -450,15 +460,21 @@ class Renderer {
viewportCtx.scale(viewscale, viewscale); viewportCtx.scale(viewscale, viewscale);
viewportCtx.drawImage( viewportCtx.drawImage(
this.canvas, this.canvas,
width / 2 / viewscale - CANVAS_WIDTH / 2 + ((cx + 0.5) * TILE_SIZE - canvasCenter - x), width / 2 / viewscale - CANVAS_WIDTH / 2 + (
height / 2 / viewscale - CANVAS_HEIGHT / 2 + ((cy + 0.5) * TILE_SIZE - canvasCenter - y), (cx + 0.5) * TILE_SIZE - canvasCenter - x),
height / 2 / viewscale - CANVAS_HEIGHT / 2 + (
(cy + 0.5) * TILE_SIZE - canvasCenter - y),
); );
viewportCtx.restore(); viewportCtx.restore();
} else { } else {
viewportCtx.drawImage( viewportCtx.drawImage(
this.canvas, this.canvas,
Math.floor(width / 2 - CANVAS_WIDTH / 2 + ((cx + 0.5) * TILE_SIZE / this.tiledScale - canvasCenter - x) * viewscale), Math.floor(width / 2 - CANVAS_WIDTH / 2
Math.floor(height / 2 - CANVAS_HEIGHT / 2 + ((cy + 0.5) * TILE_SIZE / this.tiledScale - canvasCenter - y) * viewscale), + ((cx + 0.5) * TILE_SIZE / this.tiledScale
- canvasCenter - x) * viewscale),
Math.floor(height / 2 - CANVAS_HEIGHT / 2
+ ((cy + 0.5) * TILE_SIZE / this.tiledScale
- canvasCenter - y) * viewscale),
); );
} }
@ -653,14 +669,18 @@ class Renderer {
viewportCtx.scale(viewscale, viewscale); viewportCtx.scale(viewscale, viewscale);
viewportCtx.drawImage( viewportCtx.drawImage(
this.canvas, this.canvas,
// eslint-disable-next-line max-len
width / 2 / viewscale - CANVAS_WIDTH / 2 + ((cx + 0.5) * TILE_SIZE - canvasCenter - x), width / 2 / viewscale - CANVAS_WIDTH / 2 + ((cx + 0.5) * TILE_SIZE - canvasCenter - x),
// eslint-disable-next-line max-len
height / 2 / viewscale - CANVAS_HEIGHT / 2 + ((cy + 0.5) * TILE_SIZE - canvasCenter - y), height / 2 / viewscale - CANVAS_HEIGHT / 2 + ((cy + 0.5) * TILE_SIZE - canvasCenter - y),
); );
viewportCtx.restore(); viewportCtx.restore();
} else { } else {
viewportCtx.drawImage( viewportCtx.drawImage(
this.canvas, this.canvas,
// eslint-disable-next-line max-len
Math.floor(width / 2 - CANVAS_WIDTH / 2 + ((cx + 0.5) * TILE_SIZE - canvasCenter - x) * viewscale), Math.floor(width / 2 - CANVAS_WIDTH / 2 + ((cx + 0.5) * TILE_SIZE - canvasCenter - x) * viewscale),
// eslint-disable-next-line max-len
Math.floor(height / 2 - CANVAS_HEIGHT / 2 + ((cy + 0.5) * TILE_SIZE - canvasCenter - y) * viewscale), Math.floor(height / 2 - CANVAS_HEIGHT / 2 + ((cy + 0.5) * TILE_SIZE - canvasCenter - y) * viewscale),
); );
} }

12
src/ui/setStyle.js Normal file
View File

@ -0,0 +1,12 @@
/*
* @flow
*/
export default function setStyle(style) {
const cssUri = window.availableStyles[style];
const domStyle = document.getElementById('globcss');
const curUri = domStyle.getAttribute('href');
if (curUri !== cssUri) {
domStyle.setAttribute('href', cssUri);
}
}

View File

@ -4,8 +4,11 @@
*/ */
import configureStore from '../store/configureStore'; import configureStore from '../store/configureStore';
import setStyle from './setStyle';
const store = configureStore(() => {
const store = configureStore(); const state = store.getState();
setStyle(state.gui.style);
});
export default store; export default store;

View File

@ -11,6 +11,7 @@
import run from './run'; import run from './run';
import clean from './clean'; import clean from './clean';
import copy from './copy'; import copy from './copy';
import minifyCss from './minifyCss';
import bundle from './bundle'; import bundle from './bundle';
/** /**
@ -20,6 +21,7 @@ import bundle from './bundle';
async function build() { async function build() {
await run(clean); await run(clean);
await run(copy); await run(copy);
await run(minifyCss);
await run(bundle); await run(bundle);
} }

View File

@ -31,6 +31,7 @@ function bundle() {
fileContent = fileContent.replace(regex, 'core-js/features/set-immediate'); fileContent = fileContent.replace(regex, 'core-js/features/set-immediate');
fs.writeFileSync(file, fileContent); fs.writeFileSync(file, fileContent);
}); });
console.log('Pathing image-q done');
} catch { } catch {
console.log('Error while patching image-q'); console.log('Error while patching image-q');
} }

View File

@ -11,8 +11,8 @@ import pkg from '../package.json';
*/ */
async function copy() { async function copy() {
await makeDir('build'); await makeDir('build');
await makeDir('build/log');
await Promise.all([ await Promise.all([
makeDir('build/log'),
copyFile('LICENSE', 'build/LICENSE'), copyFile('LICENSE', 'build/LICENSE'),
copyDir('public', 'build/public'), copyDir('public', 'build/public'),
copyFile('src/canvases.json', 'build/canvases.json'), copyFile('src/canvases.json', 'build/canvases.json'),
@ -20,6 +20,7 @@ async function copy() {
copyFile('tools/example-ecosystem.yml', 'build/ecosystem.example.yml'), copyFile('tools/example-ecosystem.yml', 'build/ecosystem.example.yml'),
copyFile('tools/example-ecosystem-backup.yml', 'build/ecosystem-backup.example.yml'), copyFile('tools/example-ecosystem-backup.yml', 'build/ecosystem-backup.example.yml'),
]); ]);
await makeDir('build/public/assets');
} }
export default copy; export default copy;

43
tools/minifyCss.js Normal file
View File

@ -0,0 +1,43 @@
/*
*
* @flow
*/
import fs from 'fs';
import CleanCSS from 'clean-css';
import crypto from 'crypto';
const FOLDER = './src/styles';
const FILES = [ 'default.css', 'dark.css', 'light-round.css', 'dark-round.css' ];
async function minifyCss() {
console.log('Minifying css');
const assets = {};
FILES.forEach((file) => {
const input = fs.readFileSync(`${FOLDER}/${file}`, 'utf8');
const options = {};
const output = new CleanCSS(options).minify(input);
if (output.warnings && output.warnings.length > 0) {
for (let i = 0; i < output.warnings.length; i += 1) {
console.log('\x1b[33m%s\x1b[0m', output.warnings[i]);
}
}
if (output.errors && output.errors.length > 0) {
for (let i = 0; i < output.errors.length; i += 1) {
console.log('\x1b[31m%s\x1b[0m', output.errors[i]);
}
throw new Error("Minify CSS Error Occured");
}
// eslint-disable-next-line max-len
console.log('\x1b[33m%s\x1b[0m', `Minified ${file} by ${Math.round(output.stats.efficiency * 100)}%`);
const hash = crypto.createHash('md5').update(output.styles).digest('hex');
const key = file.substr(0, file.indexOf('.'));
const filename = `${key}.${hash.substr(0, 8)}.css`;
fs.writeFileSync(`./build/public/assets/${filename}`, output.styles, 'utf8');
assets[key] = `/assets/${filename}`;
});
const json = JSON.stringify(assets);
fs.writeFileSync('./build/styleassets.json', json);
}
export default minifyCss;

View File

@ -44,60 +44,6 @@ const config = {
module: { module: {
rules: [ rules: [
{
test: /\.(js|jsx|ts|tsx)$/,
loader: 'babel-loader',
include: [
path.resolve(__dirname, '../src'),
],
query: {
// https://github.com/babel/babel-loader#options
cacheDirectory: isDebug,
// https://babeljs.io/docs/usage/options/
babelrc: false,
presets: [
// A Babel preset that can automatically determine the Babel plugins and polyfills
// https://github.com/babel/babel-preset-env
['@babel/preset-env', {
targets: {
browsers: pkg.browserslist,
},
modules: false,
useBuiltIns: 'usage',
corejs: {
version: 3,
},
debug: false,
}],
"@babel/typescript",
// JSX, Flow
// https://github.com/babel/babel/tree/master/packages/babel-preset-react
'@babel/react',
],
plugins: [
'@babel/transform-flow-strip-types',
['@babel/plugin-proposal-decorators', { legacy: true }],
'@babel/plugin-proposal-function-sent',
'@babel/plugin-proposal-export-namespace-from',
'@babel/plugin-proposal-numeric-separator',
'@babel/plugin-proposal-throw-expressions',
['@babel/plugin-proposal-class-properties', { loose: true }],
'@babel/proposal-object-rest-spread',
// Adds component stack to warning messages
// https://github.com/babel/babel/tree/master/packages/babel-plugin-transform-react-jsx-source
...isDebug ? ['@babel/transform-react-jsx-source'] : [],
// Adds __self attribute to JSX which React will use for some warnings
// https://github.com/babel/babel/tree/master/packages/babel-plugin-transform-react-jsx-self
...isDebug ? ['@babel/transform-react-jsx-self'] : [],
// react-optimize
'@babel/transform-react-constant-elements',
'@babel/transform-react-inline-elements',
'transform-react-remove-prop-types',
'transform-react-pure-class-to-function',
],
},
},
{ {
test: /\.svg$/, test: /\.svg$/,
use: [ use: [
@ -133,21 +79,6 @@ const config = {
}, },
], ],
}, },
{
test: /\.tcss/,
use: [
{
loader: 'css-loader',
options: {
// CSS Loader https://github.com/webpack/css-loader
importLoaders: 1,
sourceMap: isDebug,
// CSS Modules https://github.com/css-modules/css-modules
modules: true,
},
},
],
},
{ {
test: /\.scss/, test: /\.scss/,
use: [ use: [
@ -165,20 +96,6 @@ const config = {
'sass-loader', 'sass-loader',
], ],
}, },
{
test: /\.css/,
use: ['style-loader',
{
loader: 'css-loader',
options: {
// CSS Loader https://github.com/webpack/css-loader
sourceMap: isDebug,
// CSS Modules https://github.com/css-modules/css-modules
modules: false,
},
},
],
},
{ {
test: /\.md$/, test: /\.md$/,
loader: path.resolve(__dirname, './lib/markdown-loader.js'), loader: path.resolve(__dirname, './lib/markdown-loader.js'),
@ -242,6 +159,81 @@ const clientConfig = {
chunkFilename: isDebug ? '[name].chunk.js' : '[name].[chunkhash:8].js', chunkFilename: isDebug ? '[name].chunk.js' : '[name].[chunkhash:8].js',
}, },
module: {
...config.module,
rules: [
...config.module.rules,
{
test: /\.(js|jsx|ts|tsx)$/,
loader: 'babel-loader',
include: [
path.resolve(__dirname, '../src'),
],
query: {
// https://github.com/babel/babel-loader#options
cacheDirectory: isDebug,
// https://babeljs.io/docs/usage/options/
babelrc: false,
presets: [
// A Babel preset that can automatically determine the Babel plugins and polyfills
// https://github.com/babel/babel-preset-env
['@babel/preset-env', {
targets: {
browsers: pkg.browserslist,
},
modules: false,
useBuiltIns: 'usage',
corejs: {
version: 3,
},
debug: false,
}],
"@babel/typescript",
// JSX, Flow
// https://github.com/babel/babel/tree/master/packages/babel-preset-react
'@babel/react',
],
plugins: [
'@babel/transform-flow-strip-types',
['@babel/plugin-proposal-decorators', { legacy: true }],
'@babel/plugin-proposal-function-sent',
'@babel/plugin-proposal-export-namespace-from',
'@babel/plugin-proposal-numeric-separator',
'@babel/plugin-proposal-throw-expressions',
['@babel/plugin-proposal-class-properties', { loose: true }],
'@babel/proposal-object-rest-spread',
// Adds component stack to warning messages
// https://github.com/babel/babel/tree/master/packages/babel-plugin-transform-react-jsx-source
...isDebug ? ['@babel/transform-react-jsx-source'] : [],
// Adds __self attribute to JSX which React will use for some warnings
// https://github.com/babel/babel/tree/master/packages/babel-plugin-transform-react-jsx-self
...isDebug ? ['@babel/transform-react-jsx-self'] : [],
// react-optimize
'@babel/transform-react-constant-elements',
'@babel/transform-react-inline-elements',
'transform-react-remove-prop-types',
'transform-react-pure-class-to-function',
],
},
},
{
test: /\.css/,
use: ['style-loader',
{
loader: 'css-loader',
options: {
// CSS Loader https://github.com/webpack/css-loader
sourceMap: isDebug,
// CSS Modules https://github.com/css-modules/css-modules
modules: false,
},
},
],
},
],
},
plugins: [ plugins: [
// Define free variables // Define free variables
// https://webpack.github.io/docs/list-of-plugins.html#defineplugin // https://webpack.github.io/docs/list-of-plugins.html#defineplugin
@ -315,26 +307,85 @@ const webConfig = {
...config.module, ...config.module,
// Override babel-preset-env configuration for Node.js // Override babel-preset-env configuration for Node.js
rules: config.module.rules.map((rule) => (rule.loader !== 'babel-loader' ? rule : { rules: [
...rule, ...config.module.rules,
query: { {
...rule.query, test: /\.(js|jsx|ts|tsx)$/,
presets: rule.query.presets.map((preset) => (preset[0] !== '@babel/preset-env' ? preset : ['@babel/preset-env', { loader: 'babel-loader',
targets: { include: [
node: pkg.engines.node.replace(/^\D+/g, ''), path.resolve(__dirname, '../src'),
}, ],
modules: false, query: {
useBuiltIns: false, // https://github.com/babel/babel-loader#options
debug: false, cacheDirectory: isDebug,
}])),
// https://babeljs.io/docs/usage/options/
babelrc: false,
presets: [
// A Babel preset that can automatically determine the Babel plugins and polyfills
// https://github.com/babel/babel-preset-env
['@babel/preset-env', {
targets: {
node: pkg.engines.node.replace(/^\D+/g, ''),
},
modules: false,
useBuiltIns: false,
corejs: {
version: 3,
},
debug: false,
}],
"@babel/typescript",
// JSX, Flow
// https://github.com/babel/babel/tree/master/packages/babel-preset-react
'@babel/react',
],
plugins: [
'@babel/transform-flow-strip-types',
['@babel/plugin-proposal-decorators', { legacy: true }],
'@babel/plugin-proposal-function-sent',
'@babel/plugin-proposal-export-namespace-from',
'@babel/plugin-proposal-numeric-separator',
'@babel/plugin-proposal-throw-expressions',
['@babel/plugin-proposal-class-properties', { loose: true }],
'@babel/proposal-object-rest-spread',
// Adds component stack to warning messages
// https://github.com/babel/babel/tree/master/packages/babel-plugin-transform-react-jsx-source
...isDebug ? ['@babel/transform-react-jsx-source'] : [],
// Adds __self attribute to JSX which React will use for some warnings
// https://github.com/babel/babel/tree/master/packages/babel-plugin-transform-react-jsx-self
...isDebug ? ['@babel/transform-react-jsx-self'] : [],
// react-optimize
'@babel/transform-react-constant-elements',
'@babel/transform-react-inline-elements',
'transform-react-remove-prop-types',
'transform-react-pure-class-to-function',
],
},
}, },
})), {
test: /\.css/,
use: [
{
loader: 'css-loader',
options: {
// CSS Loader https://github.com/webpack/css-loader
importLoaders: 1,
sourceMap: isDebug,
// CSS Modules https://github.com/css-modules/css-modules
modules: false,
},
},
],
},
],
}, },
// needed because webpack tries to pack socket.io // needed because webpack tries to pack socket.io
externals: [ externals: [
/\/proxies\.json$/, /\/proxies\.json$/,
/\/canvases\.json$/, /\/canvases\.json$/,
/^\.\/styleassets\.json$/,
/^\.\/assets\.json$/, /^\.\/assets\.json$/,
(context, request, callback) => { (context, request, callback) => {
const isExternal = request.match(/^[@a-z][a-z/.\-0-9]*$/i) const isExternal = request.match(/^[@a-z][a-z/.\-0-9]*$/i)

View File

@ -18,8 +18,6 @@ def on_message(ws, message):
x = unpack_from('B', message, 1)[0] x = unpack_from('B', message, 1)[0]
y = unpack_from('B', message, 2)[0] y = unpack_from('B', message, 2)[0]
a = unpack_from('!h', message, 4)[0] a = unpack_from('!h', message, 4)[0]
if x != 10000 and y != 10000:
return
color = int(unpack_from('!B', message, 6)[0]) color = int(unpack_from('!B', message, 6)[0])
if color == 0: if color == 0:
color = 19 color = 19
@ -29,6 +27,8 @@ def on_message(ws, message):
number = (65520 & a) >> 4 number = (65520 & a) >> 4
x = int(x * 256 + a % 256 - 256 * 256 / 2) x = int(x * 256 + a % 256 - 256 * 256 / 2)
y = int(y * 256 + a // 256 + 256 - 256 * 256 / 2) y = int(y * 256 + a // 256 + 256 - 256 * 256 / 2)
if x != 10000 and y != 10000:
return
print('Pixel Received: @%s,%s - color %s' % (str(x), str(y), str(color))) print('Pixel Received: @%s,%s - color %s' % (str(x), str(y), str(color)))
def on_error(ws, error): def on_error(ws, error):