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`
#### 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
1. Make sure that mysql and redis are running
@ -206,7 +210,7 @@ After=network.target mysql.service redis.service
### 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.
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.

View File

@ -1,25 +1,22 @@
#!/bin/bash
# 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)
# canvases.json, proxies.json and ecosystem.yml are already in the terget directories
# 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.
# This hook just builds the canvas, it does not install new yarn/npm 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
# This hook just builds the canvas, it does not install new packages if needed. So this has to be done manually first
#
#discord webhook for dev canvas
WEBHOOK='https://discordapp.com/api/webhooks/'
WEBHOOK='https://discordapp.com/api/webhooks/5440815510.....'
#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)
BUILDDIR="pixelplanet-build"
BUILDDIR="/home/pixelpla/pixelplanet-build"
#folder for dev canvas
DEVFOLDER="pixelplanet-dev"
DEVFOLDER="/home/pixelpla/pixelplanet-dev"
#folder for production canvas
PFOLDER="pixelplanet"
#proxies.json path
PROXYFILE="/proxies.json"
PFOLDER="/home/pixelpla/pixelplanet"
while read oldrev newrev refname
do
@ -33,35 +30,40 @@ do
COMMITS=`echo "$COMMITS" | sed ':a;N;$!ba;s/\n/\\\n/g'`
echo "---BUILDING pixelplanet---"
cd "$BUILDDIR"
cp "$PROXYFILE" ./src/
yarn run build --release
npm run build
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"
cd "$PFOLDER"
pm2 stop web
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"
else
echo "---UPDATING REPO ON DEV SERVER---"
pm2 stop web-dev
GIT_WORK_TREE="$BUILDDIR" GIT_DIR="${BUILDDIR}/.git" git fetch --all
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=`echo "$COMMITS" | sed ':a;N;$!ba;s/\n/\\\n/g'`
echo "---BUILDING pixelplanet---"
cd "$BUILDDIR"
cp "$PROXYFILE" ./src/
nice -n 19 yarn run build --release
nice -n 19 npm run build
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"
cd "$DEVFOLDER"
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
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": {
"version": "1.0.0",
"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-react": "^7.9.4",
"@babel/preset-typescript": "^7.9.0",
"clean-css": "^4.2.3",
"assets-webpack-plugin": "^3.9.12",
"babel-eslint": "^10.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 {
return {
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 {
type: 'SHOW_MODAL',
modalType,
modalProps,
};
}

View File

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

View File

@ -1,11 +1,8 @@
/* @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 './components/font.css';
import './styles/font.css';
// import initAds, { requestAds } from './ui/ads';
import onKeyPress from './controls/keypress';
@ -24,7 +21,7 @@ import {
import store from './ui/store';
import App from './components/App';
import renderApp from './components/App';
import { initRenderer, getRenderer } from './ui/renderer';
import ProtocolClient from './socket/ProtocolClient';
@ -97,12 +94,7 @@ function init() {
init();
document.addEventListener('DOMContentLoaded', () => {
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('app'),
);
renderApp(document.getElementById('app'));
document.addEventListener('keydown', onKeyPress, false);

View File

@ -4,8 +4,12 @@
*/
import React from 'react';
import { Provider } from 'react-redux';
import ReactDOM from 'react-dom';
import { IconContext } from 'react-icons';
import store from '../ui/store';
import CoordinatesBox from './CoordinatesBox';
import CanvasSwitchButton from './CanvasSwitchButton';
import OnlineBox from './OnlineBox';
@ -17,12 +21,8 @@ import ReCaptcha from './ReCaptcha';
import ExpandMenuButton from './ExpandMenuButton';
import ModalRoot from './ModalRoot';
import baseCss from './base.tcss';
const App = () => (
<div>
{/* eslint-disable-next-line react/no-danger */}
<style dangerouslySetInnerHTML={{ __html: baseCss }} />
<div id="outstreamContainer" />
<ReCaptcha />
<IconContext.Provider value={{ style: { verticalAlign: 'middle' } }}>
@ -39,4 +39,13 @@ const App = () => (
</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';
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 = {
marginTop: 8,
marginBottom: 8,
@ -71,29 +39,29 @@ const CanvasItem = ({ canvasId, canvas, changeCanvas }) => (
alt="preview"
src={`/preview${canvasId}.png`}
/>
<p style={textStyle}>
<span style={titleStyle}>{canvas.title}</span><br />
<span style={infoStyle}>{canvas.desc}</span><br />
<p className="modalcvtext">
<span className="modaltitle">{canvas.title}</span><br />
<span className="modalinfo">{canvas.desc}</span><br />
Cooldown:
&nbsp;
<span style={infoStyle}>
<span className="modalinfo">
{(canvas.bcd !== canvas.pcd)
? <span> {canvas.bcd / 1000}s / {canvas.pcd / 1000}s</span>
: <span> {canvas.bcd / 1000}s</span>}
</span><br />
Stacking till
<span style={infoStyle}> {canvas.cds / 1000}s</span><br />
<span className="modalinfo"> {canvas.cds / 1000}s</span><br />
Ranked:
&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}
<span style={infoStyle}>
<span className="modalinfo">
{(canvas.req !== -1) ? <span>User Account </span> : null}
{(canvas.req > 0) ? <span> and {canvas.req} Pixels set</span> : null}
</span>
{(canvas.req !== -1) ? <br /> : null}
Dimensions:
<span style={infoStyle}> {canvas.size} x {canvas.size}
<span className="modalinfo"> {canvas.size} x {canvas.size}
{(canvas.v)
? <span> x {THREE_CANVAS_HEIGHT} Voxels</span>
: <span> Pixels</span>}

View File

@ -15,19 +15,6 @@ import CanvasItem from './CanvasItem';
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 }) => (
<Modal title="Canvas Selection">
<p style={{
@ -37,7 +24,7 @@ const CanvasSelectModal = ({ canvases }) => (
paddingTop: 20,
}}
>
<p style={textStyle}>
<p className="modaltext">
Select the canvas you want to use.
Every canvas is unique and has different palettes,
cooldown and requirements.

View File

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

View File

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

View File

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

View File

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

View File

@ -9,23 +9,10 @@ import Modal from './Modal';
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 = () => (
<Modal title="Chat">
<p style={{ textAlign: 'center' }}>
<p style={textStyle}>Chat with other people here</p>
<p className="modaltext">Chat with other people here</p>
</p>
<div className="inarea" style={{ height: '65%' }}>
<Chat />

View File

@ -12,28 +12,6 @@ import type { State } from '../reducers';
import printGIMPPalette from '../core/exportGPL';
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() {
const output = document.getElementById('imgoutput');
@ -284,7 +262,7 @@ function Converter({
return (
<p style={{ textAlign: 'center' }}>
<p style={textStyle}>Choose Canvas:&nbsp;
<p className="modalcotext">Choose Canvas:&nbsp;
<select
onChange={(e) => {
const sel = e.target;
@ -307,8 +285,8 @@ function Converter({
}
</select>
</p>
<h3 style={titleStyle}>Palette Download</h3>
<p style={textStyle}>
<h3 className="modaltitle">Palette Download</h3>
<p className="modalcotext">
Palette for <a href="https://www.gimp.org">GIMP</a>:&nbsp;
<button
type="button"
@ -329,8 +307,8 @@ function Converter({
<p>Credit for the Palette of the Moon goes to
<a href="https://twitter.com/starhousedev">starhouse</a>.</p>
</p>
<h3 style={titleStyle}>Image Converter</h3>
<p style={textStyle}>Convert an image to canvas colors</p>
<h3 className="modaltitle">Image Converter</h3>
<p className="modalcotext">Convert an image to canvas colors</p>
<input
type="file"
id="imgfile"
@ -341,7 +319,7 @@ function Converter({
readFile(file, selectFile, setScaleData);
}}
/>
<p style={textStyle}>Choose Strategy:&nbsp;
<p className="modalcotext">Choose Strategy:&nbsp;
<select
onChange={(e) => {
const sel = e.target;
@ -368,7 +346,7 @@ function Converter({
}
</select>
</p>
<p style={textStyle}>Choose Color Mode:&nbsp;
<p className="modalcotext">Choose Color Mode:&nbsp;
<select
onChange={(e) => {
const sel = e.target;
@ -395,7 +373,7 @@ function Converter({
}
</select>
</p>
<p style={{ ...textStyle, fontHeight: 16 }}>
<p style={{ fontHeight: 16 }} className="modalcotext">
<input
type="checkbox"
checked={gridEnabled}
@ -418,7 +396,7 @@ function Converter({
display: 'inline-block',
}}
>
<p style={{ ...textStyle, fontHeight: 16 }}>
<p style={{ fontHeight: 16 }} className="modalcotext">
<input
type="checkbox"
checked={gridLight}
@ -431,7 +409,7 @@ function Converter({
/>
Light Grid
</p>
<span style={textStyle}>Offset X:&nbsp;
<span className="modalcotext">Offset X:&nbsp;
<input
type="number"
step="1"
@ -445,9 +423,9 @@ function Converter({
offsetX: e.target.value,
});
}}
/>%
/>&nbsp;
</span>
<span style={textStyle}>Offset Y:&nbsp;
<span className="modalcotext">Offset Y:&nbsp;
<input
type="number"
step="1"
@ -461,12 +439,12 @@ function Converter({
offsetY: e.target.value,
});
}}
/>%
/>
</span>
</div>
)
: null}
<p style={{ ...textStyle, fontHeight: 16 }}>
<p style={{ fontHeight: 16 }} className="modalcotext">
<input
type="checkbox"
checked={scalingEnabled}
@ -489,7 +467,7 @@ function Converter({
display: 'inline-block',
}}
>
<span style={textStyle}>Width:&nbsp;
<span className="modalcotext">Width:&nbsp;
<input
type="number"
step="1"
@ -504,6 +482,7 @@ function Converter({
if (selectedScaleKeepRatio && selectedFile) {
const ratio = selectedFile.width / selectedFile.height;
const newHeight = Math.round(newWidth / ratio);
if (newHeight <= 0) return;
setScaleData({
...scaleData,
width: newWidth,
@ -516,9 +495,9 @@ function Converter({
width: newWidth,
});
}}
/>%
/>&nbsp;
</span>
<span style={textStyle}>Height:&nbsp;
<span className="modalcotext">Height:&nbsp;
<input
type="number"
step="1"
@ -533,6 +512,7 @@ function Converter({
if (selectedScaleKeepRatio && selectedFile) {
const ratio = selectedFile.width / selectedFile.height;
const nuWidth = Math.round(ratio * nuHeight);
if (nuWidth <= 0) return;
setScaleData({
...scaleData,
width: nuWidth,
@ -545,9 +525,9 @@ function Converter({
height: nuHeight,
});
}}
/>%
/>
</span>
<p style={{ ...textStyle, fontHeight: 16 }}>
<p style={{ fontHeight: 16 }} className="modalcotext">
<input
type="checkbox"
checked={selectedScaleKeepRatio}
@ -557,7 +537,7 @@ function Converter({
/>
Keep Ratio
</p>
<p style={{ ...textStyle, fontHeight: 16 }}>
<p style={{ fontHeight: 16 }} className="modalcotext">
<input
type="checkbox"
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"
id="path4"
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" />
</g>
</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;
}
async function submit_delete_account(password) {
async function submitDeleteAccount(password) {
const body = JSON.stringify({
password,
});
@ -56,7 +56,7 @@ class DeleteAccount extends React.Component {
if (errors.length > 0) return;
this.setState({ submitting: true });
const { errors: resperrors } = await submit_delete_account(password);
const { errors: resperrors } = await submitDeleteAccount(password);
if (resperrors) {
this.setState({
errors: resperrors,
@ -64,11 +64,13 @@ class DeleteAccount extends React.Component {
});
return;
}
this.props.set_name(null);
const { setName } = this.props;
setName(null);
}
render() {
const { errors } = this.state;
const { errors, password, submitting } = this.state;
const { done } = this.props;
return (
<div className="inarea" style={{ backgroundColor: '#ff6666' }}>
<form onSubmit={this.handleSubmit}>
@ -76,14 +78,16 @@ class DeleteAccount extends React.Component {
<p key={error} className="errormessage">Error: {error}</p>
))}
<input
value={this.state.password}
value={password}
onChange={(evt) => this.setState({ password: evt.target.value })}
type="password"
placeholder="Password"
/>
<br />
<button type="submit">{(this.state.submitting) ? '...' : 'Yes, Delete My Account!'}</button>
<button type="button" onClick={this.props.done}>Cancel</button>
<button type="submit">
{(submitting) ? '...' : 'Yes, Delete My Account!'}
</button>
<button type="button" onClick={done}>Cancel</button>
</form>
</div>
);

View File

@ -1,5 +1,7 @@
/*
* espand menu / show other menu buttons
*
* @flow
*/
import React from 'react';
@ -9,7 +11,13 @@ import { MdExpandMore, MdExpandLess } from 'react-icons/md';
import { toggleOpenMenu } from '../actions';
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 /> }
</div>
);

View File

@ -11,22 +11,10 @@ import Modal from './Modal';
import { showUserAreaModal } from '../actions';
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 }) => (
<Modal title="Restore my Password">
<p style={{ paddingLeft: '5%', paddingRight: '5%' }}>
<p style={textStyle}>
<p className="modaltext">
Enter your mail adress and we will send you a new password:
</p><br />
<p style={{ textAlign: 'center' }}>

View File

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

View File

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

View File

@ -11,16 +11,16 @@ import { toggleGrid } from '../actions';
const GridButton = ({ onToggleGrid }) => (
<div className="actionbuttons" onClick={onToggleGrid}>
<div
role="button"
tabIndex={-1}
className="actionbuttons"
onClick={onToggleGrid}
>
<FaTh />
</div>
);
// TODO simplify...
function mapStateToProps(state: State) {
return {};
}
function mapDispatchToProps(dispatch) {
return {
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 }) => (
<div id="helpbutton" className="actionbuttons" onClick={open}>
<div
id="helpbutton"
className="actionbuttons"
onClick={open}
role="button"
tabIndex={-1}
>
<FaQuestion />
</div>
);

View File

@ -13,75 +13,49 @@ import React from 'react';
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 = () => (
<Modal title="Welcome to PixelPlanet.fun">
<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
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
pixels and 7s on already set pixels.
Higher zoomlevels take some time to update, the 3D globe gets updated at least once per day.
Have fun!</p>
<p>Discord: <a href="./discord" target="_blank">pixelplanet.fun/discord</a></p>
<p>Source on <a href="https://github.com/pixelplanetdev/pixelplanet" target="_blank">github</a></p>
<p>Reddit: <a href="https://www.reddit.com/r/PixelPlanetFun/" target="_blank">r/PixelPlanetFun</a></p>
<p style={titleStyle}>Map Data</p>
<p style={textStyle}>The bare map data that we use, together with converted OpenStreetMap tiles for orientation,
<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" rel="noopener noreferrer">github</a></p>
<p>Reddit: <a href="https://www.reddit.com/r/PixelPlanetFun/" target="_blank" rel="noopener noreferrer">r/PixelPlanetFun</a></p>
<p className="modaltitle">Map Data</p>
<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>
<p style={titleStyle}>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>
<h3 style={titleStyle}>2D Controls</h3>
<p style={textStyle}>Click a color in palette to select</p>
<p style={textStyle}>Press <kbd>G</kbd> to toggle grid</p>
<p style={textStyle}>Press <kbd>X</kbd> to toggle showing of pixel activity</p>
<p style={textStyle}>Press <kbd>R</kbd> to copy coordinates</p>
<p style={textStyle}>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 style={textStyle}>Press <kbd></kbd>,<kbd></kbd>,<kbd></kbd>, <kbd></kbd> to move</p>
<p style={textStyle}>Drag mouse to move</p>
<p style={textStyle}>Scroll mouse wheel to zoom</p>
<p style={textStyle}>Click middle mouse button to current hovering color</p>
<p style={textStyle}>Pinch to zoom (on touch devices)</p>
<p style={textStyle}>Pan to move (on touch devices)</p>
<p style={textStyle}>Click or tap to place a pixel</p>
<h3 style={titleStyle}>3D Controls</h3>
<p style={textStyle}>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 style={textStyle}>Scroll mouse wheel to zoom</p>
<p style={textStyle}>Left click and drag mouse to rotate</p>
<p style={textStyle}>Middle click and drag mouse to zoom</p>
<p style={textStyle}>Right click and drag mouse to pan</p>
<p style={textStyle}>Left Click or tap to place a pixel</p>
<p style={textStyle}>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 style={textStyle}>
<p className="modaltitle">Detected as Proxy?</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 className="modaltitle">2D Controls</h3>
<p className="modaltext">Click a color in palette to select</p>
<p className="modaltext">Press <kbd>G</kbd> to toggle grid</p>
<p className="modaltext">Press <kbd>X</kbd> to toggle showing of pixel activity</p>
<p className="modaltext">Press <kbd>R</kbd> to copy coordinates</p>
<p className="modaltext">Press <kbd>Q</kbd> or <kbd>E</kbd> to zoom</p>
<p className="modaltext">Press <kbd>W</kbd>,<kbd>A</kbd>,<kbd>S</kbd>, <kbd>D</kbd> to move</p>
<p className="modaltext">Press <kbd></kbd>,<kbd></kbd>,<kbd></kbd>, <kbd></kbd> to move</p>
<p className="modaltext">Drag mouse to move</p>
<p className="modaltext">Scroll mouse wheel to zoom</p>
<p className="modaltext">Click middle mouse button to current hovering color</p>
<p className="modaltext">Pinch to zoom (on touch devices)</p>
<p className="modaltext">Pan to move (on touch devices)</p>
<p className="modaltext">Click or tap to place a pixel</p>
<h3 className="modaltitle">3D Controls</h3>
<p className="modaltext">Press <kbd>W</kbd>,<kbd>A</kbd>,<kbd>S</kbd>, <kbd>D</kbd> to move</p>
<p className="modaltext">Press <kbd></kbd>,<kbd></kbd>,<kbd></kbd>, <kbd></kbd> to move</p>
<p className="modaltext">Scroll mouse wheel to zoom</p>
<p className="modaltext">Left click and drag mouse to rotate</p>
<p className="modaltext">Middle click and drag mouse to zoom</p>
<p className="modaltext">Right click and drag mouse to pan</p>
<p className="modaltext">Left Click or tap to place 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" rel="noopener noreferrer">crazygames.com</a></p>
<p className="modaltext">
<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/terms">Terms of Service</a> apply.

View File

@ -8,82 +8,81 @@
* LICENSE.txt file in the root directory of this source tree.
*/
/* eslint-disable max-len */
import React from 'react';
import { analytics, RECAPTCHA_SITEKEY } from '../core/config';
class Html extends React.Component {
static defaultProps = {
styles: [],
scripts: [],
};
const Html = ({
title,
description,
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;

View File

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

View File

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

View File

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

View File

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

View File

@ -9,13 +9,13 @@ import { MdNearMe } from 'react-icons/md';
import type { State } from '../reducers';
async function submit_minecraft_tp(view) {
async function submitMinecraftTp(view) {
const [x, y] = view.map(Math.round);
const body = JSON.stringify({
x,
y,
});
const response = await fetch('./api/mctp', {
await fetch('./api/mctp', {
method: 'POST',
headers: {
'Content-type': 'application/json',
@ -27,7 +27,13 @@ async function submit_minecraft_tp(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 />
</div>
);

View File

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

View File

@ -13,26 +13,6 @@ import {
} 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 }) {
return (
<Modal
@ -45,8 +25,8 @@ function MyModal({ close, title, children }) {
>
<h2 style={{ paddingLeft: '5%' }}>{title}</h2>
<div
style={closeStyles}
onClick={close}
className="ModalClose"
role="button"
label="close"
tabIndex={-1}

View File

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

View File

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

View File

@ -8,49 +8,46 @@ import React from 'react';
import TotalRankings from './TotalRankings';
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 {
constructor() {
super();
this.state = {
order_daily: false,
orderDaily: false,
};
}
render() {
const { orderDaily } = this.state;
return (
<div>
<p>
<span
className={(!this.state.order_daily) ? 'modallinkselected' : 'modallink'}
onClick={() => { this.setState({ order_daily: false }); }}
role="button"
tabIndex={-1}
className={
(!orderDaily) ? 'modallinkselected' : 'modallink'
}
onClick={() => {
this.setState({ orderDaily: false });
}}
>Total</span> |
<span
className={(this.state.order_daily) ? 'modallinkselected' : 'modallink'}
onClick={() => { this.setState({ order_daily: true }); }}
role="button"
tabIndex={-1}
className={
(orderDaily) ? 'modallinkselected' : 'modallink'
}
onClick={() => { this.setState({ orderDaily: true }); }}
>Daily</span>
</p>
{(this.state.order_daily) ? <DailyRankings /> : <TotalRankings />}
<p style={textStyle}>Ranking updates every 5 min. Daily rankings get reset at midnight UTC.</p>
{(orderDaily) ? <DailyRankings /> : <TotalRankings />}
<p className="modaltext">
Ranking updates every 5 min. Daily rankings get reset at midnight UTC.
</p>
</div>
);
}
}
function mapStateToProps(state: State) {
const { totalRanking } = state.user;
return { totalRanking };
}
export default Rankings;

View File

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

View File

@ -8,32 +8,21 @@ import { connect } from 'react-redux';
import Modal from './Modal';
import { showUserAreaModal, receiveMe } from '../actions';
import { showUserAreaModal } from '../actions';
// import { send_registration } from '../ui/register';
import SignUpForm from './SignUpForm';
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 RegisterModal = ({ login, doMe }) => (
const RegisterModal = ({ login }) => (
<Modal title="Register New Account">
<p style={textStyle}>Register new account here</p><br />
<p style={{ textAlign: 'center' }}>
<SignUpForm userarea={login} me={doMe} />
<button type="button" onClick={login}>Cancel</button>
<p>Also join our Discord:&nbsp;
<a href="./discord" target="_blank">pixelplanet.fun/discord</a>
<p style={{ paddingLeft: '5%', paddingRight: '5%' }}>
<p className="modaltext">Register new account here</p><br />
<p style={{ textAlign: 'center' }}>
<SignUpForm back={login} />
<p>Also join our Discord:&nbsp;
<a href="./discord" target="_blank">pixelplanet.fun/discord</a>
</p>
</p>
</p>
</Modal>
@ -44,9 +33,6 @@ function mapDispatchToProps(dispatch) {
login() {
dispatch(showUserAreaModal());
},
doMe(me) {
dispatch(receiveMe(me));
},
};
}

View File

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

View File

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

View File

@ -4,10 +4,14 @@
*/
import React from 'react';
import { connect } from 'react-redux';
import {
validateEMail, validateName, validatePassword, parseAPIresponse,
} from '../utils/validation';
import { showUserAreaModal, receiveMe } from '../actions';
function validate(name, email, password, confirmPassword) {
const errors = [];
const mailerror = validateEMail(email);
@ -90,12 +94,23 @@ class SignUpForm extends React.Component {
});
return;
}
this.props.me(me);
this.props.userarea();
const { doMe, userarea } = this.props;
doMe(me);
userarea();
}
render() {
const { errors } = this.state;
const {
errors,
name,
email,
password,
confirmPassword,
submitting,
} = this.state;
const {
back,
} = this.props;
return (
<form onSubmit={this.handleSubmit}>
{errors.map((error) => (
@ -103,28 +118,28 @@ class SignUpForm extends React.Component {
))}
<input
style={inputStyles}
value={this.state.name}
value={name}
onChange={(evt) => this.setState({ name: evt.target.value })}
type="text"
placeholder="Name"
/><br />
<input
style={inputStyles}
value={this.state.email}
value={email}
onChange={(evt) => this.setState({ email: evt.target.value })}
type="text"
placeholder="Email"
/><br />
<input
style={inputStyles}
value={this.state.password}
value={password}
onChange={(evt) => this.setState({ password: evt.target.value })}
type="password"
placeholder="Password"
/><br />
<input
style={inputStyles}
value={this.state.confirmPassword}
value={confirmPassword}
onChange={(evt) => this.setState({
confirmPassword: evt.target.value,
})}
@ -132,11 +147,29 @@ class SignUpForm extends React.Component {
placeholder="Confirm Password"
/><br />
<button type="submit">
{(this.state.submitting) ? '...' : 'Submit'}
{(submitting) ? '...' : 'Submit'}
</button>
<button
type="button"
onClick={back}
>
Cancel
</button>
</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 PropTypes from 'prop-types';
class Tab extends Component {
static propTypes = {
activeTab: PropTypes.string.isRequired,
label: PropTypes.string.isRequired,
onClick: PropTypes.func.isRequired,
};
onClick = () => {
const { label, onClick } = this.props;
onClick(label);
@ -30,6 +23,7 @@ class Tab extends Component {
return (
<li
role="presentation"
className={className}
onClick={onClick}
>

View File

@ -1,18 +1,14 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import Tab from './Tab';
class Tabs extends Component {
static propTypes = {
children: PropTypes.instanceOf(Array).isRequired,
}
constructor(props) {
super(props);
const { children } = this.props;
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';
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 }) => (
<p>
<span className="stattext">{(rank) ? `${text}: #` : `${text}: `}</span>
&nbsp;
<span className="statvalue">{numberToString(value)}</span>
</p>
);
@ -40,95 +29,131 @@ class UserArea extends React.Component {
super();
this.state = {
// that should be an ENUM tbh
change_name_extended: false,
change_mail_extended: false,
change_passwd_extended: false,
delete_account_extended: false,
changeNameExtended: false,
changeMailExtended: false,
changePasswdExtended: false,
deleteAccountExtended: false,
};
}
render() {
const {
stats, name, logout, mailreg, setMailreg, setName,
} = this.props;
const {
changeNameExtended,
changeMailExtended,
changePasswdExtended,
deleteAccountExtended,
} = this.state;
return (
<p style={{ textAlign: 'center' }}>
<UserMessages />
<Stat text="Todays Placed Pixels" value={this.props.stats.dailyTotalPixels} />
<Stat text="Daily Rank" value={this.props.stats.dailyRanking} rank />
<Stat text="Placed Pixels" value={this.props.stats.totalPixels} />
<Stat text="Total Rank" value={this.props.stats.ranking} rank />
<p style={textStyle}>
<p>Your name is: {this.props.name}</p>(
<Stat
text="Todays Placed Pixels"
value={stats.dailyTotalPixels}
/>
<Stat
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
role="button"
tabIndex={-1}
className="modallink"
onClick={this.props.logout}
onClick={logout}
> Log out</span> |
<span
role="button"
tabIndex={-1}
className="modallink"
onClick={(evt) => this.setState({
change_name_extended: true,
change_mail_extended: false,
change_passwd_extended: false,
delete_account_extended: false,
onClick={() => this.setState({
changeNameExtended: true,
changeMailExtended: false,
changePasswdExtended: false,
deleteAccountExtended: false,
})}
> Change Username</span> |
{(this.props.mailreg)
{(mailreg)
&& (
<span>
<span
role="button"
tabIndex={-1}
className="modallink"
onClick={(evt) => this.setState({
change_name_extended: false,
change_mail_extended: true,
change_passwd_extended: false,
delete_account_extended: false,
onClick={() => this.setState({
changeNameExtended: false,
changeMailExtended: true,
changePasswdExtended: false,
deleteAccountExtended: false,
})}
> Change Mail</span> |
</span>
)}
<span
role="button"
tabIndex={-1}
className="modallink"
onClick={(evt) => this.setState({
change_name_extended: false,
change_mail_extended: false,
change_passwd_extended: true,
delete_account_extended: false,
onClick={() => this.setState({
changeNameExtended: false,
changeMailExtended: false,
changePasswdExtended: true,
deleteAccountExtended: false,
})}
> Change Password</span> |
<span
role="button"
tabIndex={-1}
className="modallink"
onClick={(evt) => this.setState({
change_name_extended: false,
change_mail_extended: false,
change_passwd_extended: false,
delete_account_extended: true,
onClick={() => this.setState({
changeNameExtended: false,
changeMailExtended: false,
changePasswdExtended: false,
deleteAccountExtended: true,
})}
> Delete Account</span> )
</p>
{(this.state.change_passwd_extended)
{(changePasswdExtended)
&& (
<ChangePassword
mailreg={this.props.mailreg}
done={() => { this.props.set_mailreg(true); this.setState({ change_passwd_extended: false }); }}
cancel={() => { this.setState({ change_passwd_extended: false }); }}
mailreg={mailreg}
done={() => {
setMailreg(true);
this.setState({ changePasswdExtended: false });
}}
cancel={() => { this.setState({ changePasswdExtended: false }); }}
/>
)}
{(this.state.change_name_extended)
{(changeNameExtended)
&& (
<ChangeName
set_name={this.props.set_name}
done={() => { this.setState({ change_name_extended: false }); }}
setName={setName}
done={() => { this.setState({ changeNameExtended: false }); }}
/>
)}
{(this.state.change_mail_extended)
{(changeMailExtended)
&& (
<ChangeMail
done={() => { this.setState({ change_mail_extended: false }); }}
done={() => { this.setState({ changeMailExtended: false }); }}
/>
)}
{(this.state.delete_account_extended)
{(deleteAccountExtended)
&& (
<DeleteAccount
set_name={this.props.set_name}
done={() => { this.setState({ delete_account_extended: false }); }}
setName={setName}
done={() => { this.setState({ deleteAccountExtended: false }); }}
/>
)}
</p>

View File

@ -26,26 +26,15 @@ const logoStyle = {
marginRight: 5,
};
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 LogInArea = ({ register, forgot_password, me }) => (
const LogInArea = ({ register, forgotPassword, me }) => (
<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>
<LogInForm me={me} />
<p
className="modallink"
onClick={forgot_password}
onClick={forgotPassword}
role="presentation"
>
I forgot my Password.</p>
<h2>or login with:</h2>
@ -95,7 +84,7 @@ const LogInArea = ({ register, forgot_password, me }) => (
);
const UserAreaModal = ({
name, register, forgot_password, doMe, logout, setUserName, setUserMailreg,
name, register, forgotPassword, doMe, logout, setUserName, setUserMailreg,
}) => (
<Modal title="User Area">
<p style={{ textAlign: 'center' }}>
@ -103,7 +92,7 @@ const UserAreaModal = ({
? (
<LogInArea
register={register}
forgot_password={forgot_password}
forgotPassword={forgotPassword}
me={doMe}
/>
)
@ -112,8 +101,8 @@ const UserAreaModal = ({
<div label="Profile">
<UserArea
logout={logout}
set_name={setUserName}
set_mailreg={setUserMailreg}
setName={setUserName}
setMailreg={setUserMailreg}
/>
</div>
<div label="Ranking">
@ -138,7 +127,7 @@ function mapDispatchToProps(dispatch) {
register() {
dispatch(showRegisterModal());
},
forgot_password() {
forgotPassword() {
dispatch(showForgotPasswordModal());
},
doMe(me) {

View File

@ -13,20 +13,21 @@ class UserMessages extends React.Component {
constructor() {
super();
this.state = {
resent_verify: false,
sent_link: false,
verify_answer: null,
link_answer: null,
resentVerify: false,
sentLink: false,
verifyAnswer: null,
linkAnswer: null,
};
this.submit_resend_verify = this.submit_resend_verify.bind(this);
this.submit_mc_link = this.submit_mc_link.bind(this);
this.submitResendVerify = this.submitResendVerify.bind(this);
this.submitMcLink = this.submitMcLink.bind(this);
}
async submit_resend_verify() {
if (this.state.resent_verify) return;
async submitResendVerify() {
const { resentVerify } = this.state;
if (resentVerify) return;
this.setState({
resent_verify: true,
resentVerify: true,
});
const response = await fetch('./api/auth/resend_verify', {
@ -34,16 +35,19 @@ class UserMessages extends React.Component {
});
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({
verify_answer,
verifyAnswer,
});
}
async submit_mc_link(accepted) {
if (this.state.sent_link) return;
async submitMcLink(accepted) {
const { sentLink } = this.state;
if (sentLink) return;
this.setState({
sent_link: true,
sentLink: true,
});
const body = JSON.stringify({ accepted });
const rep = await fetch('./api/auth/mclink', {
@ -56,43 +60,94 @@ class UserMessages extends React.Component {
const { errors } = parseAPIresponse(rep);
if (errors) {
this.setState({
link_answer: errors[0],
linkAnswer: errors[0],
});
return;
}
const { setMCName, remFromUserMessages } = this.props;
if (!accepted) {
this.props.setMCName(null);
setMCName(null);
}
this.props.rem_from_messages('not_mc_verified');
remFromUserMessages('not_mc_verified');
this.setState({
link_answer: (accepted) ? 'You successfully linked your mc account.' : 'You denied.',
linkAnswer: (accepted)
? 'You successfully linked your mc account.'
: 'You denied.',
});
}
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
const messages = [...this.props.messages];
const messages = [...messagesr];
const { verifyAnswer, linkAnswer } = this.state;
const { minecraftname } = this.props;
return (
<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">
Please verify your mail address or your account could get deleted after a few days.&nbsp;
{(this.state.verify_answer)
? <span className="modallink">{this.state.verify_answer}</span>
: <span className="modallink" onClick={this.submit_resend_verify}>Click here to request a new verification mail.</span>}
Please verify your mail address&nbsp;
or your account could get deleted after a few days.&nbsp;
{(verifyAnswer)
? (
<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>
) : 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}.
{(this.state.link_answer)
? <span className="modallink">{this.state.link_answer}</span>
<p className="usermessages">
You requested to link your mc account {minecraftname}.
&nbsp;
{(linkAnswer)
? (
<span
className="modallink"
>
{linkAnswer}
</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>
)}
</p>
@ -110,7 +165,7 @@ function mapDispatchToProps(dispatch) {
setMCName(minecraftname) {
dispatch(setMinecraftName(minecraftname));
},
rem_from_messages(message) {
remFromUserMessages(message) {
dispatch(remFromMessages(message));
},
};

View File

@ -948,8 +948,8 @@ class VoxelPainterControls extends EventDispatcher {
*/
// clamp to boundaries
const state = scope.store.getState();
const { canvasSize } = state.canvas;
const reduxState = scope.store.getState();
const { canvasSize } = reduxState.canvas;
const bound = canvasSize / 2;
scope.target.clamp({
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 logger from './logger';
import { getChunkOfPixel } from './utils';

View File

@ -4,6 +4,9 @@
* @flow
* */
// Tile creation is allowed to be slow
/* eslint-disable no-await-in-loop */
import sharp from 'sharp';
import fs from 'fs';
@ -14,7 +17,8 @@ import { getMaxTiledZoom } from './utils';
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 subtilesInTile how many subtiles are in a tile (per dimension)
* @param cell subtile to delete [dx, dy]
@ -32,8 +36,11 @@ function deleteSubtilefromTile(
let channelOffset = (offset + row * TILE_SIZE * subtilesInTile) * 3;
const max = channelOffset + TILE_SIZE * 3;
while (channelOffset < max) {
// eslint-disable-next-line prefer-destructuring
buffer[channelOffset++] = palette.rgb[0];
// eslint-disable-next-line prefer-destructuring
buffer[channelOffset++] = palette.rgb[1];
// eslint-disable-next-line prefer-destructuring
buffer[channelOffset++] = palette.rgb[2];
}
}
@ -52,7 +59,7 @@ function addRGBSubtiletoTile(
buffer: Uint8Array,
) {
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;
for (let row = 0; row < TILE_SIZE; row += 1) {
let channelOffset = (chunkOffset + row * TILE_SIZE * subtilesInTile) * 3;
@ -80,7 +87,7 @@ function addIndexedSubtiletoTile(
buffer: Uint8Array,
) {
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 clr: number;
for (let row = 0; row < TILE_SIZE; row += 1) {
@ -194,6 +201,7 @@ export async function createZoomedTile(
const na = [];
for (let dy = 0; dy < TILE_ZOOM_LEVEL; dy += 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`;
if (!fs.existsSync(chunkfile)) {
na.push([dx, dy]);
@ -244,8 +252,11 @@ export async function createEmptyTile(
let i = 0;
const max = TILE_SIZE * TILE_SIZE * 3;
while (i < max) {
// eslint-disable-next-line prefer-destructuring
tileRGBBuffer[i++] = palette.rgb[0];
// eslint-disable-next-line prefer-destructuring
tileRGBBuffer[i++] = palette.rgb[1];
// eslint-disable-next-line prefer-destructuring
tileRGBBuffer[i++] = palette.rgb[2];
}
const filename = `${canvasTileFolder}/emptytile.png`;
@ -416,6 +427,7 @@ export async function initializeTiles(
}
}
logger.info(
// eslint-disable-next-line max-len
`Tiling: Created ${cnts} / ${cnt} tiles for zoom ${zoom} for canvas${canvasId}`,
);
}
@ -429,6 +441,7 @@ export async function initializeTiles(
);
//--
logger.info(
// eslint-disable-next-line max-len
`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
* @return true if proxy, false if not
*/
// eslint-disable-next-line no-unused-vars
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`;
// eslint-disable-next-line max-len
const url = `http://check.getipintel.net/check.php?ip=${ip}&contact=${email}&flags=m`;
logger.info(`PROXYCHECK fetching getipintel ${url}`);
const response = await fetch(url, {
@ -27,6 +30,7 @@ async function getIPIntel(ip: string): Promise<boolean> {
Accept: '*/*',
'Accept-Language': 'de,en-US;q=0.7,en;q=0.3',
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',
},
});
@ -54,6 +58,7 @@ async function getProxyCheck(ip: string): Promise<boolean> {
logger.info('PROXYCHECK fetching proxycheck %s', url);
const response = await fetch(url, {
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',
},
});
@ -63,7 +68,7 @@ async function getProxyCheck(ip: string): Promise<boolean> {
}
const data = await response.json();
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> {
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}`, {
headers: {
Accept: '*/*',
'Accept-Language': 'es-ES,es;q=0.8,en;q=0.6',
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',
},
});
@ -89,21 +96,6 @@ async function getShroomey(ip: string): Promise<boolean> {
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
* @param ip IP to check
@ -150,7 +142,14 @@ async function dummy(): Promise<boolean> {
async function withoutCache(f, ip) {
if (!ip) return true;
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);
if (cache) {
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';
}
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
// use lock to just check three at a time
// do not check ip that currently gets checked
if (checking.indexOf(ipKey) == -1 && lock > 0) {
if (checking.indexOf(ipKey) === -1 && lock > 0) {
lock -= 1;
checking.push(ipKey);
withoutCache(f, ip)
@ -199,16 +203,16 @@ async function withCache(f, ip) {
return false;
}
export async function cheapDetector(ip: string): Promise<boolean> {
return (await withCache(getProxyCheck, ip));
export function cheapDetector(ip: string): Promise<boolean> {
return withCache(getProxyCheck, ip);
}
export async function strongDetector(ip: string): Promise<boolean> {
return (await withCache(getShroomey, ip));
export function strongDetector(ip: string): Promise<boolean> {
return withCache(getShroomey, ip);
}
export async function blacklistDetector(ip: string): Promise<boolean> {
return (await withCache(dummy, ip));
export function blacklistDetector(ip: string): Promise<boolean> {
return withCache(dummy, ip);
}
// export default cheapDetector;

View File

@ -15,7 +15,7 @@ class Minecraft {
this.online = {};
}
async report_login(minecraftid, minecraftname) {
async reportLogin(minecraftid, minecraftname) {
const user = new User();
user.minecraftname = minecraftname;
const reguser = await RegUser.findOne({ where: { minecraftid } });
@ -35,26 +35,27 @@ class Minecraft {
}
*/
report_logout(minecraftid) {
reportLogout(minecraftid) {
delete this.online[minecraftid];
}
report_userlist(list) {
reportUserlist(list) {
this.online = {};
list.forEach((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 {
const finduser = await RegUser.findOne({ where: { minecraftid } });
if (finduser) {
if (finduser.name == name) {
if (finduser.name === name) {
if (finduser.mcVerified) {
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 linked to other account ${finduser.name}.`;
@ -62,6 +63,7 @@ class Minecraft {
const reguser = await RegUser.findOne({ where: { name } });
if (reguser) {
if (reguser.minecraftid) {
// eslint-disable-next-line max-len
return `This pixelplanet account is already linked to ${reguser.minecraftname}`;
}
reguser.update({ minecraftname, minecraftid });
@ -95,8 +97,11 @@ class Minecraft {
minecraftname2User(minecraftname: string): User {
const searchstring = minecraftname;
for (const [minecraftid, user] of Object.entries(this.online)) {
if (user.minecraftname == searchstring) { return user; }
const onlineIds = Object.keys(this.online);
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();

View File

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

View File

@ -18,6 +18,7 @@ export type GUIState = {
paletteOpen: boolean,
menuOpen: boolean,
chatChannel: number,
style: string,
};
const initialState: GUIState = {
@ -33,6 +34,7 @@ const initialState: GUIState = {
paletteOpen: true,
menuOpen: false,
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': {
return {
...state,

View File

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

View File

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

View File

@ -45,23 +45,35 @@ router.use(bodyParser.urlencoded({ extended: true }));
* Check for POST parameters,
* 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;
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);
return;
}
const email = mailProvider.checkCode(code);
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);
return;
}
if (pass != passconf) {
const html = getPasswordResetHtml(null, null, 'Your passwords do not match :(');
if (pass !== passconf) {
const html = getPasswordResetHtml(
null,
null,
'Your passwords do not match :(',
);
res.status(400).send(html);
return;
}
@ -69,15 +81,24 @@ router.post('/', async (req: Request, res: Response, next) => {
// set password
const reguser = await RegUser.findOne({ where: { email } });
if (!reguser) {
// eslint-disable-next-line max-len
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);
return;
}
await reguser.update({ password: pass });
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);
});
@ -85,17 +106,26 @@ router.post('/', async (req: Request, res: Response, next) => {
/*
* 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;
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);
return;
}
const email = mailProvider.checkCode(token);
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);
return;
}

View File

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

View File

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

View File

@ -9,6 +9,7 @@ import audio from './audio';
import swal from './sweetAlert';
import protocolClientHook from './protocolClientHook';
import rendererHook from './rendererHook';
import styleHook from './styleHook';
// import ads from './ads';
// import analytics from './analytics';
import array from './array';
@ -40,6 +41,7 @@ const store = createStore(
title,
protocolClientHook,
rendererHook,
styleHook,
// ads,
// analytics,
logger,
@ -49,7 +51,9 @@ const store = createStore(
export default function configureStore(onComplete: ?() => void) {
persistStore(store, null, onComplete);
persistStore(store, null, () => {
onComplete(store);
});
if (isDebuggingInChrome) {
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 'sweetalert2/src/sweetalert2.scss';
export default () => (next) => (action) => {
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;
}
:global(.modallink) {
.modallink {
text-decoration: none;
color: #428bca;
cursor: pointer;
}
:global(.modallink:hover){
.modallink:hover){
font-weight: bold;
color: #226baa;
}
:global(.inarea) {
.inarea {
border-style: solid;
border-color: #d5d5d5;
padding: 4px;
@ -56,41 +56,41 @@ kbd {
border-width: 1px;
}
:global(.tab-list) {
.tab-list {
border-bottom: 1px solid #ccc;
padding-left: 0;
}
:global(.tab-list-item) {
.tab-list-item {
display: inline-block;
list-style: none;
margin-bottom: -1px;
padding: 0.5rem 0.75rem;
}
:global(.tab-list-active) {
.tab-list-active {
background-color: white;
border: solid #ccc;
border-width: 1px 1px 0 1px;
}
:global(table) {
table {
font-family: arial, sans-serif;
border-collapse: collapse;
width: 100%;
}
:global(td, th) {
td, th {
border: 1px solid #dddddd;
text-align: left;
padding: 8px;
}
:global(tr:nth-child(even)) {
tr:nth-child(even) {
background-color: #dddddd;
}
:global(.chatbox) {
.chatbox {
position: absolute;
background-color: rgba(226, 226, 226, 0.92);
width: 350px;
@ -101,7 +101,7 @@ kbd {
border-width: thin;
}
:global(.actionbuttons), :global(.coorbox), :global(.onlinebox), :global(.cooldownbox), :global(.palettebox) {
.actionbuttons, .coorbox, .onlinebox, .cooldownbox, .palettebox {
position: absolute;
background-color: rgba(226, 226, 226, 0.80);
color: black;
@ -114,114 +114,202 @@ kbd {
border: solid black;
border-width: thin;
}
:global(.coorbox) {
.coorbox {
left: 16px;
cursor: pointer;
bottom: 16px;
}
:global(.onlinebox) {
.onlinebox {
left: 16px;
bottom: 57px;
white-space: nowrap;
}
:global(#menubutton) {
#menubutton {
left: 16px;
top: 16px;
}
:global(#menu > div) {
#menu > div {
z-index: 1;
background-color: rgb(213, 238, 255);
}
:global(#helpbutton) {
#helpbutton {
left: 16px;
top: 221px;
}
:global(#minecraftbutton) {
#minecraftbutton {
left: 16px;
top: 180px;
}
:global(#settingsbutton) {
#settingsbutton {
left: 16px;
top: 57px;
}
:global(#loginbutton) {
#loginbutton {
left: 16px;
top: 98px;
}
:global(#downloadbutton) {
#downloadbutton {
left: 16px;
top: 139px;
}
:global(#globebutton) {
#globebutton {
left: 98px;
top: 16px;
}
:global(#voxelbutton) {
#voxelbutton {
left: 180px;
top: 16px;
}
:global(#canvasbutton) {
#canvasbutton {
left: 57px;
top: 16px;
}
:global(#minecrafttpbutton) {
#minecrafttpbutton {
top: 16px;
right: 16px;
}
:global(#palselbutton) {
bottom: 16px;
right: 16px;
#palselbutton {
bottom: 16px;
right: 16px;
}
:global(#chatbutton) {
#chatbutton {
bottom: 16px;
right: 57px;
}
:global(#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;
#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;
}
:global(.hsar) {
.hsar {
padding: 0;
}
:global(.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;
.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;
}
.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) {
:global(.Modal) {
.Modal {
position: fixed;
top: 0px;
left: 0px;
@ -235,7 +323,7 @@ kbd {
padding: 5%;
}
}
:global(.Overlay) {
.Overlay {
position: fixed;
top: 0px;
left: 0px;
@ -245,54 +333,51 @@ kbd {
z-index: 4;
}
:global(.chatbox div .chatarea ) {
.chatbox div .chatarea {
height: 174px;
}
:global(.chatarea) {
.chatarea {
padding: 3px 3px 0px 3px;
margin: 0px;
overflow-x: hidden;
overflow-y: scroll;
height: 95%;
}
:global(.Modal div chatarea) {
max-height: 600px;
}
:global(.chatinput) {
.chatinput {
height: 22px;
white-space: nowrap;
}
:global(.chatname) {
.chatname {
color: #4B0000;
font-size: 13px;
user-select: all;
}
:global(.chatmsg) {
.chatmsg {
color: #030303;
font-size: 13px;
user-select: text;
margin: 0;
}
:global(.usermessages) {
.usermessages {
font-size: 14px;
font-weight: 500;
line-height: normal;
}
:global(.stattext) {
.stattext {
font-size: 18px;
}
:global(.statvalue) {
.statvalue {
font-size: 18px;
font-weight: bold;
color: #2d0045;
}
:global(.pressed) {
.pressed {
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);
position: absolute;
top: 57px;
@ -313,18 +398,18 @@ kbd {
z-index: 2;
}
:global(.notifyboxvis) {
.notifyboxvis {
visibility: visible;
opacity: 1;
transition: visibility 0s, opacity 0.5s linear;
}
:global(.notifyboxhid) {
.notifyboxhid {
visibility: hidden;
opacity: 0;
transition: visibility 0.5s, opacity 0.5s linear;
}
:global(.cooldownbox) {
.cooldownbox {
top: 16px;
width: 48px;
left: 0px;
@ -334,18 +419,18 @@ kbd {
z-index: 2;
}
:global(.actionbuttons) {
.actionbuttons {
vertical-align: text-bottom;
cursor: pointer;
width: 36px;
padding: 0;
}
:global(.actionbuttons:hover) {
.actionbuttons:hover, .coorbox:hover, #menu > div:hover {
background-color: #d2d2d2cc;
}
:global(.palettebox) {
.palettebox {
z-index: 1;
bottom: 59px;
padding: 3px;
@ -355,8 +440,8 @@ kbd {
margin-right: auto;
}
:global(#colors) :global(.selected),
:global(#colors) span:hover {
#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;
@ -365,7 +450,7 @@ kbd {
transform: scale(1.10,1.10); /* Standard syntax */
}
:global(#outstreamContainer) {
#outstreamContainer {
position: fixed;
display: none;
width: 100%;
@ -378,6 +463,6 @@ kbd {
z-index: 9000;
}
:global(.grecaptcha-badge) {
.grecaptcha-badge {
visibility: hidden;
}

View File

@ -6,13 +6,13 @@ body {
font-family: Montserrat,sans-serif;
}
:global(.tm) {
.tm {
position: absolute;
top: 10px;
right: 10px;
}
:global(.webgl-error) {
.webgl-error {
font: 15px/30px monospace;
text-align: center;
color: #fff;
@ -20,12 +20,12 @@ body {
}
:global(.webgl-error) a {
.webgl-error a {
color: #fff;
}
:global(#coorbox) {
#coorbox {
position: absolute;
background-color: hsla(0,0%,89%,.8);
color: #000;
@ -42,7 +42,7 @@ body {
}
:global(#info) {
#info {
font-size: 13px;
position: absolute;
background-color: hsla(0,0%,89%,.8);
@ -59,7 +59,7 @@ body {
top: 16px
}
:global(#loading) {
#loading {
font-size: 16px;
font-weight: bold;
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,
) {
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;
const tiledZoom = canvasMaxTiledZoom + Math.log2(tiledScale) / 2;
const relScale = viewscale / tiledScale;
@ -174,7 +176,11 @@ class Renderer {
updateView(view, canvasSize) {
const [x, y] = view;
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) {
cx = curcx;
cy = curcy;
@ -233,8 +239,12 @@ class Renderer {
return false;
}
const { width, height } = this.viewport;
const CHUNK_RENDER_RADIUS_X = Math.ceil(width / TILE_SIZE / 2 / this.relScale);
const CHUNK_RENDER_RADIUS_Y = Math.ceil(height / TILE_SIZE / 2 / this.relScale);
const CHUNK_RENDER_RADIUS_X = Math.ceil(
width / TILE_SIZE / 2 / this.relScale,
);
const CHUNK_RENDER_RADIUS_Y = Math.ceil(
height / TILE_SIZE / 2 / this.relScale,
);
const [xc, yc] = this.centerChunk;
if (Math.abs(cx - xc)
<= CHUNK_RENDER_RADIUS_X && Math.abs(cy - yc)
@ -450,15 +460,21 @@ class Renderer {
viewportCtx.scale(viewscale, viewscale);
viewportCtx.drawImage(
this.canvas,
width / 2 / viewscale - CANVAS_WIDTH / 2 + ((cx + 0.5) * TILE_SIZE - canvasCenter - x),
height / 2 / viewscale - CANVAS_HEIGHT / 2 + ((cy + 0.5) * TILE_SIZE - canvasCenter - y),
width / 2 / viewscale - CANVAS_WIDTH / 2 + (
(cx + 0.5) * TILE_SIZE - canvasCenter - x),
height / 2 / viewscale - CANVAS_HEIGHT / 2 + (
(cy + 0.5) * TILE_SIZE - canvasCenter - y),
);
viewportCtx.restore();
} else {
viewportCtx.drawImage(
this.canvas,
Math.floor(width / 2 - CANVAS_WIDTH / 2 + ((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),
Math.floor(width / 2 - CANVAS_WIDTH / 2
+ ((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.drawImage(
this.canvas,
// eslint-disable-next-line max-len
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),
);
viewportCtx.restore();
} else {
viewportCtx.drawImage(
this.canvas,
// eslint-disable-next-line max-len
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),
);
}

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 setStyle from './setStyle';
const store = configureStore();
const store = configureStore(() => {
const state = store.getState();
setStyle(state.gui.style);
});
export default store;

View File

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

View File

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

View File

@ -11,8 +11,8 @@ import pkg from '../package.json';
*/
async function copy() {
await makeDir('build');
await makeDir('build/log');
await Promise.all([
makeDir('build/log'),
copyFile('LICENSE', 'build/LICENSE'),
copyDir('public', 'build/public'),
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-backup.yml', 'build/ecosystem-backup.example.yml'),
]);
await makeDir('build/public/assets');
}
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: {
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$/,
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/,
use: [
@ -165,20 +96,6 @@ const config = {
'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$/,
loader: path.resolve(__dirname, './lib/markdown-loader.js'),
@ -242,6 +159,81 @@ const clientConfig = {
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: [
// Define free variables
// https://webpack.github.io/docs/list-of-plugins.html#defineplugin
@ -315,26 +307,85 @@ const webConfig = {
...config.module,
// Override babel-preset-env configuration for Node.js
rules: config.module.rules.map((rule) => (rule.loader !== 'babel-loader' ? rule : {
...rule,
query: {
...rule.query,
presets: rule.query.presets.map((preset) => (preset[0] !== '@babel/preset-env' ? preset : ['@babel/preset-env', {
targets: {
node: pkg.engines.node.replace(/^\D+/g, ''),
},
modules: false,
useBuiltIns: false,
debug: false,
}])),
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: {
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
externals: [
/\/proxies\.json$/,
/\/canvases\.json$/,
/^\.\/styleassets\.json$/,
/^\.\/assets\.json$/,
(context, request, callback) => {
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]
y = unpack_from('B', message, 2)[0]
a = unpack_from('!h', message, 4)[0]
if x != 10000 and y != 10000:
return
color = int(unpack_from('!B', message, 6)[0])
if color == 0:
color = 19
@ -29,6 +27,8 @@ def on_message(ws, message):
number = (65520 & a) >> 4
x = int(x * 256 + a % 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)))
def on_error(ws, error):