test chart drawing

This commit is contained in:
HF 2022-09-26 01:21:09 +02:00
parent 6c9dad52df
commit 36c6d87cd3
4 changed files with 223 additions and 2 deletions

27
package-lock.json generated
View File

@ -9,6 +9,7 @@
"version": "1.0.0",
"dependencies": {
"bcrypt": "^5.0.1",
"chart.js": "^3.9.1",
"compression": "^1.7.3",
"cookie": "^0.5.0",
"core-js": "^3.23.4",
@ -30,6 +31,7 @@
"passport-vkontakte": "^0.5.0",
"ppfun-captcha": "^1.6.6",
"react": "^18.2.0",
"react-chartjs-2": "^4.3.1",
"react-dom": "^18.2.0",
"react-icons": "^4.3.1",
"react-redux": "^8.0.2",
@ -3655,6 +3657,11 @@
"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": {
"version": "3.5.3",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
@ -8759,6 +8766,15 @@
"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": {
"version": "18.2.0",
"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": {
"version": "3.5.3",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
@ -17619,6 +17640,12 @@
"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": {
"version": "18.2.0",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz",

View File

@ -23,6 +23,7 @@
],
"dependencies": {
"bcrypt": "^5.0.1",
"chart.js": "^3.9.1",
"compression": "^1.7.3",
"cookie": "^0.5.0",
"core-js": "^3.23.4",
@ -44,6 +45,7 @@
"passport-vkontakte": "^0.5.0",
"ppfun-captcha": "^1.6.6",
"react": "^18.2.0",
"react-chartjs-2": "^4.3.1",
"react-dom": "^18.2.0",
"react-icons": "^4.3.1",
"react-redux": "^8.0.2",

View File

@ -4,11 +4,114 @@
/* eslint-disable max-len */
import React, { useState } from 'react';
import React, { useState, useMemo } from 'react';
import { shallowEqual, useSelector } from 'react-redux';
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 { 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 = () => {
@ -23,6 +126,80 @@ const Rankings = () => {
histStats,
] = 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 (
<>
<div className="content">
@ -61,6 +238,15 @@ const Rankings = () => {
}
onClick={() => setArea('countries')}
>{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>
{(['total', 'today', 'yesterday', 'countries'].includes(area)) && (
<table style={{
@ -148,6 +334,12 @@ const Rankings = () => {
</tbody>
</table>
)}
{(area === 'charts') && (
<>
<Line options={options} data={cHistData} />
<Line options={onlineStatsOptions} data={onlineData} />
</>
)}
<p>
{t`Ranking updates every 5 min. Daily rankings get reset at midnight UTC.`}
</p>

View File

@ -324,7 +324,7 @@ export function requestDeleteAccount(password) {
export function requestRankings() {
return makeAPIGETRequest(
'/ranking',
'https://pixelplanet.fun/ranking',
false,
);
}