Skip to content

Commit

Permalink
Merge pull request #953 from telosnetwork/952-add-rank-and-transactio…
Browse files Browse the repository at this point in the history
…ns-count-columns-to-section-accounts

#952 | Add "rank" and "transactions count" columns to /accounts section
  • Loading branch information
pmjanus authored Feb 6, 2025
2 parents 81a567d + 5f0afb9 commit 583af68
Show file tree
Hide file tree
Showing 16 changed files with 205 additions and 131 deletions.
2 changes: 1 addition & 1 deletion src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ const checkNetworkHealth = async () => {
// print in console with background #8B3F98 and white text the following message: Using indexer {health.data.version} with {health.data.secondsBehind} seconds behind
console.debug(
`%cUsing indexer ${health.data.version} for '${chainName}' ` +
(health.data?.secondsBehind > 3 ? `%cwith ${health.data.secondsBehind} seconds behind` : ''),
(health.data?.secondsBehind > 3 ? `with ${health.data.secondsBehind} seconds behind` : ''),
`background: ${background}; color: ${color};`,
);
Expand Down
4 changes: 0 additions & 4 deletions src/components/ImagePopup.vue
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,6 @@ watch(isPopupVisible, (value) => {
}
});
watch(() => props.previewSize, () => {
console.log('props.previewSize', props.previewSize);
});
</script>

<template>
Expand Down
140 changes: 112 additions & 28 deletions src/components/Token/HolderList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -96,13 +96,13 @@ const contract: Ref<ContractProps> = ref<ContractProps>(
);

// Minimum version for the indexer
const minimumVersion = '1.2.9';
const minimumVersion = '1.2.11';

// Reactive references
const weHaveIndexerSupport = ref(false);
const holders = ref<EvmHolder[]>([]);
const loadingRows = ref<number[]>([]);
const loading = ref(true);
const loading = ref(false);
const systemContractsList = ref('');
const showSystemContracts = ref(false);

