Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions configs/app/app.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import * as cookies from 'lib/cookies';

import { getEnvValue } from './utils';

const appPort = getEnvValue('NEXT_PUBLIC_APP_PORT');
Expand All @@ -13,6 +15,7 @@ const isDev = getEnvValue('NEXT_PUBLIC_APP_ENV') === 'development';
const isReview = getEnvValue('NEXT_PUBLIC_APP_ENV') === 'review';
const isPw = getEnvValue('NEXT_PUBLIC_APP_INSTANCE') === 'pw';
const spriteHash = getEnvValue('NEXT_PUBLIC_ICON_SPRITE_HASH');
const appProfile = cookies.get(cookies.NAMES.APP_PROFILE);

const app = Object.freeze({
isDev,
Expand All @@ -24,6 +27,7 @@ const app = Object.freeze({
baseUrl,
useProxy: getEnvValue('NEXT_PUBLIC_USE_NEXT_JS_PROXY') === 'true',
spriteHash,
appProfile,
});

export default app;
8 changes: 7 additions & 1 deletion configs/app/features/account.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
import type { Feature } from './types';

import app from '../app';
import services from '../services';
import { getEnvValue } from '../utils';

const title = 'My account';

const config: Feature<{ isEnabled: true; recaptchaSiteKey: string }> = (() => {
if (getEnvValue('NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED') === 'true' && services.reCaptchaV2.siteKey) {

if (
app.appProfile !== 'private' &&
getEnvValue('NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED') === 'true' &&
services.reCaptchaV2.siteKey
) {
return Object.freeze({
title,
isEnabled: true,
Expand Down
3 changes: 2 additions & 1 deletion configs/app/features/addressProfileAPI.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { Feature } from './types';
import type { AddressProfileAPIConfig } from 'types/client/addressProfileAPIConfig';

import app from '../app';
import { getEnvValue, parseEnvJson } from '../utils';

const value = parseEnvJson<AddressProfileAPIConfig>(getEnvValue('NEXT_PUBLIC_ADDRESS_USERNAME_TAG'));
Expand All @@ -24,7 +25,7 @@ const config: Feature<{
tagBgColor?: string;
tagTextColor?: string;
}> = (() => {
if (value && checkApiUrlTemplate(value.api_url_template)) {
if (app.appProfile !== 'private' && value && checkApiUrlTemplate(value.api_url_template)) {
return Object.freeze({
title,
isEnabled: true,
Expand Down
3 changes: 2 additions & 1 deletion configs/app/features/addressVerification.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import type { Feature } from './types';

import apis from '../apis';
import app from '../app';
import account from './account';
import verifiedTokens from './verifiedTokens';

const title = 'Address verification in "My account"';

const config: Feature<{}> = (() => {
if (account.isEnabled && verifiedTokens.isEnabled && apis.admin) {
if (app.appProfile !== 'private' && account.isEnabled && verifiedTokens.isEnabled && apis.admin) {
return Object.freeze({
title: 'Address verification in "My account"',
isEnabled: true,
Expand Down
8 changes: 8 additions & 0 deletions configs/app/features/adsBanner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { AdButlerConfig } from 'types/client/adButlerConfig';
import { SUPPORTED_AD_BANNER_PROVIDERS } from 'types/client/adProviders';
import type { AdBannerProviders, AdBannerAdditionalProviders } from 'types/client/adProviders';

import app from '../app';
import { getEnvValue, parseEnvJson } from '../utils';

const provider: AdBannerProviders = (() => {
Expand Down Expand Up @@ -42,6 +43,13 @@ type AdsBannerFeaturePayload = AdsBannerFeatureProviderPayload & {
};

const config: Feature<AdsBannerFeaturePayload> = (() => {
if (app.appProfile === 'private') {
return Object.freeze({
title,
isEnabled: false,
});
}

if (provider === 'adbutler') {
const desktopConfig = parseEnvJson<AdButlerConfig>(getEnvValue('NEXT_PUBLIC_AD_ADBUTLER_CONFIG_DESKTOP'));
const mobileConfig = parseEnvJson<AdButlerConfig>(getEnvValue('NEXT_PUBLIC_AD_ADBUTLER_CONFIG_MOBILE'));
Expand Down
3 changes: 2 additions & 1 deletion configs/app/features/adsText.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { Feature } from './types';
import { SUPPORTED_AD_TEXT_PROVIDERS } from 'types/client/adProviders';
import type { AdTextProviders } from 'types/client/adProviders';

import app from '../app';
import { getEnvValue } from '../utils';

const provider: AdTextProviders = (() => {
Expand All @@ -12,7 +13,7 @@ const provider: AdTextProviders = (() => {
const title = 'Text ads';

const config: Feature<{ provider: AdTextProviders }> = (() => {
if (provider !== 'none') {
if (app.appProfile !== 'private' && provider !== 'none') {
return Object.freeze({
title,
isEnabled: true,
Expand Down
2 changes: 2 additions & 0 deletions configs/app/features/blockchainInteraction.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { Feature } from './types';

import app from '../app';
import chain from '../chain';
import { getEnvValue } from '../utils';
import opSuperchain from './opSuperchain';
Expand All @@ -24,6 +25,7 @@ const config: Feature<{ walletConnect: { projectId: string } }> = (() => {
const isOpSuperchain = opSuperchain.isEnabled;

if (
app.appProfile !== 'private' &&
(isSingleChain || isOpSuperchain) &&
walletConnectProjectId
) {
Expand Down
18 changes: 11 additions & 7 deletions configs/app/features/deFiDropdown.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,25 @@
import type { Feature } from './types';
import type { DeFiDropdownItem } from 'types/client/deFiDropdown';

import app from '../app';
import { getEnvValue, parseEnvJson } from '../utils';

const items = parseEnvJson<Array<DeFiDropdownItem>>(getEnvValue('NEXT_PUBLIC_DEFI_DROPDOWN_ITEMS')) || [];

const title = 'DeFi dropdown';

const config: Feature<{ items: Array<DeFiDropdownItem> }> = items.length > 0 ?
Object.freeze({
title,
isEnabled: true,
items,
}) :
Object.freeze({
const config: Feature<{ items: Array<DeFiDropdownItem> }> = (() => {
if (app.appProfile !== 'private' && items.length > 0) {
return Object.freeze({
title,
isEnabled: true,
items,
});
}
return Object.freeze({
title,
isEnabled: false,
});
})();

export default config;
3 changes: 2 additions & 1 deletion configs/app/features/googleAnalytics.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import type { Feature } from './types';

import app from '../app';
import { getEnvValue } from '../utils';

const propertyId = getEnvValue('NEXT_PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID');

const title = 'Google analytics';

const config: Feature<{ propertyId: string }> = (() => {
if (propertyId) {
if (app.appProfile !== 'private' && propertyId) {
return Object.freeze({
title,
isEnabled: true,
Expand Down
3 changes: 2 additions & 1 deletion configs/app/features/growthBook.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import type { Feature } from './types';

import app from '../app';
import { getEnvValue } from '../utils';

const clientKey = getEnvValue('NEXT_PUBLIC_GROWTH_BOOK_CLIENT_KEY');

const title = 'GrowthBook feature flagging and A/B testing';

const config: Feature<{ clientKey: string }> = (() => {
if (clientKey) {
if (app.appProfile !== 'private' && clientKey) {
return Object.freeze({
title,
isEnabled: true,
Expand Down
8 changes: 7 additions & 1 deletion configs/app/features/marketplace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { Feature } from './types';
import type { EssentialDappsConfig, MarketplaceTitles } from 'types/client/marketplace';

import apis from '../apis';
import app from '../app';
import chain from '../chain';
import { getEnvValue, getExternalAssetFilePath, parseEnvJson } from '../utils';
import blockchainInteraction from './blockchainInteraction';
Expand Down Expand Up @@ -44,7 +45,12 @@ const config: Feature<(
essentialDappsAdEnabled: boolean;
titles: MarketplaceTitles;
}> = (() => {
if (enabled === 'true' && chain.rpcUrls.length > 0 && submitFormUrl) {
if (
app.appProfile !== 'private' &&
chain.rpcUrls.length > 0 &&
enabled === 'true' &&
submitFormUrl
) {
const props = {
submitFormUrl,
categoriesUrl,
Expand Down
3 changes: 2 additions & 1 deletion configs/app/features/mixpanel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { Config } from 'mixpanel-browser';

import type { Feature } from './types';

import app from '../app';
import { getEnvValue, parseEnvJson } from '../utils';

const projectToken = getEnvValue('NEXT_PUBLIC_MIXPANEL_PROJECT_TOKEN');
Expand All @@ -17,7 +18,7 @@ const configOverrides = (() => {
const title = 'Mixpanel analytics';

const config: Feature<{ projectToken: string; configOverrides?: Partial<Config> }> = (() => {
if (projectToken) {
if (app.appProfile !== 'private' && projectToken) {
return Object.freeze({
title,
isEnabled: true,
Expand Down
3 changes: 2 additions & 1 deletion configs/app/features/publicTagsSubmission.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import type { Feature } from './types';

import apis from '../apis';
import app from '../app';
import services from '../services';
import addressMetadata from './addressMetadata';

const title = 'Public tag submission';

const config: Feature<{}> = (() => {
if (services.reCaptchaV2.siteKey && addressMetadata.isEnabled && apis.admin) {
if (app.appProfile !== 'private' && services.reCaptchaV2.siteKey && addressMetadata.isEnabled && apis.admin) {
return Object.freeze({
title,
isEnabled: true,
Expand Down
3 changes: 2 additions & 1 deletion configs/app/features/rewards.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import type { Feature } from './types';

import apis from '../apis';
import app from '../app';
import account from './account';
import blockchainInteraction from './blockchainInteraction';

const title = 'Rewards service integration';

const config: Feature<{}> = (() => {
if (apis.rewards && account.isEnabled && blockchainInteraction.isEnabled) {
if (app.appProfile !== 'private' && apis.rewards && account.isEnabled && blockchainInteraction.isEnabled) {
return Object.freeze({
title,
isEnabled: true,
Expand Down
2 changes: 1 addition & 1 deletion configs/app/features/rollbar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const config: Feature<{
instance: string | undefined;
codeVersion: string | undefined;
}> = (() => {
if (clientToken) {
if (app.appProfile !== 'private' && clientToken) {
return Object.freeze({
title,
isEnabled: true,
Expand Down
3 changes: 2 additions & 1 deletion configs/app/features/web3Wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { Feature } from './types';
import { SUPPORTED_WALLETS } from 'types/client/wallets';
import type { WalletType } from 'types/client/wallets';

import app from '../app';
import { getEnvValue, parseEnvJson } from '../utils';

const wallets = ((): Array<WalletType> | undefined => {
Expand All @@ -22,7 +23,7 @@ const wallets = ((): Array<WalletType> | undefined => {
const title = 'Web3 wallet integration (add token or network to the wallet)';

const config: Feature<{ wallets: Array<WalletType>; addToken: { isDisabled: boolean } }> = (() => {
if (wallets && wallets.length > 0) {
if (app.appProfile !== 'private' && wallets && wallets.length > 0) {
return Object.freeze({
title,
isEnabled: true,
Expand Down
1 change: 1 addition & 0 deletions docs/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
- [ ] I have tested these changes locally.
- [ ] I added tests to cover any new functionality, following this [guide](./CONTRIBUTING.md#writing--running-tests)
- [ ] Whenever I fix a bug, I include a regression test to ensure that the bug does not reappear silently.
- [ ] If I have added a feature or functionality that is not privacy-compliant (e.g., tracking, analytics, third-party services), I have disabled it for private mode.
- [ ] If I have added, changed, renamed, or removed an environment variable
- I updated the list of environment variables in the [documentation](ENVS.md)
- I made the necessary changes to the validator script according to the [guide](./CONTRIBUTING.md#adding-new-env-variable)
Expand Down
37 changes: 36 additions & 1 deletion lib/cookies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ import Cookies from 'js-cookie';

import { isBrowser } from 'toolkit/utils/isBrowser';

/**
* All cookie names that can be used in the application.
*/
export enum NAMES {
NAV_BAR_COLLAPSED = 'nav_bar_collapsed',
API_TOKEN = '_explorer_key',
Expand All @@ -21,8 +24,19 @@ export enum NAMES {
HIDE_ADD_TO_WALLET_BUTTON = 'hide_add_to_wallet_button',
UUID = 'uuid',
SHOW_SCAM_TOKENS = 'show_scam_tokens',
APP_PROFILE = 'app_profile',
}

/**
* Cookies that are disallowed in private mode.
* These cookies should not be set when app profile is 'private'.
*/
export const PRIVATE_MODE_DISALLOWED: ReadonlyArray<NAMES> = [
NAMES.UUID,
NAMES.ADBLOCK_DETECTED,
NAMES.MIXPANEL_DEBUG,
];

export function get(name?: NAMES | undefined | null, serverCookie?: string) {
if (!isBrowser()) {
return serverCookie ? getFromCookieString(serverCookie, name) : undefined;
Expand All @@ -33,7 +47,28 @@ export function get(name?: NAMES | undefined | null, serverCookie?: string) {
}
}

export function set(name: NAMES, value: string, attributes: Cookies.CookieAttributes = {}) {
/**
* Checks if a cookie is disallowed in private mode.
*/
function isDisallowedInPrivateMode(name: NAMES): boolean {
return PRIVATE_MODE_DISALLOWED.includes(name);
}

/**
* Checks if the app is currently in private mode by reading the APP_PROFILE cookie.
*/
function isPrivateMode(serverCookie?: string): boolean {
const appProfile = get(NAMES.APP_PROFILE, serverCookie);
return appProfile === 'private';
}

export function set(name: NAMES, value: string, attributes: Cookies.CookieAttributes = {}, serverCookie?: string) {
// Check if we're in private mode and this cookie is disallowed
if (isPrivateMode(serverCookie) && isDisallowedInPrivateMode(name)) {
// Don't set the cookie in private mode
return;
}

attributes.path = '/';

return Cookies.set(name, value, attributes);
Expand Down
6 changes: 5 additions & 1 deletion lib/recentSearchKeywords.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { uniq } from 'es-toolkit';

import config from 'configs/app';
import { isBrowser } from 'toolkit/utils/isBrowser';

const RECENT_KEYWORDS_LS_KEY = 'recent_search_keywords';
Expand All @@ -22,6 +23,9 @@ const parseKeywordsArray = (keywordsStr: string) => {
};

export function saveToRecentKeywords(value: string) {
if (config.app.appProfile === 'private') {
return;
}
if (!value) {
return;
}
Expand All @@ -32,7 +36,7 @@ export function saveToRecentKeywords(value: string) {
}

export function getRecentSearchKeywords(input?: string) {
if (!isBrowser()) {
if (!isBrowser() || config.app.appProfile === 'private') {
return [];
}
const keywordsStr = window.localStorage.getItem(RECENT_KEYWORDS_LS_KEY) || '';
Expand Down
Loading
Loading