Skip to content

Commit 32e6cfb

Browse files
committed
Enhance search functionality with immediate feedback and loading indicators
- Updated SearchForm to provide instant feedback for valid B256 addresses and improved state management for search results. - Modified SearchResultDropdown to clarify loading state with updated text and aria-label for better accessibility. - Adjusted SearchResolverSlow to streamline transaction fetching and ensure consistent data mapping for GraphQL responses.
1 parent 022bf22 commit 32e6cfb

File tree

4 files changed

+61
-38
lines changed

4 files changed

+61
-38
lines changed

packages/app-explorer/src/systems/Core/components/Search/SearchForm.tsx

Lines changed: 45 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { isB256 } from 'fuels';
12
import { ApiService } from '~/services/api';
23

34
import type { GQLSearchResult } from '@fuel-explorer/graphql';
@@ -23,27 +24,61 @@ export function SearchForm({ className, autoFocus }: SearchFormProps) {
2324
try {
2425
const query = formData.get('query')?.toString() || '';
2526

26-
// Call fast search first, update UI immediately
27+
// Call fast search first
2728
const fastResponse = await ApiService.searchFast(query).catch(() => null);
2829

29-
// Update UI with fast results immediately
30-
setResults(fastResponse || undefined);
30+
// Determine what to show immediately - evaluate all logic before setting state
31+
let initialResult: GQLSearchResult | undefined;
3132

32-
// Stop initial loading spinner - fast results are showing
33-
setLoading(false);
33+
if (fastResponse) {
34+
// If we got fast results, show them immediately
35+
initialResult = fastResponse;
36+
} else if (isB256(query)) {
37+
// If no fast result but valid B256, show empty account immediately
38+
// This gives instant feedback that it's a valid account address
39+
initialResult = {
40+
account: {
41+
address: query,
42+
transactions: [],
43+
__typename: 'SearchAccount',
44+
},
45+
} as GQLSearchResult;
46+
} else {
47+
// No fast results and not a valid B256 hash
48+
initialResult = undefined;
49+
}
3450

35-
// Show "loading more results" indicator
51+
// Update UI with immediate results - single setResults call to avoid race conditions
52+
setResults(initialResult);
53+
setLoading(false);
3654
setLoadingMore(true);
3755

3856
// Now fetch slow results in background and merge
3957
const slowResponse = await ApiService.searchSlow(query).catch(() => null);
4058

4159
if (slowResponse) {
4260
// Merge fast and slow results
43-
const merged = {
44-
...fastResponse,
45-
...slowResponse,
46-
};
61+
let merged: GQLSearchResult | null = null;
62+
63+
if (fastResponse) {
64+
// If we had fast results, merge with slow
65+
merged = {
66+
...fastResponse,
67+
...slowResponse,
68+
};
69+
} else if (slowResponse.account) {
70+
// If slow found account transactions, update the account with transactions
71+
merged = {
72+
account: {
73+
...slowResponse.account,
74+
__typename: 'SearchAccount',
75+
},
76+
} as GQLSearchResult;
77+
} else {
78+
// Keep the empty account we showed earlier, or merge slow results
79+
merged = slowResponse;
80+
}
81+
4782
setResults(merged || undefined);
4883
}
4984

packages/app-explorer/src/systems/Core/components/Search/SearchResultDropdown/SearchResultDropdown.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -252,9 +252,13 @@ export const SearchResultDropdown = forwardRef<
252252
)}
253253
{loadingMore && (
254254
<div className={classes.loadingContainer()}>
255-
<Spinner size={16} color="brand" aria-label="loading more" />
255+
<Spinner
256+
size={16}
257+
color="brand"
258+
aria-label="loading recent transactions"
259+
/>
256260
<p className={classes.loadingText()}>
257-
Loading more results...
261+
Loading recent transactions...
258262
</p>
259263
</div>
260264
)}

packages/graphql/src/graphql/resolvers/SearchResolver.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ export class SearchResolver {
138138
account: {
139139
address,
140140
transactions: transactionsResult.value.map(
141-
(transaction: TransactionEntity) => transaction.toGQLNode(),
141+
(transaction: TransactionEntity) => transaction.toGQLListNode(),
142142
),
143143
},
144144
};

packages/graphql/src/graphql/resolvers/SearchResolverSlow.ts

Lines changed: 9 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import { Hash256 } from '~/application/vo';
33
import { logger } from '~/core/Logger';
44
import type { TransactionEntity } from '~/domain/Transaction/TransactionEntity';
55
import DataCache from '~/infra/cache/DataCache';
6-
import AssetDAO from '~/infra/dao/AssetDAO';
76
import TransactionDAO from '~/infra/dao/TransactionDAO';
87

98
type Params = {
@@ -38,32 +37,17 @@ export class SearchResolverSlow {
3837
// Skip block height check (already handled by fast resolver)
3938
const address = Hash256.create(params.query).value();
4039
const transactionDAO = new TransactionDAO();
41-
const assetDAO = new AssetDAO();
4240

43-
// Slow queries: account transactions and assets
41+
// Slow queries: account transactions only
42+
// Assets are rarely searched and not critical for the initial result
4443
// Use recent transactions view for better performance on high-volume accounts
45-
const results = await Promise.allSettled([
46-
assetDAO.getByAssetId(address),
47-
transactionDAO.getRecentTransactionsByOwner(address),
48-
]);
49-
50-
const [assetResult, transactionsResult] = results;
51-
52-
// Priority order: Asset > Account
53-
54-
if (assetResult.status === 'fulfilled' && assetResult.value) {
55-
logger.debug('GraphQL', 'SearchResolverSlow.searchSlow - found asset');
56-
const result = {
57-
asset: assetResult.value,
58-
};
59-
DataCache.getInstance().save(cacheKey, 600000, result); // 10 min TTL
60-
return result;
61-
}
44+
const transactionsResult =
45+
await transactionDAO.getRecentTransactionsByOwner(address);
6246

6347
if (
64-
transactionsResult.status === 'fulfilled' &&
65-
transactionsResult.value &&
66-
transactionsResult.value.length > 0
48+
transactionsResult &&
49+
Array.isArray(transactionsResult) &&
50+
transactionsResult.length > 0
6751
) {
6852
logger.debug(
6953
'GraphQL',
@@ -72,8 +56,8 @@ export class SearchResolverSlow {
7256
const result = {
7357
account: {
7458
address,
75-
transactions: transactionsResult.value.map(
76-
(transaction: TransactionEntity) => transaction.toGQLNode(),
59+
transactions: transactionsResult.map(
60+
(transaction: TransactionEntity) => transaction.toGQLListNode(),
7761
),
7862
},
7963
};

0 commit comments

Comments
 (0)