diff --git a/src/components/app-body/nfts-for-sale/seller-profile.js b/src/components/app-body/nfts-for-sale/seller-profile.js
new file mode 100644
index 0000000..5505755
--- /dev/null
+++ b/src/components/app-body/nfts-for-sale/seller-profile.js
@@ -0,0 +1,184 @@
+/*
+ This component displays the seller's profile information for an NFT listing.
+*/
+
+// Global npm libraries
+import React, { useState, useEffect } from 'react'
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
+import { faUser } from '@fortawesome/free-solid-svg-icons'
+
+function SellerProfile (props) {
+ const { npub, appData } = props
+ const [profile, setProfile] = useState(null)
+ const [imageError, setImageError] = useState(false)
+ const [isLoading, setIsLoading] = useState(true)
+
+ const { nostrQueries } = appData
+
+ // Get profile if npub is provided.
+ useEffect(() => {
+ const start = async () => {
+ setIsLoading(true)
+ try {
+ const pubKey = nostrQueries.npubToHex(npub)
+ const profile = await nostrQueries.getProfile(pubKey)
+ setProfile(profile)
+ } catch (error) {
+ console.warn('Error fetching seller profile:', error)
+ } finally {
+ setIsLoading(false)
+ }
+ }
+ if (npub) {
+ start()
+ } else {
+ setIsLoading(false)
+ }
+ }, [npub, nostrQueries])
+
+ // handle img url errors
+ const handleImageError = (type) => {
+ setImageError(true)
+ }
+
+ // go to profile
+ const goToProfile = () => {
+ const profileUrl = `${window.location.origin}/profile/${npub}#single-view`
+ window.open(profileUrl, '_blank')
+ }
+
+ // Get display name - use profile name, npub short form, or "Anonymous User"
+ const getDisplayName = () => {
+ if (profile?.name) return profile.name
+ if (npub) {
+ return npub.slice(0, 8) + '...' + npub.slice(-6)
+ }
+ return 'Anonymous User'
+ }
+
+ return (
+
{
+ e.currentTarget.style.backgroundColor = '#e9ecef'
+ e.currentTarget.style.borderColor = '#dee2e6'
+ }}
+ onMouseLeave={(e) => {
+ e.currentTarget.style.backgroundColor = '#f8f9fa'
+ e.currentTarget.style.borderColor = '#e9ecef'
+ }}
+ >
+
+ {/* Seller Label */}
+
+ Seller:
+
+
+ {/* Profile Picture or Placeholder */}
+
{
+ e.stopPropagation()
+ goToProfile()
+ }}
+ onMouseEnter={(e) => {
+ e.currentTarget.style.transform = 'scale(1.1)'
+ }}
+ onMouseLeave={(e) => {
+ e.currentTarget.style.transform = 'scale(1)'
+ }}
+ >
+ {isLoading
+ ? (
+
+ )
+ : (profile?.picture && !imageError)
+ ? (
+

handleImageError('picture')}
+ />
+ )
+ : (
+
+ )}
+
+
+ {/* Seller Name */}
+
{
+ e.stopPropagation()
+ goToProfile()
+ }}
+ className='cursor-pointer fw-medium'
+ style={{
+ color: '#495057',
+ fontSize: '0.8rem',
+ transition: 'color 0.2s ease',
+ maxWidth: '120px',
+ overflow: 'hidden',
+ textOverflow: 'ellipsis',
+ whiteSpace: 'nowrap'
+ }}
+ onMouseEnter={(e) => {
+ e.currentTarget.style.color = '#007bff'
+ }}
+ onMouseLeave={(e) => {
+ e.currentTarget.style.color = '#495057'
+ }}
+ title={profile?.name || npub}
+ >
+ {getDisplayName()}
+
+
+
+ )
+}
+
+export default SellerProfile
diff --git a/src/components/app-body/nfts-for-sale/token-card.js b/src/components/app-body/nfts-for-sale/token-card.js
index 3c7b80a..e562354 100644
--- a/src/components/app-body/nfts-for-sale/token-card.js
+++ b/src/components/app-body/nfts-for-sale/token-card.js
@@ -10,14 +10,12 @@ import Jdenticon from '@chris.troutner/react-jdenticon'
// Local libraries
import InfoButton from './info-button'
import BuyButton from './buy-button'
-
+import SellerProfile from './seller-profile'
function TokenCard (props) {
const { token, appData, handleRefresh, hideBuyBtn } = props
const [icon, setIcon] = useState(token.icon)
const [tokenData, setTokenData] = useState(token.tokenData)
- console.log('TokenCard() appData: ', appData)
-
// Update icon state every token.icon and token.tokenData changes
useEffect(() => {
console.log('setting icon')
@@ -28,8 +26,14 @@ function TokenCard (props) {
return (
<>
-
-
+
+
+ {/* Seller Profile Section */}
+
+
+
+
+
{/** If the icon is loaded, display it */
icon && (
{token.ticker}
-