-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #332 from greymass/asynchronous-chart-loading
feat: async chart loading
- Loading branch information
Showing
9 changed files
with
266 additions
and
337 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,134 +1,42 @@ | ||
<script lang="ts"> | ||
import { onMount } from 'svelte'; | ||
import dayjs from 'dayjs'; | ||
import { Chart } from 'chart.js'; | ||
import 'chart.js/auto'; | ||
import { getContext } from 'svelte'; | ||
import { Asset } from '@wharfkit/antelope'; | ||
import type { ExtendedSelectOption } from '../select/types'; | ||
import ChartContainer from './chart-container.svelte'; | ||
interface Props { | ||
data: { date: Date; value: Asset }[]; | ||
} | ||
let { data }: Props = $props(); | ||
let ctx: HTMLCanvasElement; | ||
let chart: Chart<'line'>; | ||
const range: ExtendedSelectOption[] = [ | ||
{ label: '1D', value: 1 }, | ||
{ label: '1W', value: 7 }, | ||
{ label: '1M', value: 30 } | ||
// { label: '1Y', value: 365 } // We're currently only getting data for the last 30 days | ||
]; | ||
const MAX_NUM_POINTS = 100; // Maximum number of points to display on the chart | ||
let selectedRange: ExtendedSelectOption = $state(range[1]); | ||
let dataRange = $derived.by(() => { | ||
if (data.length === 0) return []; | ||
const rangeEndDate = dayjs(data[0].date); | ||
const rangeStartDate = rangeEndDate.subtract(Number(selectedRange.value), 'day'); | ||
const filteredData = data.filter(({ date }) => dayjs(date).isAfter(rangeStartDate)); | ||
// If we have more points than MAX_NUM_POINTS, sample them evenly | ||
if (filteredData.length > MAX_NUM_POINTS) { | ||
const result = []; | ||
const step = filteredData.length / MAX_NUM_POINTS; | ||
for (let i = 0; i < MAX_NUM_POINTS; i++) { | ||
const index = Math.floor(i * step); | ||
result.push(filteredData[index]); | ||
import type { UnicoveContext } from '$lib/state/client.svelte'; | ||
import type { HistoricalPrice } from '$lib/types'; | ||
import Loading from './loading.svelte'; | ||
const { network } = getContext<UnicoveContext>('state'); | ||
type Price = { date: string; value: number }; | ||
type APIResponse = Price[] | { error: string }; | ||
const fetchTokenPrices = async () => { | ||
try { | ||
const response = await fetch(`/${network}/api/metrics/marketprice/token`); | ||
const parsedTokenResponse: APIResponse = await response.json(); | ||
if (Array.isArray(parsedTokenResponse) && parsedTokenResponse.length) { | ||
return parsedTokenResponse.map( | ||
(price: Price) => | ||
({ | ||
date: new Date(price.date), | ||
value: Asset.from(price.value / 10000, '4,USD') | ||
}) as HistoricalPrice | ||
); | ||
} else if ('error' in parsedTokenResponse && parsedTokenResponse.error) { | ||
throw new Error(String(parsedTokenResponse.error)); | ||
} else { | ||
throw new Error('Error fetching RAM prices'); | ||
} | ||
return result; | ||
} catch (e) { | ||
throw new Error(String(e)); | ||
} | ||
return filteredData; | ||
}); | ||
let currentPoint = $derived(dataRange[0]); | ||
let currentPrice = $derived(String(currentPoint.value)); | ||
let percentChange = $derived.by(() => { | ||
const current = Number(currentPoint.value.quantity); | ||
const initial = Number(dataRange[dataRange.length - 1].value.quantity); | ||
return (((current - initial) / current) * 100).toFixed(2) + '%'; | ||
}); | ||
const labels = $derived(dataRange.map(({ date }) => String(date.toLocaleDateString()))); | ||
const values = $derived(dataRange.map(({ value }) => Number(value.quantity))); | ||
onMount(() => { | ||
chart = new Chart(ctx, { | ||
type: 'line', | ||
data: { | ||
labels: labels, | ||
datasets: [ | ||
{ | ||
label: 'USD', | ||
data: values, | ||
fill: false, | ||
borderColor: '#00ED97', | ||
pointBorderWidth: 0 | ||
} | ||
] | ||
}, | ||
options: { | ||
normalized: true, | ||
responsive: true, | ||
maintainAspectRatio: false, | ||
interaction: { | ||
mode: 'nearest', | ||
intersect: false | ||
}, | ||
scales: { | ||
x: { | ||
reverse: true, | ||
border: { | ||
display: false | ||
}, | ||
grid: { | ||
display: false | ||
}, | ||
ticks: { | ||
display: false | ||
} | ||
}, | ||
y: { | ||
border: { | ||
display: false | ||
}, | ||
grid: { | ||
display: false | ||
} | ||
} | ||
}, | ||
plugins: { | ||
legend: { | ||
display: false | ||
} | ||
} | ||
} | ||
}); | ||
}); | ||
$effect(() => { | ||
chart.data.labels = labels; | ||
chart.data.datasets[0].data = values; | ||
chart.update(); | ||
}); | ||
let startDate = $derived(dataRange[dataRange.length - 1].date.toLocaleDateString()); | ||
}; | ||
</script> | ||
|
||
<ChartContainer | ||
pair="EOS/USD" | ||
{currentPrice} | ||
{percentChange} | ||
{startDate} | ||
{range} | ||
bind:selectedRange | ||
> | ||
<canvas bind:this={ctx}></canvas> | ||
</ChartContainer> | ||
{#await fetchTokenPrices()} | ||
<Loading pair="EOS/USD" /> | ||
{:then eosPrices} | ||
<ChartContainer pair="EOS/USD" data={eosPrices} type="line" /> | ||
{/await} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
<script lang="ts"> | ||
import { onMount } from 'svelte'; | ||
import { Chart } from 'chart.js'; | ||
import 'chart.js/auto'; | ||
import type { HistoricalPrice } from '$lib/types'; | ||
let ctx: HTMLCanvasElement; | ||
let chart: Chart<'line'>; | ||
interface Props { | ||
data: HistoricalPrice[]; | ||
label: string; | ||
} | ||
let { data, label }: Props = $props(); | ||
const labels = $derived(data.map(({ date }) => String(date.toLocaleDateString()))); | ||
const values = $derived(data.map(({ value }) => Number(value.quantity))); | ||
onMount(() => { | ||
chart = new Chart(ctx, { | ||
type: 'line', | ||
data: { | ||
labels: labels, | ||
datasets: [ | ||
{ | ||
label: label, | ||
data: values, | ||
fill: false, | ||
borderColor: '#00ED97', | ||
pointBorderWidth: 0 | ||
} | ||
] | ||
}, | ||
options: { | ||
normalized: true, | ||
responsive: true, | ||
maintainAspectRatio: false, | ||
interaction: { | ||
mode: 'nearest', | ||
intersect: false | ||
}, | ||
scales: { | ||
x: { | ||
reverse: true, | ||
border: { | ||
display: false | ||
}, | ||
grid: { | ||
display: false | ||
}, | ||
ticks: { | ||
display: false | ||
} | ||
}, | ||
y: { | ||
border: { | ||
display: false | ||
}, | ||
grid: { | ||
display: false | ||
} | ||
} | ||
}, | ||
plugins: { | ||
legend: { | ||
display: false | ||
} | ||
} | ||
} | ||
}); | ||
}); | ||
$effect(() => { | ||
chart.data.labels = labels; | ||
chart.data.datasets[0].data = values; | ||
chart.update(); | ||
}); | ||
</script> | ||
|
||
<canvas bind:this={ctx}></canvas> |
Oops, something went wrong.