Commit eb86be62 authored by tom goriunov's avatar tom goriunov Committed by GitHub

Hero banner: more customizations (#2199)

* add ENVs to configure typefaces

* migrate current evns to new config

* add env to configure the banner border

* make button variants for hero and header

* add customization for hero button

* tests for new button variants

* envs for demo

* update button styles and tests

* add tests for banner

* remove unnecessary test

* remove demo values
parent fc1deede
import type { ContractCodeIde } from 'types/client/contract'; import type { ContractCodeIde } from 'types/client/contract';
import { NAVIGATION_LINK_IDS, type NavItemExternal, type NavigationLinkId, type NavigationLayout } from 'types/client/navigation'; import { NAVIGATION_LINK_IDS, type NavItemExternal, type NavigationLinkId, type NavigationLayout } from 'types/client/navigation';
import type { ChainIndicatorId } from 'types/homepage'; import type { ChainIndicatorId, HeroBannerConfig } from 'types/homepage';
import type { NetworkExplorer } from 'types/networks'; import type { NetworkExplorer } from 'types/networks';
import type { ColorThemeId } from 'types/settings'; import type { ColorThemeId } from 'types/settings';
import type { FontFamily } from 'types/ui';
import { COLOR_THEMES } from 'lib/settings/colorTheme'; import { COLOR_THEMES } from 'lib/settings/colorTheme';
...@@ -34,9 +35,6 @@ const defaultColorTheme = (() => { ...@@ -34,9 +35,6 @@ const defaultColorTheme = (() => {
return COLOR_THEMES.find((theme) => theme.id === envValue); return COLOR_THEMES.find((theme) => theme.id === envValue);
})(); })();
// 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 UI = Object.freeze({ const UI = Object.freeze({
navigation: { navigation: {
logo: { logo: {
...@@ -60,9 +58,10 @@ const UI = Object.freeze({ ...@@ -60,9 +58,10 @@ const UI = Object.freeze({
}, },
homepage: { homepage: {
charts: parseEnvJson<Array<ChainIndicatorId>>(getEnvValue('NEXT_PUBLIC_HOMEPAGE_CHARTS')) || [], charts: parseEnvJson<Array<ChainIndicatorId>>(getEnvValue('NEXT_PUBLIC_HOMEPAGE_CHARTS')) || [],
heroBanner: parseEnvJson<HeroBannerConfig>(getEnvValue('NEXT_PUBLIC_HOMEPAGE_HERO_BANNER_CONFIG')),
plate: { plate: {
background: getEnvValue('NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND') || HOMEPAGE_PLATE_BACKGROUND_DEFAULT, background: getEnvValue('NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND'),
textColor: getEnvValue('NEXT_PUBLIC_HOMEPAGE_PLATE_TEXT_COLOR') || 'white', textColor: getEnvValue('NEXT_PUBLIC_HOMEPAGE_PLATE_TEXT_COLOR'),
}, },
showAvgBlockTime: getEnvValue('NEXT_PUBLIC_HOMEPAGE_SHOW_AVG_BLOCK_TIME') === 'false' ? false : true, showAvgBlockTime: getEnvValue('NEXT_PUBLIC_HOMEPAGE_SHOW_AVG_BLOCK_TIME') === 'false' ? false : true,
}, },
...@@ -88,6 +87,10 @@ const UI = Object.freeze({ ...@@ -88,6 +87,10 @@ const UI = Object.freeze({
colorTheme: { colorTheme: {
'default': defaultColorTheme, 'default': defaultColorTheme,
}, },
fonts: {
heading: parseEnvJson<FontFamily>(getEnvValue('NEXT_PUBLIC_FONT_FAMILY_HEADING')),
body: parseEnvJson<FontFamily>(getEnvValue('NEXT_PUBLIC_FONT_FAMILY_BODY')),
},
}); });
export default UI; export default UI;
...@@ -20,6 +20,7 @@ async function run() { ...@@ -20,6 +20,7 @@ async function run() {
return result; return result;
}, {} as Record<string, string>); }, {} as Record<string, string>);
printDeprecationWarning(appEnvs);
await checkPlaceholdersCongruity(appEnvs); await checkPlaceholdersCongruity(appEnvs);
await validateEnvs(appEnvs); await validateEnvs(appEnvs);
...@@ -135,3 +136,15 @@ function getEnvsPlaceholders(filePath: string): Promise<Array<string>> { ...@@ -135,3 +136,15 @@ function getEnvsPlaceholders(filePath: string): Promise<Array<string>> {
}); });
}); });
} }
function printDeprecationWarning(envsMap: Record<string, string>) {
if (
envsMap.NEXT_PUBLIC_HOMEPAGE_PLATE_TEXT_COLOR ||
envsMap.NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND
) {
console.log('❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗');
// eslint-disable-next-line max-len
console.warn('The NEXT_PUBLIC_HOMEPAGE_PLATE_TEXT_COLOR and NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND variables are now deprecated and will be removed in the next release. Please migrate to the NEXT_PUBLIC_HOMEPAGE_HERO_BANNER_CONFIG variable.');
console.log('❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗\n');
}
}
...@@ -30,9 +30,10 @@ import type { WalletType } from '../../../types/client/wallets'; ...@@ -30,9 +30,10 @@ import type { WalletType } from '../../../types/client/wallets';
import { SUPPORTED_WALLETS } from '../../../types/client/wallets'; import { SUPPORTED_WALLETS } from '../../../types/client/wallets';
import type { CustomLink, CustomLinksGroup } from '../../../types/footerLinks'; 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, HeroBannerButtonState, HeroBannerConfig } from '../../../types/homepage';
import { type NetworkVerificationTypeEnvs, type NetworkExplorer, type FeaturedNetwork, NETWORK_GROUPS } from '../../../types/networks'; import { type NetworkVerificationTypeEnvs, type NetworkExplorer, type FeaturedNetwork, NETWORK_GROUPS } from '../../../types/networks';
import { COLOR_THEME_IDS } from '../../../types/settings'; import { COLOR_THEME_IDS } from '../../../types/settings';
import type { FontFamily } from '../../../types/ui';
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';
...@@ -390,6 +391,34 @@ const navItemExternalSchema: yup.ObjectSchema<NavItemExternal> = yup ...@@ -390,6 +391,34 @@ const navItemExternalSchema: yup.ObjectSchema<NavItemExternal> = yup
url: yup.string().test(urlTest).required(), url: yup.string().test(urlTest).required(),
}); });
const fontFamilySchema: yup.ObjectSchema<FontFamily> = yup
.object()
.transform(replaceQuotes)
.json()
.shape({
name: yup.string().required(),
url: yup.string().test(urlTest).required(),
});
const heroBannerButtonStateSchema: yup.ObjectSchema<HeroBannerButtonState> = yup.object({
background: yup.array().max(2).of(yup.string()),
text_color: yup.array().max(2).of(yup.string()),
});
const heroBannerSchema: yup.ObjectSchema<HeroBannerConfig> = yup.object()
.transform(replaceQuotes)
.json()
.shape({
background: yup.array().max(2).of(yup.string()),
text_color: yup.array().max(2).of(yup.string()),
border: yup.array().max(2).of(yup.string()),
button: yup.object({
_default: heroBannerButtonStateSchema,
_hover: heroBannerButtonStateSchema,
_selected: heroBannerButtonStateSchema,
}),
});
const footerLinkSchema: yup.ObjectSchema<CustomLink> = yup const footerLinkSchema: yup.ObjectSchema<CustomLink> = yup
.object({ .object({
text: yup.string().required(), text: yup.string().required(),
...@@ -540,6 +569,23 @@ const schema = yup ...@@ -540,6 +569,23 @@ const schema = yup
.of(yup.string<ChainIndicatorId>().oneOf(CHAIN_INDICATOR_IDS)), .of(yup.string<ChainIndicatorId>().oneOf(CHAIN_INDICATOR_IDS)),
NEXT_PUBLIC_HOMEPAGE_PLATE_TEXT_COLOR: yup.string(), NEXT_PUBLIC_HOMEPAGE_PLATE_TEXT_COLOR: yup.string(),
NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND: yup.string(), NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND: yup.string(),
NEXT_PUBLIC_HOMEPAGE_HERO_BANNER_CONFIG: yup
.mixed()
.test(
'shape',
(ctx) => {
try {
heroBannerSchema.validateSync(ctx.originalValue);
throw new Error('Unknown validation error');
} catch (error: unknown) {
const message = typeof error === 'object' && error !== null && 'errors' in error && Array.isArray(error.errors) ? error.errors.join(', ') : '';
return 'Invalid schema were provided for NEXT_PUBLIC_HOMEPAGE_HERO_BANNER_CONFIG' + (message ? `: ${ message }` : '');
}
},
(data) => {
const isUndefined = data === undefined;
return isUndefined || heroBannerSchema.isValidSync(data);
}),
NEXT_PUBLIC_HOMEPAGE_SHOW_AVG_BLOCK_TIME: yup.boolean(), NEXT_PUBLIC_HOMEPAGE_SHOW_AVG_BLOCK_TIME: yup.boolean(),
// b. sidebar // b. sidebar
...@@ -634,6 +680,18 @@ const schema = yup ...@@ -634,6 +680,18 @@ const schema = yup
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), NEXT_PUBLIC_COLOR_THEME_DEFAULT: yup.string().oneOf(COLOR_THEME_IDS),
NEXT_PUBLIC_FONT_FAMILY_HEADING: yup
.mixed()
.test('shape', 'Invalid schema were provided for NEXT_PUBLIC_FONT_FAMILY_HEADING', (data) => {
const isUndefined = data === undefined;
return isUndefined || fontFamilySchema.isValidSync(data);
}),
NEXT_PUBLIC_FONT_FAMILY_BODY: yup
.mixed()
.test('shape', 'Invalid schema were provided for NEXT_PUBLIC_FONT_FAMILY_BODY', (data) => {
const isUndefined = data === undefined;
return isUndefined || fontFamilySchema.isValidSync(data);
}),
// 5. Features configuration // 5. Features configuration
NEXT_PUBLIC_API_SPEC_URL: yup NEXT_PUBLIC_API_SPEC_URL: yup
......
...@@ -28,6 +28,8 @@ NEXT_PUBLIC_DATA_AVAILABILITY_ENABLED=true ...@@ -28,6 +28,8 @@ NEXT_PUBLIC_DATA_AVAILABILITY_ENABLED=true
NEXT_PUBLIC_FEATURED_NETWORKS=https://example.com NEXT_PUBLIC_FEATURED_NETWORKS=https://example.com
NEXT_PUBLIC_NAVIGATION_HIGHLIGHTED_ROUTES=['/accounts','/apps'] NEXT_PUBLIC_NAVIGATION_HIGHLIGHTED_ROUTES=['/accounts','/apps']
NEXT_PUBLIC_NAVIGATION_LAYOUT=horizontal NEXT_PUBLIC_NAVIGATION_LAYOUT=horizontal
NEXT_PUBLIC_FONT_FAMILY_HEADING={'name':'Montserrat','url':'https://fonts.googleapis.com/css2?family=Montserrat:wght@400;500;600;700&display=swap'}
NEXT_PUBLIC_FONT_FAMILY_BODY={'name':'Raleway','url':'https://fonts.googleapis.com/css2?family=Raleway:wght@400;500;600;700&display=swap'}
NEXT_PUBLIC_FOOTER_LINKS=https://example.com NEXT_PUBLIC_FOOTER_LINKS=https://example.com
NEXT_PUBLIC_GRAPHIQL_TRANSACTION=0xf7d4972356e6ae44ae948d0cf19ef2beaf0e574c180997e969a2837da15e349d NEXT_PUBLIC_GRAPHIQL_TRANSACTION=0xf7d4972356e6ae44ae948d0cf19ef2beaf0e574c180997e969a2837da15e349d
NEXT_PUBLIC_HIDE_INDEXING_ALERT_BLOCKS=false NEXT_PUBLIC_HIDE_INDEXING_ALERT_BLOCKS=false
...@@ -35,6 +37,7 @@ NEXT_PUBLIC_HIDE_INDEXING_ALERT_INT_TXS=false ...@@ -35,6 +37,7 @@ NEXT_PUBLIC_HIDE_INDEXING_ALERT_INT_TXS=false
NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs'] NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs']
NEXT_PUBLIC_HOMEPAGE_PLATE_TEXT_COLOR='#fff' NEXT_PUBLIC_HOMEPAGE_PLATE_TEXT_COLOR='#fff'
NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND='rgb(255, 145, 0)' NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND='rgb(255, 145, 0)'
NEXT_PUBLIC_HOMEPAGE_HERO_BANNER_CONFIG={'background':['lightpink'],'text_color':['deepskyblue','white'],'border':['3px solid black']}
NEXT_PUBLIC_HOMEPAGE_SHOW_AVG_BLOCK_TIME=true NEXT_PUBLIC_HOMEPAGE_SHOW_AVG_BLOCK_TIME=true
NEXT_PUBLIC_GAS_TRACKER_ENABLED=true NEXT_PUBLIC_GAS_TRACKER_ENABLED=true
NEXT_PUBLIC_GAS_TRACKER_UNITS=['gwei'] NEXT_PUBLIC_GAS_TRACKER_UNITS=['gwei']
......
...@@ -118,9 +118,21 @@ Please be aware that all environment variables prefixed with `NEXT_PUBLIC_` will ...@@ -118,9 +118,21 @@ Please be aware that all environment variables prefixed with `NEXT_PUBLIC_` will
| Variable | Type| Description | Compulsoriness | Default value | Example value | Version | | Variable | Type| Description | Compulsoriness | Default value | Example value | Version |
| --- | --- | --- | --- | --- | --- | --- | | --- | --- | --- | --- | --- | --- | --- |
| NEXT_PUBLIC_HOMEPAGE_CHARTS | `Array<'daily_txs' \| 'coin_price' \| 'secondary_coin_price' \| 'market_cap' \| 'tvl'>` | List of charts displayed on the home page | - | - | `['daily_txs','coin_price','market_cap']` | v1.0.x+ | | NEXT_PUBLIC_HOMEPAGE_CHARTS | `Array<'daily_txs' \| 'coin_price' \| 'secondary_coin_price' \| 'market_cap' \| 'tvl'>` | List of charts displayed on the home page | - | - | `['daily_txs','coin_price','market_cap']` | v1.0.x+ |
| NEXT_PUBLIC_HOMEPAGE_PLATE_TEXT_COLOR | `string` | Text color of the hero plate on the homepage (escape "#" symbol if you use HEX color codes or use rgba-value instead) | - | `white` | `\#DCFE76` | v1.0.x+ | | NEXT_PUBLIC_HOMEPAGE_PLATE_TEXT_COLOR | `string` | Text color of the hero plate on the homepage (escape "#" symbol if you use HEX color codes or use rgba-value instead). **DEPRECATED** _Use `NEXT_PUBLIC_HOMEPAGE_HERO_BANNER_CONFIG` instead_ | - | `white` | `\#DCFE76` | v1.0.x+ |
| NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND | `string` | Background css value for hero plate on the homepage (escape "#" symbol if you use HEX color codes or use rgba-value instead) | - | `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)` | `radial-gradient(at 15% 86%, hsla(350,65%,70%,1) 0px, transparent 50%)` \| `no-repeat bottom 20% right 0px/100% url(https://placekitten/1400/200)` | v1.1.0+ | | NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND | `string` | Background css value for hero plate on the homepage (escape "#" symbol if you use HEX color codes or use rgba-value instead). **DEPRECATED** _Use `NEXT_PUBLIC_HOMEPAGE_HERO_BANNER_CONFIG` instead_ | - | `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)` | `radial-gradient(at 15% 86%, hsla(350,65%,70%,1) 0px, transparent 50%)` \| `no-repeat bottom 20% right 0px/100% url(https://placekitten/1400/200)` | v1.1.0+ |
| NEXT_PUBLIC_HOMEPAGE_SHOW_AVG_BLOCK_TIME | `boolean` | Set to false if average block time is useless for the network | - | `true` | `false` | v1.0.x+ | | NEXT_PUBLIC_HOMEPAGE_SHOW_AVG_BLOCK_TIME | `boolean` | Set to false if average block time is useless for the network | - | `true` | `false` | v1.0.x+ |
| NEXT_PUBLIC_HOMEPAGE_HERO_BANNER_CONFIG | `HeroBannerConfig`, see details [below](#hero-banner-configuration-properties) | Configuration of hero banner appearance. | - | - | See [below](#hero-banner-configuration-properties) | v1.35.0+ |
#### Hero banner configuration properties
_Note_ Here, all values are arrays of up to two strings. The first string represents the value for the light color mode, and the second string represents the value for the dark color mode. If the array contains only one string, it will be used for both color modes.
| Variable | Type| Description | Compulsoriness | Default value | Example value |
| --- | --- | --- | --- | --- | --- |
| background | `[string, string]` | Banner background (could be a solid color, gradient or picture). The string should be a valid `background` CSS property value. | - | `['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)']` | `['lightpink','no-repeat bottom 20% right 0px/100% url(https://placekitten/1400/200)']` |
| text_color | `[string, string]` | Banner text background. The string should be a valid `color` CSS property value. | - | `['white']` | `['lightpink','#DCFE76']` |
| border | `[string, string]` | Banner border. The string should be a valid `border` CSS property value. | - | - | `['1px solid yellow','4px dashed #DCFE76']` |
| button | `Partial<Record<'_default' \| '_hover' \| '_selected', {'background'?: [string, string]; 'text_color?:[string, string]'}>>` | The button on the banner. It has three possible states: `_default`, `_hover`, and `_selected`. The `_selected` state reflects when the user is logged in or their wallet is connected to the app. | - | - | `{'_default':{'background':['deeppink'],'text_color':['white']}}` |
&nbsp; &nbsp;
...@@ -286,6 +298,8 @@ Settings for meta tags, OG tags and SEO ...@@ -286,6 +298,8 @@ Settings for meta tags, OG tags and SEO
| 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` | v1.17.0+ | | 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` | v1.17.0+ |
| 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! 🤪` | v1.13.0+ | | 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! 🤪` | v1.13.0+ |
| NEXT_PUBLIC_COLOR_THEME_DEFAULT | `'light' \| 'dim' \| 'midnight' \| 'dark'` | Preferred color theme of the app | - | - | `midnight` | v1.30.0+ | | NEXT_PUBLIC_COLOR_THEME_DEFAULT | `'light' \| 'dim' \| 'midnight' \| 'dark'` | Preferred color theme of the app | - | - | `midnight` | v1.30.0+ |
| NEXT_PUBLIC_FONT_FAMILY_HEADING | `FontFamily`, see full description [below](#font-family-configuration-properties) | Special typeface to use in page headings (`<h1>`, `<h2>`, etc.) | - | - | `{'name':'Montserrat','url':'https://fonts.googleapis.com/css2?family=Montserrat:wght@400;500;600;700&display=swap'}` | v1.35.0+ |
| NEXT_PUBLIC_FONT_FAMILY_BODY | `FontFamily`, see full description [below](#font-family-configuration-properties) | Main typeface to use in page content elements. | - | - | `{'name':'Raleway','url':'https://fonts.googleapis.com/css2?family=Raleway:wght@400;500;600;700&display=swap'}` | v1.35.0+ |
#### Network explorer configuration properties #### Network explorer configuration properties
...@@ -306,6 +320,13 @@ Settings for meta tags, OG tags and SEO ...@@ -306,6 +320,13 @@ Settings for meta tags, OG tags and SEO
| url | `string` | URL of the IDE with placeholders for contract hash (`{hash}`) and current domain (`{domain}`) | Required | - | `https://remix.blockscout.com/?address={hash}&blockscout={domain}` | | url | `string` | URL of the IDE with placeholders for contract hash (`{hash}`) and current domain (`{domain}`) | Required | - | `https://remix.blockscout.com/?address={hash}&blockscout={domain}` |
| icon_url | `string` | URL of the IDE icon | Required | - | `https://example.com/icon.svg` | | icon_url | `string` | URL of the IDE icon | Required | - | `https://example.com/icon.svg` |
#### Font family configuration properties
| Variable | Type| Description | Compulsoriness | Default value | Example value |
| --- | --- | --- | --- | --- | --- |
| name | `string` | Font family name; used to define the `font-family` CSS property. | Required | - | `Montserrat` |
| url | `string` | URL for external font. Ensure the font supports the following weights: 400, 500, 600, and 700. | Required | - | `https://fonts.googleapis.com/css2?family=Montserrat:wght@400;500;600;700&display=swap` |
&nbsp; &nbsp;
## App features ## App features
......
...@@ -30,6 +30,18 @@ const getCspReportUrl = () => { ...@@ -30,6 +30,18 @@ const getCspReportUrl = () => {
} }
}; };
const externalFontsDomains = (() => {
try {
return [
config.UI.fonts.heading?.url,
config.UI.fonts.body?.url,
]
.filter(Boolean)
.map((urlString) => new URL(urlString))
.map((url) => url.hostname);
} catch (error) {}
})();
export function app(): CspDev.DirectiveDescriptor { export function app(): CspDev.DirectiveDescriptor {
return { return {
'default-src': [ 'default-src': [
...@@ -116,6 +128,7 @@ export function app(): CspDev.DirectiveDescriptor { ...@@ -116,6 +128,7 @@ export function app(): CspDev.DirectiveDescriptor {
'font-src': [ 'font-src': [
KEY_WORDS.DATA, KEY_WORDS.DATA,
...MAIN_DOMAINS, ...MAIN_DOMAINS,
...(externalFontsDomains || []),
], ],
'object-src': [ 'object-src': [
......
...@@ -6,6 +6,7 @@ import React from 'react'; ...@@ -6,6 +6,7 @@ import React from 'react';
import logRequestFromBot from 'nextjs/utils/logRequestFromBot'; import logRequestFromBot from 'nextjs/utils/logRequestFromBot';
import * as serverTiming from 'nextjs/utils/serverTiming'; import * as serverTiming from 'nextjs/utils/serverTiming';
import config from 'configs/app';
import theme from 'theme/theme'; import theme from 'theme/theme';
import * as svgSprite from 'ui/shared/IconSvg'; import * as svgSprite from 'ui/shared/IconSvg';
...@@ -35,11 +36,11 @@ class MyDocument extends Document { ...@@ -35,11 +36,11 @@ class MyDocument extends Document {
<Head> <Head>
{ /* FONTS */ } { /* FONTS */ }
<link <link
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" href={ config.UI.fonts.heading?.url ?? 'https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap' }
rel="stylesheet" rel="stylesheet"
/> />
<link <link
href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap" href={ config.UI.fonts.body?.url ?? 'https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap' }
rel="stylesheet" rel="stylesheet"
/> />
......
...@@ -13,6 +13,8 @@ test.use({ viewport: { width: 150, height: 350 } }); ...@@ -13,6 +13,8 @@ test.use({ viewport: { width: 150, height: 350 } });
{ variant: 'ghost', withDarkMode: true, states: [ 'default', 'hovered', 'active' ] }, { variant: 'ghost', withDarkMode: true, states: [ 'default', 'hovered', 'active' ] },
{ variant: 'subtle', states: [ 'default', 'hovered' ] }, { variant: 'subtle', states: [ 'default', 'hovered' ] },
{ variant: 'subtle', colorScheme: 'gray', states: [ 'default', 'hovered' ], withDarkMode: true }, { variant: 'subtle', colorScheme: 'gray', states: [ 'default', 'hovered' ], withDarkMode: true },
{ variant: 'hero', states: [ 'default', 'hovered' ], withDarkMode: true },
{ variant: 'header', states: [ 'default', 'hovered', 'selected' ], withDarkMode: true },
].forEach(({ variant, colorScheme, withDarkMode, states }) => { ].forEach(({ variant, colorScheme, withDarkMode, states }) => {
test.describe(`variant ${ variant }${ colorScheme ? ` with ${ colorScheme } color scheme` : '' }${ withDarkMode ? ' +@dark-mode' : '' }`, () => { test.describe(`variant ${ variant }${ colorScheme ? ` with ${ colorScheme } color scheme` : '' }${ withDarkMode ? ' +@dark-mode' : '' }`, () => {
test('', async({ render }) => { test('', async({ render }) => {
......
...@@ -2,6 +2,8 @@ import { defineStyle, defineStyleConfig } from '@chakra-ui/styled-system'; ...@@ -2,6 +2,8 @@ import { defineStyle, defineStyleConfig } from '@chakra-ui/styled-system';
import { mode } from '@chakra-ui/theme-tools'; import { mode } from '@chakra-ui/theme-tools';
import { runIfFn } from '@chakra-ui/utils'; import { runIfFn } from '@chakra-ui/utils';
import config from 'configs/app';
const variantSolid = defineStyle((props) => { const variantSolid = defineStyle((props) => {
const { colorScheme: c } = props; const { colorScheme: c } = props;
...@@ -150,12 +152,76 @@ const variantSubtle = defineStyle((props) => { ...@@ -150,12 +152,76 @@ const variantSubtle = defineStyle((props) => {
}; };
}); });
// for buttons in the hero banner
const variantHero = defineStyle((props) => {
return {
bg: mode(
config.UI.homepage.heroBanner?.button?._default?.background?.[0] || 'blue.600',
config.UI.homepage.heroBanner?.button?._default?.background?.[1] || 'blue.600',
)(props),
color: mode(
config.UI.homepage.heroBanner?.button?._default?.text_color?.[0] || 'white',
config.UI.homepage.heroBanner?.button?._default?.text_color?.[1] || 'white',
)(props),
_hover: {
bg: mode(
config.UI.homepage.heroBanner?.button?._hover?.background?.[0] || 'blue.400',
config.UI.homepage.heroBanner?.button?._hover?.background?.[1] || 'blue.400',
)(props),
color: mode(
config.UI.homepage.heroBanner?.button?._hover?.text_color?.[0] || 'white',
config.UI.homepage.heroBanner?.button?._hover?.text_color?.[1] || 'white',
)(props),
},
'&[data-selected=true]': {
bg: mode(
config.UI.homepage.heroBanner?.button?._selected?.background?.[0] || 'blue.50',
config.UI.homepage.heroBanner?.button?._selected?.background?.[1] || 'blue.50',
)(props),
color: mode(
config.UI.homepage.heroBanner?.button?._selected?.text_color?.[0] || 'blackAlpha.800',
config.UI.homepage.heroBanner?.button?._selected?.text_color?.[1] || 'blackAlpha.800',
)(props),
},
};
});
// for buttons in the page header
const variantHeader = defineStyle((props) => {
return {
bgColor: 'transparent',
color: mode('blackAlpha.800', 'gray.400')(props),
borderColor: mode('gray.300', 'gray.600')(props),
borderWidth: props.borderWidth || '2px',
borderStyle: 'solid',
_hover: {
color: 'link_hovered',
borderColor: 'link_hovered',
},
'&[data-selected=true]': {
bgColor: mode('blackAlpha.50', 'whiteAlpha.100')(props),
color: mode('blackAlpha.800', 'whiteAlpha.800')(props),
borderColor: 'transparent',
borderWidth: props.borderWidth || '0px',
},
'&[data-selected=true][data-warning=true]': {
bgColor: mode('orange.100', 'orange.900')(props),
color: mode('blackAlpha.800', 'whiteAlpha.800')(props),
borderColor: 'transparent',
borderWidth: props.borderWidth || '0px',
},
};
});
const variants = { const variants = {
solid: variantSolid, solid: variantSolid,
outline: variantOutline, outline: variantOutline,
simple: variantSimple, simple: variantSimple,
ghost: variantGhost, ghost: variantGhost,
subtle: variantSubtle, subtle: variantSubtle,
hero: variantHero,
header: variantHeader,
}; };
const baseStyle = defineStyle({ const baseStyle = defineStyle({
......
import { theme } from '@chakra-ui/react'; import { theme } from '@chakra-ui/react';
export const BODY_TYPEFACE = 'Inter'; import config from 'configs/app';
export const HEADING_TYPEFACE = 'Poppins';
export const BODY_TYPEFACE = config.UI.fonts.body?.name ?? 'Inter';
export const HEADING_TYPEFACE = config.UI.fonts.heading?.name ?? 'Poppins';
const typography = { const typography = {
fonts: { fonts: {
......
export const CHAIN_INDICATOR_IDS = [ 'daily_txs', 'coin_price', 'secondary_coin_price', 'market_cap', 'tvl' ] as const; export const CHAIN_INDICATOR_IDS = [ 'daily_txs', 'coin_price', 'secondary_coin_price', 'market_cap', 'tvl' ] as const;
export type ChainIndicatorId = typeof CHAIN_INDICATOR_IDS[number]; export type ChainIndicatorId = typeof CHAIN_INDICATOR_IDS[number];
export interface HeroBannerButtonState {
background?: Array<string | undefined>;
text_color?: Array<string | undefined>;
}
export interface HeroBannerConfig {
background?: Array<string | undefined>;
text_color?: Array<string | undefined>;
border?: Array<string | undefined>;
button?: {
_default?: HeroBannerButtonState;
_hover?: HeroBannerButtonState;
_selected?: HeroBannerButtonState;
};
}
export interface FontFamily {
name: string;
url: string;
}
import type { BrowserContext } from '@playwright/test';
import React from 'react';
import * as profileMock from 'mocks/user/profile';
import { contextWithAuth } from 'playwright/fixtures/auth';
import { test, expect } from 'playwright/lib';
import * as pwConfig from 'playwright/utils/config';
import HeroBanner from './HeroBanner';
const authTest = test.extend<{ context: BrowserContext }>({
context: contextWithAuth,
});
authTest('customization +@dark-mode', async({ render, page, mockEnvs, mockApiResponse, mockAssetResponse }) => {
const IMAGE_URL = 'https://localhost:3000/my-image.png';
await mockEnvs([
// eslint-disable-next-line max-len
[ 'NEXT_PUBLIC_HOMEPAGE_HERO_BANNER_CONFIG', `{"background":["lightpink","no-repeat center/cover url(${ IMAGE_URL })"],"text_color":["deepskyblue","white"],"border":["3px solid green","3px dashed yellow"],"button":{"_default":{"background":["deeppink"],"text_color":["white"]},"_selected":{"background":["lime"]}}}` ],
]);
await page.route(IMAGE_URL, (route) => {
return route.fulfill({
status: 200,
path: './playwright/mocks/image_long.jpg',
});
});
await mockApiResponse('user_info', profileMock.base);
await mockAssetResponse(profileMock.base.avatar, './playwright/mocks/image_s.jpg');
const component = await render(<HeroBanner/>);
await expect(component).toHaveScreenshot({
mask: [ page.locator(pwConfig.adsBannerSelector) ],
maskColor: pwConfig.maskColor,
});
});
import { Box, Flex, Heading, useColorModeValue } from '@chakra-ui/react';
import React from 'react';
import config from 'configs/app';
import AdBanner from 'ui/shared/ad/AdBanner';
import ProfileMenuDesktop from 'ui/snippets/profileMenu/ProfileMenuDesktop';
import SearchBar from 'ui/snippets/searchBar/SearchBar';
import WalletMenuDesktop from 'ui/snippets/walletMenu/WalletMenuDesktop';
const 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 TEXT_COLOR_DEFAULT = 'white';
const BORDER_DEFAULT = 'none';
const HeroBanner = () => {
const background = useColorModeValue(
config.UI.homepage.heroBanner?.background?.[0] || config.UI.homepage.plate.background || BACKGROUND_DEFAULT,
config.UI.homepage.heroBanner?.background?.[1] || config.UI.homepage.plate.background || BACKGROUND_DEFAULT,
);
const textColor = useColorModeValue(
config.UI.homepage.heroBanner?.text_color?.[0] || config.UI.homepage.plate.textColor || TEXT_COLOR_DEFAULT,
config.UI.homepage.heroBanner?.text_color?.[1] || config.UI.homepage.plate.textColor || TEXT_COLOR_DEFAULT,
);
const border = useColorModeValue(
config.UI.homepage.heroBanner?.border?.[0] || BORDER_DEFAULT,
config.UI.homepage.heroBanner?.border?.[1] || BORDER_DEFAULT,
);
return (
<Flex
w="100%"
background={ background }
border={ border }
borderRadius="md"
p={{ base: 4, lg: 8 }}
columnGap={ 8 }
alignItems="center"
>
<Box flexGrow={ 1 }>
<Flex mb={{ base: 2, lg: 3 }} justifyContent="space-between" alignItems="center" columnGap={ 2 }>
<Heading
as="h1"
fontSize={{ base: '18px', lg: '30px' }}
lineHeight={{ base: '24px', lg: '36px' }}
fontWeight={{ base: 500, lg: 700 }}
color={ textColor }
>
{
config.meta.seo.enhancedDataEnabled ?
`${ config.chain.name } blockchain explorer` :
`${ config.chain.name } explorer`
}
</Heading>
{ config.UI.navigation.layout === 'vertical' && (
<Box display={{ base: 'none', lg: 'flex' }}>
{ config.features.account.isEnabled && <ProfileMenuDesktop isHomePage/> }
{ config.features.blockchainInteraction.isEnabled && <WalletMenuDesktop isHomePage/> }
</Box>
) }
</Flex>
<SearchBar isHomepage/>
</Box>
<AdBanner platform="mobile" w="fit-content" flexShrink={ 0 } borderRadius="md" overflow="hidden" display={{ base: 'none', lg: 'block ' }}/>
</Flex>
);
};
export default React.memo(HeroBanner);
...@@ -49,33 +49,6 @@ test.describe('default view', () => { ...@@ -49,33 +49,6 @@ test.describe('default view', () => {
}); });
}); });
test.describe('custom hero plate background', () => {
const IMAGE_URL = 'https://localhost:3000/my-image.png';
test.beforeEach(async({ mockEnvs }) => {
await mockEnvs([
[ 'NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND', `no-repeat center/cover url(${ IMAGE_URL })` ],
]);
});
test('default view', async({ render, page }) => {
await page.route(IMAGE_URL, (route) => {
return route.fulfill({
status: 200,
path: './playwright/mocks/image_long.jpg',
});
});
const component = await render(<Home/>);
const heroPlate = component.locator('div[data-label="hero plate"]');
await expect(heroPlate).toHaveScreenshot({
mask: [ page.locator(pwConfig.adsBannerSelector) ],
maskColor: pwConfig.maskColor,
});
});
});
// had to separate mobile test, otherwise all the tests fell on CI // had to separate mobile test, otherwise all the tests fell on CI
test.describe('mobile', () => { test.describe('mobile', () => {
test.use({ viewport: devices['iPhone 13 Pro'].viewport }); test.use({ viewport: devices['iPhone 13 Pro'].viewport });
......
import { Box, Flex, Heading } from '@chakra-ui/react'; import { Box, Flex } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import config from 'configs/app'; import config from 'configs/app';
import HeroBanner from 'ui/home/HeroBanner';
import ChainIndicators from 'ui/home/indicators/ChainIndicators'; import ChainIndicators from 'ui/home/indicators/ChainIndicators';
import LatestArbitrumL2Batches from 'ui/home/latestBatches/LatestArbitrumL2Batches'; import LatestArbitrumL2Batches from 'ui/home/latestBatches/LatestArbitrumL2Batches';
import LatestZkEvmL2Batches from 'ui/home/latestBatches/LatestZkEvmL2Batches'; import LatestZkEvmL2Batches from 'ui/home/latestBatches/LatestZkEvmL2Batches';
...@@ -9,50 +10,13 @@ import LatestBlocks from 'ui/home/LatestBlocks'; ...@@ -9,50 +10,13 @@ import LatestBlocks from 'ui/home/LatestBlocks';
import Stats from 'ui/home/Stats'; import Stats from 'ui/home/Stats';
import Transactions from 'ui/home/Transactions'; import Transactions from 'ui/home/Transactions';
import AdBanner from 'ui/shared/ad/AdBanner'; import AdBanner from 'ui/shared/ad/AdBanner';
import ProfileMenuDesktop from 'ui/snippets/profileMenu/ProfileMenuDesktop';
import SearchBar from 'ui/snippets/searchBar/SearchBar';
import WalletMenuDesktop from 'ui/snippets/walletMenu/WalletMenuDesktop';
const rollupFeature = config.features.rollup; const rollupFeature = config.features.rollup;
const Home = () => { const Home = () => {
return ( return (
<Box as="main"> <Box as="main">
<Flex <HeroBanner/>
w="100%"
background={ config.UI.homepage.plate.background }
borderRadius="md"
p={{ base: 4, lg: 8 }}
columnGap={ 8 }
alignItems="center"
data-label="hero plate"
>
<Box flexGrow={ 1 }>
<Flex mb={{ base: 2, lg: 3 }} justifyContent="space-between" alignItems="center" columnGap={ 2 }>
<Heading
as="h1"
fontSize={{ base: '18px', lg: '30px' }}
lineHeight={{ base: '24px', lg: '36px' }}
fontWeight={{ base: 500, lg: 700 }}
color={ config.UI.homepage.plate.textColor }
>
{
config.meta.seo.enhancedDataEnabled ?
`${ config.chain.name } blockchain explorer` :
`${ config.chain.name } explorer`
}
</Heading>
{ config.UI.navigation.layout === 'vertical' && (
<Box display={{ base: 'none', lg: 'flex' }}>
{ config.features.account.isEnabled && <ProfileMenuDesktop isHomePage/> }
{ config.features.blockchainInteraction.isEnabled && <WalletMenuDesktop isHomePage/> }
</Box>
) }
</Flex>
<SearchBar isHomepage/>
</Box>
<AdBanner platform="mobile" w="fit-content" flexShrink={ 0 } borderRadius="md" overflow="hidden" display={{ base: 'none', lg: 'block ' }}/>
</Flex>
<Flex flexDir={{ base: 'column', lg: 'row' }} columnGap={ 2 } rowGap={ 1 } mt={ 3 } _empty={{ mt: 0 }}> <Flex flexDir={{ base: 'column', lg: 'row' }} columnGap={ 2 } rowGap={ 1 } mt={ 3 } _empty={{ mt: 0 }}>
<Stats/> <Stats/>
<ChainIndicators/> <ChainIndicators/>
......
...@@ -9,8 +9,6 @@ import Popover from 'ui/shared/chakra/Popover'; ...@@ -9,8 +9,6 @@ import Popover from 'ui/shared/chakra/Popover';
import UserAvatar from 'ui/shared/UserAvatar'; import UserAvatar from 'ui/shared/UserAvatar';
import ProfileMenuContent from 'ui/snippets/profileMenu/ProfileMenuContent'; import ProfileMenuContent from 'ui/snippets/profileMenu/ProfileMenuContent';
import useMenuButtonColors from '../useMenuButtonColors';
type Props = { type Props = {
isHomePage?: boolean; isHomePage?: boolean;
className?: string; className?: string;
...@@ -21,7 +19,6 @@ type Props = { ...@@ -21,7 +19,6 @@ type Props = {
const ProfileMenuDesktop = ({ isHomePage, className, fallbackIconSize, buttonBoxSize }: Props) => { const ProfileMenuDesktop = ({ isHomePage, className, fallbackIconSize, buttonBoxSize }: Props) => {
const { data, error, isPending } = useFetchProfileInfo(); const { data, error, isPending } = useFetchProfileInfo();
const loginUrl = useLoginUrl(); const loginUrl = useLoginUrl();
const { themedBackground, themedBorderColor, themedColor } = useMenuButtonColors();
const [ hasMenu, setHasMenu ] = React.useState(false); const [ hasMenu, setHasMenu ] = React.useState(false);
React.useEffect(() => { React.useEffect(() => {
...@@ -50,29 +47,6 @@ const ProfileMenuDesktop = ({ isHomePage, className, fallbackIconSize, buttonBox ...@@ -50,29 +47,6 @@ const ProfileMenuDesktop = ({ isHomePage, className, fallbackIconSize, buttonBox
}; };
})(); })();
const variant = React.useMemo(() => {
if (hasMenu) {
return 'subtle';
}
return isHomePage ? 'solid' : 'outline';
}, [ hasMenu, isHomePage ]);
let iconButtonStyles: Partial<IconButtonProps> = {};
if (hasMenu) {
iconButtonStyles = {
bg: isHomePage ? 'blue.50' : themedBackground,
};
} else if (isHomePage) {
iconButtonStyles = {
color: 'white',
};
} else {
iconButtonStyles = {
borderColor: themedBorderColor,
color: themedColor,
};
}
return ( return (
<Popover openDelay={ 300 } placement="bottom-end" gutter={ 10 } isLazy> <Popover openDelay={ 300 } placement="bottom-end" gutter={ 10 } isLazy>
<Tooltip <Tooltip
...@@ -88,12 +62,11 @@ const ProfileMenuDesktop = ({ isHomePage, className, fallbackIconSize, buttonBox ...@@ -88,12 +62,11 @@ const ProfileMenuDesktop = ({ isHomePage, className, fallbackIconSize, buttonBox
className={ className } className={ className }
aria-label="profile menu" aria-label="profile menu"
icon={ <UserAvatar size={ 20 } fallbackIconSize={ fallbackIconSize }/> } icon={ <UserAvatar size={ 20 } fallbackIconSize={ fallbackIconSize }/> }
variant={ variant } variant={ isHomePage ? 'hero' : 'header' }
colorScheme="blue" data-selected={ hasMenu }
boxSize={ buttonBoxSize ?? '40px' } boxSize={ buttonBoxSize ?? '40px' }
flexShrink={ 0 } flexShrink={ 0 }
{ ...iconButtonProps } { ...iconButtonProps }
{ ...iconButtonStyles }
/> />
</PopoverTrigger> </PopoverTrigger>
</Box> </Box>
......
...@@ -8,13 +8,10 @@ import * as mixpanel from 'lib/mixpanel/index'; ...@@ -8,13 +8,10 @@ import * as mixpanel from 'lib/mixpanel/index';
import UserAvatar from 'ui/shared/UserAvatar'; import UserAvatar from 'ui/shared/UserAvatar';
import ProfileMenuContent from 'ui/snippets/profileMenu/ProfileMenuContent'; import ProfileMenuContent from 'ui/snippets/profileMenu/ProfileMenuContent';
import useMenuButtonColors from '../useMenuButtonColors';
const ProfileMenuMobile = () => { const ProfileMenuMobile = () => {
const { isOpen, onOpen, onClose } = useDisclosure(); const { isOpen, onOpen, onClose } = useDisclosure();
const { data, error, isPending } = useFetchProfileInfo(); const { data, error, isPending } = useFetchProfileInfo();
const loginUrl = useLoginUrl(); const loginUrl = useLoginUrl();
const { themedBackground, themedBorderColor, themedColor } = useMenuButtonColors();
const [ hasMenu, setHasMenu ] = React.useState(false); const [ hasMenu, setHasMenu ] = React.useState(false);
const handleSignInClick = React.useCallback(() => { const handleSignInClick = React.useCallback(() => {
...@@ -48,13 +45,10 @@ const ProfileMenuMobile = () => { ...@@ -48,13 +45,10 @@ const ProfileMenuMobile = () => {
<IconButton <IconButton
aria-label="profile menu" aria-label="profile menu"
icon={ <UserAvatar size={ 20 }/> } icon={ <UserAvatar size={ 20 }/> }
variant={ data?.avatar ? 'subtle' : 'outline' } variant="header"
colorScheme="gray" data-selected={ hasMenu }
boxSize="40px" boxSize="40px"
flexShrink={ 0 } flexShrink={ 0 }
bg={ data?.avatar ? themedBackground : undefined }
color={ themedColor }
borderColor={ !data?.avatar ? themedBorderColor : undefined }
onClick={ hasMenu ? onOpen : undefined } onClick={ hasMenu ? onOpen : undefined }
{ ...iconButtonProps } { ...iconButtonProps }
/> />
......
import { useColorModeValue } from '@chakra-ui/react';
export default function useMenuColors() {
const themedBackground = useColorModeValue('blackAlpha.50', 'whiteAlpha.50');
const themedBackgroundOrange = useColorModeValue('orange.100', 'orange.900');
const themedBorderColor = useColorModeValue('gray.300', 'gray.700');
const themedColor = useColorModeValue('blackAlpha.800', 'gray.400');
return { themedBackground, themedBackgroundOrange, themedBorderColor, themedColor };
}
import { Box, Flex, chakra } from '@chakra-ui/react'; import { Box, Flex, chakra, useColorModeValue } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
import AddressIdenticon from 'ui/shared/entities/address/AddressIdenticon'; import AddressIdenticon from 'ui/shared/entities/address/AddressIdenticon';
import IconSvg from 'ui/shared/IconSvg'; import IconSvg from 'ui/shared/IconSvg';
import useMenuButtonColors from '../useMenuButtonColors';
type Props = { type Props = {
address: string; address: string;
isAutoConnectDisabled?: boolean; isAutoConnectDisabled?: boolean;
...@@ -14,8 +12,8 @@ type Props = { ...@@ -14,8 +12,8 @@ type Props = {
}; };
const WalletIdenticon = ({ address, isAutoConnectDisabled, className }: Props) => { const WalletIdenticon = ({ address, isAutoConnectDisabled, className }: Props) => {
const { themedBackgroundOrange } = useMenuButtonColors();
const isMobile = useIsMobile(); const isMobile = useIsMobile();
const borderColor = useColorModeValue('orange.100', 'orange.900');
return ( return (
<Box className={ className } position="relative"> <Box className={ className } position="relative">
...@@ -31,7 +29,7 @@ const WalletIdenticon = ({ address, isAutoConnectDisabled, className }: Props) = ...@@ -31,7 +29,7 @@ const WalletIdenticon = ({ address, isAutoConnectDisabled, className }: Props) =
backgroundColor="rgba(16, 17, 18, 0.80)" backgroundColor="rgba(16, 17, 18, 0.80)"
borderRadius="full" borderRadius="full"
border="1px solid" border="1px solid"
borderColor={ themedBackgroundOrange } borderColor={ borderColor }
> >
<IconSvg <IconSvg
name="integration/partial" name="integration/partial"
......
import { Box, Button, Text, Flex, IconButton } from '@chakra-ui/react'; import { Box, Button, Text, Flex, IconButton, useColorModeValue } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import * as mixpanel from 'lib/mixpanel/index'; import * as mixpanel from 'lib/mixpanel/index';
...@@ -6,8 +6,6 @@ import getDefaultTransitionProps from 'theme/utils/getDefaultTransitionProps'; ...@@ -6,8 +6,6 @@ import getDefaultTransitionProps from 'theme/utils/getDefaultTransitionProps';
import AddressEntity from 'ui/shared/entities/address/AddressEntity'; import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import IconSvg from 'ui/shared/IconSvg'; import IconSvg from 'ui/shared/IconSvg';
import useMenuButtonColors from '../useMenuButtonColors';
type Props = { type Props = {
address?: string; address?: string;
ensDomainName?: string | null; ensDomainName?: string | null;
...@@ -18,7 +16,7 @@ type Props = { ...@@ -18,7 +16,7 @@ type Props = {
}; };
const WalletMenuContent = ({ address, ensDomainName, disconnect, isAutoConnectDisabled, openWeb3Modal, closeWalletMenu }: Props) => { const WalletMenuContent = ({ address, ensDomainName, disconnect, isAutoConnectDisabled, openWeb3Modal, closeWalletMenu }: Props) => {
const { themedBackgroundOrange } = useMenuButtonColors(); const bgColor = useColorModeValue('orange.100', 'orange.900');
const [ isModalOpening, setIsModalOpening ] = React.useState(false); const [ isModalOpening, setIsModalOpening ] = React.useState(false);
const onAddressClick = React.useCallback(() => { const onAddressClick = React.useCallback(() => {
...@@ -39,7 +37,7 @@ const WalletMenuContent = ({ address, ensDomainName, disconnect, isAutoConnectDi ...@@ -39,7 +37,7 @@ const WalletMenuContent = ({ address, ensDomainName, disconnect, isAutoConnectDi
p={ 3 } p={ 3 }
mb={ 3 } mb={ 3 }
alignItems="center" alignItems="center"
backgroundColor={ themedBackgroundOrange } backgroundColor={ bgColor }
> >
<IconSvg <IconSvg
name="integration/partial" name="integration/partial"
......
import type { ButtonProps } from '@chakra-ui/react'; import { PopoverContent, PopoverBody, PopoverTrigger, Button, Box, useBoolean, chakra } from '@chakra-ui/react';
import { PopoverContent, PopoverBody, PopoverTrigger, Button, Box, useBoolean, chakra, useColorModeValue } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import config from 'configs/app'; import config from 'configs/app';
...@@ -13,7 +12,6 @@ import IconSvg from 'ui/shared/IconSvg'; ...@@ -13,7 +12,6 @@ import IconSvg from 'ui/shared/IconSvg';
import useWallet from 'ui/snippets/walletMenu/useWallet'; import useWallet from 'ui/snippets/walletMenu/useWallet';
import WalletMenuContent from 'ui/snippets/walletMenu/WalletMenuContent'; import WalletMenuContent from 'ui/snippets/walletMenu/WalletMenuContent';
import useMenuButtonColors from '../useMenuButtonColors';
import WalletIdenticon from './WalletIdenticon'; import WalletIdenticon from './WalletIdenticon';
import WalletTooltip from './WalletTooltip'; import WalletTooltip from './WalletTooltip';
...@@ -37,7 +35,6 @@ export const WalletMenuDesktop = ({ ...@@ -37,7 +35,6 @@ export const WalletMenuDesktop = ({
isHomePage, className, size = 'md', isWalletConnected, address, connect, isHomePage, className, size = 'md', isWalletConnected, address, connect,
disconnect, isModalOpening, isModalOpen, openModal, disconnect, isModalOpening, isModalOpen, openModal,
}: ComponentProps) => { }: ComponentProps) => {
const { themedBackground, themedBackgroundOrange, themedBorderColor, themedColor } = useMenuButtonColors();
const [ isPopoverOpen, setIsPopoverOpen ] = useBoolean(false); const [ isPopoverOpen, setIsPopoverOpen ] = useBoolean(false);
const isMobile = useIsMobile(); const isMobile = useIsMobile();
const { isAutoConnectDisabled } = useMarketplaceContext(); const { isAutoConnectDisabled } = useMarketplaceContext();
...@@ -51,36 +48,6 @@ export const WalletMenuDesktop = ({ ...@@ -51,36 +48,6 @@ export const WalletMenuDesktop = ({
}, },
}); });
const variant = React.useMemo(() => {
if (isWalletConnected) {
return 'subtle';
}
return isHomePage ? 'solid' : 'outline';
}, [ isWalletConnected, isHomePage ]);
const themedColorForOrangeBg = useColorModeValue('blackAlpha.800', 'whiteAlpha.800');
let buttonStyles: Partial<ButtonProps> = {};
if (isWalletConnected) {
const backgroundColor = isAutoConnectDisabled ? themedBackgroundOrange : themedBackground;
const color = isAutoConnectDisabled ? themedColorForOrangeBg : themedColor;
buttonStyles = {
bg: isHomePage ? 'blue.50' : backgroundColor,
color: isHomePage ? 'blackAlpha.800' : color,
_hover: {
color: isHomePage ? 'blackAlpha.800' : color,
},
};
} else if (isHomePage) {
buttonStyles = {
color: 'white',
};
} else {
buttonStyles = {
borderColor: themedBorderColor,
color: themedColor,
};
}
const openPopover = React.useCallback(() => { const openPopover = React.useCallback(() => {
mixpanel.logEvent(mixpanel.EventTypes.WALLET_ACTION, { Action: 'Open' }); mixpanel.logEvent(mixpanel.EventTypes.WALLET_ACTION, { Action: 'Open' });
setIsPopoverOpen.toggle(); setIsPopoverOpen.toggle();
...@@ -103,8 +70,9 @@ export const WalletMenuDesktop = ({ ...@@ -103,8 +70,9 @@ export const WalletMenuDesktop = ({
> >
<Button <Button
className={ className } className={ className }
variant={ variant } variant={ isHomePage ? 'hero' : 'header' }
colorScheme="blue" data-selected={ isWalletConnected }
data-warning={ isAutoConnectDisabled }
flexShrink={ 0 } flexShrink={ 0 }
isLoading={ isLoading={
((isModalOpening || isModalOpen) && !isWalletConnected) || ((isModalOpening || isModalOpen) && !isWalletConnected) ||
...@@ -115,7 +83,6 @@ export const WalletMenuDesktop = ({ ...@@ -115,7 +83,6 @@ export const WalletMenuDesktop = ({
fontSize="sm" fontSize="sm"
size={ size } size={ size }
px={{ lg: isHomePage ? 2 : 4, xl: 4 }} px={{ lg: isHomePage ? 2 : 4, xl: 4 }}
{ ...buttonStyles }
> >
{ isWalletConnected ? ( { isWalletConnected ? (
<> <>
......
...@@ -10,7 +10,6 @@ import IconSvg from 'ui/shared/IconSvg'; ...@@ -10,7 +10,6 @@ import IconSvg from 'ui/shared/IconSvg';
import useWallet from 'ui/snippets/walletMenu/useWallet'; import useWallet from 'ui/snippets/walletMenu/useWallet';
import WalletMenuContent from 'ui/snippets/walletMenu/WalletMenuContent'; import WalletMenuContent from 'ui/snippets/walletMenu/WalletMenuContent';
import useMenuButtonColors from '../useMenuButtonColors';
import WalletIdenticon from './WalletIdenticon'; import WalletIdenticon from './WalletIdenticon';
import WalletTooltip from './WalletTooltip'; import WalletTooltip from './WalletTooltip';
...@@ -28,7 +27,6 @@ export const WalletMenuMobile = ( ...@@ -28,7 +27,6 @@ export const WalletMenuMobile = (
{ isWalletConnected, address, connect, disconnect, isModalOpening, isModalOpen, openModal }: ComponentProps, { isWalletConnected, address, connect, disconnect, isModalOpening, isModalOpen, openModal }: ComponentProps,
) => { ) => {
const { isOpen, onOpen, onClose } = useDisclosure(); const { isOpen, onOpen, onClose } = useDisclosure();
const { themedBackground, themedBackgroundOrange, themedBorderColor, themedColor } = useMenuButtonColors();
const isMobile = useIsMobile(); const isMobile = useIsMobile();
const { isAutoConnectDisabled } = useMarketplaceContext(); const { isAutoConnectDisabled } = useMarketplaceContext();
const addressDomainQuery = useApiQuery('address_domain', { const addressDomainQuery = useApiQuery('address_domain', {
...@@ -46,8 +44,6 @@ export const WalletMenuMobile = ( ...@@ -46,8 +44,6 @@ export const WalletMenuMobile = (
onOpen(); onOpen();
}, [ onOpen ]); }, [ onOpen ]);
const themedBg = isAutoConnectDisabled ? themedBackgroundOrange : themedBackground;
return ( return (
<> <>
<WalletTooltip <WalletTooltip
...@@ -62,13 +58,11 @@ export const WalletMenuMobile = ( ...@@ -62,13 +58,11 @@ export const WalletMenuMobile = (
<WalletIdenticon address={ address } isAutoConnectDisabled={ isAutoConnectDisabled }/> : <WalletIdenticon address={ address } isAutoConnectDisabled={ isAutoConnectDisabled }/> :
<IconSvg name="wallet" boxSize={ 6 } p={ 0.5 }/> <IconSvg name="wallet" boxSize={ 6 } p={ 0.5 }/>
} }
variant={ isWalletConnected ? 'subtle' : 'outline' } variant="header"
colorScheme="gray" data-selected={ isWalletConnected }
data-warning={ isAutoConnectDisabled }
boxSize="40px" boxSize="40px"
flexShrink={ 0 } flexShrink={ 0 }
bg={ isWalletConnected ? themedBg : undefined }
color={ themedColor }
borderColor={ !isWalletConnected ? themedBorderColor : undefined }
onClick={ isWalletConnected ? openPopover : connect } onClick={ isWalletConnected ? openPopover : connect }
isLoading={ isLoading={
((isModalOpening || isModalOpen) && !isWalletConnected) || ((isModalOpening || isModalOpen) && !isWalletConnected) ||
......
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