From aa021311b0431ba44d5e0537238bbd5425635873 Mon Sep 17 00:00:00 2001 From: Ammar Ahmed Date: Thu, 9 May 2024 15:04:54 -0400 Subject: [PATCH 1/2] feat: add debounce hook + update blog metadata query --- src/graphql/queries/Metadata.ts | 3 +++ src/hooks/debounce.ts | 25 +++++++++++++++++++++++++ src/pages/Blog/index.tsx | 15 ++++++++------- 3 files changed, 36 insertions(+), 7 deletions(-) create mode 100644 src/hooks/debounce.ts diff --git a/src/graphql/queries/Metadata.ts b/src/graphql/queries/Metadata.ts index d210009..f9134a4 100644 --- a/src/graphql/queries/Metadata.ts +++ b/src/graphql/queries/Metadata.ts @@ -53,11 +53,13 @@ export const BLOG_METADATA_QUERY: DocumentNode = gql` $tags: [String!] $category: String $onlyPublished: Boolean + $query: String ) { blogMetadata( tags: $tags category: $category onlyPublished: $onlyPublished + query: $query ) { id name @@ -84,5 +86,6 @@ export namespace BlogMetadataQuery { onlyPublished?: boolean tags?: string[] category?: string + query?: string } } diff --git a/src/hooks/debounce.ts b/src/hooks/debounce.ts new file mode 100644 index 0000000..13cfd9c --- /dev/null +++ b/src/hooks/debounce.ts @@ -0,0 +1,25 @@ +import { useState, useEffect } from 'react' + +export type UseDebouncedOpts = { + delay?: number +} + +export function useDebounced( + defaultValue: T, + opts?: UseDebouncedOpts, +) { + let delay = 200 + if (opts?.delay) delay = opts.delay + + const [value, setValue] = useState(defaultValue) + const [debouncedValue, setDebouncedValue] = + useState(defaultValue) + + useEffect(() => { + setTimeout(() => { + setDebouncedValue(value) + }, delay) + }, [value, delay]) + + return [value, debouncedValue, setValue] +} diff --git a/src/pages/Blog/index.tsx b/src/pages/Blog/index.tsx index d123231..4bccd34 100644 --- a/src/pages/Blog/index.tsx +++ b/src/pages/Blog/index.tsx @@ -1,7 +1,7 @@ import React, { useState } from 'react' import { useTextGradient } from '../../hooks/styles' -// import { Input } from "@nextui-org/react"; -// import { MagnifyingGlassIcon } from "@heroicons/react/24/solid"; +import { Input } from '@nextui-org/react' +import { MagnifyingGlassIcon } from '@heroicons/react/24/solid' import BlogTags from './BlogTags' import BlogCategories from './BlogCategories' import { @@ -20,6 +20,7 @@ const Blog: React.FC = () => { }) const [tags, setTags] = useState([]) const [category, setCategory] = useState() + const { data, loading } = useQuery< BlogMetadataQuery.Response, BlogMetadataQuery.Variables @@ -45,12 +46,12 @@ const Blog: React.FC = () => { experiences or anything else of interest to me.

{/* TODO: Implement search in backend and come back to this. */} - {/* } + } isClearable - /> */} + />
setTags(values)} From 33b6724288ce7231bfc3403efb70ed78cebfbc5d Mon Sep 17 00:00:00 2001 From: Ammar Ahmed Date: Thu, 9 May 2024 15:30:11 -0400 Subject: [PATCH 2/2] feat: add search to blog posts --- src/hooks/debounce.ts | 7 +++++-- src/pages/Blog/index.tsx | 9 ++++++++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/hooks/debounce.ts b/src/hooks/debounce.ts index 13cfd9c..c176092 100644 --- a/src/hooks/debounce.ts +++ b/src/hooks/debounce.ts @@ -4,10 +4,12 @@ export type UseDebouncedOpts = { delay?: number } +export type UseDebouncedResponse = [T, T, SetState] + export function useDebounced( defaultValue: T, opts?: UseDebouncedOpts, -) { +): UseDebouncedResponse { let delay = 200 if (opts?.delay) delay = opts.delay @@ -16,9 +18,10 @@ export function useDebounced( useState(defaultValue) useEffect(() => { - setTimeout(() => { + const id = setTimeout(() => { setDebouncedValue(value) }, delay) + return () => clearTimeout(id) }, [value, delay]) return [value, debouncedValue, setValue] diff --git a/src/pages/Blog/index.tsx b/src/pages/Blog/index.tsx index 4bccd34..effff99 100644 --- a/src/pages/Blog/index.tsx +++ b/src/pages/Blog/index.tsx @@ -11,6 +11,7 @@ import { import { useQuery } from '@apollo/client' import BlogCard, { BlogCardSkeleton } from './BlogCard' import { useBreakpointValue } from '../../hooks/mediaQuery' +import { useDebounced } from '../../hooks/debounce' const Blog: React.FC = () => { const textGradient = useTextGradient({ @@ -20,6 +21,9 @@ const Blog: React.FC = () => { }) const [tags, setTags] = useState([]) const [category, setCategory] = useState() + const [query, debouncedQuery, setQuery] = useDebounced('', { + delay: 400, + }) const { data, loading } = useQuery< BlogMetadataQuery.Response, @@ -29,6 +33,7 @@ const Blog: React.FC = () => { onlyPublished: true, category, tags, + query: debouncedQuery, }, }) const isMobile = useBreakpointValue({ default: true, md: false }) @@ -41,7 +46,7 @@ const Blog: React.FC = () => { > Blog -

+

Sometimes I like to write about things I've worked on, my experiences or anything else of interest to me.

@@ -51,6 +56,8 @@ const Blog: React.FC = () => { label='Search' startContent={} isClearable + value={query} + onValueChange={setQuery} />