From 2debcc60ea829bfb6fe9d832ab1f918c451bd1ff Mon Sep 17 00:00:00 2001 From: stefanaz2 Date: Sat, 29 Oct 2022 21:22:16 -0400 Subject: [PATCH] issue410 on first click searchbar shows top 5 subreddits by popularity --- components/Search.tsx | 3 +- pages/api/preSearch.ts | 138 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 140 insertions(+), 1 deletion(-) create mode 100644 pages/api/preSearch.ts diff --git a/components/Search.tsx b/components/Search.tsx index 849f6bd2..e6da8a2a 100644 --- a/components/Search.tsx +++ b/components/Search.tsx @@ -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, @@ -63,7 +64,7 @@ export default function Search() { } itemComponent={AutoCompleteItem} nothingFound="No subs found. Start typing to search." diff --git a/pages/api/preSearch.ts b/pages/api/preSearch.ts new file mode 100644 index 00000000..826c6fa8 --- /dev/null +++ b/pages/api/preSearch.ts @@ -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' + }) + } +}