Commit 46e795fe authored by tom goriunov's avatar tom goriunov Committed by GitHub

Merge pull request #908 from blockscout/test/hook-for-paginated-queries

test: pagination refactoring and test
parents b2c51065 400424a0
...@@ -199,7 +199,7 @@ module.exports = { ...@@ -199,7 +199,7 @@ module.exports = {
groups: [ groups: [
'module', 'module',
'/types/', '/types/',
[ '/^configs/', '/^data/', '/^deploy/', '/^icons/', '/^lib/', '/^mocks/', '/^pages/', '/^playwright/', '/^stubs/', '/^theme/', '/^ui/' ], [ '/^configs/', '/^data/', '/^deploy/', '/^icons/', '/^jest/', '/^lib/', '/^mocks/', '/^pages/', '/^playwright/', '/^stubs/', '/^theme/', '/^ui/' ],
[ 'parent', 'sibling', 'index' ], [ 'parent', 'sibling', 'index' ],
], ],
alphabetize: { order: 'asc', ignoreCase: true }, alphabetize: { order: 'asc', ignoreCase: true },
...@@ -289,7 +289,7 @@ module.exports = { ...@@ -289,7 +289,7 @@ module.exports = {
}, },
}, },
{ {
files: [ 'configs/**/*.js', 'configs/**/*.ts', '*.config.ts' ], files: [ 'configs/**/*.js', 'configs/**/*.ts', '*.config.ts', 'playwright/**/*.ts' ],
rules: { rules: {
// for configs allow to consume env variables from process.env directly // for configs allow to consume env variables from process.env directly
'no-restricted-properties': [ 0 ], 'no-restricted-properties': [ 0 ],
......
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Jest: watch current file",
"program": "${workspaceFolder}/node_modules/jest/bin/jest",
"args": [
"${fileBasename}",
"--runInBand",
"--verbose",
"-i",
"--no-cache",
"--watchAll",
"--testTimeout=1000000000",
],
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen"
}
]
}
\ No newline at end of file
# app config # app config
NEXT_PUBLIC_APP_HOST=blockscout.com NEXT_PUBLIC_APP_PROTOCOL=http
NEXT_PUBLIC_APP_HOST=localhost
NEXT_PUBLIC_APP_PORT=3000
NEXT_PUBLIC_APP_INSTANCE=jest NEXT_PUBLIC_APP_INSTANCE=jest
NEXT_PUBLIC_APP_ENV=testing NEXT_PUBLIC_APP_ENV=testing
# ui config # ui config
NEXT_PUBLIC_BLOCKSCOUT_VERSION=v4.1.7-beta NEXT_PUBLIC_BLOCKSCOUT_VERSION=v4.1.7-beta
NEXT_PUBLIC_NETWORK_EXPLORERS=[{'title':'Bitquery','baseUrl':'https://explorer.bitquery.io/','paths':{'tx':'/goerli/tx','address':'/goerli/address','token':'/goerli/token','block':'/goerli/block'}},{'title':'Etherscan','baseUrl':'https://goerli.etherscan.io/','paths':{'tx':'/tx','address':'/address','token':'/token','block':'/block'}}]
NEXT_PUBLIC_GIT_TAG=v1.0.11
NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs','coin_price','market_cap']
NEXT_PUBLIC_HOMEPAGE_SHOW_AVG_BLOCK_TIME=true
NEXT_PUBLIC_HOMEPAGE_SHOW_GAS_TRACKER=true
NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND=
NEXT_PUBLIC_FEATURED_NETWORKS=
NEXT_PUBLIC_FOOTER_LINKS=
NEXT_PUBLIC_NETWORK_LOGO=
NEXT_PUBLIC_NETWORK_LOGO_DARK=
NEXT_PUBLIC_NETWORK_ICON=
NEXT_PUBLIC_NETWORK_ICON_DARK=
NEXT_PUBLIC_NETWORK_RPC_URL=https://localhost:1111
NEXT_PUBLIC_IS_TESTNET=true NEXT_PUBLIC_IS_TESTNET=true
NEXT_PUBLIC_MARKETPLACE_CONFIG_URL=https://localhost:3000/marketplace-config.json
NEXT_PUBLIC_IS_L2_NETWORK=false
# network config
NEXT_PUBLIC_NETWORK_NAME=Blockscout
NEXT_PUBLIC_NETWORK_SHORT_NAME=Blockscout
NEXT_PUBLIC_NETWORK_ASSETS_PATHNAME=
NEXT_PUBLIC_NETWORK_ID=1
NEXT_PUBLIC_NETWORK_CURRENCY_NAME=Ether
NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL=ETH
NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS=18
NEXT_PUBLIC_NETWORK_TOKEN_ADDRESS=
NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED=true
NEXT_PUBLIC_NETWORK_VERIFICATION_TYPE=validation
NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM=https://localhost:3000/marketplace-submit-form
# api config # api config
NEXT_PUBLIC_API_HOST=blockscout.com NEXT_PUBLIC_API_HOST=localhost
NEXT_PUBLIC_STATS_API_HOST=https://stats-test.aws-k8s.blockscout.com NEXT_PUBLIC_API_PORT=3003
NEXT_PUBLIC_STATS_API_HOST=https://localhost:3004
NEXT_PUBLIC_CONTRACT_INFO_API_HOST=https://localhost:3005
NEXT_PUBLIC_ADMIN_SERVICE_API_HOST=https://localhost:3006
NEXT_PUBLIC_RE_CAPTCHA_APP_SITE_KEY=xxx
NEXT_PUBLIC_API_BASE_PATH=/
...@@ -38,7 +38,8 @@ NEXT_PUBLIC_NETWORK_VERIFICATION_TYPE=validation ...@@ -38,7 +38,8 @@ NEXT_PUBLIC_NETWORK_VERIFICATION_TYPE=validation
NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM=https://localhost:3000/marketplace-submit-form NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM=https://localhost:3000/marketplace-submit-form
# api config # api config
NEXT_PUBLIC_API_HOST=https://localhost:3003 NEXT_PUBLIC_API_HOST=localhost
NEXT_PUBLIC_API_PORT=3003
NEXT_PUBLIC_STATS_API_HOST=https://localhost:3004 NEXT_PUBLIC_STATS_API_HOST=https://localhost:3004
NEXT_PUBLIC_CONTRACT_INFO_API_HOST=https://localhost:3005 NEXT_PUBLIC_CONTRACT_INFO_API_HOST=https://localhost:3005
NEXT_PUBLIC_ADMIN_SERVICE_API_HOST=https://localhost:3006 NEXT_PUBLIC_ADMIN_SERVICE_API_HOST=https://localhost:3006
......
...@@ -7,198 +7,35 @@ import type { JestConfigWithTsJest } from 'ts-jest'; ...@@ -7,198 +7,35 @@ import type { JestConfigWithTsJest } from 'ts-jest';
*/ */
const config: JestConfigWithTsJest = { const config: JestConfigWithTsJest = {
// All imported modules in your tests should be mocked automatically
// automock: false,
// Stop running tests after `n` failures
// bail: 0,
// The directory where Jest should store its cached dependency information
// cacheDirectory: "/private/var/folders/3v/ry82j1y52sqgz3yvbcbt1krw0000gn/T/jest_dx",
// Automatically clear mock calls, instances, contexts and results before every test
clearMocks: true, clearMocks: true,
// Indicates whether the coverage information should be collected while executing the test
// collectCoverage: false,
// An array of glob patterns indicating a set of files for which coverage information should be collected
// collectCoverageFrom: undefined,
// The directory where Jest should output its coverage files
// coverageDirectory: undefined,
// An array of regexp pattern strings used to skip coverage collection
// coveragePathIgnorePatterns: [
// "/node_modules/"
// ],
// Indicates which provider should be used to instrument code for coverage
coverageProvider: 'v8', coverageProvider: 'v8',
globalSetup: '<rootDir>/jest/global-setup.ts',
// A list of reporter names that Jest uses when writing coverage reports
// coverageReporters: [
// "json",
// "text",
// "lcov",
// "clover"
// ],
// An object that configures minimum threshold enforcement for coverage results
// coverageThreshold: undefined,
// A path to a custom dependency extractor
// dependencyExtractor: undefined,
// Make calling deprecated APIs throw helpful error messages
// errorOnDeprecated: false,
// The default configuration for fake timers
// fakeTimers: {
// "enableGlobally": false
// },
// Force coverage collection from ignored files using an array of glob patterns
// forceCoverageMatch: [],
// A path to a module which exports an async function that is triggered once before all test suites
globalSetup: '<rootDir>/configs/jest/globalSetup.ts',
// A path to a module which exports an async function that is triggered once after all test suites
// globalTeardown: undefined,
// A set of global variables that need to be available in all test environments
// globals: {},
// The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers.
// maxWorkers: "50%",
// An array of directory names to be searched recursively up from the requiring module's location
moduleDirectories: [ moduleDirectories: [
'node_modules', 'node_modules',
__dirname, __dirname,
], ],
moduleNameMapper: {
// An array of file extensions your modules use '^jest/(.*)': '<rootDir>/jest/$1',
// moduleFileExtensions: [ },
// "js",
// "mjs",
// "cjs",
// "jsx",
// "ts",
// "tsx",
// "json",
// "node"
// ],
// A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module
// moduleNameMapper: {},
// An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader
modulePathIgnorePatterns: [ modulePathIgnorePatterns: [
'node_modules_linux', 'node_modules_linux',
], ],
// Activates notifications for test results
// notify: false,
// An enum that specifies notification mode. Requires { notify: true }
// notifyMode: "failure-change",
// A preset that is used as a base for Jest's configuration
preset: 'ts-jest', preset: 'ts-jest',
// Run tests from one or more projects
// projects: undefined,
// Use this configuration option to add custom reporters to Jest
reporters: [ 'default', 'github-actions' ], reporters: [ 'default', 'github-actions' ],
setupFiles: [
// Automatically reset mock state before every test '<rootDir>/jest/setup.ts',
// resetMocks: false, ],
// Reset the module registry before running each individual test
// resetModules: false,
// A path to a custom resolver
// resolver: undefined,
// Automatically restore mock state and implementation before every test
// restoreMocks: false,
// The root directory that Jest should scan for tests and modules within
// rootDir: undefined,
// A list of paths to directories that Jest should use to search for files in
// roots: [
// "<rootDir>"
// ],
// Allows you to use a custom runner instead of Jest's default test runner
// runner: "jest-runner",
// The paths to modules that run some code to configure or set up the testing environment before each test
// setupFiles: undefined,
// A list of paths to modules that run some code to configure or set up the testing framework before each test
// setupFilesAfterEnv: [],
// The number of seconds after which a test is considered as slow and reported as such in the results.
// slowTestThreshold: 5,
// A list of paths to snapshot serializer modules Jest should use for snapshot testing
// snapshotSerializers: [],
// The test environment that will be used for testing
testEnvironment: 'jsdom', testEnvironment: 'jsdom',
// testEnvironment: 'node', transform: {
// '^.+\\.[tj]sx?$' to process js/ts with `ts-jest`
// Options that will be passed to the testEnvironment // '^.+\\.m?[tj]sx?$' to process js/ts/mjs/mts with `ts-jest`
// testEnvironmentOptions: {}, '^.+\\.tsx?$': [
'ts-jest',
// Adds a location field to test results {
// testLocationInResults: false, tsconfig: 'tsconfig.jest.json',
},
// The glob patterns Jest uses to detect test files ],
// testMatch: [ },
// "**/__tests__/**/*.[jt]s?(x)",
// "**/?(*.)+(spec|test).[tj]s?(x)"
// ],
// An array of regexp pattern strings that are matched against all test paths, matched tests are skipped
// testPathIgnorePatterns: [
// "/node_modules/"
// ],
// The regexp pattern or array of patterns that Jest uses to detect test files
// testRegex: [],
// This option allows the use of a custom results processor
// testResultsProcessor: undefined,
// This option allows use of a custom test runner
// testRunner: "jest-circus/runner",
// A map from regular expressions to paths to transformers
// transform: undefined,
// An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation
// transformIgnorePatterns: [
// "/node_modules/",
// "\\.pnp\\.[^\\/]+$"
// ],
// An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them
// unmockedModulePathPatterns: undefined,
// Indicates whether each individual test should be reported during the run
// verbose: undefined,
// An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode
// watchPathIgnorePatterns: [],
// Whether to use watchman for file crawling
// watchman: true,
}; };
export default config; export default config;
...@@ -2,5 +2,4 @@ import dotenv from 'dotenv'; ...@@ -2,5 +2,4 @@ import dotenv from 'dotenv';
export default async function globalSetup() { export default async function globalSetup() {
dotenv.config({ path: './configs/envs/.env.jest' }); dotenv.config({ path: './configs/envs/.env.jest' });
dotenv.config({ path: './configs/envs/.env.poa_core' });
} }
import { ChakraProvider } from '@chakra-ui/react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import type { RenderOptions } from '@testing-library/react';
import { render } from '@testing-library/react';
import React from 'react';
import { AppContextProvider } from 'lib/contexts/app';
import { ScrollDirectionProvider } from 'lib/contexts/scrollDirection';
import { SocketProvider } from 'lib/socket/context';
import theme from 'theme';
import 'lib/setLocale';
const PAGE_PROPS = {
cookies: '',
referrer: '',
};
const TestApp = ({ children }: {children: React.ReactNode}) => {
const [ queryClient ] = React.useState(() => new QueryClient({
defaultOptions: {
queries: {
refetchOnWindowFocus: false,
retry: 0,
},
},
}));
return (
<ChakraProvider theme={ theme }>
<QueryClientProvider client={ queryClient }>
<AppContextProvider pageProps={ PAGE_PROPS }>
<ScrollDirectionProvider>
<SocketProvider>
{ children }
</SocketProvider>
</ScrollDirectionProvider>
</AppContextProvider>
</QueryClientProvider>
</ChakraProvider>
);
};
const customRender = (
ui: React.ReactElement,
options?: Omit<RenderOptions, 'wrapper'>,
) => render(ui, { wrapper: TestApp, ...options });
export * from '@testing-library/react';
export { customRender as render };
export { TestApp as wrapper };
import type { NextRouter } from 'next/router';
export const router = {
query: {},
push: jest.fn(() => Promise.resolve()),
};
export const useRouter = jest.fn<unknown, Array<Partial<NextRouter>>>(() => (router));
export const mockUseRouter = (params?: Partial<NextRouter>) => {
return {
useRouter: jest.fn(() => ({
...router,
...params,
})),
};
};
import fetchMock from 'jest-fetch-mock';
fetchMock.enableMocks();
Object.defineProperty(window, 'matchMedia', {
writable: true,
value: jest.fn().mockImplementation(query => ({
matches: false,
media: query,
onchange: null,
addListener: jest.fn(),
removeListener: jest.fn(),
addEventListener: jest.fn(),
removeEventListener: jest.fn(),
dispatchEvent: jest.fn(),
})),
});
// eslint-disable-next-line no-console
const consoleError = console.error;
global.console = {
...console,
error: (...args) => {
// silence some irrelevant errors
if (args.some((arg) => typeof arg === 'string' && arg.includes('Using kebab-case for css properties'))) {
return;
}
consoleError(...args);
},
};
const scheduler = typeof setImmediate === 'function' ? setImmediate : setTimeout;
export default function flushPromises() {
return new Promise(function(resolve) {
scheduler(resolve);
});
}
import { import {
ChakraProvider, ChakraProvider as ChakraProviderDefault,
cookieStorageManagerSSR, cookieStorageManagerSSR,
localStorageManager, localStorageManager,
} from '@chakra-ui/react'; } from '@chakra-ui/react';
...@@ -10,15 +10,15 @@ interface Props extends ChakraProviderProps { ...@@ -10,15 +10,15 @@ interface Props extends ChakraProviderProps {
cookies?: string; cookies?: string;
} }
export function Chakra({ cookies, theme, children }: Props) { export function ChakraProvider({ cookies, theme, children }: Props) {
const colorModeManager = const colorModeManager =
typeof cookies === 'string' ? typeof cookies === 'string' ?
cookieStorageManagerSSR(typeof document !== 'undefined' ? document.cookie : cookies) : cookieStorageManagerSSR(typeof document !== 'undefined' ? document.cookie : cookies) :
localStorageManager; localStorageManager;
return ( return (
<ChakraProvider colorModeManager={ colorModeManager } theme={ theme }> <ChakraProviderDefault colorModeManager={ colorModeManager } theme={ theme }>
{ children } { children }
</ChakraProvider> </ChakraProviderDefault>
); );
} }
import { useEffect } from 'react'; import { useEffect } from 'react';
import { useAppContext } from 'lib/appContext'; import { useAppContext } from 'lib/contexts/app';
import * as cookies from 'lib/cookies'; import * as cookies from 'lib/cookies';
import isBrowser from 'lib/isBrowser'; import isBrowser from 'lib/isBrowser';
......
import appConfig from 'configs/app/config'; import appConfig from 'configs/app/config';
import { useAppContext } from 'lib/appContext'; import { useAppContext } from 'lib/contexts/app';
import * as cookies from 'lib/cookies'; import * as cookies from 'lib/cookies';
export default function useHasAccount() { export default function useHasAccount() {
......
import React from 'react';
export default function useIsInitialLoading(isLoading: boolean | undefined) {
const [ isInitialLoading, setIsInitialLoading ] = React.useState(Boolean(isLoading));
React.useEffect(() => {
if (!isLoading) {
setIsInitialLoading(false);
}
}, [ isLoading ]);
return isInitialLoading;
}
...@@ -25,7 +25,7 @@ ...@@ -25,7 +25,7 @@
"lint:tsc": "./node_modules/.bin/tsc -p ./tsconfig.json", "lint:tsc": "./node_modules/.bin/tsc -p ./tsconfig.json",
"prepare": "husky install", "prepare": "husky install",
"format-svg": "./node_modules/.bin/svgo -r ./icons", "format-svg": "./node_modules/.bin/svgo -r ./icons",
"test:pw": "./playwright/make-envs-script.sh && NODE_OPTIONS=\"--max-old-space-size=4096\" playwright test -c playwright-ct.config.ts", "test:pw": "./playwright/make-envs-script.sh && NODE_OPTIONS=\"--max-old-space-size=4096\" ./node_modules/.bin/dotenv -e ./configs/envs/.env.pw -- playwright test -c playwright-ct.config.ts",
"test:pw:local": "export NODE_PATH=$(pwd)/node_modules && rm -rf ./playwright/.cache && yarn test:pw", "test:pw:local": "export NODE_PATH=$(pwd)/node_modules && rm -rf ./playwright/.cache && yarn test:pw",
"test:pw:docker": "docker run --rm --network host -v $(pwd):/work/ -w /work/ -it mcr.microsoft.com/playwright:v1.32.0-focal ./playwright/run-tests.sh", "test:pw:docker": "docker run --rm --network host -v $(pwd):/work/ -w /work/ -it mcr.microsoft.com/playwright:v1.32.0-focal ./playwright/run-tests.sh",
"test:pw:ci": "yarn test:pw --project=$PW_PROJECT", "test:pw:ci": "yarn test:pw --project=$PW_PROJECT",
...@@ -88,7 +88,7 @@ ...@@ -88,7 +88,7 @@
"devDependencies": { "devDependencies": {
"@playwright/experimental-ct-react": "1.32.3", "@playwright/experimental-ct-react": "1.32.3",
"@svgr/webpack": "^6.5.1", "@svgr/webpack": "^6.5.1",
"@testing-library/react": "^13.4.0", "@testing-library/react": "^14.0.0",
"@total-typescript/ts-reset": "^0.3.7", "@total-typescript/ts-reset": "^0.3.7",
"@types/crypto-js": "^4.1.1", "@types/crypto-js": "^4.1.1",
"@types/csp-dev": "^1.0.0", "@types/csp-dev": "^1.0.0",
...@@ -120,6 +120,7 @@ ...@@ -120,6 +120,7 @@
"husky": "^8.0.0", "husky": "^8.0.0",
"jest": "^29.2.1", "jest": "^29.2.1",
"jest-environment-jsdom": "^29.2.1", "jest-environment-jsdom": "^29.2.1",
"jest-fetch-mock": "^3.0.3",
"lint-staged": ">=10", "lint-staged": ">=10",
"mockdate": "^3.0.5", "mockdate": "^3.0.5",
"next-transpile-modules": "^10.0.0", "next-transpile-modules": "^10.0.0",
......
...@@ -6,8 +6,8 @@ import React, { useState } from 'react'; ...@@ -6,8 +6,8 @@ import React, { useState } from 'react';
import appConfig from 'configs/app/config'; import appConfig from 'configs/app/config';
import type { ResourceError } from 'lib/api/resources'; import type { ResourceError } from 'lib/api/resources';
import { AppContextProvider } from 'lib/appContext'; import { AppContextProvider } from 'lib/contexts/app';
import { Chakra } from 'lib/Chakra'; import { ChakraProvider } from 'lib/contexts/chakra';
import { ScrollDirectionProvider } from 'lib/contexts/scrollDirection'; import { ScrollDirectionProvider } from 'lib/contexts/scrollDirection';
import getErrorCauseStatusCode from 'lib/errors/getErrorCauseStatusCode'; import getErrorCauseStatusCode from 'lib/errors/getErrorCauseStatusCode';
import useConfigSentry from 'lib/hooks/useConfigSentry'; import useConfigSentry from 'lib/hooks/useConfigSentry';
...@@ -63,7 +63,7 @@ function MyApp({ Component, pageProps }: AppProps) { ...@@ -63,7 +63,7 @@ function MyApp({ Component, pageProps }: AppProps) {
}, []); }, []);
return ( return (
<Chakra theme={ theme } cookies={ pageProps.cookies }> <ChakraProvider theme={ theme } cookies={ pageProps.cookies }>
<ErrorBoundary renderErrorScreen={ renderErrorScreen } onError={ handleError }> <ErrorBoundary renderErrorScreen={ renderErrorScreen } onError={ handleError }>
<AppContextProvider pageProps={ pageProps }> <AppContextProvider pageProps={ pageProps }>
<QueryClientProvider client={ queryClient }> <QueryClientProvider client={ queryClient }>
...@@ -77,7 +77,7 @@ function MyApp({ Component, pageProps }: AppProps) { ...@@ -77,7 +77,7 @@ function MyApp({ Component, pageProps }: AppProps) {
</QueryClientProvider> </QueryClientProvider>
</AppContextProvider> </AppContextProvider>
</ErrorBoundary> </ErrorBoundary>
</Chakra> </ChakraProvider>
); );
} }
......
...@@ -5,10 +5,10 @@ import React from 'react'; ...@@ -5,10 +5,10 @@ import React from 'react';
import { configureChains, createConfig, WagmiConfig } from 'wagmi'; import { configureChains, createConfig, WagmiConfig } from 'wagmi';
import { mainnet } from 'wagmi/chains'; import { mainnet } from 'wagmi/chains';
import { AppContextProvider } from 'lib/appContext'; import { AppContextProvider } from 'lib/contexts/app';
import type { Props as PageProps } from 'lib/next/getServerSideProps'; import type { Props as PageProps } from 'lib/next/getServerSideProps';
import { SocketProvider } from 'lib/socket/context'; import { SocketProvider } from 'lib/socket/context';
import { PORT } from 'playwright/fixtures/socketServer'; import * as app from 'playwright/utils/app';
import theme from 'theme'; import theme from 'theme';
type Props = { type Props = {
...@@ -54,7 +54,7 @@ const TestApp = ({ children, withSocket, appContext = defaultAppContext }: Props ...@@ -54,7 +54,7 @@ const TestApp = ({ children, withSocket, appContext = defaultAppContext }: Props
return ( return (
<ChakraProvider theme={ theme }> <ChakraProvider theme={ theme }>
<QueryClientProvider client={ queryClient }> <QueryClientProvider client={ queryClient }>
<SocketProvider url={ withSocket ? `ws://localhost:${ PORT }` : undefined }> <SocketProvider url={ withSocket ? `ws://${ app.domain }:${ app.socketPort }` : undefined }>
<AppContextProvider { ...appContext }> <AppContextProvider { ...appContext }>
<WagmiConfig config={ wagmiConfig }> <WagmiConfig config={ wagmiConfig }>
{ children } { children }
......
import type { BrowserContext } from '@playwright/test'; import type { BrowserContext } from '@playwright/test';
import * as cookies from 'lib/cookies'; import * as cookies from 'lib/cookies';
import { domain } from 'playwright/utils/app';
export default function authFixture(context: BrowserContext) { export default function authFixture(context: BrowserContext) {
context.addCookies([ { name: cookies.NAMES.API_TOKEN, value: 'foo', domain: 'localhost', path: '/' } ]); context.addCookies([ { name: cookies.NAMES.API_TOKEN, value: 'foo', domain, path: '/' } ]);
} }
import type { test } from '@playwright/experimental-ct-react'; import type { test } from '@playwright/experimental-ct-react';
import type { Browser } from '@playwright/test'; import type { Browser } from '@playwright/test';
import * as app from 'playwright/utils/app';
interface Env { interface Env {
name: string; name: string;
value: string; value: string;
...@@ -20,7 +22,7 @@ export async function createContextWithEnvs(browser: Browser, envs: Array<Env>) ...@@ -20,7 +22,7 @@ export async function createContextWithEnvs(browser: Browser, envs: Array<Env>)
return browser.newContext({ return browser.newContext({
storageState: { storageState: {
origins: [ origins: [
{ origin: 'http://localhost:3100', localStorage: envs }, { origin: app.url, localStorage: envs },
], ],
cookies: [], cookies: [],
}, },
......
...@@ -8,6 +8,8 @@ import type { SmartContractVerificationResponse } from 'types/api/contract'; ...@@ -8,6 +8,8 @@ import type { SmartContractVerificationResponse } from 'types/api/contract';
import type { TokenTransfer } from 'types/api/tokenTransfer'; import type { TokenTransfer } from 'types/api/tokenTransfer';
import type { Transaction } from 'types/api/transaction'; import type { Transaction } from 'types/api/transaction';
import * as app from 'playwright/utils/app';
type ReturnType = () => Promise<WebSocket>; type ReturnType = () => Promise<WebSocket>;
type Channel = [string, string, string]; type Channel = [string, string, string];
...@@ -16,11 +18,9 @@ export interface SocketServerFixture { ...@@ -16,11 +18,9 @@ export interface SocketServerFixture {
createSocket: ReturnType; createSocket: ReturnType;
} }
export const PORT = 3200;
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
export const createSocket: TestFixture<ReturnType, { page: Page}> = async({ page }, use) => { export const createSocket: TestFixture<ReturnType, { page: Page}> = async({ page }, use) => {
const socketServer = new WebSocketServer({ port: PORT }); const socketServer = new WebSocketServer({ port: app.socketPort });
const connectionPromise = new Promise<WebSocket>((resolve) => { const connectionPromise = new Promise<WebSocket>((resolve) => {
socketServer.on('connection', (socket: WebSocket) => { socketServer.on('connection', (socket: WebSocket) => {
......
export const url = `${ process.env.NEXT_PUBLIC_APP_PROTOCOL }://${ process.env.NEXT_PUBLIC_APP_HOST }:${ process.env.NEXT_PUBLIC_APP_PORT }`;
export const domain = process.env.NEXT_PUBLIC_APP_HOST;
export const socketPort = 3200;
...@@ -5,5 +5,7 @@ import { RESOURCES } from 'lib/api/resources'; ...@@ -5,5 +5,7 @@ import { RESOURCES } from 'lib/api/resources';
export default function buildApiUrl<R extends ResourceName>(resourceName: R, pathParams?: ResourcePathParams<R>) { export default function buildApiUrl<R extends ResourceName>(resourceName: R, pathParams?: ResourcePathParams<R>) {
const resource = RESOURCES[resourceName]; const resource = RESOURCES[resourceName];
return compile('/node-api/proxy' + resource.path)(pathParams); const defaultApi = 'https://' + process.env.NEXT_PUBLIC_API_HOST + ':' + process.env.NEXT_PUBLIC_API_PORT;
const origin = 'endpoint' in resource ? resource.endpoint + resource.basePath : defaultApi;
return origin + compile(resource.path)(pathParams);
} }
{
"compilerOptions": {
"target": "es6",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "react",
"incremental": true,
"baseUrl": ".",
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "decs.d.ts", "global.d.ts"],
"exclude": ["node_modules", "node_modules_linux"],
}
...@@ -8,14 +8,14 @@ import type { AddressBlocksValidatedResponse } from 'types/api/address'; ...@@ -8,14 +8,14 @@ import type { AddressBlocksValidatedResponse } from 'types/api/address';
import appConfig from 'configs/app/config'; import appConfig from 'configs/app/config';
import { getResourceKey } from 'lib/api/useApiQuery'; import { getResourceKey } from 'lib/api/useApiQuery';
import useQueryWithPages from 'lib/hooks/useQueryWithPages';
import useSocketChannel from 'lib/socket/useSocketChannel'; import useSocketChannel from 'lib/socket/useSocketChannel';
import useSocketMessage from 'lib/socket/useSocketMessage'; import useSocketMessage from 'lib/socket/useSocketMessage';
import { BLOCK } from 'stubs/block'; import { BLOCK } from 'stubs/block';
import { generateListStub } from 'stubs/utils'; import { generateListStub } from 'stubs/utils';
import ActionBar from 'ui/shared/ActionBar'; import ActionBar from 'ui/shared/ActionBar';
import DataListDisplay from 'ui/shared/DataListDisplay'; import DataListDisplay from 'ui/shared/DataListDisplay';
import Pagination from 'ui/shared/Pagination'; import Pagination from 'ui/shared/pagination/Pagination';
import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages';
import SocketAlert from 'ui/shared/SocketAlert'; import SocketAlert from 'ui/shared/SocketAlert';
import { default as Thead } from 'ui/shared/TheadSticky'; import { default as Thead } from 'ui/shared/TheadSticky';
...@@ -88,7 +88,7 @@ const AddressBlocksValidated = ({ scrollRef }: Props) => { ...@@ -88,7 +88,7 @@ const AddressBlocksValidated = ({ scrollRef }: Props) => {
{ socketAlert && <SocketAlert mb={ 6 }/> } { socketAlert && <SocketAlert mb={ 6 }/> }
<Hide below="lg" ssr={ false }> <Hide below="lg" ssr={ false }>
<Table variant="simple" size="sm"> <Table variant="simple" size="sm">
<Thead top={ query.isPaginationVisible ? 80 : 0 }> <Thead top={ query.pagination.isVisible ? 80 : 0 }>
<Tr> <Tr>
<Th width="17%">Block</Th> <Th width="17%">Block</Th>
<Th width="17%">Age</Th> <Th width="17%">Age</Th>
...@@ -122,7 +122,7 @@ const AddressBlocksValidated = ({ scrollRef }: Props) => { ...@@ -122,7 +122,7 @@ const AddressBlocksValidated = ({ scrollRef }: Props) => {
</> </>
) : null; ) : null;
const actionBar = query.isPaginationVisible ? ( const actionBar = query.pagination.isVisible ? (
<ActionBar mt={ -6 }> <ActionBar mt={ -6 }>
<Pagination ml="auto" { ...query.pagination }/> <Pagination ml="auto" { ...query.pagination }/>
</ActionBar> </ActionBar>
......
...@@ -6,12 +6,12 @@ import type { SocketMessage } from 'lib/socket/types'; ...@@ -6,12 +6,12 @@ import type { SocketMessage } from 'lib/socket/types';
import type { AddressCoinBalanceHistoryResponse } from 'types/api/address'; import type { AddressCoinBalanceHistoryResponse } from 'types/api/address';
import { getResourceKey } from 'lib/api/useApiQuery'; import { getResourceKey } from 'lib/api/useApiQuery';
import useQueryWithPages from 'lib/hooks/useQueryWithPages';
import getQueryParamString from 'lib/router/getQueryParamString'; import getQueryParamString from 'lib/router/getQueryParamString';
import useSocketChannel from 'lib/socket/useSocketChannel'; import useSocketChannel from 'lib/socket/useSocketChannel';
import useSocketMessage from 'lib/socket/useSocketMessage'; import useSocketMessage from 'lib/socket/useSocketMessage';
import { ADDRESS_COIN_BALANCE } from 'stubs/address'; import { ADDRESS_COIN_BALANCE } from 'stubs/address';
import { generateListStub } from 'stubs/utils'; import { generateListStub } from 'stubs/utils';
import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages';
import SocketAlert from 'ui/shared/SocketAlert'; import SocketAlert from 'ui/shared/SocketAlert';
import AddressCoinBalanceChart from './coinBalance/AddressCoinBalanceChart'; import AddressCoinBalanceChart from './coinBalance/AddressCoinBalanceChart';
......
...@@ -6,6 +6,7 @@ import type { CsvExportType } from 'types/client/address'; ...@@ -6,6 +6,7 @@ import type { CsvExportType } from 'types/client/address';
import appConfig from 'configs/app/config'; import appConfig from 'configs/app/config';
import svgFileIcon from 'icons/files/csv.svg'; import svgFileIcon from 'icons/files/csv.svg';
import useIsInitialLoading from 'lib/hooks/useIsInitialLoading';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
import LinkInternal from 'ui/shared/LinkInternal'; import LinkInternal from 'ui/shared/LinkInternal';
...@@ -18,12 +19,13 @@ interface Props { ...@@ -18,12 +19,13 @@ interface Props {
const AddressCsvExportLink = ({ className, address, type, isLoading }: Props) => { const AddressCsvExportLink = ({ className, address, type, isLoading }: Props) => {
const isMobile = useIsMobile(); const isMobile = useIsMobile();
const isInitialLoading = useIsInitialLoading(isLoading);
if (!appConfig.reCaptcha.siteKey) { if (!appConfig.reCaptcha.siteKey) {
return null; return null;
} }
if (isLoading) { if (isInitialLoading) {
return ( return (
<Flex className={ className } flexShrink={ 0 } alignItems="center"> <Flex className={ className } flexShrink={ 0 } alignItems="center">
<Skeleton boxSize={{ base: '32px', lg: 6 }} borderRadius="base"/> <Skeleton boxSize={{ base: '32px', lg: 6 }} borderRadius="base"/>
......
...@@ -6,7 +6,6 @@ import type { AddressFromToFilter } from 'types/api/address'; ...@@ -6,7 +6,6 @@ import type { AddressFromToFilter } from 'types/api/address';
import { AddressFromToFilterValues } from 'types/api/address'; import { AddressFromToFilterValues } from 'types/api/address';
import getFilterValueFromQuery from 'lib/getFilterValueFromQuery'; import getFilterValueFromQuery from 'lib/getFilterValueFromQuery';
import useQueryWithPages from 'lib/hooks/useQueryWithPages';
import { apos } from 'lib/html-entities'; import { apos } from 'lib/html-entities';
import getQueryParamString from 'lib/router/getQueryParamString'; import getQueryParamString from 'lib/router/getQueryParamString';
import { INTERNAL_TX } from 'stubs/internalTx'; import { INTERNAL_TX } from 'stubs/internalTx';
...@@ -14,7 +13,8 @@ import { generateListStub } from 'stubs/utils'; ...@@ -14,7 +13,8 @@ import { generateListStub } from 'stubs/utils';
import AddressIntTxsTable from 'ui/address/internals/AddressIntTxsTable'; import AddressIntTxsTable from 'ui/address/internals/AddressIntTxsTable';
import ActionBar from 'ui/shared/ActionBar'; import ActionBar from 'ui/shared/ActionBar';
import DataListDisplay from 'ui/shared/DataListDisplay'; import DataListDisplay from 'ui/shared/DataListDisplay';
import Pagination from 'ui/shared/Pagination'; import Pagination from 'ui/shared/pagination/Pagination';
import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages';
import AddressCsvExportLink from './AddressCsvExportLink'; import AddressCsvExportLink from './AddressCsvExportLink';
import AddressTxsFilter from './AddressTxsFilter'; import AddressTxsFilter from './AddressTxsFilter';
...@@ -28,7 +28,7 @@ const AddressInternalTxs = ({ scrollRef }: {scrollRef?: React.RefObject<HTMLDivE ...@@ -28,7 +28,7 @@ const AddressInternalTxs = ({ scrollRef }: {scrollRef?: React.RefObject<HTMLDivE
const hash = getQueryParamString(router.query.hash); const hash = getQueryParamString(router.query.hash);
const { data, isPlaceholderData, isError, pagination, onFilterChange, isPaginationVisible } = useQueryWithPages({ const { data, isPlaceholderData, isError, pagination, onFilterChange } = useQueryWithPages({
resourceName: 'address_internal_txs', resourceName: 'address_internal_txs',
pathParams: { hash }, pathParams: { hash },
filters: { filter: filterValue }, filters: { filter: filterValue },
...@@ -75,7 +75,7 @@ const AddressInternalTxs = ({ scrollRef }: {scrollRef?: React.RefObject<HTMLDivE ...@@ -75,7 +75,7 @@ const AddressInternalTxs = ({ scrollRef }: {scrollRef?: React.RefObject<HTMLDivE
isLoading={ pagination.isLoading } isLoading={ pagination.isLoading }
/> />
<AddressCsvExportLink address={ hash } isLoading={ pagination.isLoading } type="internal-transactions" ml={{ base: 2, lg: 'auto' }}/> <AddressCsvExportLink address={ hash } isLoading={ pagination.isLoading } type="internal-transactions" ml={{ base: 2, lg: 'auto' }}/>
{ isPaginationVisible && <Pagination ml={{ base: 'auto', lg: 8 }} { ...pagination }/> } <Pagination ml={{ base: 'auto', lg: 8 }} { ...pagination }/>
</ActionBar> </ActionBar>
); );
......
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import React from 'react'; import React from 'react';
import useQueryWithPages from 'lib/hooks/useQueryWithPages';
import getQueryParamString from 'lib/router/getQueryParamString'; import getQueryParamString from 'lib/router/getQueryParamString';
import { LOG } from 'stubs/log'; import { LOG } from 'stubs/log';
import { generateListStub } from 'stubs/utils'; import { generateListStub } from 'stubs/utils';
import ActionBar from 'ui/shared/ActionBar'; import ActionBar from 'ui/shared/ActionBar';
import DataListDisplay from 'ui/shared/DataListDisplay'; import DataListDisplay from 'ui/shared/DataListDisplay';
import LogItem from 'ui/shared/logs/LogItem'; import LogItem from 'ui/shared/logs/LogItem';
import Pagination from 'ui/shared/Pagination'; import Pagination from 'ui/shared/pagination/Pagination';
import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages';
import AddressCsvExportLink from './AddressCsvExportLink'; import AddressCsvExportLink from './AddressCsvExportLink';
......
...@@ -14,7 +14,6 @@ import { getResourceKey } from 'lib/api/useApiQuery'; ...@@ -14,7 +14,6 @@ import { getResourceKey } from 'lib/api/useApiQuery';
import getFilterValueFromQuery from 'lib/getFilterValueFromQuery'; import getFilterValueFromQuery from 'lib/getFilterValueFromQuery';
import getFilterValuesFromQuery from 'lib/getFilterValuesFromQuery'; import getFilterValuesFromQuery from 'lib/getFilterValuesFromQuery';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
import useQueryWithPages from 'lib/hooks/useQueryWithPages';
import { apos } from 'lib/html-entities'; import { apos } from 'lib/html-entities';
import getQueryParamString from 'lib/router/getQueryParamString'; import getQueryParamString from 'lib/router/getQueryParamString';
import useSocketChannel from 'lib/socket/useSocketChannel'; import useSocketChannel from 'lib/socket/useSocketChannel';
...@@ -24,7 +23,8 @@ import { getTokenTransfersStub } from 'stubs/token'; ...@@ -24,7 +23,8 @@ import { getTokenTransfersStub } from 'stubs/token';
import ActionBar from 'ui/shared/ActionBar'; import ActionBar from 'ui/shared/ActionBar';
import DataListDisplay from 'ui/shared/DataListDisplay'; import DataListDisplay from 'ui/shared/DataListDisplay';
import HashStringShorten from 'ui/shared/HashStringShorten'; import HashStringShorten from 'ui/shared/HashStringShorten';
import Pagination from 'ui/shared/Pagination'; import Pagination from 'ui/shared/pagination/Pagination';
import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages';
import * as SocketNewItemsNotice from 'ui/shared/SocketNewItemsNotice'; import * as SocketNewItemsNotice from 'ui/shared/SocketNewItemsNotice';
import TokenLogo from 'ui/shared/TokenLogo'; import TokenLogo from 'ui/shared/TokenLogo';
import TokenTransferFilter from 'ui/shared/TokenTransfer/TokenTransferFilter'; import TokenTransferFilter from 'ui/shared/TokenTransfer/TokenTransferFilter';
...@@ -88,7 +88,7 @@ const AddressTokenTransfers = ({ scrollRef, overloadCount = OVERLOAD_COUNT }: Pr ...@@ -88,7 +88,7 @@ const AddressTokenTransfers = ({ scrollRef, overloadCount = OVERLOAD_COUNT }: Pr
}, },
); );
const { isError, isPlaceholderData, data, pagination, onFilterChange, isPaginationVisible } = useQueryWithPages({ const { isError, isPlaceholderData, data, pagination, onFilterChange } = useQueryWithPages({
resourceName: 'address_token_transfers', resourceName: 'address_token_transfers',
pathParams: { hash: currentAddress }, pathParams: { hash: currentAddress },
filters: tokenFilter ? { token: tokenFilter } : filters, filters: tokenFilter ? { token: tokenFilter } : filters,
...@@ -277,7 +277,7 @@ const AddressTokenTransfers = ({ scrollRef, overloadCount = OVERLOAD_COUNT }: Pr ...@@ -277,7 +277,7 @@ const AddressTokenTransfers = ({ scrollRef, overloadCount = OVERLOAD_COUNT }: Pr
isLoading={ isPlaceholderData } isLoading={ isPlaceholderData }
/> />
) } ) }
{ isPaginationVisible && <Pagination ml={{ base: 'auto', lg: 8 }} { ...pagination }/> } <Pagination ml={{ base: 'auto', lg: 8 }} { ...pagination }/>
</ActionBar> </ActionBar>
) } ) }
</> </>
......
...@@ -3,14 +3,14 @@ import { useRouter } from 'next/router'; ...@@ -3,14 +3,14 @@ import { useRouter } from 'next/router';
import React from 'react'; import React from 'react';
import type { TokenType } from 'types/api/token'; import type { TokenType } from 'types/api/token';
import type { PaginationParams } from 'ui/shared/pagination/types';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
import useQueryWithPages from 'lib/hooks/useQueryWithPages';
import { ADDRESS_TOKEN_BALANCE_ERC_1155, ADDRESS_TOKEN_BALANCE_ERC_20, ADDRESS_TOKEN_BALANCE_ERC_721 } from 'stubs/address'; import { ADDRESS_TOKEN_BALANCE_ERC_1155, ADDRESS_TOKEN_BALANCE_ERC_20, ADDRESS_TOKEN_BALANCE_ERC_721 } from 'stubs/address';
import { generateListStub } from 'stubs/utils'; import { generateListStub } from 'stubs/utils';
import { tokenTabsByType } from 'ui/pages/Address'; import { tokenTabsByType } from 'ui/pages/Address';
import Pagination from 'ui/shared/Pagination'; import Pagination from 'ui/shared/pagination/Pagination';
import type { Props as PaginationProps } from 'ui/shared/Pagination'; import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages';
import RoutedTabs from 'ui/shared/Tabs/RoutedTabs'; import RoutedTabs from 'ui/shared/Tabs/RoutedTabs';
import ERC1155Tokens from './tokens/ERC1155Tokens'; import ERC1155Tokens from './tokens/ERC1155Tokens';
...@@ -81,17 +81,13 @@ const AddressTokens = () => { ...@@ -81,17 +81,13 @@ const AddressTokens = () => {
{ id: tokenTabsByType['ERC-1155'], title: 'ERC-1155', component: <ERC1155Tokens tokensQuery={ erc1155Query }/> }, { id: tokenTabsByType['ERC-1155'], title: 'ERC-1155', component: <ERC1155Tokens tokensQuery={ erc1155Query }/> },
]; ];
let isPaginationVisible; let pagination: PaginationParams | undefined;
let pagination: PaginationProps | undefined;
if (tab === tokenTabsByType['ERC-1155']) { if (tab === tokenTabsByType['ERC-1155']) {
isPaginationVisible = erc1155Query.isPaginationVisible;
pagination = erc1155Query.pagination; pagination = erc1155Query.pagination;
} else if (tab === tokenTabsByType['ERC-721']) { } else if (tab === tokenTabsByType['ERC-721']) {
isPaginationVisible = erc721Query.isPaginationVisible;
pagination = erc721Query.pagination; pagination = erc721Query.pagination;
} else { } else {
isPaginationVisible = erc20Query.isPaginationVisible;
pagination = erc20Query.pagination; pagination = erc20Query.pagination;
} }
...@@ -106,7 +102,7 @@ const AddressTokens = () => { ...@@ -106,7 +102,7 @@ const AddressTokens = () => {
colorScheme="gray" colorScheme="gray"
size="sm" size="sm"
tabListProps={ isMobile ? TAB_LIST_PROPS_MOBILE : TAB_LIST_PROPS } tabListProps={ isMobile ? TAB_LIST_PROPS_MOBILE : TAB_LIST_PROPS }
rightSlot={ isPaginationVisible && !isMobile ? <Pagination { ...pagination }/> : null } rightSlot={ pagination.isVisible && !isMobile ? <Pagination { ...pagination }/> : null }
stickyEnabled={ !isMobile } stickyEnabled={ !isMobile }
/> />
</> </>
......
...@@ -10,14 +10,14 @@ import type { Transaction } from 'types/api/transaction'; ...@@ -10,14 +10,14 @@ import type { Transaction } from 'types/api/transaction';
import { getResourceKey } from 'lib/api/useApiQuery'; import { getResourceKey } from 'lib/api/useApiQuery';
import getFilterValueFromQuery from 'lib/getFilterValueFromQuery'; import getFilterValueFromQuery from 'lib/getFilterValueFromQuery';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
import useQueryWithPages from 'lib/hooks/useQueryWithPages';
import getQueryParamString from 'lib/router/getQueryParamString'; import getQueryParamString from 'lib/router/getQueryParamString';
import useSocketChannel from 'lib/socket/useSocketChannel'; import useSocketChannel from 'lib/socket/useSocketChannel';
import useSocketMessage from 'lib/socket/useSocketMessage'; import useSocketMessage from 'lib/socket/useSocketMessage';
import { TX } from 'stubs/tx'; import { TX } from 'stubs/tx';
import { generateListStub } from 'stubs/utils'; import { generateListStub } from 'stubs/utils';
import ActionBar from 'ui/shared/ActionBar'; import ActionBar from 'ui/shared/ActionBar';
import Pagination from 'ui/shared/Pagination'; import Pagination from 'ui/shared/pagination/Pagination';
import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages';
import TxsContent from 'ui/txs/TxsContent'; import TxsContent from 'ui/txs/TxsContent';
import AddressCsvExportLink from './AddressCsvExportLink'; import AddressCsvExportLink from './AddressCsvExportLink';
...@@ -172,7 +172,7 @@ const AddressTxs = ({ scrollRef, overloadCount = OVERLOAD_COUNT }: Props) => { ...@@ -172,7 +172,7 @@ const AddressTxs = ({ scrollRef, overloadCount = OVERLOAD_COUNT }: Props) => {
isLoading={ addressTxsQuery.pagination.isLoading } isLoading={ addressTxsQuery.pagination.isLoading }
/> />
) } ) }
{ addressTxsQuery.isPaginationVisible && <Pagination { ...addressTxsQuery.pagination } ml={ 8 }/> } <Pagination { ...addressTxsQuery.pagination } ml={ 8 }/>
</ActionBar> </ActionBar>
) } ) }
<TxsContent <TxsContent
......
...@@ -10,6 +10,7 @@ import React from 'react'; ...@@ -10,6 +10,7 @@ import React from 'react';
import type { AddressFromToFilter } from 'types/api/address'; import type { AddressFromToFilter } from 'types/api/address';
import useIsInitialLoading from 'lib/hooks/useIsInitialLoading';
import FilterButton from 'ui/shared/filters/FilterButton'; import FilterButton from 'ui/shared/filters/FilterButton';
interface Props { interface Props {
...@@ -21,13 +22,14 @@ interface Props { ...@@ -21,13 +22,14 @@ interface Props {
const AddressTxsFilter = ({ onFilterChange, defaultFilter, isActive, isLoading }: Props) => { const AddressTxsFilter = ({ onFilterChange, defaultFilter, isActive, isLoading }: Props) => {
const { isOpen, onToggle } = useDisclosure(); const { isOpen, onToggle } = useDisclosure();
const isInitialLoading = useIsInitialLoading(isLoading);
return ( return (
<Menu> <Menu>
<MenuButton> <MenuButton>
<FilterButton <FilterButton
isActive={ isOpen || isActive } isActive={ isOpen || isActive }
isLoading={ isLoading } isLoading={ isInitialLoading }
onClick={ onToggle } onClick={ onToggle }
as="div" as="div"
/> />
......
...@@ -2,13 +2,13 @@ import { Show, Hide } from '@chakra-ui/react'; ...@@ -2,13 +2,13 @@ import { Show, Hide } from '@chakra-ui/react';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import React from 'react'; import React from 'react';
import useQueryWithPages from 'lib/hooks/useQueryWithPages';
import getQueryParamString from 'lib/router/getQueryParamString'; import getQueryParamString from 'lib/router/getQueryParamString';
import { generateListStub } from 'stubs/utils'; import { generateListStub } from 'stubs/utils';
import { WITHDRAWAL } from 'stubs/withdrawals'; import { WITHDRAWAL } from 'stubs/withdrawals';
import ActionBar from 'ui/shared/ActionBar'; import ActionBar from 'ui/shared/ActionBar';
import DataListDisplay from 'ui/shared/DataListDisplay'; import DataListDisplay from 'ui/shared/DataListDisplay';
import Pagination from 'ui/shared/Pagination'; import Pagination from 'ui/shared/pagination/Pagination';
import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages';
import WithdrawalsListItem from 'ui/withdrawals/WithdrawalsListItem'; import WithdrawalsListItem from 'ui/withdrawals/WithdrawalsListItem';
import WithdrawalsTable from 'ui/withdrawals/WithdrawalsTable'; import WithdrawalsTable from 'ui/withdrawals/WithdrawalsTable';
...@@ -17,7 +17,7 @@ const AddressWithdrawals = ({ scrollRef }: {scrollRef?: React.RefObject<HTMLDivE ...@@ -17,7 +17,7 @@ const AddressWithdrawals = ({ scrollRef }: {scrollRef?: React.RefObject<HTMLDivE
const hash = getQueryParamString(router.query.hash); const hash = getQueryParamString(router.query.hash);
const { data, isPlaceholderData, isError, pagination, isPaginationVisible } = useQueryWithPages({ const { data, isPlaceholderData, isError, pagination } = useQueryWithPages({
resourceName: 'address_withdrawals', resourceName: 'address_withdrawals',
pathParams: { hash }, pathParams: { hash },
scrollRef, scrollRef,
...@@ -41,12 +41,12 @@ const AddressWithdrawals = ({ scrollRef }: {scrollRef?: React.RefObject<HTMLDivE ...@@ -41,12 +41,12 @@ const AddressWithdrawals = ({ scrollRef }: {scrollRef?: React.RefObject<HTMLDivE
)) } )) }
</Show> </Show>
<Hide below="lg" ssr={ false }> <Hide below="lg" ssr={ false }>
<WithdrawalsTable items={ data.items } view="address" top={ isPaginationVisible ? 80 : 0 } isLoading={ isPlaceholderData }/> <WithdrawalsTable items={ data.items } view="address" top={ pagination.isVisible ? 80 : 0 } isLoading={ isPlaceholderData }/>
</Hide> </Hide>
</> </>
) : null ; ) : null ;
const actionBar = isPaginationVisible ? ( const actionBar = pagination.isVisible ? (
<ActionBar mt={ -6 }> <ActionBar mt={ -6 }>
<Pagination ml="auto" { ...pagination }/> <Pagination ml="auto" { ...pagination }/>
</ActionBar> </ActionBar>
......
...@@ -3,12 +3,12 @@ import type { UseQueryResult } from '@tanstack/react-query'; ...@@ -3,12 +3,12 @@ import type { UseQueryResult } from '@tanstack/react-query';
import React from 'react'; import React from 'react';
import type { AddressCoinBalanceHistoryResponse } from 'types/api/address'; import type { AddressCoinBalanceHistoryResponse } from 'types/api/address';
import type { PaginationParams } from 'ui/shared/pagination/types';
import appConfig from 'configs/app/config'; import appConfig from 'configs/app/config';
import ActionBar from 'ui/shared/ActionBar'; import ActionBar from 'ui/shared/ActionBar';
import DataListDisplay from 'ui/shared/DataListDisplay'; import DataListDisplay from 'ui/shared/DataListDisplay';
import type { Props as PaginationProps } from 'ui/shared/Pagination'; import Pagination from 'ui/shared/pagination/Pagination';
import Pagination from 'ui/shared/Pagination';
import { default as Thead } from 'ui/shared/TheadSticky'; import { default as Thead } from 'ui/shared/TheadSticky';
import AddressCoinBalanceListItem from './AddressCoinBalanceListItem'; import AddressCoinBalanceListItem from './AddressCoinBalanceListItem';
...@@ -16,8 +16,7 @@ import AddressCoinBalanceTableItem from './AddressCoinBalanceTableItem'; ...@@ -16,8 +16,7 @@ import AddressCoinBalanceTableItem from './AddressCoinBalanceTableItem';
interface Props { interface Props {
query: UseQueryResult<AddressCoinBalanceHistoryResponse> & { query: UseQueryResult<AddressCoinBalanceHistoryResponse> & {
pagination: PaginationProps; pagination: PaginationParams;
isPaginationVisible: boolean;
}; };
} }
...@@ -27,7 +26,7 @@ const AddressCoinBalanceHistory = ({ query }: Props) => { ...@@ -27,7 +26,7 @@ const AddressCoinBalanceHistory = ({ query }: Props) => {
<> <>
<Hide below="lg" ssr={ false }> <Hide below="lg" ssr={ false }>
<Table variant="simple" size="sm"> <Table variant="simple" size="sm">
<Thead top={ query.isPaginationVisible ? 80 : 0 }> <Thead top={ query.pagination.isVisible ? 80 : 0 }>
<Tr> <Tr>
<Th width="20%">Block</Th> <Th width="20%">Block</Th>
<Th width="20%">Txn</Th> <Th width="20%">Txn</Th>
...@@ -61,7 +60,7 @@ const AddressCoinBalanceHistory = ({ query }: Props) => { ...@@ -61,7 +60,7 @@ const AddressCoinBalanceHistory = ({ query }: Props) => {
</> </>
) : null; ) : null;
const actionBar = query.isPaginationVisible ? ( const actionBar = query.pagination.isVisible ? (
<ActionBar mt={ -6 }> <ActionBar mt={ -6 }>
<Pagination ml="auto" { ...query.pagination }/> <Pagination ml="auto" { ...query.pagination }/>
</ActionBar> </ActionBar>
......
import { Grid } from '@chakra-ui/react'; import { Grid } from '@chakra-ui/react';
import type { UseQueryResult } from '@tanstack/react-query';
import React from 'react'; import React from 'react';
import type { AddressTokensResponse } from 'types/api/address';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
import ActionBar from 'ui/shared/ActionBar'; import ActionBar from 'ui/shared/ActionBar';
import DataListDisplay from 'ui/shared/DataListDisplay'; import DataListDisplay from 'ui/shared/DataListDisplay';
import type { Props as PaginationProps } from 'ui/shared/Pagination'; import Pagination from 'ui/shared/pagination/Pagination';
import Pagination from 'ui/shared/Pagination'; import type { QueryWithPagesResult } from 'ui/shared/pagination/useQueryWithPages';
import NFTItem from './NFTItem'; import NFTItem from './NFTItem';
type Props = { type Props = {
tokensQuery: UseQueryResult<AddressTokensResponse> & { tokensQuery: QueryWithPagesResult<'address_tokens'>;
pagination: PaginationProps;
isPaginationVisible: boolean;
};
} }
const ERC1155Tokens = ({ tokensQuery }: Props) => { const ERC1155Tokens = ({ tokensQuery }: Props) => {
const isMobile = useIsMobile(); const isMobile = useIsMobile();
const { isError, isPlaceholderData, data, pagination, isPaginationVisible } = tokensQuery; const { isError, isPlaceholderData, data, pagination } = tokensQuery;
const actionBar = isMobile && isPaginationVisible && ( const actionBar = isMobile && pagination.isVisible && (
<ActionBar mt={ -6 }> <ActionBar mt={ -6 }>
<Pagination ml="auto" { ...pagination }/> <Pagination ml="auto" { ...pagination }/>
</ActionBar> </ActionBar>
......
import { Show, Hide } from '@chakra-ui/react'; import { Show, Hide } from '@chakra-ui/react';
import type { UseQueryResult } from '@tanstack/react-query';
import React from 'react'; import React from 'react';
import type { AddressTokensResponse } from 'types/api/address';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
import ActionBar from 'ui/shared/ActionBar'; import ActionBar from 'ui/shared/ActionBar';
import DataListDisplay from 'ui/shared/DataListDisplay'; import DataListDisplay from 'ui/shared/DataListDisplay';
import Pagination from 'ui/shared/Pagination'; import Pagination from 'ui/shared/pagination/Pagination';
import type { Props as PaginationProps } from 'ui/shared/Pagination'; import type { QueryWithPagesResult } from 'ui/shared/pagination/useQueryWithPages';
import ERC20TokensListItem from './ERC20TokensListItem'; import ERC20TokensListItem from './ERC20TokensListItem';
import ERC20TokensTable from './ERC20TokensTable'; import ERC20TokensTable from './ERC20TokensTable';
type Props = { type Props = {
tokensQuery: UseQueryResult<AddressTokensResponse> & { tokensQuery: QueryWithPagesResult<'address_tokens'>;
pagination: PaginationProps;
isPaginationVisible: boolean;
};
} }
const ERC20Tokens = ({ tokensQuery }: Props) => { const ERC20Tokens = ({ tokensQuery }: Props) => {
const isMobile = useIsMobile(); const isMobile = useIsMobile();
const { isError, isPlaceholderData, data, pagination, isPaginationVisible } = tokensQuery; const { isError, isPlaceholderData, data, pagination } = tokensQuery;
const actionBar = isMobile && isPaginationVisible && ( const actionBar = isMobile && pagination.isVisible && (
<ActionBar mt={ -6 }> <ActionBar mt={ -6 }>
<Pagination ml="auto" { ...pagination }/> <Pagination ml="auto" { ...pagination }/>
</ActionBar> </ActionBar>
...@@ -33,7 +27,7 @@ const ERC20Tokens = ({ tokensQuery }: Props) => { ...@@ -33,7 +27,7 @@ const ERC20Tokens = ({ tokensQuery }: Props) => {
const content = data?.items ? ( const content = data?.items ? (
<> <>
<Hide below="lg" ssr={ false }><ERC20TokensTable data={ data.items } top={ isPaginationVisible ? 72 : 0 } isLoading={ isPlaceholderData }/></Hide> <Hide below="lg" ssr={ false }><ERC20TokensTable data={ data.items } top={ pagination.isVisible ? 72 : 0 } isLoading={ isPlaceholderData }/></Hide>
<Show below="lg" ssr={ false }>{ data.items.map((item, index) => ( <Show below="lg" ssr={ false }>{ data.items.map((item, index) => (
<ERC20TokensListItem <ERC20TokensListItem
key={ item.token.address + (isPlaceholderData ? index : '') } key={ item.token.address + (isPlaceholderData ? index : '') }
......
import { Show, Hide } from '@chakra-ui/react'; import { Show, Hide } from '@chakra-ui/react';
import type { UseQueryResult } from '@tanstack/react-query';
import React from 'react'; import React from 'react';
import type { AddressTokensResponse } from 'types/api/address';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
import ActionBar from 'ui/shared/ActionBar'; import ActionBar from 'ui/shared/ActionBar';
import DataListDisplay from 'ui/shared/DataListDisplay'; import DataListDisplay from 'ui/shared/DataListDisplay';
import Pagination from 'ui/shared/Pagination'; import Pagination from 'ui/shared/pagination/Pagination';
import type { Props as PaginationProps } from 'ui/shared/Pagination'; import type { QueryWithPagesResult } from 'ui/shared/pagination/useQueryWithPages';
import ERC721TokensListItem from './ERC721TokensListItem'; import ERC721TokensListItem from './ERC721TokensListItem';
import ERC721TokensTable from './ERC721TokensTable'; import ERC721TokensTable from './ERC721TokensTable';
type Props = { type Props = {
tokensQuery: UseQueryResult<AddressTokensResponse> & { tokensQuery: QueryWithPagesResult<'address_tokens'>;
pagination: PaginationProps;
isPaginationVisible: boolean;
};
} }
const ERC721Tokens = ({ tokensQuery }: Props) => { const ERC721Tokens = ({ tokensQuery }: Props) => {
const isMobile = useIsMobile(); const isMobile = useIsMobile();
const { isError, isPlaceholderData, data, pagination, isPaginationVisible } = tokensQuery; const { isError, isPlaceholderData, data, pagination } = tokensQuery;
const actionBar = isMobile && isPaginationVisible && ( const actionBar = isMobile && pagination.isVisible && (
<ActionBar mt={ -6 }> <ActionBar mt={ -6 }>
<Pagination ml="auto" { ...pagination }/> <Pagination ml="auto" { ...pagination }/>
</ActionBar> </ActionBar>
...@@ -33,7 +27,7 @@ const ERC721Tokens = ({ tokensQuery }: Props) => { ...@@ -33,7 +27,7 @@ const ERC721Tokens = ({ tokensQuery }: Props) => {
const content = data?.items ? ( const content = data?.items ? (
<> <>
<Hide below="lg" ssr={ false }><ERC721TokensTable data={ data.items } isLoading={ isPlaceholderData } top={ isPaginationVisible ? 72 : 0 }/></Hide> <Hide below="lg" ssr={ false }><ERC721TokensTable data={ data.items } isLoading={ isPlaceholderData } top={ pagination.isVisible ? 72 : 0 }/></Hide>
<Show below="lg" ssr={ false }>{ data.items.map((item, index) => ( <Show below="lg" ssr={ false }>{ data.items.map((item, index) => (
<ERC721TokensListItem <ERC721TokensListItem
key={ item.token.address + (isPlaceholderData ? index : '') } key={ item.token.address + (isPlaceholderData ? index : '') }
......
import { Show, Hide } from '@chakra-ui/react'; import { Show, Hide } from '@chakra-ui/react';
import type { UseQueryResult } from '@tanstack/react-query';
import React from 'react'; import React from 'react';
import type { BlockWithdrawalsResponse } from 'types/api/block';
import DataListDisplay from 'ui/shared/DataListDisplay'; import DataListDisplay from 'ui/shared/DataListDisplay';
import type { Props as PaginationProps } from 'ui/shared/Pagination'; import type { QueryWithPagesResult } from 'ui/shared/pagination/useQueryWithPages';
import WithdrawalsListItem from 'ui/withdrawals/WithdrawalsListItem'; import WithdrawalsListItem from 'ui/withdrawals/WithdrawalsListItem';
import WithdrawalsTable from 'ui/withdrawals/WithdrawalsTable'; import WithdrawalsTable from 'ui/withdrawals/WithdrawalsTable';
type QueryResult = UseQueryResult<BlockWithdrawalsResponse> & {
pagination: PaginationProps;
isPaginationVisible: boolean;
};
type Props = { type Props = {
blockWithdrawalsQuery: QueryResult; blockWithdrawalsQuery: QueryWithPagesResult<'block_withdrawals'>;
} }
const BlockWithdrawals = ({ blockWithdrawalsQuery }: Props) => { const BlockWithdrawals = ({ blockWithdrawalsQuery }: Props) => {
...@@ -35,7 +27,7 @@ const BlockWithdrawals = ({ blockWithdrawalsQuery }: Props) => { ...@@ -35,7 +27,7 @@ const BlockWithdrawals = ({ blockWithdrawalsQuery }: Props) => {
<WithdrawalsTable <WithdrawalsTable
items={ blockWithdrawalsQuery.data.items } items={ blockWithdrawalsQuery.data.items }
isLoading={ blockWithdrawalsQuery.isPlaceholderData } isLoading={ blockWithdrawalsQuery.isPlaceholderData }
top={ blockWithdrawalsQuery.isPaginationVisible ? 80 : 0 } top={ blockWithdrawalsQuery.pagination.isVisible ? 80 : 0 }
view="block" view="block"
/> />
</Hide> </Hide>
......
import { Alert, Box } from '@chakra-ui/react'; import { Alert, Box } from '@chakra-ui/react';
import type { UseQueryResult } from '@tanstack/react-query';
import { useQueryClient } from '@tanstack/react-query'; import { useQueryClient } from '@tanstack/react-query';
import React from 'react'; import React from 'react';
...@@ -14,17 +13,12 @@ import BlocksList from 'ui/blocks/BlocksList'; ...@@ -14,17 +13,12 @@ import BlocksList from 'ui/blocks/BlocksList';
import BlocksTable from 'ui/blocks/BlocksTable'; import BlocksTable from 'ui/blocks/BlocksTable';
import ActionBar from 'ui/shared/ActionBar'; import ActionBar from 'ui/shared/ActionBar';
import DataListDisplay from 'ui/shared/DataListDisplay'; import DataListDisplay from 'ui/shared/DataListDisplay';
import Pagination from 'ui/shared/Pagination'; import Pagination from 'ui/shared/pagination/Pagination';
import type { Props as PaginationProps } from 'ui/shared/Pagination'; import type { QueryWithPagesResult } from 'ui/shared/pagination/useQueryWithPages';
type QueryResult = UseQueryResult<BlocksResponse> & {
pagination: PaginationProps;
isPaginationVisible: boolean;
};
interface Props { interface Props {
type?: BlockType; type?: BlockType;
query: QueryResult; query: QueryWithPagesResult<'blocks'>;
} }
const BlocksContent = ({ type, query }: Props) => { const BlocksContent = ({ type, query }: Props) => {
...@@ -81,12 +75,17 @@ const BlocksContent = ({ type, query }: Props) => { ...@@ -81,12 +75,17 @@ const BlocksContent = ({ type, query }: Props) => {
<BlocksList data={ query.data.items } isLoading={ query.isPlaceholderData } page={ query.pagination.page }/> <BlocksList data={ query.data.items } isLoading={ query.isPlaceholderData } page={ query.pagination.page }/>
</Box> </Box>
<Box display={{ base: 'none', lg: 'block' }}> <Box display={{ base: 'none', lg: 'block' }}>
<BlocksTable data={ query.data.items } top={ query.isPaginationVisible ? 80 : 0 } page={ query.pagination.page } isLoading={ query.isPlaceholderData }/> <BlocksTable
data={ query.data.items }
top={ query.pagination.isVisible ? 80 : 0 }
page={ query.pagination.page }
isLoading={ query.isPlaceholderData }
/>
</Box> </Box>
</> </>
) : null; ) : null;
const actionBar = isMobile && query.isPaginationVisible ? ( const actionBar = isMobile && query.pagination.isVisible ? (
<ActionBar mt={ -6 }> <ActionBar mt={ -6 }>
<Pagination ml="auto" { ...query.pagination }/> <Pagination ml="auto" { ...query.pagination }/>
</ActionBar> </ActionBar>
......
import { Flex, Box, Text, Skeleton } from '@chakra-ui/react'; import { Flex, Box, Text, Skeleton } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { PaginationParams } from 'ui/shared/pagination/types';
import useApiQuery from 'lib/api/useApiQuery'; import useApiQuery from 'lib/api/useApiQuery';
import { nbsp } from 'lib/html-entities'; import { nbsp } from 'lib/html-entities';
import { HOMEPAGE_STATS } from 'stubs/stats'; import { HOMEPAGE_STATS } from 'stubs/stats';
import type { Props as PaginationProps } from 'ui/shared/Pagination'; import Pagination from 'ui/shared/pagination/Pagination';
import Pagination from 'ui/shared/Pagination';
interface Props { interface Props {
pagination: PaginationProps; pagination: PaginationParams;
isPaginationVisible: boolean;
} }
const BlocksTabSlot = ({ pagination, isPaginationVisible }: Props) => { const BlocksTabSlot = ({ pagination }: Props) => {
const statsQuery = useApiQuery('homepage_stats', { const statsQuery = useApiQuery('homepage_stats', {
queryOptions: { queryOptions: {
placeholderData: HOMEPAGE_STATS, placeholderData: HOMEPAGE_STATS,
...@@ -31,7 +31,7 @@ const BlocksTabSlot = ({ pagination, isPaginationVisible }: Props) => { ...@@ -31,7 +31,7 @@ const BlocksTabSlot = ({ pagination, isPaginationVisible }: Props) => {
</Skeleton> </Skeleton>
</Box> </Box>
) } ) }
{ isPaginationVisible && <Pagination my={ 1 } { ...pagination }/> } <Pagination my={ 1 } { ...pagination }/>
</Flex> </Flex>
); );
}; };
......
...@@ -6,7 +6,7 @@ import type { SocketMessage } from 'lib/socket/types'; ...@@ -6,7 +6,7 @@ import type { SocketMessage } from 'lib/socket/types';
import type { IndexingStatus } from 'types/api/indexingStatus'; import type { IndexingStatus } from 'types/api/indexingStatus';
import useApiQuery, { getResourceKey } from 'lib/api/useApiQuery'; import useApiQuery, { getResourceKey } from 'lib/api/useApiQuery';
import { useAppContext } from 'lib/appContext'; import { useAppContext } from 'lib/contexts/app';
import * as cookies from 'lib/cookies'; import * as cookies from 'lib/cookies';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
import { nbsp, ndash } from 'lib/html-entities'; import { nbsp, ndash } from 'lib/html-entities';
......
import { Hide, Show } from '@chakra-ui/react'; import { Hide, Show } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import useQueryWithPages from 'lib/hooks/useQueryWithPages';
import { TOP_ADDRESS } from 'stubs/address'; import { TOP_ADDRESS } from 'stubs/address';
import { generateListStub } from 'stubs/utils'; import { generateListStub } from 'stubs/utils';
import AddressesListItem from 'ui/addresses/AddressesListItem'; import AddressesListItem from 'ui/addresses/AddressesListItem';
...@@ -9,12 +8,13 @@ import AddressesTable from 'ui/addresses/AddressesTable'; ...@@ -9,12 +8,13 @@ import AddressesTable from 'ui/addresses/AddressesTable';
import ActionBar from 'ui/shared/ActionBar'; import ActionBar from 'ui/shared/ActionBar';
import DataListDisplay from 'ui/shared/DataListDisplay'; import DataListDisplay from 'ui/shared/DataListDisplay';
import PageTitle from 'ui/shared/Page/PageTitle'; import PageTitle from 'ui/shared/Page/PageTitle';
import Pagination from 'ui/shared/Pagination'; import Pagination from 'ui/shared/pagination/Pagination';
import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages';
const PAGE_SIZE = 50; const PAGE_SIZE = 50;
const Accounts = () => { const Accounts = () => {
const { isError, isPlaceholderData, data, isPaginationVisible, pagination } = useQueryWithPages({ const { isError, isPlaceholderData, data, pagination } = useQueryWithPages({
resourceName: 'addresses', resourceName: 'addresses',
options: { options: {
placeholderData: generateListStub<'addresses'>( placeholderData: generateListStub<'addresses'>(
...@@ -32,7 +32,7 @@ const Accounts = () => { ...@@ -32,7 +32,7 @@ const Accounts = () => {
}, },
}); });
const actionBar = isPaginationVisible && ( const actionBar = pagination.isVisible && (
<ActionBar mt={ -6 }> <ActionBar mt={ -6 }>
<Pagination ml="auto" { ...pagination }/> <Pagination ml="auto" { ...pagination }/>
</ActionBar> </ActionBar>
...@@ -43,7 +43,7 @@ const Accounts = () => { ...@@ -43,7 +43,7 @@ const Accounts = () => {
<> <>
<Hide below="lg" ssr={ false }> <Hide below="lg" ssr={ false }>
<AddressesTable <AddressesTable
top={ isPaginationVisible ? 80 : 0 } top={ pagination.isVisible ? 80 : 0 }
items={ data.items } items={ data.items }
totalSupply={ data.total_supply } totalSupply={ data.total_supply }
pageStartIndex={ pageStartIndex } pageStartIndex={ pageStartIndex }
......
...@@ -8,7 +8,7 @@ import type { RoutedTab } from 'ui/shared/Tabs/types'; ...@@ -8,7 +8,7 @@ import type { RoutedTab } from 'ui/shared/Tabs/types';
import appConfig from 'configs/app/config'; import appConfig from 'configs/app/config';
import iconSuccess from 'icons/status/success.svg'; import iconSuccess from 'icons/status/success.svg';
import useApiQuery from 'lib/api/useApiQuery'; import useApiQuery from 'lib/api/useApiQuery';
import { useAppContext } from 'lib/appContext'; import { useAppContext } from 'lib/contexts/app';
import useContractTabs from 'lib/hooks/useContractTabs'; import useContractTabs from 'lib/hooks/useContractTabs';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
import getQueryParamString from 'lib/router/getQueryParamString'; import getQueryParamString from 'lib/router/getQueryParamString';
......
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import React from 'react'; import React from 'react';
import type { PaginationParams } from 'ui/shared/pagination/types';
import type { RoutedTab } from 'ui/shared/Tabs/types'; import type { RoutedTab } from 'ui/shared/Tabs/types';
import appConfig from 'configs/app/config'; import appConfig from 'configs/app/config';
import useApiQuery from 'lib/api/useApiQuery'; import useApiQuery from 'lib/api/useApiQuery';
import { useAppContext } from 'lib/appContext'; import { useAppContext } from 'lib/contexts/app';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
import useQueryWithPages from 'lib/hooks/useQueryWithPages';
import getQueryParamString from 'lib/router/getQueryParamString'; import getQueryParamString from 'lib/router/getQueryParamString';
import { BLOCK } from 'stubs/block'; import { BLOCK } from 'stubs/block';
import { TX } from 'stubs/tx'; import { TX } from 'stubs/tx';
...@@ -18,8 +18,8 @@ import BlockWithdrawals from 'ui/block/BlockWithdrawals'; ...@@ -18,8 +18,8 @@ import BlockWithdrawals from 'ui/block/BlockWithdrawals';
import TextAd from 'ui/shared/ad/TextAd'; import TextAd from 'ui/shared/ad/TextAd';
import NetworkExplorers from 'ui/shared/NetworkExplorers'; import NetworkExplorers from 'ui/shared/NetworkExplorers';
import PageTitle from 'ui/shared/Page/PageTitle'; import PageTitle from 'ui/shared/Page/PageTitle';
import type { Props as PaginationProps } from 'ui/shared/Pagination'; import Pagination from 'ui/shared/pagination/Pagination';
import Pagination from 'ui/shared/Pagination'; import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages';
import RoutedTabs from 'ui/shared/Tabs/RoutedTabs'; import RoutedTabs from 'ui/shared/Tabs/RoutedTabs';
import TabsSkeleton from 'ui/shared/Tabs/TabsSkeleton'; import TabsSkeleton from 'ui/shared/Tabs/TabsSkeleton';
import TxsContent from 'ui/txs/TxsContent'; import TxsContent from 'ui/txs/TxsContent';
...@@ -87,8 +87,8 @@ const BlockPageContent = () => { ...@@ -87,8 +87,8 @@ const BlockPageContent = () => {
].filter(Boolean)), [ blockQuery, blockTxsQuery, blockWithdrawalsQuery ]); ].filter(Boolean)), [ blockQuery, blockTxsQuery, blockWithdrawalsQuery ]);
const hasPagination = !isMobile && ( const hasPagination = !isMobile && (
(tab === 'txs' && blockTxsQuery.isPaginationVisible) || (tab === 'txs' && blockTxsQuery.pagination.isVisible) ||
(tab === 'withdrawals' && blockWithdrawalsQuery.isPaginationVisible) (tab === 'withdrawals' && blockWithdrawalsQuery.pagination.isVisible)
); );
let pagination; let pagination;
...@@ -124,7 +124,7 @@ const BlockPageContent = () => { ...@@ -124,7 +124,7 @@ const BlockPageContent = () => {
<RoutedTabs <RoutedTabs
tabs={ tabs } tabs={ tabs }
tabListProps={ isMobile ? undefined : TAB_LIST_PROPS } tabListProps={ isMobile ? undefined : TAB_LIST_PROPS }
rightSlot={ hasPagination ? <Pagination { ...(pagination as PaginationProps) }/> : null } rightSlot={ hasPagination ? <Pagination { ...(pagination as PaginationParams) }/> : null }
stickyEnabled={ hasPagination } stickyEnabled={ hasPagination }
/> />
) } ) }
......
...@@ -4,13 +4,13 @@ import React from 'react'; ...@@ -4,13 +4,13 @@ import React from 'react';
import type { RoutedTab } from 'ui/shared/Tabs/types'; import type { RoutedTab } from 'ui/shared/Tabs/types';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
import useQueryWithPages from 'lib/hooks/useQueryWithPages';
import getQueryParamString from 'lib/router/getQueryParamString'; import getQueryParamString from 'lib/router/getQueryParamString';
import { BLOCK } from 'stubs/block'; import { BLOCK } from 'stubs/block';
import { generateListStub } from 'stubs/utils'; import { generateListStub } from 'stubs/utils';
import BlocksContent from 'ui/blocks/BlocksContent'; import BlocksContent from 'ui/blocks/BlocksContent';
import BlocksTabSlot from 'ui/blocks/BlocksTabSlot'; import BlocksTabSlot from 'ui/blocks/BlocksTabSlot';
import PageTitle from 'ui/shared/Page/PageTitle'; import PageTitle from 'ui/shared/Page/PageTitle';
import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages';
import RoutedTabs from 'ui/shared/Tabs/RoutedTabs'; import RoutedTabs from 'ui/shared/Tabs/RoutedTabs';
const TAB_LIST_PROPS = { const TAB_LIST_PROPS = {
...@@ -58,23 +58,14 @@ const BlocksPageContent = () => { ...@@ -58,23 +58,14 @@ const BlocksPageContent = () => {
}, },
}); });
const { pagination, isPaginationVisible } = (() => { const pagination = (() => {
if (tab === 'reorgs') { if (tab === 'reorgs') {
return { return reorgsQuery.pagination;
pagination: reorgsQuery.pagination,
isPaginationVisible: reorgsQuery.isPaginationVisible,
};
} }
if (tab === 'uncles') { if (tab === 'uncles') {
return { return unclesQuery.pagination;
pagination: unclesQuery.pagination,
isPaginationVisible: unclesQuery.isPaginationVisible,
};
} }
return { return blocksQuery.pagination;
pagination: blocksQuery.pagination,
isPaginationVisible: blocksQuery.isPaginationVisible,
};
})(); })();
const tabs: Array<RoutedTab> = [ const tabs: Array<RoutedTab> = [
...@@ -89,7 +80,7 @@ const BlocksPageContent = () => { ...@@ -89,7 +80,7 @@ const BlocksPageContent = () => {
<RoutedTabs <RoutedTabs
tabs={ tabs } tabs={ tabs }
tabListProps={ isMobile ? undefined : TAB_LIST_PROPS } tabListProps={ isMobile ? undefined : TAB_LIST_PROPS }
rightSlot={ <BlocksTabSlot pagination={ pagination } isPaginationVisible={ isPaginationVisible }/> } rightSlot={ <BlocksTabSlot pagination={ pagination }/> }
stickyEnabled={ !isMobile } stickyEnabled={ !isMobile }
/> />
</> </>
......
...@@ -5,7 +5,7 @@ import React from 'react'; ...@@ -5,7 +5,7 @@ import React from 'react';
import type { SmartContractVerificationMethod } from 'types/api/contract'; import type { SmartContractVerificationMethod } from 'types/api/contract';
import useApiQuery from 'lib/api/useApiQuery'; import useApiQuery from 'lib/api/useApiQuery';
import { useAppContext } from 'lib/appContext'; import { useAppContext } from 'lib/contexts/app';
import getQueryParamString from 'lib/router/getQueryParamString'; import getQueryParamString from 'lib/router/getQueryParamString';
import ContractVerificationForm from 'ui/contractVerification/ContractVerificationForm'; import ContractVerificationForm from 'ui/contractVerification/ContractVerificationForm';
import { isValidVerificationMethod, sortVerificationMethods } from 'ui/contractVerification/utils'; import { isValidVerificationMethod, sortVerificationMethods } from 'ui/contractVerification/utils';
......
...@@ -6,7 +6,7 @@ import type { CsvExportType } from 'types/client/address'; ...@@ -6,7 +6,7 @@ import type { CsvExportType } from 'types/client/address';
import type { ResourceName } from 'lib/api/resources'; import type { ResourceName } from 'lib/api/resources';
import useApiQuery from 'lib/api/useApiQuery'; import useApiQuery from 'lib/api/useApiQuery';
import { useAppContext } from 'lib/appContext'; import { useAppContext } from 'lib/contexts/app';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
import CsvExportForm from 'ui/csvExport/CsvExportForm'; import CsvExportForm from 'ui/csvExport/CsvExportForm';
import Address from 'ui/shared/address/Address'; import Address from 'ui/shared/address/Address';
......
...@@ -2,7 +2,6 @@ import { Box, Hide, Show, Skeleton } from '@chakra-ui/react'; ...@@ -2,7 +2,6 @@ import { Box, Hide, Show, Skeleton } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import useApiQuery from 'lib/api/useApiQuery'; import useApiQuery from 'lib/api/useApiQuery';
import useQueryWithPages from 'lib/hooks/useQueryWithPages';
import { rightLineArrow, nbsp } from 'lib/html-entities'; import { rightLineArrow, nbsp } from 'lib/html-entities';
import { L2_DEPOSIT_ITEM } from 'stubs/L2'; import { L2_DEPOSIT_ITEM } from 'stubs/L2';
import { generateListStub } from 'stubs/utils'; import { generateListStub } from 'stubs/utils';
...@@ -11,10 +10,11 @@ import DepositsTable from 'ui/l2Deposits/DepositsTable'; ...@@ -11,10 +10,11 @@ import DepositsTable from 'ui/l2Deposits/DepositsTable';
import ActionBar from 'ui/shared/ActionBar'; import ActionBar from 'ui/shared/ActionBar';
import DataListDisplay from 'ui/shared/DataListDisplay'; import DataListDisplay from 'ui/shared/DataListDisplay';
import PageTitle from 'ui/shared/Page/PageTitle'; import PageTitle from 'ui/shared/Page/PageTitle';
import Pagination from 'ui/shared/Pagination'; import Pagination from 'ui/shared/pagination/Pagination';
import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages';
const L2Deposits = () => { const L2Deposits = () => {
const { data, isError, isPlaceholderData, isPaginationVisible, pagination } = useQueryWithPages({ const { data, isError, isPlaceholderData, pagination } = useQueryWithPages({
resourceName: 'l2_deposits', resourceName: 'l2_deposits',
options: { options: {
placeholderData: generateListStub<'l2_deposits'>( placeholderData: generateListStub<'l2_deposits'>(
...@@ -49,7 +49,7 @@ const L2Deposits = () => { ...@@ -49,7 +49,7 @@ const L2Deposits = () => {
))) } ))) }
</Show> </Show>
<Hide below="lg" ssr={ false }> <Hide below="lg" ssr={ false }>
<DepositsTable items={ data.items } top={ isPaginationVisible ? 80 : 0 } isLoading={ isPlaceholderData }/> <DepositsTable items={ data.items } top={ pagination.isVisible ? 80 : 0 } isLoading={ isPlaceholderData }/>
</Hide> </Hide>
</> </>
) : null; ) : null;
...@@ -78,7 +78,7 @@ const L2Deposits = () => { ...@@ -78,7 +78,7 @@ const L2Deposits = () => {
<Box display={{ base: 'none', lg: 'block' }}> <Box display={{ base: 'none', lg: 'block' }}>
{ text } { text }
</Box> </Box>
{ isPaginationVisible && <Pagination ml="auto" { ...pagination }/> } { pagination.isVisible && <Pagination ml="auto" { ...pagination }/> }
</ActionBar> </ActionBar>
</> </>
); );
......
...@@ -2,7 +2,6 @@ import { Box, Hide, Show, Skeleton, Text } from '@chakra-ui/react'; ...@@ -2,7 +2,6 @@ import { Box, Hide, Show, Skeleton, Text } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import useApiQuery from 'lib/api/useApiQuery'; import useApiQuery from 'lib/api/useApiQuery';
import useQueryWithPages from 'lib/hooks/useQueryWithPages';
import { L2_OUTPUT_ROOTS_ITEM } from 'stubs/L2'; import { L2_OUTPUT_ROOTS_ITEM } from 'stubs/L2';
import { generateListStub } from 'stubs/utils'; import { generateListStub } from 'stubs/utils';
import OutputRootsListItem from 'ui/l2OutputRoots/OutputRootsListItem'; import OutputRootsListItem from 'ui/l2OutputRoots/OutputRootsListItem';
...@@ -10,10 +9,11 @@ import OutputRootsTable from 'ui/l2OutputRoots/OutputRootsTable'; ...@@ -10,10 +9,11 @@ import OutputRootsTable from 'ui/l2OutputRoots/OutputRootsTable';
import ActionBar from 'ui/shared/ActionBar'; import ActionBar from 'ui/shared/ActionBar';
import DataListDisplay from 'ui/shared/DataListDisplay'; import DataListDisplay from 'ui/shared/DataListDisplay';
import PageTitle from 'ui/shared/Page/PageTitle'; import PageTitle from 'ui/shared/Page/PageTitle';
import Pagination from 'ui/shared/Pagination'; import Pagination from 'ui/shared/pagination/Pagination';
import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages';
const L2OutputRoots = () => { const L2OutputRoots = () => {
const { data, isError, isPlaceholderData, isPaginationVisible, pagination } = useQueryWithPages({ const { data, isError, isPlaceholderData, pagination } = useQueryWithPages({
resourceName: 'l2_output_roots', resourceName: 'l2_output_roots',
options: { options: {
placeholderData: generateListStub<'l2_output_roots'>( placeholderData: generateListStub<'l2_output_roots'>(
...@@ -47,7 +47,7 @@ const L2OutputRoots = () => { ...@@ -47,7 +47,7 @@ const L2OutputRoots = () => {
))) } ))) }
</Show> </Show>
<Hide below="lg" ssr={ false }> <Hide below="lg" ssr={ false }>
<OutputRootsTable items={ data.items } top={ isPaginationVisible ? 80 : 0 } isLoading={ isPlaceholderData }/> <OutputRootsTable items={ data.items } top={ pagination.isVisible ? 80 : 0 } isLoading={ isPlaceholderData }/>
</Hide> </Hide>
</> </>
) : null; ) : null;
...@@ -76,7 +76,7 @@ const L2OutputRoots = () => { ...@@ -76,7 +76,7 @@ const L2OutputRoots = () => {
<Box display={{ base: 'none', lg: 'block' }}> <Box display={{ base: 'none', lg: 'block' }}>
{ text } { text }
</Box> </Box>
{ isPaginationVisible && <Pagination ml="auto" { ...pagination }/> } { pagination.isVisible && <Pagination ml="auto" { ...pagination }/> }
</ActionBar> </ActionBar>
</> </>
); );
......
...@@ -2,7 +2,6 @@ import { Box, Hide, Show, Skeleton, Text } from '@chakra-ui/react'; ...@@ -2,7 +2,6 @@ import { Box, Hide, Show, Skeleton, Text } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import useApiQuery from 'lib/api/useApiQuery'; import useApiQuery from 'lib/api/useApiQuery';
import useQueryWithPages from 'lib/hooks/useQueryWithPages';
import { nbsp } from 'lib/html-entities'; import { nbsp } from 'lib/html-entities';
import { L2_TXN_BATCHES_ITEM } from 'stubs/L2'; import { L2_TXN_BATCHES_ITEM } from 'stubs/L2';
import { generateListStub } from 'stubs/utils'; import { generateListStub } from 'stubs/utils';
...@@ -11,10 +10,11 @@ import TxnBatchesTable from 'ui/l2TxnBatches/TxnBatchesTable'; ...@@ -11,10 +10,11 @@ import TxnBatchesTable from 'ui/l2TxnBatches/TxnBatchesTable';
import ActionBar from 'ui/shared/ActionBar'; import ActionBar from 'ui/shared/ActionBar';
import DataListDisplay from 'ui/shared/DataListDisplay'; import DataListDisplay from 'ui/shared/DataListDisplay';
import PageTitle from 'ui/shared/Page/PageTitle'; import PageTitle from 'ui/shared/Page/PageTitle';
import Pagination from 'ui/shared/Pagination'; import Pagination from 'ui/shared/pagination/Pagination';
import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages';
const L2TxnBatches = () => { const L2TxnBatches = () => {
const { data, isError, isPlaceholderData, isPaginationVisible, pagination } = useQueryWithPages({ const { data, isError, isPlaceholderData, pagination } = useQueryWithPages({
resourceName: 'l2_txn_batches', resourceName: 'l2_txn_batches',
options: { options: {
placeholderData: generateListStub<'l2_txn_batches'>( placeholderData: generateListStub<'l2_txn_batches'>(
...@@ -47,7 +47,7 @@ const L2TxnBatches = () => { ...@@ -47,7 +47,7 @@ const L2TxnBatches = () => {
/> />
))) } ))) }
</Show> </Show>
<Hide below="lg" ssr={ false }><TxnBatchesTable items={ data.items } top={ isPaginationVisible ? 80 : 0 } isLoading={ isPlaceholderData }/></Hide> <Hide below="lg" ssr={ false }><TxnBatchesTable items={ data.items } top={ pagination.isVisible ? 80 : 0 } isLoading={ isPlaceholderData }/></Hide>
</> </>
) : null; ) : null;
...@@ -75,7 +75,7 @@ const L2TxnBatches = () => { ...@@ -75,7 +75,7 @@ const L2TxnBatches = () => {
<Box display={{ base: 'none', lg: 'block' }}> <Box display={{ base: 'none', lg: 'block' }}>
{ text } { text }
</Box> </Box>
{ isPaginationVisible && <Pagination ml="auto" { ...pagination }/> } { pagination.isVisible && <Pagination ml="auto" { ...pagination }/> }
</ActionBar> </ActionBar>
</> </>
); );
......
...@@ -2,7 +2,6 @@ import { Box, Hide, Show, Skeleton } from '@chakra-ui/react'; ...@@ -2,7 +2,6 @@ import { Box, Hide, Show, Skeleton } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import useApiQuery from 'lib/api/useApiQuery'; import useApiQuery from 'lib/api/useApiQuery';
import useQueryWithPages from 'lib/hooks/useQueryWithPages';
import { rightLineArrow, nbsp } from 'lib/html-entities'; import { rightLineArrow, nbsp } from 'lib/html-entities';
import { L2_WITHDRAWAL_ITEM } from 'stubs/L2'; import { L2_WITHDRAWAL_ITEM } from 'stubs/L2';
import { generateListStub } from 'stubs/utils'; import { generateListStub } from 'stubs/utils';
...@@ -11,10 +10,11 @@ import WithdrawalsTable from 'ui/l2Withdrawals/WithdrawalsTable'; ...@@ -11,10 +10,11 @@ import WithdrawalsTable from 'ui/l2Withdrawals/WithdrawalsTable';
import ActionBar from 'ui/shared/ActionBar'; import ActionBar from 'ui/shared/ActionBar';
import DataListDisplay from 'ui/shared/DataListDisplay'; import DataListDisplay from 'ui/shared/DataListDisplay';
import PageTitle from 'ui/shared/Page/PageTitle'; import PageTitle from 'ui/shared/Page/PageTitle';
import Pagination from 'ui/shared/Pagination'; import Pagination from 'ui/shared/pagination/Pagination';
import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages';
const L2Withdrawals = () => { const L2Withdrawals = () => {
const { data, isError, isPlaceholderData, isPaginationVisible, pagination } = useQueryWithPages({ const { data, isError, isPlaceholderData, pagination } = useQueryWithPages({
resourceName: 'l2_withdrawals', resourceName: 'l2_withdrawals',
options: { options: {
placeholderData: generateListStub<'l2_withdrawals'>( placeholderData: generateListStub<'l2_withdrawals'>(
...@@ -46,7 +46,7 @@ const L2Withdrawals = () => { ...@@ -46,7 +46,7 @@ const L2Withdrawals = () => {
/> />
))) }</Show> ))) }</Show>
<Hide below="lg" ssr={ false }> <Hide below="lg" ssr={ false }>
<WithdrawalsTable items={ data.items } top={ isPaginationVisible ? 80 : 0 } isLoading={ isPlaceholderData }/> <WithdrawalsTable items={ data.items } top={ pagination.isVisible ? 80 : 0 } isLoading={ isPlaceholderData }/>
</Hide> </Hide>
</> </>
) : null; ) : null;
...@@ -75,7 +75,7 @@ const L2Withdrawals = () => { ...@@ -75,7 +75,7 @@ const L2Withdrawals = () => {
<Box display={{ base: 'none', lg: 'block' }}> <Box display={{ base: 'none', lg: 'block' }}>
{ text } { text }
</Box> </Box>
{ isPaginationVisible && <Pagination ml="auto" { ...pagination }/> } { pagination.isVisible && <Pagination ml="auto" { ...pagination }/> }
</ActionBar> </ActionBar>
</> </>
); );
......
...@@ -10,7 +10,7 @@ import ContentLoader from 'ui/shared/ContentLoader'; ...@@ -10,7 +10,7 @@ import ContentLoader from 'ui/shared/ContentLoader';
import DataFetchAlert from 'ui/shared/DataFetchAlert'; import DataFetchAlert from 'ui/shared/DataFetchAlert';
import Page from 'ui/shared/Page/Page'; import Page from 'ui/shared/Page/Page';
import PageTitle from 'ui/shared/Page/PageTitle'; import PageTitle from 'ui/shared/Page/PageTitle';
import Pagination from 'ui/shared/Pagination'; import Pagination from 'ui/shared/pagination/Pagination';
import Thead from 'ui/shared/TheadSticky'; import Thead from 'ui/shared/TheadSticky';
import Header from 'ui/snippets/header/Header'; import Header from 'ui/snippets/header/Header';
import SearchBarInput from 'ui/snippets/searchBar/SearchBarInput'; import SearchBarInput from 'ui/snippets/searchBar/SearchBarInput';
...@@ -19,7 +19,7 @@ import useSearchQuery from 'ui/snippets/searchBar/useSearchQuery'; ...@@ -19,7 +19,7 @@ import useSearchQuery from 'ui/snippets/searchBar/useSearchQuery';
const SearchResultsPageContent = () => { const SearchResultsPageContent = () => {
const router = useRouter(); const router = useRouter();
const { query, redirectCheckQuery, searchTerm, debouncedSearchTerm, handleSearchTermChange } = useSearchQuery(true); const { query, redirectCheckQuery, searchTerm, debouncedSearchTerm, handleSearchTermChange } = useSearchQuery(true);
const { data, isError, isPlaceholderData, pagination, isPaginationVisible } = query; const { data, isError, isPlaceholderData, pagination } = query;
const [ showContent, setShowContent ] = React.useState(false); const [ showContent, setShowContent ] = React.useState(false);
React.useEffect(() => { React.useEffect(() => {
...@@ -70,7 +70,7 @@ const SearchResultsPageContent = () => { ...@@ -70,7 +70,7 @@ const SearchResultsPageContent = () => {
</Show> </Show>
<Hide below="lg" ssr={ false }> <Hide below="lg" ssr={ false }>
<Table variant="simple" size="md" fontWeight={ 500 }> <Table variant="simple" size="md" fontWeight={ 500 }>
<Thead top={ isPaginationVisible ? 80 : 0 }> <Thead top={ pagination.isVisible ? 80 : 0 }>
<Tr> <Tr>
<Th width="50%">Search Result</Th> <Th width="50%">Search Result</Th>
<Th width="50%"/> <Th width="50%"/>
...@@ -99,10 +99,10 @@ const SearchResultsPageContent = () => { ...@@ -99,10 +99,10 @@ const SearchResultsPageContent = () => {
} }
const text = isPlaceholderData && pagination.page === 1 ? ( const text = isPlaceholderData && pagination.page === 1 ? (
<Skeleton h={ 6 } w="280px" borderRadius="full" mb={ isPaginationVisible ? 0 : 6 }/> <Skeleton h={ 6 } w="280px" borderRadius="full" mb={ pagination.isVisible ? 0 : 6 }/>
) : ( ) : (
( (
<Box mb={ isPaginationVisible ? 0 : 6 } lineHeight="32px"> <Box mb={ pagination.isVisible ? 0 : 6 } lineHeight="32px">
<span>Found </span> <span>Found </span>
<chakra.span fontWeight={ 700 }> <chakra.span fontWeight={ 700 }>
{ pagination.page > 1 ? 50 : data?.items.length }{ data?.next_page_params || pagination.page > 1 ? '+' : '' } { pagination.page > 1 ? 50 : data?.items.length }{ data?.next_page_params || pagination.page > 1 ? '+' : '' }
...@@ -113,7 +113,7 @@ const SearchResultsPageContent = () => { ...@@ -113,7 +113,7 @@ const SearchResultsPageContent = () => {
) )
); );
if (!isPaginationVisible) { if (!pagination.isVisible) {
return text; return text;
} }
......
...@@ -2,7 +2,7 @@ import { Flex } from '@chakra-ui/react'; ...@@ -2,7 +2,7 @@ import { Flex } from '@chakra-ui/react';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import React from 'react'; import React from 'react';
import { useAppContext } from 'lib/appContext'; import { useAppContext } from 'lib/contexts/app';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
import Address from 'ui/shared/address/Address'; import Address from 'ui/shared/address/Address';
import AddressIcon from 'ui/shared/address/AddressIcon'; import AddressIcon from 'ui/shared/address/AddressIcon';
......
...@@ -5,15 +5,15 @@ import React, { useEffect } from 'react'; ...@@ -5,15 +5,15 @@ import React, { useEffect } from 'react';
import type { SocketMessage } from 'lib/socket/types'; import type { SocketMessage } from 'lib/socket/types';
import type { TokenInfo } from 'types/api/token'; import type { TokenInfo } from 'types/api/token';
import type { PaginationParams } from 'ui/shared/pagination/types';
import type { RoutedTab } from 'ui/shared/Tabs/types'; import type { RoutedTab } from 'ui/shared/Tabs/types';
import appConfig from 'configs/app/config'; import appConfig from 'configs/app/config';
import iconSuccess from 'icons/status/success.svg'; import iconSuccess from 'icons/status/success.svg';
import useApiQuery, { getResourceKey } from 'lib/api/useApiQuery'; import useApiQuery, { getResourceKey } from 'lib/api/useApiQuery';
import { useAppContext } from 'lib/appContext'; import { useAppContext } from 'lib/contexts/app';
import useContractTabs from 'lib/hooks/useContractTabs'; import useContractTabs from 'lib/hooks/useContractTabs';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
import useQueryWithPages from 'lib/hooks/useQueryWithPages';
import getQueryParamString from 'lib/router/getQueryParamString'; import getQueryParamString from 'lib/router/getQueryParamString';
import useSocketChannel from 'lib/socket/useSocketChannel'; import useSocketChannel from 'lib/socket/useSocketChannel';
import useSocketMessage from 'lib/socket/useSocketMessage'; import useSocketMessage from 'lib/socket/useSocketMessage';
...@@ -26,8 +26,8 @@ import TextAd from 'ui/shared/ad/TextAd'; ...@@ -26,8 +26,8 @@ import TextAd from 'ui/shared/ad/TextAd';
import EntityTags from 'ui/shared/EntityTags'; import EntityTags from 'ui/shared/EntityTags';
import NetworkExplorers from 'ui/shared/NetworkExplorers'; import NetworkExplorers from 'ui/shared/NetworkExplorers';
import PageTitle from 'ui/shared/Page/PageTitle'; import PageTitle from 'ui/shared/Page/PageTitle';
import type { Props as PaginationProps } from 'ui/shared/Pagination'; import Pagination from 'ui/shared/pagination/Pagination';
import Pagination from 'ui/shared/Pagination'; import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages';
import RoutedTabs from 'ui/shared/Tabs/RoutedTabs'; import RoutedTabs from 'ui/shared/Tabs/RoutedTabs';
import TabsSkeleton from 'ui/shared/Tabs/TabsSkeleton'; import TabsSkeleton from 'ui/shared/Tabs/TabsSkeleton';
import TokenLogo from 'ui/shared/TokenLogo'; import TokenLogo from 'ui/shared/TokenLogo';
...@@ -183,21 +183,17 @@ const TokenPageContent = () => { ...@@ -183,21 +183,17 @@ const TokenPageContent = () => {
} : undefined, } : undefined,
].filter(Boolean); ].filter(Boolean);
let hasPagination; let pagination: PaginationParams | undefined;
let pagination: PaginationProps | undefined;
if (!router.query.tab || router.query.tab === 'token_transfers') { if (!router.query.tab || router.query.tab === 'token_transfers') {
hasPagination = transfersQuery.isPaginationVisible;
pagination = transfersQuery.pagination; pagination = transfersQuery.pagination;
} }
if (router.query.tab === 'holders') { if (router.query.tab === 'holders') {
hasPagination = holdersQuery.isPaginationVisible;
pagination = holdersQuery.pagination; pagination = holdersQuery.pagination;
} }
if (router.query.tab === 'inventory') { if (router.query.tab === 'inventory') {
hasPagination = inventoryQuery.isPaginationVisible;
pagination = inventoryQuery.pagination; pagination = inventoryQuery.pagination;
} }
...@@ -280,7 +276,7 @@ const TokenPageContent = () => { ...@@ -280,7 +276,7 @@ const TokenPageContent = () => {
<RoutedTabs <RoutedTabs
tabs={ tabs } tabs={ tabs }
tabListProps={ tabListProps } tabListProps={ tabListProps }
rightSlot={ !isMobile && hasPagination && pagination ? <Pagination { ...pagination }/> : null } rightSlot={ !isMobile && pagination?.isVisible ? <Pagination { ...pagination }/> : null }
stickyEnabled={ !isMobile } stickyEnabled={ !isMobile }
/> />
) } ) }
......
...@@ -2,13 +2,13 @@ import { Box, Icon, Skeleton } from '@chakra-ui/react'; ...@@ -2,13 +2,13 @@ import { Box, Icon, Skeleton } from '@chakra-ui/react';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import React from 'react'; import React from 'react';
import type { PaginationParams } from 'ui/shared/pagination/types';
import type { RoutedTab } from 'ui/shared/Tabs/types'; import type { RoutedTab } from 'ui/shared/Tabs/types';
import nftIcon from 'icons/nft_shield.svg'; import nftIcon from 'icons/nft_shield.svg';
import useApiQuery from 'lib/api/useApiQuery'; import useApiQuery from 'lib/api/useApiQuery';
import { useAppContext } from 'lib/appContext'; import { useAppContext } from 'lib/contexts/app';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
import useQueryWithPages from 'lib/hooks/useQueryWithPages';
import { TOKEN_INSTANCE } from 'stubs/token'; import { TOKEN_INSTANCE } from 'stubs/token';
import * as tokenStubs from 'stubs/token'; import * as tokenStubs from 'stubs/token';
import { generateListStub } from 'stubs/utils'; import { generateListStub } from 'stubs/utils';
...@@ -17,8 +17,8 @@ import AddressHeadingInfo from 'ui/shared/AddressHeadingInfo'; ...@@ -17,8 +17,8 @@ import AddressHeadingInfo from 'ui/shared/AddressHeadingInfo';
import Tag from 'ui/shared/chakra/Tag'; import Tag from 'ui/shared/chakra/Tag';
import LinkExternal from 'ui/shared/LinkExternal'; import LinkExternal from 'ui/shared/LinkExternal';
import PageTitle from 'ui/shared/Page/PageTitle'; import PageTitle from 'ui/shared/Page/PageTitle';
import Pagination from 'ui/shared/Pagination'; import Pagination from 'ui/shared/pagination/Pagination';
import type { Props as PaginationProps } from 'ui/shared/Pagination'; import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages';
import RoutedTabs from 'ui/shared/Tabs/RoutedTabs'; import RoutedTabs from 'ui/shared/Tabs/RoutedTabs';
import TabsSkeleton from 'ui/shared/Tabs/TabsSkeleton'; import TabsSkeleton from 'ui/shared/Tabs/TabsSkeleton';
import TokenHolders from 'ui/token/TokenHolders/TokenHolders'; import TokenHolders from 'ui/token/TokenHolders/TokenHolders';
...@@ -144,15 +144,12 @@ const TokenInstanceContent = () => { ...@@ -144,15 +144,12 @@ const TokenInstanceContent = () => {
} }
})(); })();
let pagination: PaginationProps | undefined; let pagination: PaginationParams | undefined;
let isPaginationVisible;
if (tab === 'token_transfers') { if (tab === 'token_transfers') {
pagination = transfersQuery.pagination; pagination = transfersQuery.pagination;
isPaginationVisible = transfersQuery.isPaginationVisible;
} else if (tab === 'holders') { } else if (tab === 'holders') {
pagination = holdersQuery.pagination; pagination = holdersQuery.pagination;
isPaginationVisible = holdersQuery.isPaginationVisible;
} }
return ( return (
...@@ -179,7 +176,7 @@ const TokenInstanceContent = () => { ...@@ -179,7 +176,7 @@ const TokenInstanceContent = () => {
<RoutedTabs <RoutedTabs
tabs={ tabs } tabs={ tabs }
tabListProps={ isMobile ? { mt: 8 } : { mt: 3, py: 5, marginBottom: 0 } } tabListProps={ isMobile ? { mt: 8 } : { mt: 3, py: 5, marginBottom: 0 } }
rightSlot={ !isMobile && isPaginationVisible && pagination ? <Pagination { ...pagination }/> : null } rightSlot={ !isMobile && pagination?.isVisible ? <Pagination { ...pagination }/> : null }
stickyEnabled={ !isMobile } stickyEnabled={ !isMobile }
/> />
) } ) }
......
...@@ -4,7 +4,7 @@ import React from 'react'; ...@@ -4,7 +4,7 @@ import React from 'react';
import type { RoutedTab } from 'ui/shared/Tabs/types'; import type { RoutedTab } from 'ui/shared/Tabs/types';
import useApiQuery from 'lib/api/useApiQuery'; import useApiQuery from 'lib/api/useApiQuery';
import { useAppContext } from 'lib/appContext'; import { useAppContext } from 'lib/contexts/app';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
import getQueryParamString from 'lib/router/getQueryParamString'; import getQueryParamString from 'lib/router/getQueryParamString';
import { TX } from 'stubs/tx'; import { TX } from 'stubs/tx';
......
...@@ -7,13 +7,13 @@ import appConfig from 'configs/app/config'; ...@@ -7,13 +7,13 @@ import appConfig from 'configs/app/config';
import useHasAccount from 'lib/hooks/useHasAccount'; import useHasAccount from 'lib/hooks/useHasAccount';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
import useNewTxsSocket from 'lib/hooks/useNewTxsSocket'; import useNewTxsSocket from 'lib/hooks/useNewTxsSocket';
import useQueryWithPages from 'lib/hooks/useQueryWithPages';
import { TX } from 'stubs/tx'; import { TX } from 'stubs/tx';
import { generateListStub } from 'stubs/utils'; import { generateListStub } from 'stubs/utils';
import PageTitle from 'ui/shared/Page/PageTitle'; import PageTitle from 'ui/shared/Page/PageTitle';
import Pagination from 'ui/shared/pagination/Pagination';
import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages';
import RoutedTabs from 'ui/shared/Tabs/RoutedTabs'; import RoutedTabs from 'ui/shared/Tabs/RoutedTabs';
import TxsContent from 'ui/txs/TxsContent'; import TxsContent from 'ui/txs/TxsContent';
import TxsTabSlot from 'ui/txs/TxsTabSlot';
import TxsWatchlist from 'ui/txs/TxsWatchlist'; import TxsWatchlist from 'ui/txs/TxsWatchlist';
const TAB_LIST_PROPS = { const TAB_LIST_PROPS = {
...@@ -81,6 +81,8 @@ const Transactions = () => { ...@@ -81,6 +81,8 @@ const Transactions = () => {
} : undefined, } : undefined,
].filter(Boolean); ].filter(Boolean);
const pagination = router.query.tab === 'watchlist' ? txsWatchlistQuery.pagination : txsQuery.pagination;
return ( return (
<> <>
<PageTitle title="Transactions" withTextAd/> <PageTitle title="Transactions" withTextAd/>
...@@ -88,10 +90,7 @@ const Transactions = () => { ...@@ -88,10 +90,7 @@ const Transactions = () => {
tabs={ tabs } tabs={ tabs }
tabListProps={ isMobile ? undefined : TAB_LIST_PROPS } tabListProps={ isMobile ? undefined : TAB_LIST_PROPS }
rightSlot={ ( rightSlot={ (
<TxsTabSlot pagination.isVisible && !isMobile ? <Pagination my={ 1 } { ...pagination }/> : null
pagination={ router.query.tab === 'watchlist' ? txsWatchlistQuery.pagination : txsQuery.pagination }
isPaginationVisible={ txsQuery.isPaginationVisible && !isMobile }
/>
) } ) }
stickyEnabled={ !isMobile } stickyEnabled={ !isMobile }
/> />
......
...@@ -6,7 +6,6 @@ import type { VerifiedContractsFilters } from 'types/api/contracts'; ...@@ -6,7 +6,6 @@ import type { VerifiedContractsFilters } from 'types/api/contracts';
import useDebounce from 'lib/hooks/useDebounce'; import useDebounce from 'lib/hooks/useDebounce';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
import useQueryWithPages from 'lib/hooks/useQueryWithPages';
import { apos } from 'lib/html-entities'; import { apos } from 'lib/html-entities';
import getQueryParamString from 'lib/router/getQueryParamString'; import getQueryParamString from 'lib/router/getQueryParamString';
import { VERIFIED_CONTRACT_INFO } from 'stubs/contract'; import { VERIFIED_CONTRACT_INFO } from 'stubs/contract';
...@@ -15,7 +14,8 @@ import ActionBar from 'ui/shared/ActionBar'; ...@@ -15,7 +14,8 @@ import ActionBar from 'ui/shared/ActionBar';
import DataListDisplay from 'ui/shared/DataListDisplay'; import DataListDisplay from 'ui/shared/DataListDisplay';
import FilterInput from 'ui/shared/filters/FilterInput'; import FilterInput from 'ui/shared/filters/FilterInput';
import PageTitle from 'ui/shared/Page/PageTitle'; import PageTitle from 'ui/shared/Page/PageTitle';
import Pagination from 'ui/shared/Pagination'; import Pagination from 'ui/shared/pagination/Pagination';
import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages';
import Sort from 'ui/shared/sort/Sort'; import Sort from 'ui/shared/sort/Sort';
import type { SortField, Sort as TSort } from 'ui/verifiedContracts/utils'; import type { SortField, Sort as TSort } from 'ui/verifiedContracts/utils';
import { SORT_OPTIONS, sortFn, getNextSortValue } from 'ui/verifiedContracts/utils'; import { SORT_OPTIONS, sortFn, getNextSortValue } from 'ui/verifiedContracts/utils';
...@@ -34,7 +34,7 @@ const VerifiedContracts = () => { ...@@ -34,7 +34,7 @@ const VerifiedContracts = () => {
const isMobile = useIsMobile(); const isMobile = useIsMobile();
const { isError, isPlaceholderData, data, isPaginationVisible, pagination, onFilterChange } = useQueryWithPages({ const { isError, isPlaceholderData, data, pagination, onFilterChange } = useQueryWithPages({
resourceName: 'verified_contracts', resourceName: 'verified_contracts',
filters: { q: debouncedSearchTerm, filter: type }, filters: { q: debouncedSearchTerm, filter: type },
options: { options: {
...@@ -100,13 +100,13 @@ const VerifiedContracts = () => { ...@@ -100,13 +100,13 @@ const VerifiedContracts = () => {
{ sortButton } { sortButton }
{ filterInput } { filterInput }
</HStack> </HStack>
{ (!isMobile || isPaginationVisible) && ( { (!isMobile || pagination.isVisible) && (
<ActionBar mt={ -6 }> <ActionBar mt={ -6 }>
<HStack spacing={ 3 } display={{ base: 'none', lg: 'flex' }}> <HStack spacing={ 3 } display={{ base: 'none', lg: 'flex' }}>
{ typeFilter } { typeFilter }
{ filterInput } { filterInput }
</HStack> </HStack>
{ isPaginationVisible && <Pagination ml="auto" { ...pagination }/> } <Pagination ml="auto" { ...pagination }/>
</ActionBar> </ActionBar>
) } ) }
</> </>
......
...@@ -5,20 +5,20 @@ import React from 'react'; ...@@ -5,20 +5,20 @@ import React from 'react';
import useApiQuery from 'lib/api/useApiQuery'; import useApiQuery from 'lib/api/useApiQuery';
import getCurrencyValue from 'lib/getCurrencyValue'; import getCurrencyValue from 'lib/getCurrencyValue';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
import useQueryWithPages from 'lib/hooks/useQueryWithPages';
import { generateListStub } from 'stubs/utils'; import { generateListStub } from 'stubs/utils';
import { WITHDRAWAL } from 'stubs/withdrawals'; import { WITHDRAWAL } from 'stubs/withdrawals';
import ActionBar from 'ui/shared/ActionBar'; import ActionBar from 'ui/shared/ActionBar';
import DataListDisplay from 'ui/shared/DataListDisplay'; import DataListDisplay from 'ui/shared/DataListDisplay';
import PageTitle from 'ui/shared/Page/PageTitle'; import PageTitle from 'ui/shared/Page/PageTitle';
import Pagination from 'ui/shared/Pagination'; import Pagination from 'ui/shared/pagination/Pagination';
import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages';
import WithdrawalsListItem from 'ui/withdrawals/WithdrawalsListItem'; import WithdrawalsListItem from 'ui/withdrawals/WithdrawalsListItem';
import WithdrawalsTable from 'ui/withdrawals/WithdrawalsTable'; import WithdrawalsTable from 'ui/withdrawals/WithdrawalsTable';
const Withdrawals = () => { const Withdrawals = () => {
const isMobile = useIsMobile(); const isMobile = useIsMobile();
const { data, isError, isPlaceholderData, isPaginationVisible, pagination } = useQueryWithPages({ const { data, isError, isPlaceholderData, pagination } = useQueryWithPages({
resourceName: 'withdrawals', resourceName: 'withdrawals',
options: { options: {
placeholderData: generateListStub<'withdrawals'>(WITHDRAWAL, 50, { next_page_params: { placeholderData: generateListStub<'withdrawals'>(WITHDRAWAL, 50, { next_page_params: {
...@@ -43,7 +43,7 @@ const Withdrawals = () => { ...@@ -43,7 +43,7 @@ const Withdrawals = () => {
))) } ))) }
</Show> </Show>
<Hide below="lg" ssr={ false }> <Hide below="lg" ssr={ false }>
<WithdrawalsTable items={ data.items } view="list" top={ isPaginationVisible ? 80 : 0 } isLoading={ isPlaceholderData }/> <WithdrawalsTable items={ data.items } view="list" top={ pagination.isVisible ? 80 : 0 } isLoading={ isPlaceholderData }/>
</Hide> </Hide>
</> </>
) : null; ) : null;
...@@ -54,8 +54,8 @@ const Withdrawals = () => { ...@@ -54,8 +54,8 @@ const Withdrawals = () => {
<Skeleton <Skeleton
w={{ base: '100%', lg: '320px' }} w={{ base: '100%', lg: '320px' }}
h="24px" h="24px"
mb={{ base: 6, lg: isPaginationVisible ? 0 : 7 }} mb={{ base: 6, lg: pagination.isVisible ? 0 : 7 }}
mt={{ base: 0, lg: isPaginationVisible ? 0 : 7 }} mt={{ base: 0, lg: pagination.isVisible ? 0 : 7 }}
/> />
); );
} }
...@@ -66,7 +66,7 @@ const Withdrawals = () => { ...@@ -66,7 +66,7 @@ const Withdrawals = () => {
const { valueStr } = getCurrencyValue({ value: countersQuery.data.withdrawal_sum }); const { valueStr } = getCurrencyValue({ value: countersQuery.data.withdrawal_sum });
return ( return (
<Text mb={{ base: 6, lg: isPaginationVisible ? 0 : 6 }} lineHeight={{ base: '24px', lg: '32px' }}> <Text mb={{ base: 6, lg: pagination.isVisible ? 0 : 6 }} lineHeight={{ base: '24px', lg: '32px' }}>
{ BigNumber(countersQuery.data.withdrawal_count).toFormat() } withdrawals processed and { valueStr } ETH withdrawn { BigNumber(countersQuery.data.withdrawal_count).toFormat() } withdrawals processed and { valueStr } ETH withdrawn
</Text> </Text>
); );
...@@ -74,8 +74,8 @@ const Withdrawals = () => { ...@@ -74,8 +74,8 @@ const Withdrawals = () => {
const actionBar = ( const actionBar = (
<> <>
{ (isMobile || !isPaginationVisible) && text } { (isMobile || !pagination.isVisible) && text }
{ isPaginationVisible && ( { pagination.isVisible && (
<ActionBar mt={ -6 }> <ActionBar mt={ -6 }>
<Flex alignItems="center" justifyContent="space-between" w="100%"> <Flex alignItems="center" justifyContent="space-between" w="100%">
{ !isMobile && text } { !isMobile && text }
......
...@@ -9,6 +9,7 @@ import React from 'react'; ...@@ -9,6 +9,7 @@ import React from 'react';
import type { AddressFromToFilter } from 'types/api/address'; import type { AddressFromToFilter } from 'types/api/address';
import type { TokenType } from 'types/api/token'; import type { TokenType } from 'types/api/token';
import useIsInitialLoading from 'lib/hooks/useIsInitialLoading';
import PopoverFilter from 'ui/shared/filters/PopoverFilter'; import PopoverFilter from 'ui/shared/filters/PopoverFilter';
import TokenTypeFilter from 'ui/shared/filters/TokenTypeFilter'; import TokenTypeFilter from 'ui/shared/filters/TokenTypeFilter';
...@@ -31,9 +32,10 @@ const TokenTransferFilter = ({ ...@@ -31,9 +32,10 @@ const TokenTransferFilter = ({
defaultAddressFilter, defaultAddressFilter,
isLoading, isLoading,
}: Props) => { }: Props) => {
const isInitialLoading = useIsInitialLoading(isLoading);
return ( return (
<PopoverFilter appliedFiltersNum={ appliedFiltersNum } contentProps={{ w: '200px' }} isLoading={ isLoading }> <PopoverFilter appliedFiltersNum={ appliedFiltersNum } contentProps={{ w: '200px' }} isLoading={ isInitialLoading }>
{ withAddressFilter && ( { withAddressFilter && (
<> <>
<Text variant="secondary" fontWeight={ 600 }>Address</Text> <Text variant="secondary" fontWeight={ 600 }>Address</Text>
......
...@@ -2,7 +2,7 @@ import { useColorModeValue, useToken, SkeletonCircle, Image, Box } from '@chakra ...@@ -2,7 +2,7 @@ import { useColorModeValue, useToken, SkeletonCircle, Image, Box } from '@chakra
import React from 'react'; import React from 'react';
import Identicon from 'react-identicons'; import Identicon from 'react-identicons';
import { useAppContext } from 'lib/appContext'; import { useAppContext } from 'lib/contexts/app';
import * as cookies from 'lib/cookies'; import * as cookies from 'lib/cookies';
import useFetchProfileInfo from 'lib/hooks/useFetchProfileInfo'; import useFetchProfileInfo from 'lib/hooks/useFetchProfileInfo';
......
...@@ -2,7 +2,7 @@ import { chakra, Skeleton } from '@chakra-ui/react'; ...@@ -2,7 +2,7 @@ import { chakra, Skeleton } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import appConfig from 'configs/app/config'; import appConfig from 'configs/app/config';
import { useAppContext } from 'lib/appContext'; import { useAppContext } from 'lib/contexts/app';
import * as cookies from 'lib/cookies'; import * as cookies from 'lib/cookies';
import isSelfHosted from 'lib/isSelfHosted'; import isSelfHosted from 'lib/isSelfHosted';
......
import { Box, Image, Link, Text, chakra, Skeleton } from '@chakra-ui/react'; import { Box, Image, Link, Text, chakra, Skeleton } from '@chakra-ui/react';
import React, { useEffect } from 'react'; import React, { useEffect } from 'react';
import { useAppContext } from 'lib/appContext'; import { useAppContext } from 'lib/contexts/app';
import * as cookies from 'lib/cookies'; import * as cookies from 'lib/cookies';
import { ndash } from 'lib/html-entities'; import { ndash } from 'lib/html-entities';
import isBrowser from 'lib/isBrowser'; import isBrowser from 'lib/isBrowser';
......
import { test, expect } from '@playwright/experimental-ct-react';
import React from 'react';
import type { PaginationParams } from './types';
import TestApp from 'playwright/TestApp';
import Pagination from './Pagination';
test.use({ viewport: { width: 250, height: 50 } });
test('default view', async({ mount }) => {
const props: PaginationParams = {
page: 2,
isVisible: true,
isLoading: false,
hasNextPage: true,
hasPages: true,
canGoBackwards: false,
onNextPageClick: () => {},
onPrevPageClick: () => {},
resetPage: () => {},
};
const component = await mount(
<TestApp>
<Pagination { ...props } w="fit-content"/>
</TestApp>,
);
await expect(component).toHaveScreenshot();
});
import { Button, Skeleton, Flex, Icon, IconButton, chakra } from '@chakra-ui/react'; import { Button, Skeleton, Flex, Icon, IconButton, chakra } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { PaginationParams } from './types';
import arrowIcon from 'icons/arrows/east-mini.svg'; import arrowIcon from 'icons/arrows/east-mini.svg';
export type Props = { interface Props extends PaginationParams {
page: number;
onNextPageClick: () => void;
onPrevPageClick: () => void;
resetPage: () => void;
hasNextPage: boolean;
className?: string; className?: string;
canGoBackwards: boolean;
isLoading?: boolean;
} }
const Pagination = ({ page, onNextPageClick, onPrevPageClick, resetPage, hasNextPage, className, canGoBackwards, isLoading }: Props) => { const Pagination = ({ page, onNextPageClick, onPrevPageClick, resetPage, hasPages, hasNextPage, className, canGoBackwards, isLoading, isVisible }: Props) => {
if (!isVisible) {
return null;
}
const showSkeleton = page === 1 && !hasPages && isLoading;
return ( return (
<Flex <Flex
...@@ -22,28 +23,28 @@ const Pagination = ({ page, onNextPageClick, onPrevPageClick, resetPage, hasNext ...@@ -22,28 +23,28 @@ const Pagination = ({ page, onNextPageClick, onPrevPageClick, resetPage, hasNext
fontSize="sm" fontSize="sm"
alignItems="center" alignItems="center"
> >
<Skeleton isLoaded={ !isLoading } display="inline-block" mr={ 4 } borderRadius="base"> <Skeleton isLoaded={ !showSkeleton } display="inline-block" mr={ 4 } borderRadius="base">
<Button <Button
variant="outline" variant="outline"
size="sm" size="sm"
onClick={ resetPage } onClick={ resetPage }
isDisabled={ page === 1 } isDisabled={ page === 1 || isLoading }
> >
First First
</Button> </Button>
</Skeleton> </Skeleton>
<Skeleton isLoaded={ !isLoading } display="inline-block" mr={ 3 } borderRadius="base"> <Skeleton isLoaded={ !showSkeleton } display="inline-block" mr={ 3 } borderRadius="base">
<IconButton <IconButton
variant="outline" variant="outline"
onClick={ onPrevPageClick } onClick={ onPrevPageClick }
size="sm" size="sm"
aria-label="Next page" aria-label="Prev page"
w="36px" w="36px"
icon={ <Icon as={ arrowIcon } w={ 5 } h={ 5 }/> } icon={ <Icon as={ arrowIcon } w={ 5 } h={ 5 }/> }
isDisabled={ !canGoBackwards || page === 1 } isDisabled={ !canGoBackwards || page === 1 || isLoading }
/> />
</Skeleton> </Skeleton>
<Skeleton isLoaded={ !isLoading } display="inline-block" borderRadius="base"> <Skeleton isLoaded={ !showSkeleton } display="inline-block" borderRadius="base">
<Button <Button
variant="outline" variant="outline"
size="sm" size="sm"
...@@ -51,12 +52,13 @@ const Pagination = ({ page, onNextPageClick, onPrevPageClick, resetPage, hasNext ...@@ -51,12 +52,13 @@ const Pagination = ({ page, onNextPageClick, onPrevPageClick, resetPage, hasNext
borderWidth="1px" borderWidth="1px"
fontWeight={ 400 } fontWeight={ 400 }
h={ 8 } h={ 8 }
minW="36px"
cursor="unset" cursor="unset"
> >
{ page } { page }
</Button> </Button>
</Skeleton> </Skeleton>
<Skeleton isLoaded={ !isLoading } display="inline-block" ml={ 3 } borderRadius="base"> <Skeleton isLoaded={ !showSkeleton } display="inline-block" ml={ 3 } borderRadius="base">
<IconButton <IconButton
variant="outline" variant="outline"
onClick={ onNextPageClick } onClick={ onNextPageClick }
...@@ -64,7 +66,7 @@ const Pagination = ({ page, onNextPageClick, onPrevPageClick, resetPage, hasNext ...@@ -64,7 +66,7 @@ const Pagination = ({ page, onNextPageClick, onPrevPageClick, resetPage, hasNext
aria-label="Next page" aria-label="Next page"
w="36px" w="36px"
icon={ <Icon as={ arrowIcon } w={ 5 } h={ 5 } transform="rotate(180deg)"/> } icon={ <Icon as={ arrowIcon } w={ 5 } h={ 5 } transform="rotate(180deg)"/> }
isDisabled={ !hasNextPage } isDisabled={ !hasNextPage || isLoading }
/> />
</Skeleton> </Skeleton>
{ /* not implemented yet */ } { /* not implemented yet */ }
......
export interface PaginationParams {
page: number;
onNextPageClick: () => void;
onPrevPageClick: () => void;
resetPage: () => void;
hasPages: boolean;
hasNextPage: boolean;
canGoBackwards: boolean;
isLoading: boolean;
isVisible: boolean;
}
import { animateScroll } from 'react-scroll';
import fetch from 'jest-fetch-mock';
import { renderHook, wrapper, act } from 'jest/lib';
import { useRouter, router } from 'jest/mocks/next-router';
import flushPromises from 'jest/utils/flushPromises';
import * as addressMock from 'mocks/address/address';
jest.mock('next/router', () => ({ useRouter }));
jest.mock('react-scroll', () => ({ animateScroll: { scrollToTop: jest.fn() } }));
import type { Params, QueryWithPagesResult } from './useQueryWithPages';
import useQueryWithPages from './useQueryWithPages';
const responses = {
page_empty: {
items: [],
next_page_params: null,
},
page_1: {
items: [ { hash: '11' }, { hash: '12' } ],
next_page_params: {
block_number: 11,
index: 12,
items_count: 13,
},
},
page_2: {
items: [ { hash: '21' }, { hash: '22' } ],
next_page_params: {
block_number: 21,
index: 22,
items_count: 23,
},
},
page_3: {
items: [ { hash: '31' }, { hash: '32' } ],
next_page_params: null,
},
page_filtered: {
items: [ { hash: '41' }, { hash: '42' } ],
next_page_params: {
block_number: 41,
index: 42,
items_count: 43,
},
},
};
beforeEach(() => {
fetch.resetMocks();
});
it('returns correct data if there is only one page', async() => {
const params: Params<'address_txs'> = {
resourceName: 'address_txs',
pathParams: { hash: addressMock.hash },
};
fetch.mockResponse(JSON.stringify(responses.page_empty));
const { result } = renderHook(() => useQueryWithPages(params), { wrapper });
await waitForApiResponse();
expect(result.current.data).toEqual(responses.page_empty);
expect(result.current.pagination).toMatchObject({
page: 1,
canGoBackwards: true,
hasNextPage: false,
isLoading: false,
isVisible: false,
hasPages: false,
});
});
describe('if there are multiple pages', () => {
const params: Params<'address_txs'> = {
resourceName: 'address_txs',
pathParams: { hash: addressMock.hash },
};
it('return correct data for the first page', async() => {
fetch.mockResponse(JSON.stringify(responses.page_1));
const { result } = renderHook(() => useQueryWithPages(params), { wrapper });
await waitForApiResponse();
expect(result.current.data).toEqual(responses.page_1);
expect(result.current.pagination).toMatchObject({
page: 1,
canGoBackwards: true,
hasNextPage: true,
isLoading: false,
isVisible: true,
});
});
describe('correctly navigates forward and backward', () => {
const routerPush = jest.fn(() => Promise.resolve());
let result: {
current: QueryWithPagesResult<'address_txs'>;
};
beforeEach(async() => {
routerPush.mockClear();
useRouter.mockReturnValue({ ...router, pathname: '/current-route', push: routerPush });
fetch.once(JSON.stringify(responses.page_1));
fetch.once(JSON.stringify(responses.page_2));
fetch.once(JSON.stringify(responses.page_3));
fetch.once(JSON.stringify(responses.page_1));
// INITIAL LOAD
const { result: r } = renderHook(() => useQueryWithPages(params), { wrapper });
result = r;
await waitForApiResponse();
});
it('from page 1 to page 2', async() => {
await act(() => {
result.current.pagination.onNextPageClick();
});
await waitForApiResponse();
expect(result.current.data).toEqual(responses.page_2);
expect(result.current.pagination).toMatchObject({
page: 2,
canGoBackwards: true,
hasNextPage: true,
isLoading: false,
isVisible: true,
hasPages: true,
});
expect(routerPush).toHaveBeenCalledTimes(1);
expect(routerPush).toHaveBeenLastCalledWith(
{
pathname: '/current-route',
query: {
next_page_params: encodeURIComponent(JSON.stringify(responses.page_1.next_page_params)),
page: '2',
},
},
undefined,
{ shallow: true },
);
expect(animateScroll.scrollToTop).toHaveBeenCalledTimes(1);
expect(animateScroll.scrollToTop).toHaveBeenLastCalledWith({ duration: 0 });
});
it('from page 2 to page 3', async() => {
await act(async() => {
result.current.pagination.onNextPageClick();
});
await waitForApiResponse();
await act(async() => {
result.current.pagination.onNextPageClick();
});
await waitForApiResponse();
expect(result.current.data).toEqual(responses.page_3);
expect(result.current.pagination).toMatchObject({
page: 3,
canGoBackwards: true,
hasNextPage: false,
isLoading: false,
isVisible: true,
hasPages: true,
});
expect(routerPush).toHaveBeenCalledTimes(2);
expect(routerPush).toHaveBeenLastCalledWith(
{
pathname: '/current-route',
query: {
next_page_params: encodeURIComponent(JSON.stringify(responses.page_2.next_page_params)),
page: '3',
},
},
undefined,
{ shallow: true },
);
expect(animateScroll.scrollToTop).toHaveBeenCalledTimes(2);
expect(animateScroll.scrollToTop).toHaveBeenLastCalledWith({ duration: 0 });
});
it('from page 3 to page 2', async() => {
await act(() => {
result.current.pagination.onNextPageClick();
});
await waitForApiResponse();
await act(() => {
result.current.pagination.onNextPageClick();
});
await waitForApiResponse();
await act(() => {
result.current.pagination.onPrevPageClick();
});
await waitForApiResponse();
expect(result.current.data).toEqual(responses.page_2);
expect(result.current.pagination).toMatchObject({
page: 2,
canGoBackwards: true,
hasNextPage: true,
isLoading: false,
isVisible: true,
hasPages: true,
});
expect(routerPush).toHaveBeenCalledTimes(3);
expect(routerPush).toHaveBeenLastCalledWith(
{
pathname: '/current-route',
query: {
next_page_params: encodeURIComponent(JSON.stringify(responses.page_1.next_page_params)),
page: '2',
},
},
undefined,
{ shallow: true },
);
expect(animateScroll.scrollToTop).toHaveBeenCalledTimes(3);
expect(animateScroll.scrollToTop).toHaveBeenLastCalledWith({ duration: 0 });
});
it('from page 2 to page 1', async() => {
await act(() => {
result.current.pagination.onNextPageClick();
});
await waitForApiResponse();
await act(() => {
result.current.pagination.onNextPageClick();
});
await waitForApiResponse();
await act(() => {
result.current.pagination.onPrevPageClick();
});
await waitForApiResponse();
await act(() => {
result.current.pagination.onPrevPageClick();
});
await waitForApiResponse();
expect(result.current.data).toEqual(responses.page_1);
expect(result.current.pagination).toMatchObject({
page: 1,
canGoBackwards: true,
hasNextPage: true,
isLoading: false,
isVisible: true,
hasPages: true,
});
expect(routerPush).toHaveBeenCalledTimes(4);
expect(routerPush).toHaveBeenLastCalledWith(
{
pathname: '/current-route',
query: {},
},
undefined,
{ shallow: true },
);
expect(animateScroll.scrollToTop).toHaveBeenCalledTimes(4);
expect(animateScroll.scrollToTop).toHaveBeenLastCalledWith({ duration: 0 });
});
});
it('correctly resets the page', async() => {
const routerPush = jest.fn(() => Promise.resolve());
useRouter.mockReturnValue({ ...router, pathname: '/current-route', push: routerPush });
fetch.once(JSON.stringify(responses.page_1));
fetch.once(JSON.stringify(responses.page_2));
fetch.once(JSON.stringify(responses.page_3));
fetch.once(JSON.stringify(responses.page_1));
const { result } = renderHook(() => useQueryWithPages(params), { wrapper });
await waitForApiResponse();
await act(async() => {
result.current.pagination.onNextPageClick();
});
await waitForApiResponse();
await act(async() => {
result.current.pagination.onNextPageClick();
});
await waitForApiResponse();
await act(async() => {
result.current.pagination.resetPage();
});
await waitForApiResponse();
expect(result.current.data).toEqual(responses.page_1);
expect(result.current.pagination).toMatchObject({
page: 1,
canGoBackwards: true,
hasNextPage: true,
isLoading: false,
isVisible: true,
hasPages: true,
});
expect(routerPush).toHaveBeenCalledTimes(3);
expect(routerPush).toHaveBeenLastCalledWith(
{
pathname: '/current-route',
query: {},
},
undefined,
{ shallow: true },
);
expect(animateScroll.scrollToTop).toHaveBeenCalledTimes(3);
expect(animateScroll.scrollToTop).toHaveBeenLastCalledWith({ duration: 0 });
});
it('when navigates between pages can scroll to custom element', async() => {
const scrollRef = {
current: {
scrollIntoView: jest.fn(),
},
};
const params: Params<'address_txs'> = {
resourceName: 'address_txs',
pathParams: { hash: addressMock.hash },
scrollRef: scrollRef as unknown as React.RefObject<HTMLDivElement>,
};
fetch.once(JSON.stringify(responses.page_1));
fetch.once(JSON.stringify(responses.page_2));
const { result } = renderHook(() => useQueryWithPages(params), { wrapper });
await waitForApiResponse();
await act(async() => {
result.current.pagination.onNextPageClick();
});
await waitForApiResponse();
expect(scrollRef.current.scrollIntoView).toHaveBeenCalledTimes(1);
expect(scrollRef.current.scrollIntoView).toHaveBeenCalledWith(true);
});
});
describe('if there is page query param in URL', () => {
it('sets this param as the page number', async() => {
useRouter.mockReturnValueOnce({ ...router, query: { page: '3' } });
const params: Params<'address_txs'> = {
resourceName: 'address_txs',
pathParams: { hash: addressMock.hash },
};
fetch.mockResponse(JSON.stringify(responses.page_empty));
const { result } = renderHook(() => useQueryWithPages(params), { wrapper });
await waitForApiResponse();
expect(result.current.data).toEqual(responses.page_empty);
expect(result.current.pagination).toMatchObject({
page: 3,
canGoBackwards: false,
hasNextPage: false,
isLoading: false,
isVisible: true,
hasPages: true,
});
});
it('correctly navigates to the following pages', async() => {
const routerPush = jest.fn(() => Promise.resolve());
useRouter.mockReturnValue({ ...router, pathname: '/current-route', push: routerPush, query: { page: '2' } });
const params: Params<'address_txs'> = {
resourceName: 'address_txs',
pathParams: { hash: addressMock.hash },
};
fetch.once(JSON.stringify(responses.page_2));
fetch.once(JSON.stringify(responses.page_3));
const { result } = renderHook(() => useQueryWithPages(params), { wrapper });
await waitForApiResponse();
await act(async() => {
result.current.pagination.onNextPageClick();
});
await waitForApiResponse();
expect(result.current.data).toEqual(responses.page_3);
expect(result.current.pagination).toMatchObject({
page: 3,
canGoBackwards: false,
hasNextPage: false,
isLoading: false,
isVisible: true,
hasPages: true,
});
expect(routerPush).toHaveBeenCalledTimes(1);
expect(routerPush).toHaveBeenLastCalledWith(
{
pathname: '/current-route',
query: {
next_page_params: encodeURIComponent(JSON.stringify(responses.page_2.next_page_params)),
page: '3',
},
},
undefined,
{ shallow: true },
);
});
});
describe('queries with filters', () => {
it('reset page when filter is changed', async() => {
const routerPush = jest.fn(() => Promise.resolve());
useRouter.mockReturnValue({ ...router, pathname: '/current-route', push: routerPush, query: { foo: 'bar' } });
const params: Params<'address_txs'> = {
resourceName: 'address_txs',
pathParams: { hash: addressMock.hash },
};
fetch.once(JSON.stringify(responses.page_1));
fetch.once(JSON.stringify(responses.page_2));
fetch.once(JSON.stringify(responses.page_filtered));
const { result } = renderHook(() => useQueryWithPages(params), { wrapper });
await waitForApiResponse();
await act(async() => {
result.current.pagination.onNextPageClick();
});
await waitForApiResponse();
await act(async() => {
result.current.onFilterChange({ filter: 'from' });
});
await waitForApiResponse();
expect(result.current.data).toEqual(responses.page_filtered);
expect(result.current.pagination).toMatchObject({
page: 1,
canGoBackwards: true,
hasNextPage: true,
isLoading: false,
isVisible: true,
hasPages: false,
});
expect(routerPush).toHaveBeenCalledTimes(2);
expect(routerPush).toHaveBeenLastCalledWith(
{
pathname: '/current-route',
query: { filter: 'from', foo: 'bar' },
},
undefined,
{ shallow: true },
);
expect(animateScroll.scrollToTop).toHaveBeenCalledTimes(2);
expect(animateScroll.scrollToTop).toHaveBeenLastCalledWith({ duration: 0 });
});
it('saves filter params in query when navigating between pages', async() => {
const routerPush = jest.fn(() => Promise.resolve());
useRouter.mockReturnValue({ ...router, pathname: '/current-route', push: routerPush, query: { filter: 'from', foo: 'bar' } });
const params: Params<'address_txs'> = {
resourceName: 'address_txs',
pathParams: { hash: addressMock.hash },
};
fetch.once(JSON.stringify(responses.page_1));
fetch.once(JSON.stringify(responses.page_2));
const { result } = renderHook(() => useQueryWithPages(params), { wrapper });
await waitForApiResponse();
await act(async() => {
result.current.pagination.onNextPageClick();
});
await waitForApiResponse();
expect(routerPush).toHaveBeenCalledTimes(1);
expect(routerPush).toHaveBeenLastCalledWith(
{
pathname: '/current-route',
query: {
filter: 'from',
foo: 'bar',
next_page_params: encodeURIComponent(JSON.stringify(responses.page_1.next_page_params)),
page: '2',
},
},
undefined,
{ shallow: true },
);
});
});
async function waitForApiResponse() {
await flushPromises();
await act(flushPromises);
}
import type { UseQueryResult } from '@tanstack/react-query';
import { useQueryClient } from '@tanstack/react-query'; import { useQueryClient } from '@tanstack/react-query';
import omit from 'lodash/omit'; import omit from 'lodash/omit';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import React, { useCallback } from 'react'; import React, { useCallback } from 'react';
import { animateScroll } from 'react-scroll'; import { animateScroll } from 'react-scroll';
import type { PaginatedResources, PaginationFilters } from 'lib/api/resources'; import type { PaginationParams } from './types';
import type { PaginatedResources, PaginationFilters, ResourceError, ResourcePayload } from 'lib/api/resources';
import { RESOURCES } from 'lib/api/resources'; import { RESOURCES } from 'lib/api/resources';
import type { Params as UseApiQueryParams } from 'lib/api/useApiQuery'; import type { Params as UseApiQueryParams } from 'lib/api/useApiQuery';
import useApiQuery from 'lib/api/useApiQuery'; import useApiQuery from 'lib/api/useApiQuery';
import getQueryParamString from 'lib/router/getQueryParamString'; import getQueryParamString from 'lib/router/getQueryParamString';
interface Params<Resource extends PaginatedResources> { export interface Params<Resource extends PaginatedResources> {
resourceName: Resource; resourceName: Resource;
options?: UseApiQueryParams<Resource>['queryOptions']; options?: UseApiQueryParams<Resource>['queryOptions'];
pathParams?: UseApiQueryParams<Resource>['pathParams']; pathParams?: UseApiQueryParams<Resource>['pathParams'];
...@@ -30,13 +33,20 @@ function getPaginationParamsFromQuery(queryString: string | Array<string> | unde ...@@ -30,13 +33,20 @@ function getPaginationParamsFromQuery(queryString: string | Array<string> | unde
return {}; return {};
} }
export type QueryWithPagesResult<Resource extends PaginatedResources> =
UseQueryResult<ResourcePayload<Resource>, ResourceError<unknown>> &
{
onFilterChange: (filters: PaginationFilters<Resource>) => void;
pagination: PaginationParams;
}
export default function useQueryWithPages<Resource extends PaginatedResources>({ export default function useQueryWithPages<Resource extends PaginatedResources>({
resourceName, resourceName,
filters, filters,
options, options,
pathParams, pathParams,
scrollRef, scrollRef,
}: Params<Resource>) { }: Params<Resource>): QueryWithPagesResult<Resource> {
const resource = RESOURCES[resourceName]; const resource = RESOURCES[resourceName];
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const router = useRouter(); const router = useRouter();
...@@ -45,7 +55,7 @@ export default function useQueryWithPages<Resource extends PaginatedResources>({ ...@@ -45,7 +55,7 @@ export default function useQueryWithPages<Resource extends PaginatedResources>({
const [ pageParams, setPageParams ] = React.useState<Record<number, NextPageParams>>({ const [ pageParams, setPageParams ] = React.useState<Record<number, NextPageParams>>({
[page]: getPaginationParamsFromQuery(router.query.next_page_params), [page]: getPaginationParamsFromQuery(router.query.next_page_params),
}); });
const [ hasPagination, setHasPagination ] = React.useState(page > 1); const [ hasPages, setHasPages ] = React.useState(page > 1);
const isMounted = React.useRef(false); const isMounted = React.useRef(false);
const canGoBackwards = React.useRef(!router.query.page); const canGoBackwards = React.useRef(!router.query.page);
...@@ -83,7 +93,7 @@ export default function useQueryWithPages<Resource extends PaginatedResources>({ ...@@ -83,7 +93,7 @@ export default function useQueryWithPages<Resource extends PaginatedResources>({
next_page_params: encodeURIComponent(JSON.stringify(data.next_page_params)), next_page_params: encodeURIComponent(JSON.stringify(data.next_page_params)),
}; };
setHasPagination(true); setHasPages(true);
scrollToTop(); scrollToTop();
router.push({ pathname: router.pathname, query: nextPageQuery }, undefined, { shallow: true }); router.push({ pathname: router.pathname, query: nextPageQuery }, undefined, { shallow: true });
}, [ data?.next_page_params, page, router, scrollToTop ]); }, [ data?.next_page_params, page, router, scrollToTop ]);
...@@ -106,7 +116,6 @@ export default function useQueryWithPages<Resource extends PaginatedResources>({ ...@@ -106,7 +116,6 @@ export default function useQueryWithPages<Resource extends PaginatedResources>({
setPage(prev => prev - 1); setPage(prev => prev - 1);
page === 2 && queryClient.removeQueries({ queryKey: [ resourceName ] }); page === 2 && queryClient.removeQueries({ queryKey: [ resourceName ] });
}); });
setHasPagination(true);
}, [ router, page, pageParams, scrollToTop, queryClient, resourceName ]); }, [ router, page, pageParams, scrollToTop, queryClient, resourceName ]);
const resetPage = useCallback(() => { const resetPage = useCallback(() => {
...@@ -125,8 +134,6 @@ export default function useQueryWithPages<Resource extends PaginatedResources>({ ...@@ -125,8 +134,6 @@ export default function useQueryWithPages<Resource extends PaginatedResources>({
queryClient.removeQueries({ queryKey: [ resourceName ], type: 'inactive' }); queryClient.removeQueries({ queryKey: [ resourceName ], type: 'inactive' });
}, 100); }, 100);
}); });
setHasPagination(true);
}, [ queryClient, resourceName, router, scrollToTop ]); }, [ queryClient, resourceName, router, scrollToTop ]);
const onFilterChange = useCallback((newFilters: PaginationFilters<Resource> | undefined) => { const onFilterChange = useCallback((newFilters: PaginationFilters<Resource> | undefined) => {
...@@ -138,7 +145,6 @@ export default function useQueryWithPages<Resource extends PaginatedResources>({ ...@@ -138,7 +145,6 @@ export default function useQueryWithPages<Resource extends PaginatedResources>({
} }
}); });
} }
setHasPagination(false);
scrollToTop(); scrollToTop();
router.push( router.push(
{ {
...@@ -148,25 +154,27 @@ export default function useQueryWithPages<Resource extends PaginatedResources>({ ...@@ -148,25 +154,27 @@ export default function useQueryWithPages<Resource extends PaginatedResources>({
undefined, undefined,
{ shallow: true }, { shallow: true },
).then(() => { ).then(() => {
setHasPages(false);
setPage(1); setPage(1);
setPageParams({}); setPageParams({});
}); });
}, [ router, resource.filterFields, scrollToTop ]); }, [ router, resource.filterFields, scrollToTop ]);
const nextPageParams = data?.next_page_params; const nextPageParams = data?.next_page_params;
const hasNextPage = nextPageParams ? Object.keys(nextPageParams).length > 0 : false;
const pagination = { const pagination = {
page, page,
onNextPageClick, onNextPageClick,
onPrevPageClick, onPrevPageClick,
resetPage, resetPage,
hasNextPage: nextPageParams ? Object.keys(nextPageParams).length > 0 : false, hasPages,
hasNextPage,
canGoBackwards: canGoBackwards.current, canGoBackwards: canGoBackwards.current,
isLoading: queryResult.isPlaceholderData && !hasPagination, isLoading: queryResult.isPlaceholderData,
isVisible: hasPages || hasNextPage,
}; };
const isPaginationVisible = hasPagination || (!queryResult.isLoading && !queryResult.isError && pagination.hasNextPage);
React.useEffect(() => { React.useEffect(() => {
if (page !== 1 && isMounted.current) { if (page !== 1 && isMounted.current) {
queryClient.cancelQueries({ queryKey: [ resourceName ] }); queryClient.cancelQueries({ queryKey: [ resourceName ] });
...@@ -182,5 +190,5 @@ export default function useQueryWithPages<Resource extends PaginatedResources>({ ...@@ -182,5 +190,5 @@ export default function useQueryWithPages<Resource extends PaginatedResources>({
}, 0); }, 0);
}, []); }, []);
return { ...queryResult, pagination, onFilterChange, isPaginationVisible }; return { ...queryResult, pagination, onFilterChange };
} }
...@@ -7,6 +7,7 @@ import * as cookies from 'lib/cookies'; ...@@ -7,6 +7,7 @@ import * as cookies from 'lib/cookies';
import authFixture from 'playwright/fixtures/auth'; import authFixture from 'playwright/fixtures/auth';
import contextWithEnvs, { createContextWithEnvs } from 'playwright/fixtures/contextWithEnvs'; import contextWithEnvs, { createContextWithEnvs } from 'playwright/fixtures/contextWithEnvs';
import TestApp from 'playwright/TestApp'; import TestApp from 'playwright/TestApp';
import * as app from 'playwright/utils/app';
import * as configs from 'playwright/utils/configs'; import * as configs from 'playwright/utils/configs';
import NavigationDesktop from './NavigationDesktop'; import NavigationDesktop from './NavigationDesktop';
...@@ -150,7 +151,7 @@ base.describe('cookie set to false', () => { ...@@ -150,7 +151,7 @@ base.describe('cookie set to false', () => {
const context = await createContextWithEnvs(browser, [ const context = await createContextWithEnvs(browser, [
{ name: 'NEXT_PUBLIC_FEATURED_NETWORKS', value: FEATURED_NETWORKS_URL }, { name: 'NEXT_PUBLIC_FEATURED_NETWORKS', value: FEATURED_NETWORKS_URL },
]); ]);
context.addCookies([ { name: cookies.NAMES.NAV_BAR_COLLAPSED, value: 'false', domain: 'localhost', path: '/' } ]); context.addCookies([ { name: cookies.NAMES.NAV_BAR_COLLAPSED, value: 'false', domain: app.domain, path: '/' } ]);
use(context); use(context);
}, },
}); });
......
...@@ -4,7 +4,7 @@ import React from 'react'; ...@@ -4,7 +4,7 @@ import React from 'react';
import appConfig from 'configs/app/config'; import appConfig from 'configs/app/config';
import chevronIcon from 'icons/arrows/east-mini.svg'; import chevronIcon from 'icons/arrows/east-mini.svg';
import testnetIcon from 'icons/testnet.svg'; import testnetIcon from 'icons/testnet.svg';
import { useAppContext } from 'lib/appContext'; import { useAppContext } from 'lib/contexts/app';
import * as cookies from 'lib/cookies'; import * as cookies from 'lib/cookies';
import useHasAccount from 'lib/hooks/useHasAccount'; import useHasAccount from 'lib/hooks/useHasAccount';
import useNavItems, { isGroupItem } from 'lib/hooks/useNavItems'; import useNavItems, { isGroupItem } from 'lib/hooks/useNavItems';
......
...@@ -4,6 +4,7 @@ import React from 'react'; ...@@ -4,6 +4,7 @@ import React from 'react';
import * as profileMock from 'mocks/user/profile'; import * as profileMock from 'mocks/user/profile';
import authFixture from 'playwright/fixtures/auth'; import authFixture from 'playwright/fixtures/auth';
import TestApp from 'playwright/TestApp'; import TestApp from 'playwright/TestApp';
import * as app from 'playwright/utils/app';
import buildApiUrl from 'playwright/utils/buildApiUrl'; import buildApiUrl from 'playwright/utils/buildApiUrl';
import ProfileMenuDesktop from './ProfileMenuDesktop'; import ProfileMenuDesktop from './ProfileMenuDesktop';
...@@ -23,7 +24,7 @@ test('no auth', async({ mount, page }) => { ...@@ -23,7 +24,7 @@ test('no auth', async({ mount, page }) => {
); );
await component.locator('.identicon').click(); await component.locator('.identicon').click();
expect(page.url()).toBe('http://localhost:3100/auth/auth0?path=%2F'); expect(page.url()).toBe(`${ app.url }/auth/auth0?path=%2F`);
}); });
test.describe('auth', () => { test.describe('auth', () => {
......
...@@ -4,6 +4,7 @@ import React from 'react'; ...@@ -4,6 +4,7 @@ import React from 'react';
import * as profileMock from 'mocks/user/profile'; import * as profileMock from 'mocks/user/profile';
import authFixture from 'playwright/fixtures/auth'; import authFixture from 'playwright/fixtures/auth';
import TestApp from 'playwright/TestApp'; import TestApp from 'playwright/TestApp';
import * as app from 'playwright/utils/app';
import buildApiUrl from 'playwright/utils/buildApiUrl'; import buildApiUrl from 'playwright/utils/buildApiUrl';
import ProfileMenuMobile from './ProfileMenuMobile'; import ProfileMenuMobile from './ProfileMenuMobile';
...@@ -23,7 +24,7 @@ test('no auth', async({ mount, page }) => { ...@@ -23,7 +24,7 @@ test('no auth', async({ mount, page }) => {
); );
await component.locator('.identicon').click(); await component.locator('.identicon').click();
expect(page.url()).toBe('http://localhost:3100/auth/auth0?path=%2F'); expect(page.url()).toBe(`${ app.url }/auth/auth0?path=%2F`);
}); });
test.use({ viewport: devices['iPhone 13 Pro'].viewport }); test.use({ viewport: devices['iPhone 13 Pro'].viewport });
......
...@@ -3,12 +3,12 @@ import type { UseQueryResult } from '@tanstack/react-query'; ...@@ -3,12 +3,12 @@ import type { UseQueryResult } from '@tanstack/react-query';
import _uniqBy from 'lodash/uniqBy'; import _uniqBy from 'lodash/uniqBy';
import React from 'react'; import React from 'react';
import type { SearchRedirectResult, SearchResult, SearchResultItem } from 'types/api/search'; import type { SearchRedirectResult, SearchResultItem } from 'types/api/search';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
import TextAd from 'ui/shared/ad/TextAd'; import TextAd from 'ui/shared/ad/TextAd';
import ContentLoader from 'ui/shared/ContentLoader'; import ContentLoader from 'ui/shared/ContentLoader';
import type { Props as PaginationProps } from 'ui/shared/Pagination'; import type { QueryWithPagesResult } from 'ui/shared/pagination/useQueryWithPages';
import SearchBarSuggestItem from './SearchBarSuggestItem'; import SearchBarSuggestItem from './SearchBarSuggestItem';
...@@ -31,9 +31,7 @@ const getUniqueIdentifier = (item: SearchResultItem) => { ...@@ -31,9 +31,7 @@ const getUniqueIdentifier = (item: SearchResultItem) => {
}; };
interface Props { interface Props {
query: UseQueryResult<SearchResult> & { query: QueryWithPagesResult<'search'>;
pagination: PaginationProps;
};
redirectCheckQuery: UseQueryResult<SearchRedirectResult>; redirectCheckQuery: UseQueryResult<SearchRedirectResult>;
searchTerm: string; searchTerm: string;
onItemClick: (event: React.MouseEvent<HTMLAnchorElement>) => void; onItemClick: (event: React.MouseEvent<HTMLAnchorElement>) => void;
......
...@@ -3,11 +3,11 @@ import React from 'react'; ...@@ -3,11 +3,11 @@ import React from 'react';
import useApiQuery from 'lib/api/useApiQuery'; 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 useUpdateValueEffect from 'lib/hooks/useUpdateValueEffect'; import useUpdateValueEffect from 'lib/hooks/useUpdateValueEffect';
import getQueryParamString from 'lib/router/getQueryParamString'; import getQueryParamString from 'lib/router/getQueryParamString';
import { SEARCH_RESULT_ITEM, SEARCH_RESULT_NEXT_PAGE_PARAMS } from 'stubs/search'; import { SEARCH_RESULT_ITEM, SEARCH_RESULT_NEXT_PAGE_PARAMS } from 'stubs/search';
import { generateListStub } from 'stubs/utils'; import { generateListStub } from 'stubs/utils';
import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages';
export default function useSearchQuery(isSearchPage = false) { export default function useSearchQuery(isSearchPage = false) {
const router = useRouter(); const router = useRouter();
......
import { Box } from '@chakra-ui/react'; import { Box } from '@chakra-ui/react';
import type { UseQueryResult } from '@tanstack/react-query';
import React from 'react'; import React from 'react';
import type { TokenHolders, TokenInfo } from 'types/api/token'; import type { TokenInfo } from 'types/api/token';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
import ActionBar from 'ui/shared/ActionBar'; import ActionBar from 'ui/shared/ActionBar';
import DataFetchAlert from 'ui/shared/DataFetchAlert'; import DataFetchAlert from 'ui/shared/DataFetchAlert';
import DataListDisplay from 'ui/shared/DataListDisplay'; import DataListDisplay from 'ui/shared/DataListDisplay';
import Pagination from 'ui/shared/Pagination'; import Pagination from 'ui/shared/pagination/Pagination';
import type { Props as PaginationProps } from 'ui/shared/Pagination'; import type { QueryWithPagesResult } from 'ui/shared/pagination/useQueryWithPages';
import TokenHoldersList from './TokenHoldersList'; import TokenHoldersList from './TokenHoldersList';
import TokenHoldersTable from './TokenHoldersTable'; import TokenHoldersTable from './TokenHoldersTable';
type Props = { type Props = {
token?: TokenInfo; token?: TokenInfo;
holdersQuery: UseQueryResult<TokenHolders> & { holdersQuery: QueryWithPagesResult<'token_holders'>;
pagination: PaginationProps;
isPaginationVisible: boolean;
};
} }
const TokenHoldersContent = ({ holdersQuery, token }: Props) => { const TokenHoldersContent = ({ holdersQuery, token }: Props) => {
...@@ -29,7 +25,7 @@ const TokenHoldersContent = ({ holdersQuery, token }: Props) => { ...@@ -29,7 +25,7 @@ const TokenHoldersContent = ({ holdersQuery, token }: Props) => {
return <DataFetchAlert/>; return <DataFetchAlert/>;
} }
const actionBar = isMobile && holdersQuery.isPaginationVisible && ( const actionBar = isMobile && holdersQuery.pagination.isVisible && (
<ActionBar mt={ -6 }> <ActionBar mt={ -6 }>
<Pagination ml="auto" { ...holdersQuery.pagination }/> <Pagination ml="auto" { ...holdersQuery.pagination }/>
</ActionBar> </ActionBar>
...@@ -43,7 +39,7 @@ const TokenHoldersContent = ({ holdersQuery, token }: Props) => { ...@@ -43,7 +39,7 @@ const TokenHoldersContent = ({ holdersQuery, token }: Props) => {
<TokenHoldersTable <TokenHoldersTable
data={ items } data={ items }
token={ token } token={ token }
top={ holdersQuery.isPaginationVisible ? 80 : 0 } top={ holdersQuery.pagination.isVisible ? 80 : 0 }
isLoading={ holdersQuery.isPlaceholderData } isLoading={ holdersQuery.isPlaceholderData }
/> />
</Box> </Box>
......
...@@ -19,10 +19,9 @@ test('base view +@mobile', async({ mount }) => { ...@@ -19,10 +19,9 @@ test('base view +@mobile', async({ mount }) => {
items: [ tokenInstanse, tokenInstanse, tokenInstanse ], items: [ tokenInstanse, tokenInstanse, tokenInstanse ],
next_page_params: { unique_token: 1 }, next_page_params: { unique_token: 1 },
}, },
isPaginationVisible: true,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: // @ts-ignore:
pagination: { page: 1 }, pagination: { page: 1, isVisible: true },
}} }}
/> />
</TestApp>, </TestApp>,
......
import { Grid } from '@chakra-ui/react'; import { Grid } from '@chakra-ui/react';
import type { UseQueryResult } from '@tanstack/react-query';
import React from 'react'; import React from 'react';
import type { TokenInventoryResponse } from 'types/api/token';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
import ActionBar from 'ui/shared/ActionBar'; import ActionBar from 'ui/shared/ActionBar';
import DataListDisplay from 'ui/shared/DataListDisplay'; import DataListDisplay from 'ui/shared/DataListDisplay';
import type { Props as PaginationProps } from 'ui/shared/Pagination'; import Pagination from 'ui/shared/pagination/Pagination';
import Pagination from 'ui/shared/Pagination'; import type { QueryWithPagesResult } from 'ui/shared/pagination/useQueryWithPages';
import TokenInventoryItem from './TokenInventoryItem'; import TokenInventoryItem from './TokenInventoryItem';
type Props = { type Props = {
inventoryQuery: UseQueryResult<TokenInventoryResponse> & { inventoryQuery: QueryWithPagesResult<'token_inventory'>;
pagination: PaginationProps;
isPaginationVisible: boolean;
};
} }
const TokenInventory = ({ inventoryQuery }: Props) => { const TokenInventory = ({ inventoryQuery }: Props) => {
const isMobile = useIsMobile(); const isMobile = useIsMobile();
const actionBar = isMobile && inventoryQuery.isPaginationVisible && ( const actionBar = isMobile && inventoryQuery.pagination.isVisible && (
<ActionBar mt={ -6 }> <ActionBar mt={ -6 }>
<Pagination ml="auto" { ...inventoryQuery.pagination }/> <Pagination ml="auto" { ...inventoryQuery.pagination }/>
</ActionBar> </ActionBar>
......
...@@ -19,10 +19,9 @@ test('erc20 +@mobile', async({ mount }) => { ...@@ -19,10 +19,9 @@ test('erc20 +@mobile', async({ mount }) => {
items: [ tokenTransferMock.erc20 ], items: [ tokenTransferMock.erc20 ],
next_page_params: null, next_page_params: null,
}, },
isPaginationVisible: true,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: // @ts-ignore:
pagination: { page: 1 }, pagination: { page: 1, isVisible: true },
}} }}
/> />
</TestApp>, </TestApp>,
...@@ -43,10 +42,9 @@ test('erc721 +@mobile', async({ mount }) => { ...@@ -43,10 +42,9 @@ test('erc721 +@mobile', async({ mount }) => {
items: [ tokenTransferMock.erc721 ], items: [ tokenTransferMock.erc721 ],
next_page_params: null, next_page_params: null,
}, },
isPaginationVisible: true,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: // @ts-ignore:
pagination: { page: 1 }, pagination: { page: 1, isVisible: true },
}} }}
/> />
</TestApp>, </TestApp>,
...@@ -72,10 +70,9 @@ test('erc1155 +@mobile', async({ mount }) => { ...@@ -72,10 +70,9 @@ test('erc1155 +@mobile', async({ mount }) => {
], ],
next_page_params: null, next_page_params: null,
}, },
isPaginationVisible: true,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: // @ts-ignore:
pagination: { page: 1 }, pagination: { page: 1, isVisible: true },
}} }}
/> />
</TestApp>, </TestApp>,
......
import { Box } from '@chakra-ui/react'; import { Box } from '@chakra-ui/react';
import type { UseQueryResult } from '@tanstack/react-query';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import React from 'react'; import React from 'react';
import type { SocketMessage } from 'lib/socket/types'; import type { SocketMessage } from 'lib/socket/types';
import type { TokenInfo } from 'types/api/token'; import type { TokenInfo } from 'types/api/token';
import type { TokenTransferResponse } from 'types/api/tokenTransfer';
import useGradualIncrement from 'lib/hooks/useGradualIncrement'; import useGradualIncrement from 'lib/hooks/useGradualIncrement';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
...@@ -13,17 +11,14 @@ import useSocketChannel from 'lib/socket/useSocketChannel'; ...@@ -13,17 +11,14 @@ import useSocketChannel from 'lib/socket/useSocketChannel';
import useSocketMessage from 'lib/socket/useSocketMessage'; import useSocketMessage from 'lib/socket/useSocketMessage';
import ActionBar from 'ui/shared/ActionBar'; import ActionBar from 'ui/shared/ActionBar';
import DataListDisplay from 'ui/shared/DataListDisplay'; import DataListDisplay from 'ui/shared/DataListDisplay';
import Pagination from 'ui/shared/Pagination'; import Pagination from 'ui/shared/pagination/Pagination';
import type { Props as PaginationProps } from 'ui/shared/Pagination'; import type { QueryWithPagesResult } from 'ui/shared/pagination/useQueryWithPages';
import * as SocketNewItemsNotice from 'ui/shared/SocketNewItemsNotice'; import * as SocketNewItemsNotice from 'ui/shared/SocketNewItemsNotice';
import TokenTransferList from 'ui/token/TokenTransfer/TokenTransferList'; import TokenTransferList from 'ui/token/TokenTransfer/TokenTransferList';
import TokenTransferTable from 'ui/token/TokenTransfer/TokenTransferTable'; import TokenTransferTable from 'ui/token/TokenTransfer/TokenTransferTable';
type Props = { type Props = {
transfersQuery: UseQueryResult<TokenTransferResponse> & { transfersQuery: QueryWithPagesResult<'token_transfers'> | QueryWithPagesResult<'token_instance_transfers'>;
pagination: PaginationProps;
isPaginationVisible: boolean;
};
tokenId?: string; tokenId?: string;
token?: TokenInfo; token?: TokenInfo;
} }
...@@ -31,7 +26,7 @@ type Props = { ...@@ -31,7 +26,7 @@ type Props = {
const TokenTransfer = ({ transfersQuery, tokenId, token }: Props) => { const TokenTransfer = ({ transfersQuery, tokenId, token }: Props) => {
const isMobile = useIsMobile(); const isMobile = useIsMobile();
const router = useRouter(); const router = useRouter();
const { isError, isPlaceholderData, data, pagination, isPaginationVisible } = transfersQuery; const { isError, isPlaceholderData, data, pagination } = transfersQuery;
const [ newItemsCount, setNewItemsCount ] = useGradualIncrement(0); const [ newItemsCount, setNewItemsCount ] = useGradualIncrement(0);
const [ socketAlert, setSocketAlert ] = React.useState(''); const [ socketAlert, setSocketAlert ] = React.useState('');
...@@ -66,7 +61,7 @@ const TokenTransfer = ({ transfersQuery, tokenId, token }: Props) => { ...@@ -66,7 +61,7 @@ const TokenTransfer = ({ transfersQuery, tokenId, token }: Props) => {
<Box display={{ base: 'none', lg: 'block' }}> <Box display={{ base: 'none', lg: 'block' }}>
<TokenTransferTable <TokenTransferTable
data={ data?.items } data={ data?.items }
top={ isPaginationVisible ? 80 : 0 } top={ pagination.isVisible ? 80 : 0 }
showSocketInfo={ pagination.page === 1 } showSocketInfo={ pagination.page === 1 }
socketInfoAlert={ socketAlert } socketInfoAlert={ socketAlert }
socketInfoNum={ newItemsCount } socketInfoNum={ newItemsCount }
...@@ -90,7 +85,7 @@ const TokenTransfer = ({ transfersQuery, tokenId, token }: Props) => { ...@@ -90,7 +85,7 @@ const TokenTransfer = ({ transfersQuery, tokenId, token }: Props) => {
</> </>
) : null; ) : null;
const actionBar = isMobile && isPaginationVisible ? ( const actionBar = isMobile && pagination.isVisible ? (
<ActionBar mt={ -6 }> <ActionBar mt={ -6 }>
<Pagination ml="auto" { ...pagination }/> <Pagination ml="auto" { ...pagination }/>
</ActionBar> </ActionBar>
......
...@@ -6,7 +6,6 @@ import type { TokenType } from 'types/api/token'; ...@@ -6,7 +6,6 @@ import type { TokenType } from 'types/api/token';
import getFilterValuesFromQuery from 'lib/getFilterValuesFromQuery'; import getFilterValuesFromQuery from 'lib/getFilterValuesFromQuery';
import useDebounce from 'lib/hooks/useDebounce'; import useDebounce from 'lib/hooks/useDebounce';
import useQueryWithPages from 'lib/hooks/useQueryWithPages';
import { apos } from 'lib/html-entities'; import { apos } from 'lib/html-entities';
import TOKEN_TYPE from 'lib/token/tokenTypes'; import TOKEN_TYPE from 'lib/token/tokenTypes';
import { TOKEN_INFO_ERC_20 } from 'stubs/token'; import { TOKEN_INFO_ERC_20 } from 'stubs/token';
...@@ -17,7 +16,8 @@ import DataListDisplay from 'ui/shared/DataListDisplay'; ...@@ -17,7 +16,8 @@ import DataListDisplay from 'ui/shared/DataListDisplay';
import FilterInput from 'ui/shared/filters/FilterInput'; import FilterInput from 'ui/shared/filters/FilterInput';
import PopoverFilter from 'ui/shared/filters/PopoverFilter'; import PopoverFilter from 'ui/shared/filters/PopoverFilter';
import TokenTypeFilter from 'ui/shared/filters/TokenTypeFilter'; import TokenTypeFilter from 'ui/shared/filters/TokenTypeFilter';
import Pagination from 'ui/shared/Pagination'; import Pagination from 'ui/shared/pagination/Pagination';
import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages';
import TokensListItem from './TokensListItem'; import TokensListItem from './TokensListItem';
import TokensTable from './TokensTable'; import TokensTable from './TokensTable';
...@@ -32,7 +32,7 @@ const Tokens = () => { ...@@ -32,7 +32,7 @@ const Tokens = () => {
const debouncedFilter = useDebounce(filter, 300); const debouncedFilter = useDebounce(filter, 300);
const { isError, isPlaceholderData, data, isPaginationVisible, pagination, onFilterChange } = useQueryWithPages({ const { isError, isPlaceholderData, data, pagination, onFilterChange } = useQueryWithPages({
resourceName: 'tokens', resourceName: 'tokens',
filters: { q: debouncedFilter, type }, filters: { q: debouncedFilter, type },
options: { options: {
...@@ -92,7 +92,7 @@ const Tokens = () => { ...@@ -92,7 +92,7 @@ const Tokens = () => {
{ typeFilter } { typeFilter }
{ filterInput } { filterInput }
</HStack> </HStack>
{ isPaginationVisible && <Pagination ml="auto" { ...pagination }/> } <Pagination ml="auto" { ...pagination }/>
</ActionBar> </ActionBar>
</> </>
); );
......
...@@ -4,7 +4,6 @@ import React from 'react'; ...@@ -4,7 +4,6 @@ import React from 'react';
import type { InternalTransaction } from 'types/api/internalTransaction'; import type { InternalTransaction } from 'types/api/internalTransaction';
import { SECOND } from 'lib/consts'; import { SECOND } from 'lib/consts';
import useQueryWithPages from 'lib/hooks/useQueryWithPages';
// import { apos } from 'lib/html-entities'; // import { apos } from 'lib/html-entities';
import { INTERNAL_TX } from 'stubs/internalTx'; import { INTERNAL_TX } from 'stubs/internalTx';
import { generateListStub } from 'stubs/utils'; import { generateListStub } from 'stubs/utils';
...@@ -12,7 +11,8 @@ import ActionBar from 'ui/shared/ActionBar'; ...@@ -12,7 +11,8 @@ import ActionBar from 'ui/shared/ActionBar';
import DataListDisplay from 'ui/shared/DataListDisplay'; import DataListDisplay from 'ui/shared/DataListDisplay';
// import FilterInput from 'ui/shared/filters/FilterInput'; // import FilterInput from 'ui/shared/filters/FilterInput';
// import TxInternalsFilter from 'ui/tx/internals/TxInternalsFilter'; // import TxInternalsFilter from 'ui/tx/internals/TxInternalsFilter';
import Pagination from 'ui/shared/Pagination'; import Pagination from 'ui/shared/pagination/Pagination';
import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages';
import TxInternalsList from 'ui/tx/internals/TxInternalsList'; import TxInternalsList from 'ui/tx/internals/TxInternalsList';
import TxInternalsTable from 'ui/tx/internals/TxInternalsTable'; import TxInternalsTable from 'ui/tx/internals/TxInternalsTable';
import type { Sort, SortField } from 'ui/tx/internals/utils'; import type { Sort, SortField } from 'ui/tx/internals/utils';
...@@ -72,7 +72,7 @@ const TxInternals = () => { ...@@ -72,7 +72,7 @@ const TxInternals = () => {
// const [ searchTerm, setSearchTerm ] = React.useState<string>(''); // const [ searchTerm, setSearchTerm ] = React.useState<string>('');
const [ sort, setSort ] = React.useState<Sort>(); const [ sort, setSort ] = React.useState<Sort>();
const txInfo = useFetchTxInfo({ updateDelay: 5 * SECOND }); const txInfo = useFetchTxInfo({ updateDelay: 5 * SECOND });
const { data, isPlaceholderData, isError, pagination, isPaginationVisible } = useQueryWithPages({ const { data, isPlaceholderData, isError, pagination } = useQueryWithPages({
resourceName: 'tx_internal_txs', resourceName: 'tx_internal_txs',
pathParams: { hash: txInfo.data?.hash }, pathParams: { hash: txInfo.data?.hash },
options: { options: {
...@@ -112,14 +112,14 @@ const TxInternals = () => { ...@@ -112,14 +112,14 @@ const TxInternals = () => {
data={ filteredData } data={ filteredData }
sort={ sort } sort={ sort }
onSortToggle={ handleSortToggle } onSortToggle={ handleSortToggle }
top={ isPaginationVisible ? 80 : 0 } top={ pagination.isVisible ? 80 : 0 }
isLoading={ isPlaceholderData } isLoading={ isPlaceholderData }
/> />
</Hide> </Hide>
</> </>
) : null; ) : null;
const actionBar = isPaginationVisible ? ( const actionBar = pagination.isVisible ? (
<ActionBar mt={ -6 }> <ActionBar mt={ -6 }>
{ /* <TxInternalsFilter onFilterChange={ handleFilterChange } defaultFilters={ filters } appliedFiltersNum={ filters.length }/> */ } { /* <TxInternalsFilter onFilterChange={ handleFilterChange } defaultFilters={ filters } appliedFiltersNum={ filters.length }/> */ }
{ /* <FilterInput onChange={ setSearchTerm } maxW="360px" ml={ 3 } size="xs" placeholder="Search by addresses, hash, method..."/> */ } { /* <FilterInput onChange={ setSearchTerm } maxW="360px" ml={ 3 } size="xs" placeholder="Search by addresses, hash, method..."/> */ }
......
...@@ -2,20 +2,20 @@ import { Box, Text } from '@chakra-ui/react'; ...@@ -2,20 +2,20 @@ import { Box, Text } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import { SECOND } from 'lib/consts'; import { SECOND } from 'lib/consts';
import useQueryWithPages from 'lib/hooks/useQueryWithPages';
import { LOG } from 'stubs/log'; import { LOG } from 'stubs/log';
import { generateListStub } from 'stubs/utils'; import { generateListStub } from 'stubs/utils';
import ActionBar from 'ui/shared/ActionBar'; import ActionBar from 'ui/shared/ActionBar';
import DataFetchAlert from 'ui/shared/DataFetchAlert'; import DataFetchAlert from 'ui/shared/DataFetchAlert';
import LogItem from 'ui/shared/logs/LogItem'; import LogItem from 'ui/shared/logs/LogItem';
import Pagination from 'ui/shared/Pagination'; import Pagination from 'ui/shared/pagination/Pagination';
import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages';
import TxPendingAlert from 'ui/tx/TxPendingAlert'; import TxPendingAlert from 'ui/tx/TxPendingAlert';
import TxSocketAlert from 'ui/tx/TxSocketAlert'; import TxSocketAlert from 'ui/tx/TxSocketAlert';
import useFetchTxInfo from 'ui/tx/useFetchTxInfo'; import useFetchTxInfo from 'ui/tx/useFetchTxInfo';
const TxLogs = () => { const TxLogs = () => {
const txInfo = useFetchTxInfo({ updateDelay: 5 * SECOND }); const txInfo = useFetchTxInfo({ updateDelay: 5 * SECOND });
const { data, isPlaceholderData, isError, pagination, isPaginationVisible } = useQueryWithPages({ const { data, isPlaceholderData, isError, pagination } = useQueryWithPages({
resourceName: 'tx_logs', resourceName: 'tx_logs',
pathParams: { hash: txInfo.data?.hash }, pathParams: { hash: txInfo.data?.hash },
options: { options: {
...@@ -38,7 +38,7 @@ const TxLogs = () => { ...@@ -38,7 +38,7 @@ const TxLogs = () => {
return ( return (
<Box> <Box>
{ isPaginationVisible && ( { pagination.isVisible && (
<ActionBar mt={ -6 }> <ActionBar mt={ -6 }>
<Pagination ml="auto" { ...pagination }/> <Pagination ml="auto" { ...pagination }/>
</ActionBar> </ActionBar>
......
...@@ -2,11 +2,11 @@ import { Accordion, Hide, Show, Text } from '@chakra-ui/react'; ...@@ -2,11 +2,11 @@ import { Accordion, Hide, Show, Text } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import { SECOND } from 'lib/consts'; import { SECOND } from 'lib/consts';
import useQueryWithPages from 'lib/hooks/useQueryWithPages';
import { TX_STATE_CHANGES } from 'stubs/txStateChanges'; import { TX_STATE_CHANGES } from 'stubs/txStateChanges';
import ActionBar from 'ui/shared/ActionBar'; import ActionBar from 'ui/shared/ActionBar';
import DataListDisplay from 'ui/shared/DataListDisplay'; import DataListDisplay from 'ui/shared/DataListDisplay';
import Pagination from 'ui/shared/Pagination'; import Pagination from 'ui/shared/pagination/Pagination';
import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages';
import TxStateList from 'ui/tx/state/TxStateList'; import TxStateList from 'ui/tx/state/TxStateList';
import TxStateTable from 'ui/tx/state/TxStateTable'; import TxStateTable from 'ui/tx/state/TxStateTable';
import useFetchTxInfo from 'ui/tx/useFetchTxInfo'; import useFetchTxInfo from 'ui/tx/useFetchTxInfo';
...@@ -16,7 +16,7 @@ import TxSocketAlert from './TxSocketAlert'; ...@@ -16,7 +16,7 @@ import TxSocketAlert from './TxSocketAlert';
const TxState = () => { const TxState = () => {
const txInfo = useFetchTxInfo({ updateDelay: 5 * SECOND }); const txInfo = useFetchTxInfo({ updateDelay: 5 * SECOND });
const { data, isPlaceholderData, isError, isPaginationVisible, pagination } = useQueryWithPages({ const { data, isPlaceholderData, isError, pagination } = useQueryWithPages({
resourceName: 'tx_state_changes', resourceName: 'tx_state_changes',
pathParams: { hash: txInfo.data?.hash }, pathParams: { hash: txInfo.data?.hash },
options: { options: {
...@@ -38,7 +38,7 @@ const TxState = () => { ...@@ -38,7 +38,7 @@ const TxState = () => {
const content = data ? ( const content = data ? (
<Accordion allowMultiple defaultIndex={ [] }> <Accordion allowMultiple defaultIndex={ [] }>
<Hide below="lg" ssr={ false }> <Hide below="lg" ssr={ false }>
<TxStateTable data={ data.items } isLoading={ isPlaceholderData } top={ isPaginationVisible ? 80 : 0 }/> <TxStateTable data={ data.items } isLoading={ isPlaceholderData } top={ pagination.isVisible ? 80 : 0 }/>
</Hide> </Hide>
<Show below="lg" ssr={ false }> <Show below="lg" ssr={ false }>
<TxStateList data={ data.items } isLoading={ isPlaceholderData }/> <TxStateList data={ data.items } isLoading={ isPlaceholderData }/>
...@@ -46,7 +46,7 @@ const TxState = () => { ...@@ -46,7 +46,7 @@ const TxState = () => {
</Accordion> </Accordion>
) : null; ) : null;
const actionBar = isPaginationVisible ? ( const actionBar = pagination.isVisible ? (
<ActionBar mt={ -6 } showShadow> <ActionBar mt={ -6 } showShadow>
<Pagination ml="auto" { ...pagination }/> <Pagination ml="auto" { ...pagination }/>
</ActionBar> </ActionBar>
......
...@@ -6,14 +6,14 @@ import type { TokenType } from 'types/api/token'; ...@@ -6,14 +6,14 @@ import type { TokenType } from 'types/api/token';
import { SECOND } from 'lib/consts'; import { SECOND } from 'lib/consts';
import getFilterValuesFromQuery from 'lib/getFilterValuesFromQuery'; import getFilterValuesFromQuery from 'lib/getFilterValuesFromQuery';
import useQueryWithPages from 'lib/hooks/useQueryWithPages';
import { apos } from 'lib/html-entities'; import { apos } from 'lib/html-entities';
import TOKEN_TYPE from 'lib/token/tokenTypes'; import TOKEN_TYPE from 'lib/token/tokenTypes';
import { getTokenTransfersStub } from 'stubs/token'; import { getTokenTransfersStub } from 'stubs/token';
import ActionBar from 'ui/shared/ActionBar'; import ActionBar from 'ui/shared/ActionBar';
import DataFetchAlert from 'ui/shared/DataFetchAlert'; import DataFetchAlert from 'ui/shared/DataFetchAlert';
import DataListDisplay from 'ui/shared/DataListDisplay'; import DataListDisplay from 'ui/shared/DataListDisplay';
import Pagination from 'ui/shared/Pagination'; import Pagination from 'ui/shared/pagination/Pagination';
import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages';
import TokenTransferFilter from 'ui/shared/TokenTransfer/TokenTransferFilter'; import TokenTransferFilter from 'ui/shared/TokenTransfer/TokenTransferFilter';
import TokenTransferList from 'ui/shared/TokenTransfer/TokenTransferList'; import TokenTransferList from 'ui/shared/TokenTransfer/TokenTransferList';
import TokenTransferTable from 'ui/shared/TokenTransfer/TokenTransferTable'; import TokenTransferTable from 'ui/shared/TokenTransfer/TokenTransferTable';
...@@ -77,7 +77,7 @@ const TxTokenTransfer = () => { ...@@ -77,7 +77,7 @@ const TxTokenTransfer = () => {
appliedFiltersNum={ numActiveFilters } appliedFiltersNum={ numActiveFilters }
isLoading={ tokenTransferQuery.isPlaceholderData } isLoading={ tokenTransferQuery.isPlaceholderData }
/> />
{ tokenTransferQuery.isPaginationVisible && <Pagination ml="auto" { ...tokenTransferQuery.pagination }/> } <Pagination ml="auto" { ...tokenTransferQuery.pagination }/>
</ActionBar> </ActionBar>
) : null; ) : null;
......
import { Box, Show, Hide } from '@chakra-ui/react'; import { Box, Show, Hide } from '@chakra-ui/react';
import type { UseQueryResult } from '@tanstack/react-query';
import React from 'react'; import React from 'react';
import type { TxsResponse } from 'types/api/transaction';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
import AddressCsvExportLink from 'ui/address/AddressCsvExportLink'; import AddressCsvExportLink from 'ui/address/AddressCsvExportLink';
import DataListDisplay from 'ui/shared/DataListDisplay'; import DataListDisplay from 'ui/shared/DataListDisplay';
import type { Props as PaginationProps } from 'ui/shared/Pagination'; import type { QueryWithPagesResult } from 'ui/shared/pagination/useQueryWithPages';
import * as SocketNewItemsNotice from 'ui/shared/SocketNewItemsNotice'; import * as SocketNewItemsNotice from 'ui/shared/SocketNewItemsNotice';
import TxsHeaderMobile from './TxsHeaderMobile'; import TxsHeaderMobile from './TxsHeaderMobile';
...@@ -15,13 +12,8 @@ import TxsListItem from './TxsListItem'; ...@@ -15,13 +12,8 @@ import TxsListItem from './TxsListItem';
import TxsTable from './TxsTable'; import TxsTable from './TxsTable';
import useTxsSort from './useTxsSort'; import useTxsSort from './useTxsSort';
type QueryResult = UseQueryResult<TxsResponse> & {
pagination: PaginationProps;
isPaginationVisible: boolean;
};
type Props = { type Props = {
query: QueryResult; query: QueryWithPagesResult<'txs_validated' | 'txs_pending'> | QueryWithPagesResult<'txs_watchlist'> | QueryWithPagesResult<'block_txs'>;
showBlockInfo?: boolean; showBlockInfo?: boolean;
showSocketInfo?: boolean; showSocketInfo?: boolean;
socketInfoAlert?: string; socketInfoAlert?: string;
...@@ -79,7 +71,7 @@ const TxsContent = ({ ...@@ -79,7 +71,7 @@ const TxsContent = ({
showSocketInfo={ showSocketInfo } showSocketInfo={ showSocketInfo }
socketInfoAlert={ socketInfoAlert } socketInfoAlert={ socketInfoAlert }
socketInfoNum={ socketInfoNum } socketInfoNum={ socketInfoNum }
top={ top || query.isPaginationVisible ? 80 : 0 } top={ top || query.pagination.isVisible ? 80 : 0 }
currentAddress={ currentAddress } currentAddress={ currentAddress }
enableTimeIncrement={ enableTimeIncrement } enableTimeIncrement={ enableTimeIncrement }
isLoading={ isPlaceholderData } isLoading={ isPlaceholderData }
...@@ -94,7 +86,7 @@ const TxsContent = ({ ...@@ -94,7 +86,7 @@ const TxsContent = ({
sorting={ sorting } sorting={ sorting }
setSorting={ setSortByValue } setSorting={ setSortByValue }
paginationProps={ query.pagination } paginationProps={ query.pagination }
showPagination={ query.isPaginationVisible } showPagination={ query.pagination.isVisible }
filterComponent={ filter } filterComponent={ filter }
linkSlot={ currentAddress ? linkSlot={ currentAddress ?
<AddressCsvExportLink address={ currentAddress } type="transactions" ml={ 2 } isLoading={ query.pagination.isLoading }/> : null <AddressCsvExportLink address={ currentAddress } type="transactions" ml={ 2 } isLoading={ query.pagination.isLoading }/> : null
......
...@@ -2,11 +2,12 @@ import { HStack, chakra } from '@chakra-ui/react'; ...@@ -2,11 +2,12 @@ import { HStack, chakra } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { Sort as TSort } from 'types/client/txs-sort'; import type { Sort as TSort } from 'types/client/txs-sort';
import type { PaginationParams } from 'ui/shared/pagination/types';
// import FilterInput from 'ui/shared/filters/FilterInput'; // import FilterInput from 'ui/shared/filters/FilterInput';
import ActionBar from 'ui/shared/ActionBar'; import ActionBar from 'ui/shared/ActionBar';
import Pagination from 'ui/shared/Pagination'; import Pagination from 'ui/shared/pagination/Pagination';
import type { Props as PaginationProps } from 'ui/shared/Pagination';
import type { Option } from 'ui/shared/sort/Sort'; import type { Option } from 'ui/shared/sort/Sort';
import Sort from 'ui/shared/sort/Sort'; import Sort from 'ui/shared/sort/Sort';
...@@ -23,7 +24,7 @@ const SORT_OPTIONS: Array<Option<TSort>> = [ ...@@ -23,7 +24,7 @@ const SORT_OPTIONS: Array<Option<TSort>> = [
type Props = { type Props = {
sorting: TSort; sorting: TSort;
setSorting: (val: TSort | undefined) => void; setSorting: (val: TSort | undefined) => void;
paginationProps: PaginationProps; paginationProps: PaginationParams;
className?: string; className?: string;
showPagination?: boolean; showPagination?: boolean;
filterComponent?: React.ReactNode; filterComponent?: React.ReactNode;
......
...@@ -101,7 +101,7 @@ const TxsListItem = ({ tx, isLoading, showBlockInfo, currentAddress, enableTimeI ...@@ -101,7 +101,7 @@ const TxsListItem = ({ tx, isLoading, showBlockInfo, currentAddress, enableTimeI
</Box> </Box>
) } ) }
<Flex alignItems="center" height={ 6 } mt={ 6 }> <Flex alignItems="center" height={ 6 } mt={ 6 }>
<Address maxWidth={ `calc((100% - ${ currentAddress ? TAG_WIDTH + 16 : ARROW_WIDTH + 8 }px)/2)` }> <Address w={ `calc((100% - ${ currentAddress ? TAG_WIDTH + 16 : ARROW_WIDTH + 8 }px)/2)` }>
<AddressIcon address={ tx.from } isLoading={ isLoading }/> <AddressIcon address={ tx.from } isLoading={ isLoading }/>
<AddressLink <AddressLink
type="address" type="address"
...@@ -126,7 +126,7 @@ const TxsListItem = ({ tx, isLoading, showBlockInfo, currentAddress, enableTimeI ...@@ -126,7 +126,7 @@ const TxsListItem = ({ tx, isLoading, showBlockInfo, currentAddress, enableTimeI
</Box> </Box>
) } ) }
{ dataTo ? ( { dataTo ? (
<Address maxWidth={ `calc((100% - ${ currentAddress ? TAG_WIDTH + 16 : ARROW_WIDTH + 8 }px)/2)` }> <Address w={ `calc((100% - ${ currentAddress ? TAG_WIDTH + 16 : ARROW_WIDTH + 8 }px)/2)` }>
<AddressIcon address={ dataTo } isLoading={ isLoading }/> <AddressIcon address={ dataTo } isLoading={ isLoading }/>
<AddressLink <AddressLink
type="address" type="address"
......
import React from 'react';
import type { Props as PaginationProps } from 'ui/shared/Pagination';
import Pagination from 'ui/shared/Pagination';
interface Props {
pagination: PaginationProps;
isPaginationVisible: boolean;
}
const TxsTabSlot = ({ pagination, isPaginationVisible }: Props) => {
if (!isPaginationVisible) {
return null;
}
return <Pagination my={ 1 } { ...pagination }/>;
};
export default TxsTabSlot;
import type { UseQueryResult } from '@tanstack/react-query';
import React from 'react'; import React from 'react';
import type { TxsResponse } from 'types/api/transaction';
import useRedirectForInvalidAuthToken from 'lib/hooks/useRedirectForInvalidAuthToken'; import useRedirectForInvalidAuthToken from 'lib/hooks/useRedirectForInvalidAuthToken';
import type { Props as PaginationProps } from 'ui/shared/Pagination'; import type { QueryWithPagesResult } from 'ui/shared/pagination/useQueryWithPages';
import TxsContent from 'ui/txs/TxsContent'; import TxsContent from 'ui/txs/TxsContent';
type QueryResult = UseQueryResult<TxsResponse> & {
pagination: PaginationProps;
isPaginationVisible: boolean;
};
type Props = { type Props = {
query: QueryResult; query: QueryWithPagesResult<'txs_watchlist'>;
} }
const TxsWatchlist = ({ query }: Props) => { const TxsWatchlist = ({ query }: Props) => {
......
...@@ -4567,27 +4567,27 @@ ...@@ -4567,27 +4567,27 @@
"@tanstack/query-core" "4.29.11" "@tanstack/query-core" "4.29.11"
use-sync-external-store "^1.2.0" use-sync-external-store "^1.2.0"
"@testing-library/dom@^8.5.0": "@testing-library/dom@^9.0.0":
version "8.19.0" version "9.3.0"
resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-8.19.0.tgz#bd3f83c217ebac16694329e413d9ad5fdcfd785f" resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-9.3.0.tgz#ed8ce10aa5e05eb6eaf0635b5b8975d889f66075"
integrity sha512-6YWYPPpxG3e/xOo6HIWwB/58HukkwIVTOaZ0VwdMVjhRUX/01E4FtQbck9GazOOj7MXHc5RBzMrU86iBJHbI+A== integrity sha512-Dffe68pGwI6WlLRYR2I0piIkyole9cSBH5jGQKCGMRpHW5RHCqAUaqc2Kv0tUyd4dU4DLPKhJIjyKOnjv4tuUw==
dependencies: dependencies:
"@babel/code-frame" "^7.10.4" "@babel/code-frame" "^7.10.4"
"@babel/runtime" "^7.12.5" "@babel/runtime" "^7.12.5"
"@types/aria-query" "^4.2.0" "@types/aria-query" "^5.0.1"
aria-query "^5.0.0" aria-query "^5.0.0"
chalk "^4.1.0" chalk "^4.1.0"
dom-accessibility-api "^0.5.9" dom-accessibility-api "^0.5.9"
lz-string "^1.4.4" lz-string "^1.5.0"
pretty-format "^27.0.2" pretty-format "^27.0.2"
"@testing-library/react@^13.4.0": "@testing-library/react@^14.0.0":
version "13.4.0" version "14.0.0"
resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-13.4.0.tgz#6a31e3bf5951615593ad984e96b9e5e2d9380966" resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-14.0.0.tgz#59030392a6792450b9ab8e67aea5f3cc18d6347c"
integrity sha512-sXOGON+WNTh3MLE9rve97ftaZukN3oNf2KjDy7YTx6hcTO2uuLHuCGynMDhFwGw/jYf4OJ2Qk0i4i79qMNNkyw== integrity sha512-S04gSNJbYE30TlIMLTzv6QCTzt9AqIF5y6s6SzVFILNcNvbV/jU96GeiTPillGQo+Ny64M/5PV7klNYYgv5Dfg==
dependencies: dependencies:
"@babel/runtime" "^7.12.5" "@babel/runtime" "^7.12.5"
"@testing-library/dom" "^8.5.0" "@testing-library/dom" "^9.0.0"
"@types/react-dom" "^18.0.0" "@types/react-dom" "^18.0.0"
"@tootallnate/once@2": "@tootallnate/once@2":
...@@ -4625,10 +4625,10 @@ ...@@ -4625,10 +4625,10 @@
resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.3.tgz#472eaab5f15c1ffdd7f8628bd4c4f753995ec79e" resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.3.tgz#472eaab5f15c1ffdd7f8628bd4c4f753995ec79e"
integrity sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ== integrity sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==
"@types/aria-query@^4.2.0": "@types/aria-query@^5.0.1":
version "4.2.2" version "5.0.1"
resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-4.2.2.tgz#ed4e0ad92306a704f9fb132a0cfcf77486dbe2bc" resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-5.0.1.tgz#3286741fb8f1e1580ac28784add4c7a1d49bdfbc"
integrity sha512-HnYpAE1Y6kRyKM/XkEuiRQhTHvkzMBurTHnpFLYLBGPIylZNPs9jJcuOOYWxPLJCSEtmZT0Y8rHDokKN7rRTig== integrity sha512-XTIieEY+gvJ39ChLcB4If5zHtPxt3Syj5rgZR+e1ctpmK8NjPf0zFqsz4JpLJT0xla9GFDKjy8Cpu331nrmE1Q==
"@types/babel__core@^7.1.14": "@types/babel__core@^7.1.14":
version "7.1.20" version "7.1.20"
...@@ -6844,6 +6844,13 @@ create-require@^1.1.0: ...@@ -6844,6 +6844,13 @@ create-require@^1.1.0:
resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333"
integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==
cross-fetch@^3.0.4:
version "3.1.6"
resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.6.tgz#bae05aa31a4da760969756318feeee6e70f15d6c"
integrity sha512-riRvo06crlE8HiqOwIpQhxwdOk4fOeR7FVM/wXoxchFEqMNUjvbs3bfo4OTgMEMHzppd4DxFBDbyySj8Cv781g==
dependencies:
node-fetch "^2.6.11"
cross-fetch@^3.1.4, cross-fetch@^3.1.5: cross-fetch@^3.1.4, cross-fetch@^3.1.5:
version "3.1.5" version "3.1.5"
resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.5.tgz#e1389f44d9e7ba767907f7af8454787952ab534f" resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.5.tgz#e1389f44d9e7ba767907f7af8454787952ab534f"
...@@ -9555,6 +9562,14 @@ jest-environment-node@^29.3.1: ...@@ -9555,6 +9562,14 @@ jest-environment-node@^29.3.1:
jest-mock "^29.3.1" jest-mock "^29.3.1"
jest-util "^29.3.1" jest-util "^29.3.1"
jest-fetch-mock@^3.0.3:
version "3.0.3"
resolved "https://registry.yarnpkg.com/jest-fetch-mock/-/jest-fetch-mock-3.0.3.tgz#31749c456ae27b8919d69824f1c2bd85fe0a1f3b"
integrity sha512-Ux1nWprtLrdrH4XwE7O7InRY6psIi3GOsqNESJgMJ+M5cv4A8Lh7SN9d2V2kKRZ8ebAfcd1LNyZguAOb6JiDqw==
dependencies:
cross-fetch "^3.0.4"
promise-polyfill "^8.1.3"
jest-get-type@^29.2.0: jest-get-type@^29.2.0:
version "29.2.0" version "29.2.0"
resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.2.0.tgz#726646f927ef61d583a3b3adb1ab13f3a5036408" resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.2.0.tgz#726646f927ef61d583a3b3adb1ab13f3a5036408"
...@@ -10210,10 +10225,10 @@ lru_map@^0.3.3: ...@@ -10210,10 +10225,10 @@ lru_map@^0.3.3:
resolved "https://registry.yarnpkg.com/lru_map/-/lru_map-0.3.3.tgz#b5c8351b9464cbd750335a79650a0ec0e56118dd" resolved "https://registry.yarnpkg.com/lru_map/-/lru_map-0.3.3.tgz#b5c8351b9464cbd750335a79650a0ec0e56118dd"
integrity sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ== integrity sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ==
lz-string@^1.4.4: lz-string@^1.5.0:
version "1.4.4" version "1.5.0"
resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.4.4.tgz#c0d8eaf36059f705796e1e344811cf4c498d3a26" resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.5.0.tgz#c1ab50f77887b712621201ba9fd4e3a6ed099941"
integrity sha512-0ckx7ZHRPqb0oUm8zNr+90mtf9DQB60H1wMCjBtfi62Kl3a7JbHob6gA2bC+xRvZoOL+1hzUK8jeuEIQE8svEQ== integrity sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==
magic-string@^0.27.0: magic-string@^0.27.0:
version "0.27.0" version "0.27.0"
...@@ -10528,6 +10543,13 @@ node-fetch@2, node-fetch@2.6.7, node-fetch@^2.6.7: ...@@ -10528,6 +10543,13 @@ node-fetch@2, node-fetch@2.6.7, node-fetch@^2.6.7:
dependencies: dependencies:
whatwg-url "^5.0.0" whatwg-url "^5.0.0"
node-fetch@^2.6.11:
version "2.6.11"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.11.tgz#cde7fc71deef3131ef80a738919f999e6edfff25"
integrity sha512-4I6pdBY1EthSqDmJkiNk3JIT8cswwR9nfeW/cPdUagJYEQG7R95WRH74wpz7ma8Gh/9dI9FP+OU+0E4FvtA55w==
dependencies:
whatwg-url "^5.0.0"
node-fetch@^3.2.9: node-fetch@^3.2.9:
version "3.2.10" version "3.2.10"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-3.2.10.tgz#e8347f94b54ae18b57c9c049ef641cef398a85c8" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-3.2.10.tgz#e8347f94b54ae18b57c9c049ef641cef398a85c8"
...@@ -11240,6 +11262,11 @@ progress@^2.0.3: ...@@ -11240,6 +11262,11 @@ progress@^2.0.3:
resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8"
integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==
promise-polyfill@^8.1.3:
version "8.3.0"
resolved "https://registry.yarnpkg.com/promise-polyfill/-/promise-polyfill-8.3.0.tgz#9284810268138d103807b11f4e23d5e945a4db63"
integrity sha512-H5oELycFml5yto/atYqmjyigJoAo3+OXwolYiH7OfQuYlAqhxNvTfiNMbV9hsC6Yp83yE5r2KTVmtrG6R9i6Pg==
prompts@^2.0.1: prompts@^2.0.1:
version "2.4.2" version "2.4.2"
resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069" resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069"
......
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