Expand Down Expand Up @@ -159,13 +159,13 @@ const columnsDict: Record<string, TableColumn> = {
percentage: {
name: 'percentage',
field: 'percentage',
label: 'percentage',
label: 'Percentage',
align: 'left',
},
percentage_bar: {
name: 'percentage_bar',
field: 'percentage_bar',
label: 'percentage',
label: 'Percentage',
align: 'left',
},
txn_count: {
Expand Down Expand Up @@ -210,6 +210,8 @@ onBeforeMount(() => {
updateLoadingRows();
});

let timer = setTimeout(() => { /**/ }, 0);

onMounted(async () => {
// Build a list of system contracts
const list = await chainSettings.value.getContractManager().getSystemContractsList();
Expand All @@ -232,19 +234,22 @@ onMounted(async () => {
// Check indexer support
weHaveIndexerSupport.value = chainSettings.value.hasIndexerSupportOver(minimumVersion);

if (!weHaveIndexerSupport.value) {
timer = setTimeout(() => {
weHaveIndexerSupport.value = false;
loading.value = false;
}, INDEXER_SUPPORT_TIME_OUT);
}

// Listen for indexer readiness
chainSettings.value.indexerReady$.subscribe(() => {
weHaveIndexerSupport.value = chainSettings.value.hasIndexerSupportOver(minimumVersion);
console.log('HoldersList.onMounted() --> chainSettings.value.indexerReady$', { pagination: pagination.value });
onRequest();
clearTimeout(timer);
if (!loading.value) {
onRequest();
}
});

setTimeout(() => {
console.log('HoldersList.onMounted() --> setTimeout() !!!!');
weHaveIndexerSupport.value = false;
loading.value = false;
}, INDEXER_SUPPORT_TIME_OUT);

// Retrieve total supply from chain for system token
chainSettings.value.getTelosApi().get('supply/total').then((res) => {
if (systemTokenContract.value.properties) {
Expand Down Expand Up @@ -288,7 +293,6 @@ const last = {
};
async function onRequest() {
if (!weHaveIndexerSupport.value) {
console.log('onRequest() NO INDEXER; ----------------');
return;
}

Expand All @@ -301,30 +305,96 @@ async function onRequest() {
last.path = new_path;
}

// prepare skeleton rows
// Prepare skeleton rows
updateLoadingRows();
console.log('loading.value = true; ----------------');
loading.value = true;

const response = await indexerApi.get(new_path) as { data?: IndexerHoldersResponse };
if (response.data?.total_count && pagination_model.rowsNumber !== response.data?.total_count) {
pagination_model.rowsNumber = response.data.total_count;
setPaginationTotalNumber(pagination_model.rowsNumber);
}

// Calculate the starting rank based on current page and rowsPerPage
const startRank = (pagination.value.page - 1) * pagination.value.rowsPerPage;

const resultHolders: EvmHolder[] = [];
let i = 0;
for (const entry of response.data?.results || []) {
// Compute a consecutive rank
const rank = startRank + i + 1;

const holder: EvmHolder = {
rank: 0,
txn_count: 0,
rank,
txnCount: -1,
...(entry as Partial<EvmHolder>),
} as EvmHolder;
resultHolders.push(holder);

i++;
}

// fetching the transaction count for each holder
const addresses = response.data?.results.map(entry => entry.address) || [];

holders.value = resultHolders;
loading.value = false;
console.log('loading.value = false; ----------------');

fetchTxnCount(addresses);
}

interface TxnCountPair {
address: string
count: number
}

async function fetchTxnCount(addresses: string[]) {
// Step 1: Load local cache from localStorage
const localStorageKey = 'txnCountCache';
let txnCountCache: Record<string, number> = {};
const cachedData = localStorage.getItem(localStorageKey);
if (cachedData) {
try {
txnCountCache = JSON.parse(cachedData);
} catch (error) {
// If parsing fails, fallback to an empty object
txnCountCache = {};
}
}

// Step 2: For each address, if present in cache, set holders directly
for (const address of addresses) {
const holderIndex = holders.value.findIndex(h => h.address === address);
if (holderIndex !== -1 && txnCountCache[address] !== undefined) {
holders.value[holderIndex].txnCount = txnCountCache[address];
}
}

// Step 3: Perform the API request
const indexerApi = chainSettings.value.getIndexerApi();
const params = `${addresses.join(',')}`;
const txnCounts: TxnCountPair[] = (await indexerApi.get(`/v1/transactions/count?holders=${params}`))
.data?.results || [];

// Step 4: Update the cache with new data
for (const item of txnCounts) {
txnCountCache[item.address] = item.count;
}

// Save updated cache to localStorage
localStorage.setItem(localStorageKey, JSON.stringify(txnCountCache));

// Step 5: For each address, set the final value in holders
for (const address of addresses) {
const holderIndex = holders.value.findIndex(h => h.address === address);
const txnCount = txnCountCache[address] || 0;
if (holderIndex !== -1) {
holders.value[holderIndex].txnCount = txnCount;
}
}
}


// Build the URL path for the request
function getPath(): string {
const { page, rowsPerPage, descending } = pagination.value;
Expand Down Expand Up @@ -469,7 +539,7 @@ function calculateDollarValue(row: EvmHolder): string {
<template v-if="!weHaveIndexerSupport && !loading">
<MinimumVersionRequired
class="c-minimum-version-required"
required="1.2.9"
:required="minimumVersion"
/>
</template>
<template v-else>
Expand Down Expand Up @@ -504,13 +574,6 @@ function calculateDollarValue(row: EvmHolder): string {
>
<div class="u-flex--center-y">
{{ col.label }}
<!-- Add downward arrow for 'balance' or 'quantity' columns -->
<q-icon
v-if="col.name === 'balance' || col.name === 'quantity'"
name="arrow_downward"
size="16px"
class="q-ml-xs"
/>
</div>
</q-th>
</q-tr>
Expand Down Expand Up @@ -597,7 +660,12 @@ function calculateDollarValue(row: EvmHolder): string {

<!-- txn_count -->
<div v-else-if="col.name === 'txn_count'">
{{ props.row.txn_count }}
<q-skeleton
v-if="props.row.txnCount === -1"
type="text"
class="c-trx-overview__skeleton"
/>
<span v-else>{{ props.row.txnCount }}</span>
</div>

<!-- value -->
Expand Down Expand Up @@ -629,10 +697,18 @@ function calculateDollarValue(row: EvmHolder): string {
v-model:pagination="pagination"
:rows="loadingRows"
:columns="tableColumns"
:hide-bottom='true'
:rows-per-page-label="$t('global.records_per_page')"
:rows-per-page-options="[10, 25, 50, 100]"
:rows-per-page-options="default_rows_per_page_options"
>
<!-- Table header -->
<template v-slot:header="props">
<!-- Table pagination buttons in header -->
<q-tr>
<q-td :colspan="tableColumns.length">
<TablePagination/>
</q-td>
</q-tr>
<q-tr :props="props">
<q-th
v-for="col in props.cols"
Expand Down Expand Up @@ -664,6 +740,14 @@ function calculateDollarValue(row: EvmHolder): string {
</q-td>
</q-tr>
</template>
<!-- Bottom row with table pagination buttons -->
<template v-slot:bottom-row>
<q-tr>
<q-td :colspan="tableColumns.length" class="pagination-container">
<TablePagination/>
</q-td>
</q-tr>
</template>
</q-table>
</div>
</template>
Expand Down
11 changes: 2 additions & 9 deletions src/components/Token/TablePagination.vue
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,13 @@ const pagination_model = {
watch(
() => pagination.value,
(a) => {
console.log('TablePagination.watch() page:', pagination.value?.page, 'rowsPerPage:', pagination.value?.rowsPerPage, 'a:', a);
() => {
updatePaginationModel();
},
);
const updatePaginationModel = () => {
console.log('TablePagination.updatePaginationModel()', pagination.value); // FIXME: Remove
pagination_model.page = pagination.value.page;
pagination_model.rowsPerPage = pagination.value.rowsPerPage;
pagination_model.rowsNumber = pagination.value.rowsNumber || 0;
Expand All @@ -50,13 +50,6 @@ const updatePaginationModel = () => {
);
isOnFirstPage.value = pagination_model.page <= 1;
startRow.value = (pagination_model.rowsNumber === 0) ? 0 : (pagination_model.page - 1) * pagination_model.rowsPerPage + 1;
console.log('TablePagination.updatePaginationModel()', {
pagination_model: JSON.parse(JSON.stringify(pagination_model)),
isOnLastPage: isOnLastPage.value,
endRow: endRow.value,
isOnFirstPage: isOnFirstPage.value,
startRow: startRow.value,
});
};
Expand Down
Loading

0 comments on commit 583af68

Please sign in to comment.