Permanent dark mode

pull/6763/head
Viktor Baranov 2 years ago
parent ce787a24f5
commit d17d9c95db
  1. 1
      CHANGELOG.md
  2. 16
      apps/block_scout_web/assets/js/lib/history_chart.js
  3. 26
      apps/block_scout_web/assets/js/pages/dark-mode-switcher.js
  4. 7
      apps/block_scout_web/assets/package-lock.json
  5. 2
      apps/block_scout_web/assets/package.json
  6. 9
      apps/block_scout_web/lib/block_scout_web/templates/csv_export/index.html.eex
  7. 119
      apps/block_scout_web/lib/block_scout_web/templates/layout/app.html.eex
  8. 2
      apps/explorer/package.json
  9. 3
      config/runtime.exs

@ -4,6 +4,7 @@
### Features ### Features
- [#6763](https://github.com/blockscout/blockscout/pull/6763) - Permanent UI dark mode
- [#6721](https://github.com/blockscout/blockscout/pull/6721) - Implement fetching internal transactions from callTracer - [#6721](https://github.com/blockscout/blockscout/pull/6721) - Implement fetching internal transactions from callTracer
- [#6712](https://github.com/blockscout/blockscout/pull/6712) - API v2 update - [#6712](https://github.com/blockscout/blockscout/pull/6712) - API v2 update

@ -18,8 +18,18 @@ const grid = {
drawOnChartArea: false drawOnChartArea: false
} }
function isDarkMode () {
// @ts-ignore
const permanentDarkModeEnabled = document.getElementById('permanent-dark-mode').textContent === 'true'
if (!permanentDarkModeEnabled) {
return Cookies.get('chakra-ui-color-mode') === 'dark'
} else {
return true
}
}
function getTxChartColor () { function getTxChartColor () {
if (Cookies.get('chakra-ui-color-mode') === 'dark') { if ((isDarkMode())) {
return sassVariables.dashboardLineColorTransactionsDarkTheme return sassVariables.dashboardLineColorTransactionsDarkTheme
} else { } else {
return sassVariables.dashboardLineColorTransactions return sassVariables.dashboardLineColorTransactions
@ -27,7 +37,7 @@ function getTxChartColor () {
} }
function getPriceChartColor () { function getPriceChartColor () {
if (Cookies.get('chakra-ui-color-mode') === 'dark') { if ((isDarkMode())) {
return sassVariables.dashboardLineColorPriceDarkTheme return sassVariables.dashboardLineColorPriceDarkTheme
} else { } else {
return sassVariables.dashboardLineColorPrice return sassVariables.dashboardLineColorPrice
@ -35,7 +45,7 @@ function getPriceChartColor () {
} }
function getMarketCapChartColor () { function getMarketCapChartColor () {
if (Cookies.get('chakra-ui-color-mode') === 'dark') { if ((isDarkMode())) {
return sassVariables.dashboardLineColorMarketDarkTheme return sassVariables.dashboardLineColorMarketDarkTheme
} else { } else {
return sassVariables.dashboardLineColorMarket return sassVariables.dashboardLineColorMarket

@ -1,12 +1,22 @@
import $ from 'jquery'
import Cookies from 'js-cookie' import Cookies from 'js-cookie'
const permantDarkModeEl = document.getElementById('permanent-dark-mode')
// @ts-ignore
const permanentDarkModeEnabled = false || (permantDarkModeEl && permantDarkModeEl.textContent === 'true')
// @ts-ignore
const darkModeChangerEl = document.getElementsByClassName('dark-mode-changer')[0]
$('.dark-mode-changer').on('click', function () { if (permanentDarkModeEnabled) {
if (Cookies.get('chakra-ui-color-mode') === 'dark') { // @ts-ignore
Cookies.set('chakra-ui-color-mode', 'light') darkModeChangerEl.style.display = 'none'
} else { }
Cookies.set('chakra-ui-color-mode', 'dark')
darkModeChangerEl && darkModeChangerEl.addEventListener('click', function () {
if (!permanentDarkModeEnabled) {
if (Cookies.get('chakra-ui-color-mode') === 'dark') {
Cookies.set('chakra-ui-color-mode', 'light')
} else {
Cookies.set('chakra-ui-color-mode', 'dark')
}
document.location.reload()
} }
// reload each theme switch
document.location.reload()
}) })

@ -95,15 +95,16 @@
"webpack-cli": "^5.0.1" "webpack-cli": "^5.0.1"
}, },
"engines": { "engines": {
"node": "16.x", "node": "18.x",
"npm": "8.x" "npm": "8.x"
} }
}, },
"../../../deps/phoenix": { "../../../deps/phoenix": {
"version": "0.0.1" "version": "1.5.13",
"license": "MIT"
}, },
"../../../deps/phoenix_html": { "../../../deps/phoenix_html": {
"version": "0.0.1" "version": "3.0.4"
}, },
"node_modules/@amplitude/analytics-browser": { "node_modules/@amplitude/analytics-browser": {
"version": "1.6.8", "version": "1.6.8",

@ -8,7 +8,7 @@
"author": "Blockscout", "author": "Blockscout",
"license": "GPL-3.0", "license": "GPL-3.0",
"engines": { "engines": {
"node": "16.x", "node": "18.x",
"npm": "8.x" "npm": "8.x"
}, },
"scripts": { "scripts": {

@ -32,7 +32,14 @@
if (reCaptchaClientKey) { if (reCaptchaClientKey) {
widgetId1 = grecaptcha.render('recaptcha', { widgetId1 = grecaptcha.render('recaptcha', {
'sitekey': reCaptchaClientKey, 'sitekey': reCaptchaClientKey,
'theme': getCookie('chakra-ui-color-mode'), 'theme': function () {
const permanentDarkModeEnabled = document.getElementById('permanent-dark-mode').textContent === 'true'
if (permanentDarkModeEnabled) {
return 'dark'
} else {
return getCookie('chakra-ui-color-mode')
}
},
'callback': function () { 'callback': function () {
document.getElementById('export-csv-button').disabled = false document.getElementById('export-csv-button').disabled = false
} }

@ -52,59 +52,6 @@
</head> </head>
<body> <body>
<script defer data-cfasync="false">
function getCookie(name) {
const value = `; ${document.cookie}`;
const parts = value.split(`; ${name}=`);
if (parts.length === 2) return parts.pop().split(';').shift();
}
function applyDarkMode() {
if (getCookie('chakra-ui-color-mode') === "dark") {
document.body.className += " " + "dark-theme-applied";
document.body.style.backgroundColor = "#1c1d31";
}
}
window.onload = applyDarkMode()
</script>
<script defer data-cfasync="false">
if (getCookie('chakra-ui-color-mode') === "dark") {
if (document.getElementById("top-navbar")) {
document.getElementById("top-navbar").style.backgroundColor = "#282945";
}
if (document.getElementById("navbar-logo")) {
document.getElementById("navbar-logo").style.filter = "brightness(0) invert(1)";
}
let modeChanger = document.getElementById("dark-mode-changer");
if (modeChanger) {
modeChanger.className += " " + "dark-mode-changer--dark";
}
}
</script>
<script defer data-cfasync="false">
if (getCookie('chakra-ui-color-mode') === "dark") {
const search = document.getElementById("main-search-autocomplete")
const searchMobile = document.getElementById("main-search-autocomplete-mobile")
if (search && search.style) {
search.style.backgroundColor = "#22223a";
search.style.borderColor = "#22223a";
}
if (searchMobile && searchMobile.style) {
searchMobile.style.backgroundColor = "#22223a";
searchMobile.style.borderColor = "#22223a";
}
}
</script>
<% raw_dark_forest_addresses_0_4 = CustomContractsHelpers.get_raw_custom_addresses_list(:dark_forest_addresses) || "" %>
<% raw_dark_forest_addresses_0_5 = CustomContractsHelpers.get_raw_custom_addresses_list(:dark_forest_addresses_v_0_5) || "" %>
<% raw_dark_forest_addresses_0_6 = CustomContractsHelpers.get_raw_custom_addresses_list(:dark_forest_addresses_v_0_6) || "" %>
<% raw_dark_forest_addresses_0_6_r2 = CustomContractsHelpers.get_raw_custom_addresses_list(:dark_forest_addresses_v_0_6_r2) || "" %>
<% raw_dark_forest_addresses = raw_dark_forest_addresses_0_4 %>
<% raw_dark_forest_addresses = if raw_dark_forest_addresses_0_5 !== "", do: raw_dark_forest_addresses <> "," <> raw_dark_forest_addresses_0_5, else: raw_dark_forest_addresses %>
<% raw_dark_forest_addresses = if raw_dark_forest_addresses_0_6 !== "", do: raw_dark_forest_addresses <> "," <> raw_dark_forest_addresses_0_6, else: raw_dark_forest_addresses %>
<% raw_dark_forest_addresses = if raw_dark_forest_addresses_0_6_r2 !== "", do: raw_dark_forest_addresses <> "," <> raw_dark_forest_addresses_0_6_r2, else: raw_dark_forest_addresses %>
<% raw_circles_addresses = CustomContractsHelpers.get_raw_custom_addresses_list(:circles_addresses) %>
<%= cond do %> <%= cond do %>
<% ( <% (
@view_module == Elixir.BlockScoutWeb.TransactionInternalTransactionView || @view_module == Elixir.BlockScoutWeb.TransactionInternalTransactionView ||
@ -117,10 +64,6 @@
<% {:ok, created_from_address} = if @transaction.to_address_hash, do: Chain.hash_to_address(@transaction.to_address_hash), else: {:ok, nil} %> <% {:ok, created_from_address} = if @transaction.to_address_hash, do: Chain.hash_to_address(@transaction.to_address_hash), else: {:ok, nil} %>
<% created_from_address_hash_str = if from_address_hash(created_from_address), do: "0x" <> Base.encode16(from_address_hash(created_from_address).bytes, case: :lower), else: nil %> <% created_from_address_hash_str = if from_address_hash(created_from_address), do: "0x" <> Base.encode16(from_address_hash(created_from_address).bytes, case: :lower), else: nil %>
<script> <script>
function applyCustomMode() {
applyCustomTheme("<%= raw_dark_forest_addresses %>", "dark-forest-theme-applied")
applyCustomTheme("<%= raw_circles_addresses %>", "circles-theme-applied")
}
function applyCustomTheme(contractAddressHashesRaw, customClass) { function applyCustomTheme(contractAddressHashesRaw, customClass) {
if (contractAddressHashesRaw !== "") { if (contractAddressHashesRaw !== "") {
const contractAddressHashes = contractAddressHashesRaw.split(',').map(hash => hash.toLowerCase()) const contractAddressHashes = contractAddressHashesRaw.split(',').map(hash => hash.toLowerCase())
@ -158,19 +101,15 @@
) -> %> ) -> %>
<% created_from_address = if @address && from_address_hash(@address), do: "0x" <> Base.encode16(from_address_hash(@address).bytes, case: :lower), else: nil %> <% created_from_address = if @address && from_address_hash(@address), do: "0x" <> Base.encode16(from_address_hash(@address).bytes, case: :lower), else: nil %>
<script> <script>
function applyCustomMode() {
applyCustomTheme("<%= raw_dark_forest_addresses %>", "dark-forest-theme-applied")
applyCustomTheme("<%= raw_circles_addresses %>", "circles-theme-applied")
}
function applyCustomTheme(contractAddressHashesRaw, customClass) { function applyCustomTheme(contractAddressHashesRaw, customClass) {
if (contractAddressHashesRaw !== "") { if (contractAddressHashesRaw !== "") {
const contractAddressHashes = contractAddressHashesRaw.split(',').map(hash => hash.toLowerCase()) const contractAddressHashes = contractAddressHashesRaw.split(',').map(hash => hash.toLowerCase())
const created_from_address = "<%= created_from_address %>" const createdFromAddress = "<%= created_from_address %>"
contractAddressHashes.forEach(contractAddressHash => { contractAddressHashes.forEach(contractAddressHash => {
if (window.location.pathname.toLowerCase().includes(contractAddressHash)) { if (window.location.pathname.toLowerCase().includes(contractAddressHash)) {
document.body.className += " " + customClass; document.body.className += " " + customClass;
return; return;
} else if (contractAddressHash == created_from_address) { } else if (contractAddressHash == createdFromAddress) {
document.body.className += " " + customClass; document.body.className += " " + customClass;
return; return;
} }
@ -178,7 +117,6 @@
} }
} }
window.onload = applyCustomMode()
</script> </script>
<% ( <% (
@view_module == Elixir.BlockScoutWeb.Tokens.TransferView || @view_module == Elixir.BlockScoutWeb.Tokens.TransferView ||
@ -191,10 +129,6 @@
<% {:ok, created_from_address} = if @token && @token.contract_address_hash, do: Chain.hash_to_address(@token.contract_address_hash), else: {:ok, nil} %> <% {:ok, created_from_address} = if @token && @token.contract_address_hash, do: Chain.hash_to_address(@token.contract_address_hash), else: {:ok, nil} %>
<% created_from_address_hash = if from_address_hash(created_from_address), do: "0x" <> Base.encode16(from_address_hash(created_from_address).bytes, case: :lower), else: nil %> <% created_from_address_hash = if from_address_hash(created_from_address), do: "0x" <> Base.encode16(from_address_hash(created_from_address).bytes, case: :lower), else: nil %>
<script> <script>
function applyCustomMode() {
applyCustomTheme("<%= raw_dark_forest_addresses %>", "dark-forest-theme-applied")
applyCustomTheme("<%= raw_circles_addresses %>", "circles-theme-applied")
}
function applyCustomTheme(contractAddressHashesRaw, customClass) { function applyCustomTheme(contractAddressHashesRaw, customClass) {
if (contractAddressHashesRaw !== "") { if (contractAddressHashesRaw !== "") {
const contractAddressHashes = contractAddressHashesRaw.split(',').map(hash => hash.toLowerCase()) const contractAddressHashes = contractAddressHashesRaw.split(',').map(hash => hash.toLowerCase())
@ -217,6 +151,7 @@
<%= nil %> <%= nil %>
<% end %> <% end %>
<div class="layout-container"> <div class="layout-container">
<div id="permanent-dark-mode" class="d-none" ><%= Application.get_env(:block_scout_web, :permanent_dark_mode_enabled) %></div>
<% show_maintenance_alert = Application.get_env(:block_scout_web, BlockScoutWeb.Chain)[:show_maintenance_alert] %> <% show_maintenance_alert = Application.get_env(:block_scout_web, BlockScoutWeb.Chain)[:show_maintenance_alert] %>
<%= if show_maintenance_alert do %> <%= if show_maintenance_alert do %>
<div class="alert alert-warning text-center mb-0 p-3" data-selector="indexed-status"> <div class="alert alert-warning text-center mb-0 p-3" data-selector="indexed-status">
@ -310,5 +245,53 @@
<%= if @view_module in [Elixir.BlockScoutWeb.AddressContractVerificationViaMultiPartFilesView, Elixir.BlockScoutWeb.AddressContractVerificationViaJsonView, Elixir.BlockScoutWeb.AddressContractVerificationViaStandardJsonInputView] do %> <%= if @view_module in [Elixir.BlockScoutWeb.AddressContractVerificationViaMultiPartFilesView, Elixir.BlockScoutWeb.AddressContractVerificationViaJsonView, Elixir.BlockScoutWeb.AddressContractVerificationViaStandardJsonInputView] do %>
<script defer data-cfasync="false" src="<%= static_path(@conn, "/js/dropzone.js") %>"></script> <script defer data-cfasync="false" src="<%= static_path(@conn, "/js/dropzone.js") %>"></script>
<% end %> <% end %>
<script defer data-cfasync="false">
function getCookie(name) {
const value = `; ${document.cookie}`;
const parts = value.split(`; ${name}=`);
if (parts.length === 2) return parts.pop().split(';').shift();
}
function isDarkMode() {
const permanentDarkModeEnabled = document.getElementById('permanent-dark-mode').textContent === 'true'
if (!permanentDarkModeEnabled) {
return getCookie('chakra-ui-color-mode') === 'dark'
} else {
return true
}
}
function applyDarkMode() {
if (isDarkMode()) {
document.body.className += " " + "dark-theme-applied";
document.body.style.backgroundColor = "#1c1d31";
}
}
window.onload = applyDarkMode()
if (isDarkMode()) {
if (document.getElementById("top-navbar")) {
document.getElementById("top-navbar").style.backgroundColor = "#282945";
}
if (document.getElementById("navbar-logo")) {
document.getElementById("navbar-logo").style.filter = "brightness(0) invert(1)";
}
let modeChanger = document.getElementById("dark-mode-changer");
if (modeChanger) {
modeChanger.className += " " + "dark-mode-changer--dark";
}
const search = document.getElementById("main-search-autocomplete")
const searchMobile = document.getElementById("main-search-autocomplete-mobile")
if (search && search.style) {
search.style.backgroundColor = "#22223a";
search.style.borderColor = "#22223a";
}
if (searchMobile && searchMobile.style) {
searchMobile.style.backgroundColor = "#22223a";
searchMobile.style.borderColor = "#22223a";
}
}
</script>
</body> </body>
</html> </html>

@ -8,7 +8,7 @@
"author": "Blockscout", "author": "Blockscout",
"license": "GPL-3.0", "license": "GPL-3.0",
"engines": { "engines": {
"node": "16.x", "node": "18.x",
"npm": "8.x" "npm": "8.x"
}, },
"scripts": {}, "scripts": {},

@ -100,7 +100,8 @@ config :block_scout_web,
new_tags: System.get_env("NEW_TAGS"), new_tags: System.get_env("NEW_TAGS"),
chain_id: System.get_env("CHAIN_ID"), chain_id: System.get_env("CHAIN_ID"),
json_rpc: System.get_env("JSON_RPC"), json_rpc: System.get_env("JSON_RPC"),
verification_max_libraries: verification_max_libraries verification_max_libraries: verification_max_libraries,
permanent_dark_mode_enabled: System.get_env("PERMANENT_DARK_MODE_ENABLED", "false") == "true"
default_api_rate_limit = 50 default_api_rate_limit = 50
default_api_rate_limit_str = Integer.to_string(default_api_rate_limit) default_api_rate_limit_str = Integer.to_string(default_api_rate_limit)

Loading…
Cancel
Save