Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 31 additions & 11 deletions src/components/search.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,30 @@ const inputStyles = css`

export default function Search(props) {
const [searchInput, setSearchInput] = useState('');
const searchBarPlaceholder = props.isGlobalSearch ? 'Search published posts...' : 'Search your posts...'

useEffect(() => {
const delayDebounceFn = setTimeout(() => {
if (props.posts) {
filterPosts();
const handleKeyDown = (event) => {
if (props.isGlobalSearch) {
if (event.key === 'Enter') {
console.log('do validate')
props.getSearchInput(searchInput);
}
}, 500)
}
}

return () => clearTimeout(delayDebounceFn)
useEffect(() => {
if (props.isGlobalSearch) {

} else {
const delayDebounceFn = setTimeout(() => {
if (props.posts) {
filterPosts();
props.getSearchInput(searchInput);
}
}, 500)

return () => clearTimeout(delayDebounceFn)
}
}, [searchInput])

const filterPosts = () => {
Expand All @@ -39,13 +53,18 @@ export default function Search(props) {
let tempPosts = props.posts.filter(p => p.title.toLowerCase().includes(searchInput.toLowerCase()))
props.getFilteredPosts(tempPosts);
}

}

const exploreSearchBarStyles =
props.isGlobalSearch ?
css`width: 100%` // Explore page search bar styles
:
css`width: 80%` // Dashboard page search bar styles

return (
<div css={css`
width: 80%;
`}>
<div
css={exploreSearchBarStyles}
>
<svg xmlns="http://www.w3.org/2000/svg" width="1.1em" height="1.1em" fill="none" stroke-width="1.5" viewBox="0 0 24 24" color="#ffffff"css={css`
position: absolute;
margin: 0.8em
Expand All @@ -54,7 +73,8 @@ export default function Search(props) {
<input
id="search-posts"
type="text"
placeholder="Search your posts..."
placeholder={searchBarPlaceholder}
onKeyDown={handleKeyDown}
css={css`${inputStyles}`}
onChange={e => {
setSearchInput(e.target.value);
Expand Down
19 changes: 18 additions & 1 deletion src/lib/db.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,4 +112,21 @@ export async function createPostForUser(userId) {
.update({ posts: firebase.firestore.FieldValue.arrayUnion(doc.id) })

return doc.id
}
}

export async function filterExplorePosts(searchInput) {
let posts = []
const snapshot = await firebase.firestore().collection('posts')
.where("published", "==", true)
.where('title', '!=', '')
.where("title", ">=", searchInput)
.where('title', "<", searchInput + 'z')
.get()
snapshot.docs.map(doc => {
posts.push({
id: doc.id,
...doc.data()
});
})
return posts;
}
11 changes: 2 additions & 9 deletions src/pages/dashboard/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,15 +65,11 @@ export default function Dashboard() {
return (
<>
<Header>

<Link href="/dashboard/list">
<a>Reading List</a>
</Link>

{/*Adds a new Link to the Contact Page*/}

<Link href="https://linktr.ee/theabyssofficial">
<a>Contact</a>
<Link href="/explore">
<a>Explore</a>
</Link>

<ProfileSettingsModal Trigger={() => 'Profile'} uid={user?.uid} />
Expand Down Expand Up @@ -118,9 +114,6 @@ export default function Dashboard() {
isGlobalSearch={false}
getFilteredPosts={getFilteredPosts}
getSearchInput={getSearchInput}
css={css`
width: 3em
`}
></Search>

<Link href="https://theabyss.ink/solomonlijo/guideofabyss">
Expand Down
3 changes: 3 additions & 0 deletions src/pages/dashboard/list.js
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,9 @@ export default function ReadingList() {
<Link href="/dashboard">
<a>Dashboard</a>
</Link>
<Link href="/explore">
<a>Explore</a>
</Link>
<ProfileSettingsModal Trigger={() => 'Profile'} uid={user?.uid} />
<button onClick={() => auth.signOut()}>Sign Out</button>
</Header>
Expand Down
190 changes: 190 additions & 0 deletions src/pages/explore/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
/** @jsxImportSource @emotion/react */
import Link from 'next/link'
import Head from 'next/head'
import { useEffect, useState } from 'react'
import { css } from '@emotion/react'
import { useRouter } from 'next/router'
import { htmlToText } from 'html-to-text'
import { useAuthState } from 'react-firebase-hooks/auth'
import { useCollectionData } from 'react-firebase-hooks/firestore'
import { collection, query, where, getDocs } from "firebase/firestore";

import { createPostForUser, filterExplorePosts } from '../../lib/db'
import { firestore, auth } from '../../lib/firebase'

import Button from '../../components/button'
import Header from '../../components/header'
import Spinner from '../../components/spinner'
import Container from '../../components/container'
import Search from '../../components/search'
import ProfileSettingsModal from '../../components/profile-settings-modal'
import { truncate } from '../../lib/utils'
import { getPostByID } from '../../lib/db'

export default function Explore() {
const router = useRouter()

const [user, userLoading, userError] = useAuthState(auth);
const [initPosts, initPostsLoading, initPostsError] = useCollectionData(
firestore.collection('posts')
.where('published', '==', true)
.where('title', '!=', '')
.orderBy('title')
.limit(15),{ idField: 'id' },
)
const [explorePosts, setExplorePosts] = useState([]);

useEffect(() => {
console.log(user, userLoading, userError)
if (!user && !userLoading && !userError) {
router.push('/')
return
}
}, [router, user, userLoading, userError]);

// Set initial filteredPosts
useEffect(() => {
(async () => {
let posts = await setPostAuthorProfilePics(initPosts);
setExplorePosts(posts);
})()
}, initPosts)

// Set the profile pics for each author
const setPostAuthorProfilePics = async(filteredExplorePosts) => {
const postPromises = filteredExplorePosts?.map(async p => {
const post = await getPostByID(p.id)
const author = await firestore
.collection('users')
.doc(post.author)
.get()
post.author = author.data()
return post;
})
const posts = postPromises ? await Promise.all(postPromises) : null
return posts
}

// Get the searchInput from Search component and do the global search on db
const getFilteredExplorePosts = async (searchInput) => {
let filteredExplorePosts = await filterExplorePosts(searchInput);
filteredExplorePosts = await setPostAuthorProfilePics(filteredExplorePosts);
setExplorePosts(filteredExplorePosts)
return filteredExplorePosts;
}

return (
<>
<Header>
<Link href="/dashboard">
<a>Dashboard</a>
</Link>
<Link href="/dashboard/list">
<a>Reading List</a>
</Link>
<ProfileSettingsModal Trigger={() => 'Profile'} uid={user?.uid} />
<button onClick={() => auth.signOut()}>Sign Out</button>
</Header>

{userError ? (
<>
<p>Oop, we&apos;ve had an error:</p>
<pre>{JSON.stringify(error)}</pre>
</>
) : user ? (
explorePosts && explorePosts.length > 0 ? (
<>
<Search
posts={explorePosts}
isGlobalSearch={true}
getSearchInput={getFilteredExplorePosts}
css={css`
margin-left: 0em
`}
></Search>
<ul
css={css`
list-style: none;

li {
max-width: 25rem;
margin: 2.5rem 0;
}
`}
>
{explorePosts.map(post => (
<li key={post.id}>
<Link href={`/${post.author.name}/${post.slug}`}>
<a style={{ textDecoration: 'none', color: 'inherit' }}>
<h3
css={css`
font-size: 1rem;
font-weight: 400;
margin-bottom: 0.6rem;
`}
>
{post.title ? htmlToText(post.title) : 'Untitled'}
</h3>

<div
css={css`
display: flex;
align-items: center;
color: var(--grey-3);
font-size: 0.9rem;
`}
>
<img
src={post.author.photo}
alt="Profile picture"
css={css`
width: 1.5rem;
border-radius: 1rem;
margin-right: 0.75rem;
`}
/>
<p>{post.author.displayName}</p>
</div>

<p
css={css`
color: var(--grey-4);
font-family: 'Newsreader', serif;
line-height: 1.5em;
margin-top: 0.5rem;
`}
>
{post.excerpt
? htmlToText(post.excerpt)
: truncate(htmlToText(post.content), 25)}
</p>
</a>
</Link>
</li>
))}
</ul>
</>
) : (
<p>No posts have been published yet... You could be the first!</p>
)
) : (
<Spinner />
)}
</>
)
}
Explore.getLayout = function Explore(page) {
return (
<Container
maxWidth="640px"
css={css`
margin-top: 5rem;
`}
>
<Head>
<title>Explore / The Abyss</title>
</Head>
{page}
</Container>
)
}