Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
c1148b1
feat(pulse): Mobile-responsive token search UI with blockchain filter…
aldin4u Nov 21, 2025
8751c12
fix: ensure refresh and chain selector buttons visible on mobile
aldin4u Nov 21, 2025
4fa6f62
test: fix Search.test.tsx - all tests passing
aldin4u Nov 22, 2025
19e99f3
test: fix AppWrapper.test.tsx - all tests passing
aldin4u Nov 22, 2025
3d329f6
fix: resolve all real lint errors manually (no auto-fix)
aldin4u Nov 22, 2025
17cce36
fix: disable Prettier ESLint rule and resolve all lint errors
aldin4u Nov 22, 2025
08f1c2d
fix: correct chain filter logic in parseMarketPairs
aldin4u Nov 22, 2025
f0357d0
fix: address all PR #461 review comments
aldin4u Nov 24, 2025
7b5c2d0
Merge branch 'staging' into feature/pulse-search-ui-mobile-refinements
aldin4u Nov 24, 2025
d3be9cd
fix: restore ESC button in Sell mode, address PR comments (lint/unuse…
aldin4u Nov 24, 2025
1d700d5
fix: remove unused getTokenBalance function and tokenBalance variable
aldin4u Nov 25, 2025
7be09b6
fix: resolve lint errors in ChainOverlay and MarketList, update CardS…
aldin4u Nov 25, 2025
14081f3
changes for unit tests and a few feedback changes
vignesha22 Nov 27, 2025
b0fa0b9
changed styles
vignesha22 Nov 27, 2025
d98fd81
updated snapshop
vignesha22 Nov 27, 2025
94adff1
added specific width and height
vignesha22 Nov 27, 2025
9467f03
fixed lint issue
vignesha22 Nov 27, 2025
78ed786
fixed test
vignesha22 Nov 27, 2025
bae51b0
changes as per feedback
vignesha22 Dec 1, 2025
a0bcfb4
added error handling
vignesha22 Dec 1, 2025
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
42 changes: 35 additions & 7 deletions src/apps/pulse/components/Buy/Buy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,35 @@ export default function Buy(props: BuyProps) {
const [permittedChains, setPermittedChains] = useState<bigint[]>([]);
const [sumOfStableBalance, setSumOfStableBalance] = useState<number>(0);



// Get the user's balance for the selected token (to display in PnL)
const getTokenBalance = () => {
try {
if (!token || !walletPortfolioData?.result?.data?.assets) return 0;

// Find the asset in the portfolio
const assetData = walletPortfolioData.result.data.assets.find(
(asset) => asset.asset.symbol === token.symbol
);

if (!assetData) return 0;

// Find the contract balance for the specific token address and chain
const contractBalance = assetData.contracts_balances.find(
(contract) =>
contract.address.toLowerCase() === token.address.toLowerCase() &&
contract.chainId === `evm:${token.chainId}`
);
return contractBalance?.balance || 0;
} catch (error) {
console.error('Error getting token balance:', error);
return 0;
}
};
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated

const tokenBalance = getTokenBalance();

useEffect(() => {
if (!portfolioTokens || portfolioTokens.length === 0) {
console.warn('No wallet portfolio data');
Expand All @@ -179,7 +208,7 @@ export default function Buy(props: BuyProps) {
const nativeToken = portfolioTokens.find(
(t) =>
Number(getChainId(t.blockchain as MobulaChainNames)) ===
maxStableCoinBalance.chainId && isNativeToken(t.contract)
maxStableCoinBalance.chainId && isNativeToken(t.contract)
);

if (!nativeToken) {
Expand Down Expand Up @@ -747,11 +776,10 @@ export default function Buy(props: BuyProps) {
className="flex bg-black ml-2.5 mr-2.5 w-[75px] h-[30px] rounded-[10px] p-0.5 pb-1 pt-0.5"
>
<button
className={`flex-1 items-center justify-center rounded-[10px] ${
isDisabled
? 'bg-[#1E1D24] text-grey cursor-not-allowed'
: 'bg-[#121116] text-white cursor-pointer'
}`}
className={`flex-1 items-center justify-center rounded-[10px] ${isDisabled
? 'bg-[#1E1D24] text-grey cursor-not-allowed'
: 'bg-[#121116] text-white cursor-pointer'
}`}
onClick={() => {
if (!isDisabled) {
if (isMax) {
Expand Down Expand Up @@ -780,7 +808,7 @@ export default function Buy(props: BuyProps) {
areModulesInstalled={areModulesInstalled}
debouncedUsdAmount={debouncedUsdAmount}
expressIntentResponse={
USE_RELAY_BUY ? buyOffer : expressIntentResponse
USE_RELAY_BUY ? (buyOffer as any) : expressIntentResponse
Comment thread
aldin4u marked this conversation as resolved.
Outdated
}
handleBuySubmit={handleBuySubmit}
isFetching={isFetching}
Expand Down
2 changes: 1 addition & 1 deletion src/apps/pulse/components/Price/TokenPrice.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export default function TokenPrice(props: TokenPriceProps): JSX.Element {
style={{ fontSize: 13, fontWeight: 400 }}
data-testid="pulse-token-price"
>
${value.toFixed(5)}
${value.toFixed(2)}
</p>
);
}
Expand Down
208 changes: 113 additions & 95 deletions src/apps/pulse/components/Search/ChainOverlay.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import React from 'react';
import { chainNameToChainIdTokensData } from '../../../../services/tokensData';
import { getLogoForChainId } from '../../../../utils/blockchain';
import GlobeIcon from '../../assets/globe-icon.svg';
Expand All @@ -12,100 +13,117 @@ export interface ChainOverlayProps {
chains: MobulaChainNames;
}

export default function ChainOverlay(chainOverlayProps: ChainOverlayProps) {
const {
setShowChainOverlay,
setChains,
setOverlayStyle,
overlayStyle,
chains,
} = chainOverlayProps;
return (
<>
<div
style={{
position: 'fixed',
top: 0,
left: 0,
width: 200,
height: 210,
zIndex: 1999,
}}
onClick={() => {
setShowChainOverlay(false);
setOverlayStyle({});
}}
/>
<div style={overlayStyle} onClick={(e) => e.stopPropagation()}>
<div style={{ padding: '12px 0', height: '100%', overflowY: 'auto' }}>
{Object.values(MobulaChainNames).map((chain) => {
const isSelected = chains === chain;
const isAll = chain === MobulaChainNames.All;
let logo = null;
if (isAll) {
logo = (
<span
style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
width: 24,
height: 24,
}}
>
<img src={GlobeIcon} alt="globe-icon" />
</span>
);
} else {
const chainId = chainNameToChainIdTokensData(chain);
logo = (
<img
src={getLogoForChainId(chainId)}
alt={chain}
style={{
width: 24,
height: 24,
borderRadius: '50%',
background: '#23222A',
}}
/>
);
}
return (
<div
key={chain}
onClick={() => {
setChains(chain);
setShowChainOverlay(false);
setOverlayStyle({});
}}
style={{
display: 'flex',
alignItems: 'center',
gap: 8,
padding: '10px 18px',
cursor: 'pointer',
background: isSelected ? '#29292F' : 'transparent',
color: isSelected ? '#fff' : '#b0b0b0',
fontWeight: isSelected ? 500 : 400,
fontSize: 16,
position: 'relative',
}}
>
{logo}
<span style={{ flex: 1, marginLeft: 10 }}>
{chain === MobulaChainNames.All ? 'All chains' : chain}
</span>
{isSelected && (
<div>
<img src={SelectedIcon} alt="selected-icon" />
const ChainOverlay = React.forwardRef<HTMLDivElement, ChainOverlayProps>(
(chainOverlayProps, ref) => {
const {
setShowChainOverlay,
setChains,
setOverlayStyle,
overlayStyle,
chains,
} = chainOverlayProps;
return (
<>
<div
style={{
position: 'fixed',
top: 0,
left: 0,
right: 0,
bottom: 0,
width: '100vw',
height: '100vh',
zIndex: 1999,
cursor: 'default',
}}
onClick={() => {
setShowChainOverlay(false);
setOverlayStyle({});
}}
/>
<div ref={ref} style={overlayStyle} onClick={(e) => e.stopPropagation()}>
<div style={{ padding: '12px 0', height: '100%', overflowY: 'auto' }}>
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
{Object.values(MobulaChainNames)
.filter((chain) => chain !== MobulaChainNames.XDAI) // Remove XDAI
.sort((a, b) => {
// Put "All" first, then alphabetical
if (a === MobulaChainNames.All) return -1;
if (b === MobulaChainNames.All) return 1;
return a.localeCompare(b);
})
Comment thread
coderabbitai[bot] marked this conversation as resolved.
.map((chain) => {
const isSelected = chains === chain;
const isAll = chain === MobulaChainNames.All;
let logo = null;
if (isAll) {
logo = (
<span
style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
width: 24,
height: 24,
}}
>
<img src={GlobeIcon} alt="globe-icon" />
</span>
);
} else {
const chainId = chainNameToChainIdTokensData(chain);
logo = (
<img
src={getLogoForChainId(chainId)}
alt={chain}
style={{
width: 24,
height: 24,
borderRadius: '50%',
background: '#23222A',
}}
/>
);
}
return (
<div
key={chain}
onClick={() => {
setChains(chain);
setShowChainOverlay(false);
setOverlayStyle({});
}}
style={{
display: 'flex',
alignItems: 'center',
gap: 8,
padding: '10px 18px',
cursor: 'pointer',
background: isSelected ? '#29292F' : 'transparent',
color: isSelected ? '#fff' : '#b0b0b0',
fontWeight: isSelected ? 500 : 400,
fontSize: 16,
position: 'relative',
}}
>
{logo}
<span style={{ flex: 1, marginLeft: 10 }}>
{chain === MobulaChainNames.All ? 'All chains' : chain}
</span>
{isSelected && (
<div>
<img src={SelectedIcon} alt="selected-icon" />
</div>
)}
</div>
)}
</div>
);
})}
);
})}
</div>
</div>
</div>
</>
);
}
</>
);
}
);

ChainOverlay.displayName = 'ChainOverlay';

export default ChainOverlay;
Loading
Loading