diff --git a/.vscode/settings.json b/.vscode/settings.json index c5c3a86..3737ea8 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -4,6 +4,7 @@ "codemirror", "conve", "doesn", + "Dtos", "Keymap", "lezer", "nextjs", diff --git a/convex/posts.ts b/convex/posts.ts index 581dfe6..c9331fa 100644 --- a/convex/posts.ts +++ b/convex/posts.ts @@ -1,9 +1,9 @@ import { paginationOptsValidator } from 'convex/server'; import { v } from 'convex/values'; import { PostWithUserDto } from '../src/app/types'; -import { Id } from './_generated/dataModel'; -import { mutation, query } from './_generated/server'; -import { userQuery } from './users'; +import { Doc } from './_generated/dataModel.d'; +import { QueryCtx, mutation, query } from './_generated/server'; +import { createUserDto, userQuery } from './users'; export const createOrUpdatePost = mutation({ args: { @@ -82,13 +82,7 @@ export const getPostById = query({ return { ...post, - user: { - _id: user._id, - _creationTime: user._creationTime, - clerkUsername: user.clerkUser.username, - clerkImageUrl: user.clerkUser.image_url, - clerkUserId: user.clerkUser.id, - }, + user: createUserDto(user), }; }, }); @@ -102,36 +96,10 @@ export const getRepliesByParentId = query({ .order('desc') .paginate(args.paginationOpts); - const repliesWithUserData = await Promise.all( - replies.page.map(async reply => { - const user = await ctx.db.get(reply.userId); - - if (user === null) { - return { - ...reply, - user: { - _id: '' as Id<'users'>, - _creationTime: 0, - clerkUsername: null, - clerkImageUrl: '', - clerkUserId: '', - }, - }; - } - - return { - ...reply, - user: { - _id: user._id, - _creationTime: user._creationTime, - clerkUsername: user.clerkUser.username, - clerkImageUrl: user.clerkUser.image_url, - clerkUserId: user.clerkUser.id, - }, - }; - }) - ); - + const repliesWithUserData = await getPostsWithUsers({ + ctx, + posts: replies.page, + }); return { ...replies, page: repliesWithUserData }; }, }); @@ -170,35 +138,7 @@ export const getAllPostsWithUser = query({ .order('desc') .paginate(args.paginationOpts); - const postsWithUser = await Promise.all( - posts.page.map(async post => { - const user = await ctx.db.get(post.userId); - - if (user === null) { - return { - ...post, - user: { - _id: '' as Id<'users'>, - _creationTime: 0, - clerkUsername: null, - clerkImageUrl: '', - clerkUserId: '', - }, - }; - } - - return { - ...post, - user: { - _id: user._id, - _creationTime: user._creationTime, - clerkUsername: user.clerkUser.username, - clerkImageUrl: user.clerkUser.image_url, - clerkUserId: user.clerkUser.id, - }, - }; - }) - ); + const postsWithUser = await getPostsWithUsers({ ctx, posts: posts.page }); return { ...posts, page: postsWithUser }; }, @@ -239,35 +179,7 @@ export const getAllPostsForFollowings = query({ .order('desc') .paginate(args.paginationOpts); - const postsWithUser = await Promise.all( - posts.page.map(async post => { - const user = await ctx.db.get(post.userId); - - if (user === null) { - return { - ...post, - user: { - _id: '' as Id<'users'>, - _creationTime: 0, - clerkUsername: null, - clerkImageUrl: '', - clerkUserId: '', - }, - }; - } - - return { - ...post, - user: { - _id: user._id, - _creationTime: user._creationTime, - clerkUsername: user.clerkUser.username, - clerkImageUrl: user.clerkUser.image_url, - clerkUserId: user.clerkUser.id, - }, - }; - }) - ); + const postsWithUser = await getPostsWithUsers({ ctx, posts: posts.page }); return { ...posts, page: postsWithUser }; }, @@ -276,11 +188,41 @@ export const getAllPostsForFollowings = query({ export const getPostsBySearchTerm = query({ args: { term: v.string(), paginationOpts: paginationOptsValidator }, handler: async (ctx, args) => { - return await ctx.db + const posts = await ctx.db .query('posts') .withSearchIndex('search_by_content', q => q.search('content', args.term).eq('parentPostId', undefined) ) .paginate(args.paginationOpts); + + const postsWithUser = await getPostsWithUsers({ ctx, posts: posts.page }); + + return { ...posts, page: postsWithUser }; }, }); + +async function getPostsWithUsers({ + ctx, + posts, +}: { + ctx: QueryCtx; + posts: Doc<'posts'>[]; +}) { + return await Promise.all( + posts.map(async post => { + const user = await ctx.db.get(post.userId); + + if (user === null) { + return { + ...post, + user: createUserDto(user), + }; + } + + return { + ...post, + user: createUserDto(user), + }; + }) + ); +} diff --git a/convex/schema.ts b/convex/schema.ts index 3784e03..bb58827 100644 --- a/convex/schema.ts +++ b/convex/schema.ts @@ -38,6 +38,7 @@ export default defineSchema( .index('by_clerk_id', ['clerkUser.id']) .searchIndex('search_by_username', { searchField: 'clerkUser.username', + filterFields: [], }), posts: defineTable({ parentPostId: v.optional(v.id('posts')), diff --git a/convex/users.ts b/convex/users.ts index 8dbb8d6..cc8bf7f 100644 --- a/convex/users.ts +++ b/convex/users.ts @@ -1,6 +1,7 @@ import { paginationOptsValidator } from 'convex/server'; import { v } from 'convex/values'; import { UserDto } from './../src/app/types/index'; +import { Doc, Id } from './_generated/dataModel'; import { QueryCtx, internalMutation, @@ -24,13 +25,7 @@ export const getUserByClerkId = query({ return 'USER_NOT_FOUND'; } - return { - _id: user._id, - _creationTime: user._creationTime, - clerkUsername: user.clerkUser.username, - clerkImageUrl: user.clerkUser.image_url, - clerkUserId: user.clerkUser.id, - }; + return createUserDto(user); }, }); @@ -43,13 +38,7 @@ export const getUserById = query({ return 'USER_NOT_FOUND'; } - return { - _id: user._id, - _creationTime: user._creationTime, - clerkUsername: user.clerkUser.username, - clerkImageUrl: user.clerkUser.image_url, - clerkUserId: user.clerkUser.id, - }; + return createUserDto(user); }, }); @@ -126,11 +115,35 @@ export const deleteUser = internalMutation({ export const getUsersBySearchTerm = query({ args: { term: v.string(), paginationOpts: paginationOptsValidator }, handler: async (ctx, args) => { - return await ctx.db + const users = await ctx.db .query('users') .withSearchIndex('search_by_username', q => q.search('clerkUser.username', args.term) ) .paginate(args.paginationOpts); + + const userDtos = users.page.map(createUserDto); + + return { ...users, page: userDtos }; }, }); + +export function createUserDto(user: Doc<'users'> | null) { + if (user === null) { + return { + _id: '' as Id<'users'>, + _creationTime: 0, + clerkUsername: null, + clerkImageUrl: '', + clerkUserId: '', + }; + } + + return { + _id: user._id, + _creationTime: user._creationTime, + clerkUsername: user.clerkUser.username, + clerkImageUrl: user.clerkUser.image_url, + clerkUserId: user.clerkUser.id, + }; +} diff --git a/src/app/search/[term]/page.tsx b/src/app/search/[term]/page.tsx new file mode 100644 index 0000000..7a04868 --- /dev/null +++ b/src/app/search/[term]/page.tsx @@ -0,0 +1,12 @@ +import SearchResults from '@/components/SearchResults'; + +export default function SearchPage({ params }: { params: { term: string } }) { + return ( +
+
+

Here's what we found

+ +
+
+ ); +} diff --git a/src/components/Navbar.tsx b/src/components/Navbar.tsx index af9e54f..3eb50fe 100644 --- a/src/components/Navbar.tsx +++ b/src/components/Navbar.tsx @@ -1,5 +1,6 @@ 'use client'; +import { useRouter } from '@/hooks'; import { SignedIn, SignedOut, useUser } from '@clerk/nextjs'; import Link from 'next/link'; import { useEffect, useState } from 'react'; @@ -16,8 +17,10 @@ import ThemeButton from './ThemeButton'; import UserButton from './UserButton'; export default function Navbar() { + const [term, setTerm] = useState(''); const { user } = useUser(); const [navOpen, setNavOpen] = useState(false); + const router = useRouter(); useEffect(() => { function handler() { @@ -57,13 +60,27 @@ export default function Navbar() {