feat: finish search feature

This commit is contained in:
Stevan Freeborn
2023-09-16 11:28:55 -05:00
parent e2161e62c3
commit 9ad0b51b29
6 changed files with 87 additions and 20 deletions
-1
View File
@@ -201,7 +201,6 @@ export const getPostsBySearchTerm = query({
.withSearchIndex('search_by_content', q => .withSearchIndex('search_by_content', q =>
q.search('contentText', args.term) q.search('contentText', args.term)
) )
.filter(q => q.eq(q.field('parentPostId'), undefined))
.paginate(args.paginationOpts); .paginate(args.paginationOpts);
const postsWithUser = await getPostsWithUsers({ ctx, posts: posts.page }); const postsWithUser = await getPostsWithUsers({ ctx, posts: posts.page });
+1 -1
View File
@@ -11,7 +11,7 @@ export default function Loader({
}) { }) {
if (isLoading) { if (isLoading) {
return ( return (
<div className='flex w-full items-center justify-center gap-2 p-5 border-t border-gray-600 text-sm'> <div className='flex w-full items-center justify-center gap-2 p-5 text-sm'>
<SpinningLoader className='animate-spin w-5 h-5' /> <SpinningLoader className='animate-spin w-5 h-5' />
Loading... Loading...
</div> </div>
+3 -2
View File
@@ -6,7 +6,7 @@ import Link from 'next/link';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { AiFillHome } from 'react-icons/ai'; import { AiFillHome } from 'react-icons/ai';
import { BiLogIn, BiPlus } from 'react-icons/bi'; import { BiLogIn, BiPlus } from 'react-icons/bi';
import { BsPersonPlusFill } from 'react-icons/bs'; import { BsPersonPlusFill, BsSearch } from 'react-icons/bs';
import { ImProfile } from 'react-icons/im'; import { ImProfile } from 'react-icons/im';
import { import {
RiMenuFoldLine, RiMenuFoldLine,
@@ -75,10 +75,11 @@ export default function Navbar() {
className='w-full md:w-[unset] py-1 px-2 rounded-md border border-gray-600 dark:bg-primary-gray dark:border-0' className='w-full md:w-[unset] py-1 px-2 rounded-md border border-gray-600 dark:bg-primary-gray dark:border-0'
/> />
<button <button
className='flex items-center justify-center absolute right-0 top-1/2 transform -translate-x-1/2 -translate-y-1/2'
disabled={!term || !term.trim()} disabled={!term || !term.trim()}
type='submit' type='submit'
> >
Search <BsSearch />
</button> </button>
</form> </form>
</li> </li>
+81 -14
View File
@@ -1,13 +1,15 @@
'use client'; 'use client';
import { PostWithUserDto, UserDto } from '@/app/types';
import { UsePaginatedQueryReturnType, usePaginatedQuery } from 'convex/react'; import { UsePaginatedQueryReturnType, usePaginatedQuery } from 'convex/react';
import { FunctionReference } from 'convex/server'; import { FunctionReference } from 'convex/server';
import { useState } from 'react'; import { useState } from 'react';
import { api } from '../../convex/_generated/api'; import { api } from '../../convex/_generated/api';
import { Id } from '../../convex/_generated/dataModel'; import Loader from './Loader';
import Post from './Post';
import UserProfile from './UserProfile'; import UserProfile from './UserProfile';
type UserSearchResultsPager = UsePaginatedQueryReturnType< type Pager<T> = UsePaginatedQueryReturnType<
FunctionReference< FunctionReference<
'query', 'query',
'public', 'public',
@@ -20,29 +22,84 @@ type UserSearchResultsPager = UsePaginatedQueryReturnType<
}; };
}, },
{ {
page: { page: T[];
_id: Id<'users'>;
_creationTime: number;
clerkUsername: string | null;
clerkImageUrl: string;
clerkUserId: string;
}[];
isDone: boolean; isDone: boolean;
continueCursor: string; continueCursor: string;
} }
> >
>; >;
function UserSearchResults({ pager }: { pager: UserSearchResultsPager }) { function NoResults() {
return (
<div className='flex flex-col justify-center items-center gap-2'>
<h2 className='text-xl'>
Hmmm it doesn&apos;t look like we could find anything.
</h2>
</div>
);
}
function UserSearchResults({
pager,
pageSize,
}: {
pager: Pager<UserDto>;
pageSize: number;
}) {
const { status, results, isLoading, loadMore } = pager;
if (isLoading === false && results.length == 0) {
return <NoResults />;
}
return ( return (
<div> <div>
<div> <div className='flex flex-col gap-2'>
{pager.results.map(user => ( {results.map(user => (
<div key={user._id}> <div key={user._id}>
<UserProfile user={user} /> <UserProfile user={user} />
</div> </div>
))} ))}
</div> </div>
<Loader
isLoading={isLoading}
status={status}
loadButtonClickHandler={() => loadMore(pageSize)}
/>
</div>
);
}
function PostSearchResults({
pager,
pageSize,
}: {
pager: Pager<PostWithUserDto>;
pageSize: number;
}) {
const { status, results, isLoading, loadMore } = pager;
if (isLoading === false && results.length == 0) {
return <NoResults />;
}
return (
<div className='flex flex-col items-center w-full'>
<div className='flex flex-col items-center w-full'>
{results.map(post => (
<div
key={post._id}
className='w-full border border-t-0 border-gray-600 first:border-t last:rounded-b-md p-1 last:mb-4'
>
<Post post={post} />
</div>
))}
</div>
<Loader
isLoading={isLoading}
status={status}
loadButtonClickHandler={() => loadMore(pageSize)}
/>
</div> </div>
); );
} }
@@ -63,7 +120,7 @@ export default function SearchResults({ term }: { term: string }) {
); );
return ( return (
<div> <div className='flex flex-col gap-4'>
<div className='flex gap-2 border-b border-gray-600'> <div className='flex gap-2 border-b border-gray-600'>
<button <button
onClick={() => setCurrent('posts')} onClick={() => setCurrent('posts')}
@@ -83,7 +140,17 @@ export default function SearchResults({ term }: { term: string }) {
</button> </button>
</div> </div>
{current === 'posts' ? 'Posts' : <UserSearchResults pager={usersPager} />} {current === 'posts' ? (
<PostSearchResults
pager={postsPager}
pageSize={PAGE_SIZE}
/>
) : (
<UserSearchResults
pager={usersPager}
pageSize={PAGE_SIZE}
/>
)}
</div> </div>
); );
} }
+1 -1
View File
@@ -47,7 +47,7 @@ export default function UserPosts({ user }: { user: UserDto }) {
{postsWithUser.map(post => ( {postsWithUser.map(post => (
<div <div
key={post._id} key={post._id}
className='w-full border border-t-0 border-gray-600 first:border-t last:rounded-b-md p-1 last:mb-4' className='w-full border border-t-0 border-gray-600 last:rounded-b-md p-1 last:mb-4'
> >
<Post post={post} /> <Post post={post} />
</div> </div>
+1 -1
View File
@@ -90,7 +90,7 @@ export default function UserProfile({ user }: { user: UserDto }) {
</div> </div>
</div> </div>
</div> </div>
<div className='flex flex-1 bg-white dark:bg-primary-gray pl-5 border border-gray-600 border-b-0'> <div className='flex flex-1 bg-white dark:bg-primary-gray pl-5 border border-gray-600'>
<div className='flex flex-col min-h-[25px] items-start gap-1 py-1 pl-[116px] md:flex-row md:items-center md:gap-4'> <div className='flex flex-col min-h-[25px] items-start gap-1 py-1 pl-[116px] md:flex-row md:items-center md:gap-4'>
{countDataLoaded ? ( {countDataLoaded ? (
<> <>