Commit d2aeeca4 authored by tom's avatar tom

check for redirect on client

parent 21528c82
...@@ -24,7 +24,7 @@ import type { InternalTransactionsResponse } from 'types/api/internalTransaction ...@@ -24,7 +24,7 @@ import type { InternalTransactionsResponse } from 'types/api/internalTransaction
import type { LogsResponseTx, LogsResponseAddress } from 'types/api/log'; import type { LogsResponseTx, LogsResponseAddress } from 'types/api/log';
import type { OutputRootsResponse } from 'types/api/outputRoots'; import type { OutputRootsResponse } from 'types/api/outputRoots';
import type { RawTracesResponse } from 'types/api/rawTrace'; 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 { Counters, StatsCharts, StatsChart, HomeStats } from 'types/api/stats';
import type { import type {
TokenCounters, TokenCounters,
...@@ -525,6 +525,7 @@ Q extends 'token_instance_transfers' ? TokenInstanceTransferResponse : ...@@ -525,6 +525,7 @@ Q extends 'token_instance_transfers' ? TokenInstanceTransferResponse :
Q extends 'token_inventory' ? TokenInventoryResponse : Q extends 'token_inventory' ? TokenInventoryResponse :
Q extends 'tokens' ? TokensResponse : Q extends 'tokens' ? TokensResponse :
Q extends 'search' ? SearchResult : Q extends 'search' ? SearchResult :
Q extends 'search_check_redirect' ? SearchRedirectResult :
Q extends 'contract' ? SmartContract : Q extends 'contract' ? SmartContract :
Q extends 'contract_methods_read' ? Array<SmartContractReadMethod> : Q extends 'contract_methods_read' ? Array<SmartContractReadMethod> :
Q extends 'contract_methods_read_proxy' ? Array<SmartContractReadMethod> : Q extends 'contract_methods_read_proxy' ? Array<SmartContractReadMethod> :
......
import type { GetServerSideProps, NextPage } from 'next'; import type { NextPage } from 'next';
import Head from 'next/head'; import Head from 'next/head';
import { route } from 'nextjs-routes';
import React from 'react'; 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 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'; import SearchResults from 'ui/pages/SearchResults';
const SearchResultsPage: NextPage = () => { const SearchResultsPage: NextPage = () => {
...@@ -27,53 +19,4 @@ const SearchResultsPage: NextPage = () => { ...@@ -27,53 +19,4 @@ const SearchResultsPage: NextPage = () => {
export default SearchResultsPage; export default SearchResultsPage;
export const getServerSideProps: GetServerSideProps<Props> = async({ req, res, resolvedUrl, query }) => { export { getServerSideProps } from 'lib/next/getServerSideProps';
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 });
};
import { Box, chakra, Table, Tbody, Tr, Th, Skeleton, Show, Hide } from '@chakra-ui/react'; 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 type { FormEvent } from 'react';
import React from 'react'; import React from 'react';
...@@ -17,9 +18,29 @@ import SearchBarInput from 'ui/snippets/searchBar/SearchBarInput'; ...@@ -17,9 +18,29 @@ import SearchBarInput from 'ui/snippets/searchBar/SearchBarInput';
import useSearchQuery from 'ui/snippets/searchBar/useSearchQuery'; import useSearchQuery from 'ui/snippets/searchBar/useSearchQuery';
const SearchResultsPageContent = () => { 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; 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>) => { const handleSubmit = React.useCallback((event: FormEvent<HTMLFormElement>) => {
event.preventDefault(); event.preventDefault();
}, [ ]); }, [ ]);
...@@ -29,7 +50,7 @@ const SearchResultsPageContent = () => { ...@@ -29,7 +50,7 @@ const SearchResultsPageContent = () => {
return <DataFetchAlert/>; return <DataFetchAlert/>;
} }
if (isLoading) { if (isLoading || redirectCheckQuery.isLoading) {
return ( return (
<Box> <Box>
<SkeletonList display={{ base: 'block', lg: 'none' }}/> <SkeletonList display={{ base: 'block', lg: 'none' }}/>
...@@ -70,7 +91,7 @@ const SearchResultsPageContent = () => { ...@@ -70,7 +91,7 @@ const SearchResultsPageContent = () => {
return null; return null;
} }
const text = isLoading ? ( const text = isLoading || redirectCheckQuery.isLoading ? (
<Skeleton h={ 6 } w="280px" borderRadius="full" mb={ isPaginationVisible ? 0 : 6 }/> <Skeleton h={ 6 } w="280px" borderRadius="full" mb={ isPaginationVisible ? 0 : 6 }/>
) : ( ) : (
( (
...@@ -129,7 +150,10 @@ const SearchResultsPageContent = () => { ...@@ -129,7 +150,10 @@ const SearchResultsPageContent = () => {
return ( return (
<Page renderHeader={ renderHeader }> <Page renderHeader={ renderHeader }>
<PageTitle text="Search results"/> { isLoading || redirectCheckQuery.isLoading ?
<Skeleton h={ 10 } mb={ 6 } w="100%" maxW="222px"/> :
<PageTitle text="Search results"/>
}
{ bar } { bar }
{ content } { content }
</Page> </Page>
......
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import React from 'react'; import React from 'react';
import useApiQuery from 'lib/api/useApiQuery';
import useDebounce from 'lib/hooks/useDebounce'; import useDebounce from 'lib/hooks/useDebounce';
import useQueryWithPages from 'lib/hooks/useQueryWithPages'; import useQueryWithPages from 'lib/hooks/useQueryWithPages';
import useUpdateValueEffect from 'lib/hooks/useUpdateValueEffect'; import useUpdateValueEffect from 'lib/hooks/useUpdateValueEffect';
import getQueryParamString from 'lib/router/getQueryParamString';
export default function useSearchQuery(isSearchPage = false) { export default function useSearchQuery(isSearchPage = false) {
const router = useRouter(); 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); const [ searchTerm, setSearchTerm ] = React.useState(initialValue);
...@@ -19,6 +22,11 @@ export default function useSearchQuery(isSearchPage = false) { ...@@ -19,6 +22,11 @@ export default function useSearchQuery(isSearchPage = false) {
options: { enabled: debouncedSearchTerm.trim().length > 0 }, options: { enabled: debouncedSearchTerm.trim().length > 0 },
}); });
const redirectCheckQuery = useApiQuery('search_check_redirect', {
queryParams: { q: q.current },
queryOptions: { enabled: isSearchPage && Boolean(q) },
});
useUpdateValueEffect(() => { useUpdateValueEffect(() => {
if (isSearchPage) { if (isSearchPage) {
query.onFilterChange({ q: debouncedSearchTerm }); query.onFilterChange({ q: debouncedSearchTerm });
...@@ -30,5 +38,6 @@ export default function useSearchQuery(isSearchPage = false) { ...@@ -30,5 +38,6 @@ export default function useSearchQuery(isSearchPage = false) {
debouncedSearchTerm, debouncedSearchTerm,
handleSearchTermChange: setSearchTerm, handleSearchTermChange: setSearchTerm,
query, query,
redirectCheckQuery,
}; };
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment