Skip to content

Commit

Permalink
[beta] auto load more for album
Browse files Browse the repository at this point in the history
  • Loading branch information
wxydev002 committed Jan 26, 2025
1 parent f1d51bf commit 92fc7a6
Show file tree
Hide file tree
Showing 19 changed files with 194 additions and 351 deletions.
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
70 changes: 23 additions & 47 deletions src/app/displayCartoon/page.tsx
Original file line number Diff line number Diff line change
@@ -1,32 +1,28 @@
"use client";

import ACommonCartoonList from "@/components/ACommonCartoonList";
import ADisplayHeader from "@/components/ADisplayHeader";
import { TapData } from "@/components/aibum";
import ADisplayHeader, { urlToSigninFromAlbum } from "@/components/ADisplayHeader";
import { HelpTip } from "@/components/tips";
import { useCartoonList } from "@/hooks/useCartoonList";
import { useCopy } from "@/hooks/useCopy";
import backendApi from "@/lib/api";
import { SVGS } from "@/svg";
import { useRouter } from "next/navigation";
import { useEffect, useState } from "react";
import { useQuery } from "@tanstack/react-query";
import { useRouter, useSearchParams } from "next/navigation";
import { FaXTwitter } from "react-icons/fa6";
import { IoShareSocialSharp } from "react-icons/io5";
import { useSetState } from "react-use";
import { toast } from "sonner";
import { useAuthContext } from "../context/AuthContext";
import { cn } from "@nextui-org/react";


const DispalyCartoon = () => {
const ac = useAuthContext();
const user = ac.queryUserInfo?.data;
const r = useRouter()
const params = new URLSearchParams(window.location.search);
const uid = params.get("uid") || ''
const name = params.get("name") || ''
const sp = useSearchParams()
const uid = sp.get("uid") || ''
const name = sp.get("name") || ''
const username = name?.split('@')[0] || ''
const [liked, setLiked] = useState<boolean>(false)
const [data, setData] = useSetState<{ data?: TapData, loading?: boolean, }>({ data: { like: 0, list: [] }, loading: true })
const copy = useCopy();

const shareLink = `${origin}/displayCartoon?referral=${user?.inviteCode}&uid=${user?.id}&name=${user?.email}`;
Expand All @@ -44,52 +40,34 @@ Join EnReach Season 1 and earn BERRY points by running a super lite node in Chro
window.open(postXUrl, "_blank");
};

const gerCartoonList = async () => {
const params = new URLSearchParams(window.location.search);
const uid = params.get("uid") || ''
try {
if (uid) {
if (ac.user?.userId && uid) {
const likedRes = await backendApi.userIsLiked(uid)
setLiked(likedRes.liked)
}
const sharedList = await backendApi.getShareUserList(uid)
setData({ data: sharedList, loading: false })
}
} catch (error) {
setData({ loading: false })
console.error("Error fetching data:", error);
}

}


useEffect(() => {
gerCartoonList()
}, [])

const { data: likeStat, refetch: refetchLikeStat, isLoading: isLoadingLikeStat } = useQuery({
queryKey: ['queryLikeStat', uid, user?.id],
enabled: Boolean(uid),
initialData: { liked: false, likeCount: 0 },
queryFn: async () => {
const [liked, likeCount] = await Promise.all([backendApi.userIsLiked(uid).catch(_e => false), backendApi.userLikeCount(uid)])
return { liked, likeCount }
}
})
let clicked = false
const onLike = async () => {
if (clicked) return
clicked = true
if (user?.id === uid) {
return
} else if (!user?.id) {
const params = new URLSearchParams(window.location.search);
const referral = params.get("referral");
const uid = params.get("uid");
const name = params.get("name");
r.push(`signin/?page=displayCartoon&referral=${referral}&uid=${uid}&name=${name}`)
r.push(urlToSigninFromAlbum())
return
} else if (liked) {
} else if (likeStat?.liked) {
toast.success("You have liked this album.");
clicked = false
return
}

backendApi.currentUserLike('like', uid).then((res) => {
if (res.message === 'success') {
gerCartoonList()
refetchLikeStat()
}
}).catch((e) => {
toast.error(`You need to update to Teen Berry (Lv.2 User) to unlock 'Like'.`);
Expand All @@ -99,24 +77,22 @@ Join EnReach Season 1 and earn BERRY points by running a super lite node in Chro
})
}

