-
Notifications
You must be signed in to change notification settings - Fork 10
feat: add user activity tab and responsive navbar #48
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
cd8b144
f1d35a5
27150de
c0043e3
fde5453
50bafee
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -1,6 +1,10 @@ | ||||||
| import { eq } from "drizzle-orm"; | ||||||
| import { desc, eq, sql } from "drizzle-orm"; | ||||||
| import type { FastifyInstance } from "fastify"; | ||||||
| import { users } from "@/db/schema/user.schema"; | ||||||
| import { threads } from "@/db/schema/thread.schema"; | ||||||
| import { posts } from "@/db/schema/post.schema"; | ||||||
| import { votes } from "@/db/schema/vote.schema"; | ||||||
| import { topics } from "@/db/schema/topic.schema"; | ||||||
| import { DrizzleClient } from "../db/index"; | ||||||
| import { | ||||||
| type User, | ||||||
|
|
@@ -238,4 +242,69 @@ export async function userRoutes(fastify: FastifyInstance) { | |||||
| } | ||||||
| }, | ||||||
| ); | ||||||
|
|
||||||
| fastify.get( | ||||||
| "/:userId/activity", | ||||||
| { preHandler: optionalAuth }, | ||||||
| async (request, reply) => { | ||||||
| try { | ||||||
| const { userId } = request.params as { userId: string }; | ||||||
|
|
||||||
| const userThreads = await DrizzleClient.select({ | ||||||
| id: threads.id, | ||||||
| type: sql<string>`'thread'`, | ||||||
| title: threads.threadTitle, | ||||||
| createdAt: threads.createdAt, | ||||||
| }) | ||||||
| .from(threads) | ||||||
| .where(eq(threads.createdBy, userId)) | ||||||
| .orderBy(desc(threads.createdAt)) | ||||||
| .limit(5); | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The limit for individual activity types (threads, posts, likes) is set to 5. When these are merged and sliced to 15 (line 299), the resulting feed might be incomplete if a user's activity is heavily weighted toward one type. For example, a user with many posts but no threads would only see 5 items. Consider increasing the limit for each query (lines 262, 276, and 290) to 15 to ensure the feed is always populated with the most recent items across all categories.
Suggested change
|
||||||
|
|
||||||
| const userPosts = await DrizzleClient.select({ | ||||||
| id: posts.id, | ||||||
| type: sql<string>`'post'`, | ||||||
| title: threads.threadTitle, | ||||||
| content: posts.content, | ||||||
| threadId: posts.threadId, | ||||||
| createdAt: posts.createdAt, | ||||||
| }) | ||||||
| .from(posts) | ||||||
| .innerJoin(threads, eq(posts.threadId, threads.id)) | ||||||
| .where(eq(posts.createdBy, userId)) | ||||||
| .orderBy(desc(posts.createdAt)) | ||||||
| .limit(5); | ||||||
|
|
||||||
| const userLikes = await DrizzleClient.select({ | ||||||
| id: votes.id, | ||||||
| type: sql<string>`'like'`, | ||||||
| title: threads.threadTitle, | ||||||
| threadId: threads.id, | ||||||
| createdAt: votes.createdAt, | ||||||
| }) | ||||||
| .from(votes) | ||||||
| .innerJoin(posts, eq(votes.postId, posts.id)) | ||||||
| .innerJoin(threads, eq(posts.threadId, threads.id)) | ||||||
| .where(eq(votes.userId, userId)) | ||||||
| .orderBy(desc(votes.createdAt)) | ||||||
| .limit(5); | ||||||
|
|
||||||
| const activity = [...userThreads, ...userPosts, ...userLikes].sort( | ||||||
| (a, b) => | ||||||
| new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime(), | ||||||
| ); | ||||||
|
|
||||||
| return reply.send({ | ||||||
| success: true, | ||||||
| activity: activity.slice(0, 15), | ||||||
| }); | ||||||
| } catch (err) { | ||||||
| fastify.log.error("Error fetching user activity:", err); | ||||||
| return reply.status(500).send({ | ||||||
| error: "Failed to fetch user activity", | ||||||
| success: false, | ||||||
| }); | ||||||
| } | ||||||
| }, | ||||||
| ); | ||||||
| } | ||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -42,13 +42,14 @@ export default function ProfileDropdown() { | |
| <DropdownMenuTrigger asChild> | ||
| <Button | ||
| variant="neutral" | ||
| className="border-3 flex items-center gap-2 neo-brutal-button border-primary text-primary bg-secondary hover:bg-secondary hover:text-black" | ||
| size="sm" | ||
| className="h-9 border-2 border-border flex items-center gap-2 px-3 text-sm font-bold shadow-none hover:bg-secondary hover:text-black transition-all group" | ||
| > | ||
|
|
||
| <span className="text-sm tracking-tight"> | ||
| <span className="tracking-tight"> | ||
| {user.firstName || user.username} | ||
| </span> | ||
| <ChevronDown size={16} className="text-primary opacity-70" /> | ||
| <ChevronDown size={14} className="opacity-70 transition-transform group-hover:translate-y-0.5" /> | ||
| </Button> | ||
| </DropdownMenuTrigger> | ||
|
|
||
|
|
@@ -82,7 +83,7 @@ export default function ProfileDropdown() { | |
|
|
||
| <DropdownMenuItem asChild> | ||
| <Link | ||
| to="/my-threads" | ||
| to={user.username ? `/profile/${user.username}` : "/my/profile"} | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The "My Threads" link points to the same destination as "My Profile" (line 66). This redundancy is also present in the mobile header drawer. If the intention is to show the user's threads, consider linking to the profile page with a query parameter or hash to activate the threads tab (e.g., |
||
| className="flex cursor-pointer items-center gap-2 p-3 font-bold transition-all hover:translate-x-1 hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground" | ||
| > | ||
| <MessageSquare size={18} /> | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
userIdparameter is extracted fromrequest.paramswithout validation. To maintain consistency with other routes in this file (e.g., line 22) and ensure data integrity, consider using a validation schema (e.g., Zod) to parse and validate theuserIdformat before using it in database queries.