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
6 changes: 3 additions & 3 deletions netlify/edge-functions/seo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -190,13 +190,13 @@ async function buildMeta(pathname: string, fullUrl: URL): Promise<Meta> {
const userMatch = pathname.match(/^\/users\/([^/]+)/);
if (userMatch) {
const address = userMatch[1];
const apiUrl = `${API_BASE.replace(/\/$/, '')}/api/accounts/${encodeURIComponent(address)}`;
const apiUrl = `${API_BASE.replace(/\/$/, '')}/api/profile/${encodeURIComponent(address)}`;
try {
const r = await fetch(apiUrl, { headers: { accept: 'application/json' } });
if (r.ok) {
const data: any = await r.json();
const display = (data?.chain_name || address) as string;
const bio = (data?.bio || '').toString();
const display = (data?.public_name || data?.profile?.chain_name || address) as string;
const bio = (data?.profile?.bio || '').toString();
return {
title: `${display} – Profile – Superhero`,
description: bio ? truncate(bio, 200) : 'View profile on Superhero, the crypto social network.',
Expand Down
6 changes: 3 additions & 3 deletions netlify/functions/ssr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,13 +128,13 @@ async function buildMeta(pathname: string): Promise<Meta> {
const userMatch = pathname.match(/^\/users\/([^/]+)/);
if (userMatch) {
const address = userMatch[1];
const apiUrl = `${API_BASE.replace(/\/$/, '')}/api/accounts/${encodeURIComponent(address)}`;
const apiUrl = `${API_BASE.replace(/\/$/, '')}/api/profile/${encodeURIComponent(address)}`;
try {
const r = await fetch(apiUrl, { headers: { accept: 'application/json' } });
if (r.ok) {
const data: any = await r.json();
const display = (data?.chain_name || address) as string;
const bio = (data?.bio || '').toString();
const display = (data?.public_name || data?.profile?.chain_name || address) as string;
const bio = (data?.profile?.bio || '').toString();
return {
title: `${display} – Profile – Superhero`,
description: truncate(bio || `View ${display} on Superhero, the crypto social network.`, 160),
Expand Down
13 changes: 9 additions & 4 deletions server/index.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -93,14 +93,19 @@ async function buildMeta(pathname, origin){
const um = pathname.match(/^\/users\/([^/]+)/);
if (um) {
const address = um[1];
let display = address;
let bio = '';
try {
const r = await fetch(`${API_BASE.replace(/\/$/, '')}/api/accounts/${encodeURIComponent(address)}`, { headers: { accept: 'application/json' } });
if (r.ok) { const data = await r.json(); bio = String(data?.bio||'').trim(); }
const r = await fetch(`${API_BASE.replace(/\/$/, '')}/api/profile/${encodeURIComponent(address)}`, { headers: { accept: 'application/json' } });
if (r.ok) {
const data = await r.json();
display = String(data?.public_name || data?.profile?.chain_name || address).trim() || address;
bio = String(data?.profile?.bio || '').trim();
}
} catch {}
return {
title: `${address} – Profile – Superhero`,
description: bio ? truncate(bio,200) : 'View profile on Superhero, the crypto social network.',
title: `${display} – Profile – Superhero`,
description: bio ? truncate(bio,200) : `View ${display} on Superhero, the crypto social network.`,
canonical: `${origin}/users/${address}`,
ogImage: `${origin}/og-default.png`,
ogType: 'profile',
Expand Down
51 changes: 33 additions & 18 deletions src/@components/Address/AddressAvatarWithChainName.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { useAccountBalances } from '@/hooks/useAccountBalances';
import { useChainName } from '@/hooks/useChainName';
import { cn } from '@/lib/utils';
import { Decimal } from '@/libs/decimal';
import { resolveDisplayName } from '@/utils/displayName';

interface AddressAvatarWithChainNameProps {
address: string;
Expand Down Expand Up @@ -131,7 +132,10 @@ export const AddressAvatarWithChainName = memo(({
return null;
}

const preferredName = (cachedProfile?.public_name || cachedProfile?.profile?.chain_name || chainName || '').trim();
const preferredName = resolveDisplayName({
publicName: cachedProfile?.public_name,
chainName: cachedProfile?.profile?.chain_name || chainName,
});
const avatarUrl = (cachedProfile?.profile?.avatarurl || '').trim() || null;

const renderContent = () => (
Expand Down Expand Up @@ -173,19 +177,24 @@ export const AddressAvatarWithChainName = memo(({
<div className={cn(contentBaseClass, contentClassName)}>
{showPrimaryOnly ? (
(() => {
const displayName = preferredName || (!hideFallbackName ? 'Legend' : '');
return displayName ? (
<span
className={[
'chain-name text-[14px] md:text-[15px] font-bold',
'bg-gradient-to-r from-[var(--neon-teal)] via-[var(--neon-teal)] to-teal-300',
'bg-clip-text text-transparent block truncate w-full',
].join(' ')}
title={displayName}
>
{displayName}
</span>
) : (
if (preferredName) {
return (
<span
className={[
'chain-name text-[14px] md:text-[15px] font-bold',
'bg-gradient-to-r from-[var(--neon-teal)] via-[var(--neon-teal)] to-teal-300',
'bg-clip-text text-transparent block truncate w-full',
].join(' ')}
title={preferredName}
>
{preferredName}
</span>
);
}

if (hideFallbackName) return null;

return (
<span
className={cn(
'text-sm font-bold bg-gradient-to-r from-[var(--neon-teal)] via-[var(--neon-teal)] to-teal-300 bg-clip-text text-transparent leading-tight font-sans',
Expand All @@ -200,8 +209,11 @@ export const AddressAvatarWithChainName = memo(({
) : (
showAddressAndChainName && (
<>
<span className={chainNameClass}>
{preferredName || (hideFallbackName ? '' : 'Legend')}
<span
className={cn(chainNameClass, 'block truncate w-full')}
title={preferredName || address}
>
{preferredName || (hideFallbackName ? '' : address)}
</span>
<span className="text-xs text-white/70 font-mono leading-[0.9] no-gradient-text">
<AddressFormatted
Expand Down Expand Up @@ -231,10 +243,13 @@ export const AddressAvatarWithChainName = memo(({
);

return (
<span className="relative inline-flex items-center" style={{ zIndex: 'auto' }}>
<span className="relative flex min-w-0 max-w-full items-center" style={{ zIndex: 'auto' }}>
<div
ref={ref}
className={cn('flex items-center cursor-pointer transition-colors hover:text-foreground', className)}
className={cn(
'flex min-w-0 max-w-full items-center cursor-pointer transition-colors hover:text-foreground',
className,
)}
onMouseEnter={() => setHover(true)}
onMouseLeave={() => setHover(false)}
onClick={handleClick}
Expand Down
2 changes: 0 additions & 2 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import ModalProvider from './components/ModalProvider';
import {
useAeSdk, useAccount, useIsMobile, useWalletConnect,
} from './hooks';
import { useProfileFeed } from './hooks/useProfileFeed';
import { routes } from './routes';
import './styles/genz-components.scss';
import './styles/mobile-optimizations.scss';
Expand Down Expand Up @@ -39,7 +38,6 @@ const TipModal = React.lazy(
const App = () => {
const isMobile = useIsMobile();
useSuperheroChainNames();
useProfileFeed({ refetchIntervalMs: 20_000 });
const { initSdk, activeAccount } = useAeSdk();
const { loadAccountData } = useAccount();
const { attemptReconnection } = useWalletConnect();
Expand Down
39 changes: 0 additions & 39 deletions src/api/ProfileRegistryACI.json
Original file line number Diff line number Diff line change
Expand Up @@ -218,20 +218,6 @@
},
"stateful": true
},
{
"arguments": [
{
"name": "source",
"type": "ProfileRegistry.display_source"
}
],
"name": "set_display_source",
"payable": false,
"returns": {
"tuple": []
},
"stateful": true
},
{
"arguments": [
{
Expand Down Expand Up @@ -269,10 +255,6 @@
"int"
]
}
},
{
"name": "source",
"type": "ProfileRegistry.display_source"
}
],
"name": "set_profile_full",
Expand Down Expand Up @@ -349,23 +331,6 @@
"typedef": "string",
"vars": []
},
{
"name": "display_source",
"typedef": {
"variant": [
{
"Custom": []
},
{
"Chain": []
},
{
"X": []
}
]
},
"vars": []
},
{
"name": "profile",
"typedef": {
Expand Down Expand Up @@ -406,10 +371,6 @@
]
}
},
{
"name": "display_source",
"type": "ProfileRegistry.display_source"
},
{
"name": "chain_expires_at",
"type": {
Expand Down
15 changes: 0 additions & 15 deletions src/api/backend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,6 @@ export type ProfileAggregate = {
public_name: string | null;
};

export type ProfileFeedResponse = {
items?: ProfileAggregate[];
data?: ProfileAggregate[];
} | ProfileAggregate[];

export type XAttestationResponse = {
signer: string;
address: string;
Expand Down Expand Up @@ -346,8 +341,6 @@ export const SuperheroApi = {
const qp = new URLSearchParams();
if (includeOnChain != null) qp.set('includeOnChain', String(includeOnChain));
const query = qp.toString();
// TODO: uncomment this when the backend is ready
return Promise.resolve(null);
return this.fetchJson(`/api/profile/${encodeURIComponent(address)}${query ? `?${query}` : ''}`) as Promise<ProfileAggregate>;
},
getProfilesByAddresses(addresses: string[], includeOnChain?: boolean) {
Expand All @@ -356,14 +349,6 @@ export const SuperheroApi = {
if (includeOnChain != null) qp.set('includeOnChain', String(includeOnChain));
return this.fetchJson(`/api/profile?${qp.toString()}`) as Promise<ProfileAggregate[]>;
},
getProfileFeed(limit = 500, offset = 0) {
const qp = new URLSearchParams();
qp.set('limit', String(limit));
qp.set('offset', String(offset));
return Promise.resolve({ items: [], data: [] } as ProfileFeedResponse);
// TODO: uncomment this when the backend is ready
// return this.fetchJson(`/api/profile/feed?${qp.toString()}`) as Promise<ProfileFeedResponse>;
},
createXAttestation(address: string, accessToken: string) {
return this.fetchJson('/api/profile/x/attestation', {
method: 'POST',
Expand Down
2 changes: 0 additions & 2 deletions src/atoms/walletAtoms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,6 @@ export const aex9BalancesAtom = atomWithStorage<Record<string, any[]>>('wallet:a
export const profileAtom = atom<Record<string, any>>({});
export const pinnedItemsAtom = atom<any[]>([]);
export const chainNamesAtom = atomWithStorage<Record<string, string>>('wallet:chainNames', {});
/** Profile display names (public_name from /api/profile). Used for author labels in feed. */
export const profileDisplayNamesAtom = atom<Record<string, string>>({});
export const verifiedUrlsAtom = atom<string[]>([]);
export const graylistedUrlsAtom = atom<string[]>([]);
export const tokenInfoAtom = atom<Record<string, { symbol: string; decimals: number }>>({});
Expand Down
20 changes: 19 additions & 1 deletion src/components/Account/AccountFeed.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import {
import { useInfiniteQuery } from '@tanstack/react-query';
import { SuperheroApi } from '@/api/backend';
import type { PostDto } from '@/api/generated';
import { useAccountDisplayNames } from '@/hooks/useAccountDisplayNames';
import { getPostSenderAddress } from '@/features/social/utils/postSender';

interface AccountFeedProps {
address: string;
Expand Down Expand Up @@ -126,6 +128,18 @@ const AccountFeed = ({ address, tab }: AccountFeedProps) => {
});
}, [createdActivities, list]);

const tokenCreatedAddresses = useMemo(
() => combinedList
.filter((item) => String(item.id).startsWith('token-created:'))
.map((item) => getPostSenderAddress(item))
.filter(Boolean),
[combinedList],
);

const { getHeaderLabel: getTokenCreatedHeaderLabel } = useAccountDisplayNames(
tokenCreatedAddresses,
);

const sentinelRef = useRef<HTMLDivElement | null>(null);
const fetchingRef = useRef(false);
const initialLoading = aLoading || isLoading;
Expand Down Expand Up @@ -245,6 +259,10 @@ const AccountFeed = ({ address, tab }: AccountFeedProps) => {
<TokenCreatedActivityItem
key={gi.id}
item={gi}
displayName={getTokenCreatedHeaderLabel(getPostSenderAddress(gi), {
fallbackToAddress: true,
fallbackLabel: 'Legend',
})}
hideMobileDivider={hideDivider}
mobileTight={mobileTight}
mobileNoTopPadding={mobileNoTopPadding}
Expand Down Expand Up @@ -281,7 +299,7 @@ const AccountFeed = ({ address, tab }: AccountFeedProps) => {
}
}
return nodes;
}, [combinedList, expandedGroups, navigate, toggleGroup]);
}, [combinedList, expandedGroups, navigate, toggleGroup, getTokenCreatedHeaderLabel]);

return (
<div className="w-full">
Expand Down
4 changes: 2 additions & 2 deletions src/components/Identicon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ type IdenticonProps = {
};

const Identicon = ({ address, size = 32, name }: IdenticonProps) => {
// Check if this is a .chain name (has a name and it's not 'Legend')
const isChainName = name && name !== 'Legend' && name !== address;
// Treat names distinct from the raw address as display names.
const isChainName = name && name !== address;

if (isChainName) {
// Use multiavatar for .chain names
Expand Down
9 changes: 5 additions & 4 deletions src/components/modals/TipModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ import { AddressAvatarWithChainName } from '@/@components/Address/AddressAvatarW
import { encode, Encoded, Encoding } from '@aeternity/aepp-sdk';
import { useAtom } from 'jotai';
import { useQueryClient } from '@tanstack/react-query';
import { useAccountDisplayName } from '@/hooks/useAccountDisplayName';
import { useAccount, useAeSdk } from '../../hooks';
import { toAettos, fromAettos } from '../../libs/dex';
import { Decimal } from '../../libs/decimal';
import AeButton from '../AeButton';
import { IconDiamond } from '../../icons';
import { useChainName } from '../../hooks/useChainName';
import { tipStatusAtom, makeTipKey } from '../../atoms/tipAtoms';

const TipModal = ({
Expand All @@ -24,7 +24,8 @@ const TipModal = ({
}) => {
const { sdk, activeAccount, activeNetwork } = useAeSdk();
const { balance } = useAccount();
const { chainName } = useChainName(toAddress);
const { displayName } = useAccountDisplayName(toAddress);
const hasDistinctDisplayName = !!displayName && displayName !== toAddress;
const [, setTipStatus] = useAtom(tipStatusAtom);
const queryClient = useQueryClient();

Expand Down Expand Up @@ -289,8 +290,8 @@ const TipModal = ({
isHoverEnabled={false}
/>
<div className="min-w-0">
{chainName && (
<div className="text-white/90 text-xs font-semibold truncate">{chainName}</div>
{hasDistinctDisplayName && (
<div className="text-white/90 text-xs font-semibold truncate">{displayName}</div>
)}
<div className="text-white/60 text-[11px] break-all truncate">{toAddress}</div>
</div>
Expand Down
Loading