Commit e62aa4ec authored by tom's avatar tom

Merge branch 'main' of github.com:blockscout/frontend into pagination-fixes

parents 2349289e db1e20c5
...@@ -2,8 +2,8 @@ name: Run E2E tests k8s ...@@ -2,8 +2,8 @@ name: Run E2E tests k8s
on: on:
# push: # push:
# pull_request: pull_request:
workflow_dispatch workflow_dispatch:
env: env:
K8S_LOCAL_PORT: ${{ secrets.K8S_LOCAL_PORT }} K8S_LOCAL_PORT: ${{ secrets.K8S_LOCAL_PORT }}
......
...@@ -3,8 +3,6 @@ name: Deploy review environment ...@@ -3,8 +3,6 @@ name: Deploy review environment
on: on:
pull_request: pull_request:
# push: # push:
# branches-ignore:
# - 'main'
workflow_dispatch: workflow_dispatch:
env: env:
......
/// <reference types="next-react-svg" />
...@@ -8,18 +8,6 @@ blockscout: ...@@ -8,18 +8,6 @@ blockscout:
_default: ENC[AES256_GCM,data:+q7GvdSeRQ0aY4grlQrdWUvxT9tzsRoii3W/ECwpVKs13lw84bGV+rbiEJXBYoOAw7SDz1Hr+KMy9SQzHvj12w==,iv:Xjo9+Nca6eDTZtUcDPAPfXYSAoP2gYmDX+JanCyPPkY=,tag:kQxLFsfN5YXft8ihgNvA+Q==,type:str] _default: ENC[AES256_GCM,data:+q7GvdSeRQ0aY4grlQrdWUvxT9tzsRoii3W/ECwpVKs13lw84bGV+rbiEJXBYoOAw7SDz1Hr+KMy9SQzHvj12w==,iv:Xjo9+Nca6eDTZtUcDPAPfXYSAoP2gYmDX+JanCyPPkY=,tag:kQxLFsfN5YXft8ihgNvA+Q==,type:str]
MAILSLURP_EMAIL_ID: MAILSLURP_EMAIL_ID:
_default: ENC[AES256_GCM,data:tGjukx3Q+NaWpvfXPXmY6dphdEdbO531q4NMqsOa4XCudfpt,iv:6S2jTCoJCfhkRHg4aA2UR1U4fMsDhNV44+r7ofnHLTA=,tag:nr+0zaM45+UBUwOpmVD1hg==,type:str] _default: ENC[AES256_GCM,data:tGjukx3Q+NaWpvfXPXmY6dphdEdbO531q4NMqsOa4XCudfpt,iv:6S2jTCoJCfhkRHg4aA2UR1U4fMsDhNV44+r7ofnHLTA=,tag:nr+0zaM45+UBUwOpmVD1hg==,type:str]
ACCOUNT_AUTH0_DOMAIN:
_default: ENC[AES256_GCM,data:C6TAdc/RZ+qFOan+tWnTsfIDcYgxCNjuKOU=,iv:tEcHTumHxA9LreqLSwjcTTCP3igk/VQrJn/OWd+YQ4c=,tag:XOlWi3XHBZyENtLsv3afmw==,type:str]
ACCOUNT_AUTH0_CLIENT_ID:
_default: ENC[AES256_GCM,data:lojxfDVJBi1Sc6KCL28tln6RJ5leg0jmaTbSHbBd1H4=,iv:l1Iq/YsZKkFuorQgKd+KzAIkqbXHv6I4eid+7LDnyOA=,tag:/c7GBciDCruz+dAOfxncww==,type:str]
ACCOUNT_AUTH0_CLIENT_SECRET:
_default: ENC[AES256_GCM,data:Nmajob/xrOfx3sZgGHWw4/VnSgoyehubNdhJSqasxLFInJeZgxCW/QXpu5KG6j1fDdALLOT89Es+m0IO9VrG8A==,iv:RxYWdOo5G6t0FFwHQfhvUTiCOsxRzJJT0Y1nc0F5sDc=,tag:6v6+yHaHgwnw0cDSD1hXwg==,type:str]
ACCOUNT_AUTH0_CALLBACK_URL:
_default: ENC[AES256_GCM,data:v6LJLRwibjc6QgF1t6QNzy/FfL/i2G0mD2X6oxf0o4Q6A8cwovE03Wd5nXWTSyWrN95rJm0IQY7bjU3fXpDw0p/u4vu8dMpfM2bZMMOalXP4pA==,iv:VtNoyCEsesWn0PyWRXzLgbMcUa+voKa1sjsUY26RKnw=,tag:t02c9NVe4aGGo5feOP1OWQ==,type:str]
ACCOUNT_AUTH0_LOGOUT_RETURN_URL:
_default: ENC[AES256_GCM,data:JQylcEDhJUOqXqBCycJ8XLwz6wpa3Uz3p5MhCEVolLnKkYoDFogGuFt1YkKt7EEtbZjqvSjIa0xQhpezX5hvypU9KxRxoVosUQ4=,iv:dbXG4N4t5jQgx68l0r5iLh5FGGg2O/vqbSDpyxzEauI=,tag:upkfr/3h35rZ93oBNJ3Y/w==,type:str]
ACCOUNT_AUTH0_LOGOUT_URL:
_default: ENC[AES256_GCM,data:6vj0qvxktXptFDX4Tq3kGIyhaJN7Adcwfgb3qWEYH78pWtmFprwfKNdPa/4=,iv:Eb3NcZP9oSmh0ZfY8vCWwR5Ciwe7Lr/QJmcNWXiir9E=,tag:uXoFnMfwo/GD9IPuvs/9Wg==,type:str]
ACCOUNT_SENDGRID_API_KEY: ACCOUNT_SENDGRID_API_KEY:
_default: ENC[AES256_GCM,data:bNzimXY1TDUHgLijvgcJ8mw+9RXvCLchxhum77KY6ss5NUdoWuNuQjpC7M819XRPqhBmZkC2qBSX1ZRhcNQCB1OqGkRq,iv:JFmeCxsO2lkyScN8iVb8B/356SbP7KCMFffIPjGtN0o=,tag:A21hzDObJMAUhHQRWB7Z9w==,type:str] _default: ENC[AES256_GCM,data:bNzimXY1TDUHgLijvgcJ8mw+9RXvCLchxhum77KY6ss5NUdoWuNuQjpC7M819XRPqhBmZkC2qBSX1ZRhcNQCB1OqGkRq,iv:JFmeCxsO2lkyScN8iVb8B/356SbP7KCMFffIPjGtN0o=,tag:A21hzDObJMAUhHQRWB7Z9w==,type:str]
ACCOUNT_SENDGRID_SENDER: ACCOUNT_SENDGRID_SENDER:
...@@ -39,9 +27,21 @@ blockscout: ...@@ -39,9 +27,21 @@ blockscout:
DATABASE_URL: DATABASE_URL:
_default: ENC[AES256_GCM,data:r5JQJH+pq4zwo9ceFP4I8inttRFFxKKaw9+l9hDqeDMcEIPljAfJxvSF3noVlyfNtqO0ru+3GaTumDamdb9Hw2Y=,iv:OwCvrIgXpxVMI5QQfT7ZDGcsXBdzIcuv0wUSxtwkYrM=,tag:o0GFp3F+jTxw8Hg4Ce/IpQ==,type:str] _default: ENC[AES256_GCM,data:r5JQJH+pq4zwo9ceFP4I8inttRFFxKKaw9+l9hDqeDMcEIPljAfJxvSF3noVlyfNtqO0ru+3GaTumDamdb9Hw2Y=,iv:OwCvrIgXpxVMI5QQfT7ZDGcsXBdzIcuv0wUSxtwkYrM=,tag:o0GFp3F+jTxw8Hg4Ce/IpQ==,type:str]
ACCOUNT_DATABASE_URL: ACCOUNT_DATABASE_URL:
_default: ENC[AES256_GCM,data:48q2fFA3s1HGSm7YB5QV8B0eiI2PZHPgOsUS84R8x+8GecrFTenifbcHD6AivFNKovLuClUy2aFuEtEvJDOfelY=,iv:5ZLl62Yj/AXwyg+pdBLUv3EyYXSTOiSCqGYOv3rBx94=,tag:FUiy0haST4mdrqe2VlvrLg==,type:str] _default: ENC[AES256_GCM,data:HF5y8ezV5TiLqeh98WDp4rXQeUfSBETyWVHOyNZNy5pt4MdiKTdFBLauOJpD6YHWynMFsd8IJLRNLrBn4qGe3RfwprR6v3WN9Q==,iv:B9AJXO7EJexsPgDHb5s5tzpadVYoZ79fyaL8NOYXSEw=,tag:lyNKQ13Q+pn+3DrAXIzIKQ==,type:str]
ACCOUNT_REDIS_URL: ACCOUNT_REDIS_URL:
_default: ENC[AES256_GCM,data:MtAI4U/4mLdIW0d4ap5+0wTufO7hvOD/ETPnqpGJ7tGFXmirK2w/M5UdQd8M/EsH/4zk3rlM4WDGRVwMzCTiugG1FxEdIcKOA6127jXgSUrE,iv:zgrAoj7437b0TJTstU0S7UVHGt4fKnmP3wlc2qbURa0=,tag:vT3NL8PLLxB9z/dmkE/jaw==,type:str] _default: ENC[AES256_GCM,data:MtAI4U/4mLdIW0d4ap5+0wTufO7hvOD/ETPnqpGJ7tGFXmirK2w/M5UdQd8M/EsH/4zk3rlM4WDGRVwMzCTiugG1FxEdIcKOA6127jXgSUrE,iv:zgrAoj7437b0TJTstU0S7UVHGt4fKnmP3wlc2qbURa0=,tag:vT3NL8PLLxB9z/dmkE/jaw==,type:str]
ACCOUNT_AUTH0_DOMAIN:
_default: ENC[AES256_GCM,data:zRZL/kIX2R4AbanIjN7s/qFu5od1onFhYBg=,iv:4OSMUjvxUHevuXSNAnTj/DTFoZA5mC4UIerGiIgDwQc=,tag:J5I4CC9AIVXDmhkXG4X5wQ==,type:str]
ACCOUNT_AUTH0_CLIENT_ID:
_default: ENC[AES256_GCM,data:xasZDDg1IuGbKSTm9Opjh43RcqDSzE3dMdmynP0gejo=,iv:TTRqPOkwMxAQIpmc7DklBoPdZzn3FsAprxwXtHY/KSk=,tag:ZI7zQ5zyjjYOqjzy2DyY7Q==,type:str]
ACCOUNT_AUTH0_CLIENT_SECRET:
_default: ENC[AES256_GCM,data:iYnptgxESZs216/m0ArCciXfirPTxVjt5urKTATyYv3uadokC54ofzMZagG/ZVvNY48+Uxh4YGzwySyLOOM0SA==,iv:UQoZf3tesEjAJ7E5YruoZmVclsYfS5EmSo4z90wfHfE=,tag:er+hARrkCrjoQugqvRr4ow==,type:str]
ACCOUNT_AUTH0_CALLBACK_URL:
_default: ENC[AES256_GCM,data:GhyNTVlRvDpfW6swKExjVy65I3S+q6ITJWH8xp5TXuudp7hAtI4ujkYmXaa7D1BCaineTLhQ8UgHXFAgodOlrsGP1g41LIh6T2SGhrXTGGLbFw==,iv:ZflinC/h35jkrVAx0x7Z7U9mRridl85f7f6nfoc0Xts=,tag:40zL2KM9uPg/lRYv5JMxqw==,type:str]
ACCOUNT_AUTH0_LOGOUT_RETURN_URL:
_default: ENC[AES256_GCM,data:mTY6sjNKZ+VEvH47eyyoUHt//beWvuxyreu+1WrmMvkdkwR6jgXnSXh+1pTuiG8e3it96Egxraz0hjZxlkY9kSmE91/dfoqKTns=,iv:op8OuOXXeBmmUnVkCL14j4HrfjHHoN3n2XZqLdg03Mw=,tag:bbFQYi0Aa1sUdfoUjuQ/+w==,type:str]
ACCOUNT_AUTH0_LOGOUT_URL:
_default: ENC[AES256_GCM,data:IZFfi6pn+hy7g0wnEtP9TYHH1fNiC2gqgRHVdgm4C9smPerEvS0pq9dBwVY=,iv:BxbSInFQ6GE2loTv+IzdYr25PlyzdWZI1wdT6r+uvBg=,tag:psujCU372k59OGmlRdH9Fg==,type:str]
scVerifier: scVerifier:
environment: environment:
SMART_CONTRACT_VERIFIER__SOLIDITY__FETCHER__S3__ACCESS_KEY: SMART_CONTRACT_VERIFIER__SOLIDITY__FETCHER__S3__ACCESS_KEY:
...@@ -57,6 +57,12 @@ postgres: ...@@ -57,6 +57,12 @@ postgres:
POSTGRES_DB: POSTGRES_DB:
_default: ENC[AES256_GCM,data:QRCvQVzKOfOApg==,iv:eOdxyjmv6Zj5xINGXO0dD/GcDgD1J8e4OzLMyOneoDs=,tag:EBnJ7OT3u7vcBIbviRKaHg==,type:str] _default: ENC[AES256_GCM,data:QRCvQVzKOfOApg==,iv:eOdxyjmv6Zj5xINGXO0dD/GcDgD1J8e4OzLMyOneoDs=,tag:EBnJ7OT3u7vcBIbviRKaHg==,type:str]
geth: geth:
jwt:
token: ENC[AES256_GCM,data:+E6k/3vTav9yqAbOStmC5Rwx9mV80l8iAqQYGWJNZFTUfqXQKNavaVJW7AfzixZC4zaj8c0dbdsj1bv9rlJljG0q,iv:zW9spad1AdbkAjYQYAAflzsNhaH+LAIkjHpvIPHVGbs=,tag:oqvgkqVroMxSzwmna9ra5w==,type:str]
client:
environment:
JWT_TOKEN:
_default: ENC[AES256_GCM,data:T46y6KmBcEslaodBMRTNZya51uQiK4I3olCvdxCw3qZlgoOxRUvSgKLLoRNyD/d6w6PwgJA4oU7N5hmNGQ+I26Vc,iv:F+evxDb/sn6raub1LjZPGk2VFoQBVuBvkpgJ2YCJjw8=,tag:K0QV4weIEcdy1AXcpHSxQQ==,type:str]
files: files:
list: list:
genesis.json: ENC[AES256_GCM,data:nSFncQhigaTtb7YSeXiUvua2LTOGlluVDywxQR5NnhvIBZCZniIxHsEhXKF9rBYceOMfBPKY5MTLIyxYisZeaJHL71A/uXQerDEKkECpzhwUSTdNEA9gUEqmpUjf7ChAWjybWw6NCM2jVhG3bnZwTwmuzeCSek85q4J503j9HVEkGXAU52A2+yoov41c+xoCEOj7W6Ka/f7yJYkyppoeS+4AIxBFcJ5hI0pIf1VqDcSrJf68JO1pPzcmp9eK3CK2CIpD8uxV6CseBFEqlbllmgAVwSUJ4i8XhYEO2hMyULm/LkW52U31/tq89yctSkp5TB62sZEHyDxXcq6jFSqd4840o9VedyX4YuEVtLBlXdzerc4Q2tHORQv5u2BLW7ktA/5vj8d9XnblQBDKrqxtUEgvSj9T/Xm4zyKjIhJBAAwUd5rF4j8dNbUGpsBm3G/Lx1+DqrS8cyxXgumtmfsklEVTWBra2dwt/tlTpF+utOwT7KtqXo2dVNvL6tUWrWGQqCt+fgeRKw8NuHWgU9Nm6baQ0yhWIM3XaCEuuJqHLdelYgQiKVEaTvlw7my7XwQsunJCQ98Q2eO61v7WFsg1m5s3Wyts8R2EnMWrnDlGro//eUHFlxDiIQGXmWgnz597lrLTXiuZKNI4pvPsAggBQxIeUcYzICy5uLRPWAiDpfshYt/fRQhIxmrVaJCufJrq4IcUdJYKjAcpnGHqZMB6quZiUPCPYg5Lvz7w1zoIVLn4ANWVuh7J1husNH13SwrDUXEm+k/WYCkDZm5B/K9IzkihY8nNXbvxHEXUClpb943Xw8hpvZZj0BfP/ye7ywUzHGlo3OmFAdE7Fbjc9eELxgYirONoaHy9GkjWPjinTVVlvitakqzx4dGJ+U2adSB01WAj63rn4ZidaC/eUpcPe7E2OeqmpkLc/kLM9g3gEe/MCiqsXXUYtm0Idpbrf+rRTAotoVOi3fKr1inlJY6n+9UzKAxNkTIkiGUQ1E9g0s3Kn4Z83apuy5+Us8gA9xNfWNGcOO105ZUYKyow4Xb5ggV8t+UTXPOZQp97Qz/6q0VC2kY9bx991SrJLoaK+ChLQ0/t/LcLjZxKRPDU2YGOfjrPDO3iscDkZgX/8IvH56jxUXAYmNrxOgbb9encYIrMDYrsKTaj5kXETp2M3sTdHwk6d78PAteYCvgQb44gu5JtknIq2LLXb/2Ue45O4HnrB+AsICPb+oGAxIqFkHvWuOIRZLqap8wbazszBlqsrX8ABegjvReXiG0rX1VeQIXNcG3/tKPHNKrDlFJykaiC1w==,iv:pyrxyy1bbv1UiGZ2y1jer9UqOwVkZGb4GwawJoCGJuM=,tag:aVt5dASuZglsUs9Yydzjaw==,type:str] genesis.json: ENC[AES256_GCM,data:nSFncQhigaTtb7YSeXiUvua2LTOGlluVDywxQR5NnhvIBZCZniIxHsEhXKF9rBYceOMfBPKY5MTLIyxYisZeaJHL71A/uXQerDEKkECpzhwUSTdNEA9gUEqmpUjf7ChAWjybWw6NCM2jVhG3bnZwTwmuzeCSek85q4J503j9HVEkGXAU52A2+yoov41c+xoCEOj7W6Ka/f7yJYkyppoeS+4AIxBFcJ5hI0pIf1VqDcSrJf68JO1pPzcmp9eK3CK2CIpD8uxV6CseBFEqlbllmgAVwSUJ4i8XhYEO2hMyULm/LkW52U31/tq89yctSkp5TB62sZEHyDxXcq6jFSqd4840o9VedyX4YuEVtLBlXdzerc4Q2tHORQv5u2BLW7ktA/5vj8d9XnblQBDKrqxtUEgvSj9T/Xm4zyKjIhJBAAwUd5rF4j8dNbUGpsBm3G/Lx1+DqrS8cyxXgumtmfsklEVTWBra2dwt/tlTpF+utOwT7KtqXo2dVNvL6tUWrWGQqCt+fgeRKw8NuHWgU9Nm6baQ0yhWIM3XaCEuuJqHLdelYgQiKVEaTvlw7my7XwQsunJCQ98Q2eO61v7WFsg1m5s3Wyts8R2EnMWrnDlGro//eUHFlxDiIQGXmWgnz597lrLTXiuZKNI4pvPsAggBQxIeUcYzICy5uLRPWAiDpfshYt/fRQhIxmrVaJCufJrq4IcUdJYKjAcpnGHqZMB6quZiUPCPYg5Lvz7w1zoIVLn4ANWVuh7J1husNH13SwrDUXEm+k/WYCkDZm5B/K9IzkihY8nNXbvxHEXUClpb943Xw8hpvZZj0BfP/ye7ywUzHGlo3OmFAdE7Fbjc9eELxgYirONoaHy9GkjWPjinTVVlvitakqzx4dGJ+U2adSB01WAj63rn4ZidaC/eUpcPe7E2OeqmpkLc/kLM9g3gEe/MCiqsXXUYtm0Idpbrf+rRTAotoVOi3fKr1inlJY6n+9UzKAxNkTIkiGUQ1E9g0s3Kn4Z83apuy5+Us8gA9xNfWNGcOO105ZUYKyow4Xb5ggV8t+UTXPOZQp97Qz/6q0VC2kY9bx991SrJLoaK+ChLQ0/t/LcLjZxKRPDU2YGOfjrPDO3iscDkZgX/8IvH56jxUXAYmNrxOgbb9encYIrMDYrsKTaj5kXETp2M3sTdHwk6d78PAteYCvgQb44gu5JtknIq2LLXb/2Ue45O4HnrB+AsICPb+oGAxIqFkHvWuOIRZLqap8wbazszBlqsrX8ABegjvReXiG0rX1VeQIXNcG3/tKPHNKrDlFJykaiC1w==,iv:pyrxyy1bbv1UiGZ2y1jer9UqOwVkZGb4GwawJoCGJuM=,tag:aVt5dASuZglsUs9Yydzjaw==,type:str]
...@@ -65,7 +71,7 @@ geth: ...@@ -65,7 +71,7 @@ geth:
frontend: frontend:
environment: environment:
NEXT_PUBLIC_NETWORK_TOKEN_ADDRESS: NEXT_PUBLIC_NETWORK_TOKEN_ADDRESS:
_default: ENC[AES256_GCM,data:1SAbzZhCs/vzdftIX0WVLtImH27NJ6SwENee4uTu2p+ZyUso3nQCLUUm,iv:apyLxt2dQ5RN33ra1Q1sAy2cyplG9FSryksQru2ghlA=,tag:PVcCNt0bz1TfQewUebV5LA==,type:str] _default: ENC[AES256_GCM,data:D/pLeRn7C40/rc7nNDCuK5dTuADPrHZl+J8hGC9xhwNRKZnkHySxkSzV,iv:z98n49jakK1mvlJpJ75BXj5gGyDHpBcAoCbaM0FmEA8=,tag:pLTa5kHmSi+bCN+8jeMhGQ==,type:str]
NEXT_PUBLIC_SENTRY_DSN: NEXT_PUBLIC_SENTRY_DSN:
_default: ENC[AES256_GCM,data:n/H2AH2n9ovn265iFbbrqeOOWS3s7FXgDv5FXJ2Dz133GuhTaqIU5psWjTraZ/Vh+dVM8M9zBLFfUanCWOz7cerq/QUoSVZjKvIvcyp6F072DGU=,iv:Co/pSR+U0vfkmWR+LDpxcQKDJl0WNbaEZihpzrRJcbc=,tag:HUiCX8WGJXWCDB59Y6iIbw==,type:str] _default: ENC[AES256_GCM,data:n/H2AH2n9ovn265iFbbrqeOOWS3s7FXgDv5FXJ2Dz133GuhTaqIU5psWjTraZ/Vh+dVM8M9zBLFfUanCWOz7cerq/QUoSVZjKvIvcyp6F072DGU=,iv:Co/pSR+U0vfkmWR+LDpxcQKDJl0WNbaEZihpzrRJcbc=,tag:HUiCX8WGJXWCDB59Y6iIbw==,type:str]
SENTRY_CSP_REPORT_URI: SENTRY_CSP_REPORT_URI:
...@@ -78,8 +84,8 @@ sops: ...@@ -78,8 +84,8 @@ sops:
azure_kv: [] azure_kv: []
hc_vault: [] hc_vault: []
age: [] age: []
lastmodified: "2022-11-02T07:59:42Z" lastmodified: "2022-11-14T10:04:29Z"
mac: ENC[AES256_GCM,data:OrV/dUWOtL23UFQLeIsKsGluTmse42d/4sgFMDs3UXdACsZu8twMt29Y/WaPHyq8Tpn5iYzhBLU6SCUmHxEhBNVzKBd5uCUbav1faS/zW6fSd9bEP7rmbUjaJGHliBkG3T4VCSZn53jR/OMNbSynIxZ0kRpVHr+RTcalaH7dLQ8=,iv:J3gXjFgFyZoPqL+VEnjkuKzA9UIIyK3UsvPWacBZKsY=,tag:/zY1jVjIm5BsDNJlfsg5uw==,type:str] mac: ENC[AES256_GCM,data:LitRw7GOBq9mHXXiWmfmcpw0Tbn8KESGZjFjDAlYWIlYZWVzxIeApXWNin/HoFrwIwnmpn2bpyjG1yWu3FR/3CLkkekUSsZ7k4P7kBlspQyUNSqFxWfDM+2qwpJ7qUNfQ05oGJkceLTsYec5kH2Xe39fs8RQife5uoord1FkW7A=,iv:M4nSUT77FUCCTm72yteJCZNJivYCs0LLr2irRGgrq3c=,tag:HaMxRKNEgNmB3sIw93yCMw==,type:str]
pgp: pgp:
- created_at: "2022-09-14T13:42:28Z" - created_at: "2022-09-14T13:42:28Z"
enc: | enc: |
......
This diff is collapsed.
...@@ -4,9 +4,12 @@ import type { NewBlockSocketResponse } from 'types/api/block'; ...@@ -4,9 +4,12 @@ import type { NewBlockSocketResponse } from 'types/api/block';
export type SocketMessageParams = SocketMessage.NewBlock | export type SocketMessageParams = SocketMessage.NewBlock |
SocketMessage.BlocksIndexStatus | SocketMessage.BlocksIndexStatus |
SocketMessage.TxStatusUpdate; SocketMessage.TxStatusUpdate |
SocketMessage.NewTx |
SocketMessage.NewPendingTx |
SocketMessage.Unknown;
interface SocketMessageParamsGeneric<Event extends string, Payload extends object> { interface SocketMessageParamsGeneric<Event extends string | undefined, Payload extends object | unknown> {
channel: Channel | undefined; channel: Channel | undefined;
event: Event; event: Event;
handler: (payload: Payload) => void; handler: (payload: Payload) => void;
...@@ -17,4 +20,7 @@ export namespace SocketMessage { ...@@ -17,4 +20,7 @@ export namespace SocketMessage {
export type NewBlock = SocketMessageParamsGeneric<'new_block', NewBlockSocketResponse>; export type NewBlock = SocketMessageParamsGeneric<'new_block', NewBlockSocketResponse>;
export type BlocksIndexStatus = SocketMessageParamsGeneric<'index_status', {finished: boolean; ratio: string}>; export type BlocksIndexStatus = SocketMessageParamsGeneric<'index_status', {finished: boolean; ratio: string}>;
export type TxStatusUpdate = SocketMessageParamsGeneric<'collated', NewBlockSocketResponse>; export type TxStatusUpdate = SocketMessageParamsGeneric<'collated', NewBlockSocketResponse>;
export type NewTx = SocketMessageParamsGeneric<'transaction', { transaction: number }>;
export type NewPendingTx = SocketMessageParamsGeneric<'pending_transaction', { pending_transaction: number }>;
export type Unknown = SocketMessageParamsGeneric<undefined, unknown>;
} }
...@@ -6,7 +6,7 @@ import notEmpty from 'lib/notEmpty'; ...@@ -6,7 +6,7 @@ import notEmpty from 'lib/notEmpty';
import { useSocket } from './context'; import { useSocket } from './context';
interface Params { interface Params {
topic: string; topic: string | undefined;
params?: object; params?: object;
isDisabled: boolean; isDisabled: boolean;
onJoin?: (channel: Channel, message: unknown) => void; onJoin?: (channel: Channel, message: unknown) => void;
...@@ -47,7 +47,7 @@ export default function useSocketChannel({ topic, params, isDisabled, onJoin, on ...@@ -47,7 +47,7 @@ export default function useSocketChannel({ topic, params, isDisabled, onJoin, on
}, [ channel, isDisabled ]); }, [ channel, isDisabled ]);
useEffect(() => { useEffect(() => {
if (socket === null || isDisabled) { if (socket === null || isDisabled || !topic) {
return; return;
} }
......
...@@ -7,7 +7,7 @@ export default function useSocketMessage({ channel, event, handler }: SocketMess ...@@ -7,7 +7,7 @@ export default function useSocketMessage({ channel, event, handler }: SocketMess
handlerRef.current = handler; handlerRef.current = handler;
useEffect(() => { useEffect(() => {
if (channel === undefined) { if (channel === undefined || event === undefined) {
return; return;
} }
......
const withReactSvg = require('next-react-svg');
const path = require('path'); const path = require('path');
const headers = require('./configs/nextjs/headers'); const headers = require('./configs/nextjs/headers');
...@@ -15,6 +14,12 @@ const moduleExports = { ...@@ -15,6 +14,12 @@ const moduleExports = {
__SENTRY_TRACING__: false, __SENTRY_TRACING__: false,
}), }),
); );
config.module.rules.push(
{
test: /\.svg$/,
use: [ '@svgr/webpack' ],
},
);
return config; return config;
}, },
...@@ -28,4 +33,4 @@ const moduleExports = { ...@@ -28,4 +33,4 @@ const moduleExports = {
output: 'standalone', output: 'standalone',
}; };
module.exports = withReactSvg(moduleExports); module.exports = moduleExports;
...@@ -41,7 +41,6 @@ ...@@ -41,7 +41,6 @@
"framer-motion": "^6", "framer-motion": "^6",
"lodash": "^4.0.0", "lodash": "^4.0.0",
"next": "12.2.5", "next": "12.2.5",
"next-react-svg": "1.1.3",
"node-fetch": "^3.2.9", "node-fetch": "^3.2.9",
"phoenix": "^1.6.15", "phoenix": "^1.6.15",
"pino-http": "^8.2.1", "pino-http": "^8.2.1",
...@@ -57,6 +56,7 @@ ...@@ -57,6 +56,7 @@
}, },
"devDependencies": { "devDependencies": {
"@playwright/experimental-ct-react": "^1.26.1", "@playwright/experimental-ct-react": "^1.26.1",
"@svgr/webpack": "^6.5.1",
"@types/d3": "^7.4.0", "@types/d3": "^7.4.0",
"@types/node": "17.0.36", "@types/node": "17.0.36",
"@types/phoenix": "^1.5.4", "@types/phoenix": "^1.5.4",
......
...@@ -2,6 +2,7 @@ import { Grid, GridItem, Text, Icon, Link, Box, Tooltip, Alert } from '@chakra-u ...@@ -2,6 +2,7 @@ import { Grid, GridItem, Text, Icon, Link, Box, Tooltip, Alert } from '@chakra-u
import { useQuery } from '@tanstack/react-query'; import { useQuery } from '@tanstack/react-query';
import BigNumber from 'bignumber.js'; import BigNumber from 'bignumber.js';
import capitalize from 'lodash/capitalize'; import capitalize from 'lodash/capitalize';
import NextLink from 'next/link';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import React from 'react'; import React from 'react';
import { scroller, Element } from 'react-scroll'; import { scroller, Element } from 'react-scroll';
...@@ -109,9 +110,11 @@ const BlockDetails = () => { ...@@ -109,9 +110,11 @@ const BlockDetails = () => {
title="Transactions" title="Transactions"
hint="The number of transactions in the block." hint="The number of transactions in the block."
> >
<Link href={ link('block', { id: router.query.id }, { tab: 'txs' }) }> <NextLink href={ link('block', { id: router.query.id }, { tab: 'txs' }) } passHref>
{ data.tx_count } transactions <Link>
</Link> { data.tx_count } transactions
</Link>
</NextLink>
</DetailsInfoItem> </DetailsInfoItem>
<DetailsInfoItem <DetailsInfoItem
title={ appConfig.network.verificationType === 'validation' ? 'Validated by' : 'Mined by' } title={ appConfig.network.verificationType === 'validation' ? 'Validated by' : 'Mined by' }
......
...@@ -19,12 +19,15 @@ import useChartSize from 'ui/shared/chart/useChartSize'; ...@@ -19,12 +19,15 @@ import useChartSize from 'ui/shared/chart/useChartSize';
import useTimeChartController from 'ui/shared/chart/useTimeChartController'; import useTimeChartController from 'ui/shared/chart/useTimeChartController';
const CHART_MARGIN = { bottom: 20, left: 65, right: 30, top: 10 }; const CHART_MARGIN = { bottom: 20, left: 65, right: 30, top: 10 };
const CHART_OFFSET = {
y: 26, // legend height
};
const EthereumChart = () => { const EthereumChart = () => {
const ref = React.useRef<SVGSVGElement>(null); const ref = React.useRef<SVGSVGElement>(null);
const overlayRef = React.useRef<SVGRectElement>(null); const overlayRef = React.useRef<SVGRectElement>(null);
const { width, height, innerWidth, innerHeight } = useChartSize(ref.current, CHART_MARGIN); const { width, height, innerWidth, innerHeight } = useChartSize(ref.current, CHART_MARGIN, CHART_OFFSET);
const [ range, setRange ] = React.useState<[number, number]>([ 0, Infinity ]); const [ range, setRange ] = React.useState<[number, number]>([ 0, Infinity ]);
const data: TimeChartData = [ const data: TimeChartData = [
......
import { useToken } from '@chakra-ui/react';
import React from 'react';
import ethTxsData from 'data/charts_eth_txs.json';
import ChartLine from 'ui/shared/chart/ChartLine';
import useChartSize from 'ui/shared/chart/useChartSize';
import useTimeChartController from 'ui/shared/chart/useTimeChartController';
import { BlueLinearGradient } from 'ui/shared/chart/utils/gradients';
const CHART_MARGIN = { bottom: 0, left: 0, right: 0, top: 0 };
const DATA = ethTxsData.slice(-30).map((d) => ({ ...d, date: new Date(d.date) }));
const SplineChartExample = () => {
const ref = React.useRef<SVGSVGElement>(null);
const { width, height, innerWidth, innerHeight } = useChartSize(ref.current, CHART_MARGIN);
const color = useToken('colors', 'blue.500');
const { xScale, yScale } = useTimeChartController({
data: [ { items: DATA, name: 'spline', color } ],
width: innerWidth,
height: innerHeight,
});
return (
<svg width={ width || '100%' } height={ height || '100%' } ref={ ref }>
<defs>
<BlueLinearGradient.defs/>
</defs>
<ChartLine
data={ DATA }
xScale={ xScale }
yScale={ yScale }
stroke={ `url(#${ BlueLinearGradient.id })` }
animation="left"
strokeWidth={ 3 }
/>
</svg>
);
};
export default SplineChartExample;
...@@ -2,6 +2,7 @@ import { Box, Heading } from '@chakra-ui/react'; ...@@ -2,6 +2,7 @@ import { Box, Heading } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import EthereumChart from 'ui/charts/EthereumChart'; import EthereumChart from 'ui/charts/EthereumChart';
import SplineChartExample from 'ui/charts/SplineChartExample';
import Page from 'ui/shared/Page/Page'; import Page from 'ui/shared/Page/Page';
import PageTitle from 'ui/shared/Page/PageTitle'; import PageTitle from 'ui/shared/Page/PageTitle';
...@@ -13,6 +14,10 @@ const Graph = () => { ...@@ -13,6 +14,10 @@ const Graph = () => {
<Box w="100%" h="400px"> <Box w="100%" h="400px">
<EthereumChart/> <EthereumChart/>
</Box> </Box>
<Heading as="h2" size="sm" fontWeight="500" mb={ 3 } mt="80px">Ethereum Daily Transactions For Last Month</Heading>
<Box w="240px" h="150px">
<SplineChartExample/>
</Box>
</Page> </Page>
); );
}; };
......
...@@ -38,7 +38,7 @@ const Page = ({ ...@@ -38,7 +38,7 @@ const Page = ({
) : children; ) : children;
return ( return (
<SocketProvider url={ `${ appConfig.api.socket }${ appConfig.api.basePath }/socket/v2/websocket?vsn=2.0.0` }> <SocketProvider url={ `${ appConfig.api.socket }${ appConfig.api.basePath }/socket/v2` }>
<ScrollDirectionContext.Provider value={ directionContext }> <ScrollDirectionContext.Provider value={ directionContext }>
<Flex w="100%" minH="100vh" alignItems="stretch"> <Flex w="100%" minH="100vh" alignItems="stretch">
<NavigationDesktop/> <NavigationDesktop/>
......
...@@ -6,7 +6,7 @@ import type { TimeChartItem } from 'ui/shared/chart/types'; ...@@ -6,7 +6,7 @@ import type { TimeChartItem } from 'ui/shared/chart/types';
interface Props extends React.SVGProps<SVGPathElement> { interface Props extends React.SVGProps<SVGPathElement> {
xScale: d3.ScaleTime<number, number> | d3.ScaleLinear<number, number>; xScale: d3.ScaleTime<number, number> | d3.ScaleLinear<number, number>;
yScale: d3.ScaleTime<number, number> | d3.ScaleLinear<number, number>; yScale: d3.ScaleTime<number, number> | d3.ScaleLinear<number, number>;
color: string; color?: string;
data: Array<TimeChartItem>; data: Array<TimeChartItem>;
disableAnimation?: boolean; disableAnimation?: boolean;
} }
...@@ -28,19 +28,22 @@ const ChartArea = ({ xScale, yScale, color, data, disableAnimation, ...props }: ...@@ -28,19 +28,22 @@ const ChartArea = ({ xScale, yScale, color, data, disableAnimation, ...props }:
const area = d3.area<TimeChartItem>() const area = d3.area<TimeChartItem>()
.x(({ date }) => xScale(date)) .x(({ date }) => xScale(date))
.y1(({ value }) => yScale(value)) .y1(({ value }) => yScale(value))
.y0(() => yScale(yScale.domain()[0])); .y0(() => yScale(yScale.domain()[0]))
.curve(d3.curveNatural);
return area(data) || undefined; return area(data) || undefined;
}, [ xScale, yScale, data ]); }, [ xScale, yScale, data ]);
return ( return (
<> <>
<path ref={ ref } d={ d } fill={ `url(#gradient-${ color })` } opacity={ 0 } { ...props }/> <path ref={ ref } d={ d } fill={ color ? `url(#gradient-${ color })` : 'none' } opacity={ 0 } { ...props }/>
<defs> { color && (
<linearGradient id={ `gradient-${ color }` } x1="0%" x2="0%" y1="0%" y2="100%"> <defs>
<stop offset="0%" stopColor={ color } stopOpacity={ 1 }/> <linearGradient id={ `gradient-${ color }` } x1="0%" x2="0%" y1="0%" y2="100%">
<stop offset="100%" stopColor={ color } stopOpacity={ 0.15 }/> <stop offset="0%" stopColor={ color } stopOpacity={ 0.8 }/>
</linearGradient> <stop offset="100%" stopColor={ color } stopOpacity={ 0.02 }/>
</defs> </linearGradient>
</defs>
) }
</> </>
); );
}; };
......
...@@ -61,7 +61,8 @@ const ChartLine = ({ xScale, yScale, data, animation, ...props }: Props) => { ...@@ -61,7 +61,8 @@ const ChartLine = ({ xScale, yScale, data, animation, ...props }: Props) => {
const line = d3.line<TimeChartItem>() const line = d3.line<TimeChartItem>()
.x((d) => xScale(d.date)) .x((d) => xScale(d.date))
.y((d) => yScale(d.value)); .y((d) => yScale(d.value))
.curve(d3.curveNatural);
return ( return (
<path <path
......
...@@ -10,10 +10,15 @@ export interface ChartMargin { ...@@ -10,10 +10,15 @@ export interface ChartMargin {
left?: number; left?: number;
} }
export interface ChartOffset {
x?: number;
y?: number;
}
export interface TimeChartDataItem { export interface TimeChartDataItem {
items: Array<TimeChartItem>; items: Array<TimeChartItem>;
name: string; name: string;
color: string; color?: string;
} }
export type TimeChartData = Array<TimeChartDataItem>; export type TimeChartData = Array<TimeChartDataItem>;
import _debounce from 'lodash/debounce'; import _debounce from 'lodash/debounce';
import React from 'react'; import React from 'react';
import type { ChartMargin } from 'ui/shared/chart/types'; import type { ChartMargin, ChartOffset } from 'ui/shared/chart/types';
export default function useChartSize(svgEl: SVGSVGElement | null, margin?: ChartMargin) { export default function useChartSize(svgEl: SVGSVGElement | null, margin?: ChartMargin, offsets?: ChartOffset) {
const [ rect, setRect ] = React.useState<{ width: number; height: number}>({ width: 0, height: 0 }); const [ rect, setRect ] = React.useState<{ width: number; height: number}>({ width: 0, height: 0 });
const calculateRect = React.useCallback(() => { const calculateRect = React.useCallback(() => {
...@@ -34,10 +34,10 @@ export default function useChartSize(svgEl: SVGSVGElement | null, margin?: Chart ...@@ -34,10 +34,10 @@ export default function useChartSize(svgEl: SVGSVGElement | null, margin?: Chart
return React.useMemo(() => { return React.useMemo(() => {
return { return {
width: rect.width, width: Math.max(rect.width - (offsets?.x || 0), 0),
height: rect.height, height: Math.max(rect.height - (offsets?.y || 0), 0),
innerWidth: Math.max(rect.width - (margin?.left || 0) - (margin?.right || 0), 0), innerWidth: Math.max(rect.width - (offsets?.x || 0) - (margin?.left || 0) - (margin?.right || 0), 0),
innerHeight: Math.max(rect.height - (margin?.bottom || 0) - (margin?.top || 0), 0), innerHeight: Math.max(rect.height - (offsets?.y || 0) - (margin?.bottom || 0) - (margin?.top || 0), 0),
}; };
}, [ margin?.bottom, margin?.left, margin?.right, margin?.top, rect ]); }, [ margin?.bottom, margin?.left, margin?.right, margin?.top, offsets?.x, offsets?.y, rect.height, rect.width ]);
} }
import React from 'react';
export const BlueLinearGradient = {
id: 'blue-linear-gradient',
defs: () => (
<linearGradient id="blue-linear-gradient">
<stop offset="0%" stopColor="#4299E1"/>
<stop offset="100%" stopColor="#00B5D8"/>
</linearGradient>
),
};
export const RainbowGradient = {
id: 'rainbow-gradient',
defs: () => (
<linearGradient id="rainbow-gradient">
<stop offset="0%" stopColor="rgba(255, 0, 0, 1)"/>
<stop offset="10%" stopColor="rgba(255, 154, 0, 1)"/>
<stop offset="20%" stopColor="rgba(208, 222, 33, 1)"/>
<stop offset="30%" stopColor="rgba(79, 220, 74, 1)"/>
<stop offset="40%" stopColor="rgba(63, 218, 216, 1)"/>
<stop offset="50%" stopColor="rgba(47, 201, 226, 1)"/>
<stop offset="60%" stopColor="rgba(28, 127, 238, 1)"/>
<stop offset="70%" stopColor="rgba(95, 21, 242, 1)"/>
<stop offset="80%" stopColor="rgba(186, 12, 248, 1)"/>
<stop offset="90%" stopColor="rgba(251, 7, 217, 1)"/>
<stop offset="100%" stopColor="rgba(255, 0, 0, 1)"/>
</linearGradient>
),
};
...@@ -6,23 +6,34 @@ import type { PreDefinedNetwork } from 'types/networks'; ...@@ -6,23 +6,34 @@ import type { PreDefinedNetwork } from 'types/networks';
import appConfig from 'configs/app/config'; import appConfig from 'configs/app/config';
import blockscoutLogo from 'icons/logo.svg'; import blockscoutLogo from 'icons/logo.svg';
import artisLogo from 'icons/networks/logos/artis.svg';
import astarLogo from 'icons/networks/logos/astar.svg';
import etcLogo from 'icons/networks/logos/etc.svg';
import ethLogo from 'icons/networks/logos/eth.svg';
import gnosisLogo from 'icons/networks/logos/gnosis.svg';
import luksoLogo from 'icons/networks/logos/lukso.svg';
import poaLogo from 'icons/networks/logos/poa.svg';
import rskLogo from 'icons/networks/logos/rsk.svg';
import shibuyaLogo from 'icons/networks/logos/shibuya.svg';
import shidenLogo from 'icons/networks/logos/shiden.svg';
import sokolLogo from 'icons/networks/logos/sokol.svg';
import link from 'lib/link/link'; import link from 'lib/link/link';
import getDefaultTransitionProps from 'theme/utils/getDefaultTransitionProps'; import getDefaultTransitionProps from 'theme/utils/getDefaultTransitionProps';
// predefined network logos // predefined network logos
const LOGOS: Partial<Record<PreDefinedNetwork, React.FunctionComponent<React.SVGAttributes<SVGElement>>>> = { const LOGOS: Partial<Record<PreDefinedNetwork, React.FunctionComponent<React.SVGAttributes<SVGElement>>>> = {
xdai_mainnet: require('icons/networks/logos/gnosis.svg'), xdai_mainnet: gnosisLogo,
eth_mainnet: require('icons/networks/logos/eth.svg'), eth_mainnet: ethLogo,
etc_mainnet: require('icons/networks/logos/etc.svg'), etc_mainnet: etcLogo,
poa_core: require('icons/networks/logos/poa.svg'), poa_core: poaLogo,
rsk_mainnet: require('icons/networks/logos/rsk.svg'), rsk_mainnet: rskLogo,
xdai_testnet: require('icons/networks/logos/gnosis.svg'), xdai_testnet: gnosisLogo,
poa_sokol: require('icons/networks/logos/sokol.svg'), poa_sokol: sokolLogo,
artis_sigma1: require('icons/networks/logos/artis.svg'), artis_sigma1: artisLogo,
lukso_l14: require('icons/networks/logos/lukso.svg'), lukso_l14: luksoLogo,
astar: require('icons/networks/logos/astar.svg'), astar: astarLogo,
shiden: require('icons/networks/logos/shiden.svg'), shiden: shidenLogo,
shibuya: require('icons/networks/logos/shibuya.svg'), shibuya: shibuyaLogo,
}; };
interface Props { interface Props {
......
...@@ -9,6 +9,7 @@ import DataFetchAlert from 'ui/shared/DataFetchAlert'; ...@@ -9,6 +9,7 @@ import DataFetchAlert from 'ui/shared/DataFetchAlert';
import TxsHeader from './TxsHeader'; import TxsHeader from './TxsHeader';
import TxsListItem from './TxsListItem'; import TxsListItem from './TxsListItem';
import TxsNewItemNotice from './TxsNewItemNotice';
import TxsSkeletonDesktop from './TxsSkeletonDesktop'; import TxsSkeletonDesktop from './TxsSkeletonDesktop';
import TxsSkeletonMobile from './TxsSkeletonMobile'; import TxsSkeletonMobile from './TxsSkeletonMobile';
import TxsTable from './TxsTable'; import TxsTable from './TxsTable';
...@@ -64,7 +65,12 @@ const TxsContent = ({ ...@@ -64,7 +65,12 @@ const TxsContent = ({
return ( return (
<> <>
<Show below="lg" ssr={ false }> <Show below="lg" ssr={ false }>
<Box>{ txs.map(tx => <TxsListItem tx={ tx } key={ tx.hash } showBlockInfo={ showBlockInfo }/>) }</Box> <Box>
<TxsNewItemNotice>
{ ({ content }) => <Box>{ content }</Box> }
</TxsNewItemNotice>
{ txs.map(tx => <TxsListItem tx={ tx } key={ tx.hash } showBlockInfo={ showBlockInfo }/>) }
</Box>
</Show> </Show>
<Hide below="lg" ssr={ false }> <Hide below="lg" ssr={ false }>
<TxsTable txs={ txs } sort={ setSortByField } sorting={ sorting } showBlockInfo={ showBlockInfo } top={ isPaginatorHidden ? 0 : 80 }/> <TxsTable txs={ txs } sort={ setSortByField } sorting={ sorting } showBlockInfo={ showBlockInfo } top={ isPaginatorHidden ? 0 : 80 }/>
......
import { Alert, Spinner, Text, Link, chakra } from '@chakra-ui/react';
import type { NextRouter } from 'next/router';
import { useRouter } from 'next/router';
import React from 'react';
import { ROUTES } from 'lib/link/routes';
import useSocketChannel from 'lib/socket/useSocketChannel';
import useSocketMessage from 'lib/socket/useSocketMessage';
interface InjectedProps {
content: React.ReactNode;
}
interface Props {
children: (props: InjectedProps) => JSX.Element;
className?: string;
}
function getSocketParams(router: NextRouter) {
if (router.pathname === ROUTES.txs.pattern && router.query.tab === 'validated' && !router.query.block_number) {
return { topic: 'transactions:new_transaction' as const, event: 'transaction' as const };
}
if (router.pathname === ROUTES.txs.pattern && router.query.tab === 'pending' && !router.query.block_number) {
return { topic: 'transactions:new_pending_transaction' as const, event: 'pending_transaction' as const };
}
return {};
}
function assertIsNewTxResponse(response: unknown): response is { transaction: number } {
return typeof response === 'object' && response !== null && 'transaction' in response;
}
function assertIsNewPendingTxResponse(response: unknown): response is { pending_transaction: number } {
return typeof response === 'object' && response !== null && 'pending_transaction' in response;
}
const TxsNewItemNotice = ({ children, className }: Props) => {
const router = useRouter();
const [ num, setNum ] = React.useState(0);
const [ socketAlert, setSocketAlert ] = React.useState('');
const { topic, event } = getSocketParams(router);
const handleClick = React.useCallback(() => {
window.location.reload();
}, []);
const handleNewTxMessage = React.useCallback((response: { transaction: number } | { pending_transaction: number } | unknown) => {
if (assertIsNewTxResponse(response)) {
setNum((prev) => prev + response.transaction);
}
if (assertIsNewPendingTxResponse(response)) {
setNum((prev) => prev + response.pending_transaction);
}
}, []);
const handleSocketClose = React.useCallback(() => {
setSocketAlert('Connection is lost. Please click here to load new transactions.');
}, []);
const handleSocketError = React.useCallback(() => {
setSocketAlert('An error has occurred while fetching new transactions. Please click here to refresh the page.');
}, []);
const channel = useSocketChannel({
topic,
onSocketClose: handleSocketClose,
onSocketError: handleSocketError,
isDisabled: !topic,
});
useSocketMessage({
channel,
event,
handler: handleNewTxMessage,
});
if (!topic && !event) {
return null;
}
const content = (() => {
if (socketAlert) {
return (
<Alert
className={ className }
status="warning"
p={ 4 }
borderRadius={ 0 }
onClick={ handleClick }
cursor="pointer"
>
{ socketAlert }
</Alert>
);
}
if (!num) {
return null;
}
return (
<Alert className={ className } status="warning" p={ 4 } fontWeight={ 400 }>
<Spinner size="sm" mr={ 3 }/>
<Text as="span" whiteSpace="pre">+ { num } new transaction{ num > 1 ? 's' : '' }. </Text>
<Link onClick={ handleClick }>Show in list</Link>
</Alert>
);
})();
return children({ content });
};
export default chakra(TxsNewItemNotice);
...@@ -15,7 +15,7 @@ const TxsTab = ({ tab }: Props) => { ...@@ -15,7 +15,7 @@ const TxsTab = ({ tab }: Props) => {
queryName={ QueryKeys.txsValidate } queryName={ QueryKeys.txsValidate }
showDescription showDescription
stateFilter="validated" stateFilter="validated"
apiPath="/api/transactions" apiPath="/node-api/transactions"
/> />
); );
} }
...@@ -24,7 +24,7 @@ const TxsTab = ({ tab }: Props) => { ...@@ -24,7 +24,7 @@ const TxsTab = ({ tab }: Props) => {
<TxsContent <TxsContent
queryName={ QueryKeys.txsPending } queryName={ QueryKeys.txsPending }
stateFilter="pending" stateFilter="pending"
apiPath="/api/transactions" apiPath="/node-api/transactions"
showBlockInfo={ false } showBlockInfo={ false }
/> />
); );
......
import { Link, Table, Tbody, Tr, Th, Icon } from '@chakra-ui/react'; import { Link, Table, Tbody, Tr, Th, Td, Icon } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { Transaction } from 'types/api/transaction'; import type { Transaction } from 'types/api/transaction';
...@@ -8,6 +8,7 @@ import appConfig from 'configs/app/config'; ...@@ -8,6 +8,7 @@ import appConfig from 'configs/app/config';
import rightArrowIcon from 'icons/arrows/east.svg'; import rightArrowIcon from 'icons/arrows/east.svg';
import TheadSticky from 'ui/shared/TheadSticky'; import TheadSticky from 'ui/shared/TheadSticky';
import TxsNewItemNotice from './TxsNewItemNotice';
import TxsTableItem from './TxsTableItem'; import TxsTableItem from './TxsTableItem';
type Props = { type Props = {
...@@ -48,6 +49,9 @@ const TxsTable = ({ txs, sort, sorting, top, showBlockInfo }: Props) => { ...@@ -48,6 +49,9 @@ const TxsTable = ({ txs, sort, sorting, top, showBlockInfo }: Props) => {
</Tr> </Tr>
</TheadSticky> </TheadSticky>
<Tbody> <Tbody>
<TxsNewItemNotice borderRadius={ 0 }>
{ ({ content }) => <Tr><Td colSpan={ 10 } p={ 0 }>{ content }</Td></Tr> }
</TxsNewItemNotice>
{ txs.map((item) => ( { txs.map((item) => (
<TxsTableItem <TxsTableItem
key={ item.hash } key={ item.hash }
......
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment