Skip to content

Commit

Permalink
refactor: opt post
Browse files Browse the repository at this point in the history
  • Loading branch information
guanbinrui committed Dec 11, 2024
1 parent 27452f9 commit 2f40dc3
Show file tree
Hide file tree
Showing 7 changed files with 224 additions and 208 deletions.
5 changes: 3 additions & 2 deletions src/components/Advertisement/Swiper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,13 @@ import { openWindow } from '@/helpers/openWindow.js';
import { ActivityModalRef, LoginModalRef } from '@/modals/controls.js';
import { fireflyBridgeProvider } from '@/providers/firefly/Bridge.js';
import type { Advertisement } from '@/types/advertisement.js';
import { memo } from 'react';

interface Props extends React.HTMLProps<'div'> {
items: Advertisement[];
}

export function AdvertisementSwiper({ items }: Props) {
export const AdvertisementSwiper = memo(function AdvertisementSwiper({ items }: Props) {
return (
<Swiper
className="ff-advertisement"
Expand Down Expand Up @@ -78,4 +79,4 @@ export function AdvertisementSwiper({ items }: Props) {
))}
</Swiper>
);
}
});
120 changes: 61 additions & 59 deletions src/components/Image.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import type { ImageProps as NextImageProps } from 'next/image.js';
import type { SyntheticEvent } from 'react';
import { forwardRef, useCallback, useEffect, useState } from 'react';
import { forwardRef, useCallback, useEffect, useState, memo } from 'react';

import { Image as NextImage } from '@/esm/Image.js';
import { classNames } from '@/helpers/classNames.js';
Expand All @@ -13,66 +13,68 @@ export interface ImageProps extends NextImageProps {
fallbackClassName?: string;
}

export const Image = forwardRef<HTMLImageElement, ImageProps>(function Image(
{ onError, onLoad, fallback, fallbackClassName, ...props },
ref,
) {
const [imageLoadFailed, setImageLoadFailed] = useState(false);
const isDarkMode = useIsDarkMode();
const [loading, setLoading] = useState(true);
export const Image = memo(
forwardRef<HTMLImageElement, ImageProps>(function Image(
{ onError, onLoad, fallback, fallbackClassName, ...props },
ref,
) {
const [imageLoadFailed, setImageLoadFailed] = useState(false);
const isDarkMode = useIsDarkMode();
const [loading, setLoading] = useState(true);

const handleError = useCallback(
(e: SyntheticEvent<HTMLImageElement>) => {
setLoading(false);
if (imageLoadFailed) {
return;
}
setImageLoadFailed(true);
if (onError) {
onError(e);
}
},
[imageLoadFailed, setImageLoadFailed, onError],
);
const handleError = useCallback(
(e: SyntheticEvent<HTMLImageElement>) => {
setLoading(false);
if (imageLoadFailed) {
return;
}
setImageLoadFailed(true);
if (onError) {
onError(e);
}
},
[imageLoadFailed, setImageLoadFailed, onError],
);

const handleLoad = useCallback(
(e: SyntheticEvent<HTMLImageElement>) => {
setLoading(false);
onLoad?.(e);
},
[onLoad],
);
const handleLoad = useCallback(
(e: SyntheticEvent<HTMLImageElement>) => {
setLoading(false);
onLoad?.(e);
},
[onLoad],
);

useEffect(() => {
setImageLoadFailed(!props.src);
}, [props.src]);
useEffect(() => {
setImageLoadFailed(!props.src);
}, [props.src]);

const isFailed = imageLoadFailed || !props.src;
const isFailed = imageLoadFailed || !props.src;

// TODO: replace failed fallback image
return (
// Since next/image requires the domain of the image to be configured in next.config,
// But we can't predict the origin of all images.
// eslint-disable-next-line @next/next/no-img-element
<NextImage
{...props}
unoptimized
loading="lazy"
priority={false}
onLoad={handleLoad}
src={
isFailed
? fallback || (isDarkMode ? '/image/fallback-dark.png' : '/image/fallback-light.png')
: props.src
}
className={classNames(
props.className,
isFailed ? fallbackClassName : undefined,
loading ? 'animate-pulse bg-bg' : '',
)}
onError={handleError}
alt={props.alt || ''}
ref={ref}
/>
);
});
// TODO: replace failed fallback image
return (
// Since next/image requires the domain of the image to be configured in next.config,
// But we can't predict the origin of all images.
// eslint-disable-next-line @next/next/no-img-element
<NextImage
{...props}
unoptimized
loading="lazy"
priority={false}
onLoad={handleLoad}
src={
isFailed
? fallback || (isDarkMode ? '/image/fallback-dark.png' : '/image/fallback-light.png')
: props.src
}
className={classNames(
props.className,
isFailed ? fallbackClassName : undefined,
loading ? 'animate-pulse bg-bg' : '',
)}
onError={handleError}
alt={props.alt || ''}
ref={ref}
/>
);
}),
);
107 changes: 58 additions & 49 deletions src/components/NFTs/NFTPreview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -110,57 +110,66 @@ function BasePreviewContent(props: BasePreviewContentProps) {
);
}

export const NFTPreviewer = memo(function NFTPreview({ nft }: NFTPreviewProps) {
const chainId = resolveSimpleHashChainId(nft.chain);
const collectionId = nft.collection.collection_id;
const isSolanaChain = isValidSolanaChainId(chainId);
export const NFTPreviewer = memo(
function NFTPreview({ nft }: NFTPreviewProps) {
const chainId = resolveSimpleHashChainId(nft.chain);
const collectionId = nft.collection.collection_id;
const isSolanaChain = isValidSolanaChainId(chainId);

const isPoap = isSameEthereumAddress(nft.contract_address, POAP_CONTRACT_ADDRESS);
const startDate = isPoap
? nft.extra_metadata?.attributes?.find((attr) => attr.trait_type === 'startDate')?.value
: undefined;
const endDate = isPoap
? nft.extra_metadata?.attributes?.find((attr) => attr.trait_type === 'endDate')?.value
: undefined;
const isPoap = isSameEthereumAddress(nft.contract_address, POAP_CONTRACT_ADDRESS);
const startDate = isPoap
? nft.extra_metadata?.attributes?.find((attr) => attr.trait_type === 'startDate')?.value
: undefined;
const endDate = isPoap
? nft.extra_metadata?.attributes?.find((attr) => attr.trait_type === 'endDate')?.value
: undefined;

return (
<BasePreviewContent
image={nft.image_url}
icon={
isPoap ? (
<PoapIcon width={24} height={24} />
) : chainId ? (
<ChainIcon className="rounded-full" size={24} chainId={chainId} />
) : undefined
}
link={
chainId ? resolveNftUrl(chainId, nft.contract_address, isSolanaChain ? '0' : nft.token_id) : undefined
}
footer={
nft.collection?.collection_id
? {
image: nft.collection.image_url,
name: isPoap ? nft.name : nft.collection.name,
link: isPoap ? undefined : collectionId ? resolveNftUrlByCollection(collectionId) : undefined,
}
: undefined
}
tags={
isPoap
? compact([
startDate && endDate ? (
<>
<CalendarIcon className="mr-1 inline-block align-sub" width={15} height={15} />
{formatDate(startDate)} - {formatDate(endDate)}
</>
) : null,
])
: [`#${nft.name}`]
}
bookmarkProps={{ nftId: nft.nft_id, ownerAddress: first(nft.owners)?.owner_address }}
/>
);
});
return (
<BasePreviewContent
image={nft.image_url}
icon={
isPoap ? (
<PoapIcon width={24} height={24} />
) : chainId ? (
<ChainIcon className="rounded-full" size={24} chainId={chainId} />
) : undefined
}
link={
chainId
? resolveNftUrl(chainId, nft.contract_address, isSolanaChain ? '0' : nft.token_id)
: undefined
}
footer={
nft.collection?.collection_id
? {
image: nft.collection.image_url,
name: isPoap ? nft.name : nft.collection.name,
link: isPoap
? undefined
: collectionId
? resolveNftUrlByCollection(collectionId)
: undefined,
}
: undefined
}
tags={
isPoap
? compact([
startDate && endDate ? (
<>
<CalendarIcon className="mr-1 inline-block align-sub" width={15} height={15} />
{formatDate(startDate)} - {formatDate(endDate)}
</>
) : null,
])
: [`#${nft.name}`]
}
bookmarkProps={{ nftId: nft.nft_id, ownerAddress: first(nft.owners)?.owner_address }}
/>
);
},
(a, b) => a.nft.nft_id === b.nft.nft_id,
);

export const CollectionPreviewer = memo(function CollectionPreviewer({
collection,
Expand Down
1 change: 0 additions & 1 deletion src/components/NoSSR.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,5 @@ interface NoSSRProps {

export function NoSSR({ children }: NoSSRProps) {
const mounted = useMounted();

return mounted ? children : null;
}
6 changes: 3 additions & 3 deletions src/components/Posts/PostLinks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import { useQuery } from '@tanstack/react-query';
import { last } from 'lodash-es';
import { useRouter } from 'next/navigation.js';
import { useEffect, useMemo } from 'react';
import { memo, useEffect, useMemo } from 'react';

import { ArticleBody } from '@/components/Article/ArticleBody.js';
import { ActionContainer } from '@/components/Blink/ActionContainer.js';
Expand Down Expand Up @@ -33,7 +33,7 @@ interface Props {
isInCompose?: boolean;
}

export function PostLinks({ post, setContent, isInCompose = false }: Props) {
export const PostLinks = memo(function PostLinks({ post, setContent, isInCompose = false }: Props) {
const router = useRouter();
const url = resolveOembedUrl(post);
const { isLoading, error, data } = useQuery({
Expand Down Expand Up @@ -95,7 +95,7 @@ export function PostLinks({ post, setContent, isInCompose = false }: Props) {
{data.collection ? <CollectionPreviewer collection={data.collection} /> : null}
</>
);
}
});

export function PostLinksInCompose({
type,
Expand Down
Loading

0 comments on commit 2f40dc3

Please sign in to comment.