mirror of
https://github.com/Permissionless-Software-Foundation/bch-dex-taker-v2.git
synced 2026-09-21 16:52:01 -07:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
93c8eb42d3 | ||
|
|
fa3c71f0a1 |
@@ -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 (
|
||||||
|
<div
|
||||||
|
onClick={goToProfile}
|
||||||
|
className='seller-profile-container'
|
||||||
|
style={{
|
||||||
|
padding: '4px 8px',
|
||||||
|
backgroundColor: '#f8f9fa',
|
||||||
|
borderRadius: '6px',
|
||||||
|
border: '1px solid #e9ecef',
|
||||||
|
transition: 'all 0.2s ease',
|
||||||
|
cursor: 'pointer',
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'center',
|
||||||
|
width: '100%'
|
||||||
|
}}
|
||||||
|
onMouseEnter={(e) => {
|
||||||
|
e.currentTarget.style.backgroundColor = '#e9ecef'
|
||||||
|
e.currentTarget.style.borderColor = '#dee2e6'
|
||||||
|
}}
|
||||||
|
onMouseLeave={(e) => {
|
||||||
|
e.currentTarget.style.backgroundColor = '#f8f9fa'
|
||||||
|
e.currentTarget.style.borderColor = '#e9ecef'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className='d-flex align-items-center justify-content-center gap-1'>
|
||||||
|
{/* Seller Label */}
|
||||||
|
<small
|
||||||
|
className='text-muted fw-semibold'
|
||||||
|
style={{
|
||||||
|
fontSize: '0.65rem',
|
||||||
|
textTransform: 'uppercase',
|
||||||
|
letterSpacing: '0.3px'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Seller:
|
||||||
|
</small>
|
||||||
|
|
||||||
|
{/* Profile Picture or Placeholder */}
|
||||||
|
<div
|
||||||
|
className='d-flex align-items-center justify-content-center'
|
||||||
|
style={{
|
||||||
|
width: '24px',
|
||||||
|
height: '24px',
|
||||||
|
borderRadius: '50%',
|
||||||
|
backgroundColor: '#e9ecef',
|
||||||
|
border: '1.5px solid #dee2e6',
|
||||||
|
overflow: 'hidden',
|
||||||
|
flexShrink: 0,
|
||||||
|
cursor: 'pointer',
|
||||||
|
transition: 'transform 0.2s ease'
|
||||||
|
}}
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
goToProfile()
|
||||||
|
}}
|
||||||
|
onMouseEnter={(e) => {
|
||||||
|
e.currentTarget.style.transform = 'scale(1.1)'
|
||||||
|
}}
|
||||||
|
onMouseLeave={(e) => {
|
||||||
|
e.currentTarget.style.transform = 'scale(1)'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{isLoading
|
||||||
|
? (
|
||||||
|
<FontAwesomeIcon
|
||||||
|
icon={faUser}
|
||||||
|
style={{
|
||||||
|
color: '#adb5bd',
|
||||||
|
fontSize: '12px',
|
||||||
|
opacity: 0.5
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
: (profile?.picture && !imageError)
|
||||||
|
? (
|
||||||
|
<img
|
||||||
|
src={profile.picture}
|
||||||
|
alt='Seller profile'
|
||||||
|
className='w-100 h-100'
|
||||||
|
style={{ objectFit: 'cover' }}
|
||||||
|
onError={() => handleImageError('picture')}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
: (
|
||||||
|
<FontAwesomeIcon
|
||||||
|
icon={faUser}
|
||||||
|
style={{
|
||||||
|
color: '#6c757d',
|
||||||
|
fontSize: '12px'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Seller Name */}
|
||||||
|
<span
|
||||||
|
onClick={(e) => {
|
||||||
|
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()}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SellerProfile
|
||||||
@@ -10,14 +10,12 @@ import Jdenticon from '@chris.troutner/react-jdenticon'
|
|||||||
// Local libraries
|
// Local libraries
|
||||||
import InfoButton from './info-button'
|
import InfoButton from './info-button'
|
||||||
import BuyButton from './buy-button'
|
import BuyButton from './buy-button'
|
||||||
|
import SellerProfile from './seller-profile'
|
||||||
function TokenCard (props) {
|
function TokenCard (props) {
|
||||||
const { token, appData, handleRefresh, hideBuyBtn } = props
|
const { token, appData, handleRefresh, hideBuyBtn } = props
|
||||||
const [icon, setIcon] = useState(token.icon)
|
const [icon, setIcon] = useState(token.icon)
|
||||||
const [tokenData, setTokenData] = useState(token.tokenData)
|
const [tokenData, setTokenData] = useState(token.tokenData)
|
||||||
|
|
||||||
console.log('TokenCard() appData: ', appData)
|
|
||||||
|
|
||||||
// Update icon state every token.icon and token.tokenData changes
|
// Update icon state every token.icon and token.tokenData changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.log('setting icon')
|
console.log('setting icon')
|
||||||
@@ -28,8 +26,14 @@ function TokenCard (props) {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Col xs={12} sm={6} lg={4} style={{ padding: '25px' }}>
|
<Col xs={12} sm={6} lg={4} style={{ padding: '25px' }}>
|
||||||
<Card>
|
<Card className='shadow-sm'>
|
||||||
<Card.Body style={{ textAlign: 'center' }}>
|
<Card.Body style={{ textAlign: 'center', padding: '10px' }}>
|
||||||
|
{/* Seller Profile Section */}
|
||||||
|
<Row className='mb-2'>
|
||||||
|
<Col className='text-center mb-2'>
|
||||||
|
<SellerProfile npub={token.makerNpub} appData={appData} />
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
{/** If the icon is loaded, display it */
|
{/** If the icon is loaded, display it */
|
||||||
icon && (
|
icon && (
|
||||||
<Card.Img
|
<Card.Img
|
||||||
@@ -50,7 +54,6 @@ function TokenCard (props) {
|
|||||||
<Card.Title style={{ textAlign: 'center', marginTop: '10px' }}>
|
<Card.Title style={{ textAlign: 'center', marginTop: '10px' }}>
|
||||||
<h4>{token.ticker}</h4>
|
<h4>{token.ticker}</h4>
|
||||||
</Card.Title>
|
</Card.Title>
|
||||||
|
|
||||||
<Container>
|
<Container>
|
||||||
<Row>
|
<Row>
|
||||||
<Col>
|
<Col>
|
||||||
|
|||||||
Reference in New Issue
Block a user