const cartoonList = useCartoonList(data.data)

return (
<>
<ADisplayHeader />
<div className=" mx-[6.5rem] xsl:mx-[3.75rem]">
<div className=" flex justify-between ">
<div className="my-10 font-semibold text-xl leading-10">{`${(uid === user?.id ? 'My Berry Album' : username && (username + '‘s Berry Album') || '')} `}</div>
<div className="flex items-center gap-6">
<div className="flex items-center gap-2">
<div className={cn("flex items-center gap-2")}>
<button className={`${uid === user?.id && 'cursor-not-allowed'}`} onClick={onLike}>
{liked || uid === user?.id && data.data?.like ?
{likeStat.liked || uid === user?.id ?
<SVGS.SvgLiked /> :
<SVGS.SvgLike />
}
</button>
<span className="text-xl ">
{data.data?.like}
{likeStat.likeCount}
</span>
</div>
{user?.id === uid &&
Expand All @@ -135,7 +111,7 @@ Join EnReach Season 1 and earn BERRY points by running a super lite node in Chro
}
</div>
</div>
<ACommonCartoonList cartoonList={cartoonList} loading={data.loading} />
<ACommonCartoonList loadType="uid" />
</div>
</>

Expand Down
60 changes: 44 additions & 16 deletions src/components/ACartoonImage.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,47 @@
import { singleCartoon } from "./ACommonCartoonList";
const ASSET_PATH = "/svg";
import { useMemo } from "react";

export type singleCartoon = {
hat: number,
head: number,
eyes: number,
clothes: number,
leftHand: number,
rightHand: number,
pants: number,
shoes: number,
logo: number,
}
interface ACartoonImageProps {
data: singleCartoon;
data: singleCartoon | string; //
size?: number
}

const Types = [{ type: "shoes", count: 4 }, { type: "pants", count: 4 }, { type: 'leftHand', count: 4 }, { type: 'rightHand', count: 4 }, { type: "clothes", count: 5 }, { type: "logo", count: 4 }, { type: "eyes", count: 4 }, { type: "head", count: 4 }, { type: "hat", count: 4 }] as const
const getSvgPath = (
type: "hand" | "shoes" | "pants" | "clothes" | "logo" | "eyes" | "head" | "hat",
type: (typeof Types)[number]['type'],
index: number | null = 0,
otherSvgPath?: string
) => `${ASSET_PATH}/${type}/${otherSvgPath || type}${index}.svg`;
) => `/svg/${type}/${type}${index}.svg`;

const toIndex = (data: string, index: number, mod: number) => {
try {
const num = parseInt(data.slice(Math.round((index * 2)), Math.round(index * 2 + 2)))
if (Number.isSafeInteger(num)) return num % mod
return 0
} catch (error) {
return 0
}
}
const ACartoonImage: React.FC<ACartoonImageProps> = ({ data, size = 200 }) => {

const mdata = useMemo(() => {
if (typeof data == 'string') {
const sc = {} as singleCartoon
Types.forEach((item, index) => {
sc[item.type] = toIndex(data, index, item.count)
})
return sc
}
return data
}, [data])

return (
<div>
Expand All @@ -28,55 +56,55 @@ const ACartoonImage: React.FC<ACartoonImageProps> = ({ data, size = 200 }) => {
<foreignObject x="0" y="0" width="200" height="450">
<div className="relative m-auto flex items-center justify-center">
<img
src={getSvgPath("shoes", data?.shoes)}
src={getSvgPath("shoes", mdata.shoes)}
alt="shoes"
crossOrigin="anonymous"
style={{ position: "absolute", left: 6, top: "365px", zIndex: 0, width: "180px" }}
/>
<img
src={getSvgPath("pants", data.pants)}
src={getSvgPath("pants", mdata.pants)}
alt="pants"
crossOrigin="anonymous"
style={{ position: "absolute", left: "40px", top: "315px", zIndex: 1 }}
/>
<img
src={getSvgPath("hand", data.hand && data.hand[0], "leftHand")}
src={getSvgPath("leftHand", mdata.leftHand)}
alt="leftHand"
crossOrigin="anonymous"
style={{ position: "absolute", left: 3, top: "305px", width: "24px", zIndex: 2 }}
/>
<img
src={getSvgPath("hand", data.hand && data.hand[1], "rightHand")}
src={getSvgPath("rightHand", mdata.rightHand)}
alt="rightHand"
crossOrigin="anonymous"
style={{ position: "absolute", right: 10, top: "305px", width: "24px", zIndex: 2 }}
/>
<img
src={getSvgPath("clothes", data.clothes)}
src={getSvgPath("clothes", mdata.clothes)}
alt="clothes"
crossOrigin="anonymous"
style={{ position: "absolute", left: 15, top: "225px", zIndex: 2 }}
/>
<img
src={getSvgPath("logo", data.logo)}
src={getSvgPath("logo", mdata.logo)}
alt="logo"
crossOrigin="anonymous"
style={{ position: "absolute", left: 85, top: "275px", zIndex: 2 }}
/>
<img
src={getSvgPath("eyes", data.eyes)}
src={getSvgPath("eyes", mdata.eyes)}
alt="eyes"
crossOrigin="anonymous"
style={{ position: "absolute", left: "25px", top: "120px", width: "150px", zIndex: 10000 }}
/>
<img
src={getSvgPath("head", data.head)}
src={getSvgPath("head", mdata.head)}
alt="head"
crossOrigin="anonymous"
style={{ position: "absolute", left: 0, top: "50px", width: "200px", zIndex: 4 }}
/>
<img
src={getSvgPath("hat", data.hat)}
src={getSvgPath("hat", mdata.hat)}
alt="hat"
crossOrigin="anonymous"
style={{ position: "absolute", left: 0, top: 0, width: "200px", zIndex: 5 }}
Expand Down
91 changes: 53 additions & 38 deletions src/components/ACommonCartoonList.tsx
Original file line number Diff line number Diff line change
@@ -1,52 +1,66 @@
import { FC, useState } from "react"
import { FC, Fragment, useEffect, useRef, useState } from "react"
import ACartoonImage from "./ACartoonImage"
import { ConfirmDialog } from "./dialogimpls"
import { Skeleton } from "@nextui-org/react"

export type singleCartoon = {
hat: number,
head: number,
eyes: number,
clothes: number,
hand: [number, number],
pants: number,
shoes: number,
logo: number,
}

export type cartoonType = {
one?: singleCartoon
two?: singleCartoon
name?: string
}
import { TapItem } from "./aibum"
import { convertToNew } from "@/lib/utils"
import { useInfiniteQuery } from "@tanstack/react-query"
import backendApi from "@/lib/api"
import { useAuthContext } from "@/app/context/AuthContext"
import { useSearchParams } from "next/navigation"
import { useIntersection } from "react-use"

type commonCartoontype = {
cartoonList: cartoonType[]
loading?: boolean
showEmpty?: boolean
loadType: 'uid' | 'auth'
}
const ACommonCartoonList: FC<commonCartoontype> = ({ cartoonList, loading, showEmpty }) => {
const [userClickedCartoon, setUserClickedCartoon] = useState<{ value?: cartoonType, isOpen: boolean }>({ value: undefined, isOpen: false })
const onShowOne = (item: cartoonType) => {
const ACommonCartoonList: FC<commonCartoontype> = ({ showEmpty, loadType }) => {
const ac = useAuthContext()
const sp = useSearchParams()
const uid = sp.get('uid')
const { data, isFetching, fetchNextPage } = useInfiniteQuery({
queryKey: ['queryCartoonListByUid', loadType, loadType == 'auth' ? ac.user : uid],
enabled: loadType == 'auth' ? Boolean(ac.user) : Boolean(uid),
queryFn: async ({ pageParam: pageNum }) => {
const pageSize = 10
const pageParams = { pageSize, pageNum }
const data = await (loadType == 'auth' ? backendApi.getCartoonList(pageParams) : backendApi.getAlbumItemList(uid!, pageParams))
return { data: data.list, next: data.list.length < 10 ? null : pageNum + 1 }
},
initialPageParam: 1,
getNextPageParam: (lastPage) => lastPage.next
})
const ref = useRef(null)
const intersection = useIntersection(ref, { root: null, })
const inView = intersection && intersection.intersectionRatio >= 1
useEffect(() => { inView && !isFetching && fetchNextPage() }, [inView, isFetching])

const [userClickedCartoon, setUserClickedCartoon] = useState<{ value?: TapItem, isOpen: boolean }>({ value: undefined, isOpen: false })
const onShowOne = (item: TapItem) => {
setUserClickedCartoon({ value: item, isOpen: true })
}

const pages = data?.pages || []
const isEmpty = !pages || pages.length == 0 || pages[0].data.length == 0
return <div className="w-full">
{(!cartoonList || cartoonList.length == 0) && !loading && showEmpty && <div className=" text-xl w-full text-center flex justify-center ">Oops! Nothing here yet.</div>}
{(isEmpty) && !isFetching && showEmpty && <div className=" text-xl w-full text-center flex justify-center ">Oops! Nothing here yet.</div>}
<div className="grid grid-cols-[repeat(auto-fill,minmax(18.75rem,1fr))] w-full gap-5">
{
cartoonList && cartoonList.map((item, index) => {
return <div key={`cartoon_${index}`} className="flex-wrap flex-col items-center justify-center gap-5 flex w-full bg-[#7E7E7E] cursor-pointer rounded-3xl" onClick={() => onShowOne(item)} >
<span className="text-xl pt-5">{item.name}</span>
<div className="flex gap-5 flex-nowrap">
{item.one && <ACartoonImage data={item.one} size={100} />}
{item.two && <ACartoonImage data={item.two} size={100} />}
</div>
</div>
})
pages.map(({ data }, pi) => <Fragment key={`page_${pi}`}>
{
data.map((item, index) => {
return <div key={`cartoon_${pi}_${index}`} className="flex-wrap flex-col items-center justify-center gap-5 flex w-full bg-[#7E7E7E] cursor-pointer rounded-3xl" onClick={() => onShowOne(item)} >
<span className="text-xl pt-5">{item.content}</span>
<div className="flex gap-5 flex-nowrap">
{item.tapFromUserId && <ACartoonImage data={convertToNew(item.tapFromUserId)} size={100} />}
{item.tapToUserId && <ACartoonImage data={convertToNew(item.tapToUserId)} size={100} />}
</div>
</div>
})
}
</Fragment>)
}
{
loading && <>
isFetching && <>
<Skeleton className="rounded-2xl"><div className="h-[18.3125rem] rounded-3xl"></div></Skeleton>
<Skeleton className="rounded-2xl"><div className="h-[18.3125rem] rounded-3xl"></div></Skeleton>
<Skeleton className="rounded-2xl"><div className="h-[18.3125rem] rounded-3xl"></div></Skeleton>
Expand All @@ -58,10 +72,10 @@ const ACommonCartoonList: FC<commonCartoontype> = ({ cartoonList, loading, showE
tit=""
msg={
<div className="w-full flex justify-center items-center gap-5 flex-col px-5">
<span className="text-xl">{userClickedCartoon.value?.name}</span>
<span className="text-xl">{userClickedCartoon.value?.content}</span>
<div className="flex gap-5 flex-nowrap">
{userClickedCartoon.value?.one && <ACartoonImage data={userClickedCartoon.value?.one} />}
{userClickedCartoon.value?.two && <ACartoonImage data={userClickedCartoon.value?.two} />}
{userClickedCartoon.value?.tapFromUserId && <ACartoonImage data={convertToNew(userClickedCartoon.value?.tapFromUserId)} />}
{userClickedCartoon.value?.tapToUserId && <ACartoonImage data={convertToNew(userClickedCartoon.value?.tapToUserId)} />}
</div>

</div>
Expand All @@ -77,6 +91,7 @@ const ACommonCartoonList: FC<commonCartoontype> = ({ cartoonList, loading, showE
/>
}
</div >
<div ref={ref} className="h-1 opacity-0" />
</div>
}

Expand Down
Loading

0 comments on commit 92fc7a6

Please sign in to comment.