mirror of
https://github.com/luxfi/explore.git
synced 2026-09-24 00:28:18 +00:00
Merge pull request #2108 from blockscout/get-gas-button
Add a "Get gas" button
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
import type { Feature } from './types';
|
||||
import type { GasRefuelProviderConfig } from 'types/client/gasRefuelProviderConfig';
|
||||
|
||||
import chain from '../chain';
|
||||
import { getEnvValue, parseEnvJson } from '../utils';
|
||||
import marketplace from './marketplace';
|
||||
|
||||
const value = parseEnvJson<GasRefuelProviderConfig>(getEnvValue('NEXT_PUBLIC_GAS_REFUEL_PROVIDER_CONFIG'));
|
||||
|
||||
const title = 'Get gas button';
|
||||
|
||||
const config: Feature<{
|
||||
name: string;
|
||||
logoUrl?: string;
|
||||
url: string;
|
||||
dappId?: string;
|
||||
}> = (() => {
|
||||
if (value) {
|
||||
return Object.freeze({
|
||||
title,
|
||||
isEnabled: true,
|
||||
name: value.name,
|
||||
logoUrl: value.logo,
|
||||
url: value.url_template.replace('{chainId}', chain.id || ''),
|
||||
dappId: marketplace.isEnabled ? value.dapp_id : undefined,
|
||||
});
|
||||
}
|
||||
|
||||
return Object.freeze({
|
||||
title,
|
||||
isEnabled: false,
|
||||
});
|
||||
})();
|
||||
|
||||
export default config;
|
||||
@@ -11,6 +11,7 @@ export { default as dataAvailability } from './dataAvailability';
|
||||
export { default as deFiDropdown } from './deFiDropdown';
|
||||
export { default as faultProofSystem } from './faultProofSystem';
|
||||
export { default as gasTracker } from './gasTracker';
|
||||
export { default as getGasButton } from './getGasButton';
|
||||
export { default as googleAnalytics } from './googleAnalytics';
|
||||
export { default as graphqlApiDocs } from './graphqlApiDocs';
|
||||
export { default as growthBook } from './growthBook';
|
||||
|
||||
@@ -40,6 +40,7 @@ NEXT_PUBLIC_MARKETPLACE_RATING_AIRTABLE_BASE_ID=appGkvtmKI7fXE4Vs
|
||||
NEXT_PUBLIC_METADATA_SERVICE_API_HOST=https://metadata.services.blockscout.com
|
||||
NEXT_PUBLIC_METASUITES_ENABLED=true
|
||||
NEXT_PUBLIC_MULTICHAIN_BALANCE_PROVIDER_CONFIG={'name': 'zerion', 'dapp_id': 'zerion', 'url_template': 'https://app.zerion.io/{address}/overview?utm_source=blockscout&utm_medium=address', 'logo': 'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/marketplace-logos/zerion.svg'}
|
||||
NEXT_PUBLIC_GAS_REFUEL_PROVIDER_CONFIG={'name': 'Need gas?', 'dapp_id': 'smol-refuel', 'url_template': 'https://smolrefuel.com/?outboundChain={chainId}&partner=blockscout&utm_source=blockscout&utm_medium=address&disableBridges=true', 'logo': 'https://blockscout-content.s3.amazonaws.com/smolrefuel-logo-action-button.png'}
|
||||
NEXT_PUBLIC_NAME_SERVICE_API_HOST=https://bens.services.blockscout.com
|
||||
NEXT_PUBLIC_NAVIGATION_HIGHLIGHTED_ROUTES=['/apps']
|
||||
NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS=18
|
||||
|
||||
@@ -13,6 +13,7 @@ import { SUPPORTED_AD_TEXT_PROVIDERS, SUPPORTED_AD_BANNER_PROVIDERS, SUPPORTED_A
|
||||
import type { AdTextProviders, AdBannerProviders, AdBannerAdditionalProviders } from '../../../types/client/adProviders';
|
||||
import type { ContractCodeIde } from '../../../types/client/contract';
|
||||
import type { DeFiDropdownItem } from '../../../types/client/deFiDropdown';
|
||||
import type { GasRefuelProviderConfig } from '../../../types/client/gasRefuelProviderConfig';
|
||||
import { GAS_UNITS } from '../../../types/client/gasTracker';
|
||||
import type { GasUnit } from '../../../types/client/gasTracker';
|
||||
import type { MarketplaceAppOverview, MarketplaceAppSecurityReportRaw, MarketplaceAppSecurityReport } from '../../../types/client/marketplace';
|
||||
@@ -654,6 +655,19 @@ const schema = yup
|
||||
dapp_id: yup.string(),
|
||||
});
|
||||
|
||||
return isUndefined || valueSchema.isValidSync(data);
|
||||
}),
|
||||
NEXT_PUBLIC_GAS_REFUEL_PROVIDER_CONFIG: yup
|
||||
.mixed()
|
||||
.test('shape', 'Invalid schema were provided for NEXT_PUBLIC_GAS_REFUEL_PROVIDER_CONFIG, it should have name and url template', (data) => {
|
||||
const isUndefined = data === undefined;
|
||||
const valueSchema = yup.object<GasRefuelProviderConfig>().transform(replaceQuotes).json().shape({
|
||||
name: yup.string().required(),
|
||||
url_template: yup.string().required(),
|
||||
logo: yup.string(),
|
||||
dapp_id: yup.string(),
|
||||
});
|
||||
|
||||
return isUndefined || valueSchema.isValidSync(data);
|
||||
}),
|
||||
NEXT_PUBLIC_VALIDATORS_CHAIN_TYPE: yup.string<ValidatorsChainType>().oneOf(VALIDATORS_CHAIN_TYPE),
|
||||
|
||||
@@ -78,3 +78,4 @@ NEXT_PUBLIC_WEB3_WALLETS=['coinbase','metamask','token_pocket']
|
||||
NEXT_PUBLIC_VALIDATORS_CHAIN_TYPE=stability
|
||||
NEXT_PUBLIC_DEFI_DROPDOWN_ITEMS=[{'text':'Swap','icon':'swap','dappId':'uniswap'},{'text':'Payment link','icon':'payment_link','url':'https://example.com'}]
|
||||
NEXT_PUBLIC_MULTICHAIN_BALANCE_PROVIDER_CONFIG={'name': 'zerion', 'url_template': 'https://app.zerion.io/{address}/overview', 'logo': 'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/marketplace-logos/zerion.svg'}
|
||||
NEXT_PUBLIC_GAS_REFUEL_PROVIDER_CONFIG={'name': 'Need gas?', 'dapp_id': 'smol-refuel', 'url_template': 'https://smolrefuel.com/?outboundChain={chainId}&partner=blockscout&utm_source=blockscout&utm_medium=address&disableBridges=true', 'logo': 'https://blockscout-content.s3.amazonaws.com/smolrefuel-logo-action-button.png'}
|
||||
|
||||
@@ -61,6 +61,7 @@ Please be aware that all environment variables prefixed with `NEXT_PUBLIC_` will
|
||||
- [OpenTelemetry](ENVS.md#opentelemetry)
|
||||
- [Swap button](ENVS.md#defi-dropdown)
|
||||
- [Multichain balance button](ENVS.md#multichain-balance-button)
|
||||
- [Get gas button](ENVS.md#get-gas-button)
|
||||
- [3rd party services configuration](ENVS.md#external-services-configuration)
|
||||
|
||||
|
||||
@@ -708,6 +709,27 @@ If the feature is enabled, a Multichain balance button will be displayed on the
|
||||
|
||||
|
||||
|
||||
### Get gas button
|
||||
|
||||
If the feature is enabled, a Get gas button will be displayed in the top bar, which will take you to the gas refuel application in the marketplace or to an external site.
|
||||
|
||||
| Variable | Type| Description | Compulsoriness | Default value | Example value | Version |
|
||||
| --- | --- | --- | --- | --- | --- | --- |
|
||||
| NEXT_PUBLIC_GAS_REFUEL_PROVIDER_CONFIG | `{ name: string; url_template: string; dapp_id?: string; logo?: string }` | Get gas button config. See [below](#get-gas-button-configuration-properties) | - | - | `{ name: 'Need gas?', dapp_id: 'smol-refuel', url_template: 'https://smolrefuel.com/?outboundChain={chainId}', logo: 'https://example.com/icon.png' }` | v1.33.0+ |
|
||||
|
||||
|
||||
|
||||
#### Get gas button configuration properties
|
||||
|
||||
| Variable | Type| Description | Compulsoriness | Default value | Example value |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| name | `string` | Text on the button | Required | - | `Need gas?` |
|
||||
| url_template | `string` | Url template, may contain `{chainId}` variable | Required | - | `https://smolrefuel.com/?outboundChain={chainId}` |
|
||||
| dapp_id | `string` | Set for open a Blockscout dapp page instead of opening external app page | - | - | `smol-refuel` |
|
||||
| logo | `string` | Gas refuel application logo url | - | - | `https://example.com/icon.png` |
|
||||
|
||||
|
||||
|
||||
## External services configuration
|
||||
|
||||
### Google ReCaptcha
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
export type GasRefuelProviderConfig = {
|
||||
name: string;
|
||||
dapp_id?: string;
|
||||
url_template: string;
|
||||
logo?: string;
|
||||
};
|
||||
@@ -0,0 +1,64 @@
|
||||
import { Image, Box } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
|
||||
import { route } from 'nextjs-routes';
|
||||
|
||||
import config from 'configs/app';
|
||||
import useIsMobile from 'lib/hooks/useIsMobile';
|
||||
import * as mixpanel from 'lib/mixpanel/index';
|
||||
import LinkExternal from 'ui/shared/links/LinkExternal';
|
||||
import LinkInternal from 'ui/shared/links/LinkInternal';
|
||||
|
||||
const getGasFeature = config.features.getGasButton;
|
||||
|
||||
const GetGasButton = () => {
|
||||
const isMobile = useIsMobile(false);
|
||||
|
||||
const onGetGasClick = React.useCallback(() => {
|
||||
mixpanel.logEvent(mixpanel.EventTypes.BUTTON_CLICK, { Content: 'Get gas', Source: 'address' });
|
||||
}, []);
|
||||
|
||||
if (getGasFeature.isEnabled && !isMobile) {
|
||||
try {
|
||||
const dappId = getGasFeature.dappId;
|
||||
const urlObj = new URL(getGasFeature.url);
|
||||
|
||||
urlObj.searchParams.append('utm_source', 'blockscout');
|
||||
urlObj.searchParams.append('utm_medium', 'address');
|
||||
|
||||
const url = urlObj.toString();
|
||||
const isInternal = typeof dappId === 'string';
|
||||
|
||||
const Link = isInternal ? LinkInternal : LinkExternal;
|
||||
const href = isInternal ? route({ pathname: '/apps/[id]', query: { id: dappId, url } }) : url;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Box h="1px" w="8px" bg="divider" mx={ 1 }/>
|
||||
<Link
|
||||
href={ href }
|
||||
display="flex"
|
||||
alignItems="center"
|
||||
fontSize="xs"
|
||||
lineHeight={ 5 }
|
||||
onClick={ onGetGasClick }
|
||||
>
|
||||
{ getGasFeature.logoUrl && (
|
||||
<Image
|
||||
src={ getGasFeature.logoUrl }
|
||||
alt={ getGasFeature.name }
|
||||
boxSize="14px"
|
||||
mr={ 1 }
|
||||
/>
|
||||
) }
|
||||
{ getGasFeature.name }
|
||||
</Link>
|
||||
</>
|
||||
);
|
||||
} catch (error) {}
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
export default GetGasButton;
|
||||
@@ -61,3 +61,19 @@ test('with DeFi dropdown +@dark-mode +@mobile', async({ render, page, mockApiRes
|
||||
await component.getByText(/DeFi/i).click();
|
||||
await expect(page).toHaveScreenshot({ clip: { x: 0, y: 0, width: 1500, height: 220 } });
|
||||
});
|
||||
|
||||
test('with Get gas button', async({ render, mockApiResponse, mockEnvs, mockAssetResponse }) => {
|
||||
const ICON_URL = 'https://localhost:3000/my-icon.png';
|
||||
|
||||
await mockEnvs([
|
||||
[
|
||||
'NEXT_PUBLIC_GAS_REFUEL_PROVIDER_CONFIG',
|
||||
`{"name": "Need gas?", "dapp_id": "duck", "url_template": "https://duck.url/{chainId}", "logo": "${ ICON_URL }"}`,
|
||||
],
|
||||
]);
|
||||
await mockApiResponse('stats', statsMock.base);
|
||||
await mockAssetResponse(ICON_URL, './playwright/mocks/image_svg.svg');
|
||||
|
||||
const component = await render(<TopBar/>);
|
||||
await expect(component).toHaveScreenshot();
|
||||
});
|
||||
|
||||
@@ -10,6 +10,8 @@ import GasInfoTooltip from 'ui/shared/gas/GasInfoTooltip';
|
||||
import GasPrice from 'ui/shared/gas/GasPrice';
|
||||
import TextSeparator from 'ui/shared/TextSeparator';
|
||||
|
||||
import GetGasButton from './GetGasButton';
|
||||
|
||||
const TopBarStats = () => {
|
||||
const isMobile = useIsMobile();
|
||||
|
||||
@@ -76,14 +78,17 @@ const TopBarStats = () => {
|
||||
) }
|
||||
{ data?.coin_price && config.features.gasTracker.isEnabled && <TextSeparator color="divider"/> }
|
||||
{ data?.gas_prices && data.gas_prices.average !== null && config.features.gasTracker.isEnabled && (
|
||||
<Skeleton isLoaded={ !isPlaceholderData }>
|
||||
<chakra.span color="text_secondary">Gas </chakra.span>
|
||||
<GasInfoTooltip data={ data } dataUpdatedAt={ dataUpdatedAt } placement={ !data?.coin_price ? 'bottom-start' : undefined }>
|
||||
<Link>
|
||||
<GasPrice data={ data.gas_prices.average }/>
|
||||
</Link>
|
||||
</GasInfoTooltip>
|
||||
</Skeleton>
|
||||
<>
|
||||
<Skeleton isLoaded={ !isPlaceholderData }>
|
||||
<chakra.span color="text_secondary">Gas </chakra.span>
|
||||
<GasInfoTooltip data={ data } dataUpdatedAt={ dataUpdatedAt } placement={ !data?.coin_price ? 'bottom-start' : undefined }>
|
||||
<Link>
|
||||
<GasPrice data={ data.gas_prices.average }/>
|
||||
</Link>
|
||||
</GasInfoTooltip>
|
||||
</Skeleton>
|
||||
{ !isPlaceholderData && <GetGasButton/> }
|
||||
</>
|
||||
) }
|
||||
</Flex>
|
||||
);
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 7.4 KiB |
Reference in New Issue
Block a user