Skip to content
Open
Show file tree
Hide file tree
Changes from 4 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
88 changes: 70 additions & 18 deletions src/layouts/MainLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,60 @@ import { Box } from '@mui/material';
import { useRouter } from 'next/router';
import React, { ReactNode } from 'react';
import AnalyticsConsent from 'src/components/Analytics/AnalyticsConsent';
// import { useModalContext } from 'src/hooks/useModal';
import { useModalContext } from 'src/hooks/useModal';
import { SupportModal } from 'src/layouts/SupportModal';
import { useRootStore } from 'src/store/root';
import { CustomMarket } from 'src/ui-config/marketsConfig';
import { getQueryParameter } from 'src/store/utils/queryParams';
import { CustomMarket, marketsData } from 'src/ui-config/marketsConfig';
import { FORK_ENABLED } from 'src/utils/marketsAndNetworksConfig';
import { useShallow } from 'zustand/shallow';

import { AppFooter } from './AppFooter';
import { AppHeader } from './AppHeader';
import TopBarNotify from './TopBarNotify';
import TopBarNotify, { ButtonAction } from './TopBarNotify';

interface CampaignConfig {
notifyText: string;
buttonText: string;
buttonAction: ButtonAction;
bannerVersion: string;
icon: string;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does it make sense to make the icon optional for future solo-text campaings

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes 33af445

}

type CampaignChainId = ChainId.base | ChainId.mainnet | ChainId.arbitrum_one;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not just use generic ChainId if down here we use Partial of chains ids

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah good point 33af445


type CampaignConfigs = Partial<Record<CampaignChainId, CampaignConfig>>;

type NetworkCampaigns = { [chainId: number]: CampaignConfig };

const getIntendedChainId = (currentChainId?: ChainId): ChainId => {
// Priority 1: currentChainId from store
if (currentChainId) {
return currentChainId;
}

if (typeof window !== 'undefined') {
// Priority 2: localStorage selectedMarket
const selectedMarket = localStorage.getItem('selectedMarket');
if (selectedMarket && marketsData[selectedMarket as CustomMarket]) {
return marketsData[selectedMarket as CustomMarket].chainId;
}

// Priority 3: URL params marketName
const urlMarket = getQueryParameter('marketName');
if (urlMarket && marketsData[urlMarket as CustomMarket]) {
return marketsData[urlMarket as CustomMarket].chainId;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't this be prioritized e.g. someone opens a shared link but been using other network

}

// Priority 4: Default to mainnet
return ChainId.mainnet;
};

const getCampaignConfigs = (
// openSwitch: (underlyingAsset: string) => void,
openSwitch: (underlyingAsset: string, chainId: ChainId) => void,
openMarket: (market: CustomMarket) => void
) => ({
): CampaignConfigs => ({
[ChainId.base]: {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit/v2: we can leverage vercel configs or amplitude feature flags to automatically change campaings without commiting new code

notifyText: 'A new incentives campaign is live on the Base market',
buttonText: 'Explore Base',
Expand Down Expand Up @@ -73,16 +112,16 @@ const getCampaignConfigs = (
// icon: '/icons/networks/avalanche.svg',
// },

// [ChainId.arbitrum_one]: {
// notifyText: 'Swap tokens directly in the Aave App',
// buttonText: 'Swap Now',
// buttonAction: {
// type: 'function' as const,
// value: () => openSwitch('', ChainId.arbitrum_one),
// },
// bannerVersion: 'arbitrum-swap-v1',
// icon: '/icons/networks/arbitrum.svg',
// },
[ChainId.arbitrum_one]: {
notifyText: 'Limit orders are now live on Arbitrum',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure how translations work but can we ensure banner messages will be translated this way? otherwise we can set notifyText type as react node and just use

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i see
` {currentCampaign.notifyText}

`
just checking if it's enough

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah we dont really use translations much but added b2f5f50

buttonText: 'Swap Now',
buttonAction: {
type: 'function' as const,
value: () => openSwitch('', ChainId.arbitrum_one),
},
bannerVersion: 'arbitrum-swap-v1',
icon: '/icons/networks/arbitrum.svg',
},

// [ChainId.optimism]: {
// notifyText: 'Swap tokens directly in the Aave App',
Expand Down Expand Up @@ -120,18 +159,31 @@ const getCampaignConfigs = (

export function MainLayout({ children }: { children: ReactNode }) {
const router = useRouter();
const setCurrentMarket = useRootStore(useShallow((store) => store.setCurrentMarket));
const [setCurrentMarket, currentChainId] = useRootStore(
useShallow((store) => [store.setCurrentMarket, store.currentChainId])
);
const { openSwitch } = useModalContext();

const openMarket = (market: CustomMarket) => {
setCurrentMarket(market);
router.push(`/markets/?marketName=${market}`);
};

const campaignConfigs = getCampaignConfigs(openMarket);
const campaignConfigs = getCampaignConfigs(openSwitch, openMarket);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would move getCampaignConfigs to a hook useBannerCampaigns so we can get openSwitch (or others in the future) right in there and just pass the chain id

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done in 33af445


const intendedChainId = getIntendedChainId(currentChainId);

const isCampaignChainId = (chainId: ChainId): chainId is CampaignChainId => {
return chainId in campaignConfigs;
};

const filteredCampaigns: NetworkCampaigns = isCampaignChainId(intendedChainId)
? { [intendedChainId]: campaignConfigs[intendedChainId]! }
: {};
Copy link

Copilot AI Oct 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using non-null assertion operator (!) without proper null checking could cause runtime errors if campaignConfigs[intendedChainId] is undefined. Consider using optional chaining or explicit null checking.

Suggested change
const filteredCampaigns: NetworkCampaigns = isCampaignChainId(intendedChainId)
? { [intendedChainId]: campaignConfigs[intendedChainId]! }
: {};
const campaign = campaignConfigs[intendedChainId];
const filteredCampaigns: NetworkCampaigns =
isCampaignChainId(intendedChainId) && campaign !== undefined
? { [intendedChainId]: campaign }
: {};

Copilot uses AI. Check for mistakes.

return (
<>
<TopBarNotify campaigns={campaignConfigs} />
<TopBarNotify campaigns={filteredCampaigns} />

<AppHeader />
<Box component="main" sx={{ display: 'flex', flexDirection: 'column', flex: 1 }}>
Expand Down
Loading
Loading