我們要完善頭像的區塊,讓它顯示照片,如果用戶沒有設定照片就顯示預設的頭像。
修改Avatar.tsx。
import useUser from "@/Hooks/useUser";
import { useCallback } from "react";
import { useRouter } from "next/router";
import Image from "next/image";
interface AvatarProps {
userId: string;
isLarge?: boolean;
hasBorder?: boolean;
}
const Avatar: React.FC<AvatarProps> = ({
userId,
isLarge,
hasBorder
}) => {
const router = useRouter();
const{ data: fetchedUser } = useUser(userId);
const onClick = useCallback((event: any) => {
event.stopPropagation();
const url = `/users/${userId}`;
router.push(url);
}, [router, userId]);
return (
<div className={`
${hasBorder ? 'border-4 border-black' : ''}
${isLarge ? 'h-32' : 'h-12'}
${isLarge ? 'w-32' : 'w-12'}
rounded-full
hover:opacity-90
transition
cursor-pointer
relative
`}>
<Image
fill
style={{
objectFit: 'cover',
borderRadius: '100%'
}}
alt="Avatar"
onClick={onClick}
src={fetchedUser?.avatarImage || '/images/placeholder.png'}
/>
</div>
);
}
export default Avatar;
將這張照片 下載下來後,放到public/images底下,這樣頭像區塊就完成了。
下個要完成的部分是個人檔案區塊。
安裝需要的套件
npm install react-spinners
在pages資料夾下,建立users資料夾。
在users資料夾下,建立[userId].tsx,用來顯示個人檔案,等待載入時會在畫面上顯示轉圈。
import { useRouter } from "next/router";
import { ClipLoader } from "react-spinners";
import Header from "@/Components/layout/Header";
import useUser from "@/Hooks/useUser";
const UserView = () => {
const router = useRouter();
const { userId } = router.query;
const { data: fetchedUser, isLoading} = useUser(userId as string);
if(isLoading || !fetchedUser){
return (
<div className="flex justify-center items-center h-full">
<ClipLoader color="lightblue" size={80}/>
</div>
)
}
return (
<>
<Header showBackArrow label={fetchedUser?.name} />
</>
)
}
export default UserView;
修復以前Home顯示的位置錯誤。
Sidebar.tsx
import { BsHouseFill, BsBellFill } from "react-icons/bs";
import { FaUser } from "react-icons/fa";
import { BiLogOut } from "react-icons/bi";
import SidebarLogo from "./SidebarLogo";
import SidebarItem from "./SidebarItem";
import SidebarTweetButton from "./SidebarTweetButton";
import useCurrentUser from "@/Hooks/useCurrentUser";
import { signOut } from "next-auth/react";
const Sidebar = () => {
const { data: currentUser } = useCurrentUser();
const items = [
{
label: "Home",
href: "/",
icon: BsHouseFill,
},
{
label: "Notifications",
href: "/notifications",
icon: BsBellFill,
auth: true
},
{
label: "Profile",
href: "/users/1",
icon: FaUser,
auth: true
},
];
return (
<div className="col-span-1 h-full pr-4 md:pr-6">
<div className="flex flex-col items-end">
<div className="space-y-2 lg:w-[230px]">
<SidebarLogo />
{items.map((item) => (
<SidebarItem
key={item.href}
href={item.href}
label={item.label}
icon={item.icon}
auth={item.auth}
/>
))}
{currentUser && (
<SidebarItem onClick={() => signOut()} icon={BiLogOut} label="Logout" />
)}
<SidebarTweetButton />
</div>
</div>
</div>
);
};
export default Sidebar;
Layout.tsx
import FollowBar from "./layout/FollowBar";
import Sidebar from "./layout/Sidebar";
interface LayoutProps {
children: React.ReactNode;
}
const Layout: React.FC<LayoutProps> = ({ children }) => {
return (
<div className="h-screen bg-black">
<div className="container h-full mx-auto xl:px-30 max-w-6xl">
<div className="grid grid-cols-4 h-full">
<Sidebar />
<div className="col-span-3 lg:col-span-2 border-x-[1px] border-neutral-800">
{children}
</div>
<FollowBar />
</div>
</div>
</div>
);
};
export default Layout;
到MongoDB查看User的_id,假設是64f98ac22095377e41f59c90,我們就在瀏覽器開啟 http://localhost:3000/users/64f98ac22095377e41f59c90 ,就能看到中間的區塊顯示一個向左的箭頭和用戶名稱。
在Components資料夾下,建立users資料夾。
在users資料夾下,建立UserHero.tsx,用來顯示個人檔案的橫幅圖片和頭像。
import Image from "next/image";
import useUser from "@/Hooks/useUser";
import Avatar from "../Avatar";
interface UserHeroProps {
userId: string;
}
const UserHero: React.FC<UserHeroProps> = ({userId}) => {
const {data: fetchedUser} = useUser(userId);
return (
<div>
<div className="bg-neutral-700 h-44 relative">
{
fetchedUser?.bannerImage && (
<Image src={fetchedUser.bannerImage}
fill
alt="Banner Image"
style={{objectFit: 'cover'}}
/>
)
}
<div className="absolute -bottom-16 left-4">
<Avatar userId={userId} isLarge hasBorder />
</div>
</div>
</div>
)
}
export default UserHero
修改[userId].tsx,顯示UserHero。
import { useRouter } from "next/router";
import { ClipLoader } from "react-spinners";
import Header from "@/Components/layout/Header";
import useUser from "@/Hooks/useUser";
import UserHero from "@/Components/users/UserHero";
const UserView = () => {
const router = useRouter();
const { userId } = router.query;
const { data: fetchedUser, isLoading} = useUser(userId as string);
if(isLoading || !fetchedUser){
return (
<div className="flex justify-center items-center h-full">
<ClipLoader color="lightblue" size={80}/>
</div>
)
}
return (
<>
<Header showBackArrow label={fetchedUser?.name} />
<UserHero userId={userId as string}/>
</>
)
}
export default UserView;
完成後可以看到以下的畫面。
安裝date-fns用來處理時間和日期
npm i date-fns
在users資料夾下,建立UserBio.tsx,顯示指定ID的使用者個人資料,包含姓名、用戶名、自我介紹、加入的時間、關注數與被關注數。
import { useMemo } from "react";
import { format } from "date-fns";
import useCurrentUser from "@/Hooks/useCurrentUser";
import useUser from "@/Hooks/useUser";
import Button from "../Button";
import { BiCalendar } from "react-icons/bi";
interface UserBioProps {
userId: string;
}
const UserBio: React.FC<UserBioProps> = ({ userId }) => {
const {data: currentUser} = useCurrentUser();
const {data: fetchedUser} = useUser(userId);
const createdAt = useMemo(() => {
if(!fetchedUser?.createdAt){
return null;
}
return format(new Date(fetchedUser.createdAt), 'MMMM yyyy');
}, [fetchedUser?.createdAt]);
return (
<div className="border-b-[1px] border-neutral-800 pb-4">
<div className="flex justify-end p-2">
{
currentUser?.id === userId ? (
<Button secondary label="Edit" onClick={() => {}} />
) : (
<Button secondary
label="Follow"
onClick={() => {}}
/>
)
}
</div>
<div className="mt-8 px-4">
<div className="flex flex-col">
<p className="text-white text-2xl font-semibold">
{fetchedUser?.name}
</p>
<p className="text-md text-neutral-500">
@{fetchedUser?.username}
</p>
</div>
<div className="flex flex-col mt-4">
<p className="text-white">
{fetchedUser?.bio}
</p>
<div className="flex flex-row items-center gap-2 mt-4 text-neutral-500">
<BiCalendar size={24}/>
<p>
Joined {createdAt}
</p>
</div>
</div>
<div className="flex flex-row items-center mt-4 gap-6">
<div className="flex flex-row items-center gap-1">
<p className="text-white">
{fetchedUser?.followingIds?.length}
</p>
<p className="text-neutral-500">
Following
</p>
</div>
<div className="flex flex-row items-center gap-1">
<p className="text-white">
{fetchedUser?.followersCount || 0}
</p>
<p className="text-neutral-500">
Followers
</p>
</div>
</div>
</div>
</div>
)
}
export default UserBio
修改[userId].tsx顯示UserBio區塊的內容
import { useRouter } from "next/router";
import { ClipLoader } from "react-spinners";
import Header from "@/Components/layout/Header";
import useUser from "@/Hooks/useUser";
import UserHero from "@/Components/users/UserHero";
import UserBio from "@/Components/users/UserBio";
const UserView = () => {
const router = useRouter();
const { userId } = router.query;
const { data: fetchedUser, isLoading} = useUser(userId as string);
if(isLoading || !fetchedUser){
return (
<div className="flex justify-center items-center h-full">
<ClipLoader color="lightblue" size={80}/>
</div>
)
}
return (
<>
<Header showBackArrow label={fetchedUser?.name} />
<UserHero userId={userId as string}/>
<UserBio userId={userId as string}/>
</>
)
}
export default UserView;
在瀏覽器開啟 localhost:3000/users/使用者的ID ,如果是本人的情況下,會顯示以下畫面,如果是其他人的話Edit會變成Follow。