Commit 3d600593 authored by tom goriunov's avatar tom goriunov Committed by GitHub

Add ENV variable allowing to choose default color schema (#1882)

* Add ENV variable allowing to choose default color schema

Fixes #1868

* Update values.yaml.gotmpl

---------
Co-authored-by: default avatarYan Vaskov <72267126+yvaskov@users.noreply.github.com>
parent 3acc8615
...@@ -2,6 +2,9 @@ import type { ContractCodeIde } from 'types/client/contract'; ...@@ -2,6 +2,9 @@ import type { ContractCodeIde } from 'types/client/contract';
import { NAVIGATION_LINK_IDS, type NavItemExternal, type NavigationLinkId } from 'types/client/navigation-items'; import { NAVIGATION_LINK_IDS, type NavItemExternal, type NavigationLinkId } from 'types/client/navigation-items';
import type { ChainIndicatorId } from 'types/homepage'; import type { ChainIndicatorId } from 'types/homepage';
import type { NetworkExplorer } from 'types/networks'; import type { NetworkExplorer } from 'types/networks';
import type { ColorThemeId } from 'types/settings';
import { COLOR_THEMES } from 'lib/settings/colorTheme';
import * as views from './ui/views'; import * as views from './ui/views';
import { getEnvValue, getExternalAssetFilePath, parseEnvJson } from './utils'; import { getEnvValue, getExternalAssetFilePath, parseEnvJson } from './utils';
...@@ -21,6 +24,11 @@ const hiddenLinks = (() => { ...@@ -21,6 +24,11 @@ const hiddenLinks = (() => {
return result; return result;
})(); })();
const defaultColorTheme = (() => {
const envValue = getEnvValue('NEXT_PUBLIC_COLOR_THEME_DEFAULT') as ColorThemeId | undefined;
return COLOR_THEMES.find((theme) => theme.id === envValue);
})();
// eslint-disable-next-line max-len // eslint-disable-next-line max-len
const HOMEPAGE_PLATE_BACKGROUND_DEFAULT = 'radial-gradient(103.03% 103.03% at 0% 0%, rgba(183, 148, 244, 0.8) 0%, rgba(0, 163, 196, 0.8) 100%), var(--chakra-colors-blue-400)'; const HOMEPAGE_PLATE_BACKGROUND_DEFAULT = 'radial-gradient(103.03% 103.03% at 0% 0%, rgba(183, 148, 244, 0.8) 0%, rgba(0, 163, 196, 0.8) 100%), var(--chakra-colors-blue-400)';
...@@ -70,6 +78,9 @@ const UI = Object.freeze({ ...@@ -70,6 +78,9 @@ const UI = Object.freeze({
items: parseEnvJson<Array<ContractCodeIde>>(getEnvValue('NEXT_PUBLIC_CONTRACT_CODE_IDES')) || [], items: parseEnvJson<Array<ContractCodeIde>>(getEnvValue('NEXT_PUBLIC_CONTRACT_CODE_IDES')) || [],
}, },
hasContractAuditReports: getEnvValue('NEXT_PUBLIC_HAS_CONTRACT_AUDIT_REPORTS') === 'true' ? true : false, hasContractAuditReports: getEnvValue('NEXT_PUBLIC_HAS_CONTRACT_AUDIT_REPORTS') === 'true' ? true : false,
colorTheme: {
'default': defaultColorTheme,
},
}); });
export default UI; export default UI;
...@@ -28,6 +28,7 @@ import type { CustomLink, CustomLinksGroup } from '../../../types/footerLinks'; ...@@ -28,6 +28,7 @@ import type { CustomLink, CustomLinksGroup } from '../../../types/footerLinks';
import { CHAIN_INDICATOR_IDS } from '../../../types/homepage'; import { CHAIN_INDICATOR_IDS } from '../../../types/homepage';
import type { ChainIndicatorId } from '../../../types/homepage'; import type { ChainIndicatorId } from '../../../types/homepage';
import { type NetworkVerificationType, type NetworkExplorer, type FeaturedNetwork, NETWORK_GROUPS } from '../../../types/networks'; import { type NetworkVerificationType, type NetworkExplorer, type FeaturedNetwork, NETWORK_GROUPS } from '../../../types/networks';
import { COLOR_THEME_IDS } from '../../../types/settings';
import type { AddressViewId } from '../../../types/views/address'; import type { AddressViewId } from '../../../types/views/address';
import { ADDRESS_VIEWS_IDS, IDENTICON_TYPES } from '../../../types/views/address'; import { ADDRESS_VIEWS_IDS, IDENTICON_TYPES } from '../../../types/views/address';
import { BLOCK_FIELDS_IDS } from '../../../types/views/block'; import { BLOCK_FIELDS_IDS } from '../../../types/views/block';
...@@ -582,6 +583,7 @@ const schema = yup ...@@ -582,6 +583,7 @@ const schema = yup
NEXT_PUBLIC_HIDE_INDEXING_ALERT_BLOCKS: yup.boolean(), NEXT_PUBLIC_HIDE_INDEXING_ALERT_BLOCKS: yup.boolean(),
NEXT_PUBLIC_HIDE_INDEXING_ALERT_INT_TXS: yup.boolean(), NEXT_PUBLIC_HIDE_INDEXING_ALERT_INT_TXS: yup.boolean(),
NEXT_PUBLIC_MAINTENANCE_ALERT_MESSAGE: yup.string(), NEXT_PUBLIC_MAINTENANCE_ALERT_MESSAGE: yup.string(),
NEXT_PUBLIC_COLOR_THEME_DEFAULT: yup.string().oneOf(COLOR_THEME_IDS),
// 5. Features configuration // 5. Features configuration
NEXT_PUBLIC_API_SPEC_URL: yup.string().test(urlTest), NEXT_PUBLIC_API_SPEC_URL: yup.string().test(urlTest),
......
...@@ -21,6 +21,7 @@ NEXT_PUBLIC_APP_PORT=3000 ...@@ -21,6 +21,7 @@ NEXT_PUBLIC_APP_PORT=3000
NEXT_PUBLIC_APP_PROTOCOL=http NEXT_PUBLIC_APP_PROTOCOL=http
NEXT_PUBLIC_BRIDGED_TOKENS_CHAINS=[{'id':'1','title':'Ethereum','short_title':'ETH','base_url':'https://example.com'}] NEXT_PUBLIC_BRIDGED_TOKENS_CHAINS=[{'id':'1','title':'Ethereum','short_title':'ETH','base_url':'https://example.com'}]
NEXT_PUBLIC_BRIDGED_TOKENS_BRIDGES=[{'type':'omni','title':'OmniBridge','short_title':'OMNI'}] NEXT_PUBLIC_BRIDGED_TOKENS_BRIDGES=[{'type':'omni','title':'OmniBridge','short_title':'OMNI'}]
NEXT_PUBLIC_COLOR_THEME_DEFAULT=dim
NEXT_PUBLIC_CONTRACT_CODE_IDES=[{'title':'Remix IDE','url':'https://remix.blockscout.com/?address={hash}&blockscout={domain}','icon_url':'https://example.com/icon.svg'}] NEXT_PUBLIC_CONTRACT_CODE_IDES=[{'title':'Remix IDE','url':'https://remix.blockscout.com/?address={hash}&blockscout={domain}','icon_url':'https://example.com/icon.svg'}]
NEXT_PUBLIC_CONTRACT_INFO_API_HOST=https://example.com NEXT_PUBLIC_CONTRACT_INFO_API_HOST=https://example.com
NEXT_PUBLIC_DATA_AVAILABILITY_ENABLED=true NEXT_PUBLIC_DATA_AVAILABILITY_ENABLED=true
......
...@@ -94,6 +94,7 @@ frontend: ...@@ -94,6 +94,7 @@ frontend:
NEXT_PUBLIC_AD_ADBUTLER_CONFIG_MOBILE: "{ \"id\": \"632018\", \"width\": \"320\", \"height\": \"100\" }" NEXT_PUBLIC_AD_ADBUTLER_CONFIG_MOBILE: "{ \"id\": \"632018\", \"width\": \"320\", \"height\": \"100\" }"
NEXT_PUBLIC_DATA_AVAILABILITY_ENABLED: true NEXT_PUBLIC_DATA_AVAILABILITY_ENABLED: true
NEXT_PUBLIC_OG_ENHANCED_DATA_ENABLED: true NEXT_PUBLIC_OG_ENHANCED_DATA_ENABLED: true
NEXT_PUBLIC_COLOR_THEME_DEFAULT: "dim"
envFromSecret: envFromSecret:
NEXT_PUBLIC_SENTRY_DSN: ref+vault://deployment-values/blockscout/dev/review?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_SENTRY_DSN NEXT_PUBLIC_SENTRY_DSN: ref+vault://deployment-values/blockscout/dev/review?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_SENTRY_DSN
SENTRY_CSP_REPORT_URI: ref+vault://deployment-values/blockscout/dev/review?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/SENTRY_CSP_REPORT_URI SENTRY_CSP_REPORT_URI: ref+vault://deployment-values/blockscout/dev/review?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/SENTRY_CSP_REPORT_URI
......
...@@ -275,6 +275,7 @@ Settings for meta tags, OG tags and SEO ...@@ -275,6 +275,7 @@ Settings for meta tags, OG tags and SEO
| NEXT_PUBLIC_HIDE_INDEXING_ALERT_BLOCKS | `boolean` | Set to `true` to hide indexing alert in the page header about indexing chain's blocks | - | `false` | `true` | | NEXT_PUBLIC_HIDE_INDEXING_ALERT_BLOCKS | `boolean` | Set to `true` to hide indexing alert in the page header about indexing chain's blocks | - | `false` | `true` |
| NEXT_PUBLIC_HIDE_INDEXING_ALERT_INT_TXS | `boolean` | Set to `true` to hide indexing alert in the page footer about indexing block's internal transactions | - | `false` | `true` | | NEXT_PUBLIC_HIDE_INDEXING_ALERT_INT_TXS | `boolean` | Set to `true` to hide indexing alert in the page footer about indexing block's internal transactions | - | `false` | `true` |
| NEXT_PUBLIC_MAINTENANCE_ALERT_MESSAGE | `string` | Used for displaying custom announcements or alerts in the header of the site. Could be a regular string or a HTML code. | - | - | `Hello world! 🤪` | | NEXT_PUBLIC_MAINTENANCE_ALERT_MESSAGE | `string` | Used for displaying custom announcements or alerts in the header of the site. Could be a regular string or a HTML code. | - | - | `Hello world! 🤪` |
| NEXT_PUBLIC_COLOR_THEME_DEFAULT | `'light' \| 'dim' \| 'midnight' \| 'dark'` | Preferred color theme of the app | - | - | `midnight` |
#### Network explorer configuration properties #### Network explorer configuration properties
......
...@@ -6,11 +6,13 @@ import { ...@@ -6,11 +6,13 @@ import {
import type { ChakraProviderProps } from '@chakra-ui/react'; import type { ChakraProviderProps } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import theme from 'theme';
interface Props extends ChakraProviderProps { interface Props extends ChakraProviderProps {
cookies?: string; cookies?: string;
} }
export function ChakraProvider({ cookies, theme, children }: Props) { export function ChakraProvider({ cookies, children }: Props) {
const colorModeManager = const colorModeManager =
typeof cookies === 'string' ? typeof cookies === 'string' ?
cookieStorageManagerSSR(typeof document !== 'undefined' ? document.cookie : cookies) : cookieStorageManagerSSR(typeof document !== 'undefined' ? document.cookie : cookies) :
......
import type { ColorMode } from '@chakra-ui/react';
import type { ColorThemeId } from 'types/settings';
interface ColorTheme {
id: ColorThemeId;
label: string;
colorMode: ColorMode;
hex: string;
sampleBg: string;
}
export const COLOR_THEMES: Array<ColorTheme> = [
{
id: 'light',
label: 'Light',
colorMode: 'light',
hex: '#FFFFFF',
sampleBg: 'linear-gradient(154deg, #EFEFEF 50%, rgba(255, 255, 255, 0.00) 330.86%)',
},
{
id: 'dim',
label: 'Dim',
colorMode: 'dark',
hex: '#232B37',
sampleBg: 'linear-gradient(152deg, #232B37 50%, rgba(255, 255, 255, 0.00) 290.71%)',
},
{
id: 'midnight',
label: 'Midnight',
colorMode: 'dark',
hex: '#1B2E48',
sampleBg: 'linear-gradient(148deg, #1B3F71 50%, rgba(255, 255, 255, 0.00) 312.35%)',
},
{
id: 'dark',
label: 'Dark',
colorMode: 'dark',
hex: '#101112',
sampleBg: 'linear-gradient(161deg, #000 9.37%, #383838 92.52%)',
},
];
import type { IdenticonType } from 'types/views/address'; import type { IdenticonType } from 'types/views/address';
export const COLOR_THEMES = [
{
label: 'Light',
colorMode: 'light',
hex: '#FFFFFF',
sampleBg: 'linear-gradient(154deg, #EFEFEF 50%, rgba(255, 255, 255, 0.00) 330.86%)',
},
{
label: 'Dim',
colorMode: 'dark',
hex: '#232B37',
sampleBg: 'linear-gradient(152deg, #232B37 50%, rgba(255, 255, 255, 0.00) 290.71%)',
},
{
label: 'Midnight',
colorMode: 'dark',
hex: '#1B2E48',
sampleBg: 'linear-gradient(148deg, #1B3F71 50%, rgba(255, 255, 255, 0.00) 312.35%)',
},
{
label: 'Dark',
colorMode: 'dark',
hex: '#101112',
sampleBg: 'linear-gradient(161deg, #000 9.37%, #383838 92.52%)',
},
];
export type ColorTheme = typeof COLOR_THEMES[number];
export const IDENTICONS: Array<{ label: string; id: IdenticonType; sampleBg: string }> = [ export const IDENTICONS: Array<{ label: string; id: IdenticonType; sampleBg: string }> = [
{ {
label: 'GitHub', label: 'GitHub',
......
...@@ -19,8 +19,12 @@ export function middleware(req: NextRequest) { ...@@ -19,8 +19,12 @@ export function middleware(req: NextRequest) {
return accountResponse; return accountResponse;
} }
const end = Date.now();
const res = NextResponse.next(); const res = NextResponse.next();
middlewares.colorTheme(req, res);
const end = Date.now();
res.headers.append('Content-Security-Policy', cspPolicy); res.headers.append('Content-Security-Policy', cspPolicy);
res.headers.append('Server-Timing', `middleware;dur=${ end - start }`); res.headers.append('Server-Timing', `middleware;dur=${ end - start }`);
res.headers.append('Docker-ID', process.env.HOSTNAME || ''); res.headers.append('Docker-ID', process.env.HOSTNAME || '');
......
import type { NextRequest, NextResponse } from 'next/server';
import appConfig from 'configs/app';
import * as cookiesLib from 'lib/cookies';
export default function colorThemeMiddleware(req: NextRequest, res: NextResponse) {
const colorModeCookie = req.cookies.get(cookiesLib.NAMES.COLOR_MODE);
if (!colorModeCookie) {
if (appConfig.UI.colorTheme.default) {
res.cookies.set(cookiesLib.NAMES.COLOR_MODE, appConfig.UI.colorTheme.default.colorMode, { path: '/' });
res.cookies.set(cookiesLib.NAMES.COLOR_MODE_HEX, appConfig.UI.colorTheme.default.hex, { path: '/' });
}
}
}
export { account } from './account'; export { account } from './account';
export { default as colorTheme } from './colorTheme';
...@@ -18,7 +18,6 @@ import { growthBook } from 'lib/growthbook/init'; ...@@ -18,7 +18,6 @@ import { growthBook } from 'lib/growthbook/init';
import useLoadFeatures from 'lib/growthbook/useLoadFeatures'; import useLoadFeatures from 'lib/growthbook/useLoadFeatures';
import useNotifyOnNavigation from 'lib/hooks/useNotifyOnNavigation'; import useNotifyOnNavigation from 'lib/hooks/useNotifyOnNavigation';
import { SocketProvider } from 'lib/socket/context'; import { SocketProvider } from 'lib/socket/context';
import theme from 'theme';
import AppErrorBoundary from 'ui/shared/AppError/AppErrorBoundary'; import AppErrorBoundary from 'ui/shared/AppError/AppErrorBoundary';
import GoogleAnalytics from 'ui/shared/GoogleAnalytics'; import GoogleAnalytics from 'ui/shared/GoogleAnalytics';
import Layout from 'ui/shared/layout/Layout'; import Layout from 'ui/shared/layout/Layout';
...@@ -57,7 +56,7 @@ function MyApp({ Component, pageProps }: AppPropsWithLayout) { ...@@ -57,7 +56,7 @@ function MyApp({ Component, pageProps }: AppPropsWithLayout) {
const getLayout = Component.getLayout ?? ((page) => <Layout>{ page }</Layout>); const getLayout = Component.getLayout ?? ((page) => <Layout>{ page }</Layout>);
return ( return (
<ChakraProvider theme={ theme } cookies={ pageProps.cookies }> <ChakraProvider cookies={ pageProps.cookies }>
<AppErrorBoundary <AppErrorBoundary
{ ...ERROR_SCREEN_STYLES } { ...ERROR_SCREEN_STYLES }
onError={ handleError } onError={ handleError }
......
import { type ThemeConfig } from '@chakra-ui/react'; import { type ThemeConfig } from '@chakra-ui/react';
import appConfig from 'configs/app';
const config: ThemeConfig = { const config: ThemeConfig = {
initialColorMode: 'system', initialColorMode: appConfig.UI.colorTheme.default?.colorMode ?? 'system',
useSystemColorMode: false, useSystemColorMode: false,
disableTransitionOnChange: false, disableTransitionOnChange: false,
}; };
......
export const COLOR_THEME_IDS = [ 'light', 'dim', 'midnight', 'dark' ] as const;
export type ColorThemeId = typeof COLOR_THEME_IDS[number];
...@@ -2,9 +2,9 @@ import { Box, Flex, useColorMode } from '@chakra-ui/react'; ...@@ -2,9 +2,9 @@ import { Box, Flex, useColorMode } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import * as cookies from 'lib/cookies'; import * as cookies from 'lib/cookies';
import { COLOR_THEMES } from 'lib/settings/colorTheme';
import SettingsSample from './SettingsSample'; import SettingsSample from './SettingsSample';
import { COLOR_THEMES } from './utils';
const SettingsColorTheme = () => { const SettingsColorTheme = () => {
const { setColorMode } = useColorMode(); const { setColorMode } = useColorMode();
......
...@@ -3,9 +3,9 @@ import React from 'react'; ...@@ -3,9 +3,9 @@ import React from 'react';
import config from 'configs/app'; import config from 'configs/app';
import * as cookies from 'lib/cookies'; import * as cookies from 'lib/cookies';
import { IDENTICONS } from 'lib/settings/identIcon';
import SettingsSample from './SettingsSample'; import SettingsSample from './SettingsSample';
import { IDENTICONS } from './utils';
const SettingsIdentIcon = () => { const SettingsIdentIcon = () => {
const [ activeId, setActiveId ] = React.useState<string>(); const [ activeId, setActiveId ] = React.useState<string>();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment