Skip to content

Commit 03d756b

Browse files
devpavan04claude
andauthored
fix: enhance leaderboard search with hybrid filtering (#3062)
Co-authored-by: Claude <[email protected]>
1 parent 3f2a8b0 commit 03d756b

File tree

2 files changed

+178
-10
lines changed

2 files changed

+178
-10
lines changed

CLAUDE.md

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Project Overview
6+
7+
This is the Tangle dApp monorepo - a collection of decentralized applications serving as the frontend for the Tangle Network, a Substrate-based cryptocurrency network in the Polkadot ecosystem. Tangle is a layer 1 for on-demand services where developers can build and monetize decentralized services using Tangle Blueprints.
8+
9+
The monorepo uses Nx for fast, extensible building with `apps/` containing interfaces and `libs/` containing shared code.
10+
11+
## Common Development Commands
12+
13+
```bash
14+
# Install dependencies
15+
yarn install
16+
17+
# Development
18+
yarn start # Start all apps
19+
yarn start:tangle-dapp # Start main Tangle dApp
20+
yarn start:tangle-cloud # Start Tangle Cloud
21+
yarn start:leaderboard # Start leaderboard app
22+
yarn start:storybook # Start Storybook for ui-components
23+
24+
# Building
25+
yarn build # Build all projects
26+
yarn build:tangle-dapp # Build specific app
27+
yarn build:tangle-cloud
28+
yarn build:leaderboard
29+
30+
# Code Quality
31+
yarn lint # Lint all projects
32+
yarn format # Format code with Prettier
33+
yarn format:check # Check formatting
34+
yarn typecheck # Type check all projects
35+
yarn pr:check # Full pre-PR check (format + lint + build)
36+
37+
# Testing
38+
yarn test # Run all tests
39+
yarn test <project-name> # Run tests for specific project
40+
41+
# Release (maintainers only)
42+
yarn generate:release # Review version bumps and changelog
43+
```
44+
45+
## Architecture & Key Concepts
46+
47+
### Applications (apps/)
48+
- **tangle-dapp**: Main dApp for managing Tangle Network assets and MPC services
49+
- **tangle-cloud**: Cloud interface for Tangle services
50+
- **leaderboard**: Validator leaderboard application
51+
52+
### Libraries (libs/)
53+
- **abstract-api-provider**: Base classes unifying API across providers
54+
- **api-provider-environment**: React contexts, app events, error handling
55+
- **browser-utils**: Browser utilities (fetch, download, logger, storage)
56+
- **dapp-config**: Chain/wallet configurations for dApps
57+
- **dapp-types**: Shared TypeScript types and interfaces
58+
- **icons**: Shared icon components
59+
- **polkadot-api-provider**: Substrate/Polkadot provider for blockchain interaction
60+
- **solana-api-provider**: Solana blockchain provider
61+
- **tangle-shared-ui**: Tangle-specific logic, hooks, utilities (shared between dApps)
62+
- **ui-components**: Generic reusable UI components
63+
- **web3-api-provider**: EVM provider for blockchain interaction
64+
65+
### Tech Stack
66+
- **Frontend**: Vite, TypeScript, React, TailwindCSS
67+
- **Blockchain**: PolkadotJS with auto-generated types (`@tangle-network/tangle-substrate-types`)
68+
- **Build System**: Nx monorepo
69+
- **Styling**: TailwindCSS with custom preset
70+
71+
## Development Guidelines
72+
73+
### Code Style
74+
- Use `const ... => {}` over `function ... () {}`
75+
- React components: `const Component: FC<Props> = ({ prop1, prop2 }) => { ... }`
76+
- Use `useMemo`/`useCallback` when appropriate (skip for simple calculations)
77+
- Always use braces: `if (condition) { ... }`
78+
- Add empty lines between sibling JSX components
79+
- Avoid comments unless logic is complex
80+
- Use descriptive variable names, avoid acronyms
81+
- Avoid `as` type casting and `any` type
82+
83+
### Folder Structure (within apps)
84+
- `utils/`: Utility functions (one function per file, same filename as function name)
85+
- `components/`: Reusable "dumb" components specific to the app
86+
- `containers/`: "Smart" components with business logic
87+
- `hooks/`: React hooks for infrastructure logic
88+
- `data/`: Data fetching hooks organized by domain (restaking, liquid staking, etc.)
89+
- `pages/`: Route pages for react-router-dom
90+
- `abi/`: EVM ABI definitions for Substrate precompiles
91+
92+
### Important Notes
93+
- **Localize changes**: Keep changes isolated to relevant projects unless shared libraries are involved
94+
- **Package dependencies**: Don't assume packages exist - check imports or root `package.json` first
95+
- **Number handling**: For values > u32 from chain, use `BN` or `bigint`. For u32 or smaller, use `.toNumber()`
96+
- **Monorepo scope**: Avoid cross-project changes unless working with shared libs
97+
- **Storybook**: Considered legacy, avoid creating/modifying storybook files
98+
- **Testing**: No testing libraries currently used or planned
99+
100+
### Branch Strategy
101+
- Main development branch: `develop`
102+
- Main branch for releases: `master`
103+
- Release PRs should start with `[RELEASE]` in title
104+
105+
### Prerequisites
106+
- Node.js v18.18.x or later
107+
- Yarn package manager (v4.7.0)
108+
109+
## Working with Specific Libraries
110+
111+
### tangle-shared-ui
112+
Contains Tangle-specific logic shared between dApps. Use this for functionality tied to Tangle Network context.
113+
114+
### ui-components
115+
Generic, reusable components not tied to any specific context. Should be usable across different dApps.
116+
117+
### API Providers
118+
- Use `polkadot-api-provider` for Substrate/Polkadot interactions
119+
- Use `web3-api-provider` for EVM interactions
120+
- Use `abstract-api-provider` base classes when creating new providers

apps/leaderboard/src/features/leaderboard/components/LeaderboardTable.tsx

Lines changed: 58 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import type { NetworkType } from '@tangle-network/tangle-shared-ui/graphql/graph
66
import {
77
Input,
88
isSubstrateAddress,
9+
isValidAddress,
910
KeyValueWithButton,
1011
Table,
1112
TabsListWithAnimation,
@@ -14,7 +15,6 @@ import {
1415
Tooltip,
1516
TooltipBody,
1617
TooltipTrigger,
17-
toSubstrateAddress,
1818
Typography,
1919
ValidatorIdentity,
2020
} from '@tangle-network/ui-components';
@@ -56,6 +56,9 @@ const COLUMN_ID = {
5656
POINTS_HISTORY: 'pointsHistory',
5757
} as const;
5858

59+
const CLIENT_SIDE_FILTER_MIN_LENGTH = 3;
60+
const CLIENT_SIDE_FILTER_PAGE_SIZE = 100;
61+
5962
const COLUMN_HELPER = createColumnHelper<Account>();
6063

6164
const RankIcon = ({ rank }: { rank: number }) => {
@@ -232,24 +235,38 @@ export const LeaderboardTable = () => {
232235
return undefined;
233236
}
234237

235-
try {
236-
const substrateAddress = toSubstrateAddress(searchQuery);
238+
const trimmedQuery = searchQuery.trim();
237239

238-
return substrateAddress;
239-
} catch {
240-
return searchQuery;
240+
// Use server-side filtering only for valid addresses
241+
if (isValidAddress(trimmedQuery)) {
242+
return trimmedQuery;
241243
}
244+
245+
// Use client-side filtering for identity names and other searches
246+
return undefined;
242247
}, [searchQuery]);
243248

249+
const shouldUseClientSideFiltering = useMemo(() => {
250+
const trimmedQuery = searchQuery.trim();
251+
return (
252+
trimmedQuery.length >= CLIENT_SIDE_FILTER_MIN_LENGTH && !accountQuery
253+
);
254+
}, [searchQuery, accountQuery]);
255+
244256
const {
245257
data: leaderboardData,
246258
error,
247259
isPending,
248260
isFetching,
249261
} = useLeaderboard(
250262
networkTab,
251-
pagination.pageSize,
252-
pagination.pageIndex * pagination.pageSize,
263+
// Load more data when doing client-side filtering to ensure we don't miss results
264+
shouldUseClientSideFiltering
265+
? Math.max(CLIENT_SIDE_FILTER_PAGE_SIZE, pagination.pageSize)
266+
: pagination.pageSize,
267+
shouldUseClientSideFiltering
268+
? 0
269+
: pagination.pageIndex * pagination.pageSize,
253270
blockNumberSevenDaysAgo,
254271
accountQuery,
255272
);
@@ -272,7 +289,7 @@ export const LeaderboardTable = () => {
272289
return [] as Account[];
273290
}
274291

275-
return leaderboardData.nodes
292+
const processedData = leaderboardData.nodes
276293
.map((record, index) =>
277294
processLeaderboardRecord(
278295
record,
@@ -283,11 +300,37 @@ export const LeaderboardTable = () => {
283300
),
284301
)
285302
.filter((record) => record !== null);
303+
304+
// Apply client-side filtering for identity names and other searches
305+
if (!searchQuery || accountQuery || !shouldUseClientSideFiltering) {
306+
// If no search query, server-side filtering, or query too short, return as-is
307+
return processedData;
308+
}
309+
310+
const trimmedQuery = searchQuery.trim().toLowerCase();
311+
312+
// Client-side filter by identity name, address, or partial matches
313+
return processedData.filter((account) => {
314+
// Search by identity name
315+
if (account.identity?.name?.toLowerCase().includes(trimmedQuery)) {
316+
return true;
317+
}
318+
319+
// Search by address (case insensitive)
320+
if (account.id.toLowerCase().includes(trimmedQuery)) {
321+
return true;
322+
}
323+
324+
return false;
325+
});
286326
}, [
287327
leaderboardData?.nodes,
288328
pagination.pageIndex,
289329
pagination.pageSize,
290330
accountIdentities,
331+
searchQuery,
332+
accountQuery,
333+
shouldUseClientSideFiltering,
291334
]);
292335

293336
const table = useReactTable({
@@ -348,8 +391,13 @@ export const LeaderboardTable = () => {
348391
value={searchQuery}
349392
onChange={setSearchQuery}
350393
leftIcon={<Search />}
394+
rightIcon={
395+
isFetching && shouldUseClientSideFiltering ? (
396+
<Spinner size="lg" />
397+
) : undefined
398+
}
351399
id="search"
352-
placeholder="Search by address (Substrate or EVM)"
400+
placeholder="Search by address or identity name"
353401
size="md"
354402
inputClassName="py-1"
355403
/>

0 commit comments

Comments
 (0)