206 lines
7.2 KiB
TypeScript
206 lines
7.2 KiB
TypeScript
import { useMemo, useRef, useState } from 'react'
|
|
import { useLoaderData, useSearchParams } from 'react-router-dom'
|
|
import { useLocalStorage } from 'hooks'
|
|
import { BlogCardDetails, NSFWFilter, SortBy } from 'types'
|
|
import { SearchInput } from '../../components/SearchInput'
|
|
import { BlogCard } from '../../components/BlogCard'
|
|
import '../../styles/filters.css'
|
|
import '../../styles/pagination.css'
|
|
import '../../styles/search.css'
|
|
import '../../styles/styles.css'
|
|
|
|
export const BlogsPage = () => {
|
|
const blogs = useLoaderData() as Partial<BlogCardDetails>[] | undefined
|
|
const [filterOptions, setFilterOptions] = useLocalStorage('filter-blog', {
|
|
sort: SortBy.Latest,
|
|
nsfw: NSFWFilter.Hide_NSFW
|
|
})
|
|
|
|
// Search
|
|
const searchTermRef = useRef<HTMLInputElement>(null)
|
|
const [searchParams, setSearchParams] = useSearchParams()
|
|
const [searchTerm, setSearchTerm] = useState(searchParams.get('q') || '')
|
|
const handleSearch = () => {
|
|
const value = searchTermRef.current?.value || '' // Access the input value from the ref
|
|
setSearchTerm(value)
|
|
|
|
if (value) {
|
|
searchParams.set('q', value)
|
|
} else {
|
|
searchParams.delete('q')
|
|
}
|
|
|
|
setSearchParams(searchParams, {
|
|
replace: true
|
|
})
|
|
}
|
|
const handleKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
|
|
if (event.key === 'Enter') {
|
|
handleSearch()
|
|
}
|
|
}
|
|
|
|
// Filter
|
|
const filteredBlogs = useMemo(() => {
|
|
const filterNsfwFn = (blog: Partial<BlogCardDetails>) => {
|
|
switch (filterOptions.nsfw) {
|
|
case NSFWFilter.Hide_NSFW:
|
|
return !blog.nsfw
|
|
case NSFWFilter.Only_NSFW:
|
|
return blog.nsfw
|
|
default:
|
|
return blog
|
|
}
|
|
}
|
|
|
|
let filtered = blogs?.filter(filterNsfwFn) || []
|
|
const lowerCaseSearchTerm = searchTerm.toLowerCase()
|
|
|
|
if (searchTerm !== '') {
|
|
const filterSearchTermFn = (blog: Partial<BlogCardDetails>) =>
|
|
(blog.title || '').toLowerCase().includes(lowerCaseSearchTerm) ||
|
|
(blog.summary || '').toLowerCase().includes(lowerCaseSearchTerm) ||
|
|
(blog.content || '').toLowerCase().includes(lowerCaseSearchTerm) ||
|
|
(blog.tTags || []).findIndex((tag) =>
|
|
tag.toLowerCase().includes(lowerCaseSearchTerm)
|
|
) > -1
|
|
filtered = filtered.filter(filterSearchTermFn)
|
|
}
|
|
|
|
if (filterOptions.sort === SortBy.Latest) {
|
|
filtered.sort((a, b) =>
|
|
a.published_at && b.published_at ? b.published_at - a.published_at : 0
|
|
)
|
|
} else if (filterOptions.sort === SortBy.Oldest) {
|
|
filtered.sort((a, b) =>
|
|
a.published_at && b.published_at ? a.published_at - b.published_at : 0
|
|
)
|
|
}
|
|
|
|
return filtered
|
|
}, [blogs, searchTerm, filterOptions.sort, filterOptions.nsfw])
|
|
|
|
return (
|
|
<div className='InnerBodyMain'>
|
|
<div className='ContainerMain'>
|
|
<div className='IBMSecMainGroup IBMSecMainGroupAlt'>
|
|
<div className='IBMSecMain'>
|
|
<div className='SearchMainWrapper'>
|
|
<div className='IBMSMTitleMain'>
|
|
<h2 className='IBMSMTitleMainHeading'>Blogs</h2>
|
|
</div>
|
|
<SearchInput
|
|
ref={searchTermRef}
|
|
handleKeyDown={handleKeyDown}
|
|
handleSearch={handleSearch}
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div className='IBMSecMain'>
|
|
<div className='FiltersMain'>
|
|
<div className='FiltersMainElement'>
|
|
<div className='dropdown dropdownMain'>
|
|
<button
|
|
className='btn dropdown-toggle btnMain btnMainDropdown'
|
|
aria-expanded='false'
|
|
data-bs-toggle='dropdown'
|
|
type='button'
|
|
>
|
|
{filterOptions.sort}
|
|
</button>
|
|
<div className='dropdown-menu dropdownMainMenu'>
|
|
{Object.values(SortBy).map((item, index) => (
|
|
<div
|
|
key={`sortByItem-${index}`}
|
|
className='dropdown-item dropdownMainMenuItem'
|
|
onClick={() =>
|
|
setFilterOptions((prev) => ({
|
|
...prev,
|
|
sort: item
|
|
}))
|
|
}
|
|
>
|
|
{item}
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div className='FiltersMainElement'>
|
|
<div className='dropdown dropdownMain'>
|
|
<button
|
|
className='btn dropdown-toggle btnMain btnMainDropdown'
|
|
aria-expanded='false'
|
|
data-bs-toggle='dropdown'
|
|
type='button'
|
|
>
|
|
{filterOptions.nsfw}
|
|
</button>
|
|
<div className='dropdown-menu dropdownMainMenu'>
|
|
{Object.values(NSFWFilter).map((item, index) => (
|
|
<div
|
|
key={`nsfwFilterItem-${index}`}
|
|
className='dropdown-item dropdownMainMenuItem'
|
|
onClick={() =>
|
|
setFilterOptions((prev) => ({
|
|
...prev,
|
|
nsfw: item
|
|
}))
|
|
}
|
|
>
|
|
{item}
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className='IBMSecMain IBMSMListWrapper'>
|
|
<div className='IBMSMList'>
|
|
{filteredBlogs &&
|
|
filteredBlogs.map((b) => <BlogCard key={b.id} {...b} />)}
|
|
</div>
|
|
</div>
|
|
|
|
<div className='IBMSecMain'>
|
|
<div className='PaginationMain'>
|
|
<div className='PaginationMainInside'>
|
|
<a
|
|
className='PaginationMainInsideBox PaginationMainInsideBoxArrows'
|
|
href='#'
|
|
>
|
|
<i className='fas fa-chevron-left'></i>
|
|
</a>
|
|
<div className='PaginationMainInsideBoxGroup'>
|
|
<a className='PaginationMainInsideBox PMIBActive' href='#'>
|
|
<p>1</p>{' '}
|
|
</a>
|
|
<a className='PaginationMainInsideBox' href='#'>
|
|
<p>2</p>{' '}
|
|
</a>
|
|
<a className='PaginationMainInsideBox' href='#'>
|
|
<p>3</p>
|
|
</a>
|
|
<p className='PaginationMainInsideBox PMIBDots'>...</p>
|
|
<a className='PaginationMainInsideBox' href='#'>
|
|
<p>8</p>
|
|
</a>
|
|
</div>
|
|
<a
|
|
className='PaginationMainInsideBox PaginationMainInsideBoxArrows'
|
|
href='#'
|
|
>
|
|
<i className='fas fa-chevron-right'></i>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|