From 508582addb59bd773ad9f7e3b176eb75a92020b4 Mon Sep 17 00:00:00 2001 From: Kuan Lin Date: Fri, 19 Dec 2025 09:58:25 -0500 Subject: [PATCH] fix MPT icon logo rendering --- src/containers/Token/MPT/Header/index.tsx | 8 +++-- .../Token/MPT/test/Header/Header.test.tsx | 15 ++++++++ src/containers/shared/test/utils.test.ts | 35 +++++++++++++++++++ src/containers/shared/utils.js | 27 ++++++++++++++ 4 files changed, 83 insertions(+), 2 deletions(-) diff --git a/src/containers/Token/MPT/Header/index.tsx b/src/containers/Token/MPT/Header/index.tsx index 91d4b8643..5070f9901 100644 --- a/src/containers/Token/MPT/Header/index.tsx +++ b/src/containers/Token/MPT/Header/index.tsx @@ -8,6 +8,7 @@ import { shortenDomain, shortenMPTID, stripHttpProtocol, + convertToHttpURL, } from '../../../shared/utils' import { CopyableText } from '../../../shared/components/CopyableText' import DomainLink from '../../../shared/components/DomainLink' @@ -102,6 +103,9 @@ export const Header = (props: Props) => { // Only show MPT issuance ID if ticker exists (since we show ticker in header, need to show ID somewhere) const showMPTIssuanceId = !!ticker + // Convert logo URL to HTTP/HTTPS format (handles IPFS URLs) + const RenderedLogoUrl = logoUrl ? convertToHttpURL(logoUrl) : undefined + // Get all URIs for dropdown, filtering out items without uri const allUris = (uris || []).filter((u) => u.uri) @@ -137,11 +141,11 @@ export const Header = (props: Props) => {
- {logoUrl ? ( + {RenderedLogoUrl ? ( {`${ticker ) : ( diff --git a/src/containers/Token/MPT/test/Header/Header.test.tsx b/src/containers/Token/MPT/test/Header/Header.test.tsx index aa1c713a1..df7524a57 100644 --- a/src/containers/Token/MPT/test/Header/Header.test.tsx +++ b/src/containers/Token/MPT/test/Header/Header.test.tsx @@ -115,6 +115,21 @@ describe('MPT Header component', () => { wrapper.unmount() }) + it('displays logo URL without protocol by prefixing https', () => { + const dataWithNoProtocolUrl = { + ...mockMPTData, + parsedMPTMetadata: { + ...mockMPTData.parsedMPTMetadata, + icon: 'example.com/logo.png', + }, + } + const wrapper = createWrapper({ data: dataWithNoProtocolUrl }) + const logo = wrapper.find('img.token-logo') + expect(logo.length).toBe(1) + expect(logo.prop('src')).toBe('https://example.com/logo.png') + wrapper.unmount() + }) + it('displays default logo when no icon', () => { const wrapper = createWrapper({ data: mockMPTDataNoMetadata }) expect(wrapper.find('.token-logo.no-logo').length).toBeGreaterThanOrEqual(1) diff --git a/src/containers/shared/test/utils.test.ts b/src/containers/shared/test/utils.test.ts index 23bb24c5f..b475e8f14 100644 --- a/src/containers/shared/test/utils.test.ts +++ b/src/containers/shared/test/utils.test.ts @@ -13,6 +13,7 @@ import { shortenNFTTokenID, shortenMPTID, stripHttpProtocol, + convertToHttpURL, } from '../utils' describe('utils', () => { @@ -271,4 +272,38 @@ describe('Shorten utils', () => { expect(shortenMPTID(shortMPTID)).toBe(shortMPTID) }) }) + + describe('convertToHttpUrl', () => { + it('converts IPFS URLs to HTTP URLs', () => { + expect( + convertToHttpURL( + 'ipfs://QmXhvvWs3HaFkJvDuYvanj2pv31yFQGJewfEhfme1Sv47Y', + ), + ).toBe( + 'https://ipfs.io/ipfs/QmXhvvWs3HaFkJvDuYvanj2pv31yFQGJewfEhfme1Sv47Y', + ) + }) + + it('preserves https:// URLs as-is', () => { + expect(convertToHttpURL('https://example.com/logo.png')).toBe( + 'https://example.com/logo.png', + ) + }) + + it('adds https:// to plain domain URLs', () => { + expect(convertToHttpURL('logo.svgcdn.com/logos/openai-icon.png')).toBe( + 'https://logo.svgcdn.com/logos/openai-icon.png', + ) + }) + + it('handles empty strings', () => { + expect(convertToHttpURL('')).toBe('') + }) + + it('handles other protocols', () => { + expect(convertToHttpURL('ftp://example.com/file.txt')).toBe( + 'ftp://example.com/file.txt', + ) + }) + }) }) diff --git a/src/containers/shared/utils.js b/src/containers/shared/utils.js index 6c439a67d..3183a957d 100644 --- a/src/containers/shared/utils.js +++ b/src/containers/shared/utils.js @@ -557,3 +557,30 @@ export const shortenMPTID = ( export const shortenTxHash = (txHash = '') => txHash.length > 12 ? `${txHash.slice(0, 6)}...${txHash.slice(-6)}` : txHash + +/** + * Converts URLs to HTTP/HTTPS format, handling IPFS URLs and plain domains + * @param {string} url - The URL to convert (can be ipfs://, https://, http://, or plain domain) + * @returns {string} The converted HTTP/HTTPS URL + */ +export const convertToHttpURL = (url) => { + if (!url) { + return url + } + + // Handle IPFS URLs - convert to HTTP + if (url.startsWith('ipfs://')) { + return url.replace('ipfs://', 'https://ipfs.io/ipfs/') + } + + // Matches a protocol (e.g. 'http://' or 'https://') at the start of a string + const PROTOCOL_REGEX = /^([a-z][a-z0-9+\-.]*):\/\// + + // If URL already has a protocol, return as is + if (PROTOCOL_REGEX.test(url)) { + return url + } + + // Otherwise, assume it's a plain domain and add https:// + return `https://${url}` +}