Skip to content
Open
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
8 changes: 6 additions & 2 deletions src/containers/Token/MPT/Header/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
shortenDomain,
shortenMPTID,
stripHttpProtocol,
convertToHttpURL,
} from '../../../shared/utils'
import { CopyableText } from '../../../shared/components/CopyableText'
import DomainLink from '../../../shared/components/DomainLink'
Expand Down Expand Up @@ -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
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: should be in camel case.


// Get all URIs for dropdown, filtering out items without uri
const allUris = (uris || []).filter((u) => u.uri)

Expand Down Expand Up @@ -137,11 +141,11 @@ export const Header = (props: Props) => {
</div>
<div className="section box-header">
<div className="token-info-group">
{logoUrl ? (
{RenderedLogoUrl ? (
<img
className="token-logo"
alt={`${ticker || mptIssuanceId} logo`}
src={logoUrl}
src={RenderedLogoUrl}
/>
) : (
<DefaultTokenIcon className="token-logo no-logo" />
Expand Down
15 changes: 15 additions & 0 deletions src/containers/Token/MPT/test/Header/Header.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
35 changes: 35 additions & 0 deletions src/containers/shared/test/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
shortenNFTTokenID,
shortenMPTID,
stripHttpProtocol,
convertToHttpURL,
} from '../utils'

describe('utils', () => {
Expand Down Expand Up @@ -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',
)
})
})
})
27 changes: 27 additions & 0 deletions src/containers/shared/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -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}`
}