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;
}
This diff is collapsed.
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 });
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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