diff --git a/.env.example b/.env.example index 84e0dad688..34a9c98f4c 100644 --- a/.env.example +++ b/.env.example @@ -4,4 +4,5 @@ NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID=xxx NEXT_PUBLIC_RE_CAPTCHA_APP_SITE_KEY=xxx NEXT_PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID=UA-XXXXXX-X NEXT_PUBLIC_MIXPANEL_PROJECT_TOKEN=xxx -NEXT_PUBLIC_AUTH0_CLIENT_ID=xxx \ No newline at end of file +NEXT_PUBLIC_AUTH0_CLIENT_ID=xxx +NEXT_PUBLIC_FAVICON_GENERATOR_API_KEY=xxx \ No newline at end of file diff --git a/configs/app/index.ts b/configs/app/index.ts index f2253ee8fe..a946604593 100644 --- a/configs/app/index.ts +++ b/configs/app/index.ts @@ -2,6 +2,7 @@ import api from './api'; import app from './app'; import chain from './chain'; import * as features from './features'; +import meta from './meta'; import services from './services'; import UI from './ui'; @@ -12,6 +13,7 @@ const config = Object.freeze({ UI, features, services, + meta, }); export default config; diff --git a/configs/app/meta.ts b/configs/app/meta.ts new file mode 100644 index 0000000000..cf0f309534 --- /dev/null +++ b/configs/app/meta.ts @@ -0,0 +1,14 @@ +import app from './app'; +import { getEnvValue, getExternalAssetFilePath } from './utils'; + +const defaultImageUrl = app.baseUrl + '/static/og_placeholder.png'; + +const meta = Object.freeze({ + promoteBlockscoutInTitle: getEnvValue('NEXT_PUBLIC_PROMOTE_BLOCKSCOUT_IN_TITLE') || 'true', + og: { + description: getEnvValue('NEXT_PUBLIC_OG_DESCRIPTION') || '', + imageUrl: getExternalAssetFilePath('NEXT_PUBLIC_OG_IMAGE_URL') || defaultImageUrl, + }, +}); + +export default meta; diff --git a/configs/app/ui/views/address.ts b/configs/app/ui/views/address.ts new file mode 100644 index 0000000000..b121ecfde5 --- /dev/null +++ b/configs/app/ui/views/address.ts @@ -0,0 +1,16 @@ +import type { IdenticonType } from 'types/views/address'; +import { IDENTICON_TYPES } from 'types/views/address'; + +import { getEnvValue } from 'configs/app/utils'; + +const identiconType: IdenticonType = (() => { + const value = getEnvValue('NEXT_PUBLIC_VIEWS_ADDRESS_IDENTICON_TYPE'); + + return IDENTICON_TYPES.find((type) => value === type) || 'jazzicon'; +})(); + +const config = Object.freeze({ + identiconType: identiconType, +}); + +export default config; diff --git a/configs/app/ui/views/index.ts b/configs/app/ui/views/index.ts index 8f7135cbbe..9933720d91 100644 --- a/configs/app/ui/views/index.ts +++ b/configs/app/ui/views/index.ts @@ -1 +1,2 @@ export { default as block } from './block'; +export { default as address } from './address'; diff --git a/configs/envs/.env.eth b/configs/envs/.env.eth index 97cdd23a0b..475da9a39b 100644 --- a/configs/envs/.env.eth +++ b/configs/envs/.env.eth @@ -41,3 +41,6 @@ NEXT_PUBLIC_STATS_API_HOST=https://stats-eth-main.k8s.blockscout.com NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.services.blockscout.com NEXT_PUBLIC_CONTRACT_INFO_API_HOST=https://contracts-info.services.blockscout.com NEXT_PUBLIC_ADMIN_SERVICE_API_HOST=https://admin-rs.services.blockscout.com + +#meta +NEXT_PUBLIC_OG_IMAGE_URL=https://github.com/blockscout/frontend-configs/blob/main/configs/og-images/eth.jpg?raw=true diff --git a/configs/envs/.env.eth_goerli b/configs/envs/.env.eth_goerli index ba7f48949e..b6f1d9ab6c 100644 --- a/configs/envs/.env.eth_goerli +++ b/configs/envs/.env.eth_goerli @@ -46,3 +46,6 @@ NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.services.blockscout.com NEXT_PUBLIC_CONTRACT_INFO_API_HOST=https://contracts-info.services.blockscout.com NEXT_PUBLIC_ADMIN_SERVICE_API_HOST=https://admin-rs.services.blockscout.com NEXT_PUBLIC_WEB3_WALLETS=['token_pocket','metamask'] + +#meta +NEXT_PUBLIC_OG_IMAGE_URL=https://github.com/blockscout/frontend-configs/blob/main/configs/og-images/eth-goerli.png?raw=true diff --git a/configs/envs/.env.main.L2 b/configs/envs/.env.main.L2 index 7c96caa9f9..000ab742fc 100644 --- a/configs/envs/.env.main.L2 +++ b/configs/envs/.env.main.L2 @@ -31,6 +31,8 @@ NEXT_PUBLIC_NETWORK_LOGO=https://raw.githubusercontent.com/blockscout/frontend-c NEXT_PUBLIC_NETWORK_ICON=https://raw.githubusercontent.com/blockscout/frontend-configs/dev/configs/network-icons/base.svg ## footer ## misc +## views +NEXT_PUBLIC_VIEWS_ADDRESS_IDENTICON_TYPE=gradient_avatar # app features NEXT_PUBLIC_APP_INSTANCE=local diff --git a/configs/envs/.env.polygon b/configs/envs/.env.polygon index 8a53ff00f9..874444ee2b 100644 --- a/configs/envs/.env.polygon +++ b/configs/envs/.env.polygon @@ -40,4 +40,7 @@ NEXT_PUBLIC_APP_ENV=development NEXT_PUBLIC_HAS_BEACON_CHAIN=false # NEXT_PUBLIC_STATS_API_HOST=https://stats-rsk-testnet.k8s.blockscout.com NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.services.blockscout.com -NEXT_PUBLIC_WEB3_WALLETS=['token_pocket','metamask'] \ No newline at end of file +NEXT_PUBLIC_WEB3_WALLETS=['token_pocket','metamask'] + +#meta +NEXT_PUBLIC_OG_IMAGE_URL=https://github.com/blockscout/frontend-configs/blob/main/configs/og-images/polygon-mainnet.png?raw=true diff --git a/configs/envs/.env.rootstock b/configs/envs/.env.rootstock index 097fc852ee..cf7c0153d5 100644 --- a/configs/envs/.env.rootstock +++ b/configs/envs/.env.rootstock @@ -41,3 +41,6 @@ NEXT_PUBLIC_GRAPHIQL_TRANSACTION=0x97fa753626b8d44011d0b9f9a947c735f20b6e895efde NEXT_PUBLIC_HAS_BEACON_CHAIN=false NEXT_PUBLIC_STATS_API_HOST=https://stats-rsk-testnet.k8s.blockscout.com NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.services.blockscout.com + +#meta +NEXT_PUBLIC_OG_IMAGE_URL=https://github.com/blockscout/frontend-configs/blob/main/configs/og-images/rootstock-testnet.png?raw=true diff --git a/deploy/scripts/download_assets.sh b/deploy/scripts/download_assets.sh index 179062e2d5..c1c82d5b50 100755 --- a/deploy/scripts/download_assets.sh +++ b/deploy/scripts/download_assets.sh @@ -21,6 +21,7 @@ ASSETS_ENVS=( "NEXT_PUBLIC_NETWORK_LOGO_DARK" "NEXT_PUBLIC_NETWORK_ICON" "NEXT_PUBLIC_NETWORK_ICON_DARK" + "NEXT_PUBLIC_OG_IMAGE_URL" ) # Create the assets directory if it doesn't exist diff --git a/deploy/tools/envs-validator/schema.ts b/deploy/tools/envs-validator/schema.ts index 4c23d3f120..2ebb0ba851 100644 --- a/deploy/tools/envs-validator/schema.ts +++ b/deploy/tools/envs-validator/schema.ts @@ -10,6 +10,7 @@ import { SUPPORTED_WALLETS } from '../../../types/client/wallets'; import type { CustomLink, CustomLinksGroup } from '../../../types/footerLinks'; import type { ChainIndicatorId } from '../../../types/homepage'; import { type NetworkVerificationType, type NetworkExplorer, type FeaturedNetwork, NETWORK_GROUPS } from '../../../types/networks'; +import { IDENTICON_TYPES } from '../../../types/views/address'; import { BLOCK_FIELDS_IDS } from '../../../types/views/block'; import type { BlockFieldId } from '../../../types/views/block'; @@ -294,6 +295,7 @@ const schema = yup .transform(getEnvValue) .json() .of(yup.string().oneOf(BLOCK_FIELDS_IDS)), + NEXT_PUBLIC_VIEWS_ADDRESS_IDENTICON_TYPE: yup.string().oneOf(IDENTICON_TYPES), // e. misc NEXT_PUBLIC_NETWORK_EXPLORERS: yup @@ -323,6 +325,9 @@ const schema = yup }), NEXT_PUBLIC_WEB3_DISABLE_ADD_TOKEN_TO_WALLET: yup.boolean(), NEXT_PUBLIC_AD_TEXT_PROVIDER: yup.string().oneOf(SUPPORTED_AD_TEXT_PROVIDERS), + NEXT_PUBLIC_PROMOTE_BLOCKSCOUT_IN_TITLE: yup.boolean(), + NEXT_PUBLIC_OG_DESCRIPTION: yup.string(), + NEXT_PUBLIC_OG_IMAGE_URL: yup.string().url(), // 6. External services envs NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID: yup.string(), diff --git a/deploy/values/review-l2/values.yaml.gotmpl b/deploy/values/review-l2/values.yaml.gotmpl index f1b3f13708..5d544b71f6 100644 --- a/deploy/values/review-l2/values.yaml.gotmpl +++ b/deploy/values/review-l2/values.yaml.gotmpl @@ -144,3 +144,5 @@ frontend: _default: ref+vault://deployment-values/blockscout/dev/review-l2?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID NEXT_PUBLIC_FAVICON_GENERATOR_API_KEY: _default: ref+vault://deployment-values/blockscout/common?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_FAVICON_GENERATOR_API_KEY + NEXT_PUBLIC_OG_IMAGE_URL: + _default: https://github.com/blockscout/frontend-configs/blob/main/configs/og-images/base-goerli.png?raw=true diff --git a/deploy/values/review/values.yaml.gotmpl b/deploy/values/review/values.yaml.gotmpl index 3a7540f653..3c21b8b3d8 100644 --- a/deploy/values/review/values.yaml.gotmpl +++ b/deploy/values/review/values.yaml.gotmpl @@ -130,3 +130,5 @@ frontend: _default: ref+vault://deployment-values/blockscout/common?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_FAVICON_GENERATOR_API_KEY NEXT_PUBLIC_WEB3_WALLETS: _default: "['token_pocket','coinbase','metamask']" + NEXT_PUBLIC_VIEWS_ADDRESS_IDENTICON_TYPE: + _default: gradient_avatar diff --git a/docs/ENVS.md b/docs/ENVS.md index 55f37c186e..fff0582c37 100644 --- a/docs/ENVS.md +++ b/docs/ENVS.md @@ -13,6 +13,7 @@ The app instance could be customized by passing following variables to NodeJS en - [Sidebar](ENVS.md#sidebar) - [Footer](ENVS.md#footer) - [Favicon](ENVS.md#favicon) + - [Meta](ENVS.md#meta) - [Views](ENVS.md#views) - [Block](ENVS.md#block-views) - [Misc](ENVS.md#misc) @@ -145,6 +146,18 @@ By default, the app has generic favicon. You can override this behavior by provi   +### Meta + +Settings for meta tags and OG tags + +| Variable | Type| Description | Compulsoriness | Default value | Example value | +| --- | --- | --- | --- | --- | --- | +| NEXT_PUBLIC_PROMOTE_BLOCKSCOUT_IN_TITLE | `boolean` | Set to `true` to promote Blockscout in meta and OG titles | - | `true` | `true` | +| NEXT_PUBLIC_OG_DESCRIPTION | `string` | Custom OG description | - | - | `Blockscout is the #1 open-source blockchain explorer available today. 100+ chains and counting rely on Blockscout data availability, APIs, and ecosystem tools to support their networks.` | +| NEXT_PUBLIC_OG_IMAGE_URL | `string` | OG image url. Minimum image size is 200 x 20 pixels (recommended: 1200 x 600); maximum supported file size is 8 MB; 2:1 aspect ratio; supported formats: image/jpeg, image/gif, image/png | - | `static/og_placeholder.png` | `https://placekitten.com/1200/600` | + +  + ### Views #### Block views @@ -163,6 +176,14 @@ By default, the app has generic favicon. You can override this behavior by provi   +#### Address views + +| Variable | Type | Description | Compulsoriness | Default value | Example value | +| --- | --- | --- | --- | --- | --- | +| NEXT_PUBLIC_VIEWS_ADDRESS_IDENTICON_TYPE | `"github" \| "jazzicon" \| "gradient_avatar" \| "blockie"` | Style of address identicon appearance. Choose between [GitHub](https://github.blog/2013-08-14-identicons/), [Metamask Jazzicon](https://metamask.github.io/jazzicon/), [Gradient Avatar](https://github.com/varld/gradient-avatar) and [Ethereum Blocky](https://mycryptohq.github.io/ethereum-blockies-base64/) | - | `jazzicon` | `gradient_avatar` | + +  + ### Misc | Variable | Type| Description | Compulsoriness | Default value | Example value | diff --git a/icons/contract.svg b/icons/contract.svg index 7579ea76ca..1bc8e0d3d9 100644 --- a/icons/contract.svg +++ b/icons/contract.svg @@ -1,4 +1,3 @@ - - - + + diff --git a/icons/nft_shield.svg b/icons/nft_shield.svg index 05753842c0..5f055f02ee 100644 --- a/icons/nft_shield.svg +++ b/icons/nft_shield.svg @@ -1,3 +1,3 @@ - + diff --git a/icons/social/canny.svg b/icons/social/canny.svg index 13393c2f60..8041cd2bf3 100644 --- a/icons/social/canny.svg +++ b/icons/social/canny.svg @@ -1,3 +1,3 @@ - + diff --git a/lib/metadata/__snapshots__/generate.test.ts.snap b/lib/metadata/__snapshots__/generate.test.ts.snap index 362d8d2fd2..de88a039ef 100644 --- a/lib/metadata/__snapshots__/generate.test.ts.snap +++ b/lib/metadata/__snapshots__/generate.test.ts.snap @@ -3,6 +3,11 @@ exports[`generates correct metadata for: dynamic route 1`] = ` { "description": "View transaction 0x12345 on Blockscout (Blockscout) Explorer", + "opengraph": { + "description": "", + "imageUrl": "", + "title": "Blockscout transaction 0x12345 | Blockscout", + }, "title": "Blockscout transaction 0x12345 | Blockscout", } `; @@ -10,6 +15,11 @@ exports[`generates correct metadata for: dynamic route 1`] = ` exports[`generates correct metadata for: dynamic route with API data 1`] = ` { "description": "0x12345, balances and analytics on the Blockscout (Blockscout) Explorer", + "opengraph": { + "description": "", + "imageUrl": "", + "title": "Blockscout USDT token details | Blockscout", + }, "title": "Blockscout USDT token details | Blockscout", } `; @@ -17,6 +27,11 @@ exports[`generates correct metadata for: dynamic route with API data 1`] = ` exports[`generates correct metadata for: static route 1`] = ` { "description": "Blockscout is the #1 open-source blockchain explorer available today. 100+ chains and counting rely on Blockscout data availability, APIs, and ecosystem tools to support their networks.", + "opengraph": { + "description": "", + "imageUrl": "http://localhost:3000/static/og_placeholder.png", + "title": "Blockscout blocks | Blockscout", + }, "title": "Blockscout blocks | Blockscout", } `; diff --git a/lib/metadata/generate.ts b/lib/metadata/generate.ts index f688bfaa52..8d24fd1606 100644 --- a/lib/metadata/generate.ts +++ b/lib/metadata/generate.ts @@ -6,6 +6,7 @@ import config from 'configs/app'; import getNetworkTitle from 'lib/networks/getNetworkTitle'; import compileValue from './compileValue'; +import getPageOgType from './getPageOgType'; import * as templates from './templates'; export default function generate(route: R, apiData?: ApiData): Metadata { @@ -16,11 +17,19 @@ export default function generate(route: R, apiData?: ApiData network_title: getNetworkTitle(), }; - const title = compileValue(templates.title.make(route.pathname), params); + const compiledTitle = compileValue(templates.title.make(route.pathname), params); + const title = compiledTitle ? compiledTitle + (config.meta.promoteBlockscoutInTitle ? ' | Blockscout' : '') : ''; const description = compileValue(templates.description.make(route.pathname), params); + const pageOgType = getPageOgType(route.pathname); + return { - title, + title: title, description, + opengraph: { + title: title, + description: pageOgType !== 'Regular page' ? config.meta.og.description : '', + imageUrl: pageOgType !== 'Regular page' ? config.meta.og.imageUrl : '', + }, }; } diff --git a/lib/metadata/getPageOgType.ts b/lib/metadata/getPageOgType.ts new file mode 100644 index 0000000000..e8b4d0b002 --- /dev/null +++ b/lib/metadata/getPageOgType.ts @@ -0,0 +1,52 @@ +import type { Route } from 'nextjs-routes'; + +type OGPageType = 'Homepage' | 'Root page' | 'Regular page'; + +const OG_TYPE_DICT: Record = { + '/': 'Homepage', + '/txs': 'Root page', + '/tx/[hash]': 'Regular page', + '/blocks': 'Root page', + '/block/[height_or_hash]': 'Regular page', + '/accounts': 'Root page', + '/address/[hash]': 'Regular page', + '/verified-contracts': 'Root page', + '/address/[hash]/contract-verification': 'Regular page', + '/tokens': 'Root page', + '/token/[hash]': 'Regular page', + '/token/[hash]/instance/[id]': 'Regular page', + '/apps': 'Root page', + '/apps/[id]': 'Regular page', + '/stats': 'Root page', + '/api-docs': 'Regular page', + '/graphiql': 'Regular page', + '/search-results': 'Regular page', + '/auth/profile': 'Root page', + '/account/watchlist': 'Regular page', + '/account/api-key': 'Regular page', + '/account/custom-abi': 'Regular page', + '/account/public-tags-request': 'Regular page', + '/account/tag-address': 'Regular page', + '/account/verified-addresses': 'Root page', + '/withdrawals': 'Root page', + '/visualize/sol2uml': 'Regular page', + '/csv-export': 'Regular page', + '/l2-deposits': 'Root page', + '/l2-output-roots': 'Root page', + '/l2-txn-batches': 'Root page', + '/l2-withdrawals': 'Root page', + '/404': 'Regular page', + + // service routes, added only to make typescript happy + '/login': 'Regular page', + '/api/media-type': 'Regular page', + '/api/proxy': 'Regular page', + '/api/csrf': 'Regular page', + '/api/healthz': 'Regular page', + '/auth/auth0': 'Regular page', + '/auth/unverified-email': 'Regular page', +}; + +export default function getPageOgType(pathname: Route['pathname']) { + return OG_TYPE_DICT[pathname]; +} diff --git a/lib/metadata/templates/title.ts b/lib/metadata/templates/title.ts index f7e0abc81d..dc37e8164c 100644 --- a/lib/metadata/templates/title.ts +++ b/lib/metadata/templates/title.ts @@ -48,5 +48,5 @@ const TEMPLATE_MAP: Record = { export function make(pathname: Route['pathname']) { const template = TEMPLATE_MAP[pathname]; - return `%network_name% ${ template } | Blockscout`; + return `%network_name% ${ template }`; } diff --git a/lib/metadata/types.ts b/lib/metadata/types.ts index 5eefdc0c70..252dbc29cf 100644 --- a/lib/metadata/types.ts +++ b/lib/metadata/types.ts @@ -10,4 +10,9 @@ never; export interface Metadata { title: string; description: string; + opengraph: { + title: string; + description?: string; + imageUrl?: string; + }; } diff --git a/nextjs/PageNextJs.tsx b/nextjs/PageNextJs.tsx index 0a26964a9e..738fbaca5e 100644 --- a/nextjs/PageNextJs.tsx +++ b/nextjs/PageNextJs.tsx @@ -14,7 +14,7 @@ type Props = Route & { } const PageNextJs = (props: Props) => { - const { title, description } = metadata.generate(props); + const { title, description, opengraph } = metadata.generate(props); useGetCsrfToken(); useAdblockDetect(); @@ -28,6 +28,12 @@ const PageNextJs = (props: Props) => { { title } + + { /* OG TAGS */ } + + { opengraph.description && } + + { props.children } diff --git a/package.json b/package.json index 891c44973e..58ead28f36 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,9 @@ "d3": "^7.6.1", "dayjs": "^1.11.5", "dom-to-image": "^2.6.0", + "ethereum-blockies-base64": "^1.0.2", "framer-motion": "^6.5.1", + "gradient-avatar": "^1.0.2", "graphiql": "^2.2.0", "graphql": "^16.6.0", "graphql-ws": "^5.11.3", diff --git a/pages/_document.tsx b/pages/_document.tsx index 1a20b3dbc4..6d21b3532c 100644 --- a/pages/_document.tsx +++ b/pages/_document.tsx @@ -5,7 +5,6 @@ import React from 'react'; import * as serverTiming from 'nextjs/utils/serverTiming'; -import config from 'configs/app'; import theme from 'theme'; class MyDocument extends Document { @@ -49,19 +48,6 @@ class MyDocument extends Document { - - { /* OG TAGS */ } - - - - - - - diff --git a/public/static/og.png b/public/static/og.png deleted file mode 100644 index 5d35a97b4c..0000000000 Binary files a/public/static/og.png and /dev/null differ diff --git a/public/static/og_placeholder.png b/public/static/og_placeholder.png new file mode 100644 index 0000000000..1babd9e522 Binary files /dev/null and b/public/static/og_placeholder.png differ diff --git a/theme/components/Modal.ts b/theme/components/Modal.ts index aa45b50495..d2e291f8d5 100644 --- a/theme/components/Modal.ts +++ b/theme/components/Modal.ts @@ -72,6 +72,14 @@ const baseStyle = definePartsStyle((props) => ({ })); const sizes = { + sm: definePartsStyle({ + dialogContainer: { + height: '100%', + }, + dialog: { + maxW: '536px', + }, + }), md: definePartsStyle({ dialogContainer: { height: '100%', diff --git a/types/views/address.ts b/types/views/address.ts new file mode 100644 index 0000000000..2d22583182 --- /dev/null +++ b/types/views/address.ts @@ -0,0 +1,10 @@ +import type { ArrayElement } from 'types/utils'; + +export const IDENTICON_TYPES = [ + 'github', + 'jazzicon', + 'gradient_avatar', + 'blockie', +] as const; + +export type IdenticonType = ArrayElement; diff --git a/ui/address/AddressTokenTransfers.tsx b/ui/address/AddressTokenTransfers.tsx index 931880a723..4ffcf8e8b0 100644 --- a/ui/address/AddressTokenTransfers.tsx +++ b/ui/address/AddressTokenTransfers.tsx @@ -228,6 +228,7 @@ const AddressTokenTransfers = ({ scrollRef, overloadCount = OVERLOAD_COUNT }: Pr name: '', icon_url: '', symbol: '', + type: 'ERC-20' as const, }), [ tokenFilter ]); const tokenFilterComponent = tokenFilter && ( diff --git a/ui/address/AddressTxs.pw.tsx b/ui/address/AddressTxs.pw.tsx index c29594d875..b226debc32 100644 --- a/ui/address/AddressTxs.pw.tsx +++ b/ui/address/AddressTxs.pw.tsx @@ -27,7 +27,13 @@ base.describe('base view', () => { base.beforeEach(async({ page, mount }) => { await page.route(API_URL, (route) => route.fulfill({ status: 200, - body: JSON.stringify({ items: [ txMock.base, txMock.base ], next_page_params: { block: 1 } }), + body: JSON.stringify({ items: [ + txMock.base, + { + ...txMock.base, + hash: '0x62d597ebcf3e8d60096dd0363bc2f0f5e2df27ba1dacd696c51aa7c9409f3194', + }, + ], next_page_params: { block: 1 } }), })); component = await mount( diff --git a/ui/address/__screenshots__/AddressCoinBalance.pw.tsx_dark-color-mode_base-view-dark-mode-mobile-1.png b/ui/address/__screenshots__/AddressCoinBalance.pw.tsx_dark-color-mode_base-view-dark-mode-mobile-1.png index 08fcb271a1..45a0d12d91 100644 Binary files a/ui/address/__screenshots__/AddressCoinBalance.pw.tsx_dark-color-mode_base-view-dark-mode-mobile-1.png and b/ui/address/__screenshots__/AddressCoinBalance.pw.tsx_dark-color-mode_base-view-dark-mode-mobile-1.png differ diff --git a/ui/address/__screenshots__/AddressCoinBalance.pw.tsx_default_base-view-dark-mode-mobile-1.png b/ui/address/__screenshots__/AddressCoinBalance.pw.tsx_default_base-view-dark-mode-mobile-1.png index 0f27e21d60..5d93702fbb 100644 Binary files a/ui/address/__screenshots__/AddressCoinBalance.pw.tsx_default_base-view-dark-mode-mobile-1.png and b/ui/address/__screenshots__/AddressCoinBalance.pw.tsx_default_base-view-dark-mode-mobile-1.png differ diff --git a/ui/address/__screenshots__/AddressCoinBalance.pw.tsx_mobile_base-view-dark-mode-mobile-1.png b/ui/address/__screenshots__/AddressCoinBalance.pw.tsx_mobile_base-view-dark-mode-mobile-1.png index e05bcb4c04..f6e5ba4aad 100644 Binary files a/ui/address/__screenshots__/AddressCoinBalance.pw.tsx_mobile_base-view-dark-mode-mobile-1.png and b/ui/address/__screenshots__/AddressCoinBalance.pw.tsx_mobile_base-view-dark-mode-mobile-1.png differ diff --git a/ui/address/__screenshots__/AddressDetails.pw.tsx_default_token-1.png b/ui/address/__screenshots__/AddressDetails.pw.tsx_default_token-1.png index 042a754480..06144209d2 100644 Binary files a/ui/address/__screenshots__/AddressDetails.pw.tsx_default_token-1.png and b/ui/address/__screenshots__/AddressDetails.pw.tsx_default_token-1.png differ diff --git a/ui/address/__screenshots__/AddressInternalTxs.pw.tsx_default_base-view-mobile-1.png b/ui/address/__screenshots__/AddressInternalTxs.pw.tsx_default_base-view-mobile-1.png index c225b409fc..b70f40d31c 100644 Binary files a/ui/address/__screenshots__/AddressInternalTxs.pw.tsx_default_base-view-mobile-1.png and b/ui/address/__screenshots__/AddressInternalTxs.pw.tsx_default_base-view-mobile-1.png differ diff --git a/ui/address/__screenshots__/AddressInternalTxs.pw.tsx_mobile_base-view-mobile-1.png b/ui/address/__screenshots__/AddressInternalTxs.pw.tsx_mobile_base-view-mobile-1.png index 89e26ff1e4..6cae55b4a4 100644 Binary files a/ui/address/__screenshots__/AddressInternalTxs.pw.tsx_mobile_base-view-mobile-1.png and b/ui/address/__screenshots__/AddressInternalTxs.pw.tsx_mobile_base-view-mobile-1.png differ diff --git a/ui/address/__screenshots__/AddressTokenTransfers.pw.tsx_default_mobile-with-token-filter-and-no-pagination-1.png b/ui/address/__screenshots__/AddressTokenTransfers.pw.tsx_default_mobile-with-token-filter-and-no-pagination-1.png index ea6d526bb8..53d53b249c 100644 Binary files a/ui/address/__screenshots__/AddressTokenTransfers.pw.tsx_default_mobile-with-token-filter-and-no-pagination-1.png and b/ui/address/__screenshots__/AddressTokenTransfers.pw.tsx_default_mobile-with-token-filter-and-no-pagination-1.png differ diff --git a/ui/address/__screenshots__/AddressTokenTransfers.pw.tsx_default_mobile-with-token-filter-and-pagination-1.png b/ui/address/__screenshots__/AddressTokenTransfers.pw.tsx_default_mobile-with-token-filter-and-pagination-1.png index ed339a41c3..ebb49117dd 100644 Binary files a/ui/address/__screenshots__/AddressTokenTransfers.pw.tsx_default_mobile-with-token-filter-and-pagination-1.png and b/ui/address/__screenshots__/AddressTokenTransfers.pw.tsx_default_mobile-with-token-filter-and-pagination-1.png differ diff --git a/ui/address/__screenshots__/AddressTokenTransfers.pw.tsx_default_with-token-filter-and-no-pagination-1.png b/ui/address/__screenshots__/AddressTokenTransfers.pw.tsx_default_with-token-filter-and-no-pagination-1.png index 9264f569b9..c7d9422137 100644 Binary files a/ui/address/__screenshots__/AddressTokenTransfers.pw.tsx_default_with-token-filter-and-no-pagination-1.png and b/ui/address/__screenshots__/AddressTokenTransfers.pw.tsx_default_with-token-filter-and-no-pagination-1.png differ diff --git a/ui/address/__screenshots__/AddressTokenTransfers.pw.tsx_default_with-token-filter-and-pagination-1.png b/ui/address/__screenshots__/AddressTokenTransfers.pw.tsx_default_with-token-filter-and-pagination-1.png index 9ffcd1cede..6ce1cc6b65 100644 Binary files a/ui/address/__screenshots__/AddressTokenTransfers.pw.tsx_default_with-token-filter-and-pagination-1.png and b/ui/address/__screenshots__/AddressTokenTransfers.pw.tsx_default_with-token-filter-and-pagination-1.png differ diff --git a/ui/address/__screenshots__/AddressTokens.pw.tsx_dark-color-mode_erc1155-dark-mode-1.png b/ui/address/__screenshots__/AddressTokens.pw.tsx_dark-color-mode_erc1155-dark-mode-1.png index c9105e429f..8e21ba8758 100644 Binary files a/ui/address/__screenshots__/AddressTokens.pw.tsx_dark-color-mode_erc1155-dark-mode-1.png and b/ui/address/__screenshots__/AddressTokens.pw.tsx_dark-color-mode_erc1155-dark-mode-1.png differ diff --git a/ui/address/__screenshots__/AddressTokens.pw.tsx_default_erc1155-dark-mode-1.png b/ui/address/__screenshots__/AddressTokens.pw.tsx_default_erc1155-dark-mode-1.png index eae8460cae..7cbc7815c5 100644 Binary files a/ui/address/__screenshots__/AddressTokens.pw.tsx_default_erc1155-dark-mode-1.png and b/ui/address/__screenshots__/AddressTokens.pw.tsx_default_erc1155-dark-mode-1.png differ diff --git a/ui/address/__screenshots__/AddressTokens.pw.tsx_default_mobile-erc1155-1.png b/ui/address/__screenshots__/AddressTokens.pw.tsx_default_mobile-erc1155-1.png index c083aa4c05..6c1d0b61ac 100644 Binary files a/ui/address/__screenshots__/AddressTokens.pw.tsx_default_mobile-erc1155-1.png and b/ui/address/__screenshots__/AddressTokens.pw.tsx_default_mobile-erc1155-1.png differ diff --git a/ui/address/__screenshots__/AddressTxs.pw.tsx_default_base-view-mobile-1.png b/ui/address/__screenshots__/AddressTxs.pw.tsx_default_base-view-mobile-1.png index 01e856e18b..a987d3de8f 100644 Binary files a/ui/address/__screenshots__/AddressTxs.pw.tsx_default_base-view-mobile-1.png and b/ui/address/__screenshots__/AddressTxs.pw.tsx_default_base-view-mobile-1.png differ diff --git a/ui/address/__screenshots__/AddressTxs.pw.tsx_default_base-view-screen-xl-1.png b/ui/address/__screenshots__/AddressTxs.pw.tsx_default_base-view-screen-xl-1.png index 123832e8e4..6297cffaec 100644 Binary files a/ui/address/__screenshots__/AddressTxs.pw.tsx_default_base-view-screen-xl-1.png and b/ui/address/__screenshots__/AddressTxs.pw.tsx_default_base-view-screen-xl-1.png differ diff --git a/ui/address/__screenshots__/AddressTxs.pw.tsx_mobile_base-view-mobile-1.png b/ui/address/__screenshots__/AddressTxs.pw.tsx_mobile_base-view-mobile-1.png index 325b32ecd4..ce212ad1b2 100644 Binary files a/ui/address/__screenshots__/AddressTxs.pw.tsx_mobile_base-view-mobile-1.png and b/ui/address/__screenshots__/AddressTxs.pw.tsx_mobile_base-view-mobile-1.png differ diff --git a/ui/address/coinBalance/AddressCoinBalanceListItem.tsx b/ui/address/coinBalance/AddressCoinBalanceListItem.tsx index a351358fb0..f92ce41215 100644 --- a/ui/address/coinBalance/AddressCoinBalanceListItem.tsx +++ b/ui/address/coinBalance/AddressCoinBalanceListItem.tsx @@ -30,7 +30,7 @@ const AddressCoinBalanceListItem = (props: Props) => { - + { deltaBn.dp(8).toFormat() } diff --git a/ui/address/coinBalance/AddressCoinBalanceTableItem.tsx b/ui/address/coinBalance/AddressCoinBalanceTableItem.tsx index c02b2566dc..0e87a4d7fa 100644 --- a/ui/address/coinBalance/AddressCoinBalanceTableItem.tsx +++ b/ui/address/coinBalance/AddressCoinBalanceTableItem.tsx @@ -56,7 +56,7 @@ const AddressCoinBalanceTableItem = (props: Props) => { - + { deltaBn.dp(8).toFormat() } diff --git a/ui/address/contract/__screenshots__/ContractCode.pw.tsx_default_verified-with-multiple-sources-2.png b/ui/address/contract/__screenshots__/ContractCode.pw.tsx_default_verified-with-multiple-sources-2.png index cb6443f2f8..ebd69ae75f 100644 Binary files a/ui/address/contract/__screenshots__/ContractCode.pw.tsx_default_verified-with-multiple-sources-2.png and b/ui/address/contract/__screenshots__/ContractCode.pw.tsx_default_verified-with-multiple-sources-2.png differ diff --git a/ui/address/contract/__screenshots__/ContractCode.pw.tsx_default_with-proxy-address-alert-mobile-1.png b/ui/address/contract/__screenshots__/ContractCode.pw.tsx_default_with-proxy-address-alert-mobile-1.png index 5c17737777..f60ab9af44 100644 Binary files a/ui/address/contract/__screenshots__/ContractCode.pw.tsx_default_with-proxy-address-alert-mobile-1.png and b/ui/address/contract/__screenshots__/ContractCode.pw.tsx_default_with-proxy-address-alert-mobile-1.png differ diff --git a/ui/address/contract/__screenshots__/ContractCode.pw.tsx_default_with-twin-address-alert-mobile-1.png b/ui/address/contract/__screenshots__/ContractCode.pw.tsx_default_with-twin-address-alert-mobile-1.png index ed7a06dc59..39196cf219 100644 Binary files a/ui/address/contract/__screenshots__/ContractCode.pw.tsx_default_with-twin-address-alert-mobile-1.png and b/ui/address/contract/__screenshots__/ContractCode.pw.tsx_default_with-twin-address-alert-mobile-1.png differ diff --git a/ui/address/contract/__screenshots__/ContractCode.pw.tsx_mobile_with-proxy-address-alert-mobile-1.png b/ui/address/contract/__screenshots__/ContractCode.pw.tsx_mobile_with-proxy-address-alert-mobile-1.png index 544b537cd0..b9c761a578 100644 Binary files a/ui/address/contract/__screenshots__/ContractCode.pw.tsx_mobile_with-proxy-address-alert-mobile-1.png and b/ui/address/contract/__screenshots__/ContractCode.pw.tsx_mobile_with-proxy-address-alert-mobile-1.png differ diff --git a/ui/address/contract/__screenshots__/ContractCode.pw.tsx_mobile_with-twin-address-alert-mobile-1.png b/ui/address/contract/__screenshots__/ContractCode.pw.tsx_mobile_with-twin-address-alert-mobile-1.png index 549f291a9b..2e9ff60d89 100644 Binary files a/ui/address/contract/__screenshots__/ContractCode.pw.tsx_mobile_with-twin-address-alert-mobile-1.png and b/ui/address/contract/__screenshots__/ContractCode.pw.tsx_mobile_with-twin-address-alert-mobile-1.png differ diff --git a/ui/address/details/AddressQrCode.pw.tsx b/ui/address/details/AddressQrCode.pw.tsx index 1e618d9300..aef931f75e 100644 --- a/ui/address/details/AddressQrCode.pw.tsx +++ b/ui/address/details/AddressQrCode.pw.tsx @@ -1,6 +1,7 @@ import { test, expect } from '@playwright/experimental-ct-react'; import React from 'react'; +import * as addressMock from 'mocks/address/address'; import TestApp from 'playwright/TestApp'; import AddressQrCode from './AddressQrCode'; @@ -8,7 +9,7 @@ import AddressQrCode from './AddressQrCode'; test('default view +@mobile +@dark-mode', async({ mount, page }) => { await mount( - + , ); await page.getByRole('button', { name: /qr code/i }).click(); diff --git a/ui/address/details/AddressQrCode.tsx b/ui/address/details/AddressQrCode.tsx index 0257303bf1..76d0b1c53c 100644 --- a/ui/address/details/AddressQrCode.tsx +++ b/ui/address/details/AddressQrCode.tsx @@ -6,7 +6,9 @@ import { ModalBody, ModalContent, ModalCloseButton, + ModalHeader, ModalOverlay, + LightMode, Box, useDisclosure, Tooltip, @@ -18,10 +20,12 @@ import { useRouter } from 'next/router'; import QRCode from 'qrcode'; import React from 'react'; +import type { Address as AddressType } from 'types/api/address'; + import qrCodeIcon from 'icons/qr_code.svg'; -import useIsMobile from 'lib/hooks/useIsMobile'; import getPageType from 'lib/mixpanel/getPageType'; import * as mixpanel from 'lib/mixpanel/index'; +import AddressEntity from 'ui/shared/entities/address/AddressEntity'; const SVG_OPTIONS = { margin: 0, @@ -29,13 +33,13 @@ const SVG_OPTIONS = { interface Props { className?: string; - hash: string; + address: AddressType; isLoading?: boolean; } -const AddressQrCode = ({ hash, className, isLoading }: Props) => { +const AddressQrCode = ({ address, className, isLoading }: Props) => { const { isOpen, onOpen, onClose } = useDisclosure(); - const isMobile = useIsMobile(); + const router = useRouter(); const [ qr, setQr ] = React.useState(''); @@ -45,7 +49,7 @@ const AddressQrCode = ({ hash, className, isLoading }: Props) => { React.useEffect(() => { if (isOpen) { - QRCode.toString(hash, SVG_OPTIONS, (error: Error | null | undefined, svg: string) => { + QRCode.toString(address.hash, SVG_OPTIONS, (error: Error | null | undefined, svg: string) => { if (error) { setError('We were unable to generate QR code.'); Sentry.captureException(error, { tags: { source: 'qr_code' } }); @@ -57,7 +61,7 @@ const AddressQrCode = ({ hash, className, isLoading }: Props) => { mixpanel.logEvent(mixpanel.EventTypes.QR_CODE, { 'Page type': pageType }); }); } - }, [ hash, isOpen, onClose, pageType ]); + }, [ address.hash, isOpen, onClose, pageType ]); if (isLoading) { return ; @@ -77,15 +81,38 @@ const AddressQrCode = ({ hash, className, isLoading }: Props) => { icon={ } /> - - - - { isMobile && } - - { error ? { error } : } - - - + + { error && ( + + + + + { error } + + + + ) } + { !error && ( + + + + + Address QR code + + + + + + + + + ) } ); }; diff --git a/ui/address/details/__screenshots__/AddressQrCode.pw.tsx_dark-color-mode_default-view-mobile-dark-mode-1.png b/ui/address/details/__screenshots__/AddressQrCode.pw.tsx_dark-color-mode_default-view-mobile-dark-mode-1.png index 5d1b15529f..e04068ef63 100644 Binary files a/ui/address/details/__screenshots__/AddressQrCode.pw.tsx_dark-color-mode_default-view-mobile-dark-mode-1.png and b/ui/address/details/__screenshots__/AddressQrCode.pw.tsx_dark-color-mode_default-view-mobile-dark-mode-1.png differ diff --git a/ui/address/details/__screenshots__/AddressQrCode.pw.tsx_default_default-view-mobile-dark-mode-1.png b/ui/address/details/__screenshots__/AddressQrCode.pw.tsx_default_default-view-mobile-dark-mode-1.png index 31c2fd12be..63dd0a0c6a 100644 Binary files a/ui/address/details/__screenshots__/AddressQrCode.pw.tsx_default_default-view-mobile-dark-mode-1.png and b/ui/address/details/__screenshots__/AddressQrCode.pw.tsx_default_default-view-mobile-dark-mode-1.png differ diff --git a/ui/address/details/__screenshots__/AddressQrCode.pw.tsx_mobile_default-view-mobile-dark-mode-1.png b/ui/address/details/__screenshots__/AddressQrCode.pw.tsx_mobile_default-view-mobile-dark-mode-1.png index 8eabb9a2ab..e2b90c535e 100644 Binary files a/ui/address/details/__screenshots__/AddressQrCode.pw.tsx_mobile_default-view-mobile-dark-mode-1.png and b/ui/address/details/__screenshots__/AddressQrCode.pw.tsx_mobile_default-view-mobile-dark-mode-1.png differ diff --git a/ui/address/tokenSelect/__screenshots__/TokenSelect.pw.tsx_dark-color-mode_base-view-dark-mode-1.png b/ui/address/tokenSelect/__screenshots__/TokenSelect.pw.tsx_dark-color-mode_base-view-dark-mode-1.png index 78e25c602d..9dba7e43bb 100644 Binary files a/ui/address/tokenSelect/__screenshots__/TokenSelect.pw.tsx_dark-color-mode_base-view-dark-mode-1.png and b/ui/address/tokenSelect/__screenshots__/TokenSelect.pw.tsx_dark-color-mode_base-view-dark-mode-1.png differ diff --git a/ui/address/tokens/NFTItem.tsx b/ui/address/tokens/NFTItem.tsx index 72dcd0b7ac..b4439d7f66 100644 --- a/ui/address/tokens/NFTItem.tsx +++ b/ui/address/tokens/NFTItem.tsx @@ -1,55 +1,41 @@ -import { Flex, Link, Text, LinkBox, LinkOverlay, useColorModeValue, Skeleton } from '@chakra-ui/react'; +import { Box, Flex, Text, Link, useColorModeValue } from '@chakra-ui/react'; import React from 'react'; import type { AddressTokenBalance } from 'types/api/address'; import { route } from 'nextjs-routes'; +import NftEntity from 'ui/shared/entities/nft/NftEntity'; import TokenEntity from 'ui/shared/entities/token/TokenEntity'; import NftMedia from 'ui/shared/nft/NftMedia'; -import TruncatedTextTooltip from 'ui/shared/TruncatedTextTooltip'; type Props = AddressTokenBalance & { isLoading: boolean }; const NFTItem = ({ token, token_id: tokenId, token_instance: tokenInstance, isLoading }: Props) => { - const tokenLink = route({ pathname: '/token/[hash]', query: { hash: token.address } }); + const tokenInstanceLink = tokenId ? route({ pathname: '/token/[hash]/instance/[id]', query: { hash: token.address, id: tokenId } }) : undefined; return ( - - + - + { tokenId && ( ID# - - - - { tokenId } - - - + ) } - + ); }; diff --git a/ui/block/BlockDetails.tsx b/ui/block/BlockDetails.tsx index 4a7768c0e7..c5b7b9a40a 100644 --- a/ui/block/BlockDetails.tsx +++ b/ui/block/BlockDetails.tsx @@ -315,7 +315,7 @@ const BlockDetails = ({ query }: Props) => { isLoading={ isPlaceholderData } > - + { burntFees.dividedBy(WEI).toFixed() } { config.chain.currency.symbol } { !txFees.isEqualTo(ZERO) && ( diff --git a/ui/block/__screenshots__/BlockDetails.pw.tsx_dark-color-mode_regular-block-mobile-dark-mode-1.png b/ui/block/__screenshots__/BlockDetails.pw.tsx_dark-color-mode_regular-block-mobile-dark-mode-1.png index aac944ecf5..8c8e97b5b7 100644 Binary files a/ui/block/__screenshots__/BlockDetails.pw.tsx_dark-color-mode_regular-block-mobile-dark-mode-1.png and b/ui/block/__screenshots__/BlockDetails.pw.tsx_dark-color-mode_regular-block-mobile-dark-mode-1.png differ diff --git a/ui/block/__screenshots__/BlockDetails.pw.tsx_default_genesis-block-1.png b/ui/block/__screenshots__/BlockDetails.pw.tsx_default_genesis-block-1.png index fb1d3ec326..ccaa465904 100644 Binary files a/ui/block/__screenshots__/BlockDetails.pw.tsx_default_genesis-block-1.png and b/ui/block/__screenshots__/BlockDetails.pw.tsx_default_genesis-block-1.png differ diff --git a/ui/block/__screenshots__/BlockDetails.pw.tsx_default_regular-block-mobile-dark-mode-1.png b/ui/block/__screenshots__/BlockDetails.pw.tsx_default_regular-block-mobile-dark-mode-1.png index 969ae78ccc..a71de8226b 100644 Binary files a/ui/block/__screenshots__/BlockDetails.pw.tsx_default_regular-block-mobile-dark-mode-1.png and b/ui/block/__screenshots__/BlockDetails.pw.tsx_default_regular-block-mobile-dark-mode-1.png differ diff --git a/ui/block/__screenshots__/BlockDetails.pw.tsx_default_rootstock-custom-fields-1.png b/ui/block/__screenshots__/BlockDetails.pw.tsx_default_rootstock-custom-fields-1.png index f09e1fb3fe..6af195baa1 100644 Binary files a/ui/block/__screenshots__/BlockDetails.pw.tsx_default_rootstock-custom-fields-1.png and b/ui/block/__screenshots__/BlockDetails.pw.tsx_default_rootstock-custom-fields-1.png differ diff --git a/ui/block/__screenshots__/BlockDetails.pw.tsx_mobile_regular-block-mobile-dark-mode-1.png b/ui/block/__screenshots__/BlockDetails.pw.tsx_mobile_regular-block-mobile-dark-mode-1.png index e577cbc599..0a9dbf48e3 100644 Binary files a/ui/block/__screenshots__/BlockDetails.pw.tsx_mobile_regular-block-mobile-dark-mode-1.png and b/ui/block/__screenshots__/BlockDetails.pw.tsx_mobile_regular-block-mobile-dark-mode-1.png differ diff --git a/ui/blocks/BlocksListItem.tsx b/ui/blocks/BlocksListItem.tsx index 04404c7bbe..10d1cea1e1 100644 --- a/ui/blocks/BlocksListItem.tsx +++ b/ui/blocks/BlocksListItem.tsx @@ -103,7 +103,7 @@ const BlocksListItem = ({ data, isLoading, enableTimeIncrement }: Props) => { - + { burntFees.div(WEI).toFixed() } diff --git a/ui/blocks/BlocksTableItem.tsx b/ui/blocks/BlocksTableItem.tsx index b8661c5d8a..4676d82155 100644 --- a/ui/blocks/BlocksTableItem.tsx +++ b/ui/blocks/BlocksTableItem.tsx @@ -111,7 +111,7 @@ const BlocksTableItem = ({ data, isLoading, enableTimeIncrement }: Props) => { { !config.features.rollup.isEnabled && !config.UI.views.block.hiddenFields?.burnt_fees && ( - + { burntFees.dividedBy(WEI).toFixed(8) } diff --git a/ui/home/LatestBlocks.pw.tsx b/ui/home/LatestBlocks.pw.tsx index 6892124701..0e397533ea 100644 --- a/ui/home/LatestBlocks.pw.tsx +++ b/ui/home/LatestBlocks.pw.tsx @@ -3,9 +3,11 @@ import React from 'react'; import * as blockMock from 'mocks/blocks/block'; import * as statsMock from 'mocks/stats/index'; +import contextWithEnvs from 'playwright/fixtures/contextWithEnvs'; import * as socketServer from 'playwright/fixtures/socketServer'; import TestApp from 'playwright/TestApp'; import buildApiUrl from 'playwright/utils/buildApiUrl'; +import * as configs from 'playwright/utils/configs'; import LatestBlocks from './LatestBlocks'; @@ -38,6 +40,60 @@ test('default view +@mobile +@dark-mode', async({ mount, page }) => { await expect(component).toHaveScreenshot(); }); +const testL2 = test.extend({ + // eslint-disable-next-line @typescript-eslint/no-explicit-any + context: contextWithEnvs(configs.featureEnvs.rollup) as any, +}); + +testL2('L2 view', async({ mount, page }) => { + await page.route(STATS_API_URL, (route) => route.fulfill({ + status: 200, + body: JSON.stringify(statsMock.base), + })); + await page.route(BLOCKS_API_URL, (route) => route.fulfill({ + status: 200, + body: JSON.stringify([ + blockMock.base, + blockMock.base2, + ]), + })); + + const component = await mount( + + + , + ); + + await expect(component).toHaveScreenshot(); +}); + +const testNoReward = test.extend({ + // eslint-disable-next-line @typescript-eslint/no-explicit-any + context: contextWithEnvs(configs.viewsEnvs.block.hiddenFields) as any, +}); + +testNoReward('no reward view', async({ mount, page }) => { + await page.route(STATS_API_URL, (route) => route.fulfill({ + status: 200, + body: JSON.stringify(statsMock.base), + })); + await page.route(BLOCKS_API_URL, (route) => route.fulfill({ + status: 200, + body: JSON.stringify([ + blockMock.base, + blockMock.base2, + ]), + })); + + const component = await mount( + + + , + ); + + await expect(component).toHaveScreenshot(); +}); + test('with long block height', async({ mount, page }) => { await page.route(STATS_API_URL, (route) => route.fulfill({ status: 200, diff --git a/ui/home/LatestBlocks.tsx b/ui/home/LatestBlocks.tsx index 260e362669..f37799a68f 100644 --- a/ui/home/LatestBlocks.tsx +++ b/ui/home/LatestBlocks.tsx @@ -20,12 +20,7 @@ import LinkInternal from 'ui/shared/LinkInternal'; import LatestBlocksItem from './LatestBlocksItem'; -const BLOCK_HEIGHT_L1 = 166; -const BLOCK_HEIGHT_L2 = 112; -const BLOCK_MARGIN = 12; - const LatestBlocks = () => { - const blockHeight = config.features.rollup.isEnabled || config.UI.views.block.hiddenFields?.total_reward ? BLOCK_HEIGHT_L2 : BLOCK_HEIGHT_L1; const isMobile = useIsMobile(); // const blocksMaxCount = isMobile ? 2 : 3; let blocksMaxCount: number; @@ -78,7 +73,6 @@ const LatestBlocks = () => { if (data) { const dataToShow = data.slice(0, blocksMaxCount); - const blocksCount = dataToShow.length; content = ( <> @@ -92,13 +86,12 @@ const LatestBlocks = () => { ) } - + { dataToShow.map(((block, index) => ( ))) } @@ -112,7 +105,7 @@ const LatestBlocks = () => { } return ( - + Latest blocks { content } diff --git a/ui/home/LatestBlocksItem.tsx b/ui/home/LatestBlocksItem.tsx index e9948b9f18..d59856105e 100644 --- a/ui/home/LatestBlocksItem.tsx +++ b/ui/home/LatestBlocksItem.tsx @@ -18,26 +18,23 @@ import BlockEntity from 'ui/shared/entities/block/BlockEntity'; type Props = { block: Block; - h: number; isLoading?: boolean; } -const LatestBlocksItem = ({ block, h, isLoading }: Props) => { +const LatestBlocksItem = ({ block, isLoading }: Props) => { const totalReward = getBlockTotalReward(block); return ( { Txn { block.tx_count } - { !config.features.rollup.isEnabled && !config.UI.views.block.hiddenFields?.nonce && ( + + { !config.features.rollup.isEnabled && !config.UI.views.block.hiddenFields?.total_reward && ( <> Reward { totalReward.dp(10).toFixed() } + + ) } + + { !config.features.rollup.isEnabled && ( + <> { getNetworkValidatorTitle() } = { diff --git a/ui/pages/__screenshots__/Accounts.pw.tsx_dark-color-mode_base-view-mobile-dark-mode-1.png b/ui/pages/__screenshots__/Accounts.pw.tsx_dark-color-mode_base-view-mobile-dark-mode-1.png index 4ca009f643..7ebe27f5c0 100644 Binary files a/ui/pages/__screenshots__/Accounts.pw.tsx_dark-color-mode_base-view-mobile-dark-mode-1.png and b/ui/pages/__screenshots__/Accounts.pw.tsx_dark-color-mode_base-view-mobile-dark-mode-1.png differ diff --git a/ui/pages/__screenshots__/Accounts.pw.tsx_default_base-view-mobile-dark-mode-1.png b/ui/pages/__screenshots__/Accounts.pw.tsx_default_base-view-mobile-dark-mode-1.png index bbfbdbc09a..8e494c5f56 100644 Binary files a/ui/pages/__screenshots__/Accounts.pw.tsx_default_base-view-mobile-dark-mode-1.png and b/ui/pages/__screenshots__/Accounts.pw.tsx_default_base-view-mobile-dark-mode-1.png differ diff --git a/ui/pages/__screenshots__/Accounts.pw.tsx_mobile_base-view-mobile-dark-mode-1.png b/ui/pages/__screenshots__/Accounts.pw.tsx_mobile_base-view-mobile-dark-mode-1.png index 4f45133de0..7248572c46 100644 Binary files a/ui/pages/__screenshots__/Accounts.pw.tsx_mobile_base-view-mobile-dark-mode-1.png and b/ui/pages/__screenshots__/Accounts.pw.tsx_mobile_base-view-mobile-dark-mode-1.png differ diff --git a/ui/pages/__screenshots__/Blocks.pw.tsx_dark-color-mode_base-view-dark-mode-1.png b/ui/pages/__screenshots__/Blocks.pw.tsx_dark-color-mode_base-view-dark-mode-1.png index fe566bed67..949b2d523a 100644 Binary files a/ui/pages/__screenshots__/Blocks.pw.tsx_dark-color-mode_base-view-dark-mode-1.png and b/ui/pages/__screenshots__/Blocks.pw.tsx_dark-color-mode_base-view-dark-mode-1.png differ diff --git a/ui/pages/__screenshots__/Blocks.pw.tsx_default_base-view-dark-mode-1.png b/ui/pages/__screenshots__/Blocks.pw.tsx_default_base-view-dark-mode-1.png index 43b1894944..a65d7ae74e 100644 Binary files a/ui/pages/__screenshots__/Blocks.pw.tsx_default_base-view-dark-mode-1.png and b/ui/pages/__screenshots__/Blocks.pw.tsx_default_base-view-dark-mode-1.png differ diff --git a/ui/pages/__screenshots__/Blocks.pw.tsx_default_mobile-base-view-1.png b/ui/pages/__screenshots__/Blocks.pw.tsx_default_mobile-base-view-1.png index e17688cb0e..4a4ba6bce5 100644 Binary files a/ui/pages/__screenshots__/Blocks.pw.tsx_default_mobile-base-view-1.png and b/ui/pages/__screenshots__/Blocks.pw.tsx_default_mobile-base-view-1.png differ diff --git a/ui/pages/__screenshots__/Blocks.pw.tsx_default_mobile-hidden-fields-1.png b/ui/pages/__screenshots__/Blocks.pw.tsx_default_mobile-hidden-fields-1.png index 388b257335..fb202f4ff0 100644 Binary files a/ui/pages/__screenshots__/Blocks.pw.tsx_default_mobile-hidden-fields-1.png and b/ui/pages/__screenshots__/Blocks.pw.tsx_default_mobile-hidden-fields-1.png differ diff --git a/ui/pages/__screenshots__/Blocks.pw.tsx_default_new-item-from-socket-1.png b/ui/pages/__screenshots__/Blocks.pw.tsx_default_new-item-from-socket-1.png index bb797a1749..4ff5de8055 100644 Binary files a/ui/pages/__screenshots__/Blocks.pw.tsx_default_new-item-from-socket-1.png and b/ui/pages/__screenshots__/Blocks.pw.tsx_default_new-item-from-socket-1.png differ diff --git a/ui/pages/__screenshots__/Blocks.pw.tsx_default_socket-error-1.png b/ui/pages/__screenshots__/Blocks.pw.tsx_default_socket-error-1.png index 974d02ee6e..5fa494cfd1 100644 Binary files a/ui/pages/__screenshots__/Blocks.pw.tsx_default_socket-error-1.png and b/ui/pages/__screenshots__/Blocks.pw.tsx_default_socket-error-1.png differ diff --git a/ui/pages/__screenshots__/CsvExport.pw.tsx_dark-color-mode_base-view-mobile-dark-mode-1.png b/ui/pages/__screenshots__/CsvExport.pw.tsx_dark-color-mode_base-view-mobile-dark-mode-1.png index 47f9db4d6a..a7e7280953 100644 Binary files a/ui/pages/__screenshots__/CsvExport.pw.tsx_dark-color-mode_base-view-mobile-dark-mode-1.png and b/ui/pages/__screenshots__/CsvExport.pw.tsx_dark-color-mode_base-view-mobile-dark-mode-1.png differ diff --git a/ui/pages/__screenshots__/CsvExport.pw.tsx_default_base-view-mobile-dark-mode-1.png b/ui/pages/__screenshots__/CsvExport.pw.tsx_default_base-view-mobile-dark-mode-1.png index 441d111fd9..2003834634 100644 Binary files a/ui/pages/__screenshots__/CsvExport.pw.tsx_default_base-view-mobile-dark-mode-1.png and b/ui/pages/__screenshots__/CsvExport.pw.tsx_default_base-view-mobile-dark-mode-1.png differ diff --git a/ui/pages/__screenshots__/CsvExport.pw.tsx_mobile_base-view-mobile-dark-mode-1.png b/ui/pages/__screenshots__/CsvExport.pw.tsx_mobile_base-view-mobile-dark-mode-1.png index 8308c0f5e0..f3a9396fb8 100644 Binary files a/ui/pages/__screenshots__/CsvExport.pw.tsx_mobile_base-view-mobile-dark-mode-1.png and b/ui/pages/__screenshots__/CsvExport.pw.tsx_mobile_base-view-mobile-dark-mode-1.png differ diff --git a/ui/pages/__screenshots__/Home.pw.tsx_dark-color-mode_default-view---default-dark-mode-1.png b/ui/pages/__screenshots__/Home.pw.tsx_dark-color-mode_default-view---default-dark-mode-1.png index 9765b48d45..2b9d23bd8c 100644 Binary files a/ui/pages/__screenshots__/Home.pw.tsx_dark-color-mode_default-view---default-dark-mode-1.png and b/ui/pages/__screenshots__/Home.pw.tsx_dark-color-mode_default-view---default-dark-mode-1.png differ diff --git a/ui/pages/__screenshots__/Home.pw.tsx_default_default-view-screen-xl-1.png b/ui/pages/__screenshots__/Home.pw.tsx_default_default-view-screen-xl-1.png index 89d84c1967..83b4f54173 100644 Binary files a/ui/pages/__screenshots__/Home.pw.tsx_default_default-view-screen-xl-1.png and b/ui/pages/__screenshots__/Home.pw.tsx_default_default-view-screen-xl-1.png differ diff --git a/ui/pages/__screenshots__/Home.pw.tsx_default_mobile-base-view-1.png b/ui/pages/__screenshots__/Home.pw.tsx_default_mobile-base-view-1.png index efbc67ee55..7161e577b6 100644 Binary files a/ui/pages/__screenshots__/Home.pw.tsx_default_mobile-base-view-1.png and b/ui/pages/__screenshots__/Home.pw.tsx_default_mobile-base-view-1.png differ diff --git a/ui/pages/__screenshots__/Token.pw.tsx_default_base-view-1.png b/ui/pages/__screenshots__/Token.pw.tsx_default_base-view-1.png index 175d0b3177..6d1b01b56c 100644 Binary files a/ui/pages/__screenshots__/Token.pw.tsx_default_base-view-1.png and b/ui/pages/__screenshots__/Token.pw.tsx_default_base-view-1.png differ diff --git a/ui/pages/__screenshots__/Token.pw.tsx_default_mobile-base-view-1.png b/ui/pages/__screenshots__/Token.pw.tsx_default_mobile-base-view-1.png index ef6bb8e4c9..46cc87e6cf 100644 Binary files a/ui/pages/__screenshots__/Token.pw.tsx_default_mobile-base-view-1.png and b/ui/pages/__screenshots__/Token.pw.tsx_default_mobile-base-view-1.png differ diff --git a/ui/pages/__screenshots__/Token.pw.tsx_default_mobile-with-verified-info-1.png b/ui/pages/__screenshots__/Token.pw.tsx_default_mobile-with-verified-info-1.png index 98ead0b5c9..18bab404a0 100644 Binary files a/ui/pages/__screenshots__/Token.pw.tsx_default_mobile-with-verified-info-1.png and b/ui/pages/__screenshots__/Token.pw.tsx_default_mobile-with-verified-info-1.png differ diff --git a/ui/pages/__screenshots__/Token.pw.tsx_default_with-verified-info-1.png b/ui/pages/__screenshots__/Token.pw.tsx_default_with-verified-info-1.png index fb9a3154b8..30cb71646b 100644 Binary files a/ui/pages/__screenshots__/Token.pw.tsx_default_with-verified-info-1.png and b/ui/pages/__screenshots__/Token.pw.tsx_default_with-verified-info-1.png differ diff --git a/ui/pages/__screenshots__/VerifiedAddresses.pw.tsx_default_address-verification-flow-1.png b/ui/pages/__screenshots__/VerifiedAddresses.pw.tsx_default_address-verification-flow-1.png index 23802eacbe..7adbd13a6c 100644 Binary files a/ui/pages/__screenshots__/VerifiedAddresses.pw.tsx_default_address-verification-flow-1.png and b/ui/pages/__screenshots__/VerifiedAddresses.pw.tsx_default_address-verification-flow-1.png differ diff --git a/ui/pages/__screenshots__/VerifiedAddresses.pw.tsx_default_base-view-mobile-1.png b/ui/pages/__screenshots__/VerifiedAddresses.pw.tsx_default_base-view-mobile-1.png index 7e57418295..bda2779dd5 100644 Binary files a/ui/pages/__screenshots__/VerifiedAddresses.pw.tsx_default_base-view-mobile-1.png and b/ui/pages/__screenshots__/VerifiedAddresses.pw.tsx_default_base-view-mobile-1.png differ diff --git a/ui/pages/__screenshots__/VerifiedAddresses.pw.tsx_mobile_base-view-mobile-1.png b/ui/pages/__screenshots__/VerifiedAddresses.pw.tsx_mobile_base-view-mobile-1.png index b9782ce8f0..38ea0e7023 100644 Binary files a/ui/pages/__screenshots__/VerifiedAddresses.pw.tsx_mobile_base-view-mobile-1.png and b/ui/pages/__screenshots__/VerifiedAddresses.pw.tsx_mobile_base-view-mobile-1.png differ diff --git a/ui/pages/__screenshots__/VerifiedContracts.pw.tsx_default_base-view-mobile-1.png b/ui/pages/__screenshots__/VerifiedContracts.pw.tsx_default_base-view-mobile-1.png index 4e72d6b2af..b263c05a63 100644 Binary files a/ui/pages/__screenshots__/VerifiedContracts.pw.tsx_default_base-view-mobile-1.png and b/ui/pages/__screenshots__/VerifiedContracts.pw.tsx_default_base-view-mobile-1.png differ diff --git a/ui/pages/__screenshots__/VerifiedContracts.pw.tsx_mobile_base-view-mobile-1.png b/ui/pages/__screenshots__/VerifiedContracts.pw.tsx_mobile_base-view-mobile-1.png index 858a19f219..cc307a2cae 100644 Binary files a/ui/pages/__screenshots__/VerifiedContracts.pw.tsx_mobile_base-view-mobile-1.png and b/ui/pages/__screenshots__/VerifiedContracts.pw.tsx_mobile_base-view-mobile-1.png differ diff --git a/ui/searchResults/SearchResultListItem.tsx b/ui/searchResults/SearchResultListItem.tsx index bb0d2b3451..b6b24bd0ef 100644 --- a/ui/searchResults/SearchResultListItem.tsx +++ b/ui/searchResults/SearchResultListItem.tsx @@ -50,7 +50,7 @@ const SearchResultListItem = ({ data, searchTerm, isLoading }: Props) => { return ( - + { <> - + ) } - + { config.features.account.isEnabled && } ); diff --git a/ui/shared/IdenticonGithub.tsx b/ui/shared/IdenticonGithub.tsx new file mode 100644 index 0000000000..430740247c --- /dev/null +++ b/ui/shared/IdenticonGithub.tsx @@ -0,0 +1,43 @@ +import { useColorModeValue, useToken, Box, chakra, Skeleton } from '@chakra-ui/react'; +import dynamic from 'next/dynamic'; +import React from 'react'; + +const Identicon = dynamic<{ bg: string; string: string; size: number }>( + async() => { + const lib = await import('react-identicons'); + return typeof lib === 'object' && 'default' in lib ? lib.default : lib; + }, + { + loading: () => , + ssr: false, + }, +); + +interface Props { + className?: string; + size: number; + seed: string; +} + +const IdenticonGithub = ({ size, seed }: Props) => { + const bgColor = useToken('colors', useColorModeValue('gray.100', 'white')); + + return ( + + + + ); +}; + +export default React.memo(chakra(IdenticonGithub)); diff --git a/ui/shared/Page/__screenshots__/PageTitle.pw.tsx_default_default-view-mobile-1.png b/ui/shared/Page/__screenshots__/PageTitle.pw.tsx_default_default-view-mobile-1.png index 2d9e7a174a..37ff077127 100644 Binary files a/ui/shared/Page/__screenshots__/PageTitle.pw.tsx_default_default-view-mobile-1.png and b/ui/shared/Page/__screenshots__/PageTitle.pw.tsx_default_default-view-mobile-1.png differ diff --git a/ui/shared/Page/__screenshots__/PageTitle.pw.tsx_mobile_default-view-mobile-1.png b/ui/shared/Page/__screenshots__/PageTitle.pw.tsx_mobile_default-view-mobile-1.png index 485ebd761e..400f85c7bd 100644 Binary files a/ui/shared/Page/__screenshots__/PageTitle.pw.tsx_mobile_default-view-mobile-1.png and b/ui/shared/Page/__screenshots__/PageTitle.pw.tsx_mobile_default-view-mobile-1.png differ diff --git a/ui/shared/TokenTransfer/TokenTransferListItem.tsx b/ui/shared/TokenTransfer/TokenTransferListItem.tsx index b0a5ef8e2f..04bfc7d3f1 100644 --- a/ui/shared/TokenTransfer/TokenTransferListItem.tsx +++ b/ui/shared/TokenTransfer/TokenTransferListItem.tsx @@ -9,12 +9,12 @@ import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement'; import Icon from 'ui/shared/chakra/Icon'; import Tag from 'ui/shared/chakra/Tag'; import AddressEntity from 'ui/shared/entities/address/AddressEntity'; +import NftEntity from 'ui/shared/entities/nft/NftEntity'; import TokenEntity from 'ui/shared/entities/token/TokenEntity'; import TxEntity from 'ui/shared/entities/tx/TxEntity'; import InOutTag from 'ui/shared/InOutTag'; import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile'; import { getTokenTransferTypeText } from 'ui/shared/TokenTransfer/helpers'; -import TokenTransferNft from 'ui/shared/TokenTransfer/TokenTransferNft'; import TxAdditionalInfo from 'ui/txs/TxAdditionalInfo'; type Props = TokenTransfer & { @@ -66,7 +66,7 @@ const TokenTransferListItem = ({ ) } - { 'token_id' in total && } + { 'token_id' in total && } { showTxInfo && txHash && ( { - const Component = isDisabled || isLoading ? Box : LinkInternal; - - return ( - - - - { truncation === 'constant' ? : } - - - ); -}; - -export default React.memo(chakra(TokenTransferNft)); diff --git a/ui/shared/TokenTransfer/TokenTransferTableItem.tsx b/ui/shared/TokenTransfer/TokenTransferTableItem.tsx index a8f71f3895..e246a4217a 100644 --- a/ui/shared/TokenTransfer/TokenTransferTableItem.tsx +++ b/ui/shared/TokenTransfer/TokenTransferTableItem.tsx @@ -7,11 +7,11 @@ import type { TokenTransfer } from 'types/api/tokenTransfer'; import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement'; import Tag from 'ui/shared/chakra/Tag'; import AddressEntity from 'ui/shared/entities/address/AddressEntity'; +import NftEntity from 'ui/shared/entities/nft/NftEntity'; import TokenEntity from 'ui/shared/entities/token/TokenEntity'; import TxEntity from 'ui/shared/entities/tx/TxEntity'; import InOutTag from 'ui/shared/InOutTag'; import { getTokenTransferTypeText } from 'ui/shared/TokenTransfer/helpers'; -import TokenTransferNft from 'ui/shared/TokenTransfer/TokenTransferNft'; import TxAdditionalInfo from 'ui/txs/TxAdditionalInfo'; type Props = TokenTransfer & { @@ -59,7 +59,7 @@ const TokenTransferTableItem = ({ - { 'token_id' in total && } + { 'token_id' in total && } { showTxInfo && txHash && ( diff --git a/ui/shared/TokenTransfer/__screenshots__/TokenTransferList.pw.tsx_default_with-tx-info-1.png b/ui/shared/TokenTransfer/__screenshots__/TokenTransferList.pw.tsx_default_with-tx-info-1.png index bcda655d76..fcc155046a 100644 Binary files a/ui/shared/TokenTransfer/__screenshots__/TokenTransferList.pw.tsx_default_with-tx-info-1.png and b/ui/shared/TokenTransfer/__screenshots__/TokenTransferList.pw.tsx_default_with-tx-info-1.png differ diff --git a/ui/shared/TokenTransfer/__screenshots__/TokenTransferList.pw.tsx_default_without-tx-info-1.png b/ui/shared/TokenTransfer/__screenshots__/TokenTransferList.pw.tsx_default_without-tx-info-1.png index 3bff7abb33..4bfe59dc45 100644 Binary files a/ui/shared/TokenTransfer/__screenshots__/TokenTransferList.pw.tsx_default_without-tx-info-1.png and b/ui/shared/TokenTransfer/__screenshots__/TokenTransferList.pw.tsx_default_without-tx-info-1.png differ diff --git a/ui/shared/TokenTransfer/__screenshots__/TokenTransferTable.pw.tsx_default_with-tx-info-1.png b/ui/shared/TokenTransfer/__screenshots__/TokenTransferTable.pw.tsx_default_with-tx-info-1.png index 5f3f8db2ed..0698da884e 100644 Binary files a/ui/shared/TokenTransfer/__screenshots__/TokenTransferTable.pw.tsx_default_with-tx-info-1.png and b/ui/shared/TokenTransfer/__screenshots__/TokenTransferTable.pw.tsx_default_with-tx-info-1.png differ diff --git a/ui/shared/TokenTransfer/__screenshots__/TokenTransferTable.pw.tsx_default_without-tx-info-1.png b/ui/shared/TokenTransfer/__screenshots__/TokenTransferTable.pw.tsx_default_without-tx-info-1.png index 36edbac429..86af2c0a53 100644 Binary files a/ui/shared/TokenTransfer/__screenshots__/TokenTransferTable.pw.tsx_default_without-tx-info-1.png and b/ui/shared/TokenTransfer/__screenshots__/TokenTransferTable.pw.tsx_default_without-tx-info-1.png differ diff --git a/ui/shared/UserAvatar.tsx b/ui/shared/UserAvatar.tsx index 2b73219d60..0f2c2d2685 100644 --- a/ui/shared/UserAvatar.tsx +++ b/ui/shared/UserAvatar.tsx @@ -1,35 +1,10 @@ -import { useColorModeValue, useToken, SkeletonCircle, Image, Box } from '@chakra-ui/react'; +import { SkeletonCircle, Image } from '@chakra-ui/react'; import React from 'react'; -import Identicon from 'react-identicons'; import { useAppContext } from 'lib/contexts/app'; import * as cookies from 'lib/cookies'; import useFetchProfileInfo from 'lib/hooks/useFetchProfileInfo'; - -const IdenticonComponent = typeof Identicon === 'object' && 'default' in Identicon ? Identicon.default : Identicon; - -// for those who haven't got profile -// or if we cannot download the profile picture for some reasons -const FallbackImage = ({ size, id }: { size: number; id: string }) => { - const bgColor = useToken('colors', useColorModeValue('gray.100', 'white')); - - return ( - - - - - - ); -}; +import IdenticonGithub from 'ui/shared/IdenticonGithub'; interface Props { size: number; @@ -56,13 +31,10 @@ const UserAvatar = ({ size }: Props) => { flexShrink={ 0 } src={ data?.avatar } alt={ `Profile picture of ${ data?.name || data?.nickname || '' }` } - w={ sizeString } - minW={ sizeString } - h={ sizeString } - minH={ sizeString } + boxSize={ `${ size }px` } borderRadius="full" overflow="hidden" - fallback={ isImageLoadError || !data?.avatar ? : undefined } + fallback={ isImageLoadError || !data?.avatar ? : undefined } onError={ handleImageLoadError } /> ); diff --git a/ui/shared/Utilization/Utilization.tsx b/ui/shared/Utilization/Utilization.tsx index 712de5e52a..03dbe7e6f0 100644 --- a/ui/shared/Utilization/Utilization.tsx +++ b/ui/shared/Utilization/Utilization.tsx @@ -17,7 +17,7 @@ const Utilization = ({ className, value, colorScheme = 'green', isLoading }: Pro const color = colorScheme === 'gray' ? colorGrayScheme : 'green.500'; return ( - + diff --git a/ui/shared/Utilization/__screenshots__/Utilization.pw.tsx_dark-color-mode_gray-color-scheme-dark-mode-1.png b/ui/shared/Utilization/__screenshots__/Utilization.pw.tsx_dark-color-mode_gray-color-scheme-dark-mode-1.png index a5bc885067..65f3877c09 100644 Binary files a/ui/shared/Utilization/__screenshots__/Utilization.pw.tsx_dark-color-mode_gray-color-scheme-dark-mode-1.png and b/ui/shared/Utilization/__screenshots__/Utilization.pw.tsx_dark-color-mode_gray-color-scheme-dark-mode-1.png differ diff --git a/ui/shared/Utilization/__screenshots__/Utilization.pw.tsx_dark-color-mode_green-color-scheme-dark-mode-1.png b/ui/shared/Utilization/__screenshots__/Utilization.pw.tsx_dark-color-mode_green-color-scheme-dark-mode-1.png index f1e3f88508..373ecc63dc 100644 Binary files a/ui/shared/Utilization/__screenshots__/Utilization.pw.tsx_dark-color-mode_green-color-scheme-dark-mode-1.png and b/ui/shared/Utilization/__screenshots__/Utilization.pw.tsx_dark-color-mode_green-color-scheme-dark-mode-1.png differ diff --git a/ui/shared/Utilization/__screenshots__/Utilization.pw.tsx_default_gray-color-scheme-dark-mode-1.png b/ui/shared/Utilization/__screenshots__/Utilization.pw.tsx_default_gray-color-scheme-dark-mode-1.png index 97c6187eb3..5e2fbdc587 100644 Binary files a/ui/shared/Utilization/__screenshots__/Utilization.pw.tsx_default_gray-color-scheme-dark-mode-1.png and b/ui/shared/Utilization/__screenshots__/Utilization.pw.tsx_default_gray-color-scheme-dark-mode-1.png differ diff --git a/ui/shared/Utilization/__screenshots__/Utilization.pw.tsx_default_green-color-scheme-dark-mode-1.png b/ui/shared/Utilization/__screenshots__/Utilization.pw.tsx_default_green-color-scheme-dark-mode-1.png index d1790be217..a0f05ef907 100644 Binary files a/ui/shared/Utilization/__screenshots__/Utilization.pw.tsx_default_green-color-scheme-dark-mode-1.png and b/ui/shared/Utilization/__screenshots__/Utilization.pw.tsx_default_green-color-scheme-dark-mode-1.png differ diff --git a/ui/shared/entities/address/AddressEntity.tsx b/ui/shared/entities/address/AddressEntity.tsx index 129c580f75..3077610628 100644 --- a/ui/shared/entities/address/AddressEntity.tsx +++ b/ui/shared/entities/address/AddressEntity.tsx @@ -2,7 +2,6 @@ import type { As } from '@chakra-ui/react'; import { Flex, Skeleton, Tooltip, chakra } from '@chakra-ui/react'; import _omit from 'lodash/omit'; import React from 'react'; -import Jazzicon, { jsNumberForAddress } from 'react-jazzicon'; import type { AddressParam } from 'types/api/addressParams'; @@ -14,6 +13,7 @@ import iconContract from 'icons/contract.svg'; import * as EntityBase from 'ui/shared/entities/base/components'; import { getIconProps } from '../base/utils'; +import AddressIdenticon from './AddressIdenticon'; type LinkProps = EntityBase.LinkBaseProps & Pick; @@ -88,8 +88,11 @@ const Icon = (props: IconProps) => { return ( - - + + ); diff --git a/ui/shared/entities/address/AddressIdenticon.tsx b/ui/shared/entities/address/AddressIdenticon.tsx new file mode 100644 index 0000000000..5a19e6c6be --- /dev/null +++ b/ui/shared/entities/address/AddressIdenticon.tsx @@ -0,0 +1,78 @@ +import { Box, Image } from '@chakra-ui/react'; +import dynamic from 'next/dynamic'; +import React from 'react'; + +import config from 'configs/app'; +import IdenticonGithub from 'ui/shared/IdenticonGithub'; + +interface IconProps { + hash: string; + size: number; +} + +const Icon = dynamic( + async() => { + switch (config.UI.views.address.identiconType) { + case 'github': { + // eslint-disable-next-line react/display-name + return (props: IconProps) => ; + } + + case 'blockie': { + const makeBlockie = (await import('ethereum-blockies-base64')).default; + + // eslint-disable-next-line react/display-name + return (props: IconProps) => { + const data = makeBlockie(props.hash); + return ( + { + ); + }; + } + + case 'jazzicon': { + const Jazzicon = await import('react-jazzicon'); + + // eslint-disable-next-line react/display-name + return (props: IconProps) => { + return ( + + ); + }; + } + + case 'gradient_avatar': { + const GradientAvatar = (await import('gradient-avatar')).default; + + // eslint-disable-next-line react/display-name + return (props: IconProps) => { + const svg = GradientAvatar(props.hash, props.size); + return
; + }; + } + + default: { + return () => null; + } + } + }, { + ssr: false, + }); + +type Props = IconProps; + +const AddressIdenticon = ({ size, hash }: Props) => { + return ( + + + + ); +}; + +export default React.memo(AddressIdenticon); diff --git a/ui/shared/entities/address/__screenshots__/AddressEntity.pw.tsx_default_contract-unverified-1.png b/ui/shared/entities/address/__screenshots__/AddressEntity.pw.tsx_default_contract-unverified-1.png index 0b7d2dec08..56dd9fd2a0 100644 Binary files a/ui/shared/entities/address/__screenshots__/AddressEntity.pw.tsx_default_contract-unverified-1.png and b/ui/shared/entities/address/__screenshots__/AddressEntity.pw.tsx_default_contract-unverified-1.png differ diff --git a/ui/shared/entities/nft/NftEntity.pw.tsx b/ui/shared/entities/nft/NftEntity.pw.tsx new file mode 100644 index 0000000000..c426b8b8fa --- /dev/null +++ b/ui/shared/entities/nft/NftEntity.pw.tsx @@ -0,0 +1,72 @@ +import { test, expect } from '@playwright/experimental-ct-react'; +import React from 'react'; + +import TestApp from 'playwright/TestApp'; + +import NftEntity from './NftEntity'; + +const iconSizes = [ 'md', 'lg' ]; +const hash = '0xd789a607CEac2f0E14867de4EB15b15C9FFB5859'; + +test.use({ viewport: { width: 180, height: 30 } }); + +test.describe('icon sizes', () => { + iconSizes.forEach((size) => { + test(size, async({ mount }) => { + const component = await mount( + + + , + ); + + await expect(component).toHaveScreenshot(); + }); + }); +}); + +test('loading', async({ mount }) => { + const component = await mount( + + + , + ); + + await expect(component).toHaveScreenshot(); +}); + +test('long id', async({ mount }) => { + const component = await mount( + + + , + ); + + await expect(component).toHaveScreenshot(); +}); + +test('customization', async({ mount }) => { + const component = await mount( + + + , + ); + + await expect(component).toHaveScreenshot(); +}); diff --git a/ui/shared/entities/nft/NftEntity.tsx b/ui/shared/entities/nft/NftEntity.tsx new file mode 100644 index 0000000000..7117b292e1 --- /dev/null +++ b/ui/shared/entities/nft/NftEntity.tsx @@ -0,0 +1,84 @@ +import type { As } from '@chakra-ui/react'; +import { chakra } from '@chakra-ui/react'; +import _omit from 'lodash/omit'; +import React from 'react'; + +import { route } from 'nextjs-routes'; + +import nftPlaceholder from 'icons/nft_shield.svg'; +import * as EntityBase from 'ui/shared/entities/base/components'; +import TruncatedValue from 'ui/shared/TruncatedValue'; + +const Container = EntityBase.Container; + +type IconProps = Pick & { + asProp?: As; +}; + +const Icon = (props: IconProps) => { + if (props.noIcon) { + return null; + } + + return ( + + ); +}; + +type LinkProps = EntityBase.LinkBaseProps & Pick; + +const Link = chakra((props: LinkProps) => { + const defaultHref = route({ pathname: '/token/[hash]/instance/[id]', query: { hash: props.hash, id: props.id } }); + + return ( + + { props.children } + + ); +}); + +type ContentProps = Omit & Pick; + +const Content = chakra((props: ContentProps) => { + return ( + + ); +}); + +export interface EntityProps extends EntityBase.EntityBaseProps { + hash: string; + id: string; +} + +const NftEntity = (props: EntityProps) => { + const linkProps = _omit(props, [ 'className' ]); + const partsProps = _omit(props, [ 'className', 'onClick' ]); + + return ( + + + + + + + ); +}; + +export default React.memo(chakra(NftEntity)); + +export { + Container, + Link, + Icon, + Content, +}; diff --git a/ui/shared/entities/nft/__screenshots__/NftEntity.pw.tsx_default_customization-1.png b/ui/shared/entities/nft/__screenshots__/NftEntity.pw.tsx_default_customization-1.png new file mode 100644 index 0000000000..0d1fe6f54a Binary files /dev/null and b/ui/shared/entities/nft/__screenshots__/NftEntity.pw.tsx_default_customization-1.png differ diff --git a/ui/shared/entities/nft/__screenshots__/NftEntity.pw.tsx_default_icon-sizes-lg-1.png b/ui/shared/entities/nft/__screenshots__/NftEntity.pw.tsx_default_icon-sizes-lg-1.png new file mode 100644 index 0000000000..c6461d3e90 Binary files /dev/null and b/ui/shared/entities/nft/__screenshots__/NftEntity.pw.tsx_default_icon-sizes-lg-1.png differ diff --git a/ui/shared/entities/nft/__screenshots__/NftEntity.pw.tsx_default_icon-sizes-md-1.png b/ui/shared/entities/nft/__screenshots__/NftEntity.pw.tsx_default_icon-sizes-md-1.png new file mode 100644 index 0000000000..9e572ad277 Binary files /dev/null and b/ui/shared/entities/nft/__screenshots__/NftEntity.pw.tsx_default_icon-sizes-md-1.png differ diff --git a/ui/shared/entities/nft/__screenshots__/NftEntity.pw.tsx_default_loading-1.png b/ui/shared/entities/nft/__screenshots__/NftEntity.pw.tsx_default_loading-1.png new file mode 100644 index 0000000000..f9c97157f9 Binary files /dev/null and b/ui/shared/entities/nft/__screenshots__/NftEntity.pw.tsx_default_loading-1.png differ diff --git a/ui/shared/entities/nft/__screenshots__/NftEntity.pw.tsx_default_long-id-1.png b/ui/shared/entities/nft/__screenshots__/NftEntity.pw.tsx_default_long-id-1.png new file mode 100644 index 0000000000..90d0dd1922 Binary files /dev/null and b/ui/shared/entities/nft/__screenshots__/NftEntity.pw.tsx_default_long-id-1.png differ diff --git a/ui/shared/entities/token/TokenEntity.tsx b/ui/shared/entities/token/TokenEntity.tsx index b98a22cdb6..53ff91f289 100644 --- a/ui/shared/entities/token/TokenEntity.tsx +++ b/ui/shared/entities/token/TokenEntity.tsx @@ -52,6 +52,7 @@ const Icon = (props: IconProps) => { return ( { { const Container = EntityBase.Container; export interface EntityProps extends EntityBase.EntityBaseProps { - token: Pick; + token: Pick; noSymbol?: boolean; jointSymbol?: boolean; onlySymbol?: boolean; diff --git a/ui/shared/layout/__screenshots__/Layout.pw.tsx_default_base-view-mobile-1.png b/ui/shared/layout/__screenshots__/Layout.pw.tsx_default_base-view-mobile-1.png index 90961087b1..5563cdd4a2 100644 Binary files a/ui/shared/layout/__screenshots__/Layout.pw.tsx_default_base-view-mobile-1.png and b/ui/shared/layout/__screenshots__/Layout.pw.tsx_default_base-view-mobile-1.png differ diff --git a/ui/shared/layout/__screenshots__/Layout.pw.tsx_mobile_base-view-mobile-1.png b/ui/shared/layout/__screenshots__/Layout.pw.tsx_mobile_base-view-mobile-1.png index 4ee0ed66bb..1ef92871b4 100644 Binary files a/ui/shared/layout/__screenshots__/Layout.pw.tsx_mobile_base-view-mobile-1.png and b/ui/shared/layout/__screenshots__/Layout.pw.tsx_mobile_base-view-mobile-1.png differ diff --git a/ui/shared/layout/__screenshots__/LayoutError.pw.tsx_default_base-view-mobile-1.png b/ui/shared/layout/__screenshots__/LayoutError.pw.tsx_default_base-view-mobile-1.png index c2123399a6..45c55e082c 100644 Binary files a/ui/shared/layout/__screenshots__/LayoutError.pw.tsx_default_base-view-mobile-1.png and b/ui/shared/layout/__screenshots__/LayoutError.pw.tsx_default_base-view-mobile-1.png differ diff --git a/ui/shared/layout/__screenshots__/LayoutError.pw.tsx_mobile_base-view-mobile-1.png b/ui/shared/layout/__screenshots__/LayoutError.pw.tsx_mobile_base-view-mobile-1.png index 88055ff235..87190aff50 100644 Binary files a/ui/shared/layout/__screenshots__/LayoutError.pw.tsx_mobile_base-view-mobile-1.png and b/ui/shared/layout/__screenshots__/LayoutError.pw.tsx_mobile_base-view-mobile-1.png differ diff --git a/ui/shared/layout/__screenshots__/LayoutHome.pw.tsx_default_base-view-mobile-1.png b/ui/shared/layout/__screenshots__/LayoutHome.pw.tsx_default_base-view-mobile-1.png index 0acd1b9a59..f811b3d356 100644 Binary files a/ui/shared/layout/__screenshots__/LayoutHome.pw.tsx_default_base-view-mobile-1.png and b/ui/shared/layout/__screenshots__/LayoutHome.pw.tsx_default_base-view-mobile-1.png differ diff --git a/ui/shared/layout/__screenshots__/LayoutHome.pw.tsx_mobile_base-view-mobile-1.png b/ui/shared/layout/__screenshots__/LayoutHome.pw.tsx_mobile_base-view-mobile-1.png index 14cad1511a..6beb975865 100644 Binary files a/ui/shared/layout/__screenshots__/LayoutHome.pw.tsx_mobile_base-view-mobile-1.png and b/ui/shared/layout/__screenshots__/LayoutHome.pw.tsx_mobile_base-view-mobile-1.png differ diff --git a/ui/shared/nft/NftFallback.tsx b/ui/shared/nft/NftFallback.tsx index c9836d8688..73cf322650 100644 --- a/ui/shared/nft/NftFallback.tsx +++ b/ui/shared/nft/NftFallback.tsx @@ -1,16 +1,18 @@ -import { Icon, useColorModeValue } from '@chakra-ui/react'; +import { Icon, useColorModeValue, chakra } from '@chakra-ui/react'; import React from 'react'; import nftIcon from 'icons/nft_shield.svg'; -const NftFallback = () => { +const NftFallback = ({ className }: {className?: string}) => { return ( ); }; -export default NftFallback; +export default chakra(NftFallback); diff --git a/ui/shared/nft/NftHtml.tsx b/ui/shared/nft/NftHtml.tsx index 4bd9e59729..22a8ec71a3 100644 --- a/ui/shared/nft/NftHtml.tsx +++ b/ui/shared/nft/NftHtml.tsx @@ -1,20 +1,30 @@ -import { chakra } from '@chakra-ui/react'; +import { chakra, LinkOverlay } from '@chakra-ui/react'; import React from 'react'; +import { mediaStyleProps } from './utils'; + interface Props { src: string; onLoad: () => void; onError: () => void; + onClick?: () => void; } -const NftHtml = ({ src, onLoad, onError }: Props) => { +const NftHtml = ({ src, onLoad, onError, onClick }: Props) => { return ( - + + + ); }; diff --git a/ui/shared/nft/NftHtmlFullscreen.tsx b/ui/shared/nft/NftHtmlFullscreen.tsx new file mode 100644 index 0000000000..0e28fab523 --- /dev/null +++ b/ui/shared/nft/NftHtmlFullscreen.tsx @@ -0,0 +1,25 @@ +import { chakra } from '@chakra-ui/react'; +import React from 'react'; + +import NftMediaFullscreenModal from './NftMediaFullscreenModal'; + +interface Props { + src: string; + isOpen: boolean; + onClose: () => void; +} + +const NftHtmlWithFullscreen = ({ src, isOpen, onClose }: Props) => { + return ( + + + + ); +}; + +export default NftHtmlWithFullscreen; diff --git a/ui/shared/nft/NftHtmlWithFullscreen.tsx b/ui/shared/nft/NftHtmlWithFullscreen.tsx new file mode 100644 index 0000000000..eca068fae0 --- /dev/null +++ b/ui/shared/nft/NftHtmlWithFullscreen.tsx @@ -0,0 +1,33 @@ +import { chakra, useDisclosure } from '@chakra-ui/react'; +import React from 'react'; + +import NftHtml from './NftHtml'; +import NftMediaFullscreenModal from './NftMediaFullscreenModal'; + +interface Props { + src: string; + onLoad: () => void; + onError: () => void; +} + +const NftHtmlWithFullscreen = ({ src, onLoad, onError }: Props) => { + const { isOpen, onOpen, onClose } = useDisclosure(); + + return ( + <> + + + + + + ); +}; + +export default NftHtmlWithFullscreen; diff --git a/ui/shared/nft/NftImage.tsx b/ui/shared/nft/NftImage.tsx index f2ddcee44d..a728c93d65 100644 --- a/ui/shared/nft/NftImage.tsx +++ b/ui/shared/nft/NftImage.tsx @@ -1,21 +1,26 @@ import { Image } from '@chakra-ui/react'; import React from 'react'; +import { mediaStyleProps } from './utils'; + interface Props { - url: string; + src: string; onLoad: () => void; onError: () => void; + onClick?: () => void; } -const NftImage = ({ url, onLoad, onError }: Props) => { +const NftImage = ({ src, onLoad, onError, onClick }: Props) => { return ( Token instance image ); }; diff --git a/ui/shared/nft/NftImageFullscreen.tsx b/ui/shared/nft/NftImageFullscreen.tsx new file mode 100644 index 0000000000..04fad72270 --- /dev/null +++ b/ui/shared/nft/NftImageFullscreen.tsx @@ -0,0 +1,22 @@ +import { + Image, +} from '@chakra-ui/react'; +import React from 'react'; + +import NftMediaFullscreenModal from './NftMediaFullscreenModal'; + +interface Props { + src: string; + isOpen: boolean; + onClose: () => void; +} + +const NftImageWithFullscreen = ({ src, isOpen, onClose }: Props) => { + return ( + + Token instance image + + ); +}; + +export default NftImageWithFullscreen; diff --git a/ui/shared/nft/NftImageWithFullscreen.tsx b/ui/shared/nft/NftImageWithFullscreen.tsx new file mode 100644 index 0000000000..374ffbda92 --- /dev/null +++ b/ui/shared/nft/NftImageWithFullscreen.tsx @@ -0,0 +1,29 @@ +import { + Image, + useDisclosure, +} from '@chakra-ui/react'; +import React from 'react'; + +import NftImage from './NftImage'; +import NftMediaFullscreenModal from './NftMediaFullscreenModal'; + +interface Props { + src: string; + onLoad: () => void; + onError: () => void; +} + +const NftImageWithFullscreen = ({ src, onLoad, onError }: Props) => { + const { isOpen, onOpen, onClose } = useDisclosure(); + + return ( + <> + + + Token instance image + + + ); +}; + +export default NftImageWithFullscreen; diff --git a/ui/shared/nft/NftMedia.pw.tsx b/ui/shared/nft/NftMedia.pw.tsx index 2565b8dac8..71d09de045 100644 --- a/ui/shared/nft/NftMedia.pw.tsx +++ b/ui/shared/nft/NftMedia.pw.tsx @@ -5,20 +5,47 @@ import TestApp from 'playwright/TestApp'; import NftMedia from './NftMedia'; -test.use({ viewport: { width: 250, height: 250 } }); +test.describe('no url', () => { + test.use({ viewport: { width: 250, height: 250 } }); + test('preview +@dark-mode', async({ mount }) => { + const component = await mount( + + + , + ); -test('no url +@dark-mode', async({ mount }) => { - const component = await mount( - - - , - ); + await expect(component).toHaveScreenshot(); + }); +}); + +test.describe('image', () => { + test.use({ viewport: { width: 250, height: 250 } }); + + const MEDIA_URL = 'https://localhost:3000/my-image.jpg'; - await expect(component).toHaveScreenshot(); + test.beforeEach(async({ page }) => { + await page.route(MEDIA_URL, (route) => { + return route.fulfill({ + status: 200, + path: './playwright/mocks/image_long.jpg', + }); + }); + }); + + test('preview +@dark-mode', async({ mount }) => { + const component = await mount( + + + , + ); + + await expect(component).toHaveScreenshot(); + }); }); -test('image +@dark-mode', async({ mount, page }) => { +test('image preview hover', async({ mount, page }) => { const MEDIA_URL = 'https://localhost:3000/my-image.jpg'; + await page.route(MEDIA_URL, (route) => { return route.fulfill({ status: 200, @@ -28,33 +55,62 @@ test('image +@dark-mode', async({ mount, page }) => { const component = await mount( - + , ); - await expect(component).toHaveScreenshot(); + await component.getByAltText('Token instance image').hover(); + + await expect(page).toHaveScreenshot({ clip: { x: 0, y: 0, width: 250, height: 250 } }); }); -test('page', async({ mount, page }) => { - const MEDIA_URL = 'https://localhost:3000/page.html'; - const MEDIA_TYPE_API_URL = `/node-api/media-type?url=${ encodeURIComponent(MEDIA_URL) }`; +test('image fullscreen +@dark-mode +@mobile', async({ mount, page }) => { + const MEDIA_URL = 'https://localhost:3000/my-image.jpg'; await page.route(MEDIA_URL, (route) => { return route.fulfill({ status: 200, - path: './playwright/mocks/page.html', + path: './playwright/mocks/image_long.jpg', }); }); - await page.route(MEDIA_TYPE_API_URL, (route) => route.fulfill({ - status: 200, - body: JSON.stringify({ type: 'html' }), - })); - const component = await mount( - + , ); - await expect(component).toHaveScreenshot(); + await component.getByAltText('Token instance image').click(); + + await expect(page).toHaveScreenshot(); +}); + +test.describe('page', () => { + test.use({ viewport: { width: 250, height: 250 } }); + + const MEDIA_URL = 'https://localhost:3000/page.html'; + const MEDIA_TYPE_API_URL = `/node-api/media-type?url=${ encodeURIComponent(MEDIA_URL) }`; + + test.beforeEach(async({ page }) => { + + await page.route(MEDIA_URL, (route) => { + return route.fulfill({ + status: 200, + path: './playwright/mocks/page.html', + }); + }); + await page.route(MEDIA_TYPE_API_URL, (route) => route.fulfill({ + status: 200, + body: JSON.stringify({ type: 'html' }), + })); + }); + + test('preview +@dark-mode', async({ mount }) => { + const component = await mount( + + + , + ); + + await expect(component).toHaveScreenshot(); + }); }); diff --git a/ui/shared/nft/NftMedia.tsx b/ui/shared/nft/NftMedia.tsx index df37233998..9427791f5f 100644 --- a/ui/shared/nft/NftMedia.tsx +++ b/ui/shared/nft/NftMedia.tsx @@ -1,26 +1,30 @@ -import { AspectRatio, chakra, Skeleton, useColorModeValue } from '@chakra-ui/react'; +import { AspectRatio, chakra, Skeleton, useDisclosure } from '@chakra-ui/react'; import React from 'react'; import { useInView } from 'react-intersection-observer'; import NftFallback from './NftFallback'; import NftHtml from './NftHtml'; +import NftHtmlFullscreen from './NftHtmlFullscreen'; import NftImage from './NftImage'; +import NftImageFullscreen from './NftImageFullscreen'; import NftVideo from './NftVideo'; +import NftVideoFullscreen from './NftVideoFullscreen'; import useNftMediaType from './useNftMediaType'; +import { mediaStyleProps } from './utils'; interface Props { url: string | null; className?: string; isLoading?: boolean; + withFullscreen?: boolean; } -const NftMedia = ({ url, className, isLoading }: Props) => { +const NftMedia = ({ url, className, isLoading, withFullscreen }: Props) => { const [ isMediaLoading, setIsMediaLoading ] = React.useState(Boolean(url)); const [ isLoadingError, setIsLoadingError ] = React.useState(false); - const bgColor = useColorModeValue('blackAlpha.50', 'whiteAlpha.50'); - const { ref, inView } = useInView({ triggerOnce: true }); + const type = useNftMediaType(url, !isLoading && inView); const handleMediaLoaded = React.useCallback(() => { @@ -32,18 +36,51 @@ const NftMedia = ({ url, className, isLoading }: Props) => { setIsLoadingError(true); }, []); + const { isOpen, onOpen, onClose } = useDisclosure(); + const content = (() => { if (!url || isLoadingError) { - return ; + const styleProps = withFullscreen ? {} : mediaStyleProps; + return ; + } + + const props = { + src: url, + onLoad: handleMediaLoaded, + onError: handleMediaLoadError, + ...(withFullscreen ? { onClick: onOpen } : {}), + }; + + switch (type) { + case 'video': + return ; + case 'html': + return ; + case 'image': + return ; + default: + return null; } + })(); + + const modal = (() => { + if (!url || !withFullscreen) { + return null; + } + + const props = { + src: url, + isOpen, + onClose, + }; switch (type) { case 'video': - return ; + return ; case 'html': - return ; + return ; case 'image': - return ; + return ; default: return null; } @@ -53,11 +90,11 @@ const NftMedia = ({ url, className, isLoading }: Props) => { img, &>video': { objectFit: 'contain', @@ -66,6 +103,7 @@ const NftMedia = ({ url, className, isLoading }: Props) => { > <> { content } + { modal } { isMediaLoading && } diff --git a/ui/shared/nft/NftMediaFullscreenModal.tsx b/ui/shared/nft/NftMediaFullscreenModal.tsx new file mode 100644 index 0000000000..3f6e1f7fd7 --- /dev/null +++ b/ui/shared/nft/NftMediaFullscreenModal.tsx @@ -0,0 +1,27 @@ +import { + Modal, + ModalContent, + ModalCloseButton, + ModalOverlay, +} from '@chakra-ui/react'; +import React from 'react'; + +interface Props { + isOpen: boolean; + onClose: () => void; + children: React.ReactNode; +} + +const NftMediaFullscreenModal = ({ isOpen, onClose, children }: Props) => { + return ( + + + + + { children } + + + ); +}; + +export default NftMediaFullscreenModal; diff --git a/ui/shared/nft/NftVideo.tsx b/ui/shared/nft/NftVideo.tsx index 4c7d2389e4..a58cefbbda 100644 --- a/ui/shared/nft/NftVideo.tsx +++ b/ui/shared/nft/NftVideo.tsx @@ -1,24 +1,25 @@ import { chakra } from '@chakra-ui/react'; import React from 'react'; +import { mediaStyleProps, videoPlayProps } from './utils'; + interface Props { src: string; onLoad: () => void; onError: () => void; + onClick?: () => void; } -const NftVideo = ({ src, onLoad, onError }: Props) => { +const NftVideo = ({ src, onLoad, onError, onClick }: Props) => { return ( ); }; diff --git a/ui/shared/nft/NftVideoFullscreen.tsx b/ui/shared/nft/NftVideoFullscreen.tsx new file mode 100644 index 0000000000..dc1685c26b --- /dev/null +++ b/ui/shared/nft/NftVideoFullscreen.tsx @@ -0,0 +1,26 @@ +import { chakra } from '@chakra-ui/react'; +import React from 'react'; + +import NftMediaFullscreenModal from './NftMediaFullscreenModal'; +import { videoPlayProps } from './utils'; + +interface Props { + src: string; + isOpen: boolean; + onClose: () => void; +} + +const NftVideoWithFullscreen = ({ src, isOpen, onClose }: Props) => { + return ( + + + + ); +}; + +export default NftVideoWithFullscreen; diff --git a/ui/shared/nft/NftVideoWithFullscreen.tsx b/ui/shared/nft/NftVideoWithFullscreen.tsx new file mode 100644 index 0000000000..ed676379e7 --- /dev/null +++ b/ui/shared/nft/NftVideoWithFullscreen.tsx @@ -0,0 +1,37 @@ +import { + chakra, + useDisclosure, +} from '@chakra-ui/react'; +import React from 'react'; + +import NftMediaFullscreenModal from './NftMediaFullscreenModal'; +import NftVideo from './NftVideo'; +import { videoPlayProps } from './utils'; + +interface Props { + src: string; + onLoad: () => void; + onError: () => void; +} + +const NftVideoWithFullscreen = ({ src, onLoad, onError }: Props) => { + const { isOpen, onOpen, onClose } = useDisclosure(); + + return ( + <> + + + + + + ); +}; + +export default NftVideoWithFullscreen; diff --git a/ui/shared/nft/__screenshots__/NftMedia.pw.tsx_dark-color-mode_image-dark-mode-1.png b/ui/shared/nft/__screenshots__/NftMedia.pw.tsx_dark-color-mode_image-dark-mode-1.png deleted file mode 100644 index 2f38d9f7ae..0000000000 Binary files a/ui/shared/nft/__screenshots__/NftMedia.pw.tsx_dark-color-mode_image-dark-mode-1.png and /dev/null differ diff --git a/ui/shared/nft/__screenshots__/NftMedia.pw.tsx_dark-color-mode_image-fullscreen-dark-mode-mobile-1.png b/ui/shared/nft/__screenshots__/NftMedia.pw.tsx_dark-color-mode_image-fullscreen-dark-mode-mobile-1.png new file mode 100644 index 0000000000..725efb8973 Binary files /dev/null and b/ui/shared/nft/__screenshots__/NftMedia.pw.tsx_dark-color-mode_image-fullscreen-dark-mode-mobile-1.png differ diff --git a/ui/shared/nft/__screenshots__/NftMedia.pw.tsx_dark-color-mode_image-preview-dark-mode-1.png b/ui/shared/nft/__screenshots__/NftMedia.pw.tsx_dark-color-mode_image-preview-dark-mode-1.png new file mode 100644 index 0000000000..b60b305a6c Binary files /dev/null and b/ui/shared/nft/__screenshots__/NftMedia.pw.tsx_dark-color-mode_image-preview-dark-mode-1.png differ diff --git a/ui/shared/nft/__screenshots__/NftMedia.pw.tsx_dark-color-mode_no-url-dark-mode-1.png b/ui/shared/nft/__screenshots__/NftMedia.pw.tsx_dark-color-mode_no-url-dark-mode-1.png deleted file mode 100644 index abe6596ebb..0000000000 Binary files a/ui/shared/nft/__screenshots__/NftMedia.pw.tsx_dark-color-mode_no-url-dark-mode-1.png and /dev/null differ diff --git a/ui/shared/nft/__screenshots__/NftMedia.pw.tsx_dark-color-mode_no-url-preview-dark-mode-1.png b/ui/shared/nft/__screenshots__/NftMedia.pw.tsx_dark-color-mode_no-url-preview-dark-mode-1.png new file mode 100644 index 0000000000..249c60b915 Binary files /dev/null and b/ui/shared/nft/__screenshots__/NftMedia.pw.tsx_dark-color-mode_no-url-preview-dark-mode-1.png differ diff --git a/ui/shared/nft/__screenshots__/NftMedia.pw.tsx_dark-color-mode_page-preview-dark-mode-1.png b/ui/shared/nft/__screenshots__/NftMedia.pw.tsx_dark-color-mode_page-preview-dark-mode-1.png new file mode 100644 index 0000000000..09e22f5338 Binary files /dev/null and b/ui/shared/nft/__screenshots__/NftMedia.pw.tsx_dark-color-mode_page-preview-dark-mode-1.png differ diff --git a/ui/shared/nft/__screenshots__/NftMedia.pw.tsx_default_image-fullscreen-dark-mode-mobile-1.png b/ui/shared/nft/__screenshots__/NftMedia.pw.tsx_default_image-fullscreen-dark-mode-mobile-1.png new file mode 100644 index 0000000000..4772a0f61f Binary files /dev/null and b/ui/shared/nft/__screenshots__/NftMedia.pw.tsx_default_image-fullscreen-dark-mode-mobile-1.png differ diff --git a/ui/shared/nft/__screenshots__/NftMedia.pw.tsx_default_image-dark-mode-1.png b/ui/shared/nft/__screenshots__/NftMedia.pw.tsx_default_image-preview-dark-mode-1.png similarity index 100% rename from ui/shared/nft/__screenshots__/NftMedia.pw.tsx_default_image-dark-mode-1.png rename to ui/shared/nft/__screenshots__/NftMedia.pw.tsx_default_image-preview-dark-mode-1.png diff --git a/ui/shared/nft/__screenshots__/NftMedia.pw.tsx_default_image-preview-hover-1.png b/ui/shared/nft/__screenshots__/NftMedia.pw.tsx_default_image-preview-hover-1.png new file mode 100644 index 0000000000..1a906238e4 Binary files /dev/null and b/ui/shared/nft/__screenshots__/NftMedia.pw.tsx_default_image-preview-hover-1.png differ diff --git a/ui/shared/nft/__screenshots__/NftMedia.pw.tsx_default_no-url-dark-mode-1.png b/ui/shared/nft/__screenshots__/NftMedia.pw.tsx_default_no-url-dark-mode-1.png deleted file mode 100644 index 3fd8aaa96f..0000000000 Binary files a/ui/shared/nft/__screenshots__/NftMedia.pw.tsx_default_no-url-dark-mode-1.png and /dev/null differ diff --git a/ui/shared/nft/__screenshots__/NftMedia.pw.tsx_default_no-url-preview-dark-mode-1.png b/ui/shared/nft/__screenshots__/NftMedia.pw.tsx_default_no-url-preview-dark-mode-1.png new file mode 100644 index 0000000000..2249710f66 Binary files /dev/null and b/ui/shared/nft/__screenshots__/NftMedia.pw.tsx_default_no-url-preview-dark-mode-1.png differ diff --git a/ui/shared/nft/__screenshots__/NftMedia.pw.tsx_default_page-1.png b/ui/shared/nft/__screenshots__/NftMedia.pw.tsx_default_page-preview-dark-mode-1.png similarity index 100% rename from ui/shared/nft/__screenshots__/NftMedia.pw.tsx_default_page-1.png rename to ui/shared/nft/__screenshots__/NftMedia.pw.tsx_default_page-preview-dark-mode-1.png diff --git a/ui/shared/nft/__screenshots__/NftMedia.pw.tsx_mobile_image-fullscreen-dark-mode-mobile-1.png b/ui/shared/nft/__screenshots__/NftMedia.pw.tsx_mobile_image-fullscreen-dark-mode-mobile-1.png new file mode 100644 index 0000000000..991671230c Binary files /dev/null and b/ui/shared/nft/__screenshots__/NftMedia.pw.tsx_mobile_image-fullscreen-dark-mode-mobile-1.png differ diff --git a/ui/shared/nft/utils.ts b/ui/shared/nft/utils.ts index 3c33b3ef77..90410798f6 100644 --- a/ui/shared/nft/utils.ts +++ b/ui/shared/nft/utils.ts @@ -26,3 +26,24 @@ export function getPreliminaryMediaType(url: string): MediaType | undefined { return 'video'; } } + +export const mediaStyleProps = { + transitionProperty: 'transform', + transitionDuration: 'normal', + transitionTimingFunction: 'ease', + cursor: 'pointer', + _hover: { + base: {}, + lg: { + transform: 'scale(1.2)', + }, + }, +}; + +export const videoPlayProps = { + autoPlay: true, + disablePictureInPicture: true, + loop: true, + muted: true, + playsInline: true, +}; diff --git a/ui/snippets/footer/FooterLinkItem.tsx b/ui/snippets/footer/FooterLinkItem.tsx index b203956a6d..6277a64aa2 100644 --- a/ui/snippets/footer/FooterLinkItem.tsx +++ b/ui/snippets/footer/FooterLinkItem.tsx @@ -12,7 +12,7 @@ const FooterLinkItem = ({ icon, iconSize, text, url }: Props) => { return ( { icon && ( -
+
) } diff --git a/ui/snippets/footer/__screenshots__/Footer.pw.tsx_dark-color-mode_with-custom-links-2-cols-base-view-dark-mode-mobile-1.png b/ui/snippets/footer/__screenshots__/Footer.pw.tsx_dark-color-mode_with-custom-links-2-cols-base-view-dark-mode-mobile-1.png index 2fae017e2e..f971b7633e 100644 Binary files a/ui/snippets/footer/__screenshots__/Footer.pw.tsx_dark-color-mode_with-custom-links-2-cols-base-view-dark-mode-mobile-1.png and b/ui/snippets/footer/__screenshots__/Footer.pw.tsx_dark-color-mode_with-custom-links-2-cols-base-view-dark-mode-mobile-1.png differ diff --git a/ui/snippets/footer/__screenshots__/Footer.pw.tsx_dark-color-mode_with-custom-links-4-cols-mobile-dark-mode-1.png b/ui/snippets/footer/__screenshots__/Footer.pw.tsx_dark-color-mode_with-custom-links-4-cols-mobile-dark-mode-1.png index 9992af8a55..40f3f6bcf6 100644 Binary files a/ui/snippets/footer/__screenshots__/Footer.pw.tsx_dark-color-mode_with-custom-links-4-cols-mobile-dark-mode-1.png and b/ui/snippets/footer/__screenshots__/Footer.pw.tsx_dark-color-mode_with-custom-links-4-cols-mobile-dark-mode-1.png differ diff --git a/ui/snippets/footer/__screenshots__/Footer.pw.tsx_dark-color-mode_without-custom-links-base-view-dark-mode-mobile-1.png b/ui/snippets/footer/__screenshots__/Footer.pw.tsx_dark-color-mode_without-custom-links-base-view-dark-mode-mobile-1.png index 6c8497558d..cb011c1d4f 100644 Binary files a/ui/snippets/footer/__screenshots__/Footer.pw.tsx_dark-color-mode_without-custom-links-base-view-dark-mode-mobile-1.png and b/ui/snippets/footer/__screenshots__/Footer.pw.tsx_dark-color-mode_without-custom-links-base-view-dark-mode-mobile-1.png differ diff --git a/ui/snippets/footer/__screenshots__/Footer.pw.tsx_dark-color-mode_without-custom-links-with-indexing-alert-dark-mode-mobile-1.png b/ui/snippets/footer/__screenshots__/Footer.pw.tsx_dark-color-mode_without-custom-links-with-indexing-alert-dark-mode-mobile-1.png index e942fb2dc2..ac6b735f06 100644 Binary files a/ui/snippets/footer/__screenshots__/Footer.pw.tsx_dark-color-mode_without-custom-links-with-indexing-alert-dark-mode-mobile-1.png and b/ui/snippets/footer/__screenshots__/Footer.pw.tsx_dark-color-mode_without-custom-links-with-indexing-alert-dark-mode-mobile-1.png differ diff --git a/ui/snippets/footer/__screenshots__/Footer.pw.tsx_default_with-custom-links-2-cols-base-view-dark-mode-mobile-1.png b/ui/snippets/footer/__screenshots__/Footer.pw.tsx_default_with-custom-links-2-cols-base-view-dark-mode-mobile-1.png index 8753136808..6a12a8e9d3 100644 Binary files a/ui/snippets/footer/__screenshots__/Footer.pw.tsx_default_with-custom-links-2-cols-base-view-dark-mode-mobile-1.png and b/ui/snippets/footer/__screenshots__/Footer.pw.tsx_default_with-custom-links-2-cols-base-view-dark-mode-mobile-1.png differ diff --git a/ui/snippets/footer/__screenshots__/Footer.pw.tsx_default_with-custom-links-4-cols-mobile-dark-mode-1.png b/ui/snippets/footer/__screenshots__/Footer.pw.tsx_default_with-custom-links-4-cols-mobile-dark-mode-1.png index e65c1f29dc..733f2260a2 100644 Binary files a/ui/snippets/footer/__screenshots__/Footer.pw.tsx_default_with-custom-links-4-cols-mobile-dark-mode-1.png and b/ui/snippets/footer/__screenshots__/Footer.pw.tsx_default_with-custom-links-4-cols-mobile-dark-mode-1.png differ diff --git a/ui/snippets/footer/__screenshots__/Footer.pw.tsx_default_with-custom-links-4-cols-screen-xl-1.png b/ui/snippets/footer/__screenshots__/Footer.pw.tsx_default_with-custom-links-4-cols-screen-xl-1.png index 598429bafd..457fb59618 100644 Binary files a/ui/snippets/footer/__screenshots__/Footer.pw.tsx_default_with-custom-links-4-cols-screen-xl-1.png and b/ui/snippets/footer/__screenshots__/Footer.pw.tsx_default_with-custom-links-4-cols-screen-xl-1.png differ diff --git a/ui/snippets/footer/__screenshots__/Footer.pw.tsx_default_without-custom-links-base-view-dark-mode-mobile-1.png b/ui/snippets/footer/__screenshots__/Footer.pw.tsx_default_without-custom-links-base-view-dark-mode-mobile-1.png index 33272ca8e2..de37d74e1a 100644 Binary files a/ui/snippets/footer/__screenshots__/Footer.pw.tsx_default_without-custom-links-base-view-dark-mode-mobile-1.png and b/ui/snippets/footer/__screenshots__/Footer.pw.tsx_default_without-custom-links-base-view-dark-mode-mobile-1.png differ diff --git a/ui/snippets/footer/__screenshots__/Footer.pw.tsx_default_without-custom-links-with-indexing-alert-dark-mode-mobile-1.png b/ui/snippets/footer/__screenshots__/Footer.pw.tsx_default_without-custom-links-with-indexing-alert-dark-mode-mobile-1.png index 0e250ff35d..f64e79606b 100644 Binary files a/ui/snippets/footer/__screenshots__/Footer.pw.tsx_default_without-custom-links-with-indexing-alert-dark-mode-mobile-1.png and b/ui/snippets/footer/__screenshots__/Footer.pw.tsx_default_without-custom-links-with-indexing-alert-dark-mode-mobile-1.png differ diff --git a/ui/snippets/footer/__screenshots__/Footer.pw.tsx_mobile_with-custom-links-2-cols-base-view-dark-mode-mobile-1.png b/ui/snippets/footer/__screenshots__/Footer.pw.tsx_mobile_with-custom-links-2-cols-base-view-dark-mode-mobile-1.png index aa2714cbbb..1e22d20f6f 100644 Binary files a/ui/snippets/footer/__screenshots__/Footer.pw.tsx_mobile_with-custom-links-2-cols-base-view-dark-mode-mobile-1.png and b/ui/snippets/footer/__screenshots__/Footer.pw.tsx_mobile_with-custom-links-2-cols-base-view-dark-mode-mobile-1.png differ diff --git a/ui/snippets/footer/__screenshots__/Footer.pw.tsx_mobile_with-custom-links-4-cols-mobile-dark-mode-1.png b/ui/snippets/footer/__screenshots__/Footer.pw.tsx_mobile_with-custom-links-4-cols-mobile-dark-mode-1.png index 50afbdd952..01553b5a26 100644 Binary files a/ui/snippets/footer/__screenshots__/Footer.pw.tsx_mobile_with-custom-links-4-cols-mobile-dark-mode-1.png and b/ui/snippets/footer/__screenshots__/Footer.pw.tsx_mobile_with-custom-links-4-cols-mobile-dark-mode-1.png differ diff --git a/ui/snippets/footer/__screenshots__/Footer.pw.tsx_mobile_without-custom-links-base-view-dark-mode-mobile-1.png b/ui/snippets/footer/__screenshots__/Footer.pw.tsx_mobile_without-custom-links-base-view-dark-mode-mobile-1.png index 1458d490aa..4e31c1ab67 100644 Binary files a/ui/snippets/footer/__screenshots__/Footer.pw.tsx_mobile_without-custom-links-base-view-dark-mode-mobile-1.png and b/ui/snippets/footer/__screenshots__/Footer.pw.tsx_mobile_without-custom-links-base-view-dark-mode-mobile-1.png differ diff --git a/ui/snippets/footer/__screenshots__/Footer.pw.tsx_mobile_without-custom-links-with-indexing-alert-dark-mode-mobile-1.png b/ui/snippets/footer/__screenshots__/Footer.pw.tsx_mobile_without-custom-links-with-indexing-alert-dark-mode-mobile-1.png index fa630e7ecf..31096b645f 100644 Binary files a/ui/snippets/footer/__screenshots__/Footer.pw.tsx_mobile_without-custom-links-with-indexing-alert-dark-mode-mobile-1.png and b/ui/snippets/footer/__screenshots__/Footer.pw.tsx_mobile_without-custom-links-with-indexing-alert-dark-mode-mobile-1.png differ diff --git a/ui/snippets/profileMenu/ProfileMenuDesktop.tsx b/ui/snippets/profileMenu/ProfileMenuDesktop.tsx index 30f8e633a2..aa375bfa01 100644 --- a/ui/snippets/profileMenu/ProfileMenuDesktop.tsx +++ b/ui/snippets/profileMenu/ProfileMenuDesktop.tsx @@ -44,8 +44,8 @@ const ProfileMenuDesktop = () => {