Skip to content

Commit

Permalink
Merge pull request #761 from blockscout/search/client-redirect
Browse files Browse the repository at this point in the history
search results: check for redirect on client
  • Loading branch information
tom2drum authored Apr 18, 2023
2 parents ab6c0be + 4c2a0c7 commit 28f21c4
Show file tree
Hide file tree
Showing 5 changed files with 43 additions and 66 deletions.
3 changes: 2 additions & 1 deletion lib/api/resources.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import type { InternalTransactionsResponse } from 'types/api/internalTransaction
import type { LogsResponseTx, LogsResponseAddress } from 'types/api/log';
import type { OutputRootsResponse } from 'types/api/outputRoots';
import type { RawTracesResponse } from 'types/api/rawTrace';
import type { SearchResult, SearchResultFilters } from 'types/api/search';
import type { SearchRedirectResult, SearchResult, SearchResultFilters } from 'types/api/search';
import type { Counters, StatsCharts, StatsChart, HomeStats } from 'types/api/stats';
import type {
TokenCounters,
Expand Down Expand Up @@ -529,6 +529,7 @@ Q extends 'token_instance_transfers' ? TokenInstanceTransferResponse :
Q extends 'token_inventory' ? TokenInventoryResponse :
Q extends 'tokens' ? TokensResponse :
Q extends 'search' ? SearchResult :
Q extends 'search_check_redirect' ? SearchRedirectResult :
Q extends 'contract' ? SmartContract :
Q extends 'contract_methods_read' ? Array<SmartContractReadMethod> :
Q extends 'contract_methods_read_proxy' ? Array<SmartContractReadMethod> :
Expand Down
61 changes: 2 additions & 59 deletions pages/search-results.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,8 @@
import type { GetServerSideProps, NextPage } from 'next';
import type { NextPage } from 'next';
import Head from 'next/head';
import { route } from 'nextjs-routes';
import React from 'react';

import type { SearchRedirectResult } from 'types/api/search';

import buildUrlNode from 'lib/api/buildUrlNode';
import fetchFactory from 'lib/api/nodeFetch';
import getNetworkTitle from 'lib/networks/getNetworkTitle';
import type { Props } from 'lib/next/getServerSideProps';
import { getServerSideProps as getServerSidePropsBase } from 'lib/next/getServerSideProps';
import * as serverTiming from 'lib/next/serverTiming';
import SearchResults from 'ui/pages/SearchResults';

const SearchResultsPage: NextPage = () => {
Expand All @@ -27,53 +19,4 @@ const SearchResultsPage: NextPage = () => {

export default SearchResultsPage;

export const getServerSideProps: GetServerSideProps<Props> = async({ req, res, resolvedUrl, query }) => {
const start = Date.now();

try {
const q = String(query.q);
const url = buildUrlNode('search_check_redirect', undefined, { q });
const redirectsResponse = await fetchFactory(req)(url, {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore:next-line timeout property exist for AbortSignal since Node.js 17 - https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal
// but @types/node has not updated their types yet, see issue https://github.com/DefinitelyTyped/DefinitelyTyped/issues/60868
signal: AbortSignal.timeout(1_000),
});
const payload = await redirectsResponse.json() as SearchRedirectResult;

if (!payload || typeof payload !== 'object' || !payload.redirect) {
throw Error();
}

const redirectUrl = (() => {
switch (payload.type) {
case 'block': {
return route({ pathname: '/block/[height]', query: { height: q } });
}
case 'address': {
return route({ pathname: '/address/[hash]', query: { hash: payload.parameter || q } });
}
case 'transaction': {
return route({ pathname: '/tx/[hash]', query: { hash: q } });
}
}
})();

if (!redirectUrl) {
throw Error();
}

return {
redirect: {
destination: redirectUrl,
permanent: false,
},
};
} catch (error) {}

const end = Date.now();

serverTiming.appendValue(res, 'query.search.check-redirect', end - start);

return getServerSidePropsBase({ req, res, resolvedUrl, query });
};
export { getServerSideProps } from 'lib/next/getServerSideProps';
2 changes: 1 addition & 1 deletion ui/pages/SearchResults.pw.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ test('search by address hash +@mobile', async({ mount, page }) => {
test('search by block number +@mobile', async({ mount, page }) => {
const hooksConfig = {
router: {
query: { q: searchMock.block1.block_number },
query: { q: String(searchMock.block1.block_number) },
},
};
await page.route(buildApiUrl('search') + `?q=${ searchMock.block1.block_number }`, (route) => route.fulfill({
Expand Down
32 changes: 28 additions & 4 deletions ui/pages/SearchResults.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Box, chakra, Table, Tbody, Tr, Th, Skeleton, Show, Hide } from '@chakra-ui/react';
import { useRouter } from 'next/router';
import type { FormEvent } from 'react';
import React from 'react';

Expand All @@ -17,9 +18,29 @@ import SearchBarInput from 'ui/snippets/searchBar/SearchBarInput';
import useSearchQuery from 'ui/snippets/searchBar/useSearchQuery';

const SearchResultsPageContent = () => {
const { query, searchTerm, debouncedSearchTerm, handleSearchTermChange } = useSearchQuery(true);
const router = useRouter();
const { query, redirectCheckQuery, searchTerm, debouncedSearchTerm, handleSearchTermChange } = useSearchQuery(true);
const { data, isError, isLoading, pagination, isPaginationVisible } = query;

React.useEffect(() => {
if (redirectCheckQuery.data?.redirect && redirectCheckQuery.data.parameter) {
switch (redirectCheckQuery.data.type) {
case 'block': {
router.push({ pathname: '/block/[height]', query: { height: redirectCheckQuery.data.parameter } });
return;
}
case 'address': {
router.push({ pathname: '/address/[hash]', query: { hash: redirectCheckQuery.data.parameter } });
return;
}
case 'transaction': {
router.push({ pathname: '/tx/[hash]', query: { hash: redirectCheckQuery.data.parameter } });
return;
}
}
}
}, [ redirectCheckQuery.data, router ]);

const handleSubmit = React.useCallback((event: FormEvent<HTMLFormElement>) => {
event.preventDefault();
}, [ ]);
Expand All @@ -29,7 +50,7 @@ const SearchResultsPageContent = () => {
return <DataFetchAlert/>;
}

if (isLoading) {
if (isLoading || redirectCheckQuery.isLoading) {
return (
<Box>
<SkeletonList display={{ base: 'block', lg: 'none' }}/>
Expand Down Expand Up @@ -70,7 +91,7 @@ const SearchResultsPageContent = () => {
return null;
}

const text = isLoading ? (
const text = isLoading || redirectCheckQuery.isLoading ? (
<Skeleton h={ 6 } w="280px" borderRadius="full" mb={ isPaginationVisible ? 0 : 6 }/>
) : (
(
Expand Down Expand Up @@ -129,7 +150,10 @@ const SearchResultsPageContent = () => {

return (
<Page renderHeader={ renderHeader }>
<PageTitle text="Search results"/>
{ isLoading || redirectCheckQuery.isLoading ?
<Skeleton h={ 10 } mb={ 6 } w="100%" maxW="222px"/> :
<PageTitle text="Search results"/>
}
{ bar }
{ content }
</Page>
Expand Down
11 changes: 10 additions & 1 deletion ui/snippets/searchBar/useSearchQuery.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import { useRouter } from 'next/router';
import React from 'react';

import useApiQuery from 'lib/api/useApiQuery';
import useDebounce from 'lib/hooks/useDebounce';
import useQueryWithPages from 'lib/hooks/useQueryWithPages';
import useUpdateValueEffect from 'lib/hooks/useUpdateValueEffect';
import getQueryParamString from 'lib/router/getQueryParamString';

export default function useSearchQuery(isSearchPage = false) {
const router = useRouter();
const initialValue = isSearchPage ? String(router.query.q || '') : '';
const q = React.useRef(getQueryParamString(router.query.q));
const initialValue = isSearchPage ? q.current : '';

const [ searchTerm, setSearchTerm ] = React.useState(initialValue);

Expand All @@ -19,6 +22,11 @@ export default function useSearchQuery(isSearchPage = false) {
options: { enabled: debouncedSearchTerm.trim().length > 0 },
});

const redirectCheckQuery = useApiQuery('search_check_redirect', {
queryParams: { q: q.current },
queryOptions: { enabled: isSearchPage && Boolean(q) },
});

useUpdateValueEffect(() => {
if (isSearchPage) {
query.onFilterChange({ q: debouncedSearchTerm });
Expand All @@ -30,5 +38,6 @@ export default function useSearchQuery(isSearchPage = false) {
debouncedSearchTerm,
handleSearchTermChange: setSearchTerm,
query,
redirectCheckQuery,
};
}

0 comments on commit 28f21c4

Please sign in to comment.