mirror of
https://github.com/basicswap/basicswap.git
synced 2025-01-03 09:19:26 +00:00
Merge pull request #179 from gerlofvanek/chart-1
ui: Correct date display chart. Various small fixes.
This commit is contained in:
commit
b4a08ce15e
2 changed files with 78 additions and 79 deletions
|
@ -1327,7 +1327,7 @@ async function fetchLatestPrices() {
|
||||||
return cachedData.value;
|
return cachedData.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
const url = 'https://api.coingecko.com/api/v3/simple/price?ids=bitcoin,bitcoin-cash,dash,dogecoin,decred,litecoin,particl,pivx,monero,zano,wownero,zcoin&vs_currencies=USD,BTC';
|
const url = `${config.apiEndpoints.coinGecko}/simple/price?ids=bitcoin,bitcoin-cash,dash,dogecoin,decred,litecoin,particl,pivx,monero,zano,wownero,zcoin&vs_currencies=USD,BTC&api_key=${config.apiKeys.coinGecko}`;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const data = await makePostRequest(url);
|
const data = await makePostRequest(url);
|
||||||
|
|
|
@ -38,6 +38,13 @@ const config = {
|
||||||
currentResolution: 'year'
|
currentResolution: 'year'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function getAPIKeys() {
|
||||||
|
return {
|
||||||
|
cryptoCompare: '{{chart_api_key}}',
|
||||||
|
coinGecko: '{{coingecko_api_key}}'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// Utils
|
// Utils
|
||||||
const utils = {
|
const utils = {
|
||||||
formatNumber: (number, decimals = 2) =>
|
formatNumber: (number, decimals = 2) =>
|
||||||
|
@ -139,7 +146,7 @@ const api = {
|
||||||
.filter(coin => coin.usesCoinGecko)
|
.filter(coin => coin.usesCoinGecko)
|
||||||
.map(coin => coin.name)
|
.map(coin => coin.name)
|
||||||
.join(',');
|
.join(',');
|
||||||
const url = `${config.apiEndpoints.coinGecko}/simple/price?ids=${coinIds}&vs_currencies=usd,btc&include_24hr_vol=true&include_24hr_change=true`;
|
const url = `${config.apiEndpoints.coinGecko}/simple/price?ids=${coinIds}&vs_currencies=usd,btc&include_24hr_vol=true&include_24hr_change=true&api_key=${config.apiKeys.coinGecko}`;
|
||||||
|
|
||||||
console.log(`Fetching data for multiple coins from CoinGecko: ${url}`);
|
console.log(`Fetching data for multiple coins from CoinGecko: ${url}`);
|
||||||
|
|
||||||
|
@ -190,7 +197,7 @@ const api = {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (coin === 'WOW') {
|
if (coin === 'WOW') {
|
||||||
const url = `${config.apiEndpoints.coinGecko}/coins/wownero/market_chart?vs_currency=usd&days=1`;
|
const url = `${config.apiEndpoints.coinGecko}/coins/wownero/market_chart?vs_currency=usd&days=1&api_key=${config.apiKeys.coinGecko}`;
|
||||||
console.log(`CoinGecko URL for WOW: ${url}`);
|
console.log(`CoinGecko URL for WOW: ${url}`);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -507,7 +514,7 @@ const chartModule = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
initChart: () => {
|
initChart: () => {
|
||||||
const ctx = document.getElementById('coin-chart').getContext('2d');
|
const ctx = document.getElementById('coin-chart').getContext('2d');
|
||||||
if (!ctx) {
|
if (!ctx) {
|
||||||
logger.error('Failed to get chart context. Make sure the canvas element exists.');
|
logger.error('Failed to get chart context. Make sure the canvas element exists.');
|
||||||
|
@ -518,11 +525,6 @@ initChart: () => {
|
||||||
gradient.addColorStop(0, 'rgba(77, 132, 240, 0.2)');
|
gradient.addColorStop(0, 'rgba(77, 132, 240, 0.2)');
|
||||||
gradient.addColorStop(1, 'rgba(77, 132, 240, 0)');
|
gradient.addColorStop(1, 'rgba(77, 132, 240, 0)');
|
||||||
|
|
||||||
const formatTime = (date) => {
|
|
||||||
const hours = date.getHours().toString().padStart(2, '0');
|
|
||||||
return `${hours}:00`;
|
|
||||||
};
|
|
||||||
|
|
||||||
chartModule.chart = new Chart(ctx, {
|
chartModule.chart = new Chart(ctx, {
|
||||||
type: 'line',
|
type: 'line',
|
||||||
data: {
|
data: {
|
||||||
|
@ -550,9 +552,15 @@ initChart: () => {
|
||||||
time: {
|
time: {
|
||||||
unit: 'hour',
|
unit: 'hour',
|
||||||
displayFormats: {
|
displayFormats: {
|
||||||
hour: 'HH:00',
|
hour: 'h:mm a',
|
||||||
day: 'MMM d',
|
day: 'MMM d',
|
||||||
month: 'MMM yyyy'
|
month: 'MMM yyyy'
|
||||||
|
},
|
||||||
|
tooltipFormat: 'MMM d, yyyy h:mm a'
|
||||||
|
},
|
||||||
|
adapters: {
|
||||||
|
date: {
|
||||||
|
zone: 'UTC'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ticks: {
|
ticks: {
|
||||||
|
@ -566,16 +574,24 @@ initChart: () => {
|
||||||
callback: function(value) {
|
callback: function(value) {
|
||||||
const date = new Date(value);
|
const date = new Date(value);
|
||||||
if (config.currentResolution === 'day') {
|
if (config.currentResolution === 'day') {
|
||||||
return formatTime(date);
|
// Convert to AM/PM format
|
||||||
|
return date.toLocaleTimeString('en-US', {
|
||||||
|
hour: 'numeric',
|
||||||
|
minute: '2-digit',
|
||||||
|
hour12: true,
|
||||||
|
timeZone: 'UTC'
|
||||||
|
});
|
||||||
} else if (config.currentResolution === 'year') {
|
} else if (config.currentResolution === 'year') {
|
||||||
return date.toLocaleDateString('en-US', {
|
return date.toLocaleDateString('en-US', {
|
||||||
month: 'short',
|
month: 'short',
|
||||||
year: 'numeric'
|
year: 'numeric',
|
||||||
|
timeZone: 'UTC'
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
return date.toLocaleDateString('en-US', {
|
return date.toLocaleDateString('en-US', {
|
||||||
month: 'short',
|
month: 'short',
|
||||||
day: 'numeric'
|
day: 'numeric',
|
||||||
|
timeZone: 'UTC'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -618,26 +634,35 @@ initChart: () => {
|
||||||
title: (tooltipItems) => {
|
title: (tooltipItems) => {
|
||||||
const date = new Date(tooltipItems[0].parsed.x);
|
const date = new Date(tooltipItems[0].parsed.x);
|
||||||
if (config.currentResolution === 'day') {
|
if (config.currentResolution === 'day') {
|
||||||
return `${date.toLocaleDateString('en-US', {
|
return date.toLocaleString('en-US', {
|
||||||
month: 'short',
|
month: 'short',
|
||||||
day: 'numeric'
|
day: 'numeric',
|
||||||
})} ${formatTime(date)}`;
|
hour: 'numeric',
|
||||||
|
minute: '2-digit',
|
||||||
|
hour12: true,
|
||||||
|
timeZone: 'UTC'
|
||||||
|
});
|
||||||
} else if (config.currentResolution === 'year') {
|
} else if (config.currentResolution === 'year') {
|
||||||
return date.toLocaleDateString('en-US', {
|
return date.toLocaleString('en-US', {
|
||||||
year: 'numeric',
|
year: 'numeric',
|
||||||
month: 'short',
|
month: 'short',
|
||||||
day: 'numeric'
|
day: 'numeric',
|
||||||
|
timeZone: 'UTC'
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
return date.toLocaleDateString('en-US', {
|
return date.toLocaleString('en-US', {
|
||||||
month: 'short',
|
month: 'short',
|
||||||
day: 'numeric'
|
day: 'numeric',
|
||||||
|
timeZone: 'UTC'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
label: (item) => {
|
label: (item) => {
|
||||||
const value = item.parsed.y;
|
const value = item.parsed.y;
|
||||||
return `${chartModule.currentCoin}: $${value.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 8 })}`;
|
return `${chartModule.currentCoin}: $${value.toLocaleString(undefined, {
|
||||||
|
minimumFractionDigits: 2,
|
||||||
|
maximumFractionDigits: 8
|
||||||
|
})}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -645,22 +670,6 @@ initChart: () => {
|
||||||
lineWidth: 1,
|
lineWidth: 1,
|
||||||
lineColor: 'rgba(77, 132, 240, 0.5)'
|
lineColor: 'rgba(77, 132, 240, 0.5)'
|
||||||
}
|
}
|
||||||
},
|
|
||||||
elements: {
|
|
||||||
point: {
|
|
||||||
backgroundColor: 'rgba(77, 132, 240, 1)',
|
|
||||||
borderColor: 'rgba(77, 132, 240, 1)',
|
|
||||||
borderWidth: 1,
|
|
||||||
radius: 2,
|
|
||||||
hoverRadius: 4,
|
|
||||||
hitRadius: 6,
|
|
||||||
hoverBorderWidth: 2
|
|
||||||
},
|
|
||||||
line: {
|
|
||||||
backgroundColor: gradient,
|
|
||||||
borderColor: 'rgba(77, 132, 240, 1)',
|
|
||||||
fill: true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
plugins: [chartModule.verticalLinePlugin]
|
plugins: [chartModule.verticalLinePlugin]
|
||||||
|
@ -669,7 +678,7 @@ initChart: () => {
|
||||||
console.log('Chart initialized:', chartModule.chart);
|
console.log('Chart initialized:', chartModule.chart);
|
||||||
},
|
},
|
||||||
|
|
||||||
prepareChartData: (coinSymbol, data) => {
|
prepareChartData: (coinSymbol, data) => {
|
||||||
if (!data) {
|
if (!data) {
|
||||||
console.error(`No data received for ${coinSymbol}`);
|
console.error(`No data received for ${coinSymbol}`);
|
||||||
return [];
|
return [];
|
||||||
|
@ -680,8 +689,7 @@ prepareChartData: (coinSymbol, data) => {
|
||||||
|
|
||||||
if (coinSymbol === 'WOW' && Array.isArray(data)) {
|
if (coinSymbol === 'WOW' && Array.isArray(data)) {
|
||||||
const endTime = new Date(data[data.length - 1][0]);
|
const endTime = new Date(data[data.length - 1][0]);
|
||||||
// Convert to local time
|
endTime.setUTCMinutes(0, 0, 0);
|
||||||
endTime.setMinutes(0, 0, 0);
|
|
||||||
const endUnix = endTime.getTime();
|
const endUnix = endTime.getTime();
|
||||||
const startUnix = endUnix - (24 * 3600000);
|
const startUnix = endUnix - (24 * 3600000);
|
||||||
|
|
||||||
|
@ -689,7 +697,7 @@ prepareChartData: (coinSymbol, data) => {
|
||||||
|
|
||||||
for (let hourUnix = startUnix; hourUnix <= endUnix; hourUnix += 3600000) {
|
for (let hourUnix = startUnix; hourUnix <= endUnix; hourUnix += 3600000) {
|
||||||
const targetHour = new Date(hourUnix);
|
const targetHour = new Date(hourUnix);
|
||||||
targetHour.setMinutes(0, 0, 0);
|
targetHour.setUTCMinutes(0, 0, 0);
|
||||||
|
|
||||||
const closestPoint = data.reduce((prev, curr) => {
|
const closestPoint = data.reduce((prev, curr) => {
|
||||||
const prevTime = new Date(prev[0]);
|
const prevTime = new Date(prev[0]);
|
||||||
|
@ -706,7 +714,7 @@ prepareChartData: (coinSymbol, data) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const lastTime = new Date(data[data.length - 1][0]);
|
const lastTime = new Date(data[data.length - 1][0]);
|
||||||
if (lastTime.getMinutes() !== 0) {
|
if (lastTime.getUTCMinutes() !== 0) {
|
||||||
hourlyPoints.push({
|
hourlyPoints.push({
|
||||||
x: lastTime,
|
x: lastTime,
|
||||||
y: data[data.length - 1][1]
|
y: data[data.length - 1][1]
|
||||||
|
@ -735,33 +743,38 @@ prepareChartData: (coinSymbol, data) => {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
return preparedData;
|
return preparedData.map(point => ({
|
||||||
|
x: new Date(point.x).getTime(),
|
||||||
|
y: point.y
|
||||||
|
}));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`Error preparing chart data for ${coinSymbol}:`, error);
|
console.error(`Error preparing chart data for ${coinSymbol}:`, error);
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
ensureHourlyData: (data) => {
|
ensureHourlyData: (data) => {
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
const twentyFourHoursAgo = new Date(now.getTime() - 24 * 60 * 60 * 1000);
|
now.setUTCMinutes(0, 0, 0);
|
||||||
const hourlyData = [];
|
const twentyFourHoursAgo = new Date(now.getTime() - 24 * 60 * 60 * 1000);
|
||||||
|
const hourlyData = [];
|
||||||
|
|
||||||
for (let i = 0; i < 24; i++) {
|
for (let i = 0; i < 24; i++) {
|
||||||
const targetTime = new Date(twentyFourHoursAgo.getTime() + i * 60 * 60 * 1000);
|
const targetTime = new Date(twentyFourHoursAgo.getTime() + i * 60 * 60 * 1000);
|
||||||
const closestDataPoint = data.reduce((prev, curr) =>
|
const closestDataPoint = data.reduce((prev, curr) =>
|
||||||
Math.abs(curr.x - targetTime) < Math.abs(prev.x - targetTime) ? curr : prev
|
Math.abs(new Date(curr.x).getTime() - targetTime.getTime()) <
|
||||||
);
|
Math.abs(new Date(prev.x).getTime() - targetTime.getTime()) ? curr : prev
|
||||||
|
);
|
||||||
hourlyData.push({
|
|
||||||
x: targetTime,
|
hourlyData.push({
|
||||||
y: closestDataPoint.y
|
x: targetTime.getTime(),
|
||||||
});
|
y: closestDataPoint.y
|
||||||
}
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return hourlyData;
|
||||||
|
},
|
||||||
|
|
||||||
return hourlyData;
|
|
||||||
},
|
|
||||||
|
|
||||||
updateChart: async (coinSymbol, forceRefresh = false) => {
|
updateChart: async (coinSymbol, forceRefresh = false) => {
|
||||||
try {
|
try {
|
||||||
chartModule.showChartLoader();
|
chartModule.showChartLoader();
|
||||||
|
@ -797,16 +810,11 @@ ensureHourlyData: (data) => {
|
||||||
chartModule.chart.data.datasets[0].data = chartData;
|
chartModule.chart.data.datasets[0].data = chartData;
|
||||||
chartModule.chart.data.datasets[0].label = `${coinSymbol} Price (USD)`;
|
chartModule.chart.data.datasets[0].label = `${coinSymbol} Price (USD)`;
|
||||||
|
|
||||||
// Special handling for Wownero
|
|
||||||
if (coinSymbol === 'WOW') {
|
if (coinSymbol === 'WOW') {
|
||||||
chartModule.chart.options.scales.x.time.unit = 'hour';
|
chartModule.chart.options.scales.x.time.unit = 'hour';
|
||||||
chartModule.chart.options.scales.x.ticks.maxTicksLimit = 24;
|
chartModule.chart.options.scales.x.ticks.maxTicksLimit = 24;
|
||||||
chartModule.chart.options.plugins.tooltip.callbacks.title = (tooltipItems) => {
|
|
||||||
const date = new Date(tooltipItems[0].parsed.x);
|
|
||||||
return date.toLocaleString('en-US', { month: 'short', day: 'numeric', hour: 'numeric', minute: 'numeric', hour12: true, timeZone: 'UTC' });
|
|
||||||
};
|
|
||||||
} else {
|
} else {
|
||||||
const resolution = config.resolutions[config.currentResolution] || config.resolutions.year;
|
const resolution = config.resolutions[config.currentResolution];
|
||||||
chartModule.chart.options.scales.x.time.unit = resolution.interval === 'hourly' ? 'hour' : 'day';
|
chartModule.chart.options.scales.x.time.unit = resolution.interval === 'hourly' ? 'hour' : 'day';
|
||||||
|
|
||||||
if (config.currentResolution === 'year' || config.currentResolution === 'sixMonths') {
|
if (config.currentResolution === 'year' || config.currentResolution === 'sixMonths') {
|
||||||
|
@ -814,21 +822,12 @@ ensureHourlyData: (data) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.currentResolution === 'year') {
|
if (config.currentResolution === 'year') {
|
||||||
chartModule.chart.options.scales.x.ticks.maxTicksLimit = 12; // One tick per month
|
chartModule.chart.options.scales.x.ticks.maxTicksLimit = 12;
|
||||||
} else if (config.currentResolution === 'sixMonths') {
|
} else if (config.currentResolution === 'sixMonths') {
|
||||||
chartModule.chart.options.scales.x.ticks.maxTicksLimit = 6; // One tick every month
|
chartModule.chart.options.scales.x.ticks.maxTicksLimit = 6;
|
||||||
} else if (config.currentResolution === 'day') {
|
} else if (config.currentResolution === 'day') {
|
||||||
chartModule.chart.options.scales.x.ticks.maxTicksLimit = 24; // One tick every hour
|
chartModule.chart.options.scales.x.ticks.maxTicksLimit = 24;
|
||||||
}
|
}
|
||||||
|
|
||||||
chartModule.chart.options.plugins.tooltip.callbacks.title = (tooltipItems) => {
|
|
||||||
const date = new Date(tooltipItems[0].parsed.x);
|
|
||||||
if (config.currentResolution === 'year' || config.currentResolution === 'sixMonths') {
|
|
||||||
return date.toLocaleString('en-US', { year: 'numeric', month: 'short', day: 'numeric', timeZone: 'UTC' });
|
|
||||||
} else if (config.currentResolution === 'day') {
|
|
||||||
return date.toLocaleString('en-US', { month: 'short', day: 'numeric', hour: 'numeric', minute: 'numeric', hour12: true, timeZone: 'UTC' });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
chartModule.chart.update('active');
|
chartModule.chart.update('active');
|
||||||
|
|
Loading…
Reference in a new issue