Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge develop into testnet #1832

Merged
merged 1 commit into from
Jan 4, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 30 additions & 1 deletion src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,7 @@
"circulating_supply": "Circulating Supply",
"burnt": "Burnt",
"locked": "Unvested",
"week": "Time (week)",
"year": "Time (year)",
"nominal_apc": "Nominal DAO Compensation Rate",
"inflation_rate": "Inflation Rate",
Expand Down Expand Up @@ -326,6 +327,9 @@
"ckb_amount": "CKB Amount",
"contract_resource_distributed": "Contract Resource Distribution",
"contract_resource_distributed_description": "The x axis represents contract's unique address count, the y axis represents the contract's CKB amount, the symbol size represents the contract's transaction count.",
"active_addresses": "Active Addresses",
"active_addresses_description": "The number of unique addresses that have participated in the network as a sender or receiver.",
"active_address_count": "Active Addresses Count",
"country": "Country/Region",
"node_country_distribution": "Nodes distribution by Country/Region",
"top_50_holders": "Top 50 Holders",
Expand All @@ -340,7 +344,32 @@
"over_three_years": "> 3y",
"ckb_hodl_wave": "CKB HODL Wave",
"h24_transaction_count": "24hr Transaction Count",
"holder_count": "Holder Count"
"holder_count": "Holder Count",
"address_label": {
"anyoneCanPayLock": "Anyone Can Pay",
"btcTimeLock": "BTC Time",
"cheque": "Cheque",
"flashSigner": "Flash Signer",
"godwokenCustodianLock": "Godwoken Custodian",
"godwokenDepositLock": "Godwoken Deposit",
"godwokenStakeLock": "Godwoken Stake",
"godwokenWithdrawalLock": "Godwoken Withdrawal",
"iCkbLogic": "iCKB Logic",
"joyId": "Joy ID",
"nostr": "Nostr",
"omniLockV1": "Omni Lock V1",
"omniLockV2": "Omni Lock V2",
"pwLock": "Portal Wallet",
"rgb++": "RGB++",
"secp256K1/blake160": "Secp256k1/Blake160",
"secp256K1/multisig": "Secp256k1/Multisig",
"singleUseLock": "Single Use",
"udtLimitOrderr": "UDT Limit Order",
"unipassV2": "Unipass V2",
"unipassV3": "Unipass V3",
"wrOwnedOwner": "WR Owned Owner",
"others": "Others"
}
},
"home": {
"height": "Height",
Expand Down
4 changes: 4 additions & 0 deletions src/locales/zh.json
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,7 @@
"circulating_supply": "总流通量",
"burnt": "销毁量",
"locked": "未解锁数量",
"week": "时间 (周)",
"year": "时间 (年)",
"nominal_apc": "基准补偿率",
"inflation_rate": "通胀率",
Expand Down Expand Up @@ -340,6 +341,9 @@
"ckb_amount": "CKB 数量",
"contract_resource_distributed": "合约资源分布图",
"contract_resource_distributed_description": "横轴表示合约的唯一地址数量, 纵轴是合约的 CKB 数量, 图标大小代表合约的交易数量",
"active_addresses": "活跃地址",
"active_addresses_description": "活跃地址是指在特定时间段内有交易记录的地址",
"active_address_count": "活跃地址数量",
"country": "国家(地区)",
"node_country_distribution": "节点国家(地区)分布图",
"top_50_holders": "前 50 持有地址",
Expand Down
151 changes: 151 additions & 0 deletions src/pages/StatisticsChart/activities/ActiveAddressesChart.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
import { useTranslation } from 'react-i18next'
import type { ChartColorConfig } from '../../../constants/common'
import { SmartChartPage } from '../common'
import { DATA_ZOOM_CONFIG, handleAxis, variantColors } from '../../../utils/chart'
import { explorerService } from '../../../services/ExplorerService'

// Helper function to get ISO week number and year
function getWeekNumber(timestamp: string) {
const date = new Date(+timestamp * 1000)
const firstDayOfYear = new Date(date.getFullYear(), 0, 1)
const days = Math.floor((date.getTime() - firstDayOfYear.getTime()) / (24 * 60 * 60 * 1000))
const weekNumber = Math.ceil((days + firstDayOfYear.getDay() + 1) / 7)
return `${date.getFullYear()}-W${weekNumber}`
}

const useOption = (
activeAddresses: {
createdAtUnixtimestamp: string
distribution: Record<string, number>
}[],
_: ChartColorConfig,
isMobile: boolean,
isThumbnail = false,
): echarts.EChartOption => {
const { t } = useTranslation()
const gridThumbnail = {
left: '4%',
right: '10%',
top: '8%',
bottom: '6%',
containLabel: true,
}
const grid = {
left: '4%',
right: '8%',
top: '12%',
bottom: '5%',
containLabel: true,
}

const aggregatedByWeek = activeAddresses.reduce((acc, item) => {
const week = getWeekNumber(item.createdAtUnixtimestamp)

if (!acc[week]) {
acc[week] = {
createdAtWeek: week,
distribution: {},
}
}

Object.entries(item.distribution).forEach(([key, value]) => {
acc[week].distribution[key] = (acc[week].distribution[key] || 0) + value
})

return acc
}, {} as Record<string, { createdAtWeek: string; distribution: Record<string, number> }>)

const aggregatedDdata = Object.values(aggregatedByWeek)
const dataset = aggregatedDdata.slice(0, aggregatedDdata.length - 1) // Remove the last week data because it's not complete
const xAxisData = dataset.map(item => item.createdAtWeek)
const allKeys = Array.from(new Set(dataset.flatMap(item => Object.keys(item.distribution)))).sort((a, b) => {
if (a === 'others') return 1
if (b === 'others') return -1
return a.localeCompare(b)
})
const series = allKeys.map(key => ({
name: t(`statistic.address_label.${key}`),
type: 'line',
stack: 'total',
areaStyle: {},
lineStyle: {
width: 0,
},
symbol: 'none',
emphasis: {
focus: 'series',
},
data: dataset.map(item => item.distribution[key] || 0),
}))
const colors = variantColors(allKeys.length)

return {
color: colors,
tooltip: !isThumbnail
? {
trigger: 'axis',
axisPointer: { type: 'cross' },
formatter: params => {
// Filter out fields with value 0
if (!Array.isArray(params)) return ''
const filteredParams = params.filter(item => item.value !== 0)

// Construct the tooltip content
if (filteredParams.length === 0) return '' // No fields to display

const header = `${filteredParams[0].axisValue}<br/>` // Show week
const sum = `<span style="display:inline-block;margin-right:5px;border-radius:10px;width:10px;height:10px;background-color:white;"></span>
${t('statistic.active_address_count')}: ${filteredParams.reduce(
(acc, item) => acc + Number(item.value),
0,
)}<br/><hr style="margin: 4px 0" />`
const body = filteredParams
.map(
item =>
`<span style="display:inline-block;margin-right:5px;border-radius:10px;width:10px;height:10px;background-color:${item.color};"></span>
${item.seriesName}: ${item.value}`,
)
.join('<br/>')

return header + sum + body
},
}
: undefined,
grid: isThumbnail ? gridThumbnail : grid,
dataZoom: isThumbnail ? [] : DATA_ZOOM_CONFIG,
legend: { data: isThumbnail ? [] : allKeys.map(key => t(`statistic.address_label.${key}`) as string) },
xAxis: {
type: 'category',
boundaryGap: false,
data: xAxisData,
axisLabel: {
formatter: (value: string) => value, // Display week labels
},
name: isMobile || isThumbnail ? '' : t('statistic.week'),
},
yAxis: {
type: 'value',
name: isMobile || isThumbnail ? '' : `${t('statistic.active_address_count')}`,
axisLabel: {
formatter: (value: string) => handleAxis(+value),
},
},
series,
}
}

export const ActiveAddressesChart = ({ isThumbnail = false }: { isThumbnail?: boolean }) => {
const [t] = useTranslation()
return (
<SmartChartPage
title={t('statistic.active_addresses')}
description={t('statistic.active_addresses_description')}
isThumbnail={isThumbnail}
fetchData={explorerService.api.fetchStatisticActiveAddresses}
getEChartOption={useOption}
queryKey="fetchStatisticActiveAddresses"
/>
)
}

export default ActiveAddressesChart
Loading
Loading