Skip to content

Commit 56a0c39

Browse files
2 parents cfc0f9f + ea5b398 commit 56a0c39

File tree

12 files changed

+317
-31
lines changed

12 files changed

+317
-31
lines changed

CHANGELOG.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,20 @@
11
# Changelog
22

3+
4+
## [0.22.0](https://github.com/aeternity/aescan/compare/0.21.1...0.22.0) (2025-04-22)
5+
6+
7+
### Features
8+
9+
* Advanced AE price history chart ([#1124](https://github.com/aeternity/aescan/issues/1124)) ([f2152af](https://github.com/aeternity/aescan/commit/f2152afdc0687be8905d69fd15eba89ddb931fbe))
10+
11+
12+
### Bug Fixes
13+
14+
* remove delisted coinstore from app ([#1131](https://github.com/aeternity/aescan/issues/1131)) ([9e30415](https://github.com/aeternity/aescan/commit/9e30415f62da02a16812704e1e5c3203e4b0f680))
15+
* state channels pagination pagination ([#1133](https://github.com/aeternity/aescan/issues/1133)) ([cfc0f9f](https://github.com/aeternity/aescan/commit/cfc0f9f62a6e5566e0ac0bbbf3967748c886b2fd))
16+
* text content change ([#1126](https://github.com/aeternity/aescan/issues/1126)) ([3e4a821](https://github.com/aeternity/aescan/commit/3e4a821c38df21b2b2fa6968f068da5d4a494f9e))
17+
318
## [0.21.2](https://github.com/aeternity/aescan/compare/0.21.0...0.21.2) (2025-04-07)
419

520

cypress/e2e/app/aeCoin.cy.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ describe('ae coin', () => {
22
it('should display ae token detail', () => {
33
cy.visit('/tokens/ae')
44

5+
cy.get('.price-chart-panel__line-chart canvas').should('be.visible')
6+
57
cy.get('.ae-coin-panel table').should('be.visible')
68
cy.get('.ae-coin-transactions-panel .paginated-content').should('be.visible')
79

cypress/e2e/app/charts.cy.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,8 @@ describe('charts', () => {
2828
cy.visit('/charts/hashrate')
2929
cy.get('.line-chart canvas').should('be.visible')
3030
})
31+
it('should display price', () => {
32+
cy.visit('/charts/price')
33+
cy.get('.price-chart-panel__line-chart canvas').should('be.visible')
34+
})
3135
})

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "aescan",
3-
"version": "0.21.2",
3+
"version": "0.22.0",
44
"private": true,
55
"author": "æternity",
66
"description": "æScan is a blockchain explorer, analytics platform, and decentralized smart contract navigation platform based on æternity",

src/components/ChartsNavigation.vue

Lines changed: 33 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -12,34 +12,39 @@
1212
</nav>
1313
</template>
1414
<script setup>
15-
const menuOptions = ref([{
16-
name: 'Transactions',
17-
path: 'transactions',
18-
},
19-
{
20-
name: 'Keyblocks',
21-
path: 'keyblocks',
22-
},
23-
{
24-
name: 'Smart Contracts',
25-
path: 'contracts',
26-
},
27-
{
28-
name: 'Accounts',
29-
path: 'accounts',
30-
},
31-
{
32-
name: 'Names',
33-
path: 'names',
34-
},
35-
{
36-
name: 'Difficulty',
37-
path: 'difficulty',
38-
},
39-
{
40-
name: 'Hashrate',
41-
path: 'hashrate',
42-
}])
15+
const menuOptions = ref([
16+
{
17+
name: 'Price',
18+
path: 'price',
19+
},
20+
{
21+
name: 'Transactions',
22+
path: 'transactions',
23+
},
24+
{
25+
name: 'Keyblocks',
26+
path: 'keyblocks',
27+
},
28+
{
29+
name: 'Smart Contracts',
30+
path: 'contracts',
31+
},
32+
{
33+
name: 'Accounts',
34+
path: 'accounts',
35+
},
36+
{
37+
name: 'Names',
38+
path: 'names',
39+
},
40+
{
41+
name: 'Difficulty',
42+
path: 'difficulty',
43+
},
44+
{
45+
name: 'Hashrate',
46+
path: 'hashrate',
47+
}])
4348
</script>
4449

4550
<style scoped>

src/components/PriceChartControls.vue

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<template>
2+
<div>
3+
<div class="price-chart-controls__container">
4+
<app-chip
5+
v-for="option in PRICE_CHART_SCOPE_PRESETS_OPTIONS"
6+
:key="option.label"
7+
class="price-chart-controls__button"
8+
:variant="isPresetSelected(option) ? 'error' : 'secondary'"
9+
@click="selectPreset(option)">
10+
{{ option.label }}
11+
</app-chip>
12+
</div>
13+
</div>
14+
</template>
15+
16+
<script setup>
17+
import { useVModel } from '@vueuse/core'
18+
19+
const emit = defineEmits(['update:modelValue'])
20+
21+
const props = defineProps({
22+
modelValue: {
23+
type: Object,
24+
default: null,
25+
},
26+
})
27+
28+
const selectedScope = useVModel(props, 'modelValue', emit)
29+
30+
function isPresetSelected(option) {
31+
return selectedScope.value.label === option.label
32+
}
33+
34+
function selectPreset(option) {
35+
selectedScope.value = option
36+
}
37+
38+
</script>
39+
40+
<style scoped>
41+
.price-chart-controls {
42+
&__container {
43+
display: grid;
44+
grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr;
45+
column-gap: var(--space-1);
46+
row-gap: var(--space-1);
47+
48+
@media (--desktop) {
49+
display: flex;
50+
gap: var(--space-1);
51+
flex-grow: 1;
52+
flex-wrap: nowrap;
53+
}
54+
}
55+
56+
&__button {
57+
display: inline-flex;
58+
justify-content: center;
59+
cursor: pointer;
60+
}
61+
}
62+
</style>

src/components/PriceChartPanel.vue

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
<template>
2+
<app-panel>
3+
<template #title>
4+
AE PRICE TREND
5+
</template>
6+
7+
<template #end>
8+
<price-chart-controls
9+
v-model="selectedScope"
10+
class="u-hidden-mobile"/>
11+
</template>
12+
13+
<div class="price-chart-panel__line-chart">
14+
<Line
15+
v-if="priceStatistics"
16+
:options="chartOptions"
17+
:data="chartData"/>
18+
<loader-indicator v-else/>
19+
</div>
20+
21+
<price-chart-controls
22+
v-model="selectedScope"
23+
class="price-chart-panel__controls u-hidden-desktop"/>
24+
</app-panel>
25+
</template>
26+
27+
<script setup>
28+
import { Line } from 'vue-chartjs'
29+
import {
30+
CategoryScale,
31+
Chart as ChartJS,
32+
Legend,
33+
LinearScale,
34+
LineElement,
35+
PointElement,
36+
Title,
37+
Tooltip,
38+
} from 'chart.js'
39+
import { DateTime } from 'luxon'
40+
41+
const selectedScope = ref(PRICE_CHART_SCOPE_PRESETS_OPTIONS[4])
42+
43+
const { priceStatistics } = storeToRefs(useChartsStore())
44+
const { fetchPriceStatistics } = useChartsStore()
45+
46+
const labels = computed(() => {
47+
return priceStatistics.value.labels.map(label => {
48+
const format = selectedScope.value.intervalBy !== '1H' ? 'yyyy-MM-dd' : 'yyyy-MM-dd HH:mm'
49+
return DateTime.fromMillis(parseInt(label)).toFormat(format)
50+
})
51+
})
52+
53+
await useAsyncData(async() => {
54+
await loadPriceStatistics()
55+
return true
56+
})
57+
58+
if (process.client) {
59+
watch([selectedScope], async() => {
60+
await loadPriceStatistics()
61+
})
62+
}
63+
64+
async function loadPriceStatistics() {
65+
await fetchPriceStatistics(selectedScope.value.intervalBy)
66+
}
67+
68+
const chartData = computed(() => {
69+
return {
70+
labels: labels.value,
71+
datasets: [{
72+
data: priceStatistics.value.data,
73+
label: null,
74+
cubicInterpolationMode: 'monotone',
75+
tension: 0.4,
76+
borderColor: '#f5274e',
77+
backgroundColor: '#f5274e',
78+
pointRadius: 3,
79+
pointHitRadius: 20,
80+
}],
81+
}
82+
})
83+
84+
const chartOptions = {
85+
responsive: true,
86+
maintainAspectRatio: false,
87+
plugins: {
88+
legend: {
89+
display: false,
90+
},
91+
tooltip: {
92+
tooltip: {
93+
position: 'top',
94+
},
95+
callbacks: {
96+
label: value => `$ ${value.formattedValue}`,
97+
},
98+
},
99+
},
100+
scales: {
101+
y: {
102+
border: {
103+
display: false,
104+
},
105+
min: 0,
106+
ticks: {
107+
callback: value => `$ ${value}`,
108+
},
109+
},
110+
x: {
111+
grid: {
112+
color: () => 'transparent',
113+
},
114+
},
115+
},
116+
}
117+
118+
ChartJS.register(
119+
CategoryScale,
120+
LinearScale,
121+
PointElement,
122+
LineElement,
123+
Title,
124+
Tooltip,
125+
Legend,
126+
)
127+
128+
</script>
129+
130+
<style scoped>
131+
.price-chart-panel {
132+
&__line-chart {
133+
height: 250px;
134+
position: relative;
135+
136+
display: flex;
137+
align-items: center;
138+
justify-content: center;
139+
}
140+
141+
&__controls {
142+
margin-top: var(--space-4);
143+
}
144+
145+
}
146+
</style>

src/components/TheNavigation.vue

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
</template>
1616

1717
<script setup>
18-
1918
const featureFlags = useFeatureFlags()
2019
2120
const menuOptions = ref([{
@@ -96,6 +95,10 @@ const menuOptions = ref([{
9695
name: 'Charts',
9796
isActive: false,
9897
submenu: [
98+
{
99+
name: 'Price',
100+
path: '/charts/price',
101+
},
99102
{
100103
name: 'Transactions',
101104
path: '/charts/transactions',

src/composables/charts.js

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { useRuntimeConfig } from 'nuxt/app'
22

33
export const useChartsStore = defineStore('charts', () => {
4-
const { MIDDLEWARE_URL } = useRuntimeConfig().public
4+
const { MIDDLEWARE_URL, DEX_BACKEND_URL, AE_TOKEN_ID } = useRuntimeConfig().public
55
const axios = useAxios()
66

77
const transactionsStatistics = ref(null)
@@ -11,6 +11,7 @@ export const useChartsStore = defineStore('charts', () => {
1111
const difficultyStatistics = ref(null)
1212
const hashrateStatistics = ref(null)
1313
const accountsStatistics = ref(null)
14+
const priceStatistics = ref(null)
1415

1516
async function fetchTransactionsStatistics(intervalBy, limit, scope, txType) {
1617
transactionsStatistics.value = null
@@ -104,14 +105,27 @@ export const useChartsStore = defineStore('charts', () => {
104105
accountsStatistics.value = scope ? data.data.reverse() : data.data.slice(1).reverse()
105106
}
106107

108+
async function fetchPriceStatistics(intervalBy) {
109+
priceStatistics.value = null
110+
111+
const slug = `&timeFrame=${intervalBy}`
112+
113+
const { data } = await axios.get(
114+
`${DEX_BACKEND_URL}/graph?graphType=Price&tokenAddress=${AE_TOKEN_ID}${slug}`,
115+
)
116+
priceStatistics.value = data
117+
}
118+
107119
return {
120+
priceStatistics,
108121
keyblocksStatistics,
109122
transactionsStatistics,
110123
contractsStatistics,
111124
namesStatistics,
112125
difficultyStatistics,
113126
hashrateStatistics,
114127
accountsStatistics,
128+
fetchPriceStatistics,
115129
fetchKeyblocksStatistics,
116130
fetchTransactionsStatistics,
117131
fetchContractsStatistics,

0 commit comments

Comments
 (0)