feat: enhance profile and feed pages with friends display logic, update TopFriends component to support mode, and extend bio length in profile schema
This commit is contained in:
@@ -5,7 +5,7 @@ export default function AuthLayout({
|
|||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center justify-center min-h-screen bg-gray-100">
|
<div className="flex items-center justify-center min-h-screen">
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@@ -1,5 +1,12 @@
|
|||||||
import { cookies } from "next/headers";
|
import { cookies } from "next/headers";
|
||||||
import { getFeed, getMe, getUserProfile, Me, User } from "@/lib/api";
|
import {
|
||||||
|
getFeed,
|
||||||
|
getFriends,
|
||||||
|
getMe,
|
||||||
|
getUserProfile,
|
||||||
|
Me,
|
||||||
|
User,
|
||||||
|
} from "@/lib/api";
|
||||||
import { PostThoughtForm } from "@/components/post-thought-form";
|
import { PostThoughtForm } from "@/components/post-thought-form";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
@@ -39,11 +46,14 @@ async function FeedPage({ token }: { token: string }) {
|
|||||||
feedData.thoughts
|
feedData.thoughts
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const friends = (await getFriends(token)).users.map((user) => user.username);
|
||||||
|
const shouldDisplayTopFriends = me?.topFriends && me.topFriends.length > 8;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="container mx-auto max-w-6xl p-4 sm:p-6">
|
<div className="container mx-auto max-w-6xl p-4 sm:p-6">
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-4 gap-8">
|
<div className="grid grid-cols-1 lg:grid-cols-4 gap-8">
|
||||||
<aside className="hidden lg:block lg:col-span-1">
|
<aside className="hidden lg:block lg:col-span-1">
|
||||||
<div className="sticky top-20 space-y-6">
|
<div className="sticky top-20 space-y-6 glass-effect glossy-effect bottom rounded-md p-4">
|
||||||
<h2 className="text-lg font-semibold">Filters & Sorting</h2>
|
<h2 className="text-lg font-semibold">Filters & Sorting</h2>
|
||||||
<p className="text-sm text-muted-foreground">Coming soon...</p>
|
<p className="text-sm text-muted-foreground">Coming soon...</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -74,8 +84,11 @@ async function FeedPage({ token }: { token: string }) {
|
|||||||
|
|
||||||
<aside className="hidden lg:block lg:col-span-1">
|
<aside className="hidden lg:block lg:col-span-1">
|
||||||
<div className="sticky top-20 space-y-6">
|
<div className="sticky top-20 space-y-6">
|
||||||
{me?.topFriends && <TopFriends usernames={me.topFriends} />}
|
{shouldDisplayTopFriends && (
|
||||||
|
<TopFriends mode="top-friends" usernames={me.topFriends} />
|
||||||
|
)}
|
||||||
<PopularTags />
|
<PopularTags />
|
||||||
|
{token && <TopFriends mode="friends" usernames={friends || []} />}
|
||||||
</div>
|
</div>
|
||||||
</aside>
|
</aside>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
import {
|
import {
|
||||||
getFollowersList,
|
getFollowersList,
|
||||||
getFollowingList,
|
getFollowingList,
|
||||||
|
getFriends,
|
||||||
getMe,
|
getMe,
|
||||||
getUserProfile,
|
getUserProfile,
|
||||||
getUserThoughts,
|
getUserThoughts,
|
||||||
@@ -75,6 +76,13 @@ export default async function ProfilePage({ params }: ProfilePageProps) {
|
|||||||
const authorDetails = new Map<string, { avatarUrl?: string | null }>();
|
const authorDetails = new Map<string, { avatarUrl?: string | null }>();
|
||||||
authorDetails.set(user.username, { avatarUrl: user.avatarUrl });
|
authorDetails.set(user.username, { avatarUrl: user.avatarUrl });
|
||||||
|
|
||||||
|
const friends =
|
||||||
|
typeof token === "string"
|
||||||
|
? (await getFriends(token)).users.map((user) => user.username)
|
||||||
|
: [];
|
||||||
|
|
||||||
|
const shouldDisplayTopFriends = token && friends.length > 8;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div id={`profile-page-${user.username}`}>
|
<div id={`profile-page-${user.username}`}>
|
||||||
{user.customCss && (
|
{user.customCss && (
|
||||||
@@ -186,7 +194,10 @@ export default async function ProfilePage({ params }: ProfilePageProps) {
|
|||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
<TopFriends usernames={user.topFriends} />
|
{shouldDisplayTopFriends && (
|
||||||
|
<TopFriends mode="top-friends" usernames={user.topFriends} />
|
||||||
|
)}
|
||||||
|
{token && <TopFriends mode="friends" usernames={friends || []} />}
|
||||||
</div>
|
</div>
|
||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
|
@@ -25,7 +25,7 @@ export function Header() {
|
|||||||
<UserNav />
|
<UserNav />
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<Button asChild variant="ghost" size="sm">
|
<Button asChild size="sm">
|
||||||
<Link href="/login">Login</Link>
|
<Link href="/login">Login</Link>
|
||||||
</Button>
|
</Button>
|
||||||
<Button asChild size="sm">
|
<Button asChild size="sm">
|
||||||
|
@@ -5,11 +5,14 @@ import { getUserProfile, User } from "@/lib/api";
|
|||||||
import { cookies } from "next/headers";
|
import { cookies } from "next/headers";
|
||||||
|
|
||||||
interface TopFriendsProps {
|
interface TopFriendsProps {
|
||||||
|
mode: "friends" | "top-friends";
|
||||||
usernames: string[];
|
usernames: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is an async Server Component
|
export async function TopFriends({
|
||||||
export async function TopFriends({ usernames }: TopFriendsProps) {
|
mode = "top-friends",
|
||||||
|
usernames,
|
||||||
|
}: TopFriendsProps) {
|
||||||
const token = (await cookies()).get("auth_token")?.value ?? null;
|
const token = (await cookies()).get("auth_token")?.value ?? null;
|
||||||
|
|
||||||
if (usernames.length === 0) {
|
if (usernames.length === 0) {
|
||||||
@@ -27,7 +30,6 @@ export async function TopFriends({ usernames }: TopFriendsProps) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch all top friend profiles in parallel
|
|
||||||
const friendsResults = await Promise.allSettled(
|
const friendsResults = await Promise.allSettled(
|
||||||
usernames.map((username) => getUserProfile(username, token))
|
usernames.map((username) => getUserProfile(username, token))
|
||||||
);
|
);
|
||||||
@@ -43,7 +45,7 @@ export async function TopFriends({ usernames }: TopFriendsProps) {
|
|||||||
<Card id="top-friends" className="p-4">
|
<Card id="top-friends" className="p-4">
|
||||||
<CardHeader id="top-friends__header" className="p-0 pb-2">
|
<CardHeader id="top-friends__header" className="p-0 pb-2">
|
||||||
<CardTitle id="top-friends__title" className="text-lg text-shadow-md">
|
<CardTitle id="top-friends__title" className="text-lg text-shadow-md">
|
||||||
Top Friends
|
{mode === "top-friends" ? "Top Friends" : "Friends"}
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent id="top-friends__content" className="p-0">
|
<CardContent id="top-friends__content" className="p-0">
|
||||||
|
@@ -53,7 +53,7 @@ export const CreateThoughtSchema = z.object({
|
|||||||
|
|
||||||
export const UpdateProfileSchema = z.object({
|
export const UpdateProfileSchema = z.object({
|
||||||
displayName: z.string().max(50).optional(),
|
displayName: z.string().max(50).optional(),
|
||||||
bio: z.string().max(160).optional(),
|
bio: z.string().max(4000).optional(),
|
||||||
avatarUrl: z.url().or(z.literal("")).optional(),
|
avatarUrl: z.url().or(z.literal("")).optional(),
|
||||||
headerUrl: z.url().or(z.literal("")).optional(),
|
headerUrl: z.url().or(z.literal("")).optional(),
|
||||||
customCss: z.string().optional(),
|
customCss: z.string().optional(),
|
||||||
|
Reference in New Issue
Block a user