test chart drawing
This commit is contained in:
parent
6c9dad52df
commit
36c6d87cd3
27
package-lock.json
generated
27
package-lock.json
generated
|
@ -9,6 +9,7 @@
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bcrypt": "^5.0.1",
|
"bcrypt": "^5.0.1",
|
||||||
|
"chart.js": "^3.9.1",
|
||||||
"compression": "^1.7.3",
|
"compression": "^1.7.3",
|
||||||
"cookie": "^0.5.0",
|
"cookie": "^0.5.0",
|
||||||
"core-js": "^3.23.4",
|
"core-js": "^3.23.4",
|
||||||
|
@ -30,6 +31,7 @@
|
||||||
"passport-vkontakte": "^0.5.0",
|
"passport-vkontakte": "^0.5.0",
|
||||||
"ppfun-captcha": "^1.6.6",
|
"ppfun-captcha": "^1.6.6",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
|
"react-chartjs-2": "^4.3.1",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-icons": "^4.3.1",
|
"react-icons": "^4.3.1",
|
||||||
"react-redux": "^8.0.2",
|
"react-redux": "^8.0.2",
|
||||||
|
@ -3655,6 +3657,11 @@
|
||||||
"node": ">=0.8.0"
|
"node": ">=0.8.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/chart.js": {
|
||||||
|
"version": "3.9.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-3.9.1.tgz",
|
||||||
|
"integrity": "sha512-Ro2JbLmvg83gXF5F4sniaQ+lTbSv18E+TIf2cOeiH1Iqd2PGFOtem+DUufMZsCJwFE7ywPOpfXFBwRTGq7dh6w=="
|
||||||
|
},
|
||||||
"node_modules/chokidar": {
|
"node_modules/chokidar": {
|
||||||
"version": "3.5.3",
|
"version": "3.5.3",
|
||||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
|
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
|
||||||
|
@ -8759,6 +8766,15 @@
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/react-chartjs-2": {
|
||||||
|
"version": "4.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-chartjs-2/-/react-chartjs-2-4.3.1.tgz",
|
||||||
|
"integrity": "sha512-5i3mjP6tU7QSn0jvb8I4hudTzHJqS8l00ORJnVwI2sYu0ihpj83Lv2YzfxunfxTZkscKvZu2F2w9LkwNBhj6xA==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"chart.js": "^3.5.0",
|
||||||
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/react-dom": {
|
"node_modules/react-dom": {
|
||||||
"version": "18.2.0",
|
"version": "18.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz",
|
||||||
|
@ -13780,6 +13796,11 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"chart.js": {
|
||||||
|
"version": "3.9.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-3.9.1.tgz",
|
||||||
|
"integrity": "sha512-Ro2JbLmvg83gXF5F4sniaQ+lTbSv18E+TIf2cOeiH1Iqd2PGFOtem+DUufMZsCJwFE7ywPOpfXFBwRTGq7dh6w=="
|
||||||
|
},
|
||||||
"chokidar": {
|
"chokidar": {
|
||||||
"version": "3.5.3",
|
"version": "3.5.3",
|
||||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
|
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
|
||||||
|
@ -17619,6 +17640,12 @@
|
||||||
"loose-envify": "^1.1.0"
|
"loose-envify": "^1.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"react-chartjs-2": {
|
||||||
|
"version": "4.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-chartjs-2/-/react-chartjs-2-4.3.1.tgz",
|
||||||
|
"integrity": "sha512-5i3mjP6tU7QSn0jvb8I4hudTzHJqS8l00ORJnVwI2sYu0ihpj83Lv2YzfxunfxTZkscKvZu2F2w9LkwNBhj6xA==",
|
||||||
|
"requires": {}
|
||||||
|
},
|
||||||
"react-dom": {
|
"react-dom": {
|
||||||
"version": "18.2.0",
|
"version": "18.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz",
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bcrypt": "^5.0.1",
|
"bcrypt": "^5.0.1",
|
||||||
|
"chart.js": "^3.9.1",
|
||||||
"compression": "^1.7.3",
|
"compression": "^1.7.3",
|
||||||
"cookie": "^0.5.0",
|
"cookie": "^0.5.0",
|
||||||
"core-js": "^3.23.4",
|
"core-js": "^3.23.4",
|
||||||
|
@ -44,6 +45,7 @@
|
||||||
"passport-vkontakte": "^0.5.0",
|
"passport-vkontakte": "^0.5.0",
|
||||||
"ppfun-captcha": "^1.6.6",
|
"ppfun-captcha": "^1.6.6",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
|
"react-chartjs-2": "^4.3.1",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-icons": "^4.3.1",
|
"react-icons": "^4.3.1",
|
||||||
"react-redux": "^8.0.2",
|
"react-redux": "^8.0.2",
|
||||||
|
|
|
@ -4,11 +4,114 @@
|
||||||
|
|
||||||
/* eslint-disable max-len */
|
/* eslint-disable max-len */
|
||||||
|
|
||||||
import React, { useState } from 'react';
|
import React, { useState, useMemo } from 'react';
|
||||||
import { shallowEqual, useSelector } from 'react-redux';
|
import { shallowEqual, useSelector } from 'react-redux';
|
||||||
import { t } from 'ttag';
|
import { t } from 'ttag';
|
||||||
|
import {
|
||||||
|
Chart as ChartJS,
|
||||||
|
CategoryScale,
|
||||||
|
LinearScale,
|
||||||
|
PointElement,
|
||||||
|
LineElement,
|
||||||
|
Title,
|
||||||
|
Tooltip,
|
||||||
|
Legend,
|
||||||
|
LineController,
|
||||||
|
} from 'chart.js';
|
||||||
|
import { Line } from 'react-chartjs-2';
|
||||||
|
|
||||||
import { selectStats } from '../store/selectors/ranks';
|
import { selectStats } from '../store/selectors/ranks';
|
||||||
|
import { colorFromText } from '../core/utils';
|
||||||
|
|
||||||
|
ChartJS.register(
|
||||||
|
CategoryScale,
|
||||||
|
LinearScale,
|
||||||
|
PointElement,
|
||||||
|
LineElement,
|
||||||
|
Title,
|
||||||
|
Tooltip,
|
||||||
|
Legend,
|
||||||
|
LineController,
|
||||||
|
);
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
responsive: true,
|
||||||
|
aspectRatio: 1.2,
|
||||||
|
color: '#e6e6e6',
|
||||||
|
scales: {
|
||||||
|
x: {
|
||||||
|
grid: {
|
||||||
|
drawBorder: false,
|
||||||
|
color: '#656565',
|
||||||
|
},
|
||||||
|
ticks: {
|
||||||
|
color: '#e6e6e6',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
grid: {
|
||||||
|
drawBorder: false,
|
||||||
|
color: '#656565',
|
||||||
|
},
|
||||||
|
ticks: {
|
||||||
|
color: '#e6e6e6',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
interaction: {
|
||||||
|
mode: 'index',
|
||||||
|
intersect: false,
|
||||||
|
},
|
||||||
|
plugins: {
|
||||||
|
legend: {
|
||||||
|
position: 'top',
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
display: true,
|
||||||
|
color: '#e6e6e6',
|
||||||
|
text: 'Top 10 Countries [pxls / day]',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const onlineStatsOptions = {
|
||||||
|
responsive: true,
|
||||||
|
color: '#e6e6e6',
|
||||||
|
scales: {
|
||||||
|
x: {
|
||||||
|
grid: {
|
||||||
|
drawBorder: false,
|
||||||
|
color: '#656565',
|
||||||
|
},
|
||||||
|
ticks: {
|
||||||
|
color: '#e6e6e6',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
grid: {
|
||||||
|
drawBorder: false,
|
||||||
|
color: '#656565',
|
||||||
|
},
|
||||||
|
ticks: {
|
||||||
|
color: '#e6e6e6',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
interaction: {
|
||||||
|
mode: 'index',
|
||||||
|
intersect: false,
|
||||||
|
},
|
||||||
|
plugins: {
|
||||||
|
legend: {
|
||||||
|
display: false,
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
display: true,
|
||||||
|
color: '#e6e6e6',
|
||||||
|
text: 'Players Online per full hour',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
const Rankings = () => {
|
const Rankings = () => {
|
||||||
|
@ -23,6 +126,80 @@ const Rankings = () => {
|
||||||
histStats,
|
histStats,
|
||||||
] = useSelector(selectStats, shallowEqual);
|
] = useSelector(selectStats, shallowEqual);
|
||||||
|
|
||||||
|
const cHistData = useMemo(() => {
|
||||||
|
if (area !== 'charts') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const dataPerCountry = {};
|
||||||
|
const labels = [];
|
||||||
|
let ts = Date.now();
|
||||||
|
let c = cHistStats.length;
|
||||||
|
while (c) {
|
||||||
|
const dAmount = cHistStats.length - c;
|
||||||
|
c -= 1;
|
||||||
|
// x label
|
||||||
|
const date = new Date(ts);
|
||||||
|
labels.unshift(`${date.getUTCMonth() + 1} / ${date.getUTCDate()}`);
|
||||||
|
ts -= 1000 * 3600 * 24;
|
||||||
|
// y data per country
|
||||||
|
const dailyRanks = cHistStats[c];
|
||||||
|
for (let i = 0; i < dailyRanks.length; i += 1) {
|
||||||
|
const { cc, px } = dailyRanks[i];
|
||||||
|
if (!dataPerCountry[cc]) {
|
||||||
|
dataPerCountry[cc] = [];
|
||||||
|
}
|
||||||
|
const countryDat = dataPerCountry[cc];
|
||||||
|
while (countryDat.length < dAmount) {
|
||||||
|
countryDat.push(null);
|
||||||
|
}
|
||||||
|
countryDat.push(px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log(dataPerCountry);
|
||||||
|
const countries = Object.keys(dataPerCountry);
|
||||||
|
const datasets = countries.map((cc) => {
|
||||||
|
const color = colorFromText(`${cc}${cc}${cc}${cc}${cc}`);
|
||||||
|
return {
|
||||||
|
label: cc,
|
||||||
|
data: dataPerCountry[cc],
|
||||||
|
borderColor: color,
|
||||||
|
backgroundColor: color,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
labels,
|
||||||
|
datasets,
|
||||||
|
};
|
||||||
|
}, [area, cHistStats]);
|
||||||
|
|
||||||
|
const onlineData = useMemo(() => {
|
||||||
|
if (area !== 'charts') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const labels = [];
|
||||||
|
const data = [];
|
||||||
|
let ts = Date.now();
|
||||||
|
let c = onlineStats.length;
|
||||||
|
while (c) {
|
||||||
|
c -= 1;
|
||||||
|
const date = new Date(ts);
|
||||||
|
const hours = date.getHours();
|
||||||
|
const key = hours || `${date.getMonth() + 1} / ${date.getDate()}`;
|
||||||
|
labels.unshift(String(key));
|
||||||
|
ts -= 1000 * 3600;
|
||||||
|
data.push(onlineStats[c]);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
labels,
|
||||||
|
datasets: [{
|
||||||
|
label: 'Players',
|
||||||
|
data,
|
||||||
|
borderColor: '#3fadda',
|
||||||
|
backgroundColor: '#3fadda',
|
||||||
|
}],
|
||||||
|
};
|
||||||
|
}, [area, onlineStats]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="content">
|
<div className="content">
|
||||||
|
@ -61,6 +238,15 @@ const Rankings = () => {
|
||||||
}
|
}
|
||||||
onClick={() => setArea('countries')}
|
onClick={() => setArea('countries')}
|
||||||
>{t`Countries Today`}</span>
|
>{t`Countries Today`}</span>
|
||||||
|
<span className="hdivider" />
|
||||||
|
<span
|
||||||
|
role="button"
|
||||||
|
tabIndex={-1}
|
||||||
|
className={
|
||||||
|
(area === 'charts') ? 'modallink selected' : 'modallink'
|
||||||
|
}
|
||||||
|
onClick={() => setArea('charts')}
|
||||||
|
>{t`Chart`}</span>
|
||||||
</div>
|
</div>
|
||||||
{(['total', 'today', 'yesterday', 'countries'].includes(area)) && (
|
{(['total', 'today', 'yesterday', 'countries'].includes(area)) && (
|
||||||
<table style={{
|
<table style={{
|
||||||
|
@ -148,6 +334,12 @@ const Rankings = () => {
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
)}
|
)}
|
||||||
|
{(area === 'charts') && (
|
||||||
|
<>
|
||||||
|
<Line options={options} data={cHistData} />
|
||||||
|
<Line options={onlineStatsOptions} data={onlineData} />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
<p>
|
<p>
|
||||||
{t`Ranking updates every 5 min. Daily rankings get reset at midnight UTC.`}
|
{t`Ranking updates every 5 min. Daily rankings get reset at midnight UTC.`}
|
||||||
</p>
|
</p>
|
||||||
|
|
|
@ -324,7 +324,7 @@ export function requestDeleteAccount(password) {
|
||||||
|
|
||||||
export function requestRankings() {
|
export function requestRankings() {
|
||||||
return makeAPIGETRequest(
|
return makeAPIGETRequest(
|
||||||
'/ranking',
|
'https://pixelplanet.fun/ranking',
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user