diff --git a/.credo.exs b/.credo.exs index 9f69aecd10..ba4d7feb49 100644 --- a/.credo.exs +++ b/.credo.exs @@ -89,7 +89,7 @@ # or the `schema` macro in Ecto schemas to trigger DuplicatedCode, just # set the `excluded_macros` parameter to `[:schema, :setup, :test]`. # - {Credo.Check.Design.DuplicatedCode, false}, + {Credo.Check.Design.DuplicatedCode, excluded_macros: [], mass_threshold: 80}, # You can also customize the exit_status of each check. # If you don't want TODO comments to cause `mix credo` to fail, just diff --git a/.dialyzer-ignore b/.dialyzer-ignore index de105bbd51..86584c2cb5 100644 --- a/.dialyzer-ignore +++ b/.dialyzer-ignore @@ -11,3 +11,5 @@ apps/explorer/lib/explorer/repo/prometheus_logger.ex:8: The call 'Elixir.System' apps/block_scout_web/lib/block_scout_web/views/layout_view.ex:175: The call 'Elixir.Poison.Parser':'parse!'(any(),#{'keys':='atoms!'}) will never return since the success typing is (binary() | maybe_improper_list(binary() | maybe_improper_list(any(),binary() | []) | byte(),binary() | []),[{atom(),_}]) -> 'false' | 'nil' | 'true' | binary() | ['false' | 'nil' | 'true' | binary() | [any()] | number() | map()] | number() | map() and the contract is (iodata(),'Elixir.Keyword':t()) -> t() apps/explorer/lib/explorer/smart_contract/publisher_worker.ex:6: The pattern 'false' can never match the type 'true' apps/explorer/lib/explorer/smart_contract/publisher_worker.ex:6: The test 5 == 'infinity' can never evaluate to 'true' +lib/block_scout_web/router.ex:1 +lib/phoenix/router.ex:324 \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 74347d4e75..f006769dbf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,24 +1,83 @@ ## Current ### Features +- [#2561](https://github.com/poanetwork/blockscout/pull/2561) - Add token's type to the response of tokenlist method +- [#2499](https://github.com/poanetwork/blockscout/pull/2499) - import emission reward ranges +- [#2497](https://github.com/poanetwork/blockscout/pull/2497) - Add generic Ordered Cache behaviour and implementation + +### Fixes +- [#2572](https://github.com/poanetwork/blockscout/pull/2572) - Ease non-critical css +- [#2570](https://github.com/poanetwork/blockscout/pull/2570) - Network icons preload +- [#2569](https://github.com/poanetwork/blockscout/pull/2569) - do not fetch emission rewards for transactions csv exporter +- [#2568](https://github.com/poanetwork/blockscout/pull/2568) - filter pending token transfers +- [#2564](https://github.com/poanetwork/blockscout/pull/2564) - fix first page button for uncles and reorgs +- [#2563](https://github.com/poanetwork/blockscout/pull/2563) - Fix view less transfers button +- [#2538](https://github.com/poanetwork/blockscout/pull/2538) - fetch the last not empty coin balance records + +### Chore +- [#2566](https://github.com/poanetwork/blockscout/pull/2566) - upgrade absinthe phoenix + + +## 2.0.3-beta + +### Features +- [#2433](https://github.com/poanetwork/blockscout/pull/2433) - Add a functionality to try Eth RPC methods in the documentation +- [#2529](https://github.com/poanetwork/blockscout/pull/2529) - show both eth value and token transfers on transaction overview page +- [#2376](https://github.com/poanetwork/blockscout/pull/2376) - Split API and WebApp routes - [#2477](https://github.com/poanetwork/blockscout/pull/2477) - aggregate token transfers on transaction page - [#2458](https://github.com/poanetwork/blockscout/pull/2458) - Add LAST_BLOCK var to add ability indexing in the range of blocks - [#2456](https://github.com/poanetwork/blockscout/pull/2456) - fetch pending transactions for geth +- [#2403](https://github.com/poanetwork/blockscout/pull/2403) - Return gasPrice field at the result of gettxinfo method ### Fixes +- [#2562](https://github.com/poanetwork/blockscout/pull/2562) - Fix dark theme flickering +- [#2560](https://github.com/poanetwork/blockscout/pull/2560) - fix slash before not empty path in docs +- [#2559](https://github.com/poanetwork/blockscout/pull/2559) - fix rsk total supply for empty exchange rate +- [#2553](https://github.com/poanetwork/blockscout/pull/2553) - Dark theme import to the end of sass +- [#2550](https://github.com/poanetwork/blockscout/pull/2550) - correctly encode decimal values for frontend +- [#2549](https://github.com/poanetwork/blockscout/pull/2549) - Fix wrong colour of tooltip +- [#2548](https://github.com/poanetwork/blockscout/pull/2548) - CSS preload support in Firefox +- [#2547](https://github.com/poanetwork/blockscout/pull/2547) - do not show eth value if it's zero on the transaction overview page +- [#2543](https://github.com/poanetwork/blockscout/pull/2543) - do not hide search input during logs search +- [#2524](https://github.com/poanetwork/blockscout/pull/2524) - fix dark theme validator data styles +- [#2532](https://github.com/poanetwork/blockscout/pull/2532) - don't show empty token transfers on the transaction overview page +- [#2528](https://github.com/poanetwork/blockscout/pull/2528) - fix coin history chart data +- [#2520](https://github.com/poanetwork/blockscout/pull/2520) - Hide loading message when fetching is failed +- [#2523](https://github.com/poanetwork/blockscout/pull/2523) - Avoid importing internal_transactions of pending transactions +- [#2519](https://github.com/poanetwork/blockscout/pull/2519) - enable `First` page button in pagination +- [#2517](https://github.com/poanetwork/blockscout/pull/2517) - remove duplicate indexes +- [#2515](https://github.com/poanetwork/blockscout/pull/2515) - do not aggregate NFT token transfers +- [#2514](https://github.com/poanetwork/blockscout/pull/2514) - Isolating of staking dapp css && extracting of non-critical css +- [#2512](https://github.com/poanetwork/blockscout/pull/2512) - alert link fix +- [#2509](https://github.com/poanetwork/blockscout/pull/2509) - value-ticker gaps fix +- [#2508](https://github.com/poanetwork/blockscout/pull/2508) - logs view columns fix +- [#2506](https://github.com/poanetwork/blockscout/pull/2506) - fix two active tab in the top menu +- [#2503](https://github.com/poanetwork/blockscout/pull/2503) - Mitigate autocompletion library influence to page loading performance +- [#2502](https://github.com/poanetwork/blockscout/pull/2502) - increase reward task timeout - [#2463](https://github.com/poanetwork/blockscout/pull/2463) - dark theme fixes - [#2496](https://github.com/poanetwork/blockscout/pull/2496) - fix docker build - [#2495](https://github.com/poanetwork/blockscout/pull/2495) - fix logs for indexed chain - [#2459](https://github.com/poanetwork/blockscout/pull/2459) - fix top addresses query - [#2425](https://github.com/poanetwork/blockscout/pull/2425) - Force to show address view for checksummed address even if it is not in DB +- [#2551](https://github.com/poanetwork/blockscout/pull/2551) - Correctly handle dynamically created Bootstrap tooltips ### Chore +- [#2554](https://github.com/poanetwork/blockscout/pull/2554) - remove extra slash for endpoint url in docs +- [#2552](https://github.com/poanetwork/blockscout/pull/2552) - remove brackets for token holders percentage +- [#2507](https://github.com/poanetwork/blockscout/pull/2507) - update minor version of ecto, ex_machina, phoenix_live_reload +- [#2516](https://github.com/poanetwork/blockscout/pull/2516) - update absinthe plug from fork +- [#2473](https://github.com/poanetwork/blockscout/pull/2473) - get rid of cldr warnings - [#2402](https://github.com/poanetwork/blockscout/pull/2402) - bump otp version to 22.0 +- [#2492](https://github.com/poanetwork/blockscout/pull/2492) - hide decoded row if event is not decoded +- [#2490](https://github.com/poanetwork/blockscout/pull/2490) - enable credo duplicated code check - [#2432](https://github.com/poanetwork/blockscout/pull/2432) - bump credo version - [#2457](https://github.com/poanetwork/blockscout/pull/2457) - update mix.lock - [#2435](https://github.com/poanetwork/blockscout/pull/2435) - Replace deprecated extract-text-webpack-plugin with mini-css-extract-plugin - [#2450](https://github.com/poanetwork/blockscout/pull/2450) - Fix clearance of logs and node_modules folders in clearing script - [#2434](https://github.com/poanetwork/blockscout/pull/2434) - get rid of timex warnings +- [#2402](https://github.com/poanetwork/blockscout/pull/2402) - bump otp version to 22.0 +- [#2373](https://github.com/poanetwork/blockscout/pull/2373) - Add script to validate internal_transactions constraint for large DBs + ## 2.0.2-beta @@ -164,6 +223,7 @@ - [#2255](https://github.com/poanetwork/blockscout/pull/2255) - upgrade elixir version to 1.9.0 - [#2256](https://github.com/poanetwork/blockscout/pull/2256) - use the latest version of chromedriver + ## 2.0.0-beta ### Features @@ -246,6 +306,7 @@ - [#2055](https://github.com/poanetwork/blockscout/pull/2055) - Increase timeout for geth indexers - [#2069](https://github.com/poanetwork/blockscout/pull/2069) - Docsify integration: static docs page generation + ## 1.3.15-beta ### Features @@ -275,6 +336,7 @@ - [#1892](https://github.com/poanetwork/blockscout/pull/1892) - Remove temporary worker modules + ## 1.3.13-beta ### Features @@ -287,10 +349,12 @@ - [#1881](https://github.com/poanetwork/blockscout/pull/1881) - fix: store solc versions locally for performance - [#1898](https://github.com/poanetwork/blockscout/pull/1898) - check if the constructor has arguments before verifying constructor arguments + ## 1.3.12-beta Reverting of synchronous block counter, implemented in #1848 + ## 1.3.11-beta ### Features @@ -312,6 +376,7 @@ Reverting of synchronous block counter, implemented in #1848 - [#1814](https://github.com/poanetwork/blockscout/pull/1814) - Clear build artefacts script - [#1837](https://github.com/poanetwork/blockscout/pull/1837) - Add -f flag to clear_build.sh script delete static folder + ## 1.3.10-beta ### Features @@ -422,6 +487,7 @@ Reverting of synchronous block counter, implemented in #1848 - [#1610](https://github.com/poanetwork/blockscout/pull/1610) - Add PIRL to Readme + ## 1.3.6-beta ### Features diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md index 4de2ef6baf..3075397e8d 100644 --- a/ISSUE_TEMPLATE.md +++ b/ISSUE_TEMPLATE.md @@ -4,6 +4,7 @@ * Elixir & Erlang/OTP versions (`elixir -version`): * Operating System: +* Blockscout Version/branch: ### Steps to reproduce diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md index 09dbdd6b2f..4f889b7304 100644 --- a/PULL_REQUEST_TEMPLATE.md +++ b/PULL_REQUEST_TEMPLATE.md @@ -35,4 +35,5 @@ - [ ] If I added new functionality, I added tests covering it. - [ ] If I fixed a bug, I added a regression test to prevent the bug from silently reappearing again. - [ ] I checked whether I should update the docs and did so if necessary - - [ ] If I added/changed/removed ENV var, I should update the list of env vars in https://github.com/poanetwork/blockscout/blob/master/docs/env-variables.md to reflect changes in the table here https://poanetwork.github.io/blockscout/#/env-variables?id=blockscout-env-variables + - [ ] If I added/changed/removed ENV var, I should update the list of env vars in https://github.com/poanetwork/blockscout/blob/master/docs/env-variables.md to reflect changes in the table here https://poanetwork.github.io/blockscout/#/env-variables?id=blockscout-env-variables. I've set `master` in the `Version` column. + - [ ] If I add new indices into DB, I checked, that they don't redundant with PGHero or other tools diff --git a/apps/block_scout_web/.sobelow-conf b/apps/block_scout_web/.sobelow-conf index 73cbb5ae98..51d8b758cb 100644 --- a/apps/block_scout_web/.sobelow-conf +++ b/apps/block_scout_web/.sobelow-conf @@ -5,5 +5,5 @@ router: "lib/block_scout_web/router.ex", exit: "low", format: "compact", - ignore: ["Config.Headers"] + ignore: ["Config.Headers", "Config.CSWH"] ] diff --git a/apps/block_scout_web/assets/__tests__/lib/async_listing_load.js b/apps/block_scout_web/assets/__tests__/lib/async_listing_load.js index 6e86d8a6b5..adae2d6033 100644 --- a/apps/block_scout_web/assets/__tests__/lib/async_listing_load.js +++ b/apps/block_scout_web/assets/__tests__/lib/async_listing_load.js @@ -46,14 +46,12 @@ describe('REQUEST_ERROR', () => { describe('FINISH_REQUEST', () => { test('sets loading status to false', () => { const state = Object.assign({}, asyncInitialState, { - loading: true, - loadingFirstPage: true + loading: true }) const action = { type: 'FINISH_REQUEST' } const output = asyncReducer(state, action) expect(output.loading).toEqual(false) - expect(output.loadingFirstPage).toEqual(false) }) }) diff --git a/apps/block_scout_web/assets/css/_images-preload.scss b/apps/block_scout_web/assets/css/_images-preload.scss new file mode 100644 index 0000000000..8356a8f90e --- /dev/null +++ b/apps/block_scout_web/assets/css/_images-preload.scss @@ -0,0 +1,15 @@ +body:after { + position:absolute; width:0; height:0; overflow:hidden; z-index:-1; + content: + url(/images/network-selector-icons/callisto-mainnet.png) + url(/images/network-selector-icons/ethereum-mainnet.png) + url(/images/network-selector-icons/ethereum-classic.png) + url(/images/network-selector-icons/goerli-testnet.png) + url(/images/network-selector-icons/kovan-testnet.png) + url(/images/network-selector-icons/poa-core.png) + url(/images/network-selector-icons/poa-sokol.png) + url(/images/network-selector-icons/rinkeby-testnet.png) + url(/images/network-selector-icons/rsk-mainnet.png) + url(/images/network-selector-icons/ropsten-testnet.png) + url(/images/network-selector-icons/xdai-chain.png) +}; \ No newline at end of file diff --git a/apps/block_scout_web/assets/css/app.scss b/apps/block_scout_web/assets/css/app.scss index e91df91740..508bb5335b 100644 --- a/apps/block_scout_web/assets/css/app.scss +++ b/apps/block_scout_web/assets/css/app.scss @@ -24,7 +24,6 @@ $fa-font-path: "~@fortawesome/fontawesome-free/webfonts"; @import "node_modules/bootstrap/scss/reboot"; @import "node_modules/bootstrap/scss/grid"; @import "node_modules/bootstrap/scss/code"; -@import "node_modules/bootstrap/scss/modal"; @import "node_modules/bootstrap/scss/close"; @import "node_modules/bootstrap/scss/buttons"; @import "node_modules/bootstrap/scss/forms"; @@ -50,7 +49,6 @@ $fa-font-path: "~@fortawesome/fontawesome-free/webfonts"; @import "node_modules/bootstrap/scss/navbar"; @import "node_modules/bootstrap/scss/pagination"; @import "node_modules/bootstrap/scss/tables"; -@import "node_modules/bootstrap/scss/tooltip"; @import "node_modules/bootstrap/scss/transitions"; // Code highlight @@ -62,6 +60,7 @@ $fa-font-path: "~@fortawesome/fontawesome-free/webfonts"; // Custom SCSS @import "layout"; @import "typography"; +@import "images-preload"; @import "code"; @import "helpers"; @import "elements"; @@ -75,8 +74,8 @@ $fa-font-path: "~@fortawesome/fontawesome-free/webfonts"; @import "components/filter"; @import "components/button"; @import "components/table"; -@import "components/qr-code"; @import "components/navbar"; +@import "components/alerts"; @import "components/animations"; @import "components/card"; @import "components/tile"; @@ -93,41 +92,28 @@ $fa-font-path: "~@fortawesome/fontawesome-free/webfonts"; @import "components/transaction-input"; @import "components/coin-balance-tile"; @import "components/highlight"; -@import "components/copy_icon"; @import "components/btn_full"; @import "components/btn_line"; -@import "components/stakes"; @import "components/check"; +@import "components/stakes_variables"; @import "components/stakes_table"; -@import "components/i_tooltip"; -@import "components/check_tooltip"; -@import "components/tooltip"; -@import "components/progress_from_to"; -@import "components/stakes_empty_content"; -@import "components/stakes_btn_remove_pool"; -@import "components/modal"; -@import "components/modal_validator_info"; @import "components/form"; -@import "components/stakes_progress"; -@import "components/modal_status"; -@import "components/modal_bottom_disclaimer"; -@import "components/modal_become_candidate"; -@import "components/modal_stake"; @import "components/btn_copy"; @import "components/btn_qr"; @import "components/btn_address_card"; @import "components/btn_dropdown_line"; @import "components/transaction"; @import "components/api"; -@import "components/alerts"; @import "components/verify_other_explorers"; @import "components/errors"; @import "components/log-search"; @import "components/radio"; +@import "components/modal_variables"; @import "components/network-selector"; @import "components/new_smart_contract"; @import "components/radio_big"; @import "components/btn_no_border"; + @import "theme/dark-theme"; :export { diff --git a/apps/block_scout_web/assets/css/components/_api.scss b/apps/block_scout_web/assets/css/components/_api.scss index 820e1e8f01..6f46956780 100644 --- a/apps/block_scout_web/assets/css/components/_api.scss +++ b/apps/block_scout_web/assets/css/components/_api.scss @@ -116,6 +116,10 @@ $api-doc-list-item-view-more-color: $api-doc-list-item-title-color !default; margin: 0; } +.api-doc-list-item-description { + width: 100% +} + .api-doc-list-item-controls { display: flex; flex-direction: column; diff --git a/apps/block_scout_web/assets/css/components/_button.scss b/apps/block_scout_web/assets/css/components/_button.scss index d6a9e7b020..53bca7a83a 100644 --- a/apps/block_scout_web/assets/css/components/_button.scss +++ b/apps/block_scout_web/assets/css/components/_button.scss @@ -23,6 +23,7 @@ $button-secondary-color: $secondary !default; background-color: darken($button-primary-color, 10%); border-color: darken($button-primary-color, 10%); color: #fff; + outline: none !important; text-decoration: none; } diff --git a/apps/block_scout_web/assets/css/components/_form.scss b/apps/block_scout_web/assets/css/components/_form.scss index 5346afcb7c..4b13065a05 100644 --- a/apps/block_scout_web/assets/css/components/_form.scss +++ b/apps/block_scout_web/assets/css/components/_form.scss @@ -9,6 +9,11 @@ $form-control-border-color: #e2e5ec !default; border-radius: 4px; } + &:focus { + border-color: $secondary; + box-shadow: none; + } + &.n-b-r { border-right: none; } diff --git a/apps/block_scout_web/assets/css/components/_modal.scss b/apps/block_scout_web/assets/css/components/_modal.scss index bc9e16c875..4e70be687c 100644 --- a/apps/block_scout_web/assets/css/components/_modal.scss +++ b/apps/block_scout_web/assets/css/components/_modal.scss @@ -1,9 +1,3 @@ -$modal-overlay-color: rgba($primary, 0.9) !default; -$modal-horizontal-padding: 30px !default; -$modal-vertical-padding: 25px !default; -$modal-border-radius: 10px !default; -$modal-gray-background: #f6f7f9 !default; - .modal-backdrop { background-color: $modal-overlay-color; diff --git a/apps/block_scout_web/assets/css/components/_modal_variables.scss b/apps/block_scout_web/assets/css/components/_modal_variables.scss new file mode 100644 index 0000000000..92e2a94a33 --- /dev/null +++ b/apps/block_scout_web/assets/css/components/_modal_variables.scss @@ -0,0 +1,5 @@ +$modal-overlay-color: rgba($primary, 0.9) !default; +$modal-horizontal-padding: 30px !default; +$modal-vertical-padding: 25px !default; +$modal-border-radius: 10px !default; +$modal-gray-background: #f6f7f9 !default; \ No newline at end of file diff --git a/apps/block_scout_web/assets/css/components/_network-selector.scss b/apps/block_scout_web/assets/css/components/_network-selector.scss index e8b5f72e78..7f2a4c020d 100644 --- a/apps/block_scout_web/assets/css/components/_network-selector.scss +++ b/apps/block_scout_web/assets/css/components/_network-selector.scss @@ -243,6 +243,40 @@ $network-selector-item-icon-dimensions: 30px !default; height: $network-selector-item-icon-dimensions; margin: 0 15px 0 0; width: $network-selector-item-icon-dimensions; + + &-callisto-mainnet { + background-image: url(/images/network-selector-icons/callisto-mainnet.png) + } + &-ethereum-mainnet { + background-image: url(/images/network-selector-icons/ethereum-mainnet.png) + } + &-ethereum-classic { + background-image: url(/images/network-selector-icons/ethereum-classic.png) + } + &-goerli-testnet { + background-image: url(/images/network-selector-icons/goerli-testnet.png) + } + &-kovan-testnet { + background-image: url(/images/network-selector-icons/kovan-testnet.png) + } + &-poa-core { + background-image: url(/images/network-selector-icons/poa-core.png) + } + &-poa-sokol { + background-image: url(/images/network-selector-icons/poa-sokol.png) + } + &-rinkeby-testnet { + background-image: url(/images/network-selector-icons/rinkeby-testnet.png) + } + &-rsk-mainnet { + background-image: url(/images/network-selector-icons/rsk-mainnet.png) + } + &-ropsten-testnet { + background-image: url(/images/network-selector-icons/ropsten-testnet.png) + } + &-xdai-chain { + background-image: url(/images/network-selector-icons/xdai-chain.png) + } } .network-selector-item-title { diff --git a/apps/block_scout_web/assets/css/components/_stakes_variables.scss b/apps/block_scout_web/assets/css/components/_stakes_variables.scss new file mode 100644 index 0000000000..4c23ea421b --- /dev/null +++ b/apps/block_scout_web/assets/css/components/_stakes_variables.scss @@ -0,0 +1,2 @@ +$stakes-banned-background: #fff3f7 !default; +$stakes-banned-color: #ff7986 !default; \ No newline at end of file diff --git a/apps/block_scout_web/assets/css/components/_copy_icon.scss b/apps/block_scout_web/assets/css/components/stakes/_copy_icon.scss similarity index 100% rename from apps/block_scout_web/assets/css/components/_copy_icon.scss rename to apps/block_scout_web/assets/css/components/stakes/_copy_icon.scss diff --git a/apps/block_scout_web/assets/css/components/_modal_become_candidate.scss b/apps/block_scout_web/assets/css/components/stakes/_modal_become_candidate.scss similarity index 100% rename from apps/block_scout_web/assets/css/components/_modal_become_candidate.scss rename to apps/block_scout_web/assets/css/components/stakes/_modal_become_candidate.scss diff --git a/apps/block_scout_web/assets/css/components/_modal_bottom_disclaimer.scss b/apps/block_scout_web/assets/css/components/stakes/_modal_bottom_disclaimer.scss similarity index 100% rename from apps/block_scout_web/assets/css/components/_modal_bottom_disclaimer.scss rename to apps/block_scout_web/assets/css/components/stakes/_modal_bottom_disclaimer.scss diff --git a/apps/block_scout_web/assets/css/components/_modal_stake.scss b/apps/block_scout_web/assets/css/components/stakes/_modal_stake.scss similarity index 100% rename from apps/block_scout_web/assets/css/components/_modal_stake.scss rename to apps/block_scout_web/assets/css/components/stakes/_modal_stake.scss diff --git a/apps/block_scout_web/assets/css/components/_modal_validator_info.scss b/apps/block_scout_web/assets/css/components/stakes/_modal_validator_info.scss similarity index 100% rename from apps/block_scout_web/assets/css/components/_modal_validator_info.scss rename to apps/block_scout_web/assets/css/components/stakes/_modal_validator_info.scss diff --git a/apps/block_scout_web/assets/css/components/_progress_from_to.scss b/apps/block_scout_web/assets/css/components/stakes/_progress_from_to.scss similarity index 100% rename from apps/block_scout_web/assets/css/components/_progress_from_to.scss rename to apps/block_scout_web/assets/css/components/stakes/_progress_from_to.scss diff --git a/apps/block_scout_web/assets/css/components/_stakes.scss b/apps/block_scout_web/assets/css/components/stakes/_stakes.scss similarity index 96% rename from apps/block_scout_web/assets/css/components/_stakes.scss rename to apps/block_scout_web/assets/css/components/stakes/_stakes.scss index 87271bc983..c6335ec066 100644 --- a/apps/block_scout_web/assets/css/components/_stakes.scss +++ b/apps/block_scout_web/assets/css/components/stakes/_stakes.scss @@ -1,8 +1,6 @@ $stakes-dashboard-copy-icon-color: $copy-icon-color !default; $stakes-address-color: $primary !default; $stakes-control-color: $primary !default; -$stakes-banned-color: #ff7986 !default; -$stakes-banned-background: #fff3f7 !default; $stakes-stats-item-color: #fff !default; $stakes-stats-item-border-color: #fff !default; diff --git a/apps/block_scout_web/assets/css/components/_stakes_btn_remove_pool.scss b/apps/block_scout_web/assets/css/components/stakes/_stakes_btn_remove_pool.scss similarity index 100% rename from apps/block_scout_web/assets/css/components/_stakes_btn_remove_pool.scss rename to apps/block_scout_web/assets/css/components/stakes/_stakes_btn_remove_pool.scss diff --git a/apps/block_scout_web/assets/css/components/_stakes_empty_content.scss b/apps/block_scout_web/assets/css/components/stakes/_stakes_empty_content.scss similarity index 100% rename from apps/block_scout_web/assets/css/components/_stakes_empty_content.scss rename to apps/block_scout_web/assets/css/components/stakes/_stakes_empty_content.scss diff --git a/apps/block_scout_web/assets/css/components/_stakes_progress.scss b/apps/block_scout_web/assets/css/components/stakes/_stakes_progress.scss similarity index 100% rename from apps/block_scout_web/assets/css/components/_stakes_progress.scss rename to apps/block_scout_web/assets/css/components/stakes/_stakes_progress.scss diff --git a/apps/block_scout_web/assets/css/non-critical.scss b/apps/block_scout_web/assets/css/non-critical.scss new file mode 100644 index 0000000000..826f349d31 --- /dev/null +++ b/apps/block_scout_web/assets/css/non-critical.scss @@ -0,0 +1,17 @@ +// Bootstrap Core CSS +@import "node_modules/bootstrap/scss/functions"; +@import "node_modules/bootstrap/scss/mixins"; + +@import "theme/variables-non-critical"; + +@import "node_modules/bootstrap/scss/modal"; +@import "node_modules/bootstrap/scss/tooltip"; + +@import "components/i_tooltip"; +@import "components/check_tooltip"; +@import "components/tooltip"; + +@import "components/qr-code"; +@import "components/modal_variables"; +@import "components/modal"; +@import "components/modal_status"; \ No newline at end of file diff --git a/apps/block_scout_web/assets/css/stakes.scss b/apps/block_scout_web/assets/css/stakes.scss new file mode 100644 index 0000000000..77ada07b46 --- /dev/null +++ b/apps/block_scout_web/assets/css/stakes.scss @@ -0,0 +1,20 @@ +@import "./mixins"; + +// Bootstrap Core CSS +@import "node_modules/bootstrap/scss/functions"; +@import "node_modules/bootstrap/scss/mixins"; + +@import "theme/variables"; + +@import "components/stakes_variables"; +@import "components/stakes/copy_icon"; +@import "components/stakes/stakes"; +@import "components/stakes/progress_from_to"; +@import "components/stakes/stakes_empty_content"; +@import "components/stakes/stakes_btn_remove_pool"; +@import "components/modal_variables"; +@import "components/stakes/stakes_progress"; +@import "components/stakes/modal_stake"; +@import "components/stakes/modal_become_candidate"; +@import "components/stakes/modal_validator_info"; +@import "components/stakes/modal_bottom_disclaimer"; \ No newline at end of file diff --git a/apps/block_scout_web/assets/css/theme/_base_variables.scss b/apps/block_scout_web/assets/css/theme/_base_variables.scss index 2238327bd4..b4cf443f05 100644 --- a/apps/block_scout_web/assets/css/theme/_base_variables.scss +++ b/apps/block_scout_web/assets/css/theme/_base_variables.scss @@ -359,7 +359,7 @@ $input-btn-padding-y: 0.375rem !default; $input-btn-padding-x: 0.75rem !default; $input-btn-line-height: $line-height-base !default; -$input-btn-focus-width: 0.2rem !default; +$input-btn-focus-width: 1px !default; $input-btn-focus-color: rgba($component-active-bg, 0.25) !default; $input-btn-focus-box-shadow: 0 0 0 $input-btn-focus-width $input-btn-focus-color !default; diff --git a/apps/block_scout_web/assets/css/theme/_dai_variables-non-critical.scss b/apps/block_scout_web/assets/css/theme/_dai_variables-non-critical.scss new file mode 100644 index 0000000000..d9f670f980 --- /dev/null +++ b/apps/block_scout_web/assets/css/theme/_dai_variables-non-critical.scss @@ -0,0 +1,7 @@ +// general +$primary: #17314f; +$secondary: #15bba6; +$tertiary: #93d7ff; +$additional-font: #fff; + +$btn-line-color: $secondary; // button border and font color && hover bg color \ No newline at end of file diff --git a/apps/block_scout_web/assets/css/theme/_dai_variables.scss b/apps/block_scout_web/assets/css/theme/_dai_variables.scss index 0c09660240..6722369c7f 100644 --- a/apps/block_scout_web/assets/css/theme/_dai_variables.scss +++ b/apps/block_scout_web/assets/css/theme/_dai_variables.scss @@ -38,6 +38,7 @@ $btn-line-bg: #fff; // button bg $btn-line-color: $secondary; // button border and font color && hover bg color $btn-copy-color: $secondary; // btn copy $btn-qr-color: $secondary; // btn qr-code +$btn-address-card-icon-color: $secondary; // btn address color //links & tile $tile-body-a-color: $secondary; diff --git a/apps/block_scout_web/assets/css/theme/_dark-theme.scss b/apps/block_scout_web/assets/css/theme/_dark-theme.scss index c0adb0a84b..dd1ee83d96 100644 --- a/apps/block_scout_web/assets/css/theme/_dark-theme.scss +++ b/apps/block_scout_web/assets/css/theme/_dark-theme.scss @@ -234,7 +234,7 @@ $labels-dark: #8a8dba; // header nav, labels } } - .btn-copy-icon, .btn-qr-icon { + .btn-copy-icon, .btn-qr-icon, .btn-address-card-icon { border-color: $dark-primary; path { fill: $dark-primary; @@ -578,9 +578,41 @@ $labels-dark: #8a8dba; // header nav, labels } } } - .verify-other-explorers-cell { - .exp-logo { - color: #333 !important; + + #explorersModal { + .modal-title { + color: #fff; + } + + .text-muted { + color: $labels-dark; + } + + .modal-footer { + border-top-color: darken($labels-dark, 30); + } + + .modal-content { + background-color: $dark-light-bg; + + .btn-primary { + background-color: $dark-primary; + border-color: $dark-primary; + &:hover { + background-color: $dark-primary; + border-color: $dark-primary; + } + } + } + + .verify-other-explorers-cell { + .exp-logo { + color: #fff; + } + } + + .close { + color: #fff; } } @@ -680,4 +712,70 @@ $labels-dark: #8a8dba; // header nav, labels color: #3f436b !important; border-right-color: #3f436b !important; } + + // 'text dark' label + .text-dark { + color: #fff; + } + + // validator info + #validatorModal { + .modal-title { + color: #fff; + } + + .text-muted { + color: $labels-dark; + } + + .modal-footer { + border-top-color: darken($labels-dark, 30); + } + + .modal-content { + background-color: $dark-light-bg; + + .btn-primary { + background-color: $dark-primary; + border-color: $dark-primary; + &:hover { + background-color: $dark-primary; + border-color: $dark-primary; + } + } + } + + .close { + color: #fff; + } + } + + // alerts + .alert-link { + color: $labels-dark; + } + + .alert-danger { + background-color: $dark-light; + border-color: $dark-light; + .alert-link { + color: $alert-danger-color; + } + } + + .tile .alert { + background: rgba(#000, .1); + } + + // primary buttons + .btn-full-primary, .button-primary { + background: $dark-primary; + border-color: $dark-primary; + color: #fff; + &:hover { + background: darken($dark-primary, 6); + border-color: darken($dark-primary, 6); + color: #fff; + } + } } \ No newline at end of file diff --git a/apps/block_scout_web/assets/css/theme/_ether1_variables-non-critical.scss b/apps/block_scout_web/assets/css/theme/_ether1_variables-non-critical.scss new file mode 100644 index 0000000000..f7bf90f4fd --- /dev/null +++ b/apps/block_scout_web/assets/css/theme/_ether1_variables-non-critical.scss @@ -0,0 +1,7 @@ +// general +$primary: #840032; +$secondary: #343434; +$tertiary: #7f7f7f; +$additional-font: #ff95db; + +$btn-line-color: #4b021e; // button border and font color && hover bg color \ No newline at end of file diff --git a/apps/block_scout_web/assets/css/theme/_ethereum_classic_variables-non-critical.scss b/apps/block_scout_web/assets/css/theme/_ethereum_classic_variables-non-critical.scss new file mode 100644 index 0000000000..bc716596bf --- /dev/null +++ b/apps/block_scout_web/assets/css/theme/_ethereum_classic_variables-non-critical.scss @@ -0,0 +1,7 @@ +// general +$primary: #1c1c3d; +$secondary: #4ad7a7; +$tertiary: #5959d8; +$additional-font: #bdbdff; + +$btn-line-color: $tertiary; // button border and font color && hover bg color \ No newline at end of file diff --git a/apps/block_scout_web/assets/css/theme/_ethereum_classic_variables.scss b/apps/block_scout_web/assets/css/theme/_ethereum_classic_variables.scss index dff677d7cc..6b8d6d7f96 100644 --- a/apps/block_scout_web/assets/css/theme/_ethereum_classic_variables.scss +++ b/apps/block_scout_web/assets/css/theme/_ethereum_classic_variables.scss @@ -34,6 +34,7 @@ $btn-line-bg: #fff; // button bg $btn-line-color: $tertiary; // button border and font color && hover bg color $btn-copy-color: $tertiary; // btn copy $btn-qr-color: $tertiary; // btn qr-code +$btn-address-card-icon-color: $tertiary; // btn address color //links & tile $tile-body-a-color: $tertiary; diff --git a/apps/block_scout_web/assets/css/theme/_ethereum_variables-non-critical.scss b/apps/block_scout_web/assets/css/theme/_ethereum_variables-non-critical.scss new file mode 100644 index 0000000000..160c0cb684 --- /dev/null +++ b/apps/block_scout_web/assets/css/theme/_ethereum_variables-non-critical.scss @@ -0,0 +1,7 @@ +// general +$primary: #153550; +$secondary: #49a2ee; +$tertiary: #4ad7a7; +$additional-font: #89cae6; + +$btn-line-color: $secondary; // button border and font color && hover bg color \ No newline at end of file diff --git a/apps/block_scout_web/assets/css/theme/_ethereum_variables.scss b/apps/block_scout_web/assets/css/theme/_ethereum_variables.scss index 0b5cac0c43..a06e0be93e 100644 --- a/apps/block_scout_web/assets/css/theme/_ethereum_variables.scss +++ b/apps/block_scout_web/assets/css/theme/_ethereum_variables.scss @@ -37,6 +37,7 @@ $btn-line-bg: #fff; // button bg $btn-line-color: $secondary; // button border and font color && hover bg color $btn-copy-color: $secondary; // btn copy $btn-qr-color: $secondary; // btn qr-code +$btn-address-card-icon-color: $secondary; // btn address color //links & tile $tile-body-a-color: $secondary; diff --git a/apps/block_scout_web/assets/css/theme/_goerli_variables-non-critical.scss b/apps/block_scout_web/assets/css/theme/_goerli_variables-non-critical.scss new file mode 100644 index 0000000000..70cfaddd73 --- /dev/null +++ b/apps/block_scout_web/assets/css/theme/_goerli_variables-non-critical.scss @@ -0,0 +1,8 @@ +// general +$primary: #2b2b2b; +$secondary: #eac247; +$tertiary: #929292; +$additional-font: #ffffff; +$sub-accent-color: #a46f30; + +$btn-line-color: $sub-accent-color; // button border and font color && hover bg color \ No newline at end of file diff --git a/apps/block_scout_web/assets/css/theme/_goerli_variables.scss b/apps/block_scout_web/assets/css/theme/_goerli_variables.scss index 04f953b477..af57f2d1a2 100644 --- a/apps/block_scout_web/assets/css/theme/_goerli_variables.scss +++ b/apps/block_scout_web/assets/css/theme/_goerli_variables.scss @@ -43,6 +43,7 @@ $btn-line-bg: #fff; // button bg $btn-line-color: $sub-accent-color; // button border and font color && hover bg color $btn-copy-color: $sub-accent-color; // btn copy $btn-qr-color: $sub-accent-color; // btn qr-code +$btn-address-card-icon-color: $sub-accent-color; // btn address color //links & tile $tile-body-a-color: $sub-accent-color; diff --git a/apps/block_scout_web/assets/css/theme/_kovan_variables-non-critical.scss b/apps/block_scout_web/assets/css/theme/_kovan_variables-non-critical.scss new file mode 100644 index 0000000000..ab4568e14f --- /dev/null +++ b/apps/block_scout_web/assets/css/theme/_kovan_variables-non-critical.scss @@ -0,0 +1,7 @@ +// general +$primary: #101f25; +$secondary: #35e3d8; +$tertiary: #1f857f; +$additional-font: #99fff9; + +$btn-line-color: $tertiary; // button border and font color && hover bg color \ No newline at end of file diff --git a/apps/block_scout_web/assets/css/theme/_kovan_variables.scss b/apps/block_scout_web/assets/css/theme/_kovan_variables.scss index a4edc629d0..b47c3de0b2 100644 --- a/apps/block_scout_web/assets/css/theme/_kovan_variables.scss +++ b/apps/block_scout_web/assets/css/theme/_kovan_variables.scss @@ -38,6 +38,7 @@ $btn-line-bg: #fff; // button bg $btn-line-color: $tertiary; // button border and font color && hover bg color $btn-copy-color: $tertiary; // btn copy $btn-qr-color: $tertiary; // btn qr-code +$btn-address-card-icon-color: $tertiary; // btn address color //links & tile $tile-body-a-color: $tertiary; diff --git a/apps/block_scout_web/assets/css/theme/_lukso_variables-non-critical.scss b/apps/block_scout_web/assets/css/theme/_lukso_variables-non-critical.scss new file mode 100644 index 0000000000..8a8d7e5eaf --- /dev/null +++ b/apps/block_scout_web/assets/css/theme/_lukso_variables-non-critical.scss @@ -0,0 +1,7 @@ +// general +$primary: #1d3154; +$secondary: #fdcec4; +$tertiary: #a96c55; +$additional-font: #a1ded1; + +$btn-line-color: $primary; // button border and font color && hover bg color \ No newline at end of file diff --git a/apps/block_scout_web/assets/css/theme/_lukso_variables.scss b/apps/block_scout_web/assets/css/theme/_lukso_variables.scss index a7b608bfa8..abfcc05472 100644 --- a/apps/block_scout_web/assets/css/theme/_lukso_variables.scss +++ b/apps/block_scout_web/assets/css/theme/_lukso_variables.scss @@ -44,6 +44,7 @@ $btn-line-bg: #fff; // button bg $btn-line-color: $primary; // button border and font color && hover bg color $btn-copy-color: $primary; // btn copy $btn-qr-color: $primary; // btn qr-code +$btn-address-card-icon-color: $primary; // btn address color // card $card-background-1: $primary; diff --git a/apps/block_scout_web/assets/css/theme/_neutral_variables-non-critical.scss b/apps/block_scout_web/assets/css/theme/_neutral_variables-non-critical.scss new file mode 100644 index 0000000000..3d85713af9 --- /dev/null +++ b/apps/block_scout_web/assets/css/theme/_neutral_variables-non-critical.scss @@ -0,0 +1,7 @@ +// general +$primary: #5c34a2; +$secondary: #87e1a9; +$tertiary: #bf9cff; +$additional-font: #fff; + +$btn-line-color: $primary; // button border and font color && hover bg color \ No newline at end of file diff --git a/apps/block_scout_web/assets/css/theme/_neutral_variables.scss b/apps/block_scout_web/assets/css/theme/_neutral_variables.scss index 120a11c222..536ecdc105 100644 --- a/apps/block_scout_web/assets/css/theme/_neutral_variables.scss +++ b/apps/block_scout_web/assets/css/theme/_neutral_variables.scss @@ -40,6 +40,7 @@ $btn-line-bg: #fff; // button bg $btn-line-color: $primary; // button border and font color && hover bg color $btn-copy-color: $primary; // btn copy $btn-qr-color: $primary; // btn qr-code +$btn-address-card-icon-color: $primary; // btn address color //links & tile $tile-body-a-color: $primary; diff --git a/apps/block_scout_web/assets/css/theme/_poa_variables-non-critical.scss b/apps/block_scout_web/assets/css/theme/_poa_variables-non-critical.scss new file mode 100644 index 0000000000..3d85713af9 --- /dev/null +++ b/apps/block_scout_web/assets/css/theme/_poa_variables-non-critical.scss @@ -0,0 +1,7 @@ +// general +$primary: #5c34a2; +$secondary: #87e1a9; +$tertiary: #bf9cff; +$additional-font: #fff; + +$btn-line-color: $primary; // button border and font color && hover bg color \ No newline at end of file diff --git a/apps/block_scout_web/assets/css/theme/_poa_variables.scss b/apps/block_scout_web/assets/css/theme/_poa_variables.scss index 120a11c222..536ecdc105 100644 --- a/apps/block_scout_web/assets/css/theme/_poa_variables.scss +++ b/apps/block_scout_web/assets/css/theme/_poa_variables.scss @@ -40,6 +40,7 @@ $btn-line-bg: #fff; // button bg $btn-line-color: $primary; // button border and font color && hover bg color $btn-copy-color: $primary; // btn copy $btn-qr-color: $primary; // btn qr-code +$btn-address-card-icon-color: $primary; // btn address color //links & tile $tile-body-a-color: $primary; diff --git a/apps/block_scout_web/assets/css/theme/_rinkeby_variables-non-critical.scss b/apps/block_scout_web/assets/css/theme/_rinkeby_variables-non-critical.scss new file mode 100644 index 0000000000..823da9f2d6 --- /dev/null +++ b/apps/block_scout_web/assets/css/theme/_rinkeby_variables-non-critical.scss @@ -0,0 +1,7 @@ +// general +$primary: #153550; +$secondary: #38a9f5; +$tertiary: #76f1ff; +$additional-font: #89cae6; + +$btn-line-color: $secondary; // button border and font color && hover bg color \ No newline at end of file diff --git a/apps/block_scout_web/assets/css/theme/_rinkeby_variables.scss b/apps/block_scout_web/assets/css/theme/_rinkeby_variables.scss index 4fa700c50e..21388bb315 100644 --- a/apps/block_scout_web/assets/css/theme/_rinkeby_variables.scss +++ b/apps/block_scout_web/assets/css/theme/_rinkeby_variables.scss @@ -37,6 +37,7 @@ $btn-line-bg: #fff; // button bg $btn-line-color: $secondary; // button border and font color && hover bg color $btn-copy-color: $secondary; // btn copy $btn-qr-color: $secondary; // btn qr-code +$btn-address-card-icon-color: $secondary; // btn address color //links & tile $tile-body-a-color: $secondary; diff --git a/apps/block_scout_web/assets/css/theme/_ropsten_variables-non-critical.scss b/apps/block_scout_web/assets/css/theme/_ropsten_variables-non-critical.scss new file mode 100644 index 0000000000..823da9f2d6 --- /dev/null +++ b/apps/block_scout_web/assets/css/theme/_ropsten_variables-non-critical.scss @@ -0,0 +1,7 @@ +// general +$primary: #153550; +$secondary: #38a9f5; +$tertiary: #76f1ff; +$additional-font: #89cae6; + +$btn-line-color: $secondary; // button border and font color && hover bg color \ No newline at end of file diff --git a/apps/block_scout_web/assets/css/theme/_ropsten_variables.scss b/apps/block_scout_web/assets/css/theme/_ropsten_variables.scss index 4fa700c50e..21388bb315 100644 --- a/apps/block_scout_web/assets/css/theme/_ropsten_variables.scss +++ b/apps/block_scout_web/assets/css/theme/_ropsten_variables.scss @@ -37,6 +37,7 @@ $btn-line-bg: #fff; // button bg $btn-line-color: $secondary; // button border and font color && hover bg color $btn-copy-color: $secondary; // btn copy $btn-qr-color: $secondary; // btn qr-code +$btn-address-card-icon-color: $secondary; // btn address color //links & tile $tile-body-a-color: $secondary; diff --git a/apps/block_scout_web/assets/css/theme/_rsk_variables-non-critical.scss b/apps/block_scout_web/assets/css/theme/_rsk_variables-non-critical.scss new file mode 100644 index 0000000000..c874423aa6 --- /dev/null +++ b/apps/block_scout_web/assets/css/theme/_rsk_variables-non-critical.scss @@ -0,0 +1,7 @@ +// general +$primary: #101f25; +$secondary: #27ac8d; +$tertiary: #e39a54; +$additional-font: #a1ded1; + +$btn-line-color: $secondary; // button border and font color && hover bg color \ No newline at end of file diff --git a/apps/block_scout_web/assets/css/theme/_rsk_variables.scss b/apps/block_scout_web/assets/css/theme/_rsk_variables.scss index d33996a89e..8d3bff31d2 100644 --- a/apps/block_scout_web/assets/css/theme/_rsk_variables.scss +++ b/apps/block_scout_web/assets/css/theme/_rsk_variables.scss @@ -38,6 +38,7 @@ $btn-line-bg: #fff; // button bg $btn-line-color: $secondary; // button border and font color && hover bg color $btn-copy-color: $secondary; // btn copy $btn-qr-color: $secondary; // btn qr-code +$btn-address-card-icon-color: $secondary; // btn address color // card $card-background-1: $secondary; diff --git a/apps/block_scout_web/assets/css/theme/_sokol_variables-non-critical.scss b/apps/block_scout_web/assets/css/theme/_sokol_variables-non-critical.scss new file mode 100644 index 0000000000..64fd3c9f9c --- /dev/null +++ b/apps/block_scout_web/assets/css/theme/_sokol_variables-non-critical.scss @@ -0,0 +1,8 @@ +// general +$primary: #093731; +$secondary: #40bfb2; +$tertiary: #25c9ff; +$additional-font: #93e8dd; +$sub-accent-color: #1c9f90; + +$btn-line-color: $sub-accent-color; // button border and font color && hover bg color \ No newline at end of file diff --git a/apps/block_scout_web/assets/css/theme/_sokol_variables.scss b/apps/block_scout_web/assets/css/theme/_sokol_variables.scss index 8d479f397d..2860342f1e 100644 --- a/apps/block_scout_web/assets/css/theme/_sokol_variables.scss +++ b/apps/block_scout_web/assets/css/theme/_sokol_variables.scss @@ -49,6 +49,7 @@ $btn-line-bg: #fff; // button bg $btn-line-color: $sub-accent-color; // button border and font color && hover bg color $btn-copy-color: $sub-accent-color; // btn copy $btn-qr-color: $sub-accent-color; // btn qr-code +$btn-address-card-icon-color: $sub-accent-color; // btn address color //links & tile $tile-body-a-color: $sub-accent-color; diff --git a/apps/block_scout_web/assets/css/theme/_variables-non-critical.scss b/apps/block_scout_web/assets/css/theme/_variables-non-critical.scss new file mode 100644 index 0000000000..232b9cc647 --- /dev/null +++ b/apps/block_scout_web/assets/css/theme/_variables-non-critical.scss @@ -0,0 +1,22 @@ +@import "theme/base_variables"; +@import "neutral_variables-non-critical"; +// @import "dai_variables-non-critical"; +// @import "ethereum_classic_variables-non-critical"; +// @import "ethereum_variables-non-critical"; +// @import "ether1_variables-non-critical"; +// @import "expanse_variables-non-critical"; +// @import "gochain_variables-non-critical"; +// @import "goerli_variables-non-critical"; +// @import "kovan_variables-non-critical"; +// @import "lukso_variables-non-critical"; +// @import "musicoin_variables-non-critical"; +// @import "pirl_variables-non-critical"; +// @import "poa_variables-non-critical"; +// @import "posdao_variables-non-critical"; +// @import "rinkeby_variables-non-critical"; +// @import "ropsten_variables-non-critical"; +// @import "social_variables-non-critical"; +// @import "sokol_variables-non-critical"; +// @import "tobalaba_variables-non-critical"; +// @import "tomochain_variables-non-critical"; +// @import "rsk_variables-non-critical"; \ No newline at end of file diff --git a/apps/block_scout_web/assets/js/app.js b/apps/block_scout_web/assets/js/app.js index 89059dd306..4aa89e4142 100644 --- a/apps/block_scout_web/assets/js/app.js +++ b/apps/block_scout_web/assets/js/app.js @@ -20,6 +20,9 @@ import 'bootstrap' import './locale' +// support of preload in Firefox +import '../node_modules/fg-loadcss/dist/cssrelpreload.min' + import './pages/address' import './pages/address/coin_balances' import './pages/address/transactions' @@ -59,5 +62,6 @@ import './lib/async_listing_load' import './lib/tooltip' import './lib/modals' import './lib/try_api' +import './lib/try_eth_api' import './lib/card_tabs' import './lib/network_selector' diff --git a/apps/block_scout_web/assets/js/lib/async_listing_load.js b/apps/block_scout_web/assets/js/lib/async_listing_load.js index 8477a7d728..13c4aa7da2 100644 --- a/apps/block_scout_web/assets/js/lib/async_listing_load.js +++ b/apps/block_scout_web/assets/js/lib/async_listing_load.js @@ -51,8 +51,6 @@ export const asyncInitialState = { requestError: false, /* if response has no items */ emptyResponse: false, - /* if it is loading the first page */ - loadingFirstPage: true, /* link to the next page */ nextPagePath: null, /* link to the previous page */ @@ -80,8 +78,7 @@ export function asyncReducer (state = asyncInitialState, action) { } case 'FINISH_REQUEST': { return Object.assign({}, state, { - loading: false, - loadingFirstPage: false + loading: false }) } case 'ITEMS_FETCHED': { @@ -134,7 +131,7 @@ export const elements = { }, '[data-async-listing] [data-loading-message]': { render ($el, state) { - if (state.loadingFirstPage) return $el.show() + if (state.loading) return $el.show() $el.hide() } @@ -143,7 +140,7 @@ export const elements = { render ($el, state) { if ( !state.requestError && - (!state.loading || !state.loadingFirstPage) && + (!state.loading) && state.items.length === 0 ) { return $el.show() @@ -201,6 +198,25 @@ export const elements = { $el.attr('href', state.prevPagePath) } }, + '[data-async-listing] [data-first-page-button]': { + render ($el, state) { + if (state.pagesStack.length === 0) { + return $el.hide() + } + $el.show() + $el.attr('disabled', false) + + const urlParams = new URLSearchParams(window.location.search) + const blockParam = urlParams.get('block_type') + const firstPageHref = window.location.href.split('?')[0] + + if (blockParam !== null) { + $el.attr('href', firstPageHref + '?block_type=' + blockParam) + } else { + $el.attr('href', firstPageHref) + } + } + }, '[data-async-listing] [data-page-number]': { render ($el, state) { if (state.emptyResponse) { @@ -216,7 +232,7 @@ export const elements = { }, '[data-async-listing] [data-loading-button]': { render ($el, state) { - if (!state.loadingFirstPage && state.loading) return $el.show() + if (state.loading) return $el.show() $el.hide() } diff --git a/apps/block_scout_web/assets/js/lib/awesomplete-util.js b/apps/block_scout_web/assets/js/lib/awesomplete-util.js new file mode 100644 index 0000000000..5b37e1dd2f --- /dev/null +++ b/apps/block_scout_web/assets/js/lib/awesomplete-util.js @@ -0,0 +1,637 @@ +/* eslint-env browser */ +/* global Awesomplete */ +/* exported AwesompleteUtil */ + +/* + * Library endorsing Lea Verou's Awesomplete widget, providing: + * - dynamic remote data loading + * - labels with HTML markup + * - events and styling for exact matches + * - events and styling for mismatches + * - select item when TAB key is used + * + * (c) Nico Hoogervorst + * License: MIT + * + */ +window.AwesompleteUtil = (function () { + // + // event names and css classes + // + var _AWE = 'awesomplete-' + var _AWE_LOAD = _AWE + 'loadcomplete' + var _AWE_CLOSE = _AWE + 'close' + var _AWE_MATCH = _AWE + 'match' + var _AWE_PREPOP = _AWE + 'prepop' + var _AWE_SELECT = _AWE + 'select' + var _CLS_FOUND = 'awe-found' + var _CLS_NOT_FOUND = 'awe-not-found' + var $ = Awesomplete.$ /* shortcut for document.querySelector */ + + // + // private functions + // + + // Some parts are shamelessly copied from Awesomplete.js like the logic inside this _suggestion function. + // Returns an object with label and value properties. Data parameter is plain text or Object/Array with label and value. + function _suggestion (data) { + var lv = Array.isArray(data) + ? { label: data[0], value: data[1] } + : typeof data === 'object' && 'label' in data && 'value' in data ? data : { label: data, value: data } + return {label: lv.label || lv.value, value: lv.value} + } + + // Helper to send events with detail property. + function _fire (target, name, detail) { + // $.fire uses deprecated methods but other methods don't work in IE11. + return $.fire(target, name, {detail: detail}) + } + + // Look if there is an exact match or a mismatch, set awe-found, awe-not-found css class and send match events. + function _matchValue (awe, prepop) { + var input = awe.input /* the input field */ + var classList = input.classList + var utilprops = awe.utilprops /* extra properties piggybacked on Awesomplete object */ + var selected = utilprops.selected /* the exact selected Suggestion with label and value */ + var val = utilprops.convertInput.call(awe, input.value) /* trimmed lowercased value */ + var opened = awe.opened /* is the suggestion list opened? */ + var result = [] /* matches with value */ + var list = awe._list /* current list of suggestions */ + var suggestion, fake, rec, j /* function scoped variables */ + utilprops.prepop = false /* after the first call it's not a prepopulation phase anymore */ + if (list) { /* if there is a suggestion list */ + for (j = 0; j < list.length; j++) { /* loop all suggestions */ + rec = list[j] + suggestion = _suggestion(awe.data(rec, val)) /* call data convert function */ + // with maxItems = 0 cannot look if suggestion list is opened to determine if there are still matches, + // instead call the filter method to see if there are still some options. + if (awe.maxItems === 0) { + // Awesomplete.FILTER_CONTAINS and Awesomplete.FILTER_STARTSWITH use the toString method. + suggestion.toString = function () { return '' + this.label } + if (awe.filter(suggestion, val)) { + // filter returns true, so there is at least one partial match. + opened = true + } + } + // Don't want to change the real input field, emulate a fake one. + fake = {input: {value: ''}} + // Determine how this suggestion would look like if it is replaced in the input field, + // it is an exact match if somebody types exactly that. + // Use the fake input here. fake.input.value will contain the result of the replace function. + awe.replace.call(fake, suggestion) + // Trim and lowercase also the fake input and compare that with the currently typed-in value. + if (utilprops.convertInput.call(awe, fake.input.value) === val) { + // This is an exact match. However there might more suggestions with the same value. + // If the user selected a suggestion from the list, check if this one matches, assuming that + // value + label is unique (if not it will be difficult for the user to make an informed decision). + if (selected && selected.value === suggestion.value && selected.label === suggestion.label) { + // this surely is the selected one + result = [rec] + break + } + // add the matching record to the result set. + result.push(rec) + } // end if + } // end loop + + // if the result differs from the previous result + if (utilprops.prevSelected !== result) { + // if there is an exact match + if (result.length > 0) { + // if prepopulation phase (initial/autofill value); not triggered by user input + if (prepop) { + _fire(input, _AWE_PREPOP, result) + } else if (utilprops.changed) { /* if input is changed */ + utilprops.prevSelected = result /* new result */ + classList.remove(_CLS_NOT_FOUND) /* remove class */ + classList.add(_CLS_FOUND) /* add css class */ + _fire(input, _AWE_MATCH, result) /* fire event */ + } + } else if (prepop) { /* no exact match, if in prepopulation phase */ + _fire(input, _AWE_PREPOP, []) + } else if (utilprops.changed) { /* no exact match, if input is changed */ + utilprops.prevSelected = [] + classList.remove(_CLS_FOUND) + // Mark as not-found if there are no suggestions anymore or if another field is now active + if (!opened || (input !== document.activeElement)) { + if (val.length > 0) { + classList.add(_CLS_NOT_FOUND) + _fire(input, _AWE_MATCH, []) + } + } else { + classList.remove(_CLS_NOT_FOUND) + } + } + } + } + } + + // Listen to certain events of THIS awesomplete object to trigger input validation. + function _match (ev) { + var awe = this + if ((ev.type === _AWE_CLOSE || ev.type === _AWE_LOAD || ev.type === 'blur') && ev.target === awe.input) { + _matchValue(awe, awe.utilprops.prepop && ev.type === _AWE_LOAD) + } + } + + // Select currently selected item if tab or shift-tab key is used. + function _onKeydown (ev) { + var awe = this + if (ev.target === awe.input && ev.keyCode === 9) { // TAB key + awe.select() // take current selected item + } + } + + // Handle selection event. State changes when an item is selected. + function _select (ev) { + var awe = this + awe.utilprops.changed = true // yes, user made a change + awe.utilprops.selected = ev.text // Suggestion object + } + + // check if the object is empty {} object + function _isEmpty (val) { + return Object.keys(val).length === 0 && val.constructor === Object + } + + // Need an updated suggestion list if: + // - There is no result yet, or there is a result but not for the characters we entered + // - or there might be more specific results because the limit was reached. + function _ifNeedListUpdate (awe, val, queryVal) { + var utilprops = awe.utilprops + return (!utilprops.listQuery || + (!utilprops.loadall && /* with loadall, if there is a result, there is no need for new lists */ + val.lastIndexOf(queryVal, 0) === 0 && + (val.lastIndexOf(utilprops.listQuery, 0) !== 0 || + (typeof utilprops.limit === 'number' && awe._list.length >= utilprops.limit)))) + } + + // Set a new suggestion list. Trigger loadcomplete event. + function _loadComplete (awe, list, queryVal) { + awe.list = list + awe.utilprops.listQuery = queryVal + _fire(awe.input, _AWE_LOAD, queryVal) + } + + // Handle ajax response. Expects HTTP OK (200) response with JSON object with suggestion(s) (array). + function _onLoad () { + var t = this + var awe = t.awe + var xhr = t.xhr + var queryVal = t.queryVal + var val = awe.utilprops.val + var data + var prop + if (xhr.status === 200) { + data = JSON.parse(xhr.responseText) + if (awe.utilprops.convertResponse) data = awe.utilprops.convertResponse(data) + if (!Array.isArray(data)) { + if (awe.utilprops.limit === 0 || awe.utilprops.limit === 1) { + // if there is max 1 result expected, the array is not needed. + // Fur further processing, take the whole result and put it as one element in an array. + data = _isEmpty(data) ? [] : [data] + } else { + // search for the first property that contains an array + for (prop in data) { + if (Array.isArray(data[prop])) { + data = data[prop] + break + } + } + } + } + // can only handle arrays + if (Array.isArray(data)) { + // are we still interested in this response? + if (_ifNeedListUpdate(awe, val, queryVal)) { + // accept the new suggestion list + _loadComplete(awe, data, queryVal || awe.utilprops.loadall) + } + } + } + } + + // Perform suggestion list lookup for the current value and validate. Use ajax when there is an url specified. + function _lookup (awe, val) { + var xhr + if (awe.utilprops.url) { + // are we still interested in this response? + if (_ifNeedListUpdate(awe, val, val)) { + xhr = new XMLHttpRequest() + awe.utilprops.ajax.call(awe, + awe.utilprops.url, + awe.utilprops.urlEnd, + awe.utilprops.loadall ? '' : val, + _onLoad.bind({awe: awe, xhr: xhr, queryVal: val}), + xhr + ) + } else { + _matchValue(awe, awe.utilprops.prepop) + } + } else { + _matchValue(awe, awe.utilprops.prepop) + } + } + + // Restart autocomplete search: clear css classes and send match-event with empty list. + function _restart (awe) { + var elem = awe.input + var classList = elem.classList + // IE11 only handles the first parameter of the remove method. + classList.remove(_CLS_NOT_FOUND) + classList.remove(_CLS_FOUND) + _fire(elem, _AWE_MATCH, []) + } + + // handle new input value + function _update (awe, val, prepop) { + // prepop parameter is optional. Default value is false. + awe.utilprops.prepop = prepop || false + // if value changed + if (awe.utilprops.val !== val) { + // new value, clear previous selection + awe.utilprops.selected = null + // yes, user made a change + awe.utilprops.changed = true + awe.utilprops.val = val + // value is empty or smaller than minChars + if (val.length < awe.minChars || val.length === 0) { + // restart autocomplete search + _restart(awe) + } + if (val.length >= awe.minChars) { + // lookup suggestions and validate input + _lookup(awe, val) + } + } + return awe + } + + // handle input changed event for THIS awesomplete object + function _onInput (e) { + var awe = this + var val + if (e.target === awe.input) { + // lowercase and trim input value + val = awe.utilprops.convertInput.call(awe, awe.input.value) + _update(awe, val) + } + } + + // item function (as specified in Awesomplete) which just creates the 'li' HTML tag. + function _item (html /* , input */) { + return $.create('li', { + innerHTML: html, + 'aria-selected': 'false' + }) + } + + // Escape HTML characters in text. + function _htmlEscape (text) { + return text.replace('&', '&').replace('<', '<').replace('>', '>') + } + + // Function to copy a field from the selected autocomplete item to another DOM element. + function _copyFun (e) { + var t = this + var sourceId = t.sourceId + var dataField = t.dataField + var targetId = t.targetId + var elem + var val + if (e.target === $(sourceId)) { + if (typeof targetId === 'function') { + targetId(e, dataField) + } else { + // lookup target element if it isn't resolved yet + elem = $(targetId) + // don't override target inputs if user is currently editing it. + if (elem && elem !== document.activeElement) { + // event must contain 1 item from suggestion list + val = Array.isArray(e.detail) && e.detail.length === 1 ? e.detail[0] : null + // if a datafield is specified, take that value + val = (dataField && val ? val[dataField] : val) || '' + // if it is an input control + if (typeof elem.value !== 'undefined') { + // set new value + elem.value = val + // not really sure if it is an input control, check if it has a classList + if (elem.classList && elem.classList.remove) { + // it might be another awesomplete control, if so the input is not wrong anymore because it's changed now + elem.classList.remove(_CLS_NOT_FOUND) + } + } else if (typeof elem.src !== 'undefined') { /* is it an image tag? */ + elem.src = val + } else { + // use innerHTML to set the new value, because value might intentionally contain HTML markup + elem.innerHTML = val + } + } + } + } + } + + // click function for the combobox button + function _clickFun (e) { + var t = this + var awe + var minChars + if (e.target === $(t.btnId)) { + e.preventDefault() + awe = t.awe + // toggle open/close + if (awe.ul.childNodes.length === 0 || awe.ul.hasAttribute('hidden')) { + minChars = awe.minChars + // ignore that the input value is empty + awe.minChars = 0 + // show the suggestion list + awe.evaluate() + awe.minChars = minChars + } else { + awe.close() + } + } + } + + // Return text with mark tags arround matching input. Don't replace inside tags. + // When startsWith is true, mark only the matching begin text. + function _mark (text, input, startsWith) { + var searchText = $.regExpEscape(_htmlEscape(input).trim()) + var regExp = searchText.length <= 0 ? null : startsWith ? RegExp('^' + searchText, 'i') : RegExp('(?!<[^>]+?>)' + searchText + '(?![^<]*?>)', 'gi') + return text.replace(regExp, '$&') + } + + // Recursive jsonFlatten function + function _jsonFlatten (result, cur, prop, level, opts) { + var root = opts.root /* filter resulting json tree on root property (optional) */ + var value = opts.value /* search for this property and copy it's value to a new 'value' property + (optional, do not specify it if the json array contains plain strings) */ + var label = opts.label || opts.value /* search this property and copy it's value to a new 'label' property. + If there is a 'opts.value' field but no 'opts.label', assume label is the same. */ + var isEmpty = true + var arrayResult = [] + var j + // at top level, look if there is a property which starts with root (if specified) + if (level === 0 && root && prop && (prop + '.').lastIndexOf(root + '.', 0) !== 0 && (root + '.').lastIndexOf(prop + '.', 0) !== 0) { + return result + } + // handle current part of the json tree + if (Object(cur) !== cur) { + if (prop) { + result[prop] = cur + } else { + result = cur + } + } else if (Array.isArray(cur)) { + for (j = 0; j < cur.length; j++) { + arrayResult.push(_jsonFlatten({}, cur[j], '', level + 1, opts)) + } + if (prop) { + result[prop] = arrayResult + } else { + result = arrayResult + } + } else { + for (j in cur) { + isEmpty = false + _jsonFlatten(result, cur[j], prop ? prop + '.' + j : j, level, opts) + } + if (isEmpty && prop) result[prop] = {} + } + // for arrays at top and subtop level + if (level < 2 && prop) { + // if a 'value' is specified and found a mathing property, create extra 'value' property. + if (value && (prop + '.').lastIndexOf(value + '.', 0) === 0) { result['value'] = result[prop] } + // if a 'label' is specified and found a mathing property, create extra 'label' property. + if (label && (prop + '.').lastIndexOf(label + '.', 0) === 0) { result['label'] = result[prop] } + } + if (level === 0) { + // Make sure that both value and label properties exist, even if they are nil. + // This is handy with limit 0 or 1 when the result doesn't have to contain an array. + if (value && !('value' in result)) { result['value'] = null } + if (label && !('label' in result)) { result['label'] = null } + } + return result + } + + // Stop AwesompleteUtil; detach event handlers from the Awesomplete object. + function _detach () { + var t = this + var elem = t.awe.input + var boundMatch = t.boundMatch + var boundOnInput = t.boundOnInput + var boundOnKeydown = t.boundOnKeydown + var boundSelect = t.boundSelect + + elem.removeEventListener(_AWE_SELECT, boundSelect) + elem.removeEventListener(_AWE_LOAD, boundMatch) + elem.removeEventListener(_AWE_CLOSE, boundMatch) + elem.removeEventListener('blur', boundMatch) + elem.removeEventListener('input', boundOnInput) + elem.removeEventListener('keydown', boundOnKeydown) + } + + // + // public methods + // + + return { + + // ajax call for url + val + urlEnd. fn is the callback function. xhr parameter is optional. + ajax: function (url, urlEnd, val, fn, xhr) { + xhr = xhr || new XMLHttpRequest() + xhr.open('GET', url + encodeURIComponent(val) + (urlEnd || '')) + xhr.onload = fn + xhr.send() + return xhr + }, + + // Convert input before comparing it with suggestion. lowercase and trim the text + convertInput: function (text) { + return typeof text === 'string' ? text.trim().toLowerCase() : '' + }, + + // item function as defined in Awesomplete. + // item(html, input). input is optional and ignored in this implementation + item: _item, + + // Set a new suggestion list. Trigger loadcomplete event. + // load(awesomplete, list, queryVal) + load: _loadComplete, + + // Return text with mark tags arround matching input. Don't replace inside tags. + // When startsWith is true, mark only the matching begin text. + // mark(text, input, startsWith) + mark: _mark, + + // highlight items: Marks input in the first line, not in the optional description + itemContains: function (text, input) { + var arr + if (input.trim().length > 0) { + arr = ('' + text).split(/

