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>