Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

issue410 on first click searchbar shows top 5 subreddits by popularity #414

Merged
merged 1 commit into from
Oct 30, 2022
Merged
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
3 changes: 2 additions & 1 deletion components/Search.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ export default function Search() {
const {setSubreddit, searchInput, setSearchInput} = useRedditContext()
const {classes} = useStyles()
const [debounced] = useDebouncedValue(searchInput, 300)
const {data: beforeSearch} = useSWR(`/api/preSearch?limit=5`, fetcher)
const {data: results} = useSWR(`/api/search?term=${debounced}`, fetcher, {
revalidateIfStale: true,
revalidateOnFocus: false,
Expand All @@ -63,7 +64,7 @@ export default function Search() {
<Autocomplete
aria-label="Search sub-reddits"
className={classes.searchBar}
data={results ? results : []}
data={results ? results : beforeSearch ? beforeSearch : []}
icon={<IconSearch />}
itemComponent={AutoCompleteItem}
nothingFound="No subs found. Start typing to search."
Expand Down
138 changes: 138 additions & 0 deletions pages/api/preSearch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
import type {NextRequest} from 'next/server'

export const config = {
runtime: 'experimental-edge'
}

/**
* Popular Subreddit Reddit API.
*
* @example
* /api/preSearch?limit=5
*
* @see https://www.reddit.com/dev/api#GET_subreddits_{where}
* @see https://nextjs.org/docs/api-routes/edge-api-routes
* @see https://nextjs.org/docs/api-reference/edge-runtime
*/
export default async function search(req: NextRequest) {
// Parse and sanitize query params from request.
const limit = req.nextUrl.searchParams.get('limit') || '5'

try {
// Generate random device ID.
// @see https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues
const array = new Uint32Array(24)
const deviceId = self.crypto.getRandomValues(array)

// Try and fetch a new access token.
const tokenResponse = await fetch(
`https://www.reddit.com/api/v1/access_token?grant_type=client_credentials&device_id=${deviceId}`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json charset=UTF-8',
'User-Agent': 'reddit-image-viewer/* by Greg Rickaby',
Authorization: `Basic ${btoa(
`${process.env.REDDIT_CLIENT_ID}:${process.env.REDDIT_CLIENT_SECRET}`
)}`
}
}
)

// Bad response? Bail...
if (tokenResponse.status != 200) {
return new Response(
JSON.stringify({
error: `${tokenResponse.statusText}`
}),
{
status: tokenResponse.status,
statusText: tokenResponse.statusText
}
)
}

// Get the access token.
const token = await tokenResponse.json()

// Issue with token? Bail...
if (token.error) {
return new Response(
JSON.stringify({
error: token.error
}),
{
status: token.status,
statusText: token.statusText
}
)
}

// Attempt to fetch subreddits.
const response = await fetch(
`https://oauth.reddit.com/subreddits/popular?limit=${limit}`,
{
headers: {
authorization: `Bearer ${token.access_token}`
}
}
)

// No response? Bail...
if (response.status != 200) {
return new Response(
JSON.stringify({
error: `${response.statusText}`
}),
{
status: response.status,
statusText: response.statusText
}
)
}

// Parse the response.
const subs = await response.json()

// No data in the response? Bail...
if (!subs.data && !subs.data.children.length) {
return new Response(
JSON.stringify({
error: `No data returned from Reddit.`
}),
{
status: 400,
statusText: 'Bad Request'
}
)
}

// Filter uneeded data to keep the payload small.
const filtered = subs.data.children.map(
(sub: {data: {over18?: string; url?: string; display_name?: string}}) => {
return {
over_18: sub.data.over18 ? true : false,
url: sub.data.url ? sub.data.url : '',
value: sub.data.display_name ? sub.data.display_name : ''
}
}
)

// Send the response.
return new Response(JSON.stringify(filtered), {
headers: {
'Content-Type': 'application/json',
'Cache-Control': 'public, s-maxage=1, stale-while-revalidate=59'
},
status: 200,
statusText: 'OK'
})
} catch (error) {
// Issue? Leave a message and bail.
console.error(error)
return new Response(JSON.stringify({error: `${error}`}), {
status: 500,
statusText: 'Internal Server Error'
})
}
}