/) + arr[0] = _mark(arr[0], input) + text = arr.join('

') + } + return _item(text, input) + }, + + // highlight items: mark all occurrences of the input text + itemMarkAll: function (text, input) { + return _item(input.trim() === '' ? '' + text : _mark('' + text, input), input) + }, + + // highlight items: mark input in the begin text + itemStartsWith: function (text, input) { + return _item(input.trim() === '' ? '' + text : _mark('' + text, input, true), input) + }, + + // create Awesomplete object for input control elemId. opts are passed unchanged to Awesomplete. + create: function (elemId, utilOpts, opts) { + opts.item = opts.item || this.itemContains /* by default uses itemContains, can be overriden */ + var awe = new Awesomplete(elemId, opts) + awe.utilprops = utilOpts || {} + // loadall is true if there is no url (there is a static data-list) + if (!awe.utilprops.url && typeof awe.utilprops.loadall === 'undefined') { + awe.utilprops.loadall = true + } + awe.utilprops.ajax = awe.utilprops.ajax || this.ajax /* default ajax function can be overriden */ + awe.utilprops.convertInput = awe.utilprops.convertInput || this.convertInput /* the same applies for convertInput */ + return awe + }, + + // attach Awesomplete object to event listeners + attach: function (awe) { + var elem = awe.input + var boundMatch = _match.bind(awe) + var boundOnKeydown = _onKeydown.bind(awe) + var boundOnInput = _onInput.bind(awe) + var boundSelect = _select.bind(awe) + var boundDetach = _detach.bind({awe: awe, + boundMatch: boundMatch, + boundOnInput: boundOnInput, + boundOnKeydown: boundOnKeydown, + boundSelect: boundSelect + }) + var events = { + 'keydown': boundOnKeydown, + 'input': boundOnInput + } + events['blur'] = events[_AWE_CLOSE] = events[_AWE_LOAD] = boundMatch + events[_AWE_SELECT] = boundSelect + $.bind(elem, events) + + awe.utilprops.detach = boundDetach + // Perform ajax call if prepop is true and there is an initial input value, or when all values must be loaded (loadall) + if (awe.utilprops.prepop && (awe.utilprops.loadall || elem.value.length > 0)) { + awe.utilprops.val = awe.utilprops.convertInput.call(awe, elem.value) + _lookup(awe, awe.utilprops.val) + } + return awe + }, + + // update input value via javascript. Use prepop=true when this is an initial/prepopulation value. + update: function (awe, value, prepop) { + awe.input.value = value + return _update(awe, value, prepop) + }, + + // create and attach Awesomplete object for input control elemId. opts are passed unchanged to Awesomplete. + start: function (elemId, utilOpts, opts) { + return this.attach(this.create(elemId, utilOpts, opts)) + }, + + // Stop AwesompleteUtil; detach event handlers from the Awesomplete object. + detach: function (awe) { + if (awe.utilprops.detach) { + awe.utilprops.detach() + delete awe.utilprops.detach + } + return awe + }, + + // Create function to copy a field from the selected autocomplete item to another DOM element. + // dataField can be null. + createCopyFun: function (sourceId, dataField, targetId) { + return _copyFun.bind({sourceId: sourceId, dataField: dataField, targetId: $(targetId) || targetId}) + }, + + // attach copy function to event listeners. prepop is optional and by default true. + // if true the copy function will also listen to awesomplete-prepop events. + // The optional listenEl is the element that listens, defaults to document.body. + attachCopyFun: function (fun, prepop, listenEl) { + // prepop parameter defaults to true + prepop = typeof prepop === 'boolean' ? prepop : true + listenEl = listenEl || document.body + listenEl.addEventListener(_AWE_MATCH, fun) + if (prepop) listenEl.addEventListener(_AWE_PREPOP, fun) + return fun + }, + + // Create and attach copy function. + startCopy: function (sourceId, dataField, targetId, prepop) { + var sourceEl = $(sourceId) + return this.attachCopyFun(this.createCopyFun(sourceEl || sourceId, dataField, targetId), prepop, sourceEl) + }, + + // Stop copy function. Detach it from event listeners. + // The optional listenEl must be the same element that was used during startCopy/attachCopyFun; + // in general: Awesomplete.$(sourceId). listenEl defaults to document.body. + detachCopyFun: function (fun, listenEl) { + listenEl = listenEl || document.body + listenEl.removeEventListener(_AWE_PREPOP, fun) + listenEl.removeEventListener(_AWE_MATCH, fun) + return fun + }, + + // Create function for combobox button (btnId) to toggle dropdown list. + createClickFun: function (btnId, awe) { + return _clickFun.bind({btnId: btnId, awe: awe}) + }, + + // Attach click function for combobox to click event. + // The optional listenEl is the element that listens, defaults to document.body. + attachClickFun: function (fun, listenEl) { + listenEl = listenEl || document.body + listenEl.addEventListener('click', fun) + return fun + }, + + // Create and attach click function for combobox button. Toggles open/close of suggestion list. + startClick: function (btnId, awe) { + var btnEl = $(btnId) + return this.attachClickFun(this.createClickFun(btnEl || btnId, awe), btnEl) + }, + + // Stop click function. Detach it from event listeners. + // The optional listenEl must be the same element that was used during startClick/attachClickFun; + // in general: Awesomplete.$(btnId). listenEl defaults to document.body. + detachClickFun: function (fun, listenEl) { + listenEl = listenEl || document.body + listenEl.removeEventListener('click', fun) + return fun + }, + + // filter function as specified in Awesomplete. Filters suggestion list on items containing input value. + // Awesomplete.FILTER_CONTAINS filters on data.label, however + // this function filters on value and not on the shown label which may contain markup. + filterContains: function (data, input) { + return Awesomplete.FILTER_CONTAINS(data.value, input) + }, + + // filter function as specified in Awesomplete. Filters suggestion list on matching begin text. + // Awesomplete.FILTER_STARTSWITH filters on data.label, however + // this function filters on value and not on the shown label which may contain markup. + filterStartsWith: function (data, input) { + return Awesomplete.FILTER_STARTSWITH(data.value, input) + }, + + // Flatten JSON. + // { "a":{"b":{"c":[{"d":{"e":1}}]}}} becomes {"a.b.c":[{"d.e":1}]}. + // This function can be bind to configure it with extra options; + // bind({root: '', value: '', label: '

<%= gettext "Decoded" %>
-
<%= case decode(@log, @log.transaction) do %> <% {:error, :contract_not_verified} -> %> +
<%= gettext "Decoded" %>
+
<%= gettext "To see decoded input data, the contract must be verified." %> <%= case @log.transaction do %> @@ -25,10 +25,14 @@ <% end %>
<% {:error, :could_not_decode} -> %> +
<%= gettext "Decoded" %>
+
<%= gettext "Failed to decode log data." %>
<% {:ok, method_id, text, mapping} -> %> +
<%= gettext "Decoded" %>
+
@@ -78,7 +82,7 @@ <% end %>
Method Id
- <% _ -> %> + <% _ -> %> <%= nil %> <% end %>
<%= gettext "Topics" %>
diff --git a/apps/block_scout_web/lib/block_scout_web/templates/api_docs/_eth_rpc_item.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/api_docs/_eth_rpc_item.html.eex new file mode 100644 index 0000000000..7f342b4a16 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/api_docs/_eth_rpc_item.html.eex @@ -0,0 +1,182 @@ +
+
+
+

<%= @action %>

+

<%= raw @info.notes %>

+ + curl -X POST --data '{"id":0,"jsonrpc":"2.0","method": "<%= @action %>", params: []}' + +

+

+

+        
+

+
+ +
+ +
+

+ <%= gettext "Parameters" %> + + +

+ +
+
+

<%= gettext "Name" %>

+

<%= gettext "Description" %>

+
+ + <%= for param <- @info.params do %> +
+
+
+ <%= param.name %> + <%= if param.required do %> + + *<%= gettext "required" %> + + <% end %> +
+
+
+

<%= param.description %>

+ " + data-parameter-type='<%= param.type %>' + data-required='<%= if param.required, do: "true", else: "false" %>' + data-selector='<%= "eth-#{@action}-try-api-ui" %>' + type="text", + value='<%= param.default %>' + /> +
+
+ <% end %> + + +
+
+ + +
+
+ + +
+
+
<%= gettext "Curl" %>
+
+

+          
+
+
<%= gettext "Server Response" %>
+
+

<%= gettext "Code" %>

+

<%= gettext "Details" %>

+
+
+
+
+

<%= gettext "Response Body" %>

+
+

+            
+
+
+
+
+ + +

<%= gettext "Responses" %>

+
+

<%= gettext "Code" %>

+
<%= gettext "Description" %>
+
+
+
200
+
+
+
successful operation
+
+ + + +
+ +
+
+

+            
+
+
+
+
+
+
\ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/api_docs/eth_rpc.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/api_docs/eth_rpc.html.eex index 134b3e13e1..7b8a9a98de 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/api_docs/eth_rpc.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/api_docs/eth_rpc.html.eex @@ -2,7 +2,7 @@

<%= gettext("ETH RPC API Documentation") %>

-

[ <%= gettext "Base URL:" %> <%= blockscout_url() %>/api/eth_rpc ]

+

[ <%= gettext "Base URL:" %> <%= eth_rpc_api_url()%> ]

<%= gettext "This API is provided to support some rpc methods in the exact format specified for ethereum nodes, which can be found " %> @@ -17,20 +17,8 @@

-
- - - - - - - <%= for {method, info} <- Map.to_list(@documentation) do %> - - - - - - <% end %> -
Supported MethodNotesParameters example
<%= method %> <%= Map.get(info, :notes, "N/A") %> <%= Map.get(info, :example, "N/A") %>
+ <%= for {method, info} <- Map.to_list(@documentation) do %> + <%= render "_eth_rpc_item.html", action: method, info: info %> + <% end %>
diff --git a/apps/block_scout_web/lib/block_scout_web/templates/api_docs/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/api_docs/index.html.eex index 163697d324..59dae38cdb 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/api_docs/index.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/api_docs/index.html.eex @@ -2,7 +2,7 @@

<%= gettext("API Documentation") %>

-

[ <%= gettext "Base URL:" %> <%= blockscout_url() %>/api ]

+

[ <%= gettext "Base URL:" %> <%= api_url()%> ]

<%= gettext "This API is provided for developers transitioning their applications from Etherscan to BlockScout. It supports GET and POST requests." %>

diff --git a/apps/block_scout_web/lib/block_scout_web/templates/block/overview.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/block/overview.html.eex index 24b2d9e61a..65e785920f 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/block/overview.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/block/overview.html.eex @@ -57,7 +57,7 @@
<%= gettext "Difficulty" %>
- <%= @block.difficulty |> Cldr.Number.to_string! %> + <%= @block.difficulty |> BlockScoutWeb.Cldr.Number.to_string! %>
@@ -65,7 +65,7 @@
<%= gettext "Total Difficulty" %>
-
<%= @block.total_difficulty |> Cldr.Number.to_string! %>
+
<%= @block.total_difficulty |> BlockScoutWeb.Cldr.Number.to_string! %>
@@ -97,14 +97,14 @@
<%= gettext "Gas Used" %>
- <%= @block.gas_used |> Cldr.Number.to_string! %> - (<%= (Decimal.to_integer(@block.gas_used) / Decimal.to_integer(@block.gas_limit)) |> Cldr.Number.to_string!(format: "#.#%") %>) + <%= @block.gas_used |> BlockScoutWeb.Cldr.Number.to_string! %> + (<%= (Decimal.to_integer(@block.gas_used) / Decimal.to_integer(@block.gas_limit)) |> BlockScoutWeb.Cldr.Number.to_string!(format: "#.#%") %>)
<%= gettext "Gas Limit" %>
- <%= Cldr.Number.to_string!(@block.gas_limit) %> + <%= BlockScoutWeb.Cldr.Number.to_string!(@block.gas_limit) %>
<% end %> @@ -146,10 +146,10 @@

<%= gettext "Gas Used" %>

- <%= @block.gas_used |> Cldr.Number.to_string! %> - (<%= (Decimal.to_integer(@block.gas_used) / Decimal.to_integer(@block.gas_limit)) |> Cldr.Number.to_string!(format: "#.#%") %>) + <%= @block.gas_used |> BlockScoutWeb.Cldr.Number.to_string! %> + (<%= (Decimal.to_integer(@block.gas_used) / Decimal.to_integer(@block.gas_limit)) |> BlockScoutWeb.Cldr.Number.to_string!(format: "#.#%") %>)

-

<%= @block.gas_limit |> Cldr.Number.to_string! %><%= gettext "Gas Limit" %>

+

<%= @block.gas_limit |> BlockScoutWeb.Cldr.Number.to_string! %><%= gettext "Gas Limit" %>

<% end %>
diff --git a/apps/block_scout_web/lib/block_scout_web/templates/chain/_block.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/chain/_block.html.eex index 706d18ec47..0c86c88131 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/chain/_block.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/chain/_block.html.eex @@ -20,8 +20,7 @@
<%= if BlockScoutWeb.BlockView.show_reward?(@block.rewards) do %>
- <%= gettext "Reward" %> - <%= BlockScoutWeb.BlockView.combined_rewards_value(@block) %> + <%= gettext "Reward" %> <%= BlockScoutWeb.BlockView.combined_rewards_value(@block) %>
<% end %>
diff --git a/apps/block_scout_web/lib/block_scout_web/templates/chain/show.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/chain/show.html.eex index 53b21cb3fa..8285d429e0 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/chain/show.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/chain/show.html.eex @@ -56,7 +56,7 @@ <%= gettext "Total transactions" %> - <%= Cldr.Number.to_string!(@transaction_estimated_count, format: "#,###") %> + <%= BlockScoutWeb.Cldr.Number.to_string!(@transaction_estimated_count, format: "#,###") %>
@@ -64,7 +64,7 @@ <%= gettext "Total blocks" %> - <%= Cldr.Number.to_string!(@block_count, format: "#,###") %> + <%= BlockScoutWeb.Cldr.Number.to_string!(@block_count, format: "#,###") %>
@@ -72,7 +72,7 @@ <%= gettext "Wallet addresses" %> - <%= Cldr.Number.to_string!(@address_count, format: "#,###") %> + <%= BlockScoutWeb.Cldr.Number.to_string!(@address_count, format: "#,###") %>
diff --git a/apps/block_scout_web/lib/block_scout_web/templates/common_components/_pagination_container.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_pagination_container.html.eex index f9992c6d86..dfe7c51f30 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/common_components/_pagination_container.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_pagination_container.html.eex @@ -14,15 +14,14 @@