wowtoken.app/src/index.js
2024-04-12 05:08:39 -07:00

386 lines
11 KiB
JavaScript

import {
Chart,
Legend,
LinearScale,
LineController,
LineElement,
PointElement,
TimeSeriesScale,
Title,
Tooltip
} from 'chart.js';
import 'chartjs-adapter-dayjs-3';
import "./style.css"
// TODO: This file should be seperated into multiple with better ownership
Chart.register(
LineElement,
PointElement,
LineController,
LinearScale,
TimeSeriesScale,
Legend,
Title,
Tooltip
)
let currentRegionSelection = '';
let currentTimeSelection = '';
let currentAggregateSelection = '';
const currentPriceHash = {
us: 0,
eu: 0,
kr: 0,
tw: 0
};
let chartData = {
us: [],
eu: [],
kr: [],
tw: []
}
let chartOptions = {
us: {
color: 'gold'
},
eu: {
color: 'red'
},
kr: {
color: 'white'
},
tw: {
color: 'pink'
}
}
let chartJsData;
let ctx;
let tokenChart;
function populateChart() {
ctx = document.getElementById("token-chart").getContext('2d');
tokenChart = new Chart(ctx, {
type: 'line',
data: {
datasets: [{
borderColor: 'gold',
label: currentRegionSelection.toUpperCase() + " WoW Classic Token Price",
data: chartJsData,
cubicInterpolationMode: 'monotone',
pointRadius: 0
}]
},
options: {
interaction: {
intersect: false,
mode: "index"
},
scales: {
x: {
type: 'time',
ticks: {
color: '#a7a4ab',
font: {
size: 18,
}
},
},
y: {
ticks: {
color: '#a7a4ab',
font: {
size: 18,
}
}
}
},
}
});
}
async function callUpdateURL() {
let resp = await fetch("https://data.wowtoken.app/classic/token/current.json");
let data = await resp.json();
updateTokens(data);
}
function updateTokens(data) {
updateRegionalToken('us', data);
updateRegionalToken('eu', data);
updateRegionalToken('kr', data);
updateRegionalToken('tw', data);
}
function updateRegionalToken(region, data) {
if (currentPriceHash[region] !== data['price_data'][region]) {
currentPriceHash[region] = data['price_data'][region];
if (region === currentRegionSelection) {
formatToken();
if (currentAggregateSelection === 'none') {
addDataToChart(region, data);
}
}
}
}
function addDataToChart(region, data) {
if (tokenChart) {
const datum = {x: data['current_time'], y: data['price_data'][region]}
tokenChart.data.datasets.forEach((dataset) => {
dataset.data.push(datum);
})
tokenChart.update();
}
}
async function aggregateFunctionToggle() {
// TODO: We should probably make these global or something
// so if the need to be updated in the future we can do so easily
const smallTimes = ['72h', '168h', '336h'];
const longTimes = ['720h', '30d', '2190h', '90d', '1y', '2y', '6m', 'all'];
const idsToModify = ['agg_wavg']
if (smallTimes.includes(currentTimeSelection)) {
for (const id of idsToModify) {
let ele = document.getElementById(id);
ele.disabled = true;
}
} else if (longTimes.includes(currentTimeSelection)) {
for (const id of idsToModify) {
let ele = document.getElementById(id);
ele.disabled = false;
}
}
}
function addLoader() {
let loader = document.getElementById('loader');
if (!loader) {
const blank_div = document.createElement('div');
let loaderNode = blank_div.cloneNode();
loaderNode.id = 'loader';
loaderNode.className = 'lds-ripple';
loaderNode.appendChild(blank_div.cloneNode());
loaderNode.appendChild(blank_div.cloneNode());
let chartNode = document.getElementById('token-chart');
chartNode.before(loaderNode);
}
}
function removeLoader () {
let loader = document.getElementById('loader');
if (loader) {
loader.remove();
}
}
function updateRegionPreference(newRegion) {
if (newRegion !== currentRegionSelection) {
tokenChart.destroy();
addLoader();
currentRegionSelection = newRegion;
}
formatToken();
pullChartData().then(populateChart);
}
function updateTimePreference(newTime) {
if (newTime !== currentTimeSelection) {
tokenChart.destroy();
addLoader();
currentTimeSelection = newTime;
aggregateFunctionToggle();
}
pullChartData().then(populateChart);
}
function updateAggregatePreference(newAggregate) {
if (newAggregate !== currentAggregateSelection) {
tokenChart.destroy();
addLoader();
currentAggregateSelection = newAggregate;
}
pullChartData().then(populateChart);
}
function toggleAdvancedSetting() {
let element = document.getElementById('advanced-options')
if (document.getElementById('enable-advanced').checked)
{
element.style.display = 'flex';
}
else
{
element.style.display = 'none';
}
}
function urlBuilder() {
let url = "https://data.wowtoken.app/classic/token/history/";
if (currentAggregateSelection !== 'none') {
url += `${currentAggregateSelection}/`
}
url += `${currentRegionSelection}/${currentTimeSelection}.json`
return url;
}
async function pullChartData() {
let resp = await fetch(urlBuilder());
let chartData = await resp.json();
let newChartJSData = [];
for (let i = 0; i < chartData.length; i++) {
let datum = {
x: chartData[i]['time'],
y: chartData[i]['value']
};
newChartJSData.push(datum);
}
chartJsData = newChartJSData;
removeLoader();
}
function formatToken() {
document.getElementById("token").innerText = currentPriceHash[currentRegionSelection].toLocaleString();
}
// TODO: These maybe able to be collapsed into a single function with params or a lambda
function detectRegionQuery(urlSearchParams) {
const validRegions = ['us', 'eu', 'tw', 'kr'];
if (validRegions.includes(urlSearchParams.get('region').toLowerCase())) {
currentRegionSelection = urlSearchParams.get('region').toLowerCase();
let regionDDL = document.getElementById('region');
for (let i = 0; i < regionDDL.options.length; i++) {
if (regionDDL.options[i].value === currentRegionSelection) {
regionDDL.options[i].selected = true;
}
}
} else {
console.warn("An incorrect or malformed region selection was made in the query string");
}
}
function detectTimeQuery(urlSearchParams) {
// In the future, we will allow all the times to be selected,
// once I come up with a good reduction algorithm.
// For larger time selections, it's currently hardcoded into the backend
const validTimes = ['72h', '168h', '336h', '720h', '30d', '2190h', '90d', '1y', '2y', '6m', 'all'];
if (validTimes.includes(urlSearchParams.get('time').toLowerCase())) {
currentTimeSelection = urlSearchParams.get('time').toLowerCase();
let timeDDL = document.getElementById('time');
for (let i = 0; i < timeDDL.options.length; i++) {
if (timeDDL.options[i].value === currentTimeSelection) {
timeDDL.options[i].selected = true;
}
}
} else {
console.warn("An incorrect or malformed time selection was made in the query string");
}
}
function detectAggregateQuery(urlSearchParams) {
const validOperations = ['none', 'daily_mean', 'weekly_mean'];
if (validOperations.includes(urlSearchParams.get('aggregate').toLowerCase())) {
currentAggregateSelection = urlSearchParams.get('aggregate').toLowerCase();
let aggregateDDL = document.getElementById('aggregate');
for (let i = 0; i < aggregateDDL.options.length; i++) {
if (aggregateDDL.options[i].value === currentAggregateSelection) {
aggregateDDL.options[i].selected = true;
}
}
aggregateFunctionToggle();
} else {
console.warn("An incorrect or malformed aggregate selection was made in the query string");
}
}
function detectURLQuery() {
const urlSearchParams = new URLSearchParams(window.location.search);
if (urlSearchParams.has('region')) {
detectRegionQuery(urlSearchParams);
}
if (urlSearchParams.has('time')) {
detectTimeQuery(urlSearchParams);
}
if (urlSearchParams.has('aggregate')) {
detectAggregateQuery(urlSearchParams);
}
}
function buildDeepLinksURL() {
let url = "https://classic.wowtoken.app/?"
if (currentTimeSelection !== '72h'){
url += `time=${currentTimeSelection}&`
}
if (currentRegionSelection !== 'us'){
url += `region=${currentRegionSelection}&`
}
if (currentAggregateSelection !== '' && currentAggregateSelection !== 'none'){
url += `aggregate=${currentAggregateSelection}`
}
return url
}
function copyURL() {
let toolTip = document.getElementById('urlTooltip');
navigator.clipboard.writeText(buildDeepLinksURL()).then(
() => {
toolTip.innerHTML= "Copied the URL";
},
() => {
toolTip.innerHTML = "Unable to copy URL to clipboard";
}
);
}
function toolTipMouseOut() {
let tooltip = document.getElementById("urlTooltip");
tooltip.innerHTML = "Copy to clipboard";
}
function registerEventHandles() {
registerCopyHandlers();
registerOptionHandlers();
registerAdvancedHandlers();
}
function registerAdvancedHandlers() {
document.getElementById('enable-advanced').addEventListener('change', () => {
toggleAdvancedSetting();
})
}
function registerCopyHandlers() {
document.getElementById('copyURLButton').addEventListener('click', function () {
copyURL();
})
document.getElementById('copyURLButton').addEventListener('mouseout', function () {
toolTipMouseOut();
})
}
function registerOptionHandlers() {
document.getElementById('region').addEventListener('change', function() {
updateRegionPreference(this.value);
});
currentRegionSelection = document.getElementById('region').value;
document.getElementById('time').addEventListener('change', function() {
updateTimePreference(this.value);
});
currentTimeSelection = document.getElementById('time').value;
document.getElementById('aggregate').addEventListener('change', function () {
updateAggregatePreference(this.value);
})
currentAggregateSelection = document.getElementById('aggregate').value;
}
document.addEventListener('DOMContentLoaded', function () {
registerEventHandles();
detectURLQuery();
Promise.all([callUpdateURL(), pullChartData()]).then(populateChart)
setInterval(callUpdateURL, 60*1000);
}, false);