{name}
33 |@{username}
34 |Make any changes
29 | 30 |27 | Complete your profile now to use Threads 28 |
29 | 30 |@{username}
34 |35 | {link.label.split(/\s+/)[0]} 36 |
37 | 38 | ); 39 | })} 40 |{pageNumber}
44 | 51 |No users found
37 | ) : ( 38 | <> 39 | {result.users.map((person) => ( 40 |{link.label}
40 | 41 | ); 42 | })} 43 |Logout
57 |No communities found
40 | ) : ( 41 | <> 42 | {result.communities.map((community) => ( 43 |No threads found
40 | ) : ( 41 | <> 42 | {result.posts.map((post, idx) => ( 43 |No threads found
41 | ) : ( 42 | <> 43 | {result.posts.map((post, idx) => ( 44 |@{username}
35 |{bio}
39 | 40 |63 | {members.length}+ Users 64 |
65 | )} 66 |@{username}
44 |Edit
60 |{bio}
75 | 76 | 77 |42 | No communities yet 43 |
44 | )} 45 |No users yet
65 | )} 66 |No users found
64 | ) : ( 65 | <> 66 | {reactions.users.map((reaction: any) => ( 67 |No activity yet
54 | )} 55 |62 | 63 | {author.name} 64 | {" "} 65 | <> 66 | {activityType === "follow" && "followed you"} 67 | {activityType === "reaction" && "like your thread"} 68 | {text && `replied to your thread: "${truncateString(text, 100)}"`} 69 | >{" "} 70 | ~ {formatDateWithMeasure(createdAt)} 71 |
72 | ); 73 | 74 | export default Page; 75 | -------------------------------------------------------------------------------- /components/forms/PostThread.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import * as z from "zod"; 4 | import { useForm } from "react-hook-form"; 5 | import { useOrganization } from "@clerk/nextjs"; 6 | import { zodResolver } from "@hookform/resolvers/zod"; 7 | import { usePathname, useRouter } from "next/navigation"; 8 | 9 | import { 10 | Form, 11 | FormControl, 12 | FormField, 13 | FormItem, 14 | FormLabel, 15 | FormMessage, 16 | } from "@/components/ui/form"; 17 | import { Button } from "@/components/ui/button"; 18 | import { Textarea } from "@/components/ui/textarea"; 19 | 20 | import { ThreadValidation } from "@/lib/validations/thread"; 21 | import { createThread, editThread } from "@/lib/actions/thread.actions"; 22 | 23 | interface Props { 24 | userId: string; 25 | threadId?: string; 26 | threadText?: string; 27 | } 28 | 29 | function PostThread({ userId, threadId, threadText }: Props) { 30 | const router = useRouter(); 31 | const pathname = usePathname(); 32 | 33 | const { organization } = useOrganization(); 34 | 35 | const form = useForm{tab.label}
44 | 45 | {tab.label === "Threads" && ( 46 |47 | {communityDetails.threads.length} 48 |
49 | )} 50 |161 | {body} 162 |
163 | ) 164 | }) 165 | FormMessage.displayName = "FormMessage" 166 | 167 | export { 168 | useFormField, 169 | Form, 170 | FormItem, 171 | FormLabel, 172 | FormControl, 173 | FormDescription, 174 | FormMessage, 175 | FormField, 176 | } 177 | -------------------------------------------------------------------------------- /app/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | @layer components { 6 | /* main */ 7 | .main-container { 8 | @apply flex min-h-screen flex-1 flex-col items-center bg-dark-1 px-6 pb-10 pt-28 max-md:pb-32 sm:px-10; 9 | } 10 | 11 | /* Head Text */ 12 | .head-text { 13 | @apply text-heading2-bold text-light-1; 14 | } 15 | 16 | /* Activity */ 17 | .activity-card { 18 | @apply flex items-center gap-2 rounded-md bg-dark-2 px-7 py-4; 19 | } 20 | 21 | /* No Result */ 22 | .no-result { 23 | @apply text-center !text-base-regular text-light-3; 24 | } 25 | 26 | /* Community Card */ 27 | .community-card { 28 | @apply w-full rounded-lg bg-dark-3 px-4 py-5 sm:w-96; 29 | } 30 | 31 | .community-card_btn { 32 | @apply rounded-lg bg-primary-500 px-5 py-1.5 text-small-regular !text-light-1 !important; 33 | } 34 | 35 | /* thread card */ 36 | .thread-card_bar { 37 | @apply relative mt-2 w-0.5 grow rounded-full bg-neutral-800; 38 | } 39 | 40 | /* User card */ 41 | .user-card { 42 | @apply flex flex-col justify-between gap-4 max-xs:rounded-xl max-xs:bg-dark-3 max-xs:p-4 xs:flex-row xs:items-center; 43 | } 44 | 45 | .user-card_avatar { 46 | @apply flex flex-1 items-start justify-start gap-3 xs:items-center; 47 | } 48 | 49 | .user-card_btn { 50 | @apply h-auto min-w-[74px] rounded-lg bg-primary-500 text-[12px] text-light-1 !important; 51 | } 52 | 53 | .follow-card_btn { 54 | @apply rounded-lg bg-primary-500 px-3 py-1.5 text-small-regular !text-light-1 !important; 55 | } 56 | 57 | .searchbar { 58 | @apply flex gap-1 rounded-lg bg-dark-3 px-4 py-2; 59 | } 60 | 61 | .searchbar_input { 62 | @apply border-none bg-dark-3 text-base-regular text-light-4 outline-none !important; 63 | } 64 | 65 | .topbar { 66 | @apply fixed top-0 z-30 flex w-full items-center justify-between bg-dark-2 px-6 py-3; 67 | } 68 | 69 | .bottombar { 70 | @apply fixed bottom-0 z-10 w-full rounded-t-3xl bg-glassmorphism p-4 backdrop-blur-lg xs:px-7 md:hidden; 71 | } 72 | 73 | .bottombar_container { 74 | @apply flex items-center justify-between gap-3 xs:gap-5; 75 | } 76 | 77 | .bottombar_link { 78 | @apply relative flex flex-col items-center gap-2 rounded-lg p-2 sm:flex-1 sm:px-2 sm:py-2.5; 79 | } 80 | 81 | .leftsidebar { 82 | @apply sticky left-0 top-0 z-20 flex h-screen w-fit flex-col justify-between overflow-auto border-r border-r-dark-4 bg-dark-2 pb-5 pt-28 max-md:hidden; 83 | } 84 | 85 | .leftsidebar_link { 86 | @apply relative flex justify-start gap-4 rounded-lg p-4; 87 | } 88 | 89 | .pagination { 90 | @apply mt-10 flex w-full items-center justify-center gap-5; 91 | } 92 | 93 | .rightsidebar { 94 | @apply sticky right-0 top-0 z-20 flex h-screen w-fit flex-col justify-between gap-12 overflow-auto border-l border-l-dark-4 bg-dark-2 px-10 pb-6 pt-28 max-xl:hidden; 95 | } 96 | } 97 | 98 | @layer utilities { 99 | .css-invert { 100 | @apply invert-[50%] brightness-200; 101 | } 102 | 103 | .custom-scrollbar::-webkit-scrollbar { 104 | width: 3px; 105 | height: 3px; 106 | border-radius: 2px; 107 | } 108 | 109 | .custom-scrollbar::-webkit-scrollbar-track { 110 | background: #09090a; 111 | } 112 | 113 | .custom-scrollbar::-webkit-scrollbar-thumb { 114 | background: #5c5c7b; 115 | border-radius: 50px; 116 | } 117 | 118 | .custom-scrollbar::-webkit-scrollbar-thumb:hover { 119 | background: #7878a3; 120 | } 121 | } 122 | 123 | /* Clerk Responsive fix */ 124 | .cl-organizationSwitcherTrigger .cl-userPreview .cl-userPreviewTextContainer { 125 | @apply max-sm:hidden; 126 | } 127 | 128 | .cl-organizationSwitcherTrigger 129 | .cl-organizationPreview 130 | .cl-organizationPreviewTextContainer { 131 | @apply max-sm:hidden; 132 | } 133 | 134 | /* Shadcn Component Styles */ 135 | 136 | /* Tab */ 137 | .tab { 138 | @apply flex min-h-[50px] flex-1 items-center gap-3 bg-dark-2 text-light-2 data-[state=active]:bg-[#0e0e12] data-[state=active]:text-light-2 !important; 139 | } 140 | 141 | .no-focus { 142 | @apply focus-visible:ring-0 focus-visible:ring-transparent focus-visible:ring-offset-0 !important; 143 | } 144 | 145 | /* Account Profile */ 146 | .account-form_image-label { 147 | @apply flex h-24 w-24 items-center justify-center rounded-full bg-dark-4 !important; 148 | } 149 | 150 | .account-form_image-input { 151 | @apply cursor-pointer border-none bg-transparent outline-none file:text-blue !important; 152 | } 153 | 154 | .account-form_input { 155 | @apply border border-dark-4 bg-dark-3 text-light-1 !important; 156 | } 157 | 158 | /* Comment Form */ 159 | .comment-form { 160 | @apply mt-10 flex items-center gap-4 border-y border-y-dark-4 py-5 max-xs:flex-col !important; 161 | } 162 | 163 | .comment-form_btn { 164 | @apply rounded-3xl bg-primary-500 px-8 py-2 !text-small-regular text-light-1 max-xs:w-full !important; 165 | } 166 | 167 | .bg-clerk-auth { 168 | @apply bg-[linear-gradient(_to_right_top,#d16ba5,#c262a6,#b25aa7,#a054a8,#8c4ea9,#775fbe,#596fce,#257dda,#00a0ef,#00c1f7,#00dff6,#5ffbf1_)]; 169 | } 170 | -------------------------------------------------------------------------------- /app/(root)/profile/[id]/page.tsx: -------------------------------------------------------------------------------- 1 | import Image from "next/image"; 2 | import { currentUser } from "@clerk/nextjs"; 3 | import { redirect } from "next/navigation"; 4 | 5 | import { profileTabs } from "@/constants"; 6 | 7 | import ThreadsTab from "@/components/shared/ThreadsTab"; 8 | import ProfileHeader from "@/components/shared/ProfileHeader"; 9 | import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; 10 | 11 | import { 12 | fetchUser, 13 | fetchUsersByField, 14 | isUserFollowing, 15 | } from "@/lib/actions/user.actions"; 16 | import UserCard from "@/components/cards/UserCard"; 17 | 18 | async function Page({ params }: { params: { id: string } }) { 19 | const user = await currentUser(); 20 | if (!user) return null; 21 | 22 | const userInfo = await fetchUser(params.id); 23 | if (!userInfo?.onboarded) redirect("/onboarding"); 24 | 25 | const followers = await fetchUsersByField(params.id, "followers"); 26 | const following = await fetchUsersByField(params.id, "following"); 27 | 28 | const isFollowing = await isUserFollowing(user.id, params.id); 29 | 30 | return ( 31 |{tab.label}
55 | {tab.label === "Threads" && ( 56 |57 | {userInfo.threadsCount} 58 |
59 | )} 60 | {tab.label === "Followers" && ( 61 |62 | {userInfo.followersCount} 63 |
64 | )} 65 | {tab.label === "Following" && ( 66 |67 | {userInfo.followingCount} 68 |
69 | )} 70 |No threads found
79 |No users found
93 | ) : ( 94 | <> 95 | {followers.map((follower: any) => ( 96 |No users found
114 | ) : ( 115 | <> 116 | {following.map((following: any) => ( 117 |{content}
83 | 84 |124 | {comments.length}{" "} 125 | {comments.length > 1 ? "replies" : "reply"} 126 |
127 | 128 | )} 129 | 130 | {comments.length > 0 && reactions.length > 0 && ( 131 |•
132 | )} 133 | 134 | {reactions.length > 0 && ( 135 | 136 |137 | {reactions.length}{" "} 138 | {reactions.length > 1 ? "likes" : "like"} 139 |
140 | 141 | )} 142 | > 143 | )} 144 |185 | {comments.length}{" "} 186 | {comments.length > 1 ? "replies" : "reply"} 187 |
188 | 189 |•
195 |215 | {reactions.length} {reactions.length > 1 ? "likes" : "like"} 216 |
217 | 218 |230 | {formatDateString(createdAt)} 231 | {community && ` - ${community.name} Community`} 232 |
233 | 234 |