Add converter in usermodal
This commit is contained in:
parent
80fe10da7b
commit
3e86ea25be
5
package-lock.json
generated
5
package-lock.json
generated
|
@ -11285,6 +11285,11 @@
|
||||||
"any-promise": "^1.3.0"
|
"any-promise": "^1.3.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"rgbquant": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/rgbquant/-/rgbquant-1.1.2.tgz",
|
||||||
|
"integrity": "sha1-2V6Imo/LHmwaT6LMyL46QaL80YU="
|
||||||
|
},
|
||||||
"rimraf": {
|
"rimraf": {
|
||||||
"version": "3.0.2",
|
"version": "3.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
|
||||||
|
|
|
@ -72,6 +72,7 @@
|
||||||
"redux-logger": "^3.0.6",
|
"redux-logger": "^3.0.6",
|
||||||
"redux-persist": "^6.0.0",
|
"redux-persist": "^6.0.0",
|
||||||
"redux-thunk": "^2.2.0",
|
"redux-thunk": "^2.2.0",
|
||||||
|
"rgbquant": "^1.1.2",
|
||||||
"sequelize": "^5.21.7",
|
"sequelize": "^5.21.7",
|
||||||
"sharp": "^0.25.2",
|
"sharp": "^0.25.2",
|
||||||
"startaudiocontext": "^1.2.1",
|
"startaudiocontext": "^1.2.1",
|
||||||
|
|
|
@ -84,7 +84,7 @@ function init() {
|
||||||
}
|
}
|
||||||
if (cnt > 1) {
|
if (cnt > 1) {
|
||||||
document.body.style.setProperty(
|
document.body.style.setProperty(
|
||||||
"-webkit-transform", "rotate(-180deg)",
|
'-webkit-transform', 'rotate(-180deg)',
|
||||||
null,
|
null,
|
||||||
);
|
);
|
||||||
fetch('https://assets.pixelplanet.fun/iamabot');
|
fetch('https://assets.pixelplanet.fun/iamabot');
|
||||||
|
|
|
@ -3,10 +3,12 @@
|
||||||
* @flow
|
* @flow
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { useState } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import fileDownload from 'js-file-download';
|
import fileDownload from 'js-file-download';
|
||||||
|
import RgbQuant from 'rgbquant';
|
||||||
|
|
||||||
|
import printGIMPPalette from '../core/exportGPL';
|
||||||
import type { State } from '../reducers';
|
import type { State } from '../reducers';
|
||||||
|
|
||||||
const titleStyle = {
|
const titleStyle = {
|
||||||
|
@ -18,7 +20,7 @@ const titleStyle = {
|
||||||
lineHeight: '24px',
|
lineHeight: '24px',
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
fontWeight: 500,
|
fontWeight: 500,
|
||||||
// marginTop: 0,
|
marginTop: 4,
|
||||||
marginBottom: 0,
|
marginBottom: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -32,35 +34,117 @@ const textStyle = {
|
||||||
lineHeight: 'normal',
|
lineHeight: 'normal',
|
||||||
};
|
};
|
||||||
|
|
||||||
function appendNumberText(number) {
|
function downloadOutput() {
|
||||||
let appendStr = `${number} `;
|
const output = document.getElementById('imgoutput');
|
||||||
if (number < 10) appendStr += ' ';
|
output.toBlob((blob) => fileDownload(blob, 'ppfunconvert.png'));
|
||||||
else if (number < 100) appendStr += ' ';
|
|
||||||
return appendStr;
|
|
||||||
}
|
|
||||||
function appendHexColorText(clr) {
|
|
||||||
let appendStr = ' #';
|
|
||||||
clr.forEach((z) => {
|
|
||||||
if (z < 16) appendStr += '0';
|
|
||||||
appendStr += z.toString(16);
|
|
||||||
});
|
|
||||||
return appendStr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function readFile(
|
||||||
|
file,
|
||||||
|
selectFile,
|
||||||
|
) {
|
||||||
|
if (!file) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const fr = new FileReader();
|
||||||
|
fr.onload = () => {
|
||||||
|
const img = new Image();
|
||||||
|
img.onload = () => {
|
||||||
|
selectFile(img);
|
||||||
|
};
|
||||||
|
img.src = fr.result;
|
||||||
|
};
|
||||||
|
fr.readAsDataURL(file);
|
||||||
|
}
|
||||||
|
|
||||||
function printGIMPPalette(title, description, colors) {
|
function drawPixels(idxi8, width, height) {
|
||||||
let text = `GIMP Palette
|
const can = document.createElement('canvas');
|
||||||
#Palette Name: Pixelplanet${title}
|
can.width = width;
|
||||||
#Description: ${description}
|
can.height = height;
|
||||||
#Colors: ${colors.length}`;
|
const ctx = can.getContext('2d');
|
||||||
colors.forEach((clr) => {
|
ctx.imageSmoothingEnabled = false;
|
||||||
text += '\n';
|
ctx.mozImageSmoothingEnabled = false;
|
||||||
clr.forEach((z) => {
|
ctx.webkitImageSmoothingEnabled = false;
|
||||||
text += appendNumberText(z);
|
ctx.msImageSmoothingEnabled = false;
|
||||||
});
|
|
||||||
text += appendHexColorText(clr);
|
const imgd = ctx.createImageData(can.width, can.height);
|
||||||
|
const { data } = imgd;
|
||||||
|
for (let i = 0, len = data.length; i < len; ++i) data[i] = idxi8[i];
|
||||||
|
|
||||||
|
ctx.putImageData(imgd, 0, 0);
|
||||||
|
return can;
|
||||||
|
}
|
||||||
|
|
||||||
|
function addGrid(img, lightGrid, offsetX, offsetY) {
|
||||||
|
const can = document.createElement('canvas');
|
||||||
|
const ctx = can.getContext('2d');
|
||||||
|
can.width = img.width * 5;
|
||||||
|
can.height = img.height * 5;
|
||||||
|
ctx.imageSmoothingEnabled = false;
|
||||||
|
ctx.mozImageSmoothingEnabled = false;
|
||||||
|
ctx.webkitImageSmoothingEnabled = false;
|
||||||
|
ctx.msImageSmoothingEnabled = false;
|
||||||
|
ctx.save();
|
||||||
|
ctx.scale(5.0, 5.0);
|
||||||
|
ctx.drawImage(img, 0, 0);
|
||||||
|
ctx.restore();
|
||||||
|
ctx.fillStyle = (lightGrid) ? '#DDDDDD' : '#222222';
|
||||||
|
|
||||||
|
for (let i = 0; i <= img.width; i += 1) {
|
||||||
|
const thick = ((i + (offsetX * 1)) % 10 === 0) ? 2 : 1;
|
||||||
|
ctx.fillRect(i * 5, 0, thick, can.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let j = 0; j <= img.height; j += 1) {
|
||||||
|
const thick = ((j + (offsetY * 1)) % 10 === 0) ? 2 : 1;
|
||||||
|
ctx.fillRect(0, j * 5, can.width, thick);
|
||||||
|
}
|
||||||
|
|
||||||
|
return can;
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderOutputImage(
|
||||||
|
colors,
|
||||||
|
selectedFile,
|
||||||
|
selectedStrategy,
|
||||||
|
selectedSerp,
|
||||||
|
selectedColorDist,
|
||||||
|
selectedDithDelta,
|
||||||
|
selectedAddGrid,
|
||||||
|
selectedLightGrid,
|
||||||
|
selectedGridOffsetX,
|
||||||
|
selectedGridOffsetY,
|
||||||
|
) {
|
||||||
|
if (!selectedFile) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const rgbQuant = new RgbQuant({
|
||||||
|
colors: colors.length,
|
||||||
|
dithKern: selectedStrategy,
|
||||||
|
dithDelta: selectedDithDelta / 100,
|
||||||
|
dithSerp: selectedSerp,
|
||||||
|
palette: colors,
|
||||||
|
reIndex: false,
|
||||||
|
useCache: false,
|
||||||
|
colorDist: selectedColorDist,
|
||||||
});
|
});
|
||||||
fileDownload(text, `Pixelplanet${title}.gpl`);
|
rgbQuant.sample(selectedFile);
|
||||||
|
rgbQuant.palette();
|
||||||
|
const pxls = rgbQuant.reduce(selectedFile);
|
||||||
|
let can = drawPixels(pxls, selectedFile.width, selectedFile.height);
|
||||||
|
const output = document.getElementById('imgoutput');
|
||||||
|
if (selectedAddGrid) {
|
||||||
|
can = addGrid(
|
||||||
|
can,
|
||||||
|
selectedLightGrid,
|
||||||
|
selectedGridOffsetX,
|
||||||
|
selectedGridOffsetY,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
output.width = can.width;
|
||||||
|
output.height = can.height;
|
||||||
|
const ctx = output.getContext('2d');
|
||||||
|
ctx.drawImage(can, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -69,6 +153,41 @@ function Converter({
|
||||||
canvases,
|
canvases,
|
||||||
}) {
|
}) {
|
||||||
const [selectedCanvas, selectCanvas] = useState(canvasId);
|
const [selectedCanvas, selectCanvas] = useState(canvasId);
|
||||||
|
const [selectedFile, selectFile] = useState(null);
|
||||||
|
const [selectedStrategy, selectStrategy] = useState('');
|
||||||
|
const [selectedSerp, selectSerp] = useState(false);
|
||||||
|
const [selectedColorDist, selectColorDist] = useState('euclidean');
|
||||||
|
const [selectedDithDelta, selectDithDelta] = useState(0);
|
||||||
|
const [selectedAddGrid, selectAddGrid] = useState(true);
|
||||||
|
const [selectedLightGrid, selectLightGrid] = useState(false);
|
||||||
|
const [selectedGridOffsetX, selectGridOffsetX] = useState(0);
|
||||||
|
const [selectedGridOffsetY, selectGridOffsetY] = useState(0);
|
||||||
|
const input = document.createElement('canvas');
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (selectedFile) {
|
||||||
|
const canvas = canvases[selectedCanvas];
|
||||||
|
renderOutputImage(
|
||||||
|
canvas.colors.slice(canvas.cli),
|
||||||
|
selectedFile,
|
||||||
|
selectedStrategy,
|
||||||
|
selectedSerp,
|
||||||
|
selectedColorDist,
|
||||||
|
selectedDithDelta,
|
||||||
|
selectedAddGrid,
|
||||||
|
selectedLightGrid,
|
||||||
|
selectedGridOffsetX,
|
||||||
|
selectedGridOffsetY,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
const output = document.getElementById('imgoutput');
|
||||||
|
const ctx = output.getContext('2d');
|
||||||
|
output.width = 128;
|
||||||
|
output.height = 100;
|
||||||
|
ctx.fillStyle = '#C4C4C4';
|
||||||
|
ctx.fillRect(0, 0, 128, 100);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<p style={{ textAlign: 'center' }}>
|
<p style={{ textAlign: 'center' }}>
|
||||||
|
@ -95,22 +214,172 @@ function Converter({
|
||||||
}
|
}
|
||||||
</select>
|
</select>
|
||||||
</p>
|
</p>
|
||||||
|
<h3 style={titleStyle}>Palette Download</h3>
|
||||||
<p style={textStyle}>
|
<p style={textStyle}>
|
||||||
Palette for <a href="https://www.gimp.org">GIMP</a>:
|
Palette for <a href="https://www.gimp.org">GIMP</a>:
|
||||||
<button
|
<button
|
||||||
|
type="button"
|
||||||
style={{ display: 'inline' }}
|
style={{ display: 'inline' }}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
const canvas = canvases[selectedCanvas];
|
const canvas = canvases[selectedCanvas];
|
||||||
const {
|
const {
|
||||||
title, desc, colors, cli,
|
title, desc, colors, cli,
|
||||||
} = canvas;
|
} = canvas;
|
||||||
printGIMPPalette(title, desc, colors.slice(cli));
|
fileDownload(
|
||||||
|
printGIMPPalette(title, desc, colors.slice(cli)),
|
||||||
|
`Pixelplanet${title}.gpl`,
|
||||||
|
);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Download Palette
|
Download
|
||||||
</button>
|
</button>
|
||||||
<p>Credit for the Palette of the Moon goes to <a href="https://twitter.com/starhousedev">starhouse</a>.</p>
|
<p>Credit for the Palette of the Moon goes to
|
||||||
|
<a href="https://twitter.com/starhousedev">starhouse</a>.</p>
|
||||||
</p>
|
</p>
|
||||||
|
<h3 style={titleStyle}>Image Converter</h3>
|
||||||
|
<p style={textStyle}>Convert an image to canvas colors</p>
|
||||||
|
<input
|
||||||
|
type="file"
|
||||||
|
id="imgfile"
|
||||||
|
onChange={(evt) => {
|
||||||
|
const fileSel = evt.target;
|
||||||
|
const file = (!fileSel.files || !fileSel.files[0])
|
||||||
|
? null : fileSel.files[0];
|
||||||
|
readFile(file, selectFile);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<p style={textStyle}>Choose Strategy:
|
||||||
|
<select
|
||||||
|
onChange={(e) => {
|
||||||
|
const sel = e.target;
|
||||||
|
selectStrategy(sel.options[sel.selectedIndex].value);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<option
|
||||||
|
value=""
|
||||||
|
selected={(selectedStrategy === '')}
|
||||||
|
>Default</option>
|
||||||
|
{
|
||||||
|
['FloydSteinberg',
|
||||||
|
'Stucki',
|
||||||
|
'Atkinson',
|
||||||
|
'Jarvis',
|
||||||
|
'Burkes',
|
||||||
|
'Sierra',
|
||||||
|
'TwoSierra',
|
||||||
|
'SierraLite',
|
||||||
|
'FalseFloydSteinberg'].map((strat) => (
|
||||||
|
<option
|
||||||
|
value={strat}
|
||||||
|
selected={(selectedStrategy === strat)}
|
||||||
|
>{strat}</option>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</select>
|
||||||
|
</p>
|
||||||
|
<span style={{ ...textStyle, fontHeight: 16 }}>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
onChange={(e) => {
|
||||||
|
selectSerp(e.target.checked);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
Serpentine Pattern Dithering
|
||||||
|
</span>
|
||||||
|
<span style={{ ...textStyle, fontHeight: 16 }}>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
onClick={(e) => {
|
||||||
|
const colorDist = (e.target.checked)
|
||||||
|
? 'euclidean' : 'manhatten';
|
||||||
|
selectColorDist(colorDist);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
Manhatten Color Distance
|
||||||
|
</span>
|
||||||
|
<p style={textStyle}>Dithering Delta:
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
step="1"
|
||||||
|
min="0"
|
||||||
|
max="100"
|
||||||
|
style={{ width: '3em' }}
|
||||||
|
value={selectedDithDelta}
|
||||||
|
onChange={(e) => {
|
||||||
|
selectDithDelta(e.target.value);
|
||||||
|
}}
|
||||||
|
/>%
|
||||||
|
</p>
|
||||||
|
<p style={{ ...textStyle, fontHeight: 16 }}>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={selectedAddGrid}
|
||||||
|
onChange={(e) => {
|
||||||
|
selectAddGrid(e.target.checked);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
Add Grid
|
||||||
|
</p>
|
||||||
|
{(selectedAddGrid)
|
||||||
|
? (
|
||||||
|
<div style={{
|
||||||
|
borderStyle: 'solid',
|
||||||
|
borderColor: '#D4D4D4',
|
||||||
|
borderWidth: 2,
|
||||||
|
padding: 5,
|
||||||
|
display: 'inline-block',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<p style={{ ...textStyle, fontHeight: 16 }}>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
onChange={(e) => {
|
||||||
|
selectLightGrid(e.target.checked);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
Light Grid
|
||||||
|
</p>
|
||||||
|
<span style={textStyle}>Offset X:
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
step="1"
|
||||||
|
min="0"
|
||||||
|
max="10"
|
||||||
|
style={{ width: '2em' }}
|
||||||
|
value={selectedGridOffsetX}
|
||||||
|
onChange={(e) => {
|
||||||
|
selectGridOffsetX(e.target.value);
|
||||||
|
}}
|
||||||
|
/>%
|
||||||
|
</span>
|
||||||
|
<span style={textStyle}>Offset Y:
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
step="1"
|
||||||
|
min="0"
|
||||||
|
max="10"
|
||||||
|
style={{ width: '2em' }}
|
||||||
|
value={selectedGridOffsetY}
|
||||||
|
onChange={(e) => {
|
||||||
|
selectGridOffsetY(e.target.value);
|
||||||
|
}}
|
||||||
|
/>%
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
: null}
|
||||||
|
<p>
|
||||||
|
<canvas
|
||||||
|
id="imgoutput"
|
||||||
|
style={{ width: '80%' }}
|
||||||
|
/>
|
||||||
|
</p>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={downloadOutput}
|
||||||
|
>
|
||||||
|
Download Template
|
||||||
|
</button>
|
||||||
</p>
|
</p>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ import { connect } from 'react-redux';
|
||||||
import type { State } from '../reducers';
|
import type { State } from '../reducers';
|
||||||
|
|
||||||
const DailyRankings = ({ totalDailyRanking }) => (
|
const DailyRankings = ({ totalDailyRanking }) => (
|
||||||
<div style={{ overflowY: 'auto' }}>
|
<div style={{ overflowY: 'auto', display: 'inline-block' }}>
|
||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
<th>#</th>
|
<th>#</th>
|
||||||
|
|
|
@ -20,7 +20,7 @@ function validate(nameoremail, password) {
|
||||||
return errors;
|
return errors;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function submit_login(nameoremail, password, component) {
|
async function submitLogin(nameoremail, password) {
|
||||||
const body = JSON.stringify({
|
const body = JSON.stringify({
|
||||||
nameoremail,
|
nameoremail,
|
||||||
password,
|
password,
|
||||||
|
@ -37,8 +37,9 @@ async function submit_login(nameoremail, password, component) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const inputStyles = {
|
const inputStyles = {
|
||||||
display: 'block',
|
display: 'inline-block',
|
||||||
width: '100%',
|
width: '100%',
|
||||||
|
maxWidth: '35em',
|
||||||
};
|
};
|
||||||
|
|
||||||
class LogInForm extends React.Component {
|
class LogInForm extends React.Component {
|
||||||
|
@ -59,6 +60,7 @@ class LogInForm extends React.Component {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
const { nameoremail, password, submitting } = this.state;
|
const { nameoremail, password, submitting } = this.state;
|
||||||
|
const { me: setMe } = this.props;
|
||||||
if (submitting) return;
|
if (submitting) return;
|
||||||
|
|
||||||
const errors = validate(nameoremail, password);
|
const errors = validate(nameoremail, password);
|
||||||
|
@ -67,7 +69,10 @@ class LogInForm extends React.Component {
|
||||||
if (errors.length > 0) return;
|
if (errors.length > 0) return;
|
||||||
|
|
||||||
this.setState({ submitting: true });
|
this.setState({ submitting: true });
|
||||||
const { errors: resperrors, me } = await submit_login(nameoremail, password);
|
const { errors: resperrors, me } = await submitLogin(
|
||||||
|
nameoremail,
|
||||||
|
password,
|
||||||
|
);
|
||||||
if (resperrors) {
|
if (resperrors) {
|
||||||
this.setState({
|
this.setState({
|
||||||
errors: resperrors,
|
errors: resperrors,
|
||||||
|
@ -75,32 +80,37 @@ class LogInForm extends React.Component {
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.props.me(me);
|
setMe(me);
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { errors } = this.state;
|
const {
|
||||||
|
errors, nameoremail, password, submitting,
|
||||||
|
} = this.state;
|
||||||
return (
|
return (
|
||||||
<form onSubmit={this.handleSubmit}>
|
<form onSubmit={this.handleSubmit}>
|
||||||
{errors.map((error) => (
|
{errors.map((error) => (
|
||||||
<p key={error}>Error: {error}</p>
|
<p key={error}>Error: {error}</p>
|
||||||
))}
|
))}
|
||||||
<input
|
<input
|
||||||
|
value={nameoremail}
|
||||||
style={inputStyles}
|
style={inputStyles}
|
||||||
value={this.state.nameoremail}
|
|
||||||
onChange={(evt) => this.setState({ nameoremail: evt.target.value })}
|
onChange={(evt) => this.setState({ nameoremail: evt.target.value })}
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Name or Email"
|
placeholder="Name or Email"
|
||||||
/>
|
/>
|
||||||
<input
|
<input
|
||||||
|
value={password}
|
||||||
style={inputStyles}
|
style={inputStyles}
|
||||||
value={this.state.password}
|
|
||||||
onChange={(evt) => this.setState({ password: evt.target.value })}
|
onChange={(evt) => this.setState({ password: evt.target.value })}
|
||||||
type="password"
|
type="password"
|
||||||
placeholder="Password"
|
placeholder="Password"
|
||||||
/>
|
/>
|
||||||
|
<p>
|
||||||
<button type="submit">{(this.state.submitting) ? '...' : 'LogIn'}</button>
|
<button type="submit">
|
||||||
|
{(submitting) ? '...' : 'LogIn'}
|
||||||
|
</button>
|
||||||
|
</p>
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ import { connect } from 'react-redux';
|
||||||
import type { State } from '../reducers';
|
import type { State } from '../reducers';
|
||||||
|
|
||||||
const TotalRankings = ({ totalRanking }) => (
|
const TotalRankings = ({ totalRanking }) => (
|
||||||
<div style={{ overflowY: 'auto' }}>
|
<div style={{ overflowY: 'auto', display: 'inline-block' }}>
|
||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
<th>#</th>
|
<th>#</th>
|
||||||
|
|
|
@ -10,8 +10,6 @@ import Modal from './Modal';
|
||||||
|
|
||||||
import type { State } from '../reducers';
|
import type { State } from '../reducers';
|
||||||
|
|
||||||
const Converter = React.lazy(() => import(/* webpackChunkName: "converter" */ './Converter'));
|
|
||||||
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
showRegisterModal, showForgotPasswordModal, setName, setMailreg, receiveMe,
|
showRegisterModal, showForgotPasswordModal, setName, setMailreg, receiveMe,
|
||||||
|
@ -21,23 +19,13 @@ import Tabs from './Tabs';
|
||||||
import UserArea from './UserArea';
|
import UserArea from './UserArea';
|
||||||
import Rankings from './Rankings';
|
import Rankings from './Rankings';
|
||||||
|
|
||||||
|
// eslint-disable-next-line max-len
|
||||||
|
const Converter = React.lazy(() => import(/* webpackChunkName: "converter" */ './Converter'));
|
||||||
|
|
||||||
const logoStyle = {
|
const logoStyle = {
|
||||||
marginRight: 5,
|
marginRight: 5,
|
||||||
};
|
};
|
||||||
|
|
||||||
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 = {
|
const textStyle = {
|
||||||
color: 'hsla(218, 5%, 47%, .6)',
|
color: 'hsla(218, 5%, 47%, .6)',
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
|
@ -55,32 +43,77 @@ const LogInArea = ({ register, forgot_password, me }) => (
|
||||||
<p style={textStyle}>Login to access more features and stats.</p><br />
|
<p style={textStyle}>Login to access more features and stats.</p><br />
|
||||||
<h2>Login with Mail:</h2>
|
<h2>Login with Mail:</h2>
|
||||||
<LogInForm me={me} />
|
<LogInForm me={me} />
|
||||||
<p className="modallink" onClick={forgot_password}>I forgot my Password.</p>
|
<p
|
||||||
|
className="modallink"
|
||||||
|
onClick={forgot_password}
|
||||||
|
>
|
||||||
|
I forgot my Password.</p>
|
||||||
<h2>or login with:</h2>
|
<h2>or login with:</h2>
|
||||||
<a href="./api/auth/discord"><img style={logoStyle} width={32} src={`${window.assetserver}/discordlogo.svg`} alt="Discord" /></a>
|
<a href="./api/auth/discord">
|
||||||
<a href="./api/auth/google"><img style={logoStyle} width={32} src={`${window.assetserver}/googlelogo.svg`} alt="Google" /></a>
|
<img
|
||||||
<a href="./api/auth/facebook"><img style={logoStyle} width={32} src={`${window.assetserver}/facebooklogo.svg`} alt="Facebook" /></a>
|
style={logoStyle}
|
||||||
<a href="./api/auth/vk"><img style={logoStyle} width={32} src={`${window.assetserver}/vklogo.svg`} alt="vk" /></a>
|
width={32}
|
||||||
<a href="./api/auth/reddit"><img style={logoStyle} width={32} src={`${window.assetserver}/redditlogo.svg`} alt="vk" /></a>
|
src={`${window.assetserver}/discordlogo.svg`}
|
||||||
|
alt="Discord"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
<a href="./api/auth/google">
|
||||||
|
<img
|
||||||
|
style={logoStyle}
|
||||||
|
width={32}
|
||||||
|
src={`${window.assetserver}/googlelogo.svg`}
|
||||||
|
alt="Google"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
<a href="./api/auth/facebook">
|
||||||
|
<img
|
||||||
|
style={logoStyle}
|
||||||
|
width={32}
|
||||||
|
src={`${window.assetserver}/facebooklogo.svg`}
|
||||||
|
alt="Facebook"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
<a href="./api/auth/vk">
|
||||||
|
<img
|
||||||
|
style={logoStyle}
|
||||||
|
width={32}
|
||||||
|
src={`${window.assetserver}/vklogo.svg`}
|
||||||
|
alt="vk"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
<a href="./api/auth/reddit">
|
||||||
|
<img
|
||||||
|
style={logoStyle}
|
||||||
|
width={32}
|
||||||
|
src={`${window.assetserver}/redditlogo.svg`}
|
||||||
|
alt="vk"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
<h2>or register here:</h2>
|
<h2>or register here:</h2>
|
||||||
<button type="button" onClick={register}>Register</button>
|
<button type="button" onClick={register}>Register</button>
|
||||||
</p>
|
</p>
|
||||||
);
|
);
|
||||||
|
|
||||||
const UserAreaModal = ({
|
const UserAreaModal = ({
|
||||||
name, register, forgot_password, doMe, logout, setName, setMailreg, canvases,
|
name, register, forgot_password, doMe, logout, setUserName, setUserMailreg,
|
||||||
}) => (
|
}) => (
|
||||||
<Modal title="User Area">
|
<Modal title="User Area">
|
||||||
<p style={{ textAlign: 'center' }}>
|
<p style={{ textAlign: 'center' }}>
|
||||||
{(name === null)
|
{(name === null)
|
||||||
? <LogInArea register={register} forgot_password={forgot_password} me={doMe} />
|
? (
|
||||||
|
<LogInArea
|
||||||
|
register={register}
|
||||||
|
forgot_password={forgot_password}
|
||||||
|
me={doMe}
|
||||||
|
/>
|
||||||
|
)
|
||||||
: (
|
: (
|
||||||
<Tabs>
|
<Tabs>
|
||||||
<div label="Profile">
|
<div label="Profile">
|
||||||
<UserArea
|
<UserArea
|
||||||
logout={logout}
|
logout={logout}
|
||||||
set_name={setName}
|
set_name={setUserName}
|
||||||
set_mailreg={setMailreg}
|
set_mailreg={setUserMailreg}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div label="Ranking">
|
<div label="Ranking">
|
||||||
|
@ -93,7 +126,9 @@ const UserAreaModal = ({
|
||||||
</div>
|
</div>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
)}
|
)}
|
||||||
<p>Also join our Discord: <a href="./discord" target="_blank">pixelplanet.fun/discord</a></p>
|
<p>Also join our Discord:
|
||||||
|
<a href="./discord" target="_blank">pixelplanet.fun/discord</a>
|
||||||
|
</p>
|
||||||
</p>
|
</p>
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
|
@ -109,14 +144,17 @@ function mapDispatchToProps(dispatch) {
|
||||||
doMe(me) {
|
doMe(me) {
|
||||||
dispatch(receiveMe(me));
|
dispatch(receiveMe(me));
|
||||||
},
|
},
|
||||||
setName(name) {
|
setUserName(name) {
|
||||||
dispatch(setName(name));
|
dispatch(setName(name));
|
||||||
},
|
},
|
||||||
setMailreg(mailreg) {
|
setUserMailreg(mailreg) {
|
||||||
dispatch(setMailreg(mailreg));
|
dispatch(setMailreg(mailreg));
|
||||||
},
|
},
|
||||||
async logout() {
|
async logout() {
|
||||||
const response = await fetch('./api/auth/logout', { credentials: 'include' });
|
const response = await fetch(
|
||||||
|
'./api/auth/logout',
|
||||||
|
{ credentials: 'include' },
|
||||||
|
);
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
const resp = await response.json();
|
const resp = await response.json();
|
||||||
dispatch(receiveMe(resp.me));
|
dispatch(receiveMe(resp.me));
|
||||||
|
@ -127,8 +165,7 @@ function mapDispatchToProps(dispatch) {
|
||||||
|
|
||||||
function mapStateToProps(state: State) {
|
function mapStateToProps(state: State) {
|
||||||
const { name } = state.user;
|
const { name } = state.user;
|
||||||
const { canvases } = state.canvas;
|
return { name };
|
||||||
return { name, canvases };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(UserAreaModal);
|
export default connect(mapStateToProps, mapDispatchToProps)(UserAreaModal);
|
||||||
|
|
|
@ -79,7 +79,7 @@ class UserMessages extends React.Component {
|
||||||
{(messages.includes('not_verified') && messages.splice(messages.indexOf('not_verified'), 1))
|
{(messages.includes('not_verified') && messages.splice(messages.indexOf('not_verified'), 1))
|
||||||
? (
|
? (
|
||||||
<p className="usermessages">
|
<p className="usermessages">
|
||||||
Please verify your mail address or your account could get deleted after a few days.
|
Please verify your mail address or your account could get deleted after a few days.
|
||||||
{(this.state.verify_answer)
|
{(this.state.verify_answer)
|
||||||
? <span className="modallink">{this.state.verify_answer}</span>
|
? <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>}
|
: <span className="modallink" onClick={this.submit_resend_verify}>Click here to request a new verification mail.</span>}
|
||||||
|
|
|
@ -217,7 +217,8 @@ kbd {
|
||||||
padding: 20px 40px;
|
padding: 20px 40px;
|
||||||
transform: translate(-50%, -50%);
|
transform: translate(-50%, -50%);
|
||||||
height: 80%;
|
height: 80%;
|
||||||
max-height: 700px;
|
max-height: 900px;
|
||||||
|
width: 70%;
|
||||||
transition: all 0.5s ease 0s;
|
transition: all 0.5s ease 0s;
|
||||||
}
|
}
|
||||||
@media (max-width: 604px) {
|
@media (max-width: 604px) {
|
||||||
|
@ -228,6 +229,7 @@ kbd {
|
||||||
right: 0px;
|
right: 0px;
|
||||||
bottom: 0px;
|
bottom: 0px;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
transform: none;
|
transform: none;
|
||||||
max-width: none;
|
max-width: none;
|
||||||
max-height: none;
|
max-height: none;
|
||||||
|
|
37
src/core/exportGPL.js
Normal file
37
src/core/exportGPL.js
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* @flow
|
||||||
|
*/
|
||||||
|
|
||||||
|
function appendNumberText(number) {
|
||||||
|
let appendStr = `${number} `;
|
||||||
|
if (number < 10) appendStr += ' ';
|
||||||
|
else if (number < 100) appendStr += ' ';
|
||||||
|
return appendStr;
|
||||||
|
}
|
||||||
|
function appendHexColorText(clr) {
|
||||||
|
let appendStr = ' #';
|
||||||
|
clr.forEach((z) => {
|
||||||
|
if (z < 16) appendStr += '0';
|
||||||
|
appendStr += z.toString(16);
|
||||||
|
});
|
||||||
|
return appendStr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function printGIMPPalette(title, description, colors) {
|
||||||
|
let text = `GIMP Palette
|
||||||
|
#Palette Name: Pixelplanet${title}
|
||||||
|
#Description: ${description}
|
||||||
|
#Colors: ${colors.length}`;
|
||||||
|
colors.forEach((clr) => {
|
||||||
|
text += '\n';
|
||||||
|
clr.forEach((z) => {
|
||||||
|
text += appendNumberText(z);
|
||||||
|
});
|
||||||
|
text += appendHexColorText(clr);
|
||||||
|
});
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default printGIMPPalette;
|
Loading…
Reference in New Issue
Block a user