Skip to content

Commit

Permalink
initial commit of v5
Browse files Browse the repository at this point in the history
  • Loading branch information
gregrickaby committed Feb 9, 2024
1 parent 68299ba commit 9b83e19
Show file tree
Hide file tree
Showing 56 changed files with 2,039 additions and 2,537 deletions.
3 changes: 3 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,8 @@ REDDIT_CLIENT_ID="YOUR-TOKEN-HERE"
# Get one here: https://www.reddit.com/prefs/apps
REDDIT_CLIENT_SECRET="YOUR-TOKEN-HERE"

# Search Secret. Used to verify the search request.
NEXT_PUBLIC_SEARCH_SECRET="ANY-RANDOM-STRING-HERE"

# Used on production to verify the site with Google Webmaster Tools.
NEXT_PUBLIC_GOOGLE_SITE_VERIFICATION="YOUR-TOKEN-HERE"
1 change: 0 additions & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ module.exports = {
extends: ['next/core-web-vitals', 'prettier'],
rules: {
'@next/next/no-img-element': 'off',
'func-style': ['error', 'declaration'],
'no-console': ['error', {allow: ['warn', 'error']}]
}
}
12 changes: 3 additions & 9 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
/node_modules
/.pnp
.pnp.js
.yarn/install-state.gz

# testing
/coverage
Expand All @@ -25,18 +26,11 @@ yarn-debug.log*
yarn-error.log*

# local env files
.env
.env.local
.env.development.local
.env.test.local
.env.production.local
.env*.local

# vercel
.vercel

# sitemap
robots.txt
sitemap.xml

# typescript
*.tsbuildinfo
next-env.d.ts
3 changes: 2 additions & 1 deletion .prettierrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@ module.exports = {
singleQuote: true,
bracketSpacing: false,
semi: false,
trailingComma: 'none'
trailingComma: 'none',
plugins: ['prettier-plugin-tailwindcss']
}
19 changes: 18 additions & 1 deletion .stylelintrc.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,20 @@
module.exports = {
extends: ['stylelint-config-standard']
extends: ['stylelint-config-standard'],
rules: {
'at-rule-no-unknown': [
true,
{
ignoreAtRules: [
'tailwind',
'apply',
'layer',
'variants',
'responsive',
'screen'
]
}
],
'no-descending-specificity': null,
'selector-class-pattern': null
}
}
8 changes: 0 additions & 8 deletions .vscode/extensions.json

This file was deleted.

29 changes: 0 additions & 29 deletions .vscode/settings.json

This file was deleted.

3 changes: 3 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,9 @@ REDDIT_CLIENT_ID="YOUR-TOKEN-HERE"
# Get one here: https://www.reddit.com/prefs/apps
REDDIT_CLIENT_SECRET="YOUR-TOKEN-HERE"
# Search Secret
NEXT_PUBLIC_SEARCH_SECRET="ANY-RANDOM-STRING-HERE"
# Used on production to verify the site with Google Webmaster Tools.
NEXT_PUBLIC_GOOGLE_SITE_VERIFICATION="YOUR-TOKEN-HERE"
```
Expand Down
21 changes: 0 additions & 21 deletions app/Page.module.css

This file was deleted.

162 changes: 162 additions & 0 deletions app/actions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
'use server'

import config from '@/lib/config'
import {
FetchSubredditProps,
RedditPostResponse,
RedditSearchResponse,
RedditTokenResponse
} from '@/lib/types'

/**
* Fetch a Reddit oAuth token.
*
* @see https://github.com/reddit-archive/reddit/wiki/OAuth2#application-only-oauth
*/
export async function fetchToken(): Promise<RedditTokenResponse> {
try {
// Fetch the Reddit oAuth token.
const response = await fetch(
'https://www.reddit.com/api/v1/access_token?grant_type=client_credentials&device_id=DO_NOT_TRACK_THIS_DEVICE',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'User-Agent': config.userAgent,
Authorization: `Basic ${btoa(
`${process.env.REDDIT_CLIENT_ID}:${process.env.REDDIT_CLIENT_SECRET}`
)}`
},
next: {
tags: ['token'],
revalidate: config.cacheTtl
}
}
)

// Bad response? Bail.
if (!response.ok) {
throw new Error('Failed to fetch Reddit oAuth Token.')
}

// Parse the response.
const data = (await response.json()) as RedditTokenResponse

// If the response is empty, bail.
if (!data.access_token) {
throw new Error(data.error)
}

// Return the token.
return {
access_token: data.access_token
}
} catch (error) {
console.error('Exception thrown in fetchToken()')
return {error: `${error}`}
}
}

/**
* Fetch search results.
*/
export async function fetchSearchResults(
query: string
): Promise<RedditSearchResponse> {
try {
// Get the access token.
const {access_token} = await fetchToken()

// Attempt to fetch subreddits.
const response = await fetch(
`https://oauth.reddit.com/api/subreddit_autocomplete_v2?query=${query}&limit=10&include_over_18=true&include_profiles=false&typeahead_active=true&search_query_id=DO_NOT_TRACK`,
{
headers: {
authorization: `Bearer ${access_token}`
},
next: {
revalidate: config.cacheTtl
}
}
)
// Bad response? Bail.
if (!response.ok) {
throw new Error(`Failed to fetch search results. ${response.statusText}`)
}

// Parse the response.
const data = (await response.json()) as RedditSearchResponse

// If the response is empty, bail.
if (!data.data) {
throw new Error('Failed to parse search results.')
}

// Return the search results.
return data
} catch (error) {
console.error('Exception thrown in fetchSearchResults()')
return {error: `${error}`}
}
}

/**
* Fetch subreddit posts.
*/
export async function fetchSubredditPosts(
props: FetchSubredditProps
): Promise<RedditPostResponse> {
try {
// Destructure props.
const {slug, sortBy, limit, after} = props

// Fetch the Reddit oAuth token.
const {access_token} = await fetchToken()

// Fetch the subreddit posts.
const response = await fetch(
`https://oauth.reddit.com/r/${slug}/${sortBy}/.json?limit=${limit}&after=${after}&raw_json=1`,
{
headers: {
'User-Agent': config.userAgent,
authorization: `Bearer ${access_token}`
},
next: {
tags: [slug],
revalidate: config.cacheTtl
}
}
)

// Bad response? Bail.
if (!response.ok) {
throw new Error(` ${response.statusText}: /r/${slug}`)
}

// Parse the response.
const data = (await response.json()) as RedditPostResponse

// If the response is empty, bail.
if (!data.data) {
throw new Error('Failed to parse subreddit response.')
}

// Return the posts.
return {
kind: data.kind,
data: {
modhash: data.data.modhash,
dist: data.data.dist,
children: data.data.children.filter(
({data}) =>
data.post_hint && data.post_hint !== 'self' && !data.poststickied // Exclude self/stickied posts.
),
after: data.data.after,
before: data.data.before
}
}
} catch (error) {
console.error('Exception thrown in fetchSubredditPosts()')
return {error: `${error}`}
}
}
Loading

0 comments on commit 9b83e19

Please sign in to comment.