diff --git a/app/(content)/(user)/[userid]/(profile)/followers/page.tsx b/app/(content)/(user)/[userid]/(profile)/followers/page.tsx deleted file mode 100644 index 4c4cc4597bb4c954f24bb86c4cb6a19a8f08dd35..0000000000000000000000000000000000000000 --- a/app/(content)/(user)/[userid]/(profile)/followers/page.tsx +++ /dev/null @@ -1,62 +0,0 @@ - -import { GlobalLayout } from "@/components/global-layout" -import { UserAvatar } from "@/components/user-avatar" -import { db } from "@/lib/db" -import { User } from "@prisma/client" -import Link from "next/link" - -export default async function UserFollowers({ params }: { params: { userid: string } }) { - - const fullUser = await db.user.findFirst({ - where: { - username: params.userid - }, - include: { - following: true, - followers: true - } - }) - - const followers = await db.user.findMany({ - where: { - following: { - every: { - followerId: { not: undefined } - } - }, - followers: { - every: { - followingId: fullUser?.id - } - } - } - }) - - return ( - <GlobalLayout - mainContent={ - <div className="flex flex-col w-full"> - {followers?.map((follower: User) => ( - <div className="flex items-center space-y-6" key={follower.id}> - <Link href={`/${follower.username}`} className="flex flex-row"> - <UserAvatar - user={{ username: follower.username, image: follower.image }} - className="h-10 w-10" - /> - - <div className="flex flex-col ml-3"> - <span className="font-bold">{follower.name}</span> - <span className="text-sky-500 text-sm">@{follower.username}</span> - </div> - </Link> - - <div className="ml-auto"> - {/* Followbutton */} - </div> - </div> - ))} - </div> - } - /> - ) -} \ No newline at end of file diff --git a/app/(content)/(user)/[userid]/(profile)/layout.tsx b/app/(content)/(user)/[userid]/(profile)/layout.tsx deleted file mode 100644 index 5df26b9e46d0195e4b8ec474ee35f4de4ebf36d2..0000000000000000000000000000000000000000 --- a/app/(content)/(user)/[userid]/(profile)/layout.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import { GlobalLayout } from "@/components/global-layout" -import { Profile } from "@/components/profile/components/profile" -import { ProfileSideContent } from "@/components/profile/components/profile-side-content" - -export default async function ProfileLayout({ - children, -}: { - children: React.ReactNode -}) { - return ( - <GlobalLayout - mainContent={ - <Profile > - {children} - </Profile> - } - sideContent={<ProfileSideContent />} - /> - ) -} \ No newline at end of file diff --git a/app/(content)/(user)/[userid]/(profile)/following/page.tsx b/app/(content)/(user)/[username]/(profile)/followers/page.tsx similarity index 100% rename from app/(content)/(user)/[userid]/(profile)/following/page.tsx rename to app/(content)/(user)/[username]/(profile)/followers/page.tsx diff --git a/app/(content)/(user)/[username]/(profile)/following/page.tsx b/app/(content)/(user)/[username]/(profile)/following/page.tsx new file mode 100644 index 0000000000000000000000000000000000000000..5ea8f4daa5c5fef29b079a07c60284b12ab27cba --- /dev/null +++ b/app/(content)/(user)/[username]/(profile)/following/page.tsx @@ -0,0 +1,7 @@ +export default async function Following() { + return ( + <div> + <h1>Following Page WIP</h1> + </div> + ) +} \ No newline at end of file diff --git a/app/(content)/(user)/[username]/(profile)/layout.tsx b/app/(content)/(user)/[username]/(profile)/layout.tsx new file mode 100644 index 0000000000000000000000000000000000000000..9595b88dd8c71cc38cb93962ec3edf94c6039941 --- /dev/null +++ b/app/(content)/(user)/[username]/(profile)/layout.tsx @@ -0,0 +1,37 @@ +import { GlobalLayout } from "@/components/global-layout" +import { ProfileSideContent } from "@/components/profile/components/profile-side-content" +import { ProfileUserInfo } from "@/components/profile/components/profile-user-info" +import { Card } from "@/components/ui/card" +import { UserNotFound } from "@/components/user-not-found" +import getURL from "@/lib/utils" + +export const dynamic = 'force-dynamic' +export const fetchCache = 'force-no-store' + +export default async function ProfileLayout({ + params, + children, +}: { + params: { username: string } + children: React.ReactNode +}) { + const user = await fetch(getURL(`/api/users/${params.username}`)).then((result) => result.json()) + + return ( + <GlobalLayout + mainContent={ + <Card className="overflow-hidden h-full w-full"> + {!user ? + <UserNotFound /> + : + <> + <ProfileUserInfo user={user} /> + {children} + </> + } + </Card> + } + sideContent={<ProfileSideContent />} + /> + ) +} \ No newline at end of file diff --git a/app/(content)/(user)/[userid]/(profile)/likes/page.tsx b/app/(content)/(user)/[username]/(profile)/likes/page.tsx similarity index 100% rename from app/(content)/(user)/[userid]/(profile)/likes/page.tsx rename to app/(content)/(user)/[username]/(profile)/likes/page.tsx diff --git a/app/(content)/(user)/[userid]/(profile)/media/page.tsx b/app/(content)/(user)/[username]/(profile)/media/page.tsx similarity index 100% rename from app/(content)/(user)/[userid]/(profile)/media/page.tsx rename to app/(content)/(user)/[username]/(profile)/media/page.tsx diff --git a/app/(content)/(user)/[userid]/(profile)/page.tsx b/app/(content)/(user)/[username]/(profile)/page.tsx similarity index 52% rename from app/(content)/(user)/[userid]/(profile)/page.tsx rename to app/(content)/(user)/[username]/(profile)/page.tsx index 14bfa348dc6d00d3a516bb24fac2dd84a09de683..b418baaba58c58da52157ea926007546e7a09d2d 100644 --- a/app/(content)/(user)/[userid]/(profile)/page.tsx +++ b/app/(content)/(user)/[username]/(profile)/page.tsx @@ -1,9 +1,9 @@ import { ProfileUserContent } from "@/components/profile/components/profile-user-content" -export default async function User({ params }: { params: { userid: string } }) { +export default async function User({ params }: { params: { username: string } }) { return ( <div className="space-y-6 w-full"> - <ProfileUserContent userid={params.userid} /> + {/* <ProfileUserContent userid={params.username} /> */} </div> ) } \ No newline at end of file diff --git a/app/(content)/(user)/[userid]/(profile)/with_replies/page.tsx b/app/(content)/(user)/[username]/(profile)/with_replies/page.tsx similarity index 100% rename from app/(content)/(user)/[userid]/(profile)/with_replies/page.tsx rename to app/(content)/(user)/[username]/(profile)/with_replies/page.tsx diff --git a/app/(content)/(user)/[userid]/status/[id]/page.tsx b/app/(content)/(user)/[username]/status/[id]/page.tsx similarity index 100% rename from app/(content)/(user)/[userid]/status/[id]/page.tsx rename to app/(content)/(user)/[username]/status/[id]/page.tsx diff --git a/app/api/users/[username]/route.ts b/app/api/users/[username]/route.ts index ce7122b7f5b439f38f118a52c3166f0bb7974423..fde9970628ef0b6778e355e48747bf0c3402cf27 100644 --- a/app/api/users/[username]/route.ts +++ b/app/api/users/[username]/route.ts @@ -29,6 +29,7 @@ export async function GET(request: Request, context: { params: { username: strin createdAt: true, bio: true, location: true, + website: true, followers: true, following: true, diff --git a/components/follow-button.tsx b/components/follow-button.tsx index fd3e645f016d898a50324a9771f4e0ee3556a139..552c4e99459c507cefb6a123ab4da7ab42f07cb6 100644 --- a/components/follow-button.tsx +++ b/components/follow-button.tsx @@ -1,3 +1,5 @@ +"use client" + import { useFollow } from "./profile/hooks/use-follow" import { Button } from "./ui/button" import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from "./ui/dialog" diff --git a/components/profile/components/edit-profile-modal.tsx b/components/profile/components/edit-profile-modal.tsx index 2555865ee27b479d16d11e2c06f223b128fd0581..ba74fcc915f7039692a2f89ba363965dd293489e 100644 --- a/components/profile/components/edit-profile-modal.tsx +++ b/components/profile/components/edit-profile-modal.tsx @@ -20,41 +20,52 @@ import { Textarea } from "@/components/ui/textarea" import { toast } from "@/components/ui/use-toast" import { UserAvatar } from "@/components/user-avatar" import { zodResolver } from "@hookform/resolvers/zod" -import { useSession } from "next-auth/react" import Image from "next/image" -import { useRef, useState } from "react" +import { useRouter } from "next/navigation" +import { useEffect, useRef, useState } from "react" import { useForm } from "react-hook-form" import { z } from "zod" import { useUpdateProfile } from "../hooks/use-update-profile" import { IProfile, IUser } from "../types" const FormSchema = z.object({ - name: z.string().min(1, "Name can't be blank.").max(50, "Name can't be more than 50 characters"), + name: z.string().min(1, "Name can't be blank").max(50, "Name can't be more than 50 characters"), bio: z.string().max(160, "Bio can't be more than 160 characters").optional(), location: z.string().max(30, "Location can't be more than 30 characters").optional(), website: z.string().max(100, "Website can't be more than 100 characters").optional(), }) +const ImagesSchema = z.custom<File>().refine((file) => file instanceof File, { message: "Expected a file" }) + .refine((file) => file?.size < 4000000, { message: "Images must be less than 4MB" }) + .optional() + export const EditProfileModal = ({ user }: { user: IUser }) => { - const { isLoading, mutate } = useUpdateProfile() + const { isLoading, isSuccess, mutate } = useUpdateProfile() const form = useForm<z.infer<typeof FormSchema>>({ resolver: zodResolver(FormSchema), + defaultValues: { + name: user.name, + bio: user.bio || "", + location: user.location || "", + website: user.website || "", + }, }) - form.setValue('name', user.name) - form.setValue('bio', user.bio || "") - form.setValue('location', user.location || "") - form.setValue('website', user.website || "") - - const [chosenImages, setChosenImages] = useState<Partial<IProfile>>({ + const [open, setOpen] = useState(false) + const [imageErrorMessage, setImageErrorMessage] = useState("") + const [chosenBanner, setChosenBanner] = useState<Partial<IProfile>>({ banner: { url: user?.banner || "", file: undefined }, + }) + const [chosenImages, setChosenImages] = useState<Partial<IProfile>>({ image: { url: user?.image || "", file: undefined }, }) const bannerInputRef = useRef<HTMLInputElement>(null) const imageInputRef = useRef<HTMLInputElement>(null) + const router = useRouter() + async function onSave(formData: z.infer<typeof FormSchema>) { if (!user) return null @@ -63,36 +74,56 @@ export const EditProfileModal = ({ user }: { user: IUser }) => { bio: formData.bio, location: formData.location, website: formData.website, - banner: { url: user?.banner || "", file: chosenImages.banner?.file }, + banner: { url: user?.banner || "", file: chosenBanner.banner?.file }, image: { url: user?.image || "", file: chosenImages.image?.file }, } - + // TODO dont send everything, only send what has changed mutate({ profile, userId: user.id, }) - - toast({ - description: "Successfully updated profile.", - }) } + useEffect(() => { + if (isSuccess) { + toast({ + description: "Successfully updated profile.", + }) + setOpen(false) + router.refresh() + } + }, [isSuccess, router]) + const chooseImage = async (event: any, type: string) => { const file = event.target.files[0] if (!file) return + try { + ImagesSchema.parse(file) + setImageErrorMessage("") + } catch (error: any) { + const err = error as z.ZodError + setImageErrorMessage(err.issues[0].message) + return + } - if (type === "banner" && bannerInputRef.current) + const reader = new FileReader() + + if (type === "banner" && bannerInputRef.current) { bannerInputRef.current.value = "" + reader.onloadend = () => { + setChosenBanner({ + ["banner"]: { url: reader.result as string, file }, + }) + } + } - if (type === "avatar" && imageInputRef.current) + if (type === "image" && imageInputRef.current) { imageInputRef.current.value = "" - - const reader = new FileReader() - - reader.onloadend = () => { - setChosenImages({ - [type]: { url: reader.result as string, file }, - }) + reader.onloadend = () => { + setChosenImages({ + ["image"]: { url: reader.result as string, file }, + }) + } } reader.readAsDataURL(file) @@ -101,7 +132,7 @@ export const EditProfileModal = ({ user }: { user: IUser }) => { if (!user) return null return ( - <Dialog> + <Dialog open={open} onOpenChange={setOpen}> <DialogTrigger asChild> <Button variant="outline">Edit Profile</Button> </DialogTrigger> @@ -113,156 +144,191 @@ export const EditProfileModal = ({ user }: { user: IUser }) => { </DialogDescription> </DialogHeader> <Form {...form}> - <form onSubmit={form.handleSubmit(onSave)} className="grid gap-3 py-3"> - <Card className="relative w-full overflow-hidden"> - <AspectRatio ratio={889 / 500} className="bg-slate-600 dark:bg-slate-400"> - {chosenImages.banner?.url && - <Image - src={chosenImages.banner?.url} - alt={"user banner image"} - fill - priority - className="object-center" - /> + <form onSubmit={form.handleSubmit(onSave)}> + <div className="grid gap-3 py-3"> + + <div className="space-y-2"> + <div className="grid grid-cols-4 items-center gap-4"> + <div className="col-span-1 relative w-full overflow-hidden items-center"> + <div className="object-center object-cover w-full h-full"> + {!chosenImages.image?.url ? + <UserAvatar + user={{ username: user.username, image: null }} + className="object-center object-cover w-full h-full aspect-square" + /> + : + <AspectRatio ratio={1 / 1} className="overflow-hidden rounded-full"> + <Image + src={chosenImages.image?.url} + alt={"user image"} + fill + priority + className="object-center object-cover w-full h-full " + /> + </AspectRatio> + } + </div> + <input + className="hidden resize-none" + type="file" + accept="image/*" + ref={imageInputRef} + onChange={(e) => chooseImage(e, "image")} + disabled={isLoading} + /> + <div className="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 flex space-x-3"> + <Button + type="button" + variant="outline" + size="icon" + className="bg-opacity-50 dark:bg-opacity-50" + onClick={() => imageInputRef?.current?.click()} + disabled={isLoading} + > + <Icons.camera /> + </Button> + </div> + </div> + + <Card className="col-span-3 relative w-full overflow-hidden border-none"> + + <AspectRatio ratio={3 / 1} className="bg-muted overflow-hidden"> + {chosenBanner.banner?.url && + <Image + src={chosenBanner.banner?.url} + alt={"user banner image"} + fill + priority + className="object-center object-cover w-full h-full" + /> + } + </AspectRatio> + <input + className="hidden w-full resize-none" + type="file" + accept="image/*" + ref={bannerInputRef} + onChange={(e) => chooseImage(e, "banner")} + disabled={isLoading} + /> + <div className="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 flex space-x-3"> + <Button + type="button" + variant="outline" + size="icon" + className="bg-opacity-50 dark:bg-opacity-50" + onClick={() => bannerInputRef.current?.click()} + disabled={isLoading} + > + <Icons.camera /> + </Button> + {chosenBanner.banner?.url && ( + <Button + type="button" + variant="outline" + size="icon" + className="bg-opacity-50 dark:bg-opacity-50" + onClick={() => setChosenBanner({ banner: { url: "", file: undefined } })} + disabled={isLoading} + > + <Icons.close /> + </Button> + )} + </div> + </Card> + </div> + {imageErrorMessage && + <div className="grid grid-cols-4 items-center gap-4"> + <div className="col-span-1"></div> + <div className="col-span-3"> + <p className="text-sm font-medium text-destructive">{imageErrorMessage}</p> + </div> + </div> } - </AspectRatio> - <input - type="file" - accept="image/*" - ref={bannerInputRef} - onChange={(e) => chooseImage(e, "banner")} - disabled={isLoading} - /> - <div className="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 flex space-x-3"> - <Button - variant="outline" - size="icon" - className="bg-opacity-50 dark:bg-opacity-50" - onClick={() => bannerInputRef.current?.click()} - disabled={isLoading} - > - <Icons.camera /> - </Button> - {chosenImages.banner?.url && ( - <Button - variant="outline" - size="icon" - className="bg-opacity-50 dark:bg-opacity-50" - onClick={() => setChosenImages({ banner: { url: "", file: undefined } })} - disabled={isLoading} - > - <Icons.close /> - </Button> - )} </div> - </Card> - <div className="absolute w-24 h-24 -mt-12 overflow-hidden"> - <UserAvatar - user={{ username: user.username, image: chosenImages.banner?.url || null }} - className="object-cover" - /> - <input - type="file" - accept="image/*" - ref={imageInputRef} - onChange={(e) => chooseImage(e, "image")} - disabled={isLoading} - /> - <div className="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 flex space-x-3"> - <Button - variant="outline" - size="icon" - className="bg-opacity-50 dark:bg-opacity-50" - onClick={() => imageInputRef?.current?.click()} - disabled={isLoading} - > - <Icons.camera /> - </Button> + <div className="grid grid-cols-4 items-center gap-4"> + <Label htmlFor="name" className="text-right"> + Name + </Label> + <FormField + control={form.control} + name="name" + render={({ field }) => ( + <FormItem className="col-span-3"> + <FormControl> + <Input id="name" disabled={isLoading} {...field} /> + </FormControl> + <FormMessage /> + </FormItem> + )} + /> </div> - </div> - <div className="grid grid-cols-4 items-center gap-4"> - <Label htmlFor="name" className="text-right"> - Name - </Label> - <FormField - control={form.control} - name="name" - render={({ field }) => ( - <FormItem> - <FormControl> - <Input id="name" className="col-span-3" disabled={isLoading} {...field} /> - </FormControl> - <FormMessage /> - </FormItem> - )} - /> - </div> + <div className="grid grid-cols-4 items-center gap-4"> + <Label htmlFor="bio" className="text-right"> + Bio + </Label> + <FormField + control={form.control} + name="bio" + render={({ field }) => ( + <FormItem className="col-span-3"> + <FormControl> + <Textarea id="bio" className="resize-none" disabled={isLoading} {...field} /> + </FormControl> + <FormMessage /> + </FormItem> + )} + /> + </div> - <div className="grid grid-cols-4 items-center gap-4"> - <Label htmlFor="bio" className="text-right"> - Bio - </Label> - <FormField - control={form.control} - name="bio" - render={({ field }) => ( - <FormItem> - <FormControl> - <Textarea id="bio" className="col-span-3 resize-none" disabled={isLoading} {...field} /> - </FormControl> - <FormMessage /> - </FormItem> - )} - /> - </div> + <div className="grid grid-cols-4 items-center gap-4"> + <Label htmlFor="location" className="text-right"> + Location + </Label> + <FormField + control={form.control} + name="location" + render={({ field }) => ( + <FormItem className="col-span-3"> + <FormControl> + <Input id="location" disabled={isLoading} {...field} /> + </FormControl> + <FormMessage /> + </FormItem> + )} + /> + </div> - <div className="grid grid-cols-4 items-center gap-4"> - <Label htmlFor="location" className="text-right"> - Location - </Label> - <FormField - control={form.control} - name="location" - render={({ field }) => ( - <FormItem> - <FormControl> - <Input id="location" className="col-span-3" disabled={isLoading} {...field} /> - </FormControl> - <FormMessage /> - </FormItem> - )} - /> + <div className="grid grid-cols-4 items-center gap-4"> + <Label htmlFor="website" className="text-right"> + Website + </Label> + <FormField + control={form.control} + name="website" + render={({ field }) => ( + <FormItem className="col-span-3"> + <FormControl> + <Input id="website" disabled={isLoading} {...field} /> + </FormControl> + <FormMessage /> + </FormItem> + )} + /> + </div> </div> - - <div className="grid grid-cols-4 items-center gap-4"> - <Label htmlFor="website" className="text-right"> - Website - </Label> - <FormField - control={form.control} - name="website" - render={({ field }) => ( - <FormItem> - <FormControl> - <Input id="website" className="col-span-3" disabled={isLoading} {...field} /> - </FormControl> - <FormMessage /> - </FormItem> + <DialogFooter> + <Button type="submit" disabled={isLoading || !form.formState.isDirty}> + {isLoading && ( + <Icons.spinner className="mr-2 h-4 w-4 animate-spin" /> )} - /> - </div> + Save changes + </Button> + </DialogFooter> </form> </Form> - <DialogFooter> - <Button type="submit" disabled={isLoading}> - {isLoading && ( - <Icons.spinner className="mr-2 h-4 w-4 animate-spin" /> - )} - Save changes - </Button> - </DialogFooter> </DialogContent> </Dialog> ) diff --git a/components/profile/components/profile-user-info.tsx b/components/profile/components/profile-user-info.tsx index c1837db82734467900b28243b4309b7a9aee0892..e2e6924ded82bcc2f81ae493d0dba9e907a45bfa 100644 --- a/components/profile/components/profile-user-info.tsx +++ b/components/profile/components/profile-user-info.tsx @@ -13,71 +13,83 @@ import { UserJoinDate } from "./user-join-date" export const ProfileUserInfo = async ({ user }: { user: IUser }) => { const session = await getCurrentUser() - // const isFollowing = following({ - // user, - // sessionUserId: session ? session.id : "", - // }) + const isFollowing = following({ + user, + sessionUserId: session ? session.id : "", + }) return ( <> - {/* <div className="h-64 overflow-hidden"> - <AspectRatio ratio={889 / 500} className="bg-slate-600 dark:bg-slate-400"> - {user.banner && - <Image - src={user.banner} - alt={"user banner image"} - fill - priority - className="object-center" - /> - } - </AspectRatio> + <AspectRatio ratio={3 / 1} className="bg-muted w-full overflow-hidden"> + {user.banner && + <Image + src={user.banner} + alt={"user banner image"} + fill + priority + className="object-center object-cover w-full h-full" + /> + } + </AspectRatio> + <div className="relative"> + <div className="flex items-end justify-between p-6 md:px-12"> + <div> + <div className="absolute bottom-6 md:bottom-3"> + <UserAvatar + user={{ username: user.username, image: user.image || null }} + className="h-20 md:h-40 w-20 md:w-40" + /> + </div> + </div> + <div> + {session?.id === user.id ? ( + <EditProfileModal user={user} /> + ) : ( + <FollowButton + userId={user.id} + username={user.username ? user.username : ""} + isFollowing={isFollowing} + /> + )} + </div> + </div> </div> - <div className="p-6 md:p-12 ss:flex"> - <UserAvatar - user={{ username: user.username, image: user.image || null }} - className="h-52 w-52 -mt-36" - /> - <div className="ml-6 md:ml-12 space-y-3"> + <div className="px-6 md:px-12 flex flex-col space-y-3 w-full"> + <div className="pb-3 whitespace-nowrap items-center"> <h1 className="text-2xl font-bold">{user.name}</h1> <h1 className="text-md text-sky-500">@{user.username}</h1> - {user.bio && <h1 className="">{user.bio}</h1>} + </div> + {user.bio && <h1 className="">{user.bio}</h1>} + + <div className="flex whitespace-nowrap items-center space-x-6 text-muted-foreground"> {user.location && - <div className="space-x-2"> - <Icons.location /> - {user.location} + <div className="flex items-center"> + <Icons.location className="mr-1" /> + <div>{user.location}</div> </div> } {user.website && - <div className="space-x-2"> - <Icons.website /> - {user.website} + <div className="flex items-center"> + <Icons.website className="mr-1" /> + <div>{user.website}</div> </div> } {user.createdAt && <UserJoinDate date={user.createdAt} />} </div> - <div className="flex justify-end ml-6 md:ml-12"> - {session?.id === user.id ? ( - <EditProfileModal user={user} /> - ) : ( - <FollowButton userId={user.id} username={user.username ? user.username : ""} isFollowing={isFollowing} /> - )} - </div> - - <div className="flex justify-between w-full mt-6 md:mt-12"> + <div className="flex whitespace-nowrap items-center space-x-6"> <Link href={`/${user.username}/following`}> - <span className="text-sm">{user._count?.following}</span> - <span className="text-sm">Following</span> + <span className="text-sm font-bold">{user._count?.following}</span> + <span className="text-sm text-muted-foreground"> Following</span> </Link> <Link href={`/${user.username}/followers`}> - <span className="text-sm">{user._count?.followers}</span> - <span className="text-sm">Followers</span> + <span className="text-sm font-bold">{user._count?.followers}</span> + <span className="text-sm text-muted-foreground"> Followers</span> </Link> </div> - </div> */} + </div> </ > ) } \ No newline at end of file diff --git a/components/profile/components/profile.tsx b/components/profile/components/profile.tsx deleted file mode 100644 index 46cd550b9ddf208ea1bd30a0c6348186a4284cf2..0000000000000000000000000000000000000000 --- a/components/profile/components/profile.tsx +++ /dev/null @@ -1,47 +0,0 @@ -"use client" - -import LoadingItem from "@/components/loading-item" -import { TryAgain } from "@/components/try-again" -import { Card } from "@/components/ui/card" -import { UserNotFound } from "@/components/user-not-found" -import { usePathname } from "next/navigation" -import { useUser } from "../hooks/use-user" -import { ProfileUserInfo } from "./profile-user-info" - -export const Profile = ({ children }: { children: React.ReactNode }) => { - const pathname = usePathname() - const username = pathname?.split("/")[1] || "" - // TODO: Fix this client / server side rendering issue - const { data: user, isLoading, isError } = useUser(username) - - if (isLoading) { - return ( - <> - <LoadingItem /> - </> - ) - } - - if (isError) { - return ( - <> - <TryAgain /> - </> - ) - } - - if (!isLoading && !isError && !user) { - return ( - <> - <UserNotFound /> - </> - ) - } - - return ( - <Card className="overflow-hidden h-full w-full"> - {/* <ProfileUserInfo user={user} /> */} - <div>{children}</div> - </Card> - ) -} diff --git a/components/profile/components/user-join-date.tsx b/components/profile/components/user-join-date.tsx index 35b7738335c0454c2a5fac33b41b37bd3d9e4441..16706d98df60d7c109652d6122f9b5f435c1dd50 100644 --- a/components/profile/components/user-join-date.tsx +++ b/components/profile/components/user-join-date.tsx @@ -9,11 +9,11 @@ export const UserJoinDate = ({ showIcon?: boolean }) => { return ( - <div className=""> - {showIcon && (<Icons.calendar />)} - <span className=""> + <div className="flex items-center"> + {showIcon && (<Icons.calendar className="mr-1" />)} + <div> Joined {dayjs(date).format("MMMM YYYY")} - </span> + </div> </div> ) } \ No newline at end of file