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
20 changes: 11 additions & 9 deletions components/Layout/SearchBlock.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { useRouter } from 'next/router'
import axios from 'axios'
import { useSearchParams } from 'next/navigation'
import Link from 'next/link'
import { classicAddressToXAddress } from 'ripple-address-codec'
import { IoMdClose } from 'react-icons/io'

import {
Expand Down Expand Up @@ -153,9 +154,9 @@ export default function SearchBlock({ searchPlaceholderText, tab = null, userDat
const searchOnChange = (option) => {
if (!option) return
if (option.username && !option.username.includes('-')) {
onSearch(option.username)
onSearch(option.username, option?.tag)
} else {
onSearch(option.address)
onSearch(option.address, option?.tag)
}
}

Expand All @@ -178,7 +179,7 @@ export default function SearchBlock({ searchPlaceholderText, tab = null, userDat
}
}

const onSearch = async (si) => {
const onSearch = async (si, tag = null) => {
setErrorMessage('')
let searchFor = null

Expand Down Expand Up @@ -279,14 +280,15 @@ export default function SearchBlock({ searchPlaceholderText, tab = null, userDat
return
}

if (isValidPayString(searchFor) || isValidXAddress(searchFor)) {
if (isValidPayString(searchItem) || isValidXAddress(searchItem)) {
// the check for paystring/xAddress should be before the check for addressOrUsername,
// as if there is no destination tag, we will treat it as an address or username

// we need to resolve paystring and x-address first before redirecting!
// if there is a tag -
// get the new page which we can show an address and a tag
router.push('/account/' + encodeURI(searchFor) + addParams) //replace with a new page to show a tag
if(tag) {
const xAddress = classicAddressToXAddress(searchFor, tag)
router.push('/account/tag/' + encodeURI(xAddress) + addParams)
} else {
router.push('/account/' + encodeURI(searchFor) + addParams)
}
return
}

Expand Down
1 change: 1 addition & 0 deletions pages/_app.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ const WalletConnectModalSign = dynamic(
import '../styles/globals.css'
import '../styles/ui.scss'
import '../styles/components/nprogress.css'
import '../styles/pages/account-tag.scss'

import { ThemeProvider } from '../components/Layout/ThemeContext'
import { fetchCurrentFiatRate } from '../utils/common'
Expand Down
267 changes: 267 additions & 0 deletions pages/account/tag/[...id].js
Original file line number Diff line number Diff line change
@@ -0,0 +1,267 @@
import { useTranslation } from 'next-i18next'
import { useEffect } from 'react'
import { useRouter } from 'next/router'
import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
import { axiosServer, passHeaders } from '../../../utils/axios'
import { xAddressToClassicAddress } from 'ripple-address-codec'

import SEO from '../../../components/SEO'
import SearchBlock from '../../../components/Layout/SearchBlock'
import {
FaFacebook,
FaInstagram,
FaLinkedin,
FaMedium,
FaReddit,
FaTelegram,
FaYoutube,
FaXTwitter
} from 'react-icons/fa6'

export async function getServerSideProps(context) {
const { locale, query, req } = context
let resolvedData = null
let errorMessage = null

const { id } = query
const searchInput = id ? (Array.isArray(id) ? id[0] : id) : ''

if (searchInput) {
try {
let address = null
let destinationTag = null

try {
const decoded = xAddressToClassicAddress(searchInput)
address = decoded.classicAddress
destinationTag = decoded.tag
} catch (error) {
errorMessage = 'Invalid xAddress format'
}

if (address) {
// Fetch account data for the resolved address
const accountResponse = await axiosServer({
method: 'get',
url: `v2/address/${address}?username=true&service=true&verifiedDomain=true&parent=true&nickname=true&inception=true&flare=true&blacklist=true&payString=true&ledgerInfo=true&xamanMeta=true&bithomp=true&obligations=true`,
headers: passHeaders(req)
})

resolvedData = {
originalInput: searchInput,
address,
destinationTag,
accountData: accountResponse?.data
}
}
} catch (error) {
errorMessage = 'Error processing request'
}
}

return {
props: {
resolvedData: resolvedData || {},
errorMessage: errorMessage || null,
...(await serverSideTranslations(locale, ['common', 'account']))
}
}
}

export default function AccountTag({ resolvedData, errorMessage }) {
const { t } = useTranslation()
const router = useRouter()

// Function to render social links dynamically based on account data
const renderSocialLinks = (socialAccounts) => {
if (!socialAccounts) return null

return (
<div className="account-tag-social-icons">
{socialAccounts.twitter && (
<a href={'https://x.com/' + socialAccounts.twitter} aria-label="X" target="_blank" rel="noopener">
<FaXTwitter />
</a>
)}
{socialAccounts.youtube && (
<a
href={'https://youtube.com/' + socialAccounts.youtube}
aria-label="Youtube"
target="_blank"
rel="noopener"
>
<FaYoutube />
</a>
)}
{socialAccounts.linkedin && (
<a
href={'https://linkedin.com/company/' + socialAccounts.linkedin + '/'}
aria-label="Linkedin"
target="_blank"
rel="noopener"
>
<FaLinkedin />
</a>
)}
{socialAccounts.instagram && (
<a
href={'https://www.instagram.com/' + socialAccounts.instagram + '/'}
aria-label="Instagram"
target="_blank"
rel="noopener"
>
<FaInstagram />
</a>
)}
{socialAccounts.telegram && (
<a
href={'https://t.me/' + socialAccounts.telegram}
aria-label="Telegram"
target="_blank"
rel="noopener"
>
<FaTelegram />
</a>
)}
{socialAccounts.facebook && (
<a
href={'https://www.facebook.com/' + socialAccounts.facebook + '/'}
aria-label="Facebook"
target="_blank"
rel="noopener"
>
<FaFacebook />
</a>
)}
{socialAccounts.medium && (
<a
href={'https://medium.com/' + socialAccounts.medium}
aria-label="Medium"
target="_blank"
rel="noopener"
>
<FaMedium />
</a>
)}
{socialAccounts.reddit && (
<a
href={'https://www.reddit.com/' + socialAccounts.reddit + '/'}
aria-label="Reddit"
target="_blank"
rel="noopener"
>
<FaReddit />
</a>
)}
</div>
)
}

useEffect(() => {
// If no destination tag found, redirect to regular account page
if (resolvedData?.address && (!resolvedData?.destinationTag || resolvedData?.destinationTag === false)) {
router.replace(`/account/${resolvedData.address}`)
return
}
}, [resolvedData, router])

if (errorMessage) {
return (
<>
<SEO page="Account with Tag" title="Error - Account with Destination Tag" />
<SearchBlock searchPlaceholderText={t('explorer.enter-address')} tab="account" />
<div className="content-center">
<div className="center orange bold" style={{ marginTop: '50px' }}>
{errorMessage}
</div>
</div>
</>
)
}

if (!resolvedData?.address || !resolvedData?.destinationTag) {
return (
<>
<SEO page="Account with Tag" title="Account with Destination Tag" />
<SearchBlock searchPlaceholderText={t('explorer.enter-address')} tab="account" />
<div className="content-center">
<div className="center" style={{ marginTop: '50px' }}>
<span className="waiting"></span>
<br />
{t('general.loading')}
</div>
</div>
</>
)
}

const { originalInput, address, destinationTag, accountData } = resolvedData

const userData = {
username: accountData?.username,
service: accountData?.service?.name,
address: accountData?.address || address
}

return (
<>
<SEO
page="Account with Tag"
title={`${t('explorer.header.account')} ${userData.service || userData.username || userData.address} - Tag: ${destinationTag}`}
description={`Account details for ${userData.service || userData.username || ''} ${userData.address} with destination tag ${destinationTag}`}
/>
<SearchBlock searchPlaceholderText={t('explorer.enter-address')} tab="account" userData={userData} />

<div className="content-profile account">
<div className="account-tag-container">
<div className="account-tag-title">
X-ADDRESS DETAILS
</div>

<div className="account-tag-xaddress">
<b>{originalInput}</b>
</div>

<div className="account-tag-service-description">
This address belongs to{' '}
<a href={`https://${accountData?.service?.domain}`} target="_blank" rel="noopener noreferrer" className="account-tag-service-link">
{accountData?.service?.domain}
</a>{' '}
<b>({accountData?.service?.name}).</b>
</div>

{accountData?.bithomp?.avatar && (
<div className="account-tag-logo-container">
<img
src={accountData.bithomp.avatar}
alt={accountData.bithomp.name}
className="account-tag-logo"
/>
</div>
)}

{renderSocialLinks(accountData?.service?.socialAccounts)}

<div className="account-tag-details">
<div className="account-tag-detail-label">
<span>Service address:</span>
</div>
<div className="account-tag-detail-value">
<a href={`/account/${address}`} className="account-tag-address-link">
{address}
</a>
</div>
</div>
<div className="account-tag-details">
<div className="account-tag-detail-label">
<span>User destination tag:</span>
</div>
<div className="account-tag-detail-value">
<b>{destinationTag}</b>
</div>
</div>
</div>
</div>
</>
)
}
Loading