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

Support `show_scam_tokens` cookie (#2560)

* Support `show_scam_tokens` cookie

Fixes #2549

* fix test and update envs for demo

* [skip ci] pass all cookies via next proxy

* fixes
parent 548616ae
export { default as address } from './address'; export { default as address } from './address';
export { default as block } from './block'; export { default as block } from './block';
export { default as nft } from './nft'; export { default as nft } from './nft';
export { default as token } from './token';
export { default as tx } from './tx'; export { default as tx } from './tx';
import { getEnvValue } from 'configs/app/utils';
const config = Object.freeze({
hideScamTokensEnabled: getEnvValue('NEXT_PUBLIC_VIEWS_TOKEN_SCAM_TOGGLE_ENABLED') === 'true',
});
export default config;
...@@ -10,6 +10,8 @@ NEXT_PUBLIC_APP_ENV=development ...@@ -10,6 +10,8 @@ NEXT_PUBLIC_APP_ENV=development
NEXT_PUBLIC_APP_INSTANCE=rubber_duck NEXT_PUBLIC_APP_INSTANCE=rubber_duck
NEXT_PUBLIC_API_WEBSOCKET_PROTOCOL=ws NEXT_PUBLIC_API_WEBSOCKET_PROTOCOL=ws
NEXT_PUBLIC_VIEWS_TOKEN_SCAM_TOGGLE_ENABLED=true
# Instance ENVs # Instance ENVs
NEXT_PUBLIC_AD_ADBUTLER_CONFIG_DESKTOP={ "id": "632019", "width": "728", "height": "90" } NEXT_PUBLIC_AD_ADBUTLER_CONFIG_DESKTOP={ "id": "632019", "width": "728", "height": "90" }
NEXT_PUBLIC_AD_ADBUTLER_CONFIG_MOBILE={ "id": "632018", "width": "320", "height": "100" } NEXT_PUBLIC_AD_ADBUTLER_CONFIG_MOBILE={ "id": "632018", "width": "320", "height": "100" }
......
...@@ -868,6 +868,7 @@ const schema = yup ...@@ -868,6 +868,7 @@ const schema = yup
.transform(replaceQuotes) .transform(replaceQuotes)
.json() .json()
.of(nftMarketplaceSchema), .of(nftMarketplaceSchema),
NEXT_PUBLIC_VIEWS_TOKEN_SCAM_TOGGLE_ENABLED: yup.boolean(),
NEXT_PUBLIC_HELIA_VERIFIED_FETCH_ENABLED: yup.boolean(), NEXT_PUBLIC_HELIA_VERIFIED_FETCH_ENABLED: yup.boolean(),
// e. misc // e. misc
......
...@@ -78,6 +78,7 @@ frontend: ...@@ -78,6 +78,7 @@ frontend:
NEXT_PUBLIC_NAVIGATION_HIGHLIGHTED_ROUTES: "['/apps']" NEXT_PUBLIC_NAVIGATION_HIGHLIGHTED_ROUTES: "['/apps']"
PROMETHEUS_METRICS_ENABLED: true PROMETHEUS_METRICS_ENABLED: true
NEXT_PUBLIC_OG_ENHANCED_DATA_ENABLED: true NEXT_PUBLIC_OG_ENHANCED_DATA_ENABLED: true
NEXT_PUBLIC_VIEWS_TOKEN_SCAM_TOGGLE_ENABLED: true
envFromSecret: envFromSecret:
NEXT_PUBLIC_AUTH0_CLIENT_ID: ref+vault://deployment-values/blockscout/dev/review?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_AUTH0_CLIENT_ID NEXT_PUBLIC_AUTH0_CLIENT_ID: ref+vault://deployment-values/blockscout/dev/review?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_AUTH0_CLIENT_ID
NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID: ref+vault://deployment-values/blockscout/dev/review?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID: ref+vault://deployment-values/blockscout/dev/review?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID
......
...@@ -280,6 +280,13 @@ Settings for meta tags, OG tags and SEO ...@@ -280,6 +280,13 @@ Settings for meta tags, OG tags and SEO
   
#### Token views
| Variable | Type | Description | Compulsoriness | Default value | Example value | Version |
| --- | --- | --- | --- | --- | --- | --- |
| NEXT_PUBLIC_VIEWS_TOKEN_SCAM_TOGGLE_ENABLED | `boolean` | Show the "Hide scam tokens" toggle in the site settings dropdown. This option controls the visibility of tokens with a poor reputation in the search results. | - | - | `'["value","tx_fee"]'` | v1.38.0+ |
 
#### NFT views #### NFT views
| Variable | Type | Description | Compulsoriness | Default value | Example value | Version | | Variable | Type | Description | Compulsoriness | Default value | Example value | Version |
......
...@@ -17,6 +17,7 @@ export enum NAMES { ...@@ -17,6 +17,7 @@ export enum NAMES {
MIXPANEL_DEBUG = '_mixpanel_debug', MIXPANEL_DEBUG = '_mixpanel_debug',
ADDRESS_NFT_DISPLAY_TYPE = 'address_nft_display_type', ADDRESS_NFT_DISPLAY_TYPE = 'address_nft_display_type',
UUID = 'uuid', UUID = 'uuid',
SHOW_SCAM_TOKENS = 'show_scam_tokens',
} }
export function get(name?: NAMES | undefined | null, serverCookie?: string) { export function get(name?: NAMES | undefined | null, serverCookie?: string) {
......
...@@ -23,6 +23,7 @@ export function middleware(req: NextRequest) { ...@@ -23,6 +23,7 @@ export function middleware(req: NextRequest) {
middlewares.colorTheme(req, res); middlewares.colorTheme(req, res);
middlewares.addressFormat(req, res); middlewares.addressFormat(req, res);
middlewares.scamTokens(req, res);
const end = Date.now(); const end = Date.now();
......
export { account } from './account'; export { account } from './account';
export { default as colorTheme } from './colorTheme'; export { default as colorTheme } from './colorTheme';
export { default as addressFormat } from './addressFormat'; export { default as addressFormat } from './addressFormat';
export { default as scamTokens } from './scamTokens';
import type { NextRequest, NextResponse } from 'next/server';
import config from 'configs/app';
import * as cookiesLib from 'lib/cookies';
export default function scamTokensMiddleware(req: NextRequest, res: NextResponse) {
if (config.UI.views.token.hideScamTokensEnabled) {
const showScamTokensCookie = req.cookies.get(cookiesLib.NAMES.SHOW_SCAM_TOKENS);
if (!showScamTokensCookie) {
res.cookies.set(cookiesLib.NAMES.SHOW_SCAM_TOKENS, 'false', { path: '/' });
}
}
}
...@@ -7,20 +7,20 @@ import nodeFetch from 'node-fetch'; ...@@ -7,20 +7,20 @@ import nodeFetch from 'node-fetch';
import { httpLogger } from 'nextjs/utils/logger'; import { httpLogger } from 'nextjs/utils/logger';
import * as cookies from 'lib/cookies';
export default function fetchFactory( export default function fetchFactory(
_req: NextApiRequest | (IncomingMessage & { cookies: NextApiRequestCookies }), _req: NextApiRequest | (IncomingMessage & { cookies: NextApiRequestCookies }),
) { ) {
// first arg can be only a string // first arg can be only a string
// FIXME migrate to RequestInfo later if needed // FIXME migrate to RequestInfo later if needed
return function fetch(url: string, init?: RequestInit): Promise<Response> { return function fetch(url: string, init?: RequestInit): Promise<Response> {
const apiToken = _req.cookies[cookies.NAMES.API_TOKEN]; const cookie = Object.entries(_req.cookies)
.map(([ key, value ]) => `${ key }=${ value }`)
.join('; ');
const headers = { const headers = {
accept: _req.headers['accept'] || 'application/json', accept: _req.headers['accept'] || 'application/json',
'content-type': _req.headers['content-type'] || 'application/json', 'content-type': _req.headers['content-type'] || 'application/json',
cookie: apiToken ? `${ cookies.NAMES.API_TOKEN }=${ apiToken }` : '', cookie,
...pick(_req.headers, [ ...pick(_req.headers, [
'x-csrf-token', 'x-csrf-token',
'Authorization', // the old value, just in case 'Authorization', // the old value, just in case
......
...@@ -10,6 +10,7 @@ test.beforeEach(async({ mockEnvs }) => { ...@@ -10,6 +10,7 @@ test.beforeEach(async({ mockEnvs }) => {
await mockEnvs([ await mockEnvs([
[ 'NEXT_PUBLIC_DEFI_DROPDOWN_ITEMS', '[{"text":"Swap","icon":"swap","dappId":"uniswap"}]' ], [ 'NEXT_PUBLIC_DEFI_DROPDOWN_ITEMS', '[{"text":"Swap","icon":"swap","dappId":"uniswap"}]' ],
[ 'NEXT_PUBLIC_NETWORK_SECONDARY_COIN_SYMBOL', 'DUCK' ], [ 'NEXT_PUBLIC_NETWORK_SECONDARY_COIN_SYMBOL', 'DUCK' ],
[ 'NEXT_PUBLIC_VIEWS_TOKEN_SCAM_TOGGLE_ENABLED', 'true' ],
]); ]);
}); });
......
...@@ -7,6 +7,7 @@ import IconSvg from 'ui/shared/IconSvg'; ...@@ -7,6 +7,7 @@ import IconSvg from 'ui/shared/IconSvg';
import SettingsAddressFormat from './SettingsAddressFormat'; import SettingsAddressFormat from './SettingsAddressFormat';
import SettingsColorTheme from './SettingsColorTheme'; import SettingsColorTheme from './SettingsColorTheme';
import SettingsIdentIcon from './SettingsIdentIcon'; import SettingsIdentIcon from './SettingsIdentIcon';
import SettingsScamTokens from './SettingsScamTokens';
const Settings = () => { const Settings = () => {
const { isOpen, onToggle, onClose } = useDisclosure(); const { isOpen, onToggle, onClose } = useDisclosure();
...@@ -27,9 +28,10 @@ const Settings = () => { ...@@ -27,9 +28,10 @@ const Settings = () => {
<PopoverContent overflowY="hidden" w="auto" fontSize="sm"> <PopoverContent overflowY="hidden" w="auto" fontSize="sm">
<PopoverBody boxShadow="2xl" p={ 4 }> <PopoverBody boxShadow="2xl" p={ 4 }>
<SettingsColorTheme onSelect={ onClose }/> <SettingsColorTheme onSelect={ onClose }/>
<Box borderColor="divider" borderWidth="1px" my={ 3 }/> <Box borderColor="divider" borderTopWidth="1px" my={ 3 }/>
<SettingsIdentIcon/> <SettingsIdentIcon/>
<SettingsAddressFormat/> <SettingsAddressFormat/>
<SettingsScamTokens/>
</PopoverBody> </PopoverBody>
</PopoverContent> </PopoverContent>
</Popover> </Popover>
......
import { FormLabel, FormControl, Switch, Box } from '@chakra-ui/react';
import React from 'react';
import config from 'configs/app';
import { useAppContext } from 'lib/contexts/app';
import * as cookies from 'lib/cookies';
const SettingsScamTokens = () => {
const { cookies: appCookies } = useAppContext();
const initialValue = cookies.get(cookies.NAMES.SHOW_SCAM_TOKENS, appCookies);
const [ isChecked, setIsChecked ] = React.useState(initialValue !== 'true');
const handleChange = React.useCallback(() => {
setIsChecked(prev => {
const nextValue = !prev;
cookies.set(cookies.NAMES.SHOW_SCAM_TOKENS, nextValue ? 'false' : 'true');
return nextValue;
});
window.location.reload();
}, []);
if (!config.UI.views.token.hideScamTokensEnabled) {
return null;
}
return (
<>
<Box borderColor="divider" borderTopWidth="1px" my={ 3 }/>
<FormControl display="flex" alignItems="center" columnGap={ 2 } mt={ 4 }>
<FormLabel htmlFor="scam-tokens" m="0" fontWeight={ 400 } fontSize="sm" lineHeight={ 5 }>
Hide scam tokens
</FormLabel>
<Switch id="scam-tokens" isChecked={ isChecked } onChange={ handleChange }/>
</FormControl>
</>
);
};
export default React.memo(SettingsScamTokens);
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