Commit 7d7bf1fe authored by tom's avatar tom

Merge branch 'main' of github.com:blockscout/frontend into block-txs-api

parents f2d6d338 2cdc9bbd
# app config # app config
NEXT_PUBLIC_APP_ENV=__PLACEHOLDER_FOR_NEXT_PUBLIC_APP_ENV__
NEXT_PUBLIC_APP_INSTANCE=__PLACEHOLDER_FOR_NEXT_PUBLIC_APP_INSTANCE__ NEXT_PUBLIC_APP_INSTANCE=__PLACEHOLDER_FOR_NEXT_PUBLIC_APP_INSTANCE__
NEXT_PUBLIC_APP_PROTOCOL=__PLACEHOLDER_FOR_NEXT_PUBLIC_APP_PROTOCOL__ NEXT_PUBLIC_APP_PROTOCOL=__PLACEHOLDER_FOR_NEXT_PUBLIC_APP_PROTOCOL__
NEXT_PUBLIC_APP_HOST=__PLACEHOLDER_FOR_NEXT_PUBLIC_APP_HOST__ NEXT_PUBLIC_APP_HOST=__PLACEHOLDER_FOR_NEXT_PUBLIC_APP_HOST__
......
...@@ -59,10 +59,7 @@ jobs: ...@@ -59,10 +59,7 @@ jobs:
tags: ghcr.io/blockscout/frontend:prerelease-${{ env.SHORT_SHA }} tags: ghcr.io/blockscout/frontend:prerelease-${{ env.SHORT_SHA }}
labels: ${{ steps.meta.outputs.labels }} labels: ${{ steps.meta.outputs.labels }}
build-args: | build-args: |
SENTRY_DSN=${{ secrets.SENTRY_DSN }} GIT_COMMIT_SHA=${{ env.SHORT_SHA }}
NEXT_PUBLIC_SENTRY_DSN=${{ secrets.NEXT_PUBLIC_SENTRY_DSN }}
SENTRY_CSP_REPORT_URI=${{ secrets.SENTRY_CSP_REPORT_URI }}
SENTRY_AUTH_TOKEN=${{ secrets.SENTRY_AUTH_TOKEN }}
deploy_and_tests: deploy_and_tests:
needs: push_to_registry needs: push_to_registry
......
...@@ -79,6 +79,7 @@ The app instance could be customized by passing following variables to NodeJS en ...@@ -79,6 +79,7 @@ The app instance could be customized by passing following variables to NodeJS en
| NEXT_PUBLIC_APP_PROTOCOL | `http \| https` *(optional)* | App protocol (`https` used as default value) | `https` | | NEXT_PUBLIC_APP_PROTOCOL | `http \| https` *(optional)* | App protocol (`https` used as default value) | `https` |
| NEXT_PUBLIC_APP_HOST | `string` | App host | `blockscout.com` | | NEXT_PUBLIC_APP_HOST | `string` | App host | `blockscout.com` |
| NEXT_PUBLIC_APP_PORT | `number` *(optional)* | Port where app is running. Have to be provided if it is different to default port | `3000` | | NEXT_PUBLIC_APP_PORT | `number` *(optional)* | Port where app is running. Have to be provided if it is different to default port | `3000` |
| NEXT_PUBLIC_APP_ENV | `string` *(optional)* | Current app env (e.g development, review or production). Used for Sentry.io configuration | `production` |
### API configuration ### API configuration
......
...@@ -2,10 +2,10 @@ ...@@ -2,10 +2,10 @@
const env = process.env.VERCEL_ENV || process.env.NODE_ENV; const env = process.env.VERCEL_ENV || process.env.NODE_ENV;
const isDev = env === 'development'; const isDev = env === 'development';
const baseUrl = [ const baseUrl = [
process.env.NEXT_PUBLIC_APP_PROTOCOL || 'https', process.env.NEXT_PUBLIC_APP_PROTOCOL?.replaceAll('\'', '"') || 'https',
'://', '://',
process.env.NEXT_PUBLIC_VERCEL_URL || process.env.NEXT_PUBLIC_APP_HOST, process.env.NEXT_PUBLIC_VERCEL_URL || process.env.NEXT_PUBLIC_APP_HOST,
process.env.NEXT_PUBLIC_APP_PORT ? ':' + process.env.NEXT_PUBLIC_APP_PORT : '', process.env.NEXT_PUBLIC_APP_PORT?.replaceAll('\'', '"') ? ':' + process.env.NEXT_PUBLIC_APP_PORT : '',
].join(''); ].join('');
const DEFAULT_CURRENCY_DECIMALS = 18; const DEFAULT_CURRENCY_DECIMALS = 18;
......
...@@ -3,6 +3,7 @@ NEXT_PUBLIC_APP_PROTOCOL=http ...@@ -3,6 +3,7 @@ NEXT_PUBLIC_APP_PROTOCOL=http
NEXT_PUBLIC_APP_HOST=localhost NEXT_PUBLIC_APP_HOST=localhost
NEXT_PUBLIC_APP_PORT=3000 NEXT_PUBLIC_APP_PORT=3000
NEXT_PUBLIC_APP_INSTANCE=local NEXT_PUBLIC_APP_INSTANCE=local
NEXT_PUBLIC_APP_ENV=development
# ui config # ui config
NEXT_PUBLIC_BLOCKSCOUT_VERSION=v4.1.7-beta NEXT_PUBLIC_BLOCKSCOUT_VERSION=v4.1.7-beta
......
...@@ -2,7 +2,7 @@ import type * as Sentry from '@sentry/react'; ...@@ -2,7 +2,7 @@ import type * as Sentry from '@sentry/react';
import { BrowserTracing } from '@sentry/tracing'; import { BrowserTracing } from '@sentry/tracing';
export const config: Sentry.BrowserOptions = { export const config: Sentry.BrowserOptions = {
environment: process.env.VERCEL_ENV || process.env.NODE_ENV, environment: process.env.VERCEL_ENV || process.env.NEXT_PUBLIC_APP_ENV || process.env.NODE_ENV,
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN, dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
release: process.env.NEXT_PUBLIC_GIT_COMMIT_SHA || process.env.NEXT_PUBLIC_VERCEL_GIT_COMMIT_SHA, release: process.env.NEXT_PUBLIC_GIT_COMMIT_SHA || process.env.NEXT_PUBLIC_VERCEL_GIT_COMMIT_SHA,
integrations: [ new BrowserTracing() ], integrations: [ new BrowserTracing() ],
......
...@@ -94,7 +94,7 @@ ...@@ -94,7 +94,7 @@
"shortDescription": "Yield Farming for Synthetic Assets from LP tokens", "shortDescription": "Yield Farming for Synthetic Assets from LP tokens",
"site": "https://farms.baoswap.xyz/", "site": "https://farms.baoswap.xyz/",
"description": "Yield Farming for Synthetic Assets from LP tokens", "description": "Yield Farming for Synthetic Assets from LP tokens",
"url": "https://blockscout-allowance-mainnet-stage.vercel.app/", "url": "https://approval-tracker.vercel.app",
"twitter": "https://twitter.com/EasyStaking", "twitter": "https://twitter.com/EasyStaking",
"telegram": "https://t.me/easystaking", "telegram": "https://t.me/easystaking",
"github": "https://github.com/mikhin" "github": "https://github.com/mikhin"
...@@ -114,7 +114,7 @@ ...@@ -114,7 +114,7 @@
"shortDescription": "During the Unit protocol development, we faced difficulty finding a reliable, flexible protocol for stablecoin swap without interface censorship.", "shortDescription": "During the Unit protocol development, we faced difficulty finding a reliable, flexible protocol for stablecoin swap without interface censorship.",
"site": "https://xdai.component.finance", "site": "https://xdai.component.finance",
"description": "During the Unit protocol development, we faced difficulty finding a reliable, flexible protocol for stablecoin swap without interface censorship.", "description": "During the Unit protocol development, we faced difficulty finding a reliable, flexible protocol for stablecoin swap without interface censorship.",
"url": "https://blockscout-allowance-mainnet-stage.vercel.app/", "url": "https://approval-tracker.vercel.app",
"twitter": "https://twitter.com/EasyStaking", "twitter": "https://twitter.com/EasyStaking",
"telegram": "https://t.me/easystaking", "telegram": "https://t.me/easystaking",
"github": "https://github.com/mikhin" "github": "https://github.com/mikhin"
...@@ -134,7 +134,7 @@ ...@@ -134,7 +134,7 @@
"shortDescription": "View, deposit and withdraw for all V3 Pools", "shortDescription": "View, deposit and withdraw for all V3 Pools",
"site": "https://app.pooltogether.com/", "site": "https://app.pooltogether.com/",
"description": "View, deposit and withdraw for all V3 Pools", "description": "View, deposit and withdraw for all V3 Pools",
"url": "https://blockscout-allowance-mainnet-stage.vercel.app/", "url": "https://approval-tracker.vercel.app",
"twitter": "https://twitter.com/EasyStaking", "twitter": "https://twitter.com/EasyStaking",
"telegram": "https://t.me/easystaking", "telegram": "https://t.me/easystaking",
"github": "https://github.com/mikhin" "github": "https://github.com/mikhin"
...@@ -154,7 +154,7 @@ ...@@ -154,7 +154,7 @@
"shortDescription": "A governance-enabled automated market maker with adjustable fees.", "shortDescription": "A governance-enabled automated market maker with adjustable fees.",
"site": "https://swapr.eth.limo", "site": "https://swapr.eth.limo",
"description": "A governance-enabled automated market maker with adjustable fees.", "description": "A governance-enabled automated market maker with adjustable fees.",
"url": "https://blockscout-allowance-mainnet-stage.vercel.app/", "url": "https://approval-tracker.vercel.app",
"twitter": "https://twitter.com/EasyStaking", "twitter": "https://twitter.com/EasyStaking",
"telegram": "https://t.me/easystaking", "telegram": "https://t.me/easystaking",
"github": "https://github.com/mikhin" "github": "https://github.com/mikhin"
...@@ -174,7 +174,7 @@ ...@@ -174,7 +174,7 @@
"shortDescription": "AMM DEX on the xDai chain. Its uniqueness comes from the ability to trade securities tokens, specifically, tokenized real estate tokens.", "shortDescription": "AMM DEX on the xDai chain. Its uniqueness comes from the ability to trade securities tokens, specifically, tokenized real estate tokens.",
"site": "https://app.levinswap.org/", "site": "https://app.levinswap.org/",
"description": "AMM DEX on the xDai chain. Its uniqueness comes from the ability to trade securities tokens, specifically, tokenized real estate tokens.", "description": "AMM DEX on the xDai chain. Its uniqueness comes from the ability to trade securities tokens, specifically, tokenized real estate tokens.",
"url": "https://blockscout-allowance-mainnet-stage.vercel.app/", "url": "https://approval-tracker.vercel.app",
"twitter": "https://twitter.com/EasyStaking", "twitter": "https://twitter.com/EasyStaking",
"telegram": "https://t.me/easystaking", "telegram": "https://t.me/easystaking",
"github": "https://github.com/mikhin" "github": "https://github.com/mikhin"
...@@ -194,7 +194,7 @@ ...@@ -194,7 +194,7 @@
"shortDescription": "Decentralized prediction markets on Ethereum", "shortDescription": "Decentralized prediction markets on Ethereum",
"site": "https://xdai.omen.eth.link/", "site": "https://xdai.omen.eth.link/",
"description": "Decentralized prediction markets on Ethereum", "description": "Decentralized prediction markets on Ethereum",
"url": "https://blockscout-allowance-mainnet-stage.vercel.app/", "url": "https://approval-tracker.vercel.app",
"twitter": "https://twitter.com/EasyStaking", "twitter": "https://twitter.com/EasyStaking",
"telegram": "https://t.me/easystaking", "telegram": "https://t.me/easystaking",
"github": "https://github.com/mikhin" "github": "https://github.com/mikhin"
...@@ -214,7 +214,7 @@ ...@@ -214,7 +214,7 @@
"shortDescription": "NFT artwork created and sold on xDAI using meta transactions, burner wallets, and bridged to Ethereum", "shortDescription": "NFT artwork created and sold on xDAI using meta transactions, burner wallets, and bridged to Ethereum",
"site": "https://nifty.ink/explore", "site": "https://nifty.ink/explore",
"description": "NFT artwork created and sold on xDAI using meta transactions, burner wallets, and bridged to Ethereum", "description": "NFT artwork created and sold on xDAI using meta transactions, burner wallets, and bridged to Ethereum",
"url": "https://blockscout-allowance-mainnet-stage.vercel.app/", "url": "https://approval-tracker.vercel.app",
"twitter": "https://twitter.com/EasyStaking", "twitter": "https://twitter.com/EasyStaking",
"telegram": "https://t.me/easystaking", "telegram": "https://t.me/easystaking",
"github": "https://github.com/mikhin" "github": "https://github.com/mikhin"
...@@ -234,7 +234,7 @@ ...@@ -234,7 +234,7 @@
"shortDescription": "Every chess game is one-of-a-kind. Make yours a collectible.", "shortDescription": "Every chess game is one-of-a-kind. Make yours a collectible.",
"site": "https://treasure.chess.com/", "site": "https://treasure.chess.com/",
"description": "Every chess game is one-of-a-kind. Make yours a collectible.", "description": "Every chess game is one-of-a-kind. Make yours a collectible.",
"url": "https://blockscout-allowance-mainnet-stage.vercel.app/", "url": "https://approval-tracker.vercel.app",
"twitter": "https://twitter.com/EasyStaking", "twitter": "https://twitter.com/EasyStaking",
"telegram": "https://t.me/easystaking", "telegram": "https://t.me/easystaking",
"github": "https://github.com/mikhin" "github": "https://github.com/mikhin"
...@@ -254,7 +254,7 @@ ...@@ -254,7 +254,7 @@
"shortDescription": "A truly decentralised non-profit platform owned and managed by the Digital Arts community, bringing together Artists, Creators and Collectors as One.", "shortDescription": "A truly decentralised non-profit platform owned and managed by the Digital Arts community, bringing together Artists, Creators and Collectors as One.",
"site": "https://www.unique.one/", "site": "https://www.unique.one/",
"description": "A truly decentralised non-profit platform owned and managed by the Digital Arts community, bringing together Artists, Creators and Collectors as One.", "description": "A truly decentralised non-profit platform owned and managed by the Digital Arts community, bringing together Artists, Creators and Collectors as One.",
"url": "https://blockscout-allowance-mainnet-stage.vercel.app/", "url": "https://approval-tracker.vercel.app",
"twitter": "https://twitter.com/EasyStaking", "twitter": "https://twitter.com/EasyStaking",
"telegram": "https://t.me/easystaking", "telegram": "https://t.me/easystaking",
"github": "https://github.com/mikhin" "github": "https://github.com/mikhin"
...@@ -274,7 +274,7 @@ ...@@ -274,7 +274,7 @@
"shortDescription": "A Community That Empowers NFT Artists", "shortDescription": "A Community That Empowers NFT Artists",
"site": "https://www.coldtruthculture.io/", "site": "https://www.coldtruthculture.io/",
"description": "A Community That Empowers NFT Artists", "description": "A Community That Empowers NFT Artists",
"url": "https://blockscout-allowance-mainnet-stage.vercel.app/", "url": "https://approval-tracker.vercel.app",
"twitter": "https://twitter.com/EasyStaking", "twitter": "https://twitter.com/EasyStaking",
"telegram": "https://t.me/easystaking", "telegram": "https://t.me/easystaking",
"github": "https://github.com/mikhin" "github": "https://github.com/mikhin"
...@@ -294,7 +294,7 @@ ...@@ -294,7 +294,7 @@
"shortDescription": "Token bridge between the Gnosis Chain and the Ethereum network", "shortDescription": "Token bridge between the Gnosis Chain and the Ethereum network",
"site": "https://bridge.gnosischain.com/", "site": "https://bridge.gnosischain.com/",
"description": "Token bridge between the Gnosis Chain and the Ethereum network", "description": "Token bridge between the Gnosis Chain and the Ethereum network",
"url": "https://blockscout-allowance-mainnet-stage.vercel.app/", "url": "https://approval-tracker.vercel.app",
"twitter": "https://twitter.com/EasyStaking", "twitter": "https://twitter.com/EasyStaking",
"telegram": "https://t.me/easystaking", "telegram": "https://t.me/easystaking",
"github": "https://github.com/mikhin" "github": "https://github.com/mikhin"
...@@ -314,7 +314,7 @@ ...@@ -314,7 +314,7 @@
"shortDescription": "The OmniBridge multi-token extension is the simplest way to transfer ANY ERC20/ ERC677 /ERC827 token to and from the xDai chain.", "shortDescription": "The OmniBridge multi-token extension is the simplest way to transfer ANY ERC20/ ERC677 /ERC827 token to and from the xDai chain.",
"site": "https://omni.gnosischain.com/bridge", "site": "https://omni.gnosischain.com/bridge",
"description": "The OmniBridge multi-token extension is the simplest way to transfer ANY ERC20/ ERC677 /ERC827 token to and from the xDai chain.", "description": "The OmniBridge multi-token extension is the simplest way to transfer ANY ERC20/ ERC677 /ERC827 token to and from the xDai chain.",
"url": "https://blockscout-allowance-mainnet-stage.vercel.app/", "url": "https://approval-tracker.vercel.app",
"twitter": "https://twitter.com/EasyStaking", "twitter": "https://twitter.com/EasyStaking",
"telegram": "https://t.me/easystaking", "telegram": "https://t.me/easystaking",
"github": "https://github.com/mikhin" "github": "https://github.com/mikhin"
...@@ -334,7 +334,7 @@ ...@@ -334,7 +334,7 @@
"shortDescription": "Gnosis Safe is the most trusted platform to manage digital assets on Ethereum", "shortDescription": "Gnosis Safe is the most trusted platform to manage digital assets on Ethereum",
"site": "https://gnosis-safe.io/", "site": "https://gnosis-safe.io/",
"description": "Gnosis Safe is the most trusted platform to manage digital assets on Ethereum", "description": "Gnosis Safe is the most trusted platform to manage digital assets on Ethereum",
"url": "https://blockscout-allowance-mainnet-stage.vercel.app/", "url": "https://approval-tracker.vercel.app",
"twitter": "https://twitter.com/EasyStaking", "twitter": "https://twitter.com/EasyStaking",
"telegram": "https://t.me/easystaking", "telegram": "https://t.me/easystaking",
"github": "https://github.com/mikhin" "github": "https://github.com/mikhin"
...@@ -354,7 +354,7 @@ ...@@ -354,7 +354,7 @@
"shortDescription": "Send ERC20 token or ETH. Batch sender. Bulk Sender. Token Multisender allows you to airdrop tokens in a few transactions in trustless way. Batch sending ERC20, Ethereum tokens.", "shortDescription": "Send ERC20 token or ETH. Batch sender. Bulk Sender. Token Multisender allows you to airdrop tokens in a few transactions in trustless way. Batch sending ERC20, Ethereum tokens.",
"site": "https://multisender.app/", "site": "https://multisender.app/",
"description": "Send ERC20 token or ETH. Batch sender. Bulk Sender. Token Multisender allows you to airdrop tokens in a few transactions in trustless way. Batch sending ERC20, Ethereum tokens.", "description": "Send ERC20 token or ETH. Batch sender. Bulk Sender. Token Multisender allows you to airdrop tokens in a few transactions in trustless way. Batch sending ERC20, Ethereum tokens.",
"url": "https://blockscout-allowance-mainnet-stage.vercel.app/", "url": "https://approval-tracker.vercel.app",
"twitter": "https://twitter.com/EasyStaking", "twitter": "https://twitter.com/EasyStaking",
"telegram": "https://t.me/easystaking", "telegram": "https://t.me/easystaking",
"github": "https://github.com/mikhin" "github": "https://github.com/mikhin"
...@@ -374,7 +374,7 @@ ...@@ -374,7 +374,7 @@
"shortDescription": "Distribute ether or tokens to multiple addresses", "shortDescription": "Distribute ether or tokens to multiple addresses",
"site": "https://disperse.app/", "site": "https://disperse.app/",
"description": "Distribute ether or tokens to multiple addresses", "description": "Distribute ether or tokens to multiple addresses",
"url": "https://blockscout-allowance-mainnet-stage.vercel.app/", "url": "https://approval-tracker.vercel.app",
"twitter": "https://twitter.com/EasyStaking", "twitter": "https://twitter.com/EasyStaking",
"telegram": "https://t.me/easystaking", "telegram": "https://t.me/easystaking",
"github": "https://github.com/mikhin" "github": "https://github.com/mikhin"
...@@ -394,7 +394,7 @@ ...@@ -394,7 +394,7 @@
"shortDescription": "DEX or Decentralized Exchange, also called an Automated Market Maker (AMM) in Decentralized Finance (DeFi). Symmetric is live on gnosis chain (xDai) and Celo.", "shortDescription": "DEX or Decentralized Exchange, also called an Automated Market Maker (AMM) in Decentralized Finance (DeFi). Symmetric is live on gnosis chain (xDai) and Celo.",
"site": "https://symmetric.finance/", "site": "https://symmetric.finance/",
"description": "DEX or Decentralized Exchange, also called an Automated Market Maker (AMM) in Decentralized Finance (DeFi). Symmetric is live on gnosis chain (xDai) and Celo.", "description": "DEX or Decentralized Exchange, also called an Automated Market Maker (AMM) in Decentralized Finance (DeFi). Symmetric is live on gnosis chain (xDai) and Celo.",
"url": "https://blockscout-allowance-mainnet-stage.vercel.app/", "url": "https://approval-tracker.vercel.app",
"twitter": "https://twitter.com/EasyStaking", "twitter": "https://twitter.com/EasyStaking",
"telegram": "https://t.me/easystaking", "telegram": "https://t.me/easystaking",
"github": "https://github.com/mikhin" "github": "https://github.com/mikhin"
......
...@@ -21,6 +21,8 @@ blockscout: ...@@ -21,6 +21,8 @@ blockscout:
# enable https # enable https
tls: tls:
enabled: true enabled: true
path:
- "/"
# probes # probes
livenessProbe: livenessProbe:
enabled: true enabled: true
...@@ -263,6 +265,8 @@ frontend: ...@@ -263,6 +265,8 @@ frontend:
# enable https # enable https
tls: tls:
enabled: true enabled: true
path:
- "/"
resources: resources:
limits: limits:
memory: memory:
...@@ -292,7 +296,9 @@ frontend: ...@@ -292,7 +296,9 @@ frontend:
NEXT_PUBLIC_FOOTER_TWITTER_LINK: NEXT_PUBLIC_FOOTER_TWITTER_LINK:
_default: https://www.twitter.com/blockscoutcom _default: https://www.twitter.com/blockscoutcom
NEXT_PUBLIC_APP_INSTANCE: NEXT_PUBLIC_APP_INSTANCE:
_default: local _default: unknown
NEXT_PUBLIC_APP_ENV:
_default: e2e
NEXT_PUBLIC_FOOTER_TELEGRAM_LINK: NEXT_PUBLIC_FOOTER_TELEGRAM_LINK:
_default: https://t.me/poa_network _default: https://t.me/poa_network
NEXT_PUBLIC_FOOTER_STAKING_LINK: NEXT_PUBLIC_FOOTER_STAKING_LINK:
......
...@@ -21,6 +21,8 @@ blockscout: ...@@ -21,6 +21,8 @@ blockscout:
# enable https # enable https
tls: tls:
enabled: true enabled: true
path:
- "/"
# probes # probes
livenessProbe: livenessProbe:
enabled: true enabled: true
...@@ -263,6 +265,8 @@ frontend: ...@@ -263,6 +265,8 @@ frontend:
# enable https # enable https
tls: tls:
enabled: true enabled: true
path:
- "/"
resources: resources:
limits: limits:
memory: memory:
...@@ -279,16 +283,16 @@ frontend: ...@@ -279,16 +283,16 @@ frontend:
enabled: true enabled: true
app: blockscout app: blockscout
environment: environment:
NEXT_PUBLIC_APP_PORT:
_default: 80
NEXT_PUBLIC_BLOCKSCOUT_VERSION: NEXT_PUBLIC_BLOCKSCOUT_VERSION:
_default: v4.1.8-beta _default: v4.1.8-beta
NEXT_PUBLIC_FOOTER_GITHUB_LINK: NEXT_PUBLIC_FOOTER_GITHUB_LINK:
_default: https://github.com/blockscout/blockscout _default: https://github.com/blockscout/blockscout
NEXT_PUBLIC_FOOTER_TWITTER_LINK: NEXT_PUBLIC_FOOTER_TWITTER_LINK:
_default: https://www.twitter.com/blockscoutcom _default: https://www.twitter.com/blockscoutcom
NEXT_PUBLIC_APP_ENV:
_default: production
NEXT_PUBLIC_APP_INSTANCE: NEXT_PUBLIC_APP_INSTANCE:
_default: review _default: unknown
NEXT_PUBLIC_FOOTER_TELEGRAM_LINK: NEXT_PUBLIC_FOOTER_TELEGRAM_LINK:
_default: https://t.me/poa_network _default: https://t.me/poa_network
NEXT_PUBLIC_FOOTER_STAKING_LINK: NEXT_PUBLIC_FOOTER_STAKING_LINK:
...@@ -307,15 +311,15 @@ frontend: ...@@ -307,15 +311,15 @@ frontend:
_default: 77 _default: 77
NEXT_PUBLIC_NETWORK_CURRENCY_NAME: NEXT_PUBLIC_NETWORK_CURRENCY_NAME:
_default: POA Network Sokol _default: POA Network Sokol
NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL: NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL:
_default: SPOA _default: SPOA
NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS: NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS:
......
...@@ -21,6 +21,8 @@ blockscout: ...@@ -21,6 +21,8 @@ blockscout:
# enable https # enable https
tls: tls:
enabled: true enabled: true
path:
- "/"
# probes # probes
livenessProbe: livenessProbe:
enabled: true enabled: true
...@@ -263,6 +265,8 @@ frontend: ...@@ -263,6 +265,8 @@ frontend:
# enable https # enable https
tls: tls:
enabled: true enabled: true
path:
- "/"
resources: resources:
limits: limits:
memory: memory:
...@@ -279,16 +283,16 @@ frontend: ...@@ -279,16 +283,16 @@ frontend:
enabled: true enabled: true
app: blockscout app: blockscout
environment: environment:
NEXT_PUBLIC_APP_PORT:
_default: 80
NEXT_PUBLIC_BLOCKSCOUT_VERSION: NEXT_PUBLIC_BLOCKSCOUT_VERSION:
_default: v4.1.8-beta _default: v4.1.8-beta
NEXT_PUBLIC_FOOTER_GITHUB_LINK: NEXT_PUBLIC_FOOTER_GITHUB_LINK:
_default: https://github.com/blockscout/blockscout _default: https://github.com/blockscout/blockscout
NEXT_PUBLIC_FOOTER_TWITTER_LINK: NEXT_PUBLIC_FOOTER_TWITTER_LINK:
_default: https://www.twitter.com/blockscoutcom _default: https://www.twitter.com/blockscoutcom
NEXT_PUBLIC_APP_ENV:
_default: preview
NEXT_PUBLIC_APP_INSTANCE: NEXT_PUBLIC_APP_INSTANCE:
_default: review _default: unknown
NEXT_PUBLIC_FOOTER_TELEGRAM_LINK: NEXT_PUBLIC_FOOTER_TELEGRAM_LINK:
_default: https://t.me/poa_network _default: https://t.me/poa_network
NEXT_PUBLIC_FOOTER_STAKING_LINK: NEXT_PUBLIC_FOOTER_STAKING_LINK:
......
...@@ -3,6 +3,7 @@ import { useQuery } from '@tanstack/react-query'; ...@@ -3,6 +3,7 @@ import { useQuery } from '@tanstack/react-query';
import type { UserInfo } from 'types/api/account'; import type { UserInfo } from 'types/api/account';
import { QueryKeys } from 'types/client/queries'; import { QueryKeys } from 'types/client/queries';
import * as cookies from 'lib/cookies';
import useFetch from 'lib/hooks/useFetch'; import useFetch from 'lib/hooks/useFetch';
interface Error { interface Error {
...@@ -19,5 +20,6 @@ export default function useFetchProfileInfo() { ...@@ -19,5 +20,6 @@ export default function useFetchProfileInfo() {
return fetch('/api/account/profile'); return fetch('/api/account/profile');
}, { }, {
refetchOnMount: false, refetchOnMount: false,
enabled: Boolean(cookies.get(cookies.NAMES.API_TOKEN)),
}); });
} }
export type TTxsFilters = {
filter: 'pending' | 'validated';
type?: Array<TypeFilter>;
method?: Array<MethodFilter>;
}
export type TypeFilter = 'token_transfer' | 'contract_creation' | 'contract_call' | 'coin_transfer' | 'token_creation';
export type MethodFilter = 'approve' | 'transfer' | 'multicall' | 'mint' | 'commit';
export enum QueryKeys { export enum QueryKeys {
csrf = 'csrf', csrf = 'csrf',
profile = 'profile', profile = 'profile',
transactionsPending = 'transactions_pending', transactions = 'transactions',
transactionsValidated = 'transactions_validated',
tx = 'tx', tx = 'tx',
txInternals = 'tx-internals', txInternals = 'tx-internals',
txLog = 'tx-log', txLog = 'tx-log',
......
...@@ -4,6 +4,7 @@ import React from 'react'; ...@@ -4,6 +4,7 @@ import React from 'react';
import { QueryKeys } from 'types/client/queries'; import { QueryKeys } from 'types/client/queries';
import * as cookies from 'lib/cookies';
import useFetch from 'lib/hooks/useFetch'; import useFetch from 'lib/hooks/useFetch';
import PageContent from 'ui/shared/Page/PageContent'; import PageContent from 'ui/shared/Page/PageContent';
import Header from 'ui/snippets/header/Header'; import Header from 'ui/snippets/header/Header';
...@@ -17,7 +18,9 @@ interface Props { ...@@ -17,7 +18,9 @@ interface Props {
const Page = ({ children, wrapChildren = true }: Props) => { const Page = ({ children, wrapChildren = true }: Props) => {
const fetch = useFetch(); const fetch = useFetch();
useQuery<unknown, unknown, unknown>([ QueryKeys.csrf ], async() => await fetch('/api/account/csrf')); useQuery<unknown, unknown, unknown>([ QueryKeys.csrf ], async() => await fetch('/api/account/csrf'), {
enabled: Boolean(cookies.get(cookies.NAMES.API_TOKEN)),
});
const renderedChildren = wrapChildren ? ( const renderedChildren = wrapChildren ? (
<PageContent>{ children }</PageContent> <PageContent>{ children }</PageContent>
......
...@@ -6,6 +6,7 @@ import { ...@@ -6,6 +6,7 @@ import {
TabPanels, TabPanels,
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import type { StyleProps } from '@chakra-ui/styled-system'; import type { StyleProps } from '@chakra-ui/styled-system';
import { pick } from 'lodash';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
...@@ -16,6 +17,8 @@ import useIsMobile from 'lib/hooks/useIsMobile'; ...@@ -16,6 +17,8 @@ import useIsMobile from 'lib/hooks/useIsMobile';
import RoutedTabsMenu from './RoutedTabsMenu'; import RoutedTabsMenu from './RoutedTabsMenu';
import useAdaptiveTabs from './useAdaptiveTabs'; import useAdaptiveTabs from './useAdaptiveTabs';
const PRESERVED_QUERY = [ 'network_type', 'network_sub_type' ];
const hiddenItemStyles: StyleProps = { const hiddenItemStyles: StyleProps = {
position: 'absolute', position: 'absolute',
top: '-9999px', top: '-9999px',
...@@ -49,7 +52,7 @@ const RoutedTabs = ({ tabs }: Props) => { ...@@ -49,7 +52,7 @@ const RoutedTabs = ({ tabs }: Props) => {
const handleTabChange = React.useCallback((index: number) => { const handleTabChange = React.useCallback((index: number) => {
const nextTab = tabs[index]; const nextTab = tabs[index];
router.query.tab = nextTab.id; router.query = { ...pick(router.query, PRESERVED_QUERY), tab: nextTab.id };
router.push(router); router.push(router);
}, [ tabs, router ]); }, [ tabs, router ]);
......
import { Alert, Box, HStack, Show, Button } from '@chakra-ui/react'; import { Alert, Box, HStack, Show, Button } from '@chakra-ui/react';
import React, { useState, useCallback } from 'react'; import React, { useState, useCallback } from 'react';
import type { TTxsFilters } from 'types/api/txsFilters';
import type { QueryKeys } from 'types/client/queries'; import type { QueryKeys } from 'types/client/queries';
import type { Sort } from 'types/client/txs-sort'; import type { Sort } from 'types/client/txs-sort';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
import DataFetchAlert from 'ui/shared/DataFetchAlert'; import DataFetchAlert from 'ui/shared/DataFetchAlert';
import FilterButton from 'ui/shared/FilterButton'; // import FilterInput from 'ui/shared/FilterInput';
import FilterInput from 'ui/shared/FilterInput';
import Pagination from 'ui/shared/Pagination'; import Pagination from 'ui/shared/Pagination';
import SortButton from 'ui/shared/SortButton'; import SortButton from 'ui/shared/SortButton';
// import TxsFilters from './TxsFilters';
import TxsSkeletonDesktop from './TxsSkeletonDesktop'; import TxsSkeletonDesktop from './TxsSkeletonDesktop';
import TxsSkeletonMobile from './TxsSkeletonMobile'; import TxsSkeletonMobile from './TxsSkeletonMobile';
import TxsWithSort from './TxsWithSort'; import TxsWithSort from './TxsWithSort';
...@@ -19,17 +20,17 @@ import useQueryWithPages from './useQueryWithPages'; ...@@ -19,17 +20,17 @@ import useQueryWithPages from './useQueryWithPages';
type Props = { type Props = {
queryName: QueryKeys; queryName: QueryKeys;
showDescription?: boolean; showDescription?: boolean;
stateFilter?: 'validated' | 'pending'; stateFilter?: TTxsFilters['filter'];
apiPath: string; apiPath: string;
} }
const TxsContent = ({ const TxsContent = ({
showDescription, showDescription,
queryName,
stateFilter, stateFilter,
apiPath, apiPath,
}: Props) => { }: Props) => {
const [ sorting, setSorting ] = useState<Sort>(); const [ sorting, setSorting ] = useState<Sort>();
// const [ filters, setFilters ] = useState<Partial<TTxsFilters>>({ type: [], method: [] });
const sort = useCallback((field: 'val' | 'fee') => () => { const sort = useCallback((field: 'val' | 'fee') => () => {
if (field === 'val') { if (field === 'val') {
...@@ -65,7 +66,8 @@ const TxsContent = ({ ...@@ -65,7 +66,8 @@ const TxsContent = ({
onNextPageClick, onNextPageClick,
hasPagination, hasPagination,
resetPage, resetPage,
} = useQueryWithPages(queryName, stateFilter, apiPath); } = useQueryWithPages({ filter: stateFilter }, apiPath);
// } = useQueryWithPages({ ...filters, filter: stateFilter, apiPath });
const isMobile = useIsMobile(false); const isMobile = useIsMobile(false);
...@@ -94,13 +96,12 @@ const TxsContent = ({ ...@@ -94,13 +96,12 @@ const TxsContent = ({
<> <>
{ showDescription && <Box mb={ 12 }>Only the first 10,000 elements are displayed</Box> } { showDescription && <Box mb={ 12 }>Only the first 10,000 elements are displayed</Box> }
<HStack mb={ 6 }> <HStack mb={ 6 }>
{ /* TODO */ } { /* api is not implemented */ }
<FilterButton { /* <TxsFilters
isActive={ false } filters={ filters }
// eslint-disable-next-line react/jsx-no-bind onFiltersChange={ setFilters }
onClick={ () => {} }
appliedFiltersNum={ 0 } appliedFiltersNum={ 0 }
/> /> */ }
{ isMobile && ( { isMobile && (
<SortButton <SortButton
// eslint-disable-next-line react/jsx-no-bind // eslint-disable-next-line react/jsx-no-bind
...@@ -109,13 +110,14 @@ const TxsContent = ({ ...@@ -109,13 +110,14 @@ const TxsContent = ({
display={{ base: 'block', lg: 'none' }} display={{ base: 'block', lg: 'none' }}
/> />
) } ) }
<FilterInput { /* api is not implemented */ }
{ /* <FilterInput
// eslint-disable-next-line react/jsx-no-bind // eslint-disable-next-line react/jsx-no-bind
onChange={ () => {} } onChange={ () => {} }
maxW="360px" maxW="360px"
size="xs" size="xs"
placeholder="Search by addresses, hash, method..." placeholder="Search by addresses, hash, method..."
/> /> */ }
</HStack> </HStack>
{ content } { content }
<Box mx={{ base: 0, lg: 6 }} my={{ base: 6, lg: 3 }}> <Box mx={{ base: 0, lg: 6 }} my={{ base: 6, lg: 3 }}>
......
import {
Button,
Checkbox,
CheckboxGroup,
Grid,
Link,
Popover,
PopoverTrigger,
PopoverContent,
PopoverBody,
Text,
useColorModeValue,
useDisclosure,
Flex,
} from '@chakra-ui/react';
import React, { useCallback, useState } from 'react';
import type { TTxsFilters, TypeFilter, MethodFilter } from 'types/api/txsFilters';
import FilterButton from 'ui/shared/FilterButton';
interface Props {
appliedFiltersNum?: number;
filters: Partial<TTxsFilters>;
onFiltersChange: (val: Partial<TTxsFilters>) => void;
}
const TYPE_OPTIONS = [
{ title: 'Token transfer', id: 'token_transfer' },
{ title: 'Contract Creation', id: 'contract_creation' },
{ title: 'Contract Call', id: 'contract_call' },
{ title: 'Coin Transfer', id: 'coin_transfer' },
{ title: 'Token Creation', id: 'token_creation' },
];
const METHOD_OPTIONS = [
{ title: 'Approve', id: 'approve' },
{ title: 'Transfer', id: 'transfer' },
{ title: 'Multicall', id: 'multicall' },
{ title: 'Mint', id: 'mint' },
{ title: 'Commit', id: 'commit' },
];
// TODO: i think we need to reload page after applying filters,
// because we need to reset pagination, clear query caches, reconnect websocket...
// also mobile version of filters is not implemented
const TxsFilters = ({ onFiltersChange, filters, appliedFiltersNum }: Props) => {
const { isOpen, onToggle, onClose } = useDisclosure();
const [ typeFilter, setTypeFilter ] = useState<Array<TypeFilter>>(filters.type || []);
const [ methodFilter, setMethodFilter ] = useState<Array<MethodFilter>>(filters.method || []);
const onTypeFilterChange = useCallback((val: Array<TypeFilter>) => {
setTypeFilter(val);
}, []);
const onMethodFilterChange = useCallback((val: Array<MethodFilter>) => {
setMethodFilter(val);
}, []);
const onFilterReset = useCallback(() => {
setTypeFilter([]);
setMethodFilter([]);
onFiltersChange({ type: [], method: [] });
onClose();
}, [ onClose, onFiltersChange ]);
const onFilterApply = useCallback(() => {
onFiltersChange({ type: typeFilter, method: methodFilter } as Partial<TTxsFilters>);
onClose();
}, [ onClose, onFiltersChange, typeFilter, methodFilter ]);
const borderColor = useColorModeValue('gray.200', 'whiteAlpha.200');
return (
<Popover isOpen={ isOpen } onClose={ onClose } placement="bottom-start" isLazy>
<PopoverTrigger>
<FilterButton
isActive={ isOpen || Number(appliedFiltersNum) > 0 }
onClick={ onToggle }
appliedFiltersNum={ appliedFiltersNum }
/>
</PopoverTrigger>
<PopoverContent w={{ md: '100%', lg: '438px' }}>
<PopoverBody px={ 4 } py={ 6 }>
<Text variant="secondary" fontWeight="600" fontSize="sm">Type</Text>
<Grid gridTemplateColumns="1fr 1fr" rowGap={ 5 } mt={ 4 } mb={ 4 } pb={ 6 } borderBottom="1px solid" borderColor={ borderColor }>
<CheckboxGroup size="lg" onChange={ onTypeFilterChange } defaultValue={ typeFilter }>
{ TYPE_OPTIONS.map(({ title, id }) => <Checkbox key={ id } value={ id }><Text fontSize="md">{ title }</Text></Checkbox>) }
</CheckboxGroup>
</Grid>
<Text variant="secondary" fontWeight="600" fontSize="sm">Method</Text>
<Grid gridTemplateColumns="1fr 1fr" rowGap={ 5 } mt={ 4 } mb={ 4 } pb={ 6 } borderBottom="1px solid" borderColor={ borderColor }>
<CheckboxGroup size="lg" onChange={ onMethodFilterChange } defaultValue={ methodFilter }>
{ METHOD_OPTIONS.map(({ title, id }) => <Checkbox key={ id } value={ id }><Text fontSize="md">{ title }</Text></Checkbox>) }
</CheckboxGroup>
</Grid>
<Flex alignItems="center" justifyContent="space-between">
<Link fontSize="sm" onClick={ onFilterReset }>Reset filters</Link>
<Button variant="outline" size="sm" onClick={ onFilterApply }>Apply</Button>
</Flex>
</PopoverBody>
</PopoverContent>
</Popover>
);
};
export default React.memo(TxsFilters);
import React from 'react'; import React from 'react';
import { QueryKeys } from 'types/client/queries';
import TxsContent from './TxsContent'; import TxsContent from './TxsContent';
type Props = { type Props = {
...@@ -11,7 +9,6 @@ type Props = { ...@@ -11,7 +9,6 @@ type Props = {
const TxsTab = ({ tab }: Props) => { const TxsTab = ({ tab }: Props) => {
return ( return (
<TxsContent <TxsContent
queryName={ tab === 'validated' ? QueryKeys.transactionsValidated : QueryKeys.transactionsPending }
showDescription={ tab === 'validated' } showDescription={ tab === 'validated' }
stateFilter={ tab } stateFilter={ tab }
apiPath="/api/transactions" apiPath="/api/transactions"
......
...@@ -5,12 +5,14 @@ import React, { useCallback } from 'react'; ...@@ -5,12 +5,14 @@ import React, { useCallback } from 'react';
import { animateScroll } from 'react-scroll'; import { animateScroll } from 'react-scroll';
import type { TransactionsResponse } from 'types/api/transaction'; import type { TransactionsResponse } from 'types/api/transaction';
import type { TTxsFilters } from 'types/api/txsFilters';
import { QueryKeys } from 'types/client/queries';
import useFetch from 'lib/hooks/useFetch'; import useFetch from 'lib/hooks/useFetch';
const PAGINATION_FIELDS = [ 'block_number', 'index', 'items_count' ]; const PAGINATION_FIELDS = [ 'block_number', 'index', 'items_count' ];
export default function useQueryWithPages(queryName: string, filter: string | undefined, apiPath: string) { export default function useQueryWithPages(filters: TTxsFilters, apiPath: string) {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const router = useRouter(); const router = useRouter();
const [ page, setPage ] = React.useState(1); const [ page, setPage ] = React.useState(1);
...@@ -19,14 +21,17 @@ export default function useQueryWithPages(queryName: string, filter: string | un ...@@ -19,14 +21,17 @@ export default function useQueryWithPages(queryName: string, filter: string | un
const fetch = useFetch(); const fetch = useFetch();
const { data, isLoading, isError } = useQuery<unknown, unknown, TransactionsResponse>( const { data, isLoading, isError } = useQuery<unknown, unknown, TransactionsResponse>(
[ queryName, { page } ], [ QueryKeys.transactions, { page, filters } ],
async() => { async() => {
const params: Array<string> = []; const params: Array<string> = [];
Object.entries({ Object.entries({ ...filters, ...currPageParams }).forEach(([ key, val ]) => {
...currPageParams, if (Array.isArray(val)) {
filter, val.length && params.push(`${ key }=${ val.join(',') }`);
}).forEach(([ key, val ]) => val && params.push(`${ key }=${ val }`)); } else if (val) {
params.push(`${ key }=${ val }`);
}
});
return fetch(`${ apiPath }${ params.length ? '?' + params.join('&') : '' }`); return fetch(`${ apiPath }${ params.length ? '?' + params.join('&') : '' }`);
}, },
...@@ -46,9 +51,11 @@ export default function useQueryWithPages(queryName: string, filter: string | un ...@@ -46,9 +51,11 @@ export default function useQueryWithPages(queryName: string, filter: string | un
} }
const nextPageQuery = { ...router.query }; const nextPageQuery = { ...router.query };
Object.entries(nextPageParams).forEach(([ key, val ]) => nextPageQuery[key] = val.toString()); Object.entries(nextPageParams).forEach(([ key, val ]) => nextPageQuery[key] = val.toString());
router.push({ pathname: router.pathname, query: nextPageQuery }, undefined, { shallow: true }); router.push({ pathname: router.pathname, query: nextPageQuery }, undefined, { shallow: true })
animateScroll.scrollToTop({ duration: 0 }); .then(() => {
setPage(prev => prev + 1); animateScroll.scrollToTop({ duration: 0 });
setPage(prev => prev + 1);
});
}, [ data, page, pageParams, router ]); }, [ data, page, pageParams, router ]);
const onPrevPageClick = useCallback(() => { const onPrevPageClick = useCallback(() => {
...@@ -63,9 +70,11 @@ export default function useQueryWithPages(queryName: string, filter: string | un ...@@ -63,9 +70,11 @@ export default function useQueryWithPages(queryName: string, filter: string | un
Object.entries(nextPageParams).forEach(([ key, val ]) => nextPageQuery[key] = val.toString()); Object.entries(nextPageParams).forEach(([ key, val ]) => nextPageQuery[key] = val.toString());
} }
router.query = nextPageQuery; router.query = nextPageQuery;
router.push({ pathname: router.pathname, query: nextPageQuery }, undefined, { shallow: true }); router.push({ pathname: router.pathname, query: nextPageQuery }, undefined, { shallow: true })
animateScroll.scrollToTop({ duration: 0 }); .then(() => {
setPage(prev => prev - 1); animateScroll.scrollToTop({ duration: 0 });
setPage(prev => prev - 1);
});
}, [ router, page, pageParams ]); }, [ router, page, pageParams ]);
const resetPage = useCallback(() => { const resetPage = useCallback(() => {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment