Skip to content

Commit

Permalink
Merge pull request #1325 from near/develop
Browse files Browse the repository at this point in the history
Developer Toolbox
  • Loading branch information
gagdiez authored Oct 14, 2024
2 parents 6ce1729 + 1393297 commit 7e85300
Show file tree
Hide file tree
Showing 29 changed files with 1,316 additions and 1,021 deletions.
2 changes: 1 addition & 1 deletion next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ const nextConfig = {
compiler: { styledComponents: true },
reactStrictMode: true,
images: {
domains: ['ipfs.near.social','ipfs.io'],
domains: ['ipfs.near.social'],
},
experimental: {
optimizePackageImports: ['@phosphor-icons/react'],
Expand Down
3 changes: 3 additions & 0 deletions src/assets/images/near-icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 11 additions & 0 deletions src/assets/images/token_default.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
92 changes: 27 additions & 65 deletions src/components/NTFImage.tsx
Original file line number Diff line number Diff line change
@@ -1,76 +1,38 @@
import Image from 'next/image';
import { useCallback, useEffect, useState } from 'react';
import { useEffect, useState } from 'react';
import { useContext } from 'react';
import styled from 'styled-components';

import { NearContext } from './wallet-selector/WalletSelector';

const RoundedImage = styled(Image)`
border-radius: 50%;
`;
import type { NFT } from '@/utils/types';

interface Nft {
contractId: string;
tokenId: string;
}
import RoundedImage from './RoundedImage';
import { NearContext } from './wallet-selector/WalletSelector';

interface NftImageProps {
nft?: Nft;
ipfs_cid?: string;
alt: string;
nft?: NFT;
}

const DEFAULT_IMAGE = 'https://ipfs.near.social/ipfs/bafkreibmiy4ozblcgv3fm3gc6q62s55em33vconbavfd2ekkuliznaq3zm';

const getImage = (key: string) => {
const imgUrl = localStorage.getItem(`keysImage:${key}`);
return imgUrl || null;
};

const setImage = (key: string, url: string) => {
localStorage.setItem(`keysImage:${key}`, url);
};

export const NftImage: React.FC<NftImageProps> = ({ nft, ipfs_cid, alt }) => {
export const NftImage: React.FC<NftImageProps> = ({ nft }) => {
const { wallet } = useContext(NearContext);
const [imageUrl, setImageUrl] = useState<string>(DEFAULT_IMAGE);

const fetchNftData = useCallback(async () => {
if (!wallet || !nft || !nft.contractId || !nft.tokenId || ipfs_cid) return;

const imgCache = getImage(nft.tokenId);
if (imgCache) {
setImageUrl(imgCache);
return;
}
const [nftMetadata, tokenData] = await Promise.all([
wallet.viewMethod({ contractId: nft.contractId, method: 'nft_metadata' }),
wallet.viewMethod({ contractId: nft.contractId, method: 'nft_token', args: { token_id: nft.tokenId } }),
]);

const tokenMedia = tokenData?.metadata?.media || '';

if (tokenMedia.startsWith('https://') || tokenMedia.startsWith('http://') || tokenMedia.startsWith('data:image')) {
setImageUrl(tokenMedia);
} else if (nftMetadata?.base_uri) {
setImageUrl(`${nftMetadata.base_uri}/${tokenMedia}`);
} else if (tokenMedia.startsWith('Qm') || tokenMedia.startsWith('ba')) {
setImageUrl(`https://ipfs.near.social/ipfs/${tokenMedia}`);
}
}, [wallet, nft, ipfs_cid]);

useEffect(() => {
if (ipfs_cid) {
setImageUrl(`https://ipfs.near.social/ipfs/${ipfs_cid}`);
} else {
fetchNftData();
}
}, [ipfs_cid, fetchNftData]);
const [imageUrl, setImageUrl] = useState<string>('');

useEffect(() => {
if (!wallet || !nft || !nft.contractId || !nft.tokenId || ipfs_cid || DEFAULT_IMAGE === imageUrl) return;
setImage(nft.tokenId, imageUrl);
}, [imageUrl, wallet, nft, ipfs_cid]);

return <RoundedImage width={43} height={43} src={imageUrl} alt={alt} />;
const fetchNftData = async () => {
if (!wallet || !nft || !nft.token_id) return;

const tokenMedia = nft.metadata?.media || '';

if (tokenMedia.startsWith('https://') || tokenMedia.startsWith('http://')) {
setImageUrl(tokenMedia);
} else if (tokenMedia.startsWith('data:image')) {
setImageUrl(tokenMedia);
} else if (nft.metadata?.base_uri) {
setImageUrl(`${nft.metadata.base_uri}/${tokenMedia}`);
} else if (tokenMedia.startsWith('Qm') || tokenMedia.startsWith('ba')) {
setImageUrl(`https://ipfs.near.social/ipfs/${tokenMedia}`);
}
};

fetchNftData();
}, [nft, imageUrl, wallet]);

return <RoundedImage src={imageUrl} alt={nft?.metadata?.title || ''} />;
};
27 changes: 27 additions & 0 deletions src/components/RoundedImage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { useCallback, useEffect, useState } from 'react';
import styled from 'styled-components';

export const Img = styled.img`
border-radius: 50%;
overflow: hidden;
object-fit: cover;
`;

export const DEFAULT_IMAGE =
'https://ipfs.near.social/ipfs/bafkreibmiy4ozblcgv3fm3gc6q62s55em33vconbavfd2ekkuliznaq3zm';

const RoundedImage = ({ src, alt }: { src: string; alt: string }) => {
const [imageUrl, setImageUrl] = useState(src);

useEffect(() => {
setImageUrl(src);
}, [src]);

const handleError = useCallback(() => {
setImageUrl(DEFAULT_IMAGE);
}, []);

return <Img height={43} width={43} src={imageUrl || DEFAULT_IMAGE} alt={alt} onError={handleError} />;
};

export default RoundedImage;
36 changes: 27 additions & 9 deletions src/components/sidebar-navigation/UserDropdownMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ import styled from 'styled-components';

import { signInContractId } from '@/config';
import { useBosComponents } from '@/hooks/useBosComponents';
import type { NFT } from '@/utils/types';

import { NftImage } from '../NTFImage';
import RoundedImage from '../RoundedImage';
import { NearContext } from '../wallet-selector/WalletSelector';

const Wrapper = styled.div`
Expand Down Expand Up @@ -105,16 +107,29 @@ export const UserDropdownMenu = ({ collapsed }: Props) => {
}, [wallet]);

const [profile, setProfile] = useState<any>({});
const [nftProfile, setNftProfile] = useState<NFT | null>(null);

useEffect(() => {
async function getProfile() {
const profile = await wallet?.viewMethod({
contractId: 'social.near',
const socialProfile = await wallet?.viewMethod({
contractId: signInContractId,
method: 'get',
args: { keys: [`${signedAccountId}/profile/**`] },
});
if (!profile[signedAccountId]) return;
setProfile(profile[signedAccountId].profile);
if (!socialProfile[signedAccountId]) return;
const profile = socialProfile[signedAccountId].profile;
setProfile(profile);

try {
if (profile.image.nft) {
const nft = await wallet?.viewMethod({
contractId: profile.image.nft.contractId,
method: 'nft_token',
args: { token_id: profile.image.nft.tokenId },
});
setNftProfile(nft);
}
} catch (e) {}
}

async function getAvailableStorage() {
Expand All @@ -140,11 +155,14 @@ export const UserDropdownMenu = ({ collapsed }: Props) => {
</Dropdown.Trigger>
) : (
<Dropdown.Trigger>
<NftImage
nft={profile.image?.nft}
ipfs_cid={profile.image?.ipfs_cid}
alt={profile.name || signedAccountId}
/>
{nftProfile ? (
<NftImage nft={nftProfile} />
) : (
<RoundedImage
src={`https://ipfs.near.social/ipfs/${profile?.image?.ipfs_cid}`}
alt={profile.name || signedAccountId}
/>
)}
<div className="profile-info">
<div className="profile-name">{profile.name}</div>
<div className="profile-username">{signedAccountId}</div>
Expand Down
Loading

0 comments on commit 7e85300

Please sign in to